diff options
-rw-r--r-- | content/browser/download/download_resource_handler.cc | 12 | ||||
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 2 | ||||
-rw-r--r-- | net/http/http_stream_parser.cc | 38 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.cc | 28 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.h | 7 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 4 | ||||
-rw-r--r-- | webkit/glue/weburlloader_impl.cc | 12 |
8 files changed, 51 insertions, 55 deletions
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index 47180b7..26f575c 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc @@ -232,12 +232,12 @@ bool DownloadResourceHandler::OnResponseCompleted( net::Error error_code = net::OK; if (status.status() == net::URLRequestStatus::FAILED) error_code = static_cast<net::Error>(status.error()); // Normal case. - // ERR_CONTENT_LENGTH_MISMATCH is allowed since a number of servers in the - // wild advertise a larger Content-Length than the amount of bytes in the - // message body, and then close the connection. Other browsers - IE8, - // Firefox 4.0.1, and Safari 5.0.4 - treat the download as complete in this - // case, so we follow their lead. - if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH) + // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild + // advertise a larger Content-Length than the amount of bytes in the message + // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1, + // and Safari 5.0.4 - treat the download as complete in this case, so we + // follow their lead. + if (error_code == net::ERR_CONNECTION_CLOSED) error_code = net::OK; InterruptReason reason = ConvertNetErrorToInterruptReason(error_code, 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 << "\""; } } diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index de470c4..7df6ace 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -608,7 +608,7 @@ void WebURLLoaderImpl::Context::OnReceivedCachedMetadata( } void WebURLLoaderImpl::Context::OnCompletedRequest( - const net::URLRequestStatus& original_status, + const net::URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time) { if (ftp_listing_delegate_.get()) { @@ -619,16 +619,6 @@ void WebURLLoaderImpl::Context::OnCompletedRequest( multipart_delegate_.reset(NULL); } - net::URLRequestStatus status = original_status; - - // Rewrite the Content-Length mismatch as a success. - // See crbug.com/52847 for justification. - if (status.status() != net::URLRequestStatus::SUCCESS && - status.error() == net::ERR_CONTENT_LENGTH_MISMATCH) { - status.set_status(net::URLRequestStatus::SUCCESS); - status.set_error(net::OK); - } - // Prevent any further IPC to the browser now that we're complete, but // don't delete it to keep any downloaded temp files alive. DCHECK(!completed_bridge_.get()); |