org.apache.camel.component.http.HttpProducer Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.http;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.camel.CamelExchangeException;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.Message;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.TypeConverter;
import org.apache.camel.component.file.GenericFile;
import org.apache.camel.component.http.helper.HttpMethodHelper;
import org.apache.camel.converter.stream.CachedOutputStream;
import org.apache.camel.http.base.HttpOperationFailedException;
import org.apache.camel.http.base.cookie.CookieHandler;
import org.apache.camel.http.common.HttpHelper;
import org.apache.camel.http.common.HttpProtocolHeaderFilterStrategy;
import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.support.ExchangeHelper;
import org.apache.camel.support.GZIPHelper;
import org.apache.camel.support.MessageHelper;
import org.apache.camel.support.ObjectHelper;
import org.apache.camel.support.SynchronizationAdapter;
import org.apache.camel.support.http.HttpUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.URISupport;
import org.apache.camel.util.UnsafeUriCharactersEncoder;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.utils.URIUtils;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.FileEntity;
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hc.core5.http.io.entity.NullEntity;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpProducer extends DefaultProducer {
private static final Logger LOG = LoggerFactory.getLogger(HttpProducer.class);
private static final Integer OK_RESPONSE_CODE = 200;
private static final int BUFFER_SIZE = 1024 * 2;
private HttpClient httpClient;
private final HttpContext httpContext;
private final boolean throwException;
private final boolean transferException;
private final HeaderFilterStrategy httpProtocolHeaderFilterStrategy = new HttpProtocolHeaderFilterStrategy();
private int minOkRange;
private int maxOkRange;
private String defaultUrl;
private URI defaultUri;
private HttpHost defaultHttpHost;
public HttpProducer(HttpEndpoint endpoint) {
super(endpoint);
this.httpClient = endpoint.getHttpClient();
this.httpContext = endpoint.getHttpContext();
this.throwException = endpoint.isThrowExceptionOnFailure();
this.transferException = endpoint.isTransferException();
}
@Override
protected void doInit() throws Exception {
super.doInit();
String range = getEndpoint().getOkStatusCodeRange();
parseStatusRange(range);
// optimize and build default url when there are no override headers
String url = getEndpoint().getHttpUri().toASCIIString();
url = UnsafeUriCharactersEncoder.encodeHttpURI(url);
URI uri = new URI(url);
String queryString = getEndpoint().getHttpUri().getRawQuery();
if (queryString == null) {
queryString = uri.getRawQuery();
}
if (queryString != null) {
queryString = UnsafeUriCharactersEncoder.encodeHttpURI(queryString);
uri = URISupport.createURIWithQuery(uri, queryString);
}
defaultUri = uri;
defaultUrl = uri.toASCIIString();
defaultHttpHost = URIUtils.extractHost(uri);
}
private void parseStatusRange(String range) {
if (!range.contains(",")) {
if (!HttpUtil.parseStatusRange(range, this::setRanges)) {
minOkRange = Integer.parseInt(range);
maxOkRange = minOkRange;
}
}
}
private void setRanges(int minOkRange, int maxOkRange) {
this.minOkRange = minOkRange;
this.maxOkRange = maxOkRange;
}
@Override
public void process(Exchange exchange) throws Exception {
boolean cookies = !getEndpoint().getComponent().isCookieManagementDisabled();
if (cookies && getEndpoint().isClearExpiredCookies() && !getEndpoint().isBridgeEndpoint()) {
// create the cookies before the invocation
getEndpoint().getCookieStore().clearExpired(new Date());
}
// if we bridge endpoint then we need to skip matching headers with the HTTP_QUERY to avoid sending
// duplicated headers to the receiver, so use this skipRequestHeaders as the list of headers to skip
Map skipRequestHeaders = null;
if (getEndpoint().isBridgeEndpoint()) {
exchange.setProperty(Exchange.SKIP_GZIP_ENCODING, Boolean.TRUE);
String queryString = exchange.getIn().getHeader(HttpConstants.HTTP_QUERY, String.class);
if (queryString != null) {
skipRequestHeaders = URISupport.parseQuery(queryString, false, true);
}
}
HttpUriRequest httpRequest = createMethod(exchange);
HttpHost httpHost = createHost(httpRequest);
Message in = exchange.getIn();
String httpProtocolVersion = in.getHeader(HttpConstants.HTTP_PROTOCOL_VERSION, String.class);
if (httpProtocolVersion != null) {
// set the HTTP protocol version
int[] version = HttpHelper.parserHttpVersion(httpProtocolVersion);
httpRequest.setVersion(new HttpVersion(version[0], version[1]));
}
HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy();
if (!getEndpoint().isSkipRequestHeaders()) {
// propagate headers as HTTP headers
if (strategy != null) {
final TypeConverter tc = exchange.getContext().getTypeConverter();
for (Map.Entry entry : in.getHeaders().entrySet()) {
String key = entry.getKey();
// we should not add headers for the parameters in the uri if we bridge the endpoint
// as then we would duplicate headers on both the endpoint uri, and in HTTP headers as well
if (skipRequestHeaders != null && skipRequestHeaders.containsKey(key)) {
continue;
}
Object headerValue = entry.getValue();
if (headerValue != null) {
if (headerValue instanceof String || headerValue instanceof Integer || headerValue instanceof Long
|| headerValue instanceof Boolean || headerValue instanceof Date) {
// optimise for common types
String value = headerValue.toString();
if (!strategy.applyFilterToCamelHeaders(key, value, exchange)) {
httpRequest.addHeader(key, value);
}
continue;
}
// use an iterator as there can be multiple values. (must not use a delimiter, and allow empty values)
final Iterator> it = ObjectHelper.createIterator(headerValue, null, true);
HttpUtil.applyHeader(strategy, exchange, it, tc, key,
(multiValues, prev) -> applyHeader(httpRequest, key, multiValues, prev));
}
}
}
}
if (getEndpoint().getCookieHandler() != null) {
Map> cookieHeaders
= getEndpoint().getCookieHandler().loadCookies(exchange, httpRequest.getUri());
for (Map.Entry> entry : cookieHeaders.entrySet()) {
String key = entry.getKey();
if (!entry.getValue().isEmpty()) {
// join multi-values separated by semi-colon
httpRequest.addHeader(key, String.join(";", entry.getValue()));
}
}
}
if (getEndpoint().getCustomHostHeader() != null) {
httpRequest.setHeader(HttpConstants.HTTP_HEADER_HOST, getEndpoint().getCustomHostHeader());
}
//In reverse proxy applications it can be desirable for the downstream service to see the original Host header
//if this option is set, and the exchange Host header is not null, we will set it's current value on the httpRequest
if (getEndpoint().isPreserveHostHeader()) {
String hostHeader = exchange.getIn().getHeader(HttpConstants.HTTP_HEADER_HOST, String.class);
if (hostHeader != null) {
//HttpClient 4 will check to see if the Host header is present, and use it if it is, see org.apache.http.protocol.RequestTargetHost in httpcore
httpRequest.setHeader(HttpConstants.HTTP_HEADER_HOST, hostHeader);
}
}
if (getEndpoint().isConnectionClose()) {
httpRequest.addHeader("Connection", HeaderElements.CLOSE);
}
// lets store the result in the output message.
try {
executeMethod(
httpHost, httpRequest,
httpResponse -> {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("Executing http {} method: {}", httpRequest.getMethod(), httpRequest.getUri());
}
int responseCode = httpResponse.getCode();
if (LOG.isDebugEnabled()) {
LOG.debug("Http responseCode: {}", responseCode);
}
if (!throwException) {
// if we do not use failed exception then populate response for all response codes
HttpProducer.this.populateResponse(exchange, httpRequest, httpResponse, strategy, responseCode);
} else {
boolean ok;
if (minOkRange > 0) {
ok = responseCode >= minOkRange && responseCode <= maxOkRange;
} else {
ok = HttpHelper.isStatusCodeOk(responseCode,
HttpProducer.this.getEndpoint().getOkStatusCodeRange());
}
if (ok) {
// only populate response for OK response
HttpProducer.this.populateResponse(exchange, httpRequest, httpResponse, strategy,
responseCode);
} else {
// operation failed so populate exception to throw
throw HttpProducer.this.populateHttpOperationFailedException(exchange, httpRequest,
httpResponse, responseCode);
}
}
} catch (IOException | HttpException | RuntimeCamelException e) {
throw e;
} catch (Exception e) {
throw new RuntimeCamelException(e);
} finally {
if (httpResponse != null && HttpProducer.this.getEndpoint().isDisableStreamCache()) {
// close the stream at the end of the exchange to ensure it gets eventually closed later
exchange.getExchangeExtension().addOnCompletion(new SynchronizationAdapter() {
@Override
public void onDone(Exchange exchange1) {
try {
EntityUtils.consume(httpResponse.getEntity());
} catch (Exception e) {
// ignore
} finally {
try {
EntityUtils.consume(httpRequest.getEntity());
} catch (Exception e) {
// ignore
}
}
}
});
} else if (httpResponse != null) {
// close the stream now
try {
EntityUtils.consume(httpResponse.getEntity());
} catch (Exception e) {
// ignore
} finally {
try {
EntityUtils.consume(httpRequest.getEntity());
} catch (Exception e) {
// ignore
}
}
}
}
return null;
});
} catch (RuntimeCamelException e) {
if (e.getCause() instanceof Exception ex) {
// Rethrow the embedded exception to simulate the same behavior as with version 4
throw ex;
}
throw e;
}
}
private static void applyHeader(HttpUriRequest httpRequest, String key, List multiValues, String prev) {
// add the value(s) as a http request header
if (multiValues != null) {
// use the default toString of a ArrayList to create in the form [xxx, yyy]
// if multi valued, for a single value, then just output the value as is
String s = multiValues.size() > 1 ? multiValues.toString() : multiValues.get(0);
httpRequest.addHeader(key, s);
} else if (prev != null) {
httpRequest.addHeader(key, prev);
}
}
@Override
public HttpEndpoint getEndpoint() {
return (HttpEndpoint) super.getEndpoint();
}
protected void populateResponse(
Exchange exchange, HttpUriRequest httpRequest, ClassicHttpResponse httpResponse,
HeaderFilterStrategy strategy, int responseCode)
throws IOException, ClassNotFoundException {
// We just make the out message is not create when extractResponseBody throws exception
Object response = extractResponseBody(httpResponse, exchange, getEndpoint().isIgnoreResponseBody());
final Message answer = createResponseMessage(exchange, httpResponse, responseCode);
answer.setBody(response);
if (!getEndpoint().isSkipResponseHeaders()) {
// propagate HTTP response headers
Map> cookieHeaders = null;
CookieHandler cookieHandler = getEndpoint().getCookieHandler();
if (cookieHandler != null) {
cookieHeaders = new HashMap<>();
}
// optimize to walk headers with an iterator which does not create a new array as getAllHeaders does
boolean found = false;
Iterator it = httpResponse.headerIterator();
while (it.hasNext()) {
Header header = it.next();
String name = header.getName();
String value = header.getValue();
if (cookieHeaders != null) {
cookieHeaders.computeIfAbsent(name, k -> new ArrayList<>()).add(value);
}
if (!found && name.equalsIgnoreCase("content-type")) {
name = Exchange.CONTENT_TYPE;
exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, IOHelper.getCharsetNameFromContentType(value));
found = true;
}
// use http helper to extract parameter value as it may contain multiple values
Object extracted = HttpHelper.extractHttpParameterValue(value);
if (strategy != null && !strategy.applyFilterToExternalHeaders(name, extracted, exchange)) {
HttpHelper.appendHeader(answer.getHeaders(), name, extracted);
}
}
// handle cookies
if (cookieHandler != null) {
try {
cookieHandler.storeCookies(exchange, httpRequest.getUri(), cookieHeaders);
} catch (URISyntaxException e) {
throw new RuntimeCamelException(e);
}
}
}
// endpoint might be configured to copy headers from in to out
// to avoid overriding existing headers with old values just
// filter the http protocol headers
if (getEndpoint().isCopyHeaders()) {
MessageHelper.copyHeaders(exchange.getIn(), answer, httpProtocolHeaderFilterStrategy, false);
}
}
private static Message createResponseMessage(Exchange exchange, ClassicHttpResponse httpResponse, int responseCode) {
Message answer = exchange.getOut();
// optimize for 200 response code as the boxing is outside the cached integers
if (responseCode == 200) {
answer.setHeader(HttpConstants.HTTP_RESPONSE_CODE, OK_RESPONSE_CODE);
} else {
answer.setHeader(HttpConstants.HTTP_RESPONSE_CODE, responseCode);
}
if (httpResponse.getReasonPhrase() != null) {
answer.setHeader(HttpConstants.HTTP_RESPONSE_TEXT, httpResponse.getReasonPhrase());
}
return answer;
}
protected Exception populateHttpOperationFailedException(
Exchange exchange, HttpUriRequest httpRequest, ClassicHttpResponse httpResponse, int responseCode)
throws IOException, ClassNotFoundException {
Exception answer;
String statusText = httpResponse.getReasonPhrase() != null ? httpResponse.getReasonPhrase() : null;
Map headers = extractResponseHeaders(httpResponse.getHeaders());
// handle cookies
CookieHandler cookieHandler = getEndpoint().getCookieHandler();
if (cookieHandler != null) {
Map> m = new HashMap<>();
for (Entry e : headers.entrySet()) {
m.put(e.getKey(), Collections.singletonList(e.getValue()));
}
try {
cookieHandler.storeCookies(exchange, httpRequest.getUri(), m);
} catch (URISyntaxException e) {
throw new RuntimeCamelException(e);
}
}
Object responseBody = extractResponseBody(httpResponse, exchange, getEndpoint().isIgnoreResponseBody());
if (transferException && responseBody instanceof Exception ex) {
// if the response was a serialized exception then use that
return ex;
}
// make a defensive copy of the response body in the exception so its detached from the cache
String copy = null;
if (responseBody != null) {
copy = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, responseBody);
}
Header locationHeader = httpResponse.getFirstHeader("location");
try {
String uri = httpRequest.getUri().toString();
if (locationHeader != null && responseCode >= 300 && responseCode < 400) {
answer = new HttpOperationFailedException(
uri, responseCode, statusText, locationHeader.getValue(), headers, copy);
} else {
answer = new HttpOperationFailedException(uri, responseCode, statusText, null, headers, copy);
}
} catch (URISyntaxException e) {
throw new RuntimeCamelException(e);
}
return answer;
}
/**
* Strategy when executing the method (calling the remote server).
*
* @param httpHost the http host to call
* @param httpRequest the http request to execute
* @param handler the response handler
* @return the response
* @throws IOException can be thrown
*/
protected T executeMethod(HttpHost httpHost, HttpUriRequest httpRequest, HttpClientResponseHandler handler)
throws IOException {
HttpContext localContext = HttpClientContext.create();
if (httpContext != null) {
localContext = new BasicHttpContext(httpContext);
}
return httpClient.execute(httpHost, httpRequest, localContext, handler);
}
/**
* Extracts the response headers
*
* @param responseHeaders the headers
* @return the extracted headers or an empty map if no headers existed
*/
protected static Map extractResponseHeaders(Header[] responseHeaders) {
if (responseHeaders == null || responseHeaders.length == 0) {
return Map.of();
}
Map answer = new HashMap<>();
for (Header header : responseHeaders) {
answer.put(header.getName(), header.getValue());
}
return answer;
}
/**
* Extracts the response from the method as a InputStream.
*/
protected Object extractResponseBody(
ClassicHttpResponse httpResponse, Exchange exchange, boolean ignoreResponseBody)
throws IOException, ClassNotFoundException {
HttpEntity entity = httpResponse.getEntity();
if (entity == null) {
return null;
}
InputStream is = entity.getContent();
if (is == null) {
return null;
}
Header header = httpResponse.getFirstHeader(HttpConstants.CONTENT_ENCODING);
String contentEncoding = header != null ? header.getValue() : null;
final boolean gzipEncoding = exchange.getProperty(Exchange.SKIP_GZIP_ENCODING, Boolean.FALSE, Boolean.class);
if (!gzipEncoding) {
is = GZIPHelper.uncompressGzip(contentEncoding, is);
}
// Honor the character encoding
String contentType = null;
header = httpResponse.getFirstHeader("content-type");
if (header != null) {
contentType = header.getValue();
// find the charset and set it to the Exchange
HttpHelper.setCharsetFromContentType(contentType, exchange);
}
if (ignoreResponseBody) {
// ignore response
return null;
}
// if content type is a serialized java object then de-serialize it back to a Java object
if (contentType != null && contentType.equals(HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT)) {
// only deserialize java if allowed
if (getEndpoint().getComponent().isAllowJavaSerializedObject() || getEndpoint().isTransferException()) {
return HttpHelper.deserializeJavaObjectFromStream(is, exchange.getContext());
} else {
// empty response
return null;
}
} else {
if (entity.isStreaming()) {
if (getEndpoint().isDisableStreamCache()) {
// write to in-memory buffer
ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE);
entity.writeTo(bos);
return bos.toByteArray();
} else {
int max = getEndpoint().getComponent().getResponsePayloadStreamingThreshold();
if (max > 0) {
// optimize when we have content-length for small sizes to avoid creating streaming objects
long len = entity.getContentLength();
if (len > 0 && len <= max) {
int i = (int) len;
byte[] arr = new byte[i];
int read;
int offset = 0;
int remain = i;
while ((read = is.read(arr, offset, remain)) > 0 && remain > 0) {
offset += read;
remain -= read;
}
IOHelper.close(is);
return arr;
}
}
// else for bigger payloads then wrap the response in a stream cache so its re-readable
return doExtractResponseBodyAsStream(is, exchange);
}
} else {
// use the response as-is
return is;
}
}
}
private InputStream doExtractResponseBodyAsStream(InputStream is, Exchange exchange) throws IOException {
// As httpclient is using a AutoCloseInputStream, it will be closed when the connection is closed
// we need to cache the stream for it.
CachedOutputStream cos = null;
try {
// This CachedOutputStream will not be closed when the exchange is onCompletion
cos = new CachedOutputStream(exchange, false);
IOHelper.copy(is, cos);
// When the InputStream is closed, the CachedOutputStream will be closed
return cos.getWrappedInputStream();
} catch (IOException ex) {
// try to close the CachedOutputStream when we get the IOException
try {
cos.close();
} catch (IOException ignore) {
//do nothing here
}
throw ex;
} finally {
IOHelper.close(is, "Extracting response body", LOG);
}
}
/**
* Creates the HttpHost to use to call the remote server
*/
protected HttpHost createHost(HttpUriRequest httpRequest) {
try {
URI uri = httpRequest.getUri();
if (uri.equals(defaultUri)) {
return defaultHttpHost;
}
return HttpHost.create(uri);
} catch (URISyntaxException e) {
throw new RuntimeCamelException(e);
}
}
/**
* Creates the HttpMethod to use to call the remote server, either its GET or POST.
*
* @param exchange the exchange
* @return the created method as either GET or POST
* @throws URISyntaxException is thrown if the URI is invalid
* @throws Exception is thrown if error creating RequestEntity
*/
protected HttpUriRequest createMethod(Exchange exchange) throws Exception {
if (defaultUri == null || defaultUrl == null) {
throw new IllegalArgumentException("Producer must be started");
}
String url = defaultUrl;
URI uri = defaultUri;
// the exchange can have some headers that override the default url and forces to create
// a new url that is dynamic based on header values
// these checks are checks that is done in HttpHelper.createURL and HttpHelper.createURI methods
final boolean create = isCreateNewURL(exchange);
if (create) {
// creating the url to use takes 2-steps
url = HttpHelper.createURL(exchange, getEndpoint());
uri = HttpHelper.createURI(exchange, url, getEndpoint());
// get the url from the uri
url = uri.toASCIIString();
}
// create http holder objects for the request
HttpMethods methodToUse = HttpMethodHelper.createMethod(exchange, getEndpoint());
HttpUriRequest method = methodToUse.createMethod(uri);
// special for HTTP DELETE/GET if the message body should be included
if (getEndpoint().isDeleteWithBody() && "DELETE".equals(method.getMethod())
|| getEndpoint().isGetWithBody() && "GET".equals(method.getMethod())) {
method.setEntity(createRequestEntity(exchange));
}
LOG.trace("Using URL: {} with method: {}", url, method);
if (methodToUse.isEntityEnclosing()) {
// only create entity for http payload if the HTTP method carries payload (such as POST)
HttpEntity requestEntity = createRequestEntity(exchange);
method.setEntity(requestEntity);
if (requestEntity != null && requestEntity.getContentType() == null) {
LOG.debug("No Content-Type provided for URL: {} with exchange: {}", url, exchange);
}
}
// there must be a host on the method
if (uri.getScheme() == null || uri.getHost() == null) {
throw new IllegalArgumentException(
"Invalid url: " + url + ". If you are forwarding/bridging http endpoints, then enable the bridgeEndpoint option on the endpoint: "
+ getEndpoint());
}
return method;
}
private boolean isCreateNewURL(Exchange exchange) {
boolean create = false;
Message in = exchange.getIn();
if (in.getHeader(HttpConstants.REST_HTTP_URI) != null) {
create = true;
} else if (in.getHeader(HttpConstants.HTTP_URI) != null && !getEndpoint().isBridgeEndpoint()) {
create = true;
} else if (in.getHeader(HttpConstants.HTTP_PATH) != null) {
create = true;
} else if (in.getHeader(HttpConstants.REST_HTTP_QUERY) != null) {
create = true;
} else if (in.getHeader(HttpConstants.HTTP_RAW_QUERY) != null) {
create = true;
} else if (in.getHeader(HttpConstants.HTTP_QUERY) != null) {
create = true;
}
return create;
}
/**
* Creates a holder object for the data to send to the remote server.
*
* @param exchange the exchange with the IN message with data to send
* @return the data holder
* @throws CamelExchangeException is thrown if error creating RequestEntity
*/
protected HttpEntity createRequestEntity(Exchange exchange) throws CamelExchangeException {
HttpEntity answer = null;
Message in = exchange.getIn();
Object body = in.getBody();
try {
if (body == null) {
return NullEntity.INSTANCE;
} else if (body instanceof HttpEntity entity) {
answer = entity;
// special optimized for using these 3 type converters for common message payload types
} else if (body instanceof byte[] bytes) {
answer = HttpEntityConverter.toHttpEntity(bytes, exchange);
} else if (body instanceof InputStream is) {
answer = HttpEntityConverter.toHttpEntity(is, exchange);
} else if (body instanceof String content) {
answer = HttpEntityConverter.toHttpEntity(content, exchange);
}
} catch (Exception e) {
throw new CamelExchangeException("Error creating RequestEntity from message body", exchange, e);
}
if (answer == null) {
try {
Object data = in.getBody();
if (data != null) {
String contentTypeString = ExchangeHelper.getContentType(exchange);
ContentType contentType = null;
//Check the contentType is valid or not, If not it throws an exception.
//When ContentType.parse parse method parse "multipart/form-data;boundary=---------------------------j2radvtrk",
//it removes "boundary" from Content-Type; I have to use contentType.create method.
if (contentTypeString != null) {
// using ContentType.parser for charset
if (contentTypeString.contains("charset") || contentTypeString.contains(";")) {
contentType = ContentType.parse(contentTypeString);
} else {
contentType = ContentType.create(contentTypeString);
}
}
if (contentTypeString != null
&& HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentTypeString)) {
if (!getEndpoint().getComponent().isAllowJavaSerializedObject()) {
throw new CamelExchangeException(
"Content-type " + HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT
+ " is not allowed",
exchange);
}
// serialized java object
Serializable obj = in.getMandatoryBody(Serializable.class);
// write object to output stream
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
HttpHelper.writeObjectToStream(bos, obj);
answer = new ByteArrayEntity(bos.toByteArray(), HttpConstants.JAVA_SERIALIZED_OBJECT);
}
} else if (data instanceof File || data instanceof GenericFile) {
// file based (could potentially also be a FTP file etc)
File file = in.getBody(File.class);
if (file != null) {
answer = new FileEntity(file, contentType);
}
} else if (data instanceof String content) {
// be a bit careful with String as any type can most likely be converted to String
// so we only do an instanceof check and accept String if the body is really a String
// do not fallback to use the default charset as it can influence the request
// (for example application/x-www-form-urlencoded forms being sent)
String charset = ExchangeHelper.getCharsetName(exchange, false);
if (charset == null && contentType != null) {
// okay try to get the charset from the content-type
Charset cs = contentType.getCharset();
if (cs != null) {
charset = cs.name();
}
}
// sync contentType.charset and charset
if (contentType != null && contentType.getCharset() == null && charset != null) {
contentType = ContentType.parse(contentType + ";charset=" + charset);
}
answer = new StringEntity(content, contentType, false);
}
// fallback as input stream
if (answer == null) {
// force the body as an input stream since this is the fallback
InputStream is = in.getMandatoryBody(InputStream.class);
answer = new InputStreamEntity(is, -1, contentType);
}
}
} catch (UnsupportedEncodingException e) {
throw new CamelExchangeException("Error creating RequestEntity from message body", exchange, e);
} catch (IOException e) {
throw new CamelExchangeException("Error serializing message body", exchange, e);
}
}
return answer;
}
public HttpClient getHttpClient() {
return httpClient;
}
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy