mockttp
Version:
Mock HTTP server for testing HTTP clients and stubbing webservices
575 lines • 21.7 kB
TypeScript
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