Bug 1754004 - Part 7: Consistently normalize upload streams passed to HTTP channels, r=asuth,necko-reviewers,dragana

Unfortunately, upload streams used by necko have various odd behaviours
and requirements which happened to be usually preserved by the previous
IPC serialization logic, but were not consistently preserved. This
includes requiring the stream to be synchronous (as some consumers such
as WebExtensions and DevTools appear to read it assuming Available() is
the stream length), seekable (as it needs to be rewound in various
places), and cloneable (as the stream information is often handed out to
other components).

In addition, the WebExtension WebRequest code makes assumptions about
the specific topology of the input stream for optimization purposes,
meaning that nsMultiplexInputStreams need to be preserved.

The way this was previously handled was by copying the entire payload
into a nsStorageStream as an async operation. This happened very
infrequently in out test suite, however, and had some issues. It could
lead to data loss if the stream was a nsMIMEInputStream (as the metadata
would be lost), and would destroy the topology required by WebRequest.

This patch changes the code to instead manually walk and replace streams
in the input stream's data structure, to efficiently copy only the
required data, preserve the invariants, and make the type seekable
before AsyncOpen continues. This helps keep the complexity of the
invariants HTTPChannel depends on out of generic input stream handling
code.

In addition, due to how early this happens, it replaces the need for
PartiallySeekableInputStream which will be removed a later part.

Differential Revision: https://phabricator.services.mozilla.com/D141044
This commit is contained in:
Nika Layzell
2022-05-02 20:44:24 +00:00
parent 5c9a8765b4
commit 920f727ddb
10 changed files with 333 additions and 211 deletions

View File

@@ -491,24 +491,11 @@ bool HttpChannelParent::DoAsyncOpen(
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
if (stream) {
int64_t length;
if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
httpChannel->InternalSetUploadStreamLength(length >= 0 ? length : 0);
} else {
// Wait for the nputStreamLengthHelper::GetAsyncLength callback.
++mAsyncOpenBarrier;
// Let's resolve the size of the stream. The following operation is always
// async.
RefPtr<HttpChannelParent> self = this;
InputStreamLengthHelper::GetAsyncLength(stream, [self, httpChannel](
int64_t aLength) {
httpChannel->InternalSetUploadStreamLength(aLength >= 0 ? aLength : 0);
self->TryInvokeAsyncOpen(NS_OK);
});
rv = httpChannel->InternalSetUploadStream(stream);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
httpChannel->InternalSetUploadStream(stream);
httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
}
@@ -583,14 +570,6 @@ bool HttpChannelParent::DoAsyncOpen(
self->TryInvokeAsyncOpen(aStatus);
})
->Track(mRequest);
// The stream, received from the child process, must be cloneable and seekable
// in order to allow devtools to inspect its content.
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction("HttpChannelParent::EnsureUploadStreamIsCloneable",
[self]() { self->TryInvokeAsyncOpen(NS_OK); });
++mAsyncOpenBarrier;
mChannel->EnsureUploadStreamIsCloneable(r);
return true;
}