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

Unified Diff: net/spdy/spdy_stream.cc

Issue 13009012: [SPDY] Refactor SpdySession's write queue (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix use-after-free (crbug.com/230259) Created 7 years, 8 months 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_stream.h ('k') | net/spdy/spdy_write_queue.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/spdy/spdy_stream.cc
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 809c35bb1c048b8f5c7549138e8498c9b62d7d65..70412dd28cf6907394205f225caedf6a689dbf58 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -10,6 +10,7 @@
#include "base/message_loop.h"
#include "base/stringprintf.h"
#include "base/values.h"
+#include "net/spdy/spdy_frame_producer.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_session.h"
@@ -50,6 +51,57 @@ bool ContainsUpperAscii(const std::string& str) {
} // namespace
+// A wrapper around a stream that calls into ProduceSynStreamFrame().
+class SpdyStream::SynStreamFrameProducer : public SpdyFrameProducer {
+ public:
+ SynStreamFrameProducer(const base::WeakPtr<SpdyStream>& stream)
+ : stream_(stream) {
+ DCHECK(stream_);
+ }
+
+ virtual ~SynStreamFrameProducer() {}
+
+ virtual scoped_ptr<SpdyFrame> ProduceFrame() OVERRIDE {
+ if (!stream_) {
+ NOTREACHED();
+ return scoped_ptr<SpdyFrame>();
+ }
+ DCHECK_GT(stream_->stream_id(), 0u);
+ return stream_->ProduceSynStreamFrame();
+ }
+
+ private:
+ const base::WeakPtr<SpdyStream> stream_;
+};
+
+// A wrapper around a stream that calls into ProduceHeaderFrame() with
+// a given header block.
+class SpdyStream::HeaderFrameProducer : public SpdyFrameProducer {
+ public:
+ HeaderFrameProducer(const base::WeakPtr<SpdyStream>& stream,
+ scoped_ptr<SpdyHeaderBlock> headers)
+ : stream_(stream),
+ headers_(headers.Pass()) {
+ DCHECK(stream_);
+ DCHECK(headers_);
+ }
+
+ virtual ~HeaderFrameProducer() {}
+
+ virtual scoped_ptr<SpdyFrame> ProduceFrame() OVERRIDE {
+ if (!stream_) {
+ NOTREACHED();
+ return scoped_ptr<SpdyFrame>();
+ }
+ DCHECK_GT(stream_->stream_id(), 0u);
+ return stream_->ProduceHeaderFrame(headers_.Pass());
+ }
+
+ private:
+ const base::WeakPtr<SpdyStream> stream_;
+ scoped_ptr<SpdyHeaderBlock> headers_;
+};
+
SpdyStream::SpdyStream(SpdySession* session,
const std::string& path,
RequestPriority priority,
@@ -83,99 +135,8 @@ SpdyStream::SpdyStream(SpdySession* session,
domain_bound_cert_type_(CLIENT_CERT_INVALID_TYPE) {
}
-class SpdyStream::SpdyStreamIOBufferProducer
- : public SpdySession::SpdyIOBufferProducer {
- public:
- SpdyStreamIOBufferProducer(SpdyStream* stream) : stream_(stream) {}
-
- // SpdyFrameProducer
- virtual RequestPriority GetPriority() const OVERRIDE {
- return stream_->priority();
- }
-
- virtual SpdyIOBuffer* ProduceNextBuffer(SpdySession* session) OVERRIDE {
- if (stream_->cancelled())
- return NULL;
- if (stream_->stream_id() == 0)
- SpdySession::SpdyIOBufferProducer::ActivateStream(session, stream_);
- frame_ = stream_->ProduceNextFrame();
- return frame_ == NULL ? NULL :
- SpdySession::SpdyIOBufferProducer::CreateIOBuffer(
- frame_.get(), GetPriority(), stream_);
- }
-
- private:
- scoped_refptr<SpdyStream> stream_;
- scoped_ptr<SpdyFrame> frame_;
-};
-
-void SpdyStream::SetHasWriteAvailable() {
- session_->SetStreamHasWriteAvailable(this,
- new SpdyStreamIOBufferProducer(this));
-}
-
-scoped_ptr<SpdyFrame> SpdyStream::ProduceNextFrame() {
- if (io_state_ == STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE) {
- CHECK(request_.get());
- CHECK_GT(stream_id_, 0u);
-
- std::string origin = GetUrl().GetOrigin().spec();
- DCHECK(origin[origin.length() - 1] == '/');
- origin.erase(origin.length() - 1); // Trim trailing slash.
- scoped_ptr<SpdyFrame> frame(session_->CreateCredentialFrame(
- origin, domain_bound_cert_type_, domain_bound_private_key_,
- domain_bound_cert_, priority_));
- return frame.Pass();
- } else if (io_state_ == STATE_SEND_HEADERS_COMPLETE) {
- CHECK(request_.get());
- CHECK_GT(stream_id_, 0u);
-
- SpdyControlFlags flags =
- has_upload_data_ ? CONTROL_FLAG_NONE : CONTROL_FLAG_FIN;
- scoped_ptr<SpdyFrame> frame(session_->CreateSynStream(
- stream_id_, priority_, slot_, flags, *request_));
- send_time_ = base::TimeTicks::Now();
- return frame.Pass();
- } else {
- CHECK(!cancelled());
- // We must need to write stream data.
- // 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);
- DCHECK_GT(stream_id_, 0u);
- DCHECK(!pending_frames_.empty());
-
- PendingFrame frame = pending_frames_.front();
- pending_frames_.pop_front();
-
- waiting_completions_.push_back(frame.type);
-
- if (frame.type == TYPE_DATA) {
- // Send queued data frame.
- return scoped_ptr<SpdyFrame>(frame.data_frame);
- } else {
- DCHECK(frame.type == TYPE_HEADERS);
- // Create actual HEADERS frame just in time because it depends on
- // compression context and should not be reordered after the creation.
- scoped_ptr<SpdyFrame> header_frame(session_->CreateHeadersFrame(
- stream_id_, *frame.header_block, SpdyControlFlags()));
- delete frame.header_block;
- return header_frame.Pass();
- }
- }
- NOTREACHED();
-}
-
SpdyStream::~SpdyStream() {
UpdateHistograms();
- 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;
- }
}
void SpdyStream::SetDelegate(Delegate* delegate) {
@@ -228,6 +189,35 @@ void SpdyStream::PushedStreamReplayData() {
}
}
+scoped_ptr<SpdyFrame> SpdyStream::ProduceSynStreamFrame() {
+ CHECK_EQ(io_state_, STATE_SEND_HEADERS_COMPLETE);
+ CHECK(request_.get());
+ CHECK_GT(stream_id_, 0u);
+
+ SpdyControlFlags flags =
+ has_upload_data_ ? CONTROL_FLAG_NONE : CONTROL_FLAG_FIN;
+ scoped_ptr<SpdyFrame> frame(session_->CreateSynStream(
+ stream_id_, priority_, slot_, flags, *request_));
+ send_time_ = base::TimeTicks::Now();
+ return frame.Pass();
+}
+
+scoped_ptr<SpdyFrame> SpdyStream::ProduceHeaderFrame(
+ scoped_ptr<SpdyHeaderBlock> header_block) {
+ CHECK(!cancelled());
+ // We must need to write stream data.
+ // 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);
+ DCHECK_GT(stream_id_, 0u);
+
+ // Create actual HEADERS frame just in time because it depends on
+ // compression context and should not be reordered after the creation.
+ scoped_ptr<SpdyFrame> header_frame(session_->CreateHeadersFrame(
+ stream_id_, *header_block, SpdyControlFlags()));
+ return header_frame.Pass();
+}
+
void SpdyStream::DetachDelegate() {
delegate_ = NULL;
if (!closed())
@@ -578,14 +568,13 @@ void SpdyStream::QueueHeaders(scoped_ptr<SpdyHeaderBlock> headers) {
DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE);
CHECK_GT(stream_id_, 0u);
- PendingFrame frame;
- frame.type = TYPE_HEADERS;
- // |frame.header_block| is deleted by either ProduceNextFrame() or
- // the destructor.
- frame.header_block = headers.release();
- pending_frames_.push_back(frame);
+ waiting_completions_.push_back(TYPE_HEADERS);
- SetHasWriteAvailable();
+ session_->EnqueueStreamWrite(
+ this,
+ scoped_ptr<SpdyFrameProducer>(
+ new HeaderFrameProducer(
+ weak_ptr_factory_.GetWeakPtr(), headers.Pass())));
}
void SpdyStream::QueueStreamData(IOBuffer* data,
@@ -595,20 +584,19 @@ void SpdyStream::QueueStreamData(IOBuffer* data,
// that our stream_id is correct.
DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE);
CHECK_GT(stream_id_, 0u);
+ CHECK(!cancelled());
scoped_ptr<SpdyFrame> data_frame(session_->CreateDataFrame(
stream_id_, data, length, flags));
if (!data_frame)
return;
- PendingFrame frame;
- frame.type = TYPE_DATA;
- // |frame.data_frame| is either returned by ProduceNextFrame() or
- // deleted in the destructor.
- frame.data_frame = data_frame.release();
- pending_frames_.push_back(frame);
+ waiting_completions_.push_back(TYPE_DATA);
- SetHasWriteAvailable();
+ session_->EnqueueStreamWrite(
+ this,
+ scoped_ptr<SpdyFrameProducer>(
+ new SimpleFrameProducer(data_frame.Pass())));
}
bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info,
@@ -693,7 +681,7 @@ int SpdyStream::DoLoop(int result) {
break;
// State machine 2: connection is established.
// In STATE_OPEN, OnResponseReceived has already been called.
- // OnDataReceived, OnClose and OnWriteCompelte can be called.
+ // OnDataReceived, OnClose and OnWriteComplete can be called.
// Only OnWriteComplete calls DoLoop(().
//
// For HTTP streams, no data is sent from the client while in the OPEN
@@ -763,7 +751,37 @@ int SpdyStream::DoGetDomainBoundCertComplete(int result) {
int SpdyStream::DoSendDomainBoundCert() {
io_state_ = STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE;
CHECK(request_.get());
- SetHasWriteAvailable();
+
+ std::string origin = GetUrl().GetOrigin().spec();
+ DCHECK(origin[origin.length() - 1] == '/');
+ origin.erase(origin.length() - 1); // Trim trailing slash.
+ scoped_ptr<SpdyFrame> frame;
+ int rv = session_->CreateCredentialFrame(
+ origin, domain_bound_cert_type_, domain_bound_private_key_,
+ domain_bound_cert_, priority_, &frame);
+ if (rv != OK) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ return rv;
+ }
+
+ DCHECK(frame);
+ // TODO(akalin): Fix a couple of race conditions:
+ //
+ // 1) Since this counts as a write for this stream, the stream will
+ // be activated (and hence allocated a stream ID) before this frame
+ // is sent, even though the ID should only be activated for the
+ // SYN_STREAM frame. This can be solved by signalling to the session
+ // when we're sending a SYN_STREAM frame, and have it only activate
+ // the stream then.
+ //
+ // 2) Since this is decoupled from sending the SYN_STREAM frame, it
+ // is possible that other domain-bound cert frames will clobber ours
+ // before our SYN_STREAM frame gets sent. This can be solved by
+ // immediately enqueueing the SYN_STREAM frame here and adjusting
+ // the state machine appropriately.
+ session_->EnqueueStreamWrite(
+ this,
+ scoped_ptr<SpdyFrameProducer>(new SimpleFrameProducer(frame.Pass())));
return ERR_IO_PENDING;
}
@@ -777,9 +795,12 @@ int SpdyStream::DoSendDomainBoundCertComplete(int result) {
int SpdyStream::DoSendHeaders() {
CHECK(!cancelled_);
-
- SetHasWriteAvailable();
io_state_ = STATE_SEND_HEADERS_COMPLETE;
+
+ session_->EnqueueStreamWrite(
+ this,
+ scoped_ptr<SpdyFrameProducer>(
+ new SynStreamFrameProducer(weak_ptr_factory_.GetWeakPtr())));
return ERR_IO_PENDING;
}
« no previous file with comments | « net/spdy/spdy_stream.h ('k') | net/spdy/spdy_write_queue.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698