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