chromium/net/spdy/spdy_session_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 "net/spdy/spdy_session.h"

#include <algorithm>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>

#include "base/base64.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.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/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "net/base/features.h"
#include "net/base/hex_utils.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/privacy_mode.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_delegate.h"
#include "net/base/proxy_server.h"
#include "net/base/request_priority.h"
#include "net/base/schemeful_site.h"
#include "net/base/session_usage.h"
#include "net/base/test_completion_callback.h"
#include "net/base/test_data_stream.h"
#include "net/cert/ct_policy_status.h"
#include "net/dns/public/host_resolver_results.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/http_request_info.h"
#include "net/http/transport_security_state_test_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/socket/client_socket_pool.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/socket_tag.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/transport_connect_job.h"
#include "net/spdy/alps_decoder.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_session_test_util.h"
#include "net/spdy/spdy_stream.h"
#include "net/spdy/spdy_stream_test_util.h"
#include "net/spdy/spdy_test_util_common.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/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"

IsError;
IsOk;
_;

namespace net {

namespace {

const char kBodyData[] =;
const size_t kBodyDataSize =;
const std::string_view kBodyDataStringPiece(kBodyData, kBodyDataSize);

static base::TimeDelta g_time_delta;
static base::TimeTicks g_time_now;

base::TimeTicks TheNearFuture() {}

base::TimeTicks SlowReads() {}

base::TimeTicks InstantaneousReads() {}

class MockRequireCTDelegate : public TransportSecurityState::RequireCTDelegate {};

// SpdySessionRequest::Delegate implementation that does nothing. The test it's
// used in need to create a session request to trigger the creation of a session
// alias, but doesn't care about when or if OnSpdySessionAvailable() is invoked.
class SpdySessionRequestDelegate
    : public SpdySessionPool::SpdySessionRequest::Delegate {};

}  // namespace

class SpdySessionTest : public PlatformTest, public WithTaskEnvironment {};

class SpdySessionTestWithMockTime : public SpdySessionTest {};

// Try to create a SPDY session that will fail during
// initialization. Nothing should blow up.
TEST_F(SpdySessionTest, InitialReadError) {}

namespace {

// A helper class that vends a callback that, when fired, destroys a
// given SpdyStreamRequest.
class StreamRequestDestroyingCallback : public TestCompletionCallbackBase {};

}  // namespace

// Request kInitialMaxConcurrentStreams streams.  Request two more
// streams, but have the callback for one destroy the second stream
// request. Close the session. Nothing should blow up. This is a
// regression test for http://crbug.com/250841 .
TEST_F(SpdySessionTest, PendingStreamCancellingAnother) {}

// A session receiving a GOAWAY frame with no active streams should close.
TEST_F(SpdySessionTest, GoAwayWithNoActiveStreams) {}

// A session receiving a GOAWAY frame immediately with no active
// streams should then close.
TEST_F(SpdySessionTest, GoAwayImmediatelyWithNoActiveStreams) {}

// A session receiving a GOAWAY frame with active streams should close
// when the last active stream is closed.
TEST_F(SpdySessionTest, GoAwayWithActiveStreams) {}

// Regression test for https://crbug.com/547130.
TEST_F(SpdySessionTest, GoAwayWithActiveAndCreatedStream) {}

// Have a session receive two GOAWAY frames, with the last one causing
// the last active stream to be closed. The session should then be
// closed after the second GOAWAY frame.
TEST_F(SpdySessionTest, GoAwayTwice) {}

// Have a session with active streams receive a GOAWAY frame and then
// close it. It should handle the close properly (i.e., not try to
// make itself unavailable in its pool twice).
TEST_F(SpdySessionTest, GoAwayWithActiveStreamsThenClose) {}

// Process a joint read buffer which causes the session to begin draining, and
// then processes a GOAWAY. The session should gracefully drain. Regression test
// for crbug.com/379469
TEST_F(SpdySessionTest, GoAwayWhileDraining) {}

// Regression test for https://crbug.com/1510327.
// Have a session with active streams receive a GOAWAY frame. Ensure that
// the session is drained after all streams receive DATA frames of which
// END_STREAM flag is set, even when the peer doesn't close the connection.
TEST_F(SpdySessionTest, GoAwayWithActiveStreamsThenEndStreams) {}

// Try to create a stream after receiving a GOAWAY frame. It should
// fail.
TEST_F(SpdySessionTest, CreateStreamAfterGoAway) {}

// Receiving a HEADERS frame after a GOAWAY frame should result in
// the stream being refused.
TEST_F(SpdySessionTest, HeadersAfterGoAway) {}

// A session observing a network change with active streams should close
// when the last active stream is closed.
TEST_F(SpdySessionTest, NetworkChangeWithActiveStreams) {}

TEST_F(SpdySessionTestWithMockTime, ClientPing) {}

TEST_F(SpdySessionTest, ServerPing) {}

// Cause a ping to be sent out while producing a write. The write loop
// should handle this properly, i.e. another DoWriteLoop task should
// not be posted. This is a regression test for
// http://crbug.com/261043 .
TEST_F(SpdySessionTest, PingAndWriteLoop) {}

TEST_F(SpdySessionTestWithMockTime, DetectBrokenConnectionPing) {}

TEST_F(SpdySessionTest, StreamIdSpaceExhausted) {}

// Regression test for https://crbug.com/481009.
TEST_F(SpdySessionTest, MaxConcurrentStreamsZero) {}

// Verifies that an unstalled pending stream creation racing with a new stream
// creation doesn't violate the maximum stream concurrency. Regression test for
// crbug.com/373858.
TEST_F(SpdySessionTest, UnstallRacesWithStreamCreation) {}

TEST_F(SpdySessionTestWithMockTime, FailedPing) {}

// Regression test for https://crbug.com/784975.
TEST_F(SpdySessionTestWithMockTime, NoPingSentWhenCheckPingPending) {}

// Request kInitialMaxConcurrentStreams + 1 streams.  Receive a
// settings frame increasing the max concurrent streams by 1.  Make
// sure nothing blows up. This is a regression test for
// http://crbug.com/57331 .
TEST_F(SpdySessionTest, OnSettings) {}

// Create one more stream than maximum number of concurrent streams,
// so that one of them is pending.  Cancel one stream, which should trigger the
// creation of the pending stream.  Then cancel that one immediately as well,
// and make sure this does not lead to a crash.
// This is a regression test for https://crbug.com/63532.
TEST_F(SpdySessionTest, CancelPendingCreateStream) {}

TEST_F(SpdySessionTest, ChangeStreamRequestPriority) {}

// Attempts to extract a NetLogSource from a set of event parameters.  Returns
// true and writes the result to |source| on success.  Returns false and
// makes |source| an invalid source on failure.
bool NetLogSourceFromEventParameters(const base::Value::Dict* event_params,
                                     NetLogSource* source) {}

TEST_F(SpdySessionTest, Initialize) {}

TEST_F(SpdySessionTest, NetLogOnSessionGoaway) {}

TEST_F(SpdySessionTest, NetLogOnSessionEOF) {}

TEST_F(SpdySessionTest, HeadersCompressionHistograms) {}

// Queue up a low-priority HEADERS followed by a high-priority
// one. The high priority one should still send first and receive
// first.
TEST_F(SpdySessionTest, OutOfOrderHeaders) {}

TEST_F(SpdySessionTest, CancelStream) {}

// Create two streams that are set to re-close themselves on close,
// and then close the session. Nothing should blow up. Also a
// regression test for http://crbug.com/139518 .
TEST_F(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) {}

// Create two streams that are set to close each other on close, and
// then close the session. Nothing should blow up.
TEST_F(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) {}

// Create two streams that are set to re-close themselves on close,
// activate them, and then close the session. Nothing should blow up.
TEST_F(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) {}

// Create two streams that are set to close each other on close,
// activate them, and then close the session. Nothing should blow up.
TEST_F(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) {}

// Delegate that closes a given session when the stream is closed.
class SessionClosingDelegate : public test::StreamDelegateDoNothing {};

// Close an activated stream that closes its session. Nothing should
// blow up. This is a regression test for https://crbug.com/263691.
TEST_F(SpdySessionTest, CloseActivatedStreamThatClosesSession) {}

TEST_F(SpdySessionTest, VerifyDomainAuthentication) {}

TEST_F(SpdySessionTest, CloseTwoStalledCreateStream) {}

TEST_F(SpdySessionTest, CancelTwoStalledCreateStream) {}

// Test that SpdySession::DoReadLoop reads data from the socket
// without yielding.  This test makes 32k - 1 bytes of data available
// on the socket for reading. It then verifies that it has read all
// the available data without yielding.
TEST_F(SpdySessionTest, ReadDataWithoutYielding) {}

// Test that SpdySession::DoReadLoop yields if more than
// |kYieldAfterDurationMilliseconds| has passed.  This test uses a mock time
// function that makes the response frame look very slow to read.
TEST_F(SpdySessionTest, TestYieldingSlowReads) {}

// Regression test for https://crbug.com/531570.
// Test the case where DoRead() takes long but returns synchronously.
TEST_F(SpdySessionTest, TestYieldingSlowSynchronousReads) {}

// Test that SpdySession::DoReadLoop yields while reading the
// data. This test makes 32k + 1 bytes of data available on the socket
// for reading. It then verifies that DoRead has yielded even though
// there is data available for it to read (i.e, socket()->Read didn't
// return ERR_IO_PENDING during socket reads).
TEST_F(SpdySessionTest, TestYieldingDuringReadData) {}

// Test that SpdySession::DoReadLoop() tests interactions of yielding
// + async, by doing the following MockReads.
//
// MockRead of SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K
// ASYNC 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K.
//
// The above reads 26K synchronously. Since that is less that 32K, we
// will attempt to read again. However, that DoRead() will return
// ERR_IO_PENDING (because of async read), so DoReadLoop() will
// yield. When we come back, DoRead() will read the results from the
// async read, and rest of the data synchronously.
TEST_F(SpdySessionTest, TestYieldingDuringAsyncReadData) {}

// Send a GoAway frame when SpdySession is in DoReadLoop. Make sure
// nothing blows up.
TEST_F(SpdySessionTest, GoAwayWhileInDoReadLoop) {}

// Within this framework, a SpdySession should be initialized with
// flow control disabled for protocol version 2, with flow control
// enabled only for streams for protocol version 3, and with flow
// control enabled for streams and sessions for higher versions.
TEST_F(SpdySessionTest, ProtocolNegotiation) {}

// Tests the case of a non-SPDY request closing an idle SPDY session when no
// pointers to the idle session are currently held.
TEST_F(SpdySessionTest, CloseOneIdleConnection) {}

// Tests the case of a non-SPDY request closing an idle SPDY session when no
// pointers to the idle session are currently held, in the case the SPDY session
// has an alias.
TEST_F(SpdySessionTest, CloseOneIdleConnectionWithAlias) {}

// Tests that when a SPDY session becomes idle, it closes itself if there is
// a lower layer pool stalled on the per-pool socket limit.
TEST_F(SpdySessionTest, CloseSessionOnIdleWhenPoolStalled) {}

// Verify that SpdySessionKey and therefore SpdySession is different when
// privacy mode is enabled or disabled.
TEST_F(SpdySessionTest, SpdySessionKeyPrivacyMode) {}

// Delegate that creates another stream when its stream is closed.
class StreamCreatingDelegate : public test::StreamDelegateDoNothing {};

// Create another stream in response to a stream being reset. Nothing
// should blow up. This is a regression test for
// http://crbug.com/263690 .
TEST_F(SpdySessionTest, CreateStreamOnStreamReset) {}

TEST_F(SpdySessionTest, UpdateStreamsSendWindowSize) {}

// SpdySession::{Increase,Decrease}RecvWindowSize should properly
// adjust the session receive window size. In addition,
// SpdySession::IncreaseRecvWindowSize should trigger
// sending a WINDOW_UPDATE frame for a large enough delta.
TEST_F(SpdySessionTest, AdjustRecvWindowSize) {}

// SpdySession::{Increase,Decrease}RecvWindowSize should properly
// adjust the session receive window size. In addition,
// SpdySession::IncreaseRecvWindowSize should trigger
// sending a WINDOW_UPDATE frame for a small delta after
// kDefaultTimeToBufferSmallWindowUpdates time has passed.
TEST_F(SpdySessionTestWithMockTime, FlowControlSlowReads) {}

// SpdySession::{Increase,Decrease}SendWindowSize should properly
// adjust the session send window size when the "enable_spdy_31" flag
// is set.
TEST_F(SpdySessionTest, AdjustSendWindowSize) {}

// Incoming data for an inactive stream should not cause the session
// receive window size to decrease, but it should cause the unacked
// bytes to increase.
TEST_F(SpdySessionTest, SessionFlowControlInactiveStream) {}

// The frame header is not included in flow control, but frame payload
// (including optional pad length and padding) is.
TEST_F(SpdySessionTest, SessionFlowControlPadding) {}

// Peer sends more data than stream level receiving flow control window.
TEST_F(SpdySessionTest, StreamFlowControlTooMuchData) {}

// Regression test for a bug that was caused by including unsent WINDOW_UPDATE
// deltas in the receiving window size when checking incoming frames for flow
// control errors at session level.
TEST_F(SpdySessionTest, SessionFlowControlTooMuchDataTwoDataFrames) {}

// Regression test for a bug that was caused by including unsent WINDOW_UPDATE
// deltas in the receiving window size when checking incoming data frames for
// flow control errors at stream level.
TEST_F(SpdySessionTest, StreamFlowControlTooMuchDataTwoDataFrames) {}

// A delegate that drops any received data.
class DropReceivedDataDelegate : public test::StreamDelegateSendImmediate {};

// Send data back and forth but use a delegate that drops its received
// data. The receive window should still increase to its original
// value, i.e. we shouldn't "leak" receive window bytes.
TEST_F(SpdySessionTest, SessionFlowControlNoReceiveLeaks) {}

// Send data back and forth but close the stream before its data frame
// can be written to the socket. The send window should then increase
// to its original value, i.e. we shouldn't "leak" send window bytes.
TEST_F(SpdySessionTest, SessionFlowControlNoSendLeaks) {}

// Send data back and forth; the send and receive windows should
// change appropriately.
TEST_F(SpdySessionTest, SessionFlowControlEndToEnd) {}

// Given a stall function and an unstall function, runs a test to make
// sure that a stream resumes after unstall.
void SpdySessionTest::RunResumeAfterUnstallTest(
    base::OnceCallback<void(SpdyStream*)> stall_function,
    base::OnceCallback<void(SpdyStream*, int32_t)> unstall_function) {}

// Run the resume-after-unstall test with all possible stall and
// unstall sequences.

TEST_F(SpdySessionTest, ResumeAfterUnstallSession) {}

// Equivalent to
// SpdyStreamTest.ResumeAfterSendWindowSizeIncrease.
TEST_F(SpdySessionTest, ResumeAfterUnstallStream) {}

TEST_F(SpdySessionTest, StallSessionStreamResumeAfterUnstallSessionStream) {}

TEST_F(SpdySessionTest, StallStreamSessionResumeAfterUnstallSessionStream) {}

TEST_F(SpdySessionTest, StallStreamSessionResumeAfterUnstallStreamSession) {}

TEST_F(SpdySessionTest, StallSessionStreamResumeAfterUnstallStreamSession) {}

// Cause a stall by reducing the flow control send window to 0. The
// streams should resume in priority order when that window is then
// increased.
TEST_F(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) {}

// An upload stream is stalled when the session gets unstalled, then the session
// is stalled again when the stream gets unstalled.  The stream should not fail.
// Regression test for https://crbug.com/761919.
TEST_F(SpdySessionTest, ResumeSessionWithStalledStream) {}

class StreamBrokenConnectionDetectionCheckDelegate
    : public test::StreamDelegateDoNothing {};

TEST_F(SpdySessionTest, BrokenConnectionDetectionEOF) {}

TEST_F(SpdySessionTest, BrokenConnectionDetectionCloseSession) {}

TEST_F(SpdySessionTest, BrokenConnectionDetectionCloseStream) {}

TEST_F(SpdySessionTest, BrokenConnectionDetectionCancelStream) {}

// When multiple streams request broken connection detection, only the last one
// to complete should disable the connection status check.
TEST_F(SpdySessionTest, BrokenConnectionDetectionMultipleRequests) {}

// Delegate that closes a given stream after sending its body.
class StreamClosingDelegate : public test::StreamDelegateWithBody {};

// Cause a stall by reducing the flow control send window to
// 0. Unstalling the session should properly handle deleted streams.
TEST_F(SpdySessionTest, SendWindowSizeIncreaseWithDeletedStreams) {}

// Cause a stall by reducing the flow control send window to
// 0. Unstalling the session should properly handle the session itself
// being closed.
TEST_F(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) {}

TEST_F(SpdySessionTest, GoAwayOnSessionFlowControlError) {}

TEST_F(SpdySessionTest, RejectInvalidUnknownFrames) {}

TEST_F(SpdySessionTest, EnableWebSocket) {}

TEST_F(SpdySessionTest, DisableWebSocketDoesNothing) {}

TEST_F(SpdySessionTest, EnableWebSocketThenDisableIsProtocolError) {}

TEST_F(SpdySessionTest, GreaseFrameTypeAfterSettings) {}

enum ReadIfReadySupport {};

class SpdySessionReadIfReadyTest
    : public SpdySessionTest,
      public testing::WithParamInterface<ReadIfReadySupport> {};

INSTANTIATE_TEST_SUITE_P();

// Tests basic functionality of ReadIfReady() when it is enabled or disabled.
TEST_P(SpdySessionReadIfReadyTest, ReadIfReady) {}

class SendInitialSettingsOnNewSpdySessionTest : public SpdySessionTest {};

// Setting values when Params::http2_settings is empty.  Note that
// spdy::SETTINGS_INITIAL_WINDOW_SIZE is sent in production, because it is set
// to a non-default value, but it is not sent in tests, because the protocol
// default value is used in tests.
TEST_F(SendInitialSettingsOnNewSpdySessionTest, Empty) {}

// When a setting is set to the protocol default value,
// no corresponding value is sent on the wire.
TEST_F(SendInitialSettingsOnNewSpdySessionTest, ProtocolDefault) {}

// Values set in Params::http2_settings overwrite Chromium's default values.
TEST_F(SendInitialSettingsOnNewSpdySessionTest, OverwriteValues) {}

// Unknown parameters should still be sent to the server.
TEST_F(SendInitialSettingsOnNewSpdySessionTest, UnknownSettings) {}

class AltSvcFrameTest : public SpdySessionTest {};

TEST_F(AltSvcFrameTest, ProcessAltSvcFrame) {}

// Regression test for https://crbug.com/736063.
TEST_F(AltSvcFrameTest, IgnoreQuicAltSvcWithUnsupportedVersion) {}

TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameForOriginNotCoveredByCert) {}

// An ALTSVC frame on stream 0 with empty origin MUST be ignored.
// (RFC 7838 Section 4)
TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameWithEmptyOriginOnStreamZero) {}

// An ALTSVC frame on a stream other than stream 0 with non-empty origin MUST be
// ignored.  (RFC 7838 Section 4)
TEST_F(AltSvcFrameTest,
       DoNotProcessAltSvcFrameWithNonEmptyOriginOnNonZeroStream) {}

TEST_F(AltSvcFrameTest, ProcessAltSvcFrameOnActiveStream) {}

TEST_F(AltSvcFrameTest,
       ProcessAltSvcFrameOnActiveStreamWithNetworkAnonymizationKey) {}

TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameOnStreamWithInsecureOrigin) {}

TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameOnNonExistentStream) {}

// Regression test for https://crbug.com/810404.
TEST_F(AltSvcFrameTest, InvalidOrigin) {}

TEST(MapFramerErrorToProtocolError, MapsValues) {}

TEST(MapFramerErrorToNetError, MapsValue) {}

TEST(MapRstStreamStatusToProtocolError, MapsValues) {}

TEST(MapNetErrorToGoAwayStatus, MapsValue) {}

namespace {

class TestSSLConfigService : public SSLConfigService {};

}  // namespace

