Hi,

Attached is a patch (diff.txt) and the full changed files
(changes-evert.zip) which includes:
- Sachin's changes for SSL tunneling
- Changes to enable an alternative SSLSocketFactory to be set, for
getting around "untrusted server certificate chain" errors.
- Changes to give access to the time when the request was made and the
connection established, for monitoring purposes.
- Lastly, I've been trying to implement support for authenticated
proxies, BUT WITHOUT SUCCESS YET.

With the authenticated proxy, I don't know whether there is something
wrong with the proxy I am using or something else. The code is supposed
to handle authenticated proxies for both http and https requests. I am
getting "no route to host" responses with https and "access to host
forbidden on this server" responses with http. I might just no longer
have the right permissions on the proxy server.

If someone has access to an authenticated proxy and is willing to help
test and debug this, I would greatly appreciate it.

BTW, it might be a good idea to compare my files to the ones that Sachin
sent yesterday, because I had to make changes to his changes in order to
support authenticated proxies.

Regards,

Evert


Index: src/java/org/apache/commons/httpclient/HttpConnection.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
retrieving revision 1.9
diff -c -r1.9 HttpConnection.java
*** src/java/org/apache/commons/httpclient/HttpConnection.java	12 Apr 2002 21:09:20 -0000	1.9
--- src/java/org/apache/commons/httpclient/HttpConnection.java	13 Jun 2002 14:22:10 -0000
***************
*** 66,72 ****
--- 66,74 ----
  import java.net.SocketException;
  import java.io.InputStream;
  import java.io.OutputStream;
+ import java.io.UnsupportedEncodingException; 
  import java.io.IOException;
+ import javax.net.SocketFactory;
  import javax.net.ssl.SSLSocket;
  import javax.net.ssl.SSLSocketFactory;
  import org.apache.commons.httpclient.log.*;
