const CC = Components.Constructor;
const BinaryInputStream = CC(
  "@mozilla.org/binaryinputstream;1",
  "nsIBinaryInputStream",
  "setInputStream"
);
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);

// eslint-disable-next-line complexity
function handleRequest(request, response) {
  var query = {};
  request.queryString.split("&").forEach(function (val) {
    var [name, value] = val.split("=");
    query[name] = unescape(value);
  });

  var isPreflight = request.method == "OPTIONS";

  var bodyStream = new BinaryInputStream(request.bodyInputStream);
  var bodyBytes = [];
  while ((bodyAvail = bodyStream.available()) > 0) {
    Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
  }

  var body = decodeURIComponent(
    escape(String.fromCharCode.apply(null, bodyBytes))
  );

  if (query.hop) {
    query.hop = parseInt(query.hop, 10);
    hops = JSON.parse(query.hops);
    var curHop = hops[query.hop - 1];
    query.allowOrigin = curHop.allowOrigin;
    query.allowHeaders = curHop.allowHeaders;
    query.allowMethods = curHop.allowMethods;
    query.allowCred = curHop.allowCred;
    query.noAllowPreflight = curHop.noAllowPreflight;
    if (curHop.setCookie) {
      query.setCookie = unescape(curHop.setCookie);
    }
    if (curHop.cookie) {
      query.cookie = unescape(curHop.cookie);
    }
    query.noCookie = curHop.noCookie;
  }

  // Check that request was correct

  if (!isPreflight && query.body && body != query.body) {
    sendHttp500(
      response,
      "Wrong body. Expected " + query.body + " got " + body
    );
    return;
  }

  if (!isPreflight && "headers" in query) {
    headers = JSON.parse(query.headers);
    for (headerName in headers) {
      // Content-Type is changed if there was a body
      if (
        !(headerName == "Content-Type" && body) &&
        (!request.hasHeader(headerName) ||
          request.getHeader(headerName) != headers[headerName])
      ) {
        var actual = request.hasHeader(headerName)
          ? request.getHeader(headerName)
          : "<missing header>";
        sendHttp500(
          response,
          "Header " +
            headerName +
            " had wrong value. Expected " +
            headers[headerName] +
            " got " +
            actual
        );
        return;
      }
    }
  }

  if (
    isPreflight &&
    "requestHeaders" in query &&
    request.getHeader("Access-Control-Request-Headers") != query.requestHeaders
  ) {
    sendHttp500(
      response,
      "Access-Control-Request-Headers had wrong value. Expected " +
        query.requestHeaders +
        " got " +
        request.getHeader("Access-Control-Request-Headers")
    );
    return;
  }

  if (
    isPreflight &&
    "requestMethod" in query &&
    request.getHeader("Access-Control-Request-Method") != query.requestMethod
  ) {
    sendHttp500(
      response,
      "Access-Control-Request-Method had wrong value. Expected " +
        query.requestMethod +
        " got " +
        request.getHeader("Access-Control-Request-Method")
    );
    return;
  }

  if ("origin" in query && request.getHeader("Origin") != query.origin) {
    sendHttp500(
      response,
      "Origin had wrong value. Expected " +
        query.origin +
        " got " +
        request.getHeader("Origin")
    );
    return;
  }

  if ("cookie" in query) {
    cookies = {};
    request
      .getHeader("Cookie")
      .split(/ *; */)
      .forEach(function (val) {
        var [name, value] = val.split("=");
        cookies[name] = unescape(value);
      });

    query.cookie.split(",").forEach(function (val) {
      var [name, value] = val.split("=");
      if (cookies[name] != value) {
        sendHttp500(
          response,
          "Cookie " +
            name +
            " had wrong value. Expected " +
            value +
            " got " +
            cookies[name]
        );
      }
    });
  }

  if (query.noCookie && request.hasHeader("Cookie")) {
    sendHttp500(
      response,
      "Got cookies when didn't expect to: " + request.getHeader("Cookie")
    );
    return;
  }

  // Send response

  if (!isPreflight && query.status) {
    response.setStatusLine(null, query.status, query.statusMessage);
  }
  if (isPreflight && query.preflightStatus) {
    response.setStatusLine(null, query.preflightStatus, "preflight status");
  }

  if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) {
    response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
  }

  if (query.allowCred) {
    response.setHeader("Access-Control-Allow-Credentials", "true");
  }

  if (query.setCookie) {
    response.setHeader("Set-Cookie", query.setCookie + "; path=/");
  }

  if (isPreflight) {
    if (query.allowHeaders) {
      response.setHeader("Access-Control-Allow-Headers", query.allowHeaders);
    }

    if (query.allowMethods) {
      response.setHeader("Access-Control-Allow-Methods", query.allowMethods);
    }
  } else {
    if (query.responseHeaders) {
      let responseHeaders = JSON.parse(query.responseHeaders);
      for (let responseHeader in responseHeaders) {
        response.setHeader(responseHeader, responseHeaders[responseHeader]);
      }
    }

    if (query.exposeHeaders) {
      response.setHeader("Access-Control-Expose-Headers", query.exposeHeaders);
    }
  }

  if (!isPreflight && query.hop && query.hop < hops.length) {
    newURL =
      hops[query.hop].server +
      "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" +
      "hop=" +
      (query.hop + 1) +
      "&hops=" +
      escape(query.hops);
    if ("headers" in query) {
      newURL += "&headers=" + escape(query.headers);
    }
    response.setStatusLine(null, 307, "redirect");
    response.setHeader("Location", newURL);

    return;
  }

  // Send response body
  if (!isPreflight && request.method != "HEAD") {
    response.setHeader("Content-Type", "application/xml", false);
    response.write("<res>hello pass</res>\n");
  }
  if (isPreflight && "preflightBody" in query) {
    response.setHeader("Content-Type", "text/plain", false);
    response.write(query.preflightBody);
  }
}

function sendHttp500(response, text) {
  response.setStatusLine(null, 500, text);
}
