summaryrefslogtreecommitdiffstats
path: root/net/http/http_network_transaction_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/http/http_network_transaction_unittest.cc')
-rw-r--r--net/http/http_network_transaction_unittest.cc488
1 files changed, 488 insertions, 0 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index dc53f4f..507b90b 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -618,6 +618,12 @@ TEST_F(HttpNetworkTransactionTest, BasicAuth) {
request.url = GURL("http://www.google.com/");
request.load_flags = 0;
+ MockWrite data_writes1[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
MockRead data_reads1[] = {
MockRead("HTTP/1.0 401 Unauthorized\r\n"),
// Give a couple authenticate options (only the middle one is actually
@@ -650,6 +656,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuth) {
MockSocket data1;
data1.reads = data_reads1;
+ data1.writes = data_writes1;
MockSocket data2;
data2.reads = data_reads2;
data2.writes = data_writes2;
@@ -707,6 +714,12 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyThenServer) {
request.url = GURL("http://www.google.com/");
request.load_flags = 0;
+ MockWrite data_writes1[] = {
+ MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+
MockRead data_reads1[] = {
MockRead("HTTP/1.0 407 Unauthorized\r\n"),
// Give a couple authenticate options (only the middle one is actually
@@ -762,6 +775,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyThenServer) {
MockSocket data1;
data1.reads = data_reads1;
+ data1.writes = data_writes1;
MockSocket data2;
data2.reads = data_reads2;
data2.writes = data_writes2;
@@ -1017,3 +1031,477 @@ TEST_F(HttpNetworkTransactionTest, ResendRequestOnWriteBodyError) {
EXPECT_EQ(kExpectedResponseData[i], response_data);
}
}
+
+// Test the request-challenge-retry sequence for basic auth when there is
+// an identity in the URL. The request should be sent as normal, but when
+// it fails the identity from the URL is used to answer the challenge.
+TEST_F(HttpNetworkTransactionTest, AuthIdentityInUrl) {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ CreateSession(), &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ // Note: the URL has a username:password in it.
+ request.url = GURL("http://foo:bar@www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // After the challenge above, the transaction will be restarted using the
+ // identity from the url (foo, bar) to answer the challenge.
+ MockWrite data_writes2[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ MockSocket data2;
+ data2.reads = data_reads2;
+ data2.writes = data_writes2;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = &data2;
+ mock_sockets[2] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ // There is no challenge info, since the identity in URL worked.
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ EXPECT_EQ(100, response->headers->GetContentLength());
+
+ // Empty the current queue.
+ MessageLoop::current()->RunAllPending();
+}
+
+// Test that previously tried username/passwords for a realm get re-used.
+TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) {
+ scoped_refptr<net::HttpNetworkSession> session = CreateSession();
+
+ // Transaction 1: authenticate (foo, bar) on MyRealm1
+ {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ session, &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/x/y/z");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/y/z HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10000\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // Resend with authorization (username=foo, password=bar)
+ MockWrite data_writes2[] = {
+ MockWrite("GET /x/y/z HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ MockSocket data2;
+ data2.reads = data_reads2;
+ data2.writes = data_writes2;
+ mock_sockets_index = 0;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = &data2;
+ mock_sockets[2] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ // The password prompt info should have been set in
+ // response->auth_challenge.
+ EXPECT_FALSE(response->auth_challenge.get() == NULL);
+
+ // TODO(eroman): this should really include the effective port (80)
+ EXPECT_EQ(L"www.google.com", response->auth_challenge->host);
+ EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm);
+ EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+ TestCompletionCallback callback2;
+
+ rv = trans->RestartWithAuth(L"foo", L"bar", &callback2);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ }
+
+ // ------------------------------------------------------------------------
+
+ // Transaction 2: authenticate (foo2, bar2) on MyRealm2
+ {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ session, &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ // Note that Transaction 1 was at /x/y/z, so this is in the same
+ // protection space as MyRealm1.
+ request.url = GURL("http://www.google.com/x/y/a/b");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/y/a/b HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ // Send preemptive authorization for MyRealm1
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ // The server didn't like the preemptive authorization, and
+ // challenges us for a different realm (MyRealm2).
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm2\"\r\n"),
+ MockRead("Content-Length: 10000\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // Resend with authorization for MyRealm2 (username=foo2, password=bar2)
+ MockWrite data_writes2[] = {
+ MockWrite("GET /x/y/a/b HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ MockSocket data2;
+ data2.reads = data_reads2;
+ data2.writes = data_writes2;
+ mock_sockets_index = 0;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = &data2;
+ mock_sockets[2] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ // The password prompt info should have been set in
+ // response->auth_challenge.
+ EXPECT_FALSE(response->auth_challenge.get() == NULL);
+
+ // TODO(eroman): this should really include the effective port (80)
+ EXPECT_EQ(L"www.google.com", response->auth_challenge->host);
+ EXPECT_EQ(L"MyRealm2", response->auth_challenge->realm);
+ EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+ TestCompletionCallback callback2;
+
+ rv = trans->RestartWithAuth(L"foo2", L"bar2", &callback2);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ }
+
+ // ------------------------------------------------------------------------
+
+ // Transaction 3: Resend a request in MyRealm's protection space --
+ // succeed with preemptive authorization.
+ {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ session, &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/x/y/z2");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/y/z2 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ // The authorization for MyRealm1 gets sent preemptively
+ // (since the url is in the same protection space)
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ // Sever accepts the preemptive authorization
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ mock_sockets_index = 0;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ }
+
+ // ------------------------------------------------------------------------
+
+ // Transaction 4: request another URL in MyRealm (however the
+ // url is not known to belong to the protection space, so no pre-auth).
+ {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ session, &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/x/1");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /x/1 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10000\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // Resend with authorization from MyRealm's cache.
+ MockWrite data_writes2[] = {
+ MockWrite("GET /x/1 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ MockSocket data2;
+ data2.reads = data_reads2;
+ data2.writes = data_writes2;
+ mock_sockets_index = 0;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = &data2;
+ mock_sockets[2] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ }
+
+ // ------------------------------------------------------------------------
+
+ // Transaction 5: request a URL in MyRealm, but the server rejects the
+ // cached identity. Should invalidate and re-prompt.
+ {
+ scoped_ptr<net::HttpTransaction> trans(new net::HttpNetworkTransaction(
+ session, &mock_socket_factory));
+
+ net::HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/p/q/t");
+ request.load_flags = 0;
+
+ MockWrite data_writes1[] = {
+ MockWrite("GET /p/q/t HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads1[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10000\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // Resend with authorization from cache for MyRealm.
+ MockWrite data_writes2[] = {
+ MockWrite("GET /p/q/t HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ };
+
+ // Sever rejects the authorization.
+ MockRead data_reads2[] = {
+ MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+ MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10000\r\n\r\n"),
+ MockRead(false, net::ERR_FAILED),
+ };
+
+ // At this point we should prompt for new credentials for MyRealm.
+ // Restart with username=foo3, password=foo4.
+ MockWrite data_writes3[] = {
+ MockWrite("GET /p/q/t HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Authorization: Basic Zm9vMzpiYXIz\r\n\r\n"),
+ };
+
+ // Sever accepts the authorization.
+ MockRead data_reads3[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(false, net::OK),
+ };
+
+ MockSocket data1;
+ data1.reads = data_reads1;
+ data1.writes = data_writes1;
+ MockSocket data2;
+ data2.reads = data_reads2;
+ data2.writes = data_writes2;
+ MockSocket data3;
+ data3.reads = data_reads3;
+ data3.writes = data_writes3;
+ mock_sockets_index = 0;
+ mock_sockets[0] = &data1;
+ mock_sockets[1] = &data2;
+ mock_sockets[2] = &data3;
+ mock_sockets[3] = NULL;
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+
+ // The password prompt info should have been set in
+ // response->auth_challenge.
+ EXPECT_FALSE(response->auth_challenge.get() == NULL);
+
+ // TODO(eroman): this should really include the effective port (80)
+ EXPECT_EQ(L"www.google.com", response->auth_challenge->host);
+ EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm);
+ EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+ TestCompletionCallback callback2;
+
+ rv = trans->RestartWithAuth(L"foo3", L"bar3", &callback2);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(net::OK, rv);
+
+ response = trans->GetResponseInfo();
+ EXPECT_FALSE(response == NULL);
+ EXPECT_TRUE(response->auth_challenge.get() == NULL);
+ EXPECT_EQ(100, response->headers->GetContentLength());
+ }
+}