chromium/net/spdy/spdy_network_transaction_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include <cmath>
#include <string_view>
#include <utility>
#include <vector>

#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_file_util.h"
#include "build/build_config.h"
#include "net/base/auth.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/completion_once_callback.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/features.h"
#include "net/base/hex_utils.h"
#include "net/base/ip_endpoint.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/proxy_delegate.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/request_priority.h"
#include "net/base/schemeful_site.h"
#include "net/base/session_usage.h"
#include "net/base/test_proxy_delegate.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_file_element_reader.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/http_auth_scheme.h"
#include "net/http/http_connection_info.h"
#include "net/http/http_network_session.h"
#include "net/http/http_network_session_peer.h"
#include "net/http/http_network_transaction.h"
#include "net/http/http_proxy_connect_job.h"
#include "net/http/http_response_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_transaction_test_util.h"
#include "net/http/test_upload_data_stream_not_allow_http1.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_with_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_tag.h"
#include "net/spdy/alps_decoder.h"
#include "net/spdy/buffered_spdy_framer.h"
#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/test/cert_test_util.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/third_party/quiche/src/quiche/common/http/http_header_block.h"
#include "net/third_party/quiche/src/quiche/http2/test_tools/spdy_test_utils.h"
#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
#include "url/gurl.h"
#include "url/url_constants.h"

IsError;
IsOk;

//-----------------------------------------------------------------------------

namespace net {

namespace {

Each;
Eq;

const int32_t kBufferSize =;

}  // namespace

const char kPushedUrl[] =;

class SpdyNetworkTransactionTest : public TestWithTaskEnvironment,
                                   public ::testing::WithParamInterface<bool> {};

INSTANTIATE_TEST_SUITE_P();

// Verify HttpNetworkTransaction constructor.
TEST_P(SpdyNetworkTransactionTest, Constructor) {}

TEST_P(SpdyNetworkTransactionTest, Get) {}

TEST_P(SpdyNetworkTransactionTest, SetPriority) {}

// Test that changing the request priority of an existing stream triggers
// sending PRIORITY frames in case there are multiple open streams and their
// relative priorities change.
TEST_P(SpdyNetworkTransactionTest, SetPriorityOnExistingStream) {}

// Create two requests: a lower priority one first, then a higher priority one.
// Test that the second request gets sent out first.
TEST_P(SpdyNetworkTransactionTest, RequestsOrderedByPriority) {}

// Test that already enqueued HEADERS frames are reordered if their relative
// priority changes.
TEST_P(SpdyNetworkTransactionTest, QueuedFramesReorderedOnPriorityChange) {}

TEST_P(SpdyNetworkTransactionTest, GetAtEachPriority) {}

// Start three gets simultaniously; making sure that multiplexed
// streams work properly.

// This can't use the TransactionHelper method, since it only
// handles a single transaction, and finishes them as soon
// as it launches them.

// TODO(gavinp): create a working generalized TransactionHelper that
// can allow multiple streams in flight.

TEST_P(SpdyNetworkTransactionTest, ThreeGets) {}

TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBinding) {}

TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) {}

// Similar to ThreeGets above, however this test adds a SETTINGS
// frame.  The SETTINGS frame is read during the IO loop waiting on
// the first transaction completion, and sets a maximum concurrent
// stream limit of 1.  This means that our IO loop exists after the
// second transaction completes, so we can assert on read_index().
TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) {}

// Similar to ThreeGetsWithMaxConcurrent above, however this test adds
// a fourth transaction.  The third and fourth transactions have
// different data ("hello!" vs "hello!hello!") and because of the
// user specified priority, we expect to see them inverted in
// the response from the server.
TEST_P(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) {}

// Similar to ThreeGetsMaxConcurrrent above, however, this test
// deletes a session in the middle of the transaction to ensure
// that we properly remove pendingcreatestream objects from
// the spdy_session
TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) {}

namespace {

// A helper class that will delete |transaction| on error when the callback is
// invoked.
class KillerCallback : public TestCompletionCallbackBase {};

}  // namespace

// Similar to ThreeGetsMaxConcurrrentDelete above, however, this test
// closes the socket while we have a pending transaction waiting for
// a pending stream creation.  http://crbug.com/52901
TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) {}

// Test that a simple PUT request works.
TEST_P(SpdyNetworkTransactionTest, Put) {}

// Test that a simple HEAD request works.
TEST_P(SpdyNetworkTransactionTest, Head) {}

// Test that a simple POST works.
TEST_P(SpdyNetworkTransactionTest, Post) {}

// Test that a POST with a file works.
TEST_P(SpdyNetworkTransactionTest, FilePost) {}

// Test that a POST with a unreadable file fails.
TEST_P(SpdyNetworkTransactionTest, UnreadableFilePost) {}

// Test that a complex POST works.
TEST_P(SpdyNetworkTransactionTest, ComplexPost) {}

// Test that a chunked POST works.
TEST_P(SpdyNetworkTransactionTest, ChunkedPost) {}

// Test that a chunked POST works with chunks appended after transaction starts.
TEST_P(SpdyNetworkTransactionTest, DelayedChunkedPost) {}

// Test that a POST without any post data works.
TEST_P(SpdyNetworkTransactionTest, NullPost) {}

// Test that a simple POST works.
TEST_P(SpdyNetworkTransactionTest, EmptyPost) {}

// While we're doing a post, the server sends the reply before upload completes.
TEST_P(SpdyNetworkTransactionTest, ResponseBeforePostCompletes) {}

// The client upon cancellation tries to send a RST_STREAM frame. The mock
// socket causes the TCP write to return zero. This test checks that the client
// tries to queue up the RST_STREAM frame again.
TEST_P(SpdyNetworkTransactionTest, SocketWriteReturnsZero) {}

// Test that the transaction doesn't crash when we don't have a reply.
TEST_P(SpdyNetworkTransactionTest, ResponseWithoutHeaders) {}

// Test that the transaction doesn't crash when we get two replies on the same
// stream ID. See http://crbug.com/45639.
TEST_P(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) {}

TEST_P(SpdyNetworkTransactionTest, ResetReplyWithTransferEncoding) {}

TEST_P(SpdyNetworkTransactionTest, CancelledTransaction) {}

// Verify that the client sends a Rst Frame upon cancelling the stream.
TEST_P(SpdyNetworkTransactionTest, CancelledTransactionSendRst) {}

// Verify that the client can correctly deal with the user callback attempting
// to start another transaction on a session that is closing down. See
// http://crbug.com/47455
TEST_P(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) {}

// Verify that the client can correctly deal with the user callback deleting
// the transaction. Failures will usually be flagged by thread and/or memory
// checking tools. See http://crbug.com/46925
TEST_P(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) {}

TEST_P(SpdyNetworkTransactionTest, RedirectGetRequest) {}

TEST_P(SpdyNetworkTransactionTest, RedirectMultipleLocations) {}

TEST_P(SpdyNetworkTransactionTest, NoConnectionPoolingOverTunnel) {}

// Check that if a session is found after host resolution, but is closed before
// the task to try to use it executes, the request will continue to create a new
// socket and use it.
TEST_P(SpdyNetworkTransactionTest, ConnectionPoolingSessionClosedBeforeUse) {}

// Check that requests with differe LOAD_DISABLE_CERT_NETWORK_FETCHES values do
// not share a session.
TEST_P(SpdyNetworkTransactionTest,
       ConnectionPoolingDisableCertVerificationNetworkFetches) {}

#if BUILDFLAG(IS_ANDROID)

