Author: jnioche
Date: Tue Jul 20 15:42:23 2010
New Revision: 965887

URL: http://svn.apache.org/viewvc?rev=965887&view=rev
Log:
ported protocol-httpclient to the new API

Modified:
    nutch/branches/nutchbase/src/plugin/build.xml
    
nutch/branches/nutchbase/src/plugin/lib-http/src/java/org/apache/nutch/protocol/http/api/HttpBase.java
    
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/Http.java
    
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/HttpResponse.java
    
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/test/org/apache/nutch/protocol/httpclient/TestProtocolHttpClient.java

Modified: nutch/branches/nutchbase/src/plugin/build.xml
URL: 
http://svn.apache.org/viewvc/nutch/branches/nutchbase/src/plugin/build.xml?rev=965887&r1=965886&r2=965887&view=diff
==============================================================================
--- nutch/branches/nutchbase/src/plugin/build.xml (original)
+++ nutch/branches/nutchbase/src/plugin/build.xml Tue Jul 20 15:42:23 2010
@@ -40,6 +40,7 @@
      <ant dir="protocol-ftp" target="deploy"/>
      <ant dir="protocol-file" target="deploy"/>
      <ant dir="protocol-http" target="deploy"/>
+     <ant dir="protocol-httpclient" target="deploy"/>
      <ant dir="parse-js" target="deploy"/>
      <ant dir="parse-rss" target="deploy"/>
      <ant dir="parse-tika" target="deploy"/>
@@ -61,7 +62,6 @@
      <ant dir="parse-ext" target="deploy"/>
      <ant dir="parse-swf" target="deploy"/>
      <ant dir="parse-zip" target="deploy"/>
-     <ant dir="protocol-httpclient" target="deploy"/>
      -->
   </target>
 
@@ -76,6 +76,7 @@
      <ant dir="parse-rss" target="test"/>
      <ant dir="index-more" target="test"/>
      <ant dir="languageidentifier" target="test"/>
+     <ant dir="protocol-httpclient" target="test"/>
      <ant dir="urlfilter-automaton" target="test"/>
      <ant dir="urlfilter-domain" target="test" />
      <ant dir="urlfilter-regex" target="test"/>
@@ -91,7 +92,6 @@
      <ant dir="parse-ext" target="test"/>
      <ant dir="parse-swf" target="test"/>
      <ant dir="parse-zip" target="test"/>
-     <ant dir="protocol-httpclient" target="test"/>
       -->
   </target>
 

Modified: 
nutch/branches/nutchbase/src/plugin/lib-http/src/java/org/apache/nutch/protocol/http/api/HttpBase.java
URL: 
http://svn.apache.org/viewvc/nutch/branches/nutchbase/src/plugin/lib-http/src/java/org/apache/nutch/protocol/http/api/HttpBase.java?rev=965887&r1=965886&r2=965887&view=diff
==============================================================================
--- 
nutch/branches/nutchbase/src/plugin/lib-http/src/java/org/apache/nutch/protocol/http/api/HttpBase.java
 (original)
+++ 
nutch/branches/nutchbase/src/plugin/lib-http/src/java/org/apache/nutch/protocol/http/api/HttpBase.java
 Tue Jul 20 15:42:23 2010
@@ -37,6 +37,7 @@ import org.apache.nutch.protocol.RobotRu
 import org.apache.nutch.storage.ProtocolStatusUtils;
 import org.apache.nutch.storage.WebPage;
 import org.apache.nutch.util.GZIPUtils;
+import org.apache.nutch.util.DeflateUtils;
 import org.apache.nutch.util.LogUtil;
 import org.apache.nutch.util.MimeUtil;
 
@@ -89,6 +90,9 @@ public abstract class HttpBase implement
   "[email protected]");
 
 
