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