// Test this if two HttpNetworkTransactions try to repurpose the same
// SpdySession with two different SocketTags, only one request gets the session,
// while the other makes a new SPDY session.
TEST_P(SpdyNetworkTransactionTest, ConnectionPoolingMultipleSocketTags) {
  const SocketTag kSocketTag1(SocketTag::UNSET_UID, 1);
  const SocketTag kSocketTag2(SocketTag::UNSET_UID, 2);
  const SocketTag kSocketTag3(SocketTag::UNSET_UID, 3);

  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);

  // The first and third requests use the first connection.
  spdy::SpdySerializedFrame req1(
      spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
  spdy_util_.UpdateWithStreamDestruction(1);
  spdy::SpdySerializedFrame req3(
      spdy_util_.ConstructSpdyGet("https://example.test/request3", 3, LOWEST));
  MockWrite writes1[] = {
      CreateMockWrite(req1, 0),
      CreateMockWrite(req3, 3),
  };

  spdy::SpdySerializedFrame resp1(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
  spdy::SpdySerializedFrame resp3(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
  spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(3, true));
  MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
                       CreateMockRead(resp3, 4), CreateMockRead(body3, 5),
                       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};

  SequencedSocketData data1(MockConnect(ASYNC, OK), reads1, writes1);
  helper.AddData(&data1);

  // Due to the vagaries of how the socket pools work, in this particular case,
  // the second ConnectJob will be cancelled, but only after it tries to start
  // connecting. This does not happen in the general case of a bunch of requests
  // using the same socket tag.
  SequencedSocketData data2(MockConnect(SYNCHRONOUS, ERR_IO_PENDING),
                            base::span<const MockRead>(),
                            base::span<const MockWrite>());
  helper.AddData(&data2);

  // The second request uses a second connection.
  SpdyTestUtil spdy_util2(/*use_priority_header=*/true);
  spdy::SpdySerializedFrame req2(
      spdy_util2.ConstructSpdyGet("https://example.test/request2", 1, LOWEST));
  MockWrite writes2[] = {
      CreateMockWrite(req2, 0),
  };

  spdy::SpdySerializedFrame resp2(
      spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
  spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true));
  MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2),
                       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};

  SequencedSocketData data3(MockConnect(ASYNC, OK), reads2, writes2);
  helper.AddData(&data3);

  // Run a transaction to completion to set up a SPDY session. This can't use
  // RunToCompletion(), since it can't call VerifyDataConsumed() yet.
  helper.RunPreTestSetup();
  helper.RunDefaultTest();
  TransactionHelperResult out = helper.output();
  EXPECT_THAT(out.rv, IsOk());
  EXPECT_EQ("HTTP/1.1 200", out.status_line);
  EXPECT_EQ("hello!", out.response_data);

  // A new SPDY session should have been created.
  SpdySessionKey key1(HostPortPair("www.example.org", 443),
                      PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                      SessionUsage::kDestination, SocketTag(),
                      NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));

  // Set on-demand mode for the next two requests.
  helper.session_deps()->host_resolver->set_ondemand_mode(true);

  HttpRequestInfo request2;
  request2.socket_tag = kSocketTag2;
  request2.method = "GET";
  request2.url = GURL("https://example.test/request2");
  request2.load_flags = 0;
  request2.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback2;
  EXPECT_THAT(
      trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  HttpRequestInfo request3;
  request3.socket_tag = kSocketTag3;
  request3.method = "GET";
  request3.url = GURL("https://example.test/request3");
  request3.load_flags = 0;
  request3.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback3;
  EXPECT_THAT(
      trans3->Start(&request3, callback3.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  // Run the message loop until both requests are waiting on the host resolver.
  base::RunLoop().RunUntilIdle();
  ASSERT_TRUE(helper.session_deps()->host_resolver->has_pending_requests());

  // Complete the second requests's DNS lookup now, which should create an alias
  // for the SpdySession immediately, but the task to use the session for the
  // second request should run asynchronously, so it hasn't run yet.
  helper.session_deps()->host_resolver->ResolveNow(2);
  SpdySessionKey key2(HostPortPair("example.test", 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      kSocketTag2, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);

  // Complete the third requests's DNS lookup now, which should hijack the
  // SpdySession from the second request.
  helper.session_deps()->host_resolver->ResolveNow(3);
  SpdySessionKey key3(HostPortPair("example.test", 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      kSocketTag3, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);

  // Wait for the second request to get headers.  It should create a new H2
  // session to do so.
  EXPECT_THAT(callback2.WaitForResult(), IsOk());

  const HttpResponseInfo* 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);
  std::string response_data;
  ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  // Wait for the third request to get headers.  It should have reused the first
  // session.
  EXPECT_THAT(callback3.WaitForResult(), IsOk());

  response = trans3->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(trans3.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  helper.VerifyDataConsumed();
}

TEST_P(SpdyNetworkTransactionTest, SocketTagChangeSessionTagWithDnsAliases) {
  SocketTag socket_tag_1(SocketTag::UNSET_UID, 1);
  SocketTag socket_tag_2(SocketTag::UNSET_UID, 2);
  request_.socket_tag = socket_tag_1;

  std::unique_ptr<SpdySessionDependencies> session_deps =
      std::make_unique<SpdySessionDependencies>();
  std::unique_ptr<MockCachingHostResolver> host_resolver =
      std::make_unique<MockCachingHostResolver>(2 /* cache_invalidation_num */);
  session_deps->host_resolver = std::move(host_resolver);

  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
                                     std::move(session_deps));

  GURL url = request_.url;
  std::set<std::string> dns_aliases({"alias1", "alias2", "alias3"});
  helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
      url.host(), "127.0.0.1", dns_aliases);

  spdy::SpdySerializedFrame req1(
      spdy_util_.ConstructSpdyGet(url.spec().c_str(), 1, DEFAULT_PRIORITY));
  spdy_util_.UpdateWithStreamDestruction(1);
  spdy::SpdySerializedFrame req2(
      spdy_util_.ConstructSpdyGet(url.spec().c_str(), 3, DEFAULT_PRIORITY));
  MockWrite writes[] = {
      CreateMockWrite(req1, 0),
      CreateMockWrite(req2, 3),
  };

  spdy::SpdySerializedFrame resp1(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
  spdy::SpdySerializedFrame resp2(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
  MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
                      CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
                      MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};

  SequencedSocketData data(MockConnect(ASYNC, OK), reads, writes);
  helper.AddData(&data);

  // Run a transaction to completion to set up a SPDY session. This can't use
  // RunToCompletion(), since it can't call VerifyDataConsumed() yet because
  // there are still further requests expected.
  helper.RunPreTestSetup();
  helper.RunDefaultTest();
  TransactionHelperResult out = helper.output();
  EXPECT_THAT(out.rv, IsOk());
  EXPECT_EQ("HTTP/1.1 200", out.status_line);
  EXPECT_EQ("hello!", out.response_data);

  // A new SPDY session should have been created.
  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  SpdySessionKey key1(HostPortPair(url.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_1, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key1));

  // Clear host resolver rules to ensure that cached values for DNS aliases
  // are used.
  helper.session_deps()->host_resolver->rules()->ClearRules();

  HttpRequestInfo request2;
  request2.socket_tag = socket_tag_2;
  request2.method = "GET";
  request2.url = url;
  request2.load_flags = 0;
  request2.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  SpdySessionKey key2(HostPortPair(url.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_2, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback2;
  EXPECT_THAT(
      trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  // Wait for the second request to get headers.  It should have reused the
  // first session but changed the tag.
  EXPECT_THAT(callback2.WaitForResult(), IsOk());

  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_TRUE(helper.session()
                  ->spdy_session_pool()
                  ->GetDnsAliasesForSessionKey(key1)
                  .empty());
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key2));

  const HttpResponseInfo* 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);
  std::string response_data;
  ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  helper.VerifyDataConsumed();
}

TEST_P(SpdyNetworkTransactionTest,
       SocketTagChangeFromIPAliasedSessionWithDnsAliases) {
  SocketTag socket_tag_1(SocketTag::UNSET_UID, 1);
  SocketTag socket_tag_2(SocketTag::UNSET_UID, 2);
  request_.socket_tag = socket_tag_1;

  std::unique_ptr<SpdySessionDependencies> session_deps =
      std::make_unique<SpdySessionDependencies>();
  std::unique_ptr<MockCachingHostResolver> host_resolver =
      std::make_unique<MockCachingHostResolver>(2 /* cache_invalidation_num */);
  session_deps->host_resolver = std::move(host_resolver);

  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
                                     std::move(session_deps));
  GURL url1 = request_.url;
  std::set<std::string> dns_aliases1({"alias1", "alias2", "alias3"});
  GURL url2("https://example.test/");
  std::set<std::string> dns_aliases2({"example.net", "example.com"});

  helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
      url1.host(), "127.0.0.1", dns_aliases1);
  helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
      url2.host(), "127.0.0.1", dns_aliases2);

  spdy::SpdySerializedFrame req1(
      spdy_util_.ConstructSpdyGet(url1.spec().c_str(), 1, DEFAULT_PRIORITY));
  spdy_util_.UpdateWithStreamDestruction(1);
  spdy::SpdySerializedFrame req2(
      spdy_util_.ConstructSpdyGet(url2.spec().c_str(), 3, DEFAULT_PRIORITY));
  spdy_util_.UpdateWithStreamDestruction(3);
  spdy::SpdySerializedFrame req3(
      spdy_util_.ConstructSpdyGet(url2.spec().c_str(), 5, DEFAULT_PRIORITY));
  spdy_util_.UpdateWithStreamDestruction(5);
  spdy::SpdySerializedFrame req4(
      spdy_util_.ConstructSpdyGet(url1.spec().c_str(), 7, DEFAULT_PRIORITY));
  MockWrite writes[] = {
      CreateMockWrite(req1, 0),
      CreateMockWrite(req2, 3),
      CreateMockWrite(req3, 6),
      CreateMockWrite(req4, 9),
  };

  spdy::SpdySerializedFrame resp1(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
  spdy::SpdySerializedFrame resp2(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
  spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
  spdy::SpdySerializedFrame resp3(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
  spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true));
  spdy::SpdySerializedFrame resp4(
      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 7));
  spdy::SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame(7, true));
  MockRead reads[] = {CreateMockRead(resp1, 1),
                      CreateMockRead(body1, 2),
                      CreateMockRead(resp2, 4),
                      CreateMockRead(body2, 5),
                      CreateMockRead(resp3, 7),
                      CreateMockRead(body3, 8),
                      CreateMockRead(resp4, 10),
                      CreateMockRead(body4, 11),
                      MockRead(SYNCHRONOUS, ERR_IO_PENDING, 12)};

  SequencedSocketData data(MockConnect(ASYNC, OK), reads, writes);
  helper.AddData(&data);

  // Run a transaction to completion to set up a SPDY session. This can't use
  // RunToCompletion(), since it can't call VerifyDataConsumed() yet.
  helper.RunPreTestSetup();
  helper.RunDefaultTest();
  TransactionHelperResult out = helper.output();
  EXPECT_THAT(out.rv, IsOk());
  EXPECT_EQ("HTTP/1.1 200", out.status_line);
  EXPECT_EQ("hello!", out.response_data);

  // A new SPDY session should have been created.
  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  SpdySessionKey key1(HostPortPair(url1.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_1, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases1,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key1));

  HttpRequestInfo request2;
  request2.socket_tag = socket_tag_1;
  request2.method = "GET";
  request2.url = url2;
  request2.load_flags = 0;
  request2.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  SpdySessionKey key2(HostPortPair(url2.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_1, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback2;
  EXPECT_THAT(
      trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  // Wait for the second request to get headers.  It should have reused the
  // first session.
  EXPECT_THAT(callback2.WaitForResult(), IsOk());

  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases2,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key2));

  const HttpResponseInfo* 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);
  std::string response_data;
  ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  // Clear host resolver rules to ensure that cached values for DNS aliases
  // are used.
  helper.session_deps()->host_resolver->rules()->ClearRules();
  trans2.reset();

  HttpRequestInfo request3;
  request3.socket_tag = socket_tag_2;
  request3.method = "GET";
  request3.url = url2;
  request3.load_flags = 0;
  request3.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  SpdySessionKey key3(HostPortPair(url2.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_2, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback3;
  EXPECT_THAT(
      trans3->Start(&request3, callback3.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  // Wait for the third request to get headers.  It should have reused the
  // first session but changed the socket tag.
  EXPECT_THAT(callback3.WaitForResult(), IsOk());

  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_TRUE(helper.session()
                  ->spdy_session_pool()
                  ->GetDnsAliasesForSessionKey(key2)
                  .empty());
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key3, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases2,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key3));

  response = trans3->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(trans3.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  trans3.reset();

  HttpRequestInfo request4;
  request4.socket_tag = socket_tag_2;
  request4.method = "GET";
  request4.url = url1;
  request4.load_flags = 0;
  request4.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  SpdySessionKey key4(HostPortPair(url1.host(), 443), PRIVACY_MODE_DISABLED,
                      ProxyChain::Direct(), SessionUsage::kDestination,
                      socket_tag_2, NetworkAnonymizationKey(),
                      SecureDnsPolicy::kAllow,
                      /*disable_cert_verification_network_fetches=*/false);
  auto trans4 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
                                                         helper.session());
  TestCompletionCallback callback4;
  EXPECT_THAT(
      trans4->Start(&request4, callback4.callback(), NetLogWithSource()),
      IsError(ERR_IO_PENDING));

  // Wait for the third request to get headers.  It should have reused the
  // first session but changed the socket tag.
  EXPECT_THAT(callback4.WaitForResult(), IsOk());

  EXPECT_EQ(1u, helper.GetSpdySessionCount());
  EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_TRUE(helper.session()
                  ->spdy_session_pool()
                  ->GetDnsAliasesForSessionKey(key1)
                  .empty());
  EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
      key4, true /* enable_ip_based_pooling */, false /* is_websocket */,
      NetLogWithSource()));
  EXPECT_EQ(
      dns_aliases1,
      helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key4));

  response = trans4->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(trans4.get(), &response_data), IsOk());
  EXPECT_EQ("hello!", response_data);

  helper.VerifyDataConsumed();
}

