001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.http; 018 019 import java.io.File; 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.io.PrintWriter; 023 import java.io.Serializable; 024 import java.io.UnsupportedEncodingException; 025 import java.net.URLDecoder; 026 import java.util.Enumeration; 027 import java.util.Map; 028 import javax.activation.DataHandler; 029 import javax.servlet.ServletOutputStream; 030 import javax.servlet.http.HttpServletRequest; 031 import javax.servlet.http.HttpServletResponse; 032 033 import org.apache.camel.Endpoint; 034 import org.apache.camel.Exchange; 035 import org.apache.camel.InvalidPayloadException; 036 import org.apache.camel.Message; 037 import org.apache.camel.RuntimeCamelException; 038 import org.apache.camel.StreamCache; 039 import org.apache.camel.component.http.helper.CamelFileDataSource; 040 import org.apache.camel.component.http.helper.GZIPHelper; 041 import org.apache.camel.component.http.helper.HttpHelper; 042 import org.apache.camel.spi.HeaderFilterStrategy; 043 import org.apache.camel.util.IOHelper; 044 import org.apache.camel.util.MessageHelper; 045 import org.apache.camel.util.ObjectHelper; 046 047 /** 048 * Binding between {@link HttpMessage} and {@link HttpServletResponse}. 049 * 050 * @version 051 */ 052 public class DefaultHttpBinding implements HttpBinding { 053 054 private boolean useReaderForPayload; 055 private HeaderFilterStrategy headerFilterStrategy = new HttpHeaderFilterStrategy(); 056 private HttpEndpoint endpoint; 057 058 @Deprecated 059 public DefaultHttpBinding() { 060 } 061 062 @Deprecated 063 public DefaultHttpBinding(HeaderFilterStrategy headerFilterStrategy) { 064 this.headerFilterStrategy = headerFilterStrategy; 065 } 066 067 public DefaultHttpBinding(HttpEndpoint endpoint) { 068 this.endpoint = endpoint; 069 this.headerFilterStrategy = endpoint.getHeaderFilterStrategy(); 070 } 071 072 public void readRequest(HttpServletRequest request, HttpMessage message) { 073 074 // lets force a parse of the body and headers 075 message.getBody(); 076 // populate the headers from the request 077 Map<String, Object> headers = message.getHeaders(); 078 079 //apply the headerFilterStrategy 080 Enumeration names = request.getHeaderNames(); 081 while (names.hasMoreElements()) { 082 String name = (String)names.nextElement(); 083 Object value = request.getHeader(name); 084 // mapping the content-type 085 if (name.toLowerCase().equals("content-type")) { 086 name = Exchange.CONTENT_TYPE; 087 } 088 if (headerFilterStrategy != null 089 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 090 headers.put(name, value); 091 } 092 } 093 094 if (request.getCharacterEncoding() != null) { 095 headers.put(Exchange.HTTP_CHARACTER_ENCODING, request.getCharacterEncoding()); 096 message.getExchange().setProperty(Exchange.CHARSET_NAME, request.getCharacterEncoding()); 097 } 098 099 try { 100 populateRequestParameters(request, message); 101 } catch (UnsupportedEncodingException e) { 102 throw new RuntimeCamelException("Cannot read request parameters due " + e.getMessage(), e); 103 } 104 105 Object body = message.getBody(); 106 // reset the stream cache if the body is the instance of StreamCache 107 if (body instanceof StreamCache) { 108 ((StreamCache)body).reset(); 109 } 110 111 // store the method and query and other info in headers 112 headers.put(Exchange.HTTP_METHOD, request.getMethod()); 113 headers.put(Exchange.HTTP_QUERY, request.getQueryString()); 114 headers.put(Exchange.HTTP_URL, request.getRequestURL()); 115 headers.put(Exchange.HTTP_URI, request.getRequestURI()); 116 headers.put(Exchange.HTTP_PATH, request.getPathInfo()); 117 headers.put(Exchange.CONTENT_TYPE, request.getContentType()); 118 119 // if content type is serialized java object, then de-serialize it to a Java object 120 if (request.getContentType() != null && HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(request.getContentType())) { 121 try { 122 InputStream is = endpoint.getCamelContext().getTypeConverter().mandatoryConvertTo(InputStream.class, body); 123 Object object = HttpHelper.deserializeJavaObjectFromStream(is); 124 if (object != null) { 125 message.setBody(object); 126 } 127 } catch (Exception e) { 128 throw new RuntimeCamelException("Cannot deserialize body to Java object", e); 129 } 130 } 131 132 populateAttachments(request, message); 133 } 134 135 protected void populateRequestParameters(HttpServletRequest request, HttpMessage message) throws UnsupportedEncodingException { 136 //we populate the http request parameters without checking the request method 137 Map<String, Object> headers = message.getHeaders(); 138 Enumeration names = request.getParameterNames(); 139 while (names.hasMoreElements()) { 140 String name = (String)names.nextElement(); 141 Object value = request.getParameter(name); 142 if (headerFilterStrategy != null 143 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 144 headers.put(name, value); 145 } 146 } 147 148 if (request.getMethod().equals("POST") && request.getContentType() != null 149 && request.getContentType().startsWith(HttpConstants.CONTENT_TYPE_WWW_FORM_URLENCODED)) { 150 String charset = request.getCharacterEncoding(); 151 if (charset == null) { 152 charset = "UTF-8"; 153 } 154 // Push POST form params into the headers to retain compatibility with DefaultHttpBinding 155 String body = message.getBody(String.class); 156 for (String param : body.split("&")) { 157 String[] pair = param.split("=", 2); 158 String name = URLDecoder.decode(pair[0], charset); 159 String value = URLDecoder.decode(pair[1], charset); 160 if (headerFilterStrategy != null 161 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 162 headers.put(name, value); 163 } 164 } 165 } 166 } 167 168 protected void populateAttachments(HttpServletRequest request, HttpMessage message) { 169 // check if there is multipart files, if so will put it into DataHandler 170 Enumeration names = request.getAttributeNames(); 171 while (names.hasMoreElements()) { 172 String name = (String) names.nextElement(); 173 Object object = request.getAttribute(name); 174 if (object instanceof File) { 175 String fileName = request.getParameter(name); 176 message.addAttachment(fileName, new DataHandler(new CamelFileDataSource((File)object, fileName))); 177 } 178 } 179 } 180 181 public void writeResponse(Exchange exchange, HttpServletResponse response) throws IOException { 182 if (exchange.isFailed()) { 183 if (exchange.getException() != null) { 184 doWriteExceptionResponse(exchange.getException(), response); 185 } else { 186 // it must be a fault, no need to check for the fault flag on the message 187 doWriteFaultResponse(exchange.getOut(), response, exchange); 188 } 189 } else { 190 // just copy the protocol relates header 191 copyProtocolHeaders(exchange.getIn(), exchange.getOut()); 192 Message out = exchange.getOut(); 193 if (out != null) { 194 doWriteResponse(out, response, exchange); 195 } 196 } 197 } 198 199 private void copyProtocolHeaders(Message request, Message response) { 200 if (request.getHeader(Exchange.CONTENT_ENCODING) != null) { 201 String contentEncoding = request.getHeader(Exchange.CONTENT_ENCODING, String.class); 202 response.setHeader(Exchange.CONTENT_ENCODING, contentEncoding); 203 } 204 if (checkChunked(response, response.getExchange())) { 205 response.setHeader(Exchange.TRANSFER_ENCODING, "chunked"); 206 } 207 } 208 209 public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException { 210 // 500 for internal server error 211 response.setStatus(500); 212 213 if (endpoint != null && endpoint.isTransferException()) { 214 // transfer the exception as a serialized java object 215 HttpHelper.writeObjectToServletResponse(response, exception); 216 } else { 217 // write stacktrace as plain text 218 response.setContentType("text/plain"); 219 PrintWriter pw = response.getWriter(); 220 exception.printStackTrace(pw); 221 pw.flush(); 222 } 223 } 224 225 public void doWriteFaultResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 226 doWriteResponse(message, response, exchange); 227 } 228 229 public void doWriteResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 230 // set the status code in the response. Default is 200. 231 if (message.getHeader(Exchange.HTTP_RESPONSE_CODE) != null) { 232 int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); 233 response.setStatus(code); 234 } 235 // set the content type in the response. 236 String contentType = MessageHelper.getContentType(message); 237 if (MessageHelper.getContentType(message) != null) { 238 response.setContentType(contentType); 239 } 240 241 // append headers 242 // must use entrySet to ensure case of keys is preserved 243 for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) { 244 String key = entry.getKey(); 245 Object value = entry.getValue(); 246 if (value != null && headerFilterStrategy != null 247 && !headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { 248 response.setHeader(key, value.toString()); 249 } 250 } 251 252 // write the body. 253 if (message.getBody() != null) { 254 if (GZIPHelper.isGzip(message)) { 255 doWriteGZIPResponse(message, response, exchange); 256 } else { 257 doWriteDirectResponse(message, response, exchange); 258 } 259 } 260 } 261 262 protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 263 // if content type is serialized Java object, then serialize and write it to the response 264 String contentType = message.getHeader(Exchange.CONTENT_TYPE, String.class); 265 if (contentType != null && HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) { 266 try { 267 Object object = message.getMandatoryBody(Serializable.class); 268 HttpHelper.writeObjectToServletResponse(response, object); 269 // object is written so return 270 return; 271 } catch (InvalidPayloadException e) { 272 throw new IOException(e); 273 } 274 } 275 276 // other kind of content type 277 InputStream is = null; 278 if (checkChunked(message, exchange)) { 279 is = message.getBody(InputStream.class); 280 } 281 if (is != null) { 282 ServletOutputStream os = response.getOutputStream(); 283 try { 284 // copy directly from input stream to output stream 285 IOHelper.copy(is, os); 286 } finally { 287 IOHelper.close(os); 288 IOHelper.close(is); 289 } 290 } else { 291 // not convertable as a stream so try as a String 292 String data = message.getBody(String.class); 293 if (data != null) { 294 // set content length before we write data 295 response.setContentLength(data.length()); 296 response.getWriter().print(data); 297 response.getWriter().flush(); 298 } 299 } 300 } 301 302 protected boolean checkChunked(Message message, Exchange exchange) { 303 boolean answer = true; 304 if (message.getHeader(Exchange.HTTP_CHUNKED) == null) { 305 // check the endpoint option 306 Endpoint endpoint = exchange.getFromEndpoint(); 307 if (endpoint instanceof HttpEndpoint) { 308 answer = ((HttpEndpoint)endpoint).isChunked(); 309 } 310 } else { 311 answer = message.getHeader(Exchange.HTTP_CHUNKED, boolean.class); 312 } 313 return answer; 314 } 315 316 protected void doWriteGZIPResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 317 byte[] bytes; 318 try { 319 bytes = message.getMandatoryBody(byte[].class); 320 } catch (InvalidPayloadException e) { 321 throw ObjectHelper.wrapRuntimeCamelException(e); 322 } 323 324 byte[] data = GZIPHelper.compressGZIP(bytes); 325 ServletOutputStream os = response.getOutputStream(); 326 try { 327 response.setContentLength(data.length); 328 os.write(data); 329 os.flush(); 330 } finally { 331 IOHelper.close(os); 332 } 333 } 334 335 public Object parseBody(HttpMessage httpMessage) throws IOException { 336 // lets assume the body is a reader 337 HttpServletRequest request = httpMessage.getRequest(); 338 // Need to handle the GET Method which has no inputStream 339 if ("GET".equals(request.getMethod())) { 340 return null; 341 } 342 if (isUseReaderForPayload()) { 343 // use reader to read the response body 344 return request.getReader(); 345 } else { 346 // reade the response body from servlet request 347 return HttpHelper.readResponseBodyFromServletRequest(request, httpMessage.getExchange()); 348 } 349 } 350 351 public boolean isUseReaderForPayload() { 352 return useReaderForPayload; 353 } 354 355 public void setUseReaderForPayload(boolean useReaderForPayload) { 356 this.useReaderForPayload = useReaderForPayload; 357 } 358 359 public HeaderFilterStrategy getHeaderFilterStrategy() { 360 return headerFilterStrategy; 361 } 362 363 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { 364 this.headerFilterStrategy = headerFilterStrategy; 365 } 366 367 }