Bug 1244227 - Add nsIThrottledInputChannel.idl and implement. r=mcmanus

MozReview-Commit-ID: JVIjxEO901W
This commit is contained in:
Tom Tromey 2016-02-23 14:26:45 -07:00
Родитель fb0c04683f
Коммит ef548d271d
13 изменённых файлов: 727 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,392 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ThrottleQueue.h"
#include "nsISeekableStream.h"
#include "nsIAsyncInputStream.h"
#include "nsStreamUtils.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
class ThrottleInputStream final
: public nsIAsyncInputStream
, public nsISeekableStream
{
public:
ThrottleInputStream(nsIInputStream* aStream, ThrottleQueue* aQueue);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIASYNCINPUTSTREAM
void AllowInput();
private:
~ThrottleInputStream();
nsCOMPtr<nsIInputStream> mStream;
RefPtr<ThrottleQueue> mQueue;
nsresult mClosedStatus;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mEventTarget;
};
NS_IMPL_ISUPPORTS(ThrottleInputStream, nsIAsyncInputStream, nsIInputStream, nsISeekableStream)
ThrottleInputStream::ThrottleInputStream(nsIInputStream *aStream, ThrottleQueue* aQueue)
: mStream(aStream)
, mQueue(aQueue)
, mClosedStatus(NS_OK)
{
MOZ_ASSERT(aQueue != nullptr);
}
ThrottleInputStream::~ThrottleInputStream()
{
Close();
}
NS_IMETHODIMP
ThrottleInputStream::Close()
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
if (mQueue) {
mQueue->DequeueStream(this);
mQueue = nullptr;
mClosedStatus = NS_BASE_STREAM_CLOSED;
}
return mStream->Close();
}
NS_IMETHODIMP
ThrottleInputStream::Available(uint64_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
return mStream->Available(aResult);
}
NS_IMETHODIMP
ThrottleInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
uint32_t realCount;
nsresult rv = mQueue->Available(aCount, &realCount);
if (NS_FAILED(rv)) {
return rv;
}
if (realCount == 0) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
rv = mStream->Read(aBuf, realCount, aResult);
if (NS_SUCCEEDED(rv) && *aResult > 0) {
mQueue->RecordRead(*aResult);
}
return rv;
}
NS_IMETHODIMP
ThrottleInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
uint32_t realCount;
nsresult rv = mQueue->Available(aCount, &realCount);
if (NS_FAILED(rv)) {
return rv;
}
if (realCount == 0) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
rv = mStream->ReadSegments(aWriter, aClosure, realCount, aResult);
if (NS_SUCCEEDED(rv) && *aResult > 0) {
mQueue->RecordRead(*aResult);
}
return rv;
}
NS_IMETHODIMP
ThrottleInputStream::IsNonBlocking(bool* aNonBlocking)
{
*aNonBlocking = true;
return NS_OK;
}
NS_IMETHODIMP
ThrottleInputStream::Seek(int32_t aWhence, int64_t aOffset)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->Seek(aWhence, aOffset);
}
NS_IMETHODIMP
ThrottleInputStream::Tell(int64_t* aResult)
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->Tell(aResult);
}
NS_IMETHODIMP
ThrottleInputStream::SetEOF()
{
if (NS_FAILED(mClosedStatus)) {
return mClosedStatus;
}
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mStream);
if (!sstream) {
return NS_ERROR_FAILURE;
}
return sstream->SetEOF();
}
NS_IMETHODIMP
ThrottleInputStream::CloseWithStatus(nsresult aStatus)
{
if (NS_FAILED(mClosedStatus)) {
// Already closed, ignore.
return NS_OK;
}
if (NS_SUCCEEDED(aStatus)) {
aStatus = NS_BASE_STREAM_CLOSED;
}
mClosedStatus = Close();
if (NS_SUCCEEDED(mClosedStatus)) {
mClosedStatus = aStatus;
}
return NS_OK;
}
NS_IMETHODIMP
ThrottleInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
uint32_t aFlags,
uint32_t aRequestedCount,
nsIEventTarget *aEventTarget)
{
if (aFlags != 0) {
return NS_ERROR_ILLEGAL_VALUE;
}
mCallback = aCallback;
mEventTarget = aEventTarget;
if (mCallback) {
mQueue->QueueStream(this);
} else {
mQueue->DequeueStream(this);
}
return NS_OK;
}
void
ThrottleInputStream::AllowInput()
{
MOZ_ASSERT(mCallback);
nsCOMPtr<nsIInputStreamCallback> callbackEvent =
NS_NewInputStreamReadyEvent(mCallback, mEventTarget);
mCallback = nullptr;
mEventTarget = nullptr;
callbackEvent->OnInputStreamReady(this);
}
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(ThrottleQueue, nsIInputChannelThrottleQueue, nsITimerCallback)
ThrottleQueue::ThrottleQueue()
: mMeanBytesPerSecond(0)
, mMaxBytesPerSecond(0)
, mBytesProcessed(0)
, mTimerArmed(false)
{
nsresult rv;
nsCOMPtr<nsIEventTarget> sts;
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
if (NS_SUCCEEDED(rv))
sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mTimer)
mTimer->SetTarget(sts);
}
ThrottleQueue::~ThrottleQueue()
{
if (mTimer && mTimerArmed) {
mTimer->Cancel();
}
mTimer = nullptr;
}
NS_IMETHODIMP
ThrottleQueue::RecordRead(uint32_t aBytesRead)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
ThrottleEntry entry;
entry.mTime = TimeStamp::Now();
entry.mBytesRead = aBytesRead;
mReadEvents.AppendElement(entry);
mBytesProcessed += aBytesRead;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Available(uint32_t aRemaining, uint32_t* aAvailable)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
TimeStamp now = TimeStamp::Now();
TimeStamp oneSecondAgo = now - TimeDuration::FromSeconds(1);
size_t i;
// Remove all stale events.
for (i = 0; i < mReadEvents.Length(); ++i) {
if (mReadEvents[i].mTime >= oneSecondAgo) {
break;
}
}
mReadEvents.RemoveElementsAt(0, i);
uint32_t totalBytes = 0;
for (i = 0; i < mReadEvents.Length(); ++i) {
totalBytes += mReadEvents[i].mBytesRead;
}
uint32_t spread = mMaxBytesPerSecond - mMeanBytesPerSecond;
double prob = static_cast<double>(rand()) / RAND_MAX;
uint32_t thisSliceBytes = mMeanBytesPerSecond - spread +
static_cast<uint32_t>(2 * spread * prob);
if (totalBytes >= thisSliceBytes) {
*aAvailable = 0;
} else {
*aAvailable = thisSliceBytes;
}
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Init(uint32_t aMeanBytesPerSecond, uint32_t aMaxBytesPerSecond)
{
// Can be called on any thread.
if (aMeanBytesPerSecond == 0 || aMaxBytesPerSecond == 0 || aMaxBytesPerSecond < aMeanBytesPerSecond) {
return NS_ERROR_ILLEGAL_VALUE;
}
mMeanBytesPerSecond = aMeanBytesPerSecond;
mMaxBytesPerSecond = aMaxBytesPerSecond;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::BytesProcessed(uint64_t* aResult)
{
*aResult = mBytesProcessed;
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::WrapStream(nsIInputStream* aInputStream, nsIAsyncInputStream** aResult)
{
nsCOMPtr<nsIAsyncInputStream> result = new ThrottleInputStream(aInputStream, this);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
ThrottleQueue::Notify(nsITimer* aTimer)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
// A notified reader may need to push itself back on the queue.
// Swap out the list of readers so that this works properly.
nsTArray<RefPtr<ThrottleInputStream>> events;
events.SwapElements(mAsyncEvents);
// Optimistically notify all the waiting readers, and then let them
// requeue if there isn't enough bandwidth.
for (size_t i = 0; i < events.Length(); ++i) {
events[i]->AllowInput();
}
mTimerArmed = false;
return NS_OK;
}
void
ThrottleQueue::QueueStream(ThrottleInputStream* aStream)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mAsyncEvents.IndexOf(aStream) == mAsyncEvents.NoIndex) {
mAsyncEvents.AppendElement(aStream);
if (!mTimerArmed) {
uint32_t ms = 1000;
if (mReadEvents.Length() > 0) {
TimeStamp t = mReadEvents[0].mTime + TimeDuration::FromSeconds(1);
TimeStamp now = TimeStamp::Now();
if (t > now) {
ms = static_cast<uint32_t>((t - now).ToMilliseconds());
} else {
ms = 1;
}
}
if (NS_SUCCEEDED(mTimer->InitWithCallback(this, ms, nsITimer::TYPE_ONE_SHOT))) {
mTimerArmed = true;
}
}
}
}
void
ThrottleQueue::DequeueStream(ThrottleInputStream* aStream)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
mAsyncEvents.RemoveElement(aStream);
}
}
}

Просмотреть файл

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_ThrottleQueue_h
#define mozilla_net_ThrottleQueue_h
#include "mozilla/TimeStamp.h"
#include "nsIThrottledInputChannel.h"
#include "nsITimer.h"
namespace mozilla {
namespace net {
class ThrottleInputStream;
/**
* An implementation of nsIInputChannelThrottleQueue that can be used
* to throttle uploads. This class is not thread-safe.
* Initialization and calls to WrapStream may be done on any thread;
* but otherwise, after creation, it can only be used on the socket
* thread. It currently throttles with a one second granularity, so
* may be a bit choppy.
*/
class ThrottleQueue final
: public nsIInputChannelThrottleQueue
, public nsITimerCallback
{
public:
ThrottleQueue();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTCHANNELTHROTTLEQUEUE
NS_DECL_NSITIMERCALLBACK
void QueueStream(ThrottleInputStream* aStream);
void DequeueStream(ThrottleInputStream* aStream);
private:
~ThrottleQueue();
struct ThrottleEntry {
TimeStamp mTime;
uint32_t mBytesRead;
};
nsTArray<ThrottleEntry> mReadEvents;
uint32_t mMeanBytesPerSecond;
uint32_t mMaxBytesPerSecond;
uint64_t mBytesProcessed;
nsTArray<RefPtr<ThrottleInputStream>> mAsyncEvents;
nsCOMPtr<nsITimer> mTimer;
bool mTimerArmed;
};
}
}
#endif // mozilla_net_ThrottleQueue_h

