UNPKG

mockttp

Version:

Mock HTTP server for testing HTTP clients and stubbing webservices

575 lines 21.7 kB
import type * as net from 'net'; import { Readable } from 'stream'; import { Operation as JsonPatchOperation } from 'fast-json-patch'; import { MaybePromise } from '@httptoolkit/util'; import { Headers, Trailers, CompletedRequest, CompletedBody, Explainable, RawHeaders } from "../../types"; import { Replace } from '../../util/type-utils'; import { MatchReplacePairs, SerializedMatchReplacePairs } from '../match-replace'; import { Serializable, ClientServerChannel, SerializedProxyConfig } from "../../serialization/serialization"; import { SerializedBody } from '../../serialization/body-serialization'; import { ProxyConfig } from '../proxy-config'; import { CADefinition, ForwardingOptions, PassThroughStepConnectionOptions, PassThroughLookupOptions, PassThroughInitialTransforms } from '../passthrough-handling-definitions'; /** * The definition of a request rule step, which can be passed to Mockttp to define * a rule. * * Implementation of the step is not included in the definition classes, but * instead exists in an *Impl class defined separately and used internally. */ export interface RequestStepDefinition extends Explainable, Serializable { type: keyof typeof StepDefinitionLookup; } export type SerializedBuffer = { type: 'Buffer'; data: number[]; }; /** * Can be returned from callbacks to override parts of a request. * * All fields are optional, and omitted values will default to the original * request value. */ export interface CallbackRequestResult { /** * A replacement HTTP method, capitalized. */ method?: string; /** * The full URL to send the request to. If set, this will redirect * the request and automatically update the Host header accordingly, * unless you also provide a `headers` value that includes a Host * header, in which case that will take used as-is. */ url?: string; /** * The replacement HTTP headers, as an object of string keys and either * single string or array of string values. */ headers?: Headers; /** * A string or buffer, which replaces the request body if set. This will * be automatically content-encoded to match the Content-Encoding defined * in your request headers. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ body?: string | Buffer | Uint8Array; /** * A buffer, which replaces the request body if set, which is sent exactly * as is, and is not automatically encoded. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ rawBody?: Buffer | Uint8Array; /** * A JSON value, which will be stringified and send as a JSON-encoded * request body. This will be automatically content-encoded to match * the Content-Encoding defined in your request headers. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ json?: any; /** * A response: either a response object defining the fields of a response * or the string 'close' to immediately close the connection. * * See {@link CallbackResponseMessageResult} for the possible fields that can * be set to define the response. * * If set, the request will not be forwarded at all, and this will be used * as the response to immediately return to the client (or for 'close', this * will immediately close the connection to the client). */ response?: CallbackResponseResult; } export type CallbackResponseResult = CallbackResponseMessageResult | 'close' | 'reset'; /** * Can be returned from callbacks to define parts of a response, or * override parts when given an existing repsonse. * * All fields are optional, and omitted values will default to the original * response value or a default value. */ export interface CallbackResponseMessageResult { /** * The response status code as a number. * * Defaults to 200 if not set. */ statusCode?: number; /** * The response status message, as a string. This is ignored for * HTTP/2 responses. * * Defaults to the default status message for the status code if not set. */ statusMessage?: string; /** * The replacement HTTP headers, as an object of string keys and either * single string or array of string values. * * Defaults to a minimum set of standard required headers if not set. */ headers?: Headers; /** * The replacement HTTP trailers, as an object of string keys and either * single string or array of string values. Note that there are not all * header fields are valid as trailers, and there are other requirements * such as chunked encoding that must be met for trailers to be sent * successfully. */ trailers?: Trailers; /** * A string or buffer, which replaces the response body if set. This will * be automatically encoded to match the Content-Encoding defined in your * response headers. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * Defaults to empty. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ body?: string | Buffer | Uint8Array; /** * A buffer, which replaces the response body if set, which is sent exactly * as is, and is not automatically encoded. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ rawBody?: Buffer | Uint8Array; /** * A JSON value, which will be stringified and send as a JSON-encoded * request body. This will be automatically content-encoded to match the * Content-Encoding defined in your response headers. * * If this is set, the Content-Length header will be automatically updated * or added accordingly, if required. * * You should only return one body field: either `body`, `rawBody` or * `json`. */ json?: any; } export declare class FixedResponseStep extends Serializable implements RequestStepDefinition { status: number; statusMessage?: string | undefined; data?: (string | Uint8Array | Buffer | SerializedBuffer) | undefined; headers?: Headers | undefined; trailers?: Trailers | undefined; readonly type = "simple"; static readonly isFinal = true; constructor(status: number, statusMessage?: string | undefined, data?: (string | Uint8Array | Buffer | SerializedBuffer) | undefined, headers?: Headers | undefined, trailers?: Trailers | undefined); explain(): string; } /** * @internal */ export interface SerializedCallbackStepData { type: string; name?: string; } /** * @internal */ export interface CallbackRequestMessage { args: [Replace<CompletedRequest, { body: SerializedBody; }>]; } export declare class CallbackStep extends Serializable implements RequestStepDefinition { callback: (request: CompletedRequest) => MaybePromise<CallbackResponseResult>; readonly type = "callback"; static readonly isFinal = true; constructor(callback: (request: CompletedRequest) => MaybePromise<CallbackResponseResult>); explain(): string; /** * @internal */ serialize(channel: ClientServerChannel): SerializedCallbackStepData; } /** * @internal */ export interface SerializedStreamStepData { type: string; status: number; headers?: Headers; } export declare class StreamStep extends Serializable implements RequestStepDefinition { status: number; stream: Readable & { done?: true; }; headers?: Headers | undefined; readonly type = "stream"; static readonly isFinal = true; constructor(status: number, stream: Readable & { done?: true; }, headers?: Headers | undefined); explain(): string; /** * @internal */ serialize(channel: ClientServerChannel): SerializedStreamStepData; } export declare class FileStep extends Serializable implements RequestStepDefinition { status: number; statusMessage: string | undefined; filePath: string; headers?: Headers | undefined; readonly type = "file"; static readonly isFinal = true; constructor(status: number, statusMessage: string | undefined, filePath: string, headers?: Headers | undefined); explain(): string; } export interface PassThroughResponse { id: string; statusCode: number; statusMessage?: string; headers: Headers; rawHeaders: RawHeaders; body: CompletedBody; } export interface PassThroughStepOptions extends PassThroughStepConnectionOptions { /** * A set of data to automatically transform a request. This includes properties * to support many transformation common use cases. * * For advanced cases, a custom callback using beforeRequest can be used instead. * Using this field however where possible is typically simpler, more declarative, * and can be more performant. The two options are mutually exclusive: you cannot * use both transformRequest and a beforeRequest callback. * * Only one transformation for each target (e.g. method, headers & body) can be * specified. If more than one is specified then an error will be thrown when the * rule is registered. */ transformRequest?: RequestTransform; /** * A set of data to automatically transform a response. This includes properties * to support many transformation common use cases. * * For advanced cases, a custom callback using beforeResponse can be used instead. * Using this field however where possible is typically simpler, more declarative, * and can be more performant. The two options are mutually exclusive: you cannot * use both transformResponse and a beforeResponse callback. * * Only one transformation for each target (status, headers & body) can be * specified. If more than one is specified then an error will be thrown when the * rule is registered. */ transformResponse?: ResponseTransform; /** * A callback that will be passed the full request before it is passed through, * and which returns an object that defines how the the request content should * be transformed before it's passed to the upstream server. * * The callback can return an object to define how the request should be changed. * All fields on the object are optional, and returning undefined is equivalent * to returning an empty object (transforming nothing). * * See {@link CallbackRequestResult} for the possible fields that can be set. */ beforeRequest?: (req: CompletedRequest) => MaybePromise<CallbackRequestResult | void> | void; /** * A callback that will be passed the full response before it is passed through, * and which returns a value that defines how the the response content should * be transformed before it's returned to the client. The callback is also passed * the request that was sent to the server (as a 2nd parameter) for reference. * * The callback can either return an object to define how the response should be * changed, or the strings 'close' or 'reset' to immediately close/reset the * underlying connection. * * All fields on the object are optional, and returning undefined is equivalent * to returning an empty object (transforming nothing). * * See {@link CallbackResponseMessageResult} for the possible fields that can be set. */ beforeResponse?: (res: PassThroughResponse, req: CompletedRequest) => MaybePromise<CallbackResponseResult | void> | void; } export interface RequestTransform extends PassThroughInitialTransforms { /** * Override the request protocol. If replaceHost & matchReplaceHost are not specified * and the URL no explicitly specified port, this will automatically switch to the * appropriate port (e.g. from 80 to 443). */ setProtocol?: 'http' | 'https'; /** * A replacement HTTP method. Case insensitive. */ replaceMethod?: string; /** * A headers object which will be merged with the real request headers to add or * replace values. Headers with undefined values will be removed. */ updateHeaders?: Headers; /** * A headers object which will completely replace the real request headers. */ replaceHeaders?: Headers; /** * A string or buffer that replaces the request body entirely. * * If this is specified, the upstream request will not wait for the original request * body, so this may make responses faster than they would be otherwise given large * request bodies or slow/streaming clients. */ replaceBody?: string | Uint8Array | Buffer; /** * The path to a file, which will be used to replace the request body entirely. The * file will be re-read for each request, so the body will always reflect the latest * file contents. * * If this is specified, the upstream request will not wait for the original request * body, so this may make responses faster than they would be otherwise given large * request bodies or slow/streaming clients. */ replaceBodyFromFile?: string; /** * A JSON object which will be merged with the real request body. Undefined values * will be removed, and other values will be merged directly with the target value * recursively. * * Any requests which are received with an invalid JSON body that match this rule * will fail. */ updateJsonBody?: { [key: string]: any; }; /** * A series of operations to apply to the request body in JSON Patch format (RFC * 6902). * * Any requests which are received with an invalid JSON body that match this rule * will fail. */ patchJsonBody?: Array<JsonPatchOperation>; /** * Perform a series of string match & replace operations on the request body. */ matchReplaceBody?: MatchReplacePairs; } export interface ResponseTransform { /** * A replacement response status code. */ replaceStatus?: number; /** * A headers object which will be merged with the real response headers to add or * replace values. Headers with undefined values will be removed. */ updateHeaders?: Headers; /** * A headers object which will completely replace the real response headers. */ replaceHeaders?: Headers; /** * A string or buffer that replaces the response body entirely. * * If this is specified, the downstream response will not wait for the original response * body, so this may make responses arrive faster than they would be otherwise given large * response bodies or slow/streaming servers. */ replaceBody?: string | Uint8Array | Buffer; /** * The path to a file, which will be used to replace the response body entirely. The * file will be re-read for each response, so the body will always reflect the latest * file contents. * * If this is specified, the downstream response will not wait for the original response * body, so this may make responses arrive faster than they would be otherwise given large * response bodies or slow/streaming servers. */ replaceBodyFromFile?: string; /** * A JSON object which will be merged with the real response body. Undefined values * will be removed, and other values will be merged directly with the target value * recursively. * * Any responses which are received with an invalid JSON body that match this rule * will fail. */ updateJsonBody?: { [key: string]: any; }; /** * A series of operations to apply to the response body in JSON Patch format (RFC * 6902). * * Any responses which are received with an invalid JSON body that match this rule * will fail. */ patchJsonBody?: Array<JsonPatchOperation>; /** * Perform a series of string match & replace operations on the response body. */ matchReplaceBody?: MatchReplacePairs; } /** * @internal */ export interface SerializedPassThroughData { type: 'passthrough'; forwarding?: ForwardingOptions; proxyConfig?: SerializedProxyConfig; ignoreHostCertificateErrors?: string[] | boolean; extraCACertificates?: Array<{ cert: string; } | { certPath: string; }>; clientCertificateHostMap?: { [host: string]: { pfx: string; passphrase?: string; }; }; lookupOptions?: PassThroughLookupOptions; simulateConnectionErrors?: boolean; transformRequest?: Replace<RequestTransform, { 'replaceBody'?: string; 'updateHeaders'?: string; 'updateJsonBody'?: string; 'matchReplaceHost'?: { replacements: SerializedMatchReplacePairs; updateHostHeader?: boolean | string; }; 'matchReplacePath'?: SerializedMatchReplacePairs; 'matchReplaceQuery'?: SerializedMatchReplacePairs; 'matchReplaceBody'?: SerializedMatchReplacePairs; }>; transformResponse?: Replace<ResponseTransform, { 'replaceBody'?: string; 'updateHeaders'?: string; 'updateJsonBody'?: string; 'matchReplaceBody'?: SerializedMatchReplacePairs; }>; hasBeforeRequestCallback?: boolean; hasBeforeResponseCallback?: boolean; } /** * @internal */ export interface BeforePassthroughRequestRequest { args: [Replace<CompletedRequest, { body: SerializedBody; }>]; } /** * @internal */ export interface BeforePassthroughResponseRequest { args: [ Replace<PassThroughResponse, { body: SerializedBody; }>, Replace<CompletedRequest, { body: SerializedBody; }> ]; } /** * Used in merging as a marker for values to omit, because lodash ignores undefineds. * @internal */ export declare const SERIALIZED_OMIT = "__mockttp__transform__omit__"; export declare class PassThroughStep extends Serializable implements RequestStepDefinition { readonly type = "passthrough"; static readonly isFinal = true; readonly ignoreHostHttpsErrors: string[] | boolean; readonly clientCertificateHostMap: { [host: string]: { pfx: Buffer; passphrase?: string; }; }; readonly extraCACertificates: Array<CADefinition>; readonly transformRequest?: RequestTransform; readonly transformResponse?: ResponseTransform; readonly beforeRequest?: (req: CompletedRequest) => MaybePromise<CallbackRequestResult | void> | void; readonly beforeResponse?: (res: PassThroughResponse, req: CompletedRequest) => MaybePromise<CallbackResponseResult | void> | void; readonly proxyConfig?: ProxyConfig; readonly lookupOptions?: PassThroughLookupOptions; readonly simulateConnectionErrors: boolean; protected outgoingSockets: Set<net.Socket>; constructor(options?: PassThroughStepOptions); explain(): string; /** * @internal */ serialize(channel: ClientServerChannel): SerializedPassThroughData; } export declare class CloseConnectionStep extends Serializable implements RequestStepDefinition { readonly type = "close-connection"; static readonly isFinal = true; explain(): string; } export declare class ResetConnectionStep extends Serializable implements RequestStepDefinition { readonly type = "reset-connection"; static readonly isFinal = true; explain(): string; } export declare class TimeoutStep extends Serializable implements RequestStepDefinition { readonly type = "timeout"; static readonly isFinal = true; explain(): string; } export declare class JsonRpcResponseStep extends Serializable implements RequestStepDefinition { readonly result: { result: any; error?: undefined; } | { error: any; result?: undefined; }; readonly type = "json-rpc-response"; static readonly isFinal = true; constructor(result: { result: any; error?: undefined; } | { error: any; result?: undefined; }); explain(): string; } export declare class DelayStep extends Serializable implements RequestStepDefinition { readonly delayMs: number; readonly type = "delay"; static readonly isFinal = false; constructor(delayMs: number); explain(): string; } export declare const StepDefinitionLookup: { simple: typeof FixedResponseStep; callback: typeof CallbackStep; stream: typeof StreamStep; file: typeof FileStep; passthrough: typeof PassThroughStep; 'close-connection': typeof CloseConnectionStep; 'reset-connection': typeof ResetConnectionStep; timeout: typeof TimeoutStep; 'json-rpc-response': typeof JsonRpcResponseStep; delay: typeof DelayStep; }; //# sourceMappingURL=request-step-definitions.d.ts.map