mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 898524: Part 2 - Avoid IPC roundtrips for synthesized responses. r=mayhemer
This commit is contained in:
parent
162208d7d0
commit
8f259e6613
@ -38,10 +38,14 @@
|
||||
#include "PrivateBrowsingChannel.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsISecurityConsoleMessage.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
extern PRLogModuleInfo *gHttpLog;
|
||||
class nsPerformance;
|
||||
class nsISecurityConsoleMessage;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "mozilla/net/ChannelDiverterChild.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "InterceptedChannel.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::ipc;
|
||||
@ -321,7 +323,16 @@ HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
|
||||
|
||||
mTracingEnabled = false;
|
||||
|
||||
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
DoOnStartRequest(this, mListenerContext);
|
||||
|
||||
mSelfAddr = selfAddr;
|
||||
mPeerAddr = peerAddr;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
nsresult rv = mListener->OnStartRequest(aRequest, aContext);
|
||||
if (NS_FAILED(rv)) {
|
||||
Cancel(rv);
|
||||
return;
|
||||
@ -330,9 +341,6 @@ HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
|
||||
if (mResponseHead)
|
||||
SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
|
||||
|
||||
mSelfAddr = selfAddr;
|
||||
mPeerAddr = peerAddr;
|
||||
|
||||
if (mDivertingToParent) {
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
@ -442,41 +450,12 @@ HttpChannelChild::OnTransportAndData(const nsresult& channelStatus,
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// cache the progress sink so we don't have to query for it each time.
|
||||
if (!mProgressSink) {
|
||||
GetCallback(mProgressSink);
|
||||
}
|
||||
|
||||
// Hold queue lock throughout all three calls, else we might process a later
|
||||
// necko msg in between them.
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
// Block status/progress after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set.
|
||||
// Note: Progress events will be received directly in RecvOnProgress if
|
||||
// LOAD_BACKGROUND is set.
|
||||
// - JDUELL: may not need mStatus/mIsPending checks, given this is always called
|
||||
// during OnDataAvailable, and we've already checked mCanceled. Code
|
||||
// dupe'd from nsHttpChannel
|
||||
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
|
||||
!(mLoadFlags & LOAD_BACKGROUND))
|
||||
{
|
||||
// OnStatus
|
||||
//
|
||||
MOZ_ASSERT(transportStatus == NS_NET_STATUS_RECEIVING_FROM ||
|
||||
transportStatus == NS_NET_STATUS_READING);
|
||||
|
||||
nsAutoCString host;
|
||||
mURI->GetHost(host);
|
||||
mProgressSink->OnStatus(this, nullptr, transportStatus,
|
||||
NS_ConvertUTF8toUTF16(host).get());
|
||||
// OnProgress
|
||||
//
|
||||
if (progress > 0) {
|
||||
MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
|
||||
mProgressSink->OnProgress(this, nullptr, progress, progressMax);
|
||||
}
|
||||
}
|
||||
DoOnStatus(this, transportStatus);
|
||||
DoOnProgress(this, progress, progressMax);
|
||||
|
||||
// OnDataAvailable
|
||||
//
|
||||
@ -493,9 +472,70 @@ HttpChannelChild::OnTransportAndData(const nsresult& channelStatus,
|
||||
return;
|
||||
}
|
||||
|
||||
rv = mListener->OnDataAvailable(this, mListenerContext,
|
||||
stringStream, offset, count);
|
||||
DoOnDataAvailable(this, mListenerContext, stringStream, offset, count);
|
||||
stringStream->Close();
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status)
|
||||
{
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// cache the progress sink so we don't have to query for it each time.
|
||||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
||||
// block status/progress after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set.
|
||||
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
|
||||
!(mLoadFlags & LOAD_BACKGROUND))
|
||||
{
|
||||
// OnStatus
|
||||
//
|
||||
MOZ_ASSERT(status == NS_NET_STATUS_RECEIVING_FROM ||
|
||||
status == NS_NET_STATUS_READING);
|
||||
|
||||
nsAutoCString host;
|
||||
mURI->GetHost(host);
|
||||
mProgressSink->OnStatus(aRequest, nullptr, status,
|
||||
NS_ConvertUTF8toUTF16(host).get());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoOnProgress(nsIRequest* aRequest, uint64_t progress, uint64_t progressMax)
|
||||
{
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// cache the progress sink so we don't have to query for it each time.
|
||||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
||||
// block status/progress after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set.
|
||||
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
|
||||
!(mLoadFlags & LOAD_BACKGROUND))
|
||||
{
|
||||
// OnProgress
|
||||
//
|
||||
if (progress > 0) {
|
||||
MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
|
||||
mProgressSink->OnProgress(aRequest, nullptr, progress, progressMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
|
||||
nsIInputStream* aStream,
|
||||
uint64_t offset, uint32_t count)
|
||||
{
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
nsresult rv = mListener->OnDataAvailable(aRequest, aContext, aStream, offset, count);
|
||||
if (NS_FAILED(rv)) {
|
||||
Cancel(rv);
|
||||
}
|
||||
@ -545,24 +585,14 @@ HttpChannelChild::OnStopRequest(const nsresult& channelStatus)
|
||||
return;
|
||||
}
|
||||
|
||||
mIsPending = false;
|
||||
|
||||
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
|
||||
mStatus = channelStatus;
|
||||
}
|
||||
DoPreOnStopRequest(channelStatus);
|
||||
|
||||
{ // We must flush the queue before we Send__delete__
|
||||
// (although we really shouldn't receive any msgs after OnStop),
|
||||
// so make sure this goes out of scope before then.
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
mListener->OnStopRequest(this, mListenerContext, mStatus);
|
||||
|
||||
mListener = 0;
|
||||
mListenerContext = 0;
|
||||
mCacheEntryAvailable = false;
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
|
||||
DoOnStopRequest(this, mListenerContext);
|
||||
}
|
||||
|
||||
if (mLoadFlags & LOAD_DOCUMENT_URI) {
|
||||
@ -576,6 +606,30 @@ HttpChannelChild::OnStopRequest(const nsresult& channelStatus)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoPreOnStopRequest(nsresult aStatus)
|
||||
{
|
||||
mIsPending = false;
|
||||
|
||||
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
|
||||
mStatus = aStatus;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
MOZ_ASSERT(!mIsPending);
|
||||
|
||||
mListener->OnStopRequest(aRequest, aContext, mStatus);
|
||||
|
||||
mListener = 0;
|
||||
mListenerContext = 0;
|
||||
mCacheEntryAvailable = false;
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
|
||||
}
|
||||
|
||||
class ProgressEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
@ -1103,14 +1157,19 @@ HttpChannelChild::Cancel(nsresult status)
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
// SendSuspend only once, when suspend goes from 0 to 1.
|
||||
// Don't SendSuspend at all if we're diverting callbacks to the parent;
|
||||
// suspend will be called at the correct time in the parent itself.
|
||||
if (!mSuspendCount++ && !mDivertingToParent) {
|
||||
SendSuspend();
|
||||
mSuspendSent = true;
|
||||
if (RemoteChannelExists()) {
|
||||
SendSuspend();
|
||||
mSuspendSent = true;
|
||||
}
|
||||
}
|
||||
if (mSynthesizedResponsePump) {
|
||||
mSynthesizedResponsePump->Suspend();
|
||||
}
|
||||
mEventQ->Suspend();
|
||||
|
||||
@ -1120,7 +1179,7 @@ HttpChannelChild::Suspend()
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::Resume()
|
||||
{
|
||||
NS_ENSURE_TRUE(RemoteChannelExists(), NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
@ -1130,12 +1189,17 @@ HttpChannelChild::Resume()
|
||||
// suspend was sent earlier); otherwise, resume will be called at the correct
|
||||
// time in the parent itself.
|
||||
if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) {
|
||||
SendResume();
|
||||
if (RemoteChannelExists()) {
|
||||
SendResume();
|
||||
}
|
||||
if (mCallOnResume) {
|
||||
AsyncCall(mCallOnResume);
|
||||
mCallOnResume = nullptr;
|
||||
}
|
||||
}
|
||||
if (mSynthesizedResponsePump) {
|
||||
mSynthesizedResponsePump->Resume();
|
||||
}
|
||||
mEventQ->Resume();
|
||||
|
||||
return rv;
|
||||
@ -1177,6 +1241,90 @@ HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// A stream listener interposed between the nsInputStreamPump used for intercepted channels
|
||||
// and this channel's original listener. This is only used to ensure the original listener
|
||||
// sees the channel as the request object, and to synthesize OnStatus and OnProgress notifications.
|
||||
class InterceptStreamListener : public nsIStreamListener
|
||||
, public nsIProgressEventSink
|
||||
{
|
||||
nsRefPtr<HttpChannelChild> mOwner;
|
||||
nsCOMPtr<nsISupports> mContext;
|
||||
virtual ~InterceptStreamListener() {}
|
||||
public:
|
||||
InterceptStreamListener(HttpChannelChild* aOwner, nsISupports* aContext)
|
||||
: mOwner(aOwner)
|
||||
, mContext(aContext)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(InterceptStreamListener,
|
||||
nsIStreamListener,
|
||||
nsIRequestObserver,
|
||||
nsIProgressEventSink)
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptStreamListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
mOwner->DoOnStartRequest(mOwner, mContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptStreamListener::OnStatus(nsIRequest* aRequest, nsISupports* aContext,
|
||||
nsresult status, const char16_t* aStatusArg)
|
||||
{
|
||||
mOwner->DoOnStatus(mOwner, status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptStreamListener::OnProgress(nsIRequest* aRequest, nsISupports* aContext,
|
||||
uint64_t aProgress, uint64_t aProgressMax)
|
||||
{
|
||||
mOwner->DoOnProgress(mOwner, aProgress, aProgressMax);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptStreamListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
|
||||
nsIInputStream* aInputStream, uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
uint32_t loadFlags;
|
||||
mOwner->GetLoadFlags(&loadFlags);
|
||||
|
||||
if (!(loadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mOwner->GetURI(getter_AddRefs(uri));
|
||||
|
||||
nsAutoCString host;
|
||||
uri->GetHost(host);
|
||||
|
||||
OnStatus(mOwner, aContext, NS_NET_STATUS_READING, NS_ConvertUTF8toUTF16(host).get());
|
||||
|
||||
uint64_t progressMax(uint64_t(mOwner->GetResponseHead()->ContentLength()));
|
||||
uint64_t progress = aOffset + uint64_t(aCount);
|
||||
OnProgress(mOwner, aContext, progress, progressMax);
|
||||
}
|
||||
|
||||
mOwner->DoOnDataAvailable(mOwner, mContext, aInputStream, aOffset, aCount);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptStreamListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatusCode)
|
||||
{
|
||||
mOwner->DoPreOnStopRequest(aStatusCode);
|
||||
mOwner->DoOnStopRequest(mOwner, mContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
||||
{
|
||||
@ -1234,6 +1382,24 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (ShouldIntercept()) {
|
||||
nsCOMPtr<nsINetworkInterceptController> controller;
|
||||
GetCallback(controller);
|
||||
|
||||
mInterceptListener = new InterceptStreamListener(this, mListenerContext);
|
||||
|
||||
nsRefPtr<InterceptedChannelContent> intercepted =
|
||||
new InterceptedChannelContent(this, controller, mInterceptListener);
|
||||
intercepted->NotifyController();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return ContinueAsyncOpen();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelChild::ContinueAsyncOpen()
|
||||
{
|
||||
nsCString appCacheClientId;
|
||||
if (mInheritApplicationCache) {
|
||||
// Pick up an application cache from the notification
|
||||
@ -1243,7 +1409,7 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
||||
|
||||
if (appCacheContainer) {
|
||||
nsCOMPtr<nsIApplicationCache> appCache;
|
||||
rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache));
|
||||
nsresult rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache));
|
||||
if (NS_SUCCEEDED(rv) && appCache) {
|
||||
appCache->GetClientID(appCacheClientId);
|
||||
}
|
||||
@ -1709,4 +1875,29 @@ HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::ResetInterception()
|
||||
{
|
||||
mInterceptListener = nullptr;
|
||||
|
||||
// Continue with the original cross-process request
|
||||
nsresult rv = ContinueAsyncOpen();
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OverrideWithSynthesizedResponse(nsHttpResponseHead* aResponseHead,
|
||||
nsInputStreamPump* aPump)
|
||||
{
|
||||
mSynthesizedResponsePump = aPump;
|
||||
mResponseHead = aResponseHead;
|
||||
|
||||
// if this channel has been suspended previously, the pump needs to be
|
||||
// correspondingly suspended now that it exists.
|
||||
for (uint32_t i = 0; i < mSuspendCount; i++) {
|
||||
nsresult rv = mSynthesizedResponsePump->Suspend();
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
@ -30,9 +30,14 @@
|
||||
#include "nsIDivertableChannel.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
|
||||
class nsInputStreamPump;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class InterceptedChannelContent;
|
||||
class InterceptStreamListener;
|
||||
|
||||
class HttpChannelChild MOZ_FINAL : public PHttpChannelChild
|
||||
, public HttpBaseChannel
|
||||
, public HttpAsyncAborter<HttpChannelChild>
|
||||
@ -134,9 +139,28 @@ protected:
|
||||
virtual void DoNotifyListenerCleanup();
|
||||
|
||||
private:
|
||||
nsresult ContinueAsyncOpen();
|
||||
|
||||
void DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
|
||||
void DoOnStatus(nsIRequest* aRequest, nsresult status);
|
||||
void DoOnProgress(nsIRequest* aRequest, uint64_t progress, uint64_t progressMax);
|
||||
void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
|
||||
uint64_t offset, uint32_t count);
|
||||
void DoPreOnStopRequest(nsresult aStatus);
|
||||
void DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext);
|
||||
|
||||
// Discard the prior interception and continue with the original network request.
|
||||
void ResetInterception();
|
||||
|
||||
// Override this channel's pending response with a synthesized one. The content will be
|
||||
// asynchronously read from the pump.
|
||||
void OverrideWithSynthesizedResponse(nsHttpResponseHead* aResponseHead, nsInputStreamPump* aPump);
|
||||
|
||||
RequestHeaderTuples mClientSetRequestHeaders;
|
||||
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
|
||||
nsCOMPtr<nsISupports> mSecurityInfo;
|
||||
nsRefPtr<InterceptStreamListener> mInterceptListener;
|
||||
nsRefPtr<nsInputStreamPump> mSynthesizedResponsePump;
|
||||
|
||||
bool mIsFromCache;
|
||||
bool mCacheEntryAvailable;
|
||||
@ -205,6 +229,8 @@ private:
|
||||
friend class Redirect3Event;
|
||||
friend class DeleteSelfEvent;
|
||||
friend class HttpAsyncAborter<HttpChannelChild>;
|
||||
friend class InterceptStreamListener;
|
||||
friend class InterceptedChannelContent;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
232
netwerk/protocol/http/InterceptedChannel.cpp
Normal file
232
netwerk/protocol/http/InterceptedChannel.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: -*- */
|
||||
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
|
||||
/* 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 "HttpLog.h"
|
||||
|
||||
#include "InterceptedChannel.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "HttpChannelChild.h"
|
||||
#include "nsHttpResponseHead.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
extern nsresult
|
||||
DoAddCacheEntryHeaders(nsHttpChannel *self,
|
||||
nsICacheEntry *entry,
|
||||
nsHttpRequestHead *requestHead,
|
||||
nsHttpResponseHead *responseHead,
|
||||
nsISupports *securityInfo);
|
||||
|
||||
NS_IMPL_ISUPPORTS(InterceptedChannelBase, nsIInterceptedChannel)
|
||||
|
||||
InterceptedChannelBase::InterceptedChannelBase(nsINetworkInterceptController* aController)
|
||||
: mController(aController)
|
||||
{
|
||||
}
|
||||
|
||||
InterceptedChannelBase::~InterceptedChannelBase()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelBase::EnsureSynthesizedResponse()
|
||||
{
|
||||
if (mSynthesizedResponseHead.isNothing()) {
|
||||
mSynthesizedResponseHead.emplace();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelBase::DoNotifyController(nsIOutputStream* aOut)
|
||||
{
|
||||
nsresult rv = mController->ChannelIntercepted(this, aOut);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
InterceptedChannelBase::DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
|
||||
// Overwrite any existing header.
|
||||
nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
InterceptedChannelChrome::InterceptedChannelChrome(nsHttpChannel* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsICacheEntry* aEntry)
|
||||
: InterceptedChannelBase(aController)
|
||||
, mChannel(aChannel)
|
||||
, mSynthesizedCacheEntry(aEntry)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelChrome::NotifyController()
|
||||
{
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
|
||||
nsresult rv = mSynthesizedCacheEntry->OpenOutputStream(0, getter_AddRefs(out));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
DoNotifyController(out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::ResetInterception()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mSynthesizedCacheEntry->AsyncDoom(nullptr);
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::SynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
if (!mSynthesizedCacheEntry) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeHeader(aName, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::FinishSynthesizedResponse()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
mChannel->MarkIntercepted();
|
||||
|
||||
// First we ensure the appropriate metadata is set on the synthesized cache entry
|
||||
// (i.e. the flattened response head)
|
||||
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
nsresult rv = mChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = DoAddCacheEntryHeaders(mChannel, mSynthesizedCacheEntry,
|
||||
mChannel->GetRequestHead(),
|
||||
mSynthesizedResponseHead.ptr(), securityInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
bool usingSSL = false;
|
||||
uri->SchemeIs("https", &usingSSL);
|
||||
|
||||
// Then we open a real cache entry to read the synthesized response from.
|
||||
rv = mChannel->OpenCacheEntry(usingSSL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
if (!mChannel->AwaitingCacheCallbacks()) {
|
||||
rv = mChannel->ContinueConnect();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsIStreamListener* aListener)
|
||||
: InterceptedChannelBase(aController)
|
||||
, mChannel(aChannel)
|
||||
, mStreamListener(aListener)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelContent::NotifyController()
|
||||
{
|
||||
nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput),
|
||||
getter_AddRefs(mSynthesizedOutput),
|
||||
0, UINT32_MAX, true, true);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
DoNotifyController(mSynthesizedOutput);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::ResetInterception()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mSynthesizedOutput = nullptr;
|
||||
mSynthesizedInput = nullptr;
|
||||
|
||||
mChannel->ResetInterception();
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::SynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
if (!mSynthesizedOutput) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeHeader(aName, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::FinishSynthesizedResponse()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
nsresult rv = nsInputStreamPump::Create(getter_AddRefs(mStoragePump), mSynthesizedInput,
|
||||
int64_t(-1), int64_t(-1), 0, 0, true);
|
||||
if (NS_FAILED(rv)) {
|
||||
mSynthesizedInput->Close();
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSynthesizedOutput = nullptr;
|
||||
|
||||
rv = mStoragePump->AsyncRead(mStreamListener, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ptr(), mStoragePump);
|
||||
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
96
netwerk/protocol/http/InterceptedChannel.h
Normal file
96
netwerk/protocol/http/InterceptedChannel.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
|
||||
/* 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 InterceptedChannel_h
|
||||
#define InterceptedChannel_h
|
||||
|
||||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
class nsICacheEntry;
|
||||
class nsInputStreamPump;
|
||||
class nsIStorageStream;
|
||||
class nsIStreamListener;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsHttpChannel;
|
||||
class HttpChannelChild;
|
||||
class nsHttpResponseHead;
|
||||
|
||||
// An object representing a channel that has been intercepted. This avoids complicating
|
||||
// the actual channel implementation with the details of synthesizing responses.
|
||||
class InterceptedChannelBase : public nsIInterceptedChannel {
|
||||
protected:
|
||||
// The interception controller to notify about the successful channel interception
|
||||
nsCOMPtr<nsINetworkInterceptController> mController;
|
||||
|
||||
// Response head for use when synthesizing
|
||||
Maybe<nsHttpResponseHead> mSynthesizedResponseHead;
|
||||
|
||||
void EnsureSynthesizedResponse();
|
||||
void DoNotifyController(nsIOutputStream* aOut);
|
||||
nsresult DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue);
|
||||
|
||||
virtual ~InterceptedChannelBase();
|
||||
public:
|
||||
InterceptedChannelBase(nsINetworkInterceptController* aController);
|
||||
|
||||
// Notify the interception controller that the channel has been intercepted
|
||||
// and prepare the response body output stream.
|
||||
virtual void NotifyController() = 0;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
};
|
||||
|
||||
class InterceptedChannelChrome : public InterceptedChannelBase
|
||||
{
|
||||
// The actual channel being intercepted.
|
||||
nsRefPtr<nsHttpChannel> mChannel;
|
||||
|
||||
// Writeable cache entry for use when synthesizing a response in a parent process
|
||||
nsCOMPtr<nsICacheEntry> mSynthesizedCacheEntry;
|
||||
public:
|
||||
InterceptedChannelChrome(nsHttpChannel* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsICacheEntry* aEntry);
|
||||
|
||||
NS_DECL_NSIINTERCEPTEDCHANNEL
|
||||
|
||||
virtual void NotifyController() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class InterceptedChannelContent : public InterceptedChannelBase
|
||||
{
|
||||
// The actual channel being intercepted.
|
||||
nsRefPtr<HttpChannelChild> mChannel;
|
||||
|
||||
// Writeable buffer for use when synthesizing a response in a child process
|
||||
nsCOMPtr<nsIOutputStream> mSynthesizedOutput;
|
||||
nsCOMPtr<nsIInputStream> mSynthesizedInput;
|
||||
|
||||
// Pump to read the synthesized body in child processes
|
||||
nsRefPtr<nsInputStreamPump> mStoragePump;
|
||||
|
||||
// Listener for the synthesized response to fix up the notifications before they reach
|
||||
// the actual channel.
|
||||
nsCOMPtr<nsIStreamListener> mStreamListener;
|
||||
public:
|
||||
InterceptedChannelContent(HttpChannelChild* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsIStreamListener* aListener);
|
||||
|
||||
NS_DECL_NSIINTERCEPTEDCHANNEL
|
||||
|
||||
virtual void NotifyController() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // InterceptedChannel_h
|
@ -14,6 +14,7 @@
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsITimer.h"
|
||||
#include "NullHttpTransaction.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
// a TLSFilterTransaction wraps another nsAHttpTransaction but
|
||||
// applies a encode/decode filter of TLS onto the ReadSegments
|
||||
|
@ -56,6 +56,7 @@ UNIFIED_SOURCES += [
|
||||
'HttpChannelParent.cpp',
|
||||
'HttpChannelParentListener.cpp',
|
||||
'HttpInfo.cpp',
|
||||
'InterceptedChannel.cpp',
|
||||
'nsHttp.cpp',
|
||||
'nsHttpActivityDistributor.cpp',
|
||||
'nsHttpAuthManager.cpp',
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "CacheObserver.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "AlternateServices.h"
|
||||
#include "InterceptedChannel.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
@ -200,43 +201,6 @@ AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
|
||||
MOZ_EVENT_TRACER_DONE(channel, "net::http::redirect-callbacks");
|
||||
}
|
||||
|
||||
// An object representing a channel that has been intercepted. This avoids complicating
|
||||
// the actual channel implementation with the details of synthesizing responses.
|
||||
class InterceptedChannel : public nsIInterceptedChannel
|
||||
{
|
||||
// The actual channel being intercepted
|
||||
nsRefPtr<nsHttpChannel> mChannel;
|
||||
|
||||
// The interception controller to notify about the successful channel interception
|
||||
nsCOMPtr<nsINetworkInterceptController> mController;
|
||||
|
||||
// Writeable cache entry for use when synthesizing a response
|
||||
nsCOMPtr<nsICacheEntry> mSynthesizedCacheEntry;
|
||||
|
||||
// Response head for use when synthesizing
|
||||
Maybe<nsHttpResponseHead> mSynthesizedResponseHead;
|
||||
|
||||
void EnsureSynthesizedResponse();
|
||||
|
||||
virtual ~InterceptedChannel() {}
|
||||
public:
|
||||
InterceptedChannel(nsHttpChannel* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsICacheEntry* aEntry)
|
||||
: mChannel(aChannel)
|
||||
, mController(aController)
|
||||
, mSynthesizedCacheEntry(aEntry)
|
||||
{
|
||||
}
|
||||
|
||||
// Notify the interception controller that the channel has been intercepted
|
||||
// and prepare the response body output stream.
|
||||
void NotifyController();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIINTERCEPTEDCHANNEL
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -2829,7 +2793,8 @@ nsHttpChannel::OpenCacheEntry(bool isHttps)
|
||||
nsCOMPtr<nsINetworkInterceptController> controller;
|
||||
GetCallback(controller);
|
||||
|
||||
nsRefPtr<InterceptedChannel> intercepted = new InterceptedChannel(this, controller, entry);
|
||||
nsRefPtr<InterceptedChannelChrome> intercepted =
|
||||
new InterceptedChannelChrome(this, controller, entry);
|
||||
intercepted->NotifyController();
|
||||
} else {
|
||||
rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, this);
|
||||
@ -6514,102 +6479,4 @@ nsHttpChannel::AwaitingCacheCallbacks()
|
||||
return mCacheEntriesToWaitFor != 0;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(InterceptedChannel, nsIInterceptedChannel)
|
||||
|
||||
void
|
||||
InterceptedChannel::NotifyController()
|
||||
{
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
nsresult rv = mSynthesizedCacheEntry->OpenOutputStream(0, getter_AddRefs(out));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
rv = mController->ChannelIntercepted(this, out);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannel::EnsureSynthesizedResponse()
|
||||
{
|
||||
if (mSynthesizedResponseHead.isNothing()) {
|
||||
mSynthesizedResponseHead.emplace();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannel::ResetInterception()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mSynthesizedCacheEntry->AsyncDoom(nullptr);
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannel::SynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
if (!mSynthesizedCacheEntry) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
|
||||
// Overwrite any existing header.
|
||||
nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannel::FinishSynthesizedResponse()
|
||||
{
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mChannel->MarkIntercepted();
|
||||
|
||||
// First we ensure the appropriate metadata is set on the synthesized cache entry
|
||||
// (i.e. the flattened response head)
|
||||
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
nsresult rv = mChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
rv = DoAddCacheEntryHeaders(mChannel, mSynthesizedCacheEntry, mChannel->GetRequestHead(),
|
||||
mSynthesizedResponseHead.ptr(), securityInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
bool usingSSL = false;
|
||||
uri->SchemeIs("https", &usingSSL);
|
||||
|
||||
// Then we open a real cache entry to read the synthesized response from.
|
||||
rv = mChannel->OpenCacheEntry(usingSSL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
if (!mChannel->AwaitingCacheCallbacks()) {
|
||||
rv = mChannel->ContinueConnect();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsProxyRelease.h"
|
||||
#include "prinrval.h"
|
||||
#include "TunnelUtils.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
|
@ -19,16 +19,28 @@ function make_uri(url) {
|
||||
// ensure the cache service is prepped when running the test
|
||||
Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService);
|
||||
|
||||
var gotOnProgress;
|
||||
var gotOnStatus;
|
||||
|
||||
function make_channel(url, body, cb) {
|
||||
gotOnProgress = false;
|
||||
gotOnStatus = false;
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.notificationCallbacks = {
|
||||
numChecks: 0,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterceptController,
|
||||
Ci.nsIInterfaceRequestor]),
|
||||
Ci.nsIInterfaceRequestor,
|
||||
Ci.nsIProgressEventSink]),
|
||||
getInterface: function(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
onProgress: function(request, context, progress, progressMax) {
|
||||
gotOnProgress = true;
|
||||
},
|
||||
onStatus: function(request, context, status, statusArg) {
|
||||
gotOnStatus = true;
|
||||
},
|
||||
shouldPrepareForIntercept: function() {
|
||||
do_check_eq(this.numChecks, 0);
|
||||
this.numChecks++;
|
||||
@ -72,16 +84,22 @@ function run_test() {
|
||||
|
||||
function handle_synthesized_response(request, buffer) {
|
||||
do_check_eq(buffer, NON_REMOTE_BODY);
|
||||
do_check_true(gotOnStatus);
|
||||
do_check_true(gotOnProgress);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function handle_synthesized_response_2(request, buffer) {
|
||||
do_check_eq(buffer, NON_REMOTE_BODY_2);
|
||||
do_check_true(gotOnStatus);
|
||||
do_check_true(gotOnProgress);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function handle_remote_response(request, buffer) {
|
||||
do_check_eq(buffer, REMOTE_BODY);
|
||||
do_check_true(gotOnStatus);
|
||||
do_check_true(gotOnProgress);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
@ -140,6 +158,24 @@ add_test(function() {
|
||||
chan.asyncOpen(new ChannelListener(handle_remote_response, null), null);
|
||||
});
|
||||
|
||||
// ensure that the intercepted channel supports suspend/resume
|
||||
add_test(function() {
|
||||
var chan = make_channel(URL + '/body', null, function(intercepted, stream) {
|
||||
var synthesized = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
synthesized.data = NON_REMOTE_BODY;
|
||||
|
||||
NetUtil.asyncCopy(synthesized, stream, function() {
|
||||
// set the content-type to ensure that the stream converter doesn't hold up notifications
|
||||
// and cause the test to fail
|
||||
intercepted.synthesizeHeader("Content-Type", "text/plain");
|
||||
intercepted.finishSynthesizedResponse();
|
||||
});
|
||||
});
|
||||
chan.asyncOpen(new ChannelListener(handle_synthesized_response, null,
|
||||
CL_ALLOW_UNKNOWN_CL | CL_SUSPEND | CL_EXPECT_3S_DELAY), null);
|
||||
});
|
||||
|
||||
add_test(function() {
|
||||
httpServer.stop(run_next_test);
|
||||
});
|
||||
|
3
netwerk/test/unit_ipc/test_synthesized_response_wrap.js
Normal file
3
netwerk/test/unit_ipc/test_synthesized_response_wrap.js
Normal file
@ -0,0 +1,3 @@
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_synthesized_response.js");
|
||||
}
|
@ -33,6 +33,7 @@ skip-if = true
|
||||
[test_reentrancy_wrap.js]
|
||||
[test_resumable_channel_wrap.js]
|
||||
[test_simple_wrap.js]
|
||||
[test_synthesized_response_wrap.js]
|
||||
[test_xmlhttprequest_wrap.js]
|
||||
[test_XHR_redirects.js]
|
||||
[test_redirect_history_wrap.js]
|
||||
|
Loading…
Reference in New Issue
Block a user