/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Test stream failure on the session to the proxy: * - Test the case the error closes the affected stream only * - Test the case the error closes the whole session and cancels existing * streams. */ /* eslint-env node */ "use strict"; const pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(); let filter; class ProxyFilter { constructor(type, host, port, flags) { this._type = type; this._host = host; this._port = port; this._flags = flags; this.QueryInterface = ChromeUtils.generateQI(["nsIProtocolProxyFilter"]); } applyFilter(uri, pi, cb) { cb.onProxyFilterResult( pps.newProxyInfo( this._type, this._host, this._port, null, null, this._flags, 1000, null ) ); } } function createPrincipal(url) { var ssm = Services.scriptSecurityManager; try { return ssm.createContentPrincipal(Services.io.newURI(url), {}); } catch (e) { return null; } } function make_channel(url) { return Services.io.newChannelFromURIWithProxyFlags( Services.io.newURI(url), null, 16, null, createPrincipal(url), createPrincipal(url), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT, Ci.nsIContentPolicy.TYPE_OTHER ); } function get_response(channel, flags = CL_ALLOW_UNKNOWN_CL, delay = 0) { return new Promise(resolve => { var listener = new ChannelListener( (request, data) => { request.QueryInterface(Ci.nsIHttpChannel); const status = request.status; const http_code = status ? undefined : request.responseStatus; request.QueryInterface(Ci.nsIProxiedChannel); const proxy_connect_response_code = request.httpProxyConnectResponseCode; resolve({ status, http_code, data, proxy_connect_response_code }); }, null, flags ); if (delay > 0) { do_timeout(delay, function () { channel.asyncOpen(listener); }); } else { channel.asyncOpen(listener); } }); } add_task(async function setup() { // Set to allow the cert presented by our H2 server do_get_profile(); // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem // 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, "http2-ca.pem", "CTu,u,u"); let proxy_port = Services.env.get("MOZHTTP2_PORT"); Assert.notEqual(proxy_port, null); Services.prefs.setBoolPref("network.http.http2.enabled", true); // make all native resolve calls "secretly" resolve localhost instead Services.prefs.setBoolPref("network.dns.native-is-localhost", true); filter = new ProxyFilter("https", "localhost", proxy_port, 16); pps.registerFilter(filter, 10); }); registerCleanupFunction(async () => { Services.prefs.clearUserPref("network.http.http2.enabled"); Services.prefs.clearUserPref("network.dns.native-is-localhost"); pps.unregisterFilter(filter); }); add_task( async function proxy_server_stream_soft_failure_multiple_streams_not_affected() { let should_succeed = get_response(make_channel(`http://750.example.com`)); const failed = await get_response( make_channel(`http://illegalhpacksoft.example.com`), CL_EXPECT_FAILURE, 20 ); const succeeded = await should_succeed; Assert.equal(failed.status, Cr.NS_ERROR_ILLEGAL_VALUE); Assert.equal(failed.proxy_connect_response_code, 0); Assert.equal(failed.http_code, undefined); Assert.equal(succeeded.status, Cr.NS_OK); Assert.equal(succeeded.proxy_connect_response_code, 200); Assert.equal(succeeded.http_code, 200); } ); add_task( async function proxy_server_stream_hard_failure_multiple_streams_affected() { let should_failed = get_response( make_channel(`http://750.example.com`), CL_EXPECT_FAILURE ); const failed1 = await get_response( make_channel(`http://illegalhpackhard.example.com`), CL_EXPECT_FAILURE ); const failed2 = await should_failed; Assert.equal(failed1.status, 0x804b0053); Assert.equal(failed1.proxy_connect_response_code, 0); Assert.equal(failed1.http_code, undefined); Assert.equal(failed2.status, 0x804b0053); Assert.equal(failed2.proxy_connect_response_code, 0); Assert.equal(failed2.http_code, undefined); } ); add_task(async function test_http2_h11required_stream() { let should_failed = await get_response( make_channel(`http://h11required.com`), CL_EXPECT_FAILURE ); // See HTTP/1.1 connect handler in moz-http2.js. The handler returns // "404 Not Found", so the expected error code is NS_ERROR_UNKNOWN_HOST. Assert.equal(should_failed.status, Cr.NS_ERROR_UNKNOWN_HOST); Assert.equal(should_failed.proxy_connect_response_code, 404); Assert.equal(should_failed.http_code, undefined); });