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