diff options
author | erikchen@google.com <erikchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 18:47:49 +0000 |
---|---|---|
committer | erikchen@google.com <erikchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 18:47:49 +0000 |
commit | 52a69186b43c61c0f3c96abd583dc9a9e3cf447d (patch) | |
tree | 0ea5ffe8c030e3532af8b2faae98c8361c7287a7 /net/spdy | |
parent | 2535e637b7da1d6f530bf8f0a6c482c703d2c1f7 (diff) | |
download | chromium_src-52a69186b43c61c0f3c96abd583dc9a9e3cf447d.zip chromium_src-52a69186b43c61c0f3c96abd583dc9a9e3cf447d.tar.gz chromium_src-52a69186b43c61c0f3c96abd583dc9a9e3cf447d.tar.bz2 |
Streams send a Rst frame upon being closed by client. Some minor editorial fixes.
TEST=net_unittests
BUG=46589
Review URL: http://codereview.chromium.org/2804008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51007 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy')
-rw-r--r-- | net/spdy/spdy_network_transaction.h | 3 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 164 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 2 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 72 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 17 | ||||
-rw-r--r-- | net/spdy/spdy_stream.cc | 23 | ||||
-rw-r--r-- | net/spdy/spdy_stream.h | 21 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.h | 7 |
8 files changed, 267 insertions, 42 deletions
diff --git a/net/spdy/spdy_network_transaction.h b/net/spdy/spdy_network_transaction.h index 12b055c..ac9df0c 100644 --- a/net/spdy/spdy_network_transaction.h +++ b/net/spdy/spdy_network_transaction.h @@ -52,6 +52,9 @@ class SpdyNetworkTransaction : public HttpTransaction { virtual LoadState GetLoadState() const; virtual uint64 GetUploadProgress() const; + // Provide access to the stream for testing. + friend class SpdyNetworkTransactionTest; + private: enum State { STATE_INIT_CONNECTION, diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index e912b78..120d692 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -507,6 +507,10 @@ class SpdyNetworkTransactionTest : public PlatformTest { return google_get_request_; } + SpdyStream* GetSpdyStream(SpdyNetworkTransaction* trans) { + return trans->stream_->stream(); + } + private: bool google_get_request_initialized_; HttpRequestInfo google_get_request_; @@ -681,6 +685,73 @@ TEST_F(SpdyNetworkTransactionTest, ResponseWithoutSynReply) { EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv); } +class CloseStreamCallback : public CallbackRunner< Tuple1<int> > { + public: + explicit CloseStreamCallback(SpdyStream* stream) + : stream_(stream) {} + + // This callback should occur immediately after the SpdyStream has been + // closed. We check that the stream has been marked half closed from the + // client side. + virtual void RunWithParams(const Tuple1<int>& params) { + EXPECT_TRUE(stream_->half_closed_client_side()); + } + + private: + const SpdyStream* stream_; +}; + +// Test that spdy_session marks a stream as half closed immediately upon closing +// the stream. +TEST_F(SpdyNetworkTransactionTest, StreamHalfClosedClientSide) { + MockWrite writes[] = { + MockWrite(true, reinterpret_cast<const char*>(kGetSyn), + arraysize(kGetSyn), 1), + }; + + MockRead reads[] = { + MockRead(true, reinterpret_cast<const char*>(kGetSynReply), + arraysize(kGetSynReply), 2), + MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), + arraysize(kGetBodyFrame), 3), + MockRead(true, 0, 0, 4), // EOF + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + // We disable SSL for this test. + SpdySession::SetSSLMode(false); + + SessionDependencies session_deps; + scoped_ptr<SpdyNetworkTransaction> trans( + new SpdyNetworkTransaction(CreateSession(&session_deps))); + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + session_deps.socket_factory.AddSocketDataProvider(data); + + // Start the transaction with basic parameters. + TestCompletionCallback callback; + int rv = trans->Start(&request, &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + + SpdyStream* stream = GetSpdyStream(trans.get()); + + // Setup a user callback which will check that the half closed flag is marked + // immediately after the stream is closed. + CloseStreamCallback callback2(stream); + const int kSize = 3000; + scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize); + rv = trans->Read(buf, kSize, &callback2); + + // Finish running rest of tasks. + MessageLoop::current()->RunAllPending(); +} + // Test that the transaction doesn't crash when we get two replies on the same // stream ID. See http://crbug.com/45639. TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { @@ -769,6 +840,97 @@ TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) { // MockClientSocketFactory) are still alive. MessageLoop::current()->RunAllPending(); } +// The client upon cancellation tries to send a RST_STREAM frame. The mock +// socket causes the TCP write to return zero. This test checks that the client +// tries to queue up the RST_STREAM frame again. +TEST_F(SpdyNetworkTransactionTest, SocketWriteReturnsZero) { + MockWrite writes[] = { + MockWrite(true, reinterpret_cast<const char*>(kGetSyn), + arraysize(kGetSyn), 1), + MockWrite(true, 0, 0, 3), + MockWrite(true, reinterpret_cast<const char*>(kGetRst), + arraysize(kGetRst), 4), + }; + + MockRead reads[] = { + MockRead(true, reinterpret_cast<const char*>(kGetSynReply), + arraysize(kGetSynReply), 2), + MockRead(true, 0, 0, 5) // EOF + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + // We disable SSL for this test. + SpdySession::SetSSLMode(false); + + SessionDependencies session_deps; + scoped_ptr<SpdyNetworkTransaction> trans( + new SpdyNetworkTransaction(CreateSession(&session_deps))); + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + trans.reset(); // Cancel the transaction. + + // Finish running rest of tasks. + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(data->at_write_eof()) << "Write count: " << data->write_count() + << ". Write index: " << data->write_index() << "."; +} + + +// Verify that the client sends a Rst Frame upon cancelling the stream. +TEST_F(SpdyNetworkTransactionTest, CancelledTransactionSendRst) { + MockWrite writes[] = { + MockWrite(true, reinterpret_cast<const char*>(kGetSyn), + arraysize(kGetSyn), 1), + MockWrite(true, reinterpret_cast<const char*>(kGetRst), + arraysize(kGetRst), 3), + }; + + MockRead reads[] = { + MockRead(true, reinterpret_cast<const char*>(kGetSynReply), + arraysize(kGetSynReply), 2), + MockRead(true, 0, 0, 4) // EOF + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + // We disable SSL for this test. + SpdySession::SetSSLMode(false); + + SessionDependencies session_deps; + scoped_ptr<SpdyNetworkTransaction> trans( + new SpdyNetworkTransaction(CreateSession(&session_deps))); + scoped_refptr<OrderedSocketData> data( + new OrderedSocketData(reads, arraysize(reads), + writes, arraysize(writes))); + session_deps.socket_factory.AddSocketDataProvider(data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback.WaitForResult(); + trans.reset(); // Cancel the transaction. + + // Finish running rest of tasks. + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(data->at_write_eof()) << "Write count: " << data->write_count() + << ". Write index: " << data->write_index() << "."; +} class DeleteSessionCallback : public CallbackRunner< Tuple1<int> > { public: @@ -813,7 +975,7 @@ TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { SpdySession::SetSSLMode(false); SessionDependencies session_deps; - SpdyNetworkTransaction * trans = + SpdyNetworkTransaction* trans = new SpdyNetworkTransaction(CreateSession(&session_deps)); scoped_refptr<OrderedSocketData> data( new OrderedSocketData(reads, arraysize(reads), diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index a7a5ab7..ee8369f 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -63,7 +63,7 @@ // +----------------------------------+ // |1|000000000000001|0000000000000011| // +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 4 +// | flags (8) | Length (24 bits) | >= 8 // +----------------------------------+ // |X| Stream-ID(31bits) | // +----------------------------------+ diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index bc9be5e..5417674 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -230,7 +230,7 @@ net::Error SpdySession::Connect(const std::string& group_name, int rv = connection_->Init(group_name, destination, priority, &connect_callback_, session_->tcp_socket_pool(), net_log_); - DCHECK(rv <= 0); + DCHECK_LE(rv, 0); // If the connect is pending, we still return ok. The APIs enqueue // work until after the connect completes asynchronously later. @@ -368,11 +368,21 @@ int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id, const int kMaxSpdyFrameChunkSize = (2 * kMss) - spdy::SpdyFrame::size(); // Find our stream - DCHECK(IsStreamActive(stream_id)); - scoped_refptr<SpdyStream> stream = active_streams_[stream_id]; - CHECK_EQ(stream->stream_id(), stream_id); - if (!stream) + ActiveStreamMap::const_iterator it = active_streams_.find(stream_id); + if (it == active_streams_.end()) { + LOG(DFATAL) << "Attempting to write to stream " << stream_id + << ", which does not exist!"; + return ERR_INVALID_SPDY_STREAM; + } + + const scoped_refptr<SpdyStream>& stream = it->second; + if (!stream) { + LOG(DFATAL) << "Attempting to write to stream " << stream_id + << ", which does not exist!"; return ERR_INVALID_SPDY_STREAM; + } + + CHECK_EQ(stream->stream_id(), stream_id); // TODO(mbelshe): Setting of the FIN is assuming that the caller will pass // all data to write in a single chunk. Is this always true? @@ -391,12 +401,20 @@ int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id, return ERR_IO_PENDING; } -void SpdySession::CloseStream(spdy::SpdyStreamId stream_id, int status) { - LOG(INFO) << "Closing stream " << stream_id << " with status " << status; - // TODO(mbelshe): We should send a RST_STREAM control frame here - // so that the server can cancel a large send. +void SpdySession::CloseStreamAndSendRst(spdy::SpdyStreamId stream_id, + int status) { + LOG(INFO) << "Closing stream " << stream_id << " with status " << status + << " and sending RST_STREAM frame."; + + DCHECK(IsStreamActive(stream_id)); + const scoped_refptr<SpdyStream>& stream = active_streams_[stream_id]; + // We send a RST_STREAM control frame here so that the server can cancel a + // large send. + scoped_ptr<spdy::SpdyRstStreamControlFrame> rst_frame( + spdy_framer_.CreateRstStream(stream_id, spdy::CANCEL)); + QueueFrame(rst_frame.get(), stream->priority(), stream); - DeleteStream(stream_id, status); + CloseStream(stream_id, status); } bool SpdySession::IsStreamActive(spdy::SpdyStreamId stream_id) const { @@ -523,7 +541,6 @@ void SpdySession::OnReadComplete(int bytes_read) { void SpdySession::OnWriteComplete(int result) { DCHECK(write_pending_); DCHECK(in_flight_write_.size()); - DCHECK_NE(result, 0); // This shouldn't happen for write. write_pending_ = false; @@ -543,15 +560,17 @@ void SpdySession::OnWriteComplete(int result) { // We only notify the stream when we've fully written the pending frame. if (!in_flight_write_.buffer()->BytesRemaining()) { if (stream) { + // If we finished writing all the data from the buffer, it should not be + // the case that we wrote nothing. + DCHECK_NE(result, 0); + // Report the number of bytes written to the caller, but exclude the // frame size overhead. NOTE: if this frame was compressed the // reported bytes written is the compressed size, not the original // size. - if (result > 0) { - result = in_flight_write_.buffer()->size(); - DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size())); - result -= static_cast<int>(spdy::SpdyFrame::size()); - } + result = in_flight_write_.buffer()->size(); + DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size())); + result -= static_cast<int>(spdy::SpdyFrame::size()); // It is possible that the stream was cancelled while we were writing // to the socket. @@ -710,11 +729,11 @@ void SpdySession::CloseAllStreams(net::Error status) { DCHECK(stream); LOG(ERROR) << "ABANDONED (stream_id=" << stream->stream_id() << "): " << stream->path(); - DeleteStream(stream->stream_id(), status); + CloseStream(stream->stream_id(), status); } // TODO(erikchen): ideally stream->OnClose() is only ever called by - // DeleteStream, but pending streams fall into their own category for now. + // CloseStream, but pending streams fall into their own category for now. PendingStreamMap::iterator it; for (it = pending_streams_.begin(); it != pending_streams_.end(); ++it) { @@ -775,7 +794,7 @@ void SpdySession::ActivateStream(SpdyStream* stream) { active_streams_[id] = stream; } -void SpdySession::DeleteStream(spdy::SpdyStreamId id, int status) { +void SpdySession::CloseStream(spdy::SpdyStreamId id, int status) { // Remove the stream from pushed_streams_ and active_streams_. ActivePushedStreamList::iterator it; for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) { @@ -794,8 +813,13 @@ void SpdySession::DeleteStream(spdy::SpdyStreamId id, int status) { // If this is an active stream, call the callback. const scoped_refptr<SpdyStream> stream(it2->second); active_streams_.erase(it2); - if (stream) + if (stream) { + // This is the only place that should set the half_closed flag on the + // stream, and it should only be done once. + DCHECK(!stream->half_closed_client_side()); + stream->HalfCloseClientSide(); stream->OnClose(status); + } } void SpdySession::RemoveFromPool() { @@ -868,7 +892,7 @@ bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers, if (rv < 0) { DCHECK_NE(rv, ERR_IO_PENDING); const spdy::SpdyStreamId stream_id = stream->stream_id(); - DeleteStream(stream_id, rv); + CloseStream(stream_id, rv); return false; } return true; @@ -1047,7 +1071,7 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { *reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame)); break; case spdy::RST_STREAM: - OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame)); + OnRst(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame)); break; case spdy::SYN_STREAM: OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame), @@ -1063,7 +1087,7 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { } } -void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame& frame) { +void SpdySession::OnRst(const spdy::SpdyRstStreamControlFrame& frame) { spdy::SpdyStreamId stream_id = frame.stream_id(); LOG(INFO) << "Spdy Fin for stream " << stream_id; @@ -1088,7 +1112,7 @@ void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame& frame) { LOG(ERROR) << "Spdy stream closed: " << frame.status(); // TODO(mbelshe): Map from Spdy-protocol errors to something sensical. // For now, it doesn't matter much - it is a protocol error. - DeleteStream(stream_id, ERR_SPDY_PROTOCOL_ERROR); + CloseStream(stream_id, ERR_SPDY_PROTOCOL_ERROR); } } diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 8a23168..9526790 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -91,8 +91,16 @@ class SpdySession : public base::RefCounted<SpdySession>, int WriteStreamData(spdy::SpdyStreamId stream_id, net::IOBuffer* data, int len); - // Close a stream. - void CloseStream(spdy::SpdyStreamId stream_id, int status); + // This marks the stream as half closed from the client side, and removes it + // from the active_streams_ map. + void CloseStream(spdy::SpdyStreamId id, int status); + // This is identical to CloseStream, except it also sends a Rst stream frame. + void CloseStreamAndSendRst(spdy::SpdyStreamId stream_id, int status); + + // Half close a stream. + void HalfCloseStreamClientSide(spdy::SpdyStreamId stream_id, int status); + void HalfCloseStreamServerSide(spdy::SpdyStreamId stream_id, int status); + // Check if a stream is active. bool IsStreamActive(spdy::SpdyStreamId stream_id) const; @@ -142,7 +150,7 @@ class SpdySession : public base::RefCounted<SpdySession>, const linked_ptr<spdy::SpdyHeaderBlock>& headers); void OnSynReply(const spdy::SpdySynReplyControlFrame& frame, const linked_ptr<spdy::SpdyHeaderBlock>& headers); - void OnFin(const spdy::SpdyRstStreamControlFrame& frame); + void OnRst(const spdy::SpdyRstStreamControlFrame& frame); void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame); void OnSettings(const spdy::SpdySettingsControlFrame& frame); @@ -181,7 +189,6 @@ class SpdySession : public base::RefCounted<SpdySession>, // Track active streams in the active stream list. void ActivateStream(SpdyStream* stream); - void DeleteStream(spdy::SpdyStreamId id, int status); // Removes this session from the session pool. void RemoveFromPool(); @@ -270,7 +277,7 @@ class SpdySession : public base::RefCounted<SpdySession>, int streams_initiated_count_; int streams_pushed_count_; int streams_pushed_and_claimed_count_; - int streams_abandoned_count_; + int streams_abandoned_count_; // # of streams that were pushed & abandoned. bool sent_settings_; // Did this session send settings when it started. bool received_settings_; // Did this session receive at least one settings // frame. diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc index 1166cd5..ea4152d 100644 --- a/net/spdy/spdy_stream.cc +++ b/net/spdy/spdy_stream.cc @@ -28,7 +28,9 @@ SpdyStream::SpdyStream( cancelled_(false), send_bytes_(0), recv_bytes_(0), - histograms_recorded_(false) {} + histograms_recorded_(false), + half_closed_client_side_(false), + half_closed_server_side_(false) {} SpdyStream::~SpdyStream() { DLOG(INFO) << "Deleting SpdyStream for stream " << stream_id_; @@ -58,8 +60,8 @@ void SpdyStream::SetDelegate(Delegate* delegate) { void SpdyStream::DetachDelegate() { delegate_ = NULL; - if (!cancelled()) - Cancel(); + if (!half_closed_client_side()) + session_->CloseStream(stream_id_, ERR_ABORTED); } const linked_ptr<spdy::SpdyHeaderBlock>& SpdyStream::spdy_headers() const { @@ -129,6 +131,8 @@ void SpdyStream::OnDataReceived(const char* data, int length) { // received. if (response_->empty()) { session_->CloseStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED); + //session_->CloseStreamAndSendRst(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED); + // TODO(erikchen): We should close the session here. return; } @@ -159,11 +163,13 @@ void SpdyStream::OnDataReceived(const char* data, int length) { } void SpdyStream::OnWriteComplete(int status) { - // TODO(mbelshe): Check for cancellation here. If we're cancelled, we - // should discontinue the DoLoop. + // Behavior for status==0 is undefined, and should never happen. This should + // have already been checked prior to calling this function. + DCHECK_NE(status, 0); - // It is possible that this stream was closed while we had a write pending. - if (response_complete_) + // It is possible that this stream was closed or cancelled while we had a + // write pending. + if (response_complete_ || cancelled_) return; if (status > 0) @@ -184,7 +190,8 @@ void SpdyStream::OnClose(int status) { void SpdyStream::Cancel() { cancelled_ = true; - session_->CloseStream(stream_id_, ERR_ABORTED); + if (!half_closed_client_side()) + session_->CloseStreamAndSendRst(stream_id_, ERR_ABORTED); } int SpdyStream::DoSendRequest(bool has_upload_data) { diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h index 6183be2..88b3e04 100644 --- a/net/spdy/spdy_stream.h +++ b/net/spdy/spdy_stream.h @@ -109,8 +109,7 @@ class SpdyStream : public base::RefCounted<SpdyStream> { void SetRequestTime(base::Time t); // Called by the SpdySession when a response (e.g. a SYN_REPLY) has been - // received for this stream. |path| is the path of the URL for a server - // initiated stream, otherwise is empty. + // received for this stream. // Returns a status code. int OnResponseReceived(const spdy::SpdyHeaderBlock& response); @@ -120,7 +119,7 @@ class SpdyStream : public base::RefCounted<SpdyStream> { // |buffer| contains the data received. The stream must copy any data // from this buffer before returning from this callback. // |length| is the number of bytes received or an error. - // A zero-length count does not indicate end-of-stream. + // A length of zero indicates end-of-stream. void OnDataReceived(const char* buffer, int bytes); // Called by the SpdySession when a write has completed. This callback @@ -160,6 +159,20 @@ class SpdyStream : public base::RefCounted<SpdyStream> { bool response_complete() const { return response_complete_; } int response_status() const { return response_status_; } + // If this function returns true, then the spdy_session is guaranteeing that + // this spdy_stream has been removed from active_streams. + bool half_closed_client_side() const { return half_closed_client_side_; } + + bool half_closed_server_side() const { return half_closed_server_side_; } + bool half_closed_both_sides() const { + return half_closed_client_side_ && half_closed_server_side_; + } + + // These two functions should only ever be called by spdy_session, and should + // only be called once. + void HalfCloseClientSide() { half_closed_client_side_ = true; } + void HalfCloseServerSide() { half_closed_server_side_ = true; } + private: enum State { STATE_NONE, @@ -235,6 +248,8 @@ class SpdyStream : public base::RefCounted<SpdyStream> { bool histograms_recorded_; // Data received before delegate is attached. std::vector<scoped_refptr<IOBufferWithSize> > pending_buffers_; + bool half_closed_client_side_; + bool half_closed_server_side_; DISALLOW_COPY_AND_ASSIGN(SpdyStream); }; diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h index 830a290..2c9aea8 100644 --- a/net/spdy/spdy_test_util.h +++ b/net/spdy/spdy_test_util.h @@ -40,6 +40,13 @@ const uint8 kGetSynReply[] = { 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" }; +const uint8 kGetRst[] = { + 0x80, 0x01, 0x00, 0x03, // header + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, // 4 headers +}; + const uint8 kGetBodyFrame[] = { 0x00, 0x00, 0x00, 0x01, // header 0x01, 0x00, 0x00, 0x06, // FIN, length |