001/* 002 * #%L 003 * HAPI FHIR - Server Framework 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.server; 021 022import jakarta.servlet.ServletContext; 023import jakarta.servlet.http.HttpServletRequest; 024import org.apache.commons.lang3.StringUtils; 025 026/** 027 * Determines the server's base using the incoming request 028 */ 029public class IncomingRequestAddressStrategy implements IServerAddressStrategy { 030 031 private String myServletPath; 032 033 @Override 034 public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) { 035 if (theRequest == null) { 036 return null; 037 } 038 String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI()); 039 040 String servletPath; 041 if (myServletPath != null) { 042 servletPath = myServletPath; 043 } else { 044 servletPath = StringUtils.defaultString(theRequest.getServletPath()); 045 } 046 047 StringBuffer requestUrl = theRequest.getRequestURL(); 048 String servletContextPath = StringUtils.defaultString(theRequest.getContextPath()); 049 050 String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length()); 051 if (requestPath.length() > 0 && requestPath.charAt(0) == '/') { 052 requestPath = requestPath.substring(1); 053 } 054 055 int startOfPath = requestUrl.indexOf("//"); 056 int requestUrlLength = requestUrl.length(); 057 058 if (startOfPath != -1 && (startOfPath + 2) < requestUrlLength) { 059 startOfPath = requestUrl.indexOf("/", startOfPath + 2); 060 } 061 if (startOfPath == -1) { 062 startOfPath = 0; 063 } 064 065 int contextIndex; 066 if (servletPath.length() == 0 || servletPath.equals("/")) { 067 if (requestPath.length() == 0) { 068 contextIndex = requestUrlLength; 069 } else { 070 contextIndex = requestUrl.indexOf(requestPath, startOfPath); 071 } 072 } else { 073 // servletContextPath can start with servletPath 074 contextIndex = requestUrl.indexOf(servletPath + "/", startOfPath); 075 if (contextIndex == -1) { 076 contextIndex = requestUrl.indexOf(servletPath, startOfPath); 077 } 078 } 079 080 String fhirServerBase; 081 int length = contextIndex + servletPath.length(); 082 if (length > requestUrlLength) { 083 length = requestUrlLength; 084 } 085 fhirServerBase = requestUrl.substring(0, length); 086 return fhirServerBase; 087 } 088 089 /** 090 * 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 091 * deployments where it isn't obvious to the servlet which part of the path is actually the root path to reach the servlet. 092 * <p> 093 * Example values could be: 094 * <ul> 095 * <li>null</li> 096 * <li>/</li> 097 * <li>/base</li> 098 * </ul> 099 * </p> 100 * <p> 101 * <b>Wildcards are not supported!</b> 102 * </p> 103 */ 104 public void setServletPath(String theServletPath) { 105 myServletPath = theServletPath; 106 } 107 108 /** 109 * Determines the servlet's context path. 110 * 111 * This is here to try and deal with the wide variation in servers and what they return. 112 * 113 * 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 114 * 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. 115 * 116 * 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 117 * 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 118 * behavour across a wide variety of platforms. 119 * 120 * 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. 121 */ 122 public static String determineServletContextPath(HttpServletRequest theRequest, RestfulServer server) { 123 String retVal; 124 if (server.getServletContext() != null) { 125 if (server.getServletContext().getMajorVersion() >= 3 126 || (server.getServletContext().getMajorVersion() > 2 127 && 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}