Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(956)

Unified Diff: net/http/http_network_transaction_unittest.cc

Issue 2771263002: Retry upon 421 status code without IP pooling. (Closed)
Patch Set: Rebase. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);

Powered by Google App Engine
This is Rietveld 408576698