| 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 178ad60529e02259c68b37733fec80e05b252d82..11766c4b962681bf7f59de506f33f3b5ed3fdc5d 100644
|
| --- a/net/http/http_network_transaction_unittest.cc
|
| +++ b/net/http/http_network_transaction_unittest.cc
|
| @@ -13594,6 +13594,137 @@ TEST_F(HttpNetworkTransactionTest, UseIPConnectionPoolingAfterResolution) {
|
| EXPECT_EQ("hello!", response_data);
|
| }
|
|
|
| +// Regression test for https://crbug.com/546991.
|
| +// The server might not be able to serve an IP pooled request, and might send a
|
| +// 421 Misdirected Request response status to indicate this.
|
| +// HttpNetworkTransaction should reset the request and retry without IP pooling.
|
| +TEST_F(HttpNetworkTransactionTest, RetryWithoutConnectionPooling) {
|
| + // Two hosts resolve to the same IP address.
|
| + const std::string ip_addr = "1.2.3.4";
|
| + IPAddress ip;
|
| + ASSERT_TRUE(ip.AssignFromIPLiteral(ip_addr));
|
| + IPEndPoint peer_addr = IPEndPoint(ip, 443);
|
| +
|
| + session_deps_.host_resolver.reset(new MockCachingHostResolver());
|
| + session_deps_.host_resolver->rules()->AddRule("www.example.org", ip_addr);
|
| + session_deps_.host_resolver->rules()->AddRule("mail.example.org", ip_addr);
|
| +
|
| + std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
|
| +
|
| + // Two requests on the first connection.
|
| + SpdySerializedFrame req1(
|
| + spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
|
| + spdy_util_.UpdateWithStreamDestruction(1);
|
| + SpdySerializedFrame req2(
|
| + spdy_util_.ConstructSpdyGet("https://mail.example.org", 3, LOWEST));
|
| + SpdySerializedFrame rst(
|
| + spdy_util_.ConstructSpdyRstStream(3, ERROR_CODE_CANCEL));
|
| + MockWrite writes1[] = {
|
| + CreateMockWrite(req1, 0), CreateMockWrite(req2, 3),
|
| + CreateMockWrite(rst, 6),
|
| + };
|
| +
|
| + // The first one succeeds, the second gets error 421 Misdirected Request.
|
| + SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
|
| + SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
|
| + SpdyHeaderBlock response_headers;
|
| + response_headers[SpdyTestUtil::GetStatusKey()] = "421";
|
| + SpdySerializedFrame resp2(
|
| + spdy_util_.ConstructSpdyReply(3, std::move(response_headers)));
|
| + MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
|
| + CreateMockRead(resp2, 4), MockRead(ASYNC, 0, 5)};
|
| +
|
| + MockConnect connect1(ASYNC, OK, peer_addr);
|
| + SequencedSocketData data1(connect1, reads1, arraysize(reads1), writes1,
|
| + arraysize(writes1));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&data1);
|
| +
|
| + AddSSLSocketData();
|
| +
|
| + // Retry the second request on a second connection.
|
| + SpdyTestUtil spdy_util2;
|
| + SpdySerializedFrame req3(
|
| + spdy_util2.ConstructSpdyGet("https://mail.example.org", 1, LOWEST));
|
| + MockWrite writes2[] = {
|
| + CreateMockWrite(req3, 0),
|
| + };
|
| +
|
| + SpdySerializedFrame resp3(spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
|
| + SpdySerializedFrame body3(spdy_util2.ConstructSpdyDataFrame(1, true));
|
| + MockRead reads2[] = {CreateMockRead(resp3, 1), CreateMockRead(body3, 2),
|
| + MockRead(ASYNC, 0, 3)};
|
| +
|
| + MockConnect connect2(ASYNC, OK, peer_addr);
|
| + SequencedSocketData data2(connect2, reads2, arraysize(reads2), writes2,
|
| + arraysize(writes2));
|
| + session_deps_.socket_factory->AddSocketDataProvider(&data2);
|
| +
|
| + AddSSLSocketData();
|
| +
|
| + // Preload mail.example.org into HostCache.
|
| + HostPortPair host_port("mail.example.org", 443);
|
| + HostResolver::RequestInfo resolve_info(host_port);
|
| + AddressList ignored;
|
| + std::unique_ptr<HostResolver::Request> request;
|
| + TestCompletionCallback callback;
|
| + int rv = session_deps_.host_resolver->Resolve(resolve_info, DEFAULT_PRIORITY,
|
| + &ignored, callback.callback(),
|
| + &request, NetLogWithSource());
|
| + EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
| + rv = callback.WaitForResult();
|
| + EXPECT_THAT(rv, IsOk());
|
| +
|
| + HttpRequestInfo request1;
|
| + request1.method = "GET";
|
| + request1.url = GURL("https://www.example.org/");
|
| + request1.load_flags = 0;
|
| + HttpNetworkTransaction trans1(DEFAULT_PRIORITY, session.get());
|
| +
|
| + rv = trans1.Start(&request1, callback.callback(), NetLogWithSource());
|
| + EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
| + rv = callback.WaitForResult();
|
| + EXPECT_THAT(rv, IsOk());
|
| +
|
| + const HttpResponseInfo* response = trans1.GetResponseInfo();
|
| + ASSERT_TRUE(response);
|
| + ASSERT_TRUE(response->headers);
|
| + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
|
| + EXPECT_TRUE(response->was_fetched_via_spdy);
|
| + EXPECT_TRUE(response->was_alpn_negotiated);
|
| + std::string response_data;
|
| + ASSERT_THAT(ReadTransaction(&trans1, &response_data), IsOk());
|
| + EXPECT_EQ("hello!", response_data);
|
| +
|
| + HttpRequestInfo request2;
|
| + request2.method = "GET";
|
| + request2.url = GURL("https://mail.example.org/");
|
| + request2.load_flags = 0;
|
| + HttpNetworkTransaction trans2(DEFAULT_PRIORITY, session.get());
|
| +
|
| + BoundTestNetLog log;
|
| + rv = trans2.Start(&request2, callback.callback(), log.bound());
|
| + EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
| + rv = callback.WaitForResult();
|
| + EXPECT_THAT(rv, IsOk());
|
| +
|
| + response = trans2.GetResponseInfo();
|
| + ASSERT_TRUE(response);
|
| + ASSERT_TRUE(response->headers);
|
| + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
|
| + EXPECT_TRUE(response->was_fetched_via_spdy);
|
| + EXPECT_TRUE(response->was_alpn_negotiated);
|
| + ASSERT_THAT(ReadTransaction(&trans2, &response_data), IsOk());
|
| + EXPECT_EQ("hello!", response_data);
|
| +
|
| + TestNetLogEntry::List entries;
|
| + log.GetEntries(&entries);
|
| + size_t pos = ExpectLogContainsSomewhere(
|
| + entries, 0, NetLogEventType::HTTP_TRANSACTION_RESTART_AFTER_ERROR,
|
| + NetLogEventPhase::NONE);
|
| + EXPECT_TRUE(entries[pos].GetIntegerValue("net_error", &rv));
|
| + EXPECT_THAT(rv, IsError(ERR_MISDIRECTED_REQUEST));
|
| +}
|
| +
|
| class OneTimeCachingHostResolver : public HostResolver {
|
| public:
|
| explicit OneTimeCachingHostResolver(const HostPortPair& host_port)
|
| @@ -14953,6 +15084,7 @@ class FakeStreamFactory : public HttpStreamFactory {
|
| const SSLConfig& server_ssl_config,
|
| const SSLConfig& proxy_ssl_config,
|
| HttpStreamRequest::Delegate* delegate,
|
| + bool enable_ip_based_pooling,
|
| const NetLogWithSource& net_log) override {
|
| FakeStreamRequest* fake_request = new FakeStreamRequest(priority, delegate);
|
| last_stream_request_ = fake_request->AsWeakPtr();
|
| @@ -14965,6 +15097,7 @@ class FakeStreamFactory : public HttpStreamFactory {
|
| const SSLConfig& server_ssl_config,
|
| const SSLConfig& proxy_ssl_config,
|
| HttpStreamRequest::Delegate* delegate,
|
| + bool enable_ip_based_pooling,
|
| const NetLogWithSource& net_log) override {
|
| NOTREACHED();
|
| return nullptr;
|
| @@ -14977,6 +15110,7 @@ class FakeStreamFactory : public HttpStreamFactory {
|
| const SSLConfig& proxy_ssl_config,
|
| HttpStreamRequest::Delegate* delegate,
|
| WebSocketHandshakeStreamBase::CreateHelper* create_helper,
|
| + bool enable_ip_based_pooling,
|
| const NetLogWithSource& net_log) override {
|
| FakeStreamRequest* fake_request =
|
| new FakeStreamRequest(priority, delegate, create_helper);
|
|
|