| Index: net/quic/quic_http_stream.cc
|
| diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
|
| index 88cc2d9e72faecafafd13218a5a6b3d79b71d266..398c96e05086a8aa6f9bf1550a2e3e8b383f3aeb 100644
|
| --- a/net/quic/quic_http_stream.cc
|
| +++ b/net/quic/quic_http_stream.cc
|
| @@ -6,6 +6,7 @@
|
|
|
| #include "base/callback_helpers.h"
|
| #include "base/metrics/histogram_macros.h"
|
| +#include "base/strings/string_split.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "net/base/io_buffer.h"
|
| #include "net/base/net_errors.h"
|
| @@ -13,6 +14,7 @@
|
| #include "net/http/http_util.h"
|
| #include "net/quic/quic_chromium_client_session.h"
|
| #include "net/quic/quic_chromium_client_stream.h"
|
| +#include "net/quic/quic_client_promised_info.h"
|
| #include "net/quic/quic_http_utils.h"
|
| #include "net/quic/quic_utils.h"
|
| #include "net/quic/spdy_utils.h"
|
| @@ -43,6 +45,8 @@ QuicHttpStream::QuicHttpStream(
|
| closed_stream_sent_bytes_(0),
|
| user_buffer_len_(0),
|
| quic_connection_error_(QUIC_NO_ERROR),
|
| + found_promise_(false),
|
| + push_handle_(nullptr),
|
| weak_factory_(this) {
|
| DCHECK(session_);
|
| session_->AddObserver(this);
|
| @@ -54,6 +58,55 @@ QuicHttpStream::~QuicHttpStream() {
|
| session_->RemoveObserver(this);
|
| }
|
|
|
| +bool QuicHttpStream::CheckVary(const SpdyHeaderBlock& client_request,
|
| + const SpdyHeaderBlock& promise_request,
|
| + const SpdyHeaderBlock& promise_response) {
|
| + HttpResponseInfo promise_response_info;
|
| +
|
| + HttpRequestInfo promise_request_info;
|
| + ConvertHeaderBlockToHttpRequestHeaders(promise_request,
|
| + &promise_request_info.extra_headers);
|
| + HttpRequestInfo client_request_info;
|
| + ConvertHeaderBlockToHttpRequestHeaders(client_request,
|
| + &client_request_info.extra_headers);
|
| +
|
| + if (!SpdyHeadersToHttpResponse(promise_response, HTTP2,
|
| + &promise_response_info)) {
|
| + DLOG(WARNING) << "Invalid headers";
|
| + return false;
|
| + }
|
| +
|
| + HttpVaryData vary_data;
|
| + if (!vary_data.Init(promise_request_info,
|
| + *promise_response_info.headers.get())) {
|
| + // Promise didn't contain valid vary info, so URL match was sufficient.
|
| + return true;
|
| + }
|
| + // Now compare the client request for matching.
|
| + return vary_data.MatchesRequest(client_request_info,
|
| + *promise_response_info.headers.get());
|
| +}
|
| +
|
| +void QuicHttpStream::OnRendezvousResult(QuicSpdyStream* stream) {
|
| + push_handle_ = nullptr;
|
| + if (stream) {
|
| + stream_ = static_cast<QuicChromiumClientStream*>(stream);
|
| + stream_->SetDelegate(this);
|
| + }
|
| + // callback_ should be non-null in the case of asynchronous
|
| + // rendezvous; i.e. |Try()| returned QUIC_PENDING.
|
| + if (!callback_.is_null()) {
|
| + if (stream) {
|
| + next_state_ = STATE_OPEN;
|
| + DoCallback(OK);
|
| + return;
|
| + }
|
| + // rendezvous has failed so proceed as with a non-push request.
|
| + next_state_ = STATE_REQUEST_STREAM;
|
| + OnIOComplete(OK);
|
| + }
|
| +}
|
| +
|
| int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
|
| RequestPriority priority,
|
| const BoundNetLog& stream_net_log,
|
| @@ -76,29 +129,105 @@ int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
|
| DCHECK(success);
|
| DCHECK(ssl_info_.cert.get());
|
|
|
| + if (session_->push_promise_index()->GetPromised(request_info->url.spec())) {
|
| + // A PUSH_PROMISE frame for this URL has arrived, next steps of
|
| + // the rendezvous will happen in |SendRequest()| when the browser
|
| + // request headers (required for push validation) are available.
|
| + found_promise_ = true;
|
| + return OK;
|
| + }
|
| +
|
| + next_state_ = STATE_REQUEST_STREAM;
|
| + int rv = DoLoop(OK);
|
| + if (rv == ERR_IO_PENDING)
|
| + callback_ = callback;
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +int QuicHttpStream::DoStreamRequest() {
|
| int rv = stream_request_.StartRequest(
|
| session_, &stream_,
|
| base::Bind(&QuicHttpStream::OnStreamReady, weak_factory_.GetWeakPtr()));
|
| - if (rv == ERR_IO_PENDING) {
|
| - callback_ = callback;
|
| - } else if (rv == OK) {
|
| + if (rv == OK) {
|
| stream_->SetDelegate(this);
|
| - } else if (!was_handshake_confirmed_) {
|
| + if (response_info_) {
|
| + next_state_ = STATE_SET_REQUEST_PRIORITY;
|
| + }
|
| + } else if (rv != ERR_IO_PENDING && !was_handshake_confirmed_) {
|
| rv = ERR_QUIC_HANDSHAKE_FAILED;
|
| }
|
| -
|
| return rv;
|
| }
|
|
|
| +int QuicHttpStream::DoSetRequestPriority() {
|
| + // Set priority according to request and, and advance to
|
| + // STATE_SEND_HEADERS.
|
| + DCHECK(stream_);
|
| + DCHECK(response_info_);
|
| + SpdyPriority priority = ConvertRequestPriorityToQuicPriority(priority_);
|
| + stream_->SetPriority(priority);
|
| + next_state_ = STATE_SEND_HEADERS;
|
| + return OK;
|
| +}
|
| +
|
| void QuicHttpStream::OnStreamReady(int rv) {
|
| DCHECK(rv == OK || !stream_);
|
| if (rv == OK) {
|
| stream_->SetDelegate(this);
|
| + if (response_info_) {
|
| + // This happens in the case of a asynchronous push rendezvous
|
| + // that ultimately fails (e.g. vary failure). |response_info_|
|
| + // non-null implies that |DoStreamRequest()| was called via
|
| + // |SendRequest()|.
|
| + next_state_ = STATE_SET_REQUEST_PRIORITY;
|
| + rv = DoLoop(OK);
|
| + }
|
| } else if (!was_handshake_confirmed_) {
|
| rv = ERR_QUIC_HANDSHAKE_FAILED;
|
| }
|
| + if (rv != ERR_IO_PENDING) {
|
| + DoCallback(rv);
|
| + }
|
| +}
|
|
|
| - base::ResetAndReturn(&callback_).Run(rv);
|
| +bool QuicHttpStream::CancelPromiseIfHasBody() {
|
| + if (!request_body_stream_)
|
| + return false;
|
| + // Method type or request with body ineligble for push.
|
| + this->push_handle_->Cancel();
|
| + this->push_handle_ = nullptr;
|
| + next_state_ = STATE_REQUEST_STREAM;
|
| + return true;
|
| +}
|
| +
|
| +int QuicHttpStream::HandlePromise() {
|
| + QuicAsyncStatus push_status = session_->push_promise_index()->Try(
|
| + request_headers_, this, &this->push_handle_);
|
| +
|
| + switch (push_status) {
|
| + case QUIC_FAILURE:
|
| + // Push rendezvous failed.
|
| + next_state_ = STATE_REQUEST_STREAM;
|
| + break;
|
| + case QUIC_SUCCESS:
|
| + next_state_ = STATE_OPEN;
|
| + if (!CancelPromiseIfHasBody()) {
|
| + // Avoid the call to |DoLoop()| below, which would reset
|
| + // next_state_ to STATE_NONE.
|
| + return OK;
|
| + }
|
| + break;
|
| + case QUIC_PENDING:
|
| + if (!CancelPromiseIfHasBody()) {
|
| + // Have a promise but the promised stream doesn't exist yet.
|
| + // Still have to do validation before accepting the promised
|
| + // stream for sure.
|
| + return ERR_IO_PENDING;
|
| + }
|
| + break;
|
| + }
|
| + return DoLoop(OK);
|
| }
|
|
|
| int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
|
| @@ -117,12 +246,11 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
|
| UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.CookieSentToAccountsOverChannelId",
|
| ssl_info_.channel_id_sent);
|
| }
|
| - if (!stream_) {
|
| - return ERR_CONNECTION_CLOSED;
|
| + if ((!found_promise_ && !stream_) || !session_) {
|
| + return was_handshake_confirmed_ ? ERR_CONNECTION_CLOSED
|
| + : ERR_QUIC_HANDSHAKE_FAILED;
|
| }
|
|
|
| - SpdyPriority priority = ConvertRequestPriorityToQuicPriority(priority_);
|
| - stream_->SetPriority(priority);
|
| // Store the serialized request headers.
|
| CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, HTTP2,
|
| /*direct=*/true, &request_headers_);
|
| @@ -146,8 +274,15 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
|
| // Store the response info.
|
| response_info_ = response;
|
|
|
| - next_state_ = STATE_SEND_HEADERS;
|
| - int rv = DoLoop(OK);
|
| + int rv;
|
| +
|
| + if (found_promise_) {
|
| + rv = HandlePromise();
|
| + } else {
|
| + next_state_ = STATE_SET_REQUEST_PRIORITY;
|
| + rv = DoLoop(OK);
|
| + }
|
| +
|
| if (rv == ERR_IO_PENDING)
|
| callback_ = callback;
|
|
|
| @@ -390,6 +525,12 @@ int QuicHttpStream::DoLoop(int rv) {
|
| State state = next_state_;
|
| next_state_ = STATE_NONE;
|
| switch (state) {
|
| + case STATE_REQUEST_STREAM:
|
| + rv = DoStreamRequest();
|
| + break;
|
| + case STATE_SET_REQUEST_PRIORITY:
|
| + rv = DoSetRequestPriority();
|
| + break;
|
| case STATE_SEND_HEADERS:
|
| CHECK_EQ(OK, rv);
|
| rv = DoSendHeaders();
|
| @@ -585,6 +726,10 @@ void QuicHttpStream::ResetStream() {
|
| // |stream_| is going away when Read is called. Should never happen??
|
| CHECK(false);
|
| }
|
| + if (push_handle_) {
|
| + push_handle_->Cancel();
|
| + push_handle_ = nullptr;
|
| + }
|
| if (!stream_)
|
| return;
|
| closed_stream_received_bytes_ = stream_->stream_bytes_read();
|
|
|