#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/metrics/stats_counters.h"
+#include "base/profiler/scoped_tracker.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "net/http/http_response_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_status_code.h"
-#include "net/http/http_stream_base.h"
+#include "net/http/http_stream.h"
#include "net/http/http_stream_factory.h"
#include "net/http/http_util.h"
#include "net/http/transport_security_state.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/ssl_client_socket_pool.h"
#include "net/socket/transport_client_socket_pool.h"
+#include "net/spdy/hpack_huffman_aggregator.h"
#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "url/gurl.h"
-
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
-#include <algorithm>
-#include "net/proxy/proxy_server.h"
-#endif
-
-
-using base::Time;
-using base::TimeDelta;
+#include "url/url_canon.h"
namespace net {
namespace {
void ProcessAlternateProtocol(
- HttpStreamFactory* factory,
- const base::WeakPtr<HttpServerProperties>& http_server_properties,
+ HttpNetworkSession* session,
const HttpResponseHeaders& headers,
const HostPortPair& http_host_port_pair) {
- std::string alternate_protocol_str;
-
- if (!headers.EnumerateHeader(NULL, kAlternateProtocolHeader,
- &alternate_protocol_str)) {
- // Header is not present.
+ if (!headers.HasHeader(kAlternateProtocolHeader))
return;
- }
-
- factory->ProcessAlternateProtocol(http_server_properties,
- alternate_protocol_str,
- http_host_port_pair);
-}
-// Returns true if |error| is a client certificate authentication error.
-bool IsClientCertificateError(int error) {
- switch (error) {
- case ERR_BAD_SSL_CLIENT_AUTH_CERT:
- case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED:
- case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY:
- case ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED:
- return true;
- default:
- return false;
+ std::vector<std::string> alternate_protocol_values;
+ void* iter = NULL;
+ std::string alternate_protocol_str;
+ while (headers.EnumerateHeader(&iter, kAlternateProtocolHeader,
+ &alternate_protocol_str)) {
+ alternate_protocol_values.push_back(alternate_protocol_str);
}
+
+ session->http_stream_factory()->ProcessAlternateProtocol(
+ session->http_server_properties(),
+ alternate_protocol_values,
+ http_host_port_pair,
+ *session);
}
base::Value* NetLogSSLVersionFallbackCallback(
request_(NULL),
priority_(priority),
headers_valid_(false),
- logged_response_time_(false),
fallback_error_code_(ERR_SSL_INAPPROPRIATE_FALLBACK),
request_headers_(),
read_buf_len_(0),
establishing_tunnel_(false),
websocket_handshake_stream_base_create_helper_(NULL) {
session->ssl_config_service()->GetSSLConfig(&server_ssl_config_);
- if (session->http_stream_factory()->has_next_protos()) {
- server_ssl_config_.next_protos =
- session->http_stream_factory()->next_protos();
- }
+ session->GetNextProtos(&server_ssl_config_.next_protos);
proxy_ssl_config_ = server_ssl_config_;
}
stream_->Close(true /* not reusable */);
} else {
// Otherwise, we try to drain the response body.
- HttpStreamBase* stream = stream_.release();
+ HttpStream* stream = stream_.release();
stream->Drain(session_);
}
}
net_log_ = net_log;
request_ = request_info;
- start_time_ = base::Time::Now();
if (request_->load_flags & LOAD_DISABLE_CERT_REVOCATION_CHECKING) {
server_ssl_config_.rev_checking_enabled = false;
proxy_ssl_config_.rev_checking_enabled = false;
}
- // Channel ID is enabled unless --disable-tls-channel-id flag is set,
- // or if privacy mode is enabled.
- bool channel_id_enabled = server_ssl_config_.channel_id_enabled &&
- (request_->privacy_mode == kPrivacyModeDisabled);
- server_ssl_config_.channel_id_enabled = channel_id_enabled;
+ // Channel ID is disabled if privacy mode is enabled for this request.
+ if (request_->privacy_mode == PRIVACY_MODE_ENABLED)
+ server_ssl_config_.channel_id_enabled = false;
next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM;
int rv = DoLoop(OK);
// We should call connection_->set_idle_time(), but this doesn't occur
// often enough to be worth the trouble.
stream_->SetConnectionReused();
- new_stream =
- static_cast<HttpStream*>(stream_.get())->RenewStreamForAuth();
+ new_stream = stream_->RenewStreamForAuth();
}
if (!new_stream) {
if (!stream_.get())
return UploadProgress();
- // TODO(bashi): This cast is temporary. Remove later.
- return static_cast<HttpStream*>(stream_.get())->GetUploadProgress();
+ return stream_->GetUploadProgress();
}
void HttpNetworkTransaction::SetQuicServerInfo(
before_network_start_callback_ = callback;
}
+void HttpNetworkTransaction::SetBeforeProxyHeadersSentCallback(
+ const BeforeProxyHeadersSentCallback& callback) {
+ before_proxy_headers_sent_callback_ = callback;
+}
+
int HttpNetworkTransaction::ResumeNetworkStart() {
DCHECK_EQ(next_state_, STATE_CREATE_STREAM);
return DoLoop(OK);
void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
- HttpStreamBase* stream) {
+ HttpStream* stream) {
DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_);
DCHECK(stream_request_.get());
stream_request_->protocol_negotiated());
response_.was_fetched_via_spdy = stream_request_->using_spdy();
response_.was_fetched_via_proxy = !proxy_info_.is_direct();
-
+ if (response_.was_fetched_via_proxy && !proxy_info_.is_empty())
+ response_.proxy_server = proxy_info_.proxy_server().host_port_pair();
OnIOComplete(OK);
}
const HttpResponseInfo& response_info,
const SSLConfig& used_ssl_config,
const ProxyInfo& used_proxy_info,
- HttpStreamBase* stream) {
+ HttpStream* stream) {
DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_);
headers_valid_ = true;
}
void HttpNetworkTransaction::OnIOComplete(int result) {
+ // TODO(vadimt): Remove ScopedTracker below once crbug.com/424359 is fixed.
+ tracked_objects::ScopedTracker tracking_profile1(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "424359 HttpNetworkTransaction::OnIOComplete 1"));
+
int rv = DoLoop(result);
+
+ // TODO(vadimt): Remove ScopedTracker below once crbug.com/424359 is fixed.
+ tracked_objects::ScopedTracker tracking_profile2(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "424359 HttpNetworkTransaction::OnIOComplete 2"));
+
if (rv != ERR_IO_PENDING)
DoCallback(rv);
}
&request_headers_);
request_headers_.MergeFrom(request_->extra_headers);
+
+ if (using_proxy && !before_proxy_headers_sent_callback_.is_null())
+ before_proxy_headers_sent_callback_.Run(proxy_info_, &request_headers_);
+
response_.did_use_http_auth =
request_headers_.HasHeader(HttpRequestHeaders::kAuthorization) ||
request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization);
return OK;
}
- // 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.
- if (result == OK)
- LogTransactionConnectedMetrics();
-
// ERR_CONNECTION_CLOSED is treated differently at this point; if partial
// response headers were received, we do the best we can to make sense of it
// and send it back up the stack.
DCHECK(response_.headers.get());
-#if defined(SPDY_PROXY_AUTH_ORIGIN)
- // Server-induced fallback; see: http://crbug.com/143712
- if (response_.was_fetched_via_proxy) {
- ProxyService::DataReductionProxyBypassEventType proxy_bypass_event =
- ProxyService::BYPASS_EVENT_TYPE_MAX;
- net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info;
- bool chrome_proxy_used =
- proxy_info_.proxy_server().isDataReductionProxy();
- bool chrome_fallback_proxy_used = false;
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
- if (!chrome_proxy_used) {
- chrome_fallback_proxy_used =
- proxy_info_.proxy_server().isDataReductionProxyFallback();
- }
-#endif
-
- if (chrome_proxy_used || chrome_fallback_proxy_used) {
- // A Via header might not be present in a 304. Since the goal of a 304
- // response is to minimize information transfer, a sender in general
- // should not generate representation metadata other than Cache-Control,
- // Content-Location, Date, ETag, Expires, and Vary.
- if (!response_.headers->IsChromeProxyResponse() &&
- (response_.headers->response_code() != HTTP_NOT_MODIFIED)) {
- proxy_bypass_event = ProxyService::MISSING_VIA_HEADER;
- } else if (response_.headers->GetChromeProxyInfo(&chrome_proxy_info)) {
- if (chrome_proxy_info.bypass_duration < TimeDelta::FromMinutes(30))
- proxy_bypass_event = ProxyService::SHORT_BYPASS;
- else
- proxy_bypass_event = ProxyService::LONG_BYPASS;
- } else {
- // Additionally, fallback if a 500, 502 or 503 is returned via the data
- // reduction proxy. This is conservative, as the 500, 502 or 503 might
- // have been generated by the origin, and not the proxy.
- if (response_.headers->response_code() == HTTP_INTERNAL_SERVER_ERROR ||
- response_.headers->response_code() == HTTP_BAD_GATEWAY ||
- response_.headers->response_code() == HTTP_SERVICE_UNAVAILABLE) {
- proxy_bypass_event = ProxyService::INTERNAL_SERVER_ERROR_BYPASS;
- }
- }
-
- if (proxy_bypass_event < ProxyService::BYPASS_EVENT_TYPE_MAX) {
- ProxyService* proxy_service = session_->proxy_service();
-
- proxy_service->RecordDataReductionProxyBypassInfo(
- chrome_proxy_used, proxy_info_.proxy_server(), proxy_bypass_event);
-
- ProxyServer proxy_server;
-#if defined(DATA_REDUCTION_FALLBACK_HOST)
- if (chrome_proxy_used && chrome_proxy_info.bypass_all) {
- // TODO(bengr): Rename as DATA_REDUCTION_FALLBACK_ORIGIN.
- GURL proxy_url(DATA_REDUCTION_FALLBACK_HOST);
- if (proxy_url.SchemeIsHTTPOrHTTPS()) {
- proxy_server = ProxyServer(proxy_url.SchemeIs("http") ?
- ProxyServer::SCHEME_HTTP :
- ProxyServer::SCHEME_HTTPS,
- HostPortPair::FromURL(proxy_url));
- }
- }
-#endif
- if (proxy_service->MarkProxiesAsBadUntil(
- proxy_info_,
- chrome_proxy_info.bypass_duration,
- proxy_server,
- net_log_)) {
- // Only retry idempotent methods. We don't want to resubmit a POST
- // if the proxy took some action.
- if (request_->method == "GET" ||
- request_->method == "OPTIONS" ||
- request_->method == "HEAD" ||
- request_->method == "PUT" ||
- request_->method == "DELETE" ||
- request_->method == "TRACE") {
- ResetConnectionAndRequestForResend();
- return OK;
- }
- }
- }
- }
+ // On a 408 response from the server ("Request Timeout") on a stale socket,
+ // retry the request.
+ // Headers can be NULL because of http://crbug.com/384554.
+ if (response_.headers.get() && response_.headers->response_code() == 408 &&
+ stream_->IsConnectionReused()) {
+ net_log_.AddEventWithNetErrorCode(
+ NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR,
+ response_.headers->response_code());
+ // This will close the socket - it would be weird to try and reuse it, even
+ // if the server doesn't actually close it.
+ ResetConnectionAndRequestForResend();
+ return OK;
}
-#endif // defined(SPDY_PROXY_AUTH_ORIGIN)
// Like Net.HttpResponseCode, but only for MAIN_FRAME loads.
if (request_->load_flags & LOAD_MAIN_FRAME) {
HostPortPair endpoint = HostPortPair(request_->url.HostNoBrackets(),
request_->url.EffectiveIntPort());
- ProcessAlternateProtocol(session_->http_stream_factory(),
- session_->http_server_properties(),
+ ProcessAlternateProtocol(session_,
*response_.headers.get(),
endpoint);
stream_->GetSSLInfo(&response_.ssl_info);
headers_valid_ = true;
+
+ if (session_->huffman_aggregator()) {
+ session_->huffman_aggregator()->AggregateTransactionCharacterCounts(
+ *request_,
+ request_headers_,
+ proxy_info_.proxy_server(),
+ *response_.headers.get());
+ }
return OK;
}
// Clean up connection if we are done.
if (done) {
- LogTransactionMetrics();
stream_->Close(!keep_alive);
// Note: we don't reset the stream here. We've closed it, but we still
// need it around so that callers can call methods such as
return OK;
}
-void HttpNetworkTransaction::LogTransactionConnectedMetrics() {
- if (logged_response_time_)
- return;
-
- logged_response_time_ = true;
-
- base::TimeDelta total_duration = response_.response_time - start_time_;
-
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.Transaction_Connected",
- total_duration,
- base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
- 100);
-
- bool reused_socket = stream_->IsConnectionReused();
- if (!reused_socket) {
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.Transaction_Connected_New_b",
- total_duration,
- base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
- 100);
- }
-
- // Currently, non-HIGHEST priority requests are frame or sub-frame resource
- // types. This will change when we also prioritize certain subresources like
- // css, js, etc.
- if (priority_ != HIGHEST) {
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.Priority_High_Latency_b",
- total_duration,
- base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
- 100);
- } else {
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.Priority_Low_Latency_b",
- total_duration,
- base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
- 100);
- }
-}
-
-void HttpNetworkTransaction::LogTransactionMetrics() const {
- base::TimeDelta duration = base::Time::Now() -
- response_.request_time;
- if (60 < duration.InMinutes())
- return;
-
- base::TimeDelta total_duration = base::Time::Now() - start_time_;
-
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.Transaction_Latency_b", duration,
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(10),
- 100);
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.Transaction_Latency_Total",
- total_duration,
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(10), 100);
-
- if (!stream_->IsConnectionReused()) {
- UMA_HISTOGRAM_CUSTOM_TIMES(
- "Net.Transaction_Latency_Total_New_Connection",
- total_duration, base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(10), 100);
- }
-}
-
int HttpNetworkTransaction::HandleCertificateRequest(int error) {
// There are two paths through which the server can request a certificate
// from us. The first is during the initial handshake, the second is
uint16 version_max = server_ssl_config_.version_max;
switch (error) {
+ case ERR_CONNECTION_CLOSED:
case ERR_SSL_PROTOCOL_ERROR:
case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
if (version_max >= SSL_PROTOCOL_VERSION_TLS1 &&
should_fallback = true;
}
break;
+ case ERR_CONNECTION_RESET:
+ if (version_max >= SSL_PROTOCOL_VERSION_TLS1_1 &&
+ version_max > server_ssl_config_.version_min) {
+ // Some network devices that inspect application-layer packets seem to
+ // inject TCP reset packets to break the connections when they see TLS
+ // 1.1 in ClientHello or ServerHello. See http://crbug.com/130293.
+ //
+ // Only allow ERR_CONNECTION_RESET to trigger a fallback from TLS 1.1 or
+ // 1.2. We don't lose much in this fallback because the explicit IV for
+ // CBC mode in TLS 1.1 is approximated by record splitting in TLS
+ // 1.0. The fallback will be more painful for TLS 1.2 when we have GCM
+ // support.
+ //
+ // ERR_CONNECTION_RESET is a common network error, so we don't want it
+ // to trigger a version fallback in general, especially the TLS 1.0 ->
+ // SSL 3.0 fallback, which would drop TLS extensions.
+ version_max--;
+ should_fallback = true;
+ }
+ break;
case ERR_SSL_BAD_RECORD_MAC_ALERT:
if (version_max >= SSL_PROTOCOL_VERSION_TLS1_1 &&
version_max > server_ssl_config_.version_min) {
error = OK;
}
break;
- case ERR_PIPELINE_EVICTION:
- if (!session_->force_http_pipelining()) {
- net_log_.AddEventWithNetErrorCode(
- NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error);
- ResetConnectionAndRequestForResend();
- error = OK;
- }
- break;
case ERR_SPDY_PING_FAILED:
case ERR_SPDY_SERVER_REFUSED_STREAM:
case ERR_QUIC_HANDSHAKE_FAILED:
proxy_info_.proxy_server().host_port_pair().ToString());
}
case HttpAuth::AUTH_SERVER:
+ if (ForWebSocketHandshake()) {
+ const GURL& url = request_->url;
+ url::Replacements<char> ws_to_http;
+ if (url.SchemeIs("ws")) {
+ ws_to_http.SetScheme("http", url::Component(0, 4));
+ } else {
+ DCHECK(url.SchemeIs("wss"));
+ ws_to_http.SetScheme("https", url::Component(0, 5));
+ }
+ return url.ReplaceComponents(ws_to_http);
+ }
return request_->url;
default:
return GURL();