Bug 1363284 - HTTP/2 anonymous/onymous session (connection) coalescing, r=mayhemer

--HG--
extra : rebase_source : 66192a32668de8a9cd99722d1e0860cce2f84030
This commit is contained in:
Honza Bambas 2018-02-15 09:10:00 +02:00
Родитель 3f4b1d1560
Коммит 281502cac6
7 изменённых файлов: 237 добавлений и 0 удалений

Просмотреть файл

@ -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)
{