You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by cs...@apache.org on 2010/11/22 00:38:59 UTC

svn commit: r1037576 - in /cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http: ChunkedUtil.java Cookie.java Cookies.java HTTPConduit.java Headers.java TrustDecisionUtil.java

Author: cschneider
Date: Sun Nov 21 23:38:58 2010
New Revision: 1037576

URL: http://svn.apache.org/viewvc?rev=1037576&view=rev
Log:
CXF-3144 Simplify HttpConduit by factoring out header, cookie and trust handling

Added:
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java   (with props)
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java   (with props)
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java   (with props)
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java   (with props)
Modified:
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookie.java
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java?rev=1037576&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java Sun Nov 21 23:38:58 2010
@@ -0,0 +1,98 @@
+/**
+ * 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.io.InputStream;
+import java.io.PushbackInputStream;
+import java.net.HttpURLConnection;
+
+import org.apache.cxf.helpers.HttpHeaderHelper;
+
+final class ChunkedUtil {
+    private ChunkedUtil() {
+    }
+    
+    /**
+     * Get an input stream containing the partial response if one is present.
+     * 
+     * @param connection the connection in question
+     * @param responseCode the response code
+     * @return an input stream if a partial response is pending on the connection 
+     */
+    public static InputStream getPartialResponse(
+        HttpURLConnection connection,
+        int responseCode
+    ) throws IOException {
+        InputStream in = null;
+        if (responseCode == HttpURLConnection.HTTP_ACCEPTED
+            || responseCode == HttpURLConnection.HTTP_OK) {
+            if (connection.getContentLength() > 0) {
+                in = connection.getInputStream();
+            } else if (hasChunkedResponse(connection) 
+                       || hasEofTerminatedResponse(connection)) {
+                // ensure chunked or EOF-terminated response is non-empty
+                in = getNonEmptyContent(connection);        
+            }
+        }
+        return in;
+    }
+    
+    /**
+     * @param connection the given HttpURLConnection
+     * @return true iff the connection has a chunked response pending
+     */
+    private static boolean hasChunkedResponse(HttpURLConnection connection) {
+        return HttpHeaderHelper.CHUNKED.equalsIgnoreCase(
+                   connection.getHeaderField(HttpHeaderHelper.TRANSFER_ENCODING));
+    }
+    
+    /**
+     * @param connection the given HttpURLConnection
+     * @return true iff the connection has a chunked response pending
+     */
+    private static boolean hasEofTerminatedResponse(
+        HttpURLConnection connection
+    ) {
+        return HttpHeaderHelper.CLOSE.equalsIgnoreCase(
+                   connection.getHeaderField(HttpHeaderHelper.CONNECTION));
+    }
+
+    /**
+     * @param connection the given HttpURLConnection
+     * @return an input stream containing the response content if non-empty
+     */
+    private static InputStream getNonEmptyContent(
+        HttpURLConnection connection
+    ) {
+        InputStream in = null;
+        try {
+            PushbackInputStream pin = 
+                new PushbackInputStream(connection.getInputStream());
+            int c = pin.read();
+            if (c != -1) {
+                pin.unread((byte)c);
+                in = pin;
+            }
+        } catch (IOException ioe) {
+            // ignore
+        }    
+        return in;
+    }
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ChunkedUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookie.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookie.java?rev=1037576&r1=1037575&r2=1037576&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookie.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookie.java Sun Nov 21 23:38:58 2010
@@ -18,8 +18,6 @@
  */
 package org.apache.cxf.transport.http;
 
-import java.util.List;
-import java.util.Map;
 
 /**
  * Container for HTTP cookies used to track
@@ -158,55 +156,4 @@ class Cookie {
         }
         return b.toString();
     }
-    
-    /**
-     * Given a list of current cookies and a new Set-Cookie: request, construct
-     * a new set of current cookies and return it.
-     * @param current Set of previously set cookies
-     * @param header Text of a Set-Cookie: header
-     * @return New set of cookies
-     */
-    public static void handleSetCookie(Map<String, Cookie> current, List<String> headers) {
-        if (headers == null || headers.size() == 0) {
-            return;
-        }
-        
-
-        for (String header : headers) {
-            String[] cookies = header.split(",");
-            for (String cookie : cookies) {
-                String[] parts = cookie.split(";");
-    
-                String[] kv = parts[0].split("=", 2);
-                if (kv.length != 2) {
-                    continue;
-                }
-                String name = kv[0].trim();
-                String value = kv[1].trim();
-                Cookie newCookie = new Cookie(name, value);
-    
-                for (int i = 1; i < parts.length; i++) {
-                    kv = parts[i].split("=", 2);
-                    name = kv[0].trim();
-                    value = (kv.length > 1) ? kv[1].trim() : null;
-                    if (name.equalsIgnoreCase(DISCARD_ATTRIBUTE)) {
-                        newCookie.setMaxAge(0);
-                    } else if (name.equalsIgnoreCase(MAX_AGE_ATTRIBUTE) && value != null) {
-                        try {
-                            newCookie.setMaxAge(Integer.parseInt(value));
-                        } catch (NumberFormatException e) {
-                            // do nothing here
-                        }
-                    } else if (name.equalsIgnoreCase(PATH_ATTRIBUTE) && value != null) {
-                        newCookie.setPath(value);
-                    }
-                }
-                if (newCookie.getMaxAge() != 0) {
-                    current.put(newCookie.getName(), newCookie);                    
-                } else {
-                    current.remove(newCookie.getName());
-                }
-            }
-        }
-    }
 }

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java?rev=1037576&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java Sun Nov 21 23:38:58 2010
@@ -0,0 +1,108 @@
+/**
+ * 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;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.cxf.message.Message;
+
+public class Cookies {
+    /**
+     * Variables for holding session state if sessions are supposed to be maintained
+     */
+    private final Map<String, Cookie> sessionCookies = new ConcurrentHashMap<String, Cookie>();
+    private boolean maintainSession;
+    
+    public Map<String, Cookie> getSessionCookies() {
+        return sessionCookies;
+    }
+    
+    public void readFromConnection(HttpURLConnection connection) {
+        if (maintainSession) {
+            for (Map.Entry<String, List<String>> h : connection.getHeaderFields().entrySet()) {
+                if ("Set-Cookie".equalsIgnoreCase(h.getKey())) {
+                    handleSetCookie(h.getValue());
+                }
+            }
+        }
+    }
+    
+    public void writeToMessageHeaders(Message message) {
+        //Do we need to maintain a session?
+        maintainSession = Boolean.TRUE.equals((Boolean)message.get(Message.MAINTAIN_SESSION));
+
+        //If we have any cookies and we are maintaining sessions, then use them        
+        if (maintainSession && sessionCookies.size() > 0) {
+            new Headers(message).writeSessionCookies(sessionCookies);
+        }
+    }
+
+    /**
+     * Given a list of current cookies and a new Set-Cookie: request, construct
+     * a new set of current cookies and return it.
+     * @param current Set of previously set cookies
+     * @param header Text of a Set-Cookie: header
+     * @return New set of cookies
+     */
+    private void handleSetCookie(List<String> headers) {
+        if (headers == null || headers.size() == 0) {
+            return;
+        }
+    
+        for (String header : headers) {
+            String[] cookies = header.split(",");
+            for (String cookie : cookies) {
+                String[] parts = cookie.split(";");
+    
+                String[] kv = parts[0].split("=", 2);
+                if (kv.length != 2) {
+                    continue;
+                }
+                String name = kv[0].trim();
+                String value = kv[1].trim();
+                Cookie newCookie = new Cookie(name, value);
+    
+                for (int i = 1; i < parts.length; i++) {
+                    kv = parts[i].split("=", 2);
+                    name = kv[0].trim();
+                    value = (kv.length > 1) ? kv[1].trim() : null;
+                    if (name.equalsIgnoreCase(Cookie.DISCARD_ATTRIBUTE)) {
+                        newCookie.setMaxAge(0);
+                    } else if (name.equalsIgnoreCase(Cookie.MAX_AGE_ATTRIBUTE) && value != null) {
+                        try {
+                            newCookie.setMaxAge(Integer.parseInt(value));
+                        } catch (NumberFormatException e) {
+                            // do nothing here
+                        }
+                    } else if (name.equalsIgnoreCase(Cookie.PATH_ATTRIBUTE) && value != null) {
+                        newCookie.setPath(value);
+                    }
+                }
+                if (newCookie.getMaxAge() != 0) {
+                    sessionCookies.put(newCookie.getName(), newCookie);                    
+                } else {
+                    sessionCookies.remove(newCookie.getName());
+                }
+            }
+        }
+    }
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Cookies.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java?rev=1037576&r1=1037575&r2=1037576&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java Sun Nov 21 23:38:58 2010
@@ -24,22 +24,17 @@ import java.beans.PropertyChangeListener
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.PushbackInputStream;
 import java.net.HttpRetryException;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.Proxy;
 import java.net.URL;
 import java.net.URLConnection;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -55,7 +50,6 @@ import org.apache.cxf.configuration.jsse
 import org.apache.cxf.configuration.security.AuthorizationPolicy;
 import org.apache.cxf.configuration.security.CertificateConstraintsType;
 import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
