blob: b9b0bd2c0f64f0acda44db1025648bc38e019872 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright (c) 2015 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/tools/quic_spdy_client_base.h"
6
7#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
8#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
9#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
10#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
11#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"
13#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
14
15using spdy::SpdyHeaderBlock;
16
17namespace quic {
18
19void QuicSpdyClientBase::ClientQuicDataToResend::Resend() {
20 client_->SendRequest(*headers_, body_, fin_);
21 headers_ = nullptr;
22}
23
24QuicSpdyClientBase::QuicDataToResend::QuicDataToResend(
25 std::unique_ptr<SpdyHeaderBlock> headers,
26 QuicStringPiece body,
27 bool fin)
28 : headers_(std::move(headers)), body_(body), fin_(fin) {}
29
30QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() = default;
31
32QuicSpdyClientBase::QuicSpdyClientBase(
33 const QuicServerId& server_id,
34 const ParsedQuicVersionVector& supported_versions,
35 const QuicConfig& config,
36 QuicConnectionHelperInterface* helper,
37 QuicAlarmFactory* alarm_factory,
38 std::unique_ptr<NetworkHelper> network_helper,
39 std::unique_ptr<ProofVerifier> proof_verifier)
40 : QuicClientBase(server_id,
41 supported_versions,
42 config,
43 helper,
44 alarm_factory,
45 std::move(network_helper),
46 std::move(proof_verifier)),
47 store_response_(false),
48 latest_response_code_(-1) {}
49
50QuicSpdyClientBase::~QuicSpdyClientBase() {
51 // We own the push promise index. We need to explicitly kill
52 // the session before the push promise index goes out of scope.
53 ResetSession();
54}
55
56QuicSpdyClientSession* QuicSpdyClientBase::client_session() {
57 return static_cast<QuicSpdyClientSession*>(QuicClientBase::session());
58}
59
60void QuicSpdyClientBase::InitializeSession() {
61 client_session()->Initialize();
62 client_session()->CryptoConnect();
63}
64
65void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
66 DCHECK(stream != nullptr);
67 QuicSpdyClientStream* client_stream =
68 static_cast<QuicSpdyClientStream*>(stream);
69
70 const SpdyHeaderBlock& response_headers = client_stream->response_headers();
71 if (response_listener_ != nullptr) {
72 response_listener_->OnCompleteResponse(stream->id(), response_headers,
73 client_stream->data());
74 }
75
76 // Store response headers and body.
77 if (store_response_) {
78 auto status = response_headers.find(":status");
79 if (status == response_headers.end() ||
80 !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) {
81 QUIC_LOG(ERROR) << "Invalid response headers";
82 }
83 latest_response_headers_ = response_headers.DebugString();
84 preliminary_response_headers_ =
85 client_stream->preliminary_headers().DebugString();
86 latest_response_header_block_ = response_headers.Clone();
87 latest_response_body_ = client_stream->data();
88 latest_response_trailers_ =
89 client_stream->received_trailers().DebugString();
90 }
91}
92
93std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession(
94 const quic::ParsedQuicVersionVector& supported_versions,
95 QuicConnection* connection) {
96 return QuicMakeUnique<QuicSpdyClientSession>(
97 *config(), supported_versions, connection, server_id(), crypto_config(),
98 &push_promise_index_);
99}
100
101void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers,
102 QuicStringPiece body,
103 bool fin) {
104 QuicClientPushPromiseIndex::TryHandle* handle;
105 QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
106 if (rv == QUIC_SUCCESS)
107 return;
108
109 if (rv == QUIC_PENDING) {
110 // May need to retry request if asynchronous rendezvous fails.
111 AddPromiseDataToResend(headers, body, fin);
112 return;
113 }
114
115 QuicSpdyClientStream* stream = CreateClientStream();
116 if (stream == nullptr) {
117 QUIC_BUG << "stream creation failed!";
118 return;
119 }
120 stream->SendRequest(headers.Clone(), body, fin);
121 // Record this in case we need to resend.
122 MaybeAddDataToResend(headers, body, fin);
123}
124
125void QuicSpdyClientBase::SendRequestAndWaitForResponse(
126 const SpdyHeaderBlock& headers,
127 QuicStringPiece body,
128 bool fin) {
129 SendRequest(headers, body, fin);
130 while (WaitForEvents()) {
131 }
132}
133
134void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
135 const std::vector<QuicString>& url_list) {
136 for (size_t i = 0; i < url_list.size(); ++i) {
137 SpdyHeaderBlock headers;
138 if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
139 QUIC_BUG << "Unable to create request";
140 continue;
141 }
142 SendRequest(headers, "", true);
143 }
144 while (WaitForEvents()) {
145 }
146}
147
148QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() {
149 if (!connected()) {
150 return nullptr;
151 }
152
153 auto* stream = static_cast<QuicSpdyClientStream*>(
154 client_session()->CreateOutgoingBidirectionalStream());
155 if (stream) {
156 stream->SetPriority(QuicStream::kDefaultPriority);
157 stream->set_visitor(this);
158 }
159 return stream;
160}
161
162int QuicSpdyClientBase::GetNumSentClientHellosFromSession() {
163 return client_session()->GetNumSentClientHellos();
164}
165
166int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() {
167 return client_session()->GetNumReceivedServerConfigUpdates();
168}
169
170void QuicSpdyClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers,
171 QuicStringPiece body,
172 bool fin) {
173 if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
174 return;
175 }
176
177 if (client_session()->IsCryptoHandshakeConfirmed()) {
178 // The handshake is confirmed. No need to continue saving requests to
179 // resend.
180 data_to_resend_on_connect_.clear();
181 return;
182 }
183
184 // The handshake is not confirmed. Push the data onto the queue of data to
185 // resend if statelessly rejected.
186 std::unique_ptr<SpdyHeaderBlock> new_headers(
187 new SpdyHeaderBlock(headers.Clone()));
188 std::unique_ptr<QuicDataToResend> data_to_resend(
189 new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
190 MaybeAddQuicDataToResend(std::move(data_to_resend));
191}
192
193void QuicSpdyClientBase::MaybeAddQuicDataToResend(
194 std::unique_ptr<QuicDataToResend> data_to_resend) {
195 data_to_resend_on_connect_.push_back(std::move(data_to_resend));
196}
197
198void QuicSpdyClientBase::ClearDataToResend() {
199 data_to_resend_on_connect_.clear();
200}
201
202void QuicSpdyClientBase::ResendSavedData() {
203 // Calling Resend will re-enqueue the data, so swap out
204 // data_to_resend_on_connect_ before iterating.
205 std::vector<std::unique_ptr<QuicDataToResend>> old_data;
206 old_data.swap(data_to_resend_on_connect_);
207 for (const auto& data : old_data) {
208 data->Resend();
209 }
210}
211
212void QuicSpdyClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers,
213 QuicStringPiece body,
214 bool fin) {
215 std::unique_ptr<SpdyHeaderBlock> new_headers(
216 new SpdyHeaderBlock(headers.Clone()));
217 push_promise_data_to_resend_.reset(
218 new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
219}
220
221bool QuicSpdyClientBase::CheckVary(const SpdyHeaderBlock& client_request,
222 const SpdyHeaderBlock& promise_request,
223 const SpdyHeaderBlock& promise_response) {
224 return true;
225}
226
227void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
228 std::unique_ptr<ClientQuicDataToResend> data_to_resend =
229 std::move(push_promise_data_to_resend_);
230 if (stream) {
231 stream->set_visitor(this);
232 stream->OnBodyAvailable();
233 } else if (data_to_resend) {
234 data_to_resend->Resend();
235 }
236}
237
238size_t QuicSpdyClientBase::latest_response_code() const {
239 QUIC_BUG_IF(!store_response_) << "Response not stored!";
240 return latest_response_code_;
241}
242
243const QuicString& QuicSpdyClientBase::latest_response_headers() const {
244 QUIC_BUG_IF(!store_response_) << "Response not stored!";
245 return latest_response_headers_;
246}
247
248const QuicString& QuicSpdyClientBase::preliminary_response_headers() const {
249 QUIC_BUG_IF(!store_response_) << "Response not stored!";
250 return preliminary_response_headers_;
251}
252
253const SpdyHeaderBlock& QuicSpdyClientBase::latest_response_header_block()
254 const {
255 QUIC_BUG_IF(!store_response_) << "Response not stored!";
256 return latest_response_header_block_;
257}
258
259const QuicString& QuicSpdyClientBase::latest_response_body() const {
260 QUIC_BUG_IF(!store_response_) << "Response not stored!";
261 return latest_response_body_;
262}
263
264const QuicString& QuicSpdyClientBase::latest_response_trailers() const {
265 QUIC_BUG_IF(!store_response_) << "Response not stored!";
266 return latest_response_trailers_;
267}
268
QUICHE teamc2653c42019-03-08 13:30:06 -0800269bool QuicSpdyClientBase::HasActiveRequests() {
270 return client_session()->HasActiveRequestStreams();
271}
272
QUICHE teama6ef0a62019-03-07 20:34:33 -0500273} // namespace quic