QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 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 "net/third_party/quiche/src/quic/core/crypto/quic_random.h" |
| 8 | #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" |
| 9 | #include "net/third_party/quiche/src/quic/core/quic_server_id.h" |
| 10 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| 11 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| 12 | #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" |
| 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" |
| 14 | |
| 15 | using spdy::SpdyHeaderBlock; |
| 16 | |
| 17 | namespace quic { |
| 18 | |
| 19 | void QuicSpdyClientBase::ClientQuicDataToResend::Resend() { |
| 20 | client_->SendRequest(*headers_, body_, fin_); |
| 21 | headers_ = nullptr; |
| 22 | } |
| 23 | |
| 24 | QuicSpdyClientBase::QuicDataToResend::QuicDataToResend( |
| 25 | std::unique_ptr<SpdyHeaderBlock> headers, |
| 26 | QuicStringPiece body, |
| 27 | bool fin) |
| 28 | : headers_(std::move(headers)), body_(body), fin_(fin) {} |
| 29 | |
| 30 | QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() = default; |
| 31 | |
| 32 | QuicSpdyClientBase::QuicSpdyClientBase( |
| 33 | const QuicServerId& server_id, |
| 34 | const ParsedQuicVersionVector& supported_versions, |
| 35 | const QuicConfig& config, |
| 36 | QuicConnectionHelperInterface* helper, |
| 37 | QuicAlarmFactory* alarm_factory, |
| 38 | std::unique_ptr<NetworkHelper> network_helper, |
| 39 | std::unique_ptr<ProofVerifier> proof_verifier) |
| 40 | : QuicClientBase(server_id, |
| 41 | supported_versions, |
| 42 | config, |
| 43 | helper, |
| 44 | alarm_factory, |
| 45 | std::move(network_helper), |
| 46 | std::move(proof_verifier)), |
| 47 | store_response_(false), |
| 48 | latest_response_code_(-1) {} |
| 49 | |
| 50 | QuicSpdyClientBase::~QuicSpdyClientBase() { |
| 51 | // We own the push promise index. We need to explicitly kill |
| 52 | // the session before the push promise index goes out of scope. |
| 53 | ResetSession(); |
| 54 | } |
| 55 | |
| 56 | QuicSpdyClientSession* QuicSpdyClientBase::client_session() { |
| 57 | return static_cast<QuicSpdyClientSession*>(QuicClientBase::session()); |
| 58 | } |
| 59 | |
| 60 | void QuicSpdyClientBase::InitializeSession() { |
| 61 | client_session()->Initialize(); |
| 62 | client_session()->CryptoConnect(); |
| 63 | } |
| 64 | |
| 65 | void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) { |
| 66 | DCHECK(stream != nullptr); |
| 67 | QuicSpdyClientStream* client_stream = |
| 68 | static_cast<QuicSpdyClientStream*>(stream); |
| 69 | |
| 70 | const SpdyHeaderBlock& response_headers = client_stream->response_headers(); |
| 71 | if (response_listener_ != nullptr) { |
| 72 | response_listener_->OnCompleteResponse(stream->id(), response_headers, |
| 73 | client_stream->data()); |
| 74 | } |
| 75 | |
| 76 | // Store response headers and body. |
| 77 | if (store_response_) { |
| 78 | auto status = response_headers.find(":status"); |
| 79 | if (status == response_headers.end() || |
| 80 | !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) { |
| 81 | QUIC_LOG(ERROR) << "Invalid response headers"; |
| 82 | } |
| 83 | latest_response_headers_ = response_headers.DebugString(); |
| 84 | preliminary_response_headers_ = |
| 85 | client_stream->preliminary_headers().DebugString(); |
| 86 | latest_response_header_block_ = response_headers.Clone(); |
| 87 | latest_response_body_ = client_stream->data(); |
| 88 | latest_response_trailers_ = |
| 89 | client_stream->received_trailers().DebugString(); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession( |
| 94 | const quic::ParsedQuicVersionVector& supported_versions, |
| 95 | QuicConnection* connection) { |
| 96 | return QuicMakeUnique<QuicSpdyClientSession>( |
| 97 | *config(), supported_versions, connection, server_id(), crypto_config(), |
| 98 | &push_promise_index_); |
| 99 | } |
| 100 | |
| 101 | void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers, |
| 102 | QuicStringPiece body, |
| 103 | bool fin) { |
| 104 | QuicClientPushPromiseIndex::TryHandle* handle; |
| 105 | QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle); |
| 106 | if (rv == QUIC_SUCCESS) |
| 107 | return; |
| 108 | |
| 109 | if (rv == QUIC_PENDING) { |
| 110 | // May need to retry request if asynchronous rendezvous fails. |
| 111 | AddPromiseDataToResend(headers, body, fin); |
| 112 | return; |
| 113 | } |
| 114 | |
| 115 | QuicSpdyClientStream* stream = CreateClientStream(); |
| 116 | if (stream == nullptr) { |
| 117 | QUIC_BUG << "stream creation failed!"; |
| 118 | return; |
| 119 | } |
| 120 | stream->SendRequest(headers.Clone(), body, fin); |
| 121 | // Record this in case we need to resend. |
| 122 | MaybeAddDataToResend(headers, body, fin); |
| 123 | } |
| 124 | |
| 125 | void QuicSpdyClientBase::SendRequestAndWaitForResponse( |
| 126 | const SpdyHeaderBlock& headers, |
| 127 | QuicStringPiece body, |
| 128 | bool fin) { |
| 129 | SendRequest(headers, body, fin); |
| 130 | while (WaitForEvents()) { |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | void QuicSpdyClientBase::SendRequestsAndWaitForResponse( |
| 135 | const std::vector<QuicString>& url_list) { |
| 136 | for (size_t i = 0; i < url_list.size(); ++i) { |
| 137 | SpdyHeaderBlock headers; |
| 138 | if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) { |
| 139 | QUIC_BUG << "Unable to create request"; |
| 140 | continue; |
| 141 | } |
| 142 | SendRequest(headers, "", true); |
| 143 | } |
| 144 | while (WaitForEvents()) { |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() { |
| 149 | if (!connected()) { |
| 150 | return nullptr; |
| 151 | } |
| 152 | |
| 153 | auto* stream = static_cast<QuicSpdyClientStream*>( |
| 154 | client_session()->CreateOutgoingBidirectionalStream()); |
| 155 | if (stream) { |
| 156 | stream->SetPriority(QuicStream::kDefaultPriority); |
| 157 | stream->set_visitor(this); |
| 158 | } |
| 159 | return stream; |
| 160 | } |
| 161 | |
| 162 | int QuicSpdyClientBase::GetNumSentClientHellosFromSession() { |
| 163 | return client_session()->GetNumSentClientHellos(); |
| 164 | } |
| 165 | |
| 166 | int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() { |
| 167 | return client_session()->GetNumReceivedServerConfigUpdates(); |
| 168 | } |
| 169 | |
| 170 | void QuicSpdyClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers, |
| 171 | QuicStringPiece body, |
| 172 | bool fin) { |
| 173 | if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support)) { |
| 174 | return; |
| 175 | } |
| 176 | |
| 177 | if (client_session()->IsCryptoHandshakeConfirmed()) { |
| 178 | // The handshake is confirmed. No need to continue saving requests to |
| 179 | // resend. |
| 180 | data_to_resend_on_connect_.clear(); |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | // The handshake is not confirmed. Push the data onto the queue of data to |
| 185 | // resend if statelessly rejected. |
| 186 | std::unique_ptr<SpdyHeaderBlock> new_headers( |
| 187 | new SpdyHeaderBlock(headers.Clone())); |
| 188 | std::unique_ptr<QuicDataToResend> data_to_resend( |
| 189 | new ClientQuicDataToResend(std::move(new_headers), body, fin, this)); |
| 190 | MaybeAddQuicDataToResend(std::move(data_to_resend)); |
| 191 | } |
| 192 | |
| 193 | void QuicSpdyClientBase::MaybeAddQuicDataToResend( |
| 194 | std::unique_ptr<QuicDataToResend> data_to_resend) { |
| 195 | data_to_resend_on_connect_.push_back(std::move(data_to_resend)); |
| 196 | } |
| 197 | |
| 198 | void QuicSpdyClientBase::ClearDataToResend() { |
| 199 | data_to_resend_on_connect_.clear(); |
| 200 | } |
| 201 | |
| 202 | void QuicSpdyClientBase::ResendSavedData() { |
| 203 | // Calling Resend will re-enqueue the data, so swap out |
| 204 | // data_to_resend_on_connect_ before iterating. |
| 205 | std::vector<std::unique_ptr<QuicDataToResend>> old_data; |
| 206 | old_data.swap(data_to_resend_on_connect_); |
| 207 | for (const auto& data : old_data) { |
| 208 | data->Resend(); |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | void QuicSpdyClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers, |
| 213 | QuicStringPiece body, |
| 214 | bool fin) { |
| 215 | std::unique_ptr<SpdyHeaderBlock> new_headers( |
| 216 | new SpdyHeaderBlock(headers.Clone())); |
| 217 | push_promise_data_to_resend_.reset( |
| 218 | new ClientQuicDataToResend(std::move(new_headers), body, fin, this)); |
| 219 | } |
| 220 | |
| 221 | bool QuicSpdyClientBase::CheckVary(const SpdyHeaderBlock& client_request, |
| 222 | const SpdyHeaderBlock& promise_request, |
| 223 | const SpdyHeaderBlock& promise_response) { |
| 224 | return true; |
| 225 | } |
| 226 | |
| 227 | void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) { |
| 228 | std::unique_ptr<ClientQuicDataToResend> data_to_resend = |
| 229 | std::move(push_promise_data_to_resend_); |
| 230 | if (stream) { |
| 231 | stream->set_visitor(this); |
| 232 | stream->OnBodyAvailable(); |
| 233 | } else if (data_to_resend) { |
| 234 | data_to_resend->Resend(); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | size_t QuicSpdyClientBase::latest_response_code() const { |
| 239 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 240 | return latest_response_code_; |
| 241 | } |
| 242 | |
| 243 | const QuicString& QuicSpdyClientBase::latest_response_headers() const { |
| 244 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 245 | return latest_response_headers_; |
| 246 | } |
| 247 | |
| 248 | const QuicString& QuicSpdyClientBase::preliminary_response_headers() const { |
| 249 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 250 | return preliminary_response_headers_; |
| 251 | } |
| 252 | |
| 253 | const SpdyHeaderBlock& QuicSpdyClientBase::latest_response_header_block() |
| 254 | const { |
| 255 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 256 | return latest_response_header_block_; |
| 257 | } |
| 258 | |
| 259 | const QuicString& QuicSpdyClientBase::latest_response_body() const { |
| 260 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 261 | return latest_response_body_; |
| 262 | } |
| 263 | |
| 264 | const QuicString& QuicSpdyClientBase::latest_response_trailers() const { |
| 265 | QUIC_BUG_IF(!store_response_) << "Response not stored!"; |
| 266 | return latest_response_trailers_; |
| 267 | } |
| 268 | |
QUICHE team | c2653c4 | 2019-03-08 13:30:06 -0800 | [diff] [blame] | 269 | bool QuicSpdyClientBase::HasActiveRequests() { |
| 270 | return client_session()->HasActiveRequestStreams(); |
| 271 | } |
| 272 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 273 | } // namespace quic |