-import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.helpers.HttpHeaderHelper;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
@@ -76,9 +70,7 @@ import org.apache.cxf.transport.https.Ce
 import org.apache.cxf.transport.https.CertConstraintsInterceptor;
 import org.apache.cxf.transport.https.CertConstraintsJaxBUtils;
 import org.apache.cxf.transport.https.HttpsURLConnectionFactory;
-import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
-import org.apache.cxf.version.Version;
 import org.apache.cxf.workqueue.WorkQueueManager;
 import org.apache.cxf.ws.addressing.EndpointReferenceType;
 import org.apache.cxf.ws.policy.Assertor;
@@ -249,11 +241,7 @@ public class HTTPConduit 
      */
     private HttpAuthSupplier authSupplier;
 
-    /**
-     * Variables for holding session state if sessions are supposed to be maintained
-     */
-    private Map<String, Cookie> sessionCookies = new ConcurrentHashMap<String, Cookie>();
-    private boolean maintainSession;
+    private Cookies cookies;
     
     private CertConstraints certConstraints;
 
@@ -291,6 +279,7 @@ public class HTTPConduit 
         }
         proxyFactory = new ProxyFactory();
         connectionFactory = new HttpsURLConnectionFactory();
+        cookies = new Cookies();
         
         // wsdl extensors are superseded by policies which in        
         // turn are superseded by injection                          
@@ -419,7 +408,7 @@ public class HTTPConduit 
      * @return the sessionCookies map
      */
     public Map<String, Cookie> getCookies() {
-        return sessionCookies;
+        return cookies.getSessionCookies();
     }
     
     private HttpURLConnection createConnection(Message message, URL url) throws IOException {
@@ -450,8 +439,6 @@ public class HTTPConduit 
      * @param message The message to be sent.
      */
     public void prepare(Message message) throws IOException {
-        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.
@@ -464,38 +451,12 @@ public class HTTPConduit 
         HttpURLConnection connection = createConnection(message, currentURL);
         connection.setDoOutput(true);       
         
-        long timeout = csPolicy.getConnectionTimeout();
-        if (message.get(Message.CONNECTION_TIMEOUT) != null) {
-            Object obj = message.get(Message.CONNECTION_TIMEOUT);
-            try {
-                timeout = Long.parseLong(obj.toString());
-            } catch (NumberFormatException e) {
-                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
-                    Message.CONNECTION_TIMEOUT, obj.toString()
-                });
-            }
-        }
-        if (timeout > Integer.MAX_VALUE) {
-            timeout = Integer.MAX_VALUE;
-        }
-
-        connection.setConnectTimeout((int)timeout);
+        int ctimeout = determineConnectionTimeout(message, csPolicy);
+        connection.setConnectTimeout(ctimeout);
+        
+        int rtimeout = determineReceiveTimeout(message, csPolicy);
+        connection.setReadTimeout(rtimeout);
         
-        timeout = csPolicy.getReceiveTimeout();
-        if (message.get(Message.RECEIVE_TIMEOUT) != null) {
-            Object obj = message.get(Message.RECEIVE_TIMEOUT);
-            try {
-                timeout = Long.parseLong(obj.toString());
-            } catch (NumberFormatException e) {
-                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
-                    Message.RECEIVE_TIMEOUT, obj.toString()
-                });
-            }
-        }
-        if (timeout > Integer.MAX_VALUE) {
-            timeout = Integer.MAX_VALUE;
-        }
-        connection.setReadTimeout((int)timeout);
         connection.setUseCaches(false);
         // We implement redirects in this conduit. We do not
         // rely on the underlying URLConnection implementation
@@ -505,12 +466,7 @@ public class HTTPConduit 
         // 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");
-        }
+        connection.setRequestMethod((null != httpRequestMethod) ? httpRequestMethod : "POST");
                 
         boolean isChunking = false;
         int chunkThreshold = 0;
@@ -552,55 +508,70 @@ public class HTTPConduit 
             }
         }
 
-        //Do we need to maintain a session?
-        maintainSession = Boolean.TRUE.equals((Boolean)message.get(Message.MAINTAIN_SESSION));
-        
-        //If we have any cookies and we are maintaining sessions, then use them        
-        if (maintainSession && sessionCookies.size() > 0) {
-            List<String> cookies = null;
-            for (String s : headers.keySet()) {
-                if (HttpHeaderHelper.COOKIE.equalsIgnoreCase(s)) {
-                    cookies = headers.remove(s);
-                    break;
-                }
-            }
-            if (cookies == null) {
-                cookies = new ArrayList<String>();
-            } else {
-                cookies = new ArrayList<String>(cookies);
-            }
-            headers.put(HttpHeaderHelper.COOKIE, cookies);
-            for (Cookie c : sessionCookies.values()) {
-                cookies.add(c.requestCookieHeader());
-            }
-        }
+        cookies.writeToMessageHeaders(message);
 
         // The trust decision is relegated 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);
         
         if (certConstraints != null) {
             message.put(CertConstraints.class.getName(), certConstraints);
             message.getInterceptorChain().add(CertConstraintsInterceptor.INSTANCE);
         }
