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}