bug 640003 - websockets, nsIIOService2::NewChannelFromURIWithProxyFlags() r=biesi sr=bz

This commit is contained in:
Patrick McManus 2011-05-13 13:53:27 -04:00
parent 0f8bd52fce
commit bc5dd2ba37
14 changed files with 143 additions and 42 deletions

View File

@ -40,10 +40,9 @@
#include "nsIIOService.idl"
/**
* nsIIOService2 extends nsIIOService with support for automatic
* online/offline management.
* nsIIOService2 extends nsIIOService
*/
[scriptable, uuid(d44fe6d4-ee35-4789-886a-eb8f0554d04e)]
[scriptable, uuid(9a7dc724-0b5c-4b78-9722-1037074c02de)]
interface nsIIOService2 : nsIIOService
{
/**
@ -59,4 +58,18 @@ interface nsIIOService2 : nsIIOService
* this management.
*/
attribute boolean manageOfflineStatus;
/**
* Creates a channel for a given URI.
*
* @param aURI nsIURI from which to make a channel
* @param aProxyURI nsIURI to use for proxy resolution. Can be null in which
* case aURI is used
* @param aProxyFlags flags from nsIProtocolProxyService to use
* when resolving proxies for this new channel
* @return reference to the new nsIChannel object
*/
nsIChannel newChannelFromURIWithProxyFlags(in nsIURI aURI,
in nsIURI aProxyURI,
in unsigned long aProxyFlags);
};

View File

