summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-08 04:31:37 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-08 04:31:37 +0000
commitf001bd6a14612d0d547539c5a7a3cf53f95e3816 (patch)
treec072c54a7a841b24256343443ec0f1ffac384cd1 /net
parentf508e4e37229721672f9f567b856986859ff323c (diff)
downloadchromium_src-f001bd6a14612d0d547539c5a7a3cf53f95e3816.zip
chromium_src-f001bd6a14612d0d547539c5a7a3cf53f95e3816.tar.gz
chromium_src-f001bd6a14612d0d547539c5a7a3cf53f95e3816.tar.bz2
Revert 110505 - Report ERR_CONTENT_LENGTH_MISMATCH when the count of bytes received doesn't match Content-Length.
This used to be reported as ERR_CONNECTION_CLOSED. Unfortunately, there are a number of misconfigured servers on the web which report a Content-Length completely divorced from reality. Other browsers display whatever data was received without reporting errors in this case, and Chrome now does the same. An earlier CL simply removed error reporting for this case. However, some uses may want to still treat this like an error (such as URLFetcher), or could eventually treat the case as an error and recover (such as downloads). Data received will be populated in the disk cache as a truncated entry, if the server advertises byte range support. WebURLLoaderImpl ignores this for all async and sync resource loads. At some point it may want to selectively do this based on content type. Downloads ignore the error, but no longer ignore ERR_CONNECTION_CLOSED. Other uses will treat this like an error. BUG=52847 TEST=Manually, go to a site that advertises a large content-length and closes the connection before writing so many bytes, and notice that the page now displays. Automatic: HttpNetworkTransactionTest.LargeContentLengthThenClose, URLRequestTestHttp.GetZippedTest Review URL: http://codereview.chromium.org/8496016 TBR=cbentzel@chromium.org Review URL: http://codereview.chromium.org/8863002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113562 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/http/http_network_transaction_unittest.cc2
-rw-r--r--net/http/http_stream_parser.cc38
-rw-r--r--net/url_request/url_request_http_job.cc28
-rw-r--r--net/url_request/url_request_http_job.h7
-rw-r--r--net/url_request/url_request_unittest.cc4
6 files changed, 44 insertions, 38 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index f9734e0..13fba2c 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -518,9 +518,6 @@ NET_ERROR(SPDY_SERVER_REFUSED_STREAM, -351)
// SPDY server didn't respond to the PING message.
NET_ERROR(SPDY_PING_FAILED, -352)
-// Content-Length does not match the number of bytes received.
-NET_ERROR(CONTENT_LENGTH_MISMATCH, -353)
-
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index dbd7e2f..d6f966c 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -6491,7 +6491,7 @@ TEST_F(HttpNetworkTransactionTest, LargeContentLengthThenClose) {
std::string response_data;
rv = ReadTransaction(trans.get(), &response_data);
- EXPECT_EQ(ERR_CONTENT_LENGTH_MISMATCH, rv);
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
}
TEST_F(HttpNetworkTransactionTest, UploadFileSmallerThanLength) {
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 5ee1f22..cd3c9fa 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -530,38 +530,12 @@ int HttpStreamParser::DoReadBody() {
}
int HttpStreamParser::DoReadBodyComplete(int result) {
- // When the connection is closed, there are numerous ways to interpret it.
- //
- // - If a Content-Length header is present and the body contains exactly that
- // number of bytes at connection close, the response is successful.
- //
- // - If a Content-Length header is present and the body contains fewer bytes
- // than promised by the header at connection close, it may indicate that
- // the connection was closed prematurely, or it may indicate that the
- // server sent an invalid Content-Length header. Unfortunately, the invalid
- // Content-Length header case does occur in practice and other browsers are
- // tolerant of it, so this is reported as an ERR_CONTENT_LENGTH_MISMATCH
- // rather than an ERR_CONNECTION_CLOSE.
- //
- // - If chunked encoding is used and the terminating chunk has been processed
- // when the connection is closed, the response is successful.
- //
- // - If chunked encoding is used and the terminating chunk has not been
- // processed when the connection is closed, it may indicate that the
- // connection was closed prematurely or it may indicate that the server
- // sent an invalid chunked encoding. We choose to treat it as
- // an invalid chunked encoding.
- //
- // - If a Content-Length is not present and chunked encoding is not used,
- // connection close is the only way to signal that the response is
- // complete. Unfortunately, this also means that there is no way to detect
- // early close of a connection. No error is returned.
- if (result == 0 && !IsResponseBodyComplete() && CanFindEndOfResponse()) {
- if (chunked_decoder_.get())
- result = ERR_INVALID_CHUNKED_ENCODING;
- else
- result = ERR_CONTENT_LENGTH_MISMATCH;
- }
+ // If we didn't get a Content-Length and aren't using a chunked encoding,
+ // the only way to signal the end of a stream is to close the connection,
+ // so we don't treat that as an error, though in some cases we may not
+ // have completely received the resource.
+ if (result == 0 && !IsResponseBodyComplete() && CanFindEndOfResponse())
+ result = ERR_CONNECTION_CLOSED;
// Filter incoming data if appropriate. FilterBuf may return an error.
if (result > 0 && chunked_decoder_.get()) {
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 335d5ed..2cf667a 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -765,6 +765,9 @@ void URLRequestHttpJob::OnHeadersReceivedCallback(int result) {
void URLRequestHttpJob::OnReadCompleted(int result) {
read_in_progress_ = false;
+ if (ShouldFixMismatchedContentLength(result))
+ result = 0;
+
if (result == 0) {
NotifyDone(URLRequestStatus());
} else if (result < 0) {
@@ -1100,6 +1103,28 @@ void URLRequestHttpJob::ContinueDespiteLastError() {
&URLRequestHttpJob::OnStartCompleted, rv));
}
+bool URLRequestHttpJob::ShouldFixMismatchedContentLength(int rv) const {
+ // Some servers send the body compressed, but specify the content length as
+ // the uncompressed size. Although this violates the HTTP spec we want to
+ // support it (as IE and FireFox do), but *only* for an exact match.
+ // See http://crbug.com/79694.
+ if (rv == net::ERR_CONNECTION_CLOSED) {
+ if (request_ && request_->response_headers()) {
+ int64 expected_length = request_->response_headers()->GetContentLength();
+ VLOG(1) << __FUNCTION__ << "() "
+ << "\"" << request_->url().spec() << "\""
+ << " content-length = " << expected_length
+ << " pre total = " << prefilter_bytes_read()
+ << " post total = " << postfilter_bytes_read();
+ if (postfilter_bytes_read() == expected_length) {
+ // Clear the error.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool URLRequestHttpJob::ReadRawData(IOBuffer* buf, int buf_size,
int* bytes_read) {
DCHECK_NE(buf_size, 0);
@@ -1108,6 +1133,9 @@ bool URLRequestHttpJob::ReadRawData(IOBuffer* buf, int buf_size,
int rv = transaction_->Read(buf, buf_size, &read_callback_);
+ if (ShouldFixMismatchedContentLength(rv))
+ rv = 0;
+
if (rv >= 0) {
*bytes_read = rv;
if (!rv)
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index b94b462..c924e1c 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -172,6 +172,13 @@ class URLRequestHttpJob : public URLRequestJob {
void OnCookieSaved(bool cookie_status);
void CookieHandled();
+ // Some servers send the body compressed, but specify the content length as
+ // the uncompressed size. If this is the case, we return true in order
+ // to request to work around this non-adherence to the HTTP standard.
+ // |rv| is the standard return value of a read function indicating the number
+ // of bytes read or, if negative, an error code.
+ bool ShouldFixMismatchedContentLength(int rv) const;
+
// Returns the effective response headers, considering that they may be
// overridden by |override_response_headers_|.
HttpResponseHeaders* GetResponseHeaders() const;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index b4be49e..14aa8aa 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -881,7 +881,7 @@ TEST_F(URLRequestTestHTTP, GetZippedTest) {
// L & M are larger than the data sent, and show an error.
// S has too little data, but we seem to accept it.
const bool test_expect_success[num_tests] =
- { true, false, false, false, true };
+ { true, true, false, false, true };
for (int i = 0; i < num_tests ; i++) {
TestDelegate d;
@@ -913,7 +913,7 @@ TEST_F(URLRequestTestHTTP, GetZippedTest) {
<< " Parameter = \"" << test_file << "\"";
} else {
EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
- EXPECT_EQ(ERR_CONTENT_LENGTH_MISMATCH, r.status().error())
+ EXPECT_EQ(-100, r.status().error())
<< " Parameter = \"" << test_file << "\"";
}
}