/** * 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.jetty; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.RuntimeCamelException; import org.apache.camel.http.common.HttpConstants; import org.apache.camel.http.common.HttpHeaderFilterStrategy; import org.apache.camel.http.common.HttpHelper; import org.apache.camel.http.common.HttpOperationFailedException; import org.apache.camel.http.common.HttpProtocolHeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.util.IOHelper; import org.apache.camel.util.MessageHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @version */ public class DefaultJettyHttpBinding implements JettyHttpBinding { private static final Logger LOG = LoggerFactory.getLogger(DefaultJettyHttpBinding.class); private HeaderFilterStrategy headerFilterStrategy = new HttpHeaderFilterStrategy(); private HeaderFilterStrategy httpProtocolHeaderFilterStrategy = new HttpProtocolHeaderFilterStrategy(); private boolean throwExceptionOnFailure; private boolean transferException; private boolean allowJavaSerializedObject; private String okStatusCodeRange; public DefaultJettyHttpBinding() { } public void populateResponse(Exchange exchange, JettyContentExchange httpExchange) throws Exception { int responseCode = httpExchange.getResponseStatus(); LOG.debug("HTTP responseCode: {}", responseCode); Message in = exchange.getIn(); if (!isThrowExceptionOnFailure()) { // if we do not use failed exception then populate response for all response codes populateResponse(exchange, httpExchange, in, getHeaderFilterStrategy(), responseCode); } else { boolean ok = HttpHelper.isStatusCodeOk(responseCode, okStatusCodeRange); if (ok) { // only populate response for OK response populateResponse(exchange, httpExchange, in, getHeaderFilterStrategy(), responseCode); } else { // operation failed so populate exception to throw Exception ex = populateHttpOperationFailedException(exchange, httpExchange, responseCode); if (ex != null) { throw ex; } else { populateResponse(exchange, httpExchange, in, getHeaderFilterStrategy(), responseCode); } } } } public HeaderFilterStrategy getHeaderFilterStrategy() { return headerFilterStrategy; } public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { this.headerFilterStrategy = headerFilterStrategy; } public boolean isThrowExceptionOnFailure() { return throwExceptionOnFailure; } public void setThrowExceptionOnFailure(boolean throwExceptionOnFailure) { this.throwExceptionOnFailure = throwExceptionOnFailure; } public boolean isTransferException() { return transferException; } public void setTransferException(boolean transferException) { this.transferException = transferException; } public boolean isAllowJavaSerializedObject() { return allowJavaSerializedObject; } public void setAllowJavaSerializedObject(boolean allowJavaSerializedObject) { this.allowJavaSerializedObject = allowJavaSerializedObject; } public String getOkStatusCodeRange() { return okStatusCodeRange; } public void setOkStatusCodeRange(String okStatusCodeRange) { this.okStatusCodeRange = okStatusCodeRange; } protected void populateResponse(Exchange exchange, JettyContentExchange httpExchange, Message in, HeaderFilterStrategy strategy, int responseCode) throws IOException { Message answer = exchange.getOut(); answer.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode); // must use response fields to get the http headers as // httpExchange.getHeaders() does not work well with multi valued headers Map<String, Collection<String>> headers = httpExchange.getResponseHeaders(); for (Map.Entry<String, Collection<String>> ent : headers.entrySet()) { String name = ent.getKey(); Collection<String> values = ent.getValue(); for (String value : values) { if (name.toLowerCase().equals("content-type")) { name = Exchange.CONTENT_TYPE; exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.getCharsetNameFromContentType(value)); } if (strategy != null && !strategy.applyFilterToExternalHeaders(name, value, exchange)) { HttpHelper.appendHeader(answer.getHeaders(), name, value); } } } // preserve headers from in by copying any non existing headers // to avoid overriding existing headers with old values // We also need to apply the httpProtocolHeaderFilterStrategy to filter the http protocol header MessageHelper.copyHeaders(exchange.getIn(), answer, httpProtocolHeaderFilterStrategy, false); // extract body after headers has been set as we want to ensure content-type from Jetty HttpExchange // has been populated first answer.setBody(extractResponseBody(exchange, httpExchange)); } protected Exception populateHttpOperationFailedException(Exchange exchange, JettyContentExchange httpExchange, int responseCode) throws IOException { HttpOperationFailedException answer; String uri = httpExchange.getUrl(); Map<String, String> headers = getSimpleMap(httpExchange.getResponseHeaders()); Object responseBody = extractResponseBody(exchange, httpExchange); if (transferException && responseBody != null && responseBody instanceof Exception) { // if the response was a serialized exception then use that return (Exception) responseBody; } // 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); } if (responseCode >= 300 && responseCode < 400) { Collection<String> loc = httpExchange.getResponseHeaders().get("location"); if (loc != null && !loc.isEmpty()) { String locationHeader = loc.iterator().next(); answer = new HttpOperationFailedException(uri, responseCode, null, locationHeader, headers, copy); } else { // no redirect location answer = new HttpOperationFailedException(uri, responseCode, null, null, headers, copy); } } else { // internal server error (error code 500) answer = new HttpOperationFailedException(uri, responseCode, null, null, headers, copy); } return answer; } protected Object extractResponseBody(Exchange exchange, JettyContentExchange httpExchange) throws IOException { Map<String, String> headers = getSimpleMap(httpExchange.getResponseHeaders()); String contentType = headers.get(Exchange.CONTENT_TYPE); // if content type is serialized java object, then de-serialize it to a Java object if (contentType != null && HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) { // only deserialize java if allowed if (isAllowJavaSerializedObject() || isTransferException()) { try { InputStream is = exchange.getContext().getTypeConverter().mandatoryConvertTo(InputStream.class, httpExchange.getResponseContentBytes()); return HttpHelper.deserializeJavaObjectFromStream(is, exchange.getContext()); } catch (Exception e) { throw new RuntimeCamelException("Cannot deserialize body to Java object", e); } } else { // empty body return null; } } else { // just grab the raw content body return httpExchange.getBody(); } } Map<String, String> getSimpleMap(Map<String, Collection<String>> headers) { Map<String, String> result = new HashMap<String, String>(); for (String key : headers.keySet()) { Collection<String> valueCol = headers.get(key); String value = (valueCol == null) ? null : valueCol.iterator().next(); result.put(key, value); } return result; } }