Split bug 423948 into various sub-bugs and remove outdated instrumentation.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_url_request_job.cc
blob8d848f45ed2f3a5bae799ef6ffd16e2e8f47bac9
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 "content/browser/service_worker/service_worker_url_request_job.h"
7 #include <map>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/guid.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "content/browser/resource_context_impl.h"
17 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
18 #include "content/browser/service_worker/service_worker_provider_host.h"
19 #include "content/browser/streams/stream.h"
20 #include "content/browser/streams/stream_context.h"
21 #include "content/browser/streams/stream_registry.h"
22 #include "content/common/resource_request_body.h"
23 #include "content/common/service_worker/service_worker_types.h"
24 #include "content/public/browser/blob_handle.h"
25 #include "content/public/browser/resource_request_info.h"
26 #include "content/public/browser/service_worker_context.h"
27 #include "content/public/common/referrer.h"
28 #include "net/base/net_errors.h"
29 #include "net/http/http_request_headers.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/http/http_response_info.h"
32 #include "net/http/http_util.h"
33 #include "storage/browser/blob/blob_data_builder.h"
34 #include "storage/browser/blob/blob_data_handle.h"
35 #include "storage/browser/blob/blob_storage_context.h"
36 #include "storage/browser/blob/blob_url_request_job_factory.h"
37 #include "ui/base/page_transition_types.h"
39 namespace content {
41 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
42 net::URLRequest* request,
43 net::NetworkDelegate* network_delegate,
44 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
45 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
46 const ResourceContext* resource_context,
47 FetchRequestMode request_mode,
48 FetchCredentialsMode credentials_mode,
49 RequestContextType request_context_type,
50 RequestContextFrameType frame_type,
51 scoped_refptr<ResourceRequestBody> body)
52 : net::URLRequestJob(request, network_delegate),
53 provider_host_(provider_host),
54 response_type_(NOT_DETERMINED),
55 is_started_(false),
56 service_worker_response_type_(blink::WebServiceWorkerResponseTypeDefault),
57 blob_storage_context_(blob_storage_context),
58 resource_context_(resource_context),
59 stream_pending_buffer_size_(0),
60 request_mode_(request_mode),
61 credentials_mode_(credentials_mode),
62 request_context_type_(request_context_type),
63 frame_type_(frame_type),
64 fall_back_required_(false),
65 body_(body),
66 weak_factory_(this) {
69 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
70 DCHECK_EQ(NOT_DETERMINED, response_type_);
71 response_type_ = FALLBACK_TO_NETWORK;
72 MaybeStartRequest();
75 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
76 DCHECK_EQ(NOT_DETERMINED, response_type_);
77 response_type_ = FORWARD_TO_SERVICE_WORKER;
78 MaybeStartRequest();
81 void ServiceWorkerURLRequestJob::Start() {
82 is_started_ = true;
83 MaybeStartRequest();
86 void ServiceWorkerURLRequestJob::Kill() {
87 net::URLRequestJob::Kill();
88 ClearStream();
89 fetch_dispatcher_.reset();
90 blob_request_.reset();
91 weak_factory_.InvalidateWeakPtrs();
94 net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
95 // TODO(kinuko): refine this for better debug.
96 return net::URLRequestJob::GetLoadState();
99 bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
100 if (!http_info())
101 return false;
102 return http_info()->headers->GetCharset(charset);
105 bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
106 if (!http_info())
107 return false;
108 return http_info()->headers->GetMimeType(mime_type);
111 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
112 if (!http_info())
113 return;
114 const base::Time request_time = info->request_time;
115 *info = *http_info();
116 info->request_time = request_time;
117 info->response_time = response_time_;
120 void ServiceWorkerURLRequestJob::GetLoadTimingInfo(
121 net::LoadTimingInfo* load_timing_info) const {
122 *load_timing_info = load_timing_info_;
125 int ServiceWorkerURLRequestJob::GetResponseCode() const {
126 if (!http_info())
127 return -1;
128 return http_info()->headers->response_code();
131 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
132 const net::HttpRequestHeaders& headers) {
133 std::string range_header;
134 std::vector<net::HttpByteRange> ranges;
135 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) ||
136 !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
137 return;
140 // We don't support multiple range requests in one single URL request.
141 if (ranges.size() == 1U)
142 byte_range_ = ranges[0];
145 bool ServiceWorkerURLRequestJob::ReadRawData(
146 net::IOBuffer* buf, int buf_size, int *bytes_read) {
147 DCHECK(buf);
148 DCHECK_GE(buf_size, 0);
149 DCHECK(bytes_read);
150 DCHECK(waiting_stream_url_.is_empty());
151 if (stream_.get()) {
152 switch (stream_->ReadRawData(buf, buf_size, bytes_read)) {
153 case Stream::STREAM_HAS_DATA:
154 DCHECK_GT(*bytes_read, 0);
155 return true;
156 case Stream::STREAM_COMPLETE:
157 DCHECK(!*bytes_read);
158 return true;
159 case Stream::STREAM_EMPTY:
160 stream_pending_buffer_ = buf;
161 stream_pending_buffer_size_ = buf_size;
162 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
163 return false;
164 case Stream::STREAM_ABORTED:
165 // Handle this as connection reset.
166 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
167 net::ERR_CONNECTION_RESET));
168 return false;
170 NOTREACHED();
171 return false;
174 if (!blob_request_) {
175 *bytes_read = 0;
176 return true;
178 blob_request_->Read(buf, buf_size, bytes_read);
179 net::URLRequestStatus status = blob_request_->status();
180 SetStatus(status);
181 if (status.is_io_pending())
182 return false;
183 return status.is_success();
186 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
187 net::URLRequest* request,
188 const net::RedirectInfo& redirect_info,
189 bool* defer_redirect) {
190 NOTREACHED();
193 void ServiceWorkerURLRequestJob::OnAuthRequired(
194 net::URLRequest* request,
195 net::AuthChallengeInfo* auth_info) {
196 NOTREACHED();
199 void ServiceWorkerURLRequestJob::OnCertificateRequested(
200 net::URLRequest* request,
201 net::SSLCertRequestInfo* cert_request_info) {
202 NOTREACHED();
205 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
206 net::URLRequest* request,
207 const net::SSLInfo& ssl_info,
208 bool fatal) {
209 NOTREACHED();
212 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
213 bool* defer) {
214 NOTREACHED();
217 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
218 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
219 // the ServiceWorkerResponse.
220 response_time_ = base::Time::Now();
221 CommitResponseHeader();
224 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
225 int bytes_read) {
226 SetStatus(request->status());
227 if (!request->status().is_success()) {
228 NotifyDone(request->status());
229 return;
231 NotifyReadComplete(bytes_read);
232 if (bytes_read == 0)
233 NotifyDone(request->status());
236 void ServiceWorkerURLRequestJob::OnDataAvailable(Stream* stream) {
237 // Clear the IO_PENDING status.
238 SetStatus(net::URLRequestStatus());
239 // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
240 // operation waiting for IO completion.
241 if (!stream_pending_buffer_.get())
242 return;
244 // stream_pending_buffer_ is set to the IOBuffer instance provided to
245 // ReadRawData() by URLRequestJob.
247 int bytes_read = 0;
248 switch (stream_->ReadRawData(
249 stream_pending_buffer_.get(), stream_pending_buffer_size_, &bytes_read)) {
250 case Stream::STREAM_HAS_DATA:
251 DCHECK_GT(bytes_read, 0);
252 break;
253 case Stream::STREAM_COMPLETE:
254 // Calling NotifyReadComplete with 0 signals completion.
255 DCHECK(!bytes_read);
256 break;
257 case Stream::STREAM_EMPTY:
258 NOTREACHED();
259 break;
260 case Stream::STREAM_ABORTED:
261 // Handle this as connection reset.
262 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
263 net::ERR_CONNECTION_RESET));
264 break;
267 // Clear the buffers before notifying the read is complete, so that it is
268 // safe for the observer to read.
269 stream_pending_buffer_ = nullptr;
270 stream_pending_buffer_size_ = 0;
271 NotifyReadComplete(bytes_read);
274 void ServiceWorkerURLRequestJob::OnStreamRegistered(Stream* stream) {
275 StreamContext* stream_context =
276 GetStreamContextForResourceContext(resource_context_);
277 stream_context->registry()->RemoveRegisterObserver(waiting_stream_url_);
278 waiting_stream_url_ = GURL();
279 stream_ = stream;
280 stream_->SetReadObserver(this);
281 CommitResponseHeader();
284 const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
285 if (!http_response_info_)
286 return nullptr;
287 if (range_response_info_)
288 return range_response_info_.get();
289 return http_response_info_.get();
292 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
293 bool* was_fetched_via_service_worker,
294 bool* was_fallback_required_by_service_worker,
295 GURL* original_url_via_service_worker,
296 blink::WebServiceWorkerResponseType* response_type_via_service_worker,
297 base::TimeTicks* fetch_start_time,
298 base::TimeTicks* fetch_ready_time,
299 base::TimeTicks* fetch_end_time) const {
300 if (response_type_ != FORWARD_TO_SERVICE_WORKER) {
301 *was_fetched_via_service_worker = false;
302 *was_fallback_required_by_service_worker = false;
303 *original_url_via_service_worker = GURL();
304 *response_type_via_service_worker =
305 blink::WebServiceWorkerResponseTypeDefault;
306 return;
308 *was_fetched_via_service_worker = true;
309 *was_fallback_required_by_service_worker = fall_back_required_;
310 *original_url_via_service_worker = response_url_;
311 *response_type_via_service_worker = service_worker_response_type_;
312 *fetch_start_time = fetch_start_time_;
313 *fetch_ready_time = fetch_ready_time_;
314 *fetch_end_time = fetch_end_time_;
318 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
319 ClearStream();
322 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
323 if (is_started_ && response_type_ != NOT_DETERMINED) {
324 // Start asynchronously.
325 base::MessageLoop::current()->PostTask(
326 FROM_HERE,
327 base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
328 weak_factory_.GetWeakPtr()));
332 void ServiceWorkerURLRequestJob::StartRequest() {
333 switch (response_type_) {
334 case NOT_DETERMINED:
335 NOTREACHED();
336 return;
338 case FALLBACK_TO_NETWORK:
339 // Restart the request to create a new job. Our request handler will
340 // return nullptr, and the default job (which will hit network) should be
341 // created.
342 NotifyRestartRequired();
343 return;
345 case FORWARD_TO_SERVICE_WORKER:
346 DCHECK(provider_host_ && provider_host_->active_version());
347 DCHECK(!fetch_dispatcher_);
348 // Send a fetch event to the ServiceWorker associated to the
349 // provider_host.
350 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
351 CreateFetchRequest(),
352 provider_host_->active_version(),
353 base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent,
354 weak_factory_.GetWeakPtr()),
355 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
356 weak_factory_.GetWeakPtr())));
357 fetch_start_time_ = base::TimeTicks::Now();
358 load_timing_info_.send_start = fetch_start_time_;
359 fetch_dispatcher_->Run();
360 return;
363 NOTREACHED();
366 scoped_ptr<ServiceWorkerFetchRequest>
367 ServiceWorkerURLRequestJob::CreateFetchRequest() {
368 std::string blob_uuid;
369 uint64 blob_size = 0;
370 CreateRequestBodyBlob(&blob_uuid, &blob_size);
371 scoped_ptr<ServiceWorkerFetchRequest> request(
372 new ServiceWorkerFetchRequest());
373 request->mode = request_mode_;
374 request->request_context_type = request_context_type_;
375 request->frame_type = frame_type_;
376 request->url = request_->url();
377 request->method = request_->method();
378 const net::HttpRequestHeaders& headers = request_->extra_request_headers();
379 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();) {
380 if (ServiceWorkerContext::IsExcludedHeaderNameForFetchEvent(it.name()))
381 continue;
382 request->headers[it.name()] = it.value();
384 request->blob_uuid = blob_uuid;
385 request->blob_size = blob_size;
386 request->credentials_mode = credentials_mode_;
387 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
388 if (info) {
389 request->is_reload = ui::PageTransitionCoreTypeIs(
390 info->GetPageTransition(), ui::PAGE_TRANSITION_RELOAD);
391 request->referrer =
392 Referrer(GURL(request_->referrer()), info->GetReferrerPolicy());
393 } else {
394 CHECK(
395 request_->referrer_policy() ==
396 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
397 request->referrer =
398 Referrer(GURL(request_->referrer()), blink::WebReferrerPolicyDefault);
400 return request.Pass();
403 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string* blob_uuid,
404 uint64* blob_size) {
405 if (!body_.get() || !blob_storage_context_)
406 return false;
408 // To ensure the blobs stick around until the end of the reading.
409 ScopedVector<storage::BlobDataHandle> handles;
410 ScopedVector<storage::BlobDataSnapshot> snapshots;
411 // TODO(dmurph): Allow blobs to be added below, so that the context can
412 // efficiently re-use blob items for the new blob.
413 std::vector<const ResourceRequestBody::Element*> resolved_elements;
414 for (const ResourceRequestBody::Element& element : (*body_->elements())) {
415 if (element.type() != ResourceRequestBody::Element::TYPE_BLOB) {
416 resolved_elements.push_back(&element);
417 continue;
419 scoped_ptr<storage::BlobDataHandle> handle =
420 blob_storage_context_->GetBlobDataFromUUID(element.blob_uuid());
421 scoped_ptr<storage::BlobDataSnapshot> snapshot = handle->CreateSnapshot();
422 if (snapshot->items().empty())
423 continue;
424 const auto& items = snapshot->items();
425 for (const auto& item : items) {
426 DCHECK_NE(storage::DataElement::TYPE_BLOB, item->type());
427 resolved_elements.push_back(item->data_element_ptr());
429 handles.push_back(handle.release());
430 snapshots.push_back(snapshot.release());
433 const std::string uuid(base::GenerateGUID());
434 uint64 total_size = 0;
436 storage::BlobDataBuilder blob_builder(uuid);
437 for (size_t i = 0; i < resolved_elements.size(); ++i) {
438 const ResourceRequestBody::Element& element = *resolved_elements[i];
439 if (total_size != kuint64max && element.length() != kuint64max)
440 total_size += element.length();
441 else
442 total_size = kuint64max;
443 switch (element.type()) {
444 case ResourceRequestBody::Element::TYPE_BYTES:
445 blob_builder.AppendData(element.bytes(), element.length());
446 break;
447 case ResourceRequestBody::Element::TYPE_FILE:
448 blob_builder.AppendFile(element.path(), element.offset(),
449 element.length(),
450 element.expected_modification_time());
451 break;
452 case ResourceRequestBody::Element::TYPE_BLOB:
453 // Blob elements should be resolved beforehand.
454 NOTREACHED();
455 break;
456 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM:
457 blob_builder.AppendFileSystemFile(element.filesystem_url(),
458 element.offset(), element.length(),
459 element.expected_modification_time());
460 break;
461 default:
462 NOTIMPLEMENTED();
466 request_body_blob_data_handle_ =
467 blob_storage_context_->AddFinishedBlob(&blob_builder);
468 *blob_uuid = uuid;
469 *blob_size = total_size;
470 return true;
473 void ServiceWorkerURLRequestJob::DidPrepareFetchEvent() {
474 fetch_ready_time_ = base::TimeTicks::Now();
477 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
478 ServiceWorkerStatusCode status,
479 ServiceWorkerFetchEventResult fetch_result,
480 const ServiceWorkerResponse& response) {
481 fetch_dispatcher_.reset();
483 // Check if we're not orphaned.
484 if (!request())
485 return;
487 if (status != SERVICE_WORKER_OK) {
488 // Dispatching event has been failed, returns an error response.
489 // TODO(kinuko): Would be nice to log the error case.
490 DeliverErrorResponse();
491 return;
494 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
495 // When the request_mode is |CORS| or |CORS-with-forced-preflight| we can't
496 // simply fallback to the network in the browser process. It is because the
497 // CORS preflight logic is implemented in the renderer. So we returns a
498 // fall_back_required response to the renderer.
499 if (request_mode_ == FETCH_REQUEST_MODE_CORS ||
500 request_mode_ == FETCH_REQUEST_MODE_CORS_WITH_FORCED_PREFLIGHT) {
501 fall_back_required_ = true;
502 CreateResponseHeader(
503 400, "Service Worker Fallback Required", ServiceWorkerHeaderMap());
504 CommitResponseHeader();
505 return;
507 // Change the response type and restart the request to fallback to
508 // the network.
509 response_type_ = FALLBACK_TO_NETWORK;
510 NotifyRestartRequired();
511 return;
514 // We should have a response now.
515 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
517 // Treat a response whose status is 0 as a Network Error.
518 if (response.status_code == 0) {
519 NotifyDone(
520 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
521 return;
524 fetch_end_time_ = base::TimeTicks::Now();
525 load_timing_info_.send_end = fetch_end_time_;
527 // Creates a new HttpResponseInfo using the the ServiceWorker script's
528 // HttpResponseInfo to show HTTPS padlock.
529 // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
530 // ServiceWorker, we have to check the security level of the responses.
531 DCHECK(!http_response_info_);
532 const net::HttpResponseInfo* main_script_http_info =
533 provider_host_->active_version()->GetMainScriptHttpResponseInfo();
534 DCHECK(main_script_http_info);
535 http_response_info_.reset(new net::HttpResponseInfo(*main_script_http_info));
537 // Set up a request for reading the stream.
538 if (response.stream_url.is_valid()) {
539 DCHECK(response.blob_uuid.empty());
540 DCHECK(provider_host_->active_version());
541 streaming_version_ = provider_host_->active_version();
542 streaming_version_->AddStreamingURLRequestJob(this);
543 response_url_ = response.url;
544 service_worker_response_type_ = response.response_type;
545 CreateResponseHeader(
546 response.status_code, response.status_text, response.headers);
547 load_timing_info_.receive_headers_end = base::TimeTicks::Now();
548 StreamContext* stream_context =
549 GetStreamContextForResourceContext(resource_context_);
550 stream_ =
551 stream_context->registry()->GetStream(response.stream_url);
552 if (!stream_.get()) {
553 waiting_stream_url_ = response.stream_url;
554 // Wait for StreamHostMsg_StartBuilding message from the ServiceWorker.
555 stream_context->registry()->SetRegisterObserver(waiting_stream_url_,
556 this);
557 return;
559 stream_->SetReadObserver(this);
560 CommitResponseHeader();
561 return;
563 // Set up a request for reading the blob.
564 if (!response.blob_uuid.empty() && blob_storage_context_) {
565 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
566 blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid);
567 if (!blob_data_handle) {
568 // The renderer gave us a bad blob UUID.
569 DeliverErrorResponse();
570 return;
572 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
573 blob_data_handle.Pass(), request()->context(), this);
574 blob_request_->Start();
577 response_url_ = response.url;
578 service_worker_response_type_ = response.response_type;
579 CreateResponseHeader(
580 response.status_code, response.status_text, response.headers);
581 load_timing_info_.receive_headers_end = base::TimeTicks::Now();
582 if (!blob_request_)
583 CommitResponseHeader();
586 void ServiceWorkerURLRequestJob::CreateResponseHeader(
587 int status_code,
588 const std::string& status_text,
589 const ServiceWorkerHeaderMap& headers) {
590 // TODO(kinuko): If the response has an identifier to on-disk cache entry,
591 // pull response header from the disk.
592 std::string status_line(
593 base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str()));
594 status_line.push_back('\0');
595 http_response_headers_ = new net::HttpResponseHeaders(status_line);
596 for (ServiceWorkerHeaderMap::const_iterator it = headers.begin();
597 it != headers.end();
598 ++it) {
599 std::string header;
600 header.reserve(it->first.size() + 2 + it->second.size());
601 header.append(it->first);
602 header.append(": ");
603 header.append(it->second);
604 http_response_headers_->AddHeader(header);
608 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
609 if (!http_response_info_)
610 http_response_info_.reset(new net::HttpResponseInfo());
611 http_response_info_->headers.swap(http_response_headers_);
612 http_response_info_->vary_data = net::HttpVaryData();
613 http_response_info_->metadata = nullptr;
614 NotifyHeadersComplete();
617 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
618 // TODO(falken): Print an error to the console of the ServiceWorker and of
619 // the requesting page.
620 CreateResponseHeader(
621 500, "Service Worker Response Error", ServiceWorkerHeaderMap());
622 CommitResponseHeader();
625 void ServiceWorkerURLRequestJob::ClearStream() {
626 if (streaming_version_) {
627 streaming_version_->RemoveStreamingURLRequestJob(this);
628 streaming_version_ = nullptr;
630 if (stream_) {
631 stream_->RemoveReadObserver(this);
632 stream_->Abort();
633 stream_ = nullptr;
635 if (!waiting_stream_url_.is_empty()) {
636 StreamRegistry* stream_registry =
637 GetStreamContextForResourceContext(resource_context_)->registry();
638 stream_registry->RemoveRegisterObserver(waiting_stream_url_);
639 stream_registry->AbortPendingStream(waiting_stream_url_);
643 } // namespace content