diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java index 8f276310b60..0af922abb5c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java @@ -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 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 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 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 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 http:// */ @@ -168,4 +155,4 @@ public class ApacheProxyAddressStrategy extends IncomingRequestAddressStrategy { public static ApacheProxyAddressStrategy forHttps() { return new ApacheProxyAddressStrategy(true); } -} +} \ No newline at end of file diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategyTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategyTest.java index be65be2a45d..44c2d41e14d 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategyTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategyTest.java @@ -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();