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 $Revision: 1053667 $
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 IOHelper.createIOException(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 }