Java Source Code: com.limegroup.gnutella.http.HttpClientManager


   1: package com.limegroup.gnutella.http;
   2: 
   3: import java.io.IOException;
   4: import java.net.InetAddress;
   5: import java.net.Socket;
   6: import java.net.UnknownHostException;
   7: 
   8: import org.apache.commons.httpclient.Header;
   9: import org.apache.commons.httpclient.HostConfiguration;
  10: import org.apache.commons.httpclient.HttpClient;
  11: import org.apache.commons.httpclient.HttpConnectionManager;
  12: import org.apache.commons.httpclient.HttpException;
  13: import org.apache.commons.httpclient.HttpMethod;
  14: import org.apache.commons.httpclient.HttpStatus;
  15: import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
  16: import org.apache.commons.httpclient.URI;
  17: import org.apache.commons.httpclient.auth.HttpAuthenticator;
  18: import org.apache.commons.httpclient.protocol.Protocol;
  19: import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
  20: import org.apache.commons.logging.Log;
  21: import org.apache.commons.logging.LogFactory;
  22: 
  23: import com.limegroup.gnutella.settings.ConnectionSettings;
  24: import com.limegroup.gnutella.util.Sockets;
  25: 
  26: 
  27: /**
  28:  * A simple manager class that maintains a single HttpConnectionManager
  29:  * and doles out either a simple one (for Java 1.1.8) or the MultiThreaded
  30:  * one (for all other versions)
  31:  */
  32:	  public class HttpClientManager {
  33:    
  34:    private static final Log LOG = LogFactory.getLog(HttpClientManager.class);
  35:    
  36:    /**
  37:     * The amount of time to wait while trying to connect to a specified
  38:     * host via TCP.  If we exceed this value, an IOException is thrown
  39:     * while trying to connect.
  40:     */
  41:    private static final int CONNECTION_TIMEOUT = 5000;
  42:    
  43:    /**
  44:     * The amount of time to wait while receiving data from a specified
  45:     * host.  Used as an SO_TIMEOUT.
  46:     */
  47:    private static final int TIMEOUT = 8000;
  48:    
  49:    /**
  50:     * The maximum number of times to allow redirects from hosts.
  51:     */
  52:    private static final int MAXIMUM_REDIRECTS = 10;
  53:    
  54:    /**
  55:     * The time to allow a connection to sit idle, waiting for something
  56:     * to reuse it.
  57:     */
  58:    private static final long IDLE_TIME = 30 * 1000; // 30 seconds.
  59:    
  60:    /**
  61:     * The manager which all client connections use if not Java 1.1.8;
  62:     */
  63:    private static final HttpConnectionManager MANAGER;
  64:    
  65:	      static {
  66:        MANAGER = new MultiThreadedHttpConnectionManager();
  67:        ((MultiThreadedHttpConnectionManager)MANAGER).
  68:            setIdleConnectionTime(IDLE_TIME);
  69:        Protocol limeProtocol = new Protocol("http",new LimeSocketFactory(),80);
  70:        Protocol.registerProtocol("http",limeProtocol);
  71:    }
  72:            
  73:    /**
  74:     * Returns a new HttpClient with the appropriate manager.
  75:     */
  76:	      public static HttpClient getNewClient() {
  77:        return getNewClient(CONNECTION_TIMEOUT, TIMEOUT);
  78:    }
  79:    
  80:    /**
  81:     * Returns a new HttpClient with the appropriate manager and parameters.
  82:     * 
  83:     * @param connectTimeout the number of milliseconds to wait to establish
  84:     *  a TCP connection with the remote host
  85:     * @param soTimeout the socket timeout -- the number of milliseconds to 
  86:     *  wait for data before closing an established socket
  87:     */
  88:	      public static HttpClient getNewClient(int connectTimeout, int soTimeout) {
  89:        HttpClient client = new HttpClient(MANAGER);
  90:        client.setConnectionTimeout(connectTimeout);
  91:        client.setTimeout(soTimeout);
  92:        return client;
  93:    }
  94:    
  95:    /**
  96:     * Executes the given HttpMethod in the HttpClient, following redirects.
  97:     * This method is needed because HttpClient does not support redirects
  98:     * across protocols, hosts, and/or ports.
  99:     */
 100:    public static void executeMethodRedirecting(HttpClient client,
 101:                                                HttpMethod methid)
 102:	        throws IOException, HttpException {
 103:        executeMethodRedirecting(client, methid, MAXIMUM_REDIRECTS);
 104:    }
 105:    
 106:    /**
 107:     * Executes the given HttpMethod in the HttpClient, following redirecits
 108:     * up to the specific number of times.
 109:     * This method is needed because HttpClient does not support redirects
 110:     * across protocols, hosts, and/or ports.
 111:     */
 112:    public static void executeMethodRedirecting(HttpClient client,
 113:                                                HttpMethod methid,
 114:                                                int redirects)
 115:	        throws IOException, HttpException {
 116:	          for(int i = 0; i < redirects; i++) {
 117:            if(LOG.isInfoEnabled())
 118:                LOG.info("Attempting connection (" + i + ") to " + 
 119:                         methid.getURI().getEscapedURI());
 120:            client.executeMethod(methid);
 121:	              switch(methid.getStatusCode()) {
 122:            case HttpStatus.SC_MOVED_TEMPORARILY:
 123:            case HttpStatus.SC_MOVED_PERMANENTLY:
 124:            case HttpStatus.SC_SEE_OTHER:
 125:            case HttpStatus.SC_TEMPORARY_REDIRECT:
 126:	                  if(!methid.getFollowRedirects()) {
 127:                    if(LOG.isInfoEnabled())
 128:                        LOG.warn("Redirect requested but not supported");
 129:                    throw new HttpException("Redirect requested");
 130:                }
 131:                
 132:                Header locationHeader = methid.getResponseHeader("location");
 133:	                  if(locationHeader == null) {
 134:                    if(LOG.isInfoEnabled())
 135:                        LOG.warn("Redirect requested, no location header");
 136:                    throw new HttpException("Redirected without a location");
 137:                }
 138:                
 139:                String location = locationHeader.getValue();
 140:                if(LOG.isInfoEnabled())
 141:                    LOG.info("Redirected requested to: " + location);
 142:
 143:                URI newLocation = new URI(location.toCharArray());
 144:                
 145:                // Retrieve the RequestHeaders
 146:                Header[] requestHeaders = methid.getRequestHeaders();
 147:                
 148:                // Recycle this method so we can use it again.
 149:                methid.recycle();
 150:                
 151:                HostConfiguration hc = methid.getHostConfiguration();
 152:                hc.setHost(
 153:                    newLocation.getHost(),
 154:                    newLocation.getPort(),
 155:                    newLocation.getScheme()
 156:                );
 157:                
 158:                methid.setFollowRedirects(true);
 159:                
 160:	                  for(int j = 0; j < requestHeaders.length; j++) {
 161:                    if(!requestHeaders[j].getName().equals("Host"))
 162:                        methid.addRequestHeader(requestHeaders[j]);
 163:                }
 164:                
 165:                // Set up the new values for the method.
 166:                methid.setPath(newLocation.getEscapedPath());
 167:                methid.setQueryString(newLocation.getEscapedQuery());
 168:                methid.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP);
 169:
 170:                // Loop around and try the method again.
 171:                break;
 172:            default:
 173:                return;
 174:            }
 175:        }
 176:        throw new HttpException("Maximum redirects encountered, bailing");
 177:    }
 178:    
 179:	      private static class LimeSocketFactory implements ProtocolSocketFactory {
 180:
 181:        public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) 
 182:	          throws IOException, UnknownHostException {
 183:            return Sockets.connect(host,port,0);
 184:        }
 185:
 186:	          public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
 187:            return Sockets.connect(host,port,0);
 188:        }
 189:        
 190:	          public Socket createSocket(String host, int port, int timeout) throws IOException, UnknownHostException {
 191:            return Sockets.connect(host,port, timeout);
 192:        }
 193:        
 194:    }
 195:    
 196:}