• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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