package com.vtence.molecule.middlewares; import com.vtence.molecule.Request; import com.vtence.molecule.Response; import com.vtence.molecule.http.HttpStatus; import java.util.concurrent.TimeUnit; import static com.vtence.molecule.http.HeaderNames.STRICT_TRANSPORT_SECURITY; import static com.vtence.molecule.http.HttpMethod.GET; import static com.vtence.molecule.http.HttpMethod.HEAD; import static com.vtence.molecule.http.HttpStatus.MOVED_PERMANENTLY; import static com.vtence.molecule.http.HttpStatus.TEMPORARY_REDIRECT; public class ForceSSL extends AbstractMiddleware { private boolean enable; // Default HSTS settings private boolean hsts = true; private long hstsExpiry = TimeUnit.DAYS.toSeconds(365); private boolean subdomains; private String customHost; private String redirectOn; public ForceSSL() { this(true); } public ForceSSL(boolean enabled) { this.enable = enabled; } public void enable(boolean enabled) { enable = enabled; } public void hsts(boolean enabled) { hsts = enabled; } public void expires(long delayInSeconds) { hstsExpiry = delayInSeconds; } public void includesSubdomains(boolean included) { subdomains = included; } public ForceSSL redirectTo(String host) { this.customHost = host; return this; } public void redirectOn(String header) { redirectOn = header; } public void handle(Request request, Response response) throws Exception { if (enable && !secure(request)) { redirectToHttps(request, response); } else { forward(request, response).whenSuccessful(this::addHSTSHeader); } } private void addHSTSHeader(Response response) { if (response.hasHeader(STRICT_TRANSPORT_SECURITY)) return; if (!hsts) { hstsExpiry = 0; } String security = "max-age=" + hstsExpiry + (subdomains ? "; includeSubdomains" : ""); response.header(STRICT_TRANSPORT_SECURITY, security); } private boolean secure(Request request) { return request.secure() || isProxiedHttps(request); } private boolean isProxiedHttps(Request request) { return redirectOn != null && "https".equalsIgnoreCase(request.header(redirectOn)); } private void redirectToHttps(Request request, Response response) { response.redirectTo(httpsLocationFor(request)) .status(redirectionStatusFor(request)) .done(); } private String httpsLocationFor(Request request) { String host = customHost != null ? customHost : request.hostname(); return "https://" + host + request.uri(); } private HttpStatus redirectionStatusFor(Request request) { return request.method() == GET || request.method() == HEAD ? MOVED_PERMANENTLY : TEMPORARY_REDIRECT; } }