bug 723628 - speculative connect hint interface r=honzab sr=biesi

This commit is contained in:
Patrick McManus 2012-04-25 08:54:42 -04:00
parent cdc92cbfa8
commit 9e0422c738
8 changed files with 265 additions and 27 deletions

View File

@ -103,6 +103,7 @@ XPIDLSRCS = \
nsITransport.idl \
nsISocketTransport.idl \
nsISocketTransportService.idl \
nsISpeculativeConnect.idl \
nsIServerSocket.idl \
nsIResumableChannel.idl \
nsIRequestObserverProxy.idl \

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick McManus <mcmanus@ducksong.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIURI;
interface nsIInterfaceRequestor;
interface nsIEventTarget;
[scriptable, uuid(b3c53863-1313-480a-90a2-5b0da651ee5e)]
interface nsISpeculativeConnect : nsISupports
{
/**
* Called as a hint to indicate a new transaction for the URI is likely coming
* soon. The implementer may use this information to start a TCP
* and/or SSL level handshake for that resource immediately so that it is
* ready and/or progressed when the transaction is actually submitted.
*
* No obligation is taken on by the implementer, nor is the submitter obligated
* to actually open the new channel.
*
* @param aURI the URI of the hinted transaction
* @param aCallbacks any security callbacks for use with SSL for interfaces
* such as nsIBadCertListener. May be null.
* @param aTarget the thread on which the release of the callbacks will
* occur. May be null for "any thread".
*
*/
void speculativeConnect(in nsIURI aURI,
in nsIInterfaceRequestor aCallbacks,
in nsIEventTarget aTarget);
};

View File

@ -341,10 +341,11 @@ nsIOService::GetInstance() {
return gIOService;
}
NS_IMPL_THREADSAFE_ISUPPORTS5(nsIOService,
NS_IMPL_THREADSAFE_ISUPPORTS6(nsIOService,
nsIIOService,
nsIIOService2,
nsINetUtil,
nsISpeculativeConnect,
nsIObserver,
nsISupportsWeakReference)
@ -608,10 +609,47 @@ nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
return NewChannelFromURIWithProxyFlags(aURI, nsnull, 0, result);
}
void
nsIOService::LookupProxyInfo(nsIURI *aURI,
nsIURI *aProxyURI,
PRUint32 aProxyFlags,
nsCString *aScheme,
nsIProxyInfo **outPI)
{
nsresult rv;
nsCOMPtr<nsIProxyInfo> pi;
if (!mProxyService) {
mProxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
if (!mProxyService)
NS_WARNING("failed to get protocol proxy service");
}
if (mProxyService) {
PRUint32 flags = 0;
if (aScheme->EqualsLiteral("http") || aScheme->EqualsLiteral("https"))
flags = nsIProtocolProxyService::RESOLVE_NON_BLOCKING;
rv = mProxyService->Resolve(aProxyURI ? aProxyURI : aURI, aProxyFlags,
getter_AddRefs(pi));
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
// Use an UNKNOWN proxy to defer resolution and avoid blocking.
rv = mProxyService->NewProxyInfo(NS_LITERAL_CSTRING("unknown"),
NS_LITERAL_CSTRING(""),
-1, 0, 0, nsnull,
getter_AddRefs(pi));
}
if (NS_FAILED(rv))
pi = nsnull;
}
*outPI = pi;
if (pi)
pi.forget();
}
NS_IMETHODIMP
nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
nsIURI *aProxyURI,
PRUint32 proxyFlags,
PRUint32 aProxyFlags,
nsIChannel **result)
{
nsresult rv;
@ -636,27 +674,7 @@ nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
// skip this step. This allows us to lazily load the PPS at startup.
if (protoFlags & nsIProtocolHandler::ALLOWS_PROXY) {
nsCOMPtr<nsIProxyInfo> pi;
if (!mProxyService) {
mProxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
if (!mProxyService)
NS_WARNING("failed to get protocol proxy service");
}
if (mProxyService) {
PRUint32 flags = 0;
if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))
flags = nsIProtocolProxyService::RESOLVE_NON_BLOCKING;
rv = mProxyService->Resolve(aProxyURI ? aProxyURI : aURI, proxyFlags,
getter_AddRefs(pi));
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
// Use an UNKNOWN proxy to defer resolution and avoid blocking.
rv = mProxyService->NewProxyInfo(NS_LITERAL_CSTRING("unknown"),
NS_LITERAL_CSTRING(""),
-1, 0, 0, nsnull,
getter_AddRefs(pi));
}
if (NS_FAILED(rv))
pi = nsnull;
}
LookupProxyInfo(aURI, aProxyURI, aProxyFlags, &scheme, getter_AddRefs(pi));
if (pi) {
nsCAutoString type;
if (NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) {
@ -1236,3 +1254,37 @@ nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
}
return NS_OK;
}
// nsISpeculativeConnect
NS_IMETHODIMP
nsIOService::SpeculativeConnect(nsIURI *aURI,
nsIInterfaceRequestor *aCallbacks,
nsIEventTarget *aTarget)
{
nsCAutoString scheme;
nsresult rv = aURI->GetScheme(scheme);
if (NS_FAILED(rv))
return rv;
// Check for proxy information. If there is a proxy configured then a
// speculative connect should not be performed because the potential
// reward is slim with tcp peers closely located to the browser.
nsCOMPtr<nsIProxyInfo> pi;
LookupProxyInfo(aURI, nsnull, 0, &scheme, getter_AddRefs(pi));
if (pi)
return NS_OK;
nsCOMPtr<nsIProtocolHandler> handler;
rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
do_QueryInterface(handler);
if (!handler)
return NS_OK;
return speculativeHandler->SpeculativeConnect(aURI,
aCallbacks,
aTarget);
}