#endif  // BUILDFLAG(IS_ANDROID)

// Verify that various response headers parse correctly through the HTTP layer.
TEST_P(SpdyNetworkTransactionTest, ResponseHeaders) {}

// Verify that we don't crash on invalid response headers.
TEST_P(SpdyNetworkTransactionTest, InvalidResponseHeaders) {}

TEST_P(SpdyNetworkTransactionTest, CorruptFrameSessionError) {}

TEST_P(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) {}

TEST_P(SpdyNetworkTransactionTest, GoAwayOnFrameSizeError) {}

// Test that we shutdown correctly on write errors.
TEST_P(SpdyNetworkTransactionTest, WriteError) {}

// Test that partial writes work.
TEST_P(SpdyNetworkTransactionTest, PartialWrite) {}

// Test that the NetLog contains good data for a simple GET request.
TEST_P(SpdyNetworkTransactionTest, NetLog) {}

TEST_P(SpdyNetworkTransactionTest, NetLogForResponseWithNoBody) {}

// Since we buffer the IO from the stream to the renderer, this test verifies
// that when we read out the maximum amount of data (e.g. we received 50 bytes
// on the network, but issued a Read for only 5 of those bytes) that the data
// flow still works correctly.
TEST_P(SpdyNetworkTransactionTest, BufferFull) {}

