1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/devtools/devtools_network_interceptor.h" 6 7 #include <limits> 8 9 #include "base/time/time.h" 10 #include "chrome/browser/devtools/devtools_network_conditions.h" 11 #include "chrome/browser/devtools/devtools_network_transaction.h" 12 #include "net/base/load_timing_info.h" 13 14 namespace { 15 16 int64_t kPacketSize = 1500; 17 18 } // namespace 19 20 DevToolsNetworkInterceptor::DevToolsNetworkInterceptor() 21 : conditions_(new DevToolsNetworkConditions()), 22 weak_ptr_factory_(this) { 23 } 24 25 DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() { 26 } 27 28 base::WeakPtr<DevToolsNetworkInterceptor> 29 DevToolsNetworkInterceptor::GetWeakPtr() { 30 return weak_ptr_factory_.GetWeakPtr(); 31 } 32 33 void DevToolsNetworkInterceptor::AddTransaction( 34 DevToolsNetworkTransaction* transaction) { 35 DCHECK(transactions_.find(transaction) == transactions_.end()); 36 transactions_.insert(transaction); 37 } 38 39 void DevToolsNetworkInterceptor::RemoveTransaction( 40 DevToolsNetworkTransaction* transaction) { 41 DCHECK(transactions_.find(transaction) != transactions_.end()); 42 transactions_.erase(transaction); 43 44 if (!conditions_->IsThrottling()) 45 return; 46 47 base::TimeTicks now = base::TimeTicks::Now(); 48 UpdateThrottledTransactions(now); 49 throttled_transactions_.erase(std::remove(throttled_transactions_.begin(), 50 throttled_transactions_.end(), transaction), 51 throttled_transactions_.end()); 52 53 SuspendedTransactions::iterator it = suspended_transactions_.begin(); 54 for (; it != suspended_transactions_.end(); ++it) { 55 if (it->first == transaction) { 56 suspended_transactions_.erase(it); 57 break; 58 } 59 } 60 61 ArmTimer(now); 62 } 63 64 void DevToolsNetworkInterceptor::UpdateConditions( 65 scoped_ptr<DevToolsNetworkConditions> conditions) { 66 DCHECK(conditions); 67 base::TimeTicks now = base::TimeTicks::Now(); 68 if (conditions_->IsThrottling()) 69 UpdateThrottledTransactions(now); 70 71 conditions_ = conditions.Pass(); 72 73 if (conditions_->offline()) { 74 timer_.Stop(); 75 throttled_transactions_.clear(); 76 suspended_transactions_.clear(); 77 Transactions old_transactions(transactions_); 78 Transactions::iterator it = old_transactions.begin(); 79 for (;it != old_transactions.end(); ++it) { 80 if (transactions_.find(*it) == transactions_.end()) 81 continue; 82 if (!(*it)->request() || (*it)->failed()) 83 continue; 84 if (ShouldFail(*it)) 85 (*it)->Fail(); 86 } 87 return; 88 } 89 90 if (conditions_->IsThrottling()) { 91 DCHECK(conditions_->download_throughput() != 0); 92 offset_ = now; 93 last_tick_ = 0; 94 int64_t us_tick_length = 95 (1000000L * kPacketSize) / conditions_->download_throughput(); 96 DCHECK(us_tick_length != 0); 97 if (us_tick_length == 0) 98 us_tick_length = 1; 99 tick_length_ = base::TimeDelta::FromMicroseconds(us_tick_length); 100 latency_length_ = base::TimeDelta(); 101 double latency = conditions_->latency(); 102 if (latency > 0) 103 latency_length_ = base::TimeDelta::FromMillisecondsD(latency); 104 ArmTimer(now); 105 } else { 106 timer_.Stop(); 107 108 std::vector<DevToolsNetworkTransaction*> throttled_transactions; 109 throttled_transactions.swap(throttled_transactions_); 110 size_t throttle_count = throttled_transactions.size(); 111 for (size_t i = 0; i < throttle_count; ++i) 112 FireThrottledCallback(throttled_transactions[i]); 113 114 SuspendedTransactions suspended_transactions; 115 suspended_transactions_.swap(suspended_transactions_); 116 size_t suspend_count = suspended_transactions.size(); 117 for (size_t i = 0; i < suspend_count; ++i) 118 FireThrottledCallback(suspended_transactions[i].first); 119 } 120 } 121 122 void DevToolsNetworkInterceptor::FireThrottledCallback( 123 DevToolsNetworkTransaction* transaction) { 124 if (transactions_.find(transaction) != transactions_.end()) 125 transaction->FireThrottledCallback(); 126 } 127 128 void DevToolsNetworkInterceptor::UpdateThrottledTransactions( 129 base::TimeTicks now) { 130 int64_t last_tick = (now - offset_) / tick_length_; 131 int64_t ticks = last_tick - last_tick_; 132 last_tick_ = last_tick; 133 134 int64_t length = throttled_transactions_.size(); 135 if (!length) { 136 UpdateSuspendedTransactions(now); 137 return; 138 } 139 140 int64_t shift = ticks % length; 141 for (int64_t i = 0; i < length; ++i) { 142 throttled_transactions_[i]->DecreaseThrottledByteCount( 143 (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0)); 144 } 145 std::rotate(throttled_transactions_.begin(), 146 throttled_transactions_.begin() + shift, throttled_transactions_.end()); 147 148 UpdateSuspendedTransactions(now); 149 } 150 151 void DevToolsNetworkInterceptor::UpdateSuspendedTransactions( 152 base::TimeTicks now) { 153 int64_t activation_baseline = 154 (now - latency_length_ - base::TimeTicks()).InMicroseconds(); 155 SuspendedTransactions suspended_transactions; 156 SuspendedTransactions::iterator it = suspended_transactions_.begin(); 157 for (; it != suspended_transactions_.end(); ++it) { 158 if (it->second <= activation_baseline) 159 throttled_transactions_.push_back(it->first); 160 else 161 suspended_transactions.push_back(*it); 162 } 163 suspended_transactions_.swap(suspended_transactions); 164 } 165 166 void DevToolsNetworkInterceptor::OnTimer() { 167 base::TimeTicks now = base::TimeTicks::Now(); 168 UpdateThrottledTransactions(now); 169 170 std::vector<DevToolsNetworkTransaction*> active_transactions; 171 std::vector<DevToolsNetworkTransaction*> finished_transactions; 172 size_t length = throttled_transactions_.size(); 173 for (size_t i = 0; i < length; ++i) { 174 if (throttled_transactions_[i]->throttled_byte_count() < 0) 175 finished_transactions.push_back(throttled_transactions_[i]); 176 else 177 active_transactions.push_back(throttled_transactions_[i]); 178 } 179 throttled_transactions_.swap(active_transactions); 180 181 length = finished_transactions.size(); 182 for (size_t i = 0; i < length; ++i) 183 FireThrottledCallback(finished_transactions[i]); 184 185 ArmTimer(now); 186 } 187 188 void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { 189 size_t throttle_count = throttled_transactions_.size(); 190 size_t suspend_count = suspended_transactions_.size(); 191 if (!throttle_count && !suspend_count) 192 return; 193 int64_t min_ticks_left = 0x10000L; 194 for (size_t i = 0; i < throttle_count; ++i) { 195 int64_t packets_left = (throttled_transactions_[i]->throttled_byte_count() + 196 kPacketSize - 1) / kPacketSize; 197 int64_t ticks_left = (i + 1) + throttle_count * (packets_left - 1); 198 if (i == 0 || ticks_left < min_ticks_left) 199 min_ticks_left = ticks_left; 200 } 201 base::TimeTicks desired_time = 202 offset_ + tick_length_ * (last_tick_ + min_ticks_left); 203 204 int64_t min_baseline = std::numeric_limits<int64>::max(); 205 for (size_t i = 0; i < suspend_count; ++i) { 206 if (suspended_transactions_[i].second < min_baseline) 207 min_baseline = suspended_transactions_[i].second; 208 } 209 if (suspend_count) { 210 base::TimeTicks activation_time = base::TimeTicks() + 211 base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_; 212 if (activation_time < desired_time) 213 desired_time = activation_time; 214 } 215 216 timer_.Start( 217 FROM_HERE, 218 desired_time - now, 219 base::Bind( 220 &DevToolsNetworkInterceptor::OnTimer, 221 base::Unretained(this))); 222 } 223 224 void DevToolsNetworkInterceptor::ThrottleTransaction( 225 DevToolsNetworkTransaction* transaction, bool start) { 226 base::TimeTicks now = base::TimeTicks::Now(); 227 UpdateThrottledTransactions(now); 228 if (start && latency_length_ != base::TimeDelta()) { 229 net::LoadTimingInfo load_timing_info; 230 base::TimeTicks send_end; 231 if (transaction->GetLoadTimingInfo(&load_timing_info)) 232 send_end = load_timing_info.send_end; 233 if (send_end.is_null()) 234 send_end = now; 235 int64_t us_send_end = (send_end - base::TimeTicks()).InMicroseconds(); 236 suspended_transactions_.push_back( 237 SuspendedTransaction(transaction, us_send_end)); 238 UpdateSuspendedTransactions(now); 239 } else { 240 throttled_transactions_.push_back(transaction); 241 } 242 ArmTimer(now); 243 } 244 245 bool DevToolsNetworkInterceptor::ShouldFail( 246 const DevToolsNetworkTransaction* transaction) { 247 if (!conditions_->offline()) 248 return false; 249 250 if (!transaction->request_initiator().empty()) 251 return false; 252 253 return true; 254 } 255 256 bool DevToolsNetworkInterceptor::ShouldThrottle( 257 const DevToolsNetworkTransaction* transaction) { 258 if (!conditions_->IsThrottling()) 259 return false; 260 261 if (!transaction->request_initiator().empty()) 262 return false; 263 264 return true; 265 } 266