mkopylec / charon-spring-boot-starter

Reverse proxy implementation in form of a Spring Boot starter.
Apache License 2.0
248 stars 55 forks source link

Rewrite paths in location headers #120

Open jwgmeligmeyling opened 3 years ago

jwgmeligmeyling commented 3 years ago

I am trying to mimic the following Apache2 configuration with Charon:

  ProxyPreserveHost  On
  ProxyPass  / http://127.0.0.1:8080/site/
  ProxyPassReverse  / http://127.0.0.1:8080/site/
  ProxyPassReverseCookiePath  /site /

I believe ProxyPass can be replaced by regexRequestPathRewriter() and ProxyPreserveHost can be changed by including/excluding requestHostHeaderRewriter.

It appears as if rootPathResponseCookiesRewriter can be used for the ProxyPassReverseCookiePath, however the paths that need to be placed and the path it needs to be changed into are not configurable. This could potentially be an interesting addition. Currently, replacing with root fulfills my needs, so I don't think I need this change right now.

Finally I am seeking a replacement for ProxyPassReverse. This ensures Location, Content-Location and URI headers in the response are adjusted appropiately for the rewritten path. Is there a rewriter in Charon available that makes this possible?

jwgmeligmeyling commented 3 years ago

For now I cooked up the following partial solution:

public static class ProxyPassReverseConfigurer extends RequestForwardingInterceptorConfigurer<ProxyPassReverseRewriter> {

    protected ProxyPassReverseConfigurer() {
        super(new ProxyPassReverseRewriter());
    }

    public ProxyPassReverseConfigurer path(String path) {
        this.configuredObject.setPath(path);
        return this;
    }

    public static ProxyPassReverseConfigurer proxyPassReverseRewriter() {
        return new ProxyPassReverseConfigurer();
    }

}

public static class ProxyPassReverseRewriter implements RequestForwardingInterceptor {

    private static final String HEADER_LOCATION = "Location";
    private static final String HEADER_URI = "URI";
    private static final String HEADER_CONTENT_LOCATION = "Content-Location";
    private static final RequestForwardingInterceptorType RESPONSE_LOCATION_HEADER_REWRITER = new RequestForwardingInterceptorType(399);

    private String path = "./";

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) {
        URI originalRequestUri = request.getURI();
        HttpResponse response = execution.execute(request);
        HttpHeaders rewrittenHeaders = copyHeaders(response.getHeaders());
        rewriteHeader(originalRequestUri, rewrittenHeaders, HEADER_LOCATION);
        rewriteHeader(originalRequestUri, rewrittenHeaders, HEADER_URI);
        rewriteHeader(originalRequestUri, rewrittenHeaders, HEADER_CONTENT_LOCATION);
        response.setHeaders(rewrittenHeaders);
        return response;
    }

    private void rewriteHeader(URI originalRequestUri , HttpHeaders rewrittenHeaders, String header) {
        String headerValue = rewrittenHeaders.getFirst(header);

        if (headerValue != null) {
            URI uri = URI.create(headerValue);

            if (uri.getPath().startsWith(path)) {
                // Remove the context path from the redirect URI.
                uri = UriComponentsBuilder.fromUri(uri).replacePath(uri.getPath().substring(path.length())).build().toUri();
            }

            rewrittenHeaders.set(header, uri.toASCIIString());
        }
    }

    @Override
    public RequestForwardingInterceptorType getType() {
        return RESPONSE_LOCATION_HEADER_REWRITER;
    }

}
mkopylec commented 3 years ago

Hi, currently there is no way to mimic ProxyPassReverse directive in Charon (besides of what you have already done of course). I've noticed that the ProxyPassReverse* directives from Apache are not very flexible because of lack of regex rewriting support. If ProxyPassReverse* functionalities were to be added to Charon, I think they should work like regexRequestPathRewriter(). So I can consider adding regexPathResponseCookiesRewriter() and regexResponseLocationHeadersRewriter().

jwgmeligmeyling commented 3 years ago

I'll try to cook up a patch based on your suggestions if I get to it.

geezer78 commented 2 years ago

Hi There,

i have a similar requirement to change a specific header that contains a location: "X-GWT-Module-Base" i've been trying to add the code from @jwgmeligmeyling but have not succeeded this far.

could you please explain where i connect your code snippet into my charonConfigurer?