Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(789)

Side by Side Diff: content/browser/devtools/devtools_url_interceptor_request_job.cc

Issue 2739323003: DevTools protocol interception, blocking & modification of requests (Closed)
Patch Set: Add missing expects plus tweak test output of Network.interceptedRedirect for clarity Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "content/browser/devtools/devtools_url_interceptor_request_job.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/strings/stringprintf.h"
9 #include "content/browser/devtools/protocol/network_handler.h"
10 #include "net/base/elements_upload_data_stream.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/upload_bytes_element_reader.h"
13 #include "net/cert/cert_status_flags.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/http/http_util.h"
16 #include "net/url_request/url_request_context.h"
17
18 namespace content {
19 namespace {
20 // Keep in sync with X_DevTools_Intercept_Id defined in HTTPNames.json5.
21 const char kDevToolsInterceptId[] = "X-Devtools-Intercept-Id";
22 }
23
24 DevToolsURLInterceptorRequestJob::DevToolsURLInterceptorRequestJob(
25 base::WeakPtr<DevToolsURLRequestInterceptor::State> interceptor_state,
26 const std::string& intercept_id,
27 net::URLRequest* origional_request,
28 net::NetworkDelegate* origional_network_delegate,
29 protocol::NetworkHandler* network_handler,
30 bool is_redirect)
31 : net::URLRequestJob(origional_request, origional_network_delegate),
Sami 2017/03/22 17:00:44 typo: original (2x)
alex clarke (OOO till 29th) 2017/03/23 12:25:11 Done.
32 io_thread_only_(origional_request),
33 intercept_id_(intercept_id),
34 network_handler_(network_handler),
35 interceptor_state_(interceptor_state),
36 io_thread_task_runner_(
37 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)),
38 is_redirect_(is_redirect) {
39 DCHECK_CURRENTLY_ON(BrowserThread::IO);
40 network_handler_->RegisterURLInterceptor(intercept_id_, this);
41 }
42
43 DevToolsURLInterceptorRequestJob::~DevToolsURLInterceptorRequestJob() {
44 DCHECK_CURRENTLY_ON(BrowserThread::IO);
45 if (network_handler_)
46 network_handler_->UnregisterURLInterceptor(intercept_id_, this);
47 }
48
49 void DevToolsURLInterceptorRequestJob::OnNetworkHandlerDeleted() {
50 network_handler_ = nullptr;
51 }
52
53 // net::URLRequestJob implementation:
54 void DevToolsURLInterceptorRequestJob::SetExtraRequestHeaders(
55 const net::HttpRequestHeaders& headers) {
56 io_thread_only().request_details.extra_request_headers = headers;
57 }
58
59 void DevToolsURLInterceptorRequestJob::Start() {
60 DCHECK_CURRENTLY_ON(BrowserThread::IO);
61 if (is_redirect_) {
62 io_thread_only().sub_request.reset(new SubRequest(
63 io_thread_only().request_details, interceptor_state_, this));
64 } else {
65 BrowserThread::PostTask(
66 BrowserThread::UI, FROM_HERE,
67 base::Bind(&DevToolsURLInterceptorRequestJob::
68 SendInterceptedRequestEventOnUiThread,
69 base::Unretained(this)));
Sami 2017/03/22 17:00:45 The job is owned by net/ on the network thread, so
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
70 }
71 }
72
73 void DevToolsURLInterceptorRequestJob::SendInterceptedRequestEventOnUiThread() {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
75 ui_thread_only().waiting_for_user_response = true;
76 if (network_handler_) {
77 network_handler_->frontend()->InterceptedRequest(
78 intercept_id_,
79 protocol::NetworkHandler::CreateRequestFromURLRequest(request()));
80 }
81 }
82
83 void DevToolsURLInterceptorRequestJob::Kill() {
84 if (io_thread_only().sub_request)
85 io_thread_only().sub_request->Cancel();
86
87 URLRequestJob::Kill();
88 }
89
90 int DevToolsURLInterceptorRequestJob::ReadRawData(net::IOBuffer* buf,
91 int buf_size) {
92 if (io_thread_only().sub_request) {
93 int size = io_thread_only().sub_request->request()->Read(buf, buf_size);
94 return size;
95 } else {
96 CHECK(io_thread_only().mock_response);
97 return io_thread_only().mock_response->ReadRawData(buf, buf_size);
98 }
99 }
100
101 int DevToolsURLInterceptorRequestJob::GetResponseCode() const {
102 if (io_thread_only().sub_request) {
103 return io_thread_only().sub_request->request()->GetResponseCode();
104 } else {
105 CHECK(io_thread_only().mock_response);
106 return io_thread_only().mock_response->response_headers()->response_code();
107 }
108 }
109
110 void DevToolsURLInterceptorRequestJob::GetResponseInfo(
111 net::HttpResponseInfo* info) {
112 if (io_thread_only().sub_request) {
113 *info = io_thread_only().sub_request->request()->response_info();
114 } else if (io_thread_only().mock_response) {
Sami 2017/03/22 17:00:45 Can we CHECK that the mock response exists here li
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
115 info->headers = io_thread_only().mock_response->response_headers().get();
116 }
117 }
118
119 const net::HttpResponseHeaders*
120 DevToolsURLInterceptorRequestJob::GetHttpResponseHeaders() const {
121 if (io_thread_only().sub_request) {
122 net::URLRequest* request = io_thread_only().sub_request->request();
123 return request->response_info().headers.get();
124 }
125 CHECK(io_thread_only().mock_response);
126 return io_thread_only().mock_response->response_headers().get();
127 }
128
129 bool DevToolsURLInterceptorRequestJob::GetMimeType(
130 std::string* mime_type) const {
131 const net::HttpResponseHeaders* response_headers = GetHttpResponseHeaders();
132 if (!response_headers)
133 return false;
134 return response_headers->GetMimeType(mime_type);
135 }
136
137 bool DevToolsURLInterceptorRequestJob::GetCharset(std::string* charset) {
138 const net::HttpResponseHeaders* response_headers = GetHttpResponseHeaders();
139 if (!response_headers)
140 return false;
141 return response_headers->GetCharset(charset);
142 }
143
144 void DevToolsURLInterceptorRequestJob::GetLoadTimingInfo(
145 net::LoadTimingInfo* load_timing_info) const {
146 if (io_thread_only().sub_request) {
147 io_thread_only().sub_request->request()->GetLoadTimingInfo(
148 load_timing_info);
149 } else {
150 CHECK(io_thread_only().mock_response);
151 // TODO(alexclarke): Investigate setting the other members too where
152 // possible.
153 load_timing_info->receive_headers_end =
154 io_thread_only().mock_response->response_time();
155 }
156 }
157
158 void DevToolsURLInterceptorRequestJob::OnAuthRequired(
159 net::URLRequest* request,
160 net::AuthChallengeInfo* auth_info) {
161 DCHECK(io_thread_only().sub_request);
162 DCHECK_EQ(request, io_thread_only().sub_request->request());
163 LOG(WARNING) << "Auth required to fetch URL, aborting.";
Sami 2017/03/22 17:00:45 TODO to implement this?
164 request->CancelAuth();
165 DispatchError(net::ERR_NOT_IMPLEMENTED);
166 }
167
168 void DevToolsURLInterceptorRequestJob::OnResponseStarted(
169 net::URLRequest* request,
170 int net_error) {
171 DCHECK_CURRENTLY_ON(BrowserThread::IO);
172 DCHECK(io_thread_only().sub_request);
173 DCHECK_EQ(request, io_thread_only().sub_request->request());
174 DCHECK_NE(net::ERR_IO_PENDING, net_error);
175
176 if (net_error != net::OK) {
177 io_thread_only().sub_request->Cancel();
178 DispatchError(static_cast<net::Error>(net_error));
179 return;
180 }
181
182 request->response_info().headers->RemoveHeader(kDevToolsInterceptId);
183 request->response_info().headers->AddHeader(base::StringPrintf(
184 "%s: %s", kDevToolsInterceptId, intercept_id_.c_str()));
185
186 NotifyHeadersComplete();
187 }
188
189 void DevToolsURLInterceptorRequestJob::OnReadCompleted(net::URLRequest* request,
190 int num_bytes) {
191 DCHECK_CURRENTLY_ON(BrowserThread::IO);
192 DCHECK_EQ(request, io_thread_only().sub_request->request());
193
194 ReadRawDataComplete(num_bytes);
195 }
196
197 void DevToolsURLInterceptorRequestJob::OnReceivedRedirect(
198 net::URLRequest* request,
199 const net::RedirectInfo& redirectinfo,
200 bool* defer_redirect) {
201 DCHECK_CURRENTLY_ON(BrowserThread::IO);
202 *defer_redirect = true;
203
204 size_t iter = 0;
205 std::string header_name;
206 std::string header_value;
207 std::unique_ptr<protocol::DictionaryValue> headers_dict(
208 protocol::DictionaryValue::create());
209 while (request->response_headers()->EnumerateHeaderLines(&iter, &header_name,
210 &header_value)) {
211 headers_dict->setString(header_name, header_value);
212 }
213
214 io_thread_only().redirect.reset(new net::RedirectInfo(redirectinfo));
215 io_thread_only().sub_request->Cancel();
216 io_thread_only().sub_request.reset();
217
218 BrowserThread::PostTask(
219 BrowserThread::UI, FROM_HERE,
220 base::Bind(&DevToolsURLInterceptorRequestJob::
221 SendInterceptedRedirectEventOnUiThread,
222 base::Unretained(this), base::Owned(headers_dict.release()),
Sami 2017/03/22 17:00:45 Ditto about WeakPtr.
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
223 redirectinfo.status_code, redirectinfo.new_url.spec()));
224 }
225
226 void DevToolsURLInterceptorRequestJob::SendInterceptedRedirectEventOnUiThread(
227 protocol::DictionaryValue* headers_dict,
228 int http_status_code,
229 std::string redirecturl) {
Sami 2017/03/22 17:00:45 nit: const ref, redirect_url
alex clarke (OOO till 29th) 2017/03/23 12:25:11 It can't be a const ref because this is posted by
230 DCHECK_CURRENTLY_ON(BrowserThread::UI);
231 ui_thread_only().waiting_for_user_response = true;
232
233 return network_handler_->frontend()->InterceptedRedirect(
234 intercept_id_, protocol::Object::fromValue(headers_dict, nullptr),
235 http_status_code, redirecturl);
236 }
237
238 void DevToolsURLInterceptorRequestJob::OnSSLCertificateError(
Sami 2017/03/22 17:00:45 I'm trying to work out if this integrates with Iri
239 net::URLRequest* request,
240 const net::SSLInfo& ssl_info,
241 bool fatal) {
242 DCHECK_CURRENTLY_ON(BrowserThread::IO);
243 DCHECK(io_thread_only().sub_request);
244 DCHECK_EQ(request, io_thread_only().sub_request->request());
245
246 // Revocation check failures are not fatal.
247 if (net::IsCertStatusMinorError(ssl_info.cert_status)) {
248 request->ContinueDespiteLastError();
249 return;
250 }
251 LOG(WARNING) << "SSL certificate error, aborting.";
252
253 // Certificate errors are in same space as net errors.
254 io_thread_only().sub_request->Cancel();
255 DispatchError(static_cast<net::Error>(
256 net::MapCertStatusToNetError(ssl_info.cert_status)));
257 }
258
259 bool DevToolsURLInterceptorRequestJob::OnAllow() {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI);
261 if (!ui_thread_only().waiting_for_user_response)
262 return false;
263 ui_thread_only().waiting_for_user_response = false;
264 io_thread_task_runner_->PostTask(
265 FROM_HERE,
266 base::Bind(&DevToolsURLInterceptorRequestJob::ProcessOnAllowOnIoThread,
267 base::Unretained(this)));
268 return true;
269 }
270
271 void DevToolsURLInterceptorRequestJob::ProcessOnAllowOnIoThread() {
272 DCHECK_CURRENTLY_ON(BrowserThread::IO);
273 if (io_thread_only().redirect) {
274 std::string raw_headers = base::StringPrintf(
275 "HTTP/1.1 %d", io_thread_only().redirect->status_code);
Sami 2017/03/22 17:00:44 Missing the reason-phrase -- I'm sure there's a he
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Seems pointless adding it because net::HttpRespons
Sami 2017/03/24 10:53:51 Okay, but maybe add a short comment about it since
276 raw_headers.append(1, '\0');
277 raw_headers.append("Location: ");
278 raw_headers.append(io_thread_only().redirect->new_url.spec());
279 raw_headers.append(2, '\0');
280 io_thread_only().mock_response.reset(new MockResponse(
281 make_scoped_refptr(new net::HttpResponseHeaders(raw_headers)), "", 0,
282 base::TimeTicks::Now()));
283 io_thread_only().redirect.reset();
284 interceptor_state_->ExpectRequestAfterRedirect(request(), intercept_id_);
285 NotifyHeadersComplete();
286 } else {
287 io_thread_only().sub_request.reset(new SubRequest(
Sami 2017/03/22 17:00:45 Dumb question: Why can't we reuse the original req
alex clarke (OOO till 29th) 2017/03/23 12:25:10 I added a comment where I try to explain.
288 io_thread_only().request_details, interceptor_state_, this));
289 }
290 }
291
292 bool DevToolsURLInterceptorRequestJob::OnBlock(net::Error error_reason) {
293 DCHECK_CURRENTLY_ON(BrowserThread::UI);
294 if (!ui_thread_only().waiting_for_user_response)
295 return false;
296 ui_thread_only().waiting_for_user_response = false;
297 DispatchError(error_reason);
298 return true;
299 }
300
301 bool DevToolsURLInterceptorRequestJob::OnModifyRequest(
302 protocol::Maybe<protocol::String> url,
303 protocol::Maybe<protocol::String> method,
304 protocol::Maybe<protocol::String> post_data,
305 protocol::Maybe<protocol::Network::Headers> headers) {
306 DCHECK_CURRENTLY_ON(BrowserThread::UI);
307 if (!ui_thread_only().waiting_for_user_response)
308 return false;
309 ui_thread_only().waiting_for_user_response = false;
310 io_thread_task_runner_->PostTask(
311 FROM_HERE,
312 base::Bind(&DevToolsURLInterceptorRequestJob::ModifyRequestOnIoThread,
313 base::Unretained(this),
Sami 2017/03/22 17:00:44 Ditto for WeakPtr
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
314 base::Passed(base::MakeUnique<ModifyRequest>(
315 std::move(url), std::move(method), std::move(post_data),
316 std::move(headers)))));
317 return true;
318 }
319
320 void DevToolsURLInterceptorRequestJob::ModifyRequestOnIoThread(
321 std::unique_ptr<ModifyRequest> modify_request) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO);
323
324 // Note this redirect is not visible to the caller by design. If they want a
325 // visible redirect they can mock a response with a 302.
326 if (modify_request->url.isJust())
327 io_thread_only().request_details.url = GURL(modify_request->url.fromJust());
328
329 if (modify_request->method.isJust())
330 io_thread_only().request_details.method = modify_request->method.fromJust();
331
332 if (modify_request->post_data.isJust())
Sami 2017/03/22 17:00:45 nit: {}
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
333 io_thread_only().request_details.post_data =
334 modify_request->post_data.fromJust();
335
336 if (modify_request->headers.isJust()) {
337 io_thread_only().request_details.extra_request_headers.Clear();
338 std::unique_ptr<protocol::DictionaryValue> headers =
339 modify_request->headers.fromJust()->toValue();
340 for (size_t i = 0; i < headers->size(); i++) {
341 protocol::String value;
342 if (headers->at(i).second->asString(&value)) {
343 io_thread_only().request_details.extra_request_headers.SetHeader(
344 headers->at(i).first, value);
345 }
346 }
347 }
348
349 io_thread_only().sub_request.reset(new SubRequest(
350 io_thread_only().request_details, interceptor_state_, this));
351 }
352
353 bool DevToolsURLInterceptorRequestJob::OnMockResponse(
354 const protocol::String& raw_response) {
355 DCHECK_CURRENTLY_ON(BrowserThread::UI);
356 if (!ui_thread_only().waiting_for_user_response)
357 return false;
358 ui_thread_only().waiting_for_user_response = false;
359 io_thread_task_runner_->PostTask(
360 FROM_HERE,
361 base::Bind(
362 &DevToolsURLInterceptorRequestJob::ProcessMockResponeOnIoThread,
Sami 2017/03/22 17:00:45 typo: response
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
363 base::Unretained(this), raw_response));
364 return true;
365 }
366
367 void DevToolsURLInterceptorRequestJob::ProcessMockResponeOnIoThread(
368 protocol::String raw_response) {
369 DCHECK_CURRENTLY_ON(BrowserThread::IO);
370 io_thread_only().mock_response.reset(new MockResponse(
371 std::move(raw_response), base::TimeTicks::Now(), intercept_id_));
372
373 std::string value;
374 if (io_thread_only().mock_response->response_headers()->IsRedirect(&value))
375 interceptor_state_->ExpectRequestAfterRedirect(request(), intercept_id_);
376
377 NotifyHeadersComplete();
378 }
379
380 void DevToolsURLInterceptorRequestJob::DispatchError(net::Error reason) {
381 DCHECK_NE(reason, net::OK);
382 io_thread_task_runner_->PostTask(
383 FROM_HERE,
384 base::Bind(&DevToolsURLInterceptorRequestJob::NotifyStartError,
385 base::Unretained(this),
386 net::URLRequestStatus(net::URLRequestStatus::FAILED, reason)));
387 }
388
389 namespace {
390 std::string GetUploadData(net::URLRequest* request) {
391 if (!request->has_upload())
392 return "";
393
394 const net::UploadDataStream* stream = request->get_upload();
395 if (!stream->GetElementReaders())
396 return "";
397
398 DCHECK_EQ(1u, stream->GetElementReaders()->size());
399 const net::UploadBytesElementReader* reader =
400 (*stream->GetElementReaders())[0]->AsBytesReader();
401 return std::string(reader->bytes(), reader->length());
Sami 2017/03/22 17:00:45 This could be several GB. I wonder if there's a wa
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
402 }
403 } // namespace
404
405 DevToolsURLInterceptorRequestJob::IoThreadOnly::IoThreadOnly(
406 net::URLRequest* request)
407 : request_details(request->url(),
408 request->method(),
409 GetUploadData(request),
410 request->extra_request_headers(),
411 request->priority(),
412 request->context()) {}
413
414 DevToolsURLInterceptorRequestJob::IoThreadOnly::~IoThreadOnly() {}
415
416 DevToolsURLInterceptorRequestJob::UiThreadOnly::UiThreadOnly()
417 : waiting_for_user_response(false) {}
418
419 DevToolsURLInterceptorRequestJob::ModifyRequest::ModifyRequest(
420 protocol::Maybe<protocol::String> url,
421 protocol::Maybe<protocol::String> method,
422 protocol::Maybe<protocol::String> post_data,
423 protocol::Maybe<protocol::Network::Headers> headers)
424 : url(std::move(url)),
425 method(std::move(method)),
426 post_data(std::move(post_data)),
427 headers(std::move(headers)) {}
428
429 DevToolsURLInterceptorRequestJob::ModifyRequest::~ModifyRequest() {}
430
431 DevToolsURLInterceptorRequestJob::RequestDetails::RequestDetails(
432 const GURL& url,
433 const std::string& method,
434 const std::string& post_data,
435 const net::HttpRequestHeaders& extra_request_headers,
436 const net::RequestPriority& priority,
437 const net::URLRequestContext* url_request_context)
438 : url(url),
439 method(method),
440 post_data(post_data),
441 extra_request_headers(extra_request_headers),
442 priority(priority),
443 url_request_context(url_request_context) {}
444
445 DevToolsURLInterceptorRequestJob::RequestDetails::~RequestDetails() {}
446
447 DevToolsURLInterceptorRequestJob::SubRequest::SubRequest(
448 const DevToolsURLInterceptorRequestJob::RequestDetails& request_details,
449 base::WeakPtr<DevToolsURLRequestInterceptor::State> interceptor_state,
450 DevToolsURLInterceptorRequestJob* devtools_interceptor_request_job)
451 : buf_(new net::IOBuffer(kBufSize)),
452 interceptor_state_(interceptor_state),
453 devtools_interceptor_request_job_(devtools_interceptor_request_job),
454 fetch_in_progress_(true) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO);
456 request_ = request_details.url_request_context->CreateRequest(
Sami 2017/03/22 17:00:45 FWIW this method has a comment saying it shouldn't
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Yeah I noticed that. I've no idea what to put in
457 request_details.url, request_details.priority,
458 devtools_interceptor_request_job_),
459 request_->set_method(request_details.method);
460 request_->SetExtraRequestHeaders(request_details.extra_request_headers);
461
462 if (!request_details.post_data.empty()) {
463 std::unique_ptr<net::UploadElementReader> reader(
464 new net::UploadBytesElementReader(request_details.post_data.data(),
465 request_details.post_data.size()));
466 request_->set_upload(
467 net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0));
468 }
469
470 DCHECK(interceptor_state);
471 interceptor_state->RegisterSubRequest(request_.get());
472 request_->Start();
473 }
474
475 DevToolsURLInterceptorRequestJob::SubRequest::~SubRequest() {
476 DCHECK_CURRENTLY_ON(BrowserThread::IO);
477 if (interceptor_state_)
478 interceptor_state_->UnregisterSubRequest(request_.get());
479 }
480
481 void DevToolsURLInterceptorRequestJob::SubRequest::Cancel() {
482 DCHECK_CURRENTLY_ON(BrowserThread::IO);
483 if (!fetch_in_progress_)
484 return;
485
486 fetch_in_progress_ = false;
487 request_->Cancel();
488 }
489
490 DevToolsURLInterceptorRequestJob::MockResponse::MockResponse(
491 std::string response_bytes,
492 base::TimeTicks response_time,
493 const std::string& intercept_id)
494 : response_bytes_(std::move(response_bytes)),
495 read_offset_(0),
496 response_time_(response_time) {
497 int header_size = net::HttpUtil::LocateEndOfHeaders(response_bytes_.c_str(),
498 response_bytes_.size());
499 if (header_size == -1) {
500 LOG(WARNING) << "Can't find headers in result";
501 response_headers_ = new net::HttpResponseHeaders("");
502 } else {
503 response_headers_ =
504 new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
505 response_bytes_.c_str(), header_size));
506 read_offset_ = header_size;
507 }
508
509 response_headers_->RemoveHeader(kDevToolsInterceptId);
510 response_headers_->AddHeader(
511 base::StringPrintf("%s: %s", kDevToolsInterceptId, intercept_id.c_str()));
512
513 CHECK_LE(read_offset_, response_bytes_.size());
514 }
515
516 DevToolsURLInterceptorRequestJob::MockResponse::MockResponse(
517 const scoped_refptr<net::HttpResponseHeaders>& response_headers,
518 std::string response_bytes,
519 size_t read_offset,
520 base::TimeTicks response_time)
521 : response_headers_(response_headers),
522 response_bytes_(std::move(response_bytes)),
523 read_offset_(read_offset),
524 response_time_(response_time) {}
525
526 DevToolsURLInterceptorRequestJob::MockResponse::~MockResponse() {}
527
528 int DevToolsURLInterceptorRequestJob::MockResponse::ReadRawData(
529 net::IOBuffer* buf,
530 int buf_size) {
531 size_t bytes_available = response_bytes_.size() - read_offset_;
532 size_t bytes_to_copy =
533 std::min(static_cast<size_t>(buf_size), bytes_available);
534 if (bytes_to_copy > 0) {
535 std::memcpy(buf->data(), &response_bytes_.c_str()[read_offset_],
Sami 2017/03/22 17:00:45 c_str() -> data() since this doesn't need null ter
alex clarke (OOO till 29th) 2017/03/23 12:25:10 Done.
536 bytes_to_copy);
537 read_offset_ += bytes_to_copy;
538 }
539 return bytes_to_copy;
540 }
541
542 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698