Miscellaneous changes from my code review.
R=darin,nsylvain
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@404 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 5ae927f..807c87d 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -51,13 +51,6 @@
//-----------------------------------------------------------------------------
-// TODO(darin): Move this onto HttpProxyInfo
-static std::string GetProxyHostPort(const HttpProxyInfo& pi) {
- return WideToASCII(pi.proxy_server());
-}
-
-//-----------------------------------------------------------------------------
-
HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session,
ClientSocketFactory* csf)
#pragma warning(suppress: 4355)
@@ -72,7 +65,7 @@
using_ssl_(false),
using_proxy_(false),
using_tunnel_(false),
- bytes_sent_(0),
+ request_headers_bytes_sent_(0),
header_buf_capacity_(0),
header_buf_len_(0),
header_buf_body_offset_(-1),
@@ -83,6 +76,85 @@
next_state_(STATE_NONE) {
}
+void HttpNetworkTransaction::Destroy() {
+ delete this;
+}
+
+int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
+ CompletionCallback* callback) {
+ request_ = request_info;
+
+ next_state_ = STATE_RESOLVE_PROXY;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int HttpNetworkTransaction::RestartIgnoringLastError(
+ CompletionCallback* callback) {
+ return ERR_FAILED; // TODO(darin): implement me!
+}
+
+int HttpNetworkTransaction::RestartWithAuth(
+ const std::wstring& username,
+ const std::wstring& password,
+ CompletionCallback* callback) {
+ return ERR_FAILED; // TODO(darin): implement me!
+}
+
+int HttpNetworkTransaction::Read(char* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(response_.headers);
+ DCHECK(buf);
+ DCHECK(buf_len > 0);
+
+ if (!connection_.is_initialized())
+ return 0; // connection_ has been reset. Treat like EOF.
+
+ read_buf_ = buf;
+ read_buf_len_ = buf_len;
+
+ next_state_ = STATE_READ_BODY;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const {
+ return response_.headers ? &response_ : NULL;
+}
+
+LoadState HttpNetworkTransaction::GetLoadState() const {
+ // TODO(wtc): Define a new LoadState value for the
+ // STATE_INIT_CONNECTION_COMPLETE state, which delays the HTTP request.
+ switch (next_state_) {
+ case STATE_RESOLVE_PROXY_COMPLETE:
+ return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
+ case STATE_RESOLVE_HOST_COMPLETE:
+ return LOAD_STATE_RESOLVING_HOST;
+ case STATE_CONNECT_COMPLETE:
+ return LOAD_STATE_CONNECTING;
+ 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;
+ case STATE_READ_BODY_COMPLETE:
+ return LOAD_STATE_READING_RESPONSE;
+ default:
+ return LOAD_STATE_IDLE;
+ }
+}
+
+uint64 HttpNetworkTransaction::GetUploadProgress() const {
+ if (!request_body_stream_.get())
+ return 0;
+
+ return request_body_stream_->position();
+}
+
HttpNetworkTransaction::~HttpNetworkTransaction() {
// If we still have an open socket, then make sure to close it so we don't
// try to reuse it later on.
@@ -156,7 +228,7 @@
DCHECK(rv != ERR_IO_PENDING);
DCHECK(user_callback_);
- // Since Run may result in Read being called, clear callback_ up front.
+ // Since Run may result in Read being called, clear user_callback_ up front.
CompletionCallback* c = user_callback_;
user_callback_ = NULL;
c->Run(rv);
@@ -177,48 +249,56 @@
next_state_ = STATE_NONE;
switch (state) {
case STATE_RESOLVE_PROXY:
+ DCHECK(rv == OK);
rv = DoResolveProxy();
break;
case STATE_RESOLVE_PROXY_COMPLETE:
rv = DoResolveProxyComplete(rv);
break;
case STATE_INIT_CONNECTION:
+ DCHECK(rv == OK);
rv = DoInitConnection();
break;
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
case STATE_RESOLVE_HOST:
+ DCHECK(rv == OK);
rv = DoResolveHost();
break;
case STATE_RESOLVE_HOST_COMPLETE:
rv = DoResolveHostComplete(rv);
break;
case STATE_CONNECT:
+ DCHECK(rv == OK);
rv = DoConnect();
break;
case STATE_CONNECT_COMPLETE:
rv = DoConnectComplete(rv);
break;
case STATE_WRITE_HEADERS:
+ DCHECK(rv == OK);
rv = DoWriteHeaders();
break;
case STATE_WRITE_HEADERS_COMPLETE:
rv = DoWriteHeadersComplete(rv);
break;
case STATE_WRITE_BODY:
+ DCHECK(rv == OK);
rv = DoWriteBody();
break;
case STATE_WRITE_BODY_COMPLETE:
rv = DoWriteBodyComplete(rv);
break;
case STATE_READ_HEADERS:
+ DCHECK(rv == OK);
rv = DoReadHeaders();
break;
case STATE_READ_HEADERS_COMPLETE:
rv = DoReadHeadersComplete(rv);
break;
case STATE_READ_BODY:
+ DCHECK(rv == OK);
rv = DoReadBody();
break;
case STATE_READ_BODY_COMPLETE:
@@ -227,6 +307,7 @@
default:
NOTREACHED() << "bad state";
rv = ERR_FAILED;
+ break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
@@ -266,10 +347,11 @@
// Build the string used to uniquely identify connections of this type.
std::string connection_group;
if (using_proxy_ || using_tunnel_)
- connection_group = "proxy/" + GetProxyHostPort(proxy_info_) + "/";
+ connection_group = "proxy/" + proxy_info_.GetProxyServer() + "/";
if (!using_proxy_)
connection_group.append(request_->url.GetOrigin().spec());
+ DCHECK(!connection_group.empty());
return connection_.Init(connection_group, &io_callback_);
}
@@ -293,14 +375,12 @@
int HttpNetworkTransaction::DoResolveHost() {
next_state_ = STATE_RESOLVE_HOST_COMPLETE;
- DCHECK(!resolver_.get());
-
std::string host;
int port;
// Determine the host and port to connect to.
if (using_proxy_ || using_tunnel_) {
- const std::string& proxy = GetProxyHostPort(proxy_info_);
+ const std::string& proxy = proxy_info_.GetProxyServer();
StringTokenizer t(proxy, ":");
// TODO(darin): Handle errors here. Perhaps HttpProxyInfo should do this
// before claiming a proxy server configuration.
@@ -309,9 +389,10 @@
t.GetNext();
port = static_cast<int>(StringToInt64(t.token()));
} else {
+ // Direct connection
host = request_->url.host();
port = request_->url.IntPort();
- if (port == -1) {
+ if (port == url_parse::PORT_UNSPECIFIED) {
if (using_ssl_) {
port = 443; // Default HTTPS port
} else {
@@ -320,12 +401,10 @@
}
}
- resolver_.reset(new HostResolver());
- return resolver_->Resolve(host, port, &addresses_, &io_callback_);
+ return resolver_.Resolve(host, port, &addresses_, &io_callback_);
}
int HttpNetworkTransaction::DoResolveHostComplete(int result) {
- resolver_.reset();
if (result == OK)
next_state_ = STATE_CONNECT;
return result;
@@ -363,11 +442,12 @@
// Record our best estimate of the 'request time' as the time when we send
// out the first bytes of the request headers.
- if (bytes_sent_ == 0)
+ if (request_headers_bytes_sent_ == 0)
response_.request_time = Time::Now();
- const char* buf = request_headers_.data() + bytes_sent_;
- int buf_len = static_cast<int>(request_headers_.size() - bytes_sent_);
+ const char* buf = request_headers_.data() + request_headers_bytes_sent_;
+ int buf_len = static_cast<int>(request_headers_.size() -
+ request_headers_bytes_sent_);
DCHECK(buf_len > 0);
return connection_.socket()->Write(buf, buf_len, &io_callback_);
@@ -377,8 +457,8 @@
if (result < 0)
return HandleIOError(result);
- bytes_sent_ += result;
- if (bytes_sent_ < request_headers_.size()) {
+ request_headers_bytes_sent_ += result;
+ if (request_headers_bytes_sent_ < request_headers_.size()) {
next_state_ = STATE_WRITE_HEADERS;
} else if (request_->upload_data) {
next_state_ = STATE_WRITE_BODY;
@@ -450,16 +530,14 @@
// TODO(darin): Check for a HTTP/0.9 response.
int eoh = HttpUtil::LocateEndOfHeaders(header_buf_.get(), header_buf_len_);
- if (eoh != -1) {
- header_buf_body_offset_ = eoh;
- } else {
+ if (eoh == -1) {
next_state_ = STATE_READ_HEADERS; // Read more.
return OK;
}
+ header_buf_body_offset_ = eoh;
}
// And, we are done with the Start sequence.
- next_state_ = STATE_NONE;
return DidReadResponseHeaders();
}
@@ -470,13 +548,17 @@
next_state_ = STATE_READ_BODY_COMPLETE;
- // We may have some data remaining in the read buffer.
+ // We may have some data remaining in the header buffer.
if (header_buf_.get() && header_buf_body_offset_ < header_buf_len_) {
int n = std::min(read_buf_len_, header_buf_len_ - header_buf_body_offset_);
memcpy(read_buf_, header_buf_.get() + header_buf_body_offset_, n);
header_buf_body_offset_ += n;
- if (header_buf_body_offset_ == header_buf_len_)
+ 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;
}
@@ -486,10 +568,12 @@
int HttpNetworkTransaction::DoReadBodyComplete(int result) {
// We are done with the Read call.
+ bool unfiltered_eof = (result == 0);
+
// Filter incoming data if appropriate. FilterBuf may return an error.
if (result > 0 && chunked_decoder_.get()) {
result = chunked_decoder_->FilterBuf(read_buf_, result);
- if (result == 0) {
+ 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;
@@ -503,18 +587,25 @@
done = true;
} else {
content_read_ += result;
- if ((content_length_ != -1 && content_read_ >= content_length_) ||
+ if (unfiltered_eof ||
+ (content_length_ != -1 && content_read_ >= content_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 ||
+ (content_length_ != -1 && content_read_ > content_length_))
+ keep_alive = false;
}
}
- // Cleanup the HttpConnection if we are done.
+ // Clean up the HttpConnection if we are done.
if (done) {
if (!keep_alive)
connection_.set_socket(NULL);
connection_.Reset();
+ // The next Read call will return 0 (EOF).
}
// Clear these to avoid leaving around old state.
@@ -532,7 +623,13 @@
// allowed to send this response even if we didn't ask for it, so we just
// need to skip over it.
if (headers->response_code() == 100) {
- header_buf_len_ = 0;
+ header_buf_len_ -= header_buf_body_offset_;
+ // If we've already received some bytes after the 100 Continue response,
+ // move them to the beginning of header_buf_.
+ if (header_buf_len_) {
+ memmove(header_buf_.get(), header_buf_.get() + header_buf_body_offset_,
+ header_buf_len_);
+ }
header_buf_body_offset_ = -1;
next_state_ = STATE_READ_HEADERS;
return OK;
@@ -545,9 +642,9 @@
// For certain responses, we know the content length is always 0.
switch (response_.headers->response_code()) {
- case 204:
- case 205:
- case 304:
+ case 204: // No Content
+ case 205: // Reset Content
+ case 304: // Not Modified
content_length_ = 0;
break;
}
@@ -569,6 +666,10 @@
return OK;
}
+// This method determines whether it is safe to resend the request after an
+// IO error. It can only be called in response to request header or body
+// write errors or response header read errors. It should not be used in
+// other cases, such as a Connect error.
int HttpNetworkTransaction::HandleIOError(int error) {
switch (error) {
// If we try to reuse a connection that the server is in the process of
@@ -579,13 +680,13 @@
case ERR_CONNECTION_CLOSED:
case ERR_CONNECTION_ABORTED:
if (reused_socket_ && // We reused a keep-alive connection.
- !header_buf_len_) { // We have not received any response data yet.
+ !header_buf_len_) { // We haven't received any response header yet.
connection_.set_socket(NULL);
connection_.Reset();
- bytes_sent_ = 0;
+ request_headers_bytes_sent_ = 0;
if (request_body_stream_.get())
request_body_stream_->Reset();
- next_state_ = STATE_INIT_CONNECTION;
+ next_state_ = STATE_INIT_CONNECTION; // Resend the request.
error = OK;
}
break;
@@ -593,81 +694,4 @@
return error;
}
-void HttpNetworkTransaction::Destroy() {
- delete this;
-}
-
-int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
- CompletionCallback* callback) {
- request_ = request_info;
-
- next_state_ = STATE_RESOLVE_PROXY;
- int rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- user_callback_ = callback;
- return rv;
-}
-
-int HttpNetworkTransaction::RestartIgnoringLastError(
- CompletionCallback* callback) {
- return ERR_FAILED; // TODO(darin): implement me!
-}
-
-int HttpNetworkTransaction::RestartWithAuth(
- const std::wstring& username,
- const std::wstring& password,
- CompletionCallback* callback) {
- return ERR_FAILED; // TODO(darin): implement me!
-}
-
-int HttpNetworkTransaction::Read(char* buf, int buf_len,
- CompletionCallback* callback) {
- DCHECK(response_.headers);
- DCHECK(buf);
- DCHECK(buf_len > 0);
-
- if (!connection_.is_initialized())
- return 0; // Treat like EOF.
-
- read_buf_ = buf;
- read_buf_len_ = buf_len;
-
- next_state_ = STATE_READ_BODY;
- int rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- user_callback_ = callback;
- return rv;
-}
-
-const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const {
- return response_.headers ? &response_ : NULL;
-}
-
-LoadState HttpNetworkTransaction::GetLoadState() const {
- switch (next_state_) {
- case STATE_RESOLVE_PROXY_COMPLETE:
- return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
- case STATE_RESOLVE_HOST_COMPLETE:
- return LOAD_STATE_RESOLVING_HOST;
- case STATE_CONNECT_COMPLETE:
- return LOAD_STATE_CONNECTING;
- 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;
- case STATE_READ_BODY_COMPLETE:
- return LOAD_STATE_READING_RESPONSE;
- default:
- return LOAD_STATE_IDLE;
- }
-}
-
-uint64 HttpNetworkTransaction::GetUploadProgress() const {
- if (!request_body_stream_.get())
- return 0;
-
- return request_body_stream_->position();
-}
-
} // namespace net