[M120 Migration][VD]add new log tag for http request and response log
[platform/framework/web/chromium-efl.git] / net / spdy / spdy_http_stream.cc
1 // Copyright 2012 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/spdy/spdy_http_stream.h"
6
7 #include <algorithm>
8 #include <list>
9 #include <set>
10 #include <string>
11 #include <utility>
12
13 #include "base/check_op.h"
14 #include "base/functional/bind.h"
15 #include "base/location.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "base/values.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/base/upload_data_stream.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_info.h"
24 #include "net/log/net_log_event_type.h"
25 #include "net/log/net_log_with_source.h"
26 #include "net/spdy/spdy_http_utils.h"
27 #include "net/spdy/spdy_session.h"
28 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
29 #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
30 #include "url/scheme_host_port.h"
31
32 #if BUILDFLAG(IS_TIZEN_TV)
33 #include "net/http/http_response_headers.h"
34 #include "tizen_src/chromium_impl/base/logging_network.h"
35 #endif
36
37 namespace net {
38
39 // Align our request body with |kMaxSpdyFrameChunkSize| to prevent unexpected
40 // buffer chunking. This is 16KB - frame header size.
41 const size_t SpdyHttpStream::kRequestBodyBufferSize = kMaxSpdyFrameChunkSize;
42
43 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
44                                NetLogSource source_dependency,
45                                std::set<std::string> dns_aliases)
46     : MultiplexedHttpStream(
47           std::make_unique<MultiplexedSessionHandle>(spdy_session)),
48       spdy_session_(spdy_session),
49       is_reused_(spdy_session_->IsReused()),
50       source_dependency_(source_dependency),
51       dns_aliases_(std::move(dns_aliases)) {
52   DCHECK(spdy_session_.get());
53 }
54
55 SpdyHttpStream::~SpdyHttpStream() {
56   if (stream_) {
57     stream_->DetachDelegate();
58     DCHECK(!stream_);
59   }
60 }
61
62 void SpdyHttpStream::RegisterRequest(const HttpRequestInfo* request_info) {
63   DCHECK(request_info);
64   request_info_ = request_info;
65 }
66
67 int SpdyHttpStream::InitializeStream(bool can_send_early,
68                                      RequestPriority priority,
69                                      const NetLogWithSource& stream_net_log,
70                                      CompletionOnceCallback callback) {
71   DCHECK(!stream_);
72   DCHECK(request_info_);
73   if (!spdy_session_)
74     return ERR_CONNECTION_CLOSED;
75
76   priority_ = priority;
77   int rv = stream_request_.StartRequest(
78       SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
79       can_send_early, priority, request_info_->socket_tag, stream_net_log,
80       base::BindOnce(&SpdyHttpStream::OnStreamCreated,
81                      weak_factory_.GetWeakPtr(), std::move(callback)),
82       NetworkTrafficAnnotationTag{request_info_->traffic_annotation});
83
84   if (rv == OK) {
85     stream_ = stream_request_.ReleaseStream().get();
86     InitializeStreamHelper();
87   }
88
89   return rv;
90 }
91
92 int SpdyHttpStream::ReadResponseHeaders(CompletionOnceCallback callback) {
93   CHECK(!callback.is_null());
94   if (stream_closed_)
95     return closed_stream_status_;
96
97   CHECK(stream_);
98
99   // Check if we already have the response headers. If so, return synchronously.
100   if (response_headers_complete_) {
101     CHECK(!stream_->IsIdle());
102     return OK;
103   }
104
105   // Still waiting for the response, return IO_PENDING.
106   CHECK(response_callback_.is_null());
107   response_callback_ = std::move(callback);
108   return ERR_IO_PENDING;
109 }
110
111 int SpdyHttpStream::ReadResponseBody(IOBuffer* buf,
112                                      int buf_len,
113                                      CompletionOnceCallback callback) {
114   if (stream_)
115     CHECK(!stream_->IsIdle());
116
117   CHECK(buf);
118   CHECK(buf_len);
119   CHECK(!callback.is_null());
120
121   // If we have data buffered, complete the IO immediately.
122   if (!response_body_queue_.IsEmpty()) {
123     return response_body_queue_.Dequeue(buf->data(), buf_len);
124   } else if (stream_closed_) {
125     return closed_stream_status_;
126   }
127
128   CHECK(response_callback_.is_null());
129   CHECK(!user_buffer_.get());
130   CHECK_EQ(0, user_buffer_len_);
131
132   response_callback_ = std::move(callback);
133   user_buffer_ = buf;
134   user_buffer_len_ = buf_len;
135   return ERR_IO_PENDING;
136 }
137
138 void SpdyHttpStream::Close(bool not_reusable) {
139   // Note: the not_reusable flag has no meaning for SPDY streams.
140
141   Cancel();
142   DCHECK(!stream_);
143 }
144
145 bool SpdyHttpStream::IsResponseBodyComplete() const {
146   return stream_closed_;
147 }
148
149 bool SpdyHttpStream::IsConnectionReused() const {
150   return is_reused_;
151 }
152
153 int64_t SpdyHttpStream::GetTotalReceivedBytes() const {
154   if (stream_closed_)
155     return closed_stream_received_bytes_;
156
157   if (!stream_)
158     return 0;
159
160   return stream_->raw_received_bytes();
161 }
162
163 int64_t SpdyHttpStream::GetTotalSentBytes() const {
164   if (stream_closed_)
165     return closed_stream_sent_bytes_;
166
167   if (!stream_)
168     return 0;
169
170   return stream_->raw_sent_bytes();
171 }
172
173 bool SpdyHttpStream::GetAlternativeService(
174     AlternativeService* alternative_service) const {
175   return false;
176 }
177
178 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
179   if (stream_closed_) {
180     if (!closed_stream_has_load_timing_info_)
181       return false;
182     *load_timing_info = closed_stream_load_timing_info_;
183   } else {
184     // If |stream_| has yet to be created, or does not yet have an ID, fail.
185     // The reused flag can only be correctly set once a stream has an ID.
186     // Streams get their IDs once the request has been successfully sent, so
187     // this does not behave that differently from other stream types.
188     if (!stream_ || stream_->stream_id() == 0)
189       return false;
190
191     if (!stream_->GetLoadTimingInfo(load_timing_info))
192       return false;
193   }
194
195   // If the request waited for handshake confirmation, shift |ssl_end| to
196   // include that time.
197   if (!load_timing_info->connect_timing.ssl_end.is_null() &&
198       !stream_request_.confirm_handshake_end().is_null()) {
199     load_timing_info->connect_timing.ssl_end =
200         stream_request_.confirm_handshake_end();
201     load_timing_info->connect_timing.connect_end =
202         stream_request_.confirm_handshake_end();
203   }
204
205   return true;
206 }
207
208 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
209                                 HttpResponseInfo* response,
210                                 CompletionOnceCallback callback) {
211   if (stream_closed_) {
212     return closed_stream_status_;
213   }
214
215   base::Time request_time = base::Time::Now();
216   CHECK(stream_);
217
218   stream_->SetRequestTime(request_time);
219   // This should only get called in the case of a request occurring
220   // during server push that has already begun but hasn't finished,
221   // so we set the response's request time to be the actual one
222   if (response_info_)
223     response_info_->request_time = request_time;
224
225   CHECK(!request_body_buf_.get());
226   if (HasUploadData()) {
227     request_body_buf_ =
228         base::MakeRefCounted<IOBufferWithSize>(kRequestBodyBufferSize);
229     // The request body buffer is empty at first.
230     request_body_buf_size_ = 0;
231   }
232
233   CHECK(!callback.is_null());
234   CHECK(response);
235   DCHECK(!response_info_);
236
237   response_info_ = response;
238
239   // Put the peer's IP address and port into the response.
240   IPEndPoint address;
241   int result = stream_->GetPeerAddress(&address);
242   if (result != OK)
243     return result;
244   response_info_->remote_endpoint = address;
245
246 #if BUILDFLAG(IS_TIZEN_TV)
247   NET_LOGD("Request URL: %s", request_info_->url.spec().c_str());
248   NET_LOGD("Request Header: %s", request_headers.ToString().c_str());
249 #endif
250
251   spdy::Http2HeaderBlock headers;
252   CreateSpdyHeadersFromHttpRequest(*request_info_, priority_, request_headers,
253                                    &headers);
254   DispatchRequestHeadersCallback(headers);
255
256   bool will_send_data =
257       HasUploadData() || spdy_session_->EndStreamWithDataFrame();
258   result = stream_->SendRequestHeaders(
259       std::move(headers),
260       will_send_data ? MORE_DATA_TO_SEND : NO_MORE_DATA_TO_SEND);
261
262   if (result == ERR_IO_PENDING) {
263     CHECK(request_callback_.is_null());
264     request_callback_ = std::move(callback);
265   }
266   return result;
267 }
268
269 void SpdyHttpStream::Cancel() {
270   request_callback_.Reset();
271   response_callback_.Reset();
272   if (stream_) {
273     stream_->Cancel(ERR_ABORTED);
274     DCHECK(!stream_);
275   }
276 }
277
278 void SpdyHttpStream::OnHeadersSent() {
279   if (HasUploadData()) {
280     ReadAndSendRequestBodyData();
281   } else if (spdy_session_->EndStreamWithDataFrame()) {
282     SendEmptyBody();
283   } else {
284     MaybePostRequestCallback(OK);
285   }
286 }
287
288 void SpdyHttpStream::OnEarlyHintsReceived(
289     const spdy::Http2HeaderBlock& headers) {
290   DCHECK(!response_headers_complete_);
291   DCHECK(response_info_);
292   DCHECK_EQ(stream_->type(), SPDY_REQUEST_RESPONSE_STREAM);
293
294   const int rv = SpdyHeadersToHttpResponse(headers, response_info_);
295   CHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
296
297   if (!response_callback_.is_null()) {
298     DoResponseCallback(OK);
299   }
300 }
301
302 void SpdyHttpStream::OnHeadersReceived(
303     const spdy::Http2HeaderBlock& response_headers) {
304   DCHECK(!response_headers_complete_);
305   DCHECK(response_info_);
306   response_headers_complete_ = true;
307
308   const int rv = SpdyHeadersToHttpResponse(response_headers, response_info_);
309   DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
310
311   if (rv == ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION) {
312     // Cancel will call OnClose, which might call callbacks and might destroy
313     // `this`.
314     stream_->Cancel(rv);
315     return;
316   }
317
318 #if BUILDFLAG(IS_TIZEN_TV)
319   if (request_info_)
320     NET_LOGD("Response URL: %s", request_info_->url.spec().c_str());
321   else
322     NET_LOGD("Response URL: Error, no request info");
323
324   HttpResponseHeaders* headers = response_info_->headers.get();
325   std::string response_header = headers->GetStatusLine();
326   response_header.push_back('\n');
327   size_t iter = 0;
328   std::string name, value;
329   while (headers->EnumerateHeaderLines(&iter, &name, &value))
330     response_header.append(name).append(": ").append(value).push_back('\n');
331   NET_LOGD("Response Header: %s\n", response_header.c_str());
332 #endif
333
334   response_info_->response_time = stream_->response_time();
335   // Don't store the SSLInfo in the response here, HttpNetworkTransaction
336   // will take care of that part.
337   response_info_->was_alpn_negotiated = was_alpn_negotiated_;
338   response_info_->request_time = stream_->GetRequestTime();
339   response_info_->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
340   response_info_->alpn_negotiated_protocol =
341       HttpResponseInfo::ConnectionInfoToString(response_info_->connection_info);
342
343   // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
344   // shared across multiple consumers at the cache layer which might require
345   // this stream to outlive the request_info_'s owner.
346   if (!upload_stream_in_progress_)
347     request_info_ = nullptr;
348
349   if (!response_callback_.is_null()) {
350     DoResponseCallback(OK);
351   }
352 }
353
354 void SpdyHttpStream::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
355   DCHECK(response_headers_complete_);
356
357   // Note that data may be received for a SpdyStream prior to the user calling
358   // ReadResponseBody(), therefore user_buffer_ may be NULL.  This may often
359   // happen for server initiated streams.
360   DCHECK(stream_);
361   DCHECK(!stream_->IsClosed());
362   if (buffer) {
363     response_body_queue_.Enqueue(std::move(buffer));
364     MaybeScheduleBufferedReadCallback();
365   }
366 }
367
368 void SpdyHttpStream::OnDataSent() {
369   if (request_info_ && HasUploadData()) {
370     request_body_buf_size_ = 0;
371     ReadAndSendRequestBodyData();
372   } else {
373     CHECK(spdy_session_->EndStreamWithDataFrame());
374     MaybePostRequestCallback(OK);
375   }
376 }
377
378 // TODO(xunjieli): Maybe do something with the trailers. crbug.com/422958.
379 void SpdyHttpStream::OnTrailers(const spdy::Http2HeaderBlock& trailers) {}
380
381 void SpdyHttpStream::OnClose(int status) {
382   DCHECK(stream_);
383
384   // Cancel any pending reads from the upload data stream.
385   if (request_info_ && request_info_->upload_data_stream)
386     request_info_->upload_data_stream->Reset();
387
388   stream_closed_ = true;
389   closed_stream_status_ = status;
390   closed_stream_id_ = stream_->stream_id();
391   closed_stream_has_load_timing_info_ =
392       stream_->GetLoadTimingInfo(&closed_stream_load_timing_info_);
393   closed_stream_received_bytes_ = stream_->raw_received_bytes();
394   closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
395   stream_ = nullptr;
396
397   // Callbacks might destroy |this|.
398   base::WeakPtr<SpdyHttpStream> self = weak_factory_.GetWeakPtr();
399
400   if (!request_callback_.is_null()) {
401     DoRequestCallback(status);
402     if (!self)
403       return;
404   }
405
406   if (status == OK) {
407     // We need to complete any pending buffered read now.
408     DoBufferedReadCallback();
409     if (!self)
410       return;
411   }
412
413   if (!response_callback_.is_null()) {
414     DoResponseCallback(status);
415   }
416 }
417
418 bool SpdyHttpStream::CanGreaseFrameType() const {
419   return true;
420 }
421
422 NetLogSource SpdyHttpStream::source_dependency() const {
423   return source_dependency_;
424 }
425
426 bool SpdyHttpStream::HasUploadData() const {
427   CHECK(request_info_);
428   return
429       request_info_->upload_data_stream &&
430       ((request_info_->upload_data_stream->size() > 0) ||
431        request_info_->upload_data_stream->is_chunked());
432 }
433
434 void SpdyHttpStream::OnStreamCreated(CompletionOnceCallback callback, int rv) {
435   if (rv == OK) {
436     stream_ = stream_request_.ReleaseStream().get();
437     InitializeStreamHelper();
438   }
439   std::move(callback).Run(rv);
440 }
441
442 void SpdyHttpStream::ReadAndSendRequestBodyData() {
443   CHECK(HasUploadData());
444   upload_stream_in_progress_ = true;
445
446   CHECK_EQ(request_body_buf_size_, 0);
447   if (request_info_->upload_data_stream->IsEOF()) {
448     MaybePostRequestCallback(OK);
449
450     // Invalidate HttpRequestInfo pointer. This is to allow |this| to be
451     // shared across multiple consumers at the cache layer which might require
452     // this stream to outlive the request_info_'s owner.
453     upload_stream_in_progress_ = false;
454     if (response_headers_complete_)
455       request_info_ = nullptr;
456     return;
457   }
458
459   // Read the data from the request body stream.
460   const int rv = request_info_->upload_data_stream->Read(
461       request_body_buf_.get(), request_body_buf_->size(),
462       base::BindOnce(&SpdyHttpStream::OnRequestBodyReadCompleted,
463                      weak_factory_.GetWeakPtr()));
464
465   if (rv != ERR_IO_PENDING)
466     OnRequestBodyReadCompleted(rv);
467 }
468
469 void SpdyHttpStream::SendEmptyBody() {
470   CHECK(!HasUploadData());
471   CHECK(spdy_session_->EndStreamWithDataFrame());
472
473   auto buffer = base::MakeRefCounted<IOBuffer>(/* buffer_size = */ 0);
474   stream_->SendData(buffer.get(), /* length = */ 0, NO_MORE_DATA_TO_SEND);
475 }
476
477 void SpdyHttpStream::InitializeStreamHelper() {
478   stream_->SetDelegate(this);
479   was_alpn_negotiated_ = stream_->WasAlpnNegotiated();
480 }
481
482 void SpdyHttpStream::ResetStream(int error) {
483   spdy_session_->ResetStream(stream()->stream_id(), error, std::string());
484 }
485
486 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
487   if (status < 0) {
488     DCHECK_NE(ERR_IO_PENDING, status);
489     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
490         FROM_HERE, base::BindOnce(&SpdyHttpStream::ResetStream,
491                                   weak_factory_.GetWeakPtr(), status));
492
493     return;
494   }
495
496   CHECK_GE(status, 0);
497   request_body_buf_size_ = status;
498   const bool eof = request_info_->upload_data_stream->IsEOF();
499   // Only the final frame may have a length of 0.
500   if (eof) {
501     CHECK_GE(request_body_buf_size_, 0);
502   } else {
503     CHECK_GT(request_body_buf_size_, 0);
504   }
505   stream_->SendData(request_body_buf_.get(),
506                     request_body_buf_size_,
507                     eof ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
508 }
509
510 void SpdyHttpStream::MaybeScheduleBufferedReadCallback() {
511   DCHECK(!stream_closed_);
512
513   if (!user_buffer_.get())
514     return;
515
516   // If enough data was received to fill the user buffer, invoke
517   // DoBufferedReadCallback() with no delay.
518   //
519   // Note: DoBufferedReadCallback() is invoked asynchronously to preserve
520   // historical behavior. It would be interesting to evaluate whether it can be
521   // invoked synchronously to avoid the overhead of posting a task. A long time
522   // ago, the callback was invoked synchronously
523   // https://codereview.chromium.org/652209/diff/2018/net/spdy/spdy_stream.cc.
524   if (response_body_queue_.GetTotalSize() >=
525       static_cast<size_t>(user_buffer_len_)) {
526     buffered_read_timer_.Start(FROM_HERE, base::TimeDelta() /* no delay */,
527                                this, &SpdyHttpStream::DoBufferedReadCallback);
528     return;
529   }
530
531   // Handing small chunks of data to the caller creates measurable overhead.
532   // Wait 1ms to allow handing off multiple chunks of data received within a
533   // short time span at once.
534   buffered_read_timer_.Start(FROM_HERE, base::Milliseconds(1), this,
535                              &SpdyHttpStream::DoBufferedReadCallback);
536 }
537
538 void SpdyHttpStream::DoBufferedReadCallback() {
539   buffered_read_timer_.Stop();
540
541   // If the transaction is cancelled or errored out, we don't need to complete
542   // the read.
543   if (stream_closed_ && closed_stream_status_ != OK) {
544     if (response_callback_)
545       DoResponseCallback(closed_stream_status_);
546     return;
547   }
548
549   if (!user_buffer_.get())
550     return;
551
552   if (!response_body_queue_.IsEmpty()) {
553     int rv =
554         response_body_queue_.Dequeue(user_buffer_->data(), user_buffer_len_);
555     user_buffer_ = nullptr;
556     user_buffer_len_ = 0;
557     DoResponseCallback(rv);
558     return;
559   }
560
561   if (stream_closed_ && response_callback_)
562     DoResponseCallback(closed_stream_status_);
563 }
564
565 void SpdyHttpStream::DoRequestCallback(int rv) {
566   CHECK_NE(rv, ERR_IO_PENDING);
567   CHECK(!request_callback_.is_null());
568   // Since Run may result in being called back, reset request_callback_ in
569   // advance.
570   std::move(request_callback_).Run(rv);
571 }
572
573 void SpdyHttpStream::MaybeDoRequestCallback(int rv) {
574   CHECK_NE(ERR_IO_PENDING, rv);
575   if (request_callback_)
576     std::move(request_callback_).Run(rv);
577 }
578
579 void SpdyHttpStream::MaybePostRequestCallback(int rv) {
580   CHECK_NE(ERR_IO_PENDING, rv);
581   if (request_callback_)
582     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
583         FROM_HERE, base::BindOnce(&SpdyHttpStream::MaybeDoRequestCallback,
584                                   weak_factory_.GetWeakPtr(), rv));
585 }
586
587 void SpdyHttpStream::DoResponseCallback(int rv) {
588   CHECK_NE(rv, ERR_IO_PENDING);
589   CHECK(!response_callback_.is_null());
590
591   // Since Run may result in being called back, reset response_callback_ in
592   // advance.
593   std::move(response_callback_).Run(rv);
594 }
595
596 int SpdyHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
597   if (!spdy_session_)
598     return ERR_SOCKET_NOT_CONNECTED;
599
600   return spdy_session_->GetPeerAddress(endpoint);
601 }
602
603 void SpdyHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
604   details->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
605   return;
606 }
607
608 void SpdyHttpStream::SetPriority(RequestPriority priority) {
609   priority_ = priority;
610   if (stream_) {
611     stream_->SetPriority(priority);
612   }
613 }
614
615 const std::set<std::string>& SpdyHttpStream::GetDnsAliases() const {
616   return dns_aliases_;
617 }
618
619 base::StringPiece SpdyHttpStream::GetAcceptChViaAlps() const {
620   if (!request_info_) {
621     return {};
622   }
623
624   return session()->GetAcceptChViaAlps(url::SchemeHostPort(request_info_->url));
625 }
626
627 }  // namespace net