blob: 6af35d532da2b83a29a4b1120f0094a101e53920 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright (c) 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
6
vasilvv872e7a32019-03-12 16:42:44 -07007#include <string>
QUICHE teama6ef0a62019-03-07 20:34:33 -05008#include <utility>
9
danzh2a930462019-07-03 07:28:06 -070010#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050011#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
12#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050013#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
14
15using spdy::SpdyHeaderBlock;
16
17namespace quic {
18
19QuicClientPromisedInfo::QuicClientPromisedInfo(
20 QuicSpdyClientSessionBase* session,
21 QuicStreamId id,
vasilvvc48c8712019-03-11 13:38:16 -070022 std::string url)
QUICHE teama6ef0a62019-03-07 20:34:33 -050023 : session_(session),
24 id_(id),
25 url_(std::move(url)),
26 client_request_delegate_(nullptr) {}
27
28QuicClientPromisedInfo::~QuicClientPromisedInfo() {}
29
30void QuicClientPromisedInfo::CleanupAlarm::OnAlarm() {
31 QUIC_DVLOG(1) << "self GC alarm for stream " << promised_->id_;
32 promised_->session()->OnPushStreamTimedOut(promised_->id_);
33 promised_->Reset(QUIC_PUSH_STREAM_TIMED_OUT);
34}
35
36void QuicClientPromisedInfo::Init() {
37 cleanup_alarm_.reset(session_->connection()->alarm_factory()->CreateAlarm(
38 new QuicClientPromisedInfo::CleanupAlarm(this)));
39 cleanup_alarm_->Set(
40 session_->connection()->helper()->GetClock()->ApproximateNow() +
41 QuicTime::Delta::FromSeconds(kPushPromiseTimeoutSecs));
42}
43
44bool QuicClientPromisedInfo::OnPromiseHeaders(const SpdyHeaderBlock& headers) {
45 // RFC7540, Section 8.2, requests MUST be safe [RFC7231], Section
46 // 4.2.1. GET and HEAD are the methods that are safe and required.
47 SpdyHeaderBlock::const_iterator it = headers.find(spdy::kHttp2MethodHeader);
48 if (it == headers.end()) {
49 QUIC_DVLOG(1) << "Promise for stream " << id_ << " has no method";
50 Reset(QUIC_INVALID_PROMISE_METHOD);
51 return false;
52 }
53 if (!(it->second == "GET" || it->second == "HEAD")) {
54 QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid method "
55 << it->second;
56 Reset(QUIC_INVALID_PROMISE_METHOD);
57 return false;
58 }
danzh2a930462019-07-03 07:28:06 -070059 if (!SpdyServerPushUtils::PromisedUrlIsValid(headers)) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050060 QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid URL "
61 << url_;
62 Reset(QUIC_INVALID_PROMISE_URL);
63 return false;
64 }
65 if (!session_->IsAuthorized(
danzh2a930462019-07-03 07:28:06 -070066 SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers))) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050067 Reset(QUIC_UNAUTHORIZED_PROMISE_URL);
68 return false;
69 }
70 request_headers_ = headers.Clone();
71 return true;
72}
73
74void QuicClientPromisedInfo::OnResponseHeaders(const SpdyHeaderBlock& headers) {
75 response_headers_ = QuicMakeUnique<SpdyHeaderBlock>(headers.Clone());
76 if (client_request_delegate_) {
77 // We already have a client request waiting.
78 FinalValidation();
79 }
80}
81
82void QuicClientPromisedInfo::Reset(QuicRstStreamErrorCode error_code) {
83 QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_;
84 session_->ResetPromised(id_, error_code);
85 session_->DeletePromised(this);
86 if (delegate) {
87 delegate->OnRendezvousResult(nullptr);
88 }
89}
90
91QuicAsyncStatus QuicClientPromisedInfo::FinalValidation() {
92 if (!client_request_delegate_->CheckVary(
93 client_request_headers_, request_headers_, *response_headers_)) {
94 Reset(QUIC_PROMISE_VARY_MISMATCH);
95 return QUIC_FAILURE;
96 }
97 QuicSpdyStream* stream = session_->GetPromisedStream(id_);
98 if (!stream) {
99 // This shouldn't be possible, as |ClientRequest| guards against
100 // closed stream for the synchronous case. And in the
101 // asynchronous case, a RST can only be caught by |OnAlarm()|.
102 QUIC_BUG << "missing promised stream" << id_;
103 }
104 QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_;
105 session_->DeletePromised(this);
106 // Stream can start draining now
107 if (delegate) {
108 delegate->OnRendezvousResult(stream);
109 }
110 return QUIC_SUCCESS;
111}
112
113QuicAsyncStatus QuicClientPromisedInfo::HandleClientRequest(
114 const SpdyHeaderBlock& request_headers,
115 QuicClientPushPromiseIndex::Delegate* delegate) {
116 if (session_->IsClosedStream(id_)) {
117 // There was a RST on the response stream.
118 session_->DeletePromised(this);
119 return QUIC_FAILURE;
120 }
121
122 if (is_validating()) {
123 // The push promise has already been matched to another request though
124 // pending for validation. Returns QUIC_FAILURE to the caller as it couldn't
125 // match a new request any more. This will not affect the validation of the
126 // other request.
127 return QUIC_FAILURE;
128 }
129
130 client_request_delegate_ = delegate;
131 client_request_headers_ = request_headers.Clone();
132 if (response_headers_ == nullptr) {
133 return QUIC_PENDING;
134 }
135 return FinalValidation();
136}
137
138void QuicClientPromisedInfo::Cancel() {
139 // Don't fire OnRendezvousResult() for client initiated cancel.
140 client_request_delegate_ = nullptr;
141 Reset(QUIC_STREAM_CANCELLED);
142}
143
144} // namespace quic