@Override public void populateCamelHeaders( HttpResponse response, Map<String, Object> headers, Exchange exchange, NettyHttpConfiguration configuration) throws Exception { LOG.trace("populateCamelHeaders: {}", response); headers.put(Exchange.HTTP_RESPONSE_CODE, response.getStatus().getCode()); headers.put(Exchange.HTTP_RESPONSE_TEXT, response.getStatus().getReasonPhrase()); for (String name : response.headers().names()) { // mapping the content-type if (name.toLowerCase().equals("content-type")) { name = Exchange.CONTENT_TYPE; } // add the headers one by one, and use the header filter strategy List<String> values = response.headers().getAll(name); Iterator<?> it = ObjectHelper.createIterator(values); while (it.hasNext()) { Object extracted = it.next(); LOG.trace("HTTP-header: {}", extracted); if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders(name, extracted, exchange)) { NettyHttpHelper.appendHeader(headers, name, extracted); } } } }
@Override protected Object getRequestBody(Exchange exchange) throws Exception { // creating the url to use takes 2-steps String uri = NettyHttpHelper.createURL(exchange, getEndpoint()); URI u = NettyHttpHelper.createURI(exchange, uri, getEndpoint()); HttpRequest request = getEndpoint() .getNettyHttpBinding() .toNettyRequest(exchange.getIn(), u.toString(), getConfiguration()); String actualUri = request.getUri(); exchange.getIn().setHeader(Exchange.HTTP_URL, actualUri); // Need to check if we need to close the connection or not if (!HttpHeaders.isKeepAlive(request)) { // just want to make sure we close the channel if the keepAlive is not true exchange.setProperty(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, true); } if (getConfiguration().isBridgeEndpoint()) { // Need to remove the Host key as it should be not used when bridging/proxying exchange.getIn().removeHeader("host"); } return request; }
@Override public void done(boolean doneSync) { try { NettyHttpMessage nettyMessage = exchange.hasOut() ? exchange.getOut(NettyHttpMessage.class) : exchange.getIn(NettyHttpMessage.class); if (nettyMessage != null) { HttpResponse response = nettyMessage.getHttpResponse(); if (response != null) { // the actual url is stored on the IN message in the getRequestBody method as its // accessed on-demand String actualUrl = exchange.getIn().getHeader(Exchange.HTTP_URL, String.class); int code = response.getStatus() != null ? response.getStatus().getCode() : -1; log.debug("Http responseCode: {}", code); // if there was a http error code (300 or higher) then check if we should throw an // exception if (code >= 300 && getConfiguration().isThrowExceptionOnFailure()) { // operation failed so populate exception to throw Exception cause = NettyHttpHelper.populateNettyHttpOperationFailedException( exchange, actualUrl, response, code, getConfiguration().isTransferException()); exchange.setException(cause); } } } } finally { // ensure we call the delegated callback callback.done(doneSync); } }
@Override public void populateCamelHeaders( HttpRequest request, Map<String, Object> headers, Exchange exchange, NettyHttpConfiguration configuration) throws Exception { LOG.trace("populateCamelHeaders: {}", request); // NOTE: these headers is applied using the same logic as camel-http/camel-jetty to be // consistent headers.put(Exchange.HTTP_METHOD, request.getMethod().getName()); // strip query parameters from the uri String s = request.getUri(); if (s.contains("?")) { s = ObjectHelper.before(s, "?"); } // we want the full path for the url, as the client may provide the url in the HTTP headers as // absolute or relative, eg // /foo // http://servername/foo String http = configuration.isSsl() ? "https://" : "http://"; if (!s.startsWith(http)) { if (configuration.getPort() != 80) { s = http + configuration.getHost() + ":" + configuration.getPort() + s; } else { s = http + configuration.getHost() + s; } } headers.put(Exchange.HTTP_URL, s); // uri is without the host and port URI uri = new URI(request.getUri()); // uri is path and query parameters headers.put(Exchange.HTTP_URI, uri.getPath()); headers.put(Exchange.HTTP_QUERY, uri.getQuery()); headers.put(Exchange.HTTP_RAW_QUERY, uri.getRawQuery()); // strip the starting endpoint path so the path is relative to the endpoint uri String path = uri.getPath(); if (configuration.getPath() != null && path.startsWith(configuration.getPath())) { path = path.substring(configuration.getPath().length()); } headers.put(Exchange.HTTP_PATH, path); if (LOG.isTraceEnabled()) { LOG.trace("HTTP-Method {}", request.getMethod().getName()); LOG.trace("HTTP-Uri {}", request.getUri()); } for (String name : request.headers().names()) { // mapping the content-type if (name.toLowerCase(Locale.US).equals("content-type")) { name = Exchange.CONTENT_TYPE; } if (name.toLowerCase(Locale.US).equals("authorization")) { String value = request.headers().get(name); // store a special header that this request was authenticated using HTTP Basic if (value != null && value.trim().startsWith("Basic")) { if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders( NettyHttpConstants.HTTP_AUTHENTICATION, "Basic", exchange)) { NettyHttpHelper.appendHeader(headers, NettyHttpConstants.HTTP_AUTHENTICATION, "Basic"); } } } // add the headers one by one, and use the header filter strategy List<String> values = request.headers().getAll(name); Iterator<?> it = ObjectHelper.createIterator(values); while (it.hasNext()) { Object extracted = it.next(); Object decoded = shouldUrlDecodeHeader(configuration, name, extracted, "UTF-8"); LOG.trace("HTTP-header: {}", extracted); if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders(name, decoded, exchange)) { NettyHttpHelper.appendHeader(headers, name, decoded); } } } // add uri parameters as headers to the Camel message if (request.getUri().contains("?")) { String query = ObjectHelper.after(request.getUri(), "?"); Map<String, Object> uriParameters = URISupport.parseQuery(query, false, true); for (Map.Entry<String, Object> entry : uriParameters.entrySet()) { String name = entry.getKey(); Object values = entry.getValue(); Iterator<?> it = ObjectHelper.createIterator(values); while (it.hasNext()) { Object extracted = it.next(); Object decoded = shouldUrlDecodeHeader(configuration, name, extracted, "UTF-8"); LOG.trace("URI-Parameter: {}", extracted); if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders(name, decoded, exchange)) { NettyHttpHelper.appendHeader(headers, name, decoded); } } } } // if body is application/x-www-form-urlencoded then extract the body as query string and append // as headers // if it is a bridgeEndpoint we need to skip this part of work if (request.getMethod().getName().equals("POST") && request.headers().get(Exchange.CONTENT_TYPE) != null && request .headers() .get(Exchange.CONTENT_TYPE) .startsWith(NettyHttpConstants.CONTENT_TYPE_WWW_FORM_URLENCODED) && !configuration.isBridgeEndpoint()) { String charset = "UTF-8"; // Push POST form params into the headers to retain compatibility with DefaultHttpBinding String body = request.getContent().toString(Charset.forName(charset)); if (ObjectHelper.isNotEmpty(body)) { for (String param : body.split("&")) { String[] pair = param.split("=", 2); if (pair.length == 2) { String name = shouldUrlDecodeHeader(configuration, "", pair[0], charset); String value = shouldUrlDecodeHeader(configuration, name, pair[1], charset); if (headerFilterStrategy != null && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, exchange)) { NettyHttpHelper.appendHeader(headers, name, value); } } else { throw new IllegalArgumentException( "Invalid parameter, expected to be a pair but was " + param); } } } } }
@Override public HttpRequest toNettyRequest( Message message, String uri, NettyHttpConfiguration configuration) throws Exception { LOG.trace("toNettyRequest: {}", message); // the message body may already be a Netty HTTP response if (message.getBody() instanceof HttpRequest) { return (HttpRequest) message.getBody(); } // just assume GET for now, we will later change that to the actual method to use HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri); TypeConverter tc = message.getExchange().getContext().getTypeConverter(); // 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<String, Object> skipRequestHeaders = null; if (configuration.isBridgeEndpoint()) { String queryString = message.getHeader(Exchange.HTTP_QUERY, String.class); if (queryString != null) { skipRequestHeaders = URISupport.parseQuery(queryString, false, true); } // Need to remove the Host key as it should be not used message.getHeaders().remove("host"); } // append headers // must use entrySet to ensure case of keys is preserved for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // 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; } // use an iterator as there can be multiple values. (must not use a delimiter) final Iterator<?> it = ObjectHelper.createIterator(value, null, true); while (it.hasNext()) { String headerValue = tc.convertTo(String.class, it.next()); if (headerValue != null && headerFilterStrategy != null && !headerFilterStrategy.applyFilterToCamelHeaders( key, headerValue, message.getExchange())) { LOG.trace("HTTP-Header: {}={}", key, headerValue); request.headers().add(key, headerValue); } } } Object body = message.getBody(); if (body != null) { // support bodies as native Netty ChannelBuffer buffer; if (body instanceof ChannelBuffer) { buffer = (ChannelBuffer) body; } else { // try to convert to buffer first buffer = message.getBody(ChannelBuffer.class); if (buffer == null) { // fallback to byte array as last resort byte[] data = message.getMandatoryBody(byte[].class); buffer = ChannelBuffers.copiedBuffer(data); } } if (buffer != null) { request.setContent(buffer); int len = buffer.readableBytes(); // set content-length request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len); LOG.trace("Content-Length: {}", len); } else { // we do not support this kind of body throw new NoTypeConversionAvailableException(body, ChannelBuffer.class); } } // update HTTP method accordingly as we know if we have a body or not HttpMethod method = NettyHttpHelper.createMethod(message, body != null); request.setMethod(method); // set the content type in the response. String contentType = MessageHelper.getContentType(message); if (contentType != null) { // set content-type request.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType); LOG.trace("Content-Type: {}", contentType); } // must include HOST header as required by HTTP 1.1 // use URI as its faster than URL (no DNS lookup) URI u = new URI(uri); String host = u.getHost(); request.headers().set(HttpHeaders.Names.HOST, host); LOG.trace("Host: {}", host); // configure connection to accordingly to keep alive configuration // favor using the header from the message String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class); if (connection == null) { // fallback and use the keep alive from the configuration if (configuration.isKeepAlive()) { connection = HttpHeaders.Values.KEEP_ALIVE; } else { connection = HttpHeaders.Values.CLOSE; } } request.headers().set(HttpHeaders.Names.CONNECTION, connection); LOG.trace("Connection: {}", connection); return request; }