xref: /aosp_15_r20/external/cronet/net/http/bidirectional_stream.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2015 The Chromium Authors
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/http/bidirectional_stream.h"
6 
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/functional/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/task/single_thread_task_runner.h"
16 #include "base/timer/timer.h"
17 #include "base/values.h"
18 #include "net/base/load_flags.h"
19 #include "net/base/net_errors.h"
20 #include "net/http/bidirectional_stream_request_info.h"
21 #include "net/http/http_network_session.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_stream.h"
24 #include "net/log/net_log.h"
25 #include "net/log/net_log_capture_mode.h"
26 #include "net/log/net_log_event_type.h"
27 #include "net/log/net_log_source_type.h"
28 #include "net/log/net_log_values.h"
29 #include "net/spdy/spdy_http_utils.h"
30 #include "net/spdy/spdy_log_util.h"
31 #include "net/ssl/ssl_cert_request_info.h"
32 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
33 #include "net/traffic_annotation/network_traffic_annotation.h"
34 #include "url/gurl.h"
35 
36 namespace net {
37 
38 namespace {
39 
NetLogHeadersParams(const spdy::Http2HeaderBlock * headers,NetLogCaptureMode capture_mode)40 base::Value::Dict NetLogHeadersParams(const spdy::Http2HeaderBlock* headers,
41                                       NetLogCaptureMode capture_mode) {
42   base::Value::Dict dict;
43   dict.Set("headers", ElideHttp2HeaderBlockForNetLog(*headers, capture_mode));
44   return dict;
45 }
46 
NetLogParams(const GURL & url,const std::string & method,const HttpRequestHeaders * headers,NetLogCaptureMode capture_mode)47 base::Value::Dict NetLogParams(const GURL& url,
48                                const std::string& method,
49                                const HttpRequestHeaders* headers,
50                                NetLogCaptureMode capture_mode) {
51   base::Value::Dict dict;
52   dict.Set("url", url.possibly_invalid_spec());
53   dict.Set("method", method);
54   base::Value headers_param(
55       headers->NetLogParams(/*request_line=*/std::string(), capture_mode));
56   dict.Set("headers", std::move(headers_param));
57   return dict;
58 }
59 
60 }  // namespace
61 
62 BidirectionalStream::Delegate::Delegate() = default;
63 
64 BidirectionalStream::Delegate::~Delegate() = default;
65 
BidirectionalStream(std::unique_ptr<BidirectionalStreamRequestInfo> request_info,HttpNetworkSession * session,bool send_request_headers_automatically,Delegate * delegate)66 BidirectionalStream::BidirectionalStream(
67     std::unique_ptr<BidirectionalStreamRequestInfo> request_info,
68     HttpNetworkSession* session,
69     bool send_request_headers_automatically,
70     Delegate* delegate)
71     : BidirectionalStream(std::move(request_info),
72                           session,
73                           send_request_headers_automatically,
74                           delegate,
75                           std::make_unique<base::OneShotTimer>()) {}
76 
BidirectionalStream(std::unique_ptr<BidirectionalStreamRequestInfo> request_info,HttpNetworkSession * session,bool send_request_headers_automatically,Delegate * delegate,std::unique_ptr<base::OneShotTimer> timer)77 BidirectionalStream::BidirectionalStream(
78     std::unique_ptr<BidirectionalStreamRequestInfo> request_info,
79     HttpNetworkSession* session,
80     bool send_request_headers_automatically,
81     Delegate* delegate,
82     std::unique_ptr<base::OneShotTimer> timer)
83     : request_info_(std::move(request_info)),
84       net_log_(NetLogWithSource::Make(session->net_log(),
85                                       NetLogSourceType::BIDIRECTIONAL_STREAM)),
86       session_(session),
87       send_request_headers_automatically_(send_request_headers_automatically),
88       delegate_(delegate),
89       timer_(std::move(timer)) {
90   DCHECK(delegate_);
91   DCHECK(request_info_);
92 
93   // Start time should be measured before connect.
94   load_timing_info_.request_start_time = base::Time::Now();
95   load_timing_info_.request_start = base::TimeTicks::Now();
96 
97   if (net_log_.IsCapturing()) {
98     net_log_.BeginEvent(NetLogEventType::BIDIRECTIONAL_STREAM_ALIVE,
99                         [&](NetLogCaptureMode capture_mode) {
100                           return NetLogParams(
101                               request_info_->url, request_info_->method,
102                               &request_info_->extra_headers, capture_mode);
103                         });
104   }
105 
106   if (!request_info_->url.SchemeIs(url::kHttpsScheme)) {
107     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
108         FROM_HERE,
109         base::BindOnce(&BidirectionalStream::NotifyFailed,
110                        weak_factory_.GetWeakPtr(), ERR_DISALLOWED_URL_SCHEME));
111     return;
112   }
113 
114   StartRequest();
115 }
116 
~BidirectionalStream()117 BidirectionalStream::~BidirectionalStream() {
118   if (net_log_.IsCapturing()) {
119     net_log_.EndEvent(NetLogEventType::BIDIRECTIONAL_STREAM_ALIVE);
120   }
121 }
122 
SendRequestHeaders()123 void BidirectionalStream::SendRequestHeaders() {
124   DCHECK(stream_impl_);
125   DCHECK(!request_headers_sent_);
126   DCHECK(!send_request_headers_automatically_);
127 
128   stream_impl_->SendRequestHeaders();
129 }
130 
ReadData(IOBuffer * buf,int buf_len)131 int BidirectionalStream::ReadData(IOBuffer* buf, int buf_len) {
132   DCHECK(stream_impl_);
133 
134   int rv = stream_impl_->ReadData(buf, buf_len);
135   if (rv > 0) {
136     read_end_time_ = base::TimeTicks::Now();
137     net_log_.AddByteTransferEvent(
138         NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_RECEIVED, rv, buf->data());
139   } else if (rv == ERR_IO_PENDING) {
140     read_buffer_ = buf;
141     // Bytes will be logged in OnDataRead().
142   }
143   if (net_log_.IsCapturing()) {
144     net_log_.AddEventWithIntParams(
145         NetLogEventType::BIDIRECTIONAL_STREAM_READ_DATA, "rv", rv);
146   }
147   return rv;
148 }
149 
SendvData(const std::vector<scoped_refptr<IOBuffer>> & buffers,const std::vector<int> & lengths,bool end_stream)150 void BidirectionalStream::SendvData(
151     const std::vector<scoped_refptr<IOBuffer>>& buffers,
152     const std::vector<int>& lengths,
153     bool end_stream) {
154   DCHECK(stream_impl_);
155   DCHECK_EQ(buffers.size(), lengths.size());
156   DCHECK(write_buffer_list_.empty());
157   DCHECK(write_buffer_len_list_.empty());
158 
159   if (net_log_.IsCapturing()) {
160     net_log_.AddEventWithIntParams(
161         NetLogEventType::BIDIRECTIONAL_STREAM_SENDV_DATA, "num_buffers",
162         buffers.size());
163   }
164   stream_impl_->SendvData(buffers, lengths, end_stream);
165   for (size_t i = 0; i < buffers.size(); ++i) {
166     write_buffer_list_.push_back(buffers[i]);
167     write_buffer_len_list_.push_back(lengths[i]);
168   }
169 }
170 
GetProtocol() const171 NextProto BidirectionalStream::GetProtocol() const {
172   if (!stream_impl_)
173     return kProtoUnknown;
174 
175   return stream_impl_->GetProtocol();
176 }
177 
GetTotalReceivedBytes() const178 int64_t BidirectionalStream::GetTotalReceivedBytes() const {
179   if (!stream_impl_)
180     return 0;
181 
182   return stream_impl_->GetTotalReceivedBytes();
183 }
184 
GetTotalSentBytes() const185 int64_t BidirectionalStream::GetTotalSentBytes() const {
186   if (!stream_impl_)
187     return 0;
188 
189   return stream_impl_->GetTotalSentBytes();
190 }
191 
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const192 void BidirectionalStream::GetLoadTimingInfo(
193     LoadTimingInfo* load_timing_info) const {
194   *load_timing_info = load_timing_info_;
195 }
196 
PopulateNetErrorDetails(NetErrorDetails * details)197 void BidirectionalStream::PopulateNetErrorDetails(NetErrorDetails* details) {
198   DCHECK(details);
199   if (stream_impl_)
200     stream_impl_->PopulateNetErrorDetails(details);
201 }
202 
StartRequest()203 void BidirectionalStream::StartRequest() {
204   DCHECK(!stream_request_);
205   HttpRequestInfo http_request_info;
206   http_request_info.url = request_info_->url;
207   http_request_info.method = request_info_->method;
208   http_request_info.extra_headers = request_info_->extra_headers;
209   http_request_info.socket_tag = request_info_->socket_tag;
210   stream_request_ =
211       session_->http_stream_factory()->RequestBidirectionalStreamImpl(
212           http_request_info, request_info_->priority, /*allowed_bad_certs=*/{},
213           this, /* enable_ip_based_pooling = */ true,
214           /* enable_alternative_services = */ true, net_log_);
215   // Check that this call does not fail.
216   DCHECK(stream_request_);
217   // Check that HttpStreamFactory does not invoke OnBidirectionalStreamImplReady
218   // synchronously.
219   DCHECK(!stream_impl_);
220 }
221 
OnStreamReady(bool request_headers_sent)222 void BidirectionalStream::OnStreamReady(bool request_headers_sent) {
223   request_headers_sent_ = request_headers_sent;
224   if (net_log_.IsCapturing()) {
225     net_log_.AddEntryWithBoolParams(
226         NetLogEventType::BIDIRECTIONAL_STREAM_READY, NetLogEventPhase::NONE,
227         "request_headers_sent", request_headers_sent);
228   }
229   load_timing_info_.send_start = base::TimeTicks::Now();
230   load_timing_info_.send_end = load_timing_info_.send_start;
231   delegate_->OnStreamReady(request_headers_sent);
232 }
233 
OnHeadersReceived(const spdy::Http2HeaderBlock & response_headers)234 void BidirectionalStream::OnHeadersReceived(
235     const spdy::Http2HeaderBlock& response_headers) {
236   HttpResponseInfo response_info;
237   if (SpdyHeadersToHttpResponse(response_headers, &response_info) != OK) {
238     DLOG(WARNING) << "Invalid headers";
239     NotifyFailed(ERR_FAILED);
240     return;
241   }
242   if (net_log_.IsCapturing()) {
243     net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_RECV_HEADERS,
244                       [&](NetLogCaptureMode capture_mode) {
245                         return NetLogHeadersParams(&response_headers,
246                                                    capture_mode);
247                       });
248   }
249   // Impl should only provide |connect_timing| and |socket_reused| info,
250   // so use a copy to get these information only.
251   LoadTimingInfo impl_load_timing_info;
252   bool has_load_timing =
253       stream_impl_->GetLoadTimingInfo(&impl_load_timing_info);
254   if (has_load_timing) {
255     load_timing_info_.connect_timing = impl_load_timing_info.connect_timing;
256     load_timing_info_.socket_reused = impl_load_timing_info.socket_reused;
257   }
258   load_timing_info_.receive_headers_end = base::TimeTicks::Now();
259   read_end_time_ = load_timing_info_.receive_headers_end;
260   session_->http_stream_factory()->ProcessAlternativeServices(
261       session_, net::NetworkAnonymizationKey(), response_info.headers.get(),
262       url::SchemeHostPort(request_info_->url));
263   delegate_->OnHeadersReceived(response_headers);
264 }
265 
OnDataRead(int bytes_read)266 void BidirectionalStream::OnDataRead(int bytes_read) {
267   DCHECK(read_buffer_);
268 
269   if (net_log_.IsCapturing()) {
270     net_log_.AddByteTransferEvent(
271         NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_RECEIVED, bytes_read,
272         read_buffer_->data());
273   }
274   read_end_time_ = base::TimeTicks::Now();
275   read_buffer_ = nullptr;
276   delegate_->OnDataRead(bytes_read);
277 }
278 
OnDataSent()279 void BidirectionalStream::OnDataSent() {
280   DCHECK(!write_buffer_list_.empty());
281   DCHECK_EQ(write_buffer_list_.size(), write_buffer_len_list_.size());
282 
283   if (net_log_.IsCapturing()) {
284     if (write_buffer_list_.size() > 1) {
285       net_log_.BeginEvent(
286           NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED, [&] {
287             return NetLogParamsWithInt("num_buffers_coalesced",
288                                        write_buffer_list_.size());
289           });
290     }
291     for (size_t i = 0; i < write_buffer_list_.size(); ++i) {
292       net_log_.AddByteTransferEvent(
293           NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT,
294           write_buffer_len_list_[i], write_buffer_list_[i]->data());
295     }
296     if (write_buffer_list_.size() > 1) {
297       net_log_.EndEvent(
298           NetLogEventType::BIDIRECTIONAL_STREAM_BYTES_SENT_COALESCED);
299     }
300   }
301   load_timing_info_.send_end = base::TimeTicks::Now();
302   write_buffer_list_.clear();
303   write_buffer_len_list_.clear();
304   delegate_->OnDataSent();
305 }
306 
OnTrailersReceived(const spdy::Http2HeaderBlock & trailers)307 void BidirectionalStream::OnTrailersReceived(
308     const spdy::Http2HeaderBlock& trailers) {
309   if (net_log_.IsCapturing()) {
310     net_log_.AddEvent(NetLogEventType::BIDIRECTIONAL_STREAM_RECV_TRAILERS,
311                       [&](NetLogCaptureMode capture_mode) {
312                         return NetLogHeadersParams(&trailers, capture_mode);
313                       });
314   }
315   read_end_time_ = base::TimeTicks::Now();
316   delegate_->OnTrailersReceived(trailers);
317 }
318 
OnFailed(int status)319 void BidirectionalStream::OnFailed(int status) {
320   if (net_log_.IsCapturing()) {
321     net_log_.AddEventWithIntParams(NetLogEventType::BIDIRECTIONAL_STREAM_FAILED,
322                                    "net_error", status);
323   }
324   NotifyFailed(status);
325 }
326 
OnStreamReady(const ProxyInfo & used_proxy_info,std::unique_ptr<HttpStream> stream)327 void BidirectionalStream::OnStreamReady(const ProxyInfo& used_proxy_info,
328                                         std::unique_ptr<HttpStream> stream) {
329   NOTREACHED();
330 }
331 
OnBidirectionalStreamImplReady(const ProxyInfo & used_proxy_info,std::unique_ptr<BidirectionalStreamImpl> stream)332 void BidirectionalStream::OnBidirectionalStreamImplReady(
333     const ProxyInfo& used_proxy_info,
334     std::unique_ptr<BidirectionalStreamImpl> stream) {
335   DCHECK(!stream_impl_);
336 
337   net::NetworkTrafficAnnotationTag traffic_annotation =
338       net::DefineNetworkTrafficAnnotation("bidirectional_stream", R"(
339         semantics {
340           sender: "Bidirectional Stream"
341           description:
342             "Bidirectional stream is used to exchange data with a server on "
343             "behalf of an RPC API."
344           trigger:
345             "When an application makes an RPC to the server."
346           data:
347             "Any arbitrary data."
348           destination: OTHER
349           destination_other:
350             "Any destination that the application chooses."
351         }
352         policy {
353           cookies_allowed: NO
354           setting: "This feature is not used in Chrome."
355           policy_exception_justification:
356             "This feature is not used in Chrome."
357         }
358     )");
359 
360   stream_request_.reset();
361   stream_impl_ = std::move(stream);
362   stream_impl_->Start(request_info_.get(), net_log_,
363                       send_request_headers_automatically_, this,
364                       std::move(timer_), traffic_annotation);
365 }
366 
OnWebSocketHandshakeStreamReady(const ProxyInfo & used_proxy_info,std::unique_ptr<WebSocketHandshakeStreamBase> stream)367 void BidirectionalStream::OnWebSocketHandshakeStreamReady(
368     const ProxyInfo& used_proxy_info,
369     std::unique_ptr<WebSocketHandshakeStreamBase> stream) {
370   NOTREACHED();
371 }
372 
OnStreamFailed(int result,const NetErrorDetails & net_error_details,const ProxyInfo & used_proxy_info,ResolveErrorInfo resolve_error_info)373 void BidirectionalStream::OnStreamFailed(
374     int result,
375     const NetErrorDetails& net_error_details,
376     const ProxyInfo& used_proxy_info,
377     ResolveErrorInfo resolve_error_info) {
378   DCHECK_LT(result, 0);
379   DCHECK_NE(result, ERR_IO_PENDING);
380   DCHECK(stream_request_);
381 
382   NotifyFailed(result);
383 }
384 
OnCertificateError(int result,const SSLInfo & ssl_info)385 void BidirectionalStream::OnCertificateError(int result,
386                                              const SSLInfo& ssl_info) {
387   DCHECK_LT(result, 0);
388   DCHECK_NE(result, ERR_IO_PENDING);
389   DCHECK(stream_request_);
390 
391   NotifyFailed(result);
392 }
393 
OnNeedsProxyAuth(const HttpResponseInfo & proxy_response,const ProxyInfo & used_proxy_info,HttpAuthController * auth_controller)394 void BidirectionalStream::OnNeedsProxyAuth(
395     const HttpResponseInfo& proxy_response,
396     const ProxyInfo& used_proxy_info,
397     HttpAuthController* auth_controller) {
398   DCHECK(stream_request_);
399 
400   NotifyFailed(ERR_PROXY_AUTH_REQUESTED);
401 }
402 
OnNeedsClientAuth(SSLCertRequestInfo * cert_info)403 void BidirectionalStream::OnNeedsClientAuth(SSLCertRequestInfo* cert_info) {
404   DCHECK(stream_request_);
405 
406   // BidirectionalStream doesn't support client auth. It ignores client auth
407   // requests with null client cert and key.
408   session_->ssl_client_context()->SetClientCertificate(cert_info->host_and_port,
409                                                        nullptr, nullptr);
410   stream_request_ = nullptr;
411   StartRequest();
412 }
413 
OnQuicBroken()414 void BidirectionalStream::OnQuicBroken() {}
415 
NotifyFailed(int error)416 void BidirectionalStream::NotifyFailed(int error) {
417   delegate_->OnFailed(error);
418 }
419 
420 }  // namespace net
421