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}