--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -64,26 +64,27 @@
extern PRThread *gSocketThread;
using namespace mozilla;
namespace mozilla {
namespace net {
-NS_IMPL_ISUPPORTS11(WebSocketChannel,
+NS_IMPL_ISUPPORTS12(WebSocketChannel,
nsIWebSocketChannel,
nsIHttpUpgradeListener,
nsIRequestObserver,
nsIStreamListener,
nsIProtocolHandler,
nsIInputStreamCallback,
nsIOutputStreamCallback,
nsITimerCallback,
nsIDNSListener,
+ nsIProtocolProxyCallback,
nsIInterfaceRequestor,
nsIChannelEventSink)
// We implement RFC 6455, which uses Sec-WebSocket-Version: 13 on the wire.
#define SEC_WEBSOCKET_VERSION "13"
/*
* About SSL unsigned certificates
@@ -989,17 +990,17 @@ WebSocketChannel::WebSocketChannel() :
WebSocketChannel::~WebSocketChannel()
{
LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
if (mWasOpened) {
MOZ_ASSERT(mCalledOnStop, "WebSocket was opened but OnStop was not called");
MOZ_ASSERT(mStopped, "WebSocket was opened but never stopped");
}
- MOZ_ASSERT(!mDNSRequest, "DNS Request still alive at destruction");
+ MOZ_ASSERT(!mCancelable, "DNS/Proxy Request still alive at destruction");
MOZ_ASSERT(!mConnecting, "Should not be connecting in destructor");
moz_free(mBuffer);
moz_free(mDynamicOutput);
delete mCompressor;
delete mCurrentOut;
while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
@@ -1954,19 +1955,19 @@ WebSocketChannel::StopSession(nsresult r
mLingeringCloseTimer->InitWithCallback(this, kLingeringCloseTimeout,
nsITimer::TYPE_ONE_SHOT);
else
CleanupConnection();
} else {
CleanupConnection();
}
- if (mDNSRequest) {
- mDNSRequest->Cancel(NS_ERROR_UNEXPECTED);
- mDNSRequest = nullptr;
+ if (mCancelable) {
+ mCancelable->Cancel(NS_ERROR_UNEXPECTED);
+ mCancelable = nullptr;
}
mInflateReader = nullptr;
mInflateStream = nullptr;
delete mCompressor;
mCompressor = nullptr;
@@ -2193,44 +2194,61 @@ WebSocketChannel::SetupRequest()
NS_ENSURE_SUCCESS(rv, rv);
LOG(("WebSocketChannel::SetupRequest: expected server key %s\n",
mHashedSecret.get()));
return NS_OK;
}
nsresult
-WebSocketChannel::ApplyForAdmission()
+WebSocketChannel::DoAdmissionDNS()
{
- LOG(("WebSocketChannel::ApplyForAdmission() %p\n", this));
-
- // Websockets has a policy of 1 session at a time being allowed in the
- // CONNECTING state per server IP address (not hostname)
-
nsresult rv;
- nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
nsCString hostName;
rv = mURI->GetHost(hostName);
NS_ENSURE_SUCCESS(rv, rv);
mAddress = hostName;
rv = mURI->GetPort(&mPort);
NS_ENSURE_SUCCESS(rv, rv);
if (mPort == -1)
mPort = (mEncrypted ? kDefaultWSSPort : kDefaultWSPort);
-
- // expect the callback in ::OnLookupComplete
- LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
- dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
+ MOZ_ASSERT(!mCancelable);
+ return dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mCancelable));
+}
+
+nsresult
+WebSocketChannel::ApplyForAdmission()
+{
+ LOG(("WebSocketChannel::ApplyForAdmission() %p\n", this));
+
+ // Websockets has a policy of 1 session at a time being allowed in the
+ // CONNECTING state per server IP address (not hostname)
+
+ // Check to see if a proxy is being used before making DNS call
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
+
+ if (!pps) {
+ // go straight to DNS
+ // expect the callback in ::OnLookupComplete
+ LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
+ return DoAdmissionDNS();
+ }
+
+ MOZ_ASSERT(!mCancelable);
+
+ return pps->AsyncResolve(mURI,
+ nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
+ nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
+ this, getter_AddRefs(mCancelable));
}
// Called after both OnStartRequest and OnTransportAvailable have
// executed. This essentially ends the handshake and starts the websockets
// protocol state machine.
nsresult
WebSocketChannel::StartWebsocketData()
{
@@ -2295,48 +2313,79 @@ WebSocketChannel::ReportConnectionTeleme
LOG(("WebSocketChannel::ReportConnectionTelemetry() %p %d", this, value));
Telemetry::Accumulate(Telemetry::WEBSOCKETS_HANDSHAKE_TYPE, value);
}
// nsIDNSListener
NS_IMETHODIMP
WebSocketChannel::OnLookupComplete(nsICancelable *aRequest,
- nsIDNSRecord *aRecord,
- nsresult aStatus)
+ nsIDNSRecord *aRecord,
+ nsresult aStatus)
{
LOG(("WebSocketChannel::OnLookupComplete() %p [%p %p %x]\n",
this, aRequest, aRecord, aStatus));
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
- NS_ABORT_IF_FALSE(aRequest == mDNSRequest || mStopped,
- "wrong dns request");
if (mStopped) {
LOG(("WebSocketChannel::OnLookupComplete: Request Already Stopped\n"));
+ mCancelable = nullptr;
return NS_OK;
}
- mDNSRequest = nullptr;
+ mCancelable = nullptr;
// These failures are not fatal - we just use the hostname as the key
if (NS_FAILED(aStatus)) {
LOG(("WebSocketChannel::OnLookupComplete: No DNS Response\n"));
+
+ // set host in case we got here without calling DoAdmissionDNS()
+ mURI->GetHost(mAddress);
} else {
nsresult rv = aRecord->GetNextAddrAsString(mAddress);
if (NS_FAILED(rv))
LOG(("WebSocketChannel::OnLookupComplete: Failed GetNextAddr\n"));
}
LOG(("WebSocket OnLookupComplete: Proceeding to ConditionallyConnect\n"));
sWebSocketAdmissions->ConditionallyConnect(this);
return NS_OK;
}
+// nsIProtocolProxyCallback
+NS_IMETHODIMP
+WebSocketChannel::OnProxyAvailable(nsICancelable *aRequest, nsIURI *aURI,
+ nsIProxyInfo *pi, nsresult status)
+{
+ if (mStopped) {
+ LOG(("WebSocketChannel::OnProxyAvailable: [%p] Request Already Stopped\n", this));
+ mCancelable = nullptr;
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(aRequest == mCancelable);
+ mCancelable = nullptr;
+
+ nsAutoCString type;
+ if (NS_SUCCEEDED(status) && pi &&
+ NS_SUCCEEDED(pi->GetType(type)) &&
+ !type.EqualsLiteral("direct")) {
+ LOG(("WebSocket OnProxyAvailable [%p] Proxy found skip DNS lookup\n", this));
+ // call DNS callback directly without DNS resolver
+ OnLookupComplete(nullptr, nullptr, NS_ERROR_FAILURE);
+ return NS_OK;
+ }
+
+ LOG(("WebSocketChannel::OnProxyAvailable[%] checking DNS resolution\n", this));
+ DoAdmissionDNS();
+ return NS_OK;
+}
+
// nsIInterfaceRequestor
NS_IMETHODIMP
WebSocketChannel::GetInterface(const nsIID & iid, void **result)
{
LOG(("WebSocketChannel::GetInterface() %p\n", this));
if (iid.Equals(NS_GET_IID(nsIChannelEventSink)))
--- a/netwerk/protocol/websocket/WebSocketChannel.h
+++ b/netwerk/protocol/websocket/WebSocketChannel.h
@@ -9,16 +9,17 @@
#include "nsISupports.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStreamListener.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsITimer.h"
#include "nsIDNSListener.h"
+#include "nsIProtocolProxyCallback.h"
#include "nsIChannelEventSink.h"
#include "nsIHttpChannelInternal.h"
#include "BaseWebSocketChannel.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
#include "nsProxyRelease.h" // for nsMainThreadPtrHandle
#endif
@@ -56,28 +57,30 @@ enum wsConnectingState {
class WebSocketChannel : public BaseWebSocketChannel,
public nsIHttpUpgradeListener,
public nsIStreamListener,
public nsIInputStreamCallback,
public nsIOutputStreamCallback,
public nsITimerCallback,
public nsIDNSListener,
+ public nsIProtocolProxyCallback,
public nsIInterfaceRequestor,
public nsIChannelEventSink
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIHTTPUPGRADELISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSIOUTPUTSTREAMCALLBACK
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIDNSLISTENER
+ NS_DECL_NSIPROTOCOLPROXYCALLBACK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
// nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us
//
NS_IMETHOD AsyncOpen(nsIURI *aURI,
const nsACString &aOrigin,
nsIWebSocketListener *aListener,
@@ -129,16 +132,17 @@ private:
void DeleteCurrentOutGoingMessage();
void GeneratePong(uint8_t *payload, uint32_t len);
void GeneratePing();
void BeginOpen();
nsresult HandleExtensions();
nsresult SetupRequest();
nsresult ApplyForAdmission();
+ nsresult DoAdmissionDNS();
nsresult StartWebsocketData();
uint16_t ResultToCloseCode(nsresult resultCode);
void ReportConnectionTelemetry();
void StopSession(nsresult reason);
void AbortSession(nsresult reason);
void ReleaseSession();
void CleanupConnection();
@@ -160,17 +164,17 @@ private:
mPingOutstanding = 0;
mPingTimer->SetDelay(mPingInterval);
}
}
nsCOMPtr<nsIEventTarget> mSocketThread;
nsCOMPtr<nsIHttpChannelInternal> mChannel;
nsCOMPtr<nsIHttpChannel> mHttpChannel;
- nsCOMPtr<nsICancelable> mDNSRequest;
+ nsCOMPtr<nsICancelable> mCancelable;
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
nsCString mHashedSecret;
// Used as key for connection managment: Initially set to hostname from URI,
// then to IP address (unless we're leaving DNS resolution to a proxy server)
nsCString mAddress;
--- a/xpcom/glue/nsISupportsImpl.h
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -894,16 +894,34 @@ NS_IMETHODIMP _class::QueryInterface(REF
NS_INTERFACE_TABLE_ENTRY(_class, _i7) \
NS_INTERFACE_TABLE_ENTRY(_class, _i8) \
NS_INTERFACE_TABLE_ENTRY(_class, _i9) \
NS_INTERFACE_TABLE_ENTRY(_class, _i10) \
NS_INTERFACE_TABLE_ENTRY(_class, _i11) \
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsISupports, _i1) \
NS_INTERFACE_TABLE_END
+#define NS_INTERFACE_TABLE12(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, \
+ _i8, _i9, _i10, _i11, _i12) \
+ NS_INTERFACE_TABLE_BEGIN \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i1) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i2) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i3) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i4) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i5) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i6) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i7) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i8) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i9) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i10) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i11) \
+ NS_INTERFACE_TABLE_ENTRY(_class, _i12) \
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsISupports, _i1) \
+ NS_INTERFACE_TABLE_END
+
#define NS_IMPL_QUERY_INTERFACE0(_class) \
NS_INTERFACE_TABLE_HEAD(_class) \
NS_INTERFACE_TABLE0(_class) \
NS_INTERFACE_TABLE_TAIL
#define NS_IMPL_QUERY_INTERFACE1(_class, _i1) \
NS_INTERFACE_TABLE_HEAD(_class) \
NS_INTERFACE_TABLE1(_class, _i1) \
@@ -960,16 +978,23 @@ NS_IMETHODIMP _class::QueryInterface(REF
#define NS_IMPL_QUERY_INTERFACE11(_class, _i1, _i2, _i3, _i4, _i5, _i6, \
_i7, _i8, _i9, _i10, _i11) \
NS_INTERFACE_TABLE_HEAD(_class) \
NS_INTERFACE_TABLE11(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
_i9, _i10, _i11) \
NS_INTERFACE_TABLE_TAIL
+#define NS_IMPL_QUERY_INTERFACE12(_class, _i1, _i2, _i3, _i4, _i5, _i6, \
+ _i7, _i8, _i9, _i10, _i11, _i12) \
+ NS_INTERFACE_TABLE_HEAD(_class) \
+ NS_INTERFACE_TABLE12(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
+ _i9, _i10, _i11, _i12) \
+ NS_INTERFACE_TABLE_TAIL
+
/**
* Declare that you're going to inherit from something that already
* implements nsISupports, but also implements an additional interface, thus
* causing an ambiguity. In this case you don't need another mRefCnt, you
* just need to forward the definitions to the appropriate superclass. E.g.
*
* class Bar : public Foo, public nsIBar { // both provide nsISupports
@@ -1283,16 +1308,23 @@ NS_IMETHODIMP_(nsrefcnt) Class::Release(
#define NS_IMPL_ISUPPORTS11(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
_i9, _i10, _i11) \
NS_IMPL_ADDREF(_class) \
NS_IMPL_RELEASE(_class) \
NS_IMPL_QUERY_INTERFACE11(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
_i9, _i10, _i11)
+#define NS_IMPL_ISUPPORTS12(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
+ _i9, _i10, _i11, _i12) \
+ NS_IMPL_ADDREF(_class) \
+ NS_IMPL_RELEASE(_class) \
+ NS_IMPL_QUERY_INTERFACE12(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, \
+ _i9, _i10, _i11, _i12)
+
#define NS_IMPL_ISUPPORTS_INHERITED0(Class, Super) \
NS_IMPL_QUERY_INTERFACE_INHERITED0(Class, Super) \
NS_IMPL_ADDREF_INHERITED(Class, Super) \
NS_IMPL_RELEASE_INHERITED(Class, Super) \
#define NS_IMPL_ISUPPORTS_INHERITED1(Class, Super, i1) \
NS_IMPL_QUERY_INTERFACE_INHERITED1(Class, Super, i1) \
NS_IMPL_ADDREF_INHERITED(Class, Super) \