Bug 1152332 - Let connection proxy filters return the result asynchronously - core changes, r=michal

This commit is contained in:
Honza Bambas 2018-02-08 07:18:00 +02:00
parent e68b22643e
commit 7c8e2fe79d
3 changed files with 516 additions and 175 deletions

View File

@ -11,6 +11,27 @@ interface nsIProtocolProxyService;
interface nsIProxyInfo;
interface nsIURI;
/**
* Recipient of the result of implementers of nsIProtocolProxy(Channel)Filter
* allowing the proxyinfo be provided asynchronously.
*/
[scriptable, uuid(009E6C3F-FB64-40C5-8093-F1495C64773E)]
interface nsIProxyProtocolFilterResult : nsISupports
{
/**
* It's mandatory to call this method exactly once when the applyFilter()
* implementation doesn't throw and to not call it when applyFilter() does
* throw.
*
* It's mandatory to call this method on the same thread as the call to
* applyFilter() has been made on.
*
* Following the above conditions, can be called either from within
* applyFilter() or asynchronouly any time later.
*/
void onProxyFilterResult(in nsIProxyInfo aProxy);
};
/**
* This interface is used to apply filters to the proxies selected for a given
* URI. Use nsIProtocolProxyService::registerFilter to hook up instances of
@ -32,14 +53,19 @@ interface nsIProtocolProxyFilter : nsISupports
* The proxy (or list of proxies) that would be used by default for
* the given URI. This may be null.
*
* @return The proxy (or list of proxies) that should be used in place of
* aProxy. This can be just be aProxy if the filter chooses not to
* modify the proxy. It can also be null to indicate that a direct
* connection should be used. Use aProxyService.newProxyInfo to
* construct nsIProxyInfo objects.
* @param aCallback
* An object that the implementer is obligated to call on with
* the result (from within applyFilter() or asynchronously) when
* applyFilter didn't throw. The argument passed to onProxyFilterResult
* is the proxy (or list of proxies) that should be used in place of
* aProxy. This can be just be aProxy if the filter chooses not to
* modify the proxy. It can also be null to indicate that a direct
* connection should be used. Use aProxyService.newProxyInfo to
* construct nsIProxyInfo objects.
*/
nsIProxyInfo applyFilter(in nsIProtocolProxyService aProxyService,
in nsIURI aURI, in nsIProxyInfo aProxy);
void applyFilter(in nsIProtocolProxyService aProxyService,
in nsIURI aURI, in nsIProxyInfo aProxy,
in nsIProxyProtocolFilterResult aCallback);
};
/**
@ -63,12 +89,17 @@ interface nsIProtocolProxyChannelFilter : nsISupports
* The proxy (or list of proxies) that would be used by default for
* the given channel. This may be null.
*
* @return The proxy (or list of proxies) that should be used in place of
* aProxy. This can be just be aProxy if the filter chooses not to
* modify the proxy. It can also be null to indicate that a direct
* connection should be used. Use aProxyService.newProxyInfo to
* construct nsIProxyInfo objects.
* @param aCallback
* An object that the implementer is obligated to call on with
* the result (from within applyFilter() or asynchronously) when
* applyFilter didn't throw. The argument passed to onProxyFilterResult
* is the proxy (or list of proxies) that should be used in place of
* aProxy. This can be just be aProxy if the filter chooses not to
* modify the proxy. It can also be null to indicate that a direct
* connection should be used. Use aProxyService.newProxyInfo to
* construct nsIProxyInfo objects.
*/
nsIProxyInfo applyFilter(in nsIProtocolProxyService aProxyService,
in nsIChannel aChannel, in nsIProxyInfo aProxy);
void applyFilter(in nsIProtocolProxyService aProxyService,
in nsIChannel aChannel, in nsIProxyInfo aProxy,
in nsIProxyProtocolFilterResult aCallback);
};

View File