***************
*** 115,121 ****
      }
  
      /**
!      * Fully-specified constructor.
       * @param proxyHost the host I should proxy via
       * @param proxyPort the port I should proxy via
       * @param host the host I should connect to. Parameter value must be non-null.
--- 117,123 ----
      }
  
      /**
!      * Constructor.
       * @param proxyHost the host I should proxy via
       * @param proxyPort the port I should proxy via
       * @param host the host I should connect to. Parameter value must be non-null.
***************
*** 123,128 ****
--- 125,145 ----
       * @param secure when <tt>true</tt>, connect via HTTPS (SSL)
       */
      public HttpConnection(String proxyHost, int proxyPort, String host, int port, boolean secure) {
+         this(proxyHost, proxyPort, host, port, secure, null, null);
+     }
+     
+     /**
+      * Fully-specified constructor.
+      * @param proxyHost the host I should proxy via
+      * @param proxyPort the port I should proxy via
+      * @param host the host I should connect to. Parameter value must be non-null.
+      * @param port the port I should connect to
+      * @param secure when <tt>true</tt>, connect via HTTPS (SSL)
+      * @param state the HttpSharedState for establishing connections through authenticated proxies.
+      * @param factory the factory to be used for creating SSL sockets. Or, null for the default.
+      */
+     public HttpConnection(String proxyHost, int proxyPort, String host, int port, boolean secure,
+                           HttpSharedState state, SSLSocketFactory factory) {
          log.debug("HttpConnection.HttpConnection");
          if (host == null) {
              throw new NullPointerException("host parameter is null");
***************
*** 132,137 ****
--- 149,156 ----
          _host = host;
          _port = port;
          _ssl = secure;
+         _state = state;
+         _sslSocketFactory = factory;
      }
  
      // ------------------------------------------ Attribute Setters and Getters
***************
*** 278,289 ****
          assertNotOpen(); // ??? is this worth doing?
          try {
              if (null == _socket) {
-                 String host = (null == _proxyHost) ? _host : _proxyHost;
-                 int port = (null == _proxyHost) ? _port : _proxyPort;
                  if (_ssl) {
!                     _socket = SSLSocketFactory.getDefault().createSocket(host,port);
                  } else {
                      _socket = new Socket(host,port);
                  }
              }
              _socket.setSoTimeout(_so_timeout);
--- 297,322 ----
          assertNotOpen(); // ??? is this worth doing?
          try {
              if (null == _socket) {
                  if (_ssl) {
!                     if (_sslSocketFactory == null)
!                         _sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
!                 	if ((_proxyHost != null) && (_proxyPort > 0)) {
!                 		// ssl using proxy - create a tunnellec connection 
!                 		doTunnelHandshake(false, null);
!                         Socket tunnel = _socket;
!                 		_socket = _sslSocketFactory.createSocket(tunnel, _host, _port, true);
!                 	} else {
!                 		// using with no proxy 
!                 		_socket = _sslSocketFactory.createSocket(_host, _port);
!                 	}
                  } else {
+                 	// non ssl connection 
+ 	                String host = (null == _proxyHost) ? _host : _proxyHost;
+ 	                int port = (null == _proxyHost) ? _port : _proxyPort;
                      _socket = new Socket(host,port);
+                     // Not creating a tunnel socket, but authenticating.
+                 	if ((_proxyHost != null) && (_proxyPort > 0))
+                         doTunnelHandshake(false, null);
                  }
              }
              _socket.setSoTimeout(_so_timeout);
***************
*** 298,303 ****
--- 331,428 ----
          }
      }
  
+ 	private void doTunnelHandshake(boolean authenticate, String realm) throws IOException 
+ 	{
+         String authorizationHeader = null;
+         if (authenticate && _state != null) {
+             Credentials credentials = _state.getCredentials(realm);
+             if ((credentials != null) && 
+             (credentials instanceof UsernamePasswordCredentials)) {
+                 try {
+                     authorizationHeader = Authenticator.basic((UsernamePasswordCredentials)credentials);
+                 } catch (HttpException ex) {
+                     log.debug("doTunnelHandshake(): Exception reading credentials: " + ex);
+                 }
+             }
+             else if (realm != null) {
+                 // Try it once with the default credentials that are stored 
+                 // with a null realm.
+                 doTunnelHandshake(true, null);
+                 return;
+             }
+         }
+         Socket tunnel = new Socket(_proxyHost, _proxyPort);
+ 		OutputStream out = tunnel.getOutputStream();
+ 		String msg = "CONNECT " + _host + ":" + _port + " HTTP/1.0\r\n" + 
+ 			     "User-Agent: " + sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n" + 
+ 			     "Host: " + _host + ":" + _port + "\r\n" + 
+ 			     "Content-Length: 0\r\n" + 
+ 			     "Proxy-Connection: Keep-Alive\r\n" + 
+ 			     "Pragma: no-cache\r\n";
+         if (authorizationHeader != null)
+             msg += "Proxy-Authorization: " + authorizationHeader + "\r\n";
+         msg += "\r\n";
+ 	
+         wireLog.info(">>\r\n" + msg);
+     
+ 		byte b[];
+ 		try {
+ 		    b = msg.getBytes("ASCII7");
+ 		} catch (UnsupportedEncodingException ignored) {
+ 		    b = msg.getBytes();
+ 		}
+ 		out.write(b);
+ 		out.flush();
+ 		
+      	_input = tunnel.getInputStream();
+ 		boolean error = false;
+          
+         _open = true;
+ 		StringBuffer replyBuffer = new StringBuffer();
+         String proxyAuthenticate = null;
+         while (true) {
+             String line = readLine();
+             if (line == null || line.length() < 0)
+                 break;
+             replyBuffer.append(line + "\r\n");
+             int colonPosition = line.indexOf(":");
+             if (colonPosition > -1) {
+                 String key = line.substring(0, colonPosition);
+                 String value = line.substring(colonPosition);
+                 if (key.trim().equalsIgnoreCase("Proxy-Authenticate"))
+                     proxyAuthenticate = value;
+             }
+         }
+         String replyStr = replyBuffer.toString();
+         _open = false;
+         
+         realm = null;
+         if (proxyAuthenticate != null) {
+             int start = proxyAuthenticate.indexOf("\"") + 1;
+             int end = proxyAuthenticate.lastIndexOf("\"");
+             if (start > -1 && end > -1 && start != end)
+                 realm = proxyAuthenticate.substring(start, end);
+         }
+         if (realm != null)
+             log.debug("doTunnelHandshake(): Authentication required for realm '" + realm + "'");
+         
+         if (realm != null) {
+             doTunnelHandshake(true, realm);
+             return;
+         }
+         
+         log.debug("doTunnelHandshake(): Reply: " + replyStr);
+ 		
+ 		/* We asked for HTTP/1.0, so we should get that back */
+ 		if (!replyStr.startsWith("HTTP/1.0 200")) {
+ 		    throw new IOException("Unable to tunnel through "
+ 			    + _proxyHost + ":" + _proxyPort
+ 			    + ".  Proxy returns \"" + replyStr + "\"");
+ 		}
+ 		
+ 		/* tunneling Handshake was successful! */
+ 	}
+ 
      /**
       * Return a {@link RequestOutputStream}
       * suitable for writing (possibly chunked)
***************
*** 593,597 ****
      private static final byte[] CRLF = "\r\n".getBytes();
      /** SO_TIMEOUT value */
      private int _so_timeout = 0;
! 
  }
