
001/*- 002 * #%L 003 * HAPI FHIR - Server Framework 004 * %% 005 * Copyright (C) 2014 - 2025 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.util; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.rest.api.Constants; 024import ca.uhn.fhir.rest.api.PreferHeader; 025import ca.uhn.fhir.rest.api.RequestTypeEnum; 026import ca.uhn.fhir.rest.server.RestfulServerUtils; 027import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 028import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; 029import ca.uhn.fhir.rest.server.servlet.ServletSubRequestDetails; 030import com.google.common.collect.ArrayListMultimap; 031import org.apache.http.NameValuePair; 032 033import java.util.Collection; 034import java.util.HashMap; 035import java.util.List; 036import java.util.Map; 037 038public class ServletRequestUtil { 039 public static ServletSubRequestDetails getServletSubRequestDetails( 040 ServletRequestDetails theRequestDetails, 041 String url, 042 String theVerb, 043 ArrayListMultimap<String, String> theParamValues) { 044 ServletSubRequestDetails requestDetails = new ServletSubRequestDetails(theRequestDetails); 045 requestDetails.setServletRequest(theRequestDetails.getServletRequest()); 046 requestDetails.setRequestType(RequestTypeEnum.valueOf(theVerb)); 047 requestDetails.setServer(theRequestDetails.getServer()); 048 requestDetails.setRestOperationType(theRequestDetails.getRestOperationType()); 049 050 int qIndex = url.indexOf('?'); 051 requestDetails.setParameters(new HashMap<>()); 052 if (qIndex != -1) { 053 String params = url.substring(qIndex); 054 List<NameValuePair> parameters = MatchUrlUtil.translateMatchUrl(params); 055 for (NameValuePair next : parameters) { 056 theParamValues.put(next.getName(), next.getValue()); 057 } 058 for (Map.Entry<String, Collection<String>> nextParamEntry : 059 theParamValues.asMap().entrySet()) { 060 String[] nextValue = nextParamEntry 061 .getValue() 062 .toArray(new String[nextParamEntry.getValue().size()]); 063 requestDetails.addParameter(nextParamEntry.getKey(), nextValue); 064 } 065 url = url.substring(0, qIndex); 066 } 067 068 if (url.length() > 0 && url.charAt(0) == '/') { 069 url = url.substring(1); 070 } 071 072 requestDetails.setRequestPath(url); 073 requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase()); 074 requestDetails.setTenantId(theRequestDetails.getTenantId()); 075 076 theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url); 077 return requestDetails; 078 } 079 080 /** 081 * Validates that the request contains a <code>Prefer: respond-async</code> request header, and 082 * throws an {@link InvalidRequestException} if not. 083 * 084 * @param theRequestDetails The incoming request details 085 * @param theOperationName The name of the FHIR operation being invoked (e.g. <code>$export</code>) 086 * @since 8.6.0 087 */ 088 public static void validatePreferAsyncHeader(ServletRequestDetails theRequestDetails, String theOperationName) { 089 String preferHeader = theRequestDetails.getHeader(Constants.HEADER_PREFER); 090 PreferHeader prefer = RestfulServerUtils.parsePreferHeader(null, preferHeader); 091 if (!prefer.getRespondAsync()) { 092 throw new InvalidRequestException(Msg.code(513) + "Must request async processing for " + theOperationName); 093 } 094 } 095}