// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/websockets/websocket_channel.h" #include #include #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/location.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_piece.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request_context.h" #include "net/websockets/websocket_errors.h" #include "net/websockets/websocket_event_interface.h" #include "net/websockets/websocket_handshake_request_info.h" #include "net/websockets/websocket_handshake_response_info.h" #include "net/websockets/websocket_mux.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" // Hacky macros to construct the body of a Close message from a code and a // string, while ensuring the result is a compile-time constant string. // Use like CLOSE_DATA(NORMAL_CLOSURE, "Explanation String") #define CLOSE_DATA(code, string) WEBSOCKET_CLOSE_CODE_AS_STRING_##code string #define WEBSOCKET_CLOSE_CODE_AS_STRING_NORMAL_CLOSURE "\x03\xe8" #define WEBSOCKET_CLOSE_CODE_AS_STRING_GOING_AWAY "\x03\xe9" #define WEBSOCKET_CLOSE_CODE_AS_STRING_PROTOCOL_ERROR "\x03\xea" #define WEBSOCKET_CLOSE_CODE_AS_STRING_ABNORMAL_CLOSURE "\x03\xee" #define WEBSOCKET_CLOSE_CODE_AS_STRING_SERVER_ERROR "\x03\xf3" namespace net { // Printing helpers to allow GoogleMock to print frames. These are explicitly // designed to look like the static initialisation format we use in these // tests. They have to live in the net namespace in order to be found by // GoogleMock; a nested anonymous namespace will not work. std::ostream& operator<<(std::ostream& os, const WebSocketFrameHeader& header) { return os << (header.final ? "FINAL_FRAME" : "NOT_FINAL_FRAME") << ", " << header.opcode << ", " << (header.masked ? "MASKED" : "NOT_MASKED"); } std::ostream& operator<<(std::ostream& os, const WebSocketFrame& frame) { os << "{" << frame.header << ", "; if (frame.data) { return os << "\"" << base::StringPiece(frame.data->data(), frame.header.payload_length) << "\"}"; } return os << "NULL}"; } std::ostream& operator<<(std::ostream& os, const ScopedVector& vector) { os << "{"; bool first = true; for (ScopedVector::const_iterator it = vector.begin(); it != vector.end(); ++it) { if (!first) { os << ",\n"; } else { first = false; } os << **it; } return os << "}"; } std::ostream& operator<<(std::ostream& os, const ScopedVector* vector) { return os << '&' << *vector; } namespace { using ::base::TimeDelta; using ::testing::AnyNumber; using ::testing::DefaultValue; using ::testing::InSequence; using ::testing::MockFunction; using ::testing::Return; using ::testing::SaveArg; using ::testing::StrictMock; using ::testing::_; // A selection of characters that have traditionally been mangled in some // environment or other, for testing 8-bit cleanliness. const char kBinaryBlob[] = {'\n', '\r', // BACKWARDS CRNL '\0', // nul '\x7F', // DEL '\x80', '\xFF', // NOT VALID UTF-8 '\x1A', // Control-Z, EOF on DOS '\x03', // Control-C '\x04', // EOT, special for Unix terms '\x1B', // ESC, often special '\b', // backspace '\'', // single-quote, special in PHP }; const size_t kBinaryBlobSize = arraysize(kBinaryBlob); // The amount of quota a new connection gets by default. // TODO(ricea): If kDefaultSendQuotaHighWaterMark changes, then this value will // need to be updated. const size_t kDefaultInitialQuota = 1 << 17; // The amount of bytes we need to send after the initial connection to trigger a // quota refresh. TODO(ricea): Change this if kDefaultSendQuotaHighWaterMark or // kDefaultSendQuotaLowWaterMark change. const size_t kDefaultQuotaRefreshTrigger = (1 << 16) + 1; // TestTimeouts::tiny_timeout() is 100ms! I could run halfway around the world // in that time! I would like my tests to run a bit quicker. const int kVeryTinyTimeoutMillis = 1; // Enough quota to pass any test. const int64 kPlentyOfQuota = INT_MAX; typedef WebSocketEventInterface::ChannelState ChannelState; const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; // This typedef mainly exists to avoid having to repeat the "NOLINT" incantation // all over the place. typedef StrictMock< MockFunction > Checkpoint; // NOLINT // This mock is for testing expectations about how the EventInterface is used. class MockWebSocketEventInterface : public WebSocketEventInterface { public: MockWebSocketEventInterface() {} MOCK_METHOD3(OnAddChannelResponse, ChannelState(bool, const std::string&, const std::string&)); // NOLINT MOCK_METHOD3(OnDataFrame, ChannelState(bool, WebSocketMessageType, const std::vector&)); // NOLINT MOCK_METHOD1(OnFlowControl, ChannelState(int64)); // NOLINT MOCK_METHOD0(OnClosingHandshake, ChannelState(void)); // NOLINT MOCK_METHOD1(OnFailChannel, ChannelState(const std::string&)); // NOLINT MOCK_METHOD3(OnDropChannel, ChannelState(bool, uint16, const std::string&)); // NOLINT // We can't use GMock with scoped_ptr. ChannelState OnStartOpeningHandshake( scoped_ptr) OVERRIDE { OnStartOpeningHandshakeCalled(); return CHANNEL_ALIVE; } ChannelState OnFinishOpeningHandshake( scoped_ptr) OVERRIDE { OnFinishOpeningHandshakeCalled(); return CHANNEL_ALIVE; } MOCK_METHOD0(OnStartOpeningHandshakeCalled, void()); // NOLINT MOCK_METHOD0(OnFinishOpeningHandshakeCalled, void()); // NOLINT }; // This fake EventInterface is for tests which need a WebSocketEventInterface // implementation but are not verifying how it is used. class FakeWebSocketEventInterface : public WebSocketEventInterface { virtual ChannelState OnAddChannelResponse( bool fail, const std::string& selected_protocol, const std::string& extensions) OVERRIDE { return fail ? CHANNEL_DELETED : CHANNEL_ALIVE; } virtual ChannelState OnDataFrame(bool fin, WebSocketMessageType type, const std::vector& data) OVERRIDE { return CHANNEL_ALIVE; } virtual ChannelState OnFlowControl(int64 quota) OVERRIDE { return CHANNEL_ALIVE; } virtual ChannelState OnClosingHandshake() OVERRIDE { return CHANNEL_ALIVE; } virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE { return CHANNEL_DELETED; } virtual ChannelState OnDropChannel(bool was_clean, uint16 code, const std::string& reason) OVERRIDE { return CHANNEL_DELETED; } virtual ChannelState OnStartOpeningHandshake( scoped_ptr request) OVERRIDE { return CHANNEL_ALIVE; } virtual ChannelState OnFinishOpeningHandshake( scoped_ptr response) OVERRIDE { return CHANNEL_ALIVE; } }; // This fake WebSocketStream is for tests that require a WebSocketStream but are // not testing the way it is used. It has minimal functionality to return // the |protocol| and |extensions| that it was constructed with. class FakeWebSocketStream : public WebSocketStream { public: // Constructs with empty protocol and extensions. FakeWebSocketStream() {} // Constructs with specified protocol and extensions. FakeWebSocketStream(const std::string& protocol, const std::string& extensions) : protocol_(protocol), extensions_(extensions) {} virtual int ReadFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { return ERR_IO_PENDING; } virtual int WriteFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { return ERR_IO_PENDING; } virtual void Close() OVERRIDE {} // Returns the string passed to the constructor. virtual std::string GetSubProtocol() const OVERRIDE { return protocol_; } // Returns the string passed to the constructor. virtual std::string GetExtensions() const OVERRIDE { return extensions_; } private: // The string to return from GetSubProtocol(). std::string protocol_; // The string to return from GetExtensions(). std::string extensions_; }; // To make the static initialisers easier to read, we use enums rather than // bools. enum IsFinal { NOT_FINAL_FRAME, FINAL_FRAME }; enum IsMasked { NOT_MASKED, MASKED }; // This is used to initialise a WebSocketFrame but is statically initialisable. struct InitFrame { IsFinal final; // Reserved fields omitted for now. Add them if you need them. WebSocketFrameHeader::OpCode opcode; IsMasked masked; // Will be used to create the IOBuffer member. Can be NULL for NULL data. Is a // nul-terminated string for ease-of-use. |header.payload_length| is // initialised from |strlen(data)|. This means it is not 8-bit clean, but this // is not an issue for test data. const char* const data; }; // For GoogleMock std::ostream& operator<<(std::ostream& os, const InitFrame& frame) { os << "{" << (frame.final == FINAL_FRAME ? "FINAL_FRAME" : "NOT_FINAL_FRAME") << ", " << frame.opcode << ", " << (frame.masked == MASKED ? "MASKED" : "NOT_MASKED") << ", "; if (frame.data) { return os << "\"" << frame.data << "\"}"; } return os << "NULL}"; } template std::ostream& operator<<(std::ostream& os, const InitFrame (&frames)[N]) { os << "{"; bool first = true; for (size_t i = 0; i < N; ++i) { if (!first) { os << ",\n"; } else { first = false; } os << frames[i]; } return os << "}"; } // Convert a const array of InitFrame structs to the format used at // runtime. Templated on the size of the array to save typing. template ScopedVector CreateFrameVector( const InitFrame (&source_frames)[N]) { ScopedVector result_frames; result_frames.reserve(N); for (size_t i = 0; i < N; ++i) { const InitFrame& source_frame = source_frames[i]; scoped_ptr result_frame( new WebSocketFrame(source_frame.opcode)); size_t frame_length = source_frame.data ? strlen(source_frame.data) : 0; WebSocketFrameHeader& result_header = result_frame->header; result_header.final = (source_frame.final == FINAL_FRAME); result_header.masked = (source_frame.masked == MASKED); result_header.payload_length = frame_length; if (source_frame.data) { result_frame->data = new IOBuffer(frame_length); memcpy(result_frame->data->data(), source_frame.data, frame_length); } result_frames.push_back(result_frame.release()); } return result_frames.Pass(); } // A GoogleMock action which can be used to respond to call to ReadFrames with // some frames. Use like ReadFrames(_, _).WillOnce(ReturnFrames(&frames)); // |frames| is an array of InitFrame. |frames| needs to be passed by pointer // because otherwise it will be treated as a pointer and the array size // information will be lost. ACTION_P(ReturnFrames, source_frames) { *arg0 = CreateFrameVector(*source_frames); return OK; } // The implementation of a GoogleMock matcher which can be used to compare a // ScopedVector* against an expectation defined as an array of // InitFrame objects. Although it is possible to compose built-in GoogleMock // matchers to check the contents of a WebSocketFrame, the results are so // unreadable that it is better to use this matcher. template class EqualsFramesMatcher : public ::testing::MatcherInterface*> { public: EqualsFramesMatcher(const InitFrame (*expect_frames)[N]) : expect_frames_(expect_frames) {} virtual bool MatchAndExplain(ScopedVector* actual_frames, ::testing::MatchResultListener* listener) const { if (actual_frames->size() != N) { *listener << "the vector size is " << actual_frames->size(); return false; } for (size_t i = 0; i < N; ++i) { const WebSocketFrame& actual_frame = *(*actual_frames)[i]; const InitFrame& expected_frame = (*expect_frames_)[i]; if (actual_frame.header.final != (expected_frame.final == FINAL_FRAME)) { *listener << "the frame is marked as " << (actual_frame.header.final ? "" : "not ") << "final"; return false; } if (actual_frame.header.opcode != expected_frame.opcode) { *listener << "the opcode is " << actual_frame.header.opcode; return false; } if (actual_frame.header.masked != (expected_frame.masked == MASKED)) { *listener << "the frame is " << (actual_frame.header.masked ? "masked" : "not masked"); return false; } const size_t expected_length = expected_frame.data ? strlen(expected_frame.data) : 0; if (actual_frame.header.payload_length != expected_length) { *listener << "the payload length is " << actual_frame.header.payload_length; return false; } if (expected_length != 0 && memcmp(actual_frame.data->data(), expected_frame.data, actual_frame.header.payload_length) != 0) { *listener << "the data content differs"; return false; } } return true; } virtual void DescribeTo(std::ostream* os) const { *os << "matches " << *expect_frames_; } virtual void DescribeNegationTo(std::ostream* os) const { *os << "does not match " << *expect_frames_; } private: const InitFrame (*expect_frames_)[N]; }; // The definition of EqualsFrames GoogleMock matcher. Unlike the ReturnFrames // action, this can take the array by reference. template ::testing::Matcher*> EqualsFrames( const InitFrame (&frames)[N]) { return ::testing::MakeMatcher(new EqualsFramesMatcher(&frames)); } // TestClosure works like TestCompletionCallback, but doesn't take an argument. class TestClosure { public: base::Closure closure() { return base::Bind(callback_.callback(), OK); } void WaitForResult() { callback_.WaitForResult(); } private: // Delegate to TestCompletionCallback for the implementation. TestCompletionCallback callback_; }; // A GoogleMock action to run a Closure. ACTION_P(InvokeClosure, closure) { closure.Run(); } // A GoogleMock action to run a Closure and return CHANNEL_DELETED. ACTION_P(InvokeClosureReturnDeleted, closure) { closure.Run(); return WebSocketEventInterface::CHANNEL_DELETED; } // A FakeWebSocketStream whose ReadFrames() function returns data. class ReadableFakeWebSocketStream : public FakeWebSocketStream { public: enum IsSync { SYNC, ASYNC }; // After constructing the object, call PrepareReadFrames() once for each // time you wish it to return from the test. ReadableFakeWebSocketStream() : index_(0), read_frames_pending_(false) {} // Check that all the prepared responses have been consumed. virtual ~ReadableFakeWebSocketStream() { CHECK(index_ >= responses_.size()); CHECK(!read_frames_pending_); } // Prepares a fake response. Fake responses will be returned from ReadFrames() // in the same order they were prepared with PrepareReadFrames() and // PrepareReadFramesError(). If |async| is ASYNC, then ReadFrames() will // return ERR_IO_PENDING and the callback will be scheduled to run on the // message loop. This requires the test case to run the message loop. If // |async| is SYNC, the response will be returned synchronously. |error| is // returned directly from ReadFrames() in the synchronous case, or passed to // the callback in the asynchronous case. |frames| will be converted to a // ScopedVector and copied to the pointer that was passed to // ReadFrames(). template void PrepareReadFrames(IsSync async, int error, const InitFrame (&frames)[N]) { responses_.push_back(new Response(async, error, CreateFrameVector(frames))); } // An alternate version of PrepareReadFrames for when we need to construct // the frames manually. void PrepareRawReadFrames(IsSync async, int error, ScopedVector frames) { responses_.push_back(new Response(async, error, frames.Pass())); } // Prepares a fake error response (ie. there is no data). void PrepareReadFramesError(IsSync async, int error) { responses_.push_back( new Response(async, error, ScopedVector())); } virtual int ReadFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { CHECK(!read_frames_pending_); if (index_ >= responses_.size()) return ERR_IO_PENDING; if (responses_[index_]->async == ASYNC) { read_frames_pending_ = true; base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ReadableFakeWebSocketStream::DoCallback, base::Unretained(this), frames, callback)); return ERR_IO_PENDING; } else { frames->swap(responses_[index_]->frames); return responses_[index_++]->error; } } private: void DoCallback(ScopedVector* frames, const CompletionCallback& callback) { read_frames_pending_ = false; frames->swap(responses_[index_]->frames); callback.Run(responses_[index_++]->error); return; } struct Response { Response(IsSync async, int error, ScopedVector frames) : async(async), error(error), frames(frames.Pass()) {} IsSync async; int error; ScopedVector frames; private: // Bad things will happen if we attempt to copy or assign |frames|. DISALLOW_COPY_AND_ASSIGN(Response); }; ScopedVector responses_; // The index into the responses_ array of the next response to be returned. size_t index_; // True when an async response from ReadFrames() is pending. This only applies // to "real" async responses. Once all the prepared responses have been // returned, ReadFrames() returns ERR_IO_PENDING but read_frames_pending_ is // not set to true. bool read_frames_pending_; }; // A FakeWebSocketStream where writes always complete successfully and // synchronously. class WriteableFakeWebSocketStream : public FakeWebSocketStream { public: virtual int WriteFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { return OK; } }; // A FakeWebSocketStream where writes always fail. class UnWriteableFakeWebSocketStream : public FakeWebSocketStream { public: virtual int WriteFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { return ERR_CONNECTION_RESET; } }; // A FakeWebSocketStream which echoes any frames written back. Clears the // "masked" header bit, but makes no other checks for validity. Tests using this // must run the MessageLoop to receive the callback(s). If a message with opcode // Close is echoed, then an ERR_CONNECTION_CLOSED is returned in the next // callback. The test must do something to cause WriteFrames() to be called, // otherwise the ReadFrames() callback will never be called. class EchoeyFakeWebSocketStream : public FakeWebSocketStream { public: EchoeyFakeWebSocketStream() : read_frames_(NULL), done_(false) {} virtual int WriteFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { // Users of WebSocketStream will not expect the ReadFrames() callback to be // called from within WriteFrames(), so post it to the message loop instead. stored_frames_.insert(stored_frames_.end(), frames->begin(), frames->end()); frames->weak_clear(); PostCallback(); return OK; } virtual int ReadFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { read_callback_ = callback; read_frames_ = frames; if (done_) PostCallback(); return ERR_IO_PENDING; } private: void PostCallback() { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&EchoeyFakeWebSocketStream::DoCallback, base::Unretained(this))); } void DoCallback() { if (done_) { read_callback_.Run(ERR_CONNECTION_CLOSED); } else if (!stored_frames_.empty()) { done_ = MoveFrames(read_frames_); read_frames_ = NULL; read_callback_.Run(OK); } } // Copy the frames stored in stored_frames_ to |out|, while clearing the // "masked" header bit. Returns true if a Close Frame was seen, false // otherwise. bool MoveFrames(ScopedVector* out) { bool seen_close = false; *out = stored_frames_.Pass(); for (ScopedVector::iterator it = out->begin(); it != out->end(); ++it) { WebSocketFrameHeader& header = (*it)->header; header.masked = false; if (header.opcode == WebSocketFrameHeader::kOpCodeClose) seen_close = true; } return seen_close; } ScopedVector stored_frames_; CompletionCallback read_callback_; // Owned by the caller of ReadFrames(). ScopedVector* read_frames_; // True if we should close the connection. bool done_; }; // A FakeWebSocketStream where writes trigger a connection reset. // This differs from UnWriteableFakeWebSocketStream in that it is asynchronous // and triggers ReadFrames to return a reset as well. Tests using this need to // run the message loop. There are two tricky parts here: // 1. Calling the write callback may call Close(), after which the read callback // should not be called. // 2. Calling either callback may delete the stream altogether. class ResetOnWriteFakeWebSocketStream : public FakeWebSocketStream { public: ResetOnWriteFakeWebSocketStream() : closed_(false), weak_ptr_factory_(this) {} virtual int WriteFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResetOnWriteFakeWebSocketStream::CallCallbackUnlessClosed, weak_ptr_factory_.GetWeakPtr(), callback, ERR_CONNECTION_RESET)); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ResetOnWriteFakeWebSocketStream::CallCallbackUnlessClosed, weak_ptr_factory_.GetWeakPtr(), read_callback_, ERR_CONNECTION_RESET)); return ERR_IO_PENDING; } virtual int ReadFrames(ScopedVector* frames, const CompletionCallback& callback) OVERRIDE { read_callback_ = callback; return ERR_IO_PENDING; } virtual void Close() OVERRIDE { closed_ = true; } private: void CallCallbackUnlessClosed(const CompletionCallback& callback, int value) { if (!closed_) callback.Run(value); } CompletionCallback read_callback_; bool closed_; // An IO error can result in the socket being deleted, so we use weak pointers // to ensure correct behaviour in that case. base::WeakPtrFactory weak_ptr_factory_; }; // This mock is for verifying that WebSocket protocol semantics are obeyed (to // the extent that they are implemented in WebSocketCommon). class MockWebSocketStream : public WebSocketStream { public: MOCK_METHOD2(ReadFrames, int(ScopedVector* frames, const CompletionCallback& callback)); MOCK_METHOD2(WriteFrames, int(ScopedVector* frames, const CompletionCallback& callback)); MOCK_METHOD0(Close, void()); MOCK_CONST_METHOD0(GetSubProtocol, std::string()); MOCK_CONST_METHOD0(GetExtensions, std::string()); MOCK_METHOD0(AsWebSocketStream, WebSocketStream*()); }; struct ArgumentCopyingWebSocketStreamCreator { scoped_ptr Create( const GURL& socket_url, const std::vector& requested_subprotocols, const url::Origin& origin, URLRequestContext* url_request_context, const BoundNetLog& net_log, scoped_ptr connect_delegate) { this->socket_url = socket_url; this->requested_subprotocols = requested_subprotocols; this->origin = origin; this->url_request_context = url_request_context; this->net_log = net_log; this->connect_delegate = connect_delegate.Pass(); return make_scoped_ptr(new WebSocketStreamRequest); } GURL socket_url; url::Origin origin; std::vector requested_subprotocols; URLRequestContext* url_request_context; BoundNetLog net_log; scoped_ptr connect_delegate; }; // Converts a std::string to a std::vector. For test purposes, it is // convenient to be able to specify data as a string, but the // WebSocketEventInterface requires the vector type. std::vector AsVector(const std::string& s) { return std::vector(s.begin(), s.end()); } // Base class for all test fixtures. class WebSocketChannelTest : public ::testing::Test { protected: WebSocketChannelTest() : stream_(new FakeWebSocketStream) {} // Creates a new WebSocketChannel and connects it, using the settings stored // in |connect_data_|. void CreateChannelAndConnect() { channel_.reset(new WebSocketChannel(CreateEventInterface(), &connect_data_.url_request_context)); channel_->SendAddChannelRequestForTesting( connect_data_.socket_url, connect_data_.requested_subprotocols, connect_data_.origin, base::Bind(&ArgumentCopyingWebSocketStreamCreator::Create, base::Unretained(&connect_data_.creator))); } // Same as CreateChannelAndConnect(), but calls the on_success callback as // well. This method is virtual so that subclasses can also set the stream. virtual void CreateChannelAndConnectSuccessfully() { CreateChannelAndConnect(); // Most tests aren't concerned with flow control from the renderer, so allow // MAX_INT quota units. channel_->SendFlowControl(kPlentyOfQuota); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } // Returns a WebSocketEventInterface to be passed to the WebSocketChannel. // This implementation returns a newly-created fake. Subclasses may return a // mock instead. virtual scoped_ptr CreateEventInterface() { return scoped_ptr(new FakeWebSocketEventInterface); } // This method serves no other purpose than to provide a nice syntax for // assigning to stream_. class T must be a subclass of WebSocketStream or you // will have unpleasant compile errors. template void set_stream(scoped_ptr stream) { // Since the definition of "PassAs" depends on the type T, the C++ standard // requires the "template" keyword to indicate that "PassAs" should be // parsed as a template method. stream_ = stream.template PassAs(); } // A struct containing the data that will be used to connect the channel. // Grouped for readability. struct ConnectData { ConnectData() : socket_url("ws://ws/"), origin("http://ws") {} // URLRequestContext object. URLRequestContext url_request_context; // URL to (pretend to) connect to. GURL socket_url; // Requested protocols for the request. std::vector requested_subprotocols; // Origin of the request url::Origin origin; // A fake WebSocketStreamCreator that just records its arguments. ArgumentCopyingWebSocketStreamCreator creator; }; ConnectData connect_data_; // The channel we are testing. Not initialised until SetChannel() is called. scoped_ptr channel_; // A mock or fake stream for tests that need one. scoped_ptr stream_; }; // enum of WebSocketEventInterface calls. These are intended to be or'd together // in order to instruct WebSocketChannelDeletingTest when it should fail. enum EventInterfaceCall { EVENT_ON_ADD_CHANNEL_RESPONSE = 0x1, EVENT_ON_DATA_FRAME = 0x2, EVENT_ON_FLOW_CONTROL = 0x4, EVENT_ON_CLOSING_HANDSHAKE = 0x8, EVENT_ON_FAIL_CHANNEL = 0x10, EVENT_ON_DROP_CHANNEL = 0x20, EVENT_ON_START_OPENING_HANDSHAKE = 0x40, EVENT_ON_FINISH_OPENING_HANDSHAKE = 0x80, }; class WebSocketChannelDeletingTest : public WebSocketChannelTest { public: ChannelState DeleteIfDeleting(EventInterfaceCall call) { if (deleting_ & call) { channel_.reset(); return CHANNEL_DELETED; } else { return CHANNEL_ALIVE; } } protected: WebSocketChannelDeletingTest() : deleting_(EVENT_ON_ADD_CHANNEL_RESPONSE | EVENT_ON_DATA_FRAME | EVENT_ON_FLOW_CONTROL | EVENT_ON_CLOSING_HANDSHAKE | EVENT_ON_FAIL_CHANNEL | EVENT_ON_DROP_CHANNEL | EVENT_ON_START_OPENING_HANDSHAKE | EVENT_ON_FINISH_OPENING_HANDSHAKE) {} // Create a ChannelDeletingFakeWebSocketEventInterface. Defined out-of-line to // avoid circular dependency. virtual scoped_ptr CreateEventInterface() OVERRIDE; // Tests can set deleting_ to a bitmap of EventInterfaceCall members that they // want to cause Channel deletion. The default is for all calls to cause // deletion. int deleting_; }; // A FakeWebSocketEventInterface that deletes the WebSocketChannel on failure to // connect. class ChannelDeletingFakeWebSocketEventInterface : public FakeWebSocketEventInterface { public: ChannelDeletingFakeWebSocketEventInterface( WebSocketChannelDeletingTest* fixture) : fixture_(fixture) {} virtual ChannelState OnAddChannelResponse( bool fail, const std::string& selected_protocol, const std::string& extensions) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_ADD_CHANNEL_RESPONSE); } virtual ChannelState OnDataFrame(bool fin, WebSocketMessageType type, const std::vector& data) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_DATA_FRAME); } virtual ChannelState OnFlowControl(int64 quota) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_FLOW_CONTROL); } virtual ChannelState OnClosingHandshake() OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_CLOSING_HANDSHAKE); } virtual ChannelState OnFailChannel(const std::string& message) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_FAIL_CHANNEL); } virtual ChannelState OnDropChannel(bool was_clean, uint16 code, const std::string& reason) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_DROP_CHANNEL); } virtual ChannelState OnStartOpeningHandshake( scoped_ptr request) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_START_OPENING_HANDSHAKE); } virtual ChannelState OnFinishOpeningHandshake( scoped_ptr response) OVERRIDE { return fixture_->DeleteIfDeleting(EVENT_ON_FINISH_OPENING_HANDSHAKE); } private: // A pointer to the test fixture. Owned by the test harness; this object will // be deleted before it is. WebSocketChannelDeletingTest* fixture_; }; scoped_ptr WebSocketChannelDeletingTest::CreateEventInterface() { return scoped_ptr( new ChannelDeletingFakeWebSocketEventInterface(this)); } // Base class for tests which verify that EventInterface methods are called // appropriately. class WebSocketChannelEventInterfaceTest : public WebSocketChannelTest { protected: WebSocketChannelEventInterfaceTest() : event_interface_(new StrictMock) { DefaultValue::Set(CHANNEL_ALIVE); ON_CALL(*event_interface_, OnAddChannelResponse(true, _, _)) .WillByDefault(Return(CHANNEL_DELETED)); ON_CALL(*event_interface_, OnDropChannel(_, _, _)) .WillByDefault(Return(CHANNEL_DELETED)); ON_CALL(*event_interface_, OnFailChannel(_)) .WillByDefault(Return(CHANNEL_DELETED)); } virtual ~WebSocketChannelEventInterfaceTest() { DefaultValue::Clear(); } // Tests using this fixture must set expectations on the event_interface_ mock // object before calling CreateChannelAndConnect() or // CreateChannelAndConnectSuccessfully(). This will only work once per test // case, but once should be enough. virtual scoped_ptr CreateEventInterface() OVERRIDE { return scoped_ptr(event_interface_.release()); } scoped_ptr event_interface_; }; // Base class for tests which verify that WebSocketStream methods are called // appropriately by using a MockWebSocketStream. class WebSocketChannelStreamTest : public WebSocketChannelTest { protected: WebSocketChannelStreamTest() : mock_stream_(new StrictMock) {} virtual void CreateChannelAndConnectSuccessfully() OVERRIDE { set_stream(mock_stream_.Pass()); WebSocketChannelTest::CreateChannelAndConnectSuccessfully(); } scoped_ptr mock_stream_; }; // Fixture for tests which test UTF-8 validation of sent Text frames via the // EventInterface. class WebSocketChannelSendUtf8Test : public WebSocketChannelEventInterfaceTest { public: virtual void SetUp() { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); // For the purpose of the tests using this fixture, it doesn't matter // whether these methods are called or not. EXPECT_CALL(*event_interface_, OnAddChannelResponse(_, _, _)) .Times(AnyNumber()); EXPECT_CALL(*event_interface_, OnFlowControl(_)) .Times(AnyNumber()); } }; // Fixture for tests which test use of receive quota from the renderer. class WebSocketChannelFlowControlTest : public WebSocketChannelEventInterfaceTest { protected: // Tests using this fixture should use CreateChannelAndConnectWithQuota() // instead of CreateChannelAndConnectSuccessfully(). void CreateChannelAndConnectWithQuota(int64 quota) { CreateChannelAndConnect(); channel_->SendFlowControl(quota); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } virtual void CreateChannelAndConnectSuccesfully() { NOTREACHED(); } }; // Fixture for tests which test UTF-8 validation of received Text frames using a // mock WebSocketStream. class WebSocketChannelReceiveUtf8Test : public WebSocketChannelStreamTest { public: virtual void SetUp() { // For the purpose of the tests using this fixture, it doesn't matter // whether these methods are called or not. EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); } }; // Simple test that everything that should be passed to the creator function is // passed to the creator function. TEST_F(WebSocketChannelTest, EverythingIsPassedToTheCreatorFunction) { connect_data_.socket_url = GURL("ws://example.com/test"); connect_data_.origin = url::Origin("http://example.com"); connect_data_.requested_subprotocols.push_back("Sinbad"); CreateChannelAndConnect(); const ArgumentCopyingWebSocketStreamCreator& actual = connect_data_.creator; EXPECT_EQ(&connect_data_.url_request_context, actual.url_request_context); EXPECT_EQ(connect_data_.socket_url, actual.socket_url); EXPECT_EQ(connect_data_.requested_subprotocols, actual.requested_subprotocols); EXPECT_EQ(connect_data_.origin.string(), actual.origin.string()); } // Verify that calling SendFlowControl before the connection is established does // not cause a crash. TEST_F(WebSocketChannelTest, SendFlowControlDuringHandshakeOkay) { CreateChannelAndConnect(); ASSERT_TRUE(channel_); channel_->SendFlowControl(65536); } // Any WebSocketEventInterface methods can delete the WebSocketChannel and // return CHANNEL_DELETED. The WebSocketChannelDeletingTests are intended to // verify that there are no use-after-free bugs when this happens. Problems will // probably only be found when running under Address Sanitizer or a similar // tool. TEST_F(WebSocketChannelDeletingTest, OnAddChannelResponseFail) { CreateChannelAndConnect(); EXPECT_TRUE(channel_); connect_data_.creator.connect_delegate->OnFailure("bye"); EXPECT_EQ(NULL, channel_.get()); } // Deletion is possible (due to IPC failure) even if the connect succeeds. TEST_F(WebSocketChannelDeletingTest, OnAddChannelResponseSuccess) { CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnDataFrameSync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_DATA_FRAME; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnDataFrameAsync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_DATA_FRAME; CreateChannelAndConnectSuccessfully(); EXPECT_TRUE(channel_); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnFlowControlAfterConnect) { deleting_ = EVENT_ON_FLOW_CONTROL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnFlowControlAfterSend) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); // Avoid deleting the channel yet. deleting_ = EVENT_ON_FAIL_CHANNEL | EVENT_ON_DROP_CHANNEL; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); deleting_ = EVENT_ON_FLOW_CONTROL; channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultInitialQuota, 'B')); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnClosingHandshakeSync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_CLOSING_HANDSHAKE; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnClosingHandshakeAsync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_CLOSING_HANDSHAKE; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnDropChannelWriteError) { set_stream(make_scoped_ptr(new UnWriteableFakeWebSocketStream)); deleting_ = EVENT_ON_DROP_CHANNEL; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("this will fail")); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnDropChannelReadError) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, ERR_FAILED); set_stream(stream.Pass()); deleting_ = EVENT_ON_DROP_CHANNEL; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnNotifyStartOpeningHandshakeError) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_START_OPENING_HANDSHAKE; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); channel_->OnStartOpeningHandshake(scoped_ptr( new WebSocketHandshakeRequestInfo(GURL("http://www.example.com/"), base::Time()))); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, OnNotifyFinishOpeningHandshakeError) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FINISH_OPENING_HANDSHAKE; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); scoped_refptr response_headers( new HttpResponseHeaders("")); channel_->OnFinishOpeningHandshake(scoped_ptr( new WebSocketHandshakeResponseInfo(GURL("http://www.example.com/"), 200, "OK", response_headers, base::Time()))); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelInSendFrame) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultInitialQuota * 2, 'T')); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelInOnReadDone) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, ERR_WS_PROTOCOL_ERROR); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); ASSERT_TRUE(channel_); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToMaskedFrame) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToBadControlFrame) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, 0xF, NOT_MASKED, ""}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } // Version of above test with NULL data. TEST_F(WebSocketChannelDeletingTest, FailChannelDueToBadControlFrameNull) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, 0xF, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToPongAfterClose) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, ""}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToPongAfterCloseNull) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToUnknownOpCode) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = {{FINAL_FRAME, 0x7, NOT_MASKED, ""}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueToUnknownOpCodeNull) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = {{FINAL_FRAME, 0x7, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelDeletingTest, FailChannelDueInvalidCloseReason) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); deleting_ = EVENT_ON_FAIL_CHANNEL; CreateChannelAndConnectSuccessfully(); EXPECT_EQ(NULL, channel_.get()); } TEST_F(WebSocketChannelEventInterfaceTest, ConnectSuccessReported) { // false means success. EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "", "")); // OnFlowControl is always called immediately after connect to provide initial // quota to the renderer. EXPECT_CALL(*event_interface_, OnFlowControl(_)); CreateChannelAndConnect(); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } TEST_F(WebSocketChannelEventInterfaceTest, ConnectFailureReported) { EXPECT_CALL(*event_interface_, OnFailChannel("hello")); CreateChannelAndConnect(); connect_data_.creator.connect_delegate->OnFailure("hello"); } TEST_F(WebSocketChannelEventInterfaceTest, NonWebSocketSchemeRejected) { EXPECT_CALL(*event_interface_, OnAddChannelResponse(true, "", "")); connect_data_.socket_url = GURL("http://www.google.com/"); CreateChannelAndConnect(); } TEST_F(WebSocketChannelEventInterfaceTest, ProtocolPassed) { EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "Bob", "")); EXPECT_CALL(*event_interface_, OnFlowControl(_)); CreateChannelAndConnect(); connect_data_.creator.connect_delegate->OnSuccess( scoped_ptr(new FakeWebSocketStream("Bob", ""))); } TEST_F(WebSocketChannelEventInterfaceTest, ExtensionsPassed) { EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "", "extension1, extension2")); EXPECT_CALL(*event_interface_, OnFlowControl(_)); CreateChannelAndConnect(); connect_data_.creator.connect_delegate->OnSuccess(scoped_ptr( new FakeWebSocketStream("", "extension1, extension2"))); } // The first frames from the server can arrive together with the handshake, in // which case they will be available as soon as ReadFrames() is called the first // time. TEST_F(WebSocketChannelEventInterfaceTest, DataLeftFromHandshake) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); } CreateChannelAndConnectSuccessfully(); } // A remote server could accept the handshake, but then immediately send a // Close frame. TEST_F(WebSocketChannelEventInterfaceTest, CloseAfterHandshake) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(SERVER_ERROR, "Internal Server Error")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_CONNECTION_CLOSED); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnClosingHandshake()); EXPECT_CALL( *event_interface_, OnDropChannel( true, kWebSocketErrorInternalServerError, "Internal Server Error")); } CreateChannelAndConnectSuccessfully(); } // A remote server could close the connection immediately after sending the // handshake response (most likely a bug in the server). TEST_F(WebSocketChannelEventInterfaceTest, ConnectionCloseAfterHandshake) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_CONNECTION_CLOSED); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)); } CreateChannelAndConnectSuccessfully(); } TEST_F(WebSocketChannelEventInterfaceTest, NormalAsyncRead) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; // We use this checkpoint object to verify that the callback isn't called // until we expect it to be. Checkpoint checkpoint; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); EXPECT_CALL(checkpoint, Call(2)); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); base::MessageLoop::current()->RunUntilIdle(); checkpoint.Call(2); } // Extra data can arrive while a read is being processed, resulting in the next // read completing synchronously. TEST_F(WebSocketChannelEventInterfaceTest, AsyncThenSyncRead) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames1[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "HELLO"}}; static const InitFrame frames2[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "WORLD"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames1); stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames2); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("WORLD"))); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // Data frames are delivered the same regardless of how many reads they arrive // as. TEST_F(WebSocketChannelEventInterfaceTest, FragmentedMessage) { scoped_ptr stream( new ReadableFakeWebSocketStream); // Here we have one message which arrived in five frames split across three // reads. It may have been reframed on arrival, but this class doesn't care // about that. static const InitFrame frames1[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "THREE"}, {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, " "}}; static const InitFrame frames2[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "SMALL"}}; static const InitFrame frames3[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, " "}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "FRAMES"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames1); stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames2); stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames3); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("THREE"))); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector(" "))); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("SMALL"))); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector(" "))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("FRAMES"))); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // A message can consist of one frame with NULL payload. TEST_F(WebSocketChannelEventInterfaceTest, NullMessage) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector(""))); CreateChannelAndConnectSuccessfully(); } // Connection closed by the remote host without a closing handshake. TEST_F(WebSocketChannelEventInterfaceTest, AsyncAbnormalClosure) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, ERR_CONNECTION_CLOSED); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // A connection reset should produce the same event as an unexpected closure. TEST_F(WebSocketChannelEventInterfaceTest, ConnectionReset) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, ERR_CONNECTION_RESET); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // RFC6455 5.1 "A client MUST close a connection if it detects a masked frame." TEST_F(WebSocketChannelEventInterfaceTest, MaskedFramesAreRejected) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnFailChannel( "A server must not mask any frames that it sends to the client.")); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // RFC6455 5.2 "If an unknown opcode is received, the receiving endpoint MUST // _Fail the WebSocket Connection_." TEST_F(WebSocketChannelEventInterfaceTest, UnknownOpCodeIsRejected) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = {{FINAL_FRAME, 4, NOT_MASKED, "HELLO"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnFailChannel("Unrecognized frame opcode: 4")); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // RFC6455 5.4 "Control frames ... MAY be injected in the middle of a // fragmented message." TEST_F(WebSocketChannelEventInterfaceTest, ControlFrameInDataMessage) { scoped_ptr stream( new ReadableFakeWebSocketStream); // We have one message of type Text split into two frames. In the middle is a // control message of type Pong. static const InitFrame frames1[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "SPLIT "}}; static const InitFrame frames2[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, ""}}; static const InitFrame frames3[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "MESSAGE"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames1); stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames2); stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames3); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("SPLIT "))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("MESSAGE"))); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // It seems redundant to repeat the entirety of the above test, so just test a // Pong with NULL data. TEST_F(WebSocketChannelEventInterfaceTest, PongWithNullData) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // If a frame has an invalid header, then the connection is closed and // subsequent frames must not trigger events. TEST_F(WebSocketChannelEventInterfaceTest, FrameAfterInvalidFrame) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "HELLO"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, " WORLD"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnFailChannel( "A server must not mask any frames that it sends to the client.")); } CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // If the renderer sends lots of small writes, we don't want to update the quota // for each one. TEST_F(WebSocketChannelEventInterfaceTest, SmallWriteDoesntUpdateQuota) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); } CreateChannelAndConnectSuccessfully(); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("B")); } // If we send enough to go below send_quota_low_water_mask_ we should get our // quota refreshed. TEST_F(WebSocketChannelEventInterfaceTest, LargeWriteUpdatesQuota) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); // We use this checkpoint object to verify that the quota update comes after // the write. Checkpoint checkpoint; { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(2)); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultInitialQuota, 'B')); checkpoint.Call(2); } // Verify that our quota actually is refreshed when we are told it is. TEST_F(WebSocketChannelEventInterfaceTest, QuotaReallyIsRefreshed) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); Checkpoint checkpoint; { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(2)); // If quota was not really refreshed, we would get an OnDropChannel() // message. EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(3)); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultQuotaRefreshTrigger, 'D')); checkpoint.Call(2); // We should have received more quota at this point. channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultQuotaRefreshTrigger, 'E')); checkpoint.Call(3); } // If we send more than the available quota then the connection will be closed // with an error. TEST_F(WebSocketChannelEventInterfaceTest, WriteOverQuotaIsRejected) { set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota)); EXPECT_CALL(*event_interface_, OnFailChannel("Send quota exceeded")); } CreateChannelAndConnectSuccessfully(); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultInitialQuota + 1, 'C')); } // If a write fails, the channel is dropped. TEST_F(WebSocketChannelEventInterfaceTest, FailedWrite) { set_stream(make_scoped_ptr(new UnWriteableFakeWebSocketStream)); Checkpoint checkpoint; { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)); EXPECT_CALL(checkpoint, Call(2)); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("H")); checkpoint.Call(2); } // OnDropChannel() is called exactly once when StartClosingHandshake() is used. TEST_F(WebSocketChannelEventInterfaceTest, SendCloseDropsChannel) { set_stream(make_scoped_ptr(new EchoeyFakeWebSocketStream)); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDropChannel(true, kWebSocketNormalClosure, "Fred")); } CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(kWebSocketNormalClosure, "Fred"); base::MessageLoop::current()->RunUntilIdle(); } // StartClosingHandshake() also works before connection completes, and calls // OnDropChannel. TEST_F(WebSocketChannelEventInterfaceTest, CloseDuringConnection) { EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, "")); CreateChannelAndConnect(); channel_->StartClosingHandshake(kWebSocketNormalClosure, "Joe"); } // OnDropChannel() is only called once when a write() on the socket triggers a // connection reset. TEST_F(WebSocketChannelEventInterfaceTest, OnDropChannelCalledOnce) { set_stream(make_scoped_ptr(new ResetOnWriteFakeWebSocketStream)); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, "")) .Times(1); CreateChannelAndConnectSuccessfully(); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("yt?")); base::MessageLoop::current()->RunUntilIdle(); } // When the remote server sends a Close frame with an empty payload, // WebSocketChannel should report code 1005, kWebSocketErrorNoStatusReceived. TEST_F(WebSocketChannelEventInterfaceTest, CloseWithNoPayloadGivesStatus1005) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, ""}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_CONNECTION_CLOSED); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnClosingHandshake()); EXPECT_CALL(*event_interface_, OnDropChannel(true, kWebSocketErrorNoStatusReceived, _)); CreateChannelAndConnectSuccessfully(); } // A version of the above test with NULL payload. TEST_F(WebSocketChannelEventInterfaceTest, CloseWithNullPayloadGivesStatus1005) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, NULL}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_CONNECTION_CLOSED); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnClosingHandshake()); EXPECT_CALL(*event_interface_, OnDropChannel(true, kWebSocketErrorNoStatusReceived, _)); CreateChannelAndConnectSuccessfully(); } // If ReadFrames() returns ERR_WS_PROTOCOL_ERROR, then the connection must be // failed. TEST_F(WebSocketChannelEventInterfaceTest, SyncProtocolErrorGivesStatus1002) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_WS_PROTOCOL_ERROR); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header")); CreateChannelAndConnectSuccessfully(); } // Async version of above test. TEST_F(WebSocketChannelEventInterfaceTest, AsyncProtocolErrorGivesStatus1002) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, ERR_WS_PROTOCOL_ERROR); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnFailChannel("Invalid frame header")); CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } TEST_F(WebSocketChannelEventInterfaceTest, StartHandshakeRequest) { { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnStartOpeningHandshakeCalled()); } CreateChannelAndConnectSuccessfully(); scoped_ptr request_info( new WebSocketHandshakeRequestInfo(GURL("ws://www.example.com/"), base::Time())); connect_data_.creator.connect_delegate->OnStartOpeningHandshake( request_info.Pass()); base::MessageLoop::current()->RunUntilIdle(); } TEST_F(WebSocketChannelEventInterfaceTest, FinishHandshakeRequest) { { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnFinishOpeningHandshakeCalled()); } CreateChannelAndConnectSuccessfully(); scoped_refptr response_headers( new HttpResponseHeaders("")); scoped_ptr response_info( new WebSocketHandshakeResponseInfo(GURL("ws://www.example.com/"), 200, "OK", response_headers, base::Time())); connect_data_.creator.connect_delegate->OnFinishOpeningHandshake( response_info.Pass()); base::MessageLoop::current()->RunUntilIdle(); } TEST_F(WebSocketChannelEventInterfaceTest, FailJustAfterHandshake) { { InSequence s; EXPECT_CALL(*event_interface_, OnStartOpeningHandshakeCalled()); EXPECT_CALL(*event_interface_, OnFinishOpeningHandshakeCalled()); EXPECT_CALL(*event_interface_, OnFailChannel("bye")); } CreateChannelAndConnect(); WebSocketStream::ConnectDelegate* connect_delegate = connect_data_.creator.connect_delegate.get(); GURL url("ws://www.example.com/"); scoped_ptr request_info( new WebSocketHandshakeRequestInfo(url, base::Time())); scoped_refptr response_headers( new HttpResponseHeaders("")); scoped_ptr response_info( new WebSocketHandshakeResponseInfo(url, 200, "OK", response_headers, base::Time())); connect_delegate->OnStartOpeningHandshake(request_info.Pass()); connect_delegate->OnFinishOpeningHandshake(response_info.Pass()); connect_delegate->OnFailure("bye"); base::MessageLoop::current()->RunUntilIdle(); } // Any frame after close is invalid. This test uses a Text frame. See also // test "PingAfterCloseIfRejected". TEST_F(WebSocketChannelEventInterfaceTest, DataAfterCloseIsRejected) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "Payload"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); { InSequence s; EXPECT_CALL(*event_interface_, OnClosingHandshake()); EXPECT_CALL(*event_interface_, OnFailChannel("Data frame received after close")); } CreateChannelAndConnectSuccessfully(); } // A Close frame with a one-byte payload elicits a specific console error // message. TEST_F(WebSocketChannelEventInterfaceTest, OneByteClosePayloadMessage) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, "\x03"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnFailChannel( "Received a broken close frame containing an invalid size body.")); CreateChannelAndConnectSuccessfully(); } // A Close frame with a reserved status code also elicits a specific console // error message. TEST_F(WebSocketChannelEventInterfaceTest, ClosePayloadReservedStatusMessage) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(ABNORMAL_CLOSURE, "Not valid on wire")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnFailChannel( "Received a broken close frame containing a reserved status code.")); CreateChannelAndConnectSuccessfully(); } // A Close frame with invalid UTF-8 also elicits a specific console error // message. TEST_F(WebSocketChannelEventInterfaceTest, ClosePayloadInvalidReason) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnFailChannel( "Received a broken close frame containing invalid UTF-8.")); CreateChannelAndConnectSuccessfully(); } // The reserved bits must all be clear on received frames. Extensions should // clear the bits when they are set correctly before passing on the frame. TEST_F(WebSocketChannelEventInterfaceTest, ReservedBitsMustNotBeSet) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "sakana"}}; // It is not worth adding support for reserved bits to InitFrame just for this // one test, so set the bit manually. ScopedVector raw_frames = CreateFrameVector(frames); raw_frames[0]->header.reserved1 = true; stream->PrepareRawReadFrames( ReadableFakeWebSocketStream::SYNC, OK, raw_frames.Pass()); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnFailChannel( "One or more reserved bits are on: reserved1 = 1, " "reserved2 = 0, reserved3 = 0")); CreateChannelAndConnectSuccessfully(); } // The closing handshake times out and sends an OnDropChannel event if no // response to the client Close message is received. TEST_F(WebSocketChannelEventInterfaceTest, ClientInitiatedClosingHandshakeTimesOut) { scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, ERR_IO_PENDING); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); // This checkpoint object verifies that the OnDropChannel message comes after // the timeout. Checkpoint checkpoint; TestClosure completion; { InSequence s; EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)) .WillOnce(InvokeClosureReturnDeleted(completion.closure())); } CreateChannelAndConnectSuccessfully(); // OneShotTimer is not very friendly to testing; there is no apparent way to // set an expectation on it. Instead the tests need to infer that the timeout // was fired by the behaviour of the WebSocketChannel object. channel_->SetClosingHandshakeTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); channel_->StartClosingHandshake(kWebSocketNormalClosure, ""); checkpoint.Call(1); completion.WaitForResult(); } // The closing handshake times out and sends an OnDropChannel event if a Close // message is received but the connection isn't closed by the remote host. TEST_F(WebSocketChannelEventInterfaceTest, ServerInitiatedClosingHandshakeTimesOut) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); Checkpoint checkpoint; TestClosure completion; { InSequence s; EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*event_interface_, OnClosingHandshake()); EXPECT_CALL(*event_interface_, OnDropChannel(false, kWebSocketErrorAbnormalClosure, _)) .WillOnce(InvokeClosureReturnDeleted(completion.closure())); } CreateChannelAndConnectSuccessfully(); channel_->SetClosingHandshakeTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); checkpoint.Call(1); completion.WaitForResult(); } // The renderer should provide us with some quota immediately, and then // WebSocketChannel calls ReadFrames as soon as the stream is available. TEST_F(WebSocketChannelStreamTest, FlowControlEarly) { Checkpoint checkpoint; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); { InSequence s; EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(checkpoint, Call(2)); } set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); channel_->SendFlowControl(kPlentyOfQuota); checkpoint.Call(1); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); checkpoint.Call(2); } // If for some reason the connect succeeds before the renderer sends us quota, // we shouldn't call ReadFrames() immediately. // TODO(ricea): Actually we should call ReadFrames() with a small limit so we // can still handle control frames. This should be done once we have any API to // expose quota to the lower levels. TEST_F(WebSocketChannelStreamTest, FlowControlLate) { Checkpoint checkpoint; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); { InSequence s; EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(checkpoint, Call(2)); } set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); checkpoint.Call(1); channel_->SendFlowControl(kPlentyOfQuota); checkpoint.Call(2); } // We should stop calling ReadFrames() when all quota is used. TEST_F(WebSocketChannelStreamTest, FlowControlStopsReadFrames) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)); set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); channel_->SendFlowControl(4); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } // Providing extra quota causes ReadFrames() to be called again. TEST_F(WebSocketChannelStreamTest, FlowControlStartsWithMoreQuota) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}}; Checkpoint checkpoint; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); } set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); channel_->SendFlowControl(4); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); checkpoint.Call(1); channel_->SendFlowControl(4); } // ReadFrames() isn't called again until all pending data has been passed to // the renderer. TEST_F(WebSocketChannelStreamTest, ReadFramesNotCalledUntilQuotaAvailable) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}}; Checkpoint checkpoint; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); } set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); channel_->SendFlowControl(2); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); checkpoint.Call(1); channel_->SendFlowControl(2); checkpoint.Call(2); channel_->SendFlowControl(2); } // A message that needs to be split into frames to fit within quota should // maintain correct semantics. TEST_F(WebSocketChannelFlowControlTest, SingleFrameMessageSplitSync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL( *event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FO"))); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("U"))); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("R"))); } CreateChannelAndConnectWithQuota(2); channel_->SendFlowControl(1); channel_->SendFlowControl(1); } // The code path for async messages is slightly different, so test it // separately. TEST_F(WebSocketChannelFlowControlTest, SingleFrameMessageSplitAsync) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FOUR"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, frames); set_stream(stream.Pass()); Checkpoint checkpoint; { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL( *event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FO"))); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("U"))); EXPECT_CALL(checkpoint, Call(3)); EXPECT_CALL( *event_interface_, OnDataFrame( true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("R"))); } CreateChannelAndConnectWithQuota(2); checkpoint.Call(1); base::MessageLoop::current()->RunUntilIdle(); checkpoint.Call(2); channel_->SendFlowControl(1); checkpoint.Call(3); channel_->SendFlowControl(1); } // A message split into multiple frames which is further split due to quota // restrictions should stil be correct. // TODO(ricea): The message ends up split into more frames than are strictly // necessary. The complexity/performance tradeoffs here need further // examination. TEST_F(WebSocketChannelFlowControlTest, MultipleFrameSplit) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FIRST FRAME IS 25 BYTES. "}, {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "SECOND FRAME IS 26 BYTES. "}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "FINAL FRAME IS 24 BYTES."}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FIRST FRAME IS"))); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeContinuation, AsVector(" 25 BYTES. "))); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("SECOND FRAME IS 26 BYTES. "))); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("FINAL "))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("FRAME IS 24 BYTES."))); } CreateChannelAndConnectWithQuota(14); channel_->SendFlowControl(43); channel_->SendFlowControl(32); } // An empty message handled when we are out of quota must not be delivered // out-of-order with respect to other messages. TEST_F(WebSocketChannelFlowControlTest, EmptyMessageNoQuota) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "FIRST MESSAGE"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, NULL}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "THIRD MESSAGE"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); { InSequence s; EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDataFrame(false, WebSocketFrameHeader::kOpCodeText, AsVector("FIRST "))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("MESSAGE"))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector(""))); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("THIRD MESSAGE"))); } CreateChannelAndConnectWithQuota(6); channel_->SendFlowControl(128); } // RFC6455 5.1 "a client MUST mask all frames that it sends to the server". // WebSocketChannel actually only sets the mask bit in the header, it doesn't // perform masking itself (not all transports actually use masking). TEST_F(WebSocketChannelStreamTest, SentFramesAreMasked) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "NEEDS MASKING"}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("NEEDS MASKING")); } // RFC6455 5.5.1 "The application MUST NOT send any more data frames after // sending a Close frame." TEST_F(WebSocketChannelStreamTest, NothingIsSentAfterClose) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Success")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(1000, "Success"); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("SHOULD BE IGNORED")); } // RFC6455 5.5.1 "If an endpoint receives a Close frame and did not previously // send a Close frame, the endpoint MUST send a Close frame in response." TEST_F(WebSocketChannelStreamTest, CloseIsEchoedBack) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Close")}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Close")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); } // The converse of the above case; after sending a Close frame, we should not // send another one. TEST_F(WebSocketChannelStreamTest, CloseOnlySentOnce) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Close")}}; static const InitFrame frames_init[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "Close")}}; // We store the parameters that were passed to ReadFrames() so that we can // call them explicitly later. CompletionCallback read_callback; ScopedVector* frames = NULL; // These are not interesting. EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); // Use a checkpoint to make the ordering of events clearer. Checkpoint checkpoint; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(DoAll(SaveArg<0>(&frames), SaveArg<1>(&read_callback), Return(ERR_IO_PENDING))); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(checkpoint, Call(3)); // WriteFrames() must not be called again. GoogleMock will ensure that the // test fails if it is. } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); channel_->StartClosingHandshake(kWebSocketNormalClosure, "Close"); checkpoint.Call(2); *frames = CreateFrameVector(frames_init); read_callback.Run(OK); checkpoint.Call(3); } // Invalid close status codes should not be sent on the network. TEST_F(WebSocketChannelStreamTest, InvalidCloseStatusCodeNotSent) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(SERVER_ERROR, "")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)); CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(999, ""); } // A Close frame with a reason longer than 123 bytes cannot be sent on the // network. TEST_F(WebSocketChannelStreamTest, LongCloseReasonNotSent) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(SERVER_ERROR, "")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)); CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(1000, std::string(124, 'A')); } // We generate code 1005, kWebSocketErrorNoStatusReceived, when there is no // status in the Close message from the other side. Code 1005 is not allowed to // appear on the wire, so we should not echo it back. See test // CloseWithNoPayloadGivesStatus1005, above, for confirmation that code 1005 is // correctly generated internally. TEST_F(WebSocketChannelStreamTest, Code1005IsNotEchoed) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, ""}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, ""}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); } TEST_F(WebSocketChannelStreamTest, Code1005IsNotEchoedNull) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, NULL}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, ""}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); } // Receiving an invalid UTF-8 payload in a Close frame causes us to fail the // connection. TEST_F(WebSocketChannelStreamTest, CloseFrameInvalidUtf8) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "\xFF")}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in Close frame")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()); CreateChannelAndConnectSuccessfully(); } // RFC6455 5.5.2 "Upon receipt of a Ping frame, an endpoint MUST send a Pong // frame in response" // 5.5.3 "A Pong frame sent in response to a Ping frame must have identical // "Application data" as found in the message body of the Ping frame being // replied to." TEST_F(WebSocketChannelStreamTest, PingRepliedWithPong) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, "Application data"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, MASKED, "Application data"}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); } // A ping with a NULL payload should be responded to with a Pong with a NULL // payload. TEST_F(WebSocketChannelStreamTest, NullPingRepliedWithNullPong) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, NULL}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, MASKED, NULL}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); CreateChannelAndConnectSuccessfully(); } TEST_F(WebSocketChannelStreamTest, PongInTheMiddleOfDataMessage) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, "Application data"}}; static const InitFrame expected1[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "Hello "}}; static const InitFrame expected2[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodePong, MASKED, "Application data"}}; static const InitFrame expected3[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, MASKED, "World"}}; ScopedVector* read_frames; CompletionCallback read_callback; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(DoAll(SaveArg<0>(&read_frames), SaveArg<1>(&read_callback), Return(ERR_IO_PENDING))) .WillRepeatedly(Return(ERR_IO_PENDING)); { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected1), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected2), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected3), _)) .WillOnce(Return(OK)); } CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("Hello ")); *read_frames = CreateFrameVector(frames); read_callback.Run(OK); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("World")); } // WriteFrames() may not be called until the previous write has completed. // WebSocketChannel must buffer writes that happen in the meantime. TEST_F(WebSocketChannelStreamTest, WriteFramesOneAtATime) { static const InitFrame expected1[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "Hello "}}; static const InitFrame expected2[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "World"}}; CompletionCallback write_callback; Checkpoint checkpoint; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); { InSequence s; EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected1), _)) .WillOnce(DoAll(SaveArg<1>(&write_callback), Return(ERR_IO_PENDING))); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected2), _)) .WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(checkpoint, Call(3)); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("Hello ")); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("World")); checkpoint.Call(2); write_callback.Run(OK); checkpoint.Call(3); } // WebSocketChannel must buffer frames while it is waiting for a write to // complete, and then send them in a single batch. The batching behaviour is // important to get good throughput in the "many small messages" case. TEST_F(WebSocketChannelStreamTest, WaitingMessagesAreBatched) { static const char input_letters[] = "Hello"; static const InitFrame expected1[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "H"}}; static const InitFrame expected2[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "e"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "l"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "l"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, MASKED, "o"}}; CompletionCallback write_callback; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected1), _)) .WillOnce(DoAll(SaveArg<1>(&write_callback), Return(ERR_IO_PENDING))); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected2), _)) .WillOnce(Return(ERR_IO_PENDING)); } CreateChannelAndConnectSuccessfully(); for (size_t i = 0; i < strlen(input_letters); ++i) { channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(1, input_letters[i])); } write_callback.Run(OK); } // When the renderer sends more on a channel than it has quota for, we send the // remote server a kWebSocketErrorGoingAway error code. TEST_F(WebSocketChannelStreamTest, SendGoingAwayOnRendererQuotaExceeded) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(GOING_AWAY, "")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()); CreateChannelAndConnectSuccessfully(); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, std::vector(kDefaultInitialQuota + 1, 'C')); } // For convenience, most of these tests use Text frames. However, the WebSocket // protocol also has Binary frames and those need to be 8-bit clean. For the // sake of completeness, this test verifies that they are. TEST_F(WebSocketChannelStreamTest, WrittenBinaryFramesAre8BitClean) { ScopedVector* frames = NULL; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)).WillOnce(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(_, _)) .WillOnce(DoAll(SaveArg<0>(&frames), Return(ERR_IO_PENDING))); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeBinary, std::vector(kBinaryBlob, kBinaryBlob + kBinaryBlobSize)); ASSERT_TRUE(frames != NULL); ASSERT_EQ(1U, frames->size()); const WebSocketFrame* out_frame = (*frames)[0]; EXPECT_EQ(kBinaryBlobSize, out_frame->header.payload_length); ASSERT_TRUE(out_frame->data); EXPECT_EQ(0, memcmp(kBinaryBlob, out_frame->data->data(), kBinaryBlobSize)); } // Test the read path for 8-bit cleanliness as well. TEST_F(WebSocketChannelEventInterfaceTest, ReadBinaryFramesAre8BitClean) { scoped_ptr frame( new WebSocketFrame(WebSocketFrameHeader::kOpCodeBinary)); WebSocketFrameHeader& frame_header = frame->header; frame_header.final = true; frame_header.payload_length = kBinaryBlobSize; frame->data = new IOBuffer(kBinaryBlobSize); memcpy(frame->data->data(), kBinaryBlob, kBinaryBlobSize); ScopedVector frames; frames.push_back(frame.release()); scoped_ptr stream( new ReadableFakeWebSocketStream); stream->PrepareRawReadFrames( ReadableFakeWebSocketStream::SYNC, OK, frames.Pass()); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(_)); EXPECT_CALL(*event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeBinary, std::vector(kBinaryBlob, kBinaryBlob + kBinaryBlobSize))); CreateChannelAndConnectSuccessfully(); } // Invalid UTF-8 is not permitted in Text frames. TEST_F(WebSocketChannelSendUtf8Test, InvalidUtf8Rejected) { EXPECT_CALL( *event_interface_, OnFailChannel("Browser sent a text frame containing invalid UTF-8")); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("\xff")); } // A Text message cannot end with a partial UTF-8 character. TEST_F(WebSocketChannelSendUtf8Test, IncompleteCharacterInFinalFrame) { EXPECT_CALL( *event_interface_, OnFailChannel("Browser sent a text frame containing invalid UTF-8")); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("\xc2")); } // A non-final Text frame may end with a partial UTF-8 character (compare to // previous test). TEST_F(WebSocketChannelSendUtf8Test, IncompleteCharacterInNonFinalFrame) { CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("\xc2")); } // UTF-8 parsing context must be retained between frames. TEST_F(WebSocketChannelSendUtf8Test, ValidCharacterSplitBetweenFrames) { CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("\xf1")); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\x80\xa0\xbf")); } // Similarly, an invalid character should be detected even if split. TEST_F(WebSocketChannelSendUtf8Test, InvalidCharacterSplit) { EXPECT_CALL( *event_interface_, OnFailChannel("Browser sent a text frame containing invalid UTF-8")); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("\xe1")); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\x80\xa0\xbf")); } // An invalid character must be detected in continuation frames. TEST_F(WebSocketChannelSendUtf8Test, InvalidByteInContinuation) { EXPECT_CALL( *event_interface_, OnFailChannel("Browser sent a text frame containing invalid UTF-8")); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeText, AsVector("foo")); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("bar")); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\xff")); } // However, continuation frames of a Binary frame will not be tested for UTF-8 // validity. TEST_F(WebSocketChannelSendUtf8Test, BinaryContinuationNotChecked) { CreateChannelAndConnectSuccessfully(); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeBinary, AsVector("foo")); channel_->SendFrame( false, WebSocketFrameHeader::kOpCodeContinuation, AsVector("bar")); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("\xff")); } // Multiple text messages can be validated without the validation state getting // confused. TEST_F(WebSocketChannelSendUtf8Test, ValidateMultipleTextMessages) { CreateChannelAndConnectSuccessfully(); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("foo")); channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("bar")); } // UTF-8 validation is enforced on received Text frames. TEST_F(WebSocketChannelEventInterfaceTest, ReceivedInvalidUtf8) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xff"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota)); EXPECT_CALL(*event_interface_, OnFailChannel("Could not decode a text frame as UTF-8.")); CreateChannelAndConnectSuccessfully(); base::MessageLoop::current()->RunUntilIdle(); } // Invalid UTF-8 is not sent over the network. TEST_F(WebSocketChannelStreamTest, InvalidUtf8TextFrameNotSent) { static const InitFrame expected[] = {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(GOING_AWAY, "")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); CreateChannelAndConnectSuccessfully(); channel_->SendFrame( true, WebSocketFrameHeader::kOpCodeText, AsVector("\xff")); } // The rest of the tests for receiving invalid UTF-8 test the communication with // the server. Since there is only one code path, it would be redundant to // perform the same tests on the EventInterface as well. // If invalid UTF-8 is received in a Text frame, the connection is failed. TEST_F(WebSocketChannelReceiveUtf8Test, InvalidTextFrameRejected) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xff"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}}; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); } CreateChannelAndConnectSuccessfully(); } // A received Text message is not permitted to end with a partial UTF-8 // character. TEST_F(WebSocketChannelReceiveUtf8Test, IncompleteCharacterReceived) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); CreateChannelAndConnectSuccessfully(); } // However, a non-final Text frame may end with a partial UTF-8 character. TEST_F(WebSocketChannelReceiveUtf8Test, IncompleteCharacterIncompleteMessage) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); CreateChannelAndConnectSuccessfully(); } // However, it will become an error if it is followed by an empty final frame. TEST_F(WebSocketChannelReceiveUtf8Test, TricksyIncompleteCharacter) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xc2"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, ""}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); CreateChannelAndConnectSuccessfully(); } // UTF-8 parsing context must be retained between received frames of the same // message. TEST_F(WebSocketChannelReceiveUtf8Test, ReceivedParsingContextRetained) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xf1"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "\x80\xa0\xbf"}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); CreateChannelAndConnectSuccessfully(); } // An invalid character must be detected even if split between frames. TEST_F(WebSocketChannelReceiveUtf8Test, SplitInvalidCharacterReceived) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "\xe1"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "\x80\xa0\xbf"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); CreateChannelAndConnectSuccessfully(); } // An invalid character received in a continuation frame must be detected. TEST_F(WebSocketChannelReceiveUtf8Test, InvalidReceivedIncontinuation) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "foo"}, {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "bar"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "\xff"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "Invalid UTF-8 in text frame")}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); CreateChannelAndConnectSuccessfully(); } // Continuations of binary frames must not be tested for UTF-8 validity. TEST_F(WebSocketChannelReceiveUtf8Test, ReceivedBinaryNotUtf8Tested) { static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeBinary, NOT_MASKED, "foo"}, {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "bar"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "\xff"}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); CreateChannelAndConnectSuccessfully(); } // Multiple Text messages can be validated. TEST_F(WebSocketChannelReceiveUtf8Test, ValidateMultipleReceived) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "foo"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "bar"}}; EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); CreateChannelAndConnectSuccessfully(); } // A new data message cannot start in the middle of another data message. TEST_F(WebSocketChannelEventInterfaceTest, BogusContinuation) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeBinary, NOT_MASKED, "frame1"}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, "frame2"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota)); EXPECT_CALL( *event_interface_, OnDataFrame( false, WebSocketFrameHeader::kOpCodeBinary, AsVector("frame1"))); EXPECT_CALL( *event_interface_, OnFailChannel( "Received start of new message but previous message is unfinished.")); CreateChannelAndConnectSuccessfully(); } // A new message cannot start with a Continuation frame. TEST_F(WebSocketChannelEventInterfaceTest, MessageStartingWithContinuation) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, "continuation"}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota)); EXPECT_CALL(*event_interface_, OnFailChannel("Received unexpected continuation frame.")); CreateChannelAndConnectSuccessfully(); } // A frame passed to the renderer must be either non-empty or have the final bit // set. TEST_F(WebSocketChannelEventInterfaceTest, DataFramesNonEmptyOrFinal) { scoped_ptr stream( new ReadableFakeWebSocketStream); static const InitFrame frames[] = { {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, ""}, {NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, ""}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, ""}}; stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, frames); set_stream(stream.Pass()); EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _, _)); EXPECT_CALL(*event_interface_, OnFlowControl(kDefaultInitialQuota)); EXPECT_CALL( *event_interface_, OnDataFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector(""))); CreateChannelAndConnectSuccessfully(); } // If we receive another frame after Close, it is not valid. It is not // completely clear what behaviour is required from the standard in this case, // but the current implementation fails the connection. Since a Close has // already been sent, this just means closing the connection. TEST_F(WebSocketChannelStreamTest, PingAfterCloseIsRejected) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}, {FINAL_FRAME, WebSocketFrameHeader::kOpCodePing, NOT_MASKED, "Ping body"}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); { // We only need to verify the relative order of WriteFrames() and // Close(). The current implementation calls WriteFrames() for the Close // frame before calling ReadFrames() again, but that is an implementation // detail and better not to consider required behaviour. InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()).Times(1); } CreateChannelAndConnectSuccessfully(); } // A protocol error from the remote server should result in a close frame with // status 1002, followed by the connection closing. TEST_F(WebSocketChannelStreamTest, ProtocolError) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(PROTOCOL_ERROR, "WebSocket Protocol Error")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_WS_PROTOCOL_ERROR)); EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()); CreateChannelAndConnectSuccessfully(); } // Set the closing handshake timeout to a very tiny value before connecting. class WebSocketChannelStreamTimeoutTest : public WebSocketChannelStreamTest { protected: WebSocketChannelStreamTimeoutTest() {} virtual void CreateChannelAndConnectSuccessfully() OVERRIDE { set_stream(mock_stream_.Pass()); CreateChannelAndConnect(); channel_->SendFlowControl(kPlentyOfQuota); channel_->SetClosingHandshakeTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } }; // In this case the server initiates the closing handshake with a Close // message. WebSocketChannel responds with a matching Close message, and waits // for the server to close the TCP/IP connection. The server never closes the // connection, so the closing handshake times out and WebSocketChannel closes // the connection itself. TEST_F(WebSocketChannelStreamTimeoutTest, ServerInitiatedCloseTimesOut) { static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(ReturnFrames(&frames)) .WillRepeatedly(Return(ERR_IO_PENDING)); Checkpoint checkpoint; TestClosure completion; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(*mock_stream_, Close()) .WillOnce(InvokeClosure(completion.closure())); } CreateChannelAndConnectSuccessfully(); checkpoint.Call(1); completion.WaitForResult(); } // In this case the client initiates the closing handshake by sending a Close // message. WebSocketChannel waits for a Close message in response from the // server. The server never responds to the Close message, so the closing // handshake times out and WebSocketChannel closes the connection. TEST_F(WebSocketChannelStreamTimeoutTest, ClientInitiatedCloseTimesOut) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillRepeatedly(Return(ERR_IO_PENDING)); TestClosure completion; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); EXPECT_CALL(*mock_stream_, Close()) .WillOnce(InvokeClosure(completion.closure())); } CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(kWebSocketNormalClosure, "OK"); completion.WaitForResult(); } // In this case the client initiates the closing handshake and the server // responds with a matching Close message. WebSocketChannel waits for the server // to close the TCP/IP connection, but it never does. The closing handshake // times out and WebSocketChannel closes the connection. TEST_F(WebSocketChannelStreamTimeoutTest, ConnectionCloseTimesOut) { static const InitFrame expected[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; static const InitFrame frames[] = { {FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, CLOSE_DATA(NORMAL_CLOSURE, "OK")}}; EXPECT_CALL(*mock_stream_, GetSubProtocol()).Times(AnyNumber()); EXPECT_CALL(*mock_stream_, GetExtensions()).Times(AnyNumber()); TestClosure completion; ScopedVector* read_frames = NULL; CompletionCallback read_callback; { InSequence s; // Copy the arguments to ReadFrames so that the test can call the callback // after it has send the close message. EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(DoAll(SaveArg<0>(&read_frames), SaveArg<1>(&read_callback), Return(ERR_IO_PENDING))); // The first real event that happens is the client sending the Close // message. EXPECT_CALL(*mock_stream_, WriteFrames(EqualsFrames(expected), _)) .WillOnce(Return(OK)); // The |read_frames| callback is called (from this test case) at this // point. ReadFrames is called again by WebSocketChannel, waiting for // ERR_CONNECTION_CLOSED. EXPECT_CALL(*mock_stream_, ReadFrames(_, _)) .WillOnce(Return(ERR_IO_PENDING)); // The timeout happens and so WebSocketChannel closes the stream. EXPECT_CALL(*mock_stream_, Close()) .WillOnce(InvokeClosure(completion.closure())); } CreateChannelAndConnectSuccessfully(); channel_->StartClosingHandshake(kWebSocketNormalClosure, "OK"); ASSERT_TRUE(read_frames); // Provide the "Close" message from the server. *read_frames = CreateFrameVector(frames); read_callback.Run(OK); completion.WaitForResult(); } } // namespace } // namespace net