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

Unified Diff: net/spdy/spdy_session.cc

Issue 331663007: Implement PUSH_PROMISE handling in spdy_session (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebasing. Some strange andriod compile errors in skia Created 6 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_session.h ('k') | net/spdy/spdy_stream.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/spdy/spdy_session.cc
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 8362e567b95df1654368108d8e9f8f19938d1653..7561145917e92b16a88dc23e8ab24fb0d03f10d8 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -235,6 +235,19 @@ base::Value* NetLogSpdyGoAwayCallback(SpdyStreamId last_stream_id,
return dict;
}
+base::Value* NetLogSpdyPushPromiseReceivedCallback(
+ const SpdyHeaderBlock* headers,
+ SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->Set("headers",
+ SpdyHeaderBlockToListValue(*headers, log_level).release());
+ dict->SetInteger("id", stream_id);
+ dict->SetInteger("promised_stream_id", promised_stream_id);
+ return dict;
+}
+
// Helper function to return the total size of an array of objects
// with .size() member functions.
template <typename T, size_t N> size_t GetTotalSize(const T (&arr)[N]) {
@@ -501,7 +514,8 @@ SpdySession::ActiveStreamInfo::ActiveStreamInfo()
SpdySession::ActiveStreamInfo::ActiveStreamInfo(SpdyStream* stream)
: stream(stream),
- waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {}
+ waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {
+}
SpdySession::ActiveStreamInfo::~ActiveStreamInfo() {}
@@ -543,12 +557,12 @@ SpdySession::SpdySession(
read_state_(READ_STATE_DO_READ),
write_state_(WRITE_STATE_IDLE),
error_on_close_(OK),
- max_concurrent_streams_(initial_max_concurrent_streams == 0 ?
- kInitialMaxConcurrentStreams :
- initial_max_concurrent_streams),
- max_concurrent_streams_limit_(max_concurrent_streams_limit == 0 ?
- kMaxConcurrentStreamLimit :
- max_concurrent_streams_limit),
+ max_concurrent_streams_(initial_max_concurrent_streams == 0
+ ? kInitialMaxConcurrentStreams
+ : initial_max_concurrent_streams),
+ max_concurrent_streams_limit_(max_concurrent_streams_limit == 0
+ ? kMaxConcurrentStreamLimit
+ : max_concurrent_streams_limit),
streams_initiated_count_(0),
streams_pushed_count_(0),
streams_pushed_and_claimed_count_(0),
@@ -565,9 +579,9 @@ SpdySession::SpdySession(
send_connection_header_prefix_(false),
flow_control_state_(FLOW_CONTROL_NONE),
stream_initial_send_window_size_(kSpdyStreamInitialWindowSize),
- stream_initial_recv_window_size_(stream_initial_recv_window_size == 0 ?
- kDefaultInitialRecvWindowSize :
- stream_initial_recv_window_size),
+ stream_initial_recv_window_size_(stream_initial_recv_window_size == 0
+ ? kDefaultInitialRecvWindowSize
+ : stream_initial_recv_window_size),
session_send_window_size_(0),
session_recv_window_size_(0),
session_unacked_recv_window_bytes_(0),
@@ -580,8 +594,7 @@ SpdySession::SpdySession(
protocol_(default_protocol),
connection_at_risk_of_loss_time_(
base::TimeDelta::FromSeconds(kDefaultConnectionAtRiskOfLossSeconds)),
- hung_interval_(
- base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
+ hung_interval_(base::TimeDelta::FromSeconds(kHungIntervalSeconds)),
trusted_spdy_proxy_(trusted_spdy_proxy),
time_func_(time_func),
weak_factory_(this) {
@@ -2084,6 +2097,12 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
const SpdyHeaderBlock& headers) {
CHECK(in_io_loop_);
+ if (GetProtocolVersion() >= SPDY4) {
+ DCHECK_EQ(0u, associated_stream_id);
+ OnHeaders(stream_id, fin, headers);
+ return;
+ }
+
base::Time response_time = base::Time::Now();
base::TimeTicks recv_first_byte_time = time_func_();
@@ -2095,122 +2114,15 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
stream_id, associated_stream_id));
}
- // Server-initiated streams should have even sequence numbers.
- if ((stream_id & 0x1) != 0) {
- LOG(WARNING) << "Received invalid OnSyn stream id " << stream_id;
- return;
- }
-
- if (IsStreamActive(stream_id)) {
- LOG(WARNING) << "Received OnSyn for active stream " << stream_id;
- return;
- }
-
- RequestPriority request_priority =
- ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
-
- if (availability_state_ == STATE_GOING_AWAY) {
- // TODO(akalin): This behavior isn't in the SPDY spec, although it
- // probably should be.
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM,
- "OnSyn received when going away");
- return;
- }
-
- // TODO(jgraettinger): SpdyFramer simulates OnSynStream() from HEADERS
- // frames, which don't convey associated stream ID. Disable this check
- // for now, and re-enable when PUSH_PROMISE is implemented properly.
- if (associated_stream_id == 0 && GetProtocolVersion() < SPDY4) {
- std::string description = base::StringPrintf(
- "Received invalid OnSyn associated stream id %d for stream %d",
- associated_stream_id, stream_id);
- EnqueueResetStreamFrame(stream_id, request_priority,
- RST_STREAM_REFUSED_STREAM, description);
- return;
- }
-
- streams_pushed_count_++;
-
- // TODO(mbelshe): DCHECK that this is a GET method?
-
- // Verify that the response had a URL for us.
- GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
- if (!gurl.is_valid()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Pushed stream url was invalid: " + gurl.spec());
- return;
- }
-
- // Verify we have a valid stream association.
- ActiveStreamMap::iterator associated_it =
- active_streams_.find(associated_stream_id);
- // TODO(jgraettinger): (See PUSH_PROMISE comment above).
- if (GetProtocolVersion() < SPDY4 && associated_it == active_streams_.end()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_INVALID_STREAM,
- base::StringPrintf(
- "Received OnSyn with inactive associated stream %d",
- associated_stream_id));
- return;
- }
-
- // Check that the SYN advertises the same origin as its associated stream.
- // Bypass this check if and only if this session is with a SPDY proxy that
- // is trusted explicitly via the --trusted-spdy-proxy switch.
- if (trusted_spdy_proxy_.Equals(host_port_pair())) {
- // Disallow pushing of HTTPS content.
- if (gurl.SchemeIs("https")) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected push of Cross Origin HTTPS content %d",
- associated_stream_id));
- }
- } else if (GetProtocolVersion() < SPDY4) {
- // TODO(jgraettinger): (See PUSH_PROMISE comment above).
- GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
- if (associated_url.GetOrigin() != gurl.GetOrigin()) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
- base::StringPrintf(
- "Rejected Cross Origin Push Stream %d",
- associated_stream_id));
- return;
- }
- }
+ // Split headers to simulate push promise and response.
+ SpdyHeaderBlock request_headers;
+ SpdyHeaderBlock response_headers;
+ SplitPushedHeadersToRequestAndResponse(
+ headers, GetProtocolVersion(), &request_headers, &response_headers);
- // There should not be an existing pushed stream with the same path.
- PushedStreamMap::iterator pushed_it =
- unclaimed_pushed_streams_.lower_bound(gurl);
- if (pushed_it != unclaimed_pushed_streams_.end() &&
- pushed_it->first == gurl) {
- EnqueueResetStreamFrame(
- stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
- "Received duplicate pushed stream with url: " +
- gurl.spec());
+ if (!TryCreatePushStream(
+ stream_id, associated_stream_id, priority, request_headers))
return;
- }
-
- scoped_ptr<SpdyStream> stream(
- new SpdyStream(SPDY_PUSH_STREAM, GetWeakPtr(), gurl,
- request_priority,
- stream_initial_send_window_size_,
- stream_initial_recv_window_size_,
- net_log_));
- stream->set_stream_id(stream_id);
- stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
- last_compressed_frame_len_ = 0;
-
- DeleteExpiredPushedStreams();
- PushedStreamMap::iterator inserted_pushed_it =
- unclaimed_pushed_streams_.insert(
- pushed_it,
- std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
- DCHECK(inserted_pushed_it != pushed_it);
-
- InsertActivatedStream(stream.Pass());
ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
if (active_it == active_streams_.end()) {
@@ -2218,18 +2130,6 @@ void SpdySession::OnSynStream(SpdyStreamId stream_id,
return;
}
- // Parse the headers.
-
- // Split headers to simulate push promise and response.
- SpdyHeaderBlock request_headers;
- SpdyHeaderBlock response_headers;
- SplitPushedHeadersToRequestAndResponse(
- headers, GetProtocolVersion(), &request_headers, &response_headers);
-
- if (active_it->second.stream->OnPushPromiseHeadersReceived(request_headers) !=
- OK)
- return;
-
if (OnInitialResponseHeadersReceived(response_headers,
response_time,
recv_first_byte_time,
@@ -2348,6 +2248,9 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
last_compressed_frame_len_ = 0;
+ base::Time response_time = base::Time::Now();
+ base::TimeTicks recv_first_byte_time = time_func_();
+
if (it->second.waiting_for_syn_reply) {
if (GetProtocolVersion() < SPDY4) {
const std::string& error =
@@ -2356,12 +2259,13 @@ void SpdySession::OnHeaders(SpdyStreamId stream_id,
ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
return;
}
- base::Time response_time = base::Time::Now();
- base::TimeTicks recv_first_byte_time = time_func_();
it->second.waiting_for_syn_reply = false;
ignore_result(OnInitialResponseHeadersReceived(
headers, response_time, recv_first_byte_time, stream));
+ } else if (it->second.stream->IsReservedRemote()) {
+ ignore_result(OnInitialResponseHeadersReceived(
+ headers, response_time, recv_first_byte_time, stream));
} else {
int rv = stream->OnAdditionalResponseHeadersReceived(headers);
if (rv < 0) {
@@ -2521,10 +2425,172 @@ void SpdySession::OnWindowUpdate(SpdyStreamId stream_id,
}
}
+bool SpdySession::TryCreatePushStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ const SpdyHeaderBlock& headers) {
+ // Server-initiated streams should have even sequence numbers.
+ if ((stream_id & 0x1) != 0) {
+ LOG(WARNING) << "Received invalid push stream id " << stream_id;
+ return false;
+ }
+
+ if (IsStreamActive(stream_id)) {
+ LOG(WARNING) << "Received push for active stream " << stream_id;
+ return false;
+ }
+
+ RequestPriority request_priority =
+ ConvertSpdyPriorityToRequestPriority(priority, GetProtocolVersion());
+
+ if (availability_state_ == STATE_GOING_AWAY) {
+ // TODO(akalin): This behavior isn't in the SPDY spec, although it
+ // probably should be.
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ "push stream request received when going away");
+ return false;
+ }
+
+ if (associated_stream_id == 0) {
+ // In SPDY4 0 stream id in PUSH_PROMISE frame leads to framer error and
+ // session going away. We should never get here.
+ CHECK_GT(SPDY4, GetProtocolVersion());
+ std::string description = base::StringPrintf(
+ "Received invalid associated stream id %d for pushed stream %d",
+ associated_stream_id,
+ stream_id);
+ EnqueueResetStreamFrame(
+ stream_id, request_priority, RST_STREAM_REFUSED_STREAM, description);
+ return false;
+ }
+
+ streams_pushed_count_++;
+
+ // TODO(mbelshe): DCHECK that this is a GET method?
+
+ // Verify that the response had a URL for us.
+ GURL gurl = GetUrlFromHeaderBlock(headers, GetProtocolVersion(), true);
+ if (!gurl.is_valid()) {
+ EnqueueResetStreamFrame(stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Pushed stream url was invalid: " + gurl.spec());
+ return false;
+ }
+
+ // Verify we have a valid stream association.
+ ActiveStreamMap::iterator associated_it =
+ active_streams_.find(associated_stream_id);
+ if (associated_it == active_streams_.end()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_INVALID_STREAM,
+ base::StringPrintf("Received push for inactive associated stream %d",
+ associated_stream_id));
+ return false;
+ }
+
+ // Check that the pushed stream advertises the same origin as its associated
+ // stream. Bypass this check if and only if this session is with a SPDY proxy
+ // that is trusted explicitly via the --trusted-spdy-proxy switch.
+ if (trusted_spdy_proxy_.Equals(host_port_pair())) {
+ // Disallow pushing of HTTPS content.
+ if (gurl.SchemeIs("https")) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected push of Cross Origin HTTPS content %d",
+ associated_stream_id));
+ }
+ } else {
+ GURL associated_url(associated_it->second.stream->GetUrlFromHeaders());
+ if (associated_url.GetOrigin() != gurl.GetOrigin()) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_REFUSED_STREAM,
+ base::StringPrintf("Rejected Cross Origin Push Stream %d",
+ associated_stream_id));
+ return false;
+ }
+ }
+
+ // There should not be an existing pushed stream with the same path.
+ PushedStreamMap::iterator pushed_it =
+ unclaimed_pushed_streams_.lower_bound(gurl);
+ if (pushed_it != unclaimed_pushed_streams_.end() &&
+ pushed_it->first == gurl) {
+ EnqueueResetStreamFrame(
+ stream_id,
+ request_priority,
+ RST_STREAM_PROTOCOL_ERROR,
+ "Received duplicate pushed stream with url: " + gurl.spec());
+ return false;
+ }
+
+ scoped_ptr<SpdyStream> stream(new SpdyStream(SPDY_PUSH_STREAM,
+ GetWeakPtr(),
+ gurl,
+ request_priority,
+ stream_initial_send_window_size_,
+ stream_initial_recv_window_size_,
+ net_log_));
+ stream->set_stream_id(stream_id);
+
+ // In spdy4/http2 PUSH_PROMISE arrives on associated stream.
+ if (associated_it != active_streams_.end() && GetProtocolVersion() >= SPDY4) {
+ associated_it->second.stream->IncrementRawReceivedBytes(
+ last_compressed_frame_len_);
+ } else {
+ stream->IncrementRawReceivedBytes(last_compressed_frame_len_);
+ }
+
+ last_compressed_frame_len_ = 0;
+
+ DeleteExpiredPushedStreams();
+ PushedStreamMap::iterator inserted_pushed_it =
+ unclaimed_pushed_streams_.insert(
+ pushed_it,
+ std::make_pair(gurl, PushedStreamInfo(stream_id, time_func_())));
+ DCHECK(inserted_pushed_it != pushed_it);
+
+ InsertActivatedStream(stream.Pass());
+
+ ActiveStreamMap::iterator active_it = active_streams_.find(stream_id);
+ if (active_it == active_streams_.end()) {
+ NOTREACHED();
+ return false;
+ }
+
+ active_it->second.stream->OnPushPromiseHeadersReceived(headers);
+ DCHECK(active_it->second.stream->IsReservedRemote());
+ return true;
+}
+
void SpdySession::OnPushPromise(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
const SpdyHeaderBlock& headers) {
- // TODO(akalin): Handle PUSH_PROMISE frames.
+ CHECK(in_io_loop_);
+
+ if (net_log_.IsLogging()) {
+ net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_RECV_PUSH_PROMISE,
+ base::Bind(&NetLogSpdyPushPromiseReceivedCallback,
+ &headers,
+ stream_id,
+ promised_stream_id));
+ }
+
+ // Any priority will do.
+ // TODO(baranovich): pass parent stream id priority?
+ if (!TryCreatePushStream(promised_stream_id, stream_id, 0, headers))
+ return;
+
+ base::StatsCounter push_requests("spdy.pushed_streams");
+ push_requests.Increment();
}
void SpdySession::SendStreamWindowUpdate(SpdyStreamId stream_id,
« no previous file with comments | « net/spdy/spdy_session.h ('k') | net/spdy/spdy_stream.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698