Socket
Socket
Sign inDemoInstall

mockttp

Package Overview
Dependencies
163
Maintainers
1
Versions
119
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.10.2 to 3.11.0

2

dist/admin/mockttp-schema.js

@@ -164,2 +164,3 @@ "use strict";

body: Buffer!
rawTrailers: Json!
}

@@ -201,2 +202,3 @@

body: Buffer!
rawTrailers: Json!
}

@@ -203,0 +205,0 @@

135

dist/client/mockttp-admin-request-builder.js

@@ -30,2 +30,10 @@ "use strict";

}
if (message.rawTrailers) {
message.rawTrailers = JSON.parse(message.rawTrailers);
message.trailers = (0, header_utils_1.rawHeadersToObject)(message.rawTrailers);
}
else if (message.rawHeaders && message.body) { // HTTP events with bodies should have trailers
message.rawTrailers = [];
message.trailers = {};
}
if (message.body !== undefined) {

@@ -187,10 +195,10 @@ // Body is serialized as the raw encoded buffer in base64

requestInitiated {
id,
protocol,
method,
url,
path,
${this.schema.asOptionalField('InitiatedRequest', 'remoteIpAddress')},
${this.schema.asOptionalField('InitiatedRequest', 'remotePort')},
hostname,
id
protocol
method
url
path
${this.schema.asOptionalField('InitiatedRequest', 'remoteIpAddress')}
${this.schema.asOptionalField('InitiatedRequest', 'remotePort')}
hostname

@@ -200,4 +208,4 @@ ${this.schema.typeHasField('InitiatedRequest', 'rawHeaders')

: 'headers'}
timingEvents,
httpVersion,
timingEvents
httpVersion
${this.schema.asOptionalField('InitiatedRequest', 'tags')}

@@ -208,11 +216,11 @@ }

requestReceived {
id,
id
${this.schema.asOptionalField('Request', 'matchedRuleId')}
protocol,
method,
url,
path,
${this.schema.asOptionalField('Request', 'remoteIpAddress')},
${this.schema.asOptionalField('Request', 'remotePort')},
hostname,
protocol
method
url
path
${this.schema.asOptionalField('Request', 'remoteIpAddress')}
${this.schema.asOptionalField('Request', 'remotePort')}
hostname

@@ -223,3 +231,5 @@ ${this.schema.typeHasField('Request', 'rawHeaders')

body,
body
${this.schema.asOptionalField('Request', 'rawTrailers')}
${this.schema.asOptionalField('Request', 'timingEvents')}

@@ -232,5 +242,5 @@ ${this.schema.asOptionalField('Request', 'httpVersion')}

responseCompleted {
id,
statusCode,
statusMessage,
id
statusCode
statusMessage

@@ -241,3 +251,5 @@ ${this.schema.typeHasField('Response', 'rawHeaders')

body,
body
${this.schema.asOptionalField('Response', 'rawTrailers')}
${this.schema.asOptionalField('Response', 'timingEvents')}

@@ -249,17 +261,18 @@ ${this.schema.asOptionalField('Response', 'tags')}

webSocketRequest {
id,
matchedRuleId,
protocol,
method,
url,
path,
remoteIpAddress,
remotePort,
hostname,
id
matchedRuleId
protocol
method
url
path
remoteIpAddress
remotePort
hostname
rawHeaders,
body,
rawHeaders
body
${this.schema.asOptionalField('Request', 'rawTrailers')}
timingEvents,
httpVersion,
timingEvents
httpVersion
tags

@@ -270,10 +283,11 @@ }

webSocketAccepted {
id,
statusCode,
statusMessage,
id
statusCode
statusMessage
rawHeaders,
body,
rawHeaders
body
${this.schema.asOptionalField('Response', 'rawTrailers')}
timingEvents,
timingEvents
tags

@@ -284,9 +298,9 @@ }

webSocketMessageReceived {
streamId,
direction,
content,
isBinary,
eventTimestamp,
streamId
direction
content
isBinary
eventTimestamp
timingEvents,
timingEvents
tags

@@ -297,9 +311,9 @@ }

webSocketMessageSent {
streamId,
direction,
content,
isBinary,
eventTimestamp,
streamId
direction
content
isBinary
eventTimestamp
timingEvents,
timingEvents
tags

@@ -310,8 +324,8 @@ }

webSocketClose {
streamId,
streamId
closeCode,
closeReason,
closeCode
closeReason
timingEvents,
timingEvents
tags

@@ -392,4 +406,4 @@ }

${this.schema.asOptionalField('ClientErrorRequest', 'remoteIpAddress')},
${this.schema.asOptionalField('ClientErrorRequest', 'remotePort')},
${this.schema.asOptionalField('ClientErrorRequest', 'remoteIpAddress')}
${this.schema.asOptionalField('ClientErrorRequest', 'remotePort')}
}

@@ -408,2 +422,3 @@ response {

body
${this.schema.asOptionalField('Response', 'rawTrailers')}
}

@@ -410,0 +425,0 @@ }

@@ -5,3 +5,3 @@ /// <reference types="node" />

import { Readable } from 'stream';
import { Headers, CompletedRequest, CompletedBody, Explainable, RawHeaders } from "../../types";
import { Headers, Trailers, CompletedRequest, CompletedBody, Explainable, RawHeaders } from "../../types";
import { MaybePromise, Replace } from '../../util/type-utils';

@@ -131,2 +131,10 @@ import { Serializable, ClientServerChannel, SerializedProxyConfig } from "../../serialization/serialization";

/**
* 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

@@ -180,4 +188,5 @@ * be automatically encoded to match the Content-Encoding defined in your

headers?: Headers | undefined;
trailers?: Trailers | undefined;
readonly type = "simple";
constructor(status: number, statusMessage?: string | undefined, data?: string | Uint8Array | Buffer | SerializedBuffer | undefined, headers?: Headers | undefined);
constructor(status: number, statusMessage?: string | undefined, data?: string | Uint8Array | Buffer | SerializedBuffer | undefined, headers?: Headers | undefined, trailers?: Trailers | undefined);
explain(): string;

@@ -184,0 +193,0 @@ }

@@ -31,3 +31,3 @@ "use strict";

class SimpleHandlerDefinition extends serialization_1.Serializable {
constructor(status, statusMessage, data, headers) {
constructor(status, statusMessage, data, headers, trailers) {
super();

@@ -38,4 +38,11 @@ this.status = status;

this.headers = headers;
this.trailers = trailers;
this.type = 'simple';
validateCustomHeaders({}, headers);
validateCustomHeaders({}, trailers);
if (!_.isEmpty(trailers) && headers) {
if (!Object.entries(headers).some(([key, value]) => key.toLowerCase() === 'transfer-encoding' && value === 'chunked')) {
throw new Error("Trailers can only be set when using chunked transfer encoding");
}
}
}

@@ -46,3 +53,4 @@ explain() {

(this.headers ? `, headers ${JSON.stringify(this.headers)}` : "") +
(this.data ? ` and body "${this.data}"` : "");
(this.data ? ` and body "${this.data}"` : "") +
(this.trailers ? `then trailers ${JSON.stringify(this.trailers)}` : "");
}

@@ -49,0 +57,0 @@ }

@@ -46,2 +46,5 @@ "use strict";

}
if (this.trailers) {
response.addTrailers(this.trailers);
}
response.end(this.data || "");

@@ -53,3 +56,5 @@ }

if (result.json !== undefined) {
result.headers = _.assign(result.headers || {}, { 'Content-Type': 'application/json' });
result.headers = Object.assign(result.headers || {}, {
'Content-Type': 'application/json'
});
result.body = JSON.stringify(result.json);

@@ -68,2 +73,4 @@ delete result.json;

(0, request_utils_1.writeHead)(response, result.statusCode || result.status || 200, result.statusMessage, result.headers);
if (result.trailers)
response.addTrailers(result.trailers);
response.end(result.rawBody || "");

@@ -501,2 +508,26 @@ }

});
// Forward server trailers, if we receive any:
serverRes.on('end', () => {
if (!serverRes.rawTrailers?.length)
return;
const trailersToForward = (0, header_utils_1.pairFlatRawHeaders)(serverRes.rawTrailers)
.filter(([key, value]) => {
if (!(0, header_utils_1.validateHeader)(key, value)) {
console.warn(`Not forwarding invalid trailer: "${key}: ${value}"`);
// Nothing else we can do in this case regardless - setHeaders will
// throw within Node if we try to set this value.
return false;
}
return true;
});
try {
clientRes.addTrailers((0, request_utils_1.isHttp2)(clientReq)
// HTTP/2 compat doesn't support raw headers here (yet)
? (0, header_utils_1.rawHeadersToObjectPreservingCase)(trailersToForward)
: trailersToForward);
}
catch (e) {
console.warn(`Failed to forward response trailers: ${e}`);
}
});
let serverStatusCode = serverRes.statusCode;

@@ -690,2 +721,23 @@ let serverStatusMessage = serverRes.statusMessage;

});
// Forward any request trailers received from the client:
const forwardTrailers = () => {
if (clientReq.rawTrailers?.length) {
if (serverReq.addTrailers) {
serverReq.addTrailers(clientReq.rawTrailers);
}
else {
// See https://github.com/szmarczak/http2-wrapper/issues/103
console.warn('Not forwarding request trailers - not yet supported for HTTP/2');
}
}
};
// This has to be above the pipe setup below, or we end the stream before adding the
// trailers, and they're lost.
if (clientReqBody.readableEnded) {
forwardTrailers();
}
else {
clientReqBody.once('end', forwardTrailers);
}
// Forward the request body to the upstream server:
if (reqBodyOverride) {

@@ -692,0 +744,0 @@ clientReqBody.resume(); // Dump any remaining real request body

/// <reference types="node" />
/// <reference types="node" />
import { Readable } from "stream";
import { Headers, CompletedRequest, Method, MockedEndpoint } from "../../types";
import { Headers, CompletedRequest, Method, MockedEndpoint, Trailers } from "../../types";
import type { RequestRuleData } from "./request-rule";

@@ -37,6 +37,6 @@ import { CallbackResponseResult, PassThroughHandlerOptions } from "./request-handler-definitions";

* Reply to matched requests with a given status code and (optionally) status message,
* body and headers.
* body, headers & trailers.
*
* If one string argument is provided, it's used as the body. If two are
* provided (even if one is empty), then 1st is the status message, and
* provided (even if one is empty) then the 1st is the status message, and
* the 2nd the body. If no headers are provided, only the standard required

@@ -55,4 +55,4 @@ * headers are set, e.g. Date and Transfer-Encoding.

*/
thenReply(status: number, data?: string | Buffer, headers?: Headers): Promise<MockedEndpoint>;
thenReply(status: number, statusMessage: string, data: string | Buffer, headers?: Headers): Promise<MockedEndpoint>;
thenReply(status: number, data?: string | Buffer, headers?: Headers, trailers?: Trailers): Promise<MockedEndpoint>;
thenReply(status: number, statusMessage: string, data: string | Buffer, headers?: Headers, trailers?: Trailers): Promise<MockedEndpoint>;
/**

@@ -59,0 +59,0 @@ * Reply to matched requests with the given status & JSON and (optionally)

@@ -54,8 +54,10 @@ "use strict";

}
thenReply(status, dataOrMessage, dataOrHeaders, headers) {
thenReply(status, dataOrMessage, dataOrHeaders, headersOrTrailers, trailers) {
let data;
let statusMessage;
let headers;
if ((0, lodash_1.isBuffer)(dataOrHeaders) || (0, lodash_1.isString)(dataOrHeaders)) {
data = dataOrHeaders;
statusMessage = dataOrMessage;
headers = headersOrTrailers;
}

@@ -65,6 +67,7 @@ else {

headers = dataOrHeaders;
trailers = headersOrTrailers;
}
const rule = {
...this.buildBaseRuleData(),
handler: new request_handler_definitions_1.SimpleHandlerDefinition(status, statusMessage, data, headers)
handler: new request_handler_definitions_1.SimpleHandlerDefinition(status, statusMessage, data, headers, trailers)
};

@@ -71,0 +74,0 @@ return this.addRule(rule);

@@ -55,3 +55,5 @@ "use strict";

...initiatedRequest,
body: (0, request_utils_1.buildBodyReader)(Buffer.from([]), req.headers)
body: (0, request_utils_1.buildBodyReader)(Buffer.from([]), req.headers),
rawTrailers: [],
trailers: {}
};

@@ -58,0 +60,0 @@ }));

@@ -216,3 +216,7 @@ "use strict";

// nothing else is listening, so we need to catch errors on the socket:
socket.once('error', (e) => console.log('Error on client socket', e));
socket.once('error', (e) => {
if (options.debug) {
console.log('Error on client socket', e);
}
});
const connectUrl = req.url || req.headers['host'];

@@ -219,0 +223,0 @@ if (!connectUrl) {

@@ -453,2 +453,11 @@ "use strict";

(0, util_1.makePropertyWritable)(req, 'rawHeaders');
let rawTrailers;
Object.defineProperty(req, 'rawTrailers', {
get: () => rawTrailers,
set: (flatRawTrailers) => {
rawTrailers = flatRawTrailers
? (0, header_utils_1.pairFlatRawHeaders)(flatRawTrailers)
: undefined;
}
});
return Object.assign(req, {

@@ -458,2 +467,3 @@ id,

rawHeaders,
rawTrailers,
remoteIpAddress: req.socket.remoteAddress,

@@ -753,2 +763,4 @@ remotePort: req.socket.remotePort,

rawHeaders: [['Connection', 'close']],
trailers: {},
rawTrailers: [],
statusCode: isHeaderOverflow

@@ -755,0 +767,0 @@ ? 431

@@ -34,3 +34,7 @@ /// <reference types="node" />

}
export interface Trailers {
[key: string]: undefined | string | string[];
}
export declare type RawHeaders = Array<[key: string, value: string]>;
export declare type RawTrailers = RawHeaders;
export interface Request {

@@ -114,2 +118,3 @@ id: string;

body: OngoingBody;
rawTrailers?: RawHeaders;
}

@@ -195,2 +200,4 @@ export interface OngoingBody {

body: CompletedBody;
rawTrailers: RawTrailers;
trailers: Trailers;
}

@@ -212,2 +219,3 @@ export interface TimingEvents {

body: OngoingBody;
getRawTrailers(): RawTrailers;
timingEvents: TimingEvents;

@@ -223,2 +231,4 @@ tags: string[];

body: CompletedBody;
rawTrailers: RawTrailers;
trailers: Trailers;
timingEvents: TimingEvents;

@@ -225,0 +235,0 @@ tags: string[];

@@ -263,5 +263,31 @@ "use strict";

const requestData = buildInitiatedRequest(request);
return { ...requestData, body };
return {
...requestData,
body,
rawTrailers: request.rawTrailers ?? [],
trailers: (0, header_utils_1.rawHeadersToObject)(request.rawTrailers ?? [])
};
}
exports.waitForCompletedRequest = waitForCompletedRequest;
/**
* Parse the accepted format of the headers argument for writeHead and addTrailers
* into a single consistent paired-tuple format.
*/
const getHeaderPairsFromArgument = (headersArg) => {
// Two legal formats of header args (flat & object), one unofficial (tuple array)
if (Array.isArray(headersArg)) {
if (!Array.isArray(headersArg[0])) {
// Flat -> Raw tuples
return (0, header_utils_1.pairFlatRawHeaders)(headersArg);
}
else {
// Already raw tuples, cheeky
return headersArg;
}
}
else {
// Headers object -> raw tuples
return (0, header_utils_1.objectHeadersToRaw)(headersArg ?? {});
}
};
function trackResponse(response, timingEvents, tags, options) {

@@ -276,2 +302,3 @@ let trackedResponse = response;

const originalEnd = trackedResponse.end;
const originalAddTrailers = trackedResponse.addTrailers;
const originalGetHeaders = trackedResponse.getHeaders;

@@ -296,17 +323,6 @@ let writtenHeaders;

}
// Two legal formats of header args (flat & object), one unofficial (tuple array)
if (Array.isArray(headersArg)) {
if (!Array.isArray(headersArg[0])) {
// Flat -> Raw tuples
writtenHeaders = (0, header_utils_1.pairFlatRawHeaders)(headersArg);
}
else {
// Already raw tuples, cheeky
writtenHeaders = headersArg;
}
writtenHeaders = getHeaderPairsFromArgument(headersArg);
if (isHttp2(trackedResponse)) {
writtenHeaders.unshift([':status', args[0].toString()]);
}
else {
// Headers object -> raw tuples
writtenHeaders = (0, header_utils_1.objectHeadersToRaw)(headersArg ?? {});
}
// Headers might also have been set with setHeader before. They'll be combined, with headers

@@ -334,2 +350,9 @@ // here taking precendence. We simulate this by pulling in all values from getHeaders() and

};
let writtenTrailers;
trackedResponse.getRawTrailers = () => writtenTrailers ?? [];
trackedResponse.addTrailers = function (...args) {
const trailersArg = args[0];
writtenTrailers = getHeaderPairsFromArgument(trailersArg);
return originalAddTrailers.apply(this, args);
};
const trackingWrite = function (...args) {

@@ -377,3 +400,5 @@ trackingStream.write.apply(trackingStream, args);

rawHeaders: response.getRawHeaders(),
body: body
body: body,
rawTrailers: response.getRawTrailers(),
trailers: (0, header_utils_1.rawHeadersToObject)(response.getRawTrailers())
}).valueOf();

@@ -472,3 +497,5 @@ if (!(response instanceof http2.Http2ServerResponse)) {

headers,
body
body,
rawTrailers: [],
trailers: {}
};

@@ -475,0 +502,0 @@ }

{
"name": "mockttp",
"version": "3.10.2",
"version": "3.11.0",
"description": "Mock HTTP server for testing HTTP clients and stubbing webservices",

@@ -178,3 +178,3 @@ "exports": {

"cross-fetch": "^3.1.5",
"destroyable-server": "^1.0.0",
"destroyable-server": "^1.0.2",
"express": "^4.14.0",

@@ -181,0 +181,0 @@ "graphql": "^14.0.2 || ^15.5",

@@ -162,2 +162,3 @@ import gql from "graphql-tag";

body: Buffer!
rawTrailers: Json!
}

@@ -199,2 +200,3 @@

body: Buffer!
rawTrailers: Json!
}

@@ -201,0 +203,0 @@

@@ -41,2 +41,10 @@ import _ = require('lodash');

if (message.rawTrailers) {
message.rawTrailers = JSON.parse(message.rawTrailers);
message.trailers = rawHeadersToObject(message.rawTrailers);
} else if (message.rawHeaders && message.body) { // HTTP events with bodies should have trailers
message.rawTrailers = [];
message.trailers = {};
}
if (message.body !== undefined) {

@@ -222,10 +230,10 @@ // Body is serialized as the raw encoded buffer in base64

requestInitiated {
id,
protocol,
method,
url,
path,
${this.schema.asOptionalField('InitiatedRequest', 'remoteIpAddress')},
${this.schema.asOptionalField('InitiatedRequest', 'remotePort')},
hostname,
id
protocol
method
url
path
${this.schema.asOptionalField('InitiatedRequest', 'remoteIpAddress')}
${this.schema.asOptionalField('InitiatedRequest', 'remotePort')}
hostname

@@ -236,4 +244,4 @@ ${this.schema.typeHasField('InitiatedRequest', 'rawHeaders')

}
timingEvents,
httpVersion,
timingEvents
httpVersion
${this.schema.asOptionalField('InitiatedRequest', 'tags')}

@@ -244,11 +252,11 @@ }

requestReceived {
id,
id
${this.schema.asOptionalField('Request', 'matchedRuleId')}
protocol,
method,
url,
path,
${this.schema.asOptionalField('Request', 'remoteIpAddress')},
${this.schema.asOptionalField('Request', 'remotePort')},
hostname,
protocol
method
url
path
${this.schema.asOptionalField('Request', 'remoteIpAddress')}
${this.schema.asOptionalField('Request', 'remotePort')}
hostname

@@ -260,3 +268,5 @@ ${this.schema.typeHasField('Request', 'rawHeaders')

body,
body
${this.schema.asOptionalField('Request', 'rawTrailers')}
${this.schema.asOptionalField('Request', 'timingEvents')}

@@ -269,5 +279,5 @@ ${this.schema.asOptionalField('Request', 'httpVersion')}

responseCompleted {
id,
statusCode,
statusMessage,
id
statusCode
statusMessage

@@ -279,3 +289,5 @@ ${this.schema.typeHasField('Response', 'rawHeaders')

body,
body
${this.schema.asOptionalField('Response', 'rawTrailers')}
${this.schema.asOptionalField('Response', 'timingEvents')}

@@ -287,17 +299,18 @@ ${this.schema.asOptionalField('Response', 'tags')}

webSocketRequest {
id,
matchedRuleId,
protocol,
method,
url,
path,
remoteIpAddress,
remotePort,
hostname,
id
matchedRuleId
protocol
method
url
path
remoteIpAddress
remotePort
hostname
rawHeaders,
body,
rawHeaders
body
${this.schema.asOptionalField('Request', 'rawTrailers')}
timingEvents,
httpVersion,
timingEvents
httpVersion
tags

@@ -308,10 +321,11 @@ }

webSocketAccepted {
id,
statusCode,
statusMessage,
id
statusCode
statusMessage
rawHeaders,
body,
rawHeaders
body
${this.schema.asOptionalField('Response', 'rawTrailers')}
timingEvents,
timingEvents
tags

@@ -322,9 +336,9 @@ }

webSocketMessageReceived {
streamId,
direction,
content,
isBinary,
eventTimestamp,
streamId
direction
content
isBinary
eventTimestamp
timingEvents,
timingEvents
tags

@@ -335,9 +349,9 @@ }

webSocketMessageSent {
streamId,
direction,
content,
isBinary,
eventTimestamp,
streamId
direction
content
isBinary
eventTimestamp
timingEvents,
timingEvents
tags

@@ -348,8 +362,8 @@ }

webSocketClose {
streamId,
streamId
closeCode,
closeReason,
closeCode
closeReason
timingEvents,
timingEvents
tags

@@ -432,4 +446,4 @@ }

${this.schema.asOptionalField('ClientErrorRequest', 'remoteIpAddress')},
${this.schema.asOptionalField('ClientErrorRequest', 'remotePort')},
${this.schema.asOptionalField('ClientErrorRequest', 'remoteIpAddress')}
${this.schema.asOptionalField('ClientErrorRequest', 'remotePort')}
}

@@ -449,2 +463,3 @@ response {

body
${this.schema.asOptionalField('Response', 'rawTrailers')}
}

@@ -451,0 +466,0 @@ }

@@ -10,2 +10,3 @@ import _ = require('lodash');

Headers,
Trailers,
CompletedRequest,

@@ -185,2 +186,11 @@ CompletedBody,

/**
* 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

@@ -266,3 +276,4 @@ * be automatically encoded to match the Content-Encoding defined in your

public data?: string | Uint8Array | Buffer | SerializedBuffer,
public headers?: Headers
public headers?: Headers,
public trailers?: Trailers
) {

@@ -272,2 +283,11 @@ super();

validateCustomHeaders({}, headers);
validateCustomHeaders({}, trailers);
if (!_.isEmpty(trailers) && headers) {
if (!Object.entries(headers!).some(([key, value]) =>
key.toLowerCase() === 'transfer-encoding' && value === 'chunked'
)) {
throw new Error("Trailers can only be set when using chunked transfer encoding");
}
}
}

@@ -279,3 +299,4 @@

(this.headers ? `, headers ${JSON.stringify(this.headers)}` : "") +
(this.data ? ` and body "${this.data}"` : "");
(this.data ? ` and body "${this.data}"` : "") +
(this.trailers ? `then trailers ${JSON.stringify(this.trailers)}` : "");
}

@@ -282,0 +303,0 @@ }

@@ -165,2 +165,6 @@ import _ = require('lodash');

if (this.trailers) {
response.addTrailers(this.trailers);
}
response.end(this.data || "");

@@ -170,5 +174,10 @@ }

async function writeResponseFromCallback(result: CallbackResponseMessageResult, response: OngoingResponse) {
async function writeResponseFromCallback(
result: CallbackResponseMessageResult,
response: OngoingResponse
) {
if (result.json !== undefined) {
result.headers = _.assign(result.headers || {}, { 'Content-Type': 'application/json' });
result.headers = Object.assign(result.headers || {}, {
'Content-Type': 'application/json'
});
result.body = JSON.stringify(result.json);

@@ -198,2 +207,5 @@ delete result.json;

);
if (result.trailers) response.addTrailers(result.trailers);
response.end(result.rawBody || "");

@@ -761,2 +773,29 @@ }

// Forward server trailers, if we receive any:
serverRes.on('end', () => {
if (!serverRes.rawTrailers?.length) return;
const trailersToForward = pairFlatRawHeaders(serverRes.rawTrailers)
.filter(([key, value]) => {
if (!validateHeader(key, value)) {
console.warn(`Not forwarding invalid trailer: "${key}: ${value}"`);
// Nothing else we can do in this case regardless - setHeaders will
// throw within Node if we try to set this value.
return false;
}
return true;
});
try {
clientRes.addTrailers(
isHttp2(clientReq)
// HTTP/2 compat doesn't support raw headers here (yet)
? rawHeadersToObjectPreservingCase(trailersToForward)
: trailersToForward
);
} catch (e) {
console.warn(`Failed to forward response trailers: ${e}`);
}
});
let serverStatusCode = serverRes.statusCode!;

@@ -1001,2 +1040,22 @@ let serverStatusMessage = serverRes.statusMessage

// Forward any request trailers received from the client:
const forwardTrailers = () => {
if (clientReq.rawTrailers?.length) {
if (serverReq.addTrailers) {
serverReq.addTrailers(clientReq.rawTrailers);
} else {
// See https://github.com/szmarczak/http2-wrapper/issues/103
console.warn('Not forwarding request trailers - not yet supported for HTTP/2');
}
}
};
// This has to be above the pipe setup below, or we end the stream before adding the
// trailers, and they're lost.
if (clientReqBody.readableEnded) {
forwardTrailers();
} else {
clientReqBody.once('end', forwardTrailers);
}
// Forward the request body to the upstream server:
if (reqBodyOverride) {

@@ -1003,0 +1062,0 @@ clientReqBody.resume(); // Dump any remaining real request body

import { merge, isString, isBuffer } from "lodash";
import { Readable } from "stream";
import { Headers, CompletedRequest, Method, MockedEndpoint } from "../../types";
import { Headers, CompletedRequest, Method, MockedEndpoint, Trailers } from "../../types";
import type { RequestRuleData } from "./request-rule";

@@ -90,6 +90,6 @@

* Reply to matched requests with a given status code and (optionally) status message,
* body and headers.
* body, headers & trailers.
*
* If one string argument is provided, it's used as the body. If two are
* provided (even if one is empty), then 1st is the status message, and
* provided (even if one is empty) then the 1st is the status message, and
* the 2nd the body. If no headers are provided, only the standard required

@@ -108,8 +108,14 @@ * headers are set, e.g. Date and Transfer-Encoding.

*/
thenReply(status: number, data?: string | Buffer, headers?: Headers): Promise<MockedEndpoint>;
thenReply(
status: number,
data?: string | Buffer,
headers?: Headers,
trailers?: Trailers
): Promise<MockedEndpoint>;
thenReply(
status: number,
statusMessage: string,
data: string | Buffer,
headers?: Headers
headers?: Headers,
trailers?: Trailers
): Promise<MockedEndpoint>

@@ -120,12 +126,17 @@ thenReply(

dataOrHeaders?: string | Buffer | Headers,
headers?: Headers
headersOrTrailers?: Headers | Trailers,
trailers?: Trailers
): Promise<MockedEndpoint> {
let data: string | Buffer | undefined;
let statusMessage: string | undefined;
let headers: Headers | undefined;
if (isBuffer(dataOrHeaders) || isString(dataOrHeaders)) {
data = dataOrHeaders as (Buffer | string);
statusMessage = dataOrMessage as string;
headers = headersOrTrailers as Headers;
} else {
data = dataOrMessage as string | Buffer | undefined;
headers = dataOrHeaders as Headers | undefined;
trailers = headersOrTrailers as Trailers | undefined;
}

@@ -135,3 +146,9 @@

...this.buildBaseRuleData(),
handler: new SimpleHandlerDefinition(status, statusMessage, data, headers)
handler: new SimpleHandlerDefinition(
status,
statusMessage,
data,
headers,
trailers
)
};

@@ -138,0 +155,0 @@

@@ -99,3 +99,5 @@ import * as _ from 'lodash';

...initiatedRequest,
body: buildBodyReader(Buffer.from([]), req.headers)
body: buildBodyReader(Buffer.from([]), req.headers),
rawTrailers: [],
trailers: {}
};

@@ -102,0 +104,0 @@ })

@@ -276,3 +276,7 @@ import _ = require('lodash');

// nothing else is listening, so we need to catch errors on the socket:
socket.once('error', (e) => console.log('Error on client socket', e));
socket.once('error', (e) => {
if (options.debug) {
console.log('Error on client socket', e);
}
});

@@ -279,0 +283,0 @@ const connectUrl = req.url || req.headers['host'];

@@ -29,3 +29,4 @@ import _ = require("lodash");

TlsPassthroughEvent,
RuleEvent
RuleEvent,
RawTrailers
} from "../types";

@@ -618,2 +619,12 @@ import { DestroyableServer } from "destroyable-server";

let rawTrailers: RawTrailers | undefined;
Object.defineProperty(req, 'rawTrailers', {
get: () => rawTrailers,
set: (flatRawTrailers) => {
rawTrailers = flatRawTrailers
? pairFlatRawHeaders(flatRawTrailers)
: undefined;
}
});
return Object.assign(req, {

@@ -623,2 +634,3 @@ id,

rawHeaders,
rawTrailers, // Just makes the type happy - really managed by property above
remoteIpAddress: req.socket.remoteAddress,

@@ -973,2 +985,4 @@ remotePort: req.socket.remotePort,

rawHeaders: [['Connection', 'close']],
trailers: {},
rawTrailers: [],
statusCode:

@@ -975,0 +989,0 @@ isHeaderOverflow

@@ -39,3 +39,9 @@ import stream = require('stream');

export interface Trailers {
// 0+ of any trailer
[key: string]: undefined | string | string[];
}
export type RawHeaders = Array<[key: string, value: string]>;
export type RawTrailers = RawHeaders; // Just a convenient alias

@@ -146,2 +152,3 @@ export interface Request {

body: OngoingBody;
rawTrailers?: RawHeaders;
}

@@ -229,2 +236,4 @@

body: CompletedBody;
rawTrailers: RawTrailers;
trailers: Trailers;
}

@@ -254,2 +263,3 @@

body: OngoingBody;
getRawTrailers(): RawTrailers;
timingEvents: TimingEvents;

@@ -266,2 +276,4 @@ tags: string[];

body: CompletedBody;
rawTrailers: RawTrailers;
trailers: Trailers;
timingEvents: TimingEvents;

@@ -268,0 +280,0 @@ tags: string[];

@@ -347,5 +347,30 @@ import * as _ from 'lodash';

const requestData = buildInitiatedRequest(request);
return { ...requestData, body };
return {
...requestData,
body,
rawTrailers: request.rawTrailers ?? [],
trailers: rawHeadersToObject(request.rawTrailers ?? [])
};
}
/**
* Parse the accepted format of the headers argument for writeHead and addTrailers
* into a single consistent paired-tuple format.
*/
const getHeaderPairsFromArgument = (headersArg: any) => {
// Two legal formats of header args (flat & object), one unofficial (tuple array)
if (Array.isArray(headersArg)) {
if (!Array.isArray(headersArg[0])) {
// Flat -> Raw tuples
return pairFlatRawHeaders(headersArg);
} else {
// Already raw tuples, cheeky
return headersArg;
}
} else {
// Headers object -> raw tuples
return objectHeadersToRaw(headersArg ?? {});
}
};
export function trackResponse(

@@ -369,2 +394,3 @@ response: http.ServerResponse,

const originalEnd = trackedResponse.end;
const originalAddTrailers = trackedResponse.addTrailers;
const originalGetHeaders = trackedResponse.getHeaders;

@@ -393,14 +419,6 @@

// Two legal formats of header args (flat & object), one unofficial (tuple array)
if (Array.isArray(headersArg)) {
if (!Array.isArray(headersArg[0])) {
// Flat -> Raw tuples
writtenHeaders = pairFlatRawHeaders(headersArg);
} else {
// Already raw tuples, cheeky
writtenHeaders = headersArg;
}
} else {
// Headers object -> raw tuples
writtenHeaders = objectHeadersToRaw(headersArg ?? {});
writtenHeaders = getHeaderPairsFromArgument(headersArg);
if (isHttp2(trackedResponse)) {
writtenHeaders.unshift([':status', args[0].toString()]);
}

@@ -429,4 +447,13 @@

return originalWriteHeader.apply(this, args);
}
};
let writtenTrailers: RawHeaders | undefined;
trackedResponse.getRawTrailers = () => writtenTrailers ?? [];
trackedResponse.addTrailers = function (this: typeof trackedResponse, ...args: any) {
const trailersArg = args[0];
writtenTrailers = getHeaderPairsFromArgument(trailersArg);
return originalAddTrailers.apply(this, args);
};
const trackingWrite = function (this: typeof trackedResponse, ...args: any) {

@@ -487,5 +514,10 @@ trackingStream.write.apply(trackingStream, args);

statusMessage: '',
headers: response.getHeaders(),
rawHeaders: response.getRawHeaders(),
body: body
body: body,
rawTrailers: response.getRawTrailers(),
trailers: rawHeadersToObject(response.getRawTrailers())
}).valueOf();

@@ -612,4 +644,6 @@

headers,
body
body,
rawTrailers: [],
trailers: {}
};
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc