001package ca.uhn.fhir.rest.server;
002
003/*
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
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
026import org.apache.commons.lang3.StringUtils;
027
028/**
029 * Determines the server's base using the incoming request
030 */
031public class IncomingRequestAddressStrategy implements IServerAddressStrategy {
032
033        private String myServletPath;
034
035        @Override
036        public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) {
037                if (theRequest == null) {
038                        return null;
039                }
040                String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
041
042                String servletPath;
043                if (myServletPath != null) {
044                        servletPath = myServletPath;
045                } else {
046                        servletPath = StringUtils.defaultString(theRequest.getServletPath());
047                }
048
049                StringBuffer requestUrl = theRequest.getRequestURL();
050                String servletContextPath = StringUtils.defaultString(theRequest.getContextPath());
051
052                String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
053                if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
054                        requestPath = requestPath.substring(1);
055                }
056
057                int startOfPath = requestUrl.indexOf("//");
058                int requestUrlLength = requestUrl.length();
059
060                if (startOfPath != -1 && (startOfPath + 2) < requestUrlLength) {
061                        startOfPath = requestUrl.indexOf("/", startOfPath + 2);
062                }
063                if (startOfPath == -1) {
064                        startOfPath = 0;
065                }
066
067                int contextIndex;
068                if (servletPath.length() == 0 || servletPath.equals("/")) {
069                        if (requestPath.length() == 0) {
070                                contextIndex = requestUrlLength;
071                        } else {
072                                contextIndex = requestUrl.indexOf(requestPath, startOfPath);
073                        }
074                } else {
075                        //servletContextPath can start with servletPath 
076                        contextIndex = requestUrl.indexOf(servletPath + "/", startOfPath);
077                        if (contextIndex == -1) {
078                                contextIndex = requestUrl.indexOf(servletPath, startOfPath);
079                        }
080                }
081
082                String fhirServerBase;
083                int length = contextIndex + servletPath.length();
084                if (length > requestUrlLength) {
085                        length = requestUrlLength;
086                }
087                fhirServerBase = requestUrl.substring(0, length);
088                return fhirServerBase;
089        }
090
091        /**
092         * If set to a non-null value (default is <code>null</code>), this address strategy assumes that the FHIR endpoint is deployed to the given servlet path within the context. This is useful in some
093         * deployments where it isn't obvious to the servlet which part of the path is actually the root path to reach the servlet.
094         * <p>
095         * Example values could be:
096         * <ul>
097         * <li>null</li>
098         * <li>/</li>
099         * <li>/base</li>
100         * </ul>
101         * </p>
102         * <p>
103         * <b>Wildcards are not supported!</b>
104         * </p>
105         */
106        public void setServletPath(String theServletPath) {
107                myServletPath = theServletPath;
108        }
109
110        /**
111         * Determines the servlet's context path.
112         * 
113         * This is here to try and deal with the wide variation in servers and what they return.
114         * 
115         * getServletContext().getContextPath() is supposed to return the path to the specific servlet we are deployed as but it's not available everywhere. On some servers getServletContext() can return
116         * null (old Jetty seems to suffer from this, see hapi-fhir-base-test-mindeps-server) and on other old servers (Servlet 2.4) getServletContext().getContextPath() doesn't even exist.
117         * 
118         * theRequest.getContextPath() returns the context for the specific incoming request. It should be available everywhere, but it's likely to be less predicable if there are multiple servlet mappings
119         * pointing to the same servlet, so we don't favour it. This is possibly not the best strategy (maybe we should just always use theRequest.getContextPath()?) but so far people seem happy with this
120         * behavour across a wide variety of platforms.
121         * 
122         * If you are having troubles on a given platform/configuration and want to suggest a change or even report incompatibility here, we'd love to hear about it.
123         */
124        public static String determineServletContextPath(HttpServletRequest theRequest, RestfulServer server) {
125                String retVal;
126                if (server.getServletContext() != null) {
127                        if (server.getServletContext().getMajorVersion() >= 3 || (server.getServletContext().getMajorVersion() > 2 && server.getServletContext().getMinorVersion() >= 5)) {
128                                retVal = server.getServletContext().getContextPath();
129                        } else {
130                                retVal = theRequest.getContextPath();
131                        }
132                } else {
133                        retVal = theRequest.getContextPath();
134                }
135                retVal = StringUtils.defaultString(retVal);
136                return retVal;
137        }
138
139}