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