diff options
author | toyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-03 07:31:26 +0000 |
---|---|---|
committer | toyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-03 07:31:26 +0000 |
commit | 0837bc6161b7698f9a0300f38717abdf3c6f86bc (patch) | |
tree | f226ea109ad15e89e710204befa7441d557fb225 /net | |
parent | cf4b6d6d8a47f92b87674420c11f986239c51f99 (diff) | |
download | chromium_src-0837bc6161b7698f9a0300f38717abdf3c6f86bc.zip chromium_src-0837bc6161b7698f9a0300f38717abdf3c6f86bc.tar.gz chromium_src-0837bc6161b7698f9a0300f38717abdf3c6f86bc.tar.bz2 |
Add WriteHeaders interface to SpdySession and SpdyStream.
To support WebSocket over SPDY, SpdyStream should provide a interface to
send HEADERS control frames for SpdyWebSocketStream.
This is because WebSocket over SPDY requires to convert a WebSocket frame
into one HEADERS frame and subsequent DATA frame(s) in the spec.
See also http://goo.gl/mJCrx the spec draft 9, "Frame mapping" section.
BUG=42320
TEST=net_unittests
Review URL: https://chromiumcodereview.appspot.com/10810069
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149808 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/net_log_event_type_list.h | 13 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 26 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 5 | ||||
-rw-r--r-- | net/spdy/spdy_stream.cc | 62 | ||||
-rw-r--r-- | net/spdy/spdy_stream.h | 21 | ||||
-rw-r--r-- | net/spdy/spdy_stream_spdy2_unittest.cc | 105 | ||||
-rw-r--r-- | net/spdy/spdy_stream_spdy3_unittest.cc | 105 | ||||
-rw-r--r-- | net/spdy/spdy_stream_test_util.cc | 6 | ||||
-rw-r--r-- | net/spdy/spdy_stream_test_util.h | 2 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_spdy2.cc | 71 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_spdy2.h | 158 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_spdy3.cc | 71 | ||||
-rw-r--r-- | net/spdy/spdy_test_util_spdy3.h | 158 |
13 files changed, 640 insertions, 163 deletions
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index 819b115..755407a 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -976,14 +976,23 @@ EVENT_TYPE(SPDY_SESSION_SYN_STREAM) // } EVENT_TYPE(SPDY_SESSION_PUSHED_SYN_STREAM) -// This event is sent for a SPDY HEADERS frame. +// This event is sent for a sending SPDY HEADERS frame. // The following parameters are attached: // { // "flags": <The control frame flags>, // "headers": <The list of header:value pairs>, // "id": <The stream id>, // } -EVENT_TYPE(SPDY_SESSION_HEADERS) +EVENT_TYPE(SPDY_SESSION_SEND_HEADERS) + +// This event is sent for a receiving SPDY HEADERS frame. +// The following parameters are attached: +// { +// "flags": <The control frame flags>, +// "headers": <The list of header:value pairs>, +// "id": <The stream id>, +// } +EVENT_TYPE(SPDY_SESSION_RECV_HEADERS) // This event is sent for a SPDY SYN_REPLY. // The following parameters are attached: diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 0e859c5..fc0a5cc 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -675,6 +675,30 @@ SpdyCredentialControlFrame* SpdySession::CreateCredentialFrame( return credential_frame.release(); } +SpdyHeadersControlFrame* SpdySession::CreateHeadersFrame( + SpdyStreamId stream_id, + const SpdyHeaderBlock& headers, + SpdyControlFlags flags) { + // Find our stream + CHECK(IsStreamActive(stream_id)); + scoped_refptr<SpdyStream> stream = active_streams_[stream_id]; + CHECK_EQ(stream->stream_id(), stream_id); + + // Create a HEADER frame. + scoped_ptr<SpdyHeadersControlFrame> frame( + buffered_spdy_framer_->CreateHeaders(stream_id, flags, true, &headers)); + + if (net_log().IsLoggingAllEvents()) { + bool fin = flags & CONTROL_FLAG_FIN; + net_log().AddEvent( + NetLog::TYPE_SPDY_SESSION_SEND_HEADERS, + base::Bind(&NetLogSpdySynCallback, + &headers, fin, /*unidirectional=*/false, + stream_id, 0)); + } + return frame.release(); +} + SpdyDataFrame* SpdySession::CreateDataFrame(SpdyStreamId stream_id, net::IOBuffer* data, int len, SpdyDataFlags flags) { @@ -1523,7 +1547,7 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id, const SpdyHeaderBlock& headers) { if (net_log().IsLoggingAllEvents()) { net_log().AddEvent( - NetLog::TYPE_SPDY_SESSION_HEADERS, + NetLog::TYPE_SPDY_SESSION_RECV_HEADERS, base::Bind(&NetLogSpdySynCallback, &headers, fin, /*unidirectional=*/false, stream_id, 0)); diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 3d95101..579452e 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -205,6 +205,11 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, const std::string& cert, RequestPriority priority); + // Write a HEADERS frame to the stream. + SpdyHeadersControlFrame* CreateHeadersFrame(SpdyStreamId stream_id, + const SpdyHeaderBlock& headers, + SpdyControlFlags flags); + // Write a data frame to the stream. // Used to create and queue a data frame for the given stream. SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc index 90d3cfb..00dc01e 100644 --- a/net/spdy/spdy_stream.cc +++ b/net/spdy/spdy_stream.cc @@ -137,19 +137,36 @@ SpdyFrame* SpdyStream::ProduceNextFrame() { // that our stream_id is correct. DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE); DCHECK_GT(stream_id_, 0u); - DCHECK(!pending_data_frames_.empty()); - SpdyFrame* frame = pending_data_frames_.front(); - pending_data_frames_.pop_front(); - return frame; + DCHECK(!pending_frames_.empty()); + + PendingFrame frame = pending_frames_.front(); + pending_frames_.pop_front(); + + if (frame.type == TYPE_DATA) { + // Send queued data frame. + return frame.data_frame; + } else { + DCHECK(frame.type == TYPE_HEADER); + // Create actual HEADERS frame just in time because it depends on + // compression context and should not be reordered after the creation. + SpdyFrame* header_frame = session_->CreateHeadersFrame( + stream_id_, *frame.header_block, SpdyControlFlags()); + delete frame.header_block; + return header_frame; + } } + NOTREACHED(); } SpdyStream::~SpdyStream() { UpdateHistograms(); - while (!pending_data_frames_.empty()) { - SpdyFrame* frame = pending_data_frames_.back(); - pending_data_frames_.pop_back(); - delete frame; + while (!pending_frames_.empty()) { + PendingFrame frame = pending_frames_.back(); + pending_frames_.pop_back(); + if (frame.type == TYPE_DATA) + delete frame.data_frame; + else + delete frame.header_block; } } @@ -556,15 +573,38 @@ int SpdyStream::SendRequest(bool has_upload_data) { return DoLoop(OK); } -int SpdyStream::WriteStreamData(IOBuffer* data, int length, +int SpdyStream::WriteHeaders(SpdyHeaderBlock* headers) { + // Until the first headers by SYN_STREAM have been completely sent, we can + // not be sure that our stream_id is correct. + DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE); + CHECK_GT(stream_id_, 0u); + + PendingFrame frame; + frame.type = TYPE_HEADER; + frame.header_block = headers; + pending_frames_.push_back(frame); + + SetHasWriteAvailable(); + return ERR_IO_PENDING; +} + +int SpdyStream::WriteStreamData(IOBuffer* data, + int length, SpdyDataFlags flags) { // Until the headers have been completely sent, we can not be sure // that our stream_id is correct. DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE); CHECK_GT(stream_id_, 0u); - pending_data_frames_.push_back( - session_->CreateDataFrame(stream_id_, data, length, flags)); + SpdyDataFrame* data_frame = session_->CreateDataFrame( + stream_id_, data, length, flags); + if (!data_frame) + return ERR_IO_PENDING; + + PendingFrame frame; + frame.type = TYPE_DATA; + frame.data_frame = data_frame; + pending_frames_.push_back(frame); SetHasWriteAvailable(); return ERR_IO_PENDING; diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h index 8653d9f..0e383b3 100644 --- a/net/spdy/spdy_stream.h +++ b/net/spdy/spdy_stream.h @@ -88,6 +88,21 @@ class NET_EXPORT_PRIVATE SpdyStream DISALLOW_COPY_AND_ASSIGN(Delegate); }; + // Indicates pending frame type. + enum PendingFrameType { + TYPE_HEADER, + TYPE_DATA + }; + + // Structure to contains pending frame information. + typedef struct { + PendingFrameType type; + union { + SpdyHeaderBlock* header_block; + SpdyDataFrame* data_frame; + }; + } PendingFrame; + // SpdyStream constructor SpdyStream(SpdySession* session, bool pushed, @@ -222,6 +237,10 @@ class NET_EXPORT_PRIVATE SpdyStream // For non push stream, it will send SYN_STREAM frame. int SendRequest(bool has_upload_data); + // Sends a HEADERS frame. SpdyStream owns |headers| and will release it after + // the HEADERS frame is actually sent. + int WriteHeaders(SpdyHeaderBlock* headers); + // Sends DATA frame. int WriteStreamData(IOBuffer* data, int length, SpdyDataFlags flags); @@ -345,7 +364,7 @@ class NET_EXPORT_PRIVATE SpdyStream scoped_ptr<SpdyHeaderBlock> response_; base::Time response_time_; - std::list<SpdyFrame*> pending_data_frames_; + std::list<PendingFrame> pending_frames_; State io_state_; diff --git a/net/spdy/spdy_stream_spdy2_unittest.cc b/net/spdy/spdy_stream_spdy2_unittest.cc index 5974e0a..8055fd8 100644 --- a/net/spdy/spdy_stream_spdy2_unittest.cc +++ b/net/spdy/spdy_stream_spdy2_unittest.cc @@ -145,7 +145,8 @@ TEST_F(SpdyStreamSpdy2Test, SendDataAfterOpen) { TestCompletionCallback callback; scoped_ptr<TestSpdyStreamDelegate> delegate( - new TestSpdyStreamDelegate(stream.get(), buf.get(), callback.callback())); + new TestSpdyStreamDelegate( + stream.get(), NULL, buf.get(), callback.callback())); stream->SetDelegate(delegate.get()); EXPECT_FALSE(stream->HasUrl()); @@ -172,6 +173,105 @@ TEST_F(SpdyStreamSpdy2Test, SendDataAfterOpen) { EXPECT_TRUE(delegate->closed()); } +TEST_F(SpdyStreamSpdy2Test, SendHeaderAndDataAfterOpen) { + SpdySessionDependencies session_deps; + + session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps); + SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool()); + + scoped_ptr<SpdyFrame> expected_request(ConstructSpdyWebSocket( + 1, + "/chat", + "server.example.com", + "http://example.com")); + scoped_ptr<SpdyFrame> expected_headers(ConstructSpdyWebSocketHeadersFrame( + 1, "6", true)); + scoped_ptr<SpdyFrame> expected_message(ConstructSpdyBodyFrame("hello!", 6)); + MockWrite writes[] = { + CreateMockWrite(*expected_request), + CreateMockWrite(*expected_headers), + CreateMockWrite(*expected_message) + }; + writes[0].sequence_number = 0; + writes[1].sequence_number = 2; + writes[1].sequence_number = 3; + + scoped_ptr<SpdyFrame> response( + ConstructSpdyWebSocketSynReply(1)); + MockRead reads[] = { + CreateMockRead(*response), + MockRead(ASYNC, 0, 0), // EOF + }; + reads[0].sequence_number = 1; + reads[1].sequence_number = 4; + + OrderedSocketData data(reads, arraysize(reads), + writes, arraysize(writes)); + MockConnect connect_data(SYNCHRONOUS, OK); + data.set_connect_data(connect_data); + + session_deps.socket_factory->AddSocketDataProvider(&data); + + scoped_refptr<SpdySession> session(CreateSpdySession()); + const char* kStreamUrl = "ws://server.example.com/chat"; + GURL url(kStreamUrl); + + HostPortPair host_port_pair("server.example.com", 80); + scoped_refptr<TransportSocketParams> transport_params( + new TransportSocketParams(host_port_pair, LOWEST, false, false, + OnHostResolutionCallback())); + + scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); + EXPECT_EQ(OK, connection->Init(host_port_pair.ToString(), transport_params, + LOWEST, CompletionCallback(), + session_->GetTransportSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL), + BoundNetLog())); + session->InitializeWithSocket(connection.release(), false, OK); + + scoped_refptr<SpdyStream> stream; + ASSERT_EQ( + OK, + session->CreateStream(url, LOWEST, &stream, BoundNetLog(), + CompletionCallback())); + scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(6)); + memcpy(buf->data(), "hello!", 6); + TestCompletionCallback callback; + scoped_ptr<SpdyHeaderBlock> message_headers(new SpdyHeaderBlock); + (*message_headers)["opcode"] = "1"; + (*message_headers)["length"] = "6"; + (*message_headers)["fin"] = "1"; + + scoped_ptr<TestSpdyStreamDelegate> delegate( + new TestSpdyStreamDelegate(stream.get(), + message_headers.release(), + buf.get(), + callback.callback())); + stream->SetDelegate(delegate.get()); + + EXPECT_FALSE(stream->HasUrl()); + + scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); + (*headers)["path"] = url.path(); + (*headers)["host"] = url.host(); + (*headers)["version"] = "WebSocket/13"; + (*headers)["scheme"] = url.scheme(); + (*headers)["origin"] = "http://example.com"; + stream->set_spdy_headers(headers.Pass()); + EXPECT_TRUE(stream->HasUrl()); + + EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true)); + + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_TRUE(delegate->send_headers_completed()); + EXPECT_EQ("101", (*delegate->response())["status"]); + EXPECT_EQ(std::string(), delegate->received_data()); + // TODO(toyoshim): OnDataSent should be invoked when each data frame is sent. + // But current implementation invokes also when each HEADERS frame is sent. + //EXPECT_EQ(6, delegate->data_sent()); +} + TEST_F(SpdyStreamSpdy2Test, PushedStream) { const char kStreamUrl[] = "http://www.google.com/"; @@ -319,7 +419,8 @@ TEST_F(SpdyStreamSpdy2Test, StreamError) { TestCompletionCallback callback; scoped_ptr<TestSpdyStreamDelegate> delegate( - new TestSpdyStreamDelegate(stream.get(), buf.get(), callback.callback())); + new TestSpdyStreamDelegate( + stream.get(), NULL, buf.get(), callback.callback())); stream->SetDelegate(delegate.get()); EXPECT_FALSE(stream->HasUrl()); diff --git a/net/spdy/spdy_stream_spdy3_unittest.cc b/net/spdy/spdy_stream_spdy3_unittest.cc index 58d93e9..3a3ad66 100644 --- a/net/spdy/spdy_stream_spdy3_unittest.cc +++ b/net/spdy/spdy_stream_spdy3_unittest.cc @@ -146,7 +146,8 @@ TEST_F(SpdyStreamSpdy3Test, SendDataAfterOpen) { TestCompletionCallback callback; scoped_ptr<TestSpdyStreamDelegate> delegate( - new TestSpdyStreamDelegate(stream.get(), buf.get(), callback.callback())); + new TestSpdyStreamDelegate( + stream.get(), NULL, buf.get(), callback.callback())); stream->SetDelegate(delegate.get()); EXPECT_FALSE(stream->HasUrl()); @@ -173,6 +174,105 @@ TEST_F(SpdyStreamSpdy3Test, SendDataAfterOpen) { EXPECT_TRUE(delegate->closed()); } +TEST_F(SpdyStreamSpdy3Test, SendHeaderAndDataAfterOpen) { + SpdySessionDependencies session_deps; + + session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps); + SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool()); + + scoped_ptr<SpdyFrame> expected_request(ConstructSpdyWebSocket( + 1, + "/chat", + "server.example.com", + "http://example.com")); + scoped_ptr<SpdyFrame> expected_headers(ConstructSpdyWebSocketHeadersFrame( + 1, "6", true)); + scoped_ptr<SpdyFrame> expected_message(ConstructSpdyBodyFrame("hello!", 6)); + MockWrite writes[] = { + CreateMockWrite(*expected_request), + CreateMockWrite(*expected_headers), + CreateMockWrite(*expected_message) + }; + writes[0].sequence_number = 0; + writes[1].sequence_number = 2; + writes[1].sequence_number = 3; + + scoped_ptr<SpdyFrame> response( + ConstructSpdyWebSocketSynReply(1)); + MockRead reads[] = { + CreateMockRead(*response), + MockRead(ASYNC, 0, 0), // EOF + }; + reads[0].sequence_number = 1; + reads[1].sequence_number = 4; + + OrderedSocketData data(reads, arraysize(reads), + writes, arraysize(writes)); + MockConnect connect_data(SYNCHRONOUS, OK); + data.set_connect_data(connect_data); + + session_deps.socket_factory->AddSocketDataProvider(&data); + + scoped_refptr<SpdySession> session(CreateSpdySession()); + const char* kStreamUrl = "ws://server.example.com/chat"; + GURL url(kStreamUrl); + + HostPortPair host_port_pair("server.example.com", 80); + scoped_refptr<TransportSocketParams> transport_params( + new TransportSocketParams(host_port_pair, LOWEST, false, false, + OnHostResolutionCallback())); + + scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); + EXPECT_EQ(OK, connection->Init(host_port_pair.ToString(), transport_params, + LOWEST, CompletionCallback(), + session_->GetTransportSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL), + BoundNetLog())); + session->InitializeWithSocket(connection.release(), false, OK); + + scoped_refptr<SpdyStream> stream; + ASSERT_EQ( + OK, + session->CreateStream(url, LOWEST, &stream, BoundNetLog(), + CompletionCallback())); + scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(6)); + memcpy(buf->data(), "hello!", 6); + TestCompletionCallback callback; + scoped_ptr<SpdyHeaderBlock> message_headers(new SpdyHeaderBlock); + (*message_headers)[":opcode"] = "1"; + (*message_headers)[":length"] = "6"; + (*message_headers)[":fin"] = "1"; + + scoped_ptr<TestSpdyStreamDelegate> delegate( + new TestSpdyStreamDelegate(stream.get(), + message_headers.release(), + buf.get(), + callback.callback())); + stream->SetDelegate(delegate.get()); + + EXPECT_FALSE(stream->HasUrl()); + + scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); + (*headers)[":path"] = url.path(); + (*headers)[":host"] = url.host(); + (*headers)[":version"] = "WebSocket/13"; + (*headers)[":scheme"] = url.scheme(); + (*headers)[":origin"] = "http://example.com"; + stream->set_spdy_headers(headers.Pass()); + EXPECT_TRUE(stream->HasUrl()); + + EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true)); + + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_TRUE(delegate->send_headers_completed()); + EXPECT_EQ("101", (*delegate->response())[":status"]); + EXPECT_EQ(std::string(), delegate->received_data()); + // TODO(toyoshim): OnDataSent should be invoked when each data frame is sent. + // But current implementation invokes also when each HEADERS frame is sent. + //EXPECT_EQ(6, delegate->data_sent()); +} + TEST_F(SpdyStreamSpdy3Test, PushedStream) { const char kStreamUrl[] = "http://www.google.com/"; @@ -324,7 +424,8 @@ TEST_F(SpdyStreamSpdy3Test, StreamError) { TestCompletionCallback callback; scoped_ptr<TestSpdyStreamDelegate> delegate( - new TestSpdyStreamDelegate(stream.get(), buf.get(), callback.callback())); + new TestSpdyStreamDelegate( + stream.get(), NULL, buf.get(), callback.callback())); stream->SetDelegate(delegate.get()); EXPECT_FALSE(stream->HasUrl()); diff --git a/net/spdy/spdy_stream_test_util.cc b/net/spdy/spdy_stream_test_util.cc index 93863d3..b973cf8 100644 --- a/net/spdy/spdy_stream_test_util.cc +++ b/net/spdy/spdy_stream_test_util.cc @@ -14,9 +14,11 @@ namespace test { TestSpdyStreamDelegate::TestSpdyStreamDelegate( SpdyStream* stream, + SpdyHeaderBlock* headers, IOBufferWithSize* buf, const CompletionCallback& callback) : stream_(stream), + headers_(headers), buf_(buf), callback_(callback), send_headers_completed_(false), @@ -47,6 +49,10 @@ int TestSpdyStreamDelegate::OnResponseReceived(const SpdyHeaderBlock& response, int status) { EXPECT_TRUE(send_headers_completed_); *response_ = response; + if (headers_.get()) { + EXPECT_EQ(ERR_IO_PENDING, + stream_->WriteHeaders(headers_.release())); + } if (buf_) { EXPECT_EQ(ERR_IO_PENDING, stream_->WriteStreamData(buf_.get(), buf_->size(), diff --git a/net/spdy/spdy_stream_test_util.h b/net/spdy/spdy_stream_test_util.h index 76ea49e..293bd7a 100644 --- a/net/spdy/spdy_stream_test_util.h +++ b/net/spdy/spdy_stream_test_util.h @@ -17,6 +17,7 @@ namespace test { class TestSpdyStreamDelegate : public SpdyStream::Delegate { public: TestSpdyStreamDelegate(SpdyStream* stream, + SpdyHeaderBlock* headers, IOBufferWithSize* buf, const CompletionCallback& callback); virtual ~TestSpdyStreamDelegate(); @@ -41,6 +42,7 @@ class TestSpdyStreamDelegate : public SpdyStream::Delegate { private: SpdyStream* stream_; + scoped_ptr<SpdyHeaderBlock> headers_; scoped_refptr<IOBufferWithSize> buf_; CompletionCallback callback_; bool send_headers_completed_; diff --git a/net/spdy/spdy_test_util_spdy2.cc b/net/spdy/spdy_test_util_spdy2.cc index 1c08f33f..b9cd38b 100644 --- a/net/spdy/spdy_test_util_spdy2.cc +++ b/net/spdy/spdy_test_util_spdy2.cc @@ -454,6 +454,35 @@ SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[], arraysize(kConnectHeaders)); } +// Constructs a standard SPDY SYN_STREAM frame for a WebSocket over SPDY +// opening handshake. +SpdyFrame* ConstructSpdyWebSocket(int stream_id, + const char* path, + const char* host, + const char* origin) { + const char* const kWebSocketHeaders[] = { + "path", + path, + "host", + host, + "version", + "WebSocket/13", + "scheme", + "ws", + "origin", + origin + }; + return ConstructSpdyControlFrame(/*extra_headers*/ NULL, + /*extra_header_count*/ 0, + /*compressed*/ false, + stream_id, + LOWEST, + SYN_STREAM, + CONTROL_FLAG_NONE, + kWebSocketHeaders, + arraysize(kWebSocketHeaders)); +} + // Constructs a standard SPDY push SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. @@ -654,6 +683,25 @@ SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], arraysize(kStandardGetHeaders)); } +// Constructs a standard SPDY SYN_REPLY packet to match the WebSocket over SPDY +// opening handshake. +// Returns a SpdyFrame. +SpdyFrame* ConstructSpdyWebSocketSynReply(int stream_id) { + static const char* const kStandardWebSocketHeaders[] = { + "status", + "101" + }; + return ConstructSpdyControlFrame(NULL, + 0, + false, + stream_id, + LOWEST, + SYN_REPLY, + CONTROL_FLAG_NONE, + kStandardWebSocketHeaders, + arraysize(kStandardWebSocketHeaders)); +} + // Constructs a standard SPDY POST SYN packet. // |content_length| is the size of post data. // |extra_headers| are the extra header-value pairs, which typically @@ -760,6 +808,29 @@ SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data, stream_id, data, len, fin ? DATA_FLAG_FIN : DATA_FLAG_NONE); } +// Constructs a SPDY HEADERS frame for a WebSocket frame over SPDY. +SpdyFrame* ConstructSpdyWebSocketHeadersFrame(int stream_id, + const char* length, + bool fin) { + static const char* const kHeaders[] = { + "opcode", + "1", // text frame + "length", + length, + "fin", + fin ? "1" : "0" + }; + return ConstructSpdyControlFrame(/*extra_headers*/ NULL, + /*extra_header_count*/ 0, + /*compression*/ false, + stream_id, + LOWEST, + HEADERS, + CONTROL_FLAG_NONE, + kHeaders, + arraysize(kHeaders)); +} + // Wraps |frame| in the payload of a data frame in stream |stream_id|. SpdyFrame* ConstructWrappedSpdyFrame( const scoped_ptr<SpdyFrame>& frame, diff --git a/net/spdy/spdy_test_util_spdy2.h b/net/spdy/spdy_test_util_spdy2.h index 584cc49..5833214 100644 --- a/net/spdy/spdy_test_util_spdy2.h +++ b/net/spdy/spdy_test_util_spdy2.h @@ -117,31 +117,31 @@ int AppendToBuffer(int val, // |buffer| is the buffer we're filling in. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo& header_info, - const char* const extra_headers[], - int extra_header_count, - const char* const tail[], - int tail_header_count); + const char* const extra_headers[], + int extra_header_count, + const char* const tail[], + int tail_header_count); // Construct a generic SpdyControlFrame. SpdyFrame* ConstructSpdyControlFrame(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - SpdyControlType type, - SpdyControlFlags flags, - const char* const* kHeaders, - int kHeadersSize); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + SpdyControlType type, + SpdyControlFlags flags, + const char* const* kHeaders, + int kHeadersSize); SpdyFrame* ConstructSpdyControlFrame(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - SpdyControlType type, - SpdyControlFlags flags, - const char* const* kHeaders, - int kHeadersSize, - int associated_stream_id); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + SpdyControlType type, + SpdyControlFlags flags, + const char* const* kHeaders, + int kHeadersSize, + int associated_stream_id); // Construct an expected SPDY reply string. // |extra_headers| are the extra header-value pairs, which typically @@ -161,8 +161,7 @@ SpdyFrame* ConstructSpdySettings(const SettingsMap& settings); // Construct an expected SPDY CREDENTIAL frame. // |credential| is the credential to send. // Returns the constructed frame. The caller takes ownership of the frame. -SpdyFrame* ConstructSpdyCredential( - const SpdyCredential& credential); +SpdyFrame* ConstructSpdyCredential(const SpdyCredential& credential); // Construct a SPDY PING frame. // Returns the constructed frame. The caller takes ownership of the frame. @@ -174,13 +173,12 @@ SpdyFrame* ConstructSpdyGoAway(); // Construct a SPDY WINDOW_UPDATE frame. // Returns the constructed frame. The caller takes ownership of the frame. -SpdyFrame* ConstructSpdyWindowUpdate(SpdyStreamId, - uint32 delta_window_size); +SpdyFrame* ConstructSpdyWindowUpdate(SpdyStreamId, uint32 delta_window_size); // Construct a SPDY RST_STREAM frame. // Returns the constructed frame. The caller takes ownership of the frame. SpdyFrame* ConstructSpdyRstStream(SpdyStreamId stream_id, - SpdyStatusCodes status); + SpdyStatusCodes status); // Construct a single SPDY header entry, for validation. // |extra_headers| are the extra header-value pairs. @@ -199,19 +197,19 @@ int ConstructSpdyHeader(const char* const extra_headers[], // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const url, - bool compressed, - int stream_id, - RequestPriority request_priority); + bool compressed, + int stream_id, + RequestPriority request_priority); // Constructs a standard SPDY GET SYN packet, optionally compressed. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority); // Constructs a standard SPDY GET SYN packet, optionally compressed. // |extra_headers| are the extra header-value pairs, which typically @@ -219,52 +217,59 @@ SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], // the full url will be used instead of simply the path. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - bool direct); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + bool direct); // Constructs a standard SPDY SYN_STREAM frame for a CONNECT request. SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[], - int extra_header_count, - int stream_id); + int extra_header_count, + int stream_id); + +// Constructs a standard SPDY SYN_STREAM frame for WebSocket over SPDY +// opening handshake. +SpdyFrame* ConstructSpdyWebSocket(int stream_id, + const char* path, + const char* host, + const char* origin); // Constructs a standard SPDY push SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id); + int extra_header_count, + int stream_id, + int associated_stream_id); SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id, - const char* url); + int extra_header_count, + int stream_id, + int associated_stream_id, + const char* url); SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id, - const char* url, - const char* status, - const char* location); + int extra_header_count, + int stream_id, + int associated_stream_id, + const char* url, + const char* status, + const char* location); SpdyFrame* ConstructSpdyPush(int stream_id, - int associated_stream_id, - const char* url); + int associated_stream_id, + const char* url); SpdyFrame* ConstructSpdyPushHeaders(int stream_id, - const char* const extra_headers[], - int extra_header_count); + const char* const extra_headers[], + int extra_header_count); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], - int extra_header_count, - int stream_id); + int extra_header_count, + int stream_id); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically @@ -272,6 +277,11 @@ SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGetSynReplyRedirect(int stream_id); +// Constructs a standard SPDY SYN_REPLY packet to match the WebSocket over SPDY +// opening handshake. +// Returns a SpdyFrame. +SpdyFrame* ConstructSpdyWebSocketSynReply(int stream_id); + // Constructs a standard SPDY SYN_REPLY packet with an Internal Server // Error status code. // Returns a SpdyFrame. @@ -279,45 +289,49 @@ SpdyFrame* ConstructSpdySynReplyError(int stream_id); // Constructs a standard SPDY SYN_REPLY packet with the specified status code. // Returns a SpdyFrame. -SpdyFrame* ConstructSpdySynReplyError( - const char* const status, - const char* const* const extra_headers, - int extra_header_count, - int stream_id); +SpdyFrame* ConstructSpdySynReplyError(const char* const status, + const char* const* const extra_headers, + int extra_header_count, + int stream_id); // Constructs a standard SPDY POST SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPost(int64 content_length, - const char* const extra_headers[], - int extra_header_count); + const char* const extra_headers[], + int extra_header_count); // Constructs a chunked transfer SPDY POST SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructChunkedSpdyPost(const char* const extra_headers[], - int extra_header_count); + int extra_header_count); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY POST. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[], - int extra_header_count); + int extra_header_count); // Constructs a single SPDY data frame with the contents "hello!" SpdyFrame* ConstructSpdyBodyFrame(int stream_id, - bool fin); + bool fin); // Constructs a single SPDY data frame with the given content. SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data, - uint32 len, bool fin); + uint32 len, bool fin); + +// Constructs a SPDY HEADERS frame for a WebSocket frame over SPDY. +SpdyFrame* ConstructSpdyWebSocketHeadersFrame(int stream_id, + const char* length, + bool fin); // Wraps |frame| in the payload of a data frame in stream |stream_id|. -SpdyFrame* ConstructWrappedSpdyFrame( - const scoped_ptr<SpdyFrame>& frame, int stream_id); +SpdyFrame* ConstructWrappedSpdyFrame(const scoped_ptr<SpdyFrame>& frame, + int stream_id); // Create an async MockWrite from the given SpdyFrame. MockWrite CreateMockWrite(const SpdyFrame& req); diff --git a/net/spdy/spdy_test_util_spdy3.cc b/net/spdy/spdy_test_util_spdy3.cc index cffd9eb..28f8bbf 100644 --- a/net/spdy/spdy_test_util_spdy3.cc +++ b/net/spdy/spdy_test_util_spdy3.cc @@ -504,6 +504,35 @@ SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[], arraysize(kConnectHeaders)); } +// Constructs a standard SPDY SYN_STREAM frame for a WebSocket over SPDY +// opening handshake. +SpdyFrame* ConstructSpdyWebSocket(int stream_id, + const char* path, + const char* host, + const char* origin) { + const char* const kWebSocketHeaders[] = { + ":path", + path, + ":host", + host, + ":version", + "WebSocket/13", + ":scheme", + "ws", + ":origin", + origin + }; + return ConstructSpdyControlFrame(/*extra_headers*/ NULL, + /*extra_header_count*/ 0, + /*compressed*/ false, + stream_id, + LOWEST, + SYN_STREAM, + CONTROL_FLAG_NONE, + kWebSocketHeaders, + arraysize(kWebSocketHeaders)); +} + // Constructs a standard SPDY push SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. @@ -681,6 +710,25 @@ SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], arraysize(kStandardGetHeaders)); } +// Constructs a standard SPDY SYN_REPLY packet to match the WebSocket over SPDY +// opening handshake. +// Returns a SpdyFrame. +SpdyFrame* ConstructSpdyWebSocketSynReply(int stream_id) { + static const char* const kStandardWebSocketHeaders[] = { + ":status", + "101" + }; + return ConstructSpdyControlFrame(NULL, + 0, + false, + stream_id, + LOWEST, + SYN_REPLY, + CONTROL_FLAG_NONE, + kStandardWebSocketHeaders, + arraysize(kStandardWebSocketHeaders)); +} + // Constructs a standard SPDY POST SYN packet. // |content_length| is the size of post data. // |extra_headers| are the extra header-value pairs, which typically @@ -787,6 +835,29 @@ SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data, stream_id, data, len, fin ? DATA_FLAG_FIN : DATA_FLAG_NONE); } +// Constructs a SPDY HEADERS frame for a WebSocket frame over SPDY. +SpdyFrame* ConstructSpdyWebSocketHeadersFrame(int stream_id, + const char* length, + bool fin) { + static const char* const kHeaders[] = { + ":opcode", + "1", // text frame + ":length", + length, + ":fin", + fin ? "1" : "0" + }; + return ConstructSpdyControlFrame(/*extra_headers*/ NULL, + /*extra_header_count*/ 0, + /*compression*/ false, + stream_id, + LOWEST, + HEADERS, + CONTROL_FLAG_NONE, + kHeaders, + arraysize(kHeaders)); +} + // Wraps |frame| in the payload of a data frame in stream |stream_id|. SpdyFrame* ConstructWrappedSpdyFrame( const scoped_ptr<SpdyFrame>& frame, diff --git a/net/spdy/spdy_test_util_spdy3.h b/net/spdy/spdy_test_util_spdy3.h index 6f0d388..0443ac6 100644 --- a/net/spdy/spdy_test_util_spdy3.h +++ b/net/spdy/spdy_test_util_spdy3.h @@ -123,31 +123,31 @@ int AppendToBuffer(int val, // |buffer| is the buffer we're filling in. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo& header_info, - const char* const extra_headers[], - int extra_header_count, - const char* const tail[], - int tail_header_count); + const char* const extra_headers[], + int extra_header_count, + const char* const tail[], + int tail_header_count); // Construct a generic SpdyControlFrame. SpdyFrame* ConstructSpdyControlFrame(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - SpdyControlType type, - SpdyControlFlags flags, - const char* const* kHeaders, - int kHeadersSize); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + SpdyControlType type, + SpdyControlFlags flags, + const char* const* kHeaders, + int kHeadersSize); SpdyFrame* ConstructSpdyControlFrame(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - SpdyControlType type, - SpdyControlFlags flags, - const char* const* kHeaders, - int kHeadersSize, - int associated_stream_id); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + SpdyControlType type, + SpdyControlFlags flags, + const char* const* kHeaders, + int kHeadersSize, + int associated_stream_id); // Construct an expected SPDY reply string. // |extra_headers| are the extra header-value pairs, which typically @@ -167,8 +167,7 @@ SpdyFrame* ConstructSpdySettings(const SettingsMap& settings); // Construct an expected SPDY CREDENTIAL frame. // |credential| is the credential to send. // Returns the constructed frame. The caller takes ownership of the frame. -SpdyFrame* ConstructSpdyCredential( - const SpdyCredential& credential); +SpdyFrame* ConstructSpdyCredential(const SpdyCredential& credential); // Construct a SPDY PING frame. // Returns the constructed frame. The caller takes ownership of the frame. @@ -180,13 +179,12 @@ SpdyFrame* ConstructSpdyGoAway(); // Construct a SPDY WINDOW_UPDATE frame. // Returns the constructed frame. The caller takes ownership of the frame. -SpdyFrame* ConstructSpdyWindowUpdate(SpdyStreamId, - uint32 delta_window_size); +SpdyFrame* ConstructSpdyWindowUpdate(SpdyStreamId, uint32 delta_window_size); // Construct a SPDY RST_STREAM frame. // Returns the constructed frame. The caller takes ownership of the frame. SpdyFrame* ConstructSpdyRstStream(SpdyStreamId stream_id, - SpdyStatusCodes status); + SpdyStatusCodes status); // Construct a single SPDY header entry, for validation. // |extra_headers| are the extra header-value pairs. @@ -205,19 +203,19 @@ int ConstructSpdyHeader(const char* const extra_headers[], // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const url, - bool compressed, - int stream_id, - RequestPriority request_priority); + bool compressed, + int stream_id, + RequestPriority request_priority); // Constructs a standard SPDY GET SYN packet, optionally compressed. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority); // Constructs a standard SPDY GET SYN packet, optionally compressed. // |extra_headers| are the extra header-value pairs, which typically @@ -225,52 +223,59 @@ SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], // the full url will be used instead of simply the path. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count, - bool compressed, - int stream_id, - RequestPriority request_priority, - bool direct); + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + bool direct); // Constructs a standard SPDY SYN_STREAM frame for a CONNECT request. SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[], - int extra_header_count, - int stream_id); + int extra_header_count, + int stream_id); + +// Constructs a standard SPDY SYN_STREAM frame for WebSocket over SPDY +// opening handshake. +SpdyFrame* ConstructSpdyWebSocket(int stream_id, + const char* path, + const char* host, + const char* origin); // Constructs a standard SPDY push SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id); + int extra_header_count, + int stream_id, + int associated_stream_id); SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id, - const char* url); + int extra_header_count, + int stream_id, + int associated_stream_id, + const char* url); SpdyFrame* ConstructSpdyPush(const char* const extra_headers[], - int extra_header_count, - int stream_id, - int associated_stream_id, - const char* url, - const char* status, - const char* location); + int extra_header_count, + int stream_id, + int associated_stream_id, + const char* url, + const char* status, + const char* location); SpdyFrame* ConstructSpdyPush(int stream_id, - int associated_stream_id, - const char* url); + int associated_stream_id, + const char* url); SpdyFrame* ConstructSpdyPushHeaders(int stream_id, - const char* const extra_headers[], - int extra_header_count); + const char* const extra_headers[], + int extra_header_count); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], - int extra_header_count, - int stream_id); + int extra_header_count, + int stream_id); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. // |extra_headers| are the extra header-value pairs, which typically @@ -278,6 +283,11 @@ SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], // Returns a SpdyFrame. SpdyFrame* ConstructSpdyGetSynReplyRedirect(int stream_id); +// Constructs a standard SPDY SYN_REPLY packet to match the WebSocket over SPDY +// opening handshake. +// Returns a SpdyFrame. +SpdyFrame* ConstructSpdyWebSocketSynReply(int stream_id); + // Constructs a standard SPDY SYN_REPLY packet with an Internal Server // Error status code. // Returns a SpdyFrame. @@ -285,45 +295,49 @@ SpdyFrame* ConstructSpdySynReplyError(int stream_id); // Constructs a standard SPDY SYN_REPLY packet with the specified status code. // Returns a SpdyFrame. -SpdyFrame* ConstructSpdySynReplyError( - const char* const status, - const char* const* const extra_headers, - int extra_header_count, - int stream_id); +SpdyFrame* ConstructSpdySynReplyError(const char* const status, + const char* const* const extra_headers, + int extra_header_count, + int stream_id); // Constructs a standard SPDY POST SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPost(int64 content_length, - const char* const extra_headers[], - int extra_header_count); + const char* const extra_headers[], + int extra_header_count); // Constructs a chunked transfer SPDY POST SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructChunkedSpdyPost(const char* const extra_headers[], - int extra_header_count); + int extra_header_count); // Constructs a standard SPDY SYN_REPLY packet to match the SPDY POST. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. // Returns a SpdyFrame. SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[], - int extra_header_count); + int extra_header_count); // Constructs a single SPDY data frame with the contents "hello!" SpdyFrame* ConstructSpdyBodyFrame(int stream_id, - bool fin); + bool fin); // Constructs a single SPDY data frame with the given content. SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data, - uint32 len, bool fin); + uint32 len, bool fin); + +// Constructs a SPDY HEADERS frame for a WebSocket frame over SPDY. +SpdyFrame* ConstructSpdyWebSocketHeadersFrame(int stream_id, + const char* length, + bool fin); // Wraps |frame| in the payload of a data frame in stream |stream_id|. -SpdyFrame* ConstructWrappedSpdyFrame( - const scoped_ptr<SpdyFrame>& frame, int stream_id); +SpdyFrame* ConstructWrappedSpdyFrame(const scoped_ptr<SpdyFrame>& frame, + int stream_id); // Create an async MockWrite from the given SpdyFrame. MockWrite CreateMockWrite(const SpdyFrame& req); |