You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by eg...@apache.org on 2007/04/17 14:47:16 UTC
svn commit: r529583 [2/5] - in /incubator/cxf/trunk:
rt/frontend/simple/src/main/java/org/apache/cxf/service/factory/
rt/transports/http/src/main/java/org/apache/cxf/transport/http/
rt/transports/http/src/main/java/org/apache/cxf/transport/http/spring/...
Modified: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java?view=diff&rev=529583&r1=529582&r2=529583
==============================================================================
--- incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java (original)
+++ incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java Tue Apr 17 05:47:13 2007
@@ -18,6 +18,7 @@
*/
package org.apache.cxf.transport.http;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -29,8 +30,10 @@
import java.net.URLConnection;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -61,153 +64,540 @@
import org.apache.cxf.ws.policy.Assertor;
import org.apache.cxf.ws.policy.PolicyEngine;
import org.apache.cxf.wsdl.EndpointReferenceUtils;
+import org.apache.geronimo.mail.util.StringBufferOutputStream;
import static org.apache.cxf.message.Message.DECOUPLED_CHANNEL_MESSAGE;
-/**
+/*
* HTTP Conduit implementation.
+ * <p>
+ * This implementation is a based on the java.net.URLConnection interface and
+ * dependent upon installed implementations of that URLConnection,
+ * HttpURLConnection, and HttpsURLConnection. Currently, this implemenation
+ * has been known to work with the Sun JDK 1.5 default implementations. The
+ * HttpsURLConnection is part of Sun's implemenation of the JSSE.
+ * Presently, the source code for the Sun JSSE implemenation is unavaiable
+ * and therefore we may only lay a guess of whether its HttsURLConnection
+ * implemenation correctly works as far as security is concerned.
+ * <p>
+ * The Trust Decision. If a MessageTrustDecider is configured/set for the
+ * Conduit, it is called upon the first flush of the headers in the
+ * WrappedOutputStream. This reason for this approach is two fold.
+ * Theoretically, in order to get connection information out of the
+ * URLConnection, it must be "connected". We assume that its implementation will
+ * only follow through up to the point at which it will be ready to send
+ * one byte of data down to the endpoint, but through proxies, and the
+ * commpletion of a TLS handshake in the case of HttpsURLConnection.
+ * However, if we force the connect() call right away, the default
+ * implementations will not allow any calls to add/setRequestProperty,
+ * throwing an exception that the URLConnection is already connected.
+ * <p>
+ * We need to keep the semantic that later CXF interceptors may add to the
+ * PROTOCOL_HEADERS in the Message. This architectual decision forces us to
+ * delay the connection until after that point, then pulling the trust decision.
+ * <p>
+ * The security caveat is that we don't really know when the connection is
+ * really established. The call to "connect" is stated to force the
+ * "connection," but it is a no-op if the connection was already established.
+ * It is entirely possible that an implementation of an URLConnection may
+ * indeed connect at will and start sending the headers down the connection
+ * during calls to add/setRequestProperty!
+ * <p>
+ * We know that the JDK 1.5 sun.com.net.www.HttpURLConnection does not send
+ * this information before the "connect" call, because we can look at the
+ * source code. However, we can only assume, not verify, that the JSSE 1.5
+ * HttpsURLConnection does the same, in that it is probable that the
+ * HttpsURLConnection shares the HttpURLConnection implemenation.
+ * <p>
+ * Due to these implementations following redirects without trust checks, we
+ * force the URLConnection implementations not to follow redirects. If
+ * client side policy dictates that we follow redirects, trust decisions are
+ * placed before each retransmit. On a redirect, any authorization information
+ * dynamically acquired by a BasicAuth UserPass supplier is removed before
+ * being retransmitted, as it may no longer be applicable to the new url to
+ * which the connection is redirected.
+ */
+
+/**
+ * This Conduit handles the "http" and "https" transport protocols. An
+ * instance is governed by policies either explicity set or by
+ * configuration.
*/
-public class HTTPConduit extends AbstractConduit implements Configurable, Assertor {
- public static final String HTTP_CONNECTION = "http.connection";
+public class HTTPConduit
+ extends AbstractConduit
+ implements Configurable, Assertor {
+
+ /**
+ * This constant is the Message(Map) key for the HttpURLConnection that
+ * is used to get the response.
+ */
+ public static final String KEY_HTTP_CONNECTION = "http.connection";
+
+ /**
+ * This constant is the Message(Map) key for a list of visited URLs that
+ * is used in redirect loop protection.
+ */
+ private static final String KEY_VISITED_URLS = "VisitedURLs";
+
+ /**
+ * This constant is the Message(Map) key for a list of URLs that
+ * is used in authorization loop protection.
+ */
+ private static final String KEY_AUTH_URLS = "AuthURLs";
+
+ /**
+ * The Logger for this class.
+ */
private static final Logger LOG = LogUtils.getL7dLogger(HTTPConduit.class);
- private final Bus bus;
- private final URLConnectionFactory alternateConnectionFactory;
- private URLConnectionFactory connectionFactory;
- private URL url;
+ /**
+ * This constant holds the suffix ".http-conduit" that is appended to the
+ * Endpoint Qname to give the configuration name of this conduit.
+ */
+ private static final String SC_HTTP_CONDUIT_SUFFIX = ".http-conduit";
+
+
+ /**
+ * This field holds the connection factory, which primarily is used to
+ * factor out SSL specific code from this implementation.
+ * <p>
+ * This field is "protected" to facilitate some contrived UnitTesting so
+ * that an extended class may alter its value with an EasyMock URLConnection
+ * Factory.
+ */
+ protected HttpURLConnectionFactory connectionFactory;
+ /**
+ * This field holds a reference to the CXF bus associated this conduit.
+ */
+ private final Bus bus;
+
+ /**
+ * This field is used for two reasons. First it provides the base name for
+ * the conduit for Spring configuration. The other is to hold default
+ * address information, should it not be supplied in the Message Map, by the
+ * Message.ENDPOINT_ADDRESS property.
+ */
+ private final EndpointInfo endpointInfo;
+
+ /**
+ * This field holds the "default" URL for this particular conduit, which
+ * is set at construction.
+ */
+ private final URL defaultEndpointURL;
private Destination decoupledDestination;
private MessageObserver decoupledObserver;
private int decoupledDestinationRefCount;
- private EndpointInfo endpointInfo;
- // Configuration values
- private HTTPClientPolicy client;
- private AuthorizationPolicy authorization;
- private AuthorizationPolicy proxyAuthorization;
- private SSLClientPolicy sslClient;
+ // Configuratble/settable values
+
+ /**
+ * This field holds the QoS configuration settings for this conduit.
+ * This field is injected via spring configuration based on the conduit
+ * name.
+ */
+ private HTTPClientPolicy clientSidePolicy;
+
+ /**
+ * This field holds the password authorization configuration.
+ * This field is injected via spring configuration based on the conduit
+ * name.
+ */
+ private AuthorizationPolicy authorizationPolicy;
+ /**
+ * This field holds the password authorization configuration for the
+ * configured proxy. This field is injected via spring configuration based
+ * on the conduit name.
+ */
+ private AuthorizationPolicy proxyAuthorizationPolicy;
+
+ /**
+ * This field holds the configuration TLS configuration which is
+ * "injected" by spring based on the conduit name.
+ */
+ private SSLClientPolicy sslClientSidePolicy;
+
+ /**
+ * This field contains the MessageTrustDecider.
+ */
+ private MessageTrustDecider trustDecider;
+
+ /**
+ * This field contains the HttpBasicAuthSupplier.
+ */
+ private HttpBasicAuthSupplier basicAuthSupplier;
/**
- * Constructor
- *
- * @param b the associated Bus
- * @param ei the endpoint info of the initiator
- * @throws IOException
+ * This boolean signfies that that finalizeConfig is called, which is
+ * after the HTTPTransportFactory configures this object via spring.
+ * At this point, any change by a "setter" is dynamic, and any change
+ * should be handled as such.
*/
- public HTTPConduit(Bus b, EndpointInfo ei) throws IOException {
- this(b,
- ei,
- null);
- }
+ private boolean configFinalized;
/**
* Constructor
*
* @param b the associated Bus
* @param ei the endpoint info of the initiator
- * @param t the endpoint reference of the target
* @throws IOException
*/
- public HTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType t) throws IOException {
+ public HTTPConduit(Bus b, EndpointInfo ei) throws IOException {
this(b,
ei,
- t,
null);
- }
+ }
/**
- * Constructor, allowing subsititution of
- * connnection factory and decoupled engine.
+ * Constructor
*
- * @param b the associated Bus
- * @param ei the endpoint info of the initiator
- * @param t the endpoint reference of the target
- * @param factory the URL connection factory
+ * @param associatedBus The associated Bus.
+ * @param endpoint The endpoint info of the initiator.
+ * @param epr The endpoint reference of the target.
* @throws IOException
*/
- public HTTPConduit(Bus b,
- EndpointInfo ei,
- EndpointReferenceType t,
- URLConnectionFactory factory) throws IOException {
- super(getTargetReference(ei, t, b));
- bus = b;
- endpointInfo = ei;
- alternateConnectionFactory = factory;
+ public HTTPConduit(
+ Bus associatedBus,
+ EndpointInfo endpoint,
+ EndpointReferenceType epr
+ ) throws IOException {
- initConfig();
+ super(getTargetReference(endpoint, epr, associatedBus));
- url = t == null
+ bus = associatedBus;
+ endpointInfo = endpoint;
+
+ defaultEndpointURL = epr == null
? new URL(endpointInfo.getAddress())
- : new URL(t.getAddress().getValue());
- }
+ : new URL(epr.getAddress().getValue());
+ initializeConfig();
+ }
+ /**
+ * This method returns the registered Logger for this conduit.
+ */
protected Logger getLogger() {
return LOG;
}
+
+ /**
+ * This method returns the name of the conduit, which is based on the
+ * endpoint name plus the SC_HTTP_CONDUIT_SUFFIX.
+ * @return
+ */
+ public final String getConduitName() {
+ return endpointInfo.getName() + SC_HTTP_CONDUIT_SUFFIX;
+ }
+
+ /**
+ * This method is called from the constructor which initializes
+ * the configuration. The TransportFactory will call configureBean
+ * on this object after construction.
+ */
+ private void initializeConfig() {
+
+ // wsdl extensors are superseded by policies which in
+ // turn are superseded by injection
+
+ PolicyEngine pe = bus.getExtension(PolicyEngine.class);
+ if (null != pe && pe.isEnabled()) {
+ clientSidePolicy =
+ PolicyUtils.getClient(pe, endpointInfo, this);
+ }
+
+ }
+ /**
+ * This call gets called by the HTTPTransportFactory after it
+ * causes an injection of the Spring configuration properties
+ * of this Conduit.
+ */
+ void finalizeConfig() {
+ // See if not set by configuration, if there are defaults
+ // in order from the Endpoint, Service, or Bus.
+
+ if (this.clientSidePolicy == null) {
+ clientSidePolicy = endpointInfo.getTraversedExtensor(
+ new HTTPClientPolicy(), HTTPClientPolicy.class);
+ }
+ if (this.authorizationPolicy == null) {
+ authorizationPolicy = endpointInfo.getTraversedExtensor(
+ new AuthorizationPolicy(), AuthorizationPolicy.class);
+
+ }
+ // This doesn't work well for proxyAuthorization because of the
+ // sameness of the class.
+ // TODO: Fix proxyAuthorization
+ if (this.proxyAuthorizationPolicy == null) {
+ proxyAuthorizationPolicy = endpointInfo.getTraversedExtensor(
+ new AuthorizationPolicy(), AuthorizationPolicy.class);
+
+ }
+ if (this.sslClientSidePolicy == null) {
+ sslClientSidePolicy = endpointInfo.getTraversedExtensor(
+ null, SSLClientPolicy.class);
+ }
+ if (this.trustDecider == null) {
+ trustDecider = endpointInfo.getTraversedExtensor(
+ null, MessageTrustDecider.class);
+ }
+ if (this.basicAuthSupplier == null) {
+ basicAuthSupplier = endpointInfo.getTraversedExtensor(
+ null, HttpBasicAuthSupplier.class);
+ }
+ if (trustDecider == null) {
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO,
+ "No Trust Decider configured for Conduit '"
+ + getConduitName() + "'");
+ }
+ } else {
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "Message Trust Decider of class '"
+ + trustDecider.getClass().getName()
+ + "' with logical name of '"
+ + trustDecider.getLogicalName()
+ + "' has been configured for Conduit '"
+ + getConduitName()
+ + "'");
+ }
+ }
+ if (basicAuthSupplier == null) {
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO,
+ "No Basic Auth Supplier configured for Conduit '"
+ + getConduitName() + "'");
+ }
+ } else {
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "HttpBasicAuthSupplier of class '"
+ + basicAuthSupplier.getClass().getName()
+ + "' with logical name of '"
+ + basicAuthSupplier.getLogicalName()
+ + "' has been configured for Conduit '"
+ + getConduitName()
+ + "'");
+ }
+ }
+
+ // Get the correct URLConnection factory based on the
+ // configuration.
+ retrieveConnectionFactory();
+
+ // We have finalized the configuration. Any configurable entity
+ // set now, must make changes dynamically.
+ configFinalized = true;
+ }
+
+ /**
+ * This method sets the connectionFactory field for this object. It is called
+ * after an SSL Client Policy is set or an HttpsHostnameVerifier
+ * because we need to reinitialize the connection factory.
+ * <p>
+ * This method is "protected" so that this class may be extended and override
+ * this method to put an EasyMock URL Connection factory for some contrived
+ * UnitTest that will of course break, should the calls to the URL Connection
+ * Factory get altered.
+ */
protected void retrieveConnectionFactory() {
- connectionFactory = alternateConnectionFactory != null
- ? alternateConnectionFactory
- : AbstractHTTPTransportFactory.getConnectionFactory(
- getSslClient());
+ connectionFactory = AbstractHTTPTransportFactory.getConnectionFactory(this);
}
-
+
/**
- * Prepare the outbound message for sending.
+ * Prepare to send an outbound HTTP message over this http conduit to a
+ * particular endpoint.
+ * <P>
+ * If the Message.PATH_INFO property is set it gets appended
+ * to the Conduit's endpoint URL. If the Message.QUERY_STRING
+ * property is set, it gets appended to the resultant URL following
+ * a "?".
+ * <P>
+ * If the Message.HTTP_REQUEST_METHOD property is NOT set, the
+ * Http request method defaults to "POST".
+ * <P>
+ * If the Message.PROTOCOL_HEADERS is not set on the message, it is
+ * initialized to an empty map.
+ * <P>
+ * This call creates the OutputStream for the content of the message.
+ * It also assigns the created Http(s)URLConnection to the Message
+ * Map.
*
- * @param message the message to be sent.
+ * @param message The message to be sent.
*/
public void prepare(Message message) throws IOException {
- Map<String, List<String>> headers = setHeaders(message);
- URL currentURL = setupURL(message);
- HTTPClientPolicy clientPolicy = getClient(message);
- URLConnection connection =
- connectionFactory.createConnection(getProxy(clientPolicy), currentURL);
+ Map<String, List<String>> headers = getSetProtocolHeaders(message);
+
+ // This call can possibly change the conduit endpoint address and
+ // protocol from the default set in EndpointInfo that is associated
+ // with the Conduit.
+ URL currentURL = setupURL(message);
+
+ HttpBasicAuthSupplier.UserPass userPass = null;
+
+ // The need to cache the request is off by default
+ boolean needToCacheRequest = false;
+
+ HttpURLConnection connection =
+ connectionFactory.createConnection(
+ getProxy(clientSidePolicy), currentURL);
connection.setDoOutput(true);
//TODO using Message context to deceided HTTP send properties
- connection.setConnectTimeout((int)clientPolicy.getConnectionTimeout());
- connection.setReadTimeout((int)clientPolicy.getReceiveTimeout());
+ connection.setConnectTimeout((int)clientSidePolicy.getConnectionTimeout());
+ connection.setReadTimeout((int)clientSidePolicy.getReceiveTimeout());
connection.setUseCaches(false);
+ // We implement redirects in this conduit. We do not
+ // rely on the underlying URLConnection implementation
+ // because of trust issues.
+ connection.setInstanceFollowRedirects(false);
- if (connection instanceof HttpURLConnection) {
- String httpRequestMethod = (String)message.get(Message.HTTP_REQUEST_METHOD);
- HttpURLConnection hc = (HttpURLConnection)connection;
- if (null != httpRequestMethod) {
- hc.setRequestMethod(httpRequestMethod);
- } else {
- hc.setRequestMethod("POST");
- }
- if (clientPolicy.isAutoRedirect()) {
- //cannot use chunking if autoredirect as the request will need to be
- //completely cached locally and resent to the redirect target
- hc.setInstanceFollowRedirects(true);
- } else {
- hc.setInstanceFollowRedirects(false);
- if (!hc.getRequestMethod().equals("GET")
- && clientPolicy.isAllowChunking()) {
- hc.setChunkedStreamingMode(2048);
- }
+ // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
+ String httpRequestMethod =
+ (String)message.get(Message.HTTP_REQUEST_METHOD);
+
+ if (null != httpRequestMethod) {
+ connection.setRequestMethod(httpRequestMethod);
+ } else {
+ connection.setRequestMethod("POST");
+ }
+ // We must cache the request if we have basic auth supplier
+ // without preemptive basic auth.
+ if (basicAuthSupplier != null) {
+ userPass = basicAuthSupplier.getPreemptiveUserPass(
+ getConduitName(), currentURL, message);
+ needToCacheRequest = userPass == null;
+ LOG.log(Level.INFO,
+ "Basic Auth Supplier, but no Premeptive User Pass."
+ + " We must cache request.");
+ }
+ if (getClient().isAutoRedirect()) {
+ // If the AutoRedirect property is set then we cannot
+ // use chunked streaming mode. We ignore the "AllowChunking"
+ // property if AutoRedirect is turned on.
+
+ needToCacheRequest = true;
+ LOG.log(Level.INFO, "AutoRedirect is turned on.");
+ } else {
+ if (!connection.getRequestMethod().equals("GET")
+ && getClient().isAllowChunking()
+ && !needToCacheRequest) {
+ //TODO: The chunking mode be configured or at least some
+ // documented client constant.
+ LOG.log(Level.INFO, "Chunking is set at 2048.");
+ connection.setChunkedStreamingMode(2048);
}
}
- message.put(HTTP_CONNECTION, connection);
- setPolicies(message, headers);
+
+ // The trust decision is relagated to after the "flushing" of the
+ // request headers.
+
+ // We place the connection on the message to pick it up
+ // in the WrappedOutputStream.
+
+ message.put(KEY_HTTP_CONNECTION, connection);
+
+ // Set the headers on the message according to configured
+ // client side policy.
+
+ setHeadersByPolicy(message, currentURL, headers);
message.setContent(OutputStream.class,
- new WrappedOutputStream(message, connection));
+ new WrappedOutputStream(
+ message, connection, needToCacheRequest));
+
+ // We are now "ready" to "send" the message.
}
+ /**
+ * This call must take place before anything is written to the
+ * URLConnection. The URLConnection.connect() will be called in order
+ * to get the connection information.
+ *
+ * This method is invoked just after setURLRequestHeaders() from the
+ * WrappedOutputStream before it writes data to the URLConnection.
+ *
+ * If trust cannot be established the Trust Decider implemenation
+ * throws an IOException.
+ *
+ * @param message The message being sent.
+ * @throws IOException This exception is thrown if trust cannot be
+ * established by the configured MessageTrustDecider.
+ * @see MessageTrustDecider
+ */
+ private void makeTrustDecision(Message message)
+ throws IOException {
+
+ HttpURLConnection connection =
+ (HttpURLConnection) message.get(KEY_HTTP_CONNECTION);
+
+ if (trustDecider != null) {
+ try {
+ // We must connect or we will not get the credentials.
+ // The call is (said to be) ingored internally if
+ // already connected.
+ connection.connect();
+ trustDecider.establishTrust(
+ getConduitName(),
+ connectionFactory.getConnectionInfo(connection),
+ message);
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.log(Level.FINE, "Trust Decider "
+ + trustDecider.getLogicalName()
+ + " considers Conduit "
+ + getConduitName()
+ + " trusted.");
+ }
+ } catch (UntrustedURLConnectionIOException untrustedEx) {
+ // This cast covers HttpsURLConnection as well.
+ ((HttpURLConnection)connection).disconnect();
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "Trust Decider "
+ + trustDecider.getLogicalName()
+ + " considers Conduit "
+ + getConduitName()
+ + " untrusted.", untrustedEx);
+ }
+ throw untrustedEx;
+ }
+ } else {
+ // This case, when there is no trust decider, a trust
+ // decision should be a matter of policy.
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.log(Level.FINE, "No Trust Decider for Conduit '"
+ + getConduitName()
+ + "'. An afirmative Trust Decision is assumed.");
+ }
+ }
+ }
+ /**
+ * This function sets up a URL based on ENDPOINT_ADDRESS, PATH_INFO,
+ * and QUERY_STRING properties in the Message. The QUERY_STRING gets
+ * added with a "?" after the PATH_INFO. If the ENDPOINT_ADDRESS is not
+ * set on the Message, the endpoint address is taken from the
+ * "defaultEndpointURL".
+ * <p>
+ * The PATH_INFO is only added to the endpoint address string should
+ * the PATH_INFO not equal the end of the endpoint address string.
+ *
+ * @param message The message holds the addressing information.
+ *
+ * @return The full URL specifying the HTTP request to the endpoint.
+ *
+ * @throws MalformedURLException
+ */
private URL setupURL(Message message) throws MalformedURLException {
String value = (String)message.get(Message.ENDPOINT_ADDRESS);
String pathInfo = (String)message.get(Message.PATH_INFO);
String queryString = (String)message.get(Message.QUERY_STRING);
- String result = value != null ? value : url.toString();
+ String result = value != null ? value : defaultEndpointURL.toString();
+
+ // REVISIT: is this really correct?
if (null != pathInfo && !result.endsWith(pathInfo)) {
result = result + pathInfo;
}
@@ -235,16 +625,16 @@
* Close the conduit
*/
public void close() {
- if (url != null) {
+ if (defaultEndpointURL != null) {
try {
- URLConnection connect = url.openConnection();
+ URLConnection connect = defaultEndpointURL.openConnection();
if (connect instanceof HttpURLConnection) {
((HttpURLConnection)connect).disconnect();
}
} catch (IOException ex) {
//ignore
}
- url = null;
+ //defaultEndpointURL = null;
}
// in decoupled case, close response Destination if reference count
@@ -259,16 +649,19 @@
* @return the encapsulated URL
*/
protected URL getURL() {
- return url;
+ return defaultEndpointURL;
}
/**
- * Ensure an initial set of header is availbale on the outbound message.
+ * While extracting the Message.PROTOCOL_HEADERS property from the Message,
+ * this call ensures that the Message.PROTOCOL_HEADERS property is
+ * set on the Message. If it is not set, an empty map is placed there, and
+ * then returned.
*
- * @param message the outbound message
- * @return the headers
+ * @param message The outbound message
+ * @return The PROTOCOL_HEADERS map
*/
- private Map<String, List<String>> setHeaders(Message message) {
+ private Map<String, List<String>> getSetProtocolHeaders(Message message) {
Map<String, List<String>> headers =
CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
if (null == headers) {
@@ -278,61 +671,94 @@
return headers;
}
+ /* PMD for non-use!
+ private void printHeaders(URLConnection connection) {
+ int i = 0;
+ String k = connection.getHeaderFieldKey(i);
+ String h = connection.getHeaderField(i);
+ while (h != null) {
+ System.out.println(k + ": " + h);
+ k = connection.getHeaderFieldKey(++i);
+ h = connection.getHeaderField(i);
+ }
+ }
+ */
+
/**
- * Flush the headers onto the output stream.
- *
- * @param message the outbound message
- * @throws IOException
+ * This procedure sets the URLConnection request properties
+ * from the PROTOCOL_HEADERS in the message.
*/
- protected void flushHeaders(Message message) throws IOException {
- URLConnection connection = (URLConnection)message.get(HTTP_CONNECTION);
- String ct = (String) message.get(Message.CONTENT_TYPE);
- String enc = (String) message.get(Message.ENCODING);
-
- if (null != ct) {
- if (enc != null && ct.indexOf("charset=") == -1) {
- ct = ct + "; charset=" + enc;
+ private void transferProtocolHeadersToURLConnection(
+ Message message,
+ URLConnection connection
+ ) {
+ Map<String, List<String>> headers =
+ getSetProtocolHeaders(message);
+ for (String header : headers.keySet()) {
+ List<String> headerList = headers.get(header);
+ for (String value : headerList) {
+ connection.addRequestProperty(header, value);
}
- connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, ct);
- } else if (enc != null) {
- connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, "text/xml; charset=" + enc);
- } else {
- connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, "text/xml");
}
-
- Map<String, List<String>> headers =
- CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
- if (null != headers) {
- for (String header : headers.keySet()) {
- List<String> headerList = headers.get(header);
- for (String value : headerList) {
- connection.addRequestProperty(header, value);
- }
+ }
+
+ /**
+ * This procedure logs the PROTOCOL_HEADERS from the
+ * Message at the specified logging level.
+ *
+ * @param level The Logging Level.
+ * @param message The Message.
+ */
+ private void logProtocolHeaders(
+ Level level,
+ Message message
+ ) {
+ Map<String, List<String>> headers =
+ getSetProtocolHeaders(message);
+ for (String header : headers.keySet()) {
+ List<String> headerList = headers.get(header);
+ for (String value : headerList) {
+ LOG.log(level, header + ": " + value);
}
}
}
-
+
/**
- * Retrieve the respons code.
+ * Put the headers from Message.PROTOCOL_HEADERS headers into the URL
+ * connection.
+ * Note, this does not mean they immediately get written to the output
+ * stream or the wire. They just just get set on the HTTP request.
*
- * @param connection the URLConnection
- * @return the response code
+ * @param message The outbound message.
* @throws IOException
*/
- private int getResponseCode(URLConnection connection) throws IOException {
- int responseCode = HttpURLConnection.HTTP_OK;
- if (connection instanceof HttpURLConnection) {
- HttpURLConnection hc = (HttpURLConnection)connection;
- responseCode = hc.getResponseCode();
+ private void setURLRequestHeaders(Message message) throws IOException {
+ HttpURLConnection connection =
+ (HttpURLConnection)message.get(KEY_HTTP_CONNECTION);
+
+ String ct = (String) message.get(Message.CONTENT_TYPE);
+ if (null != ct) {
+ connection.setRequestProperty(
+ HttpHeaderHelper.CONTENT_TYPE, ct);
} else {
- if (connection.getHeaderField(Message.RESPONSE_CODE) != null) {
- responseCode =
- Integer.parseInt(connection.getHeaderField(Message.RESPONSE_CODE));
- }
+ connection.setRequestProperty(
+ HttpHeaderHelper.CONTENT_TYPE, "text/xml");
+ }
+
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.fine("Sending "
+ + connection.getRequestMethod()
+ + " Message with Headers to "
+ + connection.getURL()
+ + " Conduit :"
+ + getConduitName());
+ logProtocolHeaders(Level.FINE, message);
}
- return responseCode;
+
+ transferProtocolHeadersToURLConnection(message, connection);
+
}
-
+
/**
* Set up the decoupled Destination if necessary.
*/
@@ -348,7 +774,8 @@
duplicateDecoupledDestination();
} catch (Exception e) {
// REVISIT move message to localizable Messages.properties
- LOG.log(Level.WARNING, "decoupled endpoint creation failed: ", e);
+ LOG.log(Level.WARNING,
+ "decoupled endpoint creation failed: ", e);
}
}
}
@@ -392,8 +819,10 @@
}
/**
- * @param exchange the exchange in question
- * @return true iff the exchange indicates a oneway MEP
+ * This predicate returns true iff the exchange indicates
+ * a oneway MEP.
+ *
+ * @param exchange The exchange in question
*/
private boolean isOneway(Exchange exchange) {
return exchange != null && exchange.isOneWay();
@@ -404,44 +833,66 @@
* @param responseCode the response code
* @return true if a partial response is pending on the connection
*/
- private boolean isPartialResponse(URLConnection connection,
- int responseCode) {
+ private boolean isPartialResponse(
+ HttpURLConnection connection,
+ int responseCode
+ ) {
return responseCode == HttpURLConnection.HTTP_ACCEPTED
&& connection.getContentLength() != 0;
}
- private void initConfig() {
- //Initialize some default values for the configuration
-
- // wsdl extensors are superseded by policies which in turn are superseded by injection
-
- PolicyEngine pe = bus.getExtension(PolicyEngine.class);
- if (null != pe && pe.isEnabled()) {
- client = PolicyUtils.getClient(pe, endpointInfo, this);
- }
-
- if (null == client) {
- client = endpointInfo.getTraversedExtensor(new HTTPClientPolicy(), HTTPClientPolicy.class);
- }
-
- authorization = endpointInfo.getTraversedExtensor(new AuthorizationPolicy(),
- AuthorizationPolicy.class);
- proxyAuthorization = endpointInfo.getTraversedExtensor(new AuthorizationPolicy(),
- AuthorizationPolicy.class);
- }
-
+ /**
+ * This method returns the Proxy server should it be set on the
+ * Client Side Policy.
+ *
+ * @return The proxy server or null, if not set.
+ */
private Proxy getProxy(HTTPClientPolicy policy) {
Proxy proxy = null;
- if (policy.isSetProxyServer()) {
- proxy = new Proxy(Proxy.Type.valueOf(policy.getProxyServerType().toString()),
- new InetSocketAddress(policy.getProxyServer(),
- policy.getProxyServerPort()));
+ if (policy != null && policy.isSetProxyServer()) {
+ proxy = new Proxy(
+ Proxy.Type.valueOf(policy.getProxyServerType().toString()),
+ new InetSocketAddress(policy.getProxyServer(),
+ policy.getProxyServerPort()));
}
return proxy;
}
- private void setPolicies(Message message, Map<String, List<String>> headers) {
+ /**
+ * This call places HTTP Header strings into the headers that are relevant
+ * to the Authorization policies that are set on this conduit by
+ * configuration.
+ * <p>
+ * An AuthorizationPolicy may also be set on the message. If so, those
+ * policies are merged. A user name or password set on the messsage
+ * overrides settings in the AuthorizationPolicy is retrieved from the
+ * configuration.
+ * <p>
+ * The precedence is as follows:
+ * 1. AuthorizationPolicy that is set on the Message, if exists.
+ * 2. Preemptive UserPass from BasicAuthSupplier, if exists.
+ * 3. AuthorizationPolicy set/configured for conduit.
+ *
+ * REVISIT: Since the AuthorizationPolicy is set on the message by class, then
+ * how does one override the ProxyAuthorizationPolicy which is the same
+ * type?
+ *
+ * @param message
+ * @param headers
+ */
+ private void setHeadersByAuthorizationPolicy(
+ Message message,
+ URL url,
+ Map<String, List<String>> headers
+ ) {
AuthorizationPolicy authPolicy = getAuthorization();
+
+ HttpBasicAuthSupplier.UserPass userpass = null;
+ if (basicAuthSupplier != null) {
+ userpass = basicAuthSupplier.getPreemptiveUserPass(
+ getConduitName(), url, message);
+ }
+
AuthorizationPolicy newPolicy = message.get(AuthorizationPolicy.class);
String userName = null;
String passwd = null;
@@ -449,21 +900,24 @@
userName = newPolicy.getUserName();
passwd = newPolicy.getPassword();
}
- if (userName == null && authPolicy.isSetUserName()) {
+ if (userName == null
+ && userpass != null) {
+ userName = userpass.getUserid();
+ passwd = userpass.getPassword();
+ }
+ if (userName == null
+ && authPolicy != null && authPolicy.isSetUserName()) {
userName = authPolicy.getUserName();
}
if (userName != null) {
- if (passwd == null && authPolicy.isSetPassword()) {
+ if (passwd == null
+ && authPolicy != null && authPolicy.isSetPassword()) {
passwd = authPolicy.getPassword();
}
- userName += ":";
- if (passwd != null) {
- userName += passwd;
- }
- userName = Base64Utility.encode(userName.getBytes());
- headers.put("Authorization",
- Arrays.asList(new String[] {"Basic " + userName}));
- } else if (authPolicy.isSetAuthorizationType() && authPolicy.isSetAuthorization()) {
+ setBasicAuthHeader(userName, passwd, headers);
+ } else if (authPolicy != null
+ && authPolicy.isSetAuthorizationType()
+ && authPolicy.isSetAuthorization()) {
String type = authPolicy.getAuthorizationType();
type += " ";
type += authPolicy.getAuthorization();
@@ -471,21 +925,16 @@
Arrays.asList(new String[] {type}));
}
AuthorizationPolicy proxyAuthPolicy = getProxyAuthorization();
- if (proxyAuthPolicy.isSetUserName()) {
+ if (proxyAuthPolicy != null && proxyAuthPolicy.isSetUserName()) {
userName = proxyAuthPolicy.getUserName();
if (userName != null) {
passwd = "";
if (proxyAuthPolicy.isSetPassword()) {
passwd = proxyAuthPolicy.getPassword();
}
- userName += ":";
- if (passwd != null) {
- userName += passwd;
- }
- userName = Base64Utility.encode(userName.getBytes());
- headers.put("Proxy-Authorization",
- Arrays.asList(new String[] {"Basic " + userName}));
- } else if (proxyAuthPolicy.isSetAuthorizationType() && proxyAuthPolicy.isSetAuthorization()) {
+ setProxyBasicAuthHeader(userName, passwd, headers);
+ } else if (proxyAuthPolicy.isSetAuthorizationType()
+ && proxyAuthPolicy.isSetAuthorization()) {
String type = proxyAuthPolicy.getAuthorizationType();
type += " ";
type += proxyAuthPolicy.getAuthorization();
@@ -493,49 +942,88 @@
Arrays.asList(new String[] {type}));
}
}
+ }
+
+ /**
+ * This call places HTTP Header strings into the headers that are relevant
+ * to the ClientPolicy that is set on this conduit by configuration.
+ *
+ * REVISIT: A cookie is set statically from configuration?
+ */
+ private void setHeadersByClientPolicy(
+ Message message,
+ Map<String, List<String>> headers
+ ) {
HTTPClientPolicy policy = getClient(message);
+ if (policy == null) {
+ return;
+ }
if (policy.isSetCacheControl()) {
headers.put("Cache-Control",
- Arrays.asList(new String[] {policy.getCacheControl().value()}));
+ Arrays.asList(new String[] {policy.getCacheControl().value()}));
}
if (policy.isSetHost()) {
headers.put("Host",
- Arrays.asList(new String[] {policy.getHost()}));
+ Arrays.asList(new String[] {policy.getHost()}));
}
if (policy.isSetConnection()) {
headers.put("Connection",
- Arrays.asList(new String[] {policy.getConnection().value()}));
+ Arrays.asList(new String[] {policy.getConnection().value()}));
}
if (policy.isSetAccept()) {
headers.put("Accept",
- Arrays.asList(new String[] {policy.getAccept()}));
+ Arrays.asList(new String[] {policy.getAccept()}));
}
if (policy.isSetAcceptEncoding()) {
headers.put("Accept-Encoding",
- Arrays.asList(new String[] {policy.getAcceptEncoding()}));
+ Arrays.asList(new String[] {policy.getAcceptEncoding()}));
}
if (policy.isSetAcceptLanguage()) {
headers.put("Accept-Language",
- Arrays.asList(new String[] {policy.getAcceptLanguage()}));
+ Arrays.asList(new String[] {policy.getAcceptLanguage()}));
}
if (policy.isSetContentType()) {
headers.put(HttpHeaderHelper.CONTENT_TYPE,
- Arrays.asList(new String[] {policy.getContentType()}));
+ Arrays.asList(new String[] {policy.getContentType()}));
}
if (policy.isSetCookie()) {
headers.put("Cookie",
- Arrays.asList(new String[] {policy.getCookie()}));
+ Arrays.asList(new String[] {policy.getCookie()}));
}
if (policy.isSetBrowserType()) {
headers.put("BrowserType",
- Arrays.asList(new String[] {policy.getBrowserType()}));
+ Arrays.asList(new String[] {policy.getBrowserType()}));
}
if (policy.isSetReferer()) {
headers.put("Referer",
- Arrays.asList(new String[] {policy.getReferer()}));
+ Arrays.asList(new String[] {policy.getReferer()}));
}
}
+ /**
+ * This call places HTTP Header strings into the headers that are relevant
+ * to the polices that are set on this conduit by configuration for the
+ * ClientPolicy and AuthorizationPolicy.
+ *
+ *
+ * @param message The outgoing message.
+ * @param url The URL the message is going to.
+ * @param headers The headers in the outgoing message.
+ */
+ private void setHeadersByPolicy(
+ Message message,
+ URL url,
+ Map<String, List<String>> headers
+ ) {
+ setHeadersByAuthorizationPolicy(message, url, headers);
+ setHeadersByClientPolicy(message, headers);
+ }
+
+ /**
+ * This is part of the Configurable interface which retrieves the
+ * configuration from spring injection.
+ */
+ // REVISIT:What happens when the endpoint/bean name is null?
public String getBeanName() {
if (endpointInfo.getName() != null) {
return endpointInfo.getName().toString() + ".http-conduit";
@@ -543,52 +1031,533 @@
return null;
}
+ /**
+ * This method gets the Authorization Policy that was configured or
+ * explicitly set for this HTTPConduit.
+ */
public AuthorizationPolicy getAuthorization() {
- return authorization;
+ return authorizationPolicy;
}
+ /**
+ * This method is used to set the Authorization Policy for this conduit.
+ * Using this method will override any Authorization Policy set in
+ * configuration.
+ */
public void setAuthorization(AuthorizationPolicy authorization) {
- this.authorization = authorization;
+ this.authorizationPolicy = authorization;
}
public HTTPClientPolicy getClient(Message message) {
- return PolicyUtils.getClient(message, client);
+ return PolicyUtils.getClient(message, clientSidePolicy);
}
+ /**
+ * This method retrieves the Client Side Policy set/configured for this
+ * HTTPConduit.
+ */
public HTTPClientPolicy getClient() {
- return client;
+ return clientSidePolicy;
}
+ /**
+ * This method sets the Client Side Policy for this HTTPConduit. Using this
+ * method will override any HTTPClientPolicy set in configuration.
+ */
public void setClient(HTTPClientPolicy client) {
- this.client = client;
+ this.clientSidePolicy = client;
}
+ /**
+ * This method retrieves the Authorization Policy for a proxy that is
+ * set/configured for this HTTPConduit.
+ */
public AuthorizationPolicy getProxyAuthorization() {
- return proxyAuthorization;
+ return proxyAuthorizationPolicy;
}
+ /**
+ * This method sets the Authorization Policy for a specified proxy.
+ * Using this method overrides any Authorization Policy for the proxy
+ * that is set in the configuration.
+ */
public void setProxyAuthorization(AuthorizationPolicy proxyAuthorization) {
- this.proxyAuthorization = proxyAuthorization;
+ this.proxyAuthorizationPolicy = proxyAuthorization;
}
+ /**
+ * This method returns the SSL Client Side Policy that is set/configured
+ * for this HTTPConduit.
+ */
public SSLClientPolicy getSslClient() {
- return sslClient;
+ return sslClientSidePolicy;
+ }
+
+ /**
+ * This method sets the SSL Client Side Policy for this HTTPConduit.
+ * Using this method overrides any SSL Client Side Policy that is configured
+ * for this HTTPConduit.
+ */
+ public void setSslClient(SSLClientPolicy sslClientPolicy) {
+ this.sslClientSidePolicy = sslClientPolicy;
+ // If this is called after the HTTPTransportFactory called
+ // finalizeConfig, we need to update the connection factory.
+ if (configFinalized) {
+ retrieveConnectionFactory();
+ }
}
- public void setSslClient(SSLClientPolicy sslClient) {
- this.sslClient = sslClient;
- }
+ /**
+ * This method gets the Trust Decider that was set/configured for this
+ * HTTPConduit.
+ * @return The Message Trust Decider or null.
+ */
+ public MessageTrustDecider getTrustDecider() {
+ return this.trustDecider;
+ }
+
+ /**
+ * This method sets the Trust Decider for this HTTP Conduit.
+ * Using this method overrides any trust decider configured for this
+ * HTTPConduit.
+ */
+ public void setTrustDecider(MessageTrustDecider decider) {
+ this.trustDecider = decider;
+ }
+
+ /**
+ * This method gets the Basic Auth Supplier that was set/configured for this
+ * HTTPConduit.
+ * @return The Basic Auth Supplier or null.
+ */
+ public HttpBasicAuthSupplier getBasicAuthSupplier() {
+ return this.basicAuthSupplier;
+ }
+
+ /**
+ * This method sets the Trust Decider for this HTTP Conduit.
+ * Using this method overrides any trust decider configured for this
+ * HTTPConduit.
+ */
+ public void setBasicAuthSupplier(HttpBasicAuthSupplier supplier) {
+ this.basicAuthSupplier = supplier;
+ }
+
+ /**
+ * This function processes any retransmits at the direction of redirections
+ * or "unauthorized" responses.
+ * <p>
+ * If the request was not retransmitted, it returns the given connection.
+ * If the request was retransmitted, it returns the new connection on
+ * which the request was sent.
+ *
+ * @param connection The active URL connection.
+ * @param message The outgoing message.
+ * @param cachedStream The cached request.
+ * @return
+ * @throws IOException
+ */
+ private HttpURLConnection processRetransmit(
+ HttpURLConnection connection,
+ Message message,
+ CachedOutputStream cachedStream
+ ) throws IOException {
+
+ int responseCode = connection.getResponseCode();
+
+ // Process Redirects first.
+ switch(responseCode) {
+ case HttpURLConnection.HTTP_MOVED_PERM:
+ case HttpURLConnection.HTTP_MOVED_TEMP:
+ connection =
+ redirectRetransmit(connection, message, cachedStream);
+ break;
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ connection =
+ authorizationRetransmit(connection, message, cachedStream);
+ break;
+ default:
+ break;
+ }
+ return connection;
+ }
+
+ /**
+ * This method performs a redirection retransmit in response to
+ * a 302 or 305 response code.
+ *
+ * @param connection The active URL connection
+ * @param message The outbound message.
+ * @param cachedStream The cached request.
+ * @return This method returns the new HttpURLConnection if
+ * redirected. If it cannot be redirected for some reason
+ * the same connection is returned.
+ *
+ * @throws IOException
+ */
+ private HttpURLConnection redirectRetransmit(
+ HttpURLConnection connection,
+ Message message,
+ CachedOutputStream cachedStream
+ ) throws IOException {
+
+ // If we are not redirecting by policy, then we don't.
+ if (!getClient().isAutoRedirect()) {
+ return connection;
+ }
+
+ // We keep track of the redirections for redirect loop protection.
+ Set<String> visitedURLs = getSetVisitedURLs(message);
+
+ String lastURL = connection.getURL().toString();
+ visitedURLs.add(lastURL);
+
+ String newURL = extractLocation(connection.getHeaderFields());
+ if (newURL != null) {
+ // See if we are being redirected in a loop as best we can,
+ // using string equality on URL.
+ if (visitedURLs.contains(newURL)) {
+ // We are in a redirect loop; -- bail
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "Redirect loop detected on Conduit \""
+ + getConduitName()
+ + "\" on '"
+ + newURL
+ + "'");
+ }
+ return connection;
+ }
+ // We are going to redirect.
+ // Remove any Server Authentication Information for the previous
+ // URL.
+ Map<String, List<String>> headers =
+ getSetProtocolHeaders(message);
+ headers.remove("Authorization");
+ headers.remove("Proxy-Authorization");
+
+ URL url = new URL(newURL);
+
+ // If user configured this Conduit with preemptive authorization
+ // it is meant to make it to the end. (Too bad that information
+ // went to every URL along the way, but that's what the user
+ // wants!
+ // TODO: Make this issue a security release note.
+ setHeadersByAuthorizationPolicy(message, url, headers);
+
+ connection = retransmit(
+ connection, url, message, cachedStream);
+ }
+ return connection;
+ }
+
+ /**
+ * This function gets the Set of URLs on the message that is used to
+ * keep track of the URLs that were used in getting authorization
+ * information.
+ *
+ * @param message The message where the Set of URLs is stored.
+ * @return The modifiable set of URLs that were visited.
+ */
+ private Set<String> getSetAuthoriationURLs(Message message) {
+ @SuppressWarnings("unchecked")
+ Set<String> authURLs = (Set<String>) message.get(KEY_AUTH_URLS);
+ if (authURLs == null) {
+ authURLs = new HashSet<String>();
+ message.put(KEY_AUTH_URLS, authURLs);
+ }
+ return authURLs;
+ }
+
+ /**
+ * This function get the set of URLs on the message that is used to keep
+ * track of the URLs that were visited in redirects.
+ *
+ * If it is not set on the message, an new empty set is stored.
+ * @param message The message where the Set is stored.
+ * @return The modifiable set of URLs that were visited.
+ */
+ private Set<String> getSetVisitedURLs(Message message) {
+ @SuppressWarnings("unchecked")
+ Set<String> visitedURLs = (Set<String>) message.get(KEY_VISITED_URLS);
+ if (visitedURLs == null) {
+ visitedURLs = new HashSet<String>();
+ message.put(KEY_VISITED_URLS, visitedURLs);
+ }
+ return visitedURLs;
+ }
+
+ /**
+ * This method performs a retransmit for authorization information.
+ *
+ * @param connection The currently active connection.
+ * @param message The outbound message.
+ * @param cachedStream The cached request.
+ * @return A new connection if retransmitted. If not retransmitted
+ * then this method returns the same connection.
+ * @throws IOException
+ */
+ private HttpURLConnection authorizationRetransmit(
+ HttpURLConnection connection,
+ Message message,
+ CachedOutputStream cachedStream
+ ) throws IOException {
+
+ // If we don't have a dynamic supply of user pass, then
+ // we don't retransmit. We just die with a Http 401 response.
+ if (basicAuthSupplier == null) {
+ return connection;
+ }
+
+ URL currentURL = connection.getURL();
+
+ String realm = extractAuthorizationRealm(connection.getHeaderFields());
+
+ Set<String> authURLs = getSetAuthoriationURLs(message);
+
+ // If we have been here (URL & Realm) before for this particular message
+ // retransmit, it means we have already supplied information
+ // which must have been wrong, or we wouldn't be here again.
+ // Otherwise, the server may be 401 looping us around the realms.
+ if (authURLs.contains(currentURL.toString() + realm)) {
+
+ if (LOG.isLoggable(Level.INFO)) {
+ LOG.log(Level.INFO, "Authorization loop detected on Conduit \""
+ + getConduitName()
+ + "\" on URL \""
+ + "\" with realm \""
+ + realm
+ + "\"");
+ }
+
+ return connection;
+ }
+
+ HttpBasicAuthSupplier.UserPass up =
+ basicAuthSupplier.getUserPassForRealm(
+ getConduitName(), currentURL, message, realm);
+
+ // No user pass combination. We give up.
+ if (up == null) {
+ return connection;
+ }
+
+ // Register that we have been here before we go.
+ authURLs.add(currentURL.toString() + realm);
+
+ Map<String, List<String>> headers = getSetProtocolHeaders(message);
+
+ setBasicAuthHeader(up.getUserid(), up.getPassword(), headers);
+
+ return retransmit(
+ connection, currentURL, message, cachedStream);
+ }
+
+ /**
+ * This method retransmits the request.
+ *
+ * @param connection The currently active connection.
+ * @param newURL The newURL to connection to.
+ * @param message The outbound message.
+ * @param stream The cached request.
+ * @return This function returns a new connection if
+ * retransmitted, otherwise it returns the given
+ * connection.
+ *
+ * @throws IOException
+ */
+ private HttpURLConnection retransmit(
+ HttpURLConnection connection,
+ URL newURL,
+ Message message,
+ CachedOutputStream stream
+ ) throws IOException {
+
+ // Disconnect the old, and in with the new.
+ connection.disconnect();
+
+ connection =
+ connectionFactory.createConnection(
+ getProxy(clientSidePolicy), newURL);
+
+ connection.setDoOutput(true);
+ // TODO: using Message context to deceided HTTP send properties
+ connection.setConnectTimeout((int)getClient().getConnectionTimeout());
+ connection.setReadTimeout((int)getClient().getReceiveTimeout());
+ connection.setUseCaches(false);
+ connection.setInstanceFollowRedirects(false);
+
+ // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
+ String httpRequestMethod =
+ (String)message.get(Message.HTTP_REQUEST_METHOD);
+
+ if (null != httpRequestMethod) {
+ connection.setRequestMethod(httpRequestMethod);
+ } else {
+ connection.setRequestMethod("POST");
+ }
+ message.put(KEY_HTTP_CONNECTION, connection);
+
+ // Need to set the headers before the trust decision
+ // because they are set before the connect().
+ setURLRequestHeaders(message);
+
+ //
+ // This point is where the trust decision is made because the
+ // Sun implementation of URLConnection will not let us
+ // set/addRequestProperty after a connect() call, and
+ // makeTrustDecision needs to make a connect() call to
+ // make sure the proper information is available.
+ //
+ makeTrustDecision(message);
+
+ // If this is a GET method we must not touch the output
+ // stream as this automagically turns the request into a POST.
+ if (connection.getRequestMethod().equals("GET")) {
+ return connection;
+ }
+
+ // Trust is okay, write the cached request.
+ OutputStream out = connection.getOutputStream();
+
+ CachedOutputStream.copyStream(stream.getInputStream(), out, 2048);
+ out.close();
+
+ if (LOG.isLoggable(Level.FINE)) {
+ StringBuffer sbuf = new StringBuffer();
+ StringBufferOutputStream sout =
+ new StringBufferOutputStream(sbuf);
+ CachedOutputStream.copyStream(stream.getInputStream(),
+ sout, 2048);
+ sout.close();
+
+ LOG.fine("Conduit \""
+ + getConduitName()
+ + "\" Retransmit message to: "
+ + connection.getURL()
+ + ": "
+ + sbuf);
+ }
+ return connection;
+ }
+
+ /**
+ * This function extracts the authorization realm from the
+ * "WWW-Authenticate" Http response header.
+ *
+ * @param headers The Http Response Headers
+ * @return The realm, or null if it is non-existent.
+ */
+ private String extractAuthorizationRealm(
+ Map<String, List<String>> headers
+ ) {
+ List<String> auth = headers.get("WWW-Authenticate");
+ if (auth != null) {
+ for (String a : auth) {
+ if (a.startsWith("Basic realm=")) {
+ return a.substring(a.indexOf("=") + 1);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method extracts the value of the "Location" Http
+ * Response header.
+ *
+ * @param headers The Http response headers.
+ * @return The value of the "Location" header, null if non-existent.
+ */
+ private String extractLocation(
+ Map<String, List<String>> headers
+ ) {
+ List<String> locs = headers.get("Location");
+ if (locs != null && locs.size() > 0) {
+ return locs.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * This procedure sets the "Authorization" header with the
+ * BasicAuth token, which is Base64 encoded.
+ *
+ * @param userid The user's id, which cannot be null.
+ * @param password The password, it may be null.
+ *
+ * @param headers The headers map that gets the "Authorization" header set.
+ */
+ private void setBasicAuthHeader(
+ String userid,
+ String password,
+ Map<String, List<String>> headers
+ ) {
+ String userpass = userid;
+
+ userpass += ":";
+ if (password != null) {
+ userpass += password;
+ }
+ String token = Base64Utility.encode(userpass.getBytes());
+ headers.put("Authorization",
+ Arrays.asList(new String[] {"Basic " + token}));
+ }
+
+ /**
+ * This procedure sets the "ProxyAuthorization" header with the
+ * BasicAuth token, which is Base64 encoded.
+ *
+ * @param userid The user's id, which cannot be null.
+ * @param password The password, it may be null.
+ *
+ * @param headers The headers map that gets the "Proxy-Authorization"
+ * header set.
+ */
+ private void setProxyBasicAuthHeader(
+ String userid,
+ String password,
+ Map<String, List<String>> headers
+ ) {
+ String userpass = userid;
+
+ userpass += ":";
+ if (password != null) {
+ userpass += password;
+ }
+ String token = Base64Utility.encode(userpass.getBytes());
+ headers.put("Proxy-Authorization",
+ Arrays.asList(new String[] {"Basic " + token}));
+ }
/**
* Wrapper output stream responsible for flushing headers and handling
* the incoming HTTP-level response (not necessarily the MEP response).
*/
private class WrappedOutputStream extends AbstractWrappedOutputStream {
- protected URLConnection connection;
+ /**
+ * This field contains the currently active connection.
+ */
+ private HttpURLConnection connection;
+
+ /**
+ * This boolean is true if the request must be cached.
+ */
+ private boolean cachingForRetransmision;
- WrappedOutputStream(Message m, URLConnection c) {
+ /**
+ * This field contains the output stream with which we cache
+ * the request. It maybe null if we are not caching.
+ */
+ private CachedOutputStream cachedStream;
+
+ WrappedOutputStream(
+ Message m,
+ HttpURLConnection c,
+ boolean possibleRetransmit
+ ) {
super(m);
connection = c;
+ cachingForRetransmision = possibleRetransmit;
}
/**
@@ -596,15 +1565,40 @@
* reset output stream ... etc.)
*/
protected void doFlush() throws IOException {
- if (!alreadyFlushed()) {
- flushHeaders(outMessage);
- if (connection instanceof HttpURLConnection) {
- HttpURLConnection hc = (HttpURLConnection)connection;
- if (hc.getRequestMethod().equals("GET")) {
- return;
- }
+ if (!alreadyFlushed()) {
+
+ // Need to set the headers before the trust decision
+ // because they are set before the connect().
+ setURLRequestHeaders(outMessage);
+
+ //
+ // This point is where the trust decision is made because the
+ // Sun implementation of URLConnection will not let us
+ // set/addRequestProperty after a connect() call, and
+ // makeTrustDecision needs to make a connect() call to
+ // make sure the proper information is available.
+ //
+ makeTrustDecision(outMessage);
+
+ // Trust is okay, set up for writing the request.
+
+ // If this is a GET method we must not touch the output
+ // stream as this automagically turns the reqest into a POST.
+ if (connection.getRequestMethod().equals("GET")) {
+ return;
+ }
+
+ // This replaces the AbstractCachedOutputStream.currentStream
+ // with the connection's output stream directly presumably
+ // to forgoe copying. If we are caching this output, then
+ // we need to cache the output stream here.
+ if (cachingForRetransmision) {
+ cachedStream =
+ new CachedOutputStream(connection.getOutputStream());
+ resetOut(cachedStream, true);
+ } else {
+ resetOut(connection.getOutputStream(), true);
}
- resetOut(connection.getOutputStream(), true);
}
}
@@ -618,10 +1612,87 @@
protected void onWrite() throws IOException {
}
+
+ /**
+ * This procedure handles all retransmits, if any.
+ *
+ * @throws IOException
+ */
+ private void handleRetransmits() throws IOException {
+ // If we have a cachedStream, we are caching the request.
+ if (cachedStream != null) {
+
+ if (LOG.isLoggable(Level.FINE)) {
+ StringBuffer sbuf = new StringBuffer();
+ StringBufferOutputStream sout =
+ new StringBufferOutputStream(sbuf);
+ CachedOutputStream.copyStream(cachedStream.getInputStream(),
+ sout, 2048);
+ sout.close();
+
+ LOG.fine("Conduit \""
+ + getConduitName()
+ + "\" Transmit cached message to: "
+ + connection.getURL()
+ + ": "
+ + sbuf);
+ }
+ HttpURLConnection oldcon = connection;
+
+ HTTPClientPolicy policy = getClient();
+
+ // Default MaxRetransmits is -1 which means unlimited.
+ int maxRetransmits = (policy == null)
+ ? -1
+ : policy.getMaxRetransmits();
+
+ // MaxRetransmits of zero means zero.
+ if (maxRetransmits == 0) {
+ return;
+ }
+
+ int nretransmits = 0;
+
+ connection =
+ processRetransmit(connection, outMessage, cachedStream);
+
+ while (connection != oldcon) {
+ nretransmits++;
+ oldcon = connection;
+
+ // A negative max means unlimited.
+ if (maxRetransmits < 0 || nretransmits < maxRetransmits) {
+ connection =
+ processRetransmit(
+ connection, outMessage, cachedStream);
+ }
+ }
+ }
+ }
+
+ /**
+ * This procedure is called on the close of the output stream so
+ * we are ready to handle the response from the connection.
+ * We may retransmit until we finally get a response.
+ *
+ * @throws IOException
+ */
private void handleResponse() throws IOException {
+
+ // Process retransmits until we fall out.
+ handleRetransmits();
+
+ int responseCode = connection.getResponseCode();
+
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.fine("Response Code: "
+ + responseCode
+ + " Conduit: " + getConduitName());
+ }
+
Exchange exchange = outMessage.getExchange();
- int responseCode = getResponseCode(connection);
+
if (isOneway(exchange)
&& !isPartialResponse(connection, responseCode)) {
// oneway operation without partial response
@@ -632,24 +1703,24 @@
Message inMessage = new MessageImpl();
inMessage.setExchange(exchange);
InputStream in = null;
- Map<String, List<String>> headers = new HashMap<String, List<String>>();
+ Map<String, List<String>> headers =
+ new HashMap<String, List<String>>();
for (String key : connection.getHeaderFields().keySet()) {
- headers.put(HttpHeaderHelper.getHeaderKey(key), connection.getHeaderFields().get(key));
+ headers.put(HttpHeaderHelper.getHeaderKey(key),
+ connection.getHeaderFields().get(key));
}
inMessage.put(Message.PROTOCOL_HEADERS, headers);
inMessage.put(Message.RESPONSE_CODE, responseCode);
- inMessage.put(Message.CONTENT_TYPE, connection.getHeaderField(HttpHeaderHelper.CONTENT_TYPE));
+ inMessage.put(Message.CONTENT_TYPE,
+ connection.getHeaderField(HttpHeaderHelper.CONTENT_TYPE));
- if (connection instanceof HttpURLConnection) {
- HttpURLConnection hc = (HttpURLConnection)connection;
- in = hc.getErrorStream();
- if (null == in) {
- in = connection.getInputStream();
- }
- } else {
+ in = connection.getErrorStream();
+ if (null == in) {
in = connection.getInputStream();
}
-
+ if (in == null) {
+ LOG.log(Level.WARNING, "Input Stream is null!");
+ }
inMessage.setContent(InputStream.class, in);
incomingObserver.onMessage(inMessage);
@@ -674,7 +1745,7 @@
inMessage.put(DECOUPLED_CHANNEL_MESSAGE, Boolean.TRUE);
// REVISIT: how to get response headers?
//inMessage.put(Message.PROTOCOL_HEADERS, req.getXXX());
- setHeaders(inMessage);
+ getSetProtocolHeaders(inMessage);
inMessage.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
// remove server-specific properties
@@ -687,7 +1758,7 @@
}
public void assertMessage(Message message) {
- PolicyUtils.assertClientPolicy(message, client);
+ PolicyUtils.assertClientPolicy(message, clientSidePolicy);
}
public boolean canAssert(QName type) {
Added: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpBasicAuthSupplier.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpBasicAuthSupplier.java?view=auto&rev=529583
==============================================================================
--- incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpBasicAuthSupplier.java (added)
+++ incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpBasicAuthSupplier.java Tue Apr 17 05:47:13 2007
@@ -0,0 +1,175 @@
+/**
+ * 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.cxf.transport.http;
+
+import java.net.URL;
+
+import org.apache.cxf.message.Message;
+
+/**
+ * This abstract class is extended by developers who need HTTP Basic Auth
+ * functionality on the client side. It supplies userid and password
+ * combinations to an HTTPConduit.
+ * <p>
+ * The HTTPConduit will make a call to getPreemptiveUserPass before
+ * an HTTP request is made. The HTTPConduit will call on
+ * getUserPassForRealm upon getting a 401 HTTP Response with a
+ * "WWW-Authenticate: Basic realm=????" header.
+ * <p>
+ * A HTTPConduit keeps a reference to this HttpBasicAuthSupplier for the life
+ * of the HTTPConduit, unless changed out by dynamic configuration.
+ * Therefore, an implementation of this HttpBasicAuthSupplier may maintain
+ * state for subsequent calls.
+ * <p>
+ * For instance, an implemenation may not provide a UserPass preemptively for
+ * a particular URL and decide to get the realm information from
+ * a 401 response in which the HTTPConduit will call getUserPassForReam for
+ * that URL. Then this implementation may provide the UserPass for this
+ * particular URL preemptively for subsequent calls to getPreemptiveUserPass.
+ */
+public abstract class HttpBasicAuthSupplier {
+
+ /**
+ * This field contains the logical name of this HttpBasicAuthSuppler.
+ * This field is not assigned to be final, since an extension may be
+ * Spring initialized as a bean, have an appropriate setLogicalName
+ * method, and set this field.
+ */
+ protected String logicalName;
+
+ /**
+ * The default constructor assigns the class name as the LogicalName.
+ *
+ */
+ protected HttpBasicAuthSupplier() {
+ logicalName = this.getClass().getName();
+ }
+
+ /**
+ * This constructor assigns the LogicalName of this HttpBasicAuthSupplier.
+ *
+ * @param name The Logical Name.
+ */
+ protected HttpBasicAuthSupplier(String name) {
+ logicalName = name;
+ }
+
+ /**
+ * This method returns the LogicalName of this HttpBasicAuthSupplier.
+ */
+ public String getLogicalName() {
+ return logicalName;
+ }
+
+ /**
+ * This class is used to return the values of the
+ * userid and password used in the HTTP Authorization
+ * Header.
+ */
+ public static final class UserPass {
+ private final String userid;
+ private final String password;
+
+ /**
+ * This constructor forms the userid and password pair for
+ * the HTTP Authorization header.
+ *
+ * @param user The userid that will be returned from getUserid().
+ * This argument must not contain a colon (":"). If
+ * it does, it will throw an IllegalArgumentException.
+ *
+ * @param pass The password that will be returned from getPassword().
+ */
+ UserPass(String user, String pass) {
+ if (user.contains(":")) {
+ throw new IllegalArgumentException(
+ "The argument \"user\" cannot contain ':'.");
+ }
+ userid = user;
+ password = pass;
+ }
+ /**
+ * This method returns the userid.
+ */
+ public String getUserid() {
+ return userid;
+ }
+ /**
+ * This method returns the password.
+ */
+ public String getPassword() {
+ return password;
+ }
+ }
+
+ /**
+ * This method is used by extensions of this class to create
+ * a UserPass to return.
+ * @param userid The userid that will be returned from getUserid().
+ * This argument must not contain a colon (":"). If
+ * it does, it will throw an IllegalArgumentException.
+ * @param password The password that will be returned from getPassword().
+ * @return
+ */
+ protected UserPass createUserPass(
+ final String userid,
+ final String password
+ ) {
+ return new UserPass(userid, password);
+ }
+ /**
+ * The HTTPConduit makes a call to this method before connecting
+ * to the server behind a particular URL. If this implementation does not
+ * have a UserPass for this URL, it should return null.
+ *
+ * @param conduitName The HTTPConduit making the call.
+ * @param currentURL The URL to which the request is to be made.
+ * @param message The CXF Message.
+ *
+ * @return This method returns null if no UserPass is available.
+ */
+ public abstract UserPass getPreemptiveUserPass(
+ String conduitName,
+ URL currentURL,
+ Message message);
+
+ /**
+ * The HTTPConduit makes a call to this method if it
+ * receives a 401 response to a particular URL for
+ * a given message. The realm information is taken
+ * from the "WWW-Authenticate: Basic realm=?????"
+ * header. The current message may be retransmitted
+ * if this call returns a UserPass. The current message will
+ * fail with a 401 if null is returned. If no UserPass is available
+ * for this particular URL, realm, and message, then null
+ * should be returned.
+ *
+ * @param conduitName The name of the conduit making the call.
+ * @param currentURL The current URL from which the reponse came.
+ * @param message The CXF Message.
+ * @param realm The realm extraced from the basic auth header.
+ * @return
+ */
+ public abstract UserPass getUserPassForRealm(
+ String conduitName,
+ URL currentURL,
+ Message message,
+ String realm);
+}
Propchange: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpBasicAuthSupplier.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactory.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactory.java?view=auto&rev=529583
==============================================================================
--- incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactory.java (added)
+++ incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactory.java Tue Apr 17 05:47:13 2007
@@ -0,0 +1,54 @@
+/**
+ * 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.cxf.transport.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+
+/**
+ * The primary purpose for this interface is to generate HttpURLConnections
+ * and retrieve information about the connections. This interface is also
+ * meant to be used as a lower denominator for HttpURLConnections and
+ * HttpsURLConnections.
+ */
+public interface HttpURLConnectionFactory {
+
+ /**
+ * Create an HttpURLConnection, proxified if neccessary.
+ *
+ * @param proxy The proxy. May be null if connection is not to be proxied.
+ * @param url The target URL
+ * @return An appropriate URLConnection
+ */
+ HttpURLConnection createConnection(Proxy proxy, URL url) throws IOException;
+
+ /**
+ * This method returns Connection Info objects for the particular
+ * connection. The connection must be connected.
+ * @param con The connection that is the subject of the information object.
+ * @return The HttpURLConnection Info for the given connection.
+ * @throws IOException
+ */
+ HttpURLConnectionInfo getConnectionInfo(
+ HttpURLConnection connnection
+ ) throws IOException;
+}
Propchange: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactoryImpl.java?view=auto&rev=529583
==============================================================================
--- incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactoryImpl.java (added)
+++ incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactoryImpl.java Tue Apr 17 05:47:13 2007
@@ -0,0 +1,66 @@
+/**
+ * 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.cxf.transport.http;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+
+/**
+ * This class is a URLConnectionFactory that creates URLConnections
+ * for the HTTP protocol.
+ *
+ */
+public class HttpURLConnectionFactoryImpl implements HttpURLConnectionFactory {
+
+ public static final String HTTP_URL_PROTOCOL_ID = "http";
+
+ /**
+ * This call creates an URLConnection for an HTTP url.
+ * @throws IOException if the url protocol is not "http".
+ */
+ public HttpURLConnection createConnection(Proxy proxy, URL url)
+ throws IOException {
+
+ if (!url.getProtocol().equals(HTTP_URL_PROTOCOL_ID)) {
+ throw new IOException("Illegal Protocol "
+ + url.getProtocol()
+ + " for HTTP URLConnection Factory.");
+ }
+ if (proxy != null) {
+ return (HttpURLConnection) url.openConnection(proxy);
+ } else {
+ return (HttpURLConnection) url.openConnection();
+ }
+ }
+
+ /**
+ * This operation returns the HttpURLConnectionInfo object that
+ * represents the HttpURLConnection.
+ */
+ public HttpURLConnectionInfo getConnectionInfo(
+ HttpURLConnection connection
+ ) throws IOException {
+ // There is no special information other than the URL
+ // to represent for an HttpURLConnection.
+ return new HttpURLConnectionInfo(connection);
+ }
+}
Propchange: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionFactoryImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionInfo.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionInfo.java?view=auto&rev=529583
==============================================================================
--- incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionInfo.java (added)
+++ incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionInfo.java Tue Apr 17 05:47:13 2007
@@ -0,0 +1,49 @@
+/**
+ * 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.cxf.transport.http;
+
+import java.net.HttpURLConnection;
+
+/**
+ * This class contains the information about the HTTP Connection that
+ * will be making an HTTP request. This class should be used
+ * when the getURL().getProtocol() is "http" or "https".
+ */
+public class HttpURLConnectionInfo extends URLConnectionInfo {
+
+ private final String httpRequestMethod;
+
+ /**
+ * This constructor takes the HttpURLConnection and extracts
+ * the httpRequestMethod.
+ */
+ public HttpURLConnectionInfo(HttpURLConnection con) {
+ super(con);
+ httpRequestMethod = con.getRequestMethod();
+ }
+
+ /**
+ * This method returns the request method on the represented
+ * HttpURLConnection.
+ */
+ public String getHttpRequestMethod() {
+ return httpRequestMethod;
+ }
+}
Propchange: incubator/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpURLConnectionInfo.java
------------------------------------------------------------------------------
svn:eol-style = native