--- 718,725 ----
      private static final byte[] CRLF = "\r\n".getBytes();
      /** SO_TIMEOUT value */
      private int _so_timeout = 0;
!     /** The factory to be used for creating SSL sockets. **/
!     private SSLSocketFactory _sslSocketFactory = null;
!     /** The state to be used for establishing connections through authenticated proxies. **/
!     private HttpSharedState _state = null;
  }
Index: src/java/org/apache/commons/httpclient/HttpConnectionManager.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnectionManager.java,v
retrieving revision 1.5
diff -c -r1.5 HttpConnectionManager.java
*** src/java/org/apache/commons/httpclient/HttpConnectionManager.java	15 Apr 2002 18:35:29 -0000	1.5
--- src/java/org/apache/commons/httpclient/HttpConnectionManager.java	13 Jun 2002 14:22:15 -0000
***************
*** 67,72 ****
--- 67,73 ----
  import java.util.HashMap;
  import java.util.List;
  import java.util.LinkedList;
+ import javax.net.ssl.SSLSocketFactory;
  
  import org.apache.commons.httpclient.log.*;
  
***************
*** 87,92 ****
--- 88,95 ----
      private int maxConnections = 2;   // Per RFC 2616 sec 8.1.4
      private String proxyHost = null;
      private int proxyPort = -1;
+     private HttpSharedState state = null;
+     private SSLSocketFactory factory = null;
  
      /**
       * No-args constructor
***************
*** 96,101 ****
--- 99,113 ----
      }
  
      /**
+      * This constructor is necessary for making connections through authenticated proxies.
+      * @param state for making connections through authenticated proxies, or null.
+      */
+     public HttpConnectionManager(HttpSharedState state)
+     {
+         this.state = state;
+     }
+     
+     /**
       * Set the proxy host to use for all connections.
       * 
       * @param proxyHost - the proxy host name
***************
*** 157,162 ****
--- 169,183 ----
      }
  
      /**
+      * Allows you to specify a new factory to be used as the default 
+      * for creating SSL sockets. Setting the factory to null will
+      * reset it to using SSLSocketFactory.getDefault().
+      */
+     public void setSSLSocketFactory(SSLSocketFactory factory) {
+         this.factory = factory;
+     }
+     
+     /**
       * Get an HttpConnection for a given URL.  The URL must be fully
       * specified (i.e. contain a protocol and a host (and optional port number).
       * If the maximum number of connections for the host has been reached, this
***************
*** 249,255 ****
                      if(log.isDebugEnabled()){
                          log.debug("HttpConnectionManager.getConnection:  creating connection for " + host + ":" + port + " via " + proxyHost + ":" + proxyPort);
                      }
!                     conn = new HttpConnection(proxyHost, proxyPort, host, port, isSecure);
                      numConnections = new Integer(numConnections.intValue()+1);
                      mapNumConnections.put(key, numConnections);
                  }else{
--- 270,276 ----
                      if(log.isDebugEnabled()){
                          log.debug("HttpConnectionManager.getConnection:  creating connection for " + host + ":" + port + " via " + proxyHost + ":" + proxyPort);
                      }
!                     conn = new HttpConnection(proxyHost, proxyPort, host, port, isSecure, state, factory);
                      numConnections = new Integer(numConnections.intValue()+1);
                      mapNumConnections.put(key, numConnections);
                  }else{
Index: src/java/org/apache/commons/httpclient/HttpMethod.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v
retrieving revision 1.12
diff -c -r1.12 HttpMethod.java
*** src/java/org/apache/commons/httpclient/HttpMethod.java	22 Feb 2002 19:15:54 -0000	1.12
--- src/java/org/apache/commons/httpclient/HttpMethod.java	13 Jun 2002 14:22:16 -0000
***************
*** 262,267 ****
--- 262,290 ----
      public InputStream getResponseBodyAsStream() throws IOException;
  
      /**
+      * Returns the time in milliseconds when a connection was
+      * available and open.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenConnectedMillis();
+     
+     /**
+      * Returns the time in milliseconds when the headers of the request
+      * were sent, not the body of the request. It might first wait for 
+      * a 100 response before sending the body of the request.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenRequestedMillis();
+     
+     /**
+      * Returns the time in milliseconds when the first response was received
+      * from the server, regardless of whether it was a success, failure 
+      * or continue response.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenRespondedMillis();
+     
+     /**
       * Return <tt>true</tt> if I have been {@link #execute executed}
       * but not recycled.
       */