@ -6,6 +6,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "nsProtocolProxyService.h"
#include "nsProxyInfo.h"
@ -97,6 +98,26 @@ GetProxyURI(nsIChannel *channel, nsIURI **aOut)
//-----------------------------------------------------------------------------
nsProtocolProxyService::FilterLink::FilterLink(uint32_t p,
nsIProtocolProxyFilter *f)
: position(p), filter(f), channelFilter(nullptr)
{
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, filter=%p", this, f));
}
nsProtocolProxyService::FilterLink::FilterLink(uint32_t p,
nsIProtocolProxyChannelFilter *cf)
: position(p), filter(nullptr), channelFilter(cf)
{
LOG(("nsProtocolProxyService::FilterLink::FilterLink %p, channel-filter=%p", this, cf));
}
nsProtocolProxyService::FilterLink::~FilterLink()
{
LOG(("nsProtocolProxyService::FilterLink::~FilterLink %p", this));
}
//-----------------------------------------------------------------------------
// The nsPACManCallback portion of this implementation should be run
// on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
// a true mainThreadResponse parameter.
@ -151,7 +172,94 @@ private:
}
}
// Helper class to loop over all registered asynchronous filters.
// There is a cycle between nsAsyncResolveRequest and this class that
// is broken after the last filter has called back on this object.
class AsyncApplyFilters final
: public nsIProxyProtocolFilterResult
, public nsIRunnable
, public nsICancelable
{
// The reference counter is thread-safe, but the processing logic is
// considered single thread only. We want the counter be thread safe,
// since this class can be released on a background thread.
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROXYPROTOCOLFILTERRESULT
NS_DECL_NSIRUNNABLE
NS_DECL_NSICANCELABLE
typedef std::function<nsresult(nsAsyncResolveRequest*, nsIProxyInfo*, bool)> Callback;
explicit AsyncApplyFilters(nsProtocolInfo& aInfo, Callback const& aCallback);
// This method starts the processing or filters. If all of them
// answer synchronously (call back from within applyFilters) this method
// will return immediately and the returning result will carry return
// result of the callback given in constructor.
// This method is looping the registered filters (that have been copied
// locally) as long as an answer from a filter is obtained synchronously.
// Note that filters are processed serially to let them build a list
// of proxy info.
nsresult AsyncProcess(nsAsyncResolveRequest* aRequest);
private:
typedef nsProtocolProxyService::FilterLink FilterLink;
virtual ~AsyncApplyFilters();
// Processes the next filter and loops until a filter is successfully
// called on or it has called back to us.
nsresult ProcessNextFilter();
// Called after the last filter has been processed (=called back or failed to
// be called on)
nsresult Finish();
nsProtocolInfo mInfo;
// This is nullified before we call back on the request or when
// Cancel() on this object has been called to break the cycle
// and signal to stop.
RefPtr<nsAsyncResolveRequest> mRequest;
Callback mCallback;
// A shallow snapshot of filters as they were registered at the moment
// we started to process filters for the given resolve request.
nsTArray<RefPtr<FilterLink>> mFiltersCopy;
nsTArray<RefPtr<FilterLink>>::index_type mNextFilterIndex;
// true when we are calling ProcessNextFilter() from inside AsyncProcess(),
// false otherwise.
bool mProcessingInLoop;
// true after a filter called back to us with a result, dropped to false
// just before we call a filter.
bool mFilterCalledBack;
// This keeps the initial value we pass to the first filter in line and also
// collects the result from each filter call.
nsCOMPtr<nsIProxyInfo> mProxyInfo;
// The logic is written as non-thread safe, assert single-thread usage.
nsCOMPtr<nsIEventTarget> mProcessingThread;
};
public:
nsresult ProcessLocally(nsProtocolInfo &info, nsIProxyInfo* pi, bool isSyncOK)
{
SetResult(NS_OK, pi);
auto consumeFiltersResult = [isSyncOK]
(nsAsyncResolveRequest* ctx, nsIProxyInfo* pi, bool aCalledAsync) -> nsresult
{
ctx->SetResult(NS_OK, pi);
if (isSyncOK || aCalledAsync) {
ctx->Run();
return NS_OK;
}
return ctx->DispatchCallback();
};
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
// may call consumeFiltersResult() directly
return mAsyncFilterApplier->AsyncProcess(this);
}
void SetResult(nsresult status, nsIProxyInfo *pi)
{
mStatus = status;
@ -169,6 +277,10 @@ public:
{
NS_ENSURE_ARG(NS_FAILED(reason));
if (mAsyncFilterApplier) {
mAsyncFilterApplier->Cancel(reason);
}
// If we've already called DoCallback then, nothing more to do.
if (!mCallback)
return NS_OK;
@ -243,18 +355,40 @@ private:
// Now apply proxy filters
nsProtocolInfo info;
mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
if (NS_SUCCEEDED(mStatus))
mPPS->ApplyFilters(mChannel, info, mProxyInfo);
else
mProxyInfo = nullptr;
if(pacAvailable) {
// if !pacAvailable, it was already logged above
LOG(("pac thread callback %s\n", mPACString.get()));
auto consumeFiltersResult = [pacAvailable]
(nsAsyncResolveRequest* self, nsIProxyInfo* pi, bool async) -> nsresult
{
LOG(("DoCallback::consumeFiltersResult this=%p, pi=%p, async=%d",
self, pi, async));
self->mProxyInfo = pi;
if (pacAvailable) {
// if !pacAvailable, it was already logged above
LOG(("pac thread callback %s\n", self->mPACString.get()));
}
if (NS_SUCCEEDED(self->mStatus)) {
self->mPPS->MaybeDisableDNSPrefetch(self->mProxyInfo);
}
self->mCallback->OnProxyAvailable(self,
self->mChannel,
self->mProxyInfo,
self->mStatus);
return NS_OK;
};
if (NS_SUCCEEDED(mStatus)) {
mAsyncFilterApplier = new AsyncApplyFilters(info, consumeFiltersResult);
// This may call consumeFiltersResult() directly.
mAsyncFilterApplier->AsyncProcess(this);
return;
}
if (NS_SUCCEEDED(mStatus))
mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
consumeFiltersResult(this, nullptr, false);
}
else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
LOG(("pac thread callback indicates new pac file load\n"));
@ -299,7 +433,6 @@ private:
}
private:
nsresult mStatus;
nsCString mPACString;
nsCString mPACURL;
@ -311,9 +444,200 @@ private:
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIProtocolProxyCallback> mCallback;
nsCOMPtr<nsIProxyInfo> mProxyInfo;
RefPtr<AsyncApplyFilters> mAsyncFilterApplier;
};
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest,
nsICancelable,
nsIRunnable)
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest::AsyncApplyFilters,
nsIProxyProtocolFilterResult,
nsICancelable,
nsIRunnable)
nsAsyncResolveRequest::AsyncApplyFilters::AsyncApplyFilters(nsProtocolInfo& aInfo,
Callback const& aCallback)
: mInfo(aInfo)
, mCallback(aCallback)
, mNextFilterIndex(0)
, mProcessingInLoop(false)
, mFilterCalledBack(false)
{
LOG(("AsyncApplyFilters %p", this));
}
nsAsyncResolveRequest::AsyncApplyFilters::~AsyncApplyFilters()
{
LOG(("~AsyncApplyFilters %p", this));
MOZ_ASSERT(!mRequest);
MOZ_ASSERT(!mProxyInfo);
MOZ_ASSERT(!mFiltersCopy.Length());
}
nsresult
nsAsyncResolveRequest::AsyncApplyFilters::AsyncProcess(nsAsyncResolveRequest * aRequest)
{
LOG(("AsyncApplyFilters::AsyncProcess %p for req %p", this, aRequest));
MOZ_ASSERT(!mRequest, "AsyncApplyFilters started more than once!");
if (!(mInfo.flags & nsIProtocolHandler::ALLOWS_PROXY)) {
// Calling the callback directly (not via Finish()) since we
// don't want to prune.
return mCallback(aRequest, aRequest->mProxyInfo, false);
}
mProcessingThread = NS_GetCurrentThread();
mRequest = aRequest;
mProxyInfo = aRequest->mProxyInfo;
aRequest->mPPS->CopyFilters(mFiltersCopy);
// We want to give filters a chance to process in a single loop to prevent
// any current-thread dispatch delays when those are not needed.
// This code is rather "loopy" than "recursive" to prevent long stack traces.
do {
MOZ_ASSERT(!mProcessingInLoop);
mozilla::AutoRestore<bool> restore(mProcessingInLoop);
mProcessingInLoop = true;
nsresult rv = ProcessNextFilter();
if (NS_FAILED(rv)) {
return rv;
}
} while (mFilterCalledBack);
return NS_OK;
}
nsresult
nsAsyncResolveRequest::AsyncApplyFilters::ProcessNextFilter()
{
LOG(("AsyncApplyFilters::ProcessNextFilter %p ENTER pi=%p", this, mProxyInfo.get()));
RefPtr<FilterLink> filter;
do {
mFilterCalledBack = false;
if (!mRequest) {
// We got canceled
LOG((" canceled"));
return NS_OK; // should we let the consumer know?
}
if (mNextFilterIndex == mFiltersCopy.Length()) {
return Finish();
}
filter = mFiltersCopy[mNextFilterIndex++];
// Loop until a call to a filter succeeded. Other option is to recurse
// but that would waste stack trace when a number of filters gets registered
// and all from some reason tend to fail.
// The !mFilterCalledBack part of the condition is there to protect us from
// calling on another filter when the current one managed to call back and
// then threw. We already have the result so take it and use it since
// the next filter will be processed by the root loop or a call to
// ProcessNextFilter has already been dispatched to this thread.
LOG((" calling filter %p pi=%p", filter.get(), mProxyInfo.get()));
} while (!mRequest->mPPS->ApplyFilter(filter, mRequest->mChannel, mInfo, mProxyInfo, this) &&
!mFilterCalledBack);
LOG(("AsyncApplyFilters::ProcessNextFilter %p LEAVE pi=%p", this, mProxyInfo.get()));
return NS_OK;
}
NS_IMETHODIMP
nsAsyncResolveRequest::AsyncApplyFilters::OnProxyFilterResult(nsIProxyInfo* aProxyInfo)
{
LOG(("AsyncApplyFilters::OnProxyFilterResult %p pi=%p", this, aProxyInfo));
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
MOZ_ASSERT(!mFilterCalledBack);
if (mFilterCalledBack) {
LOG((" duplicate notification?"));
return NS_OK;
}
mFilterCalledBack = true;
mProxyInfo = aProxyInfo;
if (mProcessingInLoop) {
// No need to call/dispatch ProcessNextFilter(), we are in a control
// loop that will do this for us and save recursion/dispatching.
LOG((" in a root loop"));
return NS_OK;
}
if (!mRequest) {
// We got canceled
LOG((" canceled"));
return NS_OK;
}
if (mNextFilterIndex == mFiltersCopy.Length()) {
// We are done, all filters have been called on!
Finish();
return NS_OK;
}
// Redispatch, since we don't want long stacks when filters respond synchronously.
LOG((" redispatching"));
NS_DispatchToCurrentThread(this);
return NS_OK;
}
NS_IMETHODIMP
nsAsyncResolveRequest::AsyncApplyFilters::Run()
{
LOG(("AsyncApplyFilters::Run %p", this));
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
ProcessNextFilter();
return NS_OK;
}
nsresult
nsAsyncResolveRequest::AsyncApplyFilters::Finish()
{
LOG(("AsyncApplyFilters::Finish %p pi=%p", this, mProxyInfo.get()));
MOZ_ASSERT(mRequest);
mFiltersCopy.Clear();
RefPtr<nsAsyncResolveRequest> request;
request.swap(mRequest);
nsCOMPtr<nsIProxyInfo> pi;
pi.swap(mProxyInfo);
request->mPPS->PruneProxyInfo(mInfo, pi);
return mCallback(request, pi, !mProcessingInLoop);
}
NS_IMETHODIMP
nsAsyncResolveRequest::AsyncApplyFilters::Cancel(nsresult reason)
{
LOG(("AsyncApplyFilters::Cancel %p", this));
MOZ_ASSERT(mProcessingThread && mProcessingThread->IsOnCurrentThread());
// This will be called only from inside the request, so don't call
// its's callback. Dropping the members means we simply break the cycle.
mFiltersCopy.Clear();
mProxyInfo = nullptr;
mRequest = nullptr;
return NS_OK;
}
// Bug 1366133: make GetPACURI off-main-thread since it may hang on Windows platform
class AsyncGetPACURIRequest final : public nsIRunnable
@ -491,7 +815,6 @@ NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
nsProtocolProxyService::nsProtocolProxyService()
: mFilterLocalHosts(false)
, mFilters(nullptr)
, mProxyConfig(PROXYCONFIG_DIRECT)
, mHTTPProxyPort(-1)
, mFTPProxyPort(-1)
@ -510,7 +833,7 @@ nsProtocolProxyService::nsProtocolProxyService()
nsProtocolProxyService::~nsProtocolProxyService()
{
// These should have been cleaned up in our Observe method.
NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters.Length() == 0 &&
mPACMan == nullptr, "what happened to xpcom-shutdown?");
}
@ -645,13 +968,9 @@ nsProtocolProxyService::Observe(nsISupports *aSubject,
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
mIsShutdown = true;
// cleanup
if (mHostFiltersArray.Length() > 0) {
mHostFiltersArray.Clear();
}
if (mFilters) {
delete mFilters;
mFilters = nullptr;
}
mHostFiltersArray.Clear();
mFilters.Clear();
if (mPACMan) {
mPACMan->Shutdown();
mPACMan = nullptr;
@ -1366,16 +1685,10 @@ nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags
if (!usePACThread || !mPACMan) {
// we can do it locally
ApplyFilters(channel, info, pi);
ctx->SetResult(NS_OK, pi);
if (isSyncOK) {
ctx->Run();
return NS_OK;
}
rv = ctx->DispatchCallback();
if (NS_SUCCEEDED(rv))
rv = ctx->ProcessLocally(info, pi, isSyncOK);
if (NS_SUCCEEDED(rv) && !isSyncOK) {
ctx.forget(result);
}
return rv;
}
@ -1522,36 +1835,43 @@ nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy,
return NS_OK;
}
nsresult
nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
namespace { // anon
class ProxyFilterPositionComparator
{
typedef RefPtr<nsProtocolProxyService::FilterLink> FilterLinkRef;
public:
bool Equals(const FilterLinkRef& a, const FilterLinkRef& b) const {
return a->position == b->position;
}
bool LessThan(const FilterLinkRef& a, const FilterLinkRef& b) const {
return a->position < b->position;
}
};
class ProxyFilterObjectComparator
{
typedef RefPtr<nsProtocolProxyService::FilterLink> FilterLinkRef;
public:
bool Equals(const FilterLinkRef& link, const nsISupports* obj) const {
return obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->filter)) ||
obj == nsCOMPtr<nsISupports>(do_QueryInterface(link->channelFilter));
}
};
} // anon
nsresult
nsProtocolProxyService::InsertFilterLink(RefPtr<FilterLink>&& link)
{
LOG(("nsProtocolProxyService::InsertFilterLink filter=%p", link.get()));
if (mIsShutdown) {
return NS_ERROR_FAILURE;
}
if (!mFilters) {
mFilters = link;
return NS_OK;
}
// insert into mFilters in sorted order
FilterLink *last = nullptr;
for (FilterLink *iter = mFilters; iter; iter = iter->next) {
if (position < iter->position) {
if (last) {
link->next = last->next;
last->next = link;
}
else {
link->next = mFilters;
mFilters = link;
}
return NS_OK;
}
last = iter;
}
// our position is equal to or greater than the last link in the list
last->next = link;
mFilters.AppendElement(link);
mFilters.Sort(ProxyFilterPositionComparator());
return NS_OK;
}
@ -1561,15 +1881,8 @@ nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
{
UnregisterFilter(filter); // remove this filter if we already have it
FilterLink *link = new FilterLink(position, filter);
if (!link) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = InsertFilterLink(link, position);
if (NS_FAILED(rv)) {
delete link;
}
return rv;
RefPtr<FilterLink> link = new FilterLink(position, filter);
return InsertFilterLink(Move(link));
}
NS_IMETHODIMP
@ -1578,49 +1891,30 @@ nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *cha
{
UnregisterChannelFilter(channelFilter); // remove this filter if we already have it
FilterLink *link = new FilterLink(position, channelFilter);
if (!link) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = InsertFilterLink(link, position);
if (NS_FAILED(rv)) {
delete link;
}
return rv;
RefPtr<FilterLink> link = new FilterLink(position, channelFilter);
return InsertFilterLink(Move(link));
}
nsresult
nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
{
FilterLink *last = nullptr;
for (FilterLink *iter = mFilters; iter; iter = iter->next) {
nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
nsCOMPtr<nsISupports> object2 = do_QueryInterface(iter->channelFilter);
if (object == givenObject || object2 == givenObject) {
if (last)
last->next = iter->next;
else
mFilters = iter->next;
iter->next = nullptr;
delete iter;
return NS_OK;
}
last = iter;
}
LOG(("nsProtocolProxyService::RemoveFilterLink target=%p", givenObject));
// No need to throw an exception in this case.
return NS_OK;
return mFilters.RemoveElement(givenObject, ProxyFilterObjectComparator())
? NS_OK : NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter)
{
// QI to nsISupports so we can safely test object identity.
nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
return RemoveFilterLink(givenObject);
}
NS_IMETHODIMP
nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter)
{
// QI to nsISupports so we can safely test object identity.
nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
return RemoveFilterLink(givenObject);
@ -2110,39 +2404,43 @@ nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
}
void
nsProtocolProxyService::ApplyFilters(nsIChannel *channel,
const nsProtocolInfo &info,
nsIProxyInfo **list)
nsProtocolProxyService::CopyFilters(nsTArray<RefPtr<FilterLink>>& aCopy)
{
if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
return;
MOZ_ASSERT(aCopy.Length() == 0);
aCopy.AppendElements(mFilters);
}
bool
nsProtocolProxyService::ApplyFilter(FilterLink const* filterLink,
nsIChannel *channel,
const nsProtocolInfo &info,
nsCOMPtr<nsIProxyInfo> list,
nsIProxyProtocolFilterResult* callback)
{
nsresult rv;
// We prune the proxy list prior to invoking each filter. This may be
// somewhat inefficient, but it seems like a good idea since we want each
// filter to "see" a valid proxy list.
PruneProxyInfo(info, list);
nsCOMPtr<nsIProxyInfo> result;
if (filterLink->filter) {
nsCOMPtr<nsIURI> uri;
Unused << GetProxyURI(channel, getter_AddRefs(uri));
if (!uri) {
return false;
}
for (FilterLink *iter = mFilters; iter; iter = iter->next) {
PruneProxyInfo(info, list);
nsresult rv = NS_OK;
if (iter->filter) {
nsCOMPtr<nsIURI> uri;
rv = GetProxyURI(channel, getter_AddRefs(uri));
if (uri) {
rv = iter->filter->ApplyFilter(this, uri, *list,
getter_AddRefs(result));
}
} else if (iter->channelFilter) {
rv = iter->channelFilter->ApplyFilter(this, channel, *list,
getter_AddRefs(result));
}
if (NS_FAILED(rv))
continue;
result.swap(*list);
rv = filterLink->filter->ApplyFilter(this, uri, list, callback);
return NS_SUCCEEDED(rv);
}
PruneProxyInfo(info, list);
if (filterLink->channelFilter) {
rv = filterLink->channelFilter->ApplyFilter(this, channel, list, callback);
return NS_SUCCEEDED(rv);
}
return false;
}
void
@ -2151,6 +2449,9 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
{
if (!*list)
return;
LOG(("nsProtocolProxyService::PruneProxyInfo ENTER list=%p", *list));
nsProxyInfo *head = nullptr;
CallQueryInterface(*list, &head);
if (!head) {
@ -2186,8 +2487,9 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
iter = iter->mNext;
}
}
if (!head)
if (!head) {
return;
}
}
// Now, scan to see if all remaining proxies are disabled. If so, then
@ -2204,9 +2506,9 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
}
}
if (allDisabled)
if (allDisabled) {
LOG(("All proxies are disabled, so trying all again"));
else {
} else {
// remove any disabled proxies.
nsProxyInfo *last = nullptr;
for (iter = head; iter; ) {
@ -2243,6 +2545,8 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
NS_RELEASE(head);
*list = head; // Transfer ownership
LOG(("nsProtocolProxyService::PruneProxyInfo LEAVE list=%p", *list));
}
} // namespace net

View File

@ -53,6 +53,24 @@ public:
nsresult Init();
public:
// An instance of this struct is allocated for each registered
// nsIProtocolProxyFilter and each nsIProtocolProxyChannelFilter.
class FilterLink {
public:
NS_INLINE_DECL_REFCOUNTING(FilterLink)
uint32_t position;
nsCOMPtr<nsIProtocolProxyFilter> filter;
nsCOMPtr<nsIProtocolProxyChannelFilter> channelFilter;
FilterLink(uint32_t p, nsIProtocolProxyFilter *f);
FilterLink(uint32_t p, nsIProtocolProxyChannelFilter *cf);
private:
~FilterLink();
};
protected:
friend class nsAsyncResolveRequest;
friend class TestProtocolProxyService_LoadHostFilters_Test; // for gtest
@ -231,31 +249,22 @@ protected:
nsIProxyInfo **result);
/**
* This method applies the registered filters to the given proxy info
* list, and returns a possibly modified list.
*
* @param channel
* The channel corresponding to this proxy info list.
* @param info
* Information about the URI's protocol.
* @param proxyInfo
* The proxy info list to be modified. This is an inout param.
* Shallow copy of the current list of registered filters so that
* we can safely let them asynchronously process a single proxy
* resolution request.
*/
void ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
nsIProxyInfo **proxyInfo);
void CopyFilters(nsTArray<RefPtr<FilterLink>> &aCopy);
/**
* This method is a simple wrapper around ApplyFilters that takes the
* proxy info list inout param as a nsCOMPtr.
* This method applies the provided filter to the given proxy info
* list, and expects |callback| be called on (synchronously or
* asynchronously) to provide the updated proxyinfo list.
*/
inline void ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
nsCOMPtr<nsIProxyInfo> &proxyInfo)
{
nsIProxyInfo *pi = nullptr;
proxyInfo.swap(pi);
ApplyFilters(channel, info, &pi);
proxyInfo.swap(pi);
}
bool ApplyFilter(FilterLink const* filterLink,
nsIChannel *channel,
const nsProtocolInfo &info,
nsCOMPtr<nsIProxyInfo> proxyInfo,
nsIProxyProtocolFilterResult* callback);
/**
* This method prunes out disabled and disallowed proxies from a given
@ -267,7 +276,20 @@ protected:
* The proxy info list to be modified. This is an inout param.
*/
void PruneProxyInfo(const nsProtocolInfo &info,
nsIProxyInfo **proxyInfo);
nsIProxyInfo **proxyInfo);
/**
* This method is a simple wrapper around PruneProxyInfo that takes the
* proxy info list inout param as a nsCOMPtr.
*/
void PruneProxyInfo(const nsProtocolInfo &info,
nsCOMPtr<nsIProxyInfo> &proxyInfo)
{
nsIProxyInfo *pi = nullptr;
proxyInfo.swap(pi);
PruneProxyInfo(info, &pi);
proxyInfo.swap(pi);
}
/**
* This method populates mHostFiltersArray from the given string.
@ -345,24 +367,9 @@ protected:
}
};
// An instance of this struct is allocated for each registered
// nsIProtocolProxyFilter and each nsIProtocolProxyChannelFilter.
struct FilterLink {
struct FilterLink *next;
uint32_t position;
nsCOMPtr<nsIProtocolProxyFilter> filter;
nsCOMPtr<nsIProtocolProxyChannelFilter> channelFilter;
FilterLink(uint32_t p, nsIProtocolProxyFilter *f)
: next(nullptr), position(p), filter(f), channelFilter(nullptr) {}
FilterLink(uint32_t p, nsIProtocolProxyChannelFilter *cf)
: next(nullptr), position(p), filter(nullptr), channelFilter(cf) {}
// Chain deletion to simplify cleaning up the filter links
~FilterLink() { if (next) delete next; }
};
private:
// Private methods to insert and remove FilterLinks from the FilterLink chain.
nsresult InsertFilterLink(FilterLink *link, uint32_t position);
nsresult InsertFilterLink(RefPtr<FilterLink>&& link);
nsresult RemoveFilterLink(nsISupports *givenObject);
protected:
@ -370,11 +377,10 @@ protected:
bool mFilterLocalHosts;
// Holds an array of HostInfo objects
nsTArray<nsAutoPtr<HostInfo> > mHostFiltersArray;
nsTArray<nsAutoPtr<HostInfo>> mHostFiltersArray;
// Points to the start of a sorted by position, singly linked list
// of FilterLink objects.
FilterLink *mFilters;
// Filters, always sorted by the position.
nsTArray<RefPtr<FilterLink>> mFilters;
uint32_t mProxyConfig;