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

Unified Diff: net/spdy/spdy_stream.cc

Issue 17382012: [SPDY] Refactor SpdyStream's handling of response headers (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More updates from rebase Created 7 years, 6 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_stream_test_util.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 2c2749ba13a886a24ef765a5ed67171228648232..9ee4bbef882dfe75b89c932f6b1fc4b9d6b924cf 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -4,8 +4,6 @@
#include "net/spdy/spdy_stream.h"
-#include <limits>
-
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
@@ -44,7 +42,7 @@ base::Value* NetLogSpdyStreamWindowUpdateCallback(
return dict;
}
-bool ContainsUpperAscii(const std::string& str) {
+bool ContainsUppercaseAscii(const std::string& str) {
for (std::string::const_iterator i(str.begin()); i != str.end(); ++i) {
if (*i >= 'A' && *i <= 'Z') {
return true;
@@ -89,7 +87,7 @@ SpdyStream::SpdyStream(SpdyStreamType type,
: type_(type),
weak_ptr_factory_(this),
in_do_loop_(false),
- continue_buffering_data_(true),
+ continue_buffering_data_(type_ == SPDY_PUSH_STREAM),
stream_id_(0),
path_(path),
priority_(priority),
@@ -98,14 +96,13 @@ SpdyStream::SpdyStream(SpdyStreamType type,
send_window_size_(initial_send_window_size),
recv_window_size_(initial_recv_window_size),
unacked_recv_window_bytes_(0),
- response_received_(false),
session_(session),
delegate_(NULL),
send_status_(
(type_ == SPDY_PUSH_STREAM) ?
NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND),
request_time_(base::Time::Now()),
- response_(new SpdyHeaderBlock),
+ response_headers_status_(RESPONSE_HEADERS_ARE_INCOMPLETE),
io_state_((type_ == SPDY_PUSH_STREAM) ? STATE_OPEN : STATE_NONE),
response_status_(OK),
net_log_(net_log),
@@ -130,66 +127,85 @@ void SpdyStream::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
if (type_ == SPDY_PUSH_STREAM) {
- CHECK(response_received());
+ DCHECK(continue_buffering_data_);
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SpdyStream::PushedStreamReplayData, GetWeakPtr()));
- } else {
- continue_buffering_data_ = false;
}
}
+SpdyStream::Delegate* SpdyStream::GetDelegate() {
+ return delegate_;
+}
+
void SpdyStream::PushedStreamReplayData() {
+ DCHECK_EQ(type_, SPDY_PUSH_STREAM);
DCHECK_NE(stream_id_, 0u);
-
- if (!delegate_)
- return;
+ DCHECK(continue_buffering_data_);
continue_buffering_data_ = false;
- // TODO(akalin): This call may delete this object. Figure out what
- // to do in that case.
- int rv = delegate_->OnResponseHeadersReceived(*response_, response_time_, OK);
- if (rv == ERR_INCOMPLETE_SPDY_HEADERS) {
- // We don't have complete headers. Assume we're waiting for another
- // HEADERS frame. Since we don't have headers, we had better not have
- // any pending data frames.
- if (pending_buffers_.size() != 0U) {
+ // The delegate methods called below may delete |this|, so use
+ // |weak_this| to detect that.
+ base::WeakPtr<SpdyStream> weak_this = GetWeakPtr();
+
+ CHECK(delegate_);
+ SpdyResponseHeadersStatus status =
+ delegate_->OnResponseHeadersUpdated(response_headers_);
+ if (status == RESPONSE_HEADERS_ARE_INCOMPLETE) {
+ // Since RESPONSE_HEADERS_ARE_INCOMPLETE was returned, we must not
+ // have been closed. Since we don't have complete headers, assume
+ // we're waiting for another HEADERS frame, and we had better not
+ // have any pending data frames.
+ CHECK(weak_this);
+ if (!pending_buffers_.empty()) {
LogStreamError(ERR_SPDY_PROTOCOL_ERROR,
- "HEADERS incomplete headers, but pending data frames.");
+ "Data received with incomplete headers.");
session_->CloseActiveStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR);
}
return;
}
- std::vector<SpdyBuffer*> buffers;
- pending_buffers_.release(&buffers);
- for (size_t i = 0; i < buffers.size(); ++i) {
- // It is always possible that a callback to the delegate results in
- // the delegate no longer being available.
- if (!delegate_)
- break;
- if (buffers[i]) {
- delegate_->OnDataReceived(scoped_ptr<SpdyBuffer>(buffers[i]));
- } else {
- delegate_->OnDataReceived(scoped_ptr<SpdyBuffer>());
+ // OnResponseHeadersUpdated() may have closed |this|.
+ if (!weak_this)
+ return;
+
+ response_headers_status_ = RESPONSE_HEADERS_ARE_COMPLETE;
+
+ while (!pending_buffers_.empty()) {
+ // Take ownership of the first element of |pending_buffers_|.
+ scoped_ptr<SpdyBuffer> buffer(pending_buffers_.front());
+ pending_buffers_.weak_erase(pending_buffers_.begin());
+
+ bool eof = (buffer == NULL);
+
+ CHECK(delegate_);
+ delegate_->OnDataReceived(buffer.Pass());
+
+ // OnDataReceived() may have closed |this|.
+ if (!weak_this)
+ return;
+
+ if (eof) {
+ DCHECK(pending_buffers_.empty());
session_->CloseActiveStream(stream_id_, OK);
- // Note: |this| may be deleted after calling CloseActiveStream.
- DCHECK_EQ(buffers.size() - 1, i);
+ DCHECK(!weak_this);
+ // |pending_buffers_| is invalid at this point.
+ break;
}
}
}
scoped_ptr<SpdyFrame> SpdyStream::ProduceSynStreamFrame() {
CHECK_EQ(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE);
- CHECK(request_);
+ CHECK(request_headers_);
CHECK_GT(stream_id_, 0u);
SpdyControlFlags flags =
(send_status_ == NO_MORE_DATA_TO_SEND) ?
CONTROL_FLAG_FIN : CONTROL_FLAG_NONE;
scoped_ptr<SpdyFrame> frame(session_->CreateSynStream(
- stream_id_, priority_, slot_, flags, *request_));
+ stream_id_, priority_, slot_, flags, *request_headers_));
send_time_ = base::TimeTicks::Now();
return frame.Pass();
}
@@ -373,17 +389,12 @@ void SpdyStream::SetRequestTime(base::Time t) {
request_time_ = t;
}
-int SpdyStream::OnResponseHeadersReceived(const SpdyHeaderBlock& response) {
- int rv = OK;
-
- metrics_.StartStream();
-
- // TODO(akalin): This should be handled as a protocol error.
- DCHECK(response_->empty());
- *response_ = response; // TODO(ukai): avoid copy.
-
- recv_first_byte_time_ = base::TimeTicks::Now();
- response_time_ = base::Time::Now();
+int SpdyStream::OnInitialResponseHeadersReceived(
+ const SpdyHeaderBlock& initial_response_headers,
+ base::Time response_time,
+ base::TimeTicks recv_first_byte_time) {
+ // SpdySession guarantees that this is called at most once.
+ CHECK(response_headers_.empty());
// Check to make sure that we don't receive the response headers
// before we're ready for it.
@@ -411,91 +422,36 @@ int SpdyStream::OnResponseHeadersReceived(const SpdyHeaderBlock& response) {
break;
}
- DCHECK_EQ(io_state_, STATE_OPEN);
-
- // TODO(akalin): Merge the code below with the code in OnHeaders().
-
- // Append all the headers into the response header block.
- for (SpdyHeaderBlock::const_iterator it = response.begin();
- it != response.end(); ++it) {
- // Disallow uppercase headers.
- if (ContainsUpperAscii(it->first)) {
- session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
- "Upper case characters in header: " + it->first);
- return ERR_SPDY_PROTOCOL_ERROR;
- }
- }
-
- if ((*response_).find("transfer-encoding") != (*response_).end()) {
- session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
- "Received transfer-encoding header");
- return ERR_SPDY_PROTOCOL_ERROR;
- }
+ metrics_.StartStream();
- if (delegate_) {
- // May delete this object.
- rv = delegate_->OnResponseHeadersReceived(*response_, response_time_, rv);
- }
- // If delegate_ is not yet attached, we'll call
- // OnResponseHeadersReceived after the delegate gets attached to the
- // stream.
+ DCHECK_EQ(io_state_, STATE_OPEN);
- return rv;
+ response_time_ = response_time;
+ recv_first_byte_time_ = recv_first_byte_time;
+ return MergeWithResponseHeaders(initial_response_headers);
}
-int SpdyStream::OnHeaders(const SpdyHeaderBlock& headers) {
- DCHECK(!response_->empty());
-
- // Append all the headers into the response header block.
- for (SpdyHeaderBlock::const_iterator it = headers.begin();
- it != headers.end(); ++it) {
- // Disallow duplicate headers. This is just to be conservative.
- if ((*response_).find(it->first) != (*response_).end()) {
- LogStreamError(ERR_SPDY_PROTOCOL_ERROR, "HEADERS duplicate header");
- response_status_ = ERR_SPDY_PROTOCOL_ERROR;
- return ERR_SPDY_PROTOCOL_ERROR;
- }
-
- // Disallow uppercase headers.
- if (ContainsUpperAscii(it->first)) {
- session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
- "Upper case characters in header: " + it->first);
- return ERR_SPDY_PROTOCOL_ERROR;
- }
-
- (*response_)[it->first] = it->second;
- }
-
- if ((*response_).find("transfer-encoding") != (*response_).end()) {
- session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
- "Received transfer-encoding header");
- return ERR_SPDY_PROTOCOL_ERROR;
- }
-
- int rv = OK;
- if (delegate_) {
- // May delete this object.
- rv = delegate_->OnResponseHeadersReceived(*response_, response_time_, rv);
- // ERR_INCOMPLETE_SPDY_HEADERS means that we are waiting for more
- // headers before the response header block is complete.
- if (rv == ERR_INCOMPLETE_SPDY_HEADERS)
- rv = OK;
+int SpdyStream::OnAdditionalResponseHeadersReceived(
+ const SpdyHeaderBlock& additional_response_headers) {
+ if (type_ == SPDY_REQUEST_RESPONSE_STREAM) {
+ LOG(WARNING) << "Additional headers received for request/response stream";
+ return OK;
+ } else if (type_ == SPDY_PUSH_STREAM &&
+ response_headers_status_ == RESPONSE_HEADERS_ARE_COMPLETE) {
+ LOG(WARNING) << "Additional headers received for push stream";
+ return OK;
}
- return rv;
+ return MergeWithResponseHeaders(additional_response_headers);
}
void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
DCHECK(session_->IsStreamActive(stream_id_));
- // If we don't have a response, then the SYN_REPLY did not come through.
- // We cannot pass data up to the caller unless the reply headers have been
- // received.
- if (!response_received()) {
- LogStreamError(ERR_SYN_REPLY_NOT_RECEIVED, "Didn't receive a response.");
- session_->CloseActiveStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED);
- return;
- }
+ // If we're still buffering data for a push stream, we will do the
+ // check for data received with incomplete headers in
+ // PushedStreamReplayData().
if (!delegate_ || continue_buffering_data_) {
+ DCHECK_EQ(type_, SPDY_PUSH_STREAM);
// It should be valid for this to happen in the server push case.
// We'll return received data when delegate gets attached to the stream.
if (buffer) {
@@ -509,12 +465,21 @@ void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
return;
}
+ // If we have response headers but the delegate has indicated that
+ // it's still incomplete, then that's a protocol error.
+ if (response_headers_status_ == RESPONSE_HEADERS_ARE_INCOMPLETE) {
+ LogStreamError(ERR_SPDY_PROTOCOL_ERROR,
+ "Data received with incomplete headers.");
+ session_->CloseActiveStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR);
+ return;
+ }
+
CHECK(!closed());
if (!buffer) {
metrics_.StopStream();
+ // Deletes |this|.
session_->CloseActiveStream(stream_id_, OK);
- // Note: |this| may be deleted after calling CloseActiveStream.
return;
}
@@ -531,12 +496,8 @@ void SpdyStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
recv_bytes_ += length;
recv_last_byte_time_ = base::TimeTicks::Now();
- if (delegate_->OnDataReceived(buffer.Pass()) != OK) {
- // |delegate_| rejected the data.
- LogStreamError(ERR_SPDY_PROTOCOL_ERROR, "Delegate rejected the data");
- session_->CloseActiveStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR);
- return;
- }
+ // May close |this|.
+ delegate_->OnDataReceived(buffer.Pass());
}
void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type,
@@ -592,14 +553,14 @@ void SpdyStream::Close() {
}
}
-int SpdyStream::SendRequestHeaders(scoped_ptr<SpdyHeaderBlock> headers,
+int SpdyStream::SendRequestHeaders(scoped_ptr<SpdyHeaderBlock> request_headers,
SpdySendStatus send_status) {
CHECK_NE(type_, SPDY_PUSH_STREAM);
CHECK_EQ(send_status_, MORE_DATA_TO_SEND);
- CHECK(!request_);
+ CHECK(!request_headers_);
CHECK(!pending_send_data_.get());
CHECK_EQ(io_state_, STATE_NONE);
- request_ = headers.Pass();
+ request_headers_ = request_headers.Pass();
send_status_ = send_status;
io_state_ = STATE_GET_DOMAIN_BOUND_CERT;
return DoLoop(OK);
@@ -645,26 +606,25 @@ base::WeakPtr<SpdyStream> SpdyStream::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
-bool SpdyStream::HasUrl() const {
- if (type_ == SPDY_PUSH_STREAM)
- return response_received();
- return request_ != NULL;
+bool SpdyStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
+ if (stream_id_ == 0)
+ return false;
+
+ return session_->GetLoadTimingInfo(stream_id_, load_timing_info);
}
GURL SpdyStream::GetUrl() const {
- DCHECK(HasUrl());
+ if (type_ != SPDY_PUSH_STREAM && !request_headers_)
+ return GURL();
const SpdyHeaderBlock& headers =
- (type_ == SPDY_PUSH_STREAM) ? *response_ : *request_;
+ (type_ == SPDY_PUSH_STREAM) ? response_headers_ : *request_headers_;
return GetUrlFromHeaderBlock(headers, GetProtocolVersion(),
type_ == SPDY_PUSH_STREAM);
}
-bool SpdyStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
- if (stream_id_ == 0)
- return false;
-
- return session_->GetLoadTimingInfo(stream_id_, load_timing_info);
+bool SpdyStream::HasUrl() const {
+ return !GetUrl().is_empty();
}
void SpdyStream::OnGetDomainBoundCertComplete(int result) {
@@ -734,7 +694,7 @@ int SpdyStream::DoLoop(int result) {
}
int SpdyStream::DoGetDomainBoundCert() {
- CHECK(request_);
+ CHECK(request_headers_);
DCHECK_NE(type_, SPDY_PUSH_STREAM);
GURL url = GetUrl();
if (!session_->NeedsCredentials() || !url.SchemeIs("https")) {
@@ -774,7 +734,7 @@ int SpdyStream::DoGetDomainBoundCertComplete(int result) {
}
int SpdyStream::DoSendDomainBoundCert() {
- CHECK(request_);
+ CHECK(request_headers_);
DCHECK_NE(type_, SPDY_PUSH_STREAM);
io_state_ = STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE;
@@ -866,14 +826,12 @@ int SpdyStream::DoSendRequestHeadersComplete() {
io_state_ = STATE_OPEN;
- // Do this before calling into the |delegate_| as that call may
- // delete us.
- int result = GetOpenStateResult(type_, send_status_);
-
CHECK(delegate_);
+ // Must not close |this|; if it does, it will trigger the |in_do_loop_|
+ // check in the destructor.
delegate_->OnRequestHeadersSent();
- return result;
+ return GetOpenStateResult(type_, send_status_);
}
int SpdyStream::DoOpen() {
@@ -909,14 +867,12 @@ int SpdyStream::DoOpen() {
pending_send_data_ = NULL;
- // Do this before calling into the |delegate_| as that call may
- // delete us.
- int result = GetOpenStateResult(type_, send_status_);
-
CHECK(delegate_);
+ // Must not close |this|; if it does, it will trigger the
+ // |in_do_loop_| check in the destructor.
delegate_->OnDataSent();
- return result;
+ return GetOpenStateResult(type_, send_status_);
}
void SpdyStream::UpdateHistograms() {
@@ -990,4 +946,58 @@ void SpdyStream::QueueNextDataFrame() {
new SimpleBufferProducer(data_buffer.Pass())));
}
+int SpdyStream::MergeWithResponseHeaders(
+ const SpdyHeaderBlock& new_response_headers) {
+ if (new_response_headers.find("transfer-encoding") !=
+ new_response_headers.end()) {
+ session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
+ "Received transfer-encoding header");
+ return ERR_SPDY_PROTOCOL_ERROR;
+ }
+
+ for (SpdyHeaderBlock::const_iterator it = new_response_headers.begin();
+ it != new_response_headers.end(); ++it) {
+ // Disallow uppercase headers.
+ if (ContainsUppercaseAscii(it->first)) {
+ session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
+ "Upper case characters in header: " + it->first);
+ return ERR_SPDY_PROTOCOL_ERROR;
+ }
+
+ SpdyHeaderBlock::iterator it2 = response_headers_.lower_bound(it->first);
+ // Disallow duplicate headers. This is just to be conservative.
+ if (it2 != response_headers_.end() && it2->first == it->first) {
+ session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
+ "Duplicate header: " + it->first);
+ return ERR_SPDY_PROTOCOL_ERROR;
+ }
+
+ response_headers_.insert(it2, *it);
+ }
+
+ // If delegate_ is not yet attached, we'll call
+ // OnResponseHeadersUpdated() after the delegate gets attached to
+ // the stream.
+ if (delegate_) {
+ // The call to OnResponseHeadersUpdated() below may delete |this|,
+ // so use |weak_this| to detect that.
+ base::WeakPtr<SpdyStream> weak_this = GetWeakPtr();
+
+ SpdyResponseHeadersStatus status =
+ delegate_->OnResponseHeadersUpdated(response_headers_);
+ if (status == RESPONSE_HEADERS_ARE_INCOMPLETE) {
+ // Since RESPONSE_HEADERS_ARE_INCOMPLETE was returned, we must not
+ // have been closed.
+ CHECK(weak_this);
+ // Incomplete headers are OK only for push streams.
+ if (type_ != SPDY_PUSH_STREAM)
+ return ERR_INCOMPLETE_SPDY_HEADERS;
+ } else if (weak_this) {
+ response_headers_status_ = RESPONSE_HEADERS_ARE_COMPLETE;
+ }
+ }
+
+ return OK;
+}
+
} // namespace net
« no previous file with comments | « net/spdy/spdy_stream.h ('k') | net/spdy/spdy_stream_test_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698