Index: src/java/org/apache/commons/httpclient/HttpMethodBase.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
retrieving revision 1.28
diff -c -r1.28 HttpMethodBase.java
*** src/java/org/apache/commons/httpclient/HttpMethodBase.java	16 Apr 2002 14:30:42 -0000	1.28
--- src/java/org/apache/commons/httpclient/HttpMethodBase.java	13 Jun 2002 14:22:23 -0000
***************
*** 405,410 ****
--- 405,439 ----
      }
  
      /**
+      * Returns the time in milliseconds when a connection was
+      * available and open.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenConnectedMillis() {
+         return whenConnected;
+     }
+     
+     /**
+      * Returns the time in milliseconds when the headers of the request
+      * were sent, not the body of the request. It might first wait for 
+      * a 100 response before sending the body of the request.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenRequestedMillis() {
+         return whenRequested;
+     }
+     
+     /**
+      * Returns the time in milliseconds when the first response was received
+      * from the server, regardless of whether it was a success, failure 
+      * or continue response.
+      * If an error occurred before this event, then it will return <tt>-1</tt>.
+      */
+     public long getWhenRespondedMillis() {
+         return whenResponded;
+     }
+     
+     /**
       * Return <tt>true</tt> if I have been {@link #execute executed}
       * but not recycled.
       */
***************
*** 466,476 ****
--- 495,514 ----
                      connection.open();
                  }
  
+                 if (whenConnected == -1)
+                     whenConnected = System.currentTimeMillis();
+                 
                  writeRequest(state,connection);
                  used = true;
  
+                 if (whenRequested == -1)
+                     whenRequested = System.currentTimeMillis();
+ 
                  // need to close output?, but when?
                  readResponse(state,connection);
+                 
+                 if (whenResponded == -1)
+                     whenResponded = System.currentTimeMillis();
              }catch(HttpRecoverableException e){
                  if(retryCount >= maxRetries){
                      throw new HttpException(e.toString());
***************
*** 539,657 ****
                          continue;
                      }
                  }
