Commit b6d920e2 authored by Arthur Sonzogni's avatar Arthur Sonzogni Committed by Commit Bot

Service worker: match OnStartLoadingResponseBody with OnReceiveResponse.

Some URLLoaders aren't always sending a response's body datapipe to their
client after sending URLLoaderClient::OnReceiveResponse(response_head).
It even happens to send URLLoaderClient::OnComplete(net::OK) after that.

Most of the time, they do not send a datapipe, because the response's
body is empty.

The goal is to align URLLoaders so that they will always send a data
pipe by using OnStartLoadingResponseBody(response_body).

This CL is about ServiceWorker's URLLoaders.

This CL is a prerequisite for:
https://chromium-review.googlesource.com/c/chromium/src/+/1172290

Bug: 826868, 831155
Change-Id: Ib28fc2067240d611ec149099b5365f70610a9369
Reviewed-on: https://chromium-review.googlesource.com/c/1323109
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607273}
parent 2ee94ddd
...@@ -127,11 +127,11 @@ void ServiceWorkerNavigationLoader::FallbackToNetwork() { ...@@ -127,11 +127,11 @@ void ServiceWorkerNavigationLoader::FallbackToNetwork() {
// The URLJobWrapper only calls this if this loader never intercepted the // The URLJobWrapper only calls this if this loader never intercepted the
// request. Fallback to network after interception uses |fallback_callback_| // request. Fallback to network after interception uses |fallback_callback_|
// instead. // instead.
DCHECK_EQ(status_, Status::kNotStarted);
DCHECK_EQ(response_type_, ResponseType::NOT_DETERMINED); DCHECK_EQ(response_type_, ResponseType::NOT_DETERMINED);
response_type_ = ResponseType::FALLBACK_TO_NETWORK; response_type_ = ResponseType::FALLBACK_TO_NETWORK;
TransitionToStatus(Status::kCompleted); TransitionToStatus(Status::kCompleted);
std::move(loader_callback_).Run({}); std::move(loader_callback_).Run({});
} }
...@@ -249,6 +249,20 @@ void ServiceWorkerNavigationLoader::CommitResponseHeaders() { ...@@ -249,6 +249,20 @@ void ServiceWorkerNavigationLoader::CommitResponseHeaders() {
url_loader_client_->OnReceiveResponse(response_head_); url_loader_client_->OnReceiveResponse(response_head_);
} }
void ServiceWorkerNavigationLoader::CommitResponseBody(
mojo::ScopedDataPipeConsumerHandle response_body) {
TransitionToStatus(Status::kSentBody);
url_loader_client_->OnStartLoadingResponseBody(std::move(response_body));
}
void ServiceWorkerNavigationLoader::CommitResponseBodyEmpty() {
TransitionToStatus(Status::kSentBody);
mojo::DataPipe pipe;
url_loader_client_->OnStartLoadingResponseBody(
std::move(pipe.consumer_handle));
// pipe.producer_handle is closed here.
}
void ServiceWorkerNavigationLoader::CommitCompleted(int error_code) { void ServiceWorkerNavigationLoader::CommitCompleted(int error_code) {
TRACE_EVENT_WITH_FLOW1("ServiceWorker", TRACE_EVENT_WITH_FLOW1("ServiceWorker",
"ServiceWorkerNavigationLoader::CommitCompleted", this, "ServiceWorkerNavigationLoader::CommitCompleted", this,
...@@ -414,8 +428,7 @@ void ServiceWorkerNavigationLoader::StartResponse( ...@@ -414,8 +428,7 @@ void ServiceWorkerNavigationLoader::StartResponse(
"result", "stream response"); "result", "stream response");
stream_waiter_ = std::make_unique<StreamWaiter>( stream_waiter_ = std::make_unique<StreamWaiter>(
this, std::move(version), std::move(body_as_stream->callback_request)); this, std::move(version), std::move(body_as_stream->callback_request));
url_loader_client_->OnStartLoadingResponseBody( CommitResponseBody(std::move(body_as_stream->stream));
std::move(body_as_stream->stream));
// StreamWaiter will call CommitCompleted() when done. // StreamWaiter will call CommitCompleted() when done.
return; return;
} }
...@@ -431,6 +444,7 @@ void ServiceWorkerNavigationLoader::StartResponse( ...@@ -431,6 +444,7 @@ void ServiceWorkerNavigationLoader::StartResponse(
weak_factory_.GetWeakPtr()), weak_factory_.GetWeakPtr()),
&data_pipe); &data_pipe);
if (error != net::OK) { if (error != net::OK) {
CommitResponseBodyEmpty();
CommitCompleted(error); CommitCompleted(error);
return; return;
} }
...@@ -439,7 +453,7 @@ void ServiceWorkerNavigationLoader::StartResponse( ...@@ -439,7 +453,7 @@ void ServiceWorkerNavigationLoader::StartResponse(
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", "blob response"); "result", "blob response");
url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe)); CommitResponseBody(std::move(data_pipe));
// We continue in OnBlobReadingComplete(). // We continue in OnBlobReadingComplete().
return; return;
} }
...@@ -449,7 +463,7 @@ void ServiceWorkerNavigationLoader::StartResponse( ...@@ -449,7 +463,7 @@ void ServiceWorkerNavigationLoader::StartResponse(
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"result", "no body"); "result", "no body");
// The response has no body. CommitResponseBodyEmpty();
CommitCompleted(net::OK); CommitCompleted(net::OK);
} }
...@@ -589,14 +603,17 @@ void ServiceWorkerNavigationLoader::TransitionToStatus(Status new_status) { ...@@ -589,14 +603,17 @@ void ServiceWorkerNavigationLoader::TransitionToStatus(Status new_status) {
case Status::kSentHeader: case Status::kSentHeader:
DCHECK_EQ(status_, Status::kStarted); DCHECK_EQ(status_, Status::kStarted);
break; break;
case Status::kSentBody:
DCHECK_EQ(status_, Status::kSentHeader);
break;
case Status::kCompleted: case Status::kCompleted:
// kNotStarted -> kCompleted happens on network fallback before DCHECK(
// interception. // Network fallback before interception.
// kStarted -> kCompleted happens on error or network fallback after status_ == Status::kNotStarted ||
// interception. // Network fallback after interception.
// kSentHeader -> kCompleted happens in the success case or error status_ == Status::kStarted ||
// while sending the body. // Success case or error while sending the response's body.
DCHECK_NE(status_, Status::kCompleted); status_ == Status::kSentBody);
break; break;
} }
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
......
...@@ -106,9 +106,11 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader ...@@ -106,9 +106,11 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader
// |binding_| is bound and the fetch event is being dispatched to the // |binding_| is bound and the fetch event is being dispatched to the
// service worker. // service worker.
kStarted, kStarted,
// The response head has been sent to |url_loader_client_|. The response // The response head has been sent to |url_loader_client_|.
// body is being streamed.
kSentHeader, kSentHeader,
// The data pipe for the response body has been sent to
// |url_loader_client_|. The body is being written to the pipe.
kSentBody,
// OnComplete() was called on |url_loader_client_|, or fallback to network // OnComplete() was called on |url_loader_client_|, or fallback to network
// occurred so the request was not handled. // occurred so the request was not handled.
kCompleted, kCompleted,
...@@ -134,6 +136,12 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader ...@@ -134,6 +136,12 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader
// Calls url_loader_client_->OnReceiveResponse() with |response_head_|. // Calls url_loader_client_->OnReceiveResponse() with |response_head_|.
void CommitResponseHeaders(); void CommitResponseHeaders();
// Calls url_loader_client_->OnStartLoadingResponseBody() with
// |response_body| or with an empty data pipe.
void CommitResponseBody(mojo::ScopedDataPipeConsumerHandle response_body);
void CommitResponseBodyEmpty();
// Calls url_loader_client_->OnComplete(). // Calls url_loader_client_->OnComplete().
void CommitCompleted(int error_code); void CommitCompleted(int error_code);
......
...@@ -200,8 +200,7 @@ void ServiceWorkerSubresourceLoader::StartRequest( ...@@ -200,8 +200,7 @@ void ServiceWorkerSubresourceLoader::StartRequest(
TRACE_ID_WITH_SCOPE(kServiceWorkerSubresourceLoaderScope, TRACE_ID_WITH_SCOPE(kServiceWorkerSubresourceLoaderScope,
TRACE_ID_LOCAL(request_id_)), TRACE_ID_LOCAL(request_id_)),
TRACE_EVENT_FLAG_FLOW_OUT, "url", resource_request.url.spec()); TRACE_EVENT_FLAG_FLOW_OUT, "url", resource_request.url.spec());
DCHECK_EQ(Status::kNotStarted, status_); TransitionToStatus(Status::kStarted);
status_ = Status::kStarted;
DCHECK(!ServiceWorkerUtils::IsMainResourceType( DCHECK(!ServiceWorkerUtils::IsMainResourceType(
static_cast<ResourceType>(resource_request.resource_type))); static_cast<ResourceType>(resource_request.resource_type)));
...@@ -408,6 +407,7 @@ void ServiceWorkerSubresourceLoader::OnFallback( ...@@ -408,6 +407,7 @@ void ServiceWorkerSubresourceLoader::OnFallback(
response_head_.was_fetched_via_service_worker = true; response_head_.was_fetched_via_service_worker = true;
response_head_.was_fallback_required_by_service_worker = true; response_head_.was_fallback_required_by_service_worker = true;
CommitResponseHeaders(); CommitResponseHeaders();
CommitResponseBodyEmpty();
CommitCompleted(net::OK); CommitCompleted(net::OK);
return; return;
} }
...@@ -479,8 +479,7 @@ void ServiceWorkerSubresourceLoader::StartResponse( ...@@ -479,8 +479,7 @@ void ServiceWorkerSubresourceLoader::StartResponse(
} }
response_head_.encoded_data_length = 0; response_head_.encoded_data_length = 0;
url_loader_client_->OnReceiveRedirect(*redirect_info_, response_head_); url_loader_client_->OnReceiveRedirect(*redirect_info_, response_head_);
// Set status to complete, but we expect to restart in FollowRedirect. TransitionToStatus(Status::kSentRedirect);
status_ = Status::kCompleted;
return; return;
} }
...@@ -493,8 +492,7 @@ void ServiceWorkerSubresourceLoader::StartResponse( ...@@ -493,8 +492,7 @@ void ServiceWorkerSubresourceLoader::StartResponse(
DCHECK(url_loader_client_.is_bound()); DCHECK(url_loader_client_.is_bound());
stream_waiter_ = std::make_unique<StreamWaiter>( stream_waiter_ = std::make_unique<StreamWaiter>(
this, std::move(body_as_stream->callback_request)); this, std::move(body_as_stream->callback_request));
url_loader_client_->OnStartLoadingResponseBody( CommitResponseBody(std::move(body_as_stream->stream));
std::move(body_as_stream->stream));
return; return;
} }
...@@ -507,13 +505,14 @@ void ServiceWorkerSubresourceLoader::StartResponse( ...@@ -507,13 +505,14 @@ void ServiceWorkerSubresourceLoader::StartResponse(
body_as_blob_size_ = response->blob->size; body_as_blob_size_ = response->blob->size;
// If parallel reading is enabled, then start reading the body blob // If parallel reading is enabled, then start reading the body blob
// immediately. This will allow the body to start buffering in the // immediately. This will allow the body to start buffering in the
// pipe while the side data is read. // pipe while the side data is read.
mojo::ScopedDataPipeConsumerHandle data_pipe; mojo::ScopedDataPipeConsumerHandle data_pipe;
if (base::FeatureList::IsEnabled( if (base::FeatureList::IsEnabled(
blink::features::kServiceWorkerParallelSideDataReading)) { blink::features::kServiceWorkerParallelSideDataReading)) {
int error = StartBlobReading(&data_pipe); int error = StartBlobReading(&data_pipe);
if (error != net::OK) { if (error != net::OK) {
CommitResponseBodyEmpty();
CommitCompleted(error); CommitCompleted(error);
return; return;
} }
...@@ -525,18 +524,31 @@ void ServiceWorkerSubresourceLoader::StartResponse( ...@@ -525,18 +524,31 @@ void ServiceWorkerSubresourceLoader::StartResponse(
return; return;
} }
// The response has no body. CommitResponseBodyEmpty();
CommitCompleted(net::OK); CommitCompleted(net::OK);
} }
void ServiceWorkerSubresourceLoader::CommitResponseHeaders() { void ServiceWorkerSubresourceLoader::CommitResponseHeaders() {
DCHECK_EQ(Status::kStarted, status_); TransitionToStatus(Status::kSentHeader);
DCHECK(url_loader_client_.is_bound()); DCHECK(url_loader_client_.is_bound());
status_ = Status::kSentHeader;
// TODO(kinuko): Fill the ssl_info. // TODO(kinuko): Fill the ssl_info.
url_loader_client_->OnReceiveResponse(response_head_); url_loader_client_->OnReceiveResponse(response_head_);
} }
void ServiceWorkerSubresourceLoader::CommitResponseBody(
mojo::ScopedDataPipeConsumerHandle response_body) {
TransitionToStatus(Status::kSentBody);
url_loader_client_->OnStartLoadingResponseBody(std::move(response_body));
}
void ServiceWorkerSubresourceLoader::CommitResponseBodyEmpty() {
TransitionToStatus(Status::kSentBody);
mojo::DataPipe pipe;
url_loader_client_->OnStartLoadingResponseBody(
std::move(pipe.consumer_handle));
// pipe.producer_handle is closed here.
}
void ServiceWorkerSubresourceLoader::CommitCompleted(int error_code) { void ServiceWorkerSubresourceLoader::CommitCompleted(int error_code) {
TRACE_EVENT_WITH_FLOW1( TRACE_EVENT_WITH_FLOW1(
"ServiceWorker", "ServiceWorkerSubresourceLoader::CommitCompleted", "ServiceWorker", "ServiceWorkerSubresourceLoader::CommitCompleted",
...@@ -547,11 +559,10 @@ void ServiceWorkerSubresourceLoader::CommitCompleted(int error_code) { ...@@ -547,11 +559,10 @@ void ServiceWorkerSubresourceLoader::CommitCompleted(int error_code) {
if (error_code == net::OK) if (error_code == net::OK)
RecordTimingMetrics(true /* handled */); RecordTimingMetrics(true /* handled */);
DCHECK_LT(status_, Status::kCompleted); TransitionToStatus(Status::kCompleted);
DCHECK(url_loader_client_.is_bound()); DCHECK(url_loader_client_.is_bound());
body_as_blob_.reset(); body_as_blob_.reset();
stream_waiter_.reset(); stream_waiter_.reset();
status_ = Status::kCompleted;
network::URLLoaderCompletionStatus status; network::URLLoaderCompletionStatus status;
status.error_code = error_code; status.error_code = error_code;
status.completion_time = base::TimeTicks::Now(); status.completion_time = base::TimeTicks::Now();
...@@ -651,7 +662,7 @@ void ServiceWorkerSubresourceLoader::FollowRedirect( ...@@ -651,7 +662,7 @@ void ServiceWorkerSubresourceLoader::FollowRedirect(
resource_request_.referrer_policy = redirect_info_->new_referrer_policy; resource_request_.referrer_policy = redirect_info_->new_referrer_policy;
// Restart the request. // Restart the request.
status_ = Status::kNotStarted; TransitionToStatus(Status::kNotStarted);
redirect_info_.reset(); redirect_info_.reset();
response_callback_binding_.Close(); response_callback_binding_.Close();
StartRequest(resource_request_); StartRequest(resource_request_);
...@@ -726,7 +737,7 @@ void ServiceWorkerSubresourceLoader::OnBlobSideDataReadingComplete( ...@@ -726,7 +737,7 @@ void ServiceWorkerSubresourceLoader::OnBlobSideDataReadingComplete(
"ServiceWorker.SubresourceNotifyStartLoadingResponseBodyDelay", delay); "ServiceWorker.SubresourceNotifyStartLoadingResponseBodyDelay", delay);
DCHECK(data_pipe.is_valid()); DCHECK(data_pipe.is_valid());
url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe)); CommitResponseBody(std::move(data_pipe));
// If the blob reading completed before the side data reading, then we // If the blob reading completed before the side data reading, then we
// must manually finalize the blob reading now. // must manually finalize the blob reading now.
...@@ -817,4 +828,37 @@ void ServiceWorkerSubresourceLoaderFactory::OnConnectionError() { ...@@ -817,4 +828,37 @@ void ServiceWorkerSubresourceLoaderFactory::OnConnectionError() {
delete this; delete this;
} }
void ServiceWorkerSubresourceLoader::TransitionToStatus(Status new_status) {
#if DCHECK_IS_ON()
switch (new_status) {
case Status::kNotStarted:
DCHECK_EQ(status_, Status::kSentRedirect);
break;
case Status::kStarted:
DCHECK_EQ(status_, Status::kNotStarted);
break;
case Status::kSentRedirect:
DCHECK_EQ(status_, Status::kStarted);
break;
case Status::kSentHeader:
DCHECK_EQ(status_, Status::kStarted);
break;
case Status::kSentBody:
DCHECK_EQ(status_, Status::kSentHeader);
break;
case Status::kCompleted:
DCHECK(
// Network fallback before interception.
status_ == Status::kNotStarted ||
// Network fallback after interception.
status_ == Status::kStarted ||
// Success case or error while sending the response's body.
status_ == Status::kSentBody);
break;
}
#endif // DCHECK_IS_ON()
status_ = new_status;
}
} // namespace content } // namespace content
...@@ -61,6 +61,22 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader ...@@ -61,6 +61,22 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader
private: private:
class StreamWaiter; class StreamWaiter;
enum class Status {
kNotStarted,
// |binding_| is bound and the fetch event is being dispatched to the
// service worker.
kStarted,
// A redirect happened, waiting for FollowRedirect().
kSentRedirect,
// The response head has been sent to |url_loader_client_|.
kSentHeader,
// The data pipe for the response body has been sent to
// |url_loader_client_|. The body is being written to the pipe.
kSentBody,
// OnComplete() was called on |url_loader_client_|, or fallback to network
// occurred so the request was not handled.
kCompleted,
};
void OnConnectionError(); void OnConnectionError();
...@@ -108,6 +124,12 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader ...@@ -108,6 +124,12 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader
// Calls url_loader_client_->OnReceiveResponse() with |response_head_|. // Calls url_loader_client_->OnReceiveResponse() with |response_head_|.
void CommitResponseHeaders(); void CommitResponseHeaders();
// Calls url_loader_client_->OnStartLoadingResponseBody() with
// |response_body| or with an empty data pipe.
void CommitResponseBody(mojo::ScopedDataPipeConsumerHandle response_body);
void CommitResponseBodyEmpty();
// Calls url_loader_client_->OnComplete(). Expected to be called after // Calls url_loader_client_->OnComplete(). Expected to be called after
// CommitResponseHeaders (i.e. status_ == kSentHeader). // CommitResponseHeaders (i.e. status_ == kSentHeader).
void CommitCompleted(int error_code); void CommitCompleted(int error_code);
...@@ -117,6 +139,8 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader ...@@ -117,6 +139,8 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader
// occurred. |handled| is true when a fetch handler handled a request. // occurred. |handled| is true when a fetch handler handled a request.
void RecordTimingMetrics(bool handled); void RecordTimingMetrics(bool handled);
void TransitionToStatus(Status new_status);
network::ResourceResponseHead response_head_; network::ResourceResponseHead response_head_;
base::Optional<net::RedirectInfo> redirect_info_; base::Optional<net::RedirectInfo> redirect_info_;
int redirect_limit_; int redirect_limit_;
...@@ -159,12 +183,6 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader ...@@ -159,12 +183,6 @@ class CONTENT_EXPORT ServiceWorkerSubresourceLoader
// For network fallback. // For network fallback.
scoped_refptr<network::SharedURLLoaderFactory> fallback_factory_; scoped_refptr<network::SharedURLLoaderFactory> fallback_factory_;
enum class Status {
kNotStarted,
kStarted,
kSentHeader,
kCompleted,
};
Status status_ = Status::kNotStarted; Status status_ = Status::kNotStarted;
// The task runner where this loader is running. // The task runner where this loader is running.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment