Skip to content

Commit

Permalink
Bug 1362388 - dont let File backed http requests find size on main th…
Browse files Browse the repository at this point in the history
…read r=nwgh

MozReview-Commit-ID: AFM4R0M7dmj

--HG--
extra : rebase_source : 01d3020eb952ec286be30b937476161a403215ff
  • Loading branch information
mcmanus committed May 5, 2017
1 parent 8a46470 commit eab0a00
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 18 deletions.
6 changes: 6 additions & 0 deletions ipc/glue/IPCStreamDestination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ class IPCStreamDestination::DelayedStartInputStream final
return stream->Init(aStream, aBufferSize);
}

NS_IMETHODIMP
GetData(nsIInputStream **aResult) override
{
return NS_ERROR_NOT_IMPLEMENTED;
}

void
MaybeStartReading();

Expand Down
27 changes: 27 additions & 0 deletions netwerk/base/nsBufferedStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ nsBufferedStream::SetEOF()
return rv;
}

nsresult
nsBufferedStream::GetData(nsISupports **aResult)
{
nsCOMPtr<nsISupports> rv(mStream);
*aResult = rv.forget().take();
return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// nsBufferedInputStream

Expand Down Expand Up @@ -679,6 +687,16 @@ nsBufferedInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
return callback->OnInputStreamReady(this);
}

NS_IMETHODIMP
nsBufferedInputStream::GetData(nsIInputStream **aResult)
{
nsCOMPtr<nsISupports> stream;
nsBufferedStream::GetData(getter_AddRefs(stream));
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream);
*aResult = inputStream.forget().take();
return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// nsBufferedOutputStream

Expand Down Expand Up @@ -1033,6 +1051,15 @@ nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
return NS_OK;
}

NS_IMETHODIMP
nsBufferedOutputStream::GetData(nsIOutputStream **aResult)
{
nsCOMPtr<nsISupports> stream;
nsBufferedStream::GetData(getter_AddRefs(stream));
nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(stream);
*aResult = outputStream.forget().take();
return NS_OK;
}
#undef METER

////////////////////////////////////////////////////////////////////////////////
1 change: 1 addition & 0 deletions netwerk/base/nsBufferedStreams.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class nsBufferedStream : public nsISeekableStream
virtual ~nsBufferedStream();

nsresult Init(nsISupports* stream, uint32_t bufferSize);
nsresult GetData(nsISupports **aResult);
NS_IMETHOD Fill() = 0;
NS_IMETHOD Flush() = 0;

Expand Down
10 changes: 10 additions & 0 deletions netwerk/base/nsIBufferedStreams.idl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ interface nsIBufferedInputStream : nsIInputStream
*/
void init(in nsIInputStream fillFromStream,
in unsigned long bufferSize);

/**
* Get the wrapped data stream
*/
readonly attribute nsIInputStream data;
};

/**
Expand All @@ -34,4 +39,9 @@ interface nsIBufferedOutputStream : nsIOutputStream
*/
void init(in nsIOutputStream sinkToStream,
in unsigned long bufferSize);

/**
* Get the wrapped data stream
*/
readonly attribute nsIOutputStream data;
};
10 changes: 10 additions & 0 deletions netwerk/base/nsIStreamTransportService.idl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
interface nsITransport;
interface nsIInputStream;
interface nsIOutputStream;
interface nsIInputAvailableCallback;