// Verify that basic buffering works; when multiple data frames arrive
// at the same time, ensure that we don't notify a read completion for
// each data frame individually.
TEST_P(SpdyNetworkTransactionTest, Buffering) {}

// Verify the case where we buffer data but read it after it has been buffered.
TEST_P(SpdyNetworkTransactionTest, BufferedAll) {}

// Verify the case where we buffer data and close the connection.
TEST_P(SpdyNetworkTransactionTest, BufferedClosed) {}

// Verify the case where we buffer data and cancel the transaction.
TEST_P(SpdyNetworkTransactionTest, BufferedCancelled) {}

// Request should fail upon receiving a GOAWAY frame
// with Last-Stream-ID lower than the stream id corresponding to the request
// and with error code other than NO_ERROR.
TEST_P(SpdyNetworkTransactionTest, FailOnGoAway) {}

// Request should be retried on a new connection upon receiving a GOAWAY frame
// with Last-Stream-ID lower than the stream id corresponding to the request
// and with error code NO_ERROR.
TEST_P(SpdyNetworkTransactionTest, RetryOnGoAway) {}

// A server can gracefully shut down by sending a GOAWAY frame
// with maximum last-stream-id value.
// Transactions started before receiving such a GOAWAY frame should succeed,
// but SpdySession should be unavailable for new streams.
TEST_P(SpdyNetworkTransactionTest, GracefulGoaway) {}

