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