Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/netwerk/test/unit/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 46 kB image not shown  

Impressum http2_test_common.js   Sprache: JAVA

 
// test HTTP/2

"use strict";

/* import-globals-from head_channels.js */

// Generate a small and a large post with known pre-calculated md5 sums
function generateContent(size) {
  var content = "";
  for (var i = 0; i < size; i++) {
    content += "0";
  }
  return content;
}

var posts = [];
posts.push(generateContent(10));
posts.push(generateContent(250000));
posts.push(generateContent(128000));

// pre-calculated md5sums (in hex) of the above posts
var md5s = [
  "f1b708bba17f1ce948dc979f4d7092bc",
  "2ef8d3b6c8f329318eb1a119b12622b6",
];

var bigListenerData = generateContent(128 * 1024);
var bigListenerMD5 = "8f607cfdd2c87d6a7eedb657dafbd836";

function checkIsHttp2(request) {
  try {
    if (request.getResponseHeader("X-Firefox-Spdy") == "h2") {
      if (request.getResponseHeader("X-Connection-Http2") == "yes") {
        return true;
      }
      return false// Weird case, but the server disagrees with us
    }
  } catch (e) {
    // Nothing to do here
  }
  return false;
}

var Http2CheckListener = function () {};

Http2CheckListener.prototype = {
  onStartRequestFired: false,
  onDataAvailableFired: false,
  isHttp2Connection: false,
  shouldBeHttp2: true,
  accum: 0,
  expected: -1,
  shouldSucceed: true,

  onStartRequest: function testOnStartRequest(request) {
    this.onStartRequestFired = true;
    if (this.shouldSucceed && !Components.isSuccessCode(request.status)) {
      do_throw("Channel should have a success code! (" + request.status + ")");
    } else if (
      !this.shouldSucceed &&
      Components.isSuccessCode(request.status)
    ) {
      do_throw("Channel succeeded unexpectedly!");
    }

    Assert.ok(request instanceof Ci.nsIHttpChannel);
    Assert.equal(request.requestSucceeded, this.shouldSucceed);
    if (this.shouldSucceed) {
      Assert.equal(request.responseStatus, 200);
    }
  },

  onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
    this.onDataAvailableFired = true;
    this.isHttp2Connection = checkIsHttp2(request);
    this.accum += cnt;
    read_stream(stream, cnt);
  },

  onStopRequest: function testOnStopRequest(request, status) {
    Assert.ok(this.onStartRequestFired);
    if (this.expected != -1) {
      Assert.equal(this.accum, this.expected);
    }

    if (this.shouldSucceed) {
      Assert.ok(Components.isSuccessCode(status));
      Assert.ok(this.onDataAvailableFired);
      Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
    } else {
      Assert.ok(!Components.isSuccessCode(status));
    }
    request.QueryInterface(Ci.nsIProxiedChannel);
    var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
    this.finish({ httpProxyConnectResponseCode });
  },
};

/*
 * Support for testing valid multiplexing of streams
 */


var multiplexContent = generateContent(30 * 1024);

/* Listener class to control the testing of multiplexing */
var Http2MultiplexListener = function () {};

Http2MultiplexListener.prototype = new Http2CheckListener();

Http2MultiplexListener.prototype.streamID = 0;
Http2MultiplexListener.prototype.buffer = "";

Http2MultiplexListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  this.streamID = parseInt(request.getResponseHeader("X-Http2-StreamID"));
  var data = read_stream(stream, cnt);
  this.buffer = this.buffer.concat(data);
};

Http2MultiplexListener.prototype.onStopRequest = function (request) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection);
  Assert.ok(this.buffer == multiplexContent);

  request.QueryInterface(Ci.nsIProxiedChannel);
  // This is what does most of the hard work for us
  var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  var streamID = this.streamID;
  this.finish({ httpProxyConnectResponseCode, streamID });
};

// Does the appropriate checks for header gatewaying
var Http2HeaderListener = function (name, callback) {
  this.name = name;
  this.callback = callback;
};

Http2HeaderListener.prototype = new Http2CheckListener();
Http2HeaderListener.prototype.value = "";

Http2HeaderListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  var hvalue = request.getResponseHeader(this.name);
  Assert.notEqual(hvalue, "");
  this.callback(hvalue);
  read_stream(stream, cnt);
};

var Http2PushListener = function (shouldBePushed) {
  this.shouldBePushed = shouldBePushed;
};

Http2PushListener.prototype = new Http2CheckListener();

Http2PushListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  if (
    request.originalURI.spec ==
      `https://localhost:${this.serverPort}/push.js` ||
    request.originalURI.spec ==
      `https://localhost:${this.serverPort}/push2.js` ||
    request.originalURI.spec == `https://localhost:${this.serverPort}/push5.js`
  ) {
    Assert.equal(
      request.getResponseHeader("pushed"),
      this.shouldBePushed ? "yes" : "no"
    );
  }
  read_stream(stream, cnt);
};

