001package ca.uhn.fhir.rest.server.servlet;
002
003import java.io.ByteArrayInputStream;
004
005/*
006 * #%L
007 * HAPI FHIR - Core Library
008 * %%
009 * Copyright (C) 2014 - 2016 University Health Network
010 * %%
011 * Licensed under the Apache License, Version 2.0 (the "License");
012 * you may not use this file except in compliance with the License.
013 * You may obtain a copy of the License at
014 * 
015 *      http://www.apache.org/licenses/LICENSE-2.0
016 * 
017 * Unless required by applicable law or agreed to in writing, software
018 * distributed under the License is distributed on an "AS IS" BASIS,
019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020 * See the License for the specific language governing permissions and
021 * limitations under the License.
022 * #L%
023 */
024
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.Reader;
028import java.util.Collections;
029import java.util.Enumeration;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034import java.util.zip.GZIPInputStream;
035
036import javax.servlet.http.HttpServletRequest;
037import javax.servlet.http.HttpServletResponse;
038
039import org.apache.commons.io.IOUtils;
040
041import ca.uhn.fhir.context.ConfigurationException;
042import ca.uhn.fhir.rest.api.RequestTypeEnum;
043import ca.uhn.fhir.rest.method.BaseMethodBinding;
044import ca.uhn.fhir.rest.method.BaseMethodBinding.IRequestReader;
045import ca.uhn.fhir.rest.method.RequestDetails;
046import ca.uhn.fhir.rest.server.Constants;
047import ca.uhn.fhir.rest.server.RestfulServer;
048import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
049
050public class ServletRequestDetails extends RequestDetails {
051
052        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestDetails.class);
053        /**
054         * @see BaseMethodBinding#loadRequestContents(RequestDetails)
055         */
056        private static volatile IRequestReader ourRequestReader;
057        private RestfulServer myServer;
058        private HttpServletRequest myServletRequest;
059        private HttpServletResponse myServletResponse;
060        private byte[] requestContents;
061
062        public ServletRequestDetails() {
063                super();
064                setResponse(new ServletRestfulResponse(this));
065        }
066
067        @Override
068        protected byte[] getByteStreamRequestContents() {
069                /*
070                 * This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
071                 * servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
072                 * in servers. Down the road it may make sense to just split the method binding classes into server and client
073                 * versions, but this isn't actually a huge deal I don't think.
074                 */
075                IRequestReader reader = ourRequestReader;
076                if (reader == null) {
077                        try {
078                                Class.forName("javax.servlet.ServletInputStream");
079                                String className = BaseMethodBinding.class.getName() + "$" + "ActiveRequestReader";
080                                try {
081                                        reader = (IRequestReader) Class.forName(className).newInstance();
082                                } catch (Exception e1) {
083                                        throw new ConfigurationException("Failed to instantiate class " + className, e1);
084                                }
085                        } catch (ClassNotFoundException e) {
086                                String className = BaseMethodBinding.class.getName() + "$" + "InactiveRequestReader";
087                                try {
088                                        reader = (IRequestReader) Class.forName(className).newInstance();
089                                } catch (Exception e1) {
090                                        throw new ConfigurationException("Failed to instantiate class " + className, e1);
091                                }
092                        }
093                        ourRequestReader = reader;
094                }
095
096                try {
097                        InputStream inputStream = reader.getInputStream(this);
098                        requestContents = IOUtils.toByteArray(inputStream);
099                        
100                        if (myServer.isUncompressIncomingContents()) {
101                                String contentEncoding = myServletRequest.getHeader(Constants.HEADER_CONTENT_ENCODING);
102                                if ("gzip".equals(contentEncoding)) {
103                                        ourLog.debug("Uncompressing (GZip) incoming content");
104                                        GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(requestContents));
105                                        requestContents = IOUtils.toByteArray(gis);
106                                }
107                        }
108                        
109                        return requestContents;
110                } catch (IOException e) {
111                        ourLog.error("Could not load request resource", e);
112                        throw new InvalidRequestException(String.format("Could not load request resource: %s", e.getMessage()));
113                }
114        }
115
116        @Override
117        public String getHeader(String name) {
118                return getServletRequest().getHeader(name);
119        }
120
121        @Override
122        public List<String> getHeaders(String name) {
123                Enumeration<String> headers = getServletRequest().getHeaders(name);
124                return headers == null ? Collections.<String> emptyList() : Collections.list(getServletRequest().getHeaders(name));
125        }
126
127        @Override
128        public InputStream getInputStream() throws IOException {
129                return getServletRequest().getInputStream();
130        }
131
132        @Override
133        public Reader getReader() throws IOException {
134                return getServletRequest().getReader();
135        }
136
137        @Override
138        public RestfulServer getServer() {
139                return myServer;
140        }
141
142        @Override
143        public String getServerBaseForRequest() {
144                return getServer().getServerBaseForRequest(getServletRequest());
145        }
146
147        public HttpServletRequest getServletRequest() {
148                return myServletRequest;
149        }
150
151        public HttpServletResponse getServletResponse() {
152                return myServletResponse;
153        }
154
155        public void setServer(RestfulServer theServer) {
156                this.myServer = theServer;
157        }
158
159        public void setServletRequest(HttpServletRequest myServletRequest) {
160                this.myServletRequest = myServletRequest;
161        }
162
163        public void setServletResponse(HttpServletResponse myServletResponse) {
164                this.myServletResponse = myServletResponse;
165        }
166
167        public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set<String> theParamNames) {
168                RequestDetails retVal = new ServletRequestDetails();
169                retVal.setResourceName(theResourceName);
170                retVal.setRequestType(theRequestType);
171                Map<String, String[]> paramNames = new HashMap<String, String[]>();
172                for (String next : theParamNames) {
173                        paramNames.put(next, new String[0]);
174                }
175                retVal.setParameters(paramNames);
176                return retVal;
177        }
178
179}