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}