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