// Verify that an active stream with ID not exceeding the Last-Stream-ID field
// of the incoming GOAWAY frame can receive data both before and after the
// GOAWAY frame.
TEST_P(SpdyNetworkTransactionTest, ActiveStreamWhileGoingAway) {}

TEST_P(SpdyNetworkTransactionTest, CloseWithActiveStream) {}

TEST_P(SpdyNetworkTransactionTest, GoAwayImmediately) {}

// Retry with HTTP/1.1 when receiving HTTP_1_1_REQUIRED.  Note that no actual
// protocol negotiation happens, instead this test forces protocols for both
// sockets.
TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredRetry) {}

// Same as above test, but checks that NetworkAnonymizationKeys are respected.
TEST_P(SpdyNetworkTransactionTest,
       HTTP11RequiredRetryWithNetworkAnonymizationKey) {}

// Retry with HTTP/1.1 to the proxy when receiving HTTP_1_1_REQUIRED from the
// proxy.  Note that no actual protocol negotiation happens, instead this test
// forces protocols for both sockets.
TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) {}

// Same as above, but also test that NetworkAnonymizationKeys are respected.
TEST_P(SpdyNetworkTransactionTest,
       HTTP11RequiredProxyRetryWithNetworkAnonymizationKey) {}

// Same as HTTP11RequiredProxyRetry above except for nested proxies where
// HTTP_1_1_REQUIRED is received from the first proxy in the chain.
TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredNestedProxyFirstProxyRetry) {}

// Same as above except for nested proxies where HTTP_1_1_REQUIRED is received
// from the second proxy in the chain.
TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredNestedProxySecondProxyRetry) {}

// Test to make sure we can correctly connect through a proxy.
TEST_P(SpdyNetworkTransactionTest, ProxyConnect) {}

// Test to make sure we can correctly connect through a proxy to
// www.example.org, if there already exists a direct spdy connection to
// www.example.org. See https://crbug.com/49874.
TEST_P(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) {}

// When we get a TCP-level RST, we need to retry a HttpNetworkTransaction
// on a new connection, if the connection was previously known to be good.
// This can happen when a server reboots without saying goodbye, or when
// we're behind a NAT that masked the RST.
TEST_P(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) {}

// Tests that Basic authentication works over SPDY
TEST_P(SpdyNetworkTransactionTest, SpdyBasicAuth) {}

TEST_P(SpdyNetworkTransactionTest, ResponseHeadersTwice) {}

// Tests that receiving HEADERS, DATA, HEADERS, and DATA in that sequence will
// trigger a ERR_HTTP2_PROTOCOL_ERROR because trailing HEADERS must not be
// followed by any DATA frames.
TEST_P(SpdyNetworkTransactionTest, SyncReplyDataAfterTrailers) {}

TEST_P(SpdyNetworkTransactionTest, RetryAfterRefused) {}

TEST_P(SpdyNetworkTransactionTest, OutOfOrderHeaders) {}

// Test that sent data frames and received WINDOW_UPDATE frames change
// the send_window_size_ correctly.

// WINDOW_UPDATE is different than most other frames in that it can arrive
// while the client is still sending the request body.  In order to enforce
// this scenario, we feed a couple of dummy frames and give a delay of 0 to
// socket data provider, so that initial read that is done as soon as the
// stream is created, succeeds and schedules another read.  This way reads
// and writes are interleaved; after doing a full frame write, SpdyStream
// will break out of DoLoop and will read and process a WINDOW_UPDATE.
// Once our WINDOW_UPDATE is read, we cannot send HEADERS right away
// since request has not been completely written, therefore we feed
// enough number of WINDOW_UPDATEs to finish the first read and cause a
// write, leading to a complete write of request body; after that we send
// a reply with a body, to cause a graceful shutdown.

// TODO(agayev): develop a socket data provider where both, reads and
// writes are ordered so that writing tests like these are easy and rewrite
// all these tests using it.  Right now we are working around the
// limitations as described above and it's not deterministic, tests may
// fail under specific circumstances.
TEST_P(SpdyNetworkTransactionTest, WindowUpdateReceived) {}

// Test that received data frames and sent WINDOW_UPDATE frames change
// the recv_window_size_ correctly.
TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) {}

// Test that WINDOW_UPDATE frame causing overflow is handled correctly.
TEST_P(SpdyNetworkTransactionTest, WindowUpdateOverflow) {}

// Regression test for https://crbug.com/732019.
// RFC7540 Section 6.9.2: A spdy::SETTINGS_INITIAL_WINDOW_SIZE change that
// causes any stream flow control window to overflow MUST be treated as a
// connection error.
TEST_P(SpdyNetworkTransactionTest, InitialWindowSizeOverflow) {}

// Tests that we close the connection if we try to enqueue more frames than
// the cap allows.
TEST_P(SpdyNetworkTransactionTest, SessionMaxQueuedCappedFramesExceeded) {}

