// Copyright 2014 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 "chrome/browser/devtools/devtools_network_transaction.h" #include "chrome/browser/devtools/devtools_network_controller.h" #include "chrome/browser/devtools/devtools_network_interceptor.h" #include "net/base/net_errors.h" #include "net/base/upload_progress.h" #include "net/http/http_network_transaction.h" #include "net/http/http_request_info.h" #include "net/socket/connection_attempts.h" // Keep in sync with kDevToolsRequestInitiator and // kDevToolsEmulateNetworkConditionsClientId defined in // InspectorResourceAgent.cpp. const char DevToolsNetworkTransaction::kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator"; const char DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id"; DevToolsNetworkTransaction::DevToolsNetworkTransaction( DevToolsNetworkController* controller, scoped_ptr network_transaction) : controller_(controller), network_transaction_(network_transaction.Pass()), request_(NULL), failed_(false), throttled_byte_count_(0), callback_type_(NONE), proxy_callback_(base::Bind(&DevToolsNetworkTransaction::OnCallback, base::Unretained(this))) { DCHECK(controller); } DevToolsNetworkTransaction::~DevToolsNetworkTransaction() { if (interceptor_) interceptor_->RemoveTransaction(this); } void DevToolsNetworkTransaction::Throttle(int result) { throttled_result_ = result; if (callback_type_ == START) throttled_byte_count_ += network_transaction_->GetTotalReceivedBytes(); if (result > 0) throttled_byte_count_ += result; if (interceptor_) interceptor_->ThrottleTransaction(this, callback_type_ == START); } void DevToolsNetworkTransaction::OnCallback(int rv) { if (failed_) return; DCHECK(!callback_.is_null()); if (callback_type_ == START || callback_type_ == READ) { if (interceptor_ && interceptor_->ShouldThrottle(this)) { Throttle(rv); return; } } net::CompletionCallback callback = callback_; callback_.Reset(); callback_type_ = NONE; callback.Run(rv); } int DevToolsNetworkTransaction::SetupCallback( net::CompletionCallback callback, int result, CallbackType callback_type) { DCHECK(callback_type_ == NONE); if (result == net::ERR_IO_PENDING) { callback_type_ = callback_type; callback_ = callback; return result; } if (!interceptor_ || !interceptor_->ShouldThrottle(this)) return result; // Only START and READ operation throttling is supported. if (callback_type != START && callback_type != READ) return result; // In case of error |throttled_byte_count_| is unknown. if (result < 0) return result; // URLRequestJob relies on synchronous end-of-stream notification. if (callback_type == READ && result == 0) return result; callback_type_ = callback_type; callback_ = callback; Throttle(result); return net::ERR_IO_PENDING; } void DevToolsNetworkTransaction::Fail() { DCHECK(request_); DCHECK(!failed_); failed_ = true; network_transaction_->SetBeforeNetworkStartCallback( BeforeNetworkStartCallback()); if (callback_.is_null()) return; net::CompletionCallback callback = callback_; callback_.Reset(); callback_type_ = NONE; callback.Run(net::ERR_INTERNET_DISCONNECTED); } int DevToolsNetworkTransaction::Start( const net::HttpRequestInfo* request, const net::CompletionCallback& callback, const net::BoundNetLog& net_log) { DCHECK(request); request_ = request; interceptor_ = controller_->GetInterceptor(this); interceptor_->AddTransaction(this); if (interceptor_->ShouldFail(this)) { failed_ = true; network_transaction_->SetBeforeNetworkStartCallback( BeforeNetworkStartCallback()); return net::ERR_INTERNET_DISCONNECTED; } int rv = network_transaction_->Start(request_, proxy_callback_, net_log); return SetupCallback(callback, rv, START); } void DevToolsNetworkTransaction::ProcessRequest() { DCHECK(request_); bool has_devtools_client_id = request_->extra_headers.HasHeader( kDevToolsEmulateNetworkConditionsClientId); bool has_devtools_request_initiator = request_->extra_headers.HasHeader( kDevToolsRequestInitiator); if (!has_devtools_client_id && !has_devtools_request_initiator) return; custom_request_.reset(new net::HttpRequestInfo(*request_)); if (has_devtools_client_id) { custom_request_->extra_headers.GetHeader( kDevToolsEmulateNetworkConditionsClientId, &client_id_); custom_request_->extra_headers.RemoveHeader( kDevToolsEmulateNetworkConditionsClientId); } if (has_devtools_request_initiator) { custom_request_->extra_headers.GetHeader( kDevToolsRequestInitiator, &request_initiator_); custom_request_->extra_headers.RemoveHeader(kDevToolsRequestInitiator); } request_ = custom_request_.get(); } int DevToolsNetworkTransaction::RestartIgnoringLastError( const net::CompletionCallback& callback) { if (failed_) return net::ERR_INTERNET_DISCONNECTED; int rv = network_transaction_->RestartIgnoringLastError(proxy_callback_); return SetupCallback(callback, rv, RESTART_IGNORING_LAST_ERROR); } int DevToolsNetworkTransaction::RestartWithCertificate( net::X509Certificate* client_cert, const net::CompletionCallback& callback) { if (failed_) return net::ERR_INTERNET_DISCONNECTED; int rv = network_transaction_->RestartWithCertificate( client_cert, proxy_callback_); return SetupCallback(callback, rv, RESTART_WITH_CERTIFICATE); } int DevToolsNetworkTransaction::RestartWithAuth( const net::AuthCredentials& credentials, const net::CompletionCallback& callback) { if (failed_) return net::ERR_INTERNET_DISCONNECTED; int rv = network_transaction_->RestartWithAuth(credentials, proxy_callback_); return SetupCallback(callback, rv, RESTART_WITH_AUTH); } bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() { return network_transaction_->IsReadyToRestartForAuth(); } int DevToolsNetworkTransaction::Read( net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) { if (failed_) return net::ERR_INTERNET_DISCONNECTED; int rv = network_transaction_->Read(buf, buf_len, proxy_callback_); return SetupCallback(callback, rv, READ); } void DevToolsNetworkTransaction::StopCaching() { network_transaction_->StopCaching(); } bool DevToolsNetworkTransaction::GetFullRequestHeaders( net::HttpRequestHeaders* headers) const { return network_transaction_->GetFullRequestHeaders(headers); } int64_t DevToolsNetworkTransaction::GetTotalReceivedBytes() const { return network_transaction_->GetTotalReceivedBytes(); } int64_t DevToolsNetworkTransaction::GetTotalSentBytes() const { return network_transaction_->GetTotalSentBytes(); } void DevToolsNetworkTransaction::DoneReading() { network_transaction_->DoneReading(); } const net::HttpResponseInfo* DevToolsNetworkTransaction::GetResponseInfo() const { return network_transaction_->GetResponseInfo(); } net::LoadState DevToolsNetworkTransaction::GetLoadState() const { return network_transaction_->GetLoadState(); } net::UploadProgress DevToolsNetworkTransaction::GetUploadProgress() const { return network_transaction_->GetUploadProgress(); } void DevToolsNetworkTransaction::SetQuicServerInfo( net::QuicServerInfo* quic_server_info) { network_transaction_->SetQuicServerInfo(quic_server_info); } bool DevToolsNetworkTransaction::GetLoadTimingInfo( net::LoadTimingInfo* load_timing_info) const { return network_transaction_->GetLoadTimingInfo(load_timing_info); } bool DevToolsNetworkTransaction::GetRemoteEndpoint( net::IPEndPoint* endpoint) const { return network_transaction_->GetRemoteEndpoint(endpoint); } void DevToolsNetworkTransaction::SetPriority(net::RequestPriority priority) { network_transaction_->SetPriority(priority); } void DevToolsNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) { network_transaction_->SetWebSocketHandshakeStreamCreateHelper(create_helper); } void DevToolsNetworkTransaction::SetBeforeNetworkStartCallback( const BeforeNetworkStartCallback& callback) { network_transaction_->SetBeforeNetworkStartCallback(callback); } void DevToolsNetworkTransaction::SetBeforeProxyHeadersSentCallback( const BeforeProxyHeadersSentCallback& callback) { network_transaction_->SetBeforeProxyHeadersSentCallback(callback); } int DevToolsNetworkTransaction::ResumeNetworkStart() { if (failed_) return net::ERR_INTERNET_DISCONNECTED; return network_transaction_->ResumeNetworkStart(); } void DevToolsNetworkTransaction::GetConnectionAttempts(net::ConnectionAttempts* out) const { network_transaction_->GetConnectionAttempts(out); } void DevToolsNetworkTransaction::FireThrottledCallback() { DCHECK(!callback_.is_null()); DCHECK(callback_type_ == READ || callback_type_ == START); net::CompletionCallback callback = callback_; callback_.Reset(); callback_type_ = NONE; callback.Run(throttled_result_); }