Fix infinite retry on 408

Fix issue where receiving HTTP response status 408 on
an HTTP/2 connection would cause us to retry infinitely.
We now only retry for HTTP/1.1 (and 0.9 and 1.0) but not
HTTP/2 and later.

Bug: 695225
Change-Id: I6a3c2c9447e4dde536fa7925306b41de9d4afb28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1907188
Commit-Queue: David Schinazi <[email protected]>
Reviewed-by: Ryan Hamilton <[email protected]>
Auto-Submit: David Schinazi <[email protected]>
Cr-Commit-Position: refs/heads/master@{#713985}
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 86a7ad6..cb603a4 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -705,17 +705,22 @@
 
   void CreateSession() { return CreateSession(supported_versions_); }
 
-  void CheckWasQuicResponse(HttpNetworkTransaction* trans) {
+  void CheckWasQuicResponse(HttpNetworkTransaction* trans,
+                            const std::string& status_line) {
     const HttpResponseInfo* response = trans->GetResponseInfo();
     ASSERT_TRUE(response != nullptr);
     ASSERT_TRUE(response->headers.get() != nullptr);
-    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+    EXPECT_EQ(status_line, response->headers->GetStatusLine());
     EXPECT_TRUE(response->was_fetched_via_spdy);
     EXPECT_TRUE(response->was_alpn_negotiated);
     EXPECT_EQ(QuicHttpStream::ConnectionInfoFromQuicVersion(version_),
               response->connection_info);
   }
 
+  void CheckWasQuicResponse(HttpNetworkTransaction* trans) {
+    CheckWasQuicResponse(trans, "HTTP/1.1 200 OK");
+  }
+
   void CheckResponsePort(HttpNetworkTransaction* trans, uint16_t port) {
     const HttpResponseInfo* response = trans->GetResponseInfo();
     ASSERT_TRUE(response != nullptr);
@@ -784,6 +789,11 @@
       EXPECT_TRUE(trans.GetResponseInfo()->proxy_server.is_direct());
     }
   }
+  void SendRequestAndExpectQuicResponse(const std::string& expected,
+                                        const std::string& status_line) {
+    SendRequestAndExpectQuicResponseMaybeFromProxy(expected, false, 443,
+                                                   status_line);
+  }
 
   void SendRequestAndExpectQuicResponse(const std::string& expected) {
     SendRequestAndExpectQuicResponseMaybeFromProxy(expected, false, 443);
@@ -1051,14 +1061,15 @@
   void SendRequestAndExpectQuicResponseMaybeFromProxy(
       const std::string& expected,
       bool used_proxy,
-      uint16_t port) {
+      uint16_t port,
+      const std::string& status_line) {
     HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
     HeadersHandler headers_handler;
     trans.SetBeforeHeadersSentCallback(
         base::Bind(&HeadersHandler::OnBeforeHeadersSent,
                    base::Unretained(&headers_handler)));
     RunTransaction(&trans);
-    CheckWasQuicResponse(&trans);
+    CheckWasQuicResponse(&trans, status_line);
     CheckResponsePort(&trans, port);
     CheckResponseData(&trans, expected);
     EXPECT_EQ(used_proxy, headers_handler.was_proxied());
@@ -1068,6 +1079,14 @@
       EXPECT_TRUE(trans.GetResponseInfo()->proxy_server.is_direct());
     }
   }
+
+  void SendRequestAndExpectQuicResponseMaybeFromProxy(
+      const std::string& expected,
+      bool used_proxy,
+      uint16_t port) {
+    SendRequestAndExpectQuicResponseMaybeFromProxy(expected, used_proxy, port,
+                                                   "HTTP/1.1 200 OK");
+  }
 };
 
 INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
@@ -1492,6 +1511,43 @@
       test_socket_performance_watcher_factory_.rtt_notification_received());
 }
 
+// Regression test for https://crbug.com/695225
+TEST_P(QuicNetworkTransactionTest, 408Response) {
+  session_params_.quic_params.origins_to_force_quic_on.insert(HostPortPair());
+
+  AddQuicAlternateProtocolMapping(MockCryptoClientStream::CONFIRM_HANDSHAKE);
+
+  MockQuicData mock_quic_data(version_);
+  int packet_num = 1;
+  if (VersionUsesHttp3(version_.transport_version)) {
+    mock_quic_data.AddWrite(SYNCHRONOUS,
+                            ConstructInitialSettingsPacket(packet_num++));
+  }
+  mock_quic_data.AddWrite(
+      SYNCHRONOUS,
+      ConstructClientRequestHeadersPacket(
+          packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), true,
+          true, GetRequestHeaders("GET", "https", "/")));
+  mock_quic_data.AddRead(
+      ASYNC, ConstructServerResponseHeadersPacket(
+                 1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+                 GetResponseHeaders("408 Request Timeout")));
+  std::string header = ConstructDataHeader(6);
+  mock_quic_data.AddRead(
+      ASYNC, ConstructServerDataPacket(
+                 2, GetNthClientInitiatedBidirectionalStreamId(0), false, true,
+                 header + "hello!"));
+  mock_quic_data.AddWrite(SYNCHRONOUS,
+                          ConstructClientAckPacket(packet_num++, 2, 1, 1));
+  mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
+
+  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  CreateSession();
+
+  SendRequestAndExpectQuicResponse("hello!", "HTTP/1.1 408 Request Timeout");
+}
+
 TEST_P(QuicNetworkTransactionTest, QuicProxy) {
   session_params_.enable_quic = true;
   proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(