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.http4; 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.http4.helper.CamelFileDataSource; 040 import org.apache.camel.component.http4.helper.GZIPHelper; 041 import org.apache.camel.component.http4.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 $Revision: 1053671 $ 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 if (request.getMethod().equals("POST") && request.getContentType() != null 148 && request.getContentType().startsWith(HttpConstants.CONTENT_TYPE_WWW_FORM_URLENCODED)) { 149 String charset = request.getCharacterEncoding(); 150 if (charset == null) { 151 charset = "UTF-8"; 152 } 153 // Push POST form params into the headers to retain compatibility with DefaultHttpBinding 154 String body = message.getBody(String.class); 155 for (String param : body.split("&")) { 156 String[] pair = param.split("=", 2); 157 String name = URLDecoder.decode(pair[0], charset); 158 String value = URLDecoder.decode(pair[1], charset); 159 if (headerFilterStrategy != null 160 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 161 headers.put(name, value); 162 } 163 } 164 } 165 } 166 167 protected void populateAttachments(HttpServletRequest request, HttpMessage message) { 168 // check if there is multipart files, if so will put it into DataHandler 169 Enumeration names = request.getAttributeNames(); 170 while (names.hasMoreElements()) { 171 String name = (String) names.nextElement(); 172 Object object = request.getAttribute(name); 173 if (object instanceof File) { 174 String fileName = request.getParameter(name); 175 message.addAttachment(fileName, new DataHandler(new CamelFileDataSource((File) object, fileName))); 176 } 177 } 178 } 179 180 public void writeResponse(Exchange exchange, HttpServletResponse response) throws IOException { 181 if (exchange.isFailed()) { 182 if (exchange.getException() != null) { 183 doWriteExceptionResponse(exchange.getException(), response); 184 } else { 185 // it must be a fault, no need to check for the fault flag on the message 186 doWriteFaultResponse(exchange.getOut(), response, exchange); 187 } 188 } else { 189 // just copy the protocol relates header 190 copyProtocolHeaders(exchange.getIn(), exchange.getOut()); 191 Message out = exchange.getOut(); 192 if (out != null) { 193 doWriteResponse(out, response, exchange); 194 } 195 } 196 } 197 198 private void copyProtocolHeaders(Message request, Message response) { 199 if (request.getHeader(Exchange.CONTENT_ENCODING) != null) { 200 String contentEncoding = request.getHeader(Exchange.CONTENT_ENCODING, String.class); 201 response.setHeader(Exchange.CONTENT_ENCODING, contentEncoding); 202 } 203 if (checkChunked(response, response.getExchange())) { 204 response.setHeader(Exchange.TRANSFER_ENCODING, "chunked"); 205 } 206 } 207 208 public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException { 209 // 500 for internal server error 210 response.setStatus(500); 211 if (endpoint != null && endpoint.isTransferException()) { 212 // transfer the exception as a serialized java object 213 HttpHelper.writeObjectToServletResponse(response, exception); 214 } else { 215 // write stacktrace as plain text 216 response.setContentType("text/plain"); 217 PrintWriter pw = response.getWriter(); 218 exception.printStackTrace(pw); 219 pw.flush(); 220 } 221 } 222 223 public void doWriteFaultResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 224 doWriteResponse(message, response, exchange); 225 } 226 227 public void doWriteResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 228 // set the status code in the response. Default is 200. 229 if (message.getHeader(Exchange.HTTP_RESPONSE_CODE) != null) { 230 int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); 231 response.setStatus(code); 232 } 233 // set the content type in the response. 234 String contentType = MessageHelper.getContentType(message); 235 if (MessageHelper.getContentType(message) != null) { 236 response.setContentType(contentType); 237 } 238 239 // append headers 240 for (String key : message.getHeaders().keySet()) { 241 String value = message.getHeader(key, String.class); 242 if (headerFilterStrategy != null 243 && !headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { 244 response.setHeader(key, value); 245 } 246 } 247 248 // write the body. 249 if (message.getBody() != null) { 250 if (GZIPHelper.isGzip(message)) { 251 doWriteGZIPResponse(message, response, exchange); 252 } else { 253 doWriteDirectResponse(message, response, exchange); 254 } 255 } 256 } 257 258 protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 259 // if content type is serialized Java object, then serialize and write it to the response 260 String contentType = message.getHeader(Exchange.CONTENT_TYPE, String.class); 261 if (contentType != null && HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) { 262 try { 263 Object object = message.getMandatoryBody(Serializable.class); 264 HttpHelper.writeObjectToServletResponse(response, object); 265 // object is written so return 266 return; 267 } catch (InvalidPayloadException e) { 268 throw IOHelper.createIOException(e); 269 } 270 } 271 272 // other kind of content type 273 InputStream is = null; 274 if (checkChunked(message, exchange)) { 275 is = message.getBody(InputStream.class); 276 } 277 if (is != null) { 278 ServletOutputStream os = response.getOutputStream(); 279 try { 280 // copy directly from input stream to output stream 281 IOHelper.copy(is, os); 282 } finally { 283 IOHelper.close(os); 284 IOHelper.close(is); 285 } 286 } else { 287 // not convertable as a stream so try as a String 288 String data = message.getBody(String.class); 289 if (data != null) { 290 // set content length before we write data 291 response.setContentLength(data.length()); 292 response.getWriter().print(data); 293 response.getWriter().flush(); 294 } 295 } 296 } 297 298 protected boolean checkChunked(Message message, Exchange exchange) { 299 boolean answer = true; 300 if (message.getHeader(Exchange.HTTP_CHUNKED) == null) { 301 // check the endpoint option 302 Endpoint endpoint = exchange.getFromEndpoint(); 303 if (endpoint instanceof HttpEndpoint) { 304 answer = ((HttpEndpoint) endpoint).isChunked(); 305 } 306 } else { 307 answer = message.getHeader(Exchange.HTTP_CHUNKED, boolean.class); 308 } 309 return answer; 310 } 311 312 protected void doWriteGZIPResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 313 byte[] bytes; 314 try { 315 bytes = message.getMandatoryBody(byte[].class); 316 } catch (InvalidPayloadException e) { 317 throw ObjectHelper.wrapRuntimeCamelException(e); 318 } 319 320 byte[] data = GZIPHelper.compressGZIP(bytes); 321 ServletOutputStream os = response.getOutputStream(); 322 try { 323 response.setContentLength(data.length); 324 os.write(data); 325 os.flush(); 326 } finally { 327 IOHelper.close(os); 328 } 329 } 330 331 public Object parseBody(HttpMessage httpMessage) throws IOException { 332 // lets assume the body is a reader 333 HttpServletRequest request = httpMessage.getRequest(); 334 // Need to handle the GET Method which has no inputStream 335 if ("GET".equals(request.getMethod())) { 336 return null; 337 } 338 if (isUseReaderForPayload()) { 339 // use reader to read the response body 340 return request.getReader(); 341 } else { 342 // reade the response body from servlet request 343 return HttpHelper.readResponseBodyFromServletRequest(request, httpMessage.getExchange()); 344 } 345 } 346 347 public boolean isUseReaderForPayload() { 348 return useReaderForPayload; 349 } 350 351 public void setUseReaderForPayload(boolean useReaderForPayload) { 352 this.useReaderForPayload = useReaderForPayload; 353 } 354 355 public HeaderFilterStrategy getHeaderFilterStrategy() { 356 return headerFilterStrategy; 357 } 358 359 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { 360 this.headerFilterStrategy = headerFilterStrategy; 361 } 362 363 }