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/http/http_stream_parser.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <string_view>
10 #include <utility>
11
12 #include "base/check.h"
13 #include "base/compiler_specific.h"
14 #include "base/functional/bind.h"
15 #include "base/logging.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/numerics/clamped_math.h"
19 #include "base/strings/string_util.h"
20 #include "base/values.h"
21 #include "net/base/features.h"
22 #include "net/base/io_buffer.h"
23 #include "net/base/ip_endpoint.h"
24 #include "net/base/upload_data_stream.h"
25 #include "net/http/http_chunked_decoder.h"
26 #include "net/http/http_connection_info.h"
27 #include "net/http/http_log_util.h"
28 #include "net/http/http_request_headers.h"
29 #include "net/http/http_request_info.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/http/http_response_info.h"
32 #include "net/http/http_status_code.h"
33 #include "net/http/http_util.h"
34 #include "net/log/net_log_event_type.h"
35 #include "net/socket/ssl_client_socket.h"
36 #include "net/socket/stream_socket.h"
37 #include "net/ssl/ssl_cert_request_info.h"
38 #include "net/ssl/ssl_info.h"
39 #include "url/url_canon.h"
40
41 namespace net {
42
43 namespace {
44
45 const uint64_t kMaxMergedHeaderAndBodySize = 1400;
46 const size_t kRequestBodyBufferSize = 1 << 14; // 16KB
47
GetResponseHeaderLines(const HttpResponseHeaders & headers)48 std::string GetResponseHeaderLines(const HttpResponseHeaders& headers) {
49 std::string raw_headers = headers.raw_headers();
50 const char* null_separated_headers = raw_headers.c_str();
51 const char* header_line = null_separated_headers;
52 std::string cr_separated_headers;
53 while (header_line[0] != 0) {
54 cr_separated_headers += header_line;
55 cr_separated_headers += "\n";
56 header_line += strlen(header_line) + 1;
57 }
58 return cr_separated_headers;
59 }
60
NetLogSendRequestBodyParams(uint64_t length,bool is_chunked,bool did_merge)61 base::Value::Dict NetLogSendRequestBodyParams(uint64_t length,
62 bool is_chunked,
63 bool did_merge) {
64 base::Value::Dict dict;
65 dict.Set("length", static_cast<int>(length));
66 dict.Set("is_chunked", is_chunked);
67 dict.Set("did_merge", did_merge);
68 return dict;
69 }
70
NetLogSendRequestBody(const NetLogWithSource & net_log,uint64_t length,bool is_chunked,bool did_merge)71 void NetLogSendRequestBody(const NetLogWithSource& net_log,
72 uint64_t length,
73 bool is_chunked,
74 bool did_merge) {
75 net_log.AddEvent(NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST_BODY, [&] {
76 return NetLogSendRequestBodyParams(length, is_chunked, did_merge);
77 });
78 }
79
80 // Returns true if |error_code| is an error for which we give the server a
81 // chance to send a body containing error information, if the error was received
82 // while trying to upload a request body.
ShouldTryReadingOnUploadError(int error_code)83 bool ShouldTryReadingOnUploadError(int error_code) {
84 return (error_code == ERR_CONNECTION_RESET);
85 }
86
87 } // namespace
88
89 // Similar to DrainableIOBuffer(), but this version comes with its own
90 // storage. The motivation is to avoid repeated allocations of
91 // DrainableIOBuffer.
92 //
93 // Example:
94 //
95 // scoped_refptr<SeekableIOBuffer> buf =
96 // base::MakeRefCounted<SeekableIOBuffer>(1024);
97 // // capacity() == 1024. size() == BytesRemaining() == BytesConsumed() == 0.
98 // // data() points to the beginning of the buffer.
99 //
100 // // Read() takes an IOBuffer.
101 // int bytes_read = some_reader->Read(buf, buf->capacity());
102 // buf->DidAppend(bytes_read);
103 // // size() == BytesRemaining() == bytes_read. data() is unaffected.
104 //
105 // while (buf->BytesRemaining() > 0) {
106 // // Write() takes an IOBuffer. If it takes const char*, we could
107 /// // simply use the regular IOBuffer like buf->data() + offset.
108 // int bytes_written = Write(buf, buf->BytesRemaining());
109 // buf->DidConsume(bytes_written);
110 // }
111 // // BytesRemaining() == 0. BytesConsumed() == size().
112 // // data() points to the end of the consumed bytes (exclusive).
113 //
114 // // If you want to reuse the buffer, be sure to clear the buffer.
115 // buf->Clear();
116 // // size() == BytesRemaining() == BytesConsumed() == 0.
117 // // data() points to the beginning of the buffer.
118 //
119 class HttpStreamParser::SeekableIOBuffer : public IOBufferWithSize {
120 public:
SeekableIOBuffer(int capacity)121 explicit SeekableIOBuffer(int capacity)
122 : IOBufferWithSize(capacity), real_data_(data_), capacity_(capacity) {}
123
124 // DidConsume() changes the |data_| pointer so that |data_| always points
125 // to the first unconsumed byte.
DidConsume(int bytes)126 void DidConsume(int bytes) {
127 SetOffset(used_ + bytes);
128 }
129
130 // Returns the number of unconsumed bytes.
BytesRemaining() const131 int BytesRemaining() const {
132 return size_ - used_;
133 }
134
135 // Seeks to an arbitrary point in the buffer. The notion of bytes consumed
136 // and remaining are updated appropriately.
SetOffset(int bytes)137 void SetOffset(int bytes) {
138 DCHECK_GE(bytes, 0);
139 DCHECK_LE(bytes, size_);
140 used_ = bytes;
141 data_ = real_data_ + used_;
142 }
143
144 // Called after data is added to the buffer. Adds |bytes| added to
145 // |size_|. data() is unaffected.
DidAppend(int bytes)146 void DidAppend(int bytes) {
147 DCHECK_GE(bytes, 0);
148 DCHECK_GE(size_ + bytes, 0);
149 DCHECK_LE(size_ + bytes, capacity_);
150 size_ += bytes;
151 }
152
153 // Changes the logical size to 0, and the offset to 0.
Clear()154 void Clear() {
155 size_ = 0;
156 SetOffset(0);
157 }
158
159 // Returns the logical size of the buffer (i.e the number of bytes of data
160 // in the buffer).
size() const161 int size() const { return size_; }
162
163 // Returns the capacity of the buffer. The capacity is the size used when
164 // the object is created.
capacity() const165 int capacity() const { return capacity_; }
166
167 private:
~SeekableIOBuffer()168 ~SeekableIOBuffer() override {
169 // data_ will be deleted in IOBuffer::~IOBuffer().
170 data_ = real_data_;
171 }
172
173 // DanglingUntriaged because it is assigned a DanglingUntriaged pointer.
174 raw_ptr<char, AcrossTasksDanglingUntriaged | AllowPtrArithmetic> real_data_;
175 const int capacity_;
176 int size_ = 0;
177 int used_ = 0;
178 };
179
180 // 2 CRLFs + max of 8 hex chars.
181 const size_t HttpStreamParser::kChunkHeaderFooterSize = 12;
182
HttpStreamParser(StreamSocket * stream_socket,bool connection_is_reused,const HttpRequestInfo * request,GrowableIOBuffer * read_buffer,const NetLogWithSource & net_log)183 HttpStreamParser::HttpStreamParser(StreamSocket* stream_socket,
184 bool connection_is_reused,
185 const HttpRequestInfo* request,
186 GrowableIOBuffer* read_buffer,
187 const NetLogWithSource& net_log)
188 : request_(request),
189 read_buf_(read_buffer),
190 response_header_start_offset_(std::string::npos),
191 stream_socket_(stream_socket),
192 connection_is_reused_(connection_is_reused),
193 net_log_(net_log),
194 truncate_to_content_length_enabled_(base::FeatureList::IsEnabled(
195 net::features::kTruncateBodyToContentLength)) {
196 io_callback_ = base::BindRepeating(&HttpStreamParser::OnIOComplete,
197 weak_ptr_factory_.GetWeakPtr());
198 }
199
200 HttpStreamParser::~HttpStreamParser() = default;
201
SendRequest(const std::string & request_line,const HttpRequestHeaders & headers,const NetworkTrafficAnnotationTag & traffic_annotation,HttpResponseInfo * response,CompletionOnceCallback callback)202 int HttpStreamParser::SendRequest(
203 const std::string& request_line,
204 const HttpRequestHeaders& headers,
205 const NetworkTrafficAnnotationTag& traffic_annotation,
206 HttpResponseInfo* response,
207 CompletionOnceCallback callback) {
208 DCHECK_EQ(STATE_NONE, io_state_);
209 DCHECK(callback_.is_null());
210 DCHECK(!callback.is_null());
211 DCHECK(response);
212
213 NetLogRequestHeaders(net_log_,
214 NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
215 request_line, &headers);
216
217 DVLOG(1) << __func__ << "() request_line = \"" << request_line << "\""
218 << " headers = \"" << headers.ToString() << "\"";
219 traffic_annotation_ = MutableNetworkTrafficAnnotationTag(traffic_annotation);
220 response_ = response;
221
222 // Put the peer's IP address and port into the response.
223 IPEndPoint ip_endpoint;
224 int result = stream_socket_->GetPeerAddress(&ip_endpoint);
225 if (result != OK)
226 return result;
227 response_->remote_endpoint = ip_endpoint;
228
229 std::string request = request_line + headers.ToString();
230 request_headers_length_ = request.size();
231
232 if (request_->upload_data_stream != nullptr) {
233 request_body_send_buf_ =
234 base::MakeRefCounted<SeekableIOBuffer>(kRequestBodyBufferSize);
235 if (request_->upload_data_stream->is_chunked()) {
236 // Read buffer is adjusted to guarantee that |request_body_send_buf_| is
237 // large enough to hold the encoded chunk.
238 request_body_read_buf_ = base::MakeRefCounted<SeekableIOBuffer>(
239 kRequestBodyBufferSize - kChunkHeaderFooterSize);
240 } else {
241 // No need to encode request body, just send the raw data.
242 request_body_read_buf_ = request_body_send_buf_;
243 }
244 }
245
246 io_state_ = STATE_SEND_HEADERS;
247
248 // If we have a small request body, then we'll merge with the headers into a
249 // single write.
250 bool did_merge = false;
251 if (ShouldMergeRequestHeadersAndBody(request, request_->upload_data_stream)) {
252 int merged_size = static_cast<int>(
253 request_headers_length_ + request_->upload_data_stream->size());
254 auto merged_request_headers_and_body =
255 base::MakeRefCounted<IOBufferWithSize>(merged_size);
256 // We'll repurpose |request_headers_| to store the merged headers and
257 // body.
258 request_headers_ = base::MakeRefCounted<DrainableIOBuffer>(
259 merged_request_headers_and_body, merged_size);
260
261 memcpy(request_headers_->data(), request.data(), request_headers_length_);
262 request_headers_->DidConsume(request_headers_length_);
263
264 uint64_t todo = request_->upload_data_stream->size();
265 while (todo) {
266 int consumed = request_->upload_data_stream->Read(
267 request_headers_.get(), static_cast<int>(todo),
268 CompletionOnceCallback());
269 // Read() must succeed synchronously if not chunked and in memory.
270 DCHECK_GT(consumed, 0);
271 request_headers_->DidConsume(consumed);
272 todo -= consumed;
273 }
274 DCHECK(request_->upload_data_stream->IsEOF());
275 // Reset the offset, so the buffer can be read from the beginning.
276 request_headers_->SetOffset(0);
277 did_merge = true;
278
279 NetLogSendRequestBody(net_log_, request_->upload_data_stream->size(),
280 false, /* not chunked */
281 true /* merged */);
282 }
283
284 if (!did_merge) {
285 // If we didn't merge the body with the headers, then |request_headers_|
286 // contains just the HTTP headers.
287 size_t request_size = request.size();
288 scoped_refptr<StringIOBuffer> headers_io_buf =
289 base::MakeRefCounted<StringIOBuffer>(std::move(request));
290 request_headers_ = base::MakeRefCounted<DrainableIOBuffer>(
291 std::move(headers_io_buf), request_size);
292 }
293
294 result = DoLoop(OK);
295 if (result == ERR_IO_PENDING)
296 callback_ = std::move(callback);
297
298 return result > 0 ? OK : result;
299 }
300
ConfirmHandshake(CompletionOnceCallback callback)301 int HttpStreamParser::ConfirmHandshake(CompletionOnceCallback callback) {
302 int ret = stream_socket_->ConfirmHandshake(
303 base::BindOnce(&HttpStreamParser::RunConfirmHandshakeCallback,
304 weak_ptr_factory_.GetWeakPtr()));
305 if (ret == ERR_IO_PENDING)
306 confirm_handshake_callback_ = std::move(callback);
307 return ret;
308 }
309
ReadResponseHeaders(CompletionOnceCallback callback)310 int HttpStreamParser::ReadResponseHeaders(CompletionOnceCallback callback) {
311 DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
312 DCHECK(callback_.is_null());
313 DCHECK(!callback.is_null());
314 DCHECK_EQ(0, read_buf_unused_offset_);
315 DCHECK(SendRequestBuffersEmpty());
316
317 // This function can be called with io_state_ == STATE_DONE if the
318 // connection is closed after seeing just a 1xx response code.
319 if (io_state_ == STATE_DONE)
320 return ERR_CONNECTION_CLOSED;
321
322 int result = OK;
323 io_state_ = STATE_READ_HEADERS;
324
325 if (read_buf_->offset() > 0) {
326 // Simulate the state where the data was just read from the socket.
327 result = read_buf_->offset();
328 read_buf_->set_offset(0);
329 }
330 if (result > 0)
331 io_state_ = STATE_READ_HEADERS_COMPLETE;
332
333 result = DoLoop(result);
334 if (result == ERR_IO_PENDING)
335 callback_ = std::move(callback);
336
337 return result > 0 ? OK : result;
338 }
339
ReadResponseBody(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)340 int HttpStreamParser::ReadResponseBody(IOBuffer* buf,
341 int buf_len,
342 CompletionOnceCallback callback) {
343 DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE);
344 DCHECK(callback_.is_null());
345 DCHECK(!callback.is_null());
346 DCHECK_LE(buf_len, kMaxBufSize);
347 DCHECK(SendRequestBuffersEmpty());
348 // Added to investigate crbug.com/499663.
349 CHECK(buf);
350
351 if (io_state_ == STATE_DONE)
352 return OK;
353
354 user_read_buf_ = buf;
355 user_read_buf_len_ = buf_len;
356 io_state_ = STATE_READ_BODY;
357
358 // Invalidate HttpRequestInfo pointer. This is to allow the stream to be
359 // shared across multiple consumers.
360 // It is safe to reset it at this point since request_->upload_data_stream
361 // is also not needed anymore.
362 request_ = nullptr;
363
364 int result = DoLoop(OK);
365 if (result == ERR_IO_PENDING)
366 callback_ = std::move(callback);
367
368 return result;
369 }
370
OnIOComplete(int result)371 void HttpStreamParser::OnIOComplete(int result) {
372 result = DoLoop(result);
373
374 // The client callback can do anything, including destroying this class,
375 // so any pending callback must be issued after everything else is done.
376 if (result != ERR_IO_PENDING && !callback_.is_null()) {
377 std::move(callback_).Run(result);
378 }
379 }
380
DoLoop(int result)381 int HttpStreamParser::DoLoop(int result) {
382 do {
383 DCHECK_NE(ERR_IO_PENDING, result);
384 DCHECK_NE(STATE_DONE, io_state_);
385 DCHECK_NE(STATE_NONE, io_state_);
386 State state = io_state_;
387 io_state_ = STATE_NONE;
388 switch (state) {
389 case STATE_SEND_HEADERS:
390 DCHECK_EQ(OK, result);
391 result = DoSendHeaders();
392 DCHECK_NE(STATE_NONE, io_state_);
393 break;
394 case STATE_SEND_HEADERS_COMPLETE:
395 result = DoSendHeadersComplete(result);
396 DCHECK_NE(STATE_NONE, io_state_);
397 break;
398 case STATE_SEND_BODY:
399 DCHECK_EQ(OK, result);
400 result = DoSendBody();
401 DCHECK_NE(STATE_NONE, io_state_);
402 break;
403 case STATE_SEND_BODY_COMPLETE:
404 result = DoSendBodyComplete(result);
405 DCHECK_NE(STATE_NONE, io_state_);
406 break;
407 case STATE_SEND_REQUEST_READ_BODY_COMPLETE:
408 result = DoSendRequestReadBodyComplete(result);
409 DCHECK_NE(STATE_NONE, io_state_);
410 break;
411 case STATE_SEND_REQUEST_COMPLETE:
412 result = DoSendRequestComplete(result);
413 break;
414 case STATE_READ_HEADERS:
415 net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_PARSER_READ_HEADERS);
416 DCHECK_GE(result, 0);
417 result = DoReadHeaders();
418 break;
419 case STATE_READ_HEADERS_COMPLETE:
420 result = DoReadHeadersComplete(result);
421 net_log_.EndEventWithNetErrorCode(
422 NetLogEventType::HTTP_STREAM_PARSER_READ_HEADERS, result);
423 break;
424 case STATE_READ_BODY:
425 DCHECK_GE(result, 0);
426 result = DoReadBody();
427 break;
428 case STATE_READ_BODY_COMPLETE:
429 result = DoReadBodyComplete(result);
430 break;
431 default:
432 NOTREACHED();
433 break;
434 }
435 } while (result != ERR_IO_PENDING &&
436 (io_state_ != STATE_DONE && io_state_ != STATE_NONE));
437
438 return result;
439 }
440
DoSendHeaders()441 int HttpStreamParser::DoSendHeaders() {
442 int bytes_remaining = request_headers_->BytesRemaining();
443 DCHECK_GT(bytes_remaining, 0);
444
445 // Record our best estimate of the 'request time' as the time when we send
446 // out the first bytes of the request headers.
447 if (bytes_remaining == request_headers_->size())
448 response_->request_time = base::Time::Now();
449
450 io_state_ = STATE_SEND_HEADERS_COMPLETE;
451 return stream_socket_->Write(
452 request_headers_.get(), bytes_remaining, io_callback_,
453 NetworkTrafficAnnotationTag(traffic_annotation_));
454 }
455
DoSendHeadersComplete(int result)456 int HttpStreamParser::DoSendHeadersComplete(int result) {
457 if (result < 0) {
458 // In the unlikely case that the headers and body were merged, all the
459 // the headers were sent, but not all of the body way, and |result| is
460 // an error that this should try reading after, stash the error for now and
461 // act like the request was successfully sent.
462 io_state_ = STATE_SEND_REQUEST_COMPLETE;
463 if (request_headers_->BytesConsumed() >= request_headers_length_ &&
464 ShouldTryReadingOnUploadError(result)) {
465 upload_error_ = result;
466 return OK;
467 }
468 return result;
469 }
470
471 sent_bytes_ += result;
472 request_headers_->DidConsume(result);
473 if (request_headers_->BytesRemaining() > 0) {
474 io_state_ = STATE_SEND_HEADERS;
475 return OK;
476 }
477
478 if (request_->upload_data_stream != nullptr &&
479 (request_->upload_data_stream->is_chunked() ||
480 // !IsEOF() indicates that the body wasn't merged.
481 (request_->upload_data_stream->size() > 0 &&
482 !request_->upload_data_stream->IsEOF()))) {
483 NetLogSendRequestBody(net_log_, request_->upload_data_stream->size(),
484 request_->upload_data_stream->is_chunked(),
485 false /* not merged */);
486 io_state_ = STATE_SEND_BODY;
487 return OK;
488 }
489
490 // Finished sending the request.
491 io_state_ = STATE_SEND_REQUEST_COMPLETE;
492 return OK;
493 }
494
DoSendBody()495 int HttpStreamParser::DoSendBody() {
496 if (request_body_send_buf_->BytesRemaining() > 0) {
497 io_state_ = STATE_SEND_BODY_COMPLETE;
498 return stream_socket_->Write(
499 request_body_send_buf_.get(), request_body_send_buf_->BytesRemaining(),
500 io_callback_, NetworkTrafficAnnotationTag(traffic_annotation_));
501 }
502
503 if (request_->upload_data_stream->is_chunked() && sent_last_chunk_) {
504 // Finished sending the request.
505 io_state_ = STATE_SEND_REQUEST_COMPLETE;
506 return OK;
507 }
508
509 request_body_read_buf_->Clear();
510 io_state_ = STATE_SEND_REQUEST_READ_BODY_COMPLETE;
511 return request_->upload_data_stream->Read(
512 request_body_read_buf_.get(), request_body_read_buf_->capacity(),
513 base::BindOnce(&HttpStreamParser::OnIOComplete,
514 weak_ptr_factory_.GetWeakPtr()));
515 }
516
DoSendBodyComplete(int result)517 int HttpStreamParser::DoSendBodyComplete(int result) {
518 if (result < 0) {
519 // If |result| is an error that this should try reading after, stash the
520 // error for now and act like the request was successfully sent.
521 io_state_ = STATE_SEND_REQUEST_COMPLETE;
522 if (ShouldTryReadingOnUploadError(result)) {
523 upload_error_ = result;
524 return OK;
525 }
526 return result;
527 }
528
529 sent_bytes_ += result;
530 request_body_send_buf_->DidConsume(result);
531
532 io_state_ = STATE_SEND_BODY;
533 return OK;
534 }
535
DoSendRequestReadBodyComplete(int result)536 int HttpStreamParser::DoSendRequestReadBodyComplete(int result) {
537 // |result| is the result of read from the request body from the last call to
538 // DoSendBody().
539 if (result < 0) {
540 io_state_ = STATE_SEND_REQUEST_COMPLETE;
541 return result;
542 }
543
544 // Chunked data needs to be encoded.
545 if (request_->upload_data_stream->is_chunked()) {
546 if (result == 0) { // Reached the end.
547 DCHECK(request_->upload_data_stream->IsEOF());
548 sent_last_chunk_ = true;
549 }
550 // Encode the buffer as 1 chunk.
551 const std::string_view payload(request_body_read_buf_->data(), result);
552 request_body_send_buf_->Clear();
553 result = EncodeChunk(payload,
554 request_body_send_buf_->data(),
555 request_body_send_buf_->capacity());
556 }
557
558 if (result == 0) { // Reached the end.
559 // Reaching EOF means we can finish sending request body unless the data is
560 // chunked. (i.e. No need to send the terminal chunk.)
561 DCHECK(request_->upload_data_stream->IsEOF());
562 DCHECK(!request_->upload_data_stream->is_chunked());
563 // Finished sending the request.
564 io_state_ = STATE_SEND_REQUEST_COMPLETE;
565 } else if (result > 0) {
566 request_body_send_buf_->DidAppend(result);
567 result = 0;
568 io_state_ = STATE_SEND_BODY;
569 }
570 return result;
571 }
572
DoSendRequestComplete(int result)573 int HttpStreamParser::DoSendRequestComplete(int result) {
574 DCHECK_NE(result, ERR_IO_PENDING);
575 request_headers_ = nullptr;
576 request_body_send_buf_ = nullptr;
577 request_body_read_buf_ = nullptr;
578
579 return result;
580 }
581
DoReadHeaders()582 int HttpStreamParser::DoReadHeaders() {
583 io_state_ = STATE_READ_HEADERS_COMPLETE;
584
585 // Grow the read buffer if necessary.
586 if (read_buf_->RemainingCapacity() == 0)
587 read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
588
589 // http://crbug.com/16371: We're seeing |user_buf_->data()| return NULL.
590 // See if the user is passing in an IOBuffer with a NULL |data_|.
591 CHECK(read_buf_->data());
592
593 return stream_socket_->Read(read_buf_.get(), read_buf_->RemainingCapacity(),
594 io_callback_);
595 }
596
DoReadHeadersComplete(int result)597 int HttpStreamParser::DoReadHeadersComplete(int result) {
598 // DoReadHeadersComplete is called with the result of Socket::Read, which is a
599 // (byte_count | error), and returns (error | OK).
600
601 result = HandleReadHeaderResult(result);
602
603 // TODO(mmenke): The code below is ugly and hacky. A much better and more
604 // flexible long term solution would be to separate out the read and write
605 // loops, though this would involve significant changes, both here and
606 // elsewhere (WebSockets, for instance).
607
608 // If still reading the headers, or there was no error uploading the request
609 // body, just return the result.
610 if (io_state_ == STATE_READ_HEADERS || upload_error_ == OK)
611 return result;
612
613 // If the result is ERR_IO_PENDING, |io_state_| should be STATE_READ_HEADERS.
614 DCHECK_NE(ERR_IO_PENDING, result);
615
616 // On errors, use the original error received when sending the request.
617 // The main cases where these are different is when there's a header-related
618 // error code, or when there's an ERR_CONNECTION_CLOSED, which can result in
619 // special handling of partial responses and HTTP/0.9 responses.
620 if (result < 0) {
621 // Nothing else to do. In the HTTP/0.9 or only partial headers received
622 // cases, can normally go to other states after an error reading headers.
623 io_state_ = STATE_DONE;
624 // Don't let caller see the headers.
625 response_->headers = nullptr;
626 return upload_error_;
627 }
628
629 // Skip over 1xx responses as usual, and allow 4xx/5xx error responses to
630 // override the error received while uploading the body.
631 int response_code_class = response_->headers->response_code() / 100;
632 if (response_code_class == 1 || response_code_class == 4 ||
633 response_code_class == 5) {
634 return result;
635 }
636
637 // All other status codes are not allowed after an error during upload, to
638 // make sure the consumer has some indication there was an error.
639
640 // Nothing else to do.
641 io_state_ = STATE_DONE;
642 // Don't let caller see the headers.
643 response_->headers = nullptr;
644 return upload_error_;
645 }
646
DoReadBody()647 int HttpStreamParser::DoReadBody() {
648 io_state_ = STATE_READ_BODY_COMPLETE;
649
650 // Added to investigate crbug.com/499663.
651 CHECK(user_read_buf_.get());
652
653 // There may be additional data after the end of the body waiting in
654 // the socket, but in order to find out, we need to read as much as possible.
655 // If there is additional data, discard it and close the connection later.
656 int64_t remaining_read_len = user_read_buf_len_;
657 int64_t remaining_body = 0;
658 if (truncate_to_content_length_enabled_ && !chunked_decoder_.get() &&
659 response_body_length_ >= 0) {
660 remaining_body = response_body_length_ - response_body_read_;
661 remaining_read_len = std::min(remaining_read_len, remaining_body);
662 }
663
664 // There may be some data left over from reading the response headers.
665 if (read_buf_->offset()) {
666 int64_t available = read_buf_->offset() - read_buf_unused_offset_;
667 if (available) {
668 CHECK_GT(available, 0);
669 int64_t bytes_from_buffer = std::min(available, remaining_read_len);
670 memcpy(user_read_buf_->data(),
671 read_buf_->StartOfBuffer() + read_buf_unused_offset_,
672 bytes_from_buffer);
673 read_buf_unused_offset_ += bytes_from_buffer;
674 // Clear out the remaining data if we've reached the end of the body.
675 if (truncate_to_content_length_enabled_ &&
676 (remaining_body == bytes_from_buffer) &&
677 (available > bytes_from_buffer)) {
678 read_buf_->SetCapacity(0);
679 read_buf_unused_offset_ = 0;
680 discarded_extra_data_ = true;
681 } else if (bytes_from_buffer == available) {
682 read_buf_->SetCapacity(0);
683 read_buf_unused_offset_ = 0;
684 }
685 return bytes_from_buffer;
686 } else {
687 read_buf_->SetCapacity(0);
688 read_buf_unused_offset_ = 0;
689 }
690 }
691
692 // Check to see if we're done reading.
693 if (IsResponseBodyComplete())
694 return 0;
695
696 // DoReadBodyComplete will truncate the amount read if necessary whether the
697 // read completes synchronously or asynchronously.
698 DCHECK_EQ(0, read_buf_->offset());
699 return stream_socket_->Read(user_read_buf_.get(), user_read_buf_len_,
700 io_callback_);
701 }
702
DoReadBodyComplete(int result)703 int HttpStreamParser::DoReadBodyComplete(int result) {
704 // Check to see if we've read too much and need to discard data before we
705 // increment received_bytes_ and response_body_read_ or otherwise start
706 // processing the data.
707 if (truncate_to_content_length_enabled_ && !chunked_decoder_.get() &&
708 response_body_length_ >= 0) {
709 // Calculate how much we should have been allowed to read to not go beyond
710 // the Content-Length.
711 int64_t remaining_body = response_body_length_ - response_body_read_;
712 int64_t remaining_read_len =
713 std::min(static_cast<int64_t>(user_read_buf_len_), remaining_body);
714 if (result > remaining_read_len) {
715 // Truncate to only what is in the body.
716 result = remaining_read_len;
717 discarded_extra_data_ = true;
718 }
719 }
720
721 // When the connection is closed, there are numerous ways to interpret it.
722 //
723 // - If a Content-Length header is present and the body contains exactly that
724 // number of bytes at connection close, the response is successful.
725 //
726 // - If a Content-Length header is present and the body contains fewer bytes
727 // than promised by the header at connection close, it may indicate that
728 // the connection was closed prematurely, or it may indicate that the
729 // server sent an invalid Content-Length header. Unfortunately, the invalid
730 // Content-Length header case does occur in practice and other browsers are
731 // tolerant of it. We choose to treat it as an error for now, but the
732 // download system treats it as a non-error, and URLRequestHttpJob also
733 // treats it as OK if the Content-Length is the post-decoded body content
734 // length.
735 //
736 // - If chunked encoding is used and the terminating chunk has been processed
737 // when the connection is closed, the response is successful.
738 //
739 // - If chunked encoding is used and the terminating chunk has not been
740 // processed when the connection is closed, it may indicate that the
741 // connection was closed prematurely or it may indicate that the server
742 // sent an invalid chunked encoding. We choose to treat it as
743 // an invalid chunked encoding.
744 //
745 // - If a Content-Length is not present and chunked encoding is not used,
746 // connection close is the only way to signal that the response is
747 // complete. Unfortunately, this also means that there is no way to detect
748 // early close of a connection. No error is returned.
749 if (result == 0 && !IsResponseBodyComplete() && CanFindEndOfResponse()) {
750 if (chunked_decoder_.get())
751 result = ERR_INCOMPLETE_CHUNKED_ENCODING;
752 else
753 result = ERR_CONTENT_LENGTH_MISMATCH;
754 }
755
756 if (result > 0)
757 received_bytes_ += result;
758
759 // Filter incoming data if appropriate. FilterBuf may return an error.
760 if (result > 0 && chunked_decoder_.get()) {
761 result = chunked_decoder_->FilterBuf(user_read_buf_->data(), result);
762 if (result == 0 && !chunked_decoder_->reached_eof()) {
763 // Don't signal completion of the Read call yet or else it'll look like
764 // we received end-of-file. Wait for more data.
765 io_state_ = STATE_READ_BODY;
766 return OK;
767 }
768 }
769
770 if (result > 0)
771 response_body_read_ += result;
772
773 if (result <= 0 || IsResponseBodyComplete()) {
774 io_state_ = STATE_DONE;
775
776 // Save the overflow data, which can be in two places. There may be
777 // some left over in |user_read_buf_|, plus there may be more
778 // in |read_buf_|. But the part left over in |user_read_buf_| must have
779 // come from the |read_buf_|, so there's room to put it back at the
780 // start first.
781 int additional_save_amount = read_buf_->offset() - read_buf_unused_offset_;
782 int save_amount = 0;
783 if (chunked_decoder_.get()) {
784 save_amount = chunked_decoder_->bytes_after_eof();
785 } else if (response_body_length_ >= 0) {
786 int64_t extra_data_read = response_body_read_ - response_body_length_;
787 if (extra_data_read > 0) {
788 save_amount = static_cast<int>(extra_data_read);
789 if (result > 0)
790 result -= save_amount;
791 }
792 }
793
794 CHECK_LE(save_amount + additional_save_amount, kMaxBufSize);
795 if (read_buf_->capacity() < save_amount + additional_save_amount) {
796 read_buf_->SetCapacity(save_amount + additional_save_amount);
797 }
798
799 if (save_amount) {
800 received_bytes_ -= save_amount;
801 memcpy(read_buf_->StartOfBuffer(), user_read_buf_->data() + result,
802 save_amount);
803 }
804 read_buf_->set_offset(save_amount);
805 if (additional_save_amount) {
806 memmove(read_buf_->data(),
807 read_buf_->StartOfBuffer() + read_buf_unused_offset_,
808 additional_save_amount);
809 read_buf_->set_offset(save_amount + additional_save_amount);
810 }
811 read_buf_unused_offset_ = 0;
812 } else {
813 // Now waiting for more of the body to be read.
814 user_read_buf_ = nullptr;
815 user_read_buf_len_ = 0;
816 }
817
818 return result;
819 }
820
HandleReadHeaderResult(int result)821 int HttpStreamParser::HandleReadHeaderResult(int result) {
822 DCHECK_EQ(0, read_buf_unused_offset_);
823
824 if (result == 0)
825 result = ERR_CONNECTION_CLOSED;
826
827 if (result == ERR_CONNECTION_CLOSED) {
828 // The connection closed without getting any more data.
829 if (read_buf_->offset() == 0) {
830 io_state_ = STATE_DONE;
831 // If the connection has not been reused, it may have been a 0-length
832 // HTTP/0.9 responses, but it was most likely an error, so just return
833 // ERR_EMPTY_RESPONSE instead. If the connection was reused, just pass
834 // on the original connection close error, as rather than being an
835 // empty HTTP/0.9 response it's much more likely the server closed the
836 // socket before it received the request.
837 if (!connection_is_reused_)
838 return ERR_EMPTY_RESPONSE;
839 return result;
840 }
841
842 // Accepting truncated headers over HTTPS is a potential security
843 // vulnerability, so just return an error in that case.
844 //
845 // If response_header_start_offset_ is std::string::npos, this may be a < 8
846 // byte HTTP/0.9 response. However, accepting such a response over HTTPS
847 // would allow a MITM to truncate an HTTP/1.x status line to look like a
848 // short HTTP/0.9 response if the peer put a record boundary at the first 8
849 // bytes. To ensure that all response headers received over HTTPS are
850 // pristine, treat such responses as errors.
851 //
852 // TODO(mmenke): Returning ERR_RESPONSE_HEADERS_TRUNCATED when a response
853 // looks like an HTTP/0.9 response is weird. Should either come up with
854 // another error code, or, better, disable HTTP/0.9 over HTTPS (and give
855 // that a new error code).
856 if (request_->url.SchemeIsCryptographic()) {
857 io_state_ = STATE_DONE;
858 return ERR_RESPONSE_HEADERS_TRUNCATED;
859 }
860
861 // Parse things as well as we can and let the caller decide what to do.
862 int end_offset;
863 if (response_header_start_offset_ != std::string::npos) {
864 // The response looks to be a truncated set of HTTP headers.
865 io_state_ = STATE_READ_BODY_COMPLETE;
866 end_offset = read_buf_->offset();
867 } else {
868 // The response is apparently using HTTP/0.9. Treat the entire response
869 // as the body.
870 end_offset = 0;
871 }
872 int rv = ParseResponseHeaders(end_offset);
873 if (rv < 0)
874 return rv;
875 return result;
876 }
877
878 if (result < 0) {
879 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
880 // TODO(https://crbug.com/332234173): Assuming this isn't hit, remove the
881 // SchemeIsCryptographic() check.
882 DUMP_WILL_BE_CHECK(request_->url.SchemeIsCryptographic());
883 if (request_->url.SchemeIsCryptographic()) {
884 response_->cert_request_info =
885 base::MakeRefCounted<SSLCertRequestInfo>();
886 stream_socket_->GetSSLCertRequestInfo(
887 response_->cert_request_info.get());
888 }
889 }
890 io_state_ = STATE_DONE;
891 return result;
892 }
893
894 // Record our best estimate of the 'response time' as the time when we read
895 // the first bytes of the response headers.
896 if (read_buf_->offset() == 0) {
897 response_->response_time = base::Time::Now();
898 // Also keep the time as base::TimeTicks for `first_response_start_time_`
899 // and `non_informational_response_start_time_`.
900 current_response_start_time_ = base::TimeTicks::Now();
901 }
902
903 // For |first_response_start_time_|, use the time that we received the first
904 // byte of *any* response- including 1XX, as per the resource timing spec for
905 // responseStart (see note at
906 // https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-responsestart).
907 if (first_response_start_time_.is_null())
908 first_response_start_time_ = current_response_start_time_;
909
910 read_buf_->set_offset(read_buf_->offset() + result);
911 DCHECK_LE(read_buf_->offset(), read_buf_->capacity());
912 DCHECK_GT(result, 0);
913
914 int end_of_header_offset = FindAndParseResponseHeaders(result);
915
916 // Note: -1 is special, it indicates we haven't found the end of headers.
917 // Anything less than -1 is a net::Error, so we bail out.
918 if (end_of_header_offset < -1)
919 return end_of_header_offset;
920
921 if (end_of_header_offset == -1) {
922 io_state_ = STATE_READ_HEADERS;
923 // Prevent growing the headers buffer indefinitely.
924 if (read_buf_->offset() >= kMaxHeaderBufSize) {
925 io_state_ = STATE_DONE;
926 return ERR_RESPONSE_HEADERS_TOO_BIG;
927 }
928 } else {
929 CalculateResponseBodySize();
930
931 // Record the response start time if this response is not informational
932 // (non-1xx).
933 if (response_->headers->response_code() / 100 != 1) {
934 DCHECK(non_informational_response_start_time_.is_null());
935 non_informational_response_start_time_ = current_response_start_time_;
936 }
937
938 // If the body is zero length, the caller may not call ReadResponseBody,
939 // which is where any extra data is copied to read_buf_, so we move the
940 // data here.
941 if (response_body_length_ == 0) {
942 int extra_bytes = read_buf_->offset() - end_of_header_offset;
943 if (extra_bytes) {
944 CHECK_GT(extra_bytes, 0);
945 memmove(read_buf_->StartOfBuffer(),
946 read_buf_->StartOfBuffer() + end_of_header_offset,
947 extra_bytes);
948 }
949 read_buf_->SetCapacity(extra_bytes);
950 if (response_->headers->response_code() / 100 == 1) {
951 // After processing a 1xx response, the caller will ask for the next
952 // header, so reset state to support that. We don't completely ignore a
953 // 1xx response because it cannot be returned in reply to a CONNECT
954 // request so we return OK here, which lets the caller inspect the
955 // response and reject it in the event that we're setting up a CONNECT
956 // tunnel.
957 response_header_start_offset_ = std::string::npos;
958 response_body_length_ = -1;
959 // Record the timing of the 103 Early Hints response for the experiment
960 // (https://crbug.com/1093693).
961 if (response_->headers->response_code() == net::HTTP_EARLY_HINTS &&
962 first_early_hints_time_.is_null()) {
963 first_early_hints_time_ = current_response_start_time_;
964 }
965 // Now waiting for the second set of headers to be read.
966 } else {
967 // Only set keep-alive based on final set of headers.
968 response_is_keep_alive_ = response_->headers->IsKeepAlive();
969
970 io_state_ = STATE_DONE;
971 }
972 return OK;
973 }
974
975 // Only set keep-alive based on final set of headers.
976 response_is_keep_alive_ = response_->headers->IsKeepAlive();
977
978 // Note where the headers stop.
979 read_buf_unused_offset_ = end_of_header_offset;
980 // Now waiting for the body to be read.
981 }
982 return OK;
983 }
984
RunConfirmHandshakeCallback(int rv)985 void HttpStreamParser::RunConfirmHandshakeCallback(int rv) {
986 std::move(confirm_handshake_callback_).Run(rv);
987 }
988
FindAndParseResponseHeaders(int new_bytes)989 int HttpStreamParser::FindAndParseResponseHeaders(int new_bytes) {
990 DCHECK_GT(new_bytes, 0);
991 DCHECK_EQ(0, read_buf_unused_offset_);
992 size_t end_offset = std::string::npos;
993
994 // Look for the start of the status line, if it hasn't been found yet.
995 if (response_header_start_offset_ == std::string::npos) {
996 response_header_start_offset_ = HttpUtil::LocateStartOfStatusLine(
997 read_buf_->StartOfBuffer(), read_buf_->offset());
998 }
999
1000 if (response_header_start_offset_ != std::string::npos) {
1001 // LocateEndOfHeaders looks for two line breaks in a row (With or without
1002 // carriage returns). So the end of the headers includes at most the last 3
1003 // bytes of the buffer from the past read. This optimization avoids O(n^2)
1004 // performance in the case each read only returns a couple bytes. It's not
1005 // too important in production, but for fuzzers with memory instrumentation,
1006 // it's needed to avoid timing out.
1007 size_t lower_bound =
1008 (base::ClampedNumeric<size_t>(read_buf_->offset()) - new_bytes - 3)
1009 .RawValue();
1010 size_t search_start = std::max(response_header_start_offset_, lower_bound);
1011 end_offset = HttpUtil::LocateEndOfHeaders(
1012 read_buf_->StartOfBuffer(), read_buf_->offset(), search_start);
1013 } else if (read_buf_->offset() >= 8) {
1014 // Enough data to decide that this is an HTTP/0.9 response.
1015 // 8 bytes = (4 bytes of junk) + "http".length()
1016 end_offset = 0;
1017 }
1018
1019 if (end_offset == std::string::npos)
1020 return -1;
1021
1022 int rv = ParseResponseHeaders(end_offset);
1023 if (rv < 0)
1024 return rv;
1025 return end_offset;
1026 }
1027
ParseResponseHeaders(int end_offset)1028 int HttpStreamParser::ParseResponseHeaders(int end_offset) {
1029 scoped_refptr<HttpResponseHeaders> headers;
1030 DCHECK_EQ(0, read_buf_unused_offset_);
1031
1032 if (response_header_start_offset_ != std::string::npos) {
1033 received_bytes_ += end_offset;
1034 headers = HttpResponseHeaders::TryToCreate(
1035 std::string_view(read_buf_->StartOfBuffer(), end_offset));
1036 if (!headers)
1037 return net::ERR_INVALID_HTTP_RESPONSE;
1038 has_seen_status_line_ = true;
1039 } else {
1040 // Enough data was read -- there is no status line, so this is HTTP/0.9, or
1041 // the server is broken / doesn't speak HTTP.
1042
1043 if (has_seen_status_line_) {
1044 // If we saw a status line previously, the server can speak HTTP/1.x so it
1045 // is not reasonable to interpret the response as an HTTP/0.9 response.
1046 return ERR_INVALID_HTTP_RESPONSE;
1047 }
1048
1049 std::string_view scheme = request_->url.scheme_piece();
1050 if (url::DefaultPortForScheme(scheme.data(), scheme.length()) !=
1051 request_->url.EffectiveIntPort()) {
1052 // If the port is not the default for the scheme, assume it's not a real
1053 // HTTP/0.9 response, and fail the request.
1054
1055 // Allow Shoutcast responses over HTTP, as it's somewhat common and relies
1056 // on HTTP/0.9 on weird ports to work.
1057 // See
1058 // https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/qS63pYso4P0
1059 if (read_buf_->offset() < 3 || scheme != "http" ||
1060 !base::EqualsCaseInsensitiveASCII(
1061 std::string_view(read_buf_->StartOfBuffer(), 3), "icy")) {
1062 return ERR_INVALID_HTTP_RESPONSE;
1063 }
1064 }
1065
1066 headers = base::MakeRefCounted<HttpResponseHeaders>(
1067 std::string("HTTP/0.9 200 OK"));
1068 }
1069
1070 // Check for multiple Content-Length headers when the response is not
1071 // chunked-encoded. If they exist, and have distinct values, it's a potential
1072 // response smuggling attack.
1073 if (!headers->IsChunkEncoded()) {
1074 if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers,
1075 "Content-Length"))
1076 return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH;
1077 }
1078
1079 // Check for multiple Content-Disposition or Location headers. If they exist,
1080 // it's also a potential response smuggling attack.
1081 if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers,
1082 "Content-Disposition"))
1083 return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION;
1084 if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers, "Location"))
1085 return ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION;
1086
1087 response_->headers = headers;
1088 if (headers->GetHttpVersion() == HttpVersion(0, 9)) {
1089 response_->connection_info = HttpConnectionInfo::kHTTP0_9;
1090 } else if (headers->GetHttpVersion() == HttpVersion(1, 0)) {
1091 response_->connection_info = HttpConnectionInfo::kHTTP1_0;
1092 } else if (headers->GetHttpVersion() == HttpVersion(1, 1)) {
1093 response_->connection_info = HttpConnectionInfo::kHTTP1_1;
1094 }
1095 DVLOG(1) << __func__ << "() content_length = \""
1096 << response_->headers->GetContentLength() << "\n\""
1097 << " headers = \"" << GetResponseHeaderLines(*response_->headers)
1098 << "\"";
1099 return OK;
1100 }
1101
CalculateResponseBodySize()1102 void HttpStreamParser::CalculateResponseBodySize() {
1103 // Figure how to determine EOF:
1104
1105 // For certain responses, we know the content length is always 0. From
1106 // RFC 7230 Section 3.3 Message Body:
1107 //
1108 // The presence of a message body in a response depends on both the
1109 // request method to which it is responding and the response status code
1110 // (Section 3.1.2). Responses to the HEAD request method (Section 4.3.2
1111 // of [RFC7231]) never include a message body because the associated
1112 // response header fields (e.g., Transfer-Encoding, Content-Length,
1113 // etc.), if present, indicate only what their values would have been if
1114 // the request method had been GET (Section 4.3.1 of [RFC7231]). 2xx
1115 // (Successful) responses to a CONNECT request method (Section 4.3.6 of
1116 // [RFC7231]) switch to tunnel mode instead of having a message body.
1117 // All 1xx (Informational), 204 (No Content), and 304 (Not Modified)
1118 // responses do not include a message body. All other responses do
1119 // include a message body, although the body might be of zero length.
1120 //
1121 // From RFC 7231 Section 6.3.6 205 Reset Content:
1122 //
1123 // Since the 205 status code implies that no additional content will be
1124 // provided, a server MUST NOT generate a payload in a 205 response.
1125 if (response_->headers->response_code() / 100 == 1) {
1126 response_body_length_ = 0;
1127 } else {
1128 switch (response_->headers->response_code()) {
1129 case net::HTTP_NO_CONTENT: // No Content
1130 case net::HTTP_RESET_CONTENT: // Reset Content
1131 case net::HTTP_NOT_MODIFIED: // Not Modified
1132 response_body_length_ = 0;
1133 break;
1134 }
1135 }
1136 if (request_->method == "HEAD")
1137 response_body_length_ = 0;
1138
1139 if (response_body_length_ == -1) {
1140 // "Transfer-Encoding: chunked" trumps "Content-Length: N"
1141 if (response_->headers->IsChunkEncoded()) {
1142 chunked_decoder_ = std::make_unique<HttpChunkedDecoder>();
1143 } else {
1144 response_body_length_ = response_->headers->GetContentLength();
1145 // If response_body_length_ is still -1, then we have to wait
1146 // for the server to close the connection.
1147 }
1148 }
1149 }
1150
IsResponseBodyComplete() const1151 bool HttpStreamParser::IsResponseBodyComplete() const {
1152 if (chunked_decoder_.get())
1153 return chunked_decoder_->reached_eof();
1154 if (response_body_length_ != -1)
1155 return response_body_read_ >= response_body_length_;
1156
1157 return false; // Must read to EOF.
1158 }
1159
CanFindEndOfResponse() const1160 bool HttpStreamParser::CanFindEndOfResponse() const {
1161 return chunked_decoder_.get() || response_body_length_ >= 0;
1162 }
1163
IsMoreDataBuffered() const1164 bool HttpStreamParser::IsMoreDataBuffered() const {
1165 return read_buf_->offset() > read_buf_unused_offset_;
1166 }
1167
CanReuseConnection() const1168 bool HttpStreamParser::CanReuseConnection() const {
1169 if (!CanFindEndOfResponse())
1170 return false;
1171
1172 if (!response_is_keep_alive_)
1173 return false;
1174
1175 // Check if extra data was received after reading the entire response body. If
1176 // extra data was received, reusing the socket is not a great idea. This does
1177 // have the down side of papering over certain server bugs, but seems to be
1178 // the best option here.
1179 //
1180 // TODO(mmenke): Consider logging this - hard to decipher socket reuse
1181 // behavior makes NetLogs harder to read.
1182 if ((IsResponseBodyComplete() && IsMoreDataBuffered()) ||
1183 discarded_extra_data_) {
1184 return false;
1185 }
1186
1187 return stream_socket_->IsConnected();
1188 }
1189
OnConnectionClose()1190 void HttpStreamParser::OnConnectionClose() {
1191 // This is to ensure `stream_socket_` doesn't get dangling on connection
1192 // close.
1193 stream_socket_ = nullptr;
1194 }
1195
EncodeChunk(std::string_view payload,char * output,size_t output_size)1196 int HttpStreamParser::EncodeChunk(std::string_view payload,
1197 char* output,
1198 size_t output_size) {
1199 if (output_size < payload.size() + kChunkHeaderFooterSize)
1200 return ERR_INVALID_ARGUMENT;
1201
1202 char* cursor = output;
1203 // Add the header.
1204 const int num_chars = base::snprintf(output, output_size,
1205 "%X\r\n",
1206 static_cast<int>(payload.size()));
1207 cursor += num_chars;
1208 // Add the payload if any.
1209 if (payload.size() > 0) {
1210 memcpy(cursor, payload.data(), payload.size());
1211 cursor += payload.size();
1212 }
1213 // Add the trailing CRLF.
1214 memcpy(cursor, "\r\n", 2);
1215 cursor += 2;
1216
1217 return cursor - output;
1218 }
1219
1220 // static
ShouldMergeRequestHeadersAndBody(const std::string & request_headers,const UploadDataStream * request_body)1221 bool HttpStreamParser::ShouldMergeRequestHeadersAndBody(
1222 const std::string& request_headers,
1223 const UploadDataStream* request_body) {
1224 if (request_body != nullptr &&
1225 // IsInMemory() ensures that the request body is not chunked.
1226 request_body->IsInMemory() && request_body->size() > 0) {
1227 uint64_t merged_size = request_headers.size() + request_body->size();
1228 if (merged_size <= kMaxMergedHeaderAndBodySize)
1229 return true;
1230 }
1231 return false;
1232 }
1233
SendRequestBuffersEmpty()1234 bool HttpStreamParser::SendRequestBuffersEmpty() {
1235 return request_headers_ == nullptr && request_body_send_buf_ == nullptr &&
1236 request_body_read_buf_ == nullptr;
1237 }
1238
1239 } // namespace net
1240