package org.pac4j.vertx; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.Session; import org.pac4j.core.context.Cookie; import org.pac4j.core.context.WebContext; import org.pac4j.core.context.session.SessionStore; import org.pac4j.vertx.auth.Pac4jUser; import org.pac4j.vertx.core.DefaultJsonConverter; import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidParameterException; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; /** * WebContext implementation for Vert.x 3. * * @author Jeremy Prime * @since 2.0.0 */ public class VertxWebContext implements WebContext { private final RoutingContext routingContext; private final String method; private final String serverName; private final int serverPort; private final String fullUrl; private final String scheme; private final String remoteAddress; private final JsonObject headers; private final JsonObject parameters; private final Map<String, String[]> mapParameters; private final SessionStore<VertxWebContext> sessionStore; private boolean contentHasBeenWritten = false; // Need to set chunked before first write of any content public VertxWebContext(final RoutingContext routingContext, final SessionStore<VertxWebContext> sessionStore) { final HttpServerRequest request = routingContext.request(); this.routingContext = routingContext; this.method = request.method().toString(); this.sessionStore = sessionStore; this.fullUrl = request.absoluteURI(); URI uri; try { uri = new URI(fullUrl); } catch (URISyntaxException e) { e.printStackTrace(); throw new InvalidParameterException("Request to invalid URL " + fullUrl + " while constructing VertxWebContext"); } this.scheme = uri.getScheme(); this.serverName = uri.getHost(); this.serverPort = (uri.getPort() != -1) ? uri.getPort() : scheme.equals("http") ? 80 : 443; this.remoteAddress = request.remoteAddress().toString(); headers = new JsonObject(); for (String name : request.headers().names()) { headers.put(name, request.headers().get(name)); } parameters = new JsonObject(); for (String name : request.params().names()) { parameters.put(name, new JsonArray(Arrays.asList(request.params().getAll(name).toArray()))); } mapParameters = new HashMap<>(); for (String name : parameters.fieldNames()) { JsonArray params = parameters.getJsonArray(name); String[] values = new String[params.size()]; int i = 0; for (Object o : params) { values[i++] = (String) o; } mapParameters.put(name, values); } } public void failResponse(final int status) { routingContext.fail(status); } public void completeResponse() { routingContext.response().end(); } @Override public String getRequestParameter(String name) { JsonArray values = parameters.getJsonArray(name); if (values != null && values.size() > 0) { return values.getString(0); } return null; } @Override public Map<String, String[]> getRequestParameters() { return mapParameters; } @Override public Object getRequestAttribute(String s) { return routingContext.get(s); } @Override public void setRequestAttribute(String s, Object o) { routingContext.put(s, o); } @Override public String getRequestHeader(String name) { return headers.getString(name); } @Override public void setSessionAttribute(String name, Object value) { Session session = routingContext.session(); if (session == null) { throw new IllegalStateException("Session required for use of getSessionAttribute"); } // Need to convert to something that can be passed round a distributed vert.x session cleanly if (value == null) { session.remove(name); } else { session.put(name, DefaultJsonConverter.getInstance().encodeObject(value)); } } @Override public Object getSessionAttribute(String name) { Session session = routingContext.session(); if (session == null) { throw new IllegalStateException("Session required for use of getSessionAttribute"); } return DefaultJsonConverter.getInstance().decodeObject(session.get(name)); } @Override public String getSessionIdentifier() { return routingContext.session().id(); } @Override public String getRequestMethod() { return method; } @Override public String getRemoteAddr() { return remoteAddress; } @Override public void writeResponseContent(String content) { if (content != null && !content.isEmpty()) { if (!contentHasBeenWritten) { routingContext.response().setChunked(true); contentHasBeenWritten = true; } routingContext.response().write(content); } } @Override public void setResponseStatus(int code) { routingContext.response().setStatusCode(code); } @Override public void setResponseHeader(String name, String value) { routingContext.response().putHeader(name, value); } public Map<String, String> getResponseHeaders() { return routingContext.response().headers().entries().stream() .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @Override public void setResponseContentType(String s) { routingContext.response().headers().add("Content-Type", s); } @Override public String getServerName() { return serverName; } @Override public int getServerPort() { return serverPort; } @Override public String getScheme() { return scheme; } @Override public boolean isSecure() { return getScheme().equals("https"); } @Override public String getFullRequestURL() { return fullUrl; } @Override public Collection<Cookie> getRequestCookies() { return routingContext.cookies().stream().map(cookie -> { final Cookie p4jCookie = new Cookie(cookie.getName(), cookie.getValue()); p4jCookie.setDomain(cookie.getDomain()); p4jCookie.setPath(cookie.getPath()); return p4jCookie; }).collect(Collectors.toList()); } @Override public void addResponseCookie(Cookie cookie) { routingContext.addCookie(io.vertx.ext.web.Cookie.cookie(cookie.getName(), cookie.getValue())); } @Override public String getPath() { return routingContext.request().path(); } @Override public SessionStore getSessionStore() { return this.sessionStore; } @Override public void setSessionStore(SessionStore sessionStore) { throw new UnsupportedOperationException("Not possible to change the session store for VertxWebContext"); } public Pac4jUser getVertxUser() { return (Pac4jUser) routingContext.user(); } public void removeVertxUser() { routingContext.clearUser(); } public void setVertxUser(final Pac4jUser pac4jUser) { routingContext.setUser(pac4jUser); } public Session getVertxSession() { return routingContext.session(); } }