declare module "http" {
    import * as stream from "stream";
    import { URL } from "url";
    import { EventEmitter } from "events";
    import { LookupFunction, Server as NetServer, Socket } from "net";

    // incoming headers will never contain number
    interface IncomingHttpHeaders extends NodeJS.Dict<string | string[]> {
        "accept"?: string | undefined;
        "accept-language"?: string | undefined;
        "accept-patch"?: string | undefined;
        "accept-ranges"?: string | undefined;
        "access-control-allow-credentials"?: string | undefined;
        "access-control-allow-headers"?: string | undefined;
        "access-control-allow-methods"?: string | undefined;
        "access-control-allow-origin"?: string | undefined;
        "access-control-expose-headers"?: string | undefined;
        "access-control-max-age"?: string | undefined;
        "access-control-request-headers"?: string | undefined;
        "access-control-request-method"?: string | undefined;
        "age"?: string | undefined;
        "allow"?: string | undefined;
        "alt-svc"?: string | undefined;
        "authorization"?: string | undefined;
        "cache-control"?: string | undefined;
        "connection"?: string | undefined;
        "content-disposition"?: string | undefined;
        "content-encoding"?: string | undefined;
        "content-language"?: string | undefined;
        "content-length"?: string | undefined;
        "content-location"?: string | undefined;
        "content-range"?: string | undefined;
        "content-type"?: string | undefined;
        "cookie"?: string | undefined;
        "date"?: string | undefined;
        "etag"?: string | undefined;
        "expect"?: string | undefined;
        "expires"?: string | undefined;
        "forwarded"?: string | undefined;
        "from"?: string | undefined;
        "host"?: string | undefined;
        "if-match"?: string | undefined;
        "if-modified-since"?: string | undefined;
        "if-none-match"?: string | undefined;
        "if-unmodified-since"?: string | undefined;
        "last-modified"?: string | undefined;
        "location"?: string | undefined;
        "origin"?: string | undefined;
        "pragma"?: string | undefined;
        "proxy-authenticate"?: string | undefined;
        "proxy-authorization"?: string | undefined;
        "public-key-pins"?: string | undefined;
        "range"?: string | undefined;
        "referer"?: string | undefined;
        "retry-after"?: string | undefined;
        "sec-websocket-accept"?: string | undefined;
        "sec-websocket-extensions"?: string | undefined;
        "sec-websocket-key"?: string | undefined;
        "sec-websocket-protocol"?: string | undefined;
        "sec-websocket-version"?: string | undefined;
        "set-cookie"?: string[] | undefined;
        "strict-transport-security"?: string | undefined;
        "tk"?: string | undefined;
        "trailer"?: string | undefined;
        "transfer-encoding"?: string | undefined;
        "upgrade"?: string | undefined;
        "user-agent"?: string | undefined;
        "vary"?: string | undefined;
        "via"?: string | undefined;
        "warning"?: string | undefined;
        "www-authenticate"?: string | undefined;
    }

    // outgoing headers allows numbers (as they are converted internally to strings)
    type OutgoingHttpHeader = number | string | string[];

    interface OutgoingHttpHeaders extends NodeJS.Dict<OutgoingHttpHeader> {}

    interface ClientRequestArgs {
        protocol?: string | null | undefined;
        host?: string | null | undefined;
        hostname?: string | null | undefined;
        family?: number | undefined;
        port?: number | string | null | undefined;
        defaultPort?: number | string | undefined;
        localAddress?: string | undefined;
        socketPath?: string | undefined;
        /**
         * @default 8192
         */
        maxHeaderSize?: number | undefined;
        method?: string | undefined;
        path?: string | null | undefined;
        headers?: OutgoingHttpHeaders | undefined;
        auth?: string | null | undefined;
        agent?: Agent | boolean | undefined;
        _defaultAgent?: Agent | undefined;
        timeout?: number | undefined;
        setHost?: boolean | undefined;
        // https://github.com/nodejs/node/blob/master/lib/_http_client.js#L278
        createConnection?:
            | ((options: ClientRequestArgs, oncreate: (err: Error, socket: Socket) => void) => Socket)
            | undefined;
        lookup?: LookupFunction | undefined;
    }

    interface ServerOptions<
        Request extends typeof IncomingMessage = typeof IncomingMessage,
        Response extends typeof ServerResponse = typeof ServerResponse,
    > {
        IncomingMessage?: Request | undefined;
        ServerResponse?: Response | undefined;
        /**
         * Optionally overrides the value of
         * [`--max-http-header-size`][] for requests received by this server, i.e.
         * the maximum length of request headers in bytes.
         * @default 8192
         */
        maxHeaderSize?: number | undefined;
        /**
         * Use an insecure HTTP parser that accepts invalid HTTP headers when true.
         * Using the insecure parser should be avoided.
         * See --insecure-http-parser for more information.
         * @default false
         */
        insecureHTTPParser?: boolean | undefined;
    }

