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.http4.helper;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.ObjectInputStream;
022    import java.io.ObjectOutputStream;
023    import java.io.OutputStream;
024    import java.net.URI;
025    import javax.servlet.ServletResponse;
026    import javax.servlet.http.HttpServletRequest;
027    
028    import org.apache.camel.Exchange;
029    import org.apache.camel.RuntimeExchangeException;
030    import org.apache.camel.component.http4.HttpConstants;
031    import org.apache.camel.component.http4.HttpConverter;
032    import org.apache.camel.component.http4.HttpEndpoint;
033    import org.apache.camel.component.http4.HttpMethods;
034    import org.apache.camel.converter.IOConverter;
035    import org.apache.camel.converter.stream.CachedOutputStream;
036    import org.apache.camel.util.IOHelper;
037    import org.apache.http.HttpVersion;
038    import org.apache.http.ProtocolException;
039    
040    public final class HttpHelper {
041    
042        private HttpHelper() {
043            // Helper class
044        }
045    
046        public static void setCharsetFromContentType(String contentType, Exchange exchange) {
047            if (contentType != null) {
048                // find the charset and set it to the Exchange
049                int index = contentType.indexOf("charset=");
050                if (index > 0) {
051                    String charset = contentType.substring(index + 8);
052                    exchange.setProperty(Exchange.CHARSET_NAME, IOConverter.normalizeCharset(charset));
053                }
054            }
055        }
056    
057        /**
058         * Writes the given object as response body to the servlet response
059         * <p/>
060         * The content type will be set to {@link HttpConstants#CONTENT_TYPE_JAVA_SERIALIZED_OBJECT}
061         *
062         * @param response servlet response
063         * @param target   object to write
064         * @throws IOException is thrown if error writing
065         */
066        public static void writeObjectToServletResponse(ServletResponse response, Object target) throws IOException {
067            response.setContentType(HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT);
068            writeObjectToStream(response.getOutputStream(), target);
069        }
070    
071        /**
072         * Writes the given object as response body to the output stream
073         *
074         * @param stream output stream
075         * @param target object to write
076         * @throws IOException is thrown if error writing
077         */
078        public static void writeObjectToStream(OutputStream stream, Object target) throws IOException {
079            ObjectOutputStream oos = new ObjectOutputStream(stream);
080            oos.writeObject(target);
081            oos.flush();
082            IOHelper.close(oos);
083        }
084    
085        /**
086         * Deserializes the input stream to a Java object
087         *
088         * @param is input stream for the Java object
089         * @return the java object, or <tt>null</tt> if input stream was <tt>null</tt>
090         * @throws ClassNotFoundException is thrown if class not found
091         * @throws IOException can be thrown
092         */
093        public static Object deserializeJavaObjectFromStream(InputStream is) throws ClassNotFoundException, IOException {
094            if (is == null) {
095                return null;
096            }
097    
098            Object answer = null;
099            ObjectInputStream ois = new ObjectInputStream(is);
100            try {
101                answer = ois.readObject();
102            } finally {
103                IOHelper.close(ois);
104            }
105    
106            return answer;
107        }
108    
109        /**
110         * Reads the response body from the given http servlet request.
111         *
112         * @param request  http servlet request
113         * @param exchange the exchange
114         * @return the response body, can be <tt>null</tt> if no body
115         * @throws IOException is thrown if error reading response body
116         */
117        public static Object readResponseBodyFromServletRequest(HttpServletRequest request, Exchange exchange) throws IOException {
118            InputStream is = HttpConverter.toInputStream(request, exchange);
119            return readResponseBodyFromInputStream(is, exchange);
120        }
121    
122        /**
123         * Reads the response body from the given input stream.
124         *
125         * @param is       the input stream
126         * @param exchange the exchange
127         * @return the response body, can be <tt>null</tt> if no body
128         * @throws IOException is thrown if error reading response body
129         */
130        public static Object readResponseBodyFromInputStream(InputStream is, Exchange exchange) throws IOException {
131            if (is == null) {
132                return null;
133            }
134    
135            // convert the input stream to StreamCache if the stream cache is not disabled
136            if (exchange.getProperty(Exchange.DISABLE_HTTP_STREAM_CACHE, Boolean.FALSE, Boolean.class)) {
137                return is;
138            } else {
139                CachedOutputStream cos = new CachedOutputStream(exchange);
140                IOHelper.copyAndCloseInput(is, cos);
141                return cos.getStreamCache();
142            }
143        }
144    
145        /**
146         * Creates the URL to invoke.
147         *
148         * @param exchange the exchange
149         * @param endpoint the endpoint
150         * @return the URL to invoke
151         */
152        public static String createURL(Exchange exchange, HttpEndpoint endpoint) {
153            String uri = null;
154            if (!(endpoint.isBridgeEndpoint())) {
155                uri = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
156            }
157            if (uri == null) {
158                uri = endpoint.getHttpUri().toASCIIString();
159            }
160    
161            // resolve placeholders in uri
162            try {
163                uri = exchange.getContext().resolvePropertyPlaceholders(uri);
164            } catch (Exception e) {
165                throw new RuntimeExchangeException("Cannot resolve property placeholders with uri: " + uri, exchange, e);
166            }
167    
168            // append HTTP_PATH to HTTP_URI if it is provided in the header
169            String path = exchange.getIn().getHeader(Exchange.HTTP_PATH, String.class);
170            if (path != null) {
171                if (path.startsWith("/")) {
172                    URI baseURI;
173                    String baseURIString = exchange.getIn().getHeader(Exchange.HTTP_BASE_URI, String.class);
174                    try {
175                        if (baseURIString == null) {
176                            if (exchange.getFromEndpoint() != null) {
177                                baseURIString = exchange.getFromEndpoint().getEndpointUri();
178                            } else {
179                                // will set a default one for it
180                                baseURIString = "/";
181                            }
182                        }
183                        baseURI = new URI(baseURIString);
184                        String basePath = baseURI.getRawPath();
185                        if (path.startsWith(basePath)) {
186                            path = path.substring(basePath.length());
187                            if (path.startsWith("/")) {
188                                path = path.substring(1);
189                            }
190                        } else {
191                            throw new RuntimeExchangeException("Cannot analyze the Exchange.HTTP_PATH header, due to: cannot find the right HTTP_BASE_URI", exchange);
192                        }
193                    } catch (Throwable t) {
194                        throw new RuntimeExchangeException("Cannot analyze the Exchange.HTTP_PATH header, due to: " + t.getMessage(), exchange, t);
195                    }
196    
197                }
198                if (path.length() > 0) {
199                    // make sure that there is exactly one "/" between HTTP_URI and
200                    // HTTP_PATH
201                    if (!uri.endsWith("/")) {
202                        uri = uri + "/";
203                    }
204                    uri = uri.concat(path);
205                }
206            }
207            return uri;
208        }
209    
210        /**
211         * Creates the HttpMethod to use to call the remote server, often either its GET or POST.
212         *
213         * @param exchange the exchange
214         * @return the created method
215         */
216        public static HttpMethods createMethod(Exchange exchange, HttpEndpoint endpoint, boolean hasPayload) {
217            // is a query string provided in the endpoint URI or in a header (header
218            // overrules endpoint)
219            String queryString = exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class);
220            if (queryString == null) {
221                queryString = endpoint.getHttpUri().getRawQuery();
222            }
223    
224            // compute what method to use either GET or POST
225            HttpMethods answer;
226            HttpMethods m = exchange.getIn().getHeader(Exchange.HTTP_METHOD, HttpMethods.class);
227            if (m != null) {
228                // always use what end-user provides in a header
229                answer = m;
230            } else if (queryString != null) {
231                // if a query string is provided then use GET
232                answer = HttpMethods.GET;
233            } else {
234                // fallback to POST if we have payload, otherwise GET
235                answer = hasPayload ? HttpMethods.POST : HttpMethods.GET;
236            }
237    
238            return answer;
239        }
240    
241        public static HttpVersion parserHttpVersion(String s) throws ProtocolException {
242            int major;
243            int minor;
244            if (s == null) {
245                throw new IllegalArgumentException("String may not be null");
246            }
247            if (!s.startsWith("HTTP/")) {
248                throw new ProtocolException("Invalid HTTP version string: " + s);
249            }
250            int i1 = "HTTP/".length();
251            int i2 = s.indexOf(".", i1);
252            if (i2 == -1) {
253                throw new ProtocolException("Invalid HTTP version number: " + s);
254            }
255            try {
256                major = Integer.parseInt(s.substring(i1, i2));
257            } catch (NumberFormatException e) {
258                throw new ProtocolException("Invalid HTTP major version number: " + s);
259            }
260            i1 = i2 + 1;
261            i2 = s.length();
262            try {
263                minor = Integer.parseInt(s.substring(i1, i2));
264            } catch (NumberFormatException e) {
265                throw new ProtocolException("Invalid HTTP minor version number: " + s);
266            }
267            return new HttpVersion(major, minor);
268    
269        }
270    }