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.IOException;
020    import java.io.InputStream;
021    import java.io.PrintWriter;
022    import java.util.Enumeration;
023    import java.util.Map;
024    import javax.servlet.ServletOutputStream;
025    import javax.servlet.http.HttpServletRequest;
026    import javax.servlet.http.HttpServletResponse;
027    
028    import org.apache.camel.Endpoint;
029    import org.apache.camel.Exchange;
030    import org.apache.camel.InvalidPayloadException;
031    import org.apache.camel.Message;
032    import org.apache.camel.component.http.helper.GZIPHelper;
033    import org.apache.camel.spi.HeaderFilterStrategy;
034    import org.apache.camel.util.IOHelper;
035    import org.apache.camel.util.MessageHelper;
036    import org.apache.camel.util.ObjectHelper;
037    
038    /**
039     * Binding between {@link HttpMessage} and {@link HttpServletResponse}.
040     *
041     * @version $Revision: 903638 $
042     */
043    public class DefaultHttpBinding implements HttpBinding {
044    
045        private boolean useReaderForPayload;
046        private HeaderFilterStrategy headerFilterStrategy = new HttpHeaderFilterStrategy();
047    
048        public DefaultHttpBinding() {
049        }
050    
051        public DefaultHttpBinding(HeaderFilterStrategy headerFilterStrategy) {
052            this.headerFilterStrategy = headerFilterStrategy;
053        }
054    
055        public void readRequest(HttpServletRequest request, HttpMessage message) {
056            // lets parser the parameterMap first to avoid consuming the POST parameters as InputStream
057            Map parameterMap = request.getParameterMap();
058            
059            // lets force a parse of the body and headers
060            message.getBody();
061            // populate the headers from the request
062            Map<String, Object> headers = message.getHeaders();
063            
064            String contentType = "";
065            //apply the headerFilterStrategy
066            Enumeration names = request.getHeaderNames();
067            while (names.hasMoreElements()) {
068                String name = (String)names.nextElement();
069                Object value = request.getHeader(name);
070                // mapping the content-type 
071                if (name.toLowerCase().equals("content-type")) {
072                    name = Exchange.CONTENT_TYPE;
073                    contentType = (String) value;                
074                }
075                if (headerFilterStrategy != null
076                    && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) {
077                    headers.put(name, value);
078                }
079            }
080    
081            //we populate the http request parameters without checking the request method
082            
083            names = request.getParameterNames();
084            while (names.hasMoreElements()) {
085                String name = (String)names.nextElement();
086                Object value = request.getParameter(name);
087                if (headerFilterStrategy != null
088                    && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, message.getExchange())) {
089                    headers.put(name, value);
090                }
091            }
092            
093            // store the method and query and other info in headers
094            headers.put(Exchange.HTTP_METHOD, request.getMethod());
095            headers.put(Exchange.HTTP_QUERY, request.getQueryString());
096            headers.put(Exchange.HTTP_URL, request.getRequestURL());
097            headers.put(Exchange.HTTP_URI, request.getRequestURI());
098            headers.put(Exchange.HTTP_PATH, request.getPathInfo());
099            headers.put(Exchange.CONTENT_TYPE, request.getContentType());
100            headers.put(Exchange.HTTP_CHARACTER_ENCODING, request.getCharacterEncoding());
101        }
102    
103        public void writeResponse(Exchange exchange, HttpServletResponse response) throws IOException {
104            if (exchange.isFailed()) {
105                if (exchange.getException() != null) {
106                    doWriteExceptionResponse(exchange.getException(), response);
107                } else {
108                    // it must be a fault, no need to check for the fault flag on the message
109                    doWriteFaultResponse(exchange.getOut(), response, exchange);
110                }
111            } else {
112                // just copy the protocol relates header
113                copyProtocolHeaders(exchange.getIn(), exchange.getOut());
114                Message out = exchange.getOut();            
115                if (out != null) {
116                    doWriteResponse(out, response, exchange);
117                }
118            }
119        }
120    
121        private void copyProtocolHeaders(Message request, Message response) {
122            if (request.getHeader(Exchange.CONTENT_ENCODING) != null) {
123                String contentEncoding = request.getHeader(Exchange.CONTENT_ENCODING, String.class);
124                response.setHeader(Exchange.CONTENT_ENCODING, contentEncoding);
125            }        
126        }
127    
128        public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException {
129            response.setStatus(500); // 500 for internal server error
130            response.setContentType("text/plain");
131    
132            // append the stacktrace as response
133            PrintWriter pw = response.getWriter();
134            exception.printStackTrace(pw);
135    
136            pw.flush();
137        }
138    
139        public void doWriteFaultResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
140            doWriteResponse(message, response, exchange);
141        }
142    
143        public void doWriteResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
144            // set the status code in the response. Default is 200.
145            if (message.getHeader(Exchange.HTTP_RESPONSE_CODE) != null) {
146                int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
147                response.setStatus(code);
148            }
149            // set the content type in the response.
150            String contentType = MessageHelper.getContentType(message);
151            if (MessageHelper.getContentType(message) != null) {
152                response.setContentType(contentType);
153            }
154    
155            // append headers
156            for (String key : message.getHeaders().keySet()) {
157                String value = message.getHeader(key, String.class);
158                if (headerFilterStrategy != null
159                        && !headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) {
160                    response.setHeader(key, value);
161                }
162            }
163    
164            // write the body.
165            if (message.getBody() != null) {
166                if (GZIPHelper.isGzip(message)) {
167                    doWriteGZIPResponse(message, response, exchange);
168                } else {
169                    doWriteDirectResponse(message, response, exchange);
170                }
171            }
172        }
173    
174        protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
175            InputStream is = null;
176            if (checkChucked(message, exchange)) {
177                is = message.getBody(InputStream.class);
178            }
179            if (is != null) {
180                ServletOutputStream os = response.getOutputStream();
181                try {
182                    // copy directly from input stream to output stream
183                    IOHelper.copy(is, os);
184                } finally {
185                    try {
186                        os.close();
187                    } catch (Exception e) {
188                        // ignore, maybe client have disconnected or timed out
189                    }
190                    try {
191                        is.close();
192                    } catch (Exception e) {
193                        // ignore, maybe client have disconnected or timed out
194                    }
195                }
196            } else {
197                // not convertable as a stream so try as a String
198                String data = message.getBody(String.class);
199                if (data != null) {
200                    // set content length before we write data
201                    response.setContentLength(data.length());
202                    response.getWriter().print(data);
203                    response.getWriter().flush();
204                }
205            }
206        }
207    
208        protected boolean checkChucked(Message message, Exchange exchange) {
209            boolean answer = true;
210            if (message.getHeader(Exchange.HTTP_CHUNKED) == null) {
211                // check the endpoint option
212                Endpoint endpoint = exchange.getFromEndpoint();
213                if (endpoint instanceof HttpEndpoint) {
214                    answer = ((HttpEndpoint)endpoint).isChunked();
215                }
216            } else {
217                answer = message.getHeader(Exchange.HTTP_CHUNKED, boolean.class);
218            }
219            return answer;
220        }
221    
222        protected void doWriteGZIPResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
223            byte[] bytes;
224            try {
225                bytes = message.getMandatoryBody(byte[].class);
226            } catch (InvalidPayloadException e) {
227                throw ObjectHelper.wrapRuntimeCamelException(e);
228            }
229    
230            byte[] data = GZIPHelper.compressGZIP(bytes);
231            ServletOutputStream os = response.getOutputStream();
232            try {
233                response.setContentLength(data.length);
234                os.write(data);
235                os.flush();
236            } finally {
237                os.close();
238            }
239        }
240    
241        public Object parseBody(HttpMessage httpMessage) throws IOException {
242            // lets assume the body is a reader
243            HttpServletRequest request = httpMessage.getRequest();
244            // Need to handle the GET Method which has no inputStream
245            if ("GET".equals(request.getMethod())) {
246                return null;
247            }
248            if (isUseReaderForPayload()) {
249                return request.getReader();
250            } else {
251                // otherwise use input stream
252                return HttpConverter.toInputStream(request);
253            }
254        }
255    
256        public boolean isUseReaderForPayload() {
257            return useReaderForPayload;
258        }
259    
260        public void setUseReaderForPayload(boolean useReaderForPayload) {
261            this.useReaderForPayload = useReaderForPayload;
262        }
263    
264        public HeaderFilterStrategy getHeaderFilterStrategy() {
265            return headerFilterStrategy;
266        }
267    
268        public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
269            this.headerFilterStrategy = headerFilterStrategy;
270        }
271    
272    }