From 215a81d0e66eb6dcb48a5892cf5426ca828cb9fd Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 14 Mar 2016 02:35:22 +0530 Subject: [PATCH] update devtools network interceptor to throttle uploads --- .../browser/devtools_manager_delegate.cc | 6 + brightray/browser/devtools_manager_delegate.h | 2 +- .../net/devtools_network_controller.cc | 44 ++- .../browser/net/devtools_network_controller.h | 7 +- .../net/devtools_network_interceptor.cc | 357 ++++++++++-------- .../net/devtools_network_interceptor.h | 80 ++-- .../net/devtools_network_protocol_handler.cc | 9 + .../net/devtools_network_protocol_handler.h | 2 + .../net/devtools_network_transaction.cc | 271 ++++++------- .../net/devtools_network_transaction.h | 65 +--- .../devtools_network_upload_data_stream.cc | 94 +++++ .../net/devtools_network_upload_data_stream.h | 51 +++ brightray/filenames.gypi | 2 + 13 files changed, 598 insertions(+), 392 deletions(-) create mode 100644 brightray/browser/net/devtools_network_upload_data_stream.cc create mode 100644 brightray/browser/net/devtools_network_upload_data_stream.h diff --git a/brightray/browser/devtools_manager_delegate.cc b/brightray/browser/devtools_manager_delegate.cc index 155f35728dfa..b79c94f8140f 100644 --- a/brightray/browser/devtools_manager_delegate.cc +++ b/brightray/browser/devtools_manager_delegate.cc @@ -158,6 +158,12 @@ DevToolsManagerDelegate::DevToolsManagerDelegate() DevToolsManagerDelegate::~DevToolsManagerDelegate() { } +void DevToolsManagerDelegate::DevToolsAgentStateChanged( + content::DevToolsAgentHost* agent_host, + bool attached) { + handler_->DevToolsAgentStateChanged(agent_host, attached); +} + base::DictionaryValue* DevToolsManagerDelegate::HandleCommand( content::DevToolsAgentHost* agent_host, base::DictionaryValue* command) { diff --git a/brightray/browser/devtools_manager_delegate.h b/brightray/browser/devtools_manager_delegate.h index 12eedc66082f..34e6a6bd7047 100644 --- a/brightray/browser/devtools_manager_delegate.h +++ b/brightray/browser/devtools_manager_delegate.h @@ -29,7 +29,7 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate { void Inspect(content::BrowserContext* browser_context, content::DevToolsAgentHost* agent_host) override {} void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host, - bool attached) override {} + bool attached) override; base::DictionaryValue* HandleCommand(content::DevToolsAgentHost* agent_host, base::DictionaryValue* command) override; diff --git a/brightray/browser/net/devtools_network_controller.cc b/brightray/browser/net/devtools_network_controller.cc index 71eae70af3ca..7313a3784321 100644 --- a/brightray/browser/net/devtools_network_controller.cc +++ b/brightray/browser/net/devtools_network_controller.cc @@ -16,7 +16,7 @@ using content::BrowserThread; namespace brightray { DevToolsNetworkController::DevToolsNetworkController() - : default_interceptor_(new DevToolsNetworkInterceptor) { + : appcache_interceptor_(new DevToolsNetworkInterceptor) { } DevToolsNetworkController::~DevToolsNetworkController() { @@ -27,14 +27,7 @@ void DevToolsNetworkController::SetNetworkState( scoped_ptr conditions) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (client_id.empty()) { - if (!conditions) - return; - default_interceptor_->UpdateConditions(std::move(conditions)); - return; - } - - auto interceptor = interceptors_.get(client_id); + DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id); if (!interceptor) { if (!conditions) return; @@ -52,23 +45,36 @@ void DevToolsNetworkController::SetNetworkState( interceptor->UpdateConditions(std::move(conditions)); } } + + bool has_offline_interceptors = false; + InterceptorMap::iterator it = interceptors_.begin(); + for (; it != interceptors_.end(); ++it) { + if (it->second->IsOffline()) { + has_offline_interceptors = true; + break; + } + } + + bool is_appcache_offline = appcache_interceptor_->IsOffline(); + if (is_appcache_offline != has_offline_interceptors) { + scoped_ptr appcache_conditions( + new DevToolsNetworkConditions(has_offline_interceptors)); + appcache_interceptor_->UpdateConditions(std::move(appcache_conditions)); + } } -base::WeakPtr -DevToolsNetworkController::GetInterceptor(DevToolsNetworkTransaction* transaction) { +DevToolsNetworkInterceptor* +DevToolsNetworkController::GetInterceptor(const std::string& client_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(transaction->request()); - if (!interceptors_.size()) - return default_interceptor_->GetWeakPtr(); + if (!interceptors_.size() || client_id.empty()) + return nullptr; - transaction->ProcessRequest(); - auto& client_id = transaction->client_id(); - auto interceptor = interceptors_.get(client_id); + DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id); if (!interceptor) - return default_interceptor_->GetWeakPtr(); + return nullptr; - return interceptor->GetWeakPtr(); + return interceptor; } } // namespace brightray diff --git a/brightray/browser/net/devtools_network_controller.h b/brightray/browser/net/devtools_network_controller.h index a73268f798f3..2e3ab419da4f 100644 --- a/brightray/browser/net/devtools_network_controller.h +++ b/brightray/browser/net/devtools_network_controller.h @@ -8,7 +8,6 @@ #include "base/containers/scoped_ptr_hash_map.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" namespace brightray { @@ -23,13 +22,13 @@ class DevToolsNetworkController { void SetNetworkState(const std::string& client_id, scoped_ptr conditions); - base::WeakPtr GetInterceptor( - DevToolsNetworkTransaction* transaction); + DevToolsNetworkInterceptor* GetInterceptor(const std::string& client_id); private: using InterceptorMap = base::ScopedPtrHashMap>; - scoped_ptr default_interceptor_; + + scoped_ptr appcache_interceptor_; InterceptorMap interceptors_; DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController); diff --git a/brightray/browser/net/devtools_network_interceptor.cc b/brightray/browser/net/devtools_network_interceptor.cc index ed89fc1b54c0..126077c5f72d 100644 --- a/brightray/browser/net/devtools_network_interceptor.cc +++ b/brightray/browser/net/devtools_network_interceptor.cc @@ -4,13 +4,12 @@ #include "browser/net/devtools_network_interceptor.h" +#include #include -#include "browser/net/devtools_network_conditions.h" -#include "browser/net/devtools_network_transaction.h" - #include "base/time/time.h" -#include "net/base/load_timing_info.h" +#include "browser/net/devtools_network_conditions.h" +#include "net/base/net_errors.h" namespace brightray { @@ -18,10 +17,31 @@ namespace { int64_t kPacketSize = 1500; +base::TimeDelta CalculateTickLength(double throughput) { + if (!throughput) + return base::TimeDelta(); + + int64_t us_tick_length = (1000000L * kPacketSize) / throughput; + if (us_tick_length == 0) + us_tick_length = 1; + return base::TimeDelta::FromMicroseconds(us_tick_length); +} + } // namespace +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord() { +} + +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord( + const ThrottleRecord& other) = default; + +DevToolsNetworkInterceptor::ThrottleRecord::~ThrottleRecord() { +} + DevToolsNetworkInterceptor::DevToolsNetworkInterceptor() : conditions_(new DevToolsNetworkConditions(false)), + download_last_tick_(0), + upload_last_tick_(0), weak_ptr_factory_(this) { } @@ -33,186 +53,164 @@ DevToolsNetworkInterceptor::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } +void DevToolsNetworkInterceptor::FinishRecords( + ThrottleRecords* records, bool offline) { + ThrottleRecords temp; + temp.swap(*records); + for (const ThrottleRecord& record : temp) { + bool failed = offline && !record.is_upload; + record.callback.Run( + failed ? net::ERR_INTERNET_DISCONNECTED : record.result, + record.bytes); + } +} + void DevToolsNetworkInterceptor::UpdateConditions( scoped_ptr conditions) { DCHECK(conditions); base::TimeTicks now = base::TimeTicks::Now(); if (conditions_->IsThrottling()) - UpdateThrottledTransactions(now); + UpdateThrottled(now); conditions_ = std::move(conditions); - if (conditions_->offline()) { + bool offline = conditions_->offline(); + if (offline || conditions_->IsThrottling()) { timer_.Stop(); - throttled_transactions_.clear(); - suspended_transactions_.clear(); - Transactions old_transactions(transactions_); - Transactions::iterator it = old_transactions.begin(); - for (; it != old_transactions.end(); ++it) { - if (transactions_.find(*it) == transactions_.end()) - continue; - if (!(*it)->request() || (*it)->failed()) - continue; - if (ShouldFail(*it)) - (*it)->Fail(); - } + FinishRecords(&download_, offline); + FinishRecords(&upload_, offline); + FinishRecords(&suspended_, offline); return; } - if (conditions_->IsThrottling()) { - DCHECK_NE(conditions_->download_throughput(), 0); - offset_ = now; - last_tick_ = 0; - int64_t us_tick_length = - (1000000L * kPacketSize) / conditions_->download_throughput(); - DCHECK_NE(us_tick_length, 0); - if (us_tick_length == 0) - us_tick_length = 1; - tick_length_ = base::TimeDelta::FromMicroseconds(us_tick_length); - latency_length_ = base::TimeDelta(); - double latency = conditions_->latency(); - if (latency > 0) - latency_length_ = base::TimeDelta::FromMillisecondsD(latency); - ArmTimer(now); - } else { - timer_.Stop(); + // Throttling. + DCHECK(conditions_->download_throughput() != 0 || + conditions_->upload_throughput() != 0); + offset_ = now; - std::vector throttled_transactions; - throttled_transactions.swap(throttled_transactions_); - for (auto& throttled_transaction : throttled_transactions) - FireThrottledCallback(throttled_transaction); + download_last_tick_ = 0; + download_tick_length_ = CalculateTickLength( + conditions_->download_throughput()); - SuspendedTransactions suspended_transactions; - suspended_transactions.swap(suspended_transactions_); - for (auto& suspended_transaction : suspended_transactions) - FireThrottledCallback(suspended_transaction.first); - } -} - -void DevToolsNetworkInterceptor::AddTransaction( - DevToolsNetworkTransaction* transaction) { - DCHECK(transactions_.find(transaction) == transactions_.end()); - transactions_.insert(transaction); -} - -void DevToolsNetworkInterceptor::RemoveTransaction( - DevToolsNetworkTransaction* transaction) { - DCHECK(transactions_.find(transaction) != transactions_.end()); - transactions_.erase(transaction); - - if (!conditions_->IsThrottling()) - return; - - base::TimeTicks now = base::TimeTicks::Now(); - UpdateThrottledTransactions(now); - throttled_transactions_.erase(std::remove(throttled_transactions_.begin(), - throttled_transactions_.end(), transaction), - throttled_transactions_.end()); - - SuspendedTransactions::iterator it = suspended_transactions_.begin(); - for (; it != suspended_transactions_.end(); ++it) { - if (it->first == transaction) { - suspended_transactions_.erase(it); - break; - } - } + upload_last_tick_ = 0; + upload_tick_length_ = CalculateTickLength(conditions_->upload_throughput()); + latency_length_ = base::TimeDelta(); + double latency = conditions_->latency(); + if (latency > 0) + latency_length_ = base::TimeDelta::FromMilliseconds(latency); ArmTimer(now); } -bool DevToolsNetworkInterceptor::ShouldFail( - const DevToolsNetworkTransaction* transaction) { - return conditions_->offline(); -} - -bool DevToolsNetworkInterceptor::ShouldThrottle( - const DevToolsNetworkTransaction* transaction) { - return conditions_->IsThrottling(); -} - -void DevToolsNetworkInterceptor::ThrottleTransaction( - DevToolsNetworkTransaction* transaction, bool start) { - base::TimeTicks now = base::TimeTicks::Now(); - UpdateThrottledTransactions(now); - if (start && latency_length_ != base::TimeDelta()) { - net::LoadTimingInfo load_timing_info; - base::TimeTicks send_end; - if (transaction->GetLoadTimingInfo(&load_timing_info)) - send_end = load_timing_info.send_end; - if (send_end.is_null()) - send_end = now; - int64_t us_send_end = (send_end - base::TimeTicks()).InMicroseconds(); - suspended_transactions_.push_back( - SuspendedTransaction(transaction, us_send_end)); - UpdateSuspendedTransactions(now); - } else { - throttled_transactions_.push_back(transaction); +uint64_t DevToolsNetworkInterceptor::UpdateThrottledRecords( + base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length) { + if (tick_length.is_zero()) { + DCHECK(!records->size()); + return last_tick; } - ArmTimer(now); -} -void DevToolsNetworkInterceptor::UpdateThrottledTransactions( - base::TimeTicks now) { - int64_t last_tick = (now - offset_) / tick_length_; - int64_t ticks = last_tick - last_tick_; - last_tick_ = last_tick; + int64_t new_tick = (now - offset_) / tick_length; + int64_t ticks = new_tick - last_tick; - int64_t length = throttled_transactions_.size(); - if (!length) { - UpdateSuspendedTransactions(now); - return; - } + int64_t length = records->size(); + if (!length) + return new_tick; int64_t shift = ticks % length; for (int64_t i = 0; i < length; ++i) { - throttled_transactions_[i]->DecreaseThrottledByteCount( - (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0)); + (*records)[i].bytes -= + (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0); } - std::rotate(throttled_transactions_.begin(), - throttled_transactions_.begin() + shift, throttled_transactions_.end()); - - UpdateSuspendedTransactions(now); + std::rotate(records->begin(), records->end() + shift, records->end()); + return new_tick; } -void DevToolsNetworkInterceptor::UpdateSuspendedTransactions( - base::TimeTicks now) { +void DevToolsNetworkInterceptor::UpdateThrottled(base::TimeTicks now) { + download_last_tick_ = UpdateThrottledRecords( + now, &download_, download_last_tick_, download_tick_length_); + upload_last_tick_ = UpdateThrottledRecords( + now, &upload_, upload_last_tick_, upload_tick_length_); + UpdateSuspended(now); +} + +void DevToolsNetworkInterceptor::UpdateSuspended(base::TimeTicks now) { int64_t activation_baseline = - (now - latency_length_ - base::TimeTicks()).InMicroseconds(); - SuspendedTransactions suspended_transactions; - SuspendedTransactions::iterator it = suspended_transactions_.begin(); - for (; it != suspended_transactions_.end(); ++it) { - if (it->second <= activation_baseline) - throttled_transactions_.push_back(it->first); - else - suspended_transactions.push_back(*it); + (now - latency_length_ - base::TimeTicks::Now()).InMicroseconds(); + ThrottleRecords suspended; + for (const ThrottleRecord& record : suspended_) { + if (record.send_end <= activation_baseline) { + if (record.is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } else { + suspended.push_back(record); + } } - suspended_transactions_.swap(suspended_transactions); + suspended_.swap(suspended); } -void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { - size_t throttle_count = throttled_transactions_.size(); - size_t suspend_count = suspended_transactions_.size(); - if (!throttle_count && !suspend_count) - return; +void DevToolsNetworkInterceptor::CollectFinished( + ThrottleRecords* records, ThrottleRecords* finished) { + ThrottleRecords active; + for (const ThrottleRecord& record : *records) { + if (record.bytes < 0) + finished->push_back(record); + else + active.push_back(record); + } + records->swap(active); +} +void DevToolsNetworkInterceptor::OnTimer() { + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + + ThrottleRecords finished; + CollectFinished(&download_, &finished); + CollectFinished(&upload_, &finished); + for (const ThrottleRecord& record : finished) + record.callback.Run(record.result, record.bytes); + + ArmTimer(now); +} + +base::TimeTicks DevToolsNetworkInterceptor::CalculateDesiredTime( + const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length) { int64_t min_ticks_left = 0x10000L; - for (size_t i = 0; i < throttle_count; ++i) { - int64_t packets_left = (throttled_transactions_[i]->throttled_byte_count() + - kPacketSize - 1) / kPacketSize; - int64_t ticks_left = (i + 1) + throttle_count * (packets_left - 1); + size_t count = records.size(); + for (size_t i = 0; i < count; ++i) { + int64_t packets_left = (records[i].bytes + kPacketSize - 1) / kPacketSize; + int64_t ticks_left = (i + 1) + count * (packets_left - 1); if (i == 0 || ticks_left < min_ticks_left) min_ticks_left = ticks_left; } + return offset_ + tick_length * (last_tick + min_ticks_left); +} - base::TimeTicks desired_time = - offset_ + tick_length_ * (last_tick_ + min_ticks_left); +void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { + size_t suspend_count = suspended_.size(); + if (!download_.size() && !upload_.size() && !suspend_count) + return; + + base::TimeTicks desired_time = CalculateDesiredTime( + download_, download_last_tick_, download_tick_length_); + + base::TimeTicks upload_time = CalculateDesiredTime( + upload_, upload_last_tick_, upload_tick_length_); + if (upload_time < desired_time) + desired_time = upload_time; int64_t min_baseline = std::numeric_limits::max(); for (size_t i = 0; i < suspend_count; ++i) { - if (suspended_transactions_[i].second < min_baseline) - min_baseline = suspended_transactions_[i].second; + if (suspended_[i].send_end < min_baseline) + min_baseline = suspended_[i].send_end; } - if (suspend_count) { base::TimeTicks activation_time = base::TimeTicks() + base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_; @@ -222,34 +220,69 @@ void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { timer_.Start(FROM_HERE, desired_time - now, base::Bind(&DevToolsNetworkInterceptor::OnTimer, - base::Unretained(this))); + base::Unretained(this))); } -void DevToolsNetworkInterceptor::OnTimer() { - base::TimeTicks now = base::TimeTicks::Now(); - UpdateThrottledTransactions(now); +int DevToolsNetworkInterceptor::StartThrottle( + int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback) { + if (result < 0) + return result; - std::vector active_transactions; - std::vector finished_transactions; - size_t length = throttled_transactions_.size(); - for (size_t i = 0; i < length; ++i) { - if (throttled_transactions_[i]->throttled_byte_count() < 0) - finished_transactions.push_back(throttled_transactions_[i]); - else - active_transactions.push_back(throttled_transactions_[i]); + if (conditions_->offline()) + return is_upload ? result : net::ERR_INTERNET_DISCONNECTED; + + if ((is_upload && !conditions_->upload_throughput()) || + (!is_upload && !conditions_->download_throughput())) { + return result; } - throttled_transactions_.swap(active_transactions); - for (auto& transaction : finished_transactions) - FireThrottledCallback(transaction); + ThrottleRecord record; + record.result = result; + record.bytes = bytes; + record.callback = callback; + record.is_upload = is_upload; + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + if (start && latency_length_ != base::TimeDelta()) { + record.send_end = (send_end - base::TimeTicks()).InMicroseconds(); + suspended_.push_back(record); + UpdateSuspended(now); + } else { + if (is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } ArmTimer(now); + + return net::ERR_IO_PENDING; } -void DevToolsNetworkInterceptor::FireThrottledCallback( - DevToolsNetworkTransaction* transaction) { - if (transactions_.find(transaction) != transactions_.end()) - transaction->FireThrottledCallback(); +void DevToolsNetworkInterceptor::StopThrottle( + const ThrottleCallback& callback) { + RemoveRecord(&download_, callback); + RemoveRecord(&upload_, callback); + RemoveRecord(&suspended_, callback); +} + +void DevToolsNetworkInterceptor::RemoveRecord( + ThrottleRecords* records, const ThrottleCallback& callback) { + records->erase( + std::remove_if(records->begin(), records->end(), + [&callback](const ThrottleRecord& record){ + return record.callback.Equals(callback); + }), + records->end()); +} + +bool DevToolsNetworkInterceptor::IsOffline() { + return conditions_->offline(); } } // namespace brightray diff --git a/brightray/browser/net/devtools_network_interceptor.h b/brightray/browser/net/devtools_network_interceptor.h index 60093d0ad633..d1293e41acc8 100644 --- a/brightray/browser/net/devtools_network_interceptor.h +++ b/brightray/browser/net/devtools_network_interceptor.h @@ -5,7 +5,6 @@ #ifndef BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ #define BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ -#include #include #include #include @@ -26,6 +25,8 @@ class DevToolsNetworkTransaction; class DevToolsNetworkInterceptor { public: + using ThrottleCallback = base::Callback; + DevToolsNetworkInterceptor(); virtual ~DevToolsNetworkInterceptor(); @@ -34,45 +35,68 @@ class DevToolsNetworkInterceptor { // Applies network emulation configuration. void UpdateConditions(scoped_ptr conditions); - void AddTransaction(DevToolsNetworkTransaction* transaction); - void RemoveTransaction(DevToolsNetworkTransaction* transaction); + // Throttles with |is_upload == true| always succeed, even in offline mode. + int StartThrottle(int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback); + void StopThrottle(const ThrottleCallback& callback); - // Returns whether transaction should fail with |net::ERR_INTERNET_DISCONNECTED| - bool ShouldFail(const DevToolsNetworkTransaction* transaction); - // Returns whether transaction should be throttled. - bool ShouldThrottle(const DevToolsNetworkTransaction* transaction); - - void ThrottleTransaction(DevToolsNetworkTransaction* transaction, bool start); - - const DevToolsNetworkConditions* conditions() const { - return conditions_.get(); - } + bool IsOffline(); private: - void UpdateThrottledTransactions(base::TimeTicks now); - void UpdateSuspendedTransactions(base::TimeTicks now); - void ArmTimer(base::TimeTicks now); + struct ThrottleRecord { + public: + ThrottleRecord(); + ThrottleRecord(const ThrottleRecord& other); + ~ThrottleRecord(); + + int result; + int64_t bytes; + int64_t send_end; + bool is_upload; + ThrottleCallback callback; + }; + + using ThrottleRecords = std::vector; + + void FinishRecords(ThrottleRecords* records, bool offline); + + uint64_t UpdateThrottledRecords(base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length); + void UpdateThrottled(base::TimeTicks now); + void UpdateSuspended(base::TimeTicks now); + + void CollectFinished(ThrottleRecords* records, ThrottleRecords* finished); void OnTimer(); - void FireThrottledCallback(DevToolsNetworkTransaction* transaction); + + base::TimeTicks CalculateDesiredTime(const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length); + void ArmTimer(base::TimeTicks now); + + void RemoveRecord(ThrottleRecords* records, const ThrottleCallback& callback); scoped_ptr conditions_; - using Transactions = std::set; - Transactions transactions_; + // Throttables suspended for a "latency" period. + ThrottleRecords suspended_; - // Transactions suspended for a latency period. - using SuspendedTransaction = std::pair; - using SuspendedTransactions = std::vector; - SuspendedTransactions suspended_transactions_; - - // Transactions waiting certain amount of transfer to be accounted. - std::vector throttled_transactions_; + // Throttables waiting for certain amount of transfer to be "accounted". + ThrottleRecords download_; + ThrottleRecords upload_; base::OneShotTimer timer_; base::TimeTicks offset_; - base::TimeDelta tick_length_; + base::TimeDelta download_tick_length_; + base::TimeDelta upload_tick_length_; base::TimeDelta latency_length_; - uint64_t last_tick_; + uint64_t download_last_tick_; + uint64_t upload_last_tick_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/brightray/browser/net/devtools_network_protocol_handler.cc b/brightray/browser/net/devtools_network_protocol_handler.cc index b06678fe92de..9febb2a677d1 100644 --- a/brightray/browser/net/devtools_network_protocol_handler.cc +++ b/brightray/browser/net/devtools_network_protocol_handler.cc @@ -112,6 +112,15 @@ base::DictionaryValue* DevToolsNetworkProtocolHandler::HandleCommand( return nullptr; } +void DevToolsNetworkProtocolHandler::DevToolsAgentStateChanged( + content::DevToolsAgentHost* agent_host, + bool attached) { + scoped_ptr conditions; + if (attached) + conditions.reset(new DevToolsNetworkConditions(false)); + UpdateNetworkState(agent_host, std::move(conditions)); +} + scoped_ptr DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions( content::DevToolsAgentHost* agent_host, diff --git a/brightray/browser/net/devtools_network_protocol_handler.h b/brightray/browser/net/devtools_network_protocol_handler.h index 4fba01b74d3c..fd9c996cdc1c 100644 --- a/brightray/browser/net/devtools_network_protocol_handler.h +++ b/brightray/browser/net/devtools_network_protocol_handler.h @@ -25,6 +25,8 @@ class DevToolsNetworkProtocolHandler { base::DictionaryValue* HandleCommand( content::DevToolsAgentHost* agent_host, base::DictionaryValue* command); + void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host, + bool attached); private: scoped_ptr CanEmulateNetworkConditions( diff --git a/brightray/browser/net/devtools_network_transaction.cc b/brightray/browser/net/devtools_network_transaction.cc index f0d66a047bb4..5e6bd59e8666 100644 --- a/brightray/browser/net/devtools_network_transaction.cc +++ b/brightray/browser/net/devtools_network_transaction.cc @@ -5,8 +5,8 @@ #include "browser/net/devtools_network_transaction.h" #include "browser/net/devtools_network_controller.h" -#include "browser/net/devtools_network_interceptor.h" - +#include "browser/net/devtools_network_upload_data_stream.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/upload_progress.h" #include "net/http/http_network_transaction.h" @@ -23,58 +23,84 @@ const char DevToolsNetworkTransaction::DevToolsNetworkTransaction( DevToolsNetworkController* controller, scoped_ptr transaction) - : controller_(controller), + : throttled_byte_count_(0), + controller_(controller), transaction_(std::move(transaction)), request_(nullptr), - failed_(false), - throttled_byte_count_(0), - callback_type_(NONE) { - proxy_callback_ = base::Bind(&DevToolsNetworkTransaction::OnCallback, - base::Unretained(this)); + failed_(false) { + DCHECK(controller); } DevToolsNetworkTransaction::~DevToolsNetworkTransaction() { - if (interceptor_) - interceptor_->RemoveTransaction(this); + if (interceptor_ && !throttle_callback_.is_null()) + interceptor_->StopThrottle(throttle_callback_); } -void DevToolsNetworkTransaction::ProcessRequest() { - DCHECK(request_); +void DevToolsNetworkTransaction::IOCallback( + const net::CompletionCallback& callback, bool start, int result) { + result = Throttle(callback, start, result); + if (result != net::ERR_IO_PENDING) + callback.Run(result); +} - bool has_devtools_client_id = request_->extra_headers.HasHeader( - kDevToolsEmulateNetworkConditionsClientId); - if (!has_devtools_client_id) - return; +int DevToolsNetworkTransaction::Throttle( + const net::CompletionCallback& callback, bool start, int result) { + if (failed_) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_ || result < 0) + return result; - custom_request_.reset(new net::HttpRequestInfo(*request_)); - custom_request_->extra_headers.GetHeader( - kDevToolsEmulateNetworkConditionsClientId, &client_id_); - custom_request_->extra_headers.RemoveHeader( - kDevToolsEmulateNetworkConditionsClientId); + base::TimeTicks send_end; + if (start) { + throttled_byte_count_ += transaction_->GetTotalReceivedBytes(); + net::LoadTimingInfo load_timing_info; + if (GetLoadTimingInfo(&load_timing_info)) + send_end = load_timing_info.send_end; + if (send_end.is_null()) + send_end = base::TimeTicks::Now(); + } + if (result > 0) + throttled_byte_count_ += result; - request_ = custom_request_.get(); + throttle_callback_ = base::Bind(&DevToolsNetworkTransaction::ThrottleCallback, + base::Unretained(this), + callback); + int rv = interceptor_->StartThrottle(result, throttled_byte_count_, send_end, + start, false, throttle_callback_); + if (rv != net::ERR_IO_PENDING) + throttle_callback_.Reset(); + if (rv == net::ERR_INTERNET_DISCONNECTED) + Fail(); + return rv; +} + +void DevToolsNetworkTransaction::ThrottleCallback( + const net::CompletionCallback& callback, int result, int64_t bytes) { + DCHECK(!throttle_callback_.is_null()); + throttle_callback_.Reset(); + if (result == net::ERR_INTERNET_DISCONNECTED) + Fail(); + throttled_byte_count_ = bytes; + callback.Run(result); } void DevToolsNetworkTransaction::Fail() { DCHECK(request_); DCHECK(!failed_); - failed_ = true; - transaction_->SetBeforeNetworkStartCallback( - BeforeNetworkStartCallback()); - - if (callback_.is_null()) - return; - - net::CompletionCallback original_callback = callback_; - callback_.Reset(); - callback_type_ = NONE; - original_callback.Run(net::ERR_INTERNET_DISCONNECTED); + transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback()); + if (interceptor_) + interceptor_.reset(); } -void DevToolsNetworkTransaction::DecreaseThrottledByteCount( - int64_t delta) { - throttled_byte_count_ -= delta; +bool DevToolsNetworkTransaction::CheckFailed() { + if (failed_) + return true; + if (interceptor_ && interceptor_->IsOffline()) { + Fail(); + return true; + } + return false; } int DevToolsNetworkTransaction::Start( @@ -82,45 +108,90 @@ int DevToolsNetworkTransaction::Start( 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; - transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback()); - return net::ERR_INTERNET_DISCONNECTED; + std::string client_id; + bool has_devtools_client_id = request_->extra_headers.HasHeader( + kDevToolsEmulateNetworkConditionsClientId); + if (has_devtools_client_id) { + custom_request_.reset(new net::HttpRequestInfo(*request_)); + custom_request_->extra_headers.GetHeader( + kDevToolsEmulateNetworkConditionsClientId, &client_id); + custom_request_->extra_headers.RemoveHeader( + kDevToolsEmulateNetworkConditionsClientId); + + if (request_->upload_data_stream) { + custom_upload_data_stream_.reset( + new DevToolsNetworkUploadDataStream(request_->upload_data_stream)); + custom_request_->upload_data_stream = custom_upload_data_stream_.get(); + } + + request_ = custom_request_.get(); } - int rv = transaction_->Start(request_, proxy_callback_, net_log); - return SetupCallback(callback, rv, START); + + DevToolsNetworkInterceptor* interceptor = controller_->GetInterceptor(client_id); + if (interceptor) { + interceptor_ = interceptor->GetWeakPtr(); + if (custom_upload_data_stream_) + custom_upload_data_stream_->SetInterceptor(interceptor); + } + + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + + if (!interceptor_) + return transaction_->Start(request_, callback, net_log); + + int result = transaction_->Start(request_, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true), + net_log); + return Throttle(callback, true, result); } int DevToolsNetworkTransaction::RestartIgnoringLastError( const net::CompletionCallback& callback) { - if (failed_) + if (CheckFailed()) return net::ERR_INTERNET_DISCONNECTED; - int rv = transaction_->RestartIgnoringLastError(proxy_callback_); - return SetupCallback(callback, rv, RESTART_IGNORING_LAST_ERROR); + if (!interceptor_) + return transaction_->RestartIgnoringLastError(callback); + + int result = transaction_->RestartIgnoringLastError( + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); } int DevToolsNetworkTransaction::RestartWithCertificate( - net::X509Certificate* client_certificate, + net::X509Certificate* client_cert, net::SSLPrivateKey* client_private_key, const net::CompletionCallback& callback) { - if (failed_) + if (CheckFailed()) return net::ERR_INTERNET_DISCONNECTED; - int rv = transaction_->RestartWithCertificate(client_certificate, client_private_key, proxy_callback_); - return SetupCallback(callback, rv, RESTART_WITH_CERTIFICATE); + if (!interceptor_) { + return transaction_->RestartWithCertificate( + client_cert, client_private_key, callback); + } + + int result = transaction_->RestartWithCertificate( + client_cert, client_private_key, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); } int DevToolsNetworkTransaction::RestartWithAuth( const net::AuthCredentials& credentials, const net::CompletionCallback& callback) { - if (failed_) + if (CheckFailed()) return net::ERR_INTERNET_DISCONNECTED; - int rv = transaction_->RestartWithAuth(credentials, proxy_callback_); - return SetupCallback(callback, rv, RESTART_WITH_AUTH); + if (!interceptor_) + return transaction_->RestartWithAuth(credentials, callback); + + int result = transaction_->RestartWithAuth(credentials, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); } bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() { @@ -128,13 +199,21 @@ bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() { } int DevToolsNetworkTransaction::Read( - net::IOBuffer* buffer, - int length, + net::IOBuffer* buf, + int buf_len, const net::CompletionCallback& callback) { - if (failed_) + if (CheckFailed()) return net::ERR_INTERNET_DISCONNECTED; - int rv = transaction_->Read(buffer, length, proxy_callback_); - return SetupCallback(callback, rv, READ); + if (!interceptor_) + return transaction_->Read(buf, buf_len, callback); + + int result = transaction_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, false)); + // URLRequestJob relies on synchronous end-of-stream notification. + if (result == 0) + return result; + return Throttle(callback, false, result); } void DevToolsNetworkTransaction::StopCaching() { @@ -221,76 +300,4 @@ void DevToolsNetworkTransaction::GetConnectionAttempts( transaction_->GetConnectionAttempts(out); } -void DevToolsNetworkTransaction::OnCallback(int rv) { - if (failed_ || callback_.is_null()) - return; - - if (callback_type_ == START || callback_type_ == READ) { - if (interceptor_ && interceptor_->ShouldThrottle(this)) { - Throttle(rv); - return; - } - } - - net::CompletionCallback original_callback = callback_; - callback_.Reset(); - callback_type_ = NONE; - original_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::Throttle(int result) { - throttled_result_ = result; - - if (callback_type_ == START) - throttled_byte_count_ += transaction_->GetTotalReceivedBytes(); - if (result > 0) - throttled_byte_count_ += result; - - if (interceptor_) - interceptor_->ThrottleTransaction(this, callback_type_ == START); -} - -void DevToolsNetworkTransaction::FireThrottledCallback() { - DCHECK(!callback_.is_null()); - DCHECK(callback_type_ == READ || callback_type_ == START); - - net::CompletionCallback original_callback = callback_; - callback_.Reset(); - callback_type_ = NONE; - original_callback.Run(throttled_result_); -} - } // namespace brightray diff --git a/brightray/browser/net/devtools_network_transaction.h b/brightray/browser/net/devtools_network_transaction.h index f8e54e67775f..a7f08651f656 100644 --- a/brightray/browser/net/devtools_network_transaction.h +++ b/brightray/browser/net/devtools_network_transaction.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" #include "net/base/completion_callback.h" #include "net/base/load_states.h" #include "net/base/request_priority.h" @@ -18,7 +19,7 @@ namespace brightray { class DevToolsNetworkController; -class DevToolsNetworkInterceptor; +class DevToolsNetworkUploadDataStream; class DevToolsNetworkTransaction : public net::HttpTransaction { public: @@ -29,16 +30,6 @@ class DevToolsNetworkTransaction : public net::HttpTransaction { scoped_ptr network_transaction); ~DevToolsNetworkTransaction() override; - // Checks if request contains DevTools specific headers. Found values are - // remembered and corresponding keys are removed from headers. - void ProcessRequest(); - - // Runs callback with net::ERR_INTERNET_DISCONNECTED result. - void Fail(); - - void DecreaseThrottledByteCount(int64_t delta); - void FireThrottledCallback(); - // HttpTransaction methods: int Start(const net::HttpRequestInfo* request, const net::CompletionCallback& callback, @@ -77,39 +68,30 @@ class DevToolsNetworkTransaction : public net::HttpTransaction { int ResumeNetworkStart() override; void GetConnectionAttempts(net::ConnectionAttempts* out) const override; - bool failed() const { return failed_; } - - const net::HttpRequestInfo* request() const { return request_; } - - int64_t throttled_byte_count() const { return throttled_byte_count_; } - - const std::string& client_id() const { - return client_id_; - } - private: - enum CallbackType { - NONE, - READ, - RESTART_IGNORING_LAST_ERROR, - RESTART_WITH_AUTH, - RESTART_WITH_CERTIFICATE, - START - }; + void Fail(); + bool CheckFailed(); - // Proxy callback handler. Runs saved callback. - void OnCallback(int result); + void IOCallback(const net::CompletionCallback& callback, + bool start, + int result); + int Throttle(const net::CompletionCallback& callback, + bool start, + int result); + void ThrottleCallback(const net::CompletionCallback& callback, + int result, + int64_t bytes); - int SetupCallback( - net::CompletionCallback callback, - int result, - CallbackType callback_type); - void Throttle(int result); + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; DevToolsNetworkController* controller_; base::WeakPtr interceptor_; - // Modified request. Should be destructed after |transaction_| + // Modified upload data stream. Should be destructed after |custom_request_|. + scoped_ptr custom_upload_data_stream_; + + // Modified request. Should be destructed after |transaction_|. scoped_ptr custom_request_; // Original network transaction. @@ -120,15 +102,6 @@ class DevToolsNetworkTransaction : public net::HttpTransaction { // True if Fail was already invoked. bool failed_; - // Value of "X-DevTools-Emulate-Network-Conditions-Client-Id" request header. - std::string client_id_; - - int throttled_result_; - int64_t throttled_byte_count_; - CallbackType callback_type_; - net::CompletionCallback proxy_callback_; - net::CompletionCallback callback_; - DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransaction); }; diff --git a/brightray/browser/net/devtools_network_upload_data_stream.cc b/brightray/browser/net/devtools_network_upload_data_stream.cc new file mode 100644 index 000000000000..31bd8f381990 --- /dev/null +++ b/brightray/browser/net/devtools_network_upload_data_stream.cc @@ -0,0 +1,94 @@ +// Copyright 2015 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 "browser/net/devtools_network_upload_data_stream.h" + +#include "net/base/net_errors.h" + +namespace brightray { + +DevToolsNetworkUploadDataStream::DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream) + : net::UploadDataStream(upload_data_stream->is_chunked(), + upload_data_stream->identifier()), + throttle_callback_( + base::Bind(&DevToolsNetworkUploadDataStream::ThrottleCallback, + base::Unretained(this))), + throttled_byte_count_(0), + upload_data_stream_(upload_data_stream) { +} + +DevToolsNetworkUploadDataStream::~DevToolsNetworkUploadDataStream() { + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::SetInterceptor( + DevToolsNetworkInterceptor* interceptor) { + DCHECK(!interceptor_); + if (interceptor) + interceptor_ = interceptor->GetWeakPtr(); +} + +bool DevToolsNetworkUploadDataStream::IsInMemory() const { + return false; +} + +int DevToolsNetworkUploadDataStream::InitInternal() { + throttled_byte_count_ = 0; + int result = upload_data_stream_->Init( + base::Bind(&DevToolsNetworkUploadDataStream::StreamInitCallback, + base::Unretained(this))); + if (result == net::OK && !is_chunked()) + SetSize(upload_data_stream_->size()); + return result; +} + +void DevToolsNetworkUploadDataStream::StreamInitCallback(int result) { + if (!is_chunked()) + SetSize(upload_data_stream_->size()); + OnInitCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ReadInternal( + net::IOBuffer* buf, int buf_len) { + int result = upload_data_stream_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkUploadDataStream::StreamReadCallback, + base::Unretained(this))); + return ThrottleRead(result); +} + +void DevToolsNetworkUploadDataStream::StreamReadCallback(int result) { + result = ThrottleRead(result); + if (result != net::ERR_IO_PENDING) + OnReadCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ThrottleRead(int result) { + if (is_chunked() && upload_data_stream_->IsEOF()) + SetIsFinalChunk(); + + if (!interceptor_ || result < 0) + return result; + + if (result > 0) + throttled_byte_count_ += result; + return interceptor_->StartThrottle(result, throttled_byte_count_, + base::TimeTicks(), false, true, throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::ThrottleCallback( + int result, int64_t bytes) { + throttled_byte_count_ = bytes; + OnReadCompleted(result); +} + +void DevToolsNetworkUploadDataStream::ResetInternal() { + upload_data_stream_->Reset(); + throttled_byte_count_ = 0; + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_upload_data_stream.h b/brightray/browser/net/devtools_network_upload_data_stream.h new file mode 100644 index 000000000000..e297836f8c5a --- /dev/null +++ b/brightray/browser/net/devtools_network_upload_data_stream.h @@ -0,0 +1,51 @@ +// Copyright 2015 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. + +#ifndef BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ +#define BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ + +#include + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" +#include "net/base/completion_callback.h" +#include "net/base/upload_data_stream.h" + +namespace brightray { + +class DevToolsNetworkUploadDataStream : public net::UploadDataStream { + public: + // Supplied |upload_data_stream| must outlive this object. + explicit DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream); + ~DevToolsNetworkUploadDataStream() override; + + void SetInterceptor(DevToolsNetworkInterceptor* interceptor); + + private: + // net::UploadDataStream implementation. + bool IsInMemory() const override; + int InitInternal() override; + int ReadInternal(net::IOBuffer* buf, int buf_len) override; + void ResetInternal() override; + + void StreamInitCallback(int result); + void StreamReadCallback(int result); + + int ThrottleRead(int result); + void ThrottleCallback(int result, int64_t bytes); + + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; + + net::UploadDataStream* upload_data_stream_; + base::WeakPtr interceptor_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkUploadDataStream); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ diff --git a/brightray/filenames.gypi b/brightray/filenames.gypi index c61af39748b8..e65e87307c37 100644 --- a/brightray/filenames.gypi +++ b/brightray/filenames.gypi @@ -53,6 +53,8 @@ 'browser/net/devtools_network_transaction_factory.h', 'browser/net/devtools_network_transaction.cc', 'browser/net/devtools_network_transaction.h', + 'browser/net/devtools_network_upload_data_stream.cc', + 'browser/net/devtools_network_upload_data_stream.h', 'browser/net_log.cc', 'browser/net_log.h', 'browser/network_delegate.cc',