spdy_http_stream.cc 22 KB

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