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