TEST(CanPoolTest, CanPool) {}

TEST(CanPoolTest, CanNotPoolWithCertErrors) {}

TEST(CanPoolTest, CanNotPoolWithClientCerts) {}

TEST(CanPoolTest, CanNotPoolWithBadPins) {}

TEST(CanPoolTest, CanNotPoolWithBadCTWhenCTRequired) {}

TEST(CanPoolTest, CanPoolWithBadCTWhenCTNotRequired) {}

TEST(CanPoolTest, CanPoolWithGoodCTWhenCTRequired) {}

TEST(CanPoolTest, CanPoolWithAcceptablePins) {}

TEST(CanPoolTest, CanPoolWithClientCertsAndPolicy) {}

// Regression test for https://crbug.com/1115492.
TEST_F(SpdySessionTest, UpdateHeaderTableSize) {}

TEST_F(SpdySessionTest, PriorityUpdateDisabled) {}

TEST_F(SpdySessionTest, PriorityUpdateEnabledHttp2PrioritiesDeprecated) {}

TEST_F(SpdySessionTest, PriorityUpdateEnabledHttp2PrioritiesNotDeprecated) {}

TEST_F(SpdySessionTest, SettingsDeprecateHttp2PrioritiesValueMustNotChange) {}

TEST_F(SpdySessionTest, AlpsEmpty) {}

TEST_F(SpdySessionTest, AlpsSettings) {}

TEST_F(SpdySessionTest, AlpsAcceptCh) {}

TEST_F(SpdySessionTest, AlpsAcceptChInvalidOrigin) {}

// Test that ConfirmHandshake() correctly handles the client aborting the
// connection. See https://crbug.com/1211639.
TEST_F(SpdySessionTest, ConfirmHandshakeAfterClose) {}

}  // namespace net