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.FileInputStream; 021 import java.io.FileNotFoundException; 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.PrintWriter; 025 import java.io.UnsupportedEncodingException; 026 import java.net.URLDecoder; 027 import java.util.Enumeration; 028 import java.util.Map; 029 030 import javax.activation.DataHandler; 031 import javax.activation.FileDataSource; 032 import javax.activation.FileTypeMap; 033 import javax.servlet.ServletOutputStream; 034 import javax.servlet.http.HttpServletRequest; 035 import javax.servlet.http.HttpServletResponse; 036 037 import org.apache.camel.Endpoint; 038 import org.apache.camel.Exchange; 039 import org.apache.camel.InvalidPayloadException; 040 import org.apache.camel.Message; 041 import org.apache.camel.StreamCache; 042 import org.apache.camel.component.http.helper.CamelFileDataSource; 043 import org.apache.camel.component.http.helper.GZIPHelper; 044 import org.apache.camel.converter.stream.CachedOutputStream; 045 import org.apache.camel.spi.HeaderFilterStrategy; 046 import org.apache.camel.util.IOHelper; 047 import org.apache.camel.util.MessageHelper; 048 import org.apache.camel.util.ObjectHelper; 049 050 /** 051 * Binding between {@link HttpMessage} and {@link HttpServletResponse}. 052 * 053 * @version $Revision: 949923 $ 054 */ 055 public class DefaultHttpBinding implements HttpBinding { 056 057 private boolean useReaderForPayload; 058 private HeaderFilterStrategy headerFilterStrategy = new HttpHeaderFilterStrategy(); 059 060 public DefaultHttpBinding() { 061 } 062 063 public DefaultHttpBinding(HeaderFilterStrategy headerFilterStrategy) { 064 this.headerFilterStrategy = headerFilterStrategy; 065 } 066 067 public void readRequest(HttpServletRequest request, HttpMessage message) { 068 069 // lets force a parse of the body and headers 070 message.getBody(); 071 // populate the headers from the request 072 Map<String, Object> headers = message.getHeaders(); 073 074 //apply the headerFilterStrategy 075 Enumeration names = request.getHeaderNames(); 076 while (names.hasMoreElements()) { 077 String name = (String)names.nextElement(); 078 Object value = request.getHeader(name); 079 // mapping the content-type 080 if (name.toLowerCase().equals("content-type")) { 081 name = Exchange.CONTENT_TYPE; 082 } 083 if (headerFilterStrategy != null 084 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 085 headers.put(name, value); 086 } 087 } 088 089 if (request.getCharacterEncoding() != null) { 090 headers.put(Exchange.HTTP_CHARACTER_ENCODING, request.getCharacterEncoding()); 091 message.getExchange().setProperty(Exchange.CHARSET_NAME, request.getCharacterEncoding()); 092 } 093 094 popluateRequestParameters(request, message); 095 096 Object body = message.getBody(); 097 // reset the stream cache if the body is the instance of StreamCache 098 if (body instanceof StreamCache) { 099 ((StreamCache)body).reset(); 100 } 101 102 // store the method and query and other info in headers 103 headers.put(Exchange.HTTP_METHOD, request.getMethod()); 104 headers.put(Exchange.HTTP_QUERY, request.getQueryString()); 105 headers.put(Exchange.HTTP_URL, request.getRequestURL()); 106 headers.put(Exchange.HTTP_URI, request.getRequestURI()); 107 headers.put(Exchange.HTTP_PATH, request.getPathInfo()); 108 headers.put(Exchange.CONTENT_TYPE, request.getContentType()); 109 110 popluateAttachments(request, message); 111 } 112 113 protected void popluateRequestParameters(HttpServletRequest request, HttpMessage message) { 114 //we populate the http request parameters without checking the request method 115 Map<String, Object> headers = message.getHeaders(); 116 Enumeration names = request.getParameterNames(); 117 while (names.hasMoreElements()) { 118 String name = (String)names.nextElement(); 119 Object value = request.getParameter(name); 120 if (headerFilterStrategy != null 121 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 122 headers.put(name, value); 123 } 124 } 125 126 if (request.getMethod().equals("POST") && request.getContentType() != null && request.getContentType().equals("application/x-www-form-urlencoded")) { 127 String charset = request.getCharacterEncoding(); 128 if (charset == null) { 129 charset = "UTF-8"; 130 } 131 // Push POST form params into the headers to retain compatibility with DefaultHttpBinding 132 String body = message.getBody(String.class); 133 try { 134 for (String param : body.split("&")) { 135 String[] pair = param.split("=", 2); 136 String name = URLDecoder.decode(pair[0], charset); 137 String value = URLDecoder.decode(pair[1], charset); 138 if (headerFilterStrategy != null 139 && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) { 140 headers.put(name, value); 141 } 142 } 143 } catch (UnsupportedEncodingException e) { 144 throw new RuntimeException(e); 145 } 146 } 147 148 } 149 150 protected void popluateAttachments(HttpServletRequest request, HttpMessage message) { 151 // check if there is multipart files, if so will put it into DataHandler 152 Enumeration names = request.getAttributeNames(); 153 while (names.hasMoreElements()) { 154 String name = (String) names.nextElement(); 155 Object object = request.getAttribute(name); 156 if (object instanceof File) { 157 String fileName = request.getParameter(name); 158 message.addAttachment(fileName, new DataHandler(new CamelFileDataSource((File)object, fileName))); 159 } 160 } 161 } 162 163 public void writeResponse(Exchange exchange, HttpServletResponse response) throws IOException { 164 if (exchange.isFailed()) { 165 if (exchange.getException() != null) { 166 doWriteExceptionResponse(exchange.getException(), response); 167 } else { 168 // it must be a fault, no need to check for the fault flag on the message 169 doWriteFaultResponse(exchange.getOut(), response, exchange); 170 } 171 } else { 172 // just copy the protocol relates header 173 copyProtocolHeaders(exchange.getIn(), exchange.getOut()); 174 Message out = exchange.getOut(); 175 if (out != null) { 176 doWriteResponse(out, response, exchange); 177 } 178 } 179 } 180 181 private void copyProtocolHeaders(Message request, Message response) { 182 if (request.getHeader(Exchange.CONTENT_ENCODING) != null) { 183 String contentEncoding = request.getHeader(Exchange.CONTENT_ENCODING, String.class); 184 response.setHeader(Exchange.CONTENT_ENCODING, contentEncoding); 185 } 186 if (checkChunked(response, response.getExchange())) { 187 response.setHeader(Exchange.TRANSFER_ENCODING, "chunked"); 188 } 189 } 190 191 public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException { 192 response.setStatus(500); // 500 for internal server error 193 response.setContentType("text/plain"); 194 195 // append the stacktrace as response 196 PrintWriter pw = response.getWriter(); 197 exception.printStackTrace(pw); 198 199 pw.flush(); 200 } 201 202 public void doWriteFaultResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 203 doWriteResponse(message, response, exchange); 204 } 205 206 public void doWriteResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 207 // set the status code in the response. Default is 200. 208 if (message.getHeader(Exchange.HTTP_RESPONSE_CODE) != null) { 209 int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); 210 response.setStatus(code); 211 } 212 // set the content type in the response. 213 String contentType = MessageHelper.getContentType(message); 214 if (MessageHelper.getContentType(message) != null) { 215 response.setContentType(contentType); 216 } 217 218 // append headers 219 for (String key : message.getHeaders().keySet()) { 220 String value = message.getHeader(key, String.class); 221 if (headerFilterStrategy != null 222 && !headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { 223 response.setHeader(key, value); 224 } 225 } 226 227 // write the body. 228 if (message.getBody() != null) { 229 if (GZIPHelper.isGzip(message)) { 230 doWriteGZIPResponse(message, response, exchange); 231 } else { 232 doWriteDirectResponse(message, response, exchange); 233 } 234 } 235 } 236 237 protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 238 InputStream is = null; 239 if (checkChunked(message, exchange)) { 240 is = message.getBody(InputStream.class); 241 } 242 if (is != null) { 243 ServletOutputStream os = response.getOutputStream(); 244 try { 245 // copy directly from input stream to output stream 246 IOHelper.copy(is, os); 247 } finally { 248 try { 249 os.close(); 250 } catch (Exception e) { 251 // ignore, maybe client have disconnected or timed out 252 } 253 try { 254 is.close(); 255 } catch (Exception e) { 256 // ignore, maybe client have disconnected or timed out 257 } 258 } 259 } else { 260 // not convertable as a stream so try as a String 261 String data = message.getBody(String.class); 262 if (data != null) { 263 // set content length before we write data 264 response.setContentLength(data.length()); 265 response.getWriter().print(data); 266 response.getWriter().flush(); 267 } 268 } 269 } 270 271 protected boolean checkChunked(Message message, Exchange exchange) { 272 boolean answer = true; 273 if (message.getHeader(Exchange.HTTP_CHUNKED) == null) { 274 // check the endpoint option 275 Endpoint endpoint = exchange.getFromEndpoint(); 276 if (endpoint instanceof HttpEndpoint) { 277 answer = ((HttpEndpoint)endpoint).isChunked(); 278 } 279 } else { 280 answer = message.getHeader(Exchange.HTTP_CHUNKED, boolean.class); 281 } 282 return answer; 283 } 284 285 protected void doWriteGZIPResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { 286 byte[] bytes; 287 try { 288 bytes = message.getMandatoryBody(byte[].class); 289 } catch (InvalidPayloadException e) { 290 throw ObjectHelper.wrapRuntimeCamelException(e); 291 } 292 293 byte[] data = GZIPHelper.compressGZIP(bytes); 294 ServletOutputStream os = response.getOutputStream(); 295 try { 296 response.setContentLength(data.length); 297 os.write(data); 298 os.flush(); 299 } finally { 300 os.close(); 301 } 302 } 303 304 public Object parseBody(HttpMessage httpMessage) throws IOException { 305 // lets assume the body is a reader 306 HttpServletRequest request = httpMessage.getRequest(); 307 // Need to handle the GET Method which has no inputStream 308 if ("GET".equals(request.getMethod())) { 309 return null; 310 } 311 if (isUseReaderForPayload()) { 312 return request.getReader(); 313 } else { 314 // otherwise use input stream and we need to cache it first 315 InputStream is = HttpConverter.toInputStream(request, httpMessage.getExchange()); 316 if (is == null) { 317 return is; 318 } 319 // convert the input stream to StreamCache if the stream cache is not disabled 320 if (httpMessage.getExchange().getProperty(Exchange.DISABLE_HTTP_STREAM_CACHE, Boolean.FALSE, Boolean.class)) { 321 return is; 322 } else { 323 try { 324 CachedOutputStream cos = new CachedOutputStream(httpMessage.getExchange()); 325 IOHelper.copy(is, cos); 326 return cos.getStreamCache(); 327 328 } finally { 329 is.close(); 330 } 331 } 332 333 } 334 } 335 336 public boolean isUseReaderForPayload() { 337 return useReaderForPayload; 338 } 339 340 public void setUseReaderForPayload(boolean useReaderForPayload) { 341 this.useReaderForPayload = useReaderForPayload; 342 } 343 344 public HeaderFilterStrategy getHeaderFilterStrategy() { 345 return headerFilterStrategy; 346 } 347 348 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { 349 this.headerFilterStrategy = headerFilterStrategy; 350 } 351 352 }