-             } else if (HttpStatus.SC_MOVED_TEMPORARILY == statusCode ||
-                HttpStatus.SC_MOVED_PERMANENTLY == statusCode ||
-                HttpStatus.SC_TEMPORARY_REDIRECT == statusCode) {
-                 if (getFollowRedirects()) {
-                     //
-                     // Note that we cannot current support
-                     // redirects that change the HttpConnection
-                     // parameters (host, port, protocol)
-                     // because we don't yet have a good way to
-                     // get the new connection.
-                     //
-                     // For the time being, we just return
-                     // the 302 response, and allow the user
-                     // agent to resubmit if desired.
-                     //
-                     Header location = getResponseHeader("location");
-                     if (location != null) {
-                         URL url = null;
-                         try {
-                             if (location.getValue().startsWith("/")) {
-                                 if (log.isDebugEnabled()) {
-                                     log.debug("Following relative Location header \"" + location + "\".");
-                                 }
-                                 String protocol = connection.isSecure() ? "https" : "http";
-                                 int port = connection.getPort();
-                                 if (-1 == port) {
-                                     port = connection.isSecure() ? 443 : 80;
-                                 }
-                                 url = new URL(protocol,connection.getHost(),port,location.getValue());                                
-                             } else if(!isStrictMode() && location.getValue().indexOf("://") < 0) {
-                                 /*
-                                  * Location doesn't start with / but it doesn't contain a protocol.
-                                  * Per RFC 2616, that's an error.  In non-strict mode we'll try
-                                  * to build a URL relative to the current path.
-                                  */
-                                 String protocol = connection.isSecure() ? "https" : "http";
-                                 int port = connection.getPort();
-                                 if(-1 == port) {
-                                     port = connection.isSecure() ? 443 : 80;
-                                 }
-                                 URL currentUrl = new URL(protocol,connection.getHost(),port,getPath());
-                                 url = new URL(currentUrl, location.getValue());
-                             } else {
-                                 url = new URL(location.getValue());
-                             }                            
-                         } catch(MalformedURLException e) {
-                             log.error("Exception while parsing location header \"" + location + "\"",e);
-                             throw new HttpException(e.toString());
-                         }
-                         if ("http".equalsIgnoreCase(url.getProtocol())) {
-                             if (connection.isSecure()) {
-                                 log.info("Server is attempting to redirect an HTTPS request to an HTTP one.");
-                                 throw new HttpException("Server is attempting to redirect an HTTPS request to an HTTP one.");
-                             }
-                         } else if ("https".equalsIgnoreCase(url.getProtocol())) {
-                             if (!connection.isSecure()) {
-                                 log.info("Server is attempting to convert an HTTP request to an HTTP one, which is currently not supported. Returning " + statusCode + ".");
-                                 break;
-                             }
-                         }
-                         if (!connection.getHost().equalsIgnoreCase(url.getHost())) {
-                             log.info("Server is attempting to redirect a different host, which is currently not supported. Returning " + statusCode + ".");
-                             break;
-                         }
-                         if (url.getPort() == -1) {
-                             if (connection.isSecure()) {
-                                 if (connection.getPort() != 443) {
-                                     log.info("Server is attempting to redirect a different port, which is currently not supported. Returning " + statusCode + ".");
-                                     break;
-                                 }
-                             } else {
-                                 if (connection.getPort() != 80) {
-                                     log.info("Server is attempting to redirect a different port, which is currently not supported. Returning " + statusCode + ".");
-                                     break;
-                                 }
-                             }
-                         } else if (connection.getPort() != url.getPort()) {
-                             log.info("Server is attempting to redirect a different port, which is currently not supported. Returning " + statusCode + ".");
-                             break;
-                         }
-                         String absolutePath = URIUtil.getPath(url.toString());
-                         String qs = URIUtil.getQueryString(url.toString());
- 
-                         // if we haven't already, let's try it again with the new path
-                         if (visited.contains(connection.getHost() + ":" + connection.getPort() + "|" + HttpMethodBase.generateRequestLine(connection, getName(),absolutePath,qs,(http11 ? "HTTP/1.1" : "HTTP/1.0")))) {
-                             throw new HttpException("Redirect going into a loop, visited \"" + absolutePath + "\" already.");
-                         } else {
-                             if (log.isDebugEnabled()) {
-                                 log.debug("Changing path from \"" + getPath() + "\" to \"" + absolutePath + "\" in response to " + statusCode + " response.");
-                                 log.debug("Changing query string from \"" + getQueryString() + "\" to \"" + qs + "\" in response to " + statusCode + " response.");
-                             }
-                             setPath(URIUtil.decode(absolutePath));
-                             setQueryString(qs);
-                             continue;
-                         }
-                     } else {
-                         // got a redirect response, but no location header
-                         if (log.isInfoEnabled()) {
-                             log.info("HttpMethodBase.execute(): Received " + statusCode + " response, but no \"Location\" header. Returning " + statusCode + ".");
-                         }
-                         break;
-                     }
-                 } else {
-                     // got a redirect response,
-                     // but followRedirects is false
-                     log.info("HttpMethodBase.execute(): Received " + statusCode + " response, but followRedirects is false. Returning " + statusCode + ".");
-                     break;
-                 }
-             } else {
-                 // neither an UNAUTHORIZED nor a redirect response
-                 // so exit
-                 break;
              }
          }
  
          return statusCode;
--- 577,587 ----
                          continue;
                      }
                  }
              }
+             // Moved the handling of redirects to HttpMultiClient. Replaced all the 
+             // code to build a URL relative to the original URL with:
+             // URL newUrl = new URL( URL context, String newUrlStr ) --Evert
+             break;
          }
  
          return statusCode;
