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_transaction.h"
6
7#include "chrome/browser/devtools/devtools_network_controller.h"
8#include "chrome/browser/devtools/devtools_network_interceptor.h"
9#include "net/base/net_errors.h"
10#include "net/base/upload_progress.h"
11#include "net/http/http_network_transaction.h"
12#include "net/http/http_request_info.h"
13
14namespace {
15
16const char kDevToolsRequestInitiator[] = "X-DevTools-Request-Initiator";
17const char kDevToolsEmulateNetworkConditionsClientId[] =
18    "X-DevTools-Emulate-Network-Conditions-Client-Id";
19
20}  // namespace
21
22DevToolsNetworkTransaction::DevToolsNetworkTransaction(
23    DevToolsNetworkController* controller,
24    scoped_ptr<net::HttpTransaction> network_transaction)
25    : controller_(controller),
26      network_transaction_(network_transaction.Pass()),
27      request_(NULL),
28      failed_(false),
29      throttled_byte_count_(0),
30      callback_type_(NONE),
31      proxy_callback_(base::Bind(&DevToolsNetworkTransaction::OnCallback,
32                                 base::Unretained(this))) {
33  DCHECK(controller);
34}
35
36DevToolsNetworkTransaction::~DevToolsNetworkTransaction() {
37  if (interceptor_)
38    interceptor_->RemoveTransaction(this);
39}
40
41void DevToolsNetworkTransaction::Throttle(int result) {
42  throttled_result_ = result;
43
44  if (callback_type_ == START)
45    throttled_byte_count_ += network_transaction_->GetTotalReceivedBytes();
46  if (result > 0)
47    throttled_byte_count_ += result;
48
49  if (interceptor_)
50    interceptor_->ThrottleTransaction(this, callback_type_ == START);
51}
52
53void DevToolsNetworkTransaction::OnCallback(int rv) {
54  if (failed_)
55    return;
56  DCHECK(!callback_.is_null());
57  if (callback_type_ == START || callback_type_ == READ) {
58    if (interceptor_ && interceptor_->ShouldThrottle(this)) {
59      Throttle(rv);
60      return;
61    }
62  }
63  net::CompletionCallback callback = callback_;
64  callback_.Reset();
65  callback_type_ = NONE;
66  callback.Run(rv);
67}
68
69int DevToolsNetworkTransaction::SetupCallback(
70    net::CompletionCallback callback,
71    int result,
72    CallbackType callback_type) {
73  DCHECK(callback_type_ == NONE);
74
75  if (result == net::ERR_IO_PENDING) {
76    callback_type_ = callback_type;
77    callback_ = callback;
78    return result;
79  }
80
81  if (!interceptor_ || !interceptor_->ShouldThrottle(this))
82    return result;
83
84  // Only START and READ operation throttling is supported.
85  if (callback_type != START && callback_type != READ)
86    return result;
87
88  // In case of error |throttled_byte_count_| is unknown.
89  if (result < 0)
90    return result;
91
92  // URLRequestJob relies on synchronous end-of-stream notification.
93  if (callback_type == READ && result == 0)
94    return result;
95
96  callback_type_ = callback_type;
97  callback_ = callback;
98  Throttle(result);
99  return net::ERR_IO_PENDING;
100}
101
102void DevToolsNetworkTransaction::Fail() {
103  DCHECK(request_);
104  DCHECK(!failed_);
105  failed_ = true;
106  network_transaction_->SetBeforeNetworkStartCallback(
107      BeforeNetworkStartCallback());
108  if (callback_.is_null())
109    return;
110  net::CompletionCallback callback = callback_;
111  callback_.Reset();
112  callback_type_ = NONE;
113  callback.Run(net::ERR_INTERNET_DISCONNECTED);
114}
115
116int DevToolsNetworkTransaction::Start(
117    const net::HttpRequestInfo* request,
118    const net::CompletionCallback& callback,
119    const net::BoundNetLog& net_log) {
120  DCHECK(request);
121  request_ = request;
122  interceptor_ = controller_->GetInterceptor(this);
123  interceptor_->AddTransaction(this);
124
125  if (interceptor_->ShouldFail(this)) {
126    failed_ = true;
127    network_transaction_->SetBeforeNetworkStartCallback(
128        BeforeNetworkStartCallback());
129    return net::ERR_INTERNET_DISCONNECTED;
130  }
131  int rv = network_transaction_->Start(request_, proxy_callback_, net_log);
132  return SetupCallback(callback, rv, START);
133}
134
135void DevToolsNetworkTransaction::ProcessRequest() {
136  DCHECK(request_);
137  bool has_devtools_client_id = request_->extra_headers.HasHeader(
138      kDevToolsEmulateNetworkConditionsClientId);
139  bool has_devtools_request_initiator = request_->extra_headers.HasHeader(
140      kDevToolsRequestInitiator);
141  if (!has_devtools_client_id && !has_devtools_request_initiator)
142    return;
143
144  custom_request_.reset(new net::HttpRequestInfo(*request_));
145
146  if (has_devtools_client_id) {
147    custom_request_->extra_headers.GetHeader(
148        kDevToolsEmulateNetworkConditionsClientId, &client_id_);
149    custom_request_->extra_headers.RemoveHeader(
150        kDevToolsEmulateNetworkConditionsClientId);
151  }
152
153  if (has_devtools_request_initiator) {
154    custom_request_->extra_headers.GetHeader(
155        kDevToolsRequestInitiator, &request_initiator_);
156    custom_request_->extra_headers.RemoveHeader(kDevToolsRequestInitiator);
157  }
158
159  request_ = custom_request_.get();
160}
161
162int DevToolsNetworkTransaction::RestartIgnoringLastError(
163    const net::CompletionCallback& callback) {
164  if (failed_)
165    return net::ERR_INTERNET_DISCONNECTED;
166  int rv = network_transaction_->RestartIgnoringLastError(proxy_callback_);
167  return SetupCallback(callback, rv, RESTART_IGNORING_LAST_ERROR);
168}
169
170int DevToolsNetworkTransaction::RestartWithCertificate(
171    net::X509Certificate* client_cert,
172    const net::CompletionCallback& callback) {
173  if (failed_)
174    return net::ERR_INTERNET_DISCONNECTED;
175  int rv = network_transaction_->RestartWithCertificate(
176      client_cert, proxy_callback_);
177  return SetupCallback(callback, rv, RESTART_WITH_CERTIFICATE);
178}
179
180int DevToolsNetworkTransaction::RestartWithAuth(
181    const net::AuthCredentials& credentials,
182    const net::CompletionCallback& callback) {
183  if (failed_)
184    return net::ERR_INTERNET_DISCONNECTED;
185  int rv = network_transaction_->RestartWithAuth(credentials, proxy_callback_);
186  return SetupCallback(callback, rv, RESTART_WITH_AUTH);
187}
188
189bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() {
190  return network_transaction_->IsReadyToRestartForAuth();
191}
192
193int DevToolsNetworkTransaction::Read(
194    net::IOBuffer* buf,
195    int buf_len,
196    const net::CompletionCallback& callback) {
197  if (failed_)
198    return net::ERR_INTERNET_DISCONNECTED;
199  int rv = network_transaction_->Read(buf, buf_len, proxy_callback_);
200  return SetupCallback(callback, rv, READ);
201}
202
203void DevToolsNetworkTransaction::StopCaching() {
204  network_transaction_->StopCaching();
205}
206
207bool DevToolsNetworkTransaction::GetFullRequestHeaders(
208    net::HttpRequestHeaders* headers) const {
209  return network_transaction_->GetFullRequestHeaders(headers);
210}
211
212int64 DevToolsNetworkTransaction::GetTotalReceivedBytes() const {
213  return network_transaction_->GetTotalReceivedBytes();
214}
215
216void DevToolsNetworkTransaction::DoneReading() {
217  network_transaction_->DoneReading();
218}
219
220const net::HttpResponseInfo*
221DevToolsNetworkTransaction::GetResponseInfo() const {
222  return network_transaction_->GetResponseInfo();
223}
224
225net::LoadState DevToolsNetworkTransaction::GetLoadState() const {
226  return network_transaction_->GetLoadState();
227}
228
229net::UploadProgress DevToolsNetworkTransaction::GetUploadProgress() const {
230  return network_transaction_->GetUploadProgress();
231}
232
233void DevToolsNetworkTransaction::SetQuicServerInfo(
234    net::QuicServerInfo* quic_server_info) {
235  network_transaction_->SetQuicServerInfo(quic_server_info);
236}
237
238bool DevToolsNetworkTransaction::GetLoadTimingInfo(
239    net::LoadTimingInfo* load_timing_info) const {
240  return network_transaction_->GetLoadTimingInfo(load_timing_info);
241}
242
243void DevToolsNetworkTransaction::SetPriority(net::RequestPriority priority) {
244  network_transaction_->SetPriority(priority);
245}
246
247void DevToolsNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper(
248    net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
249  network_transaction_->SetWebSocketHandshakeStreamCreateHelper(create_helper);
250}
251
252void DevToolsNetworkTransaction::SetBeforeNetworkStartCallback(
253    const BeforeNetworkStartCallback& callback) {
254  network_transaction_->SetBeforeNetworkStartCallback(callback);
255}
256
257void DevToolsNetworkTransaction::SetBeforeProxyHeadersSentCallback(
258    const BeforeProxyHeadersSentCallback& callback) {
259  network_transaction_->SetBeforeProxyHeadersSentCallback(callback);
260}
261
262int DevToolsNetworkTransaction::ResumeNetworkStart() {
263  if (failed_)
264    return net::ERR_INTERNET_DISCONNECTED;
265  return network_transaction_->ResumeNetworkStart();
266}
267
268void DevToolsNetworkTransaction::FireThrottledCallback() {
269  DCHECK(!callback_.is_null());
270  DCHECK(callback_type_ == READ || callback_type_ == START);
271  net::CompletionCallback callback = callback_;
272  callback_.Reset();
273  callback_type_ = NONE;
274  callback.Run(throttled_result_);
275}
276