Bug 1416879 - Part 4: FetchDriver needs to propagate write failures. r=baku

In the scenario where a ServiceWorker returns a pass-through fetch via
`evt.respondWith(fetch("underlying"))`, in order for the "underlying"
HTTP channel to be canceled when the outer HTTP channel is canceled,
FetchDriver's OnDataAvailable method needs to return an error when
the output pipe experiences an error.

Unfortunately, the contract for ReadSegments is effectively that it
returns NS_OK regardless of what the rv of the write handler returned,
so relying on the returned rv is insufficient.  And because various
Write*() methods will all fast-path to returning NS_OK if a count of 0
is passed, it's necessary to infer a closed/broken pipe by noticing
that we tried to write more than 0 bytes of data but 0 bytes were
written.  (This is safe because the pipe we write into was created
by FetchDriver::OnStartRequest which explicitly creates an infinite
pipe, so it's not possible for the write to fail due to lack of space
in the pipe.)
This commit is contained in:
Andrew Sutherland
2018-01-04 18:04:55 -05:00
parent 7540aec721
commit 4b8889da6a

View File

@@ -1087,25 +1087,37 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
}
}
uint32_t aRead;
// Needs to be initialized to 0 because in some cases nsStringInputStream may
// not write to aRead.
uint32_t aRead = 0;
MOZ_ASSERT(mResponse);
MOZ_ASSERT(mPipeOutputStream);
// From "Main Fetch" step 19: SRI-part2.
// Note: Avoid checking the hidden opaque body.
nsresult rv;
if (mResponse->Type() != ResponseType::Opaque &&
ShouldCheckSRI(mRequest, mResponse)) {
MOZ_ASSERT(mSRIDataVerifier);
SRIVerifierAndOutputHolder holder(mSRIDataVerifier, mPipeOutputStream);
nsresult rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
&holder, aCount, &aRead);
return rv;
rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
&holder, aCount, &aRead);
} else {
rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
}
nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
// If no data was read, it's possible the output stream is closed but the
// ReadSegments call followed its contract of returning NS_OK despite write
// errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
// taken together with ReadSegments' contract, because the pipe will just
// NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
// So we must just assume the pipe is broken.
if (aRead == 0 && aCount != 0) {
return NS_BASE_STREAM_CLOSED;
}
return rv;
}