/**
* This service read/writes a stream on a background thread.
Expand Down Expand Up @@ -42,6 +43,8 @@ interface nsIStreamTransportService : nsISupports
in long long aReadLimit,
in boolean aCloseWhenDone);

void InputAvailable(in nsIInputStream aStream,
in nsIInputAvailableCallback aCallback);
/**
* CreateOutputTransport
*
Expand All @@ -66,3 +69,10 @@ interface nsIStreamTransportService : nsISupports
in long long aWriteLimit,
in boolean aCloseWhenDone);
};

[builtinclass, uuid(ff2da731-44d0-4dd9-8236-c99387fec721)]
interface nsIInputAvailableCallback : nsISupports
{
void onInputAvailableComplete(in unsigned long long available,
in nsresult available_return_code);
};
58 changes: 58 additions & 0 deletions netwerk/base/nsStreamTransportService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,5 +559,63 @@ nsStreamTransportService::Observe(nsISupports *subject, const char *topic,
return NS_OK;
}

class AvailableEvent final : public Runnable
{
public:
AvailableEvent(nsIInputStream *stream,
nsIInputAvailableCallback *callback)
: mStream(stream)
, mCallback(callback)
, mDoingCallback(false)
{
mCallbackTarget = NS_GetCurrentThread();
}

NS_IMETHOD Run() override
{
if (mDoingCallback) {
// pong
mCallback->OnInputAvailableComplete(mSize, mResultForCallback);
mCallback = nullptr;
} else {
// ping
mResultForCallback = mStream->Available(&mSize);
mStream = nullptr;
mDoingCallback = true;

nsCOMPtr<nsIRunnable> event(this); // overly cute
mCallbackTarget->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
mCallbackTarget = nullptr;
}
return NS_OK;
}

private:
virtual ~AvailableEvent() { }

nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsIInputAvailableCallback> mCallback;
nsCOMPtr<nsIEventTarget> mCallbackTarget;
bool mDoingCallback;
uint64_t mSize;
nsresult mResultForCallback;
};

NS_IMETHODIMP
nsStreamTransportService::InputAvailable(nsIInputStream *stream,
nsIInputAvailableCallback *callback)
{
nsCOMPtr<nsIThreadPool> pool;
{
mozilla::MutexAutoLock lock(mShutdownLock);
if (mIsShutdown) {
return NS_ERROR_NOT_INITIALIZED;
}
pool = mPool;
}
nsCOMPtr<nsIRunnable> event = new AvailableEvent(stream, callback);
return pool->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
}

} // namespace net
} // namespace mozilla
119 changes: 118 additions & 1 deletion netwerk/protocol/http/nsHttpChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
#include "CacheStorageService.h"
#include "HttpChannelParent.h"
#include "nsIThrottlingService.h"
#include "nsIBufferedStreams.h"
#include "nsIFileStreams.h"
#include "nsIMIMEInputStream.h"
#include "nsIMultiplexInputStream.h"

#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
Expand Down Expand Up @@ -288,6 +292,8 @@ nsHttpChannel::nsHttpChannel()
, mStronglyFramed(false)
, mUsedNetwork(0)
, mAuthConnectionRestartable(0)
, mReqContentLengthDetermined(0)
, mReqContentLength(0U)
, mPushedStream(nullptr)
, mLocalBlocklist(false)
, mWarningReporter(nullptr)
Expand Down Expand Up @@ -508,9 +514,118 @@ nsHttpChannel::TryHSTSPriming()
return ContinueConnect();
}

// nsIInputAvailableCallback (nsIStreamTransportService.idl)
NS_IMETHODIMP
nsHttpChannel::OnInputAvailableComplete(uint64_t size, nsresult status)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
LOG(("nsHttpChannel::OnInputAvailableComplete %p %" PRIx32 "\n",
this, static_cast<uint32_t>(status)));
if (NS_SUCCEEDED(status)) {
mReqContentLength = size;
} else {
// fall back to synchronous on the error path. should not happen.
if (NS_SUCCEEDED(mUploadStream->Available(&size))) {
mReqContentLength = size;
}
}

LOG(("nsHttpChannel::DetermineContentLength %p from sts\n", this));
mReqContentLengthDetermined = 1;
nsresult rv = mCanceled ? mStatus : ContinueConnect();
if (NS_FAILED(rv)) {
CloseCacheEntry(false);
Unused << AsyncAbort(rv);
}
return NS_OK;
}

// nsIFileStream needs to be sent to a worker thread
// to do Available() as it may cause disk/IO. Unfortunately
// we have to look at the streams wrapped by a few other
// abstractions to be sure.
static
bool isFileStream(nsIInputStream *stream)
{
if (!stream) {
return false;
}

nsCOMPtr<nsIFileInputStream> fileStream = do_QueryInterface(stream);
if (fileStream) {
return true;
}

nsCOMPtr<nsIBufferedInputStream> bufferedStream = do_QueryInterface(stream);
if (bufferedStream) {
nsCOMPtr<nsIInputStream> innerStream;
if (NS_SUCCEEDED(bufferedStream->GetData(getter_AddRefs(innerStream)))) {
return isFileStream(innerStream);
}
}

nsCOMPtr<nsIMIMEInputStream> mimeStream = do_QueryInterface(stream);
if (mimeStream) {
nsCOMPtr<nsIInputStream> innerStream;
if (NS_SUCCEEDED(mimeStream->GetData(getter_AddRefs(innerStream)))) {
return isFileStream(innerStream);
}
}

nsCOMPtr<nsIMultiplexInputStream> muxStream = do_QueryInterface(stream);
uint32_t muxCount = 0;
if (muxStream) {
muxStream->GetCount(&muxCount);
for (uint32_t i = 0; i < muxCount; ++i) {
nsCOMPtr<nsIInputStream> subStream;
if (NS_SUCCEEDED(muxStream->GetStream(i, getter_AddRefs(subStream))) &&
isFileStream(subStream)) {
return true;
}
}
}

return false;
}

void
nsHttpChannel::DetermineContentLength()
{
nsCOMPtr<nsIStreamTransportService> sts(services::GetStreamTransportService());

if (!mUploadStream || !sts) {
LOG(("nsHttpChannel::DetermineContentLength %p no body\n", this));
mReqContentLength = 0U;
mReqContentLengthDetermined = 1;
return;
}

if (!isFileStream(mUploadStream)) {
mUploadStream->Available(&mReqContentLength);
LOG(("nsHttpChannel::DetermineContentLength %p from mem\n", this));
mReqContentLengthDetermined = 1;
return;
}

LOG(("nsHttpChannel::DetermineContentLength Async [this=%p]\n", this));
sts->InputAvailable(mUploadStream, this);
}

nsresult
nsHttpChannel::ContinueConnect()
{
// If we have a request body that is going to require bouncing to the STS
// in order to determine the content-length as doing it on the main thread
// will incur file IO some of the time.
if (!mReqContentLengthDetermined) {
// C-L might be determined sync or async. Sync will set
// mReqContentLengthDetermined to true in DetermineContentLength()
DetermineContentLength();
}
if (!mReqContentLengthDetermined) {
return NS_OK;
}

// If we have had HSTS priming, we need to reevaluate whether we need
// a CORS preflight. Bug: 1272440
// If we need to start a CORS preflight, do it now!
Expand Down Expand Up @@ -1004,7 +1119,8 @@ nsHttpChannel::SetupTransaction()

nsCOMPtr<nsIAsyncInputStream> responseStream;
rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
mUploadStream, mUploadStreamHasHeaders,
mUploadStream, mReqContentLength,
mUploadStreamHasHeaders,
NS_GetCurrentThread(), callbacks, this,
mTopLevelOuterContentWindowId,
getter_AddRefs(responseStream));
Expand Down Expand Up @@ -5690,6 +5806,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
NS_INTERFACE_MAP_ENTRY(nsIInputAvailableCallback)
NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
Expand Down
9 changes: 9 additions & 0 deletions netwerk/protocol/http/nsHttpChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "nsIApplicationCacheChannel.h"
#include "nsIChannelWithDivertableParentListener.h"
#include "nsIProtocolProxyCallback.h"
#include "nsIStreamTransportService.h"
#include "nsIHttpAuthenticableChannel.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIThreadRetargetableRequest.h"
Expand Down Expand Up @@ -69,6 +70,7 @@ class nsHttpChannel final : public HttpBaseChannel
, public nsICacheEntryOpenCallback
, public nsITransportEventSink
, public nsIProtocolProxyCallback
, public nsIInputAvailableCallback
, public nsIHttpAuthenticableChannel
, public nsIApplicationCacheChannel
, public nsIAsyncVerifyRedirectCallback
Expand All @@ -92,6 +94,7 @@ class nsHttpChannel final : public HttpBaseChannel
NS_DECL_NSICACHEENTRYOPENCALLBACK
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIPROTOCOLPROXYCALLBACK
NS_DECL_NSIINPUTAVAILABLECALLBACK
NS_DECL_NSIPROXIEDCHANNEL
NS_DECL_NSIAPPLICATIONCACHECONTAINER
NS_DECL_NSIAPPLICATIONCACHECHANNEL
Expand Down Expand Up @@ -413,6 +416,8 @@ class nsHttpChannel final : public HttpBaseChannel
MOZ_MUST_USE nsresult ContinueAsyncRedirectChannelToURI(nsresult rv);
MOZ_MUST_USE nsresult OpenRedirectChannel(nsresult rv);

void DetermineContentLength();

/**
* A function that takes care of reading STS and PKP headers and enforcing
* STS and PKP load rules. After a secure channel is erected, STS and PKP
Expand Down Expand Up @@ -631,6 +636,10 @@ class nsHttpChannel final : public HttpBaseChannel
// the next authentication request can be sent on a whole new connection
uint32_t mAuthConnectionRestartable : 1;

uint32_t mReqContentLengthDetermined : 1;

uint64_t mReqContentLength;

nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;

// Needed for accurate DNS timing
Expand Down
Loading

0 comments on commit eab0a00

Please sign in to comment.