diff options
Diffstat (limited to 'net/http/http_network_transaction.cc')
-rw-r--r-- | net/http/http_network_transaction.cc | 169 |
1 files changed, 131 insertions, 38 deletions
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index d3ed391..55330e2 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -9,10 +9,10 @@ #include "base/compiler_specific.h" #include "base/format_macros.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" #include "base/metrics/stats_counters.h" -#include "base/scoped_ptr.h" #include "base/stl_util-inl.h" #include "base/string_number_conversions.h" #include "base/string_util.h" @@ -20,6 +20,7 @@ #include "build/build_config.h" #include "googleurl/src/gurl.h" #include "net/base/auth.h" +#include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -49,7 +50,7 @@ #include "net/socket/socks_client_socket_pool.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/ssl_client_socket_pool.h" -#include "net/socket/tcp_client_socket_pool.h" +#include "net/socket/transport_client_socket_pool.h" #include "net/spdy/spdy_http_stream.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" @@ -98,6 +99,9 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session) : pending_auth_target_(HttpAuth::AUTH_NONE), ALLOW_THIS_IN_INITIALIZER_LIST( io_callback_(this, &HttpNetworkTransaction::OnIOComplete)), + ALLOW_THIS_IN_INITIALIZER_LIST(delegate_callback_( + new CancelableCompletionCallback<HttpNetworkTransaction>( + this, &HttpNetworkTransaction::OnIOComplete))), user_callback_(NULL), session_(session), request_(NULL), @@ -128,6 +132,9 @@ HttpNetworkTransaction::~HttpNetworkTransaction() { if (stream_->IsResponseBodyComplete()) { // If the response body is complete, we can just reuse the socket. stream_->Close(false /* reusable */); + } else if (stream_->IsSpdyHttpStream()) { + // Doesn't really matter for SpdyHttpStream. Just close it. + stream_->Close(true /* not reusable */); } else { // Otherwise, we try to drain the response body. // TODO(willchan): Consider moving this response body draining to the @@ -142,6 +149,8 @@ HttpNetworkTransaction::~HttpNetworkTransaction() { } } } + + delegate_callback_->Cancel(); } int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, @@ -264,7 +273,7 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { if (stream_.get()) { HttpStream* new_stream = NULL; - if (keep_alive) { + if (keep_alive && stream_->IsConnectionReusable()) { // We should call connection_->set_idle_time(), but this doesn't occur // often enough to be worth the trouble. stream_->SetConnectionReused(); @@ -272,7 +281,10 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { } if (!new_stream) { - stream_->Close(!keep_alive); + // Close the stream and mark it as not_reusable. Even in the + // keep_alive case, we've determined that the stream_ is not + // reusable if new_stream is NULL. + stream_->Close(true); next_state_ = STATE_CREATE_STREAM; } else { next_state_ = STATE_INIT_STREAM; @@ -368,8 +380,6 @@ void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config, stream_.reset(stream); ssl_config_ = used_ssl_config; proxy_info_ = used_proxy_info; - response_.was_alternate_protocol_available = - stream_request_->was_alternate_protocol_available(); response_.was_npn_negotiated = stream_request_->was_npn_negotiated(); response_.was_fetched_via_spdy = stream_request_->using_spdy(); response_.was_fetched_via_proxy = !proxy_info_.is_direct(); @@ -512,9 +522,16 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: rv = DoGenerateServerAuthTokenComplete(rv); break; - case STATE_SEND_REQUEST: + case STATE_BUILD_REQUEST: DCHECK_EQ(OK, rv); net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, NULL); + rv = DoBuildRequest(); + break; + case STATE_BUILD_REQUEST_COMPLETE: + rv = DoBuildRequestComplete(rv); + break; + case STATE_SEND_REQUEST: + DCHECK_EQ(OK, rv); rv = DoSendRequest(); break; case STATE_SEND_REQUEST_COMPLETE: @@ -659,38 +676,123 @@ int HttpNetworkTransaction::DoGenerateServerAuthToken() { int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { DCHECK_NE(ERR_IO_PENDING, rv); if (rv == OK) - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_BUILD_REQUEST; return rv; } -int HttpNetworkTransaction::DoSendRequest() { - next_state_ = STATE_SEND_REQUEST_COMPLETE; +void HttpNetworkTransaction::BuildRequestHeaders(bool using_proxy) { + request_headers_.SetHeader(HttpRequestHeaders::kHost, + GetHostAndOptionalPort(request_->url)); - UploadDataStream* request_body = NULL; + // For compat with HTTP/1.0 servers and proxies: + if (using_proxy) { + request_headers_.SetHeader(HttpRequestHeaders::kProxyConnection, + "keep-alive"); + } else { + request_headers_.SetHeader(HttpRequestHeaders::kConnection, "keep-alive"); + } + + // Our consumer should have made sure that this is a safe referrer. See for + // instance WebCore::FrameLoader::HideReferrer. + if (request_->referrer.is_valid()) { + request_headers_.SetHeader(HttpRequestHeaders::kReferer, + request_->referrer.spec()); + } + + // Add a content length header? + if (request_body_.get()) { + if (request_body_->is_chunked()) { + request_headers_.SetHeader( + HttpRequestHeaders::kTransferEncoding, "chunked"); + } else { + request_headers_.SetHeader( + HttpRequestHeaders::kContentLength, + base::Uint64ToString(request_body_->size())); + } + } else if (request_->method == "POST" || request_->method == "PUT" || + request_->method == "HEAD") { + // An empty POST/PUT request still needs a content length. As for HEAD, + // IE and Safari also add a content length header. Presumably it is to + // support sending a HEAD request to an URL that only expects to be sent a + // POST or some other method that normally would have a message body. + request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); + } + + // Honor load flags that impact proxy caches. + if (request_->load_flags & LOAD_BYPASS_CACHE) { + request_headers_.SetHeader(HttpRequestHeaders::kPragma, "no-cache"); + request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "no-cache"); + } else if (request_->load_flags & LOAD_VALIDATE_CACHE) { + request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "max-age=0"); + } + + if (ShouldApplyProxyAuth() && HaveAuth(HttpAuth::AUTH_PROXY)) + auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader( + &request_headers_); + if (ShouldApplyServerAuth() && HaveAuth(HttpAuth::AUTH_SERVER)) + auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader( + &request_headers_); + + // Headers that will be stripped from request_->extra_headers to prevent, + // e.g., plugins from overriding headers that are controlled using other + // means. Otherwise a plugin could set a referrer although sending the + // referrer is inhibited. + // TODO(jochen): check whether also other headers should be stripped. + static const char* const kExtraHeadersToBeStripped[] = { + "Referer" + }; + + HttpRequestHeaders stripped_extra_headers; + stripped_extra_headers.CopyFrom(request_->extra_headers); + for (size_t i = 0; i < arraysize(kExtraHeadersToBeStripped); ++i) + stripped_extra_headers.RemoveHeader(kExtraHeadersToBeStripped[i]); + request_headers_.MergeFrom(stripped_extra_headers); +} + +int HttpNetworkTransaction::DoBuildRequest() { + next_state_ = STATE_BUILD_REQUEST_COMPLETE; + delegate_callback_->AddRef(); // balanced in DoSendRequestComplete + + request_body_.reset(NULL); if (request_->upload_data) { int error_code; - request_body = UploadDataStream::Create(request_->upload_data, &error_code); - if (!request_body) + request_body_.reset( + UploadDataStream::Create(request_->upload_data, &error_code)); + if (!request_body_.get()) return error_code; } + headers_valid_ = false; + // This is constructed lazily (instead of within our Start method), so that // we have proxy info available. if (request_headers_.IsEmpty()) { - bool using_proxy = (proxy_info_.is_http()|| proxy_info_.is_https()) && + bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && !is_https_request(); - HttpUtil::BuildRequestHeaders(request_, request_body, auth_controllers_, - ShouldApplyServerAuth(), - ShouldApplyProxyAuth(), using_proxy, - &request_headers_); + BuildRequestHeaders(using_proxy); + } - if (session_->network_delegate()) - session_->network_delegate()->NotifySendHttpRequest(&request_headers_); + if (session_->network_delegate()) { + return session_->network_delegate()->NotifyBeforeSendHeaders( + request_->request_id, delegate_callback_, &request_headers_); } - headers_valid_ = false; - return stream_->SendRequest(request_headers_, request_body, &response_, - &io_callback_); + return OK; +} + +int HttpNetworkTransaction::DoBuildRequestComplete(int result) { + delegate_callback_->Release(); // balanced in DoBuildRequest + + if (result == OK) + next_state_ = STATE_SEND_REQUEST; + return result; +} + +int HttpNetworkTransaction::DoSendRequest() { + next_state_ = STATE_SEND_REQUEST_COMPLETE; + + return stream_->SendRequest( + request_headers_, request_body_.release(), &response_, &io_callback_); } int HttpNetworkTransaction::DoSendRequestComplete(int result) { @@ -1062,26 +1164,16 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { case ERR_SSL_VERSION_OR_CIPHER_MISMATCH: case ERR_SSL_DECOMPRESSION_FAILURE_ALERT: case ERR_SSL_BAD_RECORD_MAC_ALERT: - if (ssl_config_.tls1_enabled && - !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) { + if (ssl_config_.tls1_enabled) { // This could be a TLS-intolerant server, an SSL 3.0 server that // chose a TLS-only cipher suite or a server with buggy DEFLATE // support. Turn off TLS 1.0, DEFLATE support and retry. - session_->http_stream_factory()->AddTLSIntolerantServer(request_->url); + session_->http_stream_factory()->AddTLSIntolerantServer( + HostPortPair::FromURL(request_->url)); ResetConnectionAndRequestForResend(); error = OK; } break; - case ERR_SSL_SNAP_START_NPN_MISPREDICTION: - // This means that we tried to Snap Start a connection, but we - // mispredicted the NPN result. This isn't a problem from the point of - // view of the SSL layer because the server will ignore the application - // data in the Snap Start extension. However, at the HTTP layer, we have - // already decided that it's a HTTP or SPDY connection and it's easier to - // abort and start again. - ResetConnectionAndRequestForResend(); - error = OK; - break; } return error; } @@ -1206,7 +1298,6 @@ bool HttpNetworkTransaction::HaveAuth(HttpAuth::Target target) const { auth_controllers_[target]->HaveAuth(); } - GURL HttpNetworkTransaction::AuthURL(HttpAuth::Target target) const { switch (target) { case HttpAuth::AUTH_PROXY: { @@ -1235,6 +1326,8 @@ std::string HttpNetworkTransaction::DescribeState(State state) { switch (state) { STATE_CASE(STATE_CREATE_STREAM); STATE_CASE(STATE_CREATE_STREAM_COMPLETE); + STATE_CASE(STATE_BUILD_REQUEST); + STATE_CASE(STATE_BUILD_REQUEST_COMPLETE); STATE_CASE(STATE_SEND_REQUEST); STATE_CASE(STATE_SEND_REQUEST_COMPLETE); STATE_CASE(STATE_READ_HEADERS); |