From 43a332bca4f76e4757d3d28f8f467f33190cfdef Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Wed, 15 Aug 2012 15:51:38 -0400 Subject: [PATCH] Fail gracefully on a 401 response code with no WWW-Authenticate header. This is more lenient than necessary; the HTTP spec says this: 10.4.2 401 Unauthorized The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. Not throwing will still cause the request to fail, since the 401 response code triggers an IOException. But this type of failure is more recoverable and allows the caller to inspect the headers and response body. --- .../net/http/HttpURLConnectionImpl.java | 6 ++- .../libcore/net/http/URLConnectionTest.java | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/src/main/java/libcore/net/http/HttpURLConnectionImpl.java index 99a6ac4f1..c35e56a2a 100644 --- a/src/main/java/libcore/net/http/HttpURLConnectionImpl.java +++ b/src/main/java/libcore/net/http/HttpURLConnectionImpl.java @@ -423,13 +423,15 @@ public class HttpURLConnectionImpl extends OkHttpConnection { } /** - * Returns the authorization credentials on the base of provided challenge. + * Returns the authorization credentials that may satisfy the challenge. + * Returns null if a challenge header was not provided or if credentials + * were not available. */ private String getAuthorizationCredentials(RawHeaders responseHeaders, String challengeHeader) throws IOException { List challenges = HeaderParser.parseChallenges(responseHeaders, challengeHeader); if (challenges.isEmpty()) { - throw new IOException("No authentication challenges found"); + return null; } for (Challenge challenge : challenges) { diff --git a/src/test/java/libcore/net/http/URLConnectionTest.java b/src/test/java/libcore/net/http/URLConnectionTest.java index 6df8f530f..779f0fd27 100644 --- a/src/test/java/libcore/net/http/URLConnectionTest.java +++ b/src/test/java/libcore/net/http/URLConnectionTest.java @@ -981,6 +981,39 @@ public final class URLConnectionTest extends TestCase { assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); } + public void testNonStandardAuthenticationScheme() throws Exception { + RecordingAuthenticator authenticator = new RecordingAuthenticator(); + Authenticator.setDefault(authenticator); + MockResponse pleaseAuthenticate = new MockResponse() + .setResponseCode(401) + .addHeader("WWW-Authenticate: Foo") + .setBody("Please authenticate."); + server.enqueue(pleaseAuthenticate); + server.play(); + + OkHttpConnection connection = openConnection(server.getUrl("/")); + assertEquals(401, connection.getResponseCode()); + assertEquals(Collections.emptyList(), authenticator.calls); + } + + public void testNonStandardAuthenticationSchemeWithRealm() throws Exception { + RecordingAuthenticator authenticator = new RecordingAuthenticator(); + Authenticator.setDefault(authenticator); + MockResponse pleaseAuthenticate = new MockResponse() + .setResponseCode(401) + .addHeader("WWW-Authenticate: Foo realm=\"Bar\"") + .setBody("Please authenticate."); + server.enqueue(pleaseAuthenticate); + server.play(); + + OkHttpConnection connection = openConnection(server.getUrl("/")); + assertEquals(401, connection.getResponseCode()); + assertEquals(1, authenticator.calls.size()); + String call = authenticator.calls.get(0); + assertTrue(call, call.contains("scheme=Foo")); + assertTrue(call, call.contains("prompt=Bar")); + } + public void testSetValidRequestMethod() throws Exception { server.play(); assertValidRequestMethod("GET"); @@ -2078,4 +2111,20 @@ public final class URLConnectionTest extends TestCase { return true; } } + + private static class RecordingAuthenticator extends Authenticator { + private final List calls = new ArrayList(); + + @Override protected PasswordAuthentication getPasswordAuthentication() { + this.calls.add("host=" + getRequestingHost() + + " port=" + getRequestingPort() + + " site=" + getRequestingSite() + + " url=" + getRequestingURL() + + " type=" + getRequestorType() + + " prompt=" + getRequestingPrompt() + + " protocol=" + getRequestingProtocol() + + " scheme=" + getRequestingScheme()); + return null; + } + } }