***************
*** 789,795 ****
      protected void addHostRequestHeader(HttpState state, HttpConnection conn) throws IOException, HttpException {
          // add host (should do this conditionally?, i.e., don't send to http/1.0?)
          if (!requestHeaders.containsKey("host")) {
!             setRequestHeader("Host",conn.getHost());
          }
      }
  
--- 719,725 ----
      protected void addHostRequestHeader(HttpState state, HttpConnection conn) throws IOException, HttpException {
          // add host (should do this conditionally?, i.e., don't send to http/1.0?)
          if (!requestHeaders.containsKey("host")) {
!             setRequestHeader("Host", conn.getHost() + ":" + conn.getPort());
          }
      }
  
***************
*** 1239,1244 ****
--- 1169,1177 ----
          http11 = true;
          bodySent = false;
          responseBody = null;
+         whenConnected = -1;
+         whenRequested = -1;
+         whenResponded = -1;
      }
  
      // ---------------------------------------------- Protected Utility Methods
***************
*** 1299,1312 ****
              return (name + " " + buf.toString() + " " + protocol + "\r\n");
          } else {
              if (connection.isSecure()) {
!                 return (name +
!                        " https://"; +
!                        connection.getHost() +
!                        ((443 == connection.getPort() || -1 == connection.getPort()) ? "" : (":" + connection.getPort()) ) +
!                        buf.toString() +
!                        " " +
!                        protocol +
!                        "\r\n");
              } else {
                  return (name +
                         " http://"; +
--- 1232,1246 ----
              return (name + " " + buf.toString() + " " + protocol + "\r\n");
          } else {
              if (connection.isSecure()) {
! //                return (name +
! //                       " https://"; +
! //                       connection.getHost() +
! //                       ((443 == connection.getPort() || -1 == connection.getPort()) ? "" : (":" + connection.getPort()) ) +
! //                       buf.toString() +
! //                       " " +
! //                       protocol +
! //                       "\r\n");
!                 return (name + " " + buf.toString() + " " + protocol + "\r\n"); 
              } else {
                  return (name +
                         " http://"; +
***************
*** 1367,1372 ****
--- 1301,1312 ----
      private int maxRetries = 3;
      /** True if we're in strict mode. */
      private boolean strictMode = true;
+     /** The moment when an open connection is available. **/
+     private long whenConnected = -1;
+     /** The moment when the request headers have been sent, not the request body. **/
+     private long whenRequested = -1;
+     /** The moment of the first response received back from the server. **/
+     private long whenResponded = -1;
  
      // -------------------------------------------------------------- Constants
  
Index: src/java/org/apache/commons/httpclient/HttpMultiClient.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMultiClient.java,v
retrieving revision 1.4
diff -c -r1.4 HttpMultiClient.java
*** src/java/org/apache/commons/httpclient/HttpMultiClient.java	9 May 2002 19:30:22 -0000	1.4
--- src/java/org/apache/commons/httpclient/HttpMultiClient.java	13 Jun 2002 14:22:24 -0000
***************
*** 63,70 ****
--- 63,74 ----
  package org.apache.commons.httpclient;
  
  import java.io.IOException;
+ import java.net.MalformedURLException;
+ import java.net.URL;
+ import javax.net.ssl.SSLSocketFactory;
  
  import org.apache.commons.httpclient.log.*;
+ import org.apache.commons.httpclient.methods.PostMethod;
  
  /**
   *
***************
*** 85,91 ****
  
      // ----------------------------------------------------- Instance Variables
      private HttpSharedState state = null;
!     private HttpConnectionManager mgr = new HttpConnectionManager();
      private boolean strictMode = true;
      private int timeoutConnection = 0;
      private int timeoutRequest = 0;
--- 89,95 ----
  
      // ----------------------------------------------------- Instance Variables
      private HttpSharedState state = null;
!     private HttpConnectionManager mgr = null;
      private boolean strictMode = true;
      private int timeoutConnection = 0;
      private int timeoutRequest = 0;
***************
*** 100,105 ****
--- 104,110 ----
       */
      public HttpMultiClient()
      {
+         mgr = new HttpConnectionManager(getState());
      }
  
      /**
***************
*** 255,260 ****
--- 260,285 ----
      }
  
      /**
+      * Set the maximum number of connections allowed for a given host:port.
+      * Per RFC 2616 section 8.1.4, this value defaults to 2.
+      *
+      * @param maxConnections - number of connections allowed for each host:port
+      */
+     public void setMaxConnectionsPerHost(int maxConnections)
+     {
+         mgr.setMaxConnectionsPerHost(maxConnections);
+     }
+ 
+     /**
+      * Allows you to specify a new factory to be used as the default 
+      * for creating SSL sockets. Setting the factory to null will
+      * reset it to using SSLSocketFactory.getDefault().
+      */
+     public void setSSLSocketFactory(SSLSocketFactory factory) {
+         mgr.setSSLSocketFactory(factory);
+     }
+     
+     /**
       *
       * Execute the given {@link HttpUrlMethod} using my current
       * {@link HttpConnection connection} and {@link HttpState}.  
***************
*** 268,279 ****
--- 293,313 ----
       */
      public int executeMethod(HttpUrlMethod method) throws IOException, HttpException
      {
+         return executeMethod(method, 0);
+     }
  
+     protected int executeMethod(HttpUrlMethod method, int numberOfRedirects) 
+     throws IOException, HttpException
+     {
          if (null == method)
          {
              throw new NullPointerException("method parameter");
          }
  
+         if(numberOfRedirects > 10)
+         {
+             throw new HttpException("Redirected more than 10 times.");
+         }
          HttpConnection connection = mgr.getConnection(method.getUrl(), timeoutConnection);
          connection.setSoTimeout(timeoutRequest);
          
***************
*** 298,305 ****
              mgr.releaseConnection(connection);
          }
  
!         if (status == 301 || status == 302 || 
!             status == 303 || status == 307)
          {
              Header header = method.getResponseHeader("Location");
              String url = header.getValue();
--- 332,340 ----
              mgr.releaseConnection(connection);
          }
  
!         if (method.getFollowRedirects() &&
!             (status == 301 || status == 302 || 
!             status == 303 || status == 307))
          {
              Header header = method.getResponseHeader("Location");
              String url = header.getValue();
***************
*** 308,320 ****
                  log.error("HttpMultiClient.executeMethod:  Received redirect without Location header.");
                  throw new HttpException("Received redirect without Location header.");
              }
! 
              method.recycle();
!             method.setUrl(url);
!             return executeMethod(method);
          }
  
          return status;
      }
- 
  }
--- 343,376 ----
                  log.error("HttpMultiClient.executeMethod:  Received redirect without Location header.");
                  throw new HttpException("Received redirect without Location header.");
              }
!             
!             log.debug("HttpMultiClient.executeMethod: Following redirect to: " + url);
!     
!             String oldUrlStr = method.getUrl();
!             String oldRequestBody = null;
!             if (method instanceof PostMethod) 
!             {
!                 oldRequestBody = ((PostMethod)method).getRequestBody();
!             }
!             URL oldUrl = null;
!             try 
!             {
!                 oldUrl = new URL(oldUrlStr);
!             } catch (MalformedURLException e) 
!             {
!                 // This means the original url was also malformed. But, if that
!                 // was the case we should never have gotten this far.
!                 log.debug("HttpMultiClient.executemethod: The original url was malformed: " + e);
!                 throw e;
!             }
!             URL newUrl = new java.net.URL(oldUrl, url);
              method.recycle();
!             method.setUrl(newUrl.toString());
!             if (method instanceof PostMethod)
!                 ((PostMethod)method).setRequestBody(oldRequestBody);
!             return executeMethod(method, numberOfRedirects++);
          }
  
          return status;
      }
  }
Index: src/java/org/apache/commons/httpclient/methods/PostMethod.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v
retrieving revision 1.9
diff -c -r1.9 PostMethod.java
*** src/java/org/apache/commons/httpclient/methods/PostMethod.java	24 Apr 2002 14:44:50 -0000	1.9
--- src/java/org/apache/commons/httpclient/methods/PostMethod.java	13 Jun 2002 14:22:26 -0000
***************
*** 236,241 ****
--- 236,248 ----
          }
      }
  
+    /**
+      * Returns the post data.
+      */
+     public String getRequestBody() {
+         return requestBody;
+     }
+ 
      /**
       * @throws IllegalStateException if request params have been added
       */

Attachment: changes_evert.zip
Description: Zip archive

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to