mirror of
https://github.com/internetarchive/heritrix3.git
synced 2026-09-26 15:46:31 +00:00
remember www-authenticate headers to make http auth work as intended, without lots of extra 401s
This commit is contained in:
@@ -116,4 +116,6 @@ public interface CoreAttributeConstants {
|
||||
public static final String A_WHOIS_SERVER_IP = "whois-server-ip";
|
||||
|
||||
public static final String A_HTTP_RESPONSE_HEADERS = "http-response-headers";
|
||||
|
||||
public static final String A_HTTP_AUTH_CHALLENGES = "http-auth-challenges";
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.archive.modules.CoreAttributeConstants.A_FETCH_COMPLETED_TIME;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_FORCE_RETIRE;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_HERITABLE_KEYS;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_HTML_BASE;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_HTTP_AUTH_CHALLENGES;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_HTTP_RESPONSE_HEADERS;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_NONFATAL_ERRORS;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_PREREQUISITE_URI;
|
||||
@@ -1877,4 +1878,13 @@ implements Reporter, Serializable, OverlayContext {
|
||||
}
|
||||
httpResponseHeaders.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String,String> getHttpAuthChallenges() {
|
||||
return (Map<String, String>) getData().get(A_HTTP_AUTH_CHALLENGES);
|
||||
}
|
||||
|
||||
public void setHttpAuthChallenges(Map<String, String> httpAuthChallenges) {
|
||||
getData().put(A_HTTP_AUTH_CHALLENGES, httpAuthChallenges);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-3
@@ -30,8 +30,12 @@ import org.apache.commons.httpclient.HttpMethodBase;
|
||||
import org.apache.commons.httpclient.NameValuePair;
|
||||
import org.apache.commons.httpclient.URIException;
|
||||
import org.apache.commons.httpclient.UsernamePasswordCredentials;
|
||||
import org.apache.commons.httpclient.auth.AuthChallengeProcessor;
|
||||
import org.apache.commons.httpclient.auth.AuthPolicy;
|
||||
import org.apache.commons.httpclient.auth.AuthScheme;
|
||||
import org.apache.commons.httpclient.auth.AuthScope;
|
||||
import org.apache.commons.httpclient.auth.AuthenticationException;
|
||||
import org.apache.commons.httpclient.auth.MalformedChallengeException;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -42,9 +46,9 @@ public class CommonsHttpCredentialUtil {
|
||||
private static Logger logger = Logger.getLogger(CommonsHttpCredentialUtil.class.getName());
|
||||
|
||||
public static boolean populate(CrawlURI curi, HttpClient http,
|
||||
HttpMethod method, Credential cred) {
|
||||
HttpMethod method, Credential cred, Map<String, String> httpAuthChallenges) {
|
||||
if (cred instanceof HttpAuthenticationCredential) {
|
||||
return populate(curi, http, method, (HttpAuthenticationCredential) cred);
|
||||
return populate(curi, http, method, (HttpAuthenticationCredential) cred, httpAuthChallenges);
|
||||
} else if (cred instanceof HtmlFormCredential) {
|
||||
return populate(curi, http, method, (HtmlFormCredential) cred);
|
||||
} else {
|
||||
@@ -99,8 +103,18 @@ public class CommonsHttpCredentialUtil {
|
||||
}
|
||||
|
||||
public static boolean populate(CrawlURI curi, HttpClient http,
|
||||
HttpMethod method, HttpAuthenticationCredential cred) {
|
||||
HttpMethod method, HttpAuthenticationCredential cred, Map<String,String> httpAuthChallenges) {
|
||||
boolean result = false;
|
||||
|
||||
AuthChallengeProcessor authChallengeProcessor = new AuthChallengeProcessor(http.getParams());
|
||||
try {
|
||||
AuthScheme authScheme = authChallengeProcessor.processChallenge(method.getHostAuthState(), httpAuthChallenges);
|
||||
method.getHostAuthState().setAuthScheme(authScheme);
|
||||
} catch (MalformedChallengeException e) {
|
||||
return result;
|
||||
} catch (AuthenticationException e) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Always add the credential to HttpState. Doing this because no way of
|
||||
// removing the credential once added AND there is a bug in the
|
||||
|
||||
@@ -71,10 +71,9 @@ import org.apache.commons.httpclient.HttpVersion;
|
||||
import org.apache.commons.httpclient.NTCredentials;
|
||||
import org.apache.commons.httpclient.URIException;
|
||||
import org.apache.commons.httpclient.auth.AuthChallengeParser;
|
||||
import org.apache.commons.httpclient.auth.AuthPolicy;
|
||||
import org.apache.commons.httpclient.auth.AuthScheme;
|
||||
import org.apache.commons.httpclient.auth.AuthScope;
|
||||
import org.apache.commons.httpclient.auth.BasicScheme;
|
||||
import org.apache.commons.httpclient.auth.DigestScheme;
|
||||
import org.apache.commons.httpclient.auth.MalformedChallengeException;
|
||||
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
||||
import org.apache.commons.httpclient.params.HttpClientParams;
|
||||
@@ -1153,7 +1152,7 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
if (server.hasCredentials()) {
|
||||
for (Credential cred : server.getCredentials()) {
|
||||
if (cred.isEveryTime()) {
|
||||
CommonsHttpCredentialUtil.populate(curi, this.http, method, cred);
|
||||
CommonsHttpCredentialUtil.populate(curi, this.http, method, cred, server.getHttpAuthChallenges());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1164,7 +1163,7 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
// by the handle401 method if its a rfc2617 or it'll have been set into
|
||||
// the curi by the preconditionenforcer as this login uri came through.
|
||||
for (Credential c: curi.getCredentials()) {
|
||||
if (CommonsHttpCredentialUtil.populate(curi, this.http, method, c)) {
|
||||
if (CommonsHttpCredentialUtil.populate(curi, this.http, method, c, curi.getHttpAuthChallenges())) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
@@ -1193,6 +1192,7 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
CrawlServer cs = serverCache.getServerFor(cd);
|
||||
if (cs != null) {
|
||||
cs.addCredential(c);
|
||||
cs.setHttpAuthChallenges(curi.getHttpAuthChallenges());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1282,6 +1282,8 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> parsedChallenges = AuthChallengeParser.parseChallenges(headers);
|
||||
authschemes = parsedChallenges;
|
||||
|
||||
curi.setHttpAuthChallenges(authschemes);
|
||||
} catch (MalformedChallengeException e) {
|
||||
logger.fine("Failed challenge parse: " + e.getMessage());
|
||||
}
|
||||
@@ -1303,12 +1305,8 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
+ Arrays.toString(headers));
|
||||
continue;
|
||||
}
|
||||
AuthScheme authscheme = null;
|
||||
if (key.equals("basic")) {
|
||||
authscheme = new BasicScheme();
|
||||
} else if (key.equals("digest")) {
|
||||
authscheme = new DigestScheme();
|
||||
} else {
|
||||
AuthScheme authscheme = AuthPolicy.getAuthScheme(key);
|
||||
if (authscheme == null) {
|
||||
logger.fine("Unsupported scheme: " + key);
|
||||
continue;
|
||||
}
|
||||
@@ -1458,7 +1456,7 @@ public class FetchHTTP extends AbstractFetchHTTP implements Lifecycle {
|
||||
// Set client to be version 1.0.
|
||||
hcp.setVersion(HttpVersion.HTTP_1_0);
|
||||
// We handle 401s, so when we do auth, we want it preemptive.
|
||||
hcp.setAuthenticationPreemptive(true);
|
||||
// hcp.setAuthenticationPreemptive(true);
|
||||
|
||||
// configureHttpCookies(defaults);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -348,5 +349,13 @@ public class CrawlServer implements Serializable, FetchStats.HasFetchStats, Iden
|
||||
@Override
|
||||
public void setIdentityCache(ObjectIdentityCache<?> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
transient private Map<String,String> httpAuthChallenges;
|
||||
public Map<String,String> getHttpAuthChallenges() {
|
||||
return httpAuthChallenges;
|
||||
}
|
||||
public void setHttpAuthChallenges(Map<String, String> httpAuthChallenges) {
|
||||
this.httpAuthChallenges = httpAuthChallenges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ public abstract class FetchHTTPTestBase extends ProcessorTestBase {
|
||||
// check that we got the expected response and the fetcher did its thing
|
||||
assertEquals(401, curi.getFetchStatus());
|
||||
assertTrue(curi.getCredentials().contains(basicAuthCredential));
|
||||
assertTrue(curi.getHttpAuthChallenges() != null && curi.getHttpAuthChallenges().containsKey("basic"));
|
||||
|
||||
// fetch again with the credentials
|
||||
getFetcher().process(curi);
|
||||
@@ -448,16 +449,18 @@ public abstract class FetchHTTPTestBase extends ProcessorTestBase {
|
||||
// check that we got the expected response and the fetcher did its thing
|
||||
assertEquals(401, curi.getFetchStatus());
|
||||
assertTrue(curi.getCredentials().contains(digestAuthCred));
|
||||
assertTrue(curi.getHttpAuthChallenges() != null && curi.getHttpAuthChallenges().containsKey("digest"));
|
||||
|
||||
// stick a basic auth 401 in there to check it doesn't mess with the digest auth we're working on
|
||||
CrawlURI interferingUri = makeCrawlURI("http://localhost:7777/auth/basic");
|
||||
getFetcher().process(interferingUri);
|
||||
assertEquals(401, interferingUri.getFetchStatus());
|
||||
logger.info('\n' + httpRequestString(interferingUri) + "\n\n" + rawResponseString(interferingUri));
|
||||
|
||||
// fetch original again with the credentials
|
||||
getFetcher().process(curi);
|
||||
String httpRequestString = httpRequestString(curi);
|
||||
// logger.info('\n' + httpRequestString + contentString(curi));
|
||||
logger.info('\n' + httpRequestString + "\n\n" + rawResponseString(interferingUri));
|
||||
assertTrue(httpRequestString.contains("Authorization: Digest"));
|
||||
// otherwise should be a normal 200 response
|
||||
runDefaultChecks(curi, new HashSet<String>(Arrays.asList("requestLine", "hostHeader")));
|
||||
@@ -468,7 +471,7 @@ public abstract class FetchHTTPTestBase extends ProcessorTestBase {
|
||||
httpRequestString = httpRequestString(curi);
|
||||
assertTrue(httpRequestString.contains("Authorization: Digest"));
|
||||
// otherwise should be a normal 200 response
|
||||
runDefaultChecks(curi, new HashSet<String>(Arrays.asList("requestLine")));
|
||||
runDefaultChecks(curi, new HashSet<String>(Arrays.asList("requestLine", "hostHeader")));
|
||||
}
|
||||
|
||||
// server for form auth is at localhost:7779
|
||||
@@ -496,7 +499,7 @@ public abstract class FetchHTTPTestBase extends ProcessorTestBase {
|
||||
// login (if not we get a NPE within jetty)
|
||||
curi = makeCrawlURI("http://localhost:7779/auth/1");
|
||||
getFetcher().process(curi);
|
||||
logger.info('\n' + httpRequestString(curi) + rawResponseString(curi));
|
||||
logger.info('\n' + httpRequestString(curi) + "\n\n" + rawResponseString(curi));
|
||||
assertEquals(302, curi.getFetchStatus());
|
||||
assertTrue(curi.getHttpResponseHeader("Location").startsWith("http://localhost:7779/login.html"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user