// Test that after hitting a send window size of 0, the write process
// stalls and upon receiving WINDOW_UPDATE frame write resumes.

// This test constructs a POST request followed by enough data frames
// containing 'a' that would make the window size 0, followed by another
// data frame containing default content (which is "hello!") and this frame
// also contains a FIN flag.  SequencedSocketData is used to enforce all
// writes, save the last, go through before a read could happen.  The last frame
// ("hello!") is not permitted to go through since by the time its turn
// arrives, window size is 0.  At this point MessageLoop::Run() called via
// callback would block.  Therefore we call MessageLoop::RunUntilIdle()
// which returns after performing all possible writes.  We use DCHECKS to
// ensure that last data frame is still there and stream has stalled.
// After that, next read is artifically enforced, which causes a
// WINDOW_UPDATE to be read and I/O process resumes.
TEST_P(SpdyNetworkTransactionTest, FlowControlStallResume) {}

// Test we correctly handle the case where the SETTINGS frame results in
// unstalling the send window.
TEST_P(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) {}

// Test we correctly handle the case where the SETTINGS frame results in a
// negative send window size.
TEST_P(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) {}

TEST_P(SpdyNetworkTransactionTest, ReceivingPushIsConnectionError) {}

// Push streams must have even stream IDs. Test that an incoming push stream
// with odd ID is reset the same way as one with even ID.
TEST_P(SpdyNetworkTransactionTest,
       ReceivingPushWithOddStreamIdIsConnectionError) {}

// Regression test for https://crbug.com/493348: request header exceeds 16 kB
// and thus sent in multiple frames when using HTTP/2.
TEST_P(SpdyNetworkTransactionTest, LargeRequest) {}

// Regression test for https://crbug.com/535629: response header exceeds 16 kB.
TEST_P(SpdyNetworkTransactionTest, LargeResponseHeader) {}

// End of line delimiter is forbidden according to RFC 7230 Section 3.2.
TEST_P(SpdyNetworkTransactionTest, CRLFInHeaderValue) {}

// Regression test for https://crbug.com/603182.
// No response headers received before RST_STREAM: error.
TEST_P(SpdyNetworkTransactionTest, RstStreamNoError) {}

// Regression test for https://crbug.com/603182.
// Response headers and data, then RST_STREAM received,
// before request body is sent: success.
TEST_P(SpdyNetworkTransactionTest, RstStreamNoErrorAfterResponse) {}

TEST_P(SpdyNetworkTransactionTest, 100Continue) {}

// "A server can send a complete response prior to the client sending an entire
// request if the response does not depend on any portion of the request that
// has not been sent and received."  (RFC7540 Section 8.1)
// Regression test for https://crbug.com/606990.  Server responds before POST
// data are sent and closes connection: this must result in
// ERR_CONNECTION_CLOSED (as opposed to ERR_HTTP2_PROTOCOL_ERROR).
TEST_P(SpdyNetworkTransactionTest, ResponseBeforePostDataSent) {}

// Regression test for https://crbug.com/606990.
// Server responds before POST data are sent and resets stream with NO_ERROR.
TEST_P(SpdyNetworkTransactionTest, ResponseAndRstStreamBeforePostDataSent) {}

// Unsupported frames must be ignored.  This is especially important for frame
// type 0xb, which used to be the BLOCKED frame in previous versions of SPDY,
// but is going to be used for the ORIGIN frame.
// TODO(bnc): Implement ORIGIN frame support.  https://crbug.com/697333
TEST_P(SpdyNetworkTransactionTest, IgnoreUnsupportedOriginFrame) {}

class SpdyNetworkTransactionTLSUsageCheckTest
    : public SpdyNetworkTransactionTest {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) {}

TEST_P(SpdyNetworkTransactionTLSUsageCheckTest, TLSCipherSuiteSucky) {}

// Regression test for https://crbug.com/737143.
// This test sets up an old TLS version just like in TLSVersionTooOld,
// and makes sure that it results in an spdy::ERROR_CODE_INADEQUATE_SECURITY
// even for a non-secure request URL.
TEST_P(SpdyNetworkTransactionTest, InsecureUrlCreatesSecureSpdySession) {}

TEST_P(SpdyNetworkTransactionTest, RequestHeadersCallback) {}

#if BUILDFLAG(ENABLE_WEBSOCKETS)

TEST_P(SpdyNetworkTransactionTest, WebSocketOpensNewConnection) {}

// Make sure that a WebSocket job doesn't pick up a newly created SpdySession
// that doesn't support WebSockets through
// HttpStreamFactory::Job::OnSpdySessionAvailable().
TEST_P(SpdyNetworkTransactionTest,
       WebSocketDoesUseNewH2SessionWithoutWebSocketSupport) {}

TEST_P(SpdyNetworkTransactionTest, WebSocketOverHTTP2) {}

