summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorvandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-17 22:29:57 +0000
committervandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-17 22:29:57 +0000
commit0877e3db672aa26d89549f545e6e6c64904fec4c (patch)
tree127ce4a310fd137427c1963dc6eb02410bcb235d /net
parentb71e79d427311fdd9ec36ac5a122fc987bdb2020 (diff)
downloadchromium_src-0877e3db672aa26d89549f545e6e6c64904fec4c.zip
chromium_src-0877e3db672aa26d89549f545e6e6c64904fec4c.tar.gz
chromium_src-0877e3db672aa26d89549f545e6e6c64904fec4c.tar.bz2
Refactor HttpNetworkTransaction so that HttpStream is responsible for parsing the Http traffic. HttpBasicStream delegates parsing to HttpStreamParser in preparation for HttpPipelinedStream.
Original review: http://codereview.chromium.org/249031 BUG=13289 TEST=unittests Review URL: http://codereview.chromium.org/283022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29379 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_basic_stream.cc50
-rw-r--r--net/http/http_basic_stream.h46
-rw-r--r--net/http/http_network_transaction.cc635
-rw-r--r--net/http/http_network_transaction.h126
-rw-r--r--net/http/http_network_transaction_unittest.cc256
-rw-r--r--net/http/http_stream.h81
-rw-r--r--net/http/http_stream_parser.cc512
-rw-r--r--net/http/http_stream_parser.h167
-rw-r--r--net/net.gyp3
-rw-r--r--net/socket/client_socket_handle.h13
10 files changed, 1255 insertions, 634 deletions
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
new file mode 100644
index 0000000..f1e6100
--- /dev/null
+++ b/net/http/http_basic_stream.cc
@@ -0,0 +1,50 @@
+// 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 2a82b7a..1b1f68a 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -9,32 +9,50 @@
#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/socket/client_socket_handle.h"
+#include "net/http/http_stream_parser.h"
namespace net {
+class ClientSocketHandle;
+class HttpRequestInfo;
+class HttpResponseInfo;
+class UploadDataStream;
+
class HttpBasicStream : public HttpStream {
public:
- explicit HttpBasicStream(ClientSocketHandle* handle) : handle_(handle) {}
+ explicit HttpBasicStream(ClientSocketHandle* handle);
virtual ~HttpBasicStream() {}
// HttpStream methods:
- 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);
- }
+ 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;
private:
- ClientSocketHandle* const handle_;
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+
+ scoped_ptr<HttpStreamParser> parser_;
DISALLOW_COPY_AND_ASSIGN(HttpBasicStream);
};
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 6324d7d..9e8b98a 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -25,6 +25,7 @@
#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"
@@ -35,10 +36,6 @@ 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,
@@ -138,20 +135,12 @@ 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_);
@@ -176,7 +165,7 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
int HttpNetworkTransaction::RestartIgnoringLastError(
CompletionCallback* callback) {
if (connection_.socket()->IsConnected()) {
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
} else {
connection_.socket()->Disconnect();
connection_.Reset();
@@ -266,19 +255,19 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
}
bool keep_alive = false;
- 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()) {
+ // 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()) {
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;
}
- 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.
+ keep_alive = true;
}
// We don't need to drain the response body, so we act as if we had drained
@@ -288,7 +277,7 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
if (keep_alive) {
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
reused_socket_ = true;
} else {
next_state_ = STATE_INIT_CONNECTION;
@@ -302,7 +291,8 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
CompletionCallback* callback) {
- DCHECK(response_.headers);
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+ DCHECK(headers.get());
DCHECK(buf);
DCHECK_LT(0, buf_len);
@@ -317,8 +307,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, response_.headers->response_code());
- LogBlockedTunnelResponse(response_.headers->response_code());
+ DCHECK_EQ(407, headers->response_code());
+ LogBlockedTunnelResponse(headers->response_code());
return ERR_TUNNEL_CONNECTION_FAILED;
}
@@ -338,8 +328,9 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
}
const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const {
- return (response_.headers || response_.ssl_info.cert ||
- response_.cert_request_info) ? &response_ : NULL;
+ const HttpResponseInfo* response = http_stream_->GetResponseInfo();
+ return ((headers_valid_ && response->headers) || response->ssl_info.cert ||
+ response->cert_request_info) ? response : NULL;
}
LoadState HttpNetworkTransaction::GetLoadState() const {
@@ -350,8 +341,7 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
case STATE_INIT_CONNECTION_COMPLETE:
return connection_.GetLoadState();
- case STATE_WRITE_HEADERS_COMPLETE:
- case STATE_WRITE_BODY_COMPLETE:
+ case STATE_SEND_REQUEST_COMPLETE:
return LOAD_STATE_SENDING_REQUEST;
case STATE_READ_HEADERS_COMPLETE:
return LOAD_STATE_WAITING_FOR_RESPONSE;
@@ -363,10 +353,10 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
}
uint64 HttpNetworkTransaction::GetUploadProgress() const {
- if (!request_body_stream_.get())
+ if (!http_stream_.get())
return 0;
- return request_body_stream_->position();
+ return http_stream_->GetUploadProgress();
}
HttpNetworkTransaction::~HttpNetworkTransaction() {
@@ -439,23 +429,14 @@ int HttpNetworkTransaction::DoLoop(int result) {
rv = DoSSLConnectComplete(rv);
TRACE_EVENT_END("http.ssl_connect", request_, request_->url.spec());
break;
- case STATE_WRITE_HEADERS:
+ case STATE_SEND_REQUEST:
DCHECK_EQ(OK, rv);
- TRACE_EVENT_BEGIN("http.write_headers", request_, request_->url.spec());
- rv = DoWriteHeaders();
+ TRACE_EVENT_BEGIN("http.send_request", request_, request_->url.spec());
+ rv = DoSendRequest();
break;
- 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());
+ case STATE_SEND_REQUEST_COMPLETE:
+ rv = DoSendRequestComplete(rv);
+ TRACE_EVENT_END("http.send_request", request_, request_->url.spec());
break;
case STATE_READ_HEADERS:
DCHECK_EQ(OK, rv);
@@ -611,7 +592,7 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
// trying to reuse a keep-alive connection.
reused_socket_ = connection_.is_reused();
if (reused_socket_) {
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
} else {
// Now we have a TCP connected socket. Perform other connection setup as
// needed.
@@ -620,11 +601,12 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
else if (using_ssl_ && proxy_mode_ == kDirectConnection) {
next_state_ = STATE_SSL_CONNECT;
} else {
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
if (proxy_mode_ == kHTTPProxyUsingTunnel)
establishing_tunnel_ = true;
}
}
+ headers_valid_ = false;
http_stream_.reset(new HttpBasicStream(&connection_));
return OK;
}
@@ -655,7 +637,7 @@ int HttpNetworkTransaction::DoSOCKSConnectComplete(int result) {
if (using_ssl_) {
next_state_ = STATE_SSL_CONNECT;
} else {
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
}
} else {
result = ReconsiderProxyAfterError(result);
@@ -694,7 +676,7 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) {
base::TimeDelta::FromMinutes(10),
100);
- next_state_ = STATE_WRITE_HEADERS;
+ next_state_ = STATE_SEND_REQUEST;
} else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
result = HandleCertificateRequest(result);
} else {
@@ -703,12 +685,16 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) {
return result;
}
-int HttpNetworkTransaction::DoWriteHeaders() {
- next_state_ = STATE_WRITE_HEADERS_COMPLETE;
+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);
// This is constructed lazily (instead of within our Start method), so that
// we have proxy info available.
- if (request_headers_->headers_.empty()) {
+ if (request_headers_.empty()) {
// Figure out if we can/should add Proxy-Authentication & Authentication
// headers.
bool have_proxy_auth =
@@ -733,91 +719,30 @@ int HttpNetworkTransaction::DoWriteHeaders() {
BuildAuthorizationHeader(HttpAuth::AUTH_SERVER));
if (establishing_tunnel_) {
- BuildTunnelRequest(request_, authorization_headers,
- &request_headers_->headers_);
+ BuildTunnelRequest(request_, authorization_headers, &request_headers_);
} else {
- 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_);
+ BuildRequestHeaders(request_, authorization_headers, request_body,
+ proxy_mode_ == kHTTPProxy, &request_headers_);
}
}
- // 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_);
+ return http_stream_->SendRequest(request_, request_headers_, request_body,
+ &io_callback_);
}
-int HttpNetworkTransaction::DoWriteHeadersComplete(int result) {
+int HttpNetworkTransaction::DoSendRequestComplete(int result) {
if (result < 0)
return HandleIOError(result);
- 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_);
-}
+ next_state_ = STATE_READ_HEADERS;
-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;
- // 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_);
+ return http_stream_->ReadResponseHeaders(&io_callback_);
}
int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
@@ -826,24 +751,12 @@ int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
return ERR_TUNNEL_CONNECTION_FAILED;
}
- 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) {
+ if (!http_stream_->GetResponseInfo()->headers) {
// 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;
}
@@ -865,99 +778,115 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
}
}
- if (result < 0)
+ if (result < 0 && result != ERR_CONNECTION_CLOSED)
return HandleIOError(result);
- if (result == 0 && ShouldResendRequest(result)) {
+ if (result == ERR_CONNECTION_CLOSED && ShouldResendRequest(result)) {
ResetConnectionAndRequestForResend();
- return result;
+ return OK;
}
- // 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();
+ // 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;
}
- // The socket was closed before we found end-of-headers.
- if (result == 0) {
+ if (result == ERR_CONNECTION_CLOSED) {
int rv = HandleConnectionClosedBeforeEndOfHeaders();
if (rv != OK)
return rv;
- } else {
- header_buf_len_ += result;
- DCHECK(header_buf_len_ <= header_buf_capacity_);
+ // 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;
+ }
- // 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_);
- }
+ 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;
- 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;
+ // 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;
+ }
- // Haven't found the end of headers yet, keep reading.
- next_state_ = STATE_READ_HEADERS;
+ 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;
return OK;
- }
- 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;
+
+ // 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;
}
}
- // And, we are done with the Start or the SSL tunnel CONNECT sequence.
- return DidReadResponseHeaders();
+ // 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;
}
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;
-
- // 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_);
+ return http_stream_->ReadResponseBody(read_buf_, read_buf_len_,
+ &io_callback_);
}
int HttpNetworkTransaction::DoReadBodyComplete(int result) {
@@ -965,39 +894,18 @@ 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 while reading the socket.
+ // Error or closed connection while reading the socket.
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;
- 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;
- }
+ // 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()) {
+ done = true;
+ keep_alive = GetResponseHeaders()->IsKeepAlive();
}
// Clean up connection_ if we are done.
@@ -1026,45 +934,18 @@ int HttpNetworkTransaction::DoDrainBodyForAuthRestart() {
return rv;
}
-// 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.
+// TODO(wtc): 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 while reading the socket.
+ // Error or closed connection while reading the socket.
done = true;
keep_alive = false;
- } 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;
- }
+ } else if (http_stream_->IsResponseBodyComplete()) {
+ done = true;
}
if (done) {
@@ -1213,7 +1094,8 @@ void HttpNetworkTransaction::LogIOErrorMetrics(
}
void HttpNetworkTransaction::LogTransactionConnectedMetrics() const {
- base::TimeDelta total_duration = response_.response_time - start_time_;
+ base::TimeDelta total_duration =
+ http_stream_->GetResponseInfo()->response_time - start_time_;
UMA_HISTOGRAM_CLIPPED_TIMES(
"Net.Transaction_Connected_Under_10",
@@ -1272,7 +1154,8 @@ void HttpNetworkTransaction::LogTransactionConnectedMetrics() const {
}
void HttpNetworkTransaction::LogTransactionMetrics() const {
- base::TimeDelta duration = base::Time::Now() - response_.request_time;
+ base::TimeDelta duration = base::Time::Now() -
+ http_stream_->GetResponseInfo()->request_time;
if (60 < duration.InMinutes())
return;
@@ -1300,143 +1183,6 @@ 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_);
@@ -1462,17 +1208,18 @@ 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;
@@ -1489,10 +1236,11 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) {
// test.
DCHECK(reused_socket_ || !ssl_config_.send_client_cert);
- response_.cert_request_info = new SSLCertRequestInfo;
+ HttpResponseInfo* response = http_stream_->GetResponseInfo();
+ 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.
@@ -1505,7 +1253,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;
@@ -1570,20 +1318,16 @@ 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;
- request_headers_->headers_.clear();
- request_headers_bytes_sent_ = 0;
- chunked_decoder_.reset();
- // Reset all the members of response_.
- response_ = HttpResponseInfo();
+ 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;
}
bool HttpNetworkTransaction::ShouldResendRequest(int error) const {
@@ -1591,12 +1335,8 @@ 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_ ||
- // 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.
+ !connection_.ShouldResendFailedRequest(error) ||
+ GetResponseHeaders()) { // We have received some response headers.
return false;
}
return true;
@@ -1605,13 +1345,10 @@ bool HttpNetworkTransaction::ShouldResendRequest(int error) const {
void HttpNetworkTransaction::ResetConnectionAndRequestForResend() {
connection_.socket()->Disconnect();
connection_.Reset();
- // 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;
+ // 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();
next_state_ = STATE_INIT_CONNECTION; // Resend the request.
}
@@ -1653,7 +1390,6 @@ 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;
@@ -1813,15 +1549,14 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const {
std::string msg;
std::string header_val;
void* iter = NULL;
- while (response_.headers->EnumerateHeader(&iter, "proxy-authenticate",
- &header_val)) {
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+ while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
msg.append("\n Has header Proxy-Authenticate: ");
msg.append(header_val);
}
iter = NULL;
- while (response_.headers->EnumerateHeader(&iter, "www-authenticate",
- &header_val)) {
+ while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
msg.append("\n Has header WWW-Authenticate: ");
msg.append(header_val);
}
@@ -1830,8 +1565,7 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const {
// authentication with a "Proxy-Support: Session-Based-Authentication"
// response header.
iter = NULL;
- while (response_.headers->EnumerateHeader(&iter, "proxy-support",
- &header_val)) {
+ while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
msg.append("\n Has header Proxy-Support: ");
msg.append(header_val);
}
@@ -1840,9 +1574,10 @@ std::string HttpNetworkTransaction::AuthChallengeLogMessage() const {
}
int HttpNetworkTransaction::HandleAuthChallenge() {
- DCHECK(response_.headers);
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+ DCHECK(headers);
- int status = response_.headers->response_code();
+ int status = headers->response_code();
if (status != 401 && status != 407)
return OK;
HttpAuth::Target target = status == 407 ?
@@ -1874,9 +1609,7 @@ 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(response_.headers.get(),
- target,
- auth_origin,
+ HttpAuth::ChooseBestChallenge(headers, target, auth_origin,
&auth_handler_[target]);
}
@@ -1932,7 +1665,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());
- response_.auth_challenge = auth_info;
+ http_stream_->GetResponseInfo()->auth_challenge = auth_info;
}
} // namespace net
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 22acf492..f540bd9 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -28,10 +28,8 @@
namespace net {
class ClientSocketFactory;
-class HttpChunkedDecoder;
class HttpNetworkSession;
class HttpStream;
-class UploadDataStream;
class HttpNetworkTransaction : public HttpTransaction {
public:
@@ -62,38 +60,6 @@ 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,
@@ -103,10 +69,8 @@ class HttpNetworkTransaction : public HttpTransaction {
STATE_SOCKS_CONNECT_COMPLETE,
STATE_SSL_CONNECT,
STATE_SSL_CONNECT_COMPLETE,
- STATE_WRITE_HEADERS,
- STATE_WRITE_HEADERS_COMPLETE,
- STATE_WRITE_BODY,
- STATE_WRITE_BODY_COMPLETE,
+ STATE_SEND_REQUEST,
+ STATE_SEND_REQUEST_COMPLETE,
STATE_READ_HEADERS,
STATE_READ_HEADERS_COMPLETE,
STATE_READ_BODY,
@@ -141,10 +105,8 @@ class HttpNetworkTransaction : public HttpTransaction {
int DoSOCKSConnectComplete(int result);
int DoSSLConnect();
int DoSSLConnectComplete(int result);
- int DoWriteHeaders();
- int DoWriteHeadersComplete(int result);
- int DoWriteBody();
- int DoWriteBodyComplete(int result);
+ int DoSendRequest();
+ int DoSendRequestComplete(int result);
int DoReadHeaders();
int DoReadHeadersComplete(int result);
int DoReadBody();
@@ -167,9 +129,6 @@ 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.
@@ -188,6 +147,9 @@ 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;
@@ -209,13 +171,6 @@ 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);
@@ -309,7 +264,6 @@ class HttpNetworkTransaction : public HttpTransaction {
scoped_refptr<LoadLog> load_log_;
const HttpRequestInfo* request_;
- HttpResponseInfo response_;
ProxyService::PacRequest* pac_request_;
ProxyInfo proxy_info_;
@@ -318,33 +272,24 @@ 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_WRITE_HEADERS,
- // STATE_WRITE_HEADERS_COMPLETE, STATE_READ_HEADERS, and
+ // request/response to reuse the STATE_SEND_REQUEST,
+ // STATE_SEND_REQUEST_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.
@@ -352,48 +297,13 @@ class HttpNetworkTransaction : public HttpTransaction {
SSLConfig ssl_config_;
- 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.
+ std::string request_headers_;
// 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 a3a4647..b47e34a 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -12,8 +12,10 @@
#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"
@@ -320,6 +322,24 @@ 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) {
@@ -471,7 +491,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.
+// HTTP/1.1 and the two status headers are read in one read.
TEST_F(HttpNetworkTransactionTest, Ignores1xx) {
SessionDependencies session_deps;
scoped_ptr<HttpTransaction> trans(
@@ -483,8 +503,8 @@ TEST_F(HttpNetworkTransactionTest, Ignores1xx) {
request.load_flags = 0;
MockRead data_reads[] = {
- MockRead("HTTP/1.1 102 Unspecified status code\r\n\r\n"),
- MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("HTTP/1.1 102 Unspecified status code\r\n\r\n"
+ "HTTP/1.1 200 OK\r\n\r\n"),
MockRead("hello world"),
MockRead(false, OK),
};
@@ -2678,53 +2698,40 @@ 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_->headers_ = "Authorization: NTLM";
- trans->request_headers_bytes_sent_ = 3;
+ trans->request_headers_ = "Authorization: NTLM";
// Setup state in response_
- 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...)
+ 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...)
{ // 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> response = new HttpResponseHeaders(temp);
+ scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders(temp);
request.extra_headers = "Foo: 1\nbar: 23";
- EXPECT_TRUE(trans->response_.vary_data.Init(request, *response));
+ EXPECT_TRUE(response->vary_data.Init(request, *headers));
}
// Cause the above state to be reset.
trans->ResetStateForRestart();
// Verify that the state that needed to be reset, has been reset.
- 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_);
+ response = trans->http_stream_->GetResponseInfo();
EXPECT_TRUE(trans->read_buf_.get() == NULL);
EXPECT_EQ(0, trans->read_buf_len_);
- 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());
+ 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());
}
// Test HTTPS connections to a site with a bad certificate
@@ -3595,4 +3602,189 @@ 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 361edb0..d0b9dc9 100644
--- a/net/http/http_stream.h
+++ b/net/http/http_stream.h
@@ -6,50 +6,73 @@
// 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/base/completion_callback.h"
+#include "net/socket/client_socket_handle.h"
namespace net {
+class HttpRequestInfo;
+class HttpResponseInfo;
class IOBuffer;
+class UploadDataStream;
class HttpStream {
public:
HttpStream() {}
virtual ~HttpStream() {}
- // 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;
+ // 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;
private:
DISALLOW_COPY_AND_ASSIGN(HttpStream);
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
new file mode 100644
index 0000000..6456c9b
--- /dev/null
+++ b/net/http/http_stream_parser.cc
@@ -0,0 +1,512 @@
+// 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);
+ // 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);
+ read_buf_->set_offset(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
new file mode 100644
index 0000000..012039f
--- /dev/null
+++ b/net/http/http_stream_parser.h
@@ -0,0 +1,167 @@
+// 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 2d0b342..00c5661 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -240,6 +240,7 @@
'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',
@@ -259,6 +260,8 @@
'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 83aa70f..25a2f35 100644
--- a/net/socket/client_socket_handle.h
+++ b/net/socket/client_socket_handle.h
@@ -104,6 +104,19 @@ 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.