update devtools network interceptor to throttle uploads
This commit is contained in:
parent
95e63f6b8e
commit
215a81d0e6
13 changed files with 598 additions and 392 deletions
|
@ -158,6 +158,12 @@ DevToolsManagerDelegate::DevToolsManagerDelegate()
|
||||||
DevToolsManagerDelegate::~DevToolsManagerDelegate() {
|
DevToolsManagerDelegate::~DevToolsManagerDelegate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DevToolsManagerDelegate::DevToolsAgentStateChanged(
|
||||||
|
content::DevToolsAgentHost* agent_host,
|
||||||
|
bool attached) {
|
||||||
|
handler_->DevToolsAgentStateChanged(agent_host, attached);
|
||||||
|
}
|
||||||
|
|
||||||
base::DictionaryValue* DevToolsManagerDelegate::HandleCommand(
|
base::DictionaryValue* DevToolsManagerDelegate::HandleCommand(
|
||||||
content::DevToolsAgentHost* agent_host,
|
content::DevToolsAgentHost* agent_host,
|
||||||
base::DictionaryValue* command) {
|
base::DictionaryValue* command) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate {
|
||||||
void Inspect(content::BrowserContext* browser_context,
|
void Inspect(content::BrowserContext* browser_context,
|
||||||
content::DevToolsAgentHost* agent_host) override {}
|
content::DevToolsAgentHost* agent_host) override {}
|
||||||
void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host,
|
void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host,
|
||||||
bool attached) override {}
|
bool attached) override;
|
||||||
base::DictionaryValue* HandleCommand(content::DevToolsAgentHost* agent_host,
|
base::DictionaryValue* HandleCommand(content::DevToolsAgentHost* agent_host,
|
||||||
base::DictionaryValue* command) override;
|
base::DictionaryValue* command) override;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ using content::BrowserThread;
|
||||||
namespace brightray {
|
namespace brightray {
|
||||||
|
|
||||||
DevToolsNetworkController::DevToolsNetworkController()
|
DevToolsNetworkController::DevToolsNetworkController()
|
||||||
: default_interceptor_(new DevToolsNetworkInterceptor) {
|
: appcache_interceptor_(new DevToolsNetworkInterceptor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DevToolsNetworkController::~DevToolsNetworkController() {
|
DevToolsNetworkController::~DevToolsNetworkController() {
|
||||||
|
@ -27,14 +27,7 @@ void DevToolsNetworkController::SetNetworkState(
|
||||||
scoped_ptr<DevToolsNetworkConditions> conditions) {
|
scoped_ptr<DevToolsNetworkConditions> conditions) {
|
||||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||||
|
|
||||||
if (client_id.empty()) {
|
DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id);
|
||||||
if (!conditions)
|
|
||||||
return;
|
|
||||||
default_interceptor_->UpdateConditions(std::move(conditions));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto interceptor = interceptors_.get(client_id);
|
|
||||||
if (!interceptor) {
|
if (!interceptor) {
|
||||||
if (!conditions)
|
if (!conditions)
|
||||||
return;
|
return;
|
||||||
|
@ -52,23 +45,36 @@ void DevToolsNetworkController::SetNetworkState(
|
||||||
interceptor->UpdateConditions(std::move(conditions));
|
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<DevToolsNetworkConditions> appcache_conditions(
|
||||||
|
new DevToolsNetworkConditions(has_offline_interceptors));
|
||||||
|
appcache_interceptor_->UpdateConditions(std::move(appcache_conditions));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base::WeakPtr<DevToolsNetworkInterceptor>
|
DevToolsNetworkInterceptor*
|
||||||
DevToolsNetworkController::GetInterceptor(DevToolsNetworkTransaction* transaction) {
|
DevToolsNetworkController::GetInterceptor(const std::string& client_id) {
|
||||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||||
DCHECK(transaction->request());
|
|
||||||
|
|
||||||
if (!interceptors_.size())
|
if (!interceptors_.size() || client_id.empty())
|
||||||
return default_interceptor_->GetWeakPtr();
|
return nullptr;
|
||||||
|
|
||||||
transaction->ProcessRequest();
|
DevToolsNetworkInterceptor* interceptor = interceptors_.get(client_id);
|
||||||
auto& client_id = transaction->client_id();
|
|
||||||
auto interceptor = interceptors_.get(client_id);
|
|
||||||
if (!interceptor)
|
if (!interceptor)
|
||||||
return default_interceptor_->GetWeakPtr();
|
return nullptr;
|
||||||
|
|
||||||
return interceptor->GetWeakPtr();
|
return interceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace brightray
|
} // namespace brightray
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "base/containers/scoped_ptr_hash_map.h"
|
#include "base/containers/scoped_ptr_hash_map.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
|
||||||
|
|
||||||
namespace brightray {
|
namespace brightray {
|
||||||
|
|
||||||
|
@ -23,13 +22,13 @@ class DevToolsNetworkController {
|
||||||
|
|
||||||
void SetNetworkState(const std::string& client_id,
|
void SetNetworkState(const std::string& client_id,
|
||||||
scoped_ptr<DevToolsNetworkConditions> conditions);
|
scoped_ptr<DevToolsNetworkConditions> conditions);
|
||||||
base::WeakPtr<DevToolsNetworkInterceptor> GetInterceptor(
|
DevToolsNetworkInterceptor* GetInterceptor(const std::string& client_id);
|
||||||
DevToolsNetworkTransaction* transaction);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using InterceptorMap = base::ScopedPtrHashMap<std::string,
|
using InterceptorMap = base::ScopedPtrHashMap<std::string,
|
||||||
scoped_ptr<DevToolsNetworkInterceptor>>;
|
scoped_ptr<DevToolsNetworkInterceptor>>;
|
||||||
scoped_ptr<DevToolsNetworkInterceptor> default_interceptor_;
|
|
||||||
|
scoped_ptr<DevToolsNetworkInterceptor> appcache_interceptor_;
|
||||||
InterceptorMap interceptors_;
|
InterceptorMap interceptors_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController);
|
DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController);
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
|
|
||||||
#include "browser/net/devtools_network_interceptor.h"
|
#include "browser/net/devtools_network_interceptor.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "browser/net/devtools_network_conditions.h"
|
|
||||||
#include "browser/net/devtools_network_transaction.h"
|
|
||||||
|
|
||||||
#include "base/time/time.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 {
|
namespace brightray {
|
||||||
|
|
||||||
|
@ -18,10 +17,31 @@ namespace {
|
||||||
|
|
||||||
int64_t kPacketSize = 1500;
|
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
|
} // namespace
|
||||||
|
|
||||||
|
DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord() {
|
||||||
|
}
|
||||||
|
|
||||||
|
DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord(
|
||||||
|
const ThrottleRecord& other) = default;
|
||||||
|
|
||||||
|
DevToolsNetworkInterceptor::ThrottleRecord::~ThrottleRecord() {
|
||||||
|
}
|
||||||
|
|
||||||
DevToolsNetworkInterceptor::DevToolsNetworkInterceptor()
|
DevToolsNetworkInterceptor::DevToolsNetworkInterceptor()
|
||||||
: conditions_(new DevToolsNetworkConditions(false)),
|
: conditions_(new DevToolsNetworkConditions(false)),
|
||||||
|
download_last_tick_(0),
|
||||||
|
upload_last_tick_(0),
|
||||||
weak_ptr_factory_(this) {
|
weak_ptr_factory_(this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,186 +53,164 @@ DevToolsNetworkInterceptor::GetWeakPtr() {
|
||||||
return weak_ptr_factory_.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(
|
void DevToolsNetworkInterceptor::UpdateConditions(
|
||||||
scoped_ptr<DevToolsNetworkConditions> conditions) {
|
scoped_ptr<DevToolsNetworkConditions> conditions) {
|
||||||
DCHECK(conditions);
|
DCHECK(conditions);
|
||||||
base::TimeTicks now = base::TimeTicks::Now();
|
base::TimeTicks now = base::TimeTicks::Now();
|
||||||
if (conditions_->IsThrottling())
|
if (conditions_->IsThrottling())
|
||||||
UpdateThrottledTransactions(now);
|
UpdateThrottled(now);
|
||||||
|
|
||||||
conditions_ = std::move(conditions);
|
conditions_ = std::move(conditions);
|
||||||
|
|
||||||
if (conditions_->offline()) {
|
bool offline = conditions_->offline();
|
||||||
|
if (offline || conditions_->IsThrottling()) {
|
||||||
timer_.Stop();
|
timer_.Stop();
|
||||||
throttled_transactions_.clear();
|
FinishRecords(&download_, offline);
|
||||||
suspended_transactions_.clear();
|
FinishRecords(&upload_, offline);
|
||||||
Transactions old_transactions(transactions_);
|
FinishRecords(&suspended_, offline);
|
||||||
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();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conditions_->IsThrottling()) {
|
// Throttling.
|
||||||
DCHECK_NE(conditions_->download_throughput(), 0);
|
DCHECK(conditions_->download_throughput() != 0 ||
|
||||||
offset_ = now;
|
conditions_->upload_throughput() != 0);
|
||||||
last_tick_ = 0;
|
offset_ = now;
|
||||||
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();
|
|
||||||
|
|
||||||
std::vector<DevToolsNetworkTransaction*> throttled_transactions;
|
download_last_tick_ = 0;
|
||||||
throttled_transactions.swap(throttled_transactions_);
|
download_tick_length_ = CalculateTickLength(
|
||||||
for (auto& throttled_transaction : throttled_transactions)
|
conditions_->download_throughput());
|
||||||
FireThrottledCallback(throttled_transaction);
|
|
||||||
|
|
||||||
SuspendedTransactions suspended_transactions;
|
upload_last_tick_ = 0;
|
||||||
suspended_transactions.swap(suspended_transactions_);
|
upload_tick_length_ = CalculateTickLength(conditions_->upload_throughput());
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
latency_length_ = base::TimeDelta();
|
||||||
|
double latency = conditions_->latency();
|
||||||
|
if (latency > 0)
|
||||||
|
latency_length_ = base::TimeDelta::FromMilliseconds(latency);
|
||||||
ArmTimer(now);
|
ArmTimer(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DevToolsNetworkInterceptor::ShouldFail(
|
uint64_t DevToolsNetworkInterceptor::UpdateThrottledRecords(
|
||||||
const DevToolsNetworkTransaction* transaction) {
|
base::TimeTicks now,
|
||||||
return conditions_->offline();
|
ThrottleRecords* records,
|
||||||
}
|
uint64_t last_tick,
|
||||||
|
base::TimeDelta tick_length) {
|
||||||
bool DevToolsNetworkInterceptor::ShouldThrottle(
|
if (tick_length.is_zero()) {
|
||||||
const DevToolsNetworkTransaction* transaction) {
|
DCHECK(!records->size());
|
||||||
return conditions_->IsThrottling();
|
return last_tick;
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
ArmTimer(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DevToolsNetworkInterceptor::UpdateThrottledTransactions(
|
int64_t new_tick = (now - offset_) / tick_length;
|
||||||
base::TimeTicks now) {
|
int64_t ticks = new_tick - last_tick;
|
||||||
int64_t last_tick = (now - offset_) / tick_length_;
|
|
||||||
int64_t ticks = last_tick - last_tick_;
|
|
||||||
last_tick_ = last_tick;
|
|
||||||
|
|
||||||
int64_t length = throttled_transactions_.size();
|
int64_t length = records->size();
|
||||||
if (!length) {
|
if (!length)
|
||||||
UpdateSuspendedTransactions(now);
|
return new_tick;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t shift = ticks % length;
|
int64_t shift = ticks % length;
|
||||||
for (int64_t i = 0; i < length; ++i) {
|
for (int64_t i = 0; i < length; ++i) {
|
||||||
throttled_transactions_[i]->DecreaseThrottledByteCount(
|
(*records)[i].bytes -=
|
||||||
(ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0));
|
(ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0);
|
||||||
}
|
}
|
||||||
std::rotate(throttled_transactions_.begin(),
|
std::rotate(records->begin(), records->end() + shift, records->end());
|
||||||
throttled_transactions_.begin() + shift, throttled_transactions_.end());
|
return new_tick;
|
||||||
|
|
||||||
UpdateSuspendedTransactions(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkInterceptor::UpdateSuspendedTransactions(
|
void DevToolsNetworkInterceptor::UpdateThrottled(base::TimeTicks now) {
|
||||||
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 =
|
int64_t activation_baseline =
|
||||||
(now - latency_length_ - base::TimeTicks()).InMicroseconds();
|
(now - latency_length_ - base::TimeTicks::Now()).InMicroseconds();
|
||||||
SuspendedTransactions suspended_transactions;
|
ThrottleRecords suspended;
|
||||||
SuspendedTransactions::iterator it = suspended_transactions_.begin();
|
for (const ThrottleRecord& record : suspended_) {
|
||||||
for (; it != suspended_transactions_.end(); ++it) {
|
if (record.send_end <= activation_baseline) {
|
||||||
if (it->second <= activation_baseline)
|
if (record.is_upload)
|
||||||
throttled_transactions_.push_back(it->first);
|
upload_.push_back(record);
|
||||||
else
|
else
|
||||||
suspended_transactions.push_back(*it);
|
download_.push_back(record);
|
||||||
|
} else {
|
||||||
|
suspended.push_back(record);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
suspended_transactions_.swap(suspended_transactions);
|
suspended_.swap(suspended);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) {
|
void DevToolsNetworkInterceptor::CollectFinished(
|
||||||
size_t throttle_count = throttled_transactions_.size();
|
ThrottleRecords* records, ThrottleRecords* finished) {
|
||||||
size_t suspend_count = suspended_transactions_.size();
|
ThrottleRecords active;
|
||||||
if (!throttle_count && !suspend_count)
|
for (const ThrottleRecord& record : *records) {
|
||||||
return;
|
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;
|
int64_t min_ticks_left = 0x10000L;
|
||||||
for (size_t i = 0; i < throttle_count; ++i) {
|
size_t count = records.size();
|
||||||
int64_t packets_left = (throttled_transactions_[i]->throttled_byte_count() +
|
for (size_t i = 0; i < count; ++i) {
|
||||||
kPacketSize - 1) / kPacketSize;
|
int64_t packets_left = (records[i].bytes + kPacketSize - 1) / kPacketSize;
|
||||||
int64_t ticks_left = (i + 1) + throttle_count * (packets_left - 1);
|
int64_t ticks_left = (i + 1) + count * (packets_left - 1);
|
||||||
if (i == 0 || ticks_left < min_ticks_left)
|
if (i == 0 || ticks_left < min_ticks_left)
|
||||||
min_ticks_left = ticks_left;
|
min_ticks_left = ticks_left;
|
||||||
}
|
}
|
||||||
|
return offset_ + tick_length * (last_tick + min_ticks_left);
|
||||||
|
}
|
||||||
|
|
||||||
base::TimeTicks desired_time =
|
void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) {
|
||||||
offset_ + tick_length_ * (last_tick_ + min_ticks_left);
|
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<int64_t>::max();
|
int64_t min_baseline = std::numeric_limits<int64_t>::max();
|
||||||
for (size_t i = 0; i < suspend_count; ++i) {
|
for (size_t i = 0; i < suspend_count; ++i) {
|
||||||
if (suspended_transactions_[i].second < min_baseline)
|
if (suspended_[i].send_end < min_baseline)
|
||||||
min_baseline = suspended_transactions_[i].second;
|
min_baseline = suspended_[i].send_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suspend_count) {
|
if (suspend_count) {
|
||||||
base::TimeTicks activation_time = base::TimeTicks() +
|
base::TimeTicks activation_time = base::TimeTicks() +
|
||||||
base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_;
|
base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_;
|
||||||
|
@ -222,34 +220,69 @@ void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) {
|
||||||
|
|
||||||
timer_.Start(FROM_HERE, desired_time - now,
|
timer_.Start(FROM_HERE, desired_time - now,
|
||||||
base::Bind(&DevToolsNetworkInterceptor::OnTimer,
|
base::Bind(&DevToolsNetworkInterceptor::OnTimer,
|
||||||
base::Unretained(this)));
|
base::Unretained(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkInterceptor::OnTimer() {
|
int DevToolsNetworkInterceptor::StartThrottle(
|
||||||
base::TimeTicks now = base::TimeTicks::Now();
|
int result,
|
||||||
UpdateThrottledTransactions(now);
|
int64_t bytes,
|
||||||
|
base::TimeTicks send_end,
|
||||||
|
bool start,
|
||||||
|
bool is_upload,
|
||||||
|
const ThrottleCallback& callback) {
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
std::vector<DevToolsNetworkTransaction*> active_transactions;
|
if (conditions_->offline())
|
||||||
std::vector<DevToolsNetworkTransaction*> finished_transactions;
|
return is_upload ? result : net::ERR_INTERNET_DISCONNECTED;
|
||||||
size_t length = throttled_transactions_.size();
|
|
||||||
for (size_t i = 0; i < length; ++i) {
|
if ((is_upload && !conditions_->upload_throughput()) ||
|
||||||
if (throttled_transactions_[i]->throttled_byte_count() < 0)
|
(!is_upload && !conditions_->download_throughput())) {
|
||||||
finished_transactions.push_back(throttled_transactions_[i]);
|
return result;
|
||||||
else
|
|
||||||
active_transactions.push_back(throttled_transactions_[i]);
|
|
||||||
}
|
}
|
||||||
throttled_transactions_.swap(active_transactions);
|
|
||||||
|
|
||||||
for (auto& transaction : finished_transactions)
|
ThrottleRecord record;
|
||||||
FireThrottledCallback(transaction);
|
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);
|
ArmTimer(now);
|
||||||
|
|
||||||
|
return net::ERR_IO_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkInterceptor::FireThrottledCallback(
|
void DevToolsNetworkInterceptor::StopThrottle(
|
||||||
DevToolsNetworkTransaction* transaction) {
|
const ThrottleCallback& callback) {
|
||||||
if (transactions_.find(transaction) != transactions_.end())
|
RemoveRecord(&download_, callback);
|
||||||
transaction->FireThrottledCallback();
|
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
|
} // namespace brightray
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#ifndef BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_
|
#ifndef BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_
|
||||||
#define BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_
|
#define BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -26,6 +25,8 @@ class DevToolsNetworkTransaction;
|
||||||
|
|
||||||
class DevToolsNetworkInterceptor {
|
class DevToolsNetworkInterceptor {
|
||||||
public:
|
public:
|
||||||
|
using ThrottleCallback = base::Callback<void(int, int64_t)>;
|
||||||
|
|
||||||
DevToolsNetworkInterceptor();
|
DevToolsNetworkInterceptor();
|
||||||
virtual ~DevToolsNetworkInterceptor();
|
virtual ~DevToolsNetworkInterceptor();
|
||||||
|
|
||||||
|
@ -34,45 +35,68 @@ class DevToolsNetworkInterceptor {
|
||||||
// Applies network emulation configuration.
|
// Applies network emulation configuration.
|
||||||
void UpdateConditions(scoped_ptr<DevToolsNetworkConditions> conditions);
|
void UpdateConditions(scoped_ptr<DevToolsNetworkConditions> conditions);
|
||||||
|
|
||||||
void AddTransaction(DevToolsNetworkTransaction* transaction);
|
// Throttles with |is_upload == true| always succeed, even in offline mode.
|
||||||
void RemoveTransaction(DevToolsNetworkTransaction* transaction);
|
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 IsOffline();
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateThrottledTransactions(base::TimeTicks now);
|
struct ThrottleRecord {
|
||||||
void UpdateSuspendedTransactions(base::TimeTicks now);
|
public:
|
||||||
void ArmTimer(base::TimeTicks now);
|
ThrottleRecord();
|
||||||
|
ThrottleRecord(const ThrottleRecord& other);
|
||||||
|
~ThrottleRecord();
|
||||||
|
|
||||||
|
int result;
|
||||||
|
int64_t bytes;
|
||||||
|
int64_t send_end;
|
||||||
|
bool is_upload;
|
||||||
|
ThrottleCallback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ThrottleRecords = std::vector<ThrottleRecord>;
|
||||||
|
|
||||||
|
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 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<DevToolsNetworkConditions> conditions_;
|
scoped_ptr<DevToolsNetworkConditions> conditions_;
|
||||||
|
|
||||||
using Transactions = std::set<DevToolsNetworkTransaction*>;
|
// Throttables suspended for a "latency" period.
|
||||||
Transactions transactions_;
|
ThrottleRecords suspended_;
|
||||||
|
|
||||||
// Transactions suspended for a latency period.
|
// Throttables waiting for certain amount of transfer to be "accounted".
|
||||||
using SuspendedTransaction = std::pair<DevToolsNetworkTransaction*, int64_t>;
|
ThrottleRecords download_;
|
||||||
using SuspendedTransactions = std::vector<SuspendedTransaction>;
|
ThrottleRecords upload_;
|
||||||
SuspendedTransactions suspended_transactions_;
|
|
||||||
|
|
||||||
// Transactions waiting certain amount of transfer to be accounted.
|
|
||||||
std::vector<DevToolsNetworkTransaction*> throttled_transactions_;
|
|
||||||
|
|
||||||
base::OneShotTimer timer_;
|
base::OneShotTimer timer_;
|
||||||
base::TimeTicks offset_;
|
base::TimeTicks offset_;
|
||||||
base::TimeDelta tick_length_;
|
base::TimeDelta download_tick_length_;
|
||||||
|
base::TimeDelta upload_tick_length_;
|
||||||
base::TimeDelta latency_length_;
|
base::TimeDelta latency_length_;
|
||||||
uint64_t last_tick_;
|
uint64_t download_last_tick_;
|
||||||
|
uint64_t upload_last_tick_;
|
||||||
|
|
||||||
base::WeakPtrFactory<DevToolsNetworkInterceptor> weak_ptr_factory_;
|
base::WeakPtrFactory<DevToolsNetworkInterceptor> weak_ptr_factory_;
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,15 @@ base::DictionaryValue* DevToolsNetworkProtocolHandler::HandleCommand(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DevToolsNetworkProtocolHandler::DevToolsAgentStateChanged(
|
||||||
|
content::DevToolsAgentHost* agent_host,
|
||||||
|
bool attached) {
|
||||||
|
scoped_ptr<DevToolsNetworkConditions> conditions;
|
||||||
|
if (attached)
|
||||||
|
conditions.reset(new DevToolsNetworkConditions(false));
|
||||||
|
UpdateNetworkState(agent_host, std::move(conditions));
|
||||||
|
}
|
||||||
|
|
||||||
scoped_ptr<base::DictionaryValue>
|
scoped_ptr<base::DictionaryValue>
|
||||||
DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions(
|
DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions(
|
||||||
content::DevToolsAgentHost* agent_host,
|
content::DevToolsAgentHost* agent_host,
|
||||||
|
|
|
@ -25,6 +25,8 @@ class DevToolsNetworkProtocolHandler {
|
||||||
base::DictionaryValue* HandleCommand(
|
base::DictionaryValue* HandleCommand(
|
||||||
content::DevToolsAgentHost* agent_host,
|
content::DevToolsAgentHost* agent_host,
|
||||||
base::DictionaryValue* command);
|
base::DictionaryValue* command);
|
||||||
|
void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host,
|
||||||
|
bool attached);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
scoped_ptr<base::DictionaryValue> CanEmulateNetworkConditions(
|
scoped_ptr<base::DictionaryValue> CanEmulateNetworkConditions(
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include "browser/net/devtools_network_transaction.h"
|
#include "browser/net/devtools_network_transaction.h"
|
||||||
|
|
||||||
#include "browser/net/devtools_network_controller.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/net_errors.h"
|
||||||
#include "net/base/upload_progress.h"
|
#include "net/base/upload_progress.h"
|
||||||
#include "net/http/http_network_transaction.h"
|
#include "net/http/http_network_transaction.h"
|
||||||
|
@ -23,58 +23,84 @@ const char
|
||||||
DevToolsNetworkTransaction::DevToolsNetworkTransaction(
|
DevToolsNetworkTransaction::DevToolsNetworkTransaction(
|
||||||
DevToolsNetworkController* controller,
|
DevToolsNetworkController* controller,
|
||||||
scoped_ptr<net::HttpTransaction> transaction)
|
scoped_ptr<net::HttpTransaction> transaction)
|
||||||
: controller_(controller),
|
: throttled_byte_count_(0),
|
||||||
|
controller_(controller),
|
||||||
transaction_(std::move(transaction)),
|
transaction_(std::move(transaction)),
|
||||||
request_(nullptr),
|
request_(nullptr),
|
||||||
failed_(false),
|
failed_(false) {
|
||||||
throttled_byte_count_(0),
|
DCHECK(controller);
|
||||||
callback_type_(NONE) {
|
|
||||||
proxy_callback_ = base::Bind(&DevToolsNetworkTransaction::OnCallback,
|
|
||||||
base::Unretained(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DevToolsNetworkTransaction::~DevToolsNetworkTransaction() {
|
DevToolsNetworkTransaction::~DevToolsNetworkTransaction() {
|
||||||
if (interceptor_)
|
if (interceptor_ && !throttle_callback_.is_null())
|
||||||
interceptor_->RemoveTransaction(this);
|
interceptor_->StopThrottle(throttle_callback_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkTransaction::ProcessRequest() {
|
void DevToolsNetworkTransaction::IOCallback(
|
||||||
DCHECK(request_);
|
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(
|
int DevToolsNetworkTransaction::Throttle(
|
||||||
kDevToolsEmulateNetworkConditionsClientId);
|
const net::CompletionCallback& callback, bool start, int result) {
|
||||||
if (!has_devtools_client_id)
|
if (failed_)
|
||||||
return;
|
return net::ERR_INTERNET_DISCONNECTED;
|
||||||
|
if (!interceptor_ || result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
custom_request_.reset(new net::HttpRequestInfo(*request_));
|
base::TimeTicks send_end;
|
||||||
custom_request_->extra_headers.GetHeader(
|
if (start) {
|
||||||
kDevToolsEmulateNetworkConditionsClientId, &client_id_);
|
throttled_byte_count_ += transaction_->GetTotalReceivedBytes();
|
||||||
custom_request_->extra_headers.RemoveHeader(
|
net::LoadTimingInfo load_timing_info;
|
||||||
kDevToolsEmulateNetworkConditionsClientId);
|
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() {
|
void DevToolsNetworkTransaction::Fail() {
|
||||||
DCHECK(request_);
|
DCHECK(request_);
|
||||||
DCHECK(!failed_);
|
DCHECK(!failed_);
|
||||||
|
|
||||||
failed_ = true;
|
failed_ = true;
|
||||||
transaction_->SetBeforeNetworkStartCallback(
|
transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback());
|
||||||
BeforeNetworkStartCallback());
|
if (interceptor_)
|
||||||
|
interceptor_.reset();
|
||||||
if (callback_.is_null())
|
|
||||||
return;
|
|
||||||
|
|
||||||
net::CompletionCallback original_callback = callback_;
|
|
||||||
callback_.Reset();
|
|
||||||
callback_type_ = NONE;
|
|
||||||
original_callback.Run(net::ERR_INTERNET_DISCONNECTED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevToolsNetworkTransaction::DecreaseThrottledByteCount(
|
bool DevToolsNetworkTransaction::CheckFailed() {
|
||||||
int64_t delta) {
|
if (failed_)
|
||||||
throttled_byte_count_ -= delta;
|
return true;
|
||||||
|
if (interceptor_ && interceptor_->IsOffline()) {
|
||||||
|
Fail();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DevToolsNetworkTransaction::Start(
|
int DevToolsNetworkTransaction::Start(
|
||||||
|
@ -82,45 +108,90 @@ int DevToolsNetworkTransaction::Start(
|
||||||
const net::CompletionCallback& callback,
|
const net::CompletionCallback& callback,
|
||||||
const net::BoundNetLog& net_log) {
|
const net::BoundNetLog& net_log) {
|
||||||
DCHECK(request);
|
DCHECK(request);
|
||||||
|
|
||||||
request_ = request;
|
request_ = request;
|
||||||
interceptor_ = controller_->GetInterceptor(this);
|
|
||||||
interceptor_->AddTransaction(this);
|
|
||||||
|
|
||||||
if (interceptor_->ShouldFail(this)) {
|
std::string client_id;
|
||||||
failed_ = true;
|
bool has_devtools_client_id = request_->extra_headers.HasHeader(
|
||||||
transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback());
|
kDevToolsEmulateNetworkConditionsClientId);
|
||||||
return net::ERR_INTERNET_DISCONNECTED;
|
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(
|
int DevToolsNetworkTransaction::RestartIgnoringLastError(
|
||||||
const net::CompletionCallback& callback) {
|
const net::CompletionCallback& callback) {
|
||||||
if (failed_)
|
if (CheckFailed())
|
||||||
return net::ERR_INTERNET_DISCONNECTED;
|
return net::ERR_INTERNET_DISCONNECTED;
|
||||||
int rv = transaction_->RestartIgnoringLastError(proxy_callback_);
|
if (!interceptor_)
|
||||||
return SetupCallback(callback, rv, RESTART_IGNORING_LAST_ERROR);
|
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(
|
int DevToolsNetworkTransaction::RestartWithCertificate(
|
||||||
net::X509Certificate* client_certificate,
|
net::X509Certificate* client_cert,
|
||||||
net::SSLPrivateKey* client_private_key,
|
net::SSLPrivateKey* client_private_key,
|
||||||
const net::CompletionCallback& callback) {
|
const net::CompletionCallback& callback) {
|
||||||
if (failed_)
|
if (CheckFailed())
|
||||||
return net::ERR_INTERNET_DISCONNECTED;
|
return net::ERR_INTERNET_DISCONNECTED;
|
||||||
int rv = transaction_->RestartWithCertificate(client_certificate, client_private_key, proxy_callback_);
|
if (!interceptor_) {
|
||||||
return SetupCallback(callback, rv, RESTART_WITH_CERTIFICATE);
|
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(
|
int DevToolsNetworkTransaction::RestartWithAuth(
|
||||||
const net::AuthCredentials& credentials,
|
const net::AuthCredentials& credentials,
|
||||||
const net::CompletionCallback& callback) {
|
const net::CompletionCallback& callback) {
|
||||||
if (failed_)
|
if (CheckFailed())
|
||||||
return net::ERR_INTERNET_DISCONNECTED;
|
return net::ERR_INTERNET_DISCONNECTED;
|
||||||
int rv = transaction_->RestartWithAuth(credentials, proxy_callback_);
|
if (!interceptor_)
|
||||||
return SetupCallback(callback, rv, RESTART_WITH_AUTH);
|
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() {
|
bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() {
|
||||||
|
@ -128,13 +199,21 @@ bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DevToolsNetworkTransaction::Read(
|
int DevToolsNetworkTransaction::Read(
|
||||||
net::IOBuffer* buffer,
|
net::IOBuffer* buf,
|
||||||
int length,
|
int buf_len,
|
||||||
const net::CompletionCallback& callback) {
|
const net::CompletionCallback& callback) {
|
||||||
if (failed_)
|
if (CheckFailed())
|
||||||
return net::ERR_INTERNET_DISCONNECTED;
|
return net::ERR_INTERNET_DISCONNECTED;
|
||||||
int rv = transaction_->Read(buffer, length, proxy_callback_);
|
if (!interceptor_)
|
||||||
return SetupCallback(callback, rv, READ);
|
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() {
|
void DevToolsNetworkTransaction::StopCaching() {
|
||||||
|
@ -221,76 +300,4 @@ void DevToolsNetworkTransaction::GetConnectionAttempts(
|
||||||
transaction_->GetConnectionAttempts(out);
|
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
|
} // namespace brightray
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
|
#include "browser/net/devtools_network_interceptor.h"
|
||||||
#include "net/base/completion_callback.h"
|
#include "net/base/completion_callback.h"
|
||||||
#include "net/base/load_states.h"
|
#include "net/base/load_states.h"
|
||||||
#include "net/base/request_priority.h"
|
#include "net/base/request_priority.h"
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
namespace brightray {
|
namespace brightray {
|
||||||
|
|
||||||
class DevToolsNetworkController;
|
class DevToolsNetworkController;
|
||||||
class DevToolsNetworkInterceptor;
|
class DevToolsNetworkUploadDataStream;
|
||||||
|
|
||||||
class DevToolsNetworkTransaction : public net::HttpTransaction {
|
class DevToolsNetworkTransaction : public net::HttpTransaction {
|
||||||
public:
|
public:
|
||||||
|
@ -29,16 +30,6 @@ class DevToolsNetworkTransaction : public net::HttpTransaction {
|
||||||
scoped_ptr<net::HttpTransaction> network_transaction);
|
scoped_ptr<net::HttpTransaction> network_transaction);
|
||||||
~DevToolsNetworkTransaction() override;
|
~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:
|
// HttpTransaction methods:
|
||||||
int Start(const net::HttpRequestInfo* request,
|
int Start(const net::HttpRequestInfo* request,
|
||||||
const net::CompletionCallback& callback,
|
const net::CompletionCallback& callback,
|
||||||
|
@ -77,39 +68,30 @@ class DevToolsNetworkTransaction : public net::HttpTransaction {
|
||||||
int ResumeNetworkStart() override;
|
int ResumeNetworkStart() override;
|
||||||
void GetConnectionAttempts(net::ConnectionAttempts* out) const 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:
|
private:
|
||||||
enum CallbackType {
|
void Fail();
|
||||||
NONE,
|
bool CheckFailed();
|
||||||
READ,
|
|
||||||
RESTART_IGNORING_LAST_ERROR,
|
|
||||||
RESTART_WITH_AUTH,
|
|
||||||
RESTART_WITH_CERTIFICATE,
|
|
||||||
START
|
|
||||||
};
|
|
||||||
|
|
||||||
// Proxy callback handler. Runs saved callback.
|
void IOCallback(const net::CompletionCallback& callback,
|
||||||
void OnCallback(int result);
|
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(
|
DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_;
|
||||||
net::CompletionCallback callback,
|
int64_t throttled_byte_count_;
|
||||||
int result,
|
|
||||||
CallbackType callback_type);
|
|
||||||
void Throttle(int result);
|
|
||||||
|
|
||||||
DevToolsNetworkController* controller_;
|
DevToolsNetworkController* controller_;
|
||||||
base::WeakPtr<DevToolsNetworkInterceptor> interceptor_;
|
base::WeakPtr<DevToolsNetworkInterceptor> interceptor_;
|
||||||
|
|
||||||
// Modified request. Should be destructed after |transaction_|
|
// Modified upload data stream. Should be destructed after |custom_request_|.
|
||||||
|
scoped_ptr<DevToolsNetworkUploadDataStream> custom_upload_data_stream_;
|
||||||
|
|
||||||
|
// Modified request. Should be destructed after |transaction_|.
|
||||||
scoped_ptr<net::HttpRequestInfo> custom_request_;
|
scoped_ptr<net::HttpRequestInfo> custom_request_;
|
||||||
|
|
||||||
// Original network transaction.
|
// Original network transaction.
|
||||||
|
@ -120,15 +102,6 @@ class DevToolsNetworkTransaction : public net::HttpTransaction {
|
||||||
// True if Fail was already invoked.
|
// True if Fail was already invoked.
|
||||||
bool failed_;
|
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);
|
DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransaction);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
94
brightray/browser/net/devtools_network_upload_data_stream.cc
Normal file
94
brightray/browser/net/devtools_network_upload_data_stream.cc
Normal file
|
@ -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
|
51
brightray/browser/net/devtools_network_upload_data_stream.h
Normal file
51
brightray/browser/net/devtools_network_upload_data_stream.h
Normal file
|
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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<DevToolsNetworkInterceptor> interceptor_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkUploadDataStream);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace brightray
|
||||||
|
|
||||||
|
#endif // BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_
|
|
@ -53,6 +53,8 @@
|
||||||
'browser/net/devtools_network_transaction_factory.h',
|
'browser/net/devtools_network_transaction_factory.h',
|
||||||
'browser/net/devtools_network_transaction.cc',
|
'browser/net/devtools_network_transaction.cc',
|
||||||
'browser/net/devtools_network_transaction.h',
|
'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.cc',
|
||||||
'browser/net_log.h',
|
'browser/net_log.h',
|
||||||
'browser/network_delegate.cc',
|
'browser/network_delegate.cc',
|
||||||
|
|
Loading…
Reference in a new issue