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