Resolve #3490 ApacheProxyAddressStrategy should support Forward header from (#3491)

* #3490 ApacheProxyAddressStrategy should support Forward header from
Rfc7239

* #3490 ApacheProxyAddressStrategy should support Forward header from
Rfc7239

Remove one copy & paste error

* #3490 ApacheProxyAddressStrategy should support Forward header from
Rfc7239

Fix failing test

* #3490 ApacheProxyAddressStrategy should support Forward header from
Rfc7239

Fix failing test
This commit is contained in:
Thomas Papke 2022-03-25 10:50:59 +01:00 committed by GitHub
parent 9e46fad1b9
commit 91799b7a8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 46 deletions

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.rest.server;
import java.net.URI;
/*
* #%L
* HAPI FHIR - Server Framework
@ -24,18 +22,21 @@ import java.net.URI;
import java.util.Optional;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import static java.util.Optional.ofNullable;
import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy;
/**
* Works like the normal
@ -77,7 +78,7 @@ public class ApacheProxyAddressStrategy extends IncomingRequestAddressStrategy {
private static final String X_FORWARDED_PREFIX = "x-forwarded-prefix";
private static final String X_FORWARDED_PROTO = "x-forwarded-proto";
private static final String X_FORWARDED_HOST = "x-forwarded-host";
private static final String X_FORWARDED_PORT = "x-forwarded-port";
private static final Logger LOG = LoggerFactory
.getLogger(ApacheProxyAddressStrategy.class);
@ -99,62 +100,48 @@ public class ApacheProxyAddressStrategy extends IncomingRequestAddressStrategy {
String serverBase = super.determineServerBase(servletContext, request);
ServletServerHttpRequest requestWrapper = new ServletServerHttpRequest(
request);
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpRequest(requestWrapper);
uriBuilder.replaceQuery(null);
HttpHeaders headers = requestWrapper.getHeaders();
Optional<String> forwardedHost = headers
.getValuesAsList(X_FORWARDED_HOST).stream().findFirst();
return forwardedHost
.map(s -> forwardedServerBase(serverBase, headers, s))
.orElse(serverBase);
adjustSchemeWithDefault(uriBuilder, headers);
return forwardedServerBase(serverBase, headers, uriBuilder);
}
/**
* If forward host ist defined, but no forward protocol, use the configured default.
*
* @param uriBuilder
* @param headers
*/
private void adjustSchemeWithDefault(UriComponentsBuilder uriBuilder,
HttpHeaders headers) {
if (headers.getFirst(X_FORWARDED_HOST) != null
&& headers.getFirst(X_FORWARDED_PROTO) == null) {
uriBuilder.scheme(useHttps ? "https" : "http");
}
}
private String forwardedServerBase(String originalServerBase,
HttpHeaders headers, String forwardedHost) {
HttpHeaders headers, UriComponentsBuilder uriBuilder) {
Optional<String> forwardedPrefix = getForwardedPrefix(headers);
LOG.debug("serverBase: {}, forwardedHost: {}, forwardedPrefix: {}",
originalServerBase, forwardedHost, forwardedPrefix);
LOG.debug("serverBase: {}, forwardedPrefix: {}", originalServerBase, forwardedPrefix);
LOG.debug("request header: {}", headers);
String host = protocol(headers) + "://" + forwardedHost;
String hostWithOptionalPort = port(headers).map(p -> (host + ":" + p))
.orElse(host);
String path = forwardedPrefix
.orElseGet(() -> pathFrom(originalServerBase));
return joinStringsWith(hostWithOptionalPort, path, "/");
}
private Optional<String> port(HttpHeaders headers) {
return ofNullable(headers.getFirst(X_FORWARDED_PORT));
uriBuilder.replacePath(path);
return uriBuilder.build().toUriString();
}
private String pathFrom(String serverBase) {
String serverBasePath = URI.create(serverBase).getPath();
return StringUtils.defaultIfBlank(serverBasePath, "");
}
private static String joinStringsWith(String left, String right,
String joiner) {
if (left.endsWith(joiner) && right.startsWith(joiner)) {
return left + right.substring(1);
} else if (left.endsWith(joiner) || right.startsWith(joiner)) {
return left + right;
} else {
return left + joiner + right;
}
UriComponents build = UriComponentsBuilder.fromHttpUrl(serverBase).build();
return StringUtils.defaultIfBlank(build.getPath(), "");
}
private Optional<String> getForwardedPrefix(HttpHeaders headers) {
return ofNullable(headers.getFirst(X_FORWARDED_PREFIX));
}
private String protocol(HttpHeaders headers) {
String protocol = headers.getFirst(X_FORWARDED_PROTO);
if (protocol != null) {
return protocol;
}
return useHttps ? "https" : "http";
}
/**
* Static factory for instance using <code>http://</code>
*/
@ -168,4 +155,4 @@ public class ApacheProxyAddressStrategy extends IncomingRequestAddressStrategy {
public static ApacheProxyAddressStrategy forHttps() {
return new ApacheProxyAddressStrategy(true);
}
}
}

View File

@ -86,6 +86,32 @@ public class ApacheProxyAddressStrategyTest {
assertEquals("https://my.example.host:345/imagingstudy/fhir",
serverBase);
}
@Test
public void testWithForwardedHostAndUnsetPort() {
ApacheProxyAddressStrategy addressStrategy = new ApacheProxyAddressStrategy(
true);
MockHttpServletRequest request = prepareRequest();
request.addHeader("X-Forwarded-Host", "my.example.host");
request.addHeader("X-Forwarded-Port", "-1");
String serverBase = addressStrategy.determineServerBase(null, request);
assertEquals("https://my.example.host/imagingstudy/fhir",
serverBase);
}
@Test
public void testWithRfc7239 () {
ApacheProxyAddressStrategy addressStrategy = new ApacheProxyAddressStrategy(
true);
MockHttpServletRequest request = prepareRequest();
request.addHeader("Forwarded", "Forwarded: for=192.0.2.43,"
+ " for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com");
String serverBase = addressStrategy.determineServerBase(null, request);
assertEquals("http://example.com/imagingstudy/fhir",
serverBase);
}
private MockHttpServletRequest prepareRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();