001package ca.uhn.fhir.rest.server.servlet;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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 */
022import static org.apache.commons.lang3.StringUtils.isNotBlank;
023
024import java.io.ByteArrayInputStream;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.Reader;
028import java.nio.charset.Charset;
029import java.util.Collections;
030import java.util.Enumeration;
031import java.util.List;
032import java.util.zip.GZIPInputStream;
033
034import javax.servlet.http.HttpServletRequest;
035import javax.servlet.http.HttpServletResponse;
036
037import org.apache.commons.io.IOUtils;
038import org.apache.http.entity.ContentType;
039
040import ca.uhn.fhir.context.ConfigurationException;
041import ca.uhn.fhir.rest.method.BaseMethodBinding;
042import ca.uhn.fhir.rest.method.BaseMethodBinding.IRequestReader;
043import ca.uhn.fhir.rest.method.RequestDetails;
044import ca.uhn.fhir.rest.server.Constants;
045import ca.uhn.fhir.rest.server.RestfulServer;
046import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
047
048public class ServletRequestDetails extends RequestDetails {
049
050        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestDetails.class);
051        /**
052         * @see BaseMethodBinding#loadRequestContents(RequestDetails)
053         */
054        private static volatile IRequestReader ourRequestReader;
055        private RestfulServer myServer;
056        private HttpServletRequest myServletRequest;
057        private HttpServletResponse myServletResponse;
058        private byte[] requestContents;
059
060        public ServletRequestDetails() {
061                super();
062                setResponse(new ServletRestfulResponse(this));
063        }
064
065        @Override
066        protected byte[] getByteStreamRequestContents() {
067                /*
068                 * This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
069                 * servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
070                 * in servers. Down the road it may make sense to just split the method binding classes into server and client
071                 * versions, but this isn't actually a huge deal I don't think.
072                 */
073                IRequestReader reader = ourRequestReader;
074                if (reader == null) {
075                        try {
076                                Class.forName("javax.servlet.ServletInputStream");
077                                String className = BaseMethodBinding.class.getName() + "$" + "ActiveRequestReader";
078                                try {
079                                        reader = (IRequestReader) Class.forName(className).newInstance();
080                                } catch (Exception e1) {
081                                        throw new ConfigurationException("Failed to instantiate class " + className, e1);
082                                }
083                        } catch (ClassNotFoundException e) {
084                                String className = BaseMethodBinding.class.getName() + "$" + "InactiveRequestReader";
085                                try {
086                                        reader = (IRequestReader) Class.forName(className).newInstance();
087                                } catch (Exception e1) {
088                                        throw new ConfigurationException("Failed to instantiate class " + className, e1);
089                                }
090                        }
091                        ourRequestReader = reader;
092                }
093
094                try {
095                        InputStream inputStream = reader.getInputStream(this);
096                        requestContents = IOUtils.toByteArray(inputStream);
097
098                        if (myServer.isUncompressIncomingContents()) {
099                                String contentEncoding = myServletRequest.getHeader(Constants.HEADER_CONTENT_ENCODING);
100                                if ("gzip".equals(contentEncoding)) {
101                                        ourLog.debug("Uncompressing (GZip) incoming content");
102                                        if (requestContents.length > 0) {
103                                                GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(requestContents));
104                                                requestContents = IOUtils.toByteArray(gis);
105                                        }
106                                }
107                        }
108                        // FIXME resource leak
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        @Override
168        public Charset getCharset() {
169                String ct = getHeader(Constants.HEADER_CONTENT_TYPE);
170
171                Charset charset = null;
172                if (isNotBlank(ct)) {
173                        ContentType parsedCt = ContentType.parse(ct);
174                        charset = parsedCt.getCharset();
175                }
176                return charset;
177        }
178
179}