зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1203680 P3 Add a method to ensure an http upload stream is directly cloneable. r=mcmanus
This commit is contained in:
Родитель
02126738d3
Коммит
6195e9f02c
|
@ -6,8 +6,9 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
interface nsIRunnable;
|
||||
|
||||
[scriptable, uuid(62e6529a-5cf6-491a-82ef-b3a8273cdd19)]
|
||||
[scriptable, uuid(2f712b52-19c5-4e0c-9e8f-b5c7c3b67049)]
|
||||
interface nsIUploadChannel2 : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -47,7 +48,19 @@ interface nsIUploadChannel2 : nsISupports
|
|||
readonly attribute boolean uploadStreamHasHeaders;
|
||||
|
||||
/**
|
||||
* Clones the upload stream and returns an equivalent stream.
|
||||
* Ensure the upload stream, if any, is cloneable. This may involve
|
||||
* async copying, so a callback runnable must be provided. It will
|
||||
* invoked on the current thread when the upload stream is ready
|
||||
* for cloning. If the stream is already cloneable, then the callback
|
||||
* will be invoked synchronously.
|
||||
*/
|
||||
[noscript]
|
||||
void ensureUploadStreamIsCloneable(in nsIRunnable aCallback);
|
||||
|
||||
/**
|
||||
* Clones the upload stream. May return failure if the upload stream
|
||||
* is not cloneable. If this is not acceptable, use the
|
||||
* ensureUploadStreamIsCloneable() method first.
|
||||
*/
|
||||
[noscript]
|
||||
nsIInputStream cloneUploadStream();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIStorageStream.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIEncodedChannel.h"
|
||||
#include "nsIApplicationCacheChannel.h"
|
||||
|
@ -574,17 +575,98 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
EnsureStreamBuffered(nsCOMPtr<nsIInputStream>& aStream)
|
||||
namespace {
|
||||
|
||||
void
|
||||
CopyComplete(void* aClosure, nsresult aStatus) {
|
||||
// Called on the STS thread by NS_AsyncCopy
|
||||
auto channel = static_cast<HttpBaseChannel*>(aClosure);
|
||||
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArg<nsresult>(
|
||||
channel, &HttpBaseChannel::EnsureUploadStreamIsCloneableComplete, aStatus);
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::EnsureUploadStreamIsCloneable(nsIRunnable* aCallback)
|
||||
{
|
||||
if (!NS_InputStreamIsBuffered(aStream)) {
|
||||
nsCOMPtr<nsIInputStream> bufferedStream;
|
||||
nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
|
||||
aStream,
|
||||
4096);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
aStream.swap(bufferedStream);
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
|
||||
NS_ENSURE_ARG_POINTER(aCallback);
|
||||
|
||||
// We could in theory allow multiple callers to use this method,
|
||||
// but the complexity does not seem worth it yet. Just fail if
|
||||
// this is called more than once simultaneously.
|
||||
NS_ENSURE_FALSE(mUploadCloneableCallback, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// If the CloneUploadStream() will succeed, then synchronously invoke
|
||||
// the callback to indicate we're already cloneable.
|
||||
if (!mUploadStream || NS_InputStreamIsCloneable(mUploadStream)) {
|
||||
aCallback->Run();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStorageStream> storageStream;
|
||||
nsresult rv = NS_NewStorageStream(4096, UINT32_MAX,
|
||||
getter_AddRefs(storageStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> newUploadStream;
|
||||
rv = storageStream->NewInputStream(0, getter_AddRefs(newUploadStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> sink;
|
||||
rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> source;
|
||||
if (NS_InputStreamIsBuffered(mUploadStream)) {
|
||||
source = mUploadStream;
|
||||
} else {
|
||||
rv = NS_NewBufferedInputStream(getter_AddRefs(source), mUploadStream, 4096);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
|
||||
mUploadCloneableCallback = aCallback;
|
||||
|
||||
rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
|
||||
4096, // copy segment size
|
||||
CopyComplete, this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mUploadCloneableCallback = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Since we're consuming the old stream, replace it with the new
|
||||
// stream immediately.
|
||||
mUploadStream = newUploadStream;
|
||||
|
||||
// Explicity hold the stream alive until copying is complete. This will
|
||||
// be released in EnsureUploadStreamIsCloneableComplete().
|
||||
AddRef();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HttpBaseChannel::EnsureUploadStreamIsCloneableComplete(nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
|
||||
MOZ_ASSERT(mUploadCloneableCallback);
|
||||
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
mStatus = aStatus;
|
||||
}
|
||||
|
||||
mUploadCloneableCallback->Run();
|
||||
mUploadCloneableCallback = nullptr;
|
||||
|
||||
// Release the reference we grabbed in EnsureUploadStreamIsCloneable() now
|
||||
// that the copying is complete.
|
||||
Release();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -598,21 +680,9 @@ HttpBaseChannel::CloneUploadStream(nsIInputStream** aClonedStream)
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedStream;
|
||||
nsCOMPtr<nsIInputStream> replacementStream;
|
||||
nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream),
|
||||
getter_AddRefs(replacementStream));
|
||||
nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (replacementStream) {
|
||||
mUploadStream.swap(replacementStream);
|
||||
|
||||
// Ensure that the replacement stream is buffered.
|
||||
EnsureStreamBuffered(mUploadStream);
|
||||
}
|
||||
|
||||
// Ensure that the cloned stream is buffered.
|
||||
EnsureStreamBuffered(clonedStream);
|
||||
|
||||
clonedStream.forget(aClonedStream);
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -269,6 +269,10 @@ public: /* Necko internal use only... */
|
|||
nsresult DoApplyContentConversions(nsIStreamListener *aNextListener,
|
||||
nsIStreamListener **aNewNextListener);
|
||||
|
||||
// Callback on main thread when NS_AsyncCopy() is finished populating
|
||||
// the new mUploadStream.
|
||||
void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
|
||||
|
||||
protected:
|
||||
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
|
||||
|
||||
|
@ -329,6 +333,7 @@ protected:
|
|||
|
||||
nsHttpRequestHead mRequestHead;
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
nsCOMPtr<nsIRunnable> mUploadCloneableCallback;
|
||||
nsAutoPtr<nsHttpResponseHead> mResponseHead;
|
||||
nsRefPtr<nsHttpConnectionInfo> mConnectionInfo;
|
||||
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
||||
|
|
Загрузка…
Ссылка в новой задаче