| Index: net/http/http_network_transaction.cc
|
| ===================================================================
|
| --- net/http/http_network_transaction.cc (revision 29289)
|
| +++ net/http/http_network_transaction.cc (working copy)
|
| @@ -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 @@
|
|
|
| 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 @@
|
| 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::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 @@
|
| }
|
|
|
| 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::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 @@
|
|
|
| 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 @@
|
| // 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 @@
|
| }
|
|
|
| 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 @@
|
| 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 @@
|
| }
|
|
|
| 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,24 +429,15 @@
|
| 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());
|
| + case STATE_SEND_REQUEST_COMPLETE:
|
| + rv = DoSendRequestComplete(rv);
|
| + TRACE_EVENT_END("http.send_request", request_, request_->url.spec());
|
| break;
|
| - case STATE_WRITE_BODY:
|
| - DCHECK_EQ(OK, rv);
|
| - TRACE_EVENT_BEGIN("http.write_body", request_, request_->url.spec());
|
| - rv = DoWriteBody();
|
| - break;
|
| - case STATE_WRITE_BODY_COMPLETE:
|
| - rv = DoWriteBodyComplete(rv);
|
| - TRACE_EVENT_END("http.write_body", request_, request_->url.spec());
|
| - break;
|
| case STATE_READ_HEADERS:
|
| DCHECK_EQ(OK, rv);
|
| TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec());
|
| @@ -611,7 +592,7 @@
|
| // 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 @@
|
| 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 @@
|
| 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 @@
|
| 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 @@
|
| 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 @@
|
| 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;
|
| -}
|
| + next_state_ = STATE_READ_HEADERS;
|
|
|
| -int HttpNetworkTransaction::DoWriteBody() {
|
| - next_state_ = STATE_WRITE_BODY_COMPLETE;
|
| -
|
| - DCHECK(request_body_stream_.get());
|
| - DCHECK(request_body_stream_->size());
|
| -
|
| - int buf_len = static_cast<int>(request_body_stream_->buf_len());
|
| -
|
| - return http_stream_->Write(request_body_stream_->buf(), buf_len,
|
| - &io_callback_);
|
| -}
|
| -
|
| -int HttpNetworkTransaction::DoWriteBodyComplete(int result) {
|
| - if (result < 0)
|
| - return HandleIOError(result);
|
| -
|
| - request_body_stream_->DidConsume(result);
|
| -
|
| - if (request_body_stream_->position() < request_body_stream_->size()) {
|
| - next_state_ = STATE_WRITE_BODY;
|
| - } else {
|
| - next_state_ = STATE_READ_HEADERS;
|
| - }
|
| return OK;
|
| }
|
|
|
| int HttpNetworkTransaction::DoReadHeaders() {
|
| next_state_ = STATE_READ_HEADERS_COMPLETE;
|
|
|
| - // 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 @@
|
| 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 @@
|
| }
|
| }
|
|
|
| - 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 @@
|
| 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 @@
|
| 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::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::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 @@
|
| << 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 @@
|
| }
|
|
|
| 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 @@
|
| // 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 @@
|
| 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,33 +1318,25 @@
|
|
|
| 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 {
|
| // 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 (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 @@
|
| 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 @@
|
| 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 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 @@
|
| // 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 @@
|
| }
|
|
|
| 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 @@
|
| 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 @@
|
| 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
|
|
|