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: 793361 $
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.hasFault()) {
107                    doWriteFaultResponse(exchange.getFault(), response, exchange);
108                } else {
109                    doWriteExceptionResponse(exchange.getException(), response);
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 = message.getBody(InputStream.class);
176            if (is != null) {
177                ServletOutputStream os = response.getOutputStream();
178                try {
179                    // copy directly from input stream to output stream
180                    IOHelper.copy(is, os);
181                } finally {
182                    os.close();
183                    is.close();
184                }
185            } else {
186                // not convertable as a stream so try as a String
187                String data = message.getBody(String.class);
188                if (data != null) {
189                    // set content length before we write data
190                    response.setContentLength(data.length());
191                    response.getWriter().print(data);
192                    response.getWriter().flush();
193                }
194            }
195        }
196    
197        protected void doWriteGZIPResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
198            byte[] bytes;
199            try {
200                bytes = message.getMandatoryBody(byte[].class);
201            } catch (InvalidPayloadException e) {
202                throw ObjectHelper.wrapRuntimeCamelException(e);
203            }
204    
205            byte[] data = GZIPHelper.compressGZIP(bytes);
206            ServletOutputStream os = response.getOutputStream();
207            try {
208                response.setContentLength(data.length);
209                os.write(data);
210                os.flush();
211            } finally {
212                os.close();
213            }
214        }
215    
216        public Object parseBody(HttpMessage httpMessage) throws IOException {
217            // lets assume the body is a reader
218            HttpServletRequest request = httpMessage.getRequest();
219            // Need to handle the GET Method which has no inputStream
220            if ("GET".equals(request.getMethod())) {
221                return null;
222            }
223            if (isUseReaderForPayload()) {
224                return request.getReader();
225            } else {
226                // otherwise use input stream
227                return HttpConverter.toInputStream(request);
228            }
229        }
230    
231        public boolean isUseReaderForPayload() {
232            return useReaderForPayload;
233        }
234    
235        public void setUseReaderForPayload(boolean useReaderForPayload) {
236            this.useReaderForPayload = useReaderForPayload;
237        }
238    
239        public HeaderFilterStrategy getHeaderFilterStrategy() {
240            return headerFilterStrategy;
241        }
242    
243        public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
244            this.headerFilterStrategy = headerFilterStrategy;
245        }
246    
247    }