Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Unified Diff: net/spdy/spdy_websocket_stream_unittest.cc

Issue 3020068: WebSocket over SPDY. (Closed)
Patch Set: fix unittests Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/spdy/spdy_websocket_stream.cc ('k') | net/websockets/websocket_job.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/spdy/spdy_websocket_stream_unittest.cc
diff --git a/net/spdy/spdy_websocket_stream_unittest.cc b/net/spdy/spdy_websocket_stream_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..05f3110b60a0bc723aabee1ced47bcb3a263b60d
--- /dev/null
+++ b/net/spdy/spdy_websocket_stream_unittest.cc
@@ -0,0 +1,433 @@
+// Copyright (c) 2010 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 <string>
+#include <vector>
+
+#include "net/spdy/spdy_websocket_stream.h"
+#include "net/base/completion_callback.h"
+#include "net/proxy/proxy_server.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+spdy::SpdyFrame* ConstructSpdyWebSocketHandshakeReq(
+ const char* const url,
+ const char* const origin,
+ const char* const protocol,
+ bool compressed,
+ spdy::SpdyStreamId stream_id,
+ RequestPriority request_priority) {
+ const SpdyHeaderInfo kSynStreamHeader = {
+ spdy::SYN_STREAM,
+ stream_id,
+ 0, // Associated stream ID
+ net::ConvertRequestPriorityToSpdyPriority(request_priority),
+ spdy::CONTROL_FLAG_NONE,
+ compressed,
+ spdy::INVALID, // Status
+ NULL, // Data,
+ 0, // Length
+ spdy::DATA_FLAG_NONE
+ };
+
+ const char* const headers[] = {
+ "url",
+ url,
+ "origin",
+ origin,
+ "protocol",
+ protocol,
+ };
+ int header_size = arraysize(headers) / 2;
+ if (protocol == NULL)
+ header_size -= 1;
+
+ return ConstructSpdyPacket(
+ kSynStreamHeader,
+ NULL,
+ 0,
+ headers,
+ header_size);
+}
+
+spdy::SpdyFrame* ConstructSpdyWebSocketHandshakeResp(
+ const char* const url,
+ const char* const origin,
+ const char* const protocol,
+ bool compressed,
+ spdy::SpdyStreamId stream_id,
+ RequestPriority request_priority) {
+ const SpdyHeaderInfo kSynReplyHeader = {
+ spdy::SYN_REPLY,
+ stream_id,
+ 0, // Associated stream ID
+ net::ConvertRequestPriorityToSpdyPriority(request_priority),
+ spdy::CONTROL_FLAG_NONE,
+ false,
+ spdy::INVALID, // Status
+ NULL, // Data
+ 0, // Length
+ spdy::DATA_FLAG_NONE
+ };
+
+ const char* const headers[] = {
+ "sec-websocket-location",
+ url,
+ "sec-websocket-origin",
+ origin,
+ "sec-websocket-protocol",
+ protocol,
+ };
+ int header_size = arraysize(headers) / 2;
+ if (protocol == NULL)
+ header_size -= 1;
+
+ return ConstructSpdyPacket(
+ kSynReplyHeader,
+ NULL,
+ 0,
+ headers,
+ header_size);
+}
+
+spdy::SpdyFrame* ConstructSpdyWebSocketFrame(
+ const char* data,
+ int len,
+ spdy::SpdyStreamId stream_id,
+ bool fin) {
+ spdy::SpdyFramer framer;
+ return framer.CreateDataFrame(
+ stream_id, data, len,
+ fin ? spdy::DATA_FLAG_FIN : spdy::DATA_FLAG_NONE);
+}
+
+struct SpdyWebSocketStreamEvent {
+ enum EventType {
+ EVENT_CREATED,
+ EVENT_SENT_HEADERS, EVENT_RECEIVED_HEADER,
+ EVENT_SENT_DATA, EVENT_RECEIVED_DATA,
+ EVENT_CLOSE,
+ };
+ SpdyWebSocketStreamEvent(EventType type,
+ SpdyWebSocketStream* stream,
+ const spdy::SpdyHeaderBlock& headers,
+ int result,
+ const std::string& data)
+ : event_type(type),
+ stream(stream),
+ headers(headers),
+ result(result),
+ data(data) {}
+
+ EventType event_type;
+ SpdyWebSocketStream* stream;
+ spdy::SpdyHeaderBlock headers;
+ int result;
+ std::string data;
+};
+
+class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStreamDelegate {
+ public:
+ explicit SpdyWebSocketStreamEventRecorder(CompletionCallback* callback)
+ : on_created_(NULL),
+ on_sent_headers_(NULL),
+ on_received_header_(NULL),
+ on_sent_data_(NULL),
+ on_received_data_(NULL),
+ on_close_(NULL),
+ callback_(callback) {}
+ virtual ~SpdyWebSocketStreamEventRecorder() {
+ delete on_created_;
+ delete on_sent_headers_;
+ delete on_received_header_;
+ delete on_sent_data_;
+ delete on_received_data_;
+ delete on_close_;
+ }
+
+ void SetOnCreated(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_created_ = callback;
+ }
+ void SetOnSentHeaders(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_sent_headers_ = callback;
+ }
+ void SetOnReceivedHeader(
+ Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_received_header_ = callback;
+ }
+ void SetOnSentData(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_sent_data_ = callback;
+ }
+ void SetOnReceivedData(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_received_data_ = callback;
+ }
+ void SetOnClose(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
+ on_close_ = callback;
+ }
+
+ virtual void OnCreatedSpdyStream(SpdyWebSocketStream* stream, int result) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED,
+ stream,
+ spdy::SpdyHeaderBlock(),
+ result,
+ std::string()));
+ if (on_created_)
+ on_created_->Run(&events_.back());
+ }
+
+ virtual void OnSentSpdyHeaders(SpdyWebSocketStream* stream) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
+ stream,
+ spdy::SpdyHeaderBlock(),
+ OK,
+ std::string()));
+ if (on_sent_data_)
+ on_sent_data_->Run(&events_.back());
+ }
+ virtual int OnReceivedSpdyResponseHeader(
+ SpdyWebSocketStream* stream,
+ const spdy::SpdyHeaderBlock& headers,
+ int status) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(
+ SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
+ stream,
+ headers,
+ status,
+ std::string()));
+ if (on_received_header_)
+ on_received_header_->Run(&events_.back());
+ return status;
+ }
+ virtual void OnSentSpdyData(
+ SpdyWebSocketStream* stream, int amount_sent) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(
+ SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
+ stream,
+ spdy::SpdyHeaderBlock(),
+ amount_sent,
+ std::string()));
+ if (on_sent_data_)
+ on_sent_data_->Run(&events_.back());
+ }
+ virtual void OnReceivedSpdyData(
+ SpdyWebSocketStream* stream, const char* data, int length) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(
+ SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
+ stream,
+ spdy::SpdyHeaderBlock(),
+ length,
+ std::string(data, length)));
+ if (on_received_data_)
+ on_received_data_->Run(&events_.back());
+ }
+ virtual void OnCloseSpdyStream(SpdyWebSocketStream* stream) {
+ events_.push_back(
+ SpdyWebSocketStreamEvent(
+ SpdyWebSocketStreamEvent::EVENT_CLOSE,
+ stream,
+ spdy::SpdyHeaderBlock(),
+ OK,
+ std::string()));
+ if (on_close_)
+ on_close_->Run(&events_.back());
+ if (callback_)
+ callback_->Run(net::OK);
+ }
+
+ const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const {
+ return events_;
+ }
+
+ private:
+ std::vector<SpdyWebSocketStreamEvent> events_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_created_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_sent_headers_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_received_header_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_sent_data_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_received_data_;
+ Callback1<SpdyWebSocketStreamEvent*>::Type* on_close_;
+ CompletionCallback* callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder);
+};
+
+class SpdyWebSocketStreamTest : public testing::Test {
+ public:
+ OrderedSocketData* data() { return data_; }
+
+ void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) {
+ std::string frame;
+ frame.append(1, '\0');
+ frame.append("hello");
+ frame.append(1, '\xff');
+ event->stream->SendData(frame.data(), frame.size());
+ }
+
+ void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) {
+ static const char kClosingFrame[] = "\xff\0";
+ event->stream->SendData(kClosingFrame, 2);
+ }
+
+ void DoClose(SpdyWebSocketStreamEvent* event) {
+ event->stream->Close();
+ }
+
+ protected:
+ SpdyWebSocketStreamTest() {}
+ virtual ~SpdyWebSocketStreamTest() {}
+
+ virtual void SetUp() {}
+ virtual void TearDown() {
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void EnableCompression(bool enabled) {
+ spdy::SpdyFramer::set_enable_compression_default(enabled);
+ }
+ int InitSession(MockRead* reads, size_t reads_count,
+ MockWrite* writes, size_t writes_count,
+ HostPortPair& host_port_pair) {
+ HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
+ data_ = new OrderedSocketData(reads, reads_count, writes, writes_count);
+ session_deps_.socket_factory->AddSocketDataProvider(data_.get());
+ http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
+ session_ = http_session_->spdy_session_pool()->
+ Get(pair, http_session_->mutable_spdy_settings(), BoundNetLog());
+ tcp_params_ = new TCPSocketParams(host_port_pair.host(),
+ host_port_pair.port(),
+ MEDIUM, GURL(), false);
+ TestCompletionCallback callback;
+ scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
+ EXPECT_EQ(ERR_IO_PENDING,
+ connection->Init(host_port_pair.ToString(), tcp_params_, MEDIUM,
+ &callback, http_session_->tcp_socket_pool(),
+ BoundNetLog()));
+ EXPECT_EQ(OK, callback.WaitForResult());
+ return session_->InitializeWithSocket(connection.release(), false, OK);
+ }
+
+ SpdySessionDependencies session_deps_;
+ scoped_refptr<OrderedSocketData> data_;
+ scoped_refptr<HttpNetworkSession> http_session_;
+ scoped_refptr<SpdySession> session_;
+ scoped_refptr<TCPSocketParams> tcp_params_;
+};
+
+TEST_F(SpdyWebSocketStreamTest, Echo) {
+ EnableCompression(false);
+ SpdySession::SetSSLMode(false);
+
+ spdy::SpdyStreamId stream_id = 1;
+
+ scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyWebSocketHandshakeReq(
+ "ws://example.com/echo",
+ "http://example.com/wsdemo",
+ NULL,
+ false,
+ stream_id,
+ HIGHEST));
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyWebSocketHandshakeResp(
+ "ws://example.com/echo",
+ "http://example.com/wsdemo",
+ NULL,
+ false,
+ stream_id,
+ HIGHEST));
+
+ static const char* const msg_frame = "\0hello\xff";
+ scoped_ptr<spdy::SpdyFrame> msg(ConstructSpdyWebSocketFrame(
+ msg_frame, 7, stream_id, false));
+
+ static const char* const closing_frame = "\xff\0";
+ scoped_ptr<spdy::SpdyFrame> closing(ConstructSpdyWebSocketFrame(
+ closing_frame, 2, stream_id, false));
+
+ MockWrite writes[] = {
+ CreateMockWrite(*req.get(), 1),
+ CreateMockWrite(*msg.get(), 3),
+ CreateMockWrite(*closing.get(), 5)
+ };
+
+ MockRead reads[] = {
+ CreateMockRead(*resp.get(), 2),
+ CreateMockRead(*msg.get(), 4),
+ // Skip sequence 6 to notify closing has been sent.
+ CreateMockRead(*closing.get(), 7),
+ MockRead(false, 0, 8) // EOF
+ };
+
+ HostPortPair host_port_pair("example.com", 80);
+ HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
+ EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
+ host_port_pair));
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<SpdyWebSocketStreamEventRecorder> delegate(
+ new SpdyWebSocketStreamEventRecorder(&callback));
+ // Necessary for NewCallback.
+ SpdyWebSocketStreamTest* test = this;
+ delegate->SetOnReceivedHeader(
+ NewCallback(test, &SpdyWebSocketStreamTest::DoSendHelloFrame));
+ delegate->SetOnReceivedData(
+ NewCallback(test, &SpdyWebSocketStreamTest::DoSendClosingFrame));
+
+ scoped_ptr<SpdyWebSocketStream> websocket_stream(
+ new SpdyWebSocketStream(session_, delegate.get()));
+
+ BoundNetLog net_log;
+ GURL url("ws://example.com/echo");
+ ASSERT_EQ(OK, websocket_stream->InitializeStream(url, HIGHEST, net_log));
+
+ EXPECT_EQ(stream_id, websocket_stream->stream_id());
+
+
+ linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+ (*headers)["url"] = "ws://example.com/echo";
+ (*headers)["origin"] = "http://example.com/wsdemo";
+
+ websocket_stream->SendRequest(headers);
+
+ callback.WaitForResult();
+
+ const std::vector<SpdyWebSocketStreamEvent>& events =
+ delegate->GetSeenEvents();
+ ASSERT_EQ(8U, events.size());
+
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED,
+ events[0].event_type);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
+ events[1].event_type);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
+ events[2].event_type);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
+ events[3].event_type);
+ EXPECT_EQ(7, events[3].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
+ events[4].event_type);
+ EXPECT_EQ(7, events[4].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
+ events[5].event_type);
+ EXPECT_EQ(2, events[5].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
+ events[6].event_type);
+ EXPECT_EQ(2, events[6].result);
+ EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
+ events[7].event_type);
+
+ EXPECT_TRUE(!http_session_->spdy_session_pool()->HasSession(pair));
+ EXPECT_TRUE(data()->at_read_eof());
+ EXPECT_TRUE(data()->at_write_eof());
+}
+
+} // namespace net
« no previous file with comments | « net/spdy/spdy_websocket_stream.cc ('k') | net/websockets/websocket_job.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698