diff options
author | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 20:08:03 +0000 |
---|---|---|
committer | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 20:08:03 +0000 |
commit | 2f497d2c984fe9c7a646d6d78b04a7c922033f50 (patch) | |
tree | 9ed144aa1a4fb14e09b74ed39e680a61679e2ca4 /net | |
parent | cde4e80d78fee0902c0290b2c2a4052e1e7132cd (diff) | |
download | chromium_src-2f497d2c984fe9c7a646d6d78b04a7c922033f50.zip chromium_src-2f497d2c984fe9c7a646d6d78b04a7c922033f50.tar.gz chromium_src-2f497d2c984fe9c7a646d6d78b04a7c922033f50.tar.bz2 |
Reverting 29316.
Review URL: http://codereview.chromium.org/292002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29320 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_basic_stream.cc | 50 | ||||
-rw-r--r-- | net/http/http_basic_stream.h | 46 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 635 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 126 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 256 | ||||
-rw-r--r-- | net/http/http_stream.h | 81 | ||||
-rw-r--r-- | net/http/http_stream_parser.cc | 512 | ||||
-rw-r--r-- | net/http/http_stream_parser.h | 167 | ||||
-rw-r--r-- | net/net.gyp | 3 | ||||
-rw-r--r-- | net/socket/client_socket_handle.h | 13 |
10 files changed, 634 insertions, 1255 deletions
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc deleted file mode 100644 index f1e6100..0000000 --- a/net/http/http_basic_stream.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/http/http_basic_stream.h" - -namespace net { - -HttpBasicStream::HttpBasicStream(ClientSocketHandle* handle) - : read_buf_(new GrowableIOBuffer()), - parser_(new HttpStreamParser(handle, read_buf_)) { -} - -int HttpBasicStream::SendRequest(const HttpRequestInfo* request, - const std::string& headers, - UploadDataStream* request_body, - CompletionCallback* callback) { - return parser_->SendRequest(request, headers, request_body, callback); -} - -uint64 HttpBasicStream::GetUploadProgress() const { - return parser_->GetUploadProgress(); -} - -int HttpBasicStream::ReadResponseHeaders(CompletionCallback* callback) { - return parser_->ReadResponseHeaders(callback); -} - -HttpResponseInfo* HttpBasicStream::GetResponseInfo() const { - return parser_->GetResponseInfo(); -} - -int HttpBasicStream::ReadResponseBody(IOBuffer* buf, int buf_len, - CompletionCallback* callback) { - return parser_->ReadResponseBody(buf, buf_len, callback); -} - -bool HttpBasicStream::IsResponseBodyComplete() const { - return parser_->IsResponseBodyComplete(); -} - -bool HttpBasicStream::CanFindEndOfResponse() const { - return parser_->CanFindEndOfResponse(); -} - -bool HttpBasicStream::IsMoreDataBuffered() const { - return parser_->IsMoreDataBuffered(); -} - -} // namespace net diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h index 1b1f68a..2a82b7a 100644 --- a/net/http/http_basic_stream.h +++ b/net/http/http_basic_stream.h @@ -9,50 +9,32 @@ #ifndef NET_HTTP_HTTP_BASIC_STREAM_H_ #define NET_HTTP_HTTP_BASIC_STREAM_H_ -#include <string> - #include "base/basictypes.h" -#include "net/base/io_buffer.h" #include "net/http/http_stream.h" -#include "net/http/http_stream_parser.h" +#include "net/socket/client_socket_handle.h" namespace net { -class ClientSocketHandle; -class HttpRequestInfo; -class HttpResponseInfo; -class UploadDataStream; - class HttpBasicStream : public HttpStream { public: - explicit HttpBasicStream(ClientSocketHandle* handle); + explicit HttpBasicStream(ClientSocketHandle* handle) : handle_(handle) {} virtual ~HttpBasicStream() {} // HttpStream methods: - virtual int SendRequest(const HttpRequestInfo* request, - const std::string& headers, - UploadDataStream* request_body, - CompletionCallback* callback); - - virtual uint64 GetUploadProgress() const; - - virtual int ReadResponseHeaders(CompletionCallback* callback); - - virtual HttpResponseInfo* GetResponseInfo() const; - - virtual int ReadResponseBody(IOBuffer* buf, int buf_len, - CompletionCallback* callback); - - virtual bool IsResponseBodyComplete() const; - - virtual bool CanFindEndOfResponse() const; - - virtual bool IsMoreDataBuffered() const; + virtual int Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + return handle_->socket()->Read(buf, buf_len, callback); + } + + virtual int Write(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + return handle_->socket()->Write(buf, buf_len, callback); + } private: - scoped_refptr<GrowableIOBuffer> read_buf_; - - scoped_ptr<HttpStreamParser> parser_; + ClientSocketHandle* const handle_; DISALLOW_COPY_AND_ASSIGN(HttpBasicStream); }; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 9e8b98a..6324d7d 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -25,7 +25,6 @@ #include "net/http/http_network_session.h" #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" -#include "net/http/http_response_info.h" #include "net/http/http_util.h" #include "net/socket/client_socket_factory.h" #include "net/socket/socks5_client_socket.h" @@ -36,6 +35,10 @@ using base::Time; namespace net { +void HttpNetworkTransaction::ResponseHeaders::Realloc(size_t new_size) { + headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size))); +} + namespace { void BuildRequestHeaders(const HttpRequestInfo* request_info, @@ -135,12 +138,20 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session) request_(NULL), pac_request_(NULL), reused_socket_(false), - headers_valid_(false), - logged_response_time(false), using_ssl_(false), proxy_mode_(kDirectConnection), establishing_tunnel_(false), + reading_body_from_socket_(false), embedded_identity_used_(false), + request_headers_(new RequestHeaders()), + request_headers_bytes_sent_(0), + header_buf_(new ResponseHeaders()), + header_buf_capacity_(0), + header_buf_len_(0), + header_buf_body_offset_(-1), + header_buf_http_offset_(-1), + response_body_length_(-1), // -1 means unspecified. + response_body_read_(0), read_buf_len_(0), next_state_(STATE_NONE) { session->ssl_config_service()->GetSSLConfig(&ssl_config_); @@ -165,7 +176,7 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, int HttpNetworkTransaction::RestartIgnoringLastError( CompletionCallback* callback) { if (connection_.socket()->IsConnected()) { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; } else { connection_.socket()->Disconnect(); connection_.Reset(); @@ -255,19 +266,19 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) { } bool keep_alive = false; - // Even if the server says the connection is keep-alive, we have to be - // able to find the end of each response in order to reuse the connection. - if (GetResponseHeaders()->IsKeepAlive() && - http_stream_->CanFindEndOfResponse()) { - // If the response body hasn't been completely read, we need to drain - // it first. - if (!http_stream_->IsResponseBodyComplete()) { + if (response_.headers->IsKeepAlive()) { + // If there is a response body of known length, we need to drain it first. + if (response_body_length_ > 0 || chunked_decoder_.get()) { next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; - read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket. + read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket read_buf_len_ = kDrainBodyBufferSize; return; } - keep_alive = true; + if (response_body_length_ == 0) // No response body to drain. + keep_alive = true; + // response_body_length_ is -1 and we're not using chunked encoding. We + // don't know the length of the response body, so we can't reuse this + // connection even though the server says it's keep-alive. } // We don't need to drain the response body, so we act as if we had drained @@ -277,7 +288,7 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) { void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { if (keep_alive) { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; reused_socket_ = true; } else { next_state_ = STATE_INIT_CONNECTION; @@ -291,8 +302,7 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { - scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); - DCHECK(headers.get()); + DCHECK(response_.headers); DCHECK(buf); DCHECK_LT(0, buf_len); @@ -307,8 +317,8 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, // network attacker can already control HTTP sessions. // We reach this case when the user cancels a 407 proxy auth prompt. // See http://crbug.com/8473 - DCHECK_EQ(407, headers->response_code()); - LogBlockedTunnelResponse(headers->response_code()); + DCHECK_EQ(407, response_.headers->response_code()); + LogBlockedTunnelResponse(response_.headers->response_code()); return ERR_TUNNEL_CONNECTION_FAILED; } @@ -328,9 +338,8 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, } const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const { - const HttpResponseInfo* response = http_stream_->GetResponseInfo(); - return ((headers_valid_ && response->headers) || response->ssl_info.cert || - response->cert_request_info) ? response : NULL; + return (response_.headers || response_.ssl_info.cert || + response_.cert_request_info) ? &response_ : NULL; } LoadState HttpNetworkTransaction::GetLoadState() const { @@ -341,7 +350,8 @@ LoadState HttpNetworkTransaction::GetLoadState() const { return LOAD_STATE_RESOLVING_PROXY_FOR_URL; case STATE_INIT_CONNECTION_COMPLETE: return connection_.GetLoadState(); - case STATE_SEND_REQUEST_COMPLETE: + case STATE_WRITE_HEADERS_COMPLETE: + case STATE_WRITE_BODY_COMPLETE: return LOAD_STATE_SENDING_REQUEST; case STATE_READ_HEADERS_COMPLETE: return LOAD_STATE_WAITING_FOR_RESPONSE; @@ -353,10 +363,10 @@ LoadState HttpNetworkTransaction::GetLoadState() const { } uint64 HttpNetworkTransaction::GetUploadProgress() const { - if (!http_stream_.get()) + if (!request_body_stream_.get()) return 0; - return http_stream_->GetUploadProgress(); + return request_body_stream_->position(); } HttpNetworkTransaction::~HttpNetworkTransaction() { @@ -429,14 +439,23 @@ int HttpNetworkTransaction::DoLoop(int result) { rv = DoSSLConnectComplete(rv); TRACE_EVENT_END("http.ssl_connect", request_, request_->url.spec()); break; - case STATE_SEND_REQUEST: + case STATE_WRITE_HEADERS: DCHECK_EQ(OK, rv); - TRACE_EVENT_BEGIN("http.send_request", request_, request_->url.spec()); - rv = DoSendRequest(); + TRACE_EVENT_BEGIN("http.write_headers", request_, request_->url.spec()); + rv = DoWriteHeaders(); break; - case STATE_SEND_REQUEST_COMPLETE: - rv = DoSendRequestComplete(rv); - TRACE_EVENT_END("http.send_request", request_, request_->url.spec()); + case STATE_WRITE_HEADERS_COMPLETE: + rv = DoWriteHeadersComplete(rv); + TRACE_EVENT_END("http.write_headers", request_, request_->url.spec()); + break; + case STATE_WRITE_BODY: + DCHECK_EQ(OK, rv); + TRACE_EVENT_BEGIN("http.write_body", request_, request_->url.spec()); + rv = DoWriteBody(); + break; + case STATE_WRITE_BODY_COMPLETE: + rv = DoWriteBodyComplete(rv); + TRACE_EVENT_END("http.write_body", request_, request_->url.spec()); break; case STATE_READ_HEADERS: DCHECK_EQ(OK, rv); @@ -592,7 +611,7 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { // trying to reuse a keep-alive connection. reused_socket_ = connection_.is_reused(); if (reused_socket_) { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; } else { // Now we have a TCP connected socket. Perform other connection setup as // needed. @@ -601,12 +620,11 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { else if (using_ssl_ && proxy_mode_ == kDirectConnection) { next_state_ = STATE_SSL_CONNECT; } else { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; if (proxy_mode_ == kHTTPProxyUsingTunnel) establishing_tunnel_ = true; } } - headers_valid_ = false; http_stream_.reset(new HttpBasicStream(&connection_)); return OK; } @@ -637,7 +655,7 @@ int HttpNetworkTransaction::DoSOCKSConnectComplete(int result) { if (using_ssl_) { next_state_ = STATE_SSL_CONNECT; } else { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; } } else { result = ReconsiderProxyAfterError(result); @@ -676,7 +694,7 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { base::TimeDelta::FromMinutes(10), 100); - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_WRITE_HEADERS; } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { result = HandleCertificateRequest(result); } else { @@ -685,16 +703,12 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { return result; } -int HttpNetworkTransaction::DoSendRequest() { - next_state_ = STATE_SEND_REQUEST_COMPLETE; - - UploadDataStream* request_body = NULL; - if (!establishing_tunnel_ && request_->upload_data) - request_body = new UploadDataStream(request_->upload_data); +int HttpNetworkTransaction::DoWriteHeaders() { + next_state_ = STATE_WRITE_HEADERS_COMPLETE; // This is constructed lazily (instead of within our Start method), so that // we have proxy info available. - if (request_headers_.empty()) { + if (request_headers_->headers_.empty()) { // Figure out if we can/should add Proxy-Authentication & Authentication // headers. bool have_proxy_auth = @@ -719,30 +733,91 @@ int HttpNetworkTransaction::DoSendRequest() { BuildAuthorizationHeader(HttpAuth::AUTH_SERVER)); if (establishing_tunnel_) { - BuildTunnelRequest(request_, authorization_headers, &request_headers_); + BuildTunnelRequest(request_, authorization_headers, + &request_headers_->headers_); } else { - BuildRequestHeaders(request_, authorization_headers, request_body, - proxy_mode_ == kHTTPProxy, &request_headers_); + if (request_->upload_data) + request_body_stream_.reset(new UploadDataStream(request_->upload_data)); + BuildRequestHeaders(request_, authorization_headers, + request_body_stream_.get(), + proxy_mode_ == kHTTPProxy, + &request_headers_->headers_); } } - return http_stream_->SendRequest(request_, request_headers_, request_body, - &io_callback_); + // Record our best estimate of the 'request time' as the time when we send + // out the first bytes of the request headers. + if (request_headers_bytes_sent_ == 0) { + response_.request_time = Time::Now(); + } + + request_headers_->SetDataOffset(request_headers_bytes_sent_); + int buf_len = static_cast<int>(request_headers_->headers_.size() - + request_headers_bytes_sent_); + DCHECK_GT(buf_len, 0); + + return http_stream_->Write(request_headers_, buf_len, &io_callback_); } -int HttpNetworkTransaction::DoSendRequestComplete(int result) { +int HttpNetworkTransaction::DoWriteHeadersComplete(int result) { if (result < 0) return HandleIOError(result); - next_state_ = STATE_READ_HEADERS; + request_headers_bytes_sent_ += result; + if (request_headers_bytes_sent_ < request_headers_->headers_.size()) { + next_state_ = STATE_WRITE_HEADERS; + } else if (!establishing_tunnel_ && request_body_stream_.get() && + request_body_stream_->size()) { + next_state_ = STATE_WRITE_BODY; + } else { + next_state_ = STATE_READ_HEADERS; + } + return OK; +} + +int HttpNetworkTransaction::DoWriteBody() { + next_state_ = STATE_WRITE_BODY_COMPLETE; + + DCHECK(request_body_stream_.get()); + DCHECK(request_body_stream_->size()); + + int buf_len = static_cast<int>(request_body_stream_->buf_len()); + + return http_stream_->Write(request_body_stream_->buf(), buf_len, + &io_callback_); +} +int HttpNetworkTransaction::DoWriteBodyComplete(int result) { + if (result < 0) + return HandleIOError(result); + + request_body_stream_->DidConsume(result); + + if (request_body_stream_->position() < request_body_stream_->size()) { + next_state_ = STATE_WRITE_BODY; + } else { + next_state_ = STATE_READ_HEADERS; + } return OK; } int HttpNetworkTransaction::DoReadHeaders() { next_state_ = STATE_READ_HEADERS_COMPLETE; - return http_stream_->ReadResponseHeaders(&io_callback_); + // Grow the read buffer if necessary. + if (header_buf_len_ == header_buf_capacity_) { + header_buf_capacity_ += kHeaderBufInitialSize; + header_buf_->Realloc(header_buf_capacity_); + } + + int buf_len = header_buf_capacity_ - header_buf_len_; + header_buf_->set_data(header_buf_len_); + + // http://crbug.com/16371: We're seeing |user_buf_->data()| return NULL. + // See if the user is passing in an IOBuffer with a NULL |data_|. + CHECK(header_buf_->data()); + + return http_stream_->Read(header_buf_, buf_len, &io_callback_); } int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { @@ -751,12 +826,24 @@ int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { return ERR_TUNNEL_CONNECTION_FAILED; } - if (!http_stream_->GetResponseInfo()->headers) { + if (has_found_status_line_start()) { + // Assume EOF is end-of-headers. + header_buf_body_offset_ = header_buf_len_; + return OK; + } + + // No status line was matched yet. Could have been a HTTP/0.9 response, or + // a partial HTTP/1.x response. + + if (header_buf_len_ == 0) { // The connection was closed before any data was sent. Likely an error // rather than empty HTTP/0.9 response. return ERR_EMPTY_RESPONSE; } + // Assume everything else is a HTTP/0.9 response (including responses + // of 'h', 'ht', 'htt'). + header_buf_body_offset_ = 0; return OK; } @@ -778,115 +865,99 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { } } - if (result < 0 && result != ERR_CONNECTION_CLOSED) + if (result < 0) return HandleIOError(result); - if (result == ERR_CONNECTION_CLOSED && ShouldResendRequest(result)) { + if (result == 0 && ShouldResendRequest(result)) { ResetConnectionAndRequestForResend(); - return OK; + return result; } - // After we call RestartWithAuth a new response_time will be recorded, and - // we need to be cautious about incorrectly logging the duration across the - // authentication activity. - HttpResponseInfo* response = http_stream_->GetResponseInfo(); - if (!logged_response_time) { - LogTransactionConnectedMetrics(); - logged_response_time = true; + // Record our best estimate of the 'response time' as the time when we read + // the first bytes of the response headers. + if (header_buf_len_ == 0) { + // After we call RestartWithAuth header_buf_len will be zero again, and + // we need to be cautious about incorrectly logging the duration across the + // authentication activitiy. + bool first_response = response_.response_time == Time(); + response_.response_time = Time::Now(); + if (first_response) + LogTransactionConnectedMetrics(); } - if (result == ERR_CONNECTION_CLOSED) { + // The socket was closed before we found end-of-headers. + if (result == 0) { int rv = HandleConnectionClosedBeforeEndOfHeaders(); if (rv != OK) return rv; - // TODO(wtc): Traditionally this code has returned 0 when reading a closed - // socket. That is partially corrected in classes that we call, but - // callers need to be updated. - result = 0; - } + } else { + header_buf_len_ += result; + DCHECK(header_buf_len_ <= header_buf_capacity_); - if (response->headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { - // Require the "HTTP/1.x" status line for SSL CONNECT. - if (establishing_tunnel_) - return ERR_TUNNEL_CONNECTION_FAILED; + // Look for the start of the status line, if it hasn't been found yet. + if (!has_found_status_line_start()) { + header_buf_http_offset_ = HttpUtil::LocateStartOfStatusLine( + header_buf_->headers(), header_buf_len_); + } - // HTTP/0.9 doesn't support the PUT method, so lack of response headers - // indicates a buggy server. See: - // https://bugzilla.mozilla.org/show_bug.cgi?id=193921 - if (request_->method == "PUT") - return ERR_METHOD_NOT_SUPPORTED; - } + if (has_found_status_line_start()) { + int eoh = HttpUtil::LocateEndOfHeaders( + header_buf_->headers(), header_buf_len_, header_buf_http_offset_); + if (eoh == -1) { + // Prevent growing the headers buffer indefinitely. + if (header_buf_len_ >= kMaxHeaderBufSize) + return ERR_RESPONSE_HEADERS_TOO_BIG; - if (establishing_tunnel_) { - switch (response->headers->response_code()) { - case 200: // OK - if (http_stream_->IsMoreDataBuffered()) { - // The proxy sent extraneous data after the headers. - return ERR_TUNNEL_CONNECTION_FAILED; - } - next_state_ = STATE_SSL_CONNECT; - // Reset for the real request and response headers. - request_headers_.clear(); - http_stream_.reset(new HttpBasicStream(&connection_)); - headers_valid_ = false; - establishing_tunnel_ = false; + // Haven't found the end of headers yet, keep reading. + next_state_ = STATE_READ_HEADERS; return OK; - - // We aren't able to CONNECT to the remote host through the proxy. We - // need to be very suspicious about the response because an active network - // attacker can force us into this state by masquerading as the proxy. - // The only safe thing to do here is to fail the connection because our - // client is expecting an SSL protected response. - // See http://crbug.com/7338. - case 407: // Proxy Authentication Required - // We need this status code to allow proxy authentication. Our - // authentication code is smart enough to avoid being tricked by an - // active network attacker. - break; - default: - // For all other status codes, we conservatively fail the CONNECT - // request. - // We lose something by doing this. We have seen proxy 403, 404, and - // 501 response bodies that contain a useful error message. For - // example, Squid uses a 404 response to report the DNS error: "The - // domain name does not exist." - LogBlockedTunnelResponse(response->headers->response_code()); - return ERR_TUNNEL_CONNECTION_FAILED; + } + header_buf_body_offset_ = eoh; + } else if (header_buf_len_ < 8) { + // Not enough data to decide whether this is HTTP/0.9 yet. + // 8 bytes = (4 bytes of junk) + "http".length() + next_state_ = STATE_READ_HEADERS; + return OK; + } else { + // Enough data was read -- there is no status line. + header_buf_body_offset_ = 0; } } - // Check for an intermediate 100 Continue response. An origin server is - // allowed to send this response even if we didn't ask for it, so we just - // need to skip over it. - // We treat any other 1xx in this same way (although in practice getting - // a 1xx that isn't a 100 is rare). - if (response->headers->response_code() / 100 == 1) { - next_state_ = STATE_READ_HEADERS; - return OK; - } - - int rv = HandleAuthChallenge(); - if (rv != OK) - return rv; - - if (using_ssl_ && !establishing_tunnel_) { - SSLClientSocket* ssl_socket = - reinterpret_cast<SSLClientSocket*>(connection_.socket()); - ssl_socket->GetSSLInfo(&response->ssl_info); - } - - headers_valid_ = true; - return OK; + // And, we are done with the Start or the SSL tunnel CONNECT sequence. + return DidReadResponseHeaders(); } int HttpNetworkTransaction::DoReadBody() { DCHECK(read_buf_); DCHECK_GT(read_buf_len_, 0); DCHECK(connection_.is_initialized()); + DCHECK(!header_buf_->headers() || header_buf_body_offset_ >= 0); next_state_ = STATE_READ_BODY_COMPLETE; - return http_stream_->ReadResponseBody(read_buf_, read_buf_len_, - &io_callback_); + + // We may have already consumed the indicated content length. + if (response_body_length_ != -1 && + response_body_read_ >= response_body_length_) + return 0; + + // We may have some data remaining in the header buffer. + if (header_buf_->headers() && header_buf_body_offset_ < header_buf_len_) { + int n = std::min(read_buf_len_, header_buf_len_ - header_buf_body_offset_); + memcpy(read_buf_->data(), header_buf_->headers() + header_buf_body_offset_, + n); + header_buf_body_offset_ += n; + if (header_buf_body_offset_ == header_buf_len_) { + header_buf_->Reset(); + header_buf_capacity_ = 0; + header_buf_len_ = 0; + header_buf_body_offset_ = -1; + } + return n; + } + + reading_body_from_socket_ = true; + return http_stream_->Read(read_buf_, read_buf_len_, &io_callback_); } int HttpNetworkTransaction::DoReadBodyComplete(int result) { @@ -894,18 +965,39 @@ int HttpNetworkTransaction::DoReadBodyComplete(int result) { DCHECK(!establishing_tunnel_) << "We should never read a response body of a tunnel."; + bool unfiltered_eof = (result == 0 && reading_body_from_socket_); + reading_body_from_socket_ = false; + + // Filter incoming data if appropriate. FilterBuf may return an error. + if (result > 0 && chunked_decoder_.get()) { + result = chunked_decoder_->FilterBuf(read_buf_->data(), result); + if (result == 0 && !chunked_decoder_->reached_eof()) { + // Don't signal completion of the Read call yet or else it'll look like + // we received end-of-file. Wait for more data. + next_state_ = STATE_READ_BODY; + return OK; + } + } + bool done = false, keep_alive = false; if (result < 0) { - // Error or closed connection while reading the socket. - done = true; - // TODO(wtc): Traditionally this code has returned 0 when reading a closed - // socket. That is partially corrected in classes that we call, but - // callers need to be updated. - if (result == ERR_CONNECTION_CLOSED) - result = 0; - } else if (http_stream_->IsResponseBodyComplete()) { + // Error while reading the socket. done = true; - keep_alive = GetResponseHeaders()->IsKeepAlive(); + } else { + response_body_read_ += result; + if (unfiltered_eof || + (response_body_length_ != -1 && + response_body_read_ >= response_body_length_) || + (chunked_decoder_.get() && chunked_decoder_->reached_eof())) { + done = true; + keep_alive = response_.headers->IsKeepAlive(); + // We can't reuse the connection if we read more than the advertised + // content length. + if (unfiltered_eof || + (response_body_length_ != -1 && + response_body_read_ > response_body_length_)) + keep_alive = false; + } } // Clean up connection_ if we are done. @@ -934,18 +1026,45 @@ int HttpNetworkTransaction::DoDrainBodyForAuthRestart() { return rv; } -// TODO(wtc): This method and the DoReadBodyComplete method are almost -// the same. Figure out a good way for these two methods to share code. +// TODO(wtc): The first two thirds of this method and the DoReadBodyComplete +// method are almost the same. Figure out a good way for these two methods +// to share code. int HttpNetworkTransaction::DoDrainBodyForAuthRestartComplete(int result) { + bool unfiltered_eof = (result == 0 && reading_body_from_socket_); + reading_body_from_socket_ = false; + + // Filter incoming data if appropriate. FilterBuf may return an error. + if (result > 0 && chunked_decoder_.get()) { + result = chunked_decoder_->FilterBuf(read_buf_->data(), result); + if (result == 0 && !chunked_decoder_->reached_eof()) { + // Don't signal completion of the Read call yet or else it'll look like + // we received end-of-file. Wait for more data. + next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; + return OK; + } + } + // keep_alive defaults to true because the very reason we're draining the // response body is to reuse the connection for auth restart. bool done = false, keep_alive = true; if (result < 0) { - // Error or closed connection while reading the socket. + // Error while reading the socket. done = true; keep_alive = false; - } else if (http_stream_->IsResponseBodyComplete()) { - done = true; + } else { + response_body_read_ += result; + if (unfiltered_eof || + (response_body_length_ != -1 && + response_body_read_ >= response_body_length_) || + (chunked_decoder_.get() && chunked_decoder_->reached_eof())) { + done = true; + // We can't reuse the connection if we read more than the advertised + // content length. + if (unfiltered_eof || + (response_body_length_ != -1 && + response_body_read_ > response_body_length_)) + keep_alive = false; + } } if (done) { @@ -1094,8 +1213,7 @@ void HttpNetworkTransaction::LogIOErrorMetrics( } void HttpNetworkTransaction::LogTransactionConnectedMetrics() const { - base::TimeDelta total_duration = - http_stream_->GetResponseInfo()->response_time - start_time_; + base::TimeDelta total_duration = response_.response_time - start_time_; UMA_HISTOGRAM_CLIPPED_TIMES( "Net.Transaction_Connected_Under_10", @@ -1154,8 +1272,7 @@ void HttpNetworkTransaction::LogTransactionConnectedMetrics() const { } void HttpNetworkTransaction::LogTransactionMetrics() const { - base::TimeDelta duration = base::Time::Now() - - http_stream_->GetResponseInfo()->request_time; + base::TimeDelta duration = base::Time::Now() - response_.request_time; if (60 < duration.InMinutes()) return; @@ -1183,6 +1300,143 @@ void HttpNetworkTransaction::LogBlockedTunnelResponse( << GetHostAndPort(request_->url) << "."; } +int HttpNetworkTransaction::DidReadResponseHeaders() { + DCHECK_GE(header_buf_body_offset_, 0); + + scoped_refptr<HttpResponseHeaders> headers; + if (has_found_status_line_start()) { + headers = new HttpResponseHeaders( + HttpUtil::AssembleRawHeaders( + header_buf_->headers(), header_buf_body_offset_)); + } else { + // Fabricate a status line to to preserve the HTTP/0.9 version. + // (otherwise HttpResponseHeaders will default it to HTTP/1.0). + headers = new HttpResponseHeaders(std::string("HTTP/0.9 200 OK")); + } + + if (headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { + // Require the "HTTP/1.x" status line for SSL CONNECT. + if (establishing_tunnel_) + return ERR_TUNNEL_CONNECTION_FAILED; + + // HTTP/0.9 doesn't support the PUT method, so lack of response headers + // indicates a buggy server. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=193921 + if (request_->method == "PUT") + return ERR_METHOD_NOT_SUPPORTED; + } + + if (establishing_tunnel_) { + switch (headers->response_code()) { + case 200: // OK + if (header_buf_body_offset_ != header_buf_len_) { + // The proxy sent extraneous data after the headers. + return ERR_TUNNEL_CONNECTION_FAILED; + } + next_state_ = STATE_SSL_CONNECT; + // Reset for the real request and response headers. + request_headers_->headers_.clear(); + request_headers_bytes_sent_ = 0; + header_buf_len_ = 0; + header_buf_body_offset_ = -1; + establishing_tunnel_ = false; + return OK; + + // We aren't able to CONNECT to the remote host through the proxy. We + // need to be very suspicious about the response because an active network + // attacker can force us into this state by masquerading as the proxy. + // The only safe thing to do here is to fail the connection because our + // client is expecting an SSL protected response. + // See http://crbug.com/7338. + case 407: // Proxy Authentication Required + // We need this status code to allow proxy authentication. Our + // authentication code is smart enough to avoid being tricked by an + // active network attacker. + break; + default: + // For all other status codes, we conservatively fail the CONNECT + // request. + // We lose something by doing this. We have seen proxy 403, 404, and + // 501 response bodies that contain a useful error message. For + // example, Squid uses a 404 response to report the DNS error: "The + // domain name does not exist." + LogBlockedTunnelResponse(headers->response_code()); + return ERR_TUNNEL_CONNECTION_FAILED; + } + } + + // Check for an intermediate 100 Continue response. An origin server is + // allowed to send this response even if we didn't ask for it, so we just + // need to skip over it. + // We treat any other 1xx in this same way (although in practice getting + // a 1xx that isn't a 100 is rare). + if (headers->response_code() / 100 == 1) { + header_buf_len_ -= header_buf_body_offset_; + // If we've already received some bytes after the 1xx response, + // move them to the beginning of header_buf_. + if (header_buf_len_) { + memmove(header_buf_->headers(), + header_buf_->headers() + header_buf_body_offset_, + header_buf_len_); + } + header_buf_body_offset_ = -1; + next_state_ = STATE_READ_HEADERS; + return OK; + } + + response_.headers = headers; + response_.vary_data.Init(*request_, *response_.headers); + + // Figure how to determine EOF: + + // For certain responses, we know the content length is always 0. From + // RFC 2616 Section 4.3 Message Body: + // + // For response messages, whether or not a message-body is included with + // a message is dependent on both the request method and the response + // status code (section 6.1.1). All responses to the HEAD request method + // MUST NOT include a message-body, even though the presence of entity- + // header fields might lead one to believe they do. All 1xx + // (informational), 204 (no content), and 304 (not modified) responses + // MUST NOT include a message-body. All other responses do include a + // message-body, although it MAY be of zero length. + switch (response_.headers->response_code()) { + // Note that 1xx was already handled earlier. + case 204: // No Content + case 205: // Reset Content + case 304: // Not Modified + response_body_length_ = 0; + break; + } + if (request_->method == "HEAD") + response_body_length_ = 0; + + if (response_body_length_ == -1) { + // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. + // Otherwise "Transfer-Encoding: chunked" trumps "Content-Length: N" + if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1) && + response_.headers->HasHeaderValue("Transfer-Encoding", "chunked")) { + chunked_decoder_.reset(new HttpChunkedDecoder()); + } else { + response_body_length_ = response_.headers->GetContentLength(); + // If response_body_length_ is still -1, then we have to wait for the + // server to close the connection. + } + } + + int rv = HandleAuthChallenge(); + if (rv != OK) + return rv; + + if (using_ssl_ && !establishing_tunnel_) { + SSLClientSocket* ssl_socket = + reinterpret_cast<SSLClientSocket*>(connection_.socket()); + ssl_socket->GetSSLInfo(&response_.ssl_info); + } + + return OK; +} + int HttpNetworkTransaction::HandleCertificateError(int error) { DCHECK(using_ssl_); @@ -1208,18 +1462,17 @@ int HttpNetworkTransaction::HandleCertificateError(int error) { } if (error != OK) { - HttpResponseInfo* response = http_stream_->GetResponseInfo(); SSLClientSocket* ssl_socket = reinterpret_cast<SSLClientSocket*>(connection_.socket()); - ssl_socket->GetSSLInfo(&response->ssl_info); + ssl_socket->GetSSLInfo(&response_.ssl_info); // Add the bad certificate to the set of allowed certificates in the // SSL info object. This data structure will be consulted after calling // RestartIgnoringLastError(). And the user will be asked interactively // before RestartIgnoringLastError() is ever called. SSLConfig::CertAndStatus bad_cert; - bad_cert.cert = response->ssl_info.cert; - bad_cert.cert_status = response->ssl_info.cert_status; + bad_cert.cert = response_.ssl_info.cert; + bad_cert.cert_status = response_.ssl_info.cert_status; ssl_config_.allowed_bad_certs.push_back(bad_cert); } return error; @@ -1236,11 +1489,10 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) { // test. DCHECK(reused_socket_ || !ssl_config_.send_client_cert); - HttpResponseInfo* response = http_stream_->GetResponseInfo(); - response->cert_request_info = new SSLCertRequestInfo; + response_.cert_request_info = new SSLCertRequestInfo; SSLClientSocket* ssl_socket = reinterpret_cast<SSLClientSocket*>(connection_.socket()); - ssl_socket->GetSSLCertRequestInfo(response->cert_request_info); + ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info); // Close the connection while the user is selecting a certificate to send // to the server. @@ -1253,7 +1505,7 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) { Lookup(GetHostAndPort(request_->url)); if (client_cert) { const std::vector<scoped_refptr<X509Certificate> >& client_certs = - response->cert_request_info->client_certs; + response_.cert_request_info->client_certs; for (size_t i = 0; i < client_certs.size(); ++i) { if (client_cert->fingerprint().Equals(client_certs[i]->fingerprint())) { ssl_config_.client_cert = client_cert; @@ -1318,16 +1570,20 @@ int HttpNetworkTransaction::HandleIOError(int error) { void HttpNetworkTransaction::ResetStateForRestart() { pending_auth_target_ = HttpAuth::AUTH_NONE; + header_buf_->Reset(); + header_buf_capacity_ = 0; + header_buf_len_ = 0; + header_buf_body_offset_ = -1; + header_buf_http_offset_ = -1; + response_body_length_ = -1; + response_body_read_ = 0; read_buf_ = NULL; read_buf_len_ = 0; - http_stream_.reset(new HttpBasicStream(&connection_)); - headers_valid_ = false; - request_headers_.clear(); -} - -HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const { - CHECK(http_stream_.get()); - return http_stream_->GetResponseInfo()->headers; + request_headers_->headers_.clear(); + request_headers_bytes_sent_ = 0; + chunked_decoder_.reset(); + // Reset all the members of response_. + response_ = HttpResponseInfo(); } bool HttpNetworkTransaction::ShouldResendRequest(int error) const { @@ -1335,8 +1591,12 @@ bool HttpNetworkTransaction::ShouldResendRequest(int error) const { // This automatically prevents an infinite resend loop because we'll run // out of the cached keep-alive connections eventually. if (establishing_tunnel_ || - !connection_.ShouldResendFailedRequest(error) || - GetResponseHeaders()) { // We have received some response headers. + // We used a socket that was never idle. + connection_.reuse_type() == ClientSocketHandle::UNUSED || + // We used an unused, idle socket and got a error that wasn't a TCP RST. + (connection_.reuse_type() == ClientSocketHandle::UNUSED_IDLE && + (error != OK && error != ERR_CONNECTION_RESET)) || + header_buf_len_) { // We have received some response headers. return false; } return true; @@ -1345,10 +1605,13 @@ bool HttpNetworkTransaction::ShouldResendRequest(int error) const { void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { connection_.socket()->Disconnect(); connection_.Reset(); - // We need to clear request_headers_ because it contains the real request - // headers, but we may need to resend the CONNECT request first to recreate - // the SSL tunnel. - request_headers_.clear(); + // There are two reasons we need to clear request_headers_. 1) It contains + // the real request headers, but we may need to resend the CONNECT request + // first to recreate the SSL tunnel. 2) An empty request_headers_ causes + // BuildRequestHeaders to be called, which rewinds request_body_stream_ to + // the beginning of request_->upload_data. + request_headers_->headers_.clear(); + request_headers_bytes_sent_ = 0; next_state_ = STATE_INIT_CONNECTION; // Resend the request. } @@ -1390,6 +1653,7 @@ int HttpNetworkTransaction::ReconsiderProxyAfterError(int error) { if (connection_.socket()) connection_.socket()->Disconnect(); connection_.Reset(); + DCHECK(!request_headers_bytes_sent_); next_state_ = STATE_RESOLVE_PROXY_COMPLETE; } else { rv = error; @@ -1549,14 +1813,15 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const { std::string msg; std::string header_val; void* iter = NULL; - scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); - while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) { + while (response_.headers->EnumerateHeader(&iter, "proxy-authenticate", + &header_val)) { msg.append("\n Has header Proxy-Authenticate: "); msg.append(header_val); } iter = NULL; - while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) { + while (response_.headers->EnumerateHeader(&iter, "www-authenticate", + &header_val)) { msg.append("\n Has header WWW-Authenticate: "); msg.append(header_val); } @@ -1565,7 +1830,8 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const { // authentication with a "Proxy-Support: Session-Based-Authentication" // response header. iter = NULL; - while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) { + while (response_.headers->EnumerateHeader(&iter, "proxy-support", + &header_val)) { msg.append("\n Has header Proxy-Support: "); msg.append(header_val); } @@ -1574,10 +1840,9 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const { } int HttpNetworkTransaction::HandleAuthChallenge() { - scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); - DCHECK(headers); + DCHECK(response_.headers); - int status = headers->response_code(); + int status = response_.headers->response_code(); if (status != 401 && status != 407) return OK; HttpAuth::Target target = status == 407 ? @@ -1609,7 +1874,9 @@ int HttpNetworkTransaction::HandleAuthChallenge() { if (target != HttpAuth::AUTH_SERVER || !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) { // Find the best authentication challenge that we support. - HttpAuth::ChooseBestChallenge(headers, target, auth_origin, + HttpAuth::ChooseBestChallenge(response_.headers.get(), + target, + auth_origin, &auth_handler_[target]); } @@ -1665,7 +1932,7 @@ void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target, auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme()); // TODO(eroman): decode realm according to RFC 2047. auth_info->realm = ASCIIToWide(auth_handler_[target]->realm()); - http_stream_->GetResponseInfo()->auth_challenge = auth_info; + response_.auth_challenge = auth_info; } } // namespace net diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index f540bd9..22acf492 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -28,8 +28,10 @@ namespace net { class ClientSocketFactory; +class HttpChunkedDecoder; class HttpNetworkSession; class HttpStream; +class UploadDataStream; class HttpNetworkTransaction : public HttpTransaction { public: @@ -60,6 +62,38 @@ class HttpNetworkTransaction : public HttpTransaction { private: FRIEND_TEST(HttpNetworkTransactionTest, ResetStateForRestart); + // This version of IOBuffer lets us use a string as the real storage and + // "move" the data pointer inside the string before using it to do actual IO. + class RequestHeaders : public net::IOBuffer { + public: + RequestHeaders() : net::IOBuffer() {} + ~RequestHeaders() { data_ = NULL; } + + void SetDataOffset(size_t offset) { + data_ = const_cast<char*>(headers_.data()) + offset; + } + + // This is intentionally a public member. + std::string headers_; + }; + + // This version of IOBuffer lets us use a malloc'ed buffer as the real storage + // and "move" the data pointer inside the buffer before using it to do actual + // IO. + class ResponseHeaders : public net::IOBuffer { + public: + ResponseHeaders() : net::IOBuffer() {} + ~ResponseHeaders() { data_ = NULL; } + + void set_data(size_t offset) { data_ = headers_.get() + offset; } + char* headers() { return headers_.get(); } + void Reset() { headers_.reset(); } + void Realloc(size_t new_size); + + private: + scoped_ptr_malloc<char> headers_; + }; + enum State { STATE_RESOLVE_PROXY, STATE_RESOLVE_PROXY_COMPLETE, @@ -69,8 +103,10 @@ class HttpNetworkTransaction : public HttpTransaction { STATE_SOCKS_CONNECT_COMPLETE, STATE_SSL_CONNECT, STATE_SSL_CONNECT_COMPLETE, - STATE_SEND_REQUEST, - STATE_SEND_REQUEST_COMPLETE, + STATE_WRITE_HEADERS, + STATE_WRITE_HEADERS_COMPLETE, + STATE_WRITE_BODY, + STATE_WRITE_BODY_COMPLETE, STATE_READ_HEADERS, STATE_READ_HEADERS_COMPLETE, STATE_READ_BODY, @@ -105,8 +141,10 @@ class HttpNetworkTransaction : public HttpTransaction { int DoSOCKSConnectComplete(int result); int DoSSLConnect(); int DoSSLConnectComplete(int result); - int DoSendRequest(); - int DoSendRequestComplete(int result); + int DoWriteHeaders(); + int DoWriteHeadersComplete(int result); + int DoWriteBody(); + int DoWriteBodyComplete(int result); int DoReadHeaders(); int DoReadHeadersComplete(int result); int DoReadBody(); @@ -129,6 +167,9 @@ class HttpNetworkTransaction : public HttpTransaction { static void LogIOErrorMetrics(const ClientSocketHandle& handle); + // Called when header_buf_ contains the complete response headers. + int DidReadResponseHeaders(); + // Called to handle a certificate error. Returns OK if the error should be // ignored. Otherwise, stores the certificate in response_.ssl_info and // returns the same error code. @@ -147,9 +188,6 @@ class HttpNetworkTransaction : public HttpTransaction { // is returned. int HandleIOError(int error); - // Gets the response headers from the HttpStream. - HttpResponseHeaders* GetResponseHeaders() const; - // Called when we reached EOF or got an error. Returns true if we should // resend the request. |error| is OK when we reached EOF. bool ShouldResendRequest(int error) const; @@ -171,6 +209,13 @@ class HttpNetworkTransaction : public HttpTransaction { // requests. int HandleConnectionClosedBeforeEndOfHeaders(); + // Return true if based on the bytes read so far, the start of the + // status line is known. This is used to distingish between HTTP/0.9 + // responses (which have no status line) and HTTP/1.x responses. + bool has_found_status_line_start() const { + return header_buf_http_offset_ != -1; + } + // Sets up the state machine to restart the transaction with auth. void PrepareForAuthRestart(HttpAuth::Target target); @@ -264,6 +309,7 @@ class HttpNetworkTransaction : public HttpTransaction { scoped_refptr<LoadLog> load_log_; const HttpRequestInfo* request_; + HttpResponseInfo response_; ProxyService::PacRequest* pac_request_; ProxyInfo proxy_info_; @@ -272,24 +318,33 @@ class HttpNetworkTransaction : public HttpTransaction { scoped_ptr<HttpStream> http_stream_; bool reused_socket_; - // True if we've validated the headers that the stream parser has returned. - bool headers_valid_; - - // True if we've logged the time of the first response byte. Used to - // prevent logging across authentication activity where we see multiple - // responses. - bool logged_response_time; - bool using_ssl_; // True if handling a HTTPS request ProxyMode proxy_mode_; // True while establishing a tunnel. This allows the HTTP CONNECT - // request/response to reuse the STATE_SEND_REQUEST, - // STATE_SEND_REQUEST_COMPLETE, STATE_READ_HEADERS, and + // request/response to reuse the STATE_WRITE_HEADERS, + // STATE_WRITE_HEADERS_COMPLETE, STATE_READ_HEADERS, and // STATE_READ_HEADERS_COMPLETE states and allows us to tell them apart from // the real request/response of the transaction. bool establishing_tunnel_; + // Only used between the states + // STATE_READ_BODY/STATE_DRAIN_BODY_FOR_AUTH and + // STATE_READ_BODY_COMPLETE/STATE_DRAIN_BODY_FOR_AUTH_COMPLETE. + // + // Set to true when DoReadBody or DoDrainBodyForAuthRestart starts to read + // the response body from the socket, and set to false when the socket read + // call completes. DoReadBodyComplete and DoDrainBodyForAuthRestartComplete + // use this boolean to disambiguate a |result| of 0 between a connection + // closure (EOF) and reaching the end of the response body (no more data). + // + // TODO(wtc): this is similar to the |ignore_ok_result_| member of the + // SSLClientSocketWin class. We may want to add an internal error code, say + // ERR_EOF, to indicate a connection closure, so that 0 simply means 0 bytes + // or OK. Note that we already have an ERR_CONNECTION_CLOSED error code, + // but it isn't really being used. + bool reading_body_from_socket_; + // True if we've used the username/password embedded in the URL. This // makes sure we use the embedded identity only once for the transaction, // preventing an infinite auth restart loop. @@ -297,13 +352,48 @@ class HttpNetworkTransaction : public HttpTransaction { SSLConfig ssl_config_; - std::string request_headers_; + scoped_refptr<RequestHeaders> request_headers_; + size_t request_headers_bytes_sent_; + scoped_ptr<UploadDataStream> request_body_stream_; + + // The read buffer |header_buf_| may be larger than it is full. The + // 'capacity' indicates the allocation size of the buffer, and the 'len' + // indicates how much data is in the buffer already. The 'body offset' + // indicates the offset of the start of the response body within the read + // buffer. + scoped_refptr<ResponseHeaders> header_buf_; + int header_buf_capacity_; + int header_buf_len_; + int header_buf_body_offset_; + + // The number of bytes by which the header buffer is grown when it reaches + // capacity. + enum { kHeaderBufInitialSize = 4096 }; + + // |kMaxHeaderBufSize| is the number of bytes that the response headers can + // grow to. If the body start is not found within this range of the + // response, the transaction will fail with ERR_RESPONSE_HEADERS_TOO_BIG. + // Note: |kMaxHeaderBufSize| should be a multiple of |kHeaderBufInitialSize|. + enum { kMaxHeaderBufSize = 256 * 1024 }; // 256 kilobytes. // The size in bytes of the buffer we use to drain the response body that // we want to throw away. The response body is typically a small error // page just a few hundred bytes long. enum { kDrainBodyBufferSize = 1024 }; + // The position where status line starts; -1 if not found yet. + int header_buf_http_offset_; + + // Indicates the content length remaining to read. If this value is less + // than zero (and chunked_decoder_ is null), then we read until the server + // closes the connection. + int64 response_body_length_; + + // Keeps track of the number of response body bytes read so far. + int64 response_body_read_; + + scoped_ptr<HttpChunkedDecoder> chunked_decoder_; + // User buffer and length passed to the Read method. scoped_refptr<IOBuffer> read_buf_; int read_buf_len_; diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index b47e34a..a3a4647 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -12,10 +12,8 @@ #include "net/base/test_completion_callback.h" #include "net/base/upload_data.h" #include "net/http/http_auth_handler_ntlm.h" -#include "net/http/http_basic_stream.h" #include "net/http/http_network_session.h" #include "net/http/http_network_transaction.h" -#include "net/http/http_stream.h" #include "net/http/http_transaction_unittest.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/socket/client_socket_factory.h" @@ -322,24 +320,6 @@ TEST_F(HttpNetworkTransactionTest, StopsReading204) { EXPECT_EQ("", out.response_data); } -// A simple request using chunked encoding with some extra data after. -// (Like might be seen in a pipelined response.) -TEST_F(HttpNetworkTransactionTest, ChunkedEncoding) { - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"), - MockRead("5\r\nHello\r\n"), - MockRead("1\r\n"), - MockRead(" \r\n"), - MockRead("5\r\nworld\r\n"), - MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"), - MockRead(false, OK), - }; - SimpleGetHelperResult out = SimpleGetHelper(data_reads); - EXPECT_EQ(OK, out.rv); - EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); - EXPECT_EQ("Hello world", out.response_data); -} - // Do a request using the HEAD method. Verify that we don't try to read the // message body (since HEAD has none). TEST_F(HttpNetworkTransactionTest, Head) { @@ -491,7 +471,7 @@ TEST_F(HttpNetworkTransactionTest, Ignores100) { // This test is almost the same as Ignores100 above, but the response contains // a 102 instead of a 100. Also, instead of HTTP/1.0 the response is -// HTTP/1.1 and the two status headers are read in one read. +// HTTP/1.1. TEST_F(HttpNetworkTransactionTest, Ignores1xx) { SessionDependencies session_deps; scoped_ptr<HttpTransaction> trans( @@ -503,8 +483,8 @@ TEST_F(HttpNetworkTransactionTest, Ignores1xx) { request.load_flags = 0; MockRead data_reads[] = { - MockRead("HTTP/1.1 102 Unspecified status code\r\n\r\n" - "HTTP/1.1 200 OK\r\n\r\n"), + MockRead("HTTP/1.1 102 Unspecified status code\r\n\r\n"), + MockRead("HTTP/1.1 200 OK\r\n\r\n"), MockRead("hello world"), MockRead(false, OK), }; @@ -2698,40 +2678,53 @@ TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) { new HttpNetworkTransaction(CreateSession(&session_deps))); // Setup some state (which we expect ResetStateForRestart() will clear). + trans->header_buf_->Realloc(10); + trans->header_buf_capacity_ = 10; + trans->header_buf_len_ = 3; + trans->header_buf_body_offset_ = 11; + trans->header_buf_http_offset_ = 0; + trans->response_body_length_ = 100; + trans->response_body_read_ = 1; trans->read_buf_ = new IOBuffer(15); trans->read_buf_len_ = 15; - trans->request_headers_ = "Authorization: NTLM"; + trans->request_headers_->headers_ = "Authorization: NTLM"; + trans->request_headers_bytes_sent_ = 3; // Setup state in response_ - trans->http_stream_.reset(new HttpBasicStream(NULL)); - HttpResponseInfo* response = trans->http_stream_->GetResponseInfo(); - response->auth_challenge = new AuthChallengeInfo(); - response->ssl_info.cert_status = -15; - response->response_time = base::Time::Now(); - response->was_cached = true; // (Wouldn't ever actually be true...) + trans->response_.auth_challenge = new AuthChallengeInfo(); + trans->response_.ssl_info.cert_status = -15; + trans->response_.response_time = base::Time::Now(); + trans->response_.was_cached = true; // (Wouldn't ever actually be true...) { // Setup state for response_.vary_data HttpRequestInfo request; std::string temp("HTTP/1.1 200 OK\nVary: foo, bar\n\n"); std::replace(temp.begin(), temp.end(), '\n', '\0'); - scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders(temp); + scoped_refptr<HttpResponseHeaders> response = new HttpResponseHeaders(temp); request.extra_headers = "Foo: 1\nbar: 23"; - EXPECT_TRUE(response->vary_data.Init(request, *headers)); + EXPECT_TRUE(trans->response_.vary_data.Init(request, *response)); } // Cause the above state to be reset. trans->ResetStateForRestart(); // Verify that the state that needed to be reset, has been reset. - response = trans->http_stream_->GetResponseInfo(); + EXPECT_TRUE(trans->header_buf_->headers() == NULL); + EXPECT_EQ(0, trans->header_buf_capacity_); + EXPECT_EQ(0, trans->header_buf_len_); + EXPECT_EQ(-1, trans->header_buf_body_offset_); + EXPECT_EQ(-1, trans->header_buf_http_offset_); + EXPECT_EQ(-1, trans->response_body_length_); + EXPECT_EQ(0, trans->response_body_read_); EXPECT_TRUE(trans->read_buf_.get() == NULL); EXPECT_EQ(0, trans->read_buf_len_); - EXPECT_EQ(0U, trans->request_headers_.size()); - EXPECT_TRUE(response->auth_challenge.get() == NULL); - EXPECT_TRUE(response->headers.get() == NULL); - EXPECT_EQ(false, response->was_cached); - EXPECT_EQ(0, response->ssl_info.cert_status); - EXPECT_FALSE(response->vary_data.is_valid()); + EXPECT_EQ("", trans->request_headers_->headers_); + EXPECT_EQ(0U, trans->request_headers_bytes_sent_); + EXPECT_TRUE(trans->response_.auth_challenge.get() == NULL); + EXPECT_TRUE(trans->response_.headers.get() == NULL); + EXPECT_EQ(false, trans->response_.was_cached); + EXPECT_EQ(0, trans->response_.ssl_info.cert_status); + EXPECT_FALSE(trans->response_.vary_data.is_valid()); } // Test HTTPS connections to a site with a bad certificate @@ -3602,189 +3595,4 @@ TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh) { EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); } -// Make sure we can handle an error when writing the request. -TEST_F(HttpNetworkTransactionTest, RequestWriteError) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); - - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.foo.com/"); - request.load_flags = 0; - - MockWrite write_failure[] = { - MockWrite(true, ERR_CONNECTION_RESET), - }; - StaticMockSocket data(NULL, write_failure); - session_deps.socket_factory.AddMockSocket(&data); - - TestCompletionCallback callback; - - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - - int rv = trans->Start(&request, &callback, NULL); - EXPECT_EQ(ERR_IO_PENDING, rv); - - rv = callback.WaitForResult(); - EXPECT_EQ(ERR_CONNECTION_RESET, rv); -} - -// Check that a connection closed after the start of the headers finishes ok. -TEST_F(HttpNetworkTransactionTest, ConnectionClosedAfterStartOfHeaders) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); - - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.foo.com/"); - request.load_flags = 0; - - MockRead data_reads[] = { - MockRead("HTTP/1."), - MockRead(false, OK), - }; - - StaticMockSocket data(data_reads, NULL); - session_deps.socket_factory.AddMockSocket(&data); - - TestCompletionCallback callback; - - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - - int rv = trans->Start(&request, &callback, NULL); - EXPECT_EQ(ERR_IO_PENDING, rv); - - rv = callback.WaitForResult(); - EXPECT_EQ(OK, rv); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - EXPECT_TRUE(response != NULL); - - EXPECT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.0 200 OK", response->headers->GetStatusLine()); - - std::string response_data; - rv = ReadTransaction(trans.get(), &response_data); - EXPECT_EQ(OK, rv); - EXPECT_EQ("", response_data); -} - -// Make sure that a dropped connection while draining the body for auth -// restart does the right thing. -TEST_F(HttpNetworkTransactionTest, DrainResetOK) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - - MockWrite data_writes1[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - - MockRead data_reads1[] = { - MockRead("HTTP/1.1 401 Unauthorized\r\n"), - MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"), - MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"), - MockRead("Content-Length: 14\r\n\r\n"), - MockRead("Unauth"), - MockRead(true, ERR_CONNECTION_RESET), - }; - - StaticMockSocket data1(data_reads1, data_writes1); - session_deps.socket_factory.AddMockSocket(&data1); - - // After calling trans->RestartWithAuth(), this is the request we should - // be issuing -- the final header line contains the credentials. - MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), - }; - - // Lastly, the server responds with the actual content. - MockRead data_reads2[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"), - MockRead("Content-Length: 100\r\n\r\n"), - MockRead(false, OK), - }; - - StaticMockSocket data2(data_reads2, data_writes2); - session_deps.socket_factory.AddMockSocket(&data2); - - TestCompletionCallback callback1; - - int rv = trans->Start(&request, &callback1, NULL); - EXPECT_EQ(ERR_IO_PENDING, rv); - - rv = callback1.WaitForResult(); - EXPECT_EQ(OK, rv); - - const HttpResponseInfo* response = trans->GetResponseInfo(); - EXPECT_FALSE(response == NULL); - - // The password prompt info should have been set in response->auth_challenge. - EXPECT_FALSE(response->auth_challenge.get() == NULL); - - EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port); - EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm); - EXPECT_EQ(L"basic", response->auth_challenge->scheme); - - TestCompletionCallback callback2; - - rv = trans->RestartWithAuth(L"foo", L"bar", &callback2); - EXPECT_EQ(ERR_IO_PENDING, rv); - - rv = callback2.WaitForResult(); - EXPECT_EQ(OK, rv); - - response = trans->GetResponseInfo(); - EXPECT_FALSE(response == NULL); - EXPECT_TRUE(response->auth_challenge.get() == NULL); - EXPECT_EQ(100, response->headers->GetContentLength()); -} - -// Test HTTPS connections going through a proxy that sends extra data. -TEST_F(HttpNetworkTransactionTest, HTTPSViaProxyWithExtraData) { - SessionDependencies session_deps(CreateFixedProxyService("myproxy:70")); - - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - - MockRead proxy_reads[] = { - MockRead("HTTP/1.0 200 Connected\r\n\r\nExtra data"), - MockRead(false, OK) - }; - - StaticMockSocket data(proxy_reads, NULL); - MockSSLSocket ssl(true, OK); - - session_deps.socket_factory.AddMockSocket(&data); - session_deps.socket_factory.AddMockSSLSocket(&ssl); - - TestCompletionCallback callback; - - session_deps.socket_factory.ResetNextMockIndexes(); - - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - - int rv = trans->Start(&request, &callback, NULL); - EXPECT_EQ(ERR_IO_PENDING, rv); - - rv = callback.WaitForResult(); - EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv); -} - } // namespace net diff --git a/net/http/http_stream.h b/net/http/http_stream.h index d0b9dc9..361edb0 100644 --- a/net/http/http_stream.h +++ b/net/http/http_stream.h @@ -6,73 +6,50 @@ // keeps the client agnostic of the actual underlying transport layer. This // provides an abstraction for both a basic http stream as well as http // pipelining implementations. +// +// NOTE(willchan): This interface is a work in progress. It will most likely +// change, since for a pipelining implementation, the stream needs to contain +// the http parsing code. For symmetry, the writing methods will probably +// contain the code for constructing http requests. #ifndef NET_HTTP_HTTP_STREAM_H_ #define NET_HTTP_HTTP_STREAM_H_ -#include <string> - #include "base/basictypes.h" -#include "net/socket/client_socket_handle.h" +#include "net/base/completion_callback.h" namespace net { -class HttpRequestInfo; -class HttpResponseInfo; class IOBuffer; -class UploadDataStream; class HttpStream { public: HttpStream() {} virtual ~HttpStream() {} - // Writes the headers and uploads body data to the underlying socket. - // ERR_IO_PENDING is returned if the operation could not be completed - // synchronously, in which case the result will be passed to the callback - // when available. Returns OK on success. The HttpStream takes ownership - // of the request_body. - virtual int SendRequest(const HttpRequestInfo* request, - const std::string& request_headers, - UploadDataStream* request_body, - CompletionCallback* callback) = 0; - - // Queries the UploadDataStream for its progress (bytes sent). - virtual uint64 GetUploadProgress() const = 0; - - // Reads from the underlying socket until the response headers have been - // completely received. ERR_IO_PENDING is returned if the operation could - // not be completed synchronously, in which case the result will be passed - // to the callback when available. Returns OK on success. The response - // headers are available in the HttpResponseInfo returned by GetResponseInfo - virtual int ReadResponseHeaders(CompletionCallback* callback) = 0; - - // Provides access to HttpResponseInfo (owned by HttpStream). - virtual HttpResponseInfo* GetResponseInfo() const = 0; - - // Reads response body data, up to |buf_len| bytes. The number of bytes read - // is returned, or an error is returned upon failure. ERR_CONNECTION_CLOSED - // is returned to indicate end-of-connection. ERR_IO_PENDING is returned if - // the operation could not be completed synchronously, in which case the - // result will be passed to the callback when available. If the operation is - // not completed immediately, the socket acquires a reference to the provided - // buffer until the callback is invoked or the socket is destroyed. - virtual int ReadResponseBody(IOBuffer* buf, int buf_len, - CompletionCallback* callback) = 0; - - // Indicates if the response body has been completely read. - virtual bool IsResponseBodyComplete() const = 0; - - // Indicates that the end of the response is detectable. This means that - // the response headers indicate either chunked encoding or content length. - // If neither is sent, the server must close the connection for us to detect - // the end of the response. - virtual bool CanFindEndOfResponse() const = 0; - - // After the response headers have been read and after the response body - // is complete, this function indicates if more data (either erroneous or - // as part of the next pipelined response) has been read from the socket. - virtual bool IsMoreDataBuffered() const = 0; + // Reads data, up to buf_len bytes, from the socket. The number of bytes + // read is returned, or an error is returned upon failure. Zero is returned + // to indicate end-of-file. ERR_IO_PENDING is returned if the operation + // could not be completed synchronously, in which case the result will be + // passed to the callback when available. If the operation is not completed + // immediately, the socket acquires a reference to the provided buffer until + // the callback is invoked or the socket is destroyed. + virtual int Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) = 0; + + // Writes data, up to buf_len bytes, to the socket. Note: only part of the + // data may be written! The number of bytes written is returned, or an error + // is returned upon failure. ERR_IO_PENDING is returned if the operation + // could not be completed synchronously, in which case the result will be + // passed to the callback when available. If the operation is not completed + // immediately, the socket acquires a reference to the provided buffer until + // the callback is invoked or the socket is destroyed. + // Implementations of this method should not modify the contents of the actual + // buffer that is written to the socket. + virtual int Write(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) = 0; private: DISALLOW_COPY_AND_ASSIGN(HttpStream); diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc deleted file mode 100644 index ff67081..0000000 --- a/net/http/http_stream_parser.cc +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/http/http_stream_parser.h" - -#include "base/compiler_specific.h" -#include "base/trace_event.h" -#include "net/base/io_buffer.h" -#include "net/http/http_request_info.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_util.h" - -namespace net { - -HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection, - GrowableIOBuffer* read_buffer) - : io_state_(STATE_NONE), - request_(NULL), - request_headers_(NULL), - request_body_(NULL), - read_buf_(read_buffer), - read_buf_unused_offset_(0), - response_header_start_offset_(-1), - response_body_length_(-1), - response_body_read_(0), - chunked_decoder_(NULL), - user_read_buf_(NULL), - user_read_buf_len_(0), - user_callback_(NULL), - connection_(connection), - ALLOW_THIS_IN_INITIALIZER_LIST( - io_callback_(this, &HttpStreamParser::OnIOComplete)) { - DCHECK_EQ(0, read_buffer->offset()); -} - -int HttpStreamParser::SendRequest(const HttpRequestInfo* request, - const std::string& headers, - UploadDataStream* request_body, - CompletionCallback* callback) { - DCHECK_EQ(STATE_NONE, io_state_); - DCHECK(!user_callback_); - DCHECK(callback); - - request_ = request; - scoped_refptr<StringIOBuffer> headers_io_buf = new StringIOBuffer(headers); - request_headers_ = new DrainableIOBuffer(headers_io_buf, - headers_io_buf->size()); - request_body_.reset(request_body); - - io_state_ = STATE_SENDING_HEADERS; - int result = DoLoop(OK); - if (result == ERR_IO_PENDING) - user_callback_ = callback; - - return result > 0 ? OK : result; -} - -int HttpStreamParser::ReadResponseHeaders(CompletionCallback* callback) { - DCHECK_EQ(STATE_REQUEST_SENT, io_state_); - DCHECK(!user_callback_); - DCHECK(callback); - - int result = OK; - io_state_ = STATE_READ_HEADERS; - - if (read_buf_->offset() > 0) { - // Simulate the state where the data was just read from the socket. - result = read_buf_->offset() - read_buf_unused_offset_; - read_buf_->set_offset(read_buf_unused_offset_); - } - if (result > 0) - io_state_ = STATE_READ_HEADERS_COMPLETE; - - result = DoLoop(result); - if (result == ERR_IO_PENDING) - user_callback_ = callback; - - return result > 0 ? OK : result; -} - -int HttpStreamParser::ReadResponseBody(IOBuffer* buf, int buf_len, - CompletionCallback* callback) { - DCHECK(io_state_ == STATE_BODY_PENDING || io_state_ == STATE_DONE); - DCHECK(!user_callback_); - DCHECK(callback); - - if (io_state_ == STATE_DONE) - return OK; - - user_read_buf_ = buf; - user_read_buf_len_ = buf_len; - io_state_ = STATE_READ_BODY; - - int result = DoLoop(OK); - if (result == ERR_IO_PENDING) - user_callback_ = callback; - - return result; -} - -void HttpStreamParser::OnIOComplete(int result) { - result = DoLoop(result); - - // The client callback can do anything, including destroying this class, - // so any pending callback must be issued after everything else is done. - if (result != ERR_IO_PENDING && user_callback_) { - CompletionCallback* c = user_callback_; - user_callback_ = NULL; - c->Run(result); - } -} - -int HttpStreamParser::DoLoop(int result) { - bool can_do_more = true; - do { - switch (io_state_) { - case STATE_SENDING_HEADERS: - TRACE_EVENT_BEGIN("http.write_headers", request_, request_->url.spec()); - if (result < 0) - can_do_more = false; - else - result = DoSendHeaders(result); - TRACE_EVENT_END("http.write_headers", request_, request_->url.spec()); - break; - case STATE_SENDING_BODY: - TRACE_EVENT_BEGIN("http.write_body", request_, request_->url.spec()); - if (result < 0) - can_do_more = false; - else - result = DoSendBody(result); - TRACE_EVENT_END("http.write_body", request_, request_->url.spec()); - break; - case STATE_REQUEST_SENT: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; - break; - case STATE_READ_HEADERS: - TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec()); - result = DoReadHeaders(); - break; - case STATE_READ_HEADERS_COMPLETE: - result = DoReadHeadersComplete(result); - TRACE_EVENT_END("http.read_headers", request_, request_->url.spec()); - break; - case STATE_BODY_PENDING: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; - break; - case STATE_READ_BODY: - TRACE_EVENT_BEGIN("http.read_body", request_, request_->url.spec()); - result = DoReadBody(); - // DoReadBodyComplete handles error conditions. - break; - case STATE_READ_BODY_COMPLETE: - result = DoReadBodyComplete(result); - TRACE_EVENT_END("http.read_body", request_, request_->url.spec()); - break; - case STATE_DONE: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; - break; - default: - NOTREACHED(); - can_do_more = false; - break; - } - } while (result != ERR_IO_PENDING && can_do_more); - - return result; -} - -int HttpStreamParser::DoSendHeaders(int result) { - request_headers_->DidConsume(result); - - if (request_headers_->BytesRemaining() > 0) { - // Record our best estimate of the 'request time' as the time when we send - // out the first bytes of the request headers. - if (request_headers_->BytesRemaining() == request_headers_->size()) { - response_.request_time = base::Time::Now(); - } - result = connection_->socket()->Write(request_headers_, - request_headers_->BytesRemaining(), - &io_callback_); - } else if (request_body_ != NULL && request_body_->size()) { - io_state_ = STATE_SENDING_BODY; - result = OK; - } else { - io_state_ = STATE_REQUEST_SENT; - } - return result; -} - -int HttpStreamParser::DoSendBody(int result) { - request_body_->DidConsume(result); - - if (request_body_->position() < request_body_->size()) { - int buf_len = static_cast<int>(request_body_->buf_len()); - result = connection_->socket()->Write(request_body_->buf(), buf_len, - &io_callback_); - } else { - io_state_ = STATE_REQUEST_SENT; - } - return result; -} - -int HttpStreamParser::DoReadHeaders() { - io_state_ = STATE_READ_HEADERS_COMPLETE; - - // Grow the read buffer if necessary. - if (read_buf_->RemainingCapacity() == 0) - read_buf_->set_capacity(read_buf_->capacity() + kHeaderBufInitialSize); - - // http://crbug.com/16371: We're seeing |user_buf_->data()| return NULL. - // See if the user is passing in an IOBuffer with a NULL |data_|. - CHECK(read_buf_->data()); - - int bytes_read = connection_->socket()->Read(read_buf_, - read_buf_->RemainingCapacity(), - &io_callback_); - if (bytes_read == 0) - bytes_read = ERR_CONNECTION_CLOSED; - - return bytes_read; -} - -int HttpStreamParser::DoReadHeadersComplete(int result) { - if (result < 0 && result != ERR_CONNECTION_CLOSED) { - io_state_ = STATE_DONE; - return result; - } - if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 && - connection_->ShouldResendFailedRequest(result)) { - io_state_ = STATE_DONE; - return result; - } - - // Record our best estimate of the 'response time' as the time when we read - // the first bytes of the response headers. - if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED) - response_.response_time = base::Time::Now(); - - if (result == ERR_CONNECTION_CLOSED) { - // The connection closed before we detected the end of the headers. - // parse things as well as we can and let the caller decide what to do. - if (read_buf_->offset() == 0) { - // The connection was closed before any data was sent. Likely an error - // rather than empty HTTP/0.9 response. - io_state_ = STATE_DONE; - return ERR_EMPTY_RESPONSE; - } else { - int end_offset; - if (response_header_start_offset_ >= 0) { - io_state_ = STATE_READ_BODY_COMPLETE; - end_offset = read_buf_->offset(); - } else { - io_state_ = STATE_BODY_PENDING; - end_offset = 0; - } - DoParseResponseHeaders(end_offset); - return result; - } - } - - read_buf_->set_offset(read_buf_->offset() + result); - DCHECK_LE(read_buf_->offset(), read_buf_->capacity()); - DCHECK(result >= 0); - - int end_of_header_offset = ParseResponseHeaders(); - if (end_of_header_offset == -1) { - io_state_ = STATE_READ_HEADERS; - // Prevent growing the headers buffer indefinitely. - if (read_buf_->offset() - read_buf_unused_offset_ >= kMaxHeaderBufSize) { - io_state_ = STATE_DONE; - return ERR_RESPONSE_HEADERS_TOO_BIG; - } - } else { - // Note where the headers stop. - read_buf_unused_offset_ = end_of_header_offset; - - if (response_.headers->response_code() / 100 == 1) { - // After processing a 1xx response, the caller will ask for the next - // header, so reset state to support that. We don't just skip these - // completely because 1xx codes aren't acceptable when establishing a - // tunnel. - io_state_ = STATE_REQUEST_SENT; - response_header_start_offset_ = -1; - } else { - io_state_ = STATE_BODY_PENDING; - CalculateResponseBodySize(); - // If the body is 0, the caller may not call ReadResponseBody, which - // is where any extra data is copied to read_buf_, so we trigger - // the progression to DONE here. - if (response_body_length_ == 0) { - io_state_ = STATE_READ_BODY; - user_read_buf_ = read_buf_; - user_read_buf_len_ = read_buf_->capacity(); - return OK; - } - } - } - return result; -} - -int HttpStreamParser::DoReadBody() { - io_state_ = STATE_READ_BODY_COMPLETE; - - int bytes_read; - // There may be some data left over from reading the response headers. - if (read_buf_->offset()) { - int available = read_buf_->offset() - read_buf_unused_offset_; - if (available) { - bytes_read = std::min(available, user_read_buf_len_); - // memmove is used here so that the caller can pass read_buf_ - // for user_read_buf. - memmove(user_read_buf_->data(), - read_buf_->StartOfBuffer() + read_buf_unused_offset_, - bytes_read); - read_buf_unused_offset_ += bytes_read; - if (bytes_read == available) { - read_buf_->set_capacity(0); - read_buf_unused_offset_ = 0; - } - return bytes_read; - } else { - read_buf_->set_capacity(0); - read_buf_unused_offset_ = 0; - } - } - - // Check to see if we're done reading. - if (IsResponseBodyComplete()) - return 0; - - DCHECK_EQ(0, read_buf_->offset()); - bytes_read = connection_->socket()->Read(user_read_buf_, user_read_buf_len_, - &io_callback_); - if (bytes_read == 0) - bytes_read = ERR_CONNECTION_CLOSED; - - return bytes_read; -} - -int HttpStreamParser::DoReadBodyComplete(int result) { - // Filter incoming data if appropriate. FilterBuf may return an error. - if (result > 0 && chunked_decoder_.get()) { - result = chunked_decoder_->FilterBuf(user_read_buf_->data(), result); - if (result == 0 && !chunked_decoder_->reached_eof()) { - // Don't signal completion of the Read call yet or else it'll look like - // we received end-of-file. Wait for more data. - io_state_ = STATE_READ_BODY; - return OK; - } - } - - if (result > 0) - response_body_read_ += result; - - if (result < 0 || IsResponseBodyComplete()) { - io_state_ = STATE_DONE; - - // Save the overflow data, which can be in two places. There may be - // some left over in |user_read_buf_|, plus there may be more - // in |read_buf_|. But the part left over in |user_read_buf_| must have - // come from the |read_buf_|, so there's room to put it back at the - // start first. - int save_amount = 0; - int additional_save_amount = read_buf_->offset() - read_buf_unused_offset_; - if (chunked_decoder_.get()) { - save_amount = chunked_decoder_->bytes_after_eof(); - } else if (response_body_length_ >= 0) { - save_amount = static_cast<int>(response_body_read_ - - response_body_length_); - if (result > 0) - result -= save_amount; - } - if (save_amount > 0) { - if (static_cast<int>(read_buf_->capacity()) < save_amount) - read_buf_->set_capacity(save_amount + additional_save_amount); - read_buf_->set_offset(save_amount); - // memmove is used here so that the caller can pass read_buf_ - // for body_buf. - memmove(read_buf_->StartOfBuffer(), user_read_buf_->data() + result, - save_amount); - } - if (additional_save_amount) { - memmove(read_buf_->data(), - read_buf_->StartOfBuffer() + read_buf_unused_offset_, - additional_save_amount); - read_buf_->set_offset(save_amount + additional_save_amount); - } - read_buf_unused_offset_ = 0; - } else { - io_state_ = STATE_BODY_PENDING; - user_read_buf_ = NULL; - user_read_buf_len_ = 0; - } - - return result; -} - -int HttpStreamParser::ParseResponseHeaders() { - int end_offset = -1; - - // Look for the start of the status line, if it hasn't been found yet. - if (response_header_start_offset_ < 0) { - response_header_start_offset_ = HttpUtil::LocateStartOfStatusLine( - read_buf_->StartOfBuffer() + read_buf_unused_offset_, - read_buf_->offset() - read_buf_unused_offset_); - } - - if (response_header_start_offset_ >= 0) { - end_offset = HttpUtil::LocateEndOfHeaders( - read_buf_->StartOfBuffer() + read_buf_unused_offset_, - read_buf_->offset() - read_buf_unused_offset_, - response_header_start_offset_); - } else if (read_buf_->offset() - read_buf_unused_offset_ >= 8) { - // Enough data to decide that this is an HTTP/0.9 response. - // 8 bytes = (4 bytes of junk) + "http".length() - end_offset = 0; - } - - if (end_offset == -1) - return -1; - - DoParseResponseHeaders(end_offset); - return end_offset + read_buf_unused_offset_; -} - -void HttpStreamParser::DoParseResponseHeaders(int end_offset) { - scoped_refptr<HttpResponseHeaders> headers; - if (response_header_start_offset_ >= 0) { - headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders( - read_buf_->StartOfBuffer() + read_buf_unused_offset_, end_offset)); - } else { - // Enough data was read -- there is no status line. - headers = new HttpResponseHeaders(std::string("HTTP/0.9 200 OK")); - } - - response_.headers = headers; - response_.vary_data.Init(*request_, *response_.headers); -} - -void HttpStreamParser::CalculateResponseBodySize() { - // Figure how to determine EOF: - - // For certain responses, we know the content length is always 0. From - // RFC 2616 Section 4.3 Message Body: - // - // For response messages, whether or not a message-body is included with - // a message is dependent on both the request method and the response - // status code (section 6.1.1). All responses to the HEAD request method - // MUST NOT include a message-body, even though the presence of entity- - // header fields might lead one to believe they do. All 1xx - // (informational), 204 (no content), and 304 (not modified) responses - // MUST NOT include a message-body. All other responses do include a - // message-body, although it MAY be of zero length. - switch (response_.headers->response_code()) { - // Note that 1xx was already handled earlier. - case 204: // No Content - case 205: // Reset Content - case 304: // Not Modified - response_body_length_ = 0; - break; - } - if (request_->method == "HEAD") - response_body_length_ = 0; - - if (response_body_length_ == -1) { - // Ignore spurious chunked responses from HTTP/1.0 servers and - // proxies. Otherwise "Transfer-Encoding: chunked" trumps - // "Content-Length: N" - if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1) && - response_.headers->HasHeaderValue("Transfer-Encoding", "chunked")) { - chunked_decoder_.reset(new HttpChunkedDecoder()); - } else { - response_body_length_ = response_.headers->GetContentLength(); - // If response_body_length_ is still -1, then we have to wait - // for the server to close the connection. - } - } -} - -uint64 HttpStreamParser::GetUploadProgress() const { - if (!request_body_.get()) - return 0; - - return request_body_->position(); -} - -HttpResponseInfo* HttpStreamParser::GetResponseInfo() { - return &response_; -} - -bool HttpStreamParser::IsResponseBodyComplete() const { - if (chunked_decoder_.get()) - return chunked_decoder_->reached_eof(); - if (response_body_length_ != -1) - return response_body_read_ >= response_body_length_; - - return false; // Must read to EOF. -} - -bool HttpStreamParser::CanFindEndOfResponse() const { - return chunked_decoder_.get() || response_body_length_ >= 0; -} - -bool HttpStreamParser::IsMoreDataBuffered() const { - return read_buf_->offset() > read_buf_unused_offset_; -} - -} // namespace net diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h deleted file mode 100644 index 012039f..0000000 --- a/net/http/http_stream_parser.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_HTTP_HTTP_STREAM_PARSER_H_ -#define NET_HTTP_HTTP_STREAM_PARSER_H_ - -#include <string> - -#include "base/basictypes.h" -#include "net/base/io_buffer.h" -#include "net/base/upload_data_stream.h" -#include "net/http/http_chunked_decoder.h" -#include "net/http/http_response_info.h" -#include "net/socket/client_socket_handle.h" - -namespace net { - -class ClientSocketHandle; -class HttpRequestInfo; - -class HttpStreamParser { - public: - // Any data in |read_buffer| will be used before reading from the socket - // and any data left over after parsing the stream will be put into - // |read_buffer|. The left over data will start at offset 0 and the - // buffer's offset will be set to the first free byte. |read_buffer| may - // have its capacity changed. - HttpStreamParser(ClientSocketHandle* connection, - GrowableIOBuffer* read_buffer); - ~HttpStreamParser() {} - - // These functions implement the interface described in HttpStream with - // some additional functionality - int SendRequest(const HttpRequestInfo* request, const std::string& headers, - UploadDataStream* request_body, CompletionCallback* callback); - - int ReadResponseHeaders(CompletionCallback* callback); - - int ReadResponseBody(IOBuffer* buf, int buf_len, - CompletionCallback* callback); - - uint64 GetUploadProgress() const; - - HttpResponseInfo* GetResponseInfo(); - - bool IsResponseBodyComplete() const; - - bool CanFindEndOfResponse() const; - - bool IsMoreDataBuffered() const; - - private: - // FOO_COMPLETE states implement the second half of potentially asynchronous - // operations and don't necessarily mean that FOO is complete. - enum State { - STATE_NONE, - STATE_SENDING_HEADERS, - STATE_SENDING_BODY, - STATE_REQUEST_SENT, - STATE_READ_HEADERS, - STATE_READ_HEADERS_COMPLETE, - STATE_BODY_PENDING, - STATE_READ_BODY, - STATE_READ_BODY_COMPLETE, - STATE_DONE - }; - - // The number of bytes by which the header buffer is grown when it reaches - // capacity. - enum { kHeaderBufInitialSize = 4096 }; - - // |kMaxHeaderBufSize| is the number of bytes that the response headers can - // grow to. If the body start is not found within this range of the - // response, the transaction will fail with ERR_RESPONSE_HEADERS_TOO_BIG. - // Note: |kMaxHeaderBufSize| should be a multiple of |kHeaderBufInitialSize|. - enum { kMaxHeaderBufSize = 256 * 1024 }; // 256 kilobytes. - - // Handle callbacks. - void OnIOComplete(int result); - - // Try to make progress sending/receiving the request/response. - int DoLoop(int result); - - // The implementations of each state of the state machine. - int DoSendHeaders(int result); - int DoSendBody(int result); - int DoReadHeaders(); - int DoReadHeadersComplete(int result); - int DoReadBody(); - int DoReadBodyComplete(int result); - - // Examines |read_buf_| to find the start and end of the headers. Return - // the offset for the end of the headers, or -1 if the complete headers - // were not found. If they are are found, parse them with - // DoParseResponseHeaders(). - int ParseResponseHeaders(); - - // Parse the headers into response_. - void DoParseResponseHeaders(int end_of_header_offset); - - // Examine the parsed headers to try to determine the response body size. - void CalculateResponseBodySize(); - - // Current state of the request. - State io_state_; - - // The request to send. - const HttpRequestInfo* request_; - - // The request header data. - scoped_refptr<DrainableIOBuffer> request_headers_; - - // The request body data. - scoped_ptr<UploadDataStream> request_body_; - - // Temporary buffer for reading. - scoped_refptr<GrowableIOBuffer> read_buf_; - - // Offset of the first unused byte in |read_buf_|. May be nonzero due to - // a 1xx header, or body data in the same packet as header data. - int read_buf_unused_offset_; - - // The amount beyond |read_buf_unused_offset_| where the status line starts; - // -1 if not found yet. - int response_header_start_offset_; - - // The parsed response headers. - HttpResponseInfo response_; - - // Indicates the content length. If this value is less than zero - // (and chunked_decoder_ is null), then we must read until the server - // closes the connection. - int64 response_body_length_; - - // Keep track of the number of response body bytes read so far. - int64 response_body_read_; - - // Helper if the data is chunked. - scoped_ptr<HttpChunkedDecoder> chunked_decoder_; - - // Where the caller wants the body data. - scoped_refptr<IOBuffer> user_read_buf_; - int user_read_buf_len_; - - // The callback to notify a user that their request or response is - // complete or there was an error - CompletionCallback* user_callback_; - - // In the client callback, the client can do anything, including - // destroying this class, so any pending callback must be issued - // after everything else is done. When it is time to issue the client - // callback, move it from |user_callback_| to |scheduled_callback_|. - CompletionCallback* scheduled_callback_; - - // The underlying socket. - ClientSocketHandle* const connection_; - - // Callback to be used when doing IO. - CompletionCallbackImpl<HttpStreamParser> io_callback_; - - DISALLOW_COPY_AND_ASSIGN(HttpStreamParser); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_STREAM_PARSER_H_ diff --git a/net/net.gyp b/net/net.gyp index 00c5661..2d0b342 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -240,7 +240,6 @@ 'http/http_auth_handler_ntlm.h', 'http/http_auth_handler_ntlm_portable.cc', 'http/http_auth_handler_ntlm_win.cc', - 'http/http_basic_stream.cc', 'http/http_basic_stream.h', 'http/http_byte_range.cc', 'http/http_byte_range.h', @@ -260,8 +259,6 @@ 'http/http_response_info.cc', 'http/http_response_info.h', 'http/http_stream.h', - 'http/http_stream_parser.cc', - 'http/http_stream_parser.h', 'http/http_transaction.h', 'http/http_transaction_factory.h', 'http/http_util.cc', diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h index 25a2f35..83aa70f 100644 --- a/net/socket/client_socket_handle.h +++ b/net/socket/client_socket_handle.h @@ -104,19 +104,6 @@ class ClientSocketHandle { return UNUSED_IDLE; } } - bool ShouldResendFailedRequest(int error) const { - // NOTE: we resend a request only if we reused a keep-alive connection. - // This automatically prevents an infinite resend loop because we'll run - // out of the cached keep-alive connections eventually. - if ( // We used a socket that was never idle. - reuse_type() == ClientSocketHandle::UNUSED || - // We used an unused, idle socket and got a error that wasn't a TCP RST. - (reuse_type() == ClientSocketHandle::UNUSED_IDLE && - (error != OK && error != ERR_CONNECTION_RESET))) { - return false; - } - return true; - } private: // Called on asynchronous completion of an Init() request. |