/// import * as http from "http"; import * as https from "https"; import { AsyncQueue } from "./async"; /** * An arbitrary set of header names and values. */ export interface TestHttpHeaders { [key: string]: string; } /** * Properties of an HTTP request recorded by [[TestHttpServer]]. */ export interface TestHttpRequest { /** * The HTTP method, lowercased ("get"). */ method: string; /** * The URL path ("/index.html"). */ path: string; /** * The request headers. */ headers: TestHttpHeaders; /** * The request body, if any. Requests with a streamed body will be read in full before being handled. */ body?: string; } /** * A function that provides a response for [[TestHttpServer]]. May be synchronous or async. */ export declare type TestHttpHandler = ((req: TestHttpRequest, res: http.ServerResponse) => void) | ((req: TestHttpRequest, res: http.ServerResponse) => Promise); /** * A wrapper for Node's HTTP server API that provides convenient semantics for test code. * * Do not use this for actual server applications, since its request matching logic is inefficient * and its request queue can grow without limit. * * ``` * const server = await TestHttpServer.start(); * * // simulate an endpoint * server.forMethodAndPath('get', '/thing', * TestHttpHandlers.respond(200, {}, 'hello')); * * // return 500 error for non-matching requests * server.byDefault(TestHttpHandlers.respond(500)); * * // ... do some HTTP requests to server.url * * const request = await server.nextRequest(); // retrieve the first request * server.close(); * ``` */ export declare class TestHttpServer { /** * Creates and starts a [[TestHttpServer]] instance. * * Note: in a non-TypeScript project that uses a transpiler, you may not be able to access this * static method; if so, use the same method in [[TestHttpServers]] instead. * * @param options * Any desired [[http.ServerOptions]]. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static start(options?: http.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that uses HTTPS, with a self-signed certificate. * * Note: in a non-TypeScript project that uses a transpiler, you may not be able to access this * static method; if so, use the same method in [[TestHttpServers]] instead. * * @param options * Any desired [[https.ServerOptions]] other than the certificate. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startSecure(options?: https.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that acts as an HTTP proxy. * * The server will only act as a proxy and will ignore any request handlers that you specify, but * it still has the same properties as a regular [[TestHttpServer]], behaves the same in terms of * dynamically choosing a port, and allows you to inspect received requests. The received * requests will have a `path` property equal to either the full request URL or, if using a * tunneling agent, the request URL minus the path. * * Note that the current implementation does not support proxying a request to an HTTPS URL. * * @param options * Any desired [[http.ServerOptions]]. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startProxy(options?: http.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that acts as a secure HTTP proxy with a * self-signed certificate. * * This is the same as [[TestHttpServer.startProxy]], but the proxy server itself is secure. * Note that the current implementation does not support proxying a request to an HTTPS URL * (that is, when the target server is itself secure). * * @param options * Any desired [[https.ServerOptions]] other than the certificate. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startSecureProxy(options?: https.ServerOptions, port?: number): Promise; private static nextPort; private static startSecureInternal; /** * The server's base URL ("http://localhost:8000"). */ url: string; /** * The server's hostname (always "localhost" in this implementation). */ hostname: string; /** * The server's port. */ port: number; /** * The server's self-signed certificate, if it is a secure server. */ certificate?: string; /** * An [[AsyncQueue]] of all requests handled so far. Call `await server.requests.take()` to block * untiil the server has handled a request. */ requests: AsyncQueue; private realServer; private responses; private matchers; private defaultHandler; private secure; private count; private constructor(); /** * Consumes the next received request, waiting until one is available. * * @returns * A Promise that will be resolved with a [[TestHttpRequest]]. */ nextRequest(): Promise; /** * Returns the total number of requests that have been received. * * @returns * The number of requests so far. */ requestCount(): number; /** * Specifies a [[TestHttpHandler]] to use for all requests that are not otherwise matched. * * @param handler * The request handler. This is normally created with a function like [[respond]]. * @returns * The same server. */ byDefault(handler: TestHttpHandler): TestHttpServer; /** * Specifies a [[TestHttpHandler]] to use for requests with the specified method and path. * This overrides any previous handler for the same method and path. * * @param method * The HTTP method. * @param path * The request path. * @handler * The request handler. This is normally created with a function like [[respond]]. * @returns * The same server. */ forMethodAndPath(method: string, path: string, handler: TestHttpHandler): TestHttpServer; /** * Stops the server. */ close(): void; /** * Stops the server and provides a Promise to indicate when it is completely stopped. */ closeAndWait(): Promise; private startInstance; private preprocessRequest; } /** * Abstract class that provides the same static factory methods as [[TestHttpServer]]. * * This is provided only because some JavaScript projects may have difficulty importing a class that * has both a constructor and static methods (transpilers may copy the import in a way that preserves * only the constructor function and not the static members). `TestHttpServers.start()` is exactly * equivalent to `TestHttpServer.start()`. */ export declare abstract class TestHttpServers { /** * Creates and starts a [[TestHttpServer]] instance. * * @param options * Any desired [[http.ServerOptions]]. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static start(options?: http.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that uses HTTPS, with a self-signed certificate. * * @param options * Any desired [[https.ServerOptions]] other than the certificate. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startSecure(options?: https.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that acts as an HTTP proxy. * * The server will only act as a proxy and will ignore any request handlers that you specify, but * it still has the same properties as a regular [[TestHttpServer]], behaves the same in terms of * dynamically choosing a port, and allows you to inspect received requests. The received * requests will have a `path` property equal to either the full request URL or, if using a * tunneling agent, the request URL minus the path. * * Note that the current implementation does not support proxying a request to an HTTPS URL. * * @param options * Any desired [[http.ServerOptions]]. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startProxy(options?: http.ServerOptions, port?: number): Promise; /** * Creates and starts a [[TestHttpServer]] instance that acts as a secure HTTP proxy with a * self-signed certificate. * * This is the same as [[TestHttpServers.startProxy]], but the proxy server itself is secure. * Note that the current implementation does not support proxying a request to an HTTPS URL * (that is, when the target server is itself secure). * * @param options * Any desired [[https.ServerOptions]] other than the certificate. * @param port * A specific port to listen on; if omitted, it picks an available port. */ static startSecureProxy(options?: https.ServerOptions, port?: number): Promise; } /** * Predefined implementations of [[TestHttpHandler]] for use with [[TestHttpServer]]. */ export declare abstract class TestHttpHandlers { /** * Creates a [[TestHttpHandler]] that sends a simple response. * * ``` * server.forMethodAndPath("get", "/path", TestHttpHandlers.respond(500)); * server.forMethodAndPath("get", "/path", * TestHttpHandlers.respond(200, { "content-type": "text/plain" }, "hi")); * ``` * * @param status * The desired HTTP status. * @param headers * Response headers, if any. * @param body * Response body, if any. * @returns * A response handler. */ static respond(status: number, headers?: TestHttpHeaders, body?: string): TestHttpHandler; /** * Shortcut for creating a [[TestHttpHandler]] that sends a 200 response with JSON content. * * ``` * server.forMethodAndPath("get", "/path", * TestHttpHandlers.respondJson({ message: "hi" })); * ``` * * @param serializableData * A value of any type that will be converted to JSON. * @returns * A response handler. */ static respondJson(serializableData: any): TestHttpHandler; /** * Creates a [[TestHttpHandler]] that sends a chunked HTTP response, using an [[AsyncQueue]] as a pipe. * * ``` * const chunkQueue = new AsyncQueue(); * server.forMethodAndPath("get", "/path", * TestHttpHandlers.chunkedStream(200, {}, chunkQueue)); * chunkQueue.add("a chunk of data"); * chunkQueue.add("another one"); * chunkQueue.close(); * ``` * * @param status * The desired HTTP status. * @param headers * Response headers, if any. * @param chunkQueue * An existing [[AsyncQueue]]. As you add chunks of response data to the queue, they will be consumed and * sent. Call `close()` on the queue to end the response. * @returns * A response handler. */ static chunkedStream(status: number, headers: TestHttpHeaders, chunkQueue: AsyncQueue): TestHttpHandler; /** * Creates a [[TestHttpHandler]] that sends a streaming response in Server-Sent Events format, using * an [[AsyncQueue]] as an event pipe. * * ``` * const eventQueue = new AsyncQueue(); * server.forMethodAndPath("get", "/path", * TestHttpHandlers.sseStream(eventQueue)); * eventQueue.add({ type: "patch", data: { path: "/flags", key: "x" } }); * eventQueue.add({ comment: "" }); * eventQueue.close(); * ``` * * @param eventQueue * An existing [[AsyncQueue]]. As you add [[SSEItem]] objects to the queue, they will be consumed and * sent. Call `close()` on the queue to end the response. */ static sseStream(eventQueue: AsyncQueue): TestHttpHandler; /** * Creates a [[TestHttpHandler]] that will cause the request to terminate with a network error, by * closing the response socket prematurely. * * @returns * A response handler. */ static networkError(): TestHttpHandler; } /** * A data item or comment in a Server-Sent Events stream, for [[TestHttpHandlers.sseStream]]. */ export interface SSEItem { /** * The event type ("event:" field). */ type?: string; /** * The event ID ("id:" field). */ id?: string; /** * The event data ("data:" field). This must be defined unless `comment` is provided. */ data?: string; /** * A comment string to be sent instead of an event (":" will be prepended). */ comment?: string; }