@ -102,6 +102,13 @@ interface nsIProtocolProxyService : nsISupports
const unsigned long RESOLVE_PREFER_HTTPS_PROXY =
(1 << 3) | RESOLVE_IGNORE_URI_SCHEME;
/**
* When the proxy configuration is manual this flag may be passed to the
* resolve and asyncResolve methods to that all methods will be tunneled via
* CONNECT through the http proxy.
*/
const unsigned long RESOLVE_ALWAYS_TUNNEL = (1 << 4);
/**
* This method returns a nsIProxyInfo instance that identifies a proxy to
* be used for loading the given URI. Otherwise, this method returns null

View File

@ -42,7 +42,7 @@
/**
* This interface identifies a proxy server.
*/
[scriptable, uuid(3fe9308b-1608-4fa0-933c-c5ec2c6175fd)]
[scriptable, uuid(9e557d99-7af0-4895-95b7-e6dba28c9ad9)]
interface nsIProxyInfo : nsISupports
{
/**
@ -77,6 +77,12 @@ interface nsIProxyInfo : nsISupports
*/
readonly attribute unsigned long flags;
/**
* This attribute specifies flags that were used by nsIProxyProtocolService when
* creating this ProxyInfo element.
*/
readonly attribute unsigned long resolveFlags;
/**
* This attribute specifies the failover timeout in seconds for this proxy.
* If a nsIProxyInfo is reported as failed via nsIProtocolProxyService::

View File

@ -595,6 +595,15 @@ nsIOService::NewFileURI(nsIFile *file, nsIURI **result)
NS_IMETHODIMP
nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
{
return NewChannelFromURIWithProxyFlags(aURI, nsnull, 0, result);
}
NS_IMETHODIMP
nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
nsIURI *aProxyURI,
PRUint32 proxyFlags,
nsIChannel **result)
{
nsresult rv;
NS_ENSURE_ARG_POINTER(aURI);
@ -625,7 +634,8 @@ nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
NS_WARNING("failed to get protocol proxy service");
}
if (mProxyService) {
rv = mProxyService->Resolve(aURI, 0, getter_AddRefs(pi));
rv = mProxyService->Resolve(aProxyURI ? aProxyURI : aURI,
proxyFlags, getter_AddRefs(pi));
if (NS_FAILED(rv))
pi = nsnull;
}

View File

@ -94,9 +94,11 @@ public:
NS_DECL_ISUPPORTS
nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIURI *uri,
PRUint32 aResolveFlags,
nsIProtocolProxyCallback *callback)
: mStatus(NS_OK)
, mDispatched(PR_FALSE)
, mResolveFlags(0)
, mPPS(pps)
, mURI(uri)
, mCallback(callback)
@ -172,7 +174,8 @@ private:
{
// Generate proxy info from the PAC string if appropriate
if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty())
mPPS->ProcessPACString(mPACString, getter_AddRefs(mProxyInfo));
mPPS->ProcessPACString(mPACString, mResolveFlags,
getter_AddRefs(mProxyInfo));
// Now apply proxy filters
if (NS_SUCCEEDED(mStatus)) {
@ -193,6 +196,7 @@ private:
nsresult mStatus;
nsCString mPACString;
PRBool mDispatched;
PRUint32 mResolveFlags;
nsRefPtr<nsProtocolProxyService> mPPS;
nsCOMPtr<nsIURI> mURI;
@ -584,7 +588,9 @@ static const char kProxyType_DIRECT[] = "direct";
static const char kProxyType_UNKNOWN[] = "unknown";
const char *
nsProtocolProxyService::ExtractProxyInfo(const char *start, nsProxyInfo **result)
nsProtocolProxyService::ExtractProxyInfo(const char *start,
PRUint32 aResolveFlags,
nsProxyInfo **result)
{
*result = nsnull;
PRUint32 flags = 0;
@ -651,6 +657,7 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, nsProxyInfo **result
if (pi) {
pi->mType = type;
pi->mFlags = flags;
pi->mResolveFlags = aResolveFlags;
pi->mTimeout = mFailedProxyTimeout;
// YES, it is ok to specify a null proxy host.
if (host) {
@ -780,6 +787,7 @@ nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
void
nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
PRUint32 aResolveFlags,
nsIProxyInfo **result)
{
if (pacString.IsEmpty()) {
@ -791,7 +799,7 @@ nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
nsProxyInfo *pi = nsnull, *first = nsnull, *last = nsnull;
while (*proxies) {
proxies = ExtractProxyInfo(proxies, &pi);
proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
if (pi) {
if (last) {
NS_ASSERTION(last->mNext == nsnull, "leaking nsProxyInfo");
@ -855,12 +863,12 @@ nsProtocolProxyService::Resolve(nsIURI *uri, PRUint32 flags,
nsCString pacString;
rv = mPACMan->GetProxyForURI(uri, pacString);
if (NS_SUCCEEDED(rv))
ProcessPACString(pacString, result);
ProcessPACString(pacString, flags, result);
else if (rv == NS_ERROR_IN_PROGRESS) {
// Construct a special UNKNOWN proxy entry that informs the caller
// that the proxy info is yet to be determined.
rv = NewProxyInfo_Internal(kProxyType_UNKNOWN, EmptyCString(), -1,
0, 0, nsnull, result);
0, 0, nsnull, flags, result);
if (NS_FAILED(rv))
return rv;
}
@ -878,7 +886,7 @@ nsProtocolProxyService::AsyncResolve(nsIURI *uri, PRUint32 flags,
nsICancelable **result)
{
nsRefPtr<nsAsyncResolveRequest> ctx =
new nsAsyncResolveRequest(this, uri, callback);
new nsAsyncResolveRequest(this, uri, flags, callback);
if (!ctx)
return NS_ERROR_OUT_OF_MEMORY;
@ -940,7 +948,7 @@ nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
aPort = -1;
return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
aFailoverProxy, aResult);
aFailoverProxy, 0, aResult);
}
NS_IMETHODIMP
@ -1206,6 +1214,7 @@ nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
PRUint32 aFlags,
PRUint32 aFailoverTimeout,
nsIProxyInfo *aFailoverProxy,
PRUint32 aResolveFlags,
nsIProxyInfo **aResult)
{
nsCOMPtr<nsProxyInfo> failover;
@ -1222,6 +1231,7 @@ nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
proxyInfo->mHost = aHost;
proxyInfo->mPort = aPort;
proxyInfo->mFlags = aFlags;
proxyInfo->mResolveFlags = aResolveFlags;
proxyInfo->mTimeout = aFailoverTimeout == PR_UINT32_MAX
? mFailedProxyTimeout : aFailoverTimeout;
failover.swap(proxyInfo->mNext);
@ -1252,7 +1262,7 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
nsCAutoString proxy;
nsresult rv = mSystemProxySettings->GetProxyForURI(uri, proxy);
if (NS_SUCCEEDED(rv)) {
ProcessPACString(proxy, result);
ProcessPACString(proxy, flags, result);
return NS_OK;
}
// no proxy, stop search
@ -1345,7 +1355,8 @@ nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
if (type) {
nsresult rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
PR_UINT32_MAX, nsnull, result);
PR_UINT32_MAX, nsnull, flags,
result);
if (NS_FAILED(rv))
return rv;
}

View File

@ -99,6 +99,9 @@ protected:
*
* @param proxy
* The PAC-style proxy string to parse. This must not be null.
* @param aResolveFlags
* The flags passed to Resolve or AsyncResolve that are stored in
* proxyInfo.
* @param result
* Upon return this points to a newly allocated nsProxyInfo or null
* if the proxy string was invalid.
@ -106,6 +109,7 @@ protected:
* @return A pointer beyond the parsed proxy string (never null).
*/
NS_HIDDEN_(const char *) ExtractProxyInfo(const char *proxy,
PRUint32 aResolveFlags,
nsProxyInfo **result);
/**
@ -122,10 +126,14 @@ protected:
*
* @param pacString
* The PAC-style proxy string to parse. This may be empty.
* @param aResolveFlags
* The flags passed to Resolve or AsyncResolve that are stored in
* proxyInfo.
* @param result
* The resulting list of proxy info objects.
*/
NS_HIDDEN_(void) ProcessPACString(const nsCString &pacString,
PRUint32 aResolveFlags,
nsIProxyInfo **result);
/**
@ -199,6 +207,8 @@ protected:
* The failover timeout for this proxy.
* @param next
* The next proxy to try if this one fails.
* @param aResolveFlags
* The flags passed to resolve (from nsIProtocolProxyService).
* @param result
* The resulting nsIProxyInfo object.
*/
@ -208,6 +218,7 @@ protected:
PRUint32 flags,
PRUint32 timeout,
nsIProxyInfo *next,
PRUint32 aResolveFlags,
nsIProxyInfo **result);
/**

View File

@ -70,6 +70,13 @@ nsProxyInfo::GetFlags(PRUint32 *result)
return NS_OK;
}
NS_IMETHODIMP
nsProxyInfo::GetResolveFlags(PRUint32 *result)
{
*result = mResolveFlags;
return NS_OK;
}
NS_IMETHODIMP
nsProxyInfo::GetFailoverTimeout(PRUint32 *result)
{

View File

@ -74,6 +74,7 @@ private:
: mType(type)
, mPort(-1)
, mFlags(0)
, mResolveFlags(0)
, mTimeout(PR_UINT32_MAX)
, mNext(nsnull)
{}
@ -87,6 +88,7 @@ private:
nsCString mHost;
PRInt32 mPort;
PRUint32 mFlags;
PRUint32 mResolveFlags;
PRUint32 mTimeout;
nsProxyInfo *mNext;
};

View File

@ -553,7 +553,9 @@ nsHttpChannel::SetupTransaction()
// does not count here). also, figure out what version we should be speaking.
nsCAutoString buf, path;
nsCString* requestURI;
if (mConnectionInfo->UsingSSL() || !mConnectionInfo->UsingHttpProxy()) {
if (mConnectionInfo->UsingSSL() ||
mConnectionInfo->ShouldForceConnectMethod() ||
!mConnectionInfo->UsingHttpProxy()) {
rv = mURI->GetPath(path);
if (NS_FAILED(rv)) return rv;
// path may contain UTF-8 characters, so ensure that they're escaped.
@ -1554,7 +1556,11 @@ nsHttpChannel::ResolveProxy()
if (NS_FAILED(rv))
return rv;
return pps->AsyncResolve(mURI, 0, this, getter_AddRefs(mProxyRequest));
PRUint32 resolveFlags = 0;
if (mConnectionInfo->ProxyInfo())
mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags);
return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest));
}
PRBool
@ -3784,7 +3790,8 @@ NS_IMETHODIMP
nsHttpChannel::GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect)
{
*aProxyMethodIsConnect =
(mConnectionInfo->UsingHttpProxy() && mConnectionInfo->UsingSSL());
(mConnectionInfo->UsingHttpProxy() && mConnectionInfo->UsingSSL()) ||
mConnectionInfo->ShouldForceConnectMethod();
return NS_OK;
}

View File

@ -76,7 +76,7 @@ nsHttpConnection::nsHttpConnection()
, mKeepAliveMask(PR_TRUE)
, mSupportsPipelining(PR_FALSE) // assume low-grade server
, mIsReused(PR_FALSE)
, mCompletedSSLConnect(PR_FALSE)
, mCompletedProxyConnect(PR_FALSE)
, mLastTransactionExpectedNoContent(PR_FALSE)
{
LOG(("Creating nsHttpConnection @%x\n", this));
@ -156,9 +156,11 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
// set mKeepAlive according to what will be requested
mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
// need to handle SSL proxy CONNECT if this is the first time.
if (mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy() && !mCompletedSSLConnect) {
rv = SetupSSLProxyConnect();
// need to handle HTTP CONNECT tunnels if this is the first time if
// we are tunneling through a proxy
if (((mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy()) ||
mConnInfo->ShouldForceConnectMethod()) && !mCompletedProxyConnect) {
rv = SetupProxyConnect();
if (NS_FAILED(rv))
goto failed_activation;
}
@ -378,7 +380,7 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
// determination must be based on comunication with the
// target server in this case. See bug 422016 for futher
// details.
if (!mSSLProxyConnectStream)
if (!mProxyConnectStream)
mSupportsPipelining = SupportsPipelining(responseHead);
}
}
@ -407,21 +409,26 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
// the connect was successful. if so, then we have to reset the transaction
// and step-up the socket connection to SSL. finally, we have to wake up the
// socket write request.
if (mSSLProxyConnectStream) {
mSSLProxyConnectStream = 0;
if (mProxyConnectStream) {
mProxyConnectStream = 0;
if (responseHead->Status() == 200) {
LOG(("SSL proxy CONNECT succeeded!\n"));
LOG(("proxy CONNECT succeeded! ssl=%s\n",
mConnInfo->UsingSSL() ? "true" :"false"));
*reset = PR_TRUE;
nsresult rv = ProxyStartSSL();
if (NS_FAILED(rv)) // XXX need to handle this for real
LOG(("ProxyStartSSL failed [rv=%x]\n", rv));
mCompletedSSLConnect = PR_TRUE;
nsresult rv;
if (mConnInfo->UsingSSL()) {
rv = ProxyStartSSL();
if (NS_FAILED(rv)) // XXX need to handle this for real
LOG(("ProxyStartSSL failed [rv=%x]\n", rv));
}
mCompletedProxyConnect = PR_TRUE;
rv = mSocketOut->AsyncWait(this, 0, 0, nsnull);
// XXX what if this fails -- need to handle this error
NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
}
else {
LOG(("SSL proxy CONNECT failed!\n"));
LOG(("proxy CONNECT failed! ssl=%s\n",
mConnInfo->UsingSSL() ? "true" :"false"));
mTransaction->SetSSLConnectFailed();
}
}
@ -574,9 +581,9 @@ nsHttpConnection::OnSocketWritable()
// implement nsIInputStream. doing so is not worth the added cost of
// extra indirections during normal reading.
//
if (mSSLProxyConnectStream) {
if (mProxyConnectStream) {
LOG((" writing CONNECT request stream\n"));
rv = mSSLProxyConnectStream->ReadSegments(ReadFromStream, this,
rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
nsIOService::gDefaultSegmentSize,
&n);
}
@ -699,13 +706,13 @@ nsHttpConnection::OnSocketReadable()
}
nsresult
nsHttpConnection::SetupSSLProxyConnect()
nsHttpConnection::SetupProxyConnect()
{
const char *val;
LOG(("nsHttpConnection::SetupSSLProxyConnect [this=%x]\n", this));
LOG(("nsHttpConnection::SetupProxyConnect [this=%x]\n", this));
NS_ENSURE_TRUE(!mSSLProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
nsCAutoString buf;
nsresult rv = nsHttpHandler::GenerateHostPort(
@ -741,7 +748,7 @@ nsHttpConnection::SetupSSLProxyConnect()
request.Flatten(buf, PR_FALSE);
buf.AppendLiteral("\r\n");
return NS_NewCStringInputStream(getter_AddRefs(mSSLProxyConnectStream), buf);
return NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), buf);
}
//-----------------------------------------------------------------------------

View File

@ -151,7 +151,7 @@ private:
nsresult OnSocketWritable();
nsresult OnSocketReadable();
nsresult SetupSSLProxyConnect();
nsresult SetupProxyConnect();
PRBool IsAlive();
PRBool SupportsPipelining(nsHttpResponseHead *);
@ -164,7 +164,7 @@ private:
nsresult mSocketInCondition;
nsresult mSocketOutCondition;
nsCOMPtr<nsIInputStream> mSSLProxyConnectStream;
nsCOMPtr<nsIInputStream> mProxyConnectStream;
nsCOMPtr<nsIInputStream> mRequestStream;
// mTransaction only points to the HTTP Transaction callbacks if the
@ -188,7 +188,7 @@ private:
PRPackedBool mKeepAliveMask;
PRPackedBool mSupportsPipelining;
PRPackedBool mIsReused;
PRPackedBool mCompletedSSLConnect;
PRPackedBool mCompletedProxyConnect;
PRPackedBool mLastTransactionExpectedNoContent;
};

View File

@ -38,6 +38,7 @@
#include "nsHttpConnectionInfo.h"
#include "nsPrintfCString.h"
#include "nsIProtocolProxyService.h"
void
nsHttpConnectionInfo::SetOriginServer(const nsACString &host, PRInt32 port)
@ -99,3 +100,19 @@ nsHttpConnectionInfo::Clone() const
return clone;
}
PRBool
nsHttpConnectionInfo::ShouldForceConnectMethod()
{
if (!mProxyInfo)
return PR_FALSE;
PRUint32 resolveFlags;
nsresult rv;
rv = mProxyInfo->GetResolveFlags(&resolveFlags);
if (NS_FAILED(rv))
return PR_FALSE;
return resolveFlags & nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL;
}

View File

@ -124,7 +124,8 @@ public:
PRInt32 DefaultPort() const { return mUsingSSL ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT; }
void SetAnonymous(PRBool anon)
{ mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
PRBool ShouldForceConnectMethod();
private:
nsrefcnt mRef;
nsCString mHashKey;

View File

@ -229,8 +229,10 @@ nsHttpTransaction::Init(PRUint8 caps,
// make sure we eliminate any proxy specific headers from
// the request if we are talking HTTPS via a SSL tunnel.
PRBool pruneProxyHeaders = cinfo->UsingSSL() &&
cinfo->UsingHttpProxy();
PRBool pruneProxyHeaders =
cinfo->ShouldForceConnectMethod() ||
(cinfo->UsingSSL() && cinfo->UsingHttpProxy());
mReqHeaderBuf.Truncate();
requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);