Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1192)

Side by Side Diff: net/http/http_stream_parser.cc

Issue 1266713007: Net: Stop treating partial HTTP headers as a valid response. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix < 8-byte HTTP/0.9 responses over HTTPS Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_stream_parser.h" 5 #include "net/http/http_stream_parser.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
(...skipping 11 matching lines...) Expand all
22 #include "net/socket/client_socket_handle.h" 22 #include "net/socket/client_socket_handle.h"
23 #include "net/socket/ssl_client_socket.h" 23 #include "net/socket/ssl_client_socket.h"
24 24
25 namespace net { 25 namespace net {
26 26
27 namespace { 27 namespace {
28 28
29 enum HttpHeaderParserEvent { 29 enum HttpHeaderParserEvent {
30 HEADER_PARSER_INVOKED = 0, 30 HEADER_PARSER_INVOKED = 0,
31 HEADER_HTTP_09_RESPONSE = 1, 31 HEADER_HTTP_09_RESPONSE = 1,
32 HEADER_ALLOWED_TRUNCATED_HEADERS = 2, 32 // Obsolete: HEADER_ALLOWED_TRUNCATED_HEADERS = 2,
33 HEADER_SKIPPED_WS_PREFIX = 3, 33 HEADER_SKIPPED_WS_PREFIX = 3,
34 HEADER_SKIPPED_NON_WS_PREFIX = 4, 34 HEADER_SKIPPED_NON_WS_PREFIX = 4,
35 NUM_HEADER_EVENTS 35 NUM_HEADER_EVENTS
36 }; 36 };
37 37
38 void RecordHeaderParserEvent(HttpHeaderParserEvent header_event) { 38 void RecordHeaderParserEvent(HttpHeaderParserEvent header_event) {
39 UMA_HISTOGRAM_ENUMERATION("Net.HttpHeaderParserEvent", header_event, 39 UMA_HISTOGRAM_ENUMERATION("Net.HttpHeaderParserEvent", header_event,
40 NUM_HEADER_EVENTS); 40 NUM_HEADER_EVENTS);
41 } 41 }
42 42
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after
764 764
765 int HttpStreamParser::HandleReadHeaderResult(int result) { 765 int HttpStreamParser::HandleReadHeaderResult(int result) {
766 DCHECK_EQ(0, read_buf_unused_offset_); 766 DCHECK_EQ(0, read_buf_unused_offset_);
767 767
768 if (result == 0) 768 if (result == 0)
769 result = ERR_CONNECTION_CLOSED; 769 result = ERR_CONNECTION_CLOSED;
770 770
771 if (result < 0 && result != ERR_CONNECTION_CLOSED) { 771 if (result < 0 && result != ERR_CONNECTION_CLOSED) {
772 io_state_ = STATE_DONE; 772 io_state_ = STATE_DONE;
773 return result; 773 return result;
774 } 774 }
davidben 2015/07/31 21:32:50 If you want, you could further simplify checks by
mmenke 2015/07/31 21:45:36 Good idea, done.
775 // If we've used the connection before, then we know it is not a HTTP/0.9 775
776 // response and return ERR_CONNECTION_CLOSED. 776 if (result == ERR_CONNECTION_CLOSED) {
777 if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 && 777 if (read_buf_->offset() == 0) {
778 connection_->is_reused()) { 778 io_state_ = STATE_DONE;
779 io_state_ = STATE_DONE; 779 // If the connection has not been reused, it may have been a 0-length
780 // HTTP/0.9 responses, but it was most likely an error, so just return
781 // ERR_EMPTY_RESPONSE instead. If the connection was reused, just pass
782 // on the original connection close error, as rather than being an
783 // empty HTTP/0.9 response it's much more likely the server closed the
784 // socket before it received the request.
785 if (!connection_->is_reused())
786 return ERR_EMPTY_RESPONSE;
787 return result;
788 }
789
790 // The connection closed before the end of the headers. For HTTPS,
791 // accepting the partial headers headers is a potential security
792 // vulnerability. For HTTP, it just doesn't seem like a good idea.
793 if (response_header_start_offset_ >= 0) {
794 io_state_ = STATE_DONE;
795 return ERR_RESPONSE_HEADERS_TRUNCATED;
796 }
797
798 // The response is apparently using HTTP/0.9. Treat the entire response as
799 // the body.
800 int rv = ParseResponseHeaders(0);
801 if (rv < 0)
802 return rv;
780 return result; 803 return result;
781 } 804 }
782 805
783 // Record our best estimate of the 'response time' as the time when we read 806 // Record our best estimate of the 'response time' as the time when we read
784 // the first bytes of the response headers. 807 // the first bytes of the response headers.
785 if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED) 808 if (read_buf_->offset() == 0)
786 response_->response_time = base::Time::Now(); 809 response_->response_time = base::Time::Now();
787 810
788 if (result == ERR_CONNECTION_CLOSED) {
789 // The connection closed before we detected the end of the headers.
790 if (read_buf_->offset() == 0) {
791 // The connection was closed before any data was sent. Likely an error
792 // rather than empty HTTP/0.9 response.
793 io_state_ = STATE_DONE;
794 return ERR_EMPTY_RESPONSE;
795 } else if (request_->url.SchemeIsCryptographic()) {
796 // The connection was closed in the middle of the headers. For HTTPS we
797 // don't parse partial headers. Return a different error code so that we
798 // know that we shouldn't attempt to retry the request.
799 io_state_ = STATE_DONE;
800 return ERR_RESPONSE_HEADERS_TRUNCATED;
801 }
802 // Parse things as well as we can and let the caller decide what to do.
803 int end_offset;
804 if (response_header_start_offset_ >= 0) {
805 // The response looks to be a truncated set of HTTP headers.
806 io_state_ = STATE_READ_BODY_COMPLETE;
807 end_offset = read_buf_->offset();
808 RecordHeaderParserEvent(HEADER_ALLOWED_TRUNCATED_HEADERS);
809 } else {
810 // The response is apparently using HTTP/0.9. Treat the entire response
811 // the body.
812 end_offset = 0;
813 }
814 int rv = ParseResponseHeaders(end_offset);
815 if (rv < 0)
816 return rv;
817 return result;
818 }
819
820 read_buf_->set_offset(read_buf_->offset() + result); 811 read_buf_->set_offset(read_buf_->offset() + result);
821 DCHECK_LE(read_buf_->offset(), read_buf_->capacity()); 812 DCHECK_LE(read_buf_->offset(), read_buf_->capacity());
822 DCHECK_GE(result, 0); 813 DCHECK_GE(result, 0);
823 814
824 int end_of_header_offset = FindAndParseResponseHeaders(); 815 int end_of_header_offset = FindAndParseResponseHeaders();
825 816
826 // Note: -1 is special, it indicates we haven't found the end of headers. 817 // Note: -1 is special, it indicates we haven't found the end of headers.
827 // Anything less than -1 is a net::Error, so we bail out. 818 // Anything less than -1 is a net::Error, so we bail out.
828 if (end_of_header_offset < -1) 819 if (end_of_header_offset < -1)
829 return end_of_header_offset; 820 return end_of_header_offset;
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after
1097 request_body->IsInMemory() && 1088 request_body->IsInMemory() &&
1098 request_body->size() > 0) { 1089 request_body->size() > 0) {
1099 uint64 merged_size = request_headers.size() + request_body->size(); 1090 uint64 merged_size = request_headers.size() + request_body->size();
1100 if (merged_size <= kMaxMergedHeaderAndBodySize) 1091 if (merged_size <= kMaxMergedHeaderAndBodySize)
1101 return true; 1092 return true;
1102 } 1093 }
1103 return false; 1094 return false;
1104 } 1095 }
1105 1096
1106 } // namespace net 1097 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698