View File

@ -58,6 +58,7 @@
#include "nsCategoryCache.h"
#include "nsINetworkLinkService.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsISpeculativeConnect.h"
#define NS_N(x) (sizeof(x)/sizeof(*x))
@ -74,6 +75,7 @@ class nsIPrefBranch;
class nsIOService : public nsIIOService2
, public nsIObserver
, public nsINetUtil
, public nsISpeculativeConnect
, public nsSupportsWeakReference
{
public:
@ -82,6 +84,7 @@ public:
NS_DECL_NSIIOSERVICE2
NS_DECL_NSIOBSERVER
NS_DECL_NSINETUTIL
NS_DECL_NSISPECULATIVECONNECT
// Gets the singleton instance of the IO Service, creating it as needed
// Returns nsnull on out of memory or failure to initialize.
@ -135,6 +138,10 @@ private:
nsresult InitializeSocketTransportService();
nsresult InitializeNetworkLinkService();
// consolidated helper function
void LookupProxyInfo(nsIURI *aURI, nsIURI *aProxyURI, PRUint32 aProxyFlags,
nsCString *aScheme, nsIProxyInfo **outPI);
private:
bool mOffline;
bool mOfflineForProfileChange;

View File

@ -1417,12 +1417,13 @@ nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings)
// nsHttpHandler::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ISUPPORTS5(nsHttpHandler,
NS_IMPL_THREADSAFE_ISUPPORTS6(nsHttpHandler,
nsIHttpProtocolHandler,
nsIProxiedProtocolHandler,
nsIProtocolHandler,
nsIObserver,
nsISupportsWeakReference)
nsISupportsWeakReference,
nsISpeculativeConnect)
//-----------------------------------------------------------------------------
// nsHttpHandler::nsIProtocolHandler
@ -1669,15 +1670,75 @@ nsHttpHandler::Observe(nsISupports *subject,
return NS_OK;
}
// nsISpeculativeConnect
NS_IMETHODIMP
nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
nsIInterfaceRequestor *aCallbacks,
nsIEventTarget *aTarget)
{
nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
bool isStsHost = false;
if (!stss)
return NS_OK;
nsCOMPtr<nsIURI> clone;
if (NS_SUCCEEDED(stss->IsStsURI(aURI, &isStsHost)) && isStsHost) {
if (NS_SUCCEEDED(aURI->Clone(getter_AddRefs(clone)))) {
clone->SetScheme(NS_LITERAL_CSTRING("https"));
aURI = clone.get();
}
}
nsCAutoString scheme;
nsresult rv = aURI->GetScheme(scheme);
if (NS_FAILED(rv))
return rv;
// If this is HTTPS, make sure PSM is initialized as the channel
// creation path may have been bypassed
if (scheme.EqualsLiteral("https")) {
if (!IsNeckoChild()) {
// make sure PSM gets initialized on the main thread.
net_EnsurePSMInit();
}
}
// Ensure that this is HTTP or HTTPS, otherwise we don't do preconnect here
else if (!scheme.EqualsLiteral("http"))
return NS_ERROR_UNEXPECTED;
// Construct connection info object
bool usingSSL = false;
rv = aURI->SchemeIs("https", &usingSSL);
if (NS_FAILED(rv))
return rv;
nsCAutoString host;
rv = aURI->GetAsciiHost(host);
if (NS_FAILED(rv))
return rv;
PRInt32 port = -1;
rv = aURI->GetPort(&port);
if (NS_FAILED(rv))
return rv;
nsHttpConnectionInfo *ci =
new nsHttpConnectionInfo(host, port, nsnull, usingSSL);
return SpeculativeConnect(ci, aCallbacks, aTarget);
}
//-----------------------------------------------------------------------------
// nsHttpsHandler implementation
//-----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpsHandler,
NS_IMPL_THREADSAFE_ISUPPORTS5(nsHttpsHandler,
nsIHttpProtocolHandler,
nsIProxiedProtocolHandler,
nsIProtocolHandler,
nsISupportsWeakReference)
nsISupportsWeakReference,
nsISpeculativeConnect)
nsresult
nsHttpsHandler::Init()