const pushHdrTxt =
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const pullHdrTxt = pushHdrTxt.split("").reverse().join("");

function checkContinuedHeaders(getHeader, headerPrefix, headerText) {
  for (var i = 0; i < 265; i++) {
    Assert.equal(getHeader(headerPrefix + 1), headerText);
  }
}

var Http2ContinuedHeaderListener = function () {};

Http2ContinuedHeaderListener.prototype = new Http2CheckListener();

Http2ContinuedHeaderListener.prototype.onStopsLeft = 2;

Http2ContinuedHeaderListener.prototype.QueryInterface = ChromeUtils.generateQI([
  "nsIHttpPushListener",
  "nsIStreamListener",
]);

Http2ContinuedHeaderListener.prototype.getInterface = function (aIID) {
  return this.QueryInterface(aIID);
};

Http2ContinuedHeaderListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  if (
    request.originalURI.spec ==
    `https://localhost:${this.serverPort}/continuedheaders`
  ) {
    // This is the original request, so the only one where we'll have continued response headers
    checkContinuedHeaders(
      request.getResponseHeader,
      "X-Pull-Test-Header-",
      pullHdrTxt
    );
  }
  read_stream(stream, cnt);
};

Http2ContinuedHeaderListener.prototype.onStopRequest = function (
  request,
  status
) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(Components.isSuccessCode(status));
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection);

  --this.onStopsLeft;
  if (this.onStopsLeft === 0) {
    request.QueryInterface(Ci.nsIProxiedChannel);
    var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
    this.finish({ httpProxyConnectResponseCode });
  }
};

Http2ContinuedHeaderListener.prototype.onPush = function (
  associatedChannel,
  pushChannel
) {
  Assert.equal(
    associatedChannel.originalURI.spec,
    "https://localhost:" + this.serverPort + "/continuedheaders"
  );
  Assert.equal(pushChannel.getRequestHeader("x-pushed-request"), "true");
  checkContinuedHeaders(
    pushChannel.getRequestHeader,
    "X-Push-Test-Header-",
    pushHdrTxt
  );

  pushChannel.asyncOpen(this);
};

// Does the appropriate checks for a large GET response
var Http2BigListener = function () {};

Http2BigListener.prototype = new Http2CheckListener();
Http2BigListener.prototype.buffer = "";

Http2BigListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  this.buffer = this.buffer.concat(read_stream(stream, cnt));
  // We know the server should send us the same data as our big post will be,
  // so the md5 should be the same
  Assert.equal(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
};

Http2BigListener.prototype.onStopRequest = function (request) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection);

  // Don't want to flood output, so don't use do_check_eq
  Assert.ok(this.buffer == bigListenerData);

  request.QueryInterface(Ci.nsIProxiedChannel);
  var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  this.finish({ httpProxyConnectResponseCode });
};

var Http2HugeSuspendedListener = function () {};

Http2HugeSuspendedListener.prototype = new Http2CheckListener();
Http2HugeSuspendedListener.prototype.count = 0;

Http2HugeSuspendedListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  this.count += cnt;
  read_stream(stream, cnt);
};

Http2HugeSuspendedListener.prototype.onStopRequest = function (request) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection);
  Assert.equal(this.count, 1024 * 1024 * 1); // 1mb of data expected
  request.QueryInterface(Ci.nsIProxiedChannel);
  var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  this.finish({ httpProxyConnectResponseCode });
};

// Does the appropriate checks for POSTs
var Http2PostListener = function (expected_md5) {
  this.expected_md5 = expected_md5;
};

Http2PostListener.prototype = new Http2CheckListener();
Http2PostListener.prototype.expected_md5 = "";

Http2PostListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.isHttp2Connection = checkIsHttp2(request);
  read_stream(stream, cnt);
  Assert.equal(
    this.expected_md5,
    request.getResponseHeader("X-Calculated-MD5")
  );
};

var ResumeStalledChannelListener = function () {};

ResumeStalledChannelListener.prototype = {
  onStartRequestFired: false,
  onDataAvailableFired: false,
  isHttp2Connection: false,
  shouldBeHttp2: true,
  resumable: null,

  onStartRequest: function testOnStartRequest(request) {
    this.onStartRequestFired = true;
    if (!Components.isSuccessCode(request.status)) {
      do_throw("Channel should have a success code! (" + request.status + ")");
    }

    Assert.ok(request instanceof Ci.nsIHttpChannel);
    Assert.equal(request.responseStatus, 200);
    Assert.equal(request.requestSucceeded, true);
  },

  onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
    this.onDataAvailableFired = true;
    this.isHttp2Connection = checkIsHttp2(request);
    read_stream(stream, cnt);
  },

  onStopRequest: function testOnStopRequest(request, status) {
    Assert.ok(this.onStartRequestFired);
    Assert.ok(Components.isSuccessCode(status));
    Assert.ok(this.onDataAvailableFired);
    Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
    this.resumable.resume();
  },
};

// test a large download that creates stream flow control and
// confirm we can do another independent stream while the download
// stream is stuck
async function test_http2_blocking_download(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/bigdownload`);
  var internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
  internalChannel.initialRwin = 500000; // make the stream.suspend push back in h2
  var p = new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.expected = 3 * 1024 * 1024;
    chan.asyncOpen(listener);
    chan.suspend();
  });
  // wait 5 seconds so that stream flow control kicks in and then see if we
  // can do a basic transaction (i.e. session not blocked). afterwards resume
  // channel
  do_timeout(5000, function () {
    var simpleChannel = makeHTTPChannel(`https://localhost:${serverPort}/`);
    var sl = new ResumeStalledChannelListener();
    sl.resumable = chan;
    simpleChannel.asyncOpen(sl);
  });
  return p;
}

// Make sure we make a HTTP2 connection and both us and the server mark it as such
async function test_http2_basic(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/`);
  var p = new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
  return p;
}

async function test_http2_basic_unblocked_dep(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/basic_unblocked_dep`
  );
  var cos = chan.QueryInterface(Ci.nsIClassOfService);
  cos.addClassFlags(Ci.nsIClassOfService.Unblocked);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

// make sure we don't use h2 when disallowed
async function test_http2_nospdy(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/`);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    var internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
    internalChannel.allowSpdy = false;
    listener.shouldBeHttp2 = false;
    chan.asyncOpen(listener);
  });
}

// Support for making sure XHR works over SPDY
function checkXhr(xhr, finish) {
  if (xhr.readyState != 4) {
    return;
  }

  Assert.equal(xhr.status, 200);
  Assert.equal(checkIsHttp2(xhr), true);
  finish();
}

// Fires off an XHR request over h2
async function test_http2_xhr(serverPort) {
  return new Promise(resolve => {
    var req = new XMLHttpRequest();
    req.open("GET", `https://localhost:${serverPort}/`, true);
    req.addEventListener("readystatechange"function () {
      checkXhr(req, resolve);
    });
    req.send(null);
  });
}

var Http2ConcurrentListener = function () {};

Http2ConcurrentListener.prototype = new Http2CheckListener();
Http2ConcurrentListener.prototype.count = 0;
Http2ConcurrentListener.prototype.target = 0;
Http2ConcurrentListener.prototype.reset = 0;
Http2ConcurrentListener.prototype.recvdHdr = 0;

Http2ConcurrentListener.prototype.onStopRequest = function (request) {
  this.count++;
  Assert.ok(this.isHttp2Connection);
  if (this.recvdHdr > 0) {
    Assert.equal(request.getResponseHeader("X-Recvd"), this.recvdHdr);
  }

  if (this.count == this.target) {
    if (this.reset > 0) {
      Services.prefs.setIntPref(
        "network.http.http2.default-concurrent",
        this.reset
      );
    }
    request.QueryInterface(Ci.nsIProxiedChannel);
    var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
    this.finish({ httpProxyConnectResponseCode });
  }
};

async function test_http2_concurrent(concurrent_channels, serverPort) {
  var p = new Promise(resolve => {
    var concurrent_listener = new Http2ConcurrentListener();
    concurrent_listener.finish = resolve;
    concurrent_listener.target = 201;
    concurrent_listener.reset = Services.prefs.getIntPref(
      "network.http.http2.default-concurrent"
    );
    Services.prefs.setIntPref("network.http.http2.default-concurrent", 100);

    for (var i = 0; i < concurrent_listener.target; i++) {
      concurrent_channels[i] = makeHTTPChannel(
        `https://localhost:${serverPort}/750ms`
      );
      concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
      concurrent_channels[i].asyncOpen(concurrent_listener);
    }
  });
  return p;
}

async function test_http2_concurrent_post(concurrent_channels, serverPort) {
  return new Promise(resolve => {
    var concurrent_listener = new Http2ConcurrentListener();
    concurrent_listener.finish = resolve;
    concurrent_listener.target = 8;
    concurrent_listener.recvdHdr = posts[2].length;
    concurrent_listener.reset = Services.prefs.getIntPref(
      "network.http.http2.default-concurrent"
    );
    Services.prefs.setIntPref("network.http.http2.default-concurrent", 3);

    for (var i = 0; i < concurrent_listener.target; i++) {
      concurrent_channels[i] = makeHTTPChannel(
        `https://localhost:${serverPort}/750msPost`
      );
      concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
      var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
        Ci.nsIStringInputStream
      );
      stream.setByteStringData(posts[2]);
      var uchan = concurrent_channels[i].QueryInterface(Ci.nsIUploadChannel);
      uchan.setUploadStream(stream, "text/plain", stream.available());
      concurrent_channels[i].requestMethod = "POST";
      concurrent_channels[i].asyncOpen(concurrent_listener);
    }
  });
}

// Test to make sure we get multiplexing right
async function test_http2_multiplex(serverPort) {
  let chan1 = makeHTTPChannel(`https://localhost:${serverPort}/multiplex1`);
  let chan2 = makeHTTPChannel(`https://localhost:${serverPort}/multiplex2`);
  let listener1 = new Http2MultiplexListener();
  let listener2 = new Http2MultiplexListener();

  let promises = [];
  let p1 = new Promise(resolve => {
    listener1.finish = resolve;
  });
  promises.push(p1);
  let p2 = new Promise(resolve => {
    listener2.finish = resolve;
  });
  promises.push(p2);

  chan1.asyncOpen(listener1);
  chan2.asyncOpen(listener2);
  return Promise.all(promises);
}

// Test to make sure we gateway non-standard headers properly
async function test_http2_header(serverPort) {
  let chan = makeHTTPChannel(`https://localhost:${serverPort}/header`);
  let hvalue = "Headers are fun";
  chan.setRequestHeader("X-Test-Header", hvalue, false);
  return new Promise(resolve => {
    let listener = new Http2HeaderListener("X-Received-Test-Header"function (
      received_hvalue
    ) {
      Assert.equal(received_hvalue, hvalue);
    });
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

// Test to make sure headers with invalid characters in the name are rejected
async function test_http2_invalid_response_header(serverPort, invalid_kind) {
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.shouldSucceed = false;
    var chan = makeHTTPChannel(
      `https://localhost:${serverPort}/invalid_response_header/${invalid_kind}`
    );
    chan.asyncOpen(listener);
  });
}

// Test to make sure cookies are split into separate fields before compression
async function test_http2_cookie_crumbling(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/cookie_crumbling`
  );
  var cookiesSent = ["a=b""c=d01234567890123456789""e=f"].sort();
  chan.setRequestHeader("Cookie", cookiesSent.join("; "), false);
  return new Promise(resolve => {
    var listener = new Http2HeaderListener("X-Received-Header-Pairs"function (
      pairsReceived
    ) {
      var cookiesReceived = JSON.parse(pairsReceived)
        .filter(function (pair) {
          return pair[0] == "cookie";
        })
        .map(function (pair) {
          return pair[1];
        })
        .sort();
      Assert.equal(cookiesReceived.length, cookiesSent.length);
      cookiesReceived.forEach(function (cookieReceived, index) {
        Assert.equal(cookiesSent[index], cookieReceived);
      });
    });
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push1(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push2(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push.js`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push3(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push2`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push4(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push2.js`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push5(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push5`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push6(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push5.js`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

// this is a basic test where the server sends a simple document with 2 header
// blocks. bug 1027364
async function test_http2_doubleheader(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/doubleheader`);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

// Make sure we handle GETs that cover more than 2 frames properly
async function test_http2_big(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/big`);
  return new Promise(resolve => {
    var listener = new Http2BigListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

async function test_http2_huge_suspended(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/huge`);
  return new Promise(resolve => {
    var listener = new Http2HugeSuspendedListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
    chan.suspend();
    do_timeout(500, chan.resume);
  });
}

// Support for doing a POST
function do_post(content, chan, listener, method) {
  var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
    Ci.nsIStringInputStream
  );
  stream.setByteStringData(content);

  var uchan = chan.QueryInterface(Ci.nsIUploadChannel);
  uchan.setUploadStream(stream, "text/plain", stream.available());

  chan.requestMethod = method;

  chan.asyncOpen(listener);
}

// Make sure we can do a simple POST
async function test_http2_post(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/post`);
  var p = new Promise(resolve => {
    var listener = new Http2PostListener(md5s[0]);
    listener.finish = resolve;
    do_post(posts[0], chan, listener, "POST");
  });
  return p;
}

async function test_http2_empty_post(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/post`);
  var p = new Promise(resolve => {
    var listener = new Http2PostListener("0");
    listener.finish = resolve;
    do_post("", chan, listener, "POST");
  });
  return p;
}

// Make sure we can do a simple PATCH
async function test_http2_patch(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/patch`);
  return new Promise(resolve => {
    var listener = new Http2PostListener(md5s[0]);
    listener.finish = resolve;
    do_post(posts[0], chan, listener, "PATCH");
  });
}

// Make sure we can do a POST that covers more than 2 frames
async function test_http2_post_big(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/post`);
  return new Promise(resolve => {
    var listener = new Http2PostListener(md5s[1]);
    listener.finish = resolve;
    do_post(posts[1], chan, listener, "POST");
  });
}

// When a http proxy is used alt-svc is disable. Therefore if withProxy is true,
// try numberOfTries times to connect and make sure that alt-svc is not use and we never
// connect to the HTTP/2 server.
var altsvcClientListener = function (
  finish,
  httpserv,
  httpserv2,
  withProxy,
  numberOfTries
) {
  this.finish = finish;
  this.httpserv = httpserv;
  this.httpserv2 = httpserv2;
  this.withProxy = withProxy;
  this.numberOfTries = numberOfTries;
};

altsvcClientListener.prototype = {
  onStartRequest: function test_onStartR(request) {
    Assert.equal(request.status, Cr.NS_OK);
  },

  onDataAvailable: function test_ODA(request, stream, offset, cnt) {
    read_stream(stream, cnt);
  },

  onStopRequest: function test_onStopR(request) {
    var isHttp2Connection = checkIsHttp2(
      request.QueryInterface(Ci.nsIHttpChannel)
    );
    if (!isHttp2Connection) {
      dump("/altsvc1 not over h2 yet - retry\n");
      if (this.withProxy && this.numberOfTries == 0) {
        request.QueryInterface(Ci.nsIProxiedChannel);
        var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
        this.finish({ httpProxyConnectResponseCode });
        return;
      }
      let chan = makeHTTPChannel(
        `http://foo.example.com:${this.httpserv}/altsvc1`,
        this.withProxy
      ).QueryInterface(Ci.nsIHttpChannel);
      // we use this header to tell the server to issue a altsvc frame for the
      // speficied origin we will use in the next part of the test
      chan.setRequestHeader(
        "x-redirect-origin",
        `http://foo.example.com:${this.httpserv2}`,
        false
      );
      chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
      chan.asyncOpen(
        new altsvcClientListener(
          this.finish,
          this.httpserv,
          this.httpserv2,
          this.withProxy,
          this.numberOfTries - 1
        )
      );
    } else {
      Assert.ok(isHttp2Connection);
      let chan = makeHTTPChannel(
        `http://foo.example.com:${this.httpserv2}/altsvc2`
      ).QueryInterface(Ci.nsIHttpChannel);
      chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
      chan.asyncOpen(
        new altsvcClientListener2(this.finish, this.httpserv, this.httpserv2)
      );
    }
  },
};

var altsvcClientListener2 = function (finish, httpserv, httpserv2) {
  this.finish = finish;
  this.httpserv = httpserv;
  this.httpserv2 = httpserv2;
};

altsvcClientListener2.prototype = {
  onStartRequest: function test_onStartR(request) {
    Assert.equal(request.status, Cr.NS_OK);
  },

  onDataAvailable: function test_ODA(request, stream, offset, cnt) {
    read_stream(stream, cnt);
  },

  onStopRequest: function test_onStopR(request) {
    var isHttp2Connection = checkIsHttp2(
      request.QueryInterface(Ci.nsIHttpChannel)
    );
    if (!isHttp2Connection) {
      dump("/altsvc2 not over h2 yet - retry\n");
      var chan = makeHTTPChannel(
        `http://foo.example.com:${this.httpserv2}/altsvc2`
      ).QueryInterface(Ci.nsIHttpChannel);
      chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
      chan.asyncOpen(
        new altsvcClientListener2(this.finish, this.httpserv, this.httpserv2)
      );
    } else {
      Assert.ok(isHttp2Connection);
      request.QueryInterface(Ci.nsIProxiedChannel);
      var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
      this.finish({ httpProxyConnectResponseCode });
    }
  },
};

async function test_http2_altsvc(httpserv, httpserv2, withProxy) {
  var chan = makeHTTPChannel(
    `http://foo.example.com:${httpserv}/altsvc1`,
    withProxy
  ).QueryInterface(Ci.nsIHttpChannel);
  return new Promise(resolve => {
    var numberOfTries = 0;
    if (withProxy) {
      numberOfTries = 20;
    }
    chan.asyncOpen(
      new altsvcClientListener(
        resolve,
        httpserv,
        httpserv2,
        withProxy,
        numberOfTries
      )
    );
  });
}

var Http2PushApiListener = function (finish, serverPort) {
  this.finish = finish;
  this.serverPort = serverPort;
};

Http2PushApiListener.prototype = {
  checksPending: 9, // 4 onDataAvailable and 5 onStop

  getInterface(aIID) {
    return this.QueryInterface(aIID);
  },

  QueryInterface: ChromeUtils.generateQI([
    "nsIHttpPushListener",
    "nsIStreamListener",
  ]),

  // nsIHttpPushListener
  onPush: function onPush(associatedChannel, pushChannel) {
    Assert.equal(
      associatedChannel.originalURI.spec,
      "https://localhost:" + this.serverPort + "/pushapi1"
    );
    Assert.equal(pushChannel.getRequestHeader("x-pushed-request"), "true");

    pushChannel.asyncOpen(this);
    if (
      pushChannel.originalURI.spec ==
      "https://localhost:" + this.serverPort + "/pushapi1/2"
    ) {
      pushChannel.cancel(Cr.NS_ERROR_ABORT);
    } else if (
      pushChannel.originalURI.spec ==
      "https://localhost:" + this.serverPort + "/pushapi1/3"
    ) {
      Assert.ok(pushChannel.getRequestHeader("Accept-Encoding").includes("br"));
    }
  },

  // normal Channel listeners
  onStartRequest: function pushAPIOnStart() {},

  onDataAvailable: function pushAPIOnDataAvailable(
    request,
    stream,
    offset,
    cnt
  ) {
    Assert.notEqual(
      request.originalURI.spec,
      `https://localhost:${this.serverPort}/pushapi1/2`
    );

    var data = read_stream(stream, cnt);

    if (
      request.originalURI.spec ==
      `https://localhost:${this.serverPort}/pushapi1`
    ) {
      Assert.equal(data[0], "0");
      --this.checksPending;
    } else if (
      request.originalURI.spec ==
      `https://localhost:${this.serverPort}/pushapi1/1`
    ) {
      Assert.equal(data[0], "1");
      --this.checksPending; // twice
    } else if (
      request.originalURI.spec ==
      `https://localhost:${this.serverPort}/pushapi1/3`
    ) {
      Assert.equal(data[0], "3");
      --this.checksPending;
    } else {
      Assert.equal(truefalse);
    }
  },

  onStopRequest: function test_onStopR(request) {
    if (
      request.originalURI.spec ==
      `https://localhost:${this.serverPort}/pushapi1/2`
    ) {
      Assert.equal(request.status, Cr.NS_ERROR_ABORT);
    } else {
      Assert.equal(request.status, Cr.NS_OK);
    }

    --this.checksPending; // 5 times - one for each push plus the pull
    if (!this.checksPending) {
      request.QueryInterface(Ci.nsIProxiedChannel);
      var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
      this.finish({ httpProxyConnectResponseCode });
    }
  },
};

// pushAPI testcase 1 expects
// 1 to pull /pushapi1 with 0
// 2 to see /pushapi1/1 with 1
// 3 to see /pushapi1/1 with 1 (again)
// 4 to see /pushapi1/2 that it will cancel
// 5 to see /pushapi1/3 with 3 with brotli

async function test_http2_pushapi_1(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/pushapi1`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2PushApiListener(resolve, serverPort);
    chan.notificationCallbacks = listener;
    chan.asyncOpen(listener);
  });
}

var WrongSuiteListener = function () {};

WrongSuiteListener.prototype = new Http2CheckListener();
WrongSuiteListener.prototype.shouldBeHttp2 = false;
WrongSuiteListener.prototype.onStopRequest = function (request, status) {
  Services.prefs.setBoolPref(
    "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
    true
  );
  Services.prefs.clearUserPref("security.tls.version.max");
  Http2CheckListener.prototype.onStopRequest.call(this, request, status);
};

// test that we use h1 without the mandatory cipher suite available when
// offering at most tls1.2
async function test_http2_wrongsuite_tls12(serverPort) {
  Services.prefs.setBoolPref(
    "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
    false
  );
  Services.prefs.setIntPref("security.tls.version.max", 3);
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/wrongsuite`);
  chan.loadFlags =
    Ci.nsIRequest.LOAD_FRESH_CONNECTION |
    Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
  return new Promise(resolve => {
    var listener = new WrongSuiteListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

// test that we use h2 when offering tls1.3 or higher regardless of if the
// mandatory cipher suite is available
async function test_http2_wrongsuite_tls13(serverPort) {
  Services.prefs.setBoolPref(
    "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
    false
  );
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/wrongsuite`);
  chan.loadFlags =
    Ci.nsIRequest.LOAD_FRESH_CONNECTION |
    Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
  return new Promise(resolve => {
    var listener = new WrongSuiteListener();
    listener.finish = resolve;
    listener.shouldBeHttp2 = true;
    chan.asyncOpen(listener);
  });
}

async function test_http2_h11required_stream(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/h11required_stream`
  );
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.shouldBeHttp2 = false;
    chan.asyncOpen(listener);
  });
}

function H11RequiredSessionListener() {}

H11RequiredSessionListener.prototype = new Http2CheckListener();

H11RequiredSessionListener.prototype.onStopRequest = function (request) {
  var streamReused = request.getResponseHeader("X-H11Required-Stream-Ok");
  Assert.equal(streamReused, "yes");

  Assert.ok(this.onStartRequestFired);
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);

  request.QueryInterface(Ci.nsIProxiedChannel);
  var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  this.finish({ httpProxyConnectResponseCode });
};

async function test_http2_h11required_session(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/h11required_session`
  );
  return new Promise(resolve => {
    var listener = new H11RequiredSessionListener();
    listener.finish = resolve;
    listener.shouldBeHttp2 = false;
    chan.asyncOpen(listener);
  });
}

async function test_http2_retry_rst(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/rstonce`);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

async function test_http2_continuations(loadGroup, serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/continuedheaders`
  );
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2ContinuedHeaderListener();
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.notificationCallbacks = listener;
    chan.asyncOpen(listener);
  });
}

async function test_http2_continuations_over_max_response_limit(
  loadGroup,
  serverPort
) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/hugecontinuedheaders?size=385`
  );
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.shouldSucceed = false;
    chan.asyncOpen(listener);
  });
}

function Http2IllegalHpackValidationListener() {}

Http2IllegalHpackValidationListener.prototype = new Http2CheckListener();
Http2IllegalHpackValidationListener.prototype.shouldGoAway = false;

Http2IllegalHpackValidationListener.prototype.onStopRequest = function (
  request
) {
  var wentAway = request.getResponseHeader("X-Did-Goaway") === "yes";
  Assert.equal(wentAway, this.shouldGoAway);

  Assert.ok(this.onStartRequestFired);
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);

  request.QueryInterface(Ci.nsIProxiedChannel);
  var httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  this.finish({ httpProxyConnectResponseCode });
};

function Http2IllegalHpackListener() {}
Http2IllegalHpackListener.prototype = new Http2CheckListener();
Http2IllegalHpackListener.prototype.shouldGoAway = false;

Http2IllegalHpackListener.prototype.onStopRequest = function () {
  var chan = makeHTTPChannel(
    `https://localhost:${this.serverPort}/illegalhpack_validate`
  );
  var listener = new Http2IllegalHpackValidationListener();
  listener.finish = this.finish;
  listener.shouldGoAway = this.shouldGoAway;
  chan.asyncOpen(listener);
};

async function test_http2_illegalhpacksoft(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/illegalhpacksoft`
  );
  return new Promise(resolve => {
    var listener = new Http2IllegalHpackListener();
    listener.finish = resolve;
    listener.serverPort = serverPort;
    listener.shouldGoAway = false;
    listener.shouldSucceed = false;
    chan.asyncOpen(listener);
  });
}

async function test_http2_illegalhpackhard(serverPort) {
  var chan = makeHTTPChannel(
    `https://localhost:${serverPort}/illegalhpackhard`
  );
  return new Promise(resolve => {
    var listener = new Http2IllegalHpackListener();
    listener.finish = resolve;
    listener.serverPort = serverPort;
    listener.shouldGoAway = true;
    listener.shouldSucceed = false;
    chan.asyncOpen(listener);
  });
}

async function test_http2_folded_header(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/foldedheader`);
  chan.loadGroup = loadGroup;
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.shouldSucceed = false;
    chan.asyncOpen(listener);
  });
}

async function test_http2_empty_data(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/emptydata`);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_firstparty1(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { firstPartyDomain: "foo.com" };
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_firstparty2(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push.js`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { firstPartyDomain: "bar.com" };
  return new Promise(resolve => {
    var listener = new Http2PushListener(false);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_firstparty3(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push.js`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { firstPartyDomain: "foo.com" };
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_userContext1(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { userContextId: 1 };
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_userContext2(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push.js`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { userContextId: 2 };
  return new Promise(resolve => {
    var listener = new Http2PushListener(false);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_push_userContext3(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/push.js`);
  chan.loadGroup = loadGroup;
  chan.loadInfo.originAttributes = { userContextId: 1 };
  return new Promise(resolve => {
    var listener = new Http2PushListener(true);
    listener.finish = resolve;
    listener.serverPort = serverPort;
    chan.asyncOpen(listener);
  });
}

async function test_http2_status_phrase(serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/statusphrase`);
  return new Promise(resolve => {
    var listener = new Http2CheckListener();
    listener.finish = resolve;
    listener.shouldSucceed = false;
    chan.asyncOpen(listener);
  });
}

var PulledDiskCacheListener = function () {};
PulledDiskCacheListener.prototype = new Http2CheckListener();
PulledDiskCacheListener.prototype.EXPECTED_DATA = "this was pulled via h2";
PulledDiskCacheListener.prototype.readData = "";
PulledDiskCacheListener.prototype.onDataAvailable =
  function testOnDataAvailable(request, stream, off, cnt) {
    this.onDataAvailableFired = true;
    this.isHttp2Connection = checkIsHttp2(request);
    this.accum += cnt;
    this.readData += read_stream(stream, cnt);
  };
PulledDiskCacheListener.prototype.onStopRequest = function testOnStopRequest(
  request,
  status
) {
  Assert.equal(this.EXPECTED_DATA, this.readData);
  Http2CheckListener.prorotype.onStopRequest.call(this, request, status);
};

const DISK_CACHE_DATA = "this is from disk cache";

var FromDiskCacheListener = function (finish, loadGroup, serverPort) {
  this.finish = finish;
  this.loadGroup = loadGroup;
  this.serverPort = serverPort;
};
FromDiskCacheListener.prototype = {
  onStartRequestFired: false,
  onDataAvailableFired: false,
  readData: "",

  onStartRequest: function testOnStartRequest(request) {
    this.onStartRequestFired = true;
    if (!Components.isSuccessCode(request.status)) {
      do_throw("Channel should have a success code! (" + request.status + ")");
    }

    Assert.ok(request instanceof Ci.nsIHttpChannel);
    Assert.ok(request.requestSucceeded);
    Assert.equal(request.responseStatus, 200);
  },

  onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
    this.onDataAvailableFired = true;
    this.readData += read_stream(stream, cnt);
  },

  onStopRequest: function testOnStopRequest(request, status) {
    Assert.ok(this.onStartRequestFired);
    Assert.ok(Components.isSuccessCode(status));
    Assert.ok(this.onDataAvailableFired);
    Assert.equal(this.readData, DISK_CACHE_DATA);

    evict_cache_entries("disk");
    syncWithCacheIOThread(() => {
      // Now that we know the entry is out of the disk cache, check to make sure
      // we don't have this hiding in the push cache somewhere - if we do, it
      // didn't get cancelled, and we have a bug.
      var chan = makeHTTPChannel(
        `https://localhost:${this.serverPort}/diskcache`
      );
      var listener = new PulledDiskCacheListener();
      listener.finish = this.finish;
      chan.loadGroup = this.loadGroup;
      chan.asyncOpen(listener);
    });
  },
};

var Http2DiskCachePushListener = function () {};
Http2DiskCachePushListener.prototype = new Http2CheckListener();

Http2DiskCachePushListener.onStopRequest = function (request, status) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(Components.isSuccessCode(status));
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);

  // Now we need to open a channel to ensure we get data from the disk cache
  // for the pushed item, instead of from the push cache.
  var chan = makeHTTPChannel(`https://localhost:${this.serverPort}/diskcache`);
  var listener = new FromDiskCacheListener(
    this.finish,
    this.loadGroup,
    this.serverPort
  );
  chan.loadGroup = this.loadGroup;
  chan.asyncOpen(listener);
};

function continue_test_http2_disk_cache_push(
  status,
  entry,
  finish,
  loadGroup,
  serverPort
) {
  // TODO - store stuff in cache entry, then open an h2 channel that will push
  // this, once that completes, open a channel for the cache entry we made and
  // ensure it came from disk cache, not the push cache.
  var outputStream = entry.openOutputStream(0, -1);
  outputStream.write(DISK_CACHE_DATA, DISK_CACHE_DATA.length);

  // Now we open our URL that will push data for the URL above
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/pushindisk`);
  var listener = new Http2DiskCachePushListener();
  listener.finish = finish;
  listener.loadGroup = loadGroup;
  listener.serverPort = serverPort;
  chan.loadGroup = loadGroup;
  chan.asyncOpen(listener);
}

async function test_http2_disk_cache_push(loadGroup, serverPort) {
  return new Promise(resolve => {
    asyncOpenCacheEntry(
      `https://localhost:${serverPort}/diskcache`,
      "disk",
      Ci.nsICacheStorage.OPEN_NORMALLY,
      null,
      function (status, entry) {
        continue_test_http2_disk_cache_push(
          status,
          entry,
          resolve,
          loadGroup,
          serverPort
        );
      },
      false
    );
  });
}

var Http2DoublepushListener = function () {};
Http2DoublepushListener.prototype = new Http2CheckListener();
Http2DoublepushListener.prototype.onStopRequest = function (request, status) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(Components.isSuccessCode(status));
  Assert.ok(this.onDataAvailableFired);
  Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);

  var chan = makeHTTPChannel(
    `https://localhost:${this.serverPort}/doublypushed`
  );
  var listener = new Http2DoublypushedListener();
  listener.finish = this.finish;
  chan.loadGroup = this.loadGroup;
  chan.asyncOpen(listener);
};

var Http2DoublypushedListener = function () {};
Http2DoublypushedListener.prototype = new Http2CheckListener();
Http2DoublypushedListener.prototype.readData = "";
Http2DoublypushedListener.prototype.onDataAvailable = function (
  request,
  stream,
  off,
  cnt
) {
  this.onDataAvailableFired = true;
  this.accum += cnt;
  this.readData += read_stream(stream, cnt);
};
Http2DoublypushedListener.prototype.onStopRequest = function (request, status) {
  Assert.ok(this.onStartRequestFired);
  Assert.ok(Components.isSuccessCode(status));
  Assert.ok(this.onDataAvailableFired);
  Assert.equal(this.readData, "pushed");

  request.QueryInterface(Ci.nsIProxiedChannel);
  let httpProxyConnectResponseCode = request.httpProxyConnectResponseCode;
  this.finish({ httpProxyConnectResponseCode });
};

function test_http2_doublepush(loadGroup, serverPort) {
  var chan = makeHTTPChannel(`https://localhost:${serverPort}/doublepush`);
  return new Promise(resolve => {
    var listener = new Http2DoublepushListener();
    listener.finish = resolve;
    listener.loadGroup = loadGroup;
    listener.serverPort = serverPort;
    chan.loadGroup = loadGroup;
    chan.asyncOpen(listener);
  });
}

Messung V0.5
C=91 H=99 G=94

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.15Bemerkung:  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.