Issue
We have re-written our webservices with Vert.x 4 and we're more than satisfied. Before putting them in production we want to secure them and we're trying to enable https. This is the main verticle:
public class MainVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
//Deploy the HTTP server
vertx.deployVerticle(
"com.albertomiola.equations.http.HttpServerVerticle",
new DeploymentOptions().setInstances(3)
);
}
// I use this only in IntelliJ Idea because when I hit "Run" the build starts
public static void main(String[] args) {
Launcher.executeCommand("run", MainVerticle.class.getName());
}
}
And this is the most relevant part of the code of the HTTP server verticle:
public class HttpServerVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) throws Exception {
var options = new HttpServerOptions();
options.setPort(443)
.setSSl(true)
.setPemTrustOptions(...)
.setPemKeyCertOptions(...);
var server = vertx.createHttpServer(options);
var router = Router.router(vertx);
//Start
server
.requestHandler(router)
.listen(portNumber, ar -> {
if (ar.succeeded()) {
startPromise.complete();
} else {
startPromise.fail(ar.cause());
}
});
}
}
The above code works fine because my website is reachable at https://website.it and https://www.website.it (with letsencrypt certs).
The problem is that when I try to access http://website.it or http://www.website.it it doesn't work (meaning that I can't see the home because the server is unreachable).
How can I redirect http://website.it to https//website.it?
I have googled a lot and what I've managed to find is:
- this vertx example that setups HTTPS as I do but it does not mention the redirect
- this SO question that seems to tell me what to do but I cannot understand what to do and I am not sure if that setup has to go in the HttpServerOption object
Maybe is my https configuration wrong? I am using Java 11 on IntelliJ IDEA (Maven build) and Vert.x 4 latest version. Thank you
Solution
The final solutions I came up with are shown below and they are equivalent. The idea in both cases is to have an http server (that listens on port 80 of course) that redirects every call to an https server.
So in my case I can do exactly what I want because http://mydomain.it is redirected to https://mydomain.it as expected. For example when I make a call to
http://mydomain.it/api/polynomial/laguerre
There is an http server alive that receives the request but then it throws the ball immediately to
https://mydomain.it/api/polynomial/laguerre
Of course if you directly call the https version this "intermediate" step does not happen.
Using Vert.x
The answer from Hugo in the above post gives a valid solution using Vertx. I have a main verticle that looks like this:
public class MainVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
//Deploy the HTTPS server
vertx.deployVerticle(
"com.albertomiola.equations.http.HttpsServerVerticle",
new DeploymentOptions().setInstances(n)
);
//Deploy the HTTP server
vertx.deployVerticle(
"com.albertomiola.equations.http.HttpServerVerticle",
new DeploymentOptions().setInstances(1)
);
}
}
The first verticle is the "real" website because it contains all the logic that I need in my webservice (routers, handlers, models...) and it looks like this:
public class HttpsServerVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) throws Exception {
// Setup the HTTPS
var httpOptions = new HttpServerOptions()
.setCompressionSupported(true)
.setPort(443)
.setSsl(true)
.setPemTrustOptions(...)
.setPemKeyCertOptions(...);
// Start the server and the routes
var server = vertx.createHttpServer(httpOptions);
var router = Router.router(vertx);
//Start
server
.requestHandler(router)
.listen(ar -> {
if (ar.succeeded()) {
startPromise.complete();
} else {
startPromise.fail(ar.cause());
}
});
}
}
The other verticle instead is just an http server that permanently redirects (with the 301) to the https version of the webserver. Here's the code:
public class HttpsServerVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) throws Exception {
var server = vertx.createHttpServer(httpOptions);
var router = Router.router(vertx);
//Start
server
.requestHandler(r -> {
r.response()
.setStatusCode(301)
.putHeader("Location", r.absoluteURI().replace("http", "https"))
.end();
});
}
}
In this way there are 2 servers active but actually it's like if there were only 1 because the http server (port 80) redirects every call to the https server (port 443).
Using Nginx
The other approach that I have tested requires nginx but it does the same things that I've done in the above example. It listens to http requests on port 80 and then it redirects them to the https version.
- Install Nginx on my Ubuntu server (or whatever you have)
- Go in the configuration file which is in
/etc/nginx/nginx.conf
in my case Add the below code
http { server { listen 80; server_name mydomain.it; return 301 https://$server_name$request_uri; } //other code... }
- Restart with
systemctl restart nginx
- Restart with
Now every call to the http version is redirected to the https version. Thanks to the user injecteer which has suggested me this way.
I am using this approach because I prefer not having a single verticle only for the http version. Also this article from the Vertx website says that this approach is valid:
It is common to expose HTTP servers in production through a front HTTP server / proxy like Nginx, and have it use HTTPS for incoming connections. Vert.x can also expose HTTPS by itself, so as to provide end-to-end encryption.
So yeah, setup https with Vertx (I'd recommend letsencrypt certifies) but also redirect calls to https with nginx.
I wrongly thought that I could do something particular with Vertx to handle this redirect but that's not possible. After the suggestions of the people in this answer AND some good googling around I've learned that this approach is common and that's what I should do!
Answered By - Alberto Miola
Answer Checked By - Senaida (JavaFixing Volunteer)