    type RequestListener<
        Request extends typeof IncomingMessage = typeof IncomingMessage,
        Response extends typeof ServerResponse = typeof ServerResponse,
    > = (req: InstanceType<Request>, res: InstanceType<Response>) => void;

    class Server<
        Request extends typeof IncomingMessage = typeof IncomingMessage,
        Response extends typeof ServerResponse = typeof ServerResponse,
    > extends NetServer {
        constructor(requestListener?: RequestListener<Request, Response>);
        constructor(options: ServerOptions<Request, Response>, requestListener?: RequestListener<Request, Response>);
        setTimeout(msecs?: number, callback?: () => void): this;
        setTimeout(callback: () => void): this;
        /**
         * Limits maximum incoming headers count. If set to 0, no limit will be applied.
         * @default 2000
         * {@link https://nodejs.org/api/http.html#http_server_maxheaderscount}
         */
        maxHeadersCount: number | null;
        timeout: number;
        /**
         * Limit the amount of time the parser will wait to receive the complete HTTP headers.
         * @default 60000
         * {@link https://nodejs.org/api/http.html#http_server_headerstimeout}
         */
        headersTimeout: number;
        keepAliveTimeout: number;
        /**
         * Sets the timeout value in milliseconds for receiving the entire request from the client.
         * @default 0
         * {@link https://nodejs.org/api/http.html#http_server_requesttimeout}
         */
        requestTimeout: number;
        addListener(event: string, listener: (...args: any[]) => void): this;
        addListener(event: "close", listener: () => void): this;
        addListener(event: "connection", listener: (socket: Socket) => void): this;
        addListener(event: "error", listener: (err: Error) => void): this;
        addListener(event: "listening", listener: () => void): this;
        addListener(event: "checkContinue", listener: RequestListener<Request, Response>): this;
        addListener(event: "checkExpectation", listener: RequestListener<Request, Response>): this;
        addListener(event: "clientError", listener: (err: Error, socket: stream.Duplex) => void): this;
        addListener(
            event: "connect",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        addListener(event: "request", listener: RequestListener<Request, Response>): this;
        addListener(
            event: "upgrade",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        emit(event: string, ...args: any[]): boolean;
        emit(event: "close"): boolean;
        emit(event: "connection", socket: Socket): boolean;
        emit(event: "error", err: Error): boolean;
        emit(event: "listening"): boolean;
        emit(event: "checkContinue", req: InstanceType<Request>, res: InstanceType<Response>): boolean;
        emit(event: "checkExpectation", req: InstanceType<Request>, res: InstanceType<Response>): boolean;
        emit(event: "clientError", err: Error, socket: stream.Duplex): boolean;
        emit(event: "connect", req: InstanceType<Request>, socket: stream.Duplex, head: Buffer): boolean;
        emit(event: "request", req: InstanceType<Request>, res: InstanceType<Response>): boolean;
        emit(event: "upgrade", req: InstanceType<Request>, socket: stream.Duplex, head: Buffer): boolean;
        on(event: string, listener: (...args: any[]) => void): this;
        on(event: "close", listener: () => void): this;
        on(event: "connection", listener: (socket: Socket) => void): this;
        on(event: "error", listener: (err: Error) => void): this;
        on(event: "listening", listener: () => void): this;
        on(event: "checkContinue", listener: RequestListener<Request, Response>): this;
        on(event: "checkExpectation", listener: RequestListener<Request, Response>): this;
        on(event: "clientError", listener: (err: Error, socket: stream.Duplex) => void): this;
        on(event: "connect", listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void): this;
        on(event: "request", listener: RequestListener<Request, Response>): this;
        on(event: "upgrade", listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void): this;
        once(event: string, listener: (...args: any[]) => void): this;
        once(event: "close", listener: () => void): this;
        once(event: "connection", listener: (socket: Socket) => void): this;
        once(event: "error", listener: (err: Error) => void): this;
        once(event: "listening", listener: () => void): this;
        once(event: "checkContinue", listener: RequestListener<Request, Response>): this;
        once(event: "checkExpectation", listener: RequestListener<Request, Response>): this;
        once(event: "clientError", listener: (err: Error, socket: stream.Duplex) => void): this;
        once(
            event: "connect",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        once(event: "request", listener: RequestListener<Request, Response>): this;
        once(
            event: "upgrade",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        prependListener(event: string, listener: (...args: any[]) => void): this;
        prependListener(event: "close", listener: () => void): this;
        prependListener(event: "connection", listener: (socket: Socket) => void): this;
        prependListener(event: "error", listener: (err: Error) => void): this;
        prependListener(event: "listening", listener: () => void): this;
        prependListener(event: "checkContinue", listener: RequestListener<Request, Response>): this;
        prependListener(event: "checkExpectation", listener: RequestListener<Request, Response>): this;
        prependListener(event: "clientError", listener: (err: Error, socket: stream.Duplex) => void): this;
        prependListener(
            event: "connect",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        prependListener(event: "request", listener: RequestListener<Request, Response>): this;
        prependListener(
            event: "upgrade",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        prependOnceListener(event: string, listener: (...args: any[]) => void): this;
        prependOnceListener(event: "close", listener: () => void): this;
        prependOnceListener(event: "connection", listener: (socket: Socket) => void): this;
        prependOnceListener(event: "error", listener: (err: Error) => void): this;
        prependOnceListener(event: "listening", listener: () => void): this;
        prependOnceListener(event: "checkContinue", listener: RequestListener<Request, Response>): this;
        prependOnceListener(event: "checkExpectation", listener: RequestListener<Request, Response>): this;
        prependOnceListener(event: "clientError", listener: (err: Error, socket: stream.Duplex) => void): this;
        prependOnceListener(
            event: "connect",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
        prependOnceListener(event: "request", listener: RequestListener<Request, Response>): this;
        prependOnceListener(
            event: "upgrade",
            listener: (req: InstanceType<Request>, socket: stream.Duplex, head: Buffer) => void,
        ): this;
    }

    // https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js
    class OutgoingMessage extends stream.Writable {
        upgrading: boolean;
        chunkedEncoding: boolean;
        shouldKeepAlive: boolean;
        useChunkedEncodingByDefault: boolean;
        sendDate: boolean;
        /**
         * @deprecated Use `writableEnded` instead.
         */
        finished: boolean;
        headersSent: boolean;
        /**
         * @deprecated Use `socket` instead.
         */
        connection: Socket | null;
        socket: Socket | null;

        constructor();

        setTimeout(msecs: number, callback?: () => void): this;
        setHeader(name: string, value: number | string | ReadonlyArray<string>): this;
        getHeader(name: string): number | string | string[] | undefined;
        getHeaders(): OutgoingHttpHeaders;
        getHeaderNames(): string[];
        hasHeader(name: string): boolean;
        removeHeader(name: string): void;
        addTrailers(headers: OutgoingHttpHeaders | ReadonlyArray<[string, string]>): void;
        flushHeaders(): void;
    }

    // https://github.com/nodejs/node/blob/master/lib/_http_server.js#L108-L256
    class ServerResponse extends OutgoingMessage {
        statusCode: number;
        statusMessage: string;

        constructor(req: IncomingMessage);

        assignSocket(socket: Socket): void;
        detachSocket(socket: Socket): void;
        // https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53
        // no args in writeContinue callback
        writeContinue(callback?: () => void): void;
        writeHead(
            statusCode: number,
            statusMessage?: string,
            headers?: OutgoingHttpHeaders | OutgoingHttpHeader[],
        ): this;
        writeHead(statusCode: number, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[]): this;
        writeProcessing(): void;
    }

    interface InformationEvent {
        statusCode: number;
        statusMessage: string;
        httpVersion: string;
        httpVersionMajor: number;
        httpVersionMinor: number;
        headers: IncomingHttpHeaders;
        rawHeaders: string[];
    }

    // https://github.com/nodejs/node/blob/master/lib/_http_client.js#L77
    class ClientRequest extends OutgoingMessage {
        aborted: boolean;
        host: string;
        protocol: string;
        reusedSocket: boolean;
        maxHeadersCount: number;

        constructor(url: string | URL | ClientRequestArgs, cb?: (res: IncomingMessage) => void);

        method: string;
        path: string;
        /** @deprecated since v14.1.0 Use `request.destroy()` instead. */
        abort(): void;
        onSocket(socket: Socket): void;
        setTimeout(timeout: number, callback?: () => void): this;
        setNoDelay(noDelay?: boolean): void;
        setSocketKeepAlive(enable?: boolean, initialDelay?: number): void;

        addListener(event: "abort", listener: () => void): this;
        addListener(
            event: "connect",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        addListener(event: "continue", listener: () => void): this;
        addListener(event: "information", listener: (info: InformationEvent) => void): this;
        addListener(event: "response", listener: (response: IncomingMessage) => void): this;
        addListener(event: "socket", listener: (socket: Socket) => void): this;
        addListener(event: "timeout", listener: () => void): this;
        addListener(
            event: "upgrade",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        addListener(event: "close", listener: () => void): this;
        addListener(event: "drain", listener: () => void): this;
        addListener(event: "error", listener: (err: Error) => void): this;
        addListener(event: "finish", listener: () => void): this;
        addListener(event: "pipe", listener: (src: stream.Readable) => void): this;
        addListener(event: "unpipe", listener: (src: stream.Readable) => void): this;
        addListener(event: string | symbol, listener: (...args: any[]) => void): this;

        on(event: "abort", listener: () => void): this;
        on(event: "connect", listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void): this;
        on(event: "continue", listener: () => void): this;
        on(event: "information", listener: (info: InformationEvent) => void): this;
        on(event: "response", listener: (response: IncomingMessage) => void): this;
        on(event: "socket", listener: (socket: Socket) => void): this;
        on(event: "timeout", listener: () => void): this;
        on(event: "upgrade", listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void): this;
        on(event: "close", listener: () => void): this;
        on(event: "drain", listener: () => void): this;
        on(event: "error", listener: (err: Error) => void): this;
        on(event: "finish", listener: () => void): this;
        on(event: "pipe", listener: (src: stream.Readable) => void): this;
        on(event: "unpipe", listener: (src: stream.Readable) => void): this;
        on(event: string | symbol, listener: (...args: any[]) => void): this;

        once(event: "abort", listener: () => void): this;
        once(event: "connect", listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void): this;
        once(event: "continue", listener: () => void): this;
        once(event: "information", listener: (info: InformationEvent) => void): this;
        once(event: "response", listener: (response: IncomingMessage) => void): this;
        once(event: "socket", listener: (socket: Socket) => void): this;
        once(event: "timeout", listener: () => void): this;
        once(event: "upgrade", listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void): this;
        once(event: "close", listener: () => void): this;
        once(event: "drain", listener: () => void): this;
        once(event: "error", listener: (err: Error) => void): this;
        once(event: "finish", listener: () => void): this;
        once(event: "pipe", listener: (src: stream.Readable) => void): this;
        once(event: "unpipe", listener: (src: stream.Readable) => void): this;
        once(event: string | symbol, listener: (...args: any[]) => void): this;

        prependListener(event: "abort", listener: () => void): this;
        prependListener(
            event: "connect",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        prependListener(event: "continue", listener: () => void): this;
        prependListener(event: "information", listener: (info: InformationEvent) => void): this;
        prependListener(event: "response", listener: (response: IncomingMessage) => void): this;
        prependListener(event: "socket", listener: (socket: Socket) => void): this;
        prependListener(event: "timeout", listener: () => void): this;
        prependListener(
            event: "upgrade",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        prependListener(event: "close", listener: () => void): this;
        prependListener(event: "drain", listener: () => void): this;
        prependListener(event: "error", listener: (err: Error) => void): this;
        prependListener(event: "finish", listener: () => void): this;
        prependListener(event: "pipe", listener: (src: stream.Readable) => void): this;
        prependListener(event: "unpipe", listener: (src: stream.Readable) => void): this;
        prependListener(event: string | symbol, listener: (...args: any[]) => void): this;

        prependOnceListener(event: "abort", listener: () => void): this;
        prependOnceListener(
            event: "connect",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        prependOnceListener(event: "continue", listener: () => void): this;
        prependOnceListener(event: "information", listener: (info: InformationEvent) => void): this;
        prependOnceListener(event: "response", listener: (response: IncomingMessage) => void): this;
        prependOnceListener(event: "socket", listener: (socket: Socket) => void): this;
        prependOnceListener(event: "timeout", listener: () => void): this;
        prependOnceListener(
            event: "upgrade",
            listener: (response: IncomingMessage, socket: Socket, head: Buffer) => void,
        ): this;
        prependOnceListener(event: "close", listener: () => void): this;
        prependOnceListener(event: "drain", listener: () => void): this;
        prependOnceListener(event: "error", listener: (err: Error) => void): this;
        prependOnceListener(event: "finish", listener: () => void): this;
        prependOnceListener(event: "pipe", listener: (src: stream.Readable) => void): this;
        prependOnceListener(event: "unpipe", listener: (src: stream.Readable) => void): this;
        prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this;
    }

    class IncomingMessage extends stream.Readable {
        constructor(socket: Socket);

        aborted: boolean;
        httpVersion: string;
        httpVersionMajor: number;
        httpVersionMinor: number;
        complete: boolean;
        /**
         * @deprecated since v13.0.0 - Use `socket` instead.
         */
        connection: Socket;
        socket: Socket;
        headers: IncomingHttpHeaders;
        rawHeaders: string[];
        trailers: NodeJS.Dict<string>;
        rawTrailers: string[];
        setTimeout(msecs: number, callback?: () => void): this;
        /**
         * Only valid for request obtained from http.Server.
         */
        method?: string | undefined;
        /**
         * Only valid for request obtained from http.Server.
         */
        url?: string | undefined;
        /**
         * Only valid for response obtained from http.ClientRequest.
         */
        statusCode?: number | undefined;
        /**
         * Only valid for response obtained from http.ClientRequest.
         */
        statusMessage?: string | undefined;
        destroy(error?: Error): this;
    }

    interface AgentOptions {
        /**
         * Keep sockets around in a pool to be used by other requests in the future. Default = false
         */
        keepAlive?: boolean | undefined;
        /**
         * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000.
         * Only relevant if keepAlive is set to true.
         */
        keepAliveMsecs?: number | undefined;
        /**
         * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity
         */
        maxSockets?: number | undefined;
        /**
         * Maximum number of sockets allowed for all hosts in total. Each request will use a new socket until the maximum is reached. Default: Infinity.
         */
        maxTotalSockets?: number | undefined;
        /**
         * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256.
         */
        maxFreeSockets?: number | undefined;
        /**
         * Socket timeout in milliseconds. This will set the timeout after the socket is connected.
         */
        timeout?: number | undefined;
        /**
         * Scheduling strategy to apply when picking the next free socket to use. Default: 'fifo'.
         */
        scheduling?: "fifo" | "lifo" | undefined;
    }

    class Agent extends EventEmitter {
        maxFreeSockets: number;
        maxSockets: number;
        maxTotalSockets: number;
        readonly freeSockets: NodeJS.ReadOnlyDict<Socket[]>;
        readonly sockets: NodeJS.ReadOnlyDict<Socket[]>;
        readonly requests: NodeJS.ReadOnlyDict<IncomingMessage[]>;

        constructor(opts?: AgentOptions);

        /**
         * Destroy any sockets that are currently in use by the agent.
         * It is usually not necessary to do this. However, if you are using an agent with KeepAlive enabled,
         * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise,
         * sockets may hang open for quite a long time before the server terminates them.
         */
        destroy(): void;
    }

    const METHODS: string[];

    const STATUS_CODES: {
        [errorCode: number]: string | undefined;
        [errorCode: string]: string | undefined;
    };

    function createServer<
        Request extends typeof IncomingMessage = typeof IncomingMessage,
        Response extends typeof ServerResponse = typeof ServerResponse,
    >(requestListener?: RequestListener<Request, Response>): Server<Request, Response>;
    function createServer<
        Request extends typeof IncomingMessage = typeof IncomingMessage,
        Response extends typeof ServerResponse = typeof ServerResponse,
    >(
        options: ServerOptions<Request, Response>,
        requestListener?: RequestListener<Request, Response>,
    ): Server<Request, Response>;

    // although RequestOptions are passed as ClientRequestArgs to ClientRequest directly,
    // create interface RequestOptions would make the naming more clear to developers
    interface RequestOptions extends ClientRequestArgs {}
    function request(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest;
    function request(
        url: string | URL,
        options: RequestOptions,
        callback?: (res: IncomingMessage) => void,
    ): ClientRequest;
    function get(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest;
    function get(url: string | URL, options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;

    /**
     * Performs the low-level validations on the provided name that are done when `res.setHeader(name, value)` is called.
     * Passing illegal value as name will result in a TypeError being thrown, identified by `code: 'ERR_INVALID_HTTP_TOKEN'`.
     * @param name Header name
     * @since v14.3.0
     */
    function validateHeaderName(name: string): void;
    /**
     * Performs the low-level validations on the provided value that are done when `res.setHeader(name, value)` is called.
     * Passing illegal value as value will result in a TypeError being thrown.
     * - Undefined value error is identified by `code: 'ERR_HTTP_INVALID_HEADER_VALUE'`.
     * - Invalid value character error is identified by `code: 'ERR_INVALID_CHAR'`.
     * @param name Header name
     * @param value Header value
     * @since v14.3.0
     */
    function validateHeaderValue(name: string, value: string): void;

    let globalAgent: Agent;

    /**
     * Read-only property specifying the maximum allowed size of HTTP headers in bytes.
     * Defaults to 16KB. Configurable using the [`--max-http-header-size`][] CLI option.
     */
    const maxHeaderSize: number;
}
declare module "node:http" {
    export * from "http";
}