View File

@ -61,6 +61,7 @@
#include "nsIIDNService.h"
#include "nsITimer.h"
#include "nsIStrictTransportSecurityService.h"
#include "nsISpeculativeConnect.h"
class nsHttpConnectionInfo;
class nsHttpHeaderArray;
@ -76,6 +77,7 @@ class nsIPrefBranch;
class nsHttpHandler : public nsIHttpProtocolHandler
, public nsIObserver
, public nsSupportsWeakReference
, public nsISpeculativeConnect
{
public:
NS_DECL_ISUPPORTS
@ -83,6 +85,7 @@ public:
NS_DECL_NSIPROXIEDPROTOCOLHANDLER
NS_DECL_NSIHTTPPROTOCOLHANDLER
NS_DECL_NSIOBSERVER
NS_DECL_NSISPECULATIVECONNECT
nsHttpHandler();
virtual ~nsHttpHandler();
@ -412,6 +415,7 @@ extern nsHttpHandler *gHttpHandler;
class nsHttpsHandler : public nsIHttpProtocolHandler
, public nsSupportsWeakReference
, public nsISpeculativeConnect
{
public:
// we basically just want to override GetScheme and GetDefaultPort...
@ -421,6 +425,7 @@ public:
NS_DECL_NSIPROTOCOLHANDLER
NS_FORWARD_NSIPROXIEDPROTOCOLHANDLER (gHttpHandler->)
NS_FORWARD_NSIHTTPPROTOCOLHANDLER (gHttpHandler->)
NS_FORWARD_NSISPECULATIVECONNECT (gHttpHandler->)
nsHttpsHandler() { }
virtual ~nsHttpsHandler() { }

View File

@ -0,0 +1,42 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const CC = Components.Constructor;
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
"init");
var serv;
function TestServer() {
this.listener = ServerSocket(4444, true, -1);
this.listener.asyncListen(this);
}
TestServer.prototype = {
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIServerSocket) ||
iid.equals(Ci.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
onSocketAccepted: function(socket, trans) {
try { this.listener.close(); } catch(e) {}
do_check_true(true);
do_test_finished();
},
onStopListening: function(socket) {}
}
function run_test() {
var ios = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
serv = new TestServer();
URI = ios.newURI("http://localhost:4444/just/a/test", null, null);
ios.QueryInterface(Components.interfaces.nsISpeculativeConnect)
.speculativeConnect(URI, null, null);
do_test_pending();
}

View File

@ -174,6 +174,7 @@ skip-if = os == "win"
[test_socks.js]
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_speculative_connect.js]
[test_standardurl.js]
[test_standardurl_port.js]
[test_streamcopier.js]