Search code examples
jersey-2.0dropwizardmod-proxy

How can I force UriBuilder to use https?


I'm currently running Dropwizard behind Apache httpd acting as a reverse proxy, configured like so:

<VirtualHost *:443>
  <Location /api>
    ProxyPass "http://my.app.org:8080/api"
  <Location>
  ...
</VirtualHost>

With other Location settings serving static assets and some authentication thrown in. Now, httpd also performs SSL offloading, so my Dropwizard only receives the plain HTTP request.

In my Dropwizard API, I like to return a Location header indicating newly created resources:

@Path("/comment")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
class CommentResource() {
  @PUT
  fun create(@Context uri: UriInfo, entity: EventComment): Response {
    val stored: EventComment = createEntity(entity)
    return Response.created(uri.baseUriBuilder.path(MessageStream::class.java)
            .resolveTemplate("realmish", entity.realmId)
            .path(stored.id.toString()).build()).build()
}

This creates a Response with a Location header from JerseyUriBuilder:

Location http://my.app.org/api/messages/123

Which, on my SSL-only app, naturally fails to load (I'm actually surprised this didn't turn out to render as http://my.app.org:8080/api/messages/123 - probably also the reason why ProxyPassReverse didn't help).

I know I can force the scheme to be https by using baseUriBuilder.scheme("https"), but this gets repetitive and is an easy source of errors.

Thus my question: how can I either make Jersey generate correct front-end URLs or successfully make httpd rewrite those generated by Dropwizard?


Solution

  • For Jersey, you can use a pre-matching ContainerRequestFilter to rewrite the URI. For example

    @PreMatching
    public class SchemeRewriteFilter implements ContainerRequestFilter {
    
        @Override
        public void filter(ContainerRequestContext request) throws IOException {
            URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
            request.setRequestUri(newUri);
        }
    }
    

    Then just register it with Jersey (Dropwizard)

    env.jersey().register(SchemeRewriteFilter.class);
    

    EDIT

    The above only works when you use uriInfo.getAbsolutePathBuilder(). If you want to use uriInfo.getBaseUriBuilder(), then you need to use the overloaded setRequestUri that accepts the base uri as the first arg.

    URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
    URI baseUri = request.getUriInfo().getBaseUriBuilder().scheme("https").build();
    request.setRequestUri(baseUri, newUri);