+  /** The "Accept-Language" request header value. */
+  protected String acceptLanguage = "en-us,en-gb,en;q=0.7,*;q=0.3";
+    
   /**
    * Maps from host to a Long naming the time it should be unblocked.
    * The Long is zero while the host is in use, then set to now+wait when
@@ -161,6 +165,7 @@ public abstract class HttpBase implement
     this.maxThreadsPerHost = conf.getInt("fetcher.threads.per.host", 1);
     this.userAgent = getAgentString(conf.get("http.agent.name"), 
conf.get("http.agent.version"), conf
         .get("http.agent.description"), conf.get("http.agent.url"), 
conf.get("http.agent.email"));
+    this.acceptLanguage = conf.get("http.accept.language", acceptLanguage);
     this.serverDelay = (long) (conf.getFloat("fetcher.server.delay", 1.0f) * 
1000);
     this.maxCrawlDelay = (conf.getInt("fetcher.max.crawl.delay", -1) * 1000);
     // backward-compatible default setting
@@ -329,6 +334,13 @@ public abstract class HttpBase implement
   public String getUserAgent() {
     return userAgent;
   }
+  
+  /** Value of "Accept-Language" request header sent by Nutch.
+   * @return The value of the header "Accept-Language" header.
+   */
+  public String getAcceptLanguage() {
+         return acceptLanguage;
+  }
 
   public boolean getUseHttp11() {
     return useHttp11;
@@ -474,6 +486,7 @@ public abstract class HttpBase implement
       logger.info("http.timeout = " + timeout);
       logger.info("http.content.limit = " + maxContent);
       logger.info("http.agent = " + userAgent);
+      logger.info("http.accept.language = " + acceptLanguage);
       logger.info(Protocol.CHECK_BLOCKING + " = " + checkBlocking);
       logger.info(Protocol.CHECK_ROBOTS + " = " + checkRobots);
       if (checkBlocking) {
@@ -505,6 +518,23 @@ public abstract class HttpBase implement
     return content;
   }
 
+  public byte[] processDeflateEncoded(byte[] compressed, URL url) throws 
IOException {
+
+    if (LOGGER.isTraceEnabled()) { LOGGER.trace("inflating...."); }
+
+    byte[] content = DeflateUtils.inflateBestEffort(compressed, 
getMaxContent());
+
+    if (content == null)
+      throw new IOException("inflateBestEffort returned null");
+
+    if (LOGGER.isTraceEnabled()) {
+      LOGGER.trace("fetched " + compressed.length
+                 + " bytes of compressed content (expanded to "
+                 + content.length + " bytes) from " + url);
+    }
+    return content;
+  }
+
   protected static void main(HttpBase http, String[] args) throws Exception {
     @SuppressWarnings("unused")
     boolean verbose = false;

Modified: 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/Http.java
URL: 
http://svn.apache.org/viewvc/nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/Http.java?rev=965887&r1=965886&r2=965887&view=diff
==============================================================================
--- 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/Http.java
 (original)
+++ 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/Http.java
 Tue Jul 20 15:42:23 2010
@@ -21,6 +21,9 @@ import java.io.InputStream;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 import org.xml.sax.SAXException;
@@ -44,8 +47,9 @@ import org.apache.commons.httpclient.par
 import org.apache.commons.httpclient.protocol.Protocol;
 
 // Nutch imports
+import org.apache.nutch.storage.WebPage;
+import org.apache.nutch.storage.WebPage.Field;
 import org.apache.nutch.util.LogUtil;
-import org.apache.nutch.crawl.CrawlDatum;
 import org.apache.nutch.net.protocols.Response;
 import org.apache.nutch.protocol.ProtocolException;
 import org.apache.nutch.protocol.http.api.HttpBase;
@@ -53,377 +57,406 @@ import org.apache.hadoop.conf.Configurat
 import org.apache.nutch.util.NutchConfiguration;
 
 /**
- * This class is a protocol plugin that configures an HTTP client for
- * Basic, Digest and NTLM authentication schemes for web server as well
- * as proxy server. It takes care of HTTPS protocol as well as cookies
- * in a single fetch session.
- *
+ * This class is a protocol plugin that configures an HTTP client for Basic,
+ * Digest and NTLM authentication schemes for web server as well as proxy
+ * server. It takes care of HTTPS protocol as well as cookies in a single fetch
+ * session.
+ * 
  * @author Susam Pal
  */
 public class Http extends HttpBase {
 
-  public static final Log LOG = LogFactory.getLog(Http.class);
+       public static final Log LOG = LogFactory.getLog(Http.class);
 
-  private static MultiThreadedHttpConnectionManager connectionManager =
-          new MultiThreadedHttpConnectionManager();
+       private static MultiThreadedHttpConnectionManager connectionManager = 
new MultiThreadedHttpConnectionManager();
 
-  // Since the Configuration has not yet been set,
-  // then an unconfigured client is returned.
-  private static HttpClient client = new HttpClient(connectionManager);
-  private static String defaultUsername;
-  private static String defaultPassword;
-  private static String defaultRealm;
-  private static String defaultScheme;
-  private static String authFile;
-  private static String agentHost;
-  private static boolean authRulesRead = false;
-  private static Configuration conf;
-
-  int maxThreadsTotal = 10;
-
-  private String proxyUsername;
-  private String proxyPassword;
-  private String proxyRealm;
-
-
-  /**
-   * Returns the configured HTTP client.
-   *
-   * @return HTTP client
-   */
-  static synchronized HttpClient getClient() {
-    return client;
-  }
-
-  /**
-   * Constructs this plugin.
-   */
-  public Http() {
-    super(LOG);
-  }
-
-  /**
-   * Reads the configuration from the Nutch configuration files and sets
-   * the configuration.
-   *
-   * @param conf Configuration
-   */
-  public void setConf(Configuration conf) {
-    super.setConf(conf);
-    this.conf = conf;
-    this.maxThreadsTotal = conf.getInt("fetcher.threads.fetch", 10);
-    this.proxyUsername = conf.get("http.proxy.username", "");
-    this.proxyPassword = conf.get("http.proxy.password", "");
-    this.proxyRealm = conf.get("http.proxy.realm", "");
-    agentHost = conf.get("http.agent.host", "");
-    authFile = conf.get("http.auth.file", "");
-    configureClient();
-    try {
-      setCredentials();
-    } catch (Exception ex) {
-      if (LOG.isFatalEnabled()) {
-        LOG.fatal("Could not read " + authFile + " : " + ex.getMessage());
-        ex.printStackTrace(LogUtil.getErrorStream(LOG));
-      }
-    }
-  }
-
-  /**
-   * Main method.
-   *
-   * @param args Command line arguments
-   */
-  public static void main(String[] args) throws Exception {
-    Http http = new Http();
-    http.setConf(NutchConfiguration.create());
-    main(http, args);
-  }
-
-  /**
-   * Fetches the <code>url</code> with a configured HTTP client and
-   * gets the response.
-   *
-   * @param url       URL to be fetched
-   * @param datum     Crawl data
-   * @param redirect  Follow redirects if and only if true
-   * @return          HTTP response
-   */
-  protected Response getResponse(URL url, CrawlDatum datum, boolean redirect)
-    throws ProtocolException, IOException {
-    resolveCredentials(url);
-    return new HttpResponse(this, url, datum, redirect);
-  }
-
-  /**
-   * Configures the HTTP client
-   */
-  private void configureClient() {
-
-    // Set up an HTTPS socket factory that accepts self-signed certs.
-    Protocol https = new Protocol("https",
-        new DummySSLProtocolSocketFactory(), 443);
-    Protocol.registerProtocol("https", https);
-
-    HttpConnectionManagerParams params = connectionManager.getParams();
-    params.setConnectionTimeout(timeout);
-    params.setSoTimeout(timeout);
-    params.setSendBufferSize(BUFFER_SIZE);
-    params.setReceiveBufferSize(BUFFER_SIZE);
-    params.setMaxTotalConnections(maxThreadsTotal);
-    if (maxThreadsTotal > maxThreadsPerHost) {
-      params.setDefaultMaxConnectionsPerHost(maxThreadsPerHost);
-    } else {
-      params.setDefaultMaxConnectionsPerHost(maxThreadsTotal);
-    }
-
-    // executeMethod(HttpMethod) seems to ignore the connection timeout on the 
connection manager.
-    // set it explicitly on the HttpClient.
-    client.getParams().setConnectionManagerTimeout(timeout);
-
-    HostConfiguration hostConf = client.getHostConfiguration();
-    ArrayList headers = new ArrayList();
-    // Set the User Agent in the header
-    headers.add(new Header("User-Agent", userAgent));
-    // prefer English
-    headers.add(new Header("Accept-Language", "en-us,en-gb,en;q=0.7,*;q=0.3"));
-    // prefer UTF-8
-    headers.add(new Header("Accept-Charset", 
"utf-8,ISO-8859-1;q=0.7,*;q=0.7"));
-    // prefer understandable formats
-    headers.add(new Header("Accept",
-            
"text/html,application/xml;q=0.9,application/xhtml+xml,text/xml;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"));
-    // accept gzipped content
-    headers.add(new Header("Accept-Encoding", "x-gzip, gzip, deflate"));
-    hostConf.getParams().setParameter("http.default-headers", headers);
-
-    // HTTP proxy server details
-    if (useProxy) {
-      hostConf.setProxy(proxyHost, proxyPort);
-
-      if (proxyUsername.length() > 0) {
-
-        AuthScope proxyAuthScope = getAuthScope(
-            this.proxyHost, this.proxyPort, this.proxyRealm);
-
-        NTCredentials proxyCredentials = new NTCredentials(
-            this.proxyUsername, this.proxyPassword,
-            this.agentHost, this.proxyRealm);
-
-        client.getState().setProxyCredentials(
-            proxyAuthScope, proxyCredentials);
-      }
-    }
-
-  }
-
-  /**
-   * Reads authentication configuration file (defined as
-   * 'http.auth.file' in Nutch configuration file) and sets the
-   * credentials for the configured authentication scopes in the HTTP
-   * client object.
-   *
-   * @throws ParserConfigurationException  If a document builder can not
-   *                                       be created.
-   * @throws SAXException                  If any parsing error occurs.
-   * @throws IOException                   If any I/O error occurs.
-   */
-  private static synchronized void setCredentials() throws 
-      ParserConfigurationException, SAXException, IOException {
-
-    if (authRulesRead)
-      return;
-
-    authRulesRead = true; // Avoid re-attempting to read
-
-    InputStream is = conf.getConfResourceAsInputStream(authFile);    
-    if (is != null) {
-      Document doc = DocumentBuilderFactory.newInstance()
-                     .newDocumentBuilder().parse(is);
-
-      Element rootElement = doc.getDocumentElement();
-      if (!"auth-configuration".equals(rootElement.getTagName())) {
-        if (LOG.isWarnEnabled())
-          LOG.warn("Bad auth conf file: root element <"
-              + rootElement.getTagName() + "> found in " + authFile
-              + " - must be <auth-configuration>");
-      }
-
-      // For each set of credentials
-      NodeList credList = rootElement.getChildNodes();
-      for (int i = 0; i < credList.getLength(); i++) {
-        Node credNode = credList.item(i);
-        if (!(credNode instanceof Element))
-          continue;    
-
-        Element credElement = (Element) credNode;
-        if (!"credentials".equals(credElement.getTagName())) {
-          if (LOG.isWarnEnabled())
-            LOG.warn("Bad auth conf file: Element <"
-            + credElement.getTagName() + "> not recognized in "
-            + authFile + " - expected <credentials>");
-          continue;
-        }
-
-        String username = credElement.getAttribute("username");
-        String password = credElement.getAttribute("password");
-
-        // For each authentication scope
-        NodeList scopeList = credElement.getChildNodes();
-        for (int j = 0; j < scopeList.getLength(); j++) {
-          Node scopeNode = scopeList.item(j);
-          if (!(scopeNode instanceof Element))
-            continue;
-          
-          Element scopeElement = (Element) scopeNode;
-
-          if ("default".equals(scopeElement.getTagName())) {
-
-            // Determine realm and scheme, if any
-            String realm = scopeElement.getAttribute("realm");
-            String scheme = scopeElement.getAttribute("scheme");
-
-            // Set default credentials
-            defaultUsername = username;
-            defaultPassword = password;
-            defaultRealm = realm;
-            defaultScheme = scheme;
-
-            if (LOG.isTraceEnabled()) {
-              LOG.trace("Credentials - username: " + username 
-                  + "; set as default"
-                  + " for realm: " + realm + "; scheme: " + scheme);
-            }
-
-          } else if ("authscope".equals(scopeElement.getTagName())) {
-
-            // Determine authentication scope details
-            String host = scopeElement.getAttribute("host");
-            int port = -1; // For setting port to AuthScope.ANY_PORT
-            try {
-              port = Integer.parseInt(
-                  scopeElement.getAttribute("port"));
-            } catch (Exception ex) {
-              // do nothing, port is already set to any port
-            }
-            String realm = scopeElement.getAttribute("realm");
-            String scheme = scopeElement.getAttribute("scheme");
-
-            // Set credentials for the determined scope
-            AuthScope authScope = getAuthScope(host, port, realm, scheme);
-            NTCredentials credentials = new NTCredentials(
-                username, password, agentHost, realm);
-
-            client.getState().setCredentials(authScope, credentials);
-
-            if (LOG.isTraceEnabled()) {
-              LOG.trace("Credentials - username: " + username
-                  + "; set for AuthScope - " + "host: " + host
-                  + "; port: " + port + "; realm: " + realm
-                  + "; scheme: " + scheme);
-            }
-
-          } else {
-            if (LOG.isWarnEnabled())
-              LOG.warn("Bad auth conf file: Element <"
-                  + scopeElement.getTagName() + "> not recognized in "
-                  + authFile + " - expected <authscope>");
-          }
-        }
-        is.close();
-      }
-    }
-  }
-
-  /**
-   * If credentials for the authentication scope determined from the
-   * specified <code>url</code> is not already set in the HTTP client,
-   * then this method sets the default credentials to fetch the
-   * specified <code>url</code>. If credentials are found for the
-   * authentication scope, the method returns without altering the
-   * client.
-   *
-   * @param url URL to be fetched
-   */
-  private void resolveCredentials(URL url) {
-
-    if (defaultUsername != null && defaultUsername.length() > 0) {
-
-      int port = url.getPort();
-      if (port == -1) {
-        if ("https".equals(url.getProtocol()))
-          port = 443;
-        else
-          port = 80;
-      }
-
-      AuthScope scope = new AuthScope(url.getHost(), port);
-
-      if (client.getState().getCredentials(scope) != null) {
-        if (LOG.isTraceEnabled())
-          LOG.trace("Pre-configured credentials with scope - host: "
-              + url.getHost() + "; port: " + port
-              + "; found for url: " + url);
-
-        // Credentials are already configured, so do nothing and return
-        return;
-      }
-
-      if (LOG.isTraceEnabled())
-          LOG.trace("Pre-configured credentials with scope -  host: "
-              + url.getHost() + "; port: " + port
-              + "; not found for url: " + url);
-
-      AuthScope serverAuthScope = getAuthScope(
-          url.getHost(), port, defaultRealm, defaultScheme);
-
-      NTCredentials serverCredentials = new NTCredentials(
-          defaultUsername, defaultPassword,
-          agentHost, defaultRealm);
-
-      client.getState().setCredentials(
-          serverAuthScope, serverCredentials);
-    }
-  }
-
-  /**
-   * Returns an authentication scope for the specified
-   * <code>host</code>, <code>port</code>, <code>realm</code> and
-   * <code>scheme</code>.
-   *
-   * @param host    Host name or address.
-   * @param port    Port number.
-   * @param realm   Authentication realm.
-   * @param scheme  Authentication scheme.
-   */
-  private static AuthScope getAuthScope(String host, int port,
-      String realm, String scheme) {
-    
-    if (host.length() == 0)
-      host = null;
-
-    if (port < 0)
-      port = -1;
-
-    if (realm.length() == 0)
-      realm = null;
-
-    if (scheme.length() == 0)
-      scheme = null;
-
-    return new AuthScope(host, port, realm, scheme);
-  }
-
-  /**
-   * Returns an authentication scope for the specified
-   * <code>host</code>, <code>port</code> and <code>realm</code>.
-   *
-   * @param host    Host name or address.
-   * @param port    Port number.
-   * @param realm   Authentication realm.
-   */
-  private static AuthScope getAuthScope(String host, int port,
-      String realm) {
+       // Since the Configuration has not yet been set,
+       // then an unconfigured client is returned.
+       private static HttpClient client = new HttpClient(connectionManager);
+       private static String defaultUsername;
+       private static String defaultPassword;
+       private static String defaultRealm;
+       private static String defaultScheme;
+       private static String authFile;
+       private static String agentHost;
+       private static boolean authRulesRead = false;
+       private static Configuration conf;
+
+       int maxThreadsTotal = 10;
+
+       private String proxyUsername;
+       private String proxyPassword;
+       private String proxyRealm;
+
+       private static final Collection<WebPage.Field> FIELDS = new 
HashSet<WebPage.Field>();
+
+       static {
+               FIELDS.add(WebPage.Field.MODIFIED_TIME);
+               FIELDS.add(WebPage.Field.HEADERS);
+       }
+
+       @Override
+       public Collection<Field> getFields() {
+               return FIELDS;
+       }
+       
+       /**
+        * Returns the configured HTTP client.
+        * 
+        * @return HTTP client
+        */
+       static synchronized HttpClient getClient() {
+               return client;
+       }
+
+       /**
+        * Constructs this plugin.
+        */
+       public Http() {
+               super(LOG);
+       }
+
+       /**
+        * Reads the configuration from the Nutch configuration files and sets 
the
+        * configuration.
+        * 
+        * @param conf
+        *            Configuration
+        */
+       public void setConf(Configuration conf) {
+               super.setConf(conf);
+               this.conf = conf;
+               this.maxThreadsTotal = conf.getInt("fetcher.threads.fetch", 10);
+               this.proxyUsername = conf.get("http.proxy.username", "");
+               this.proxyPassword = conf.get("http.proxy.password", "");
+               this.proxyRealm = conf.get("http.proxy.realm", "");
+               agentHost = conf.get("http.agent.host", "");
+               authFile = conf.get("http.auth.file", "");
+               configureClient();
+               try {
+                       setCredentials();
+               } catch (Exception ex) {
+                       if (LOG.isFatalEnabled()) {
+                               LOG.fatal("Could not read " + authFile + " : "
+                                               + ex.getMessage());
+                               ex.printStackTrace(LogUtil.getErrorStream(LOG));
+                       }
+               }
+       }
+
+       /**
+        * Main method.
+        * 
+        * @param args
+        *            Command line arguments
+        */
+       public static void main(String[] args) throws Exception {
+               Http http = new Http();
+               http.setConf(NutchConfiguration.create());
+               main(http, args);
+       }
+
+       /**
+        * Fetches the <code>url</code> with a configured HTTP client and gets 
the
+        * response.
+        * 
+        * @param url
+        *            URL to be fetched
+        * @param datum
+        *            Crawl data
+        * @param redirect
+        *            Follow redirects if and only if true
+        * @return HTTP response
+        */
+       protected Response getResponse(URL url, WebPage page, boolean redirect)
+                       throws ProtocolException, IOException {
+               resolveCredentials(url);
+               return new HttpResponse(this, url, page, redirect);
+       }
+
+       /**
+        * Configures the HTTP client
+        */
+       private void configureClient() {
+
+               // Set up an HTTPS socket factory that accepts self-signed 
certs.
+               Protocol https = new Protocol("https",
+                               new DummySSLProtocolSocketFactory(), 443);
+               Protocol.registerProtocol("https", https);
+
+               HttpConnectionManagerParams params = 
connectionManager.getParams();
+               params.setConnectionTimeout(timeout);
+               params.setSoTimeout(timeout);
+               params.setSendBufferSize(BUFFER_SIZE);
+               params.setReceiveBufferSize(BUFFER_SIZE);
+               params.setMaxTotalConnections(maxThreadsTotal);
+               if (maxThreadsTotal > maxThreadsPerHost) {
+                       
params.setDefaultMaxConnectionsPerHost(maxThreadsPerHost);
+               } else {
+                       params.setDefaultMaxConnectionsPerHost(maxThreadsTotal);
+               }
+
+               // executeMethod(HttpMethod) seems to ignore the connection 
timeout on
+               // the connection manager.
+               // set it explicitly on the HttpClient.
+               client.getParams().setConnectionManagerTimeout(timeout);
+
+               HostConfiguration hostConf = client.getHostConfiguration();
+               ArrayList headers = new ArrayList();
+               // Set the User Agent in the header
+               headers.add(new Header("User-Agent", userAgent));
+               // prefer English
+               headers.add(new Header("Accept-Language",
+                               "en-us,en-gb,en;q=0.7,*;q=0.3"));
+               // prefer UTF-8
+               headers.add(new Header("Accept-Charset",
+                               "utf-8,ISO-8859-1;q=0.7,*;q=0.7"));
+               // prefer understandable formats
+               headers.add(new Header(
+                               "Accept",
+                               
"text/html,application/xml;q=0.9,application/xhtml+xml,text/xml;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"));
+               // accept gzipped content
+               headers.add(new Header("Accept-Encoding", "x-gzip, gzip, 
deflate"));
+               hostConf.getParams().setParameter("http.default-headers", 
headers);
+
+               // HTTP proxy server details
+               if (useProxy) {
+                       hostConf.setProxy(proxyHost, proxyPort);
+
+                       if (proxyUsername.length() > 0) {
+
+                               AuthScope proxyAuthScope = 
getAuthScope(this.proxyHost,
+                                               this.proxyPort, 
this.proxyRealm);
+
+                               NTCredentials proxyCredentials = new 
NTCredentials(
+                                               this.proxyUsername, 
this.proxyPassword, this.agentHost,
+                                               this.proxyRealm);
+
+                               
client.getState().setProxyCredentials(proxyAuthScope,
+                                               proxyCredentials);
+                       }
+               }
+
+       }
+
+       /**
+        * Reads authentication configuration file (defined as 'http.auth.file' 
in
+        * Nutch configuration file) and sets the credentials for the configured
+        * authentication scopes in the HTTP client object.
+        * 
+        * @throws ParserConfigurationException
+        *             If a document builder can not be created.
+        * @throws SAXException
+        *             If any parsing error occurs.
+        * @throws IOException
+        *             If any I/O error occurs.
+        */
+       private static synchronized void setCredentials()
+                       throws ParserConfigurationException, SAXException, 
IOException {
+
+               if (authRulesRead)
+                       return;
+
+               authRulesRead = true; // Avoid re-attempting to read
+
+               InputStream is = conf.getConfResourceAsInputStream(authFile);
+               if (is != null) {
+                       Document doc = DocumentBuilderFactory.newInstance()
+                                       .newDocumentBuilder().parse(is);
+
+                       Element rootElement = doc.getDocumentElement();
+                       if 
(!"auth-configuration".equals(rootElement.getTagName())) {
+                               if (LOG.isWarnEnabled())
+                                       LOG.warn("Bad auth conf file: root 
element <"
+                                                       + 
rootElement.getTagName() + "> found in "
+                                                       + authFile + " - must 
be <auth-configuration>");
+                       }
+
+                       // For each set of credentials
+                       NodeList credList = rootElement.getChildNodes();
+                       for (int i = 0; i < credList.getLength(); i++) {
+                               Node credNode = credList.item(i);
+                               if (!(credNode instanceof Element))
+                                       continue;
+
+                               Element credElement = (Element) credNode;
+                               if 
(!"credentials".equals(credElement.getTagName())) {
+                                       if (LOG.isWarnEnabled())
+                                               LOG.warn("Bad auth conf file: 
Element <"
+                                                               + 
credElement.getTagName()
+                                                               + "> not 
recognized in " + authFile
+                                                               + " - expected 
<credentials>");
+                                       continue;
+                               }
+
+                               String username = 
credElement.getAttribute("username");
+                               String password = 
credElement.getAttribute("password");
+
+                               // For each authentication scope
+                               NodeList scopeList = 
credElement.getChildNodes();
+                               for (int j = 0; j < scopeList.getLength(); j++) 
{
+                                       Node scopeNode = scopeList.item(j);
+                                       if (!(scopeNode instanceof Element))
+                                               continue;
+
+                                       Element scopeElement = (Element) 
scopeNode;
+
+                                       if 
("default".equals(scopeElement.getTagName())) {
+
+                                               // Determine realm and scheme, 
if any
+                                               String realm = 
scopeElement.getAttribute("realm");
+                                               String scheme = 
scopeElement.getAttribute("scheme");
+
+                                               // Set default credentials
+                                               defaultUsername = username;
+                                               defaultPassword = password;
+                                               defaultRealm = realm;
+                                               defaultScheme = scheme;
+
+                                               if (LOG.isTraceEnabled()) {
+                                                       LOG.trace("Credentials 
- username: " + username
+                                                                       + "; 
set as default" + " for realm: "
+                                                                       + realm 
+ "; scheme: " + scheme);
+                                               }
+
+                                       } else if 
("authscope".equals(scopeElement.getTagName())) {
+
+                                               // Determine authentication 
scope details
+                                               String host = 
scopeElement.getAttribute("host");
+                                               int port = -1; // For setting 
port to AuthScope.ANY_PORT
+                                               try {
+                                                       port = 
Integer.parseInt(scopeElement
+                                                                       
.getAttribute("port"));
+                                               } catch (Exception ex) {
+                                                       // do nothing, port is 
already set to any port
+                                               }
+                                               String realm = 
scopeElement.getAttribute("realm");
+                                               String scheme = 
scopeElement.getAttribute("scheme");
+
+                                               // Set credentials for the 
determined scope
+                                               AuthScope authScope = 
getAuthScope(host, port, realm,
+                                                               scheme);
+                                               NTCredentials credentials = new 
NTCredentials(username,
+                                                               password, 
agentHost, realm);
+
+                                               client.getState()
+                                                               
.setCredentials(authScope, credentials);
+
+                                               if (LOG.isTraceEnabled()) {
+                                                       LOG.trace("Credentials 
- username: " + username
+                                                                       + "; 
set for AuthScope - " + "host: "
+                                                                       + host 
+ "; port: " + port + "; realm: "
+                                                                       + realm 
+ "; scheme: " + scheme);
+                                               }
+
+                                       } else {
+                                               if (LOG.isWarnEnabled())
+                                                       LOG.warn("Bad auth conf 
file: Element <"
+                                                                       + 
scopeElement.getTagName()
+                                                                       + "> 
not recognized in " + authFile
+                                                                       + " - 
expected <authscope>");
+                                       }
+                               }
+                               is.close();
+                       }
+               }
+       }
+
+       /**
+        * If credentials for the authentication scope determined from the 
specified
+        * <code>url</code> is not already set in the HTTP client, then this 
method
+        * sets the default credentials to fetch the specified 
<code>url</code>. If
+        * credentials are found for the authentication scope, the method 
returns
+        * without altering the client.
+        * 
+        * @param url
+        *            URL to be fetched
+        */
+       private void resolveCredentials(URL url) {
+
+               if (defaultUsername != null && defaultUsername.length() > 0) {
+
+                       int port = url.getPort();
+                       if (port == -1) {
+                               if ("https".equals(url.getProtocol()))
+                                       port = 443;
+                               else
+                                       port = 80;
+                       }
+
+                       AuthScope scope = new AuthScope(url.getHost(), port);
+
+                       if (client.getState().getCredentials(scope) != null) {
+                               if (LOG.isTraceEnabled())
+                                       LOG.trace("Pre-configured credentials 
with scope - host: "
+                                                       + url.getHost() + "; 
port: " + port
+                                                       + "; found for url: " + 
url);
+
+                               // Credentials are already configured, so do 
nothing and return
+                               return;
+                       }
+
+                       if (LOG.isTraceEnabled())
+                               LOG.trace("Pre-configured credentials with 
scope -  host: "
+                                               + url.getHost() + "; port: " + 
port
+                                               + "; not found for url: " + 
url);
+
+                       AuthScope serverAuthScope = getAuthScope(url.getHost(), 
port,
+                                       defaultRealm, defaultScheme);
+
+                       NTCredentials serverCredentials = new NTCredentials(
+                                       defaultUsername, defaultPassword, 
agentHost, defaultRealm);
+
+                       client.getState()
+                                       .setCredentials(serverAuthScope, 
serverCredentials);
+               }
+       }
+
+       /**
+        * Returns an authentication scope for the specified <code>host</code>,
+        * <code>port</code>, <code>realm</code> and <code>scheme</code>.
+        * 
+        * @param host
+        *            Host name or address.
+        * @param port
+        *            Port number.
+        * @param realm
+        *            Authentication realm.
+        * @param scheme
+        *            Authentication scheme.
+        */
+       private static AuthScope getAuthScope(String host, int port, String 
realm,
+                       String scheme) {
+
+               if (host.length() == 0)
+                       host = null;
+
+               if (port < 0)
+                       port = -1;
+
+               if (realm.length() == 0)
+                       realm = null;
+
+               if (scheme.length() == 0)
+                       scheme = null;
+
+               return new AuthScope(host, port, realm, scheme);
+       }
+
+       /**
+        * Returns an authentication scope for the specified <code>host</code>,
+        * <code>port</code> and <code>realm</code>.
+        * 
+        * @param host
+        *            Host name or address.
+        * @param port
+        *            Port number.
+        * @param realm
+        *            Authentication realm.
+        */
+       private static AuthScope getAuthScope(String host, int port, String 
realm) {
 
-      return getAuthScope(host, port, realm, "");
-  }
-}
+               return getAuthScope(host, port, realm, "");
+       }
 
+}

Modified: 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/HttpResponse.java
URL: 
http://svn.apache.org/viewvc/nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/HttpResponse.java?rev=965887&r1=965886&r2=965887&view=diff
==============================================================================
--- 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/HttpResponse.java
 (original)
+++ 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/java/org/apache/nutch/protocol/httpclient/HttpResponse.java
 Tue Jul 20 15:42:23 2010
@@ -31,12 +31,12 @@ import org.apache.commons.httpclient.par
 import org.apache.commons.httpclient.HttpException;
 
 // Nutch imports
-import org.apache.nutch.crawl.CrawlDatum;
 import org.apache.nutch.metadata.Metadata;
 import org.apache.nutch.metadata.SpellCheckedMetadata;
 import org.apache.nutch.net.protocols.HttpDateFormat;
 import org.apache.nutch.net.protocols.Response;
 import org.apache.nutch.protocol.http.api.HttpBase;
+import org.apache.nutch.storage.WebPage;
 
 /**
  * An HTTP response.
@@ -56,13 +56,13 @@ public class HttpResponse implements Res
    * @param http                An instance of the implementation class
    *                            of this plugin
    * @param url                 URL to be fetched
-   * @param datum               Crawl data
+   * @param page                WebPage
    * @param followRedirects     Whether to follow redirects; follows
    *                            redirect if and only if this is true
    * @return                    HTTP response
    * @throws IOException        When an error occurs
    */
-  HttpResponse(Http http, URL url, CrawlDatum datum,
+  HttpResponse(Http http, URL url, WebPage page,
       boolean followRedirects) throws IOException {
 
     // Prepare GET method for HTTP request
@@ -70,9 +70,9 @@ public class HttpResponse implements Res
     GetMethod get = new GetMethod(url.toString());
     get.setFollowRedirects(followRedirects);
     get.setDoAuthentication(true);
-    if (datum.getModifiedTime() > 0) {
+    if (page.getModifiedTime() > 0) {
       get.setRequestHeader("If-Modified-Since",
-          HttpDateFormat.toString(datum.getModifiedTime()));
+          HttpDateFormat.toString(page.getModifiedTime()));
     }
 
     // Set HTTP parameters

Modified: 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/test/org/apache/nutch/protocol/httpclient/TestProtocolHttpClient.java
URL: 
http://svn.apache.org/viewvc/nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/test/org/apache/nutch/protocol/httpclient/TestProtocolHttpClient.java?rev=965887&r1=965886&r2=965887&view=diff
==============================================================================
--- 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/test/org/apache/nutch/protocol/httpclient/TestProtocolHttpClient.java
 (original)
+++ 
nutch/branches/nutchbase/src/plugin/protocol-httpclient/src/test/org/apache/nutch/protocol/httpclient/TestProtocolHttpClient.java
 Tue Jul 20 15:42:23 2010
@@ -17,176 +17,196 @@
 
 package org.apache.nutch.protocol.httpclient;
 
-import java.net.URL;
 import java.net.MalformedURLException;
+import java.net.URL;
+
 import junit.framework.TestCase;
-import org.mortbay.jetty.Server;
-import org.mortbay.jetty.servlet.ServletHttpContext;
-import org.mortbay.jetty.servlet.ServletHandler;
-import org.mortbay.http.SocketListener;
-import org.mortbay.http.handler.ResourceHandler;
+
 import org.apache.hadoop.conf.Configuration;
-import org.apache.nutch.crawl.CrawlDatum;
 import org.apache.nutch.net.protocols.Response;
+import org.apache.nutch.storage.WebPage;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
 
 /**
  * Test cases for protocol-httpclient.
- *
+ * 
  * @author Susam Pal
  */
 public class TestProtocolHttpClient extends TestCase {
 
-  private Server server;
-  private Configuration conf;
-  private static final String RES_DIR = System.getProperty("test.data", ".");
-  private int port;
-  private Http http = new Http();
-
-  protected void setUp() throws Exception {
-
-    ServletHttpContext context = new ServletHttpContext();
-    context.setContextPath("/");
-    context.setResourceBase(RES_DIR);
-    context.addServlet("JSP", "*.jsp", "org.apache.jasper.servlet.JspServlet");
-    context.addHandler(new ResourceHandler());
-
-    server = new Server();
-    server.addContext(context);
-
-    conf = new Configuration();
-    conf.addResource("nutch-default.xml");
-    conf.addResource("nutch-site-test.xml");
-    
-    http = new Http();
-    http.setConf(conf);
-  }
-
-  protected void tearDown() throws Exception {
-    server.stop();
-  }
-
-  /**
-   * Tests whether the client can remember cookies.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testCookies() throws Exception {
-    startServer(47500);
-    fetchPage("/cookies.jsp", 200);
-    fetchPage("/cookies.jsp?cookie=yes", 200);
-  }
-
-  /**
-   * Tests that no pre-emptive authorization headers are sent by the
-   * client.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testNoPreemptiveAuth() throws Exception {
-    startServer(47500);
-    fetchPage("/noauth.jsp", 200);
-  }
-
-  /**
-   * Tests default credentials.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testDefaultCredentials() throws Exception {
-    startServer(47502);
-    fetchPage("/basic.jsp", 200);
-  }
-
-  /**
-   * Tests basic authentication scheme for various realms.
-   * 
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testBasicAuth() throws Exception {
-    startServer(47500);
-    fetchPage("/basic.jsp", 200);
-    fetchPage("/basic.jsp?case=1", 200);
-    fetchPage("/basic.jsp?case=2", 200);
-    server.start();
-  }
-
-  /**
-   * Tests that authentication happens for a defined realm and not for
-   * other realms for a host:port when an extra <code>authscope</code>
-   * tag is not defined to match all other realms.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testOtherRealmsNoAuth() throws Exception {
-    startServer(47501);
-    fetchPage("/basic.jsp", 200);
-    fetchPage("/basic.jsp?case=1", 401);
-    fetchPage("/basic.jsp?case=2", 401);
-  }
-
-  /**
-   * Tests Digest authentication scheme.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testDigestAuth() throws Exception {
-    startServer(47500);
-    fetchPage("/digest.jsp", 200);
-  }
-
-  /**
-   * Tests NTLM authentication scheme.
-   *
-   * @throws Exception If an error occurs or the test case fails.
-   */
-  public void testNtlmAuth() throws Exception {
-    startServer(47501);
-    fetchPage("/ntlm.jsp", 200);
-  }
-
-  /**
-   * Starts the Jetty server at a specified port.
-   *
-   * @param  portno     Port number.
-   * @throws Exception  When an error occurs.
-   */
-  private void startServer(int portno) throws Exception {
-    port = portno;
-    SocketListener listener = new SocketListener();
-    listener.setHost("127.0.0.1");
-    listener.setPort(port);
-    server.addListener(listener);
-    server.start();
-  }
-
-  /**
-   * Fetches the specified <code>page</code> from the local Jetty server
-   * and checks whether the HTTP response status code matches with the
-   * expected code.
-   *
-   * @param page          Page to be fetched.
-   * @param expectedCode  HTTP response status code expected while
-   *                      fetching the page.
-   * @throws Exception    When an error occurs or test case fails.
-   */
-  private void fetchPage(String page, int expectedCode)
-      throws Exception {
-    URL url = new URL("http", "127.0.0.1", port, page);
-    Response response = null;
-    response = http.getResponse(url, new CrawlDatum(), true);
-
-    int code = response.getCode();
-    assertEquals("HTTP Status Code for " + url, expectedCode, code);
-  }
-  
-  /**
-   * Returns an URL to the specified page.
-   *
-   * @param  page                  Page available in the local Jetty
-   *                               server.
-   * @throws MalformedURLException If an URL can not be formed.
-   */
-  private URL getURL(String page) throws MalformedURLException {
-    return new URL("http", "127.0.0.1", port, page);
-  }
+       private Server server;
+       private Configuration conf;
+       private static final String RES_DIR = System.getProperty("test.data", 
".");
+       private int port;
+       private Http http = new Http();
+
+       protected void setUp() throws Exception {
+
+               server = new Server();
+               
+//             Context scontext = new Context();
+//             scontext.setContextPath("/");
+//             scontext.setResourceBase(RES_DIR);
+//             // servlet handler?
+//             scontext.addServlet("JSP", "*.jsp",
+//                             "org.apache.jasper.servlet.JspServlet");
+//             scontext.addHandler(new ResourceHandler());
+
+               Context root = new Context(server,"/",Context.SESSIONS);
+               root.setContextPath("/");
+               root.setResourceBase(RES_DIR);
+               ServletHolder sh = new 
ServletHolder(org.apache.jasper.servlet.JspServlet.class);
+               root.addServlet(sh, "*.jsp");
+
+               conf = new Configuration();
+               conf.addResource("nutch-default.xml");
+               conf.addResource("nutch-site-test.xml");
+
+               http = new Http();
+               http.setConf(conf);
+       }
+
+       protected void tearDown() throws Exception {
+               server.stop();
+       }
+
+       /**
+        * Tests whether the client can remember cookies.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testCookies() throws Exception {
+               startServer(47500);
+               fetchPage("/cookies.jsp", 200);
+               fetchPage("/cookies.jsp?cookie=yes", 200);
+       }
+
+       /**
+        * Tests that no pre-emptive authorization headers are sent by the 
client.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testNoPreemptiveAuth() throws Exception {
+               startServer(47500);
+               fetchPage("/noauth.jsp", 200);
+       }
+
+       /**
+        * Tests default credentials.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testDefaultCredentials() throws Exception {
+               startServer(47502);
+               fetchPage("/basic.jsp", 200);
+       }
+
+       /**
+        * Tests basic authentication scheme for various realms.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testBasicAuth() throws Exception {
+               startServer(47500);
+               fetchPage("/basic.jsp", 200);
+               fetchPage("/basic.jsp?case=1", 200);
+               fetchPage("/basic.jsp?case=2", 200);
+               server.start();
+       }
+
+       /**
+        * Tests that authentication happens for a defined realm and not for 
other
+        * realms for a host:port when an extra <code>authscope</code> tag is 
not
+        * defined to match all other realms.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testOtherRealmsNoAuth() throws Exception {
+               startServer(47501);
+               fetchPage("/basic.jsp", 200);
+               fetchPage("/basic.jsp?case=1", 401);
+               fetchPage("/basic.jsp?case=2", 401);
+       }
+
+       /**
+        * Tests Digest authentication scheme.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testDigestAuth() throws Exception {
+               startServer(47500);
+               fetchPage("/digest.jsp", 200);
+       }
+
+       /**
+        * Tests NTLM authentication scheme.
+        * 
+        * @throws Exception
+        *             If an error occurs or the test case fails.
+        */
+       public void testNtlmAuth() throws Exception {
+               startServer(47501);
+               fetchPage("/ntlm.jsp", 200);
+       }
+
+       /**
+        * Starts the Jetty server at a specified port.
+        * 
+        * @param portno
+        *            Port number.
+        * @throws Exception
+        *             When an error occurs.
+        */
+       private void startServer(int portno) throws Exception {
+               port = portno;
+
+               SelectChannelConnector connector1 = new 
SelectChannelConnector();
+               connector1.setHost("127.0.0.1");
+               connector1.setPort(port);
+
+               server.addConnector(connector1);
+               server.start();
+       }
+
+       /**
+        * Fetches the specified <code>page</code> from the local Jetty server 
and
+        * checks whether the HTTP response status code matches with the 
expected
+        * code.
+        * 
+        * @param page
+        *            Page to be fetched.
+        * @param expectedCode
+        *            HTTP response status code expected while fetching the 
page.
+        * @throws Exception
+        *             When an error occurs or test case fails.
+        */
+       private void fetchPage(String page, int expectedCode) throws Exception {
+               URL url = new URL("http", "127.0.0.1", port, page);
+               Response response = null;
+               response = http.getResponse(url, new WebPage(), true);
+
+               int code = response.getCode();
+               assertEquals("HTTP Status Code for " + url, expectedCode, code);
+       }
+
+       /**
+        * Returns an URL to the specified page.
+        * 
+        * @param page
+        *            Page available in the local Jetty server.
+        * @throws MalformedURLException
+        *             If an URL can not be formed.
+        */
+       private URL getURL(String page) throws MalformedURLException {
+               return new URL("http", "127.0.0.1", port, page);
+       }
 }


Reply via email to