-        
-        // Set the headers on the message according to configured 
-        // client side policy.
-        setHeadersByPolicy(message, currentURL, headers);
-        
+
+        setHeadersByAuthorizationPolicy(message, currentURL);
+        new Headers(message).setHeadersByClientPolicy(getClient(message));
 
         message.setContent(OutputStream.class, 
                            new WrappedOutputStream(
                                    message, connection,
                                    needToCacheRequest, 
                                    isChunking,
-                                   chunkThreshold));
+                                   chunkThreshold,
+                                   getConduitName()));
         // We are now "ready" to "send" the message. 
     }
+
+    private static int determineReceiveTimeout(Message message,
+            HTTPClientPolicy csPolicy) {
+        long rtimeout = csPolicy.getReceiveTimeout();
+        if (message.get(Message.RECEIVE_TIMEOUT) != null) {
+            Object obj = message.get(Message.RECEIVE_TIMEOUT);
+            try {
+                rtimeout = Long.parseLong(obj.toString());
+            } catch (NumberFormatException e) {
+                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
+                    Message.RECEIVE_TIMEOUT, obj.toString()
+                });
+            }
+        }
+        if (rtimeout > Integer.MAX_VALUE) {
+            rtimeout = Integer.MAX_VALUE;
+        }
+        return (int)rtimeout;
+    }
+
+    private static int determineConnectionTimeout(Message message,
+            HTTPClientPolicy csPolicy) {
+        long ctimeout = csPolicy.getConnectionTimeout();
+        if (message.get(Message.CONNECTION_TIMEOUT) != null) {
+            Object obj = message.get(Message.CONNECTION_TIMEOUT);
+            try {
+                ctimeout = Long.parseLong(obj.toString());
+            } catch (NumberFormatException e) {
+                LOG.log(Level.WARNING, "INVALID_TIMEOUT_FORMAT", new Object[] {
+                    Message.CONNECTION_TIMEOUT, obj.toString()
+                });
+            }
+        }
+        if (ctimeout > Integer.MAX_VALUE) {
+            ctimeout = Integer.MAX_VALUE;
+        }
+        return (int)ctimeout;
+    }
     
     public void close(Message msg) throws IOException {
         InputStream in = msg.getContent(InputStream.class);
@@ -623,81 +594,6 @@ public class HTTPConduit 
     }
 
     /**
-     * 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, HttpURLConnection connection)
-        throws IOException {
-        
-        MessageTrustDecider decider2 = message.get(MessageTrustDecider.class);
-        if (trustDecider != null || decider2 != 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();
-                HttpsURLConnectionInfo info = new HttpsURLConnectionInfo(connection);
-                if (trustDecider != null) {
-                    trustDecider.establishTrust(
-                        getConduitName(), 
-                        info,
-                        message);
-                    if (LOG.isLoggable(Level.FINE)) {
-                        LOG.log(Level.FINE, "Trust Decider "
-                            + trustDecider.getLogicalName()
-                            + " considers Conduit "
-                            + getConduitName() 
-                            + " trusted.");
-                    }
-                }
-                if (decider2 != null) {
-                    decider2.establishTrust(getConduitName(), 
-                                            info,
-                                            message);
-                    if (LOG.isLoggable(Level.FINE)) {
-                        LOG.log(Level.FINE, "Trust Decider "
-                            + decider2.getLogicalName()
-                            + " considers Conduit "
-                            + getConduitName() 
-                            + " trusted.");
-                    }
-                }
-            } catch (UntrustedURLConnectionIOException untrustedEx) {
-                // This cast covers HttpsURLConnection as well.
-                ((HttpURLConnection)connection).disconnect();
-                if (LOG.isLoggable(Level.FINE)) {
-                    LOG.log(Level.FINE, "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
@@ -772,10 +668,7 @@ public class HTTPConduit 
      * @return the default target URL
      */
     protected URL getURL() throws MalformedURLException {
-        if (defaultEndpointURL == null) {
-            return getURL(true);
-        } 
-        return defaultEndpointURL;
+        return getURL(true);
     }
 
     /**
@@ -800,204 +693,6 @@ public class HTTPConduit 
     }
 
     /**
-     * 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 PROTOCOL_HEADERS map
-     */
-    private Map<String, List<String>> getSetProtocolHeaders(Message message) {
-        Map<String, List<String>> headers =
-            CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));        
-        if (null == headers) {
-            headers = new LinkedHashMap<String, List<String>>();
-        } else if (headers instanceof HashMap) {
-            headers = new LinkedHashMap<String, List<String>>(headers);
-        }
-        message.put(Message.PROTOCOL_HEADERS, headers);
-        return headers;
-    }
-    
-    
-    /**
-     * This procedure sets the URLConnection request properties
-     * from the PROTOCOL_HEADERS in the message.
-     */
-    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);
-            if (HttpHeaderHelper.CONTENT_TYPE.equalsIgnoreCase(header)) {
-                continue;
-            }
-            if (HttpHeaderHelper.COOKIE.equalsIgnoreCase(header)) {
-                for (String s : headerList) {
-                    connection.addRequestProperty(HttpHeaderHelper.COOKIE, s);
-                }
-            } else {
-                StringBuilder b = new StringBuilder();
-                for (int i = 0; i < headerList.size(); i++) {
-                    b.append(headerList.get(i));
-                    if (i + 1 < headerList.size()) {
-                        b.append(',');
-                    }
-                }
-                connection.setRequestProperty(header, b.toString());
-            }
-        }
-        if (!connection.getRequestProperties().containsKey("User-Agent")) {
-            connection.addRequestProperty("User-Agent", Version.getCompleteVersionString());
-        }
-    }
-    
-    /**
-     * This procedure logs the PROTOCOL_HEADERS from the 
-     * Message at the specified logging level.
-     * 
-     * @param level   The Logging Level.
-     * @param headers The Message protocol headers.
-     */
-    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);
-            }
-        }
-    }
-    
-    /**
-     * 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 message The outbound message.
-     * @throws IOException
-     */
-    private void setURLRequestHeaders(Message message) throws IOException {
-        HttpURLConnection connection = 
-            (HttpURLConnection)message.get(KEY_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.toLowerCase().contains("multipart/related")) {
-                ct = ct + "; charset=" + enc;
-            }
-        } else if (enc != null) {
-            ct = "text/xml; charset=" + enc;
-        } else {
-            ct = "text/xml";
-        }
-        connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, ct);
-        
-        if (LOG.isLoggable(Level.FINE)) {
-            LOG.fine("Sending "
-                + connection.getRequestMethod() 
-                + " Message with Headers to " 
-                           + connection.getURL()
-                + " Conduit :"
-                + getConduitName()
-                + "\nContent-Type: " + ct + "\n");
-            logProtocolHeaders(Level.FINE, message);
-        }
-        
-        transferProtocolHeadersToURLConnection(message, connection);
-        
-    }
-    
-    
-    /**
-     * 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();
-    }
-
-    /**
-     * Get an input stream containing the partial response if one is present.
-     * 
-     * @param connection the connection in question
-     * @param responseCode the response code
-     * @return an input stream if a partial response is pending on the connection 
-     */
-    protected static InputStream getPartialResponse(
-        HttpURLConnection connection,
-        int responseCode
-    ) throws IOException {
-        InputStream in = null;
-        if (responseCode == HttpURLConnection.HTTP_ACCEPTED
-            || responseCode == HttpURLConnection.HTTP_OK) {
-            if (connection.getContentLength() > 0) {
-                in = connection.getInputStream();
-            } else if (hasChunkedResponse(connection) 
-                       || hasEofTerminatedResponse(connection)) {
-                // ensure chunked or EOF-terminated response is non-empty
-                in = getNonEmptyContent(connection);        
-            }
-        }
-        return in;
-    }
-    
-    /**
-     * @param connection the given HttpURLConnection
-     * @return true iff the connection has a chunked response pending
-     */
-    private static boolean hasChunkedResponse(HttpURLConnection connection) {
-        return HttpHeaderHelper.CHUNKED.equalsIgnoreCase(
-                   connection.getHeaderField(HttpHeaderHelper.TRANSFER_ENCODING));
-    }
-    
-    /**
-     * @param connection the given HttpURLConnection
-     * @return true iff the connection has a chunked response pending
-     */
-    private static boolean hasEofTerminatedResponse(
-        HttpURLConnection connection
-    ) {
-        return HttpHeaderHelper.CLOSE.equalsIgnoreCase(
-                   connection.getHeaderField(HttpHeaderHelper.CONNECTION));
-    }
-
-    /**
-     * @param connection the given HttpURLConnection
-     * @return an input stream containing the response content if non-empty
-     */
-    private static InputStream getNonEmptyContent(
-        HttpURLConnection connection
-    ) {
-        InputStream in = null;
-        try {
-            PushbackInputStream pin = 
-                new PushbackInputStream(connection.getInputStream());
-            int c = pin.read();
-            if (c != -1) {
-                pin.unread((byte)c);
-                in = pin;
-            }
-        } catch (IOException ioe) {
-            // ignore
-        }    
-        return in;
-    }
-
-    /**
      * This call places HTTP Header strings into the headers that are relevant
      * to the Authorization policies that are set on this conduit by
      * configuration.
@@ -1021,9 +716,9 @@ public class HTTPConduit 
      */
     private void setHeadersByAuthorizationPolicy(
             Message message,
-            URL url,
-            Map<String, List<String>> headers
+            URL url
     ) {
+        Headers headers = new Headers(message);
         AuthorizationPolicy authPolicy = getAuthorization();
         AuthorizationPolicy newPolicy = message.get(AuthorizationPolicy.class);
         
@@ -1040,8 +735,7 @@ public class HTTPConduit 
                 message.remove("AUTH_VALUE");
             }
             if (authString != null) {
-                headers.put("Authorization",
-                            createMutableList(authString));
+                headers.setAuthorization(authString);
             }
             return;
         }
