001package ca.uhn.fhir.rest.server;
002
003/*
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2019 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import javax.servlet.ServletContext;
024import javax.servlet.http.HttpServletRequest;
025
026/**
027 * Works like the normal {@link ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy} unless there's an x-forwarded-host present, in which case that's used in place of the server's address.
028 *      <p>
029 * If the Apache Http Server <code>mod_proxy</code> isn't configured to supply <code>x-forwarded-proto</code>, the factory method that you use to create the address strategy will determine the default. Note that
030 * <code>mod_proxy</code> doesn't set this by default, but it can be configured via <code>RequestHeader set X-Forwarded-Proto http</code> (or https)
031 *      </p>
032 *      <p>
033 * If you want to set the protocol based on something other than the constructor argument, you should be able to do so by overriding <code>protocol</code>.
034 *      </p>
035 *      <p>
036 * Note that while this strategy was designed to work with Apache Http Server, and has been tested against it, it should work with any proxy server that sets <code>x-forwarded-host</code>
037 * </p>
038 *
039 * @author Created by Bill de Beaubien on 3/30/2015.
040 */
041public class ApacheProxyAddressStrategy extends IncomingRequestAddressStrategy {
042        private boolean myUseHttps = false;
043
044        protected ApacheProxyAddressStrategy(boolean theUseHttps) {
045                myUseHttps = theUseHttps;
046        }
047
048        @Override
049        public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) {
050                String forwardedHost = getForwardedHost(theRequest);
051                if (forwardedHost != null) {
052                        return forwardedServerBase(theServletContext, theRequest, forwardedHost);
053                }
054                return super.determineServerBase(theServletContext, theRequest);
055        }
056
057        public String forwardedServerBase(ServletContext theServletContext, HttpServletRequest theRequest, String theForwardedHost) {
058                String serverBase = super.determineServerBase(theServletContext, theRequest);
059                String host = theRequest.getHeader("host");
060                if (host != null) {
061                        serverBase = serverBase.replace(host, theForwardedHost);
062                        serverBase = serverBase.substring(serverBase.indexOf("://"));
063                        return protocol(theRequest) + serverBase;
064                }
065                return serverBase;
066        }
067
068        private String getForwardedHost(HttpServletRequest theRequest) {
069                String forwardedHost = theRequest.getHeader("x-forwarded-host");
070                if (forwardedHost != null) {
071                        int commaPos = forwardedHost.indexOf(',');
072                        if (commaPos >= 0) {
073                                forwardedHost = forwardedHost.substring(0, commaPos - 1);
074                        }
075                }
076                return forwardedHost;
077        }
078
079        protected String protocol(HttpServletRequest theRequest) {
080                String protocol = theRequest.getHeader("x-forwarded-proto");
081                if (protocol != null) {
082                        return protocol;
083                }
084                return myUseHttps ? "https" : "http";
085        }
086
087        /**
088         * Static factory for instance using <code>http://</code>
089         */
090        public static ApacheProxyAddressStrategy forHttp() {
091                return new ApacheProxyAddressStrategy(false);
092        }
093
094        /**
095         * Static factory for instance using <code>https://</code>
096         */
097        public static ApacheProxyAddressStrategy forHttps() {
098                return new ApacheProxyAddressStrategy(true);
099        }
100}