1 // 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 <utility>
8
9 #include "absl/strings/numbers.h"
10 #include "absl/strings/string_view.h"
11 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
12 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
13 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
14 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
15 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
16 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
17
18 using spdy::Http2HeaderBlock;
19
20 namespace quic {
21
Resend()22 void QuicSpdyClientBase::ClientQuicDataToResend::Resend() {
23 client_->SendRequest(*headers_, body_, fin_);
24 headers_ = nullptr;
25 }
26
QuicDataToResend(std::unique_ptr<Http2HeaderBlock> headers,absl::string_view body,bool fin)27 QuicSpdyClientBase::QuicDataToResend::QuicDataToResend(
28 std::unique_ptr<Http2HeaderBlock> headers,
29 absl::string_view body,
30 bool fin)
31 : headers_(std::move(headers)), body_(body), fin_(fin) {}
32
33 QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() = default;
34
QuicSpdyClientBase(const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,const QuicConfig & config,QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<NetworkHelper> network_helper,std::unique_ptr<ProofVerifier> proof_verifier,std::unique_ptr<SessionCache> session_cache)35 QuicSpdyClientBase::QuicSpdyClientBase(
36 const QuicServerId& server_id,
37 const ParsedQuicVersionVector& supported_versions,
38 const QuicConfig& config,
39 QuicConnectionHelperInterface* helper,
40 QuicAlarmFactory* alarm_factory,
41 std::unique_ptr<NetworkHelper> network_helper,
42 std::unique_ptr<ProofVerifier> proof_verifier,
43 std::unique_ptr<SessionCache> session_cache)
44 : QuicClientBase(server_id,
45 supported_versions,
46 config,
47 helper,
48 alarm_factory,
49 std::move(network_helper),
50 std::move(proof_verifier),
51 std::move(session_cache)),
52 store_response_(false),
53 latest_response_code_(-1),
54 max_allowed_push_id_(0) {}
55
~QuicSpdyClientBase()56 QuicSpdyClientBase::~QuicSpdyClientBase() {
57 // We own the push promise index. We need to explicitly kill
58 // the session before the push promise index goes out of scope.
59 ResetSession();
60 }
61
client_session()62 QuicSpdyClientSession* QuicSpdyClientBase::client_session() {
63 return static_cast<QuicSpdyClientSession*>(QuicClientBase::session());
64 }
65
client_session() const66 const QuicSpdyClientSession* QuicSpdyClientBase::client_session() const {
67 return static_cast<const QuicSpdyClientSession*>(QuicClientBase::session());
68 }
69
InitializeSession()70 void QuicSpdyClientBase::InitializeSession() {
71 client_session()->Initialize();
72 client_session()->CryptoConnect();
73 if (max_allowed_push_id_ > 0 &&
74 VersionUsesHttp3(client_session()->transport_version())) {
75 client_session()->SetMaxPushId(max_allowed_push_id_);
76 }
77 }
78
OnClose(QuicSpdyStream * stream)79 void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
80 DCHECK(stream != nullptr);
81 QuicSpdyClientStream* client_stream =
82 static_cast<QuicSpdyClientStream*>(stream);
83
84 const Http2HeaderBlock& response_headers = client_stream->response_headers();
85 if (response_listener_ != nullptr) {
86 response_listener_->OnCompleteResponse(stream->id(), response_headers,
87 client_stream->data());
88 }
89
90 // Store response headers and body.
91 if (store_response_) {
92 auto status = response_headers.find(":status");
93 if (status == response_headers.end()) {
94 QUIC_LOG(ERROR) << "Missing :status response header";
95 } else if (!absl::SimpleAtoi(status->second, &latest_response_code_)) {
96 QUIC_LOG(ERROR) << "Invalid :status response header: " << status->second;
97 }
98 latest_response_headers_ = response_headers.DebugString();
99 preliminary_response_headers_ =
100 client_stream->preliminary_headers().DebugString();
101 latest_response_header_block_ = response_headers.Clone();
102 latest_response_body_ = client_stream->data();
103 latest_response_trailers_ =
104 client_stream->received_trailers().DebugString();
105 }
106 }
107
CreateQuicClientSession(const quic::ParsedQuicVersionVector & supported_versions,QuicConnection * connection)108 std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession(
109 const quic::ParsedQuicVersionVector& supported_versions,
110 QuicConnection* connection) {
111 return std::make_unique<QuicSpdyClientSession>(
112 *config(), supported_versions, connection, server_id(), crypto_config(),
113 &push_promise_index_);
114 }
115
SendRequest(const Http2HeaderBlock & headers,absl::string_view body,bool fin)116 void QuicSpdyClientBase::SendRequest(const Http2HeaderBlock& headers,
117 absl::string_view body,
118 bool fin) {
119 if (GetQuicFlag(FLAGS_quic_client_convert_http_header_name_to_lowercase)) {
120 QUIC_CODE_COUNT(quic_client_convert_http_header_name_to_lowercase);
121 Http2HeaderBlock sanitized_headers;
122 for (const auto& p : headers) {
123 sanitized_headers[quiche::QuicheTextUtils::ToLower(p.first)] = p.second;
124 }
125
126 SendRequestInternal(std::move(sanitized_headers), body, fin);
127 } else {
128 SendRequestInternal(headers.Clone(), body, fin);
129 }
130 }
131
SendRequestInternal(Http2HeaderBlock sanitized_headers,absl::string_view body,bool fin)132 void QuicSpdyClientBase::SendRequestInternal(Http2HeaderBlock sanitized_headers,
133 absl::string_view body,
134 bool fin) {
135 QuicClientPushPromiseIndex::TryHandle* handle;
136 QuicAsyncStatus rv =
137 push_promise_index()->Try(sanitized_headers, this, &handle);
138 if (rv == QUIC_SUCCESS)
139 return;
140
141 if (rv == QUIC_PENDING) {
142 // May need to retry request if asynchronous rendezvous fails.
143 AddPromiseDataToResend(sanitized_headers, body, fin);
144 return;
145 }
146
147 QuicSpdyClientStream* stream = CreateClientStream();
148 if (stream == nullptr) {
149 QUIC_BUG << "stream creation failed!";
150 return;
151 }
152 stream->SendRequest(std::move(sanitized_headers), body, fin);
153 }
154
SendRequestAndWaitForResponse(const Http2HeaderBlock & headers,absl::string_view body,bool fin)155 void QuicSpdyClientBase::SendRequestAndWaitForResponse(
156 const Http2HeaderBlock& headers,
157 absl::string_view body,
158 bool fin) {
159 SendRequest(headers, body, fin);
160 while (WaitForEvents()) {
161 }
162 }
163
SendRequestsAndWaitForResponse(const std::vector<std::string> & url_list)164 void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
165 const std::vector<std::string>& url_list) {
166 for (size_t i = 0; i < url_list.size(); ++i) {
167 Http2HeaderBlock headers;
168 if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
169 QUIC_BUG << "Unable to create request";
170 continue;
171 }
172 SendRequest(headers, "", true);
173 }
174 while (WaitForEvents()) {
175 }
176 }
177
CreateClientStream()178 QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() {
179 if (!connected()) {
180 return nullptr;
181 }
182 if (VersionHasIetfQuicFrames(client_session()->transport_version())) {
183 // Process MAX_STREAMS from peer or wait for liveness testing succeeds.
184 while (!client_session()->CanOpenNextOutgoingBidirectionalStream()) {
185 network_helper()->RunEventLoop();
186 }
187 }
188 auto* stream = static_cast<QuicSpdyClientStream*>(
189 client_session()->CreateOutgoingBidirectionalStream());
190 if (stream) {
191 stream->set_visitor(this);
192 }
193 return stream;
194 }
195
goaway_received() const196 bool QuicSpdyClientBase::goaway_received() const {
197 return client_session() && client_session()->goaway_received();
198 }
199
EarlyDataAccepted()200 bool QuicSpdyClientBase::EarlyDataAccepted() {
201 return client_session()->EarlyDataAccepted();
202 }
203
ReceivedInchoateReject()204 bool QuicSpdyClientBase::ReceivedInchoateReject() {
205 return client_session()->ReceivedInchoateReject();
206 }
207
GetNumSentClientHellosFromSession()208 int QuicSpdyClientBase::GetNumSentClientHellosFromSession() {
209 return client_session()->GetNumSentClientHellos();
210 }
211
GetNumReceivedServerConfigUpdatesFromSession()212 int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() {
213 return client_session()->GetNumReceivedServerConfigUpdates();
214 }
215
MaybeAddQuicDataToResend(std::unique_ptr<QuicDataToResend> data_to_resend)216 void QuicSpdyClientBase::MaybeAddQuicDataToResend(
217 std::unique_ptr<QuicDataToResend> data_to_resend) {
218 data_to_resend_on_connect_.push_back(std::move(data_to_resend));
219 }
220
ClearDataToResend()221 void QuicSpdyClientBase::ClearDataToResend() {
222 data_to_resend_on_connect_.clear();
223 }
224
ResendSavedData()225 void QuicSpdyClientBase::ResendSavedData() {
226 // Calling Resend will re-enqueue the data, so swap out
227 // data_to_resend_on_connect_ before iterating.
228 std::vector<std::unique_ptr<QuicDataToResend>> old_data;
229 old_data.swap(data_to_resend_on_connect_);
230 for (const auto& data : old_data) {
231 data->Resend();
232 }
233 }
234
AddPromiseDataToResend(const Http2HeaderBlock & headers,absl::string_view body,bool fin)235 void QuicSpdyClientBase::AddPromiseDataToResend(const Http2HeaderBlock& headers,
236 absl::string_view body,
237 bool fin) {
238 std::unique_ptr<Http2HeaderBlock> new_headers(
239 new Http2HeaderBlock(headers.Clone()));
240 push_promise_data_to_resend_.reset(
241 new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
242 }
243
CheckVary(const Http2HeaderBlock &,const Http2HeaderBlock &,const Http2HeaderBlock &)244 bool QuicSpdyClientBase::CheckVary(
245 const Http2HeaderBlock& /*client_request*/,
246 const Http2HeaderBlock& /*promise_request*/,
247 const Http2HeaderBlock& /*promise_response*/) {
248 return true;
249 }
250
OnRendezvousResult(QuicSpdyStream * stream)251 void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
252 std::unique_ptr<ClientQuicDataToResend> data_to_resend =
253 std::move(push_promise_data_to_resend_);
254 if (stream) {
255 stream->set_visitor(this);
256 stream->OnBodyAvailable();
257 } else if (data_to_resend) {
258 data_to_resend->Resend();
259 }
260 }
261
latest_response_code() const262 int QuicSpdyClientBase::latest_response_code() const {
263 QUIC_BUG_IF(!store_response_) << "Response not stored!";
264 return latest_response_code_;
265 }
266
latest_response_headers() const267 const std::string& QuicSpdyClientBase::latest_response_headers() const {
268 QUIC_BUG_IF(!store_response_) << "Response not stored!";
269 return latest_response_headers_;
270 }
271
preliminary_response_headers() const272 const std::string& QuicSpdyClientBase::preliminary_response_headers() const {
273 QUIC_BUG_IF(!store_response_) << "Response not stored!";
274 return preliminary_response_headers_;
275 }
276
latest_response_header_block() const277 const Http2HeaderBlock& QuicSpdyClientBase::latest_response_header_block()
278 const {
279 QUIC_BUG_IF(!store_response_) << "Response not stored!";
280 return latest_response_header_block_;
281 }
282
latest_response_body() const283 const std::string& QuicSpdyClientBase::latest_response_body() const {
284 QUIC_BUG_IF(!store_response_) << "Response not stored!";
285 return latest_response_body_;
286 }
287
latest_response_trailers() const288 const std::string& QuicSpdyClientBase::latest_response_trailers() const {
289 QUIC_BUG_IF(!store_response_) << "Response not stored!";
290 return latest_response_trailers_;
291 }
292
HasActiveRequests()293 bool QuicSpdyClientBase::HasActiveRequests() {
294 return client_session()->HasActiveRequestStreams();
295 }
296
297 } // namespace quic
298