@@ -1061,15 +755,11 @@ public class HTTPConduit 
                 && authPolicy != null && authPolicy.isSetPassword()) {
                 passwd = authPolicy.getPassword();
             }
-            setBasicAuthHeader(userName, passwd, headers);
+            headers.setAuthorization(getBasicAuthHeader(userName, passwd));
         } else if (authPolicy != null 
                 && authPolicy.isSetAuthorizationType() 
                 && authPolicy.isSetAuthorization()) {
-            String type = authPolicy.getAuthorizationType();
-            type += " ";
-            type += authPolicy.getAuthorization();
-            headers.put("Authorization",
-                        createMutableList(type));
+            headers.setAuthorization(authPolicy.getAuthorizationType() + " " + authPolicy.getAuthorization());
         }
         AuthorizationPolicy proxyAuthPolicy = getProxyAuthorization();
         if (proxyAuthPolicy != null && proxyAuthPolicy.isSetUserName()) {
@@ -1079,97 +769,16 @@ public class HTTPConduit 
                 if (proxyAuthPolicy.isSetPassword()) {
                     passwd = proxyAuthPolicy.getPassword();
                 }
-                setProxyBasicAuthHeader(userName, passwd, headers);
+                headers.setProxyAuthorization(getBasicAuthHeader(userName, passwd));
             } else if (proxyAuthPolicy.isSetAuthorizationType() 
                        && proxyAuthPolicy.isSetAuthorization()) {
-                String type = proxyAuthPolicy.getAuthorizationType();
-                type += " ";
-                type += proxyAuthPolicy.getAuthorization();
-                headers.put("Proxy-Authorization",
-                            createMutableList(type));
+                headers.setProxyAuthorization(proxyAuthPolicy.getAuthorizationType() + " " 
+                        + proxyAuthPolicy.getAuthorization());
             }
         }
     }
