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.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 java.net.URISyntaxException;
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.Map;
029    import javax.servlet.ServletResponse;
030    import javax.servlet.http.HttpServletRequest;
031    
032    import org.apache.camel.Exchange;
033    import org.apache.camel.RuntimeExchangeException;
034    import org.apache.camel.component.http.HttpConstants;
035    import org.apache.camel.component.http.HttpConverter;
036    import org.apache.camel.component.http.HttpEndpoint;
037    import org.apache.camel.component.http.HttpMethods;
038    import org.apache.camel.converter.IOConverter;
039    import org.apache.camel.converter.stream.CachedOutputStream;
040    import org.apache.camel.util.IOHelper;
041    import org.apache.camel.util.ObjectHelper;
042    
043    public final class HttpHelper {
044    
045        private HttpHelper() {
046            // Helper class
047        }
048    
049        public static void setCharsetFromContentType(String contentType, Exchange exchange) {
050            if (contentType != null) {
051                // find the charset and set it to the Exchange
052                int index = contentType.indexOf("charset=");
053                if (index > 0) {
054                    String charset = contentType.substring(index + 8);
055                    exchange.setProperty(Exchange.CHARSET_NAME, IOConverter.normalizeCharset(charset));
056                }
057            }
058        }
059        
060        /**
061         * Writes the given object as response body to the servlet response
062         * <p/>
063         * The content type will be set to {@link HttpConstants#CONTENT_TYPE_JAVA_SERIALIZED_OBJECT}
064         *
065         * @param response servlet response
066         * @param target   object to write
067         * @throws IOException is thrown if error writing
068         */
069        public static void writeObjectToServletResponse(ServletResponse response, Object target) throws IOException {
070            response.setContentType(HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT);
071            writeObjectToStream(response.getOutputStream(), target);
072        }
073    
074        /**
075         * Writes the given object as response body to the output stream
076         *
077         * @param stream output stream
078         * @param target   object to write
079         * @throws IOException is thrown if error writing
080         */
081        public static void writeObjectToStream(OutputStream stream, Object target) throws IOException {
082            ObjectOutputStream oos = new ObjectOutputStream(stream);
083            oos.writeObject(target);
084            oos.flush();
085            IOHelper.close(oos);
086        }
087    
088        /**
089         * Deserializes the input stream to a Java object
090         *
091         * @param is input stream for the Java object
092         * @return the java object, or <tt>null</tt> if input stream was <tt>null</tt>
093         * @throws ClassNotFoundException is thrown if class not found
094         * @throws IOException can be thrown
095         */
096        public static Object deserializeJavaObjectFromStream(InputStream is) throws ClassNotFoundException, IOException {
097            if (is == null) {
098                return null;
099            }
100    
101            Object answer = null;
102            ObjectInputStream ois = new ObjectInputStream(is);
103            try {
104                answer = ois.readObject();
105            } finally {
106                IOHelper.close(ois);
107            }
108    
109            return answer;
110        }
111    
112        /**
113         * Reads the response body from the given http servlet request.
114         *
115         * @param request  http servlet request
116         * @param exchange the exchange
117         * @return the response body, can be <tt>null</tt> if no body
118         * @throws IOException is thrown if error reading response body
119         */
120        public static Object readResponseBodyFromServletRequest(HttpServletRequest request, Exchange exchange) throws IOException {
121            InputStream is = HttpConverter.toInputStream(request, exchange);
122            return readResponseBodyFromInputStream(is, exchange);
123        }
124    
125        /**
126         * Reads the response body from the given input stream.
127         *
128         * @param is       the input stream
129         * @param exchange the exchange
130         * @return the response body, can be <tt>null</tt> if no body
131         * @throws IOException is thrown if error reading response body
132         */
133        public static Object readResponseBodyFromInputStream(InputStream is, Exchange exchange) throws IOException {
134            if (is == null) {
135                return null;
136            }
137    
138            // convert the input stream to StreamCache if the stream cache is not disabled
139            if (exchange.getProperty(Exchange.DISABLE_HTTP_STREAM_CACHE, Boolean.FALSE, Boolean.class)) {
140                return is;
141            } else {
142                CachedOutputStream cos = new CachedOutputStream(exchange);
143                IOHelper.copyAndCloseInput(is, cos);
144                return cos.getStreamCache();
145            }
146        }
147    
148        /**
149         * Creates the URL to invoke.
150         *
151         * @param exchange the exchange
152         * @param endpoint the endpoint
153         * @return the URL to invoke
154         */
155        public static String createURL(Exchange exchange, HttpEndpoint endpoint) {
156            String uri = null;
157            if (!(endpoint.isBridgeEndpoint())) {
158                uri = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
159            }
160            if (uri == null) {
161                uri = endpoint.getHttpUri().toASCIIString();
162            }
163    
164            // resolve placeholders in uri
165            try {
166                uri = exchange.getContext().resolvePropertyPlaceholders(uri);
167            } catch (Exception e) {
168                throw new RuntimeExchangeException("Cannot resolve property placeholders with uri: " + uri, exchange, e);
169            }
170    
171            // append HTTP_PATH to HTTP_URI if it is provided in the header
172            String path = exchange.getIn().getHeader(Exchange.HTTP_PATH, String.class);
173            if (path != null) {
174                if (path.startsWith("/")) {
175                    URI baseURI;
176                    String baseURIString = exchange.getIn().getHeader(Exchange.HTTP_BASE_URI, String.class);
177                    try {
178                        if (baseURIString == null) {
179                            if (exchange.getFromEndpoint() != null) {
180                                baseURIString = exchange.getFromEndpoint().getEndpointUri();
181                            } else {
182                                // will set a default one for it
183                                baseURIString = "/";
184                            }
185                        }
186                        baseURI = new URI(baseURIString);
187                        String basePath = baseURI.getRawPath();
188                        if (path.startsWith(basePath)) {
189                            path = path.substring(basePath.length());
190                            if (path.startsWith("/")) {
191                                path = path.substring(1);
192                            }
193                        } else {
194                            throw new RuntimeExchangeException("Cannot analyze the Exchange.HTTP_PATH header, due to: cannot find the right HTTP_BASE_URI", exchange);
195                        }
196                    } catch (Throwable t) {
197                        throw new RuntimeExchangeException("Cannot analyze the Exchange.HTTP_PATH header, due to: " + t.getMessage(), exchange, t);
198                    }
199                }
200                if (path.length() > 0) {
201                    // make sure that there is exactly one "/" between HTTP_URI and
202                    // HTTP_PATH
203                    if (!uri.endsWith("/")) {
204                        uri = uri + "/";
205                    }
206                    uri = uri.concat(path);
207                }
208            }
209            return uri;
210        }
211    
212        /**
213         * Creates the HttpMethod to use to call the remote server, often either its GET or POST.
214         *
215         * @param exchange  the exchange
216         * @return the created method
217         * @throws URISyntaxException 
218         */
219        public static HttpMethods createMethod(Exchange exchange, HttpEndpoint endpoint, boolean hasPayload) throws URISyntaxException {
220            // is a query string provided in the endpoint URI or in a header (header
221            // overrules endpoint)
222            String queryString = exchange.getIn().getHeader(Exchange.HTTP_QUERY, String.class);
223            // We need also check the HTTP_URI header query part
224            String uriString = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
225            // resolve placeholders in uriString
226            try {
227                uriString = exchange.getContext().resolvePropertyPlaceholders(uriString);
228            } catch (Exception e) {
229                throw new RuntimeExchangeException("Cannot resolve property placeholders with uri: " + uriString, exchange, e);
230            }
231            if (uriString != null) {
232                URI uri = new URI(uriString);
233                queryString = uri.getQuery();
234            }
235            if (queryString == null) {
236                queryString = endpoint.getHttpUri().getQuery();
237            }
238            
239    
240            // compute what method to use either GET or POST
241            HttpMethods answer;
242            HttpMethods m = exchange.getIn().getHeader(Exchange.HTTP_METHOD, HttpMethods.class);
243            if (m != null) {
244                // always use what end-user provides in a header
245                answer = m;
246            } else if (queryString != null) {
247                // if a query string is provided then use GET
248                answer = HttpMethods.GET;
249            } else {
250                // fallback to POST if we have payload, otherwise GET
251                answer = hasPayload ? HttpMethods.POST : HttpMethods.GET;
252            }
253    
254            return answer;
255        }
256    
257        /**
258         * Appends the key/value to the headers.
259         * <p/>
260         * This implementation supports keys with multiple values. In such situations the value
261         * will be a {@link java.util.List} that contains the multiple values.
262         *
263         * @param headers  headers
264         * @param key      the key
265         * @param value    the value
266         */
267        @SuppressWarnings("unchecked")
268        public static void appendHeader(Map headers, String key, Object value) {
269            if (headers.containsKey(key)) {
270                Object existing = headers.get(key);
271                List list;
272                if (existing instanceof List) {
273                    list = (List) existing;
274                } else {
275                    list = new ArrayList();
276                    list.add(existing);
277                }
278                list.add(value);
279                value = list;
280            }
281    
282            headers.put(key, value);
283        }
284    
285        /**
286         * Extracts the parameter value.
287         * <p/>
288         * This implementation supports HTTP multi value parameters which
289         * is based on the syntax of <tt>[value1, value2, value3]</tt> by returning
290         * a {@link List} containing the values.
291         * <p/>
292         * If the value is not a HTTP mulit value the value is returned as is.
293         *
294         * @param value the parameter value
295         * @return the extracted parameter value, see more details in javadoc.
296         */
297        @SuppressWarnings("unchecked")
298        public static Object extractHttpParameterValue(String value) {
299            if (value == null || ObjectHelper.isEmpty(value)) {
300                return value;
301            }
302    
303            // trim value before checking for multiple parameters
304            String trimmed = value.trim();
305    
306            if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
307                // remove the [ ] markers
308                trimmed = trimmed.substring(1, trimmed.length() - 1);
309                List list = new ArrayList<String>();
310                String[] values = trimmed.split(",");
311                for (String s : values) {
312                    list.add(s.trim());
313                }
314                return list;
315            }
316    
317            return value;
318        }
319    
320    }