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.InvalidTypeException;
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.ExchangeHelper;
034    import org.apache.camel.util.IOHelper;
035    import org.apache.camel.util.ObjectHelper;
036    
037    /**
038     * Binding between {@link HttpMessage} and {@link HttpServletResponse}.
039     *
040     * @version $Revision: 955530 $
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                    contentType = (String) value;
072                }
073                if (headerFilterStrategy != null
074                    && !headerFilterStrategy.applyFilterToExternalHeaders(name, value)) {
075                    headers.put(name, value);
076                }
077            }
078    
079            //we populate the http request parameters without checking the request method
080     
081            names = request.getParameterNames();
082            while (names.hasMoreElements()) {
083                String name = (String)names.nextElement();
084                Object value = request.getParameter(name);
085                if (headerFilterStrategy != null
086                    && !headerFilterStrategy.applyFilterToExternalHeaders(name, value)) {
087                    headers.put(name, value);
088                }
089            }
090            
091            // store the method and query and other info in headers
092            headers.put(HttpMethods.HTTP_METHOD, request.getMethod());
093            headers.put(HttpProducer.QUERY, request.getQueryString());
094        }
095    
096        public void writeResponse(HttpExchange exchange, HttpServletResponse response) throws IOException {
097            if (exchange.isFailed()) {
098                Message fault = exchange.getFault(false);
099                if (fault != null) {
100                    doWriteFaultResponse(fault, response);
101                } else {
102                    doWriteExceptionResponse(exchange.getException(), response);
103                }
104            } else {
105                // just copy the protocol relates header
106                copyProtocolHeaders(exchange.getIn(), exchange.getOut());
107                Message out = exchange.getOut();            
108                if (out != null) {
109                    doWriteResponse(out, response);
110                }
111            }
112        }
113    
114        private void copyProtocolHeaders(Message request, Message response) {
115            if (request.getHeader(GZIPHelper.CONTENT_ENCODING) != null) {
116                String contentEncoding = request.getHeader(GZIPHelper.CONTENT_ENCODING, String.class);
117                response.setHeader(GZIPHelper.CONTENT_ENCODING, contentEncoding);
118            }
119            if (checkChunked(response)) {
120                response.setHeader(Exchange.TRANSFER_ENCODING, "chunked");
121            }
122        }
123        
124        protected boolean checkChunked(Message message) {
125            // set the default value to be true
126            boolean answer = true;
127            if (message.getHeader(Exchange.HTTP_CHUNKED) != null) {
128                
129                answer = message.getHeader(Exchange.HTTP_CHUNKED, boolean.class);
130            }
131            return answer;
132        }
133    
134        public void doWriteExceptionResponse(Throwable exception, HttpServletResponse response) throws IOException {
135            response.setStatus(500); // 500 for internal server error
136            response.setContentType("text/plain");
137    
138            // append the stacktrace as response
139            PrintWriter pw = response.getWriter();
140            exception.printStackTrace(pw);
141    
142            pw.flush();
143        }
144    
145        public void doWriteFaultResponse(Message message, HttpServletResponse response) throws IOException {
146            doWriteResponse(message, response);
147        }
148    
149        public void doWriteResponse(Message message, HttpServletResponse response) throws IOException {
150            // set the status code in the response. Default is 200.
151            if (message.getHeader(HttpProducer.HTTP_RESPONSE_CODE) != null) {
152                int code = message.getHeader(HttpProducer.HTTP_RESPONSE_CODE, Integer.class);
153                response.setStatus(code);
154            }
155            // set the content type in the response.
156            if (message.getHeader("Content-Type") != null) {            
157                String contentType = message.getHeader("Content-Type", String.class);            
158                response.setContentType(contentType);
159            }
160    
161            // append headers
162            for (String key : message.getHeaders().keySet()) {
163                String value = message.getHeader(key, String.class);
164                if (headerFilterStrategy != null
165                        && !headerFilterStrategy.applyFilterToCamelHeaders(key, value)) {
166                    response.setHeader(key, value);
167                }
168            }
169    
170            if (message.getBody() != null) {
171                if (GZIPHelper.isGzip(message)) {
172                    doWriteGZIPResponse(message, response);
173                } else {
174                    doWriteDirectResponse(message, response);
175                }
176            }
177        }
178    
179        protected void doWriteDirectResponse(Message message, HttpServletResponse response) throws IOException {
180            InputStream is = null;
181            if (checkChunked(message)) {
182                is = message.getBody(InputStream.class);
183            }
184            if (is != null) {
185                ServletOutputStream os = response.getOutputStream();
186                try {
187                    // copy directly from input stream to output stream
188                    IOHelper.copy(is, os);
189                } finally {
190                    os.close();
191                    is.close();
192                }
193            } else {
194                // not convertable as a stream so try as a String
195                String data = message.getBody(String.class);
196                if (data != null) {
197                    // set content length before we write data
198                    response.setContentLength(data.length());
199                    response.getWriter().print(data);
200                    response.getWriter().flush();
201                }
202            }
203        }
204    
205        protected void doWriteGZIPResponse(Message message, HttpServletResponse response) throws IOException {
206            byte[] bytes;
207            try {
208                bytes = ExchangeHelper.convertToMandatoryType(message.getExchange(), byte[].class, message.getBody());
209            } catch (InvalidTypeException e) {
210                throw ObjectHelper.wrapRuntimeCamelException(e);
211            }
212    
213            byte[] data = GZIPHelper.compressGZIP(bytes);
214            ServletOutputStream os = response.getOutputStream();
215            try {
216                response.setContentLength(data.length);
217                os.write(data);
218                os.flush();
219            } finally {
220                os.close();
221            }
222        }
223    
224        public Object parseBody(HttpMessage httpMessage) throws IOException {
225            // lets assume the body is a reader
226            HttpServletRequest request = httpMessage.getRequest();
227            // Need to handle the GET Method which has no inputStream
228            if ("GET".equals(request.getMethod())) {
229                return null;
230            }
231            if (isUseReaderForPayload()) {
232                return request.getReader();
233            } else {
234                // otherwise use input stream
235                return HttpConverter.toInputStream(request);
236            }
237        }
238    
239        public boolean isUseReaderForPayload() {
240            return useReaderForPayload;
241        }
242    
243        public void setUseReaderForPayload(boolean useReaderForPayload) {
244            this.useReaderForPayload = useReaderForPayload;
245        }
246    
247        public HeaderFilterStrategy getHeaderFilterStrategy() {
248            return headerFilterStrategy;
249        }
250    
251        public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
252            this.headerFilterStrategy = headerFilterStrategy;
253        }
254    }