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.
5 #include "chrome/browser/devtools/devtools_network_interceptor.h"
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"
16 int64_t kPacketSize
= 1500;
20 DevToolsNetworkInterceptor::DevToolsNetworkInterceptor()
21 : conditions_(new DevToolsNetworkConditions()),
22 weak_ptr_factory_(this) {
25 DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() {
28 base::WeakPtr
<DevToolsNetworkInterceptor
>
29 DevToolsNetworkInterceptor::GetWeakPtr() {
30 return weak_ptr_factory_
.GetWeakPtr();
33 void DevToolsNetworkInterceptor::AddTransaction(
34 DevToolsNetworkTransaction
* transaction
) {
35 DCHECK(transactions_
.find(transaction
) == transactions_
.end());
36 transactions_
.insert(transaction
);
39 void DevToolsNetworkInterceptor::RemoveTransaction(
40 DevToolsNetworkTransaction
* transaction
) {
41 DCHECK(transactions_
.find(transaction
) != transactions_
.end());
42 transactions_
.erase(transaction
);
44 if (!conditions_
->IsThrottling())
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());
53 SuspendedTransactions::iterator it
= suspended_transactions_
.begin();
54 for (; it
!= suspended_transactions_
.end(); ++it
) {
55 if (it
->first
== transaction
) {
56 suspended_transactions_
.erase(it
);
64 void DevToolsNetworkInterceptor::UpdateConditions(
65 scoped_ptr
<DevToolsNetworkConditions
> conditions
) {
67 base::TimeTicks now
= base::TimeTicks::Now();
68 if (conditions_
->IsThrottling())
69 UpdateThrottledTransactions(now
);
71 conditions_
= conditions
.Pass();
73 if (conditions_
->offline()) {
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())
82 if (!(*it
)->request() || (*it
)->failed())
90 if (conditions_
->IsThrottling()) {
91 DCHECK(conditions_
->download_throughput() != 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)
99 tick_length_
= base::TimeDelta::FromMicroseconds(us_tick_length
);
100 latency_length_
= base::TimeDelta();
101 double latency
= conditions_
->latency();
103 latency_length_
= base::TimeDelta::FromMillisecondsD(latency
);
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
]);
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
);
122 void DevToolsNetworkInterceptor::FireThrottledCallback(
123 DevToolsNetworkTransaction
* transaction
) {
124 if (transactions_
.find(transaction
) != transactions_
.end())
125 transaction
->FireThrottledCallback();
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
;
134 int64_t length
= throttled_transactions_
.size();
136 UpdateSuspendedTransactions(now
);
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));
145 std::rotate(throttled_transactions_
.begin(),
146 throttled_transactions_
.begin() + shift
, throttled_transactions_
.end());
148 UpdateSuspendedTransactions(now
);
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
);
161 suspended_transactions
.push_back(*it
);
163 suspended_transactions_
.swap(suspended_transactions
);
166 void DevToolsNetworkInterceptor::OnTimer() {
167 base::TimeTicks now
= base::TimeTicks::Now();
168 UpdateThrottledTransactions(now
);
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
]);
177 active_transactions
.push_back(throttled_transactions_
[i
]);
179 throttled_transactions_
.swap(active_transactions
);
181 length
= finished_transactions
.size();
182 for (size_t i
= 0; i
< length
; ++i
)
183 FireThrottledCallback(finished_transactions
[i
]);
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
)
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
;
201 base::TimeTicks desired_time
=
202 offset_
+ tick_length_
* (last_tick_
+ min_ticks_left
);
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
;
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
;
220 &DevToolsNetworkInterceptor::OnTimer
,
221 base::Unretained(this)));
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())
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
);
240 throttled_transactions_
.push_back(transaction
);
245 bool DevToolsNetworkInterceptor::ShouldFail(
246 const DevToolsNetworkTransaction
* transaction
) {
247 if (!conditions_
->offline())
250 if (!transaction
->request_initiator().empty())
256 bool DevToolsNetworkInterceptor::ShouldThrottle(
257 const DevToolsNetworkTransaction
* transaction
) {
258 if (!conditions_
->IsThrottling())
261 if (!transaction
->request_initiator().empty())