@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; }
@Override public HttpResponse toNettyResponse(Message message, NettyHttpConfiguration configuration) throws Exception { LOG.trace("toNettyResponse: {}", message); // the message body may already be a Netty HTTP response if (message.getBody() instanceof HttpResponse) { return (HttpResponse) message.getBody(); } // the response code is 200 for OK and 500 for failed boolean failed = message.getExchange().isFailed(); int defaultCode = failed ? 500 : 200; int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, defaultCode, int.class); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(code)); LOG.trace("HTTP Status Code: {}", code); TypeConverter tc = message.getExchange().getContext().getTypeConverter(); // 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(); // use an iterator as there can be multiple values. (must not use a delimiter) final Iterator<?> it = ObjectHelper.createIterator(value, null); 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); response.headers().add(key, headerValue); } } } Object body = message.getBody(); Exception cause = message.getExchange().getException(); // support bodies as native Netty ChannelBuffer buffer; // if there was an exception then use that as body if (cause != null) { if (configuration.isTransferException()) { // we failed due an exception, and transfer it as java serialized object ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(cause); oos.flush(); IOHelper.close(oos, bos); // the body should be the serialized java object of the exception body = ChannelBuffers.copiedBuffer(bos.toByteArray()); // force content type to be serialized java object message.setHeader( Exchange.CONTENT_TYPE, NettyHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT); } else { // we failed due an exception so print it as plain text StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); cause.printStackTrace(pw); // the body should then be the stacktrace body = ChannelBuffers.copiedBuffer(sw.toString().getBytes()); // force content type to be text/plain as that is what the stacktrace is message.setHeader(Exchange.CONTENT_TYPE, "text/plain"); } // and mark the exception as failure handled, as we handled it by returning it as the response ExchangeHelper.setFailureHandled(message.getExchange()); } 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.getBody(byte[].class); if (data != null) { buffer = ChannelBuffers.copiedBuffer(data); } else { // and if byte array fails then try String String str; if (body != null) { str = message.getMandatoryBody(String.class); } else { str = ""; } buffer = ChannelBuffers.copiedBuffer(str.getBytes()); } } } if (buffer != null) { response.setContent(buffer); // We just need to reset the readerIndex this time if (buffer.readerIndex() == buffer.writerIndex()) { buffer.setIndex(0, buffer.writerIndex()); } // TODO How to enable the chunk transport int len = buffer.readableBytes(); // set content-length response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len); LOG.trace("Content-Length: {}", len); } // set the content type in the response. String contentType = MessageHelper.getContentType(message); if (contentType != null) { // set content-type response.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType); LOG.trace("Content-Type: {}", contentType); } // configure connection to accordingly to keep alive configuration // favor using the header from the message String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class); // Read the connection header from the exchange property if (connection == null) { connection = message.getExchange().getProperty(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; } } response.headers().set(HttpHeaders.Names.CONNECTION, connection); // Just make sure we close the channel when the connection value is close if (connection.equalsIgnoreCase(HttpHeaders.Values.CLOSE)) { message.setHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, true); } LOG.trace("Connection: {}", connection); return response; }