chromium/net/websockets/websocket_stream_test.cc

// Copyright 2013 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/websockets/websocket_stream.h"

#include <algorithm>
#include <iterator>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_samples.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/timer/mock_timer.h"
#include "base/timer/timer.h"
#include "net/base/auth.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/base/url_util.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/cookies/site_for_cookies.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/ssl/ssl_info.h"
#include "net/storage_access_api/status.h"
#include "net/test/cert_test_util.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.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.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "net/websockets/websocket_frame.h"
#include "net/websockets/websocket_handshake_request_info.h"
#include "net/websockets/websocket_handshake_response_info.h"
#include "net/websockets/websocket_handshake_stream_base.h"
#include "net/websockets/websocket_stream_create_test_base.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"

IsError;
IsOk;
TestWithParam;
Values;

namespace net {
namespace {

enum HandshakeStreamType {};

// Simple builder for a SequencedSocketData object to save repetitive code.
// It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
// be used in tests where the connect fails. In practice, those tests never have
// any read/write data and so can't benefit from it anyway.  The arrays are not
// copied. It is up to the caller to ensure they stay in scope until the test
// ends.
std::unique_ptr<SequencedSocketData> BuildSocketData(
    base::span<MockRead> reads,
    base::span<MockWrite> writes) {}

// Builder for a SequencedSocketData that expects nothing. This does not
// set the connect data, so the calling code must do that explicitly.
std::unique_ptr<SequencedSocketData> BuildNullSocketData() {}

class MockWeakTimer : public base::MockOneShotTimer {};

constexpr char kOrigin[] =;

static url::Origin Origin() {}

static net::SiteForCookies SiteForCookies() {}

static IsolationInfo CreateIsolationInfo() {}

class WebSocketStreamCreateTest
    : public TestWithParam<std::tuple<HandshakeStreamType, bool>>,
      public WebSocketStreamCreateTestBase {};

INSTANTIATE_TEST_SUITE_P();

WebSocketMultiProtocolStreamCreateTest;

INSTANTIATE_TEST_SUITE_P();

// There are enough tests of the Sec-WebSocket-Extensions header that they
// deserve their own test fixture.
class WebSocketStreamCreateExtensionTest
    : public WebSocketMultiProtocolStreamCreateTest {};

INSTANTIATE_TEST_SUITE_P();

// Common code to construct expectations for authentication tests that receive
// the auth challenge on one connection and then create a second connection to
// send the authenticated request on.
class CommonAuthTestHelper {};

// Data and methods for BasicAuth tests.
class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {};

INSTANTIATE_TEST_SUITE_P();

class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {};

INSTANTIATE_TEST_SUITE_P();

const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =;

// These negotiation values are borrowed from
// http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
// you are bored. Only the weakest (no qop) variants of Digest authentication
// can be tested by this method, because the others involve random input.
const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =;

const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =;

// Confirm that the basic case works as expected.
TEST_P(WebSocketMultiProtocolStreamCreateTest, SimpleSuccess) {}

TEST_P(WebSocketStreamCreateTest, HandshakeInfo) {}

// Confirms that request headers are overriden/added after handshake
TEST_P(WebSocketStreamCreateTest, HandshakeOverrideHeaders) {}

TEST_P(WebSocketStreamCreateTest, OmitsHasStorageAccess) {}

TEST_P(WebSocketStreamCreateTest, PlumbsHasStorageAccess) {}

// Confirm that the stream isn't established until the message loop runs.
TEST_P(WebSocketStreamCreateTest, NeedsToRunLoop) {}

// Check the path is used.
TEST_P(WebSocketMultiProtocolStreamCreateTest, PathIsUsed) {}

// Check that sub-protocols are sent and parsed.
TEST_P(WebSocketMultiProtocolStreamCreateTest, SubProtocolIsUsed) {}

// Unsolicited sub-protocols are rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest, UnsolicitedSubProtocol) {}

// Missing sub-protocol response is rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest, UnacceptedSubProtocol) {}

// Only one sub-protocol can be accepted.
TEST_P(WebSocketMultiProtocolStreamCreateTest, MultipleSubProtocolsInResponse) {}

// Unmatched sub-protocol should be rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest, UnmatchedSubProtocolInResponse) {}

// permessage-deflate extension basic success case.
TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {}

// permessage-deflate extensions success with all parameters.
TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {}

// Verify that incoming messages are actually decompressed with
// permessage-deflate enabled.
TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {}

// Unknown extension in the response is rejected
TEST_P(WebSocketStreamCreateExtensionTest, UnknownExtension) {}

// Malformed extensions are rejected (this file does not cover all possible
// parse failures, as the parser is covered thoroughly by its own unit tests).
TEST_P(WebSocketStreamCreateExtensionTest, MalformedExtension) {}

// The permessage-deflate extension may only be specified once.
TEST_P(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {}

// client_max_window_bits must have an argument
TEST_P(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {}

// Other cases for permessage-deflate parameters are tested in
// websocket_deflate_parameters_test.cc.

// TODO(ricea): Check that WebSocketDeflateStream is initialised with the
// arguments from the server. This is difficult because the data written to the
// socket is randomly masked.

// Additional Sec-WebSocket-Accept headers should be rejected.
TEST_P(WebSocketStreamCreateTest, DoubleAccept) {}

// When upgrading an HTTP/1 connection, response code 200 is invalid and must be
// rejected.  Response code 101 means success.  On the other hand, when
// requesting a WebSocket stream over HTTP/2, response code 101 is invalid and
// must be rejected.  Response code 200 means success.
TEST_P(WebSocketMultiProtocolStreamCreateTest, InvalidStatusCode) {}

// Redirects are not followed (according to the WHATWG WebSocket API, which
// overrides RFC6455 for browser applications).
TEST_P(WebSocketMultiProtocolStreamCreateTest, RedirectsRejected) {}

// Malformed responses should be rejected. HttpStreamParser will accept just
// about any garbage in the middle of the headers. To make it give up, the junk
// has to be at the start of the response. Even then, it just gets treated as an
// HTTP/0.9 response.
TEST_P(WebSocketStreamCreateTest, MalformedResponse) {}

// Upgrade header must be present.
TEST_P(WebSocketStreamCreateTest, MissingUpgradeHeader) {}

// There must only be one upgrade header.
TEST_P(WebSocketStreamCreateTest, DoubleUpgradeHeader) {}

// There must only be one correct upgrade header.
TEST_P(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {}

// Connection header must be present.
TEST_P(WebSocketStreamCreateTest, MissingConnectionHeader) {}

// Connection header must contain "Upgrade".
TEST_P(WebSocketStreamCreateTest, IncorrectConnectionHeader) {}

// Connection header is permitted to contain other tokens.
TEST_P(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {}

// Sec-WebSocket-Accept header must be present.
TEST_P(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {}

// Sec-WebSocket-Accept header must match the key that was sent.
TEST_P(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {}

// Cancellation works.
TEST_P(WebSocketStreamCreateTest, Cancellation) {}

// Connect failure must look just like negotiation failure.
TEST_P(WebSocketStreamCreateTest, ConnectionFailure) {}

// Connect timeout must look just like any other failure.
TEST_P(WebSocketStreamCreateTest, ConnectionTimeout) {}

// The server doesn't respond to the opening handshake.
TEST_P(WebSocketStreamCreateTest, HandshakeTimeout) {}

// When the connection establishes the timer should be stopped.
TEST_P(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {}

// When the connection fails the timer should be stopped.
TEST_P(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {}

// Cancellation during connect works.
TEST_P(WebSocketStreamCreateTest, CancellationDuringConnect) {}

// Cancellation during write of the request headers works.
TEST_P(WebSocketStreamCreateTest, CancellationDuringWrite) {}

// Cancellation during read of the response headers works.
TEST_P(WebSocketStreamCreateTest, CancellationDuringRead) {}

// Over-size response headers (> 256KB) should not cause a crash.  This is a
// regression test for crbug.com/339456. It is based on the layout test
// "cookie-flood.html".
TEST_P(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {}

// If the remote host closes the connection without sending headers, we should
// log the console message "Connection closed before receiving a handshake
// response".
TEST_P(WebSocketStreamCreateTest, NoResponse) {}

TEST_P(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {}

TEST_P(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {}

// If the server requests authorisation, but we have no credentials, the
// connection should fail cleanly.
TEST_P(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {}

TEST_P(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {}

TEST_P(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {}

TEST_P(WebSocketStreamCreateBasicAuthTest, SuccessfulConnectionReuse) {}

TEST_P(WebSocketStreamCreateBasicAuthTest, OnAuthRequiredCancelAuth) {}

TEST_P(WebSocketStreamCreateBasicAuthTest, OnAuthRequiredSetAuth) {}

// Digest auth has the same connection semantics as Basic auth, so we can
// generally assume that whatever works for Basic auth will also work for
// Digest. There's just one test here, to confirm that it works at all.
TEST_P(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {}

TEST_P(WebSocketMultiProtocolStreamCreateTest, Incomplete) {}

TEST_P(WebSocketMultiProtocolStreamCreateTest, Http2StreamReset) {}

TEST_P(WebSocketStreamCreateTest, HandleErrConnectionClosed) {}

TEST_P(WebSocketStreamCreateTest, HandleErrTunnelConnectionFailed) {}

TEST_P(WebSocketStreamCreateTest, CancelSSLRequestAfterDelete) {}

TEST_P(WebSocketStreamCreateTest, ContinueSSLRequestAfterDelete) {}

TEST_P(WebSocketStreamCreateTest, HandleConnectionCloseInFirstSegment) {}

}  // namespace
}  // namespace net