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 }