// Make sure that a WebSocket job doesn't pick up a newly created SpdySession
// that supports WebSockets through an HTTPS proxy when an H2 server doesn't
// support websockets. See https://crbug.com/1010491.
TEST_P(SpdyNetworkTransactionTest,
       WebSocketDoesNotUseNewH2SessionWithoutWebSocketSupportOverHttpsProxy) {}

// Same as above, but checks that a WebSocket connection avoids creating a new
// socket if it detects an H2 session when host resolution completes, and
// requests also use different hostnames.
TEST_P(SpdyNetworkTransactionTest,
       WebSocketOverHTTP2DetectsNewSessionWithAliasing) {}

// Same as above, but the SpdySession is closed just before use, so the
// WebSocket is sent over a new HTTP/1.x connection instead.
TEST_P(SpdyNetworkTransactionTest,
       WebSocketOverDetectsNewSessionWithAliasingButClosedBeforeUse) {}

TEST_P(SpdyNetworkTransactionTest, WebSocketNegotiatesHttp2) {}

TEST_P(SpdyNetworkTransactionTest, WebSocketHttp11Required) {}

// When using an HTTP(S) proxy, plaintext WebSockets use CONNECT tunnels. This
// should work for HTTP/2 proxies.
TEST_P(SpdyNetworkTransactionTest, PlaintextWebSocketOverHttp2Proxy) {}

TEST_P(SpdyNetworkTransactionTest, SecureWebSocketOverH2OverH2Proxy) {}

TEST_P(SpdyNetworkTransactionTest, SecureWebSocketOverHttp2Proxy) {}

// Regression test for https://crbug.com/828865.
TEST_P(SpdyNetworkTransactionTest,
       SecureWebSocketOverHttp2ProxyNegotiatesHttp2) {}

#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

TEST_P(SpdyNetworkTransactionTest, ZeroRTTDoesntConfirm) {}

// Run multiple concurrent streams that don't require handshake confirmation.
TEST_P(SpdyNetworkTransactionTest, ZeroRTTNoConfirmMultipleStreams) {}

// Run multiple concurrent streams that require handshake confirmation.
TEST_P(SpdyNetworkTransactionTest, ZeroRTTConfirmMultipleStreams) {}

// Run multiple concurrent streams, the first require a confirmation and the
// second not requiring confirmation.
TEST_P(SpdyNetworkTransactionTest, ZeroRTTConfirmNoConfirmStreams) {}

// Run multiple concurrent streams, the first not requiring confirmation and the
// second requiring confirmation.
TEST_P(SpdyNetworkTransactionTest, ZeroRTTNoConfirmConfirmStreams) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorSync) {}

TEST_P(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {}

TEST_P(SpdyNetworkTransactionTest, GreaseSettings) {}

// If |http2_end_stream_with_data_frame| is false, then the HEADERS frame of a
// GET request will close the stream using the END_STREAM flag.  Test that
// |greased_http2_frame| is ignored and no reserved frames are sent on a closed
// stream.
TEST_P(SpdyNetworkTransactionTest,
       DoNotGreaseFrameTypeWithGetRequestIfHeadersFrameClosesStream) {}

// Test that if |http2_end_stream_with_data_frame| and |greased_http2_frame| are
// both set, then the HEADERS frame does not have the END_STREAM flag set, it is
// followed by a greased frame, and then by an empty DATA frame with END_STREAM
// set.
TEST_P(SpdyNetworkTransactionTest, GreaseFrameTypeWithGetRequest) {}

// Test sending a greased frame before DATA frame that closes the stream when
// |http2_end_stream_with_data_frame| is false.
TEST_P(SpdyNetworkTransactionTest,
       GreaseFrameTypeWithPostRequestWhenHeadersFrameClosesStream) {}

// Test sending a greased frame before DATA frame that closes the stream.
// |http2_end_stream_with_data_frame| is true but should make no difference,
// because the stream is already closed by a DATA frame.
TEST_P(SpdyNetworkTransactionTest,
       GreaseFrameTypeWithPostRequestWhenEmptyDataFrameClosesStream) {}

// According to https://httpwg.org/specs/rfc7540.html#CONNECT, "frame types
// other than DATA or stream management frames (RST_STREAM, WINDOW_UPDATE, and
// PRIORITY) MUST NOT be sent on a connected stream".
// Also test that |http2_end_stream_with_data_frame| has no effect on proxy
// streams.
TEST_P(SpdyNetworkTransactionTest, DoNotGreaseFrameTypeWithConnect) {}

// Regression test for https://crbug.com/1081955.
// Greasing frame types is enabled, the outgoing HEADERS frame is followed by a
// frame of reserved type, then an empty DATA frame to close the stream.
// Response arrives before reserved frame and DATA frame can be sent.
// SpdyHttpStream::OnDataSent() must not crash.
TEST_P(SpdyNetworkTransactionTest, OnDataSentDoesNotCrashWithGreasedFrameType) {}

TEST_P(SpdyNetworkTransactionTest, NotAllowHTTP1NotBlockH2Post) {}

TEST_P(SpdyNetworkTransactionTest, AlpsFramingError) {}

}  // namespace net