-    private static List<String> createMutableList(String val) {
-        return new ArrayList<String>(Arrays.asList(new String[] {val}));
-    }
-    /**
-     * 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",
-                        createMutableList(policy.getCacheControl().value()));
-        }
-        if (policy.isSetHost()) {
-            headers.put("Host",
-                        createMutableList(policy.getHost()));
-        }
-        if (policy.isSetConnection()) {
-            headers.put("Connection",
-                        createMutableList(policy.getConnection().value()));
-        }
-        if (policy.isSetAccept()) {
-            headers.put("Accept",
-                        createMutableList(policy.getAccept()));
-        } else if (!headers.containsKey("Accept")) {
-            headers.put("Accept", createMutableList("*/*"));
-        }
-        if (policy.isSetAcceptEncoding()) {
-            headers.put("Accept-Encoding",
-                        createMutableList(policy.getAcceptEncoding()));
-        }
-        if (policy.isSetAcceptLanguage()) {
-            headers.put("Accept-Language",
-                        createMutableList(policy.getAcceptLanguage()));
-        }
-        if (policy.isSetContentType()) {
-            message.put(Message.CONTENT_TYPE, policy.getContentType());
-        }
-        if (policy.isSetCookie()) {
-            headers.put("Cookie",
-                        createMutableList(policy.getCookie()));
-        }
-        if (policy.isSetBrowserType()) {
-            headers.put("BrowserType",
-                        createMutableList(policy.getBrowserType()));
-        }
-        if (policy.isSetReferer()) {
-            headers.put("Referer",
-                        createMutableList(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.
      */
@@ -1320,26 +929,26 @@ public class HTTPConduit 
      * @throws IOException
      */
     private HttpURLConnection processRetransmit(
-        HttpURLConnection connection,
+        final HttpURLConnection origConnection,
         Message message,
         CacheAndWriteOutputStream cachedStream
     ) throws IOException {
 
-        int responseCode = connection.getResponseCode();
+        int responseCode = origConnection.getResponseCode();
         if ((message != null) && (message.getExchange() != null)) {
             message.getExchange().put(Message.RESPONSE_CODE, responseCode);
         }
-        
+        HttpURLConnection connection = origConnection;
         // Process Redirects first.
         switch(responseCode) {
         case HttpURLConnection.HTTP_MOVED_PERM:
         case HttpURLConnection.HTTP_MOVED_TEMP:
             connection = 
-                redirectRetransmit(connection, message, cachedStream);
+                redirectRetransmit(origConnection, message, cachedStream);
             break;
         case HttpURLConnection.HTTP_UNAUTHORIZED:
             connection = 
-                authorizationRetransmit(connection, message, cachedStream);
+                authorizationRetransmit(origConnection, message, cachedStream);
             break;
         default:
             break;
@@ -1399,10 +1008,7 @@ public class HTTPConduit 
             // 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");
+            new Headers(message).removeAuthorizationHeaders();
             
             URL url = new URL(newURL);
             
@@ -1411,7 +1017,7 @@ public class HTTPConduit 
             // 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);
+            setHeadersByAuthorizationPolicy(message, url);
             
             connection = retransmit(
                     connection, url, message, cachedStream);
@@ -1427,7 +1033,7 @@ public class HTTPConduit 
      * @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) {
+    private static Set<String> getSetAuthoriationURLs(Message message) {
         @SuppressWarnings("unchecked")
         Set<String> authURLs = (Set<String>) message.get(KEY_AUTH_URLS);
         if (authURLs == null) {
@@ -1445,7 +1051,7 @@ public class HTTPConduit 
      * @param message The message where the Set is stored.
      * @return The modifiable set of URLs that were visited.
      */
-    private Set<String> getSetVisitedURLs(Message message) {
+    private static Set<String> getSetVisitedURLs(Message message) {
         @SuppressWarnings("unchecked")
         Set<String> visitedURLs = (Set<String>) message.get(KEY_VISITED_URLS);
         if (visitedURLs == null) {
@@ -1485,49 +1091,46 @@ public class HTTPConduit 
         URL currentURL = connection.getURL();
         
         String realm = extractAuthorizationRealm(connection.getHeaderFields());
+
+        detectAuthorizationLoop(getConduitName(), message, currentURL, realm);
         
-        Set<String> authURLs = getSetAuthoriationURLs(message);
+        String up = 
+            authSupplier.getAuthorizationForRealm(
+                this, currentURL, message, realm, connection.getHeaderField("WWW-Authenticate"));
         
+        // No user pass combination. We give up.
+        if (up == null) {
+            return connection;
+        }
+
+        new Headers(message).setAuthorization(up);
+        return retransmit(
+                connection, currentURL, message, cachedStream);
+    }
+
+    private static void detectAuthorizationLoop(String conduitName, Message message, 
+            URL currentURL, String realm) throws IOException {
+        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)) {
-
+            String logMessage = "Authorization loop detected on Conduit \""
+                + conduitName
+                + "\" on URL \""
+                + currentURL
+                + "\" with realm \""
+                + realm
+                + "\"";
             if (LOG.isLoggable(Level.INFO)) {
-                LOG.log(Level.INFO, "Authorization loop detected on Conduit \""
-                    + getConduitName()
-                    + "\" on URL \""
-                    + "\" with realm \""
-                    + realm
-                    + "\"");
+                LOG.log(Level.INFO, logMessage);
             }
                     
-            throw new IOException("Authorization loop detected on Conduit \"" 
-                                  + getConduitName() 
-                                  + "\" on URL \""
-                                  + "\" with realm \""
-                                  + realm
-                                  + "\"");
-        }
-        
-        String up = 
-            authSupplier.getAuthorizationForRealm(
-                this, currentURL, message, realm, connection.getHeaderField("WWW-Authenticate"));
-        
-        // No user pass combination. We give up.
-        if (up == null) {
-            return connection;
+            throw new IOException(logMessage);
         }
-        
         // Register that we have been here before we go.
         authURLs.add(currentURL.toString() + realm);
-        
-        Map<String, List<String>> headers = getSetProtocolHeaders(message);
-        headers.put("Authorization",
-                    createMutableList(up));
-        return retransmit(
-                connection, currentURL, message, cachedStream);
     }
     
     /**
@@ -1566,11 +1169,8 @@ public class HTTPConduit 
         String httpRequestMethod = 
             (String)message.get(Message.HTTP_REQUEST_METHOD);
 
-        if (null != httpRequestMethod) {
-            connection.setRequestMethod(httpRequestMethod);
-        } else {
-            connection.setRequestMethod("POST");
-        }
+        connection.setRequestMethod((null != httpRequestMethod) ? httpRequestMethod : "POST");
+
         message.put(KEY_HTTP_CONNECTION, connection);
 
         if (stream != null) {
@@ -1579,7 +1179,7 @@ public class HTTPConduit 
         
         // Need to set the headers before the trust decision
         // because they are set before the connect().
-        setURLRequestHeaders(message);
+        new Headers(message).setURLRequestHeaders(getConduitName());
         
         //
         // This point is where the trust decision is made because the
@@ -1588,7 +1188,7 @@ public class HTTPConduit 
         // makeTrustDecision needs to make a connect() call to
         // make sure the proper information is available.
         // 
-        makeTrustDecision(message, connection);
+        TrustDecisionUtil.makeTrustDecision(trustDecider, message, connection, getConduitName());
 
         // If this is a GET method we must not touch the output
         // stream as this automagically turns the request into a POST.
@@ -1670,10 +1270,9 @@ public class HTTPConduit 
      * 
      * @param headers  The headers map that gets the "Authorization" header set.
      */
-    private void setBasicAuthHeader(
+    private String getBasicAuthHeader(
         String                    userid,
-        String                    password,
-        Map<String, List<String>> headers
+        String                    password
     ) {
         String userpass = userid;
 
@@ -1682,41 +1281,15 @@ public class HTTPConduit 
             userpass += password;
         }
         String token = Base64Utility.encode(userpass.getBytes());
-        headers.put("Authorization",
-                    createMutableList("Basic " + token));
+        return "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",
-                    createMutableList("Basic " + token));
-    }
-    
-    /**
      * Wrapper output stream responsible for flushing headers and handling
      * the incoming HTTP-level response (not necessarily the MEP response).
      */
     protected class WrappedOutputStream extends AbstractThresholdOutputStream {
+        
         /**
          * This field contains the currently active connection.
          */
@@ -1740,18 +1313,23 @@ public class HTTPConduit 
         protected CacheAndWriteOutputStream cachedStream;
 
         protected Message outMessage;
+
+        protected String conduitName;
+
         protected WrappedOutputStream(
-                Message m, 
-                HttpURLConnection c, 
+                Message outMessage, 
+                HttpURLConnection connection,
                 boolean possibleRetransmit,
                 boolean isChunking,
-                int chunkThreshold
+                int chunkThreshold,
+                String conduitName
         ) {
             super(chunkThreshold);
-            this.outMessage = m;
-            connection = c;
-            cachingForRetransmission = possibleRetransmit;
-            chunking = isChunking;
+            this.outMessage = outMessage;
+            this.connection = connection;
+            this.cachingForRetransmission = possibleRetransmit;
+            this.chunking = isChunking;
+            this.conduitName = conduitName;
         }
         
         
@@ -1792,7 +1370,7 @@ public class HTTPConduit 
         protected void handleHeadersTrustCaching() throws IOException {
             // Need to set the headers before the trust decision
             // because they are set before the connect().
-            setURLRequestHeaders(outMessage);
+            new Headers(outMessage).setURLRequestHeaders(conduitName);
            
             //
             // This point is where the trust decision is made because the
@@ -1801,7 +1379,7 @@ public class HTTPConduit 
             // makeTrustDecision needs to make a connect() call to
             // make sure the proper information is available.
             // 
-            makeTrustDecision(outMessage, connection);
+            TrustDecisionUtil.makeTrustDecision(trustDecider, outMessage, connection, conduitName);
             
             // Trust is okay, set up for writing the request.
             
@@ -1865,48 +1443,7 @@ public class HTTPConduit 
                     }
                 }
             } catch (HttpRetryException e) {
-                String msg = "HTTP response '" + e.responseCode() + ": "
-                             + connection.getResponseMessage() + "' invoking " + connection.getURL();
-                switch (e.responseCode()) {
-                case HttpURLConnection.HTTP_MOVED_PERM: // 301
-                case HttpURLConnection.HTTP_MOVED_TEMP: // 302
-                    msg += " that returned location header '" + e.getLocation() + "'";
-                    break;
-                case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
-                    if (authorizationPolicy == null || authorizationPolicy.getUserName() == null) {
-                        msg += " with NO authorization username configured in conduit " + getConduitName();
-                    } else {
-                        msg += " with authorization username '" + authorizationPolicy.getUserName() + "'";
-                    }
-                    break;
-                case HttpURLConnection.HTTP_PROXY_AUTH: // 407
-                    if (proxyAuthorizationPolicy == null || proxyAuthorizationPolicy.getUserName() == null) {
-                        msg += " with NO proxy authorization configured in conduit " + getConduitName();
-                    } else {
-                        msg += " with proxy authorization username '"
-                               + proxyAuthorizationPolicy.getUserName() + "'";
-                    }
-                    if (clientSidePolicy == null || clientSidePolicy.getProxyServer() == null) {
-                        if (connection.usingProxy()) {
-                            msg += " using a proxy even if NONE is configured in CXF conduit "
-                                   + getConduitName()
-                                   + " (maybe one is configured by java.net.ProxySelector)";
-                        } else {
-                            msg += " but NO proxy was used by the connection (none configured in cxf "
-                                   + "conduit and none selected by java.net.ProxySelector)";
-                        }
-                    } else {
-                        msg += " using " + clientSidePolicy.getProxyServerType() + " proxy "
-                               + clientSidePolicy.getProxyServer() + ":"
-                               + clientSidePolicy.getProxyServerPort();
-                    }
-                    break;
-                default:
-                    // No other type of HttpRetryException should be thrown
-                    break;
-                }
-                // pass cause with initCause() instead of constructor for jdk 1.5 compatibility
-                throw (IOException) new IOException(msg).initCause(e);
+                handleHttpRetryException(e, connection);
             } catch (IOException e) {
                 String url = connection.getURL().toString();
                 String origMessage = e.getMessage();
@@ -1973,8 +1510,7 @@ public class HTTPConduit 
                 
                 int nretransmits = 0;
                 
-                connection = 
-                    processRetransmit(connection, outMessage, cachedStream);
+                connection = processRetransmit(connection, outMessage, cachedStream);
                 
                 while (connection != oldcon) {
                     nretransmits++;
@@ -1982,9 +1518,7 @@ public class HTTPConduit 
 
                     // A negative max means unlimited.
                     if (maxRetransmits < 0 || nretransmits < maxRetransmits) {
-                        connection = 
-                            processRetransmit(
-                                    connection, outMessage, cachedStream);
+                        connection = processRetransmit(connection, outMessage, cachedStream);
                     }
                 }
             }
@@ -2034,6 +1568,17 @@ public class HTTPConduit 
                 ex.execute(runnable);
             }
         }
+
+        /**
+         * 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();
+        }
+        
         protected void handleResponseInternal() throws IOException {
             Exchange exchange = outMessage.getExchange();
             int responseCode = connection.getResponseCode();
@@ -2041,26 +1586,7 @@ public class HTTPConduit 
                 exchange.put(Message.RESPONSE_CODE, responseCode);
             }
             
-            if (LOG.isLoggable(Level.FINE)) {
-                LOG.fine("Response Code: " 
-                        + responseCode
-                        + " Conduit: " + getConduitName());
-                LOG.fine("Content length: " + connection.getContentLength());
-                Map<String, List<String>> headerFields = connection.getHeaderFields();
-                if (null != headerFields) {
-                    StringBuilder buf = new StringBuilder();
-                    buf.append("Header fields: ");
-                    buf.append(System.getProperty("line.separator"));
-                    for (String h : headerFields.keySet()) {
-                        buf.append("    ");
-                        buf.append(h);
-                        buf.append(": ");
-                        buf.append(headerFields.get(h));
-                        buf.append(System.getProperty("line.separator"));
-                    }
-                    LOG.fine(buf.toString());
-                }
-            }
+            logResponseInfo(responseCode);
             
             if (responseCode == HttpURLConnection.HTTP_NOT_FOUND
                 && !MessageUtils.isTrue(outMessage.getContextualProperty(
@@ -2069,11 +1595,9 @@ public class HTTPConduit 
                     + connection.getResponseMessage() + "'");
             }
 
-            
-
             InputStream in = null;
             if (isOneway(exchange)) {
-                in = getPartialResponse(connection, responseCode);
+                in = ChunkedUtil.getPartialResponse(connection, responseCode);
                 if (in == null) {
                     // oneway operation or decoupled MEP without 
                     // partial response
@@ -2092,17 +1616,7 @@ public class HTTPConduit 
             
             Message inMessage = new MessageImpl();
             inMessage.setExchange(exchange);
-            Map<String, List<String>> origHeaders = connection.getHeaderFields();
-            Map<String, List<String>> headers = 
-                new HashMap<String, List<String>>();
-            for (String key : connection.getHeaderFields().keySet()) {
-                if (key != null) {
-                    headers.put(HttpHeaderHelper.getHeaderKey(key), 
-                        origHeaders.get(key));
-                }
-            }
-            
-            inMessage.put(Message.PROTOCOL_HEADERS, headers);
+            new Headers(inMessage).readFromConnection(connection);
             inMessage.put(Message.RESPONSE_CODE, responseCode);
             String ct = connection.getContentType();
             inMessage.put(Message.CONTENT_TYPE, ct);
@@ -2114,15 +1628,8 @@ public class HTTPConduit 
                 LOG.log(Level.WARNING, m);
                 throw new IOException(m);   
             } 
-            inMessage.put(Message.ENCODING, normalizedEncoding);            
-                        
-            if (maintainSession) {
-                for (Map.Entry<String, List<String>> h : connection.getHeaderFields().entrySet()) {
-                    if ("Set-Cookie".equalsIgnoreCase(h.getKey())) {
-                        Cookie.handleSetCookie(sessionCookies, h.getValue());
-                    }
-                }
-            }
+            inMessage.put(Message.ENCODING, normalizedEncoding);
+            cookies.readFromConnection(connection);
             if (in == null) {
                 if (responseCode >= HttpURLConnection.HTTP_BAD_REQUEST) {
                     in = connection.getErrorStream();
@@ -2147,6 +1654,24 @@ public class HTTPConduit 
             
         }
 
+
+        private void logResponseInfo(int responseCode) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.fine("Response Code: " + responseCode + " Conduit: " + conduitName);
+                LOG.fine("Content length: " + connection.getContentLength());
+                Map<String, List<String>> headerFields = connection.getHeaderFields();
+                if (null != headerFields) {
+                    String newLine = System.getProperty("line.separator");
+                    StringBuilder buf = new StringBuilder();
+                    buf.append("Header fields: " + newLine);
+                    for (String headerKey : headerFields.keySet()) {
+                        buf.append("    " + headerKey + ": " + headerFields.get(headerKey) + newLine);
+                    }
+                    LOG.fine(buf.toString());
+                }
+            }
+        }
+
     }
     
     /**
@@ -2168,7 +1693,7 @@ public class HTTPConduit 
             inMessage.put(DECOUPLED_CHANNEL_MESSAGE, Boolean.TRUE);
             // REVISIT: how to get response headers?
             //inMessage.put(Message.PROTOCOL_HEADERS, req.getXXX());
-            getSetProtocolHeaders(inMessage);
+            Headers.getSetProtocolHeaders(inMessage);
             inMessage.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
 
             // remove server-specific properties
@@ -2211,5 +1736,51 @@ public class HTTPConduit 
                                           evt.getNewValue());
         }
     }
+
+    private void handleHttpRetryException(HttpRetryException e, HttpURLConnection connection) 
+        throws IOException {
+        String msg = "HTTP response '" + e.responseCode() + ": "
+                     + connection.getResponseMessage() + "' invoking " + connection.getURL();
+        switch (e.responseCode()) {
+        case HttpURLConnection.HTTP_MOVED_PERM: // 301
+        case HttpURLConnection.HTTP_MOVED_TEMP: // 302
+            msg += " that returned location header '" + e.getLocation() + "'";
+            break;
+        case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
+            if (authorizationPolicy == null || authorizationPolicy.getUserName() == null) {
+                msg += " with NO authorization username configured in conduit " + getConduitName();
+            } else {
+                msg += " with authorization username '" + authorizationPolicy.getUserName() + "'";
+            }
+            break;
+        case HttpURLConnection.HTTP_PROXY_AUTH: // 407
+            if (proxyAuthorizationPolicy == null || proxyAuthorizationPolicy.getUserName() == null) {
+                msg += " with NO proxy authorization configured in conduit " + getConduitName();
+            } else {
+                msg += " with proxy authorization username '"
+                       + proxyAuthorizationPolicy.getUserName() + "'";
+            }
+            if (clientSidePolicy == null || clientSidePolicy.getProxyServer() == null) {
+                if (connection.usingProxy()) {
+                    msg += " using a proxy even if NONE is configured in CXF conduit "
+                           + getConduitName()
+                           + " (maybe one is configured by java.net.ProxySelector)";
+                } else {
+                    msg += " but NO proxy was used by the connection (none configured in cxf "
+                           + "conduit and none selected by java.net.ProxySelector)";
+                }
+            } else {
+                msg += " using " + clientSidePolicy.getProxyServerType() + " proxy "
+                       + clientSidePolicy.getProxyServer() + ":"
+                       + clientSidePolicy.getProxyServerPort();
+            }
+            break;
+        default:
+            // No other type of HttpRetryException should be thrown
+            break;
+        }
+        // pass cause with initCause() instead of constructor for jdk 1.5 compatibility
+        throw (IOException) new IOException(msg).initCause(e);
+    }
     
 }

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java?rev=1037576&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java Sun Nov 21 23:38:58 2010
@@ -0,0 +1,269 @@
+/**
+ * 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.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.helpers.HttpHeaderHelper;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.version.Version;
+
+public class Headers {
+    /**
+     *  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";
+    private static final Logger LOG = LogUtils.getL7dLogger(Headers.class);
+    private final Message message;
+    private final Map<String, List<String>> headers;
+
+    public Headers(Message message) {
+        this.message = message;
+        this.headers = getSetProtocolHeaders(message);
+    }
+
+    public void writeSessionCookies(Map<String, Cookie> sessionCookies) {
+        List<String> cookies = null;
+        for (String s : headers.keySet()) {
+            if (HttpHeaderHelper.COOKIE.equalsIgnoreCase(s)) {
+                cookies = headers.remove(s);
+                break;
+            }
+        }
+        if (cookies == null) {
+            cookies = new ArrayList<String>();
+        } else {
+            cookies = new ArrayList<String>(cookies);
+        }
+        headers.put(HttpHeaderHelper.COOKIE, cookies);
+        for (Cookie c : sessionCookies.values()) {
+            cookies.add(c.requestCookieHeader());
+        }
+    }
+
+    /**
+     * 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? 
+     */
+    void setHeadersByClientPolicy(HTTPClientPolicy policy) {
+        if (policy == null) {
+            return;
+        }
+        if (policy.isSetCacheControl()) {
+            headers.put("Cache-Control",
+                    createMutableList(policy.getCacheControl().value()));
+        }
+        if (policy.isSetHost()) {
+            headers.put("Host",
+                    createMutableList(policy.getHost()));
+        }
+        if (policy.isSetConnection()) {
+            headers.put("Connection",
+                    createMutableList(policy.getConnection().value()));
+        }
+        if (policy.isSetAccept()) {
+            headers.put("Accept",
+                    createMutableList(policy.getAccept()));
+        } else if (!headers.containsKey("Accept")) {
+            headers.put("Accept", createMutableList("*/*"));
+        }
+        if (policy.isSetAcceptEncoding()) {
+            headers.put("Accept-Encoding",
+                    createMutableList(policy.getAcceptEncoding()));
+        }
+        if (policy.isSetAcceptLanguage()) {
+            headers.put("Accept-Language",
+                    createMutableList(policy.getAcceptLanguage()));
+        }
+        if (policy.isSetContentType()) {
+            message.put(Message.CONTENT_TYPE, policy.getContentType());
+        }
+        if (policy.isSetCookie()) {
+            headers.put("Cookie",
+                    createMutableList(policy.getCookie()));
+        }
+        if (policy.isSetBrowserType()) {
+            headers.put("BrowserType",
+                    createMutableList(policy.getBrowserType()));
+        }
+        if (policy.isSetReferer()) {
+            headers.put("Referer",
+                    createMutableList(policy.getReferer()));
+        }
+    }
+
+    public void removeAuthorizationHeaders() {
+        headers.remove("Authorization");
+        headers.remove("Proxy-Authorization");
+    }
+    
+    public void setAuthorization(String authorization) {
+        headers.put("Authorization",
+                createMutableList(authorization));
+    }
+    
+    public void setProxyAuthorization(String authorization) {
+        headers.put("Proxy-Authorization",
+                createMutableList(authorization));
+    }
+    
+    
+    /**
+     * 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 PROTOCOL_HEADERS map
+     */
+    public static Map<String, List<String>> getSetProtocolHeaders(final Message message) {
+        Map<String, List<String>> headers =
+            CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));        
+        if (null == headers) {
+            headers = new LinkedHashMap<String, List<String>>();
+        } else if (headers instanceof HashMap) {
+            headers = new LinkedHashMap<String, List<String>>(headers);
+        }
+        message.put(Message.PROTOCOL_HEADERS, headers);
+        return headers;
+    }
+    
+    public void readFromConnection(HttpURLConnection connection) {
+        Map<String, List<String>> origHeaders = connection.getHeaderFields();
+        headers.clear();
+        for (String key : connection.getHeaderFields().keySet()) {
+            if (key != null) {
+                headers.put(HttpHeaderHelper.getHeaderKey(key), 
+                    origHeaders.get(key));
+            }
+        }
+    }
+
+    private static List<String> createMutableList(String val) {
+        return new ArrayList<String>(Arrays.asList(new String[] {val}));
+    }
+    
+    /**
+     * This procedure logs the PROTOCOL_HEADERS from the 
+     * Message at the specified logging level.
+     * 
+     * @param level   The Logging Level.
+     * @param headers The Message protocol headers.
+     */
+    void logProtocolHeaders(Level level) {
+        for (String header : headers.keySet()) {
+            List<String> headerList = headers.get(header);
+            for (String value : headerList) {
+                LOG.log(level, header + ": " + value);
+            }
+        }
+    }
+    
+    /**
+     * 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 message The outbound message.
+     * @throws IOException
+     */
+    public void setURLRequestHeaders(String conduitName) throws IOException {
+        HttpURLConnection connection = 
+            (HttpURLConnection)message.get(KEY_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.toLowerCase().contains("multipart/related")) {
+                ct = ct + "; charset=" + enc;
+            }
+        } else if (enc != null) {
+            ct = "text/xml; charset=" + enc;
+        } else {
+            ct = "text/xml";
+        }
+        connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, ct);
+        
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine("Sending "
+                + connection.getRequestMethod() 
+                + " Message with Headers to " 
+                + connection.getURL()
+                + " Conduit :"
+                + conduitName
+                + "\nContent-Type: " + ct + "\n");
+            new Headers(message).logProtocolHeaders(Level.FINE);
+        }
+        
+        transferProtocolHeadersToURLConnection(connection);
+    }
+    
+    /**
+     * This procedure sets the URLConnection request properties
+     * from the PROTOCOL_HEADERS in the message.
+     */
+    private void transferProtocolHeadersToURLConnection(URLConnection connection) {
+        for (String header : headers.keySet()) {
+            List<String> headerList = headers.get(header);
+            if (HttpHeaderHelper.CONTENT_TYPE.equalsIgnoreCase(header)) {
+                continue;
+            }
+            if (HttpHeaderHelper.COOKIE.equalsIgnoreCase(header)) {
+                for (String s : headerList) {
+                    connection.addRequestProperty(HttpHeaderHelper.COOKIE, s);
+                }
+            } else {
+                StringBuilder b = new StringBuilder();
+                for (int i = 0; i < headerList.size(); i++) {
+                    b.append(headerList.get(i));
+                    if (i + 1 < headerList.size()) {
+                        b.append(',');
+                    }
+                }
+                connection.setRequestProperty(header, b.toString());
+            }
+        }
+        if (!connection.getRequestProperties().containsKey("User-Agent")) {
+            connection.addRequestProperty("User-Agent", Version.getCompleteVersionString());
+        }
+    }
+
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java?rev=1037576&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java Sun Nov 21 23:38:58 2010
@@ -0,0 +1,112 @@
+/**
+ * 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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.transport.https.HttpsURLConnectionInfo;
+
+final class TrustDecisionUtil {
+    private static final Logger LOG = LogUtils.getL7dLogger(TrustDecisionUtil.class);
+    
+    private TrustDecisionUtil() {
+    }
+
+    /**
+     * 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
+     */
+    static void makeTrustDecision(
+            MessageTrustDecider trustDecider, 
+            Message message,
+            HttpURLConnection connection, 
+            String conduitName) throws IOException {
+    
+        MessageTrustDecider decider2 = message.get(MessageTrustDecider.class);
+        if (trustDecider != null || decider2 != 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();
+                HttpsURLConnectionInfo info = new HttpsURLConnectionInfo(connection);
+                if (trustDecider != null) {
+                    trustDecider.establishTrust(
+                            conduitName, 
+                        info,
+                        message);
+                    if (LOG.isLoggable(Level.FINE)) {
+                        LOG.log(Level.FINE, "Trust Decider "
+                            + trustDecider.getLogicalName()
+                            + " considers Conduit "
+                            + conduitName 
+                            + " trusted.");
+                    }
+                }
+                if (decider2 != null) {
+                    decider2.establishTrust(conduitName, 
+                                            info,
+                                            message);
+                    if (LOG.isLoggable(Level.FINE)) {
+                        LOG.log(Level.FINE, "Trust Decider "
+                            + decider2.getLogicalName()
+                            + " considers Conduit "
+                            + conduitName 
+                            + " trusted.");
+                    }
+                }
+            } catch (UntrustedURLConnectionIOException untrustedEx) {
+                connection.disconnect();
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.log(Level.FINE, "Trust Decider "
+                        + trustDecider.getLogicalName()
+                        + " considers Conduit "
+                        + conduitName 
+                        + " 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 '"
+                    + conduitName
+                    + "'. An afirmative Trust Decision is assumed.");
+            }
+        }
+    }
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/TrustDecisionUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain