Ctrl K
ComunidadGuías…

server/body-deadline.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { createServer, type Server } from "node:http";
import { connect, type AddressInfo } from "node:net";
import { afterEach, describe, expect, it } from "vitest";

import { holdBodyDeadline } from "./body-deadline.ts";

let server: Server | null = null;

afterEach(() => new Promise<void>((resolve) => (server ? server.close(() => resolve()) : resolve())));

async function listen(baseMs: number): Promise<number> {
  server = createServer((req, res) => {
    holdBodyDeadline(req, res, baseMs);
    let bytes = 0;
    req.on("data", (chunk: Buffer) => { bytes += chunk.length; });
    req.on("end", () => res.end(String(bytes)));
  });
  await new Promise<void>((resolve) => server!.listen(0, "127.0.0.1", resolve));
  return (server.address() as AddressInfo).port;
}

function send(port: number, head: string, chunks: Array<{ after: number; data: string }>): Promise<string> {
  return new Promise((resolve, reject) => {
    const socket = connect(port, "127.0.0.1", () => {
      socket.write(head);
      for (const { after, data } of chunks) setTimeout(() => socket.writable && socket.write(data), after);
    });
    let text = "";
    socket.on("data", (chunk) => {
      text += chunk.toString("utf8");
      if (/\r\n\r\n\d+$/.test(text)) socket.end();
    });
    socket.on("close", () => resolve(text));
    socket.on("error", reject);
  });
}

describe("holdBodyDeadline", () => {
  it("answers 408 and closes a request whose body stops arriving", async () => {
    const port = await listen(200);
    const answer = await send(port, "POST / HTTP/1.1\r\nHost: x\r\nContent-Length: 10\r\n\r\nab", []);
    expect(answer).toMatch(/^HTTP\/1\.1 408 Request Timeout/);
    const [, body] = answer.split("\r\n\r\n");
    expect(JSON.parse(body!)).toEqual({ error: "The request took too long. Check the connection and try again.", errorKey: "serverError.request.bodyTimeout" });
    expect(answer).toContain(`Content-Length: ${Buffer.byteLength(body!)}`);
  });

  it("gives a large upload time in proportion to its size", async () => {
    const port = await listen(200);
    const piece = "x".repeat(64 * 1024);
    const answer = await send(port, `POST / HTTP/1.1\r\nHost: x\r\nContent-Length: ${piece.length * 4}\r\n\r\n`,
      [0, 300, 600, 900].map((after) => ({ after, data: piece })));
    expect(answer).toMatch(/^HTTP\/1\.1 200/);
    expect(answer).toMatch(/262144$/);
  });

  it("never times a request without a body", async () => {
    const port = await listen(50);
    server!.removeAllListeners("request");
    server!.on("request", (req, res) => {
      holdBodyDeadline(req, res, 50);
      setTimeout(() => res.end("late"), 300);
    });
    const answer = await send(port, "GET / HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n", []);
    expect(answer).toMatch(/^HTTP\/1\.1 200/);
  });
});