Просмотреть файл

@ -124,6 +124,7 @@ XPIDL_SOURCES += [
'nsISystemProxySettings.idl',
'nsIThreadRetargetableRequest.idl',
'nsIThreadRetargetableStreamListener.idl',
'nsIThrottledInputChannel.idl',
'nsITimedChannel.idl',
'nsITLSServerSocket.idl',
'nsITraceableChannel.idl',
@ -259,6 +260,7 @@ UNIFIED_SOURCES += [
'RequestContextService.cpp',
'SimpleBuffer.cpp',
'StreamingProtocolService.cpp',
'ThrottleQueue.cpp',
'Tickler.cpp',
'TLSServerSocket.cpp',
]

Просмотреть файл

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIInputStream;
interface nsIAsyncInputStream;
/**
* An instance of this interface can be used to throttle the uploads
* of a group of associated channels.
*/
[scriptable, uuid(6b4b96fe-3c67-4587-af7b-58b6b17da411)]
interface nsIInputChannelThrottleQueue : nsISupports
{
/**
* Initialize this object with the mean and maximum bytes per
* second that will be allowed. Neither value may be zero, and
* the maximum must not be less than the mean.
*
* @param aMeanBytesPerSecond
* Mean number of bytes per second.
* @param aMaxBytesPerSecond
* Maximum number of bytes per second.
*/
void init(in unsigned long aMeanBytesPerSecond, in unsigned long aMaxBytesPerSecond);
/**
* Return the number of bytes that are available to the caller in
* this time slice.
*
* @param aRemaining
* The number of bytes available to be processed
* @return the number of bytes allowed to be processed during this
* time slice; this will never be greater than aRemaining.
*/
unsigned long available(in unsigned long aRemaining);
/**
* Record a successful read.
*
* @param aBytesRead
* The number of bytes actually read.
*/
void recordRead(in unsigned long aBytesRead);
/**
* Return the number of bytes allowed through this queue. This is
* the sum of all the values passed to recordRead. This method is
* primarily useful for testing.
*/
unsigned long long bytesProcessed();
/**
* Wrap the given input stream in a new input stream which
* throttles the incoming data.
*
* @param aInputStream the input stream to wrap
* @return a new input stream that throttles the data.
*/
nsIAsyncInputStream wrapStream(in nsIInputStream aInputStream);
};
/**
* A throttled input channel can be managed by an
* nsIInputChannelThrottleQueue to limit how much data is sent during
* a given time slice.
*/
[scriptable, uuid(0a32a100-c031-45b6-9e8b-0444c7d4a143)]
interface nsIThrottledInputChannel : nsISupports
{
/**
* The queue that manages this channel. Multiple channels can
* share a single queue. A null value means that no throttling
* will be done.
*/
attribute nsIInputChannelThrottleQueue throttleQueue;
};

Просмотреть файл

@ -625,6 +625,16 @@
{0x96, 0x1f, 0x65, 0x53, 0xcd, 0x60, 0xb1, 0xa2} \
}
#define NS_THROTTLEQUEUE_CONTRACTID \
"@mozilla.org/network/throttlequeue;1"
#define NS_THROTTLEQUEUE_CID \
{ /* 4c39159c-cd90-4dd3-97a7-06af5e6d84c4 */ \
0x4c39159c, \
0xcd90, \
0x4dd3, \
{0x97, 0xa7, 0x06, 0xaf, 0x5e, 0x6d, 0x84, 0xc4} \
}
/******************************************************************************
* netwerk/protocol/ftp/ classes
*/

