зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1363284 - HTTP/2 anonymous/onymous session (connection) coalescing, r=mayhemer
--HG-- extra : rebase_source : 66192a32668de8a9cd99722d1e0860cce2f84030
This commit is contained in:
Родитель
3f4b1d1560
Коммит
281502cac6
|
@ -2580,5 +2580,26 @@ nsHttpConnection::SetEvent(nsresult aStatus)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpConnection::NoClientCertAuth() const
|
||||
{
|
||||
if (!mSocketTransport) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> secInfo;
|
||||
mSocketTransport->GetSecurityInfo(getter_AddRefs(secInfo));
|
||||
if (!secInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISSLSocketControl> ssc(do_QueryInterface(secInfo));
|
||||
if (!ssc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ssc->GetClientCertSent();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -243,6 +243,11 @@ public:
|
|||
|
||||
void SetEvent(nsresult aStatus);
|
||||
|
||||
// Return true when the socket this connection is using has not been
|
||||
// authenticated using a client certificate. Before SSL negotiation
|
||||
// has finished this returns false.
|
||||
bool NoClientCertAuth() const;
|
||||
|
||||
private:
|
||||
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
|
||||
enum TCPKeepaliveConfig {
|
||||
|
|
|
@ -3791,6 +3791,21 @@ nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI
|
|||
return specificEnt;
|
||||
}
|
||||
|
||||
// step 1 repeated for an inverted anonymous flag; we return an entry
|
||||
// only when it has an h2 established connection that is not authenticated
|
||||
// with a client certificate.
|
||||
RefPtr<nsHttpConnectionInfo> anonInvertedCI(specificCI->Clone());
|
||||
anonInvertedCI->SetAnonymous(!specificCI->GetAnonymous());
|
||||
nsConnectionEntry *invertedEnt = mCT.GetWeak(anonInvertedCI->HashKey());
|
||||
if (invertedEnt) {
|
||||
nsHttpConnection* h2conn = GetSpdyActiveConn(invertedEnt);
|
||||
if (h2conn && h2conn->IsExperienced() && h2conn->NoClientCertAuth()) {
|
||||
MOZ_ASSERT(h2conn->UsingSpdy());
|
||||
LOG(("GetOrCreateConnectionEntry is coalescing h2 an/onymous connections, ent=%p", invertedEnt));
|
||||
return invertedEnt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!specificCI->UsingHttpsProxy()) {
|
||||
prohibitWildCard = true;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,12 @@ interface nsISSLSocketControl : nsISupports {
|
|||
*/
|
||||
attribute nsIX509Cert clientCert;
|
||||
|
||||
/**
|
||||
* True iff a client cert has been sent to the server - i.e. this
|
||||
* socket has been client-cert authenticated.
|
||||
*/
|
||||
[infallible] readonly attribute boolean clientCertSent;
|
||||
|
||||
/**
|
||||
* bypassAuthentication is true if the server certificate checks are
|
||||
* not be enforced. This is to enable non-secure transport over TLS.
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
ChromeUtils.import("resource://testing-common/httpd.js");
|
||||
ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
/*
|
||||
- test to check we use only a single connection for both onymous and anonymous requests over an existing h2 session
|
||||
- request from a domain w/o LOAD_ANONYMOUS flag
|
||||
- request again from the same domain, but different URI, with LOAD_ANONYMOUS flag, check the client is using the same conn
|
||||
- close all and do it in the opposite way (do an anonymous req first)
|
||||
*/
|
||||
|
||||
var h2Port;
|
||||
var prefs;
|
||||
var spdypref;
|
||||
var http2pref;
|
||||
var extpref;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
h2Port = env.get("MOZHTTP2_PORT");
|
||||
Assert.notEqual(h2Port, null);
|
||||
Assert.notEqual(h2Port, "");
|
||||
|
||||
// Set to allow the cert presented by our H2 server
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
spdypref = prefs.getBoolPref("network.http.spdy.enabled");
|
||||
http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
|
||||
extpref = prefs.getBoolPref("network.http.originextension");
|
||||
|
||||
prefs.setBoolPref("network.http.spdy.enabled", true);
|
||||
prefs.setBoolPref("network.http.spdy.enabled.http2", true);
|
||||
prefs.setBoolPref("network.http.originextension", true);
|
||||
prefs.setCharPref("network.dns.localDomains", "foo.example.com, alt1.example.com");
|
||||
|
||||
// The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by CA.cert.der
|
||||
// so add that cert to the trust list as a signing cert.
|
||||
let certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
|
||||
|
||||
doTest1();
|
||||
}
|
||||
|
||||
function resetPrefs() {
|
||||
prefs.setBoolPref("network.http.spdy.enabled", spdypref);
|
||||
prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
|
||||
prefs.setBoolPref("network.http.originextension", extpref);
|
||||
prefs.clearUserPref("network.dns.localDomains");
|
||||
}
|
||||
|
||||
function readFile(file) {
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
fstream.init(file, -1, 0, 0);
|
||||
let data = NetUtil.readInputStreamToString(fstream, fstream.available());
|
||||
fstream.close();
|
||||
return data;
|
||||
}
|
||||
|
||||
function addCertFromFile(certdb, filename, trustString) {
|
||||
let certFile = do_get_file(filename, false);
|
||||
let der = readFile(certFile);
|
||||
certdb.addCert(der, trustString);
|
||||
}
|
||||
|
||||
function makeChan(origin) {
|
||||
return NetUtil.newChannel({
|
||||
uri: origin,
|
||||
loadUsingSystemPrincipal: true
|
||||
}).QueryInterface(Ci.nsIHttpChannel);
|
||||
}
|
||||
|
||||
var nextTest;
|
||||
var origin;
|
||||
var nextPortExpectedToBeSame = false;
|
||||
var currentPort = 0;
|
||||
var forceReload = false;
|
||||
var anonymous = false;
|
||||
|
||||
var Listener = function() {};
|
||||
Listener.prototype.clientPort = 0;
|
||||
Listener.prototype = {
|
||||
onStartRequest: function testOnStartRequest(request, ctx) {
|
||||
Assert.ok(request instanceof Components.interfaces.nsIHttpChannel);
|
||||
|
||||
if (!Components.isSuccessCode(request.status)) {
|
||||
do_throw("Channel should have a success code! (" + request.status + ")");
|
||||
}
|
||||
Assert.equal(request.responseStatus, 200);
|
||||
this.clientPort = parseInt(request.getResponseHeader("x-client-port"));
|
||||
},
|
||||
|
||||
onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
|
||||
read_stream(stream, cnt);
|
||||
},
|
||||
|
||||
onStopRequest: function testOnStopRequest(request, ctx, status) {
|
||||
Assert.ok(Components.isSuccessCode(status));
|
||||
if (nextPortExpectedToBeSame) {
|
||||
Assert.equal(currentPort, this.clientPort);
|
||||
} else {
|
||||
Assert.notEqual(currentPort, this.clientPort);
|
||||
}
|
||||
currentPort = this.clientPort;
|
||||
nextTest();
|
||||
do_test_finished();
|
||||
}
|
||||
};
|
||||
|
||||
function testsDone()
|
||||
{
|
||||
dump("testsDone\n");
|
||||
resetPrefs();
|
||||
}
|
||||
|
||||
function doTest()
|
||||
{
|
||||
dump("execute doTest " + origin + "\n");
|
||||
|
||||
var loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
|
||||
if (anonymous) {
|
||||
loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS;
|
||||
}
|
||||
anonymous = false;
|
||||
if (forceReload) {
|
||||
loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION;
|
||||
}
|
||||
forceReload = false;
|
||||
|
||||
var chan = makeChan(origin);
|
||||
chan.loadFlags = loadFlags;
|
||||
|
||||
var listener = new Listener();
|
||||
chan.asyncOpen2(listener);
|
||||
}
|
||||
|
||||
function doTest1()
|
||||
{
|
||||
dump("doTest1()\n");
|
||||
origin = "https://foo.example.com:" + h2Port + "/origin-1";
|
||||
nextTest = doTest2;
|
||||
nextPortExpectedToBeSame = false;
|
||||
do_test_pending();
|
||||
doTest();
|
||||
}
|
||||
|
||||
function doTest2()
|
||||
{
|
||||
// connection expected to be reused for an anonymous request
|
||||
dump("doTest2()\n");
|
||||
origin = "https://foo.example.com:" + h2Port + "/origin-2";
|
||||
nextTest = doTest3;
|
||||
nextPortExpectedToBeSame = true;
|
||||
anonymous = true;
|
||||
do_test_pending();
|
||||
doTest();
|
||||
}
|
||||
|
||||
function doTest3()
|
||||
{
|
||||
dump("doTest3()\n");
|
||||
origin = "https://foo.example.com:" + h2Port + "/origin-3";
|
||||
nextTest = doTest4;
|
||||
nextPortExpectedToBeSame = false;
|
||||
forceReload = true;
|
||||
anonymous = true;
|
||||
do_test_pending();
|
||||
doTest();
|
||||
}
|
||||
|
||||
function doTest4()
|
||||
{
|
||||
dump("doTest4()\n");
|
||||
origin = "https://foo.example.com:" + h2Port + "/origin-4";
|
||||
nextTest = testsDone;
|
||||
nextPortExpectedToBeSame = true;
|
||||
do_test_pending();
|
||||
doTest();
|
||||
}
|
|
@ -260,6 +260,9 @@ skip-if = os == "win"
|
|||
[test_origin.js]
|
||||
# node server not runinng on android
|
||||
skip-if = os == "android"
|
||||
[test_anonymous-coalescing.js]
|
||||
# node server not runinng on android
|
||||
skip-if = os == "android"
|
||||
[test_original_sent_received_head.js]
|
||||
[test_parse_content_type.js]
|
||||
[test_permmgr.js]
|
||||
|
|
|
@ -220,6 +220,13 @@ nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetClientCertSent(bool* arg)
|
||||
{
|
||||
*arg = mSentClientCert;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetBypassAuthentication(bool* arg)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче