Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/testing/xpcshell/moz-http2/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 10.2.2025 mit Größe 59 kB image not shown  

Quelle  moz-http2.js   Sprache: JAVA

 
/* 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/. */


// This module is the stateful server side of test_http2.js and is meant
// to have node be restarted in between each invocation

/* eslint-env node */

var node_http2_root = "../node-http2";
if (process.env.NODE_HTTP2_ROOT) {
  node_http2_root = process.env.NODE_HTTP2_ROOT;
}
var http2 = require(node_http2_root);
var fs = require("fs");
var url = require("url");
var crypto = require("crypto");
const dnsPacket = require(`${node_http2_root}/../dns-packet`);
const ip = require(`${node_http2_root}/../node_ip`);
const { fork } = require("child_process");
const { spawn } = require("child_process");
const path = require("path");
const zlib = require("zlib");

// Hook into the decompression code to log the decompressed name-value pairs
var compression_module = node_http2_root + "/lib/protocol/compressor";
var http2_compression = require(compression_module);
var HeaderSetDecompressor = http2_compression.HeaderSetDecompressor;
var originalRead = HeaderSetDecompressor.prototype.read;
var lastDecompressor;
var decompressedPairs;
HeaderSetDecompressor.prototype.read = function () {
  if (this != lastDecompressor) {
    lastDecompressor = this;
    decompressedPairs = [];
  }
  var pair = originalRead.apply(this, arguments);
  if (pair) {
    decompressedPairs.push(pair);
  }
  return pair;
};

var connection_module = node_http2_root + "/lib/protocol/connection";
var http2_connection = require(connection_module);
var Connection = http2_connection.Connection;
var originalClose = Connection.prototype.close;
Connection.prototype.close = function (error, lastId) {
  if (lastId !== undefined) {
    this._lastIncomingStream = lastId;
  }

  originalClose.apply(this, arguments);
};

var framer_module = node_http2_root + "/lib/protocol/framer";
var http2_framer = require(framer_module);
var Serializer = http2_framer.Serializer;
var originalTransform = Serializer.prototype._transform;
var newTransform = function (frame) {
  if (frame.type == "DATA") {
    // Insert our empty DATA frame
    const emptyFrame = {};
    emptyFrame.type = "DATA";
    emptyFrame.data = Buffer.alloc(0);
    emptyFrame.flags = [];
    emptyFrame.stream = frame.stream;
    var buffers = [];
    Serializer.DATA(emptyFrame, buffers);
    Serializer.commonHeader(emptyFrame, buffers);
    for (var i = 0; i < buffers.length; i++) {
      this.push(buffers[i]);
    }

    // Reset to the original version for later uses
    Serializer.prototype._transform = originalTransform;
  }
  originalTransform.apply(this, arguments);
};

function getHttpContent(pathName) {
  var content =
    "" +
    "" +
    "HOORAY!" +
    // 'You Win!' used in tests to check we reached this server
    "You Win! (by requesting" +
    pathName +
    ")" +
    "";
  return content;
}

function generateContent(size) {
  var content = "";
  for (var i = 0; i < size; i++) {
    content += "0";
  }
  return content;
}

/* This takes care of responding to the multiplexed request for us */
var m = {
  mp1res: null,
  mp2res: null,
  buf: null,
  mp1start: 0,
  mp2start: 0,

  checkReady() {
    if (this.mp1res != null && this.mp2res != null) {
      this.buf = generateContent(30 * 1024);
      this.mp1start = 0;
      this.mp2start = 0;
      this.send(this.mp1res, 0);
      setTimeout(this.send.bind(thisthis.mp2res, 0), 5);
    }
  },

  send(res, start) {
    var end = Math.min(start + 1024, this.buf.length);
    var content = this.buf.substring(start, end);
    res.write(content);
    if (end < this.buf.length) {
      setTimeout(this.send.bind(this, res, end), 10);
    } else {
      // Clear these variables so we can run the test again with --verify
      if (res == this.mp1res) {
        this.mp1res = null;
      } else {
        this.mp2res = null;
      }
      res.end();
    }
  },
};

var runlater = function () {};
runlater.prototype = {
  req: null,
  resp: null,
  fin: true,

  onTimeout: function onTimeout() {
    this.resp.writeHead(200);
    if (this.fin) {
      this.resp.end("It's all good 750ms.");
    }
  },
};

var runConnectLater = function () {};
runConnectLater.prototype = {
  req: null,
  resp: null,
  connect: false,

  onTimeout: function onTimeout() {
    if (this.connect) {
      this.resp.writeHead(200);
      this.connect = true;
      setTimeout(executeRunLaterCatchError, 50, this);
    } else {
      this.resp.end("HTTP/1.1 200\n\r\n\r");
    }
  },
};

var moreData = function () {};
moreData.prototype = {
  req: null,
  resp: null,
  iter: 3,

  onTimeout: function onTimeout() {
    // 1mb of data
    const content = generateContent(1024 * 1024);
    this.resp.write(content); // 1mb chunk
    this.iter--;
    if (!this.iter) {
      this.resp.end();
    } else {
      setTimeout(executeRunLater, 1, this);
    }
  },
};

var resetLater = function () {};
resetLater.prototype = {
  resp: null,

  onTimeout: function onTimeout() {
    this.resp.stream.reset("HTTP_1_1_REQUIRED");
  },
};

function executeRunLater(arg) {
  arg.onTimeout();
}

function executeRunLaterCatchError(arg) {
  arg.onTimeout();
}

var h11required_conn = null;
var h11required_header = "yes";
var didRst = false;
var rstConnection = null;
var illegalheader_conn = null;

var gDoHPortsLog = [];
var gDoHNewConnLog = {};
var gDoHRequestCount = 0;

// eslint-disable-next-line complexity
function handleRequest(req, res) {
  var u = "";
  if (req.url != undefined) {
    u = url.parse(req.url, true);
  }
  var content = getHttpContent(u.pathname);
  var push, push1, push1a, push2, push3;

  // PushService tests.
  var pushPushServer1, pushPushServer2, pushPushServer3, pushPushServer4;

  function createCNameContent(payload) {
    let packet = dnsPacket.decode(payload);
    if (
      packet.questions[0].name == "cname.example.com" &&
      packet.questions[0].type == "A"
    ) {
      return dnsPacket.encode({
        id: 0,
        type: "response",
        flags: dnsPacket.RECURSION_DESIRED,
        questions: [{ name: packet.questions[0].name, type: "A"class"IN" }],
        answers: [
          {
            name: packet.questions[0].name,
            ttl: 55,
            type: "CNAME",
            flush: false,
            data: "pointing-elsewhere.example.com",
          },
        ],
      });
    }
    if (
      packet.questions[0].name == "pointing-elsewhere.example.com" &&
      packet.questions[0].type == "A"
    ) {
      return dnsPacket.encode({
        id: 0,
        type: "response",
        flags: dnsPacket.RECURSION_DESIRED,
        questions: [{ name: packet.questions[0].name, type: "A"class"IN" }],
        answers: [
          {
            name: packet.questions[0].name,
            ttl: 55,
            type: "A",
            flush: false,
            data: "99.88.77.66",
          },
        ],
      });
    }

    return dnsPacket.encode({
      id: 0,
      type: "response",
      flags: dnsPacket.RECURSION_DESIRED | dnsPacket.rcodes.toRcode("NXDOMAIN"),
      questions: [
        {
          name: packet.questions[0].name,
          type: packet.questions[0].type,
          class"IN",
        },
      ],
      answers: [],
    });
  }

  function createCNameARecord() {
    // test23 asks for cname-a.example.com
    // this responds with a CNAME to here.example.com *and* an A record
    // for here.example.com
    let rContent;

    rContent = Buffer.from(
      "0000" +
        "0100" +
        "0001" + // QDCOUNT
        "0002" + // ANCOUNT
        "00000000" + // NSCOUNT + ARCOUNT
        "07636E616D652d61" + // cname-a
        "076578616D706C6503636F6D00" + // .example.com
        "00010001" + // question type (A) + question class (IN)
        // answer record 1
        "C00C" + // name pointer to cname-a.example.com
        "0005" + // type (CNAME)
        "0001" + // class
        "00000037" + // TTL
        "0012" + // RDLENGTH
        "0468657265" + // here
        "076578616D706C6503636F6D00" + // .example.com
        // answer record 2, the A entry for the CNAME above
        "0468657265" + // here
        "076578616D706C6503636F6D00" + // .example.com
        "0001" + // type (A)
        "0001" + // class
        "00000037" + // TTL
        "0004" + // RDLENGTH
        "09080706"// IPv4 address
      "hex"
    );

    return rContent;
  }

  function responseType(packet, responseIP) {
    if (
      !!packet.questions.length &&
      packet.questions[0].name == "confirm.example.com" &&
      packet.questions[0].type == "NS"
    ) {
      return "NS";
    }

    return ip.isV4Format(responseIP) ? "A" : "AAAA";
  }

  function handleAuth() {
    // There's a Set-Cookie: header in the response for "/dns" , which this
    // request subsequently would include if the http channel wasn't
    // anonymous. Thus, if there's a cookie in this request, we know Firefox
    // mishaved. If there's not, we're fine.
    if (req.headers.cookie) {
      res.writeHead(403);
      res.end("cookie for me, not for you");
      return false;
    }
    if (req.headers.authorization != "user:password") {
      res.writeHead(401);
      res.end("bad boy!");
      return false;
    }

    return true;
  }

  function createDNSAnswer(response, packet, responseIP, requestPayload) {
    // This shuts down the connection so we can test if the client reconnects
    if (packet.questions.length && packet.questions[0].name == "closeme.com") {
      response.stream.connection.close("INTERNAL_ERROR", response.stream.id);
      return null;
    }

    let answers = [];
    if (packet.questions.length && packet.questions[0].name.endsWith(".pd")) {
      // Bug 1543811: test edns padding extension. Return whether padding was
      // included via the first half of the ip address (1.1 vs 2.2) and the
      // size of the request in the second half of the ip address allowing to
      // verify that the correct amount of padding was added.
      if (
        !!packet.additionals.length &&
        packet.additionals[0].type == "OPT" &&
        packet.additionals[0].options.some(o => o.type === "PADDING")
      ) {
        // add padding to the response, because the client must be able ignore it
        answers.push({
          name: ".",
          type: "PADDING",
          data: Buffer.from(
            // PADDING_PADDING_PADDING
            "50414444494e475f50414444494e475f50414444494e47",
            "hex"
          ),
        });
        responseIP =
          "1.1." +
          ((requestPayload.length >> 8) & 0xff) +
          "." +
          (requestPayload.length & 0xff);
      } else {
        responseIP =
          "2.2." +
          ((requestPayload.length >> 8) & 0xff) +
          "." +
          (requestPayload.length & 0xff);
      }
    }

    if (u.query.corruptedAnswer) {
      // DNS response header is 12 bytes, we check for this minimum length
      // at the start of decoding so this is the simplest way to force
      // a decode error.
      return "\xFF\xFF\xFF\xFF";
    }

    // Because we send two TRR requests (A and AAAA), skip the first two
    // requests when testing retry.
    if (u.query.retryOnDecodeFailure && gDoHRequestCount < 2) {
      gDoHRequestCount++;
      return "\xFF\xFF\xFF\xFF";
    }

    function responseData() {
      if (
        !!packet.questions.length &&
        packet.questions[0].name == "confirm.example.com" &&
        packet.questions[0].type == "NS"
      ) {
        return "ns.example.com";
      }

      return responseIP;
    }

    if (
      responseIP != "none" &&
      responseType(packet, responseIP) == packet.questions[0].type
    ) {
      answers.push({
        name: u.query.hostname ? u.query.hostname : packet.questions[0].name,
        ttl: 55,
        type: responseType(packet, responseIP),
        flush: false,
        data: responseData(),
      });
    }

    // for use with test_dns_by_type_resolve.js
    if (packet.questions[0].type == "TXT") {
      answers.push({
        name: packet.questions[0].name,
        type: packet.questions[0].type,
        ttl: 55,
        class"IN",
        flush: false,
        data: Buffer.from(
          "62586B67646D39705932556761584D6762586B676347467A63336476636D513D",
          "hex"
        ),
      });
    }

    if (u.query.cnameloop) {
      answers.push({
        name: "cname.example.com",
        type: "CNAME",
        ttl: 55,
        class"IN",
        flush: false,
        data: "pointing-elsewhere.example.com",
      });
    }

    if (req.headers["accept-language"] || req.headers["user-agent"]) {
      // If we get this header, don't send back any response. This should
      // cause the tests to fail. This is easier then actually sending back
      // the header value into test_trr.js
      answers = [];
    }

    let buf = dnsPacket.encode({
      type: "response",
      id: packet.id,
      flags: dnsPacket.RECURSION_DESIRED,
      questions: packet.questions,
      answers,
    });

    return buf;
  }

  function getDelayFromPacket(packet, type) {
    let delay = 0;
    if (packet.questions[0].type == "A") {
      delay = parseInt(u.query.delayIPv4);
    } else if (packet.questions[0].type == "AAAA") {
      delay = parseInt(u.query.delayIPv6);
    }

    if (u.query.slowConfirm && type == "NS") {
      delay += 1000;
    }

    return delay;
  }

  function writeDNSResponse(response, buf, delay, contentType) {
    function writeResponse(resp, buffer) {
      resp.setHeader("Set-Cookie""trackyou=yes; path=/; max-age=100000;");
      resp.setHeader("Content-Type", contentType);
      if (req.headers["accept-encoding"].includes("gzip")) {
        zlib.gzip(buffer, function (err, result) {
          resp.setHeader("Content-Encoding""gzip");
          resp.setHeader("Content-Length", result.length);
          try {
            resp.writeHead(200);
            resp.end(result);
          } catch (e) {
            // connection was closed by the time we started writing.
          }
        });
      } else {
        const output = Buffer.from(buffer, "utf-8");
        resp.setHeader("Content-Length", output.length);
        try {
          resp.writeHead(200);
          resp.write(output);
          resp.end("");
        } catch (e) {
          // connection was closed by the time we started writing.
        }
      }
    }

    if (delay) {
      setTimeout(
        arg => {
          writeResponse(arg[0], arg[1]);
        },
        delay,
        [response, buf]
      );
      return;
    }

    writeResponse(response, buf);
  }

  if (req.httpVersionMajor === 2) {
    res.setHeader("X-Connection-Http2""yes");
    res.setHeader("X-Http2-StreamId""" + req.stream.id);
  } else {
    res.setHeader("X-Connection-Http2""no");
  }

  if (u.pathname === "/exit") {
    res.setHeader("Content-Type""text/plain");
    res.setHeader("Connection""close");
    res.writeHead(200);
    res.end("ok");
    process.exit();
  }

  if (req.method == "CONNECT") {
    if (req.headers.host == "illegalhpacksoft.example.com:80") {
      illegalheader_conn = req.stream.connection;
      res.setHeader("Content-Type""text/html");
      res.setHeader("x-softillegalhpack""true");
      res.writeHead(200);
      res.end(content);
      return;
    } else if (req.headers.host == "illegalhpackhard.example.com:80") {
      res.setHeader("Content-Type""text/html");
      res.setHeader("x-hardillegalhpack""true");
      res.writeHead(200);
      res.end(content);
      return;
    } else if (req.headers.host == "750.example.com:80") {
      // This response will mock a response through a proxy to a HTTP server.
      // After 750ms , a 200 response for the proxy will be sent then
      // after additional 50ms a 200 response for the HTTP GET request.
      let rl = new runConnectLater();
      rl.req = req;
      rl.resp = res;
      setTimeout(executeRunLaterCatchError, 750, rl);
      return;
    } else if (req.headers.host == "h11required.com:80") {
      if (req.httpVersionMajor === 2) {
        res.stream.reset("HTTP_1_1_REQUIRED");
      }
      return;
    }
  } else if (u.pathname === "/750ms") {
    let rl = new runlater();
    rl.req = req;
    rl.resp = res;
    setTimeout(executeRunLater, 750, rl);
    return;
  } else if (u.pathname === "/750msNoData") {
    let rl = new runlater();
    rl.req = req;
    rl.resp = res;
    rl.fin = false;
    setTimeout(executeRunLater, 750, rl);
    return;
  } else if (u.pathname === "/multiplex1" && req.httpVersionMajor === 2) {
    res.setHeader("Content-Type""text/plain");
    res.writeHead(200);
    m.mp1res = res;
    m.checkReady();
    return;
  } else if (u.pathname === "/multiplex2" && req.httpVersionMajor === 2) {
    res.setHeader("Content-Type""text/plain");
    res.writeHead(200);
    m.mp2res = res;
    m.checkReady();
    return;
  } else if (u.pathname === "/header") {
    var val = req.headers["x-test-header"];
    if (val) {
      res.setHeader("X-Received-Test-Header", val);
    }
  } else if (u.pathname === "/doubleheader") {
    res.setHeader("Content-Type""text/html");
    res.writeHead(200);
    res.write(content);
    res.writeHead(200);
    res.end();
    return;
  } else if (u.pathname === "/cookie_crumbling") {
    res.setHeader("X-Received-Header-Pairs", JSON.stringify(decompressedPairs));
  } else if (u.pathname === "/push") {
    push = res.push("/push.js");
    push.writeHead(200, {
      "content-type""application/javascript",
      pushed: "yes",
      "content-length": 11,
      "X-Connection-Http2""yes",
    });
    push.end("// comments");
    content = '