| Index: net/http/http_network_transaction_unittest.cc
|
| diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
|
| index 20ea0d411e55d66cfd97fa7c47d90991d64a1144..16e9ff7a546f0f1c4765e72ae9b25c35e7ea082d 100644
|
| --- a/net/http/http_network_transaction_unittest.cc
|
| +++ b/net/http/http_network_transaction_unittest.cc
|
| @@ -1842,50 +1842,76 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) {
|
| session_deps_.net_log = &net_log;
|
| scoped_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
|
|
| + const char* request_data =
|
| + "GET / HTTP/1.1\r\n"
|
| + "Host: www.foo.com\r\n"
|
| + "Connection: keep-alive\r\n\r\n";
|
| + MockWrite data_writes[] = {
|
| + MockWrite(ASYNC, 0, request_data), MockWrite(ASYNC, 2, request_data),
|
| + MockWrite(ASYNC, 4, request_data), MockWrite(ASYNC, 6, request_data),
|
| + MockWrite(ASYNC, 8, request_data), MockWrite(ASYNC, 10, request_data),
|
| + MockWrite(ASYNC, 12, request_data), MockWrite(ASYNC, 14, request_data),
|
| + MockWrite(ASYNC, 17, request_data), MockWrite(ASYNC, 20, request_data),
|
| + };
|
| +
|
| // Note that because all these reads happen in the same
|
| // StaticSocketDataProvider, it shows that the same socket is being reused for
|
| // all transactions.
|
| - MockRead data1_reads[] = {
|
| - MockRead("HTTP/1.1 204 No Content\r\n\r\n"),
|
| - MockRead("HTTP/1.1 205 Reset Content\r\n\r\n"),
|
| - MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
|
| - MockRead("HTTP/1.1 302 Found\r\n"
|
| - "Content-Length: 0\r\n\r\n"),
|
| - MockRead("HTTP/1.1 302 Found\r\n"
|
| - "Content-Length: 5\r\n\r\n"
|
| - "hello"),
|
| - MockRead("HTTP/1.1 301 Moved Permanently\r\n"
|
| - "Content-Length: 0\r\n\r\n"),
|
| - MockRead("HTTP/1.1 301 Moved Permanently\r\n"
|
| - "Content-Length: 5\r\n\r\n"
|
| - "hello"),
|
| - MockRead("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"),
|
| - MockRead("hello"),
|
| - };
|
| - StaticSocketDataProvider data1(data1_reads, arraysize(data1_reads), NULL, 0);
|
| - session_deps_.socket_factory->AddSocketDataProvider(&data1);
|
| -
|
| - MockRead data2_reads[] = {
|
| - MockRead(SYNCHRONOUS, ERR_UNEXPECTED), // Should not be reached.
|
| + MockRead data_reads[] = {
|
| + MockRead(ASYNC, 1, "HTTP/1.1 204 No Content\r\n\r\n"),
|
| + MockRead(ASYNC, 3, "HTTP/1.1 205 Reset Content\r\n\r\n"),
|
| + MockRead(ASYNC, 5, "HTTP/1.1 304 Not Modified\r\n\r\n"),
|
| + MockRead(ASYNC, 7,
|
| + "HTTP/1.1 302 Found\r\n"
|
| + "Content-Length: 0\r\n\r\n"),
|
| + MockRead(ASYNC, 9,
|
| + "HTTP/1.1 302 Found\r\n"
|
| + "Content-Length: 5\r\n\r\n"
|
| + "hello"),
|
| + MockRead(ASYNC, 11,
|
| + "HTTP/1.1 301 Moved Permanently\r\n"
|
| + "Content-Length: 0\r\n\r\n"),
|
| + MockRead(ASYNC, 13,
|
| + "HTTP/1.1 301 Moved Permanently\r\n"
|
| + "Content-Length: 5\r\n\r\n"
|
| + "hello"),
|
| +
|
| + // In the next two rounds, IsConnectedAndIdle returns false, due to
|
| + // the set_busy_before_sync_reads(true) call, while the
|
| + // HttpNetworkTransaction is being shut down, but the socket is still
|
| + // reuseable. See http://crbug.com/544255.
|
| + MockRead(ASYNC, 15,
|
| + "HTTP/1.1 200 Hunky-Dory\r\n"
|
| + "Content-Length: 5\r\n\r\n"),
|
| + MockRead(SYNCHRONOUS, 16, "hello"),
|
| +
|
| + MockRead(ASYNC, 18,
|
| + "HTTP/1.1 200 Hunky-Dory\r\n"
|
| + "Content-Length: 5\r\n\r\n"
|
| + "he"),
|
| + MockRead(SYNCHRONOUS, 19, "llo"),
|
| +
|
| + // The body of the final request is actually read.
|
| + MockRead(ASYNC, 21, "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"),
|
| + MockRead(ASYNC, 22, "hello"),
|
| };
|
| - StaticSocketDataProvider data2(data2_reads, arraysize(data2_reads), NULL, 0);
|
| - session_deps_.socket_factory->AddSocketDataProvider(&data2);
|
| + SequencedSocketData data(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + data.set_busy_before_sync_reads(true);
|
| + session_deps_.socket_factory->AddSocketDataProvider(&data);
|
|
|
| - const int kNumUnreadBodies = arraysize(data1_reads) - 2;
|
| + const int kNumUnreadBodies = arraysize(data_writes) - 1;
|
| std::string response_lines[kNumUnreadBodies];
|
|
|
| uint32 first_socket_log_id = NetLog::Source::kInvalidId;
|
| - for (size_t i = 0; i < arraysize(data1_reads) - 2; ++i) {
|
| + for (size_t i = 0; i < kNumUnreadBodies; ++i) {
|
| TestCompletionCallback callback;
|
|
|
| scoped_ptr<HttpTransaction> trans(
|
| new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
|
|
| int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| - EXPECT_EQ(ERR_IO_PENDING, rv);
|
| -
|
| - rv = callback.WaitForResult();
|
| - EXPECT_EQ(OK, rv);
|
| + EXPECT_EQ(OK, callback.GetResult(rv));
|
|
|
| LoadTimingInfo load_timing_info;
|
| EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
|
| @@ -1898,22 +1924,27 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) {
|
| }
|
|
|
| const HttpResponseInfo* response = trans->GetResponseInfo();
|
| - ASSERT_TRUE(response != NULL);
|
| + ASSERT_TRUE(response);
|
|
|
| - ASSERT_TRUE(response->headers.get() != NULL);
|
| + ASSERT_TRUE(response->headers);
|
| response_lines[i] = response->headers->GetStatusLine();
|
|
|
| - // We intentionally don't read the response bodies.
|
| + // Delete the transaction without reading the response bodies. Then spin
|
| + // the message loop, so the response bodies are drained.
|
| + trans.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| }
|
|
|
| const char* const kStatusLines[] = {
|
| - "HTTP/1.1 204 No Content",
|
| - "HTTP/1.1 205 Reset Content",
|
| - "HTTP/1.1 304 Not Modified",
|
| - "HTTP/1.1 302 Found",
|
| - "HTTP/1.1 302 Found",
|
| - "HTTP/1.1 301 Moved Permanently",
|
| - "HTTP/1.1 301 Moved Permanently",
|
| + "HTTP/1.1 204 No Content",
|
| + "HTTP/1.1 205 Reset Content",
|
| + "HTTP/1.1 304 Not Modified",
|
| + "HTTP/1.1 302 Found",
|
| + "HTTP/1.1 302 Found",
|
| + "HTTP/1.1 301 Moved Permanently",
|
| + "HTTP/1.1 301 Moved Permanently",
|
| + "HTTP/1.1 200 Hunky-Dory",
|
| + "HTTP/1.1 200 Hunky-Dory",
|
| };
|
|
|
| static_assert(kNumUnreadBodies == arraysize(kStatusLines),
|
| @@ -1926,12 +1957,10 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) {
|
| scoped_ptr<HttpTransaction> trans(
|
| new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| - EXPECT_EQ(ERR_IO_PENDING, rv);
|
| - rv = callback.WaitForResult();
|
| - EXPECT_EQ(OK, rv);
|
| + EXPECT_EQ(OK, callback.GetResult(rv));
|
| const HttpResponseInfo* response = trans->GetResponseInfo();
|
| - ASSERT_TRUE(response != NULL);
|
| - ASSERT_TRUE(response->headers.get() != NULL);
|
| + ASSERT_TRUE(response);
|
| + ASSERT_TRUE(response->headers);
|
| EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
|
| std::string response_data;
|
| rv = ReadTransaction(trans.get(), &response_data);
|
| @@ -2214,106 +2243,96 @@ TEST_P(HttpNetworkTransactionTest, DoNotSendAuth) {
|
| // Test the request-challenge-retry sequence for basic auth, over a keep-alive
|
| // connection.
|
| TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
|
| - HttpRequestInfo request;
|
| - request.method = "GET";
|
| - request.url = GURL("http://www.example.org/");
|
| - request.load_flags = 0;
|
| -
|
| - TestNetLog log;
|
| - session_deps_.net_log = &log;
|
| - scoped_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
| -
|
| - MockWrite data_writes1[] = {
|
| - MockWrite(
|
| - "GET / HTTP/1.1\r\n"
|
| - "Host: www.example.org\r\n"
|
| - "Connection: keep-alive\r\n\r\n"),
|
| -
|
| - // After calling trans->RestartWithAuth(), this is the request we should
|
| - // be issuing -- the final header line contains the credentials.
|
| - MockWrite(
|
| - "GET / HTTP/1.1\r\n"
|
| - "Host: www.example.org\r\n"
|
| - "Connection: keep-alive\r\n"
|
| - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
|
| - };
|
| -
|
| - MockRead data_reads1[] = {
|
| - MockRead("HTTP/1.1 401 Unauthorized\r\n"),
|
| - MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
|
| - MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
|
| - MockRead("Content-Length: 14\r\n\r\n"),
|
| - MockRead("Unauthorized\r\n"),
|
| + // On the second pass, the body read of the auth challenge is synchronous, so
|
| + // IsConnectedAndIdle returns false. The socket should still be drained and
|
| + // reused. See http://crbug.com/544255.
|
| + for (int i = 0; i < 2; ++i) {
|
| + HttpRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.example.org/");
|
| + request.load_flags = 0;
|
|
|
| - // Lastly, the server responds with the actual content.
|
| - MockRead("HTTP/1.1 200 OK\r\n"),
|
| - MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
|
| - MockRead("Content-Length: 5\r\n\r\n"),
|
| - MockRead("Hello"),
|
| - };
|
| + TestNetLog log;
|
| + session_deps_.net_log = &log;
|
| + scoped_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
|
|
| - // If there is a regression where we disconnect a Keep-Alive
|
| - // connection during an auth roundtrip, we'll end up reading this.
|
| - MockRead data_reads2[] = {
|
| - MockRead(SYNCHRONOUS, ERR_FAILED),
|
| - };
|
| + MockWrite data_writes[] = {
|
| + MockWrite(ASYNC, 0,
|
| + "GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n\r\n"),
|
|
|
| - StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
|
| - data_writes1, arraysize(data_writes1));
|
| - StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
|
| - NULL, 0);
|
| - session_deps_.socket_factory->AddSocketDataProvider(&data1);
|
| - session_deps_.socket_factory->AddSocketDataProvider(&data2);
|
| + // After calling trans->RestartWithAuth(), this is the request we should
|
| + // be issuing -- the final header line contains the credentials.
|
| + MockWrite(ASYNC, 6,
|
| + "GET / HTTP/1.1\r\n"
|
| + "Host: www.example.org\r\n"
|
| + "Connection: keep-alive\r\n"
|
| + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
|
| + };
|
|
|
| - TestCompletionCallback callback1;
|
| + MockRead data_reads[] = {
|
| + MockRead(ASYNC, 1, "HTTP/1.1 401 Unauthorized\r\n"),
|
| + MockRead(ASYNC, 2, "WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
|
| + MockRead(ASYNC, 3, "Content-Type: text/html; charset=iso-8859-1\r\n"),
|
| + MockRead(ASYNC, 4, "Content-Length: 14\r\n\r\n"),
|
| + MockRead(i == 0 ? ASYNC : SYNCHRONOUS, 5, "Unauthorized\r\n"),
|
| +
|
| + // Lastly, the server responds with the actual content.
|
| + MockRead(ASYNC, 7, "HTTP/1.1 200 OK\r\n"),
|
| + MockRead(ASYNC, 8, "Content-Type: text/html; charset=iso-8859-1\r\n"),
|
| + MockRead(ASYNC, 9, "Content-Length: 5\r\n\r\n"),
|
| + MockRead(ASYNC, 10, "Hello"),
|
| + };
|
|
|
| - scoped_ptr<HttpTransaction> trans(
|
| - new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| - int rv = trans->Start(&request, callback1.callback(), BoundNetLog());
|
| - EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + SequencedSocketData data(data_reads, arraysize(data_reads), data_writes,
|
| + arraysize(data_writes));
|
| + data.set_busy_before_sync_reads(true);
|
| + session_deps_.socket_factory->AddSocketDataProvider(&data);
|
|
|
| - rv = callback1.WaitForResult();
|
| - EXPECT_EQ(OK, rv);
|
| + TestCompletionCallback callback1;
|
|
|
| - LoadTimingInfo load_timing_info1;
|
| - EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1));
|
| - TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES);
|
| + scoped_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| + int rv = trans->Start(&request, callback1.callback(), BoundNetLog());
|
| + ASSERT_EQ(OK, callback1.GetResult(rv));
|
|
|
| - const HttpResponseInfo* response = trans->GetResponseInfo();
|
| - ASSERT_TRUE(response != NULL);
|
| - EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
|
| + LoadTimingInfo load_timing_info1;
|
| + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1));
|
| + TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES);
|
|
|
| - TestCompletionCallback callback2;
|
| + const HttpResponseInfo* response = trans->GetResponseInfo();
|
| + ASSERT_TRUE(response);
|
| + EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
|
|
|
| - rv = trans->RestartWithAuth(
|
| - AuthCredentials(kFoo, kBar), callback2.callback());
|
| - EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + TestCompletionCallback callback2;
|
|
|
| - rv = callback2.WaitForResult();
|
| - EXPECT_EQ(OK, rv);
|
| + rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBar),
|
| + callback2.callback());
|
| + ASSERT_EQ(OK, callback2.GetResult(rv));
|
|
|
| - LoadTimingInfo load_timing_info2;
|
| - EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2));
|
| - TestLoadTimingReused(load_timing_info2);
|
| - // The load timing after restart should have the same socket ID, and times
|
| - // those of the first load timing.
|
| - EXPECT_LE(load_timing_info1.receive_headers_end,
|
| - load_timing_info2.send_start);
|
| - EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
|
| + LoadTimingInfo load_timing_info2;
|
| + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2));
|
| + TestLoadTimingReused(load_timing_info2);
|
| + // The load timing after restart should have the same socket ID, and times
|
| + // those of the first load timing.
|
| + EXPECT_LE(load_timing_info1.receive_headers_end,
|
| + load_timing_info2.send_start);
|
| + EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
|
|
|
| - response = trans->GetResponseInfo();
|
| - ASSERT_TRUE(response != NULL);
|
| - EXPECT_TRUE(response->auth_challenge.get() == NULL);
|
| - EXPECT_EQ(5, response->headers->GetContentLength());
|
| + response = trans->GetResponseInfo();
|
| + ASSERT_TRUE(response);
|
| + EXPECT_FALSE(response->auth_challenge);
|
| + EXPECT_EQ(5, response->headers->GetContentLength());
|
|
|
| - std::string response_data;
|
| - rv = ReadTransaction(trans.get(), &response_data);
|
| - EXPECT_EQ(OK, rv);
|
| + std::string response_data;
|
| + EXPECT_EQ(OK, ReadTransaction(trans.get(), &response_data));
|
|
|
| - int64_t writes_size1 = CountWriteBytes(data_writes1, arraysize(data_writes1));
|
| - EXPECT_EQ(writes_size1, trans->GetTotalSentBytes());
|
| - int64_t reads_size1 = CountReadBytes(data_reads1, arraysize(data_reads1));
|
| - EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes());
|
| + int64_t writes_size = CountWriteBytes(data_writes, arraysize(data_writes));
|
| + EXPECT_EQ(writes_size, trans->GetTotalSentBytes());
|
| + int64_t reads_size = CountReadBytes(data_reads, arraysize(data_reads));
|
| + EXPECT_EQ(reads_size, trans->GetTotalReceivedBytes());
|
| + }
|
| }
|
|
|
| // Test the request-challenge-retry sequence for basic auth, over a keep-alive
|
| @@ -5881,11 +5900,8 @@ TEST_P(HttpNetworkTransactionTest, RecycleDeadSSLSocket) {
|
| };
|
|
|
| MockRead data_reads[] = {
|
| - MockRead("HTTP/1.1 200 OK\r\n"),
|
| - MockRead("Content-Length: 11\r\n\r\n"),
|
| - MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
|
| - MockRead("hello world"),
|
| - MockRead(ASYNC, 0, 0) // EOF
|
| + MockRead("HTTP/1.1 200 OK\r\n"), MockRead("Content-Length: 11\r\n\r\n"),
|
| + MockRead("hello world"), MockRead(ASYNC, ERR_CONNECTION_CLOSED) // EOF
|
| };
|
|
|
| SSLSocketDataProvider ssl(ASYNC, OK);
|
| @@ -5972,9 +5988,6 @@ TEST_P(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) {
|
|
|
| scoped_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
|
|
| - scoped_ptr<HttpTransaction> trans(
|
| - new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| -
|
| MockRead data_reads[] = {
|
| MockRead("HTTP/1.1 204 No Content\r\n"
|
| "Content-Length: 0\r\n"
|
| @@ -5986,6 +5999,11 @@ TEST_P(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) {
|
| StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
|
| session_deps_.socket_factory->AddSocketDataProvider(&data);
|
|
|
| + // Transaction must be created after the MockReads, so it's destroyed before
|
| + // them.
|
| + scoped_ptr<HttpTransaction> trans(
|
| + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
|
| +
|
| TestCompletionCallback callback;
|
|
|
| int rv = trans->Start(&request, callback.callback(), BoundNetLog());
|
| @@ -11155,7 +11173,6 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) {
|
| request.load_flags = 0;
|
|
|
| scoped_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
| - HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
|
|
|
| SSLSocketDataProvider ssl_socket_data_provider(SYNCHRONOUS, OK);
|
|
|
| @@ -11197,6 +11214,10 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) {
|
| data_providers.back());
|
| }
|
|
|
| + // Transaction must be created after DataProviders, so it's destroyed before
|
| + // they are as well.
|
| + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
|
| +
|
| for (int round = 0; round < test_config.num_auth_rounds; ++round) {
|
| const TestRound& read_write_round = test_config.rounds[round];
|
| // Start or restart the transaction.
|
|
|