001package ca.uhn.fhir.rest.server;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 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
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                String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
038
039                String servletPath;
040                if (myServletPath != null) {
041                        servletPath = myServletPath;
042                } else {
043                        servletPath = StringUtils.defaultString(theRequest.getServletPath());
044                }
045
046                StringBuffer requestUrl = theRequest.getRequestURL();
047                String servletContextPath = StringUtils.defaultString(theRequest.getContextPath());
048
049                String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
050                if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
051                        requestPath = requestPath.substring(1);
052                }
053
054                int startOfPath = requestUrl.indexOf("//");
055                int requestUrlLength = requestUrl.length();
056
057                if (startOfPath != -1 && (startOfPath + 2) < requestUrlLength) {
058                        startOfPath = requestUrl.indexOf("/", startOfPath + 2);
059                }
060                if (startOfPath == -1) {
061                        startOfPath = 0;
062                }
063
064                int contextIndex;
065                if (servletPath.length() == 0 || servletPath.equals("/")) {
066                        if (requestPath.length() == 0) {
067                                contextIndex = requestUrlLength;
068                        } else {
069                                contextIndex = requestUrl.indexOf(requestPath, startOfPath);
070                        }
071                } else {
072                        //servletContextPath can start with servletPath 
073                        contextIndex = requestUrl.indexOf(servletPath + "/", startOfPath);
074                        if (contextIndex == -1) {
075                                contextIndex = requestUrl.indexOf(servletPath, startOfPath);
076                        }
077                }
078
079                String fhirServerBase;
080                int length = contextIndex + servletPath.length();
081                if (length > requestUrlLength) {
082                        length = requestUrlLength;
083                }
084                fhirServerBase = requestUrl.substring(0, length);
085                return fhirServerBase;
086        }
087
088        /**
089         * 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
090         * deployments where it isn't obvious to the servlet which part of the path is actually the root path to reach the servlet.
091         * <p>
092         * Example values could be:
093         * <ul>
094         * <li>null</li>
095         * <li>/</li>
096         * <li>/base</li>
097         * </ul>
098         * </p>
099         * <p>
100         * <b>Wildcards are not supported!</b>
101         * </p>
102         */
103        public void setServletPath(String theServletPath) {
104                myServletPath = theServletPath;
105        }
106
107        /**
108         * Determines the servlet's context path.
109         * 
110         * This is here to try and deal with the wide variation in servers and what they return.
111         * 
112         * 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
113         * 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.
114         * 
115         * 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
116         * 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
117         * behavour across a wide variety of platforms.
118         * 
119         * 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.
120         */
121        public static String determineServletContextPath(HttpServletRequest theRequest, RestfulServer server) {
122                String retVal;
123                if (server.getServletContext() != null) {
124                        if (server.getServletContext().getMajorVersion() >= 3 || (server.getServletContext().getMajorVersion() > 2 && server.getServletContext().getMinorVersion() >= 5)) {
125                                retVal = server.getServletContext().getContextPath();
126                        } else {
127                                retVal = theRequest.getContextPath();
128                        }
129                } else {
130                        retVal = theRequest.getContextPath();
131                }
132                retVal = StringUtils.defaultString(retVal);
133                return retVal;
134        }
135
136}