Просмотреть файл

@ -269,6 +269,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFtpProtocolHandler, Init)
#include "nsHttpDigestAuth.h"
#include "nsHttpNTLMAuth.h"
#include "nsHttpActivityDistributor.h"
#include "ThrottleQueue.h"
#undef LOG
#undef LOG_ENABLED
namespace mozilla {
@ -281,6 +282,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpChannelAuthProvider)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR(ThrottleQueue)
} // namespace net
} // namespace mozilla
#endif // !NECKO_PROTOCOL_http
@ -794,6 +796,7 @@ NS_DEFINE_NAMED_CID(NS_HTTPNTLMAUTH_CID);
NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_HTTPCHANNELAUTHPROVIDER_CID);
NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID);
NS_DEFINE_NAMED_CID(NS_THROTTLEQUEUE_CID);
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
@ -944,6 +947,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
{ &kNS_HTTPAUTHMANAGER_CID, false, nullptr, mozilla::net::nsHttpAuthManagerConstructor },
{ &kNS_HTTPCHANNELAUTHPROVIDER_CID, false, nullptr, mozilla::net::nsHttpChannelAuthProviderConstructor },
{ &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor },
{ &kNS_THROTTLEQUEUE_CID, false, nullptr, mozilla::net::ThrottleQueueConstructor },
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
{ &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor },
@ -1105,6 +1109,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
{ NS_HTTPAUTHMANAGER_CONTRACTID, &kNS_HTTPAUTHMANAGER_CID },
{ NS_HTTPCHANNELAUTHPROVIDER_CONTRACTID, &kNS_HTTPCHANNELAUTHPROVIDER_CID },
{ NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID },
{ NS_THROTTLEQUEUE_CONTRACTID, &kNS_THROTTLEQUEUE_CID },
#endif // !NECKO_PROTOCOL_http
#ifdef NECKO_PROTOCOL_ftp
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID },

Просмотреть файл

@ -227,6 +227,7 @@ NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector)
NS_INTERFACE_MAP_ENTRY(nsIThrottledInputChannel)
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
//-----------------------------------------------------------------------------
@ -3458,6 +3459,28 @@ HttpBaseChannel::GetInnerDOMWindow()
return innerWindow;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIThrottledInputChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::SetThrottleQueue(nsIInputChannelThrottleQueue* aQueue)
{
if (!XRE_IsParentProcess()) {
return NS_ERROR_FAILURE;
}
mThrottleQueue = aQueue;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetThrottleQueue(nsIInputChannelThrottleQueue** aQueue)
{
*aQueue = mThrottleQueue;
return NS_OK;
}
//------------------------------------------------------------------------------
bool

Просмотреть файл

@ -43,6 +43,7 @@
#include "nsISecurityConsoleMessage.h"
#include "nsCOMArray.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "nsIThrottledInputChannel.h"
class nsISecurityConsoleMessage;
class nsIPrincipal;
@ -79,6 +80,7 @@ class HttpBaseChannel : public nsHashPropertyBag
, public nsITimedChannel
, public nsIForcePendingChannel
, public nsIConsoleReportCollector
, public nsIThrottledInputChannel
{
protected:
virtual ~HttpBaseChannel();
@ -90,6 +92,7 @@ public:
NS_DECL_NSIUPLOADCHANNEL2
NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSITIMEDCHANNEL
NS_DECL_NSITHROTTLEDINPUTCHANNEL
HttpBaseChannel();
@ -387,6 +390,8 @@ protected:
nsCOMPtr<nsIStreamListener> mCompressListener;
nsHttpRequestHead mRequestHead;
// Upload throttling.
nsCOMPtr<nsIInputChannelThrottleQueue> mThrottleQueue;
nsCOMPtr<nsIInputStream> mUploadStream;
nsCOMPtr<nsIRunnable> mUploadCloneableCallback;
nsAutoPtr<nsHttpResponseHead> mResponseHead;

Просмотреть файл

@ -33,6 +33,7 @@
#include "nsIEventTarget.h"
#include "nsIHttpChannelInternal.h"
#include "nsIInputStream.h"
#include "nsIThrottledInputChannel.h"
#include "nsITransport.h"
#include "nsIOService.h"
#include "nsIRequestContext.h"
@ -232,6 +233,7 @@ nsHttpTransaction::Init(uint32_t caps,
MOZ_ASSERT(cinfo);
MOZ_ASSERT(requestHead);
MOZ_ASSERT(target);
MOZ_ASSERT(NS_IsMainThread());
mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
@ -379,6 +381,25 @@ nsHttpTransaction::Init(uint32_t caps,
else
mRequestStream = headers;
nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mChannel);
nsIInputChannelThrottleQueue* queue;
if (throttled) {
rv = throttled->GetThrottleQueue(&queue);
// In case of failure, just carry on without throttling.
if (NS_SUCCEEDED(rv) && queue) {
nsCOMPtr<nsIAsyncInputStream> wrappedStream;
rv = queue->WrapStream(mRequestStream, getter_AddRefs(wrappedStream));
// Failure to throttle isn't sufficient reason to fail
// initialization
if (NS_SUCCEEDED(rv)) {
MOZ_ASSERT(wrappedStream != nullptr);
LOG(("nsHttpTransaction::Init %p wrapping input stream using throttle queue %p\n",
this, queue));
mRequestStream = do_QueryInterface(wrappedStream);
}
}
}
uint64_t size_u64;
rv = mRequestStream->Available(&size_u64);
if (NS_FAILED(rv)) {

Просмотреть файл

@ -0,0 +1,41 @@
// Test nsIThrottledInputChannel interface.
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
function test_handler(metadata, response) {
const originalBody = "the response";
response.setHeader("Content-Type", "text/html", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.bodyOutputStream.write(originalBody, originalBody.length);
}
function make_channel(url) {
return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
.QueryInterface(Components.interfaces.nsIHttpChannel);
}
function run_test() {
let httpserver = new HttpServer();
httpserver.start(-1);
const PORT = httpserver.identity.primaryPort;
httpserver.registerPathHandler("/testdir", test_handler);
let channel = make_channel("http://localhost:" + PORT + "/testdir");
let tq = Cc["@mozilla.org/network/throttlequeue;1"]
.createInstance(Ci.nsIInputChannelThrottleQueue);
tq.init(1000, 1000);
let tic = channel.QueryInterface(Ci.nsIThrottledInputChannel);
tic.throttleQueue = tq;
channel.asyncOpen2(new ChannelListener(() => {
ok(tq.bytesProcessed() > 0, "throttled queue processed some bytes");
httpserver.stop(do_test_finished);
}));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,23 @@
// Test ThrottleQueue initialization.
function init(tq, mean, max) {
let threw = false;
try {
tq.init(mean, max);
} catch (e) {
threw = true;
}
return !threw;
}
function run_test() {
let tq = Cc["@mozilla.org/network/throttlequeue;1"]
.createInstance(Ci.nsIInputChannelThrottleQueue);
ok(!init(tq, 0, 50), "mean bytes cannot be 0");
ok(!init(tq, 50, 0), "max bytes cannot be 0");
ok(!init(tq, 0, 0), "mean and max bytes cannot be 0");
ok(!init(tq, 70, 20), "max cannot be less than mean");
ok(init(tq, 2, 2), "valid initialization");
}

Просмотреть файл

@ -0,0 +1,57 @@
// Test nsIThrottledInputChannel interface.
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
function test_handler(metadata, response) {
const originalBody = "the response";
response.setHeader("Content-Type", "text/html", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.bodyOutputStream.write(originalBody, originalBody.length);
}
function make_channel(url) {
return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
.QueryInterface(Ci.nsIHttpChannel);
}
function run_test() {
let httpserver = new HttpServer();
httpserver.registerPathHandler("/testdir", test_handler);
httpserver.start(-1);
const PORT = httpserver.identity.primaryPort;
const size = 4096;
let sstream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
sstream.data = 'x'.repeat(size);
let mime = Cc["@mozilla.org/network/mime-input-stream;1"].
createInstance(Ci.nsIMIMEInputStream);
mime.addHeader("Content-Type", "multipart/form-data; boundary=zzzzz");
mime.setData(sstream);
mime.addContentLength = true;
let tq = Cc["@mozilla.org/network/throttlequeue;1"]
.createInstance(Ci.nsIInputChannelThrottleQueue);
// Make sure the request takes more than one read.
tq.init(100 + size / 2, 100 + size / 2);
let channel = make_channel("http://localhost:" + PORT + "/testdir");
channel.QueryInterface(Ci.nsIUploadChannel)
.setUploadStream(mime, "", mime.available());
channel.requestMethod = "POST";
let tic = channel.QueryInterface(Ci.nsIThrottledInputChannel);
tic.throttleQueue = tq;
let startTime = Date.now();
channel.asyncOpen2(new ChannelListener(() => {
ok(Date.now() - startTime > 1000, "request took more than one second");
httpserver.stop(do_test_finished);
}));
do_test_pending();
}

Просмотреть файл

@ -360,3 +360,6 @@ skip-if = os == "android"
[test_bug464591.js]
[test_cache-control_request.js]
[test_bug1279246.js]
[test_throttlequeue.js]
[test_throttlechannel.js]
[test_throttling.js]