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