I figured this out for Jetty servlet which I use. If you use the default TomCat servlet you will have to do something similar that works for TomCat I suppose.
So to start with I have a ssl port as default that is activated. To also allow http you need to configure an additional http port in your config. Then you need to add a server Handler. You could add the Handler SecuredRedirectHandler to redirect ALL http requests to the https port. Since we don't want to redirect ALL http requests we make our own CustomRedirectHandler that extends SecuredRedirectHandler.
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.addServerCustomizers(new JettyServerCustomizer() {
@Override
public void customize(Server server) {
final HttpConnectionFactory httpConnectionFactory = server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class);
// Enable HTTP for assigned port
final ServerConnector httpConnector = new ServerConnector(server, httpConnectionFactory);
httpConnector.setPort(serverProperties.intHttpPort() /* HTTP */);
server.addConnector(httpConnector);
// Add a CustomRedirectHandler to Server Handlers
final HandlerList handlerList = new HandlerList();
handlerList.addHandler(new CustomRedirectHandler());
for(Handler handler : server.getHandlers()) {
handlerList.addHandler(handler);
}
server.setHandler(handlerList);
}
});
return factory;
}
In our CustomRedirectHandler we can check if the requested endpoint is in our "allowed http" array. If it already request https or is allowed http then we do nothing, else redirect to https. My example allows http only for the endpoint that starts with "/.well-known/acme-challenge/" to allow requests to http://example.com/.well-known/acme-challenge/TOKEN for example.
public class CustomRedirectHandler extends SecuredRedirectHandler {
private final String[] allowedHttp = {"/.well-known/acme-challenge/"};
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
HttpChannel channel = baseRequest.getHttpChannel();
if (baseRequest.isSecure() || channel == null) {
// nothing to do, already requested https
return;
}
// Check if request is for allowed http
if (allowHttp(baseRequest)) {
return;
}
// Else Redirect to https
super.handle(target, baseRequest, request, response);
}
public boolean allowHttp(Request baseRequest) {
String pathInfo = baseRequest.getPathInfo();
if (pathInfo == null) {
return false;
}
for (String allowed : allowedHttp) {
if (pathInfo.startsWith(allowed)) {
return true;
}
}
return false;
}
}