Socket
Socket
Sign inDemoInstall

mockttp

Package Overview
Dependencies
Maintainers
1
Versions
124
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mockttp - npm Package Compare versions

Comparing version 0.21.1 to 0.21.2

15

custom-typings/node-type-extensions.d.ts

@@ -6,3 +6,3 @@ // There's a few places where we attach extra data to some node objects during

import * as net from 'net';
import * as stream from 'stream';
import * as streams from 'stream';

@@ -29,4 +29,15 @@ interface Socket {

// Internal reference to a parent socket, e.g. for TLS wrappers
// Our recordings of various timestamps, used for monitoring &
// performance analysis later on
__timingInfo?: {
initialSocket?: number; // Initial raw socket time
tunnelSetup?: number; // Latest CONNECT completion, if any
tlsConnected?: number; // Latest TLS handshake completion, if any
};
// Internal reference to the parent socket, available on TLS sockets
_parent?: Socket;
// Internal reference to the underlying stream, available on _stream_wrap
stream?: streams.Duplex & Partial<net.Socket>;
}

@@ -33,0 +44,0 @@ }

25

dist/rules/handlers.js

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

!_.includes(this.ignoreHostCertificateErrors, hostWithPort);
// Use a client cert if it's listed for the host+port or whole hostname
// Use a client cert if it's listed for the host+port or whole hostname
const clientCert = this.clientCertificateHostMap[hostWithPort] ||

@@ -540,5 +540,14 @@ this.clientCertificateHostMap[hostname] ||

});
clientRes.writeHead(serverStatusCode, serverStatusMessage || clientRes.statusMessage);
try {
clientRes.writeHead(serverStatusCode, serverStatusMessage || clientRes.statusMessage);
}
catch (e) {
serverReq.abort();
reject(e);
}
if (resBodyOverride) {
// Return the override data to the client:
clientRes.end(resBodyOverride);
// Dump the real response data:
serverRes.resume();
resolve();

@@ -548,3 +557,3 @@ }

serverRes.pipe(clientRes);
serverRes.once('end', resolve);
clientRes.once('finish', resolve);
}

@@ -572,4 +581,10 @@ }));

}
clientReq.once('abort', () => serverReq.abort());
clientRes.once('close', () => serverReq.abort());
// If the downstream connection aborts, before the response has been completed,
// we also abort the upstream connection. Important to avoid unnecessary connections,
// and to correctly proxy client connection behaviour to the upstream server.
function abortUpstream() {
serverReq.abort();
}
clientReq.on('aborted', abortUpstream);
clientRes.once('finish', () => clientReq.removeListener('aborted', abortUpstream));
serverReq.once('error', (e) => {

@@ -576,0 +591,0 @@ if (serverReq.aborted)

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

const tls = require("tls");
const SocketWrapper = require("_stream_wrap");
const httpolyglot = require("@httptoolkit/httpolyglot");

@@ -46,8 +45,15 @@ const destroyable_server_1 = require("../util/destroyable-server");

socket.once('data', resolve);
// If you keep it open, you probably trust the TLS connection
// (some clients open unused connections for connection pools etc)
util_1.delay(5000).then(resolve);
// If you silently close it very quicky, you probably don't trust us
socket.once('close', reject);
socket.once('end', reject);
// Some clients open later-unused TLS connections for connection pools, preconnect, etc.
// Even if these are shut later on, that doesn't mean they're are rejected connections.
// To differentiate the two cases, we consider connections OK after waiting 10x longer
// than the initial TLS handshake for an unhappy disconnection.
const timing = socket.__timingInfo || {};
const tlsSetupDuration = timing.tlsConnected - (timing.tunnelSetup || timing.initialSocket);
const maxTlsRejectionTime = (tlsSetupDuration !== NaN && tlsSetupDuration !== 0)
? tlsSetupDuration * 10
: 5000;
util_1.delay(maxTlsRejectionTime).then(resolve);
})

@@ -126,2 +132,3 @@ .then(() => {

server.on('connection', (socket) => {
socket.__timingInfo = socket.__timingInfo || { initialSocket: Date.now() };
// All sockets are initially marked as using unencrypted upstream connections.

@@ -132,7 +139,13 @@ // If TLS is used, this is upgraded to 'true' by secureConnection below.

server.on('secureConnection', (socket) => {
if (socket._parent) {
const parentSocket = getParentSocket(socket);
if (parentSocket) {
// Sometimes wrapper TLS sockets created by the HTTP/2 server don't include the
// underlying port details, so it's better to make sure we copy them up.
copyAddressDetails(socket._parent, socket);
// underlying socket details, so it's better to make sure we copy them up.
copyAddressDetails(parentSocket, socket);
copyTimingDetails(parentSocket, socket);
}
else if (!socket.__timingInfo) {
socket.__timingInfo = { initialSocket: Date.now() };
}
socket.__timingInfo.tlsConnected = Date.now();
socket.lastHopEncrypted = true;

@@ -189,6 +202,4 @@ ifTlsDropped(socket, () => {

socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'utf-8', () => {
// Required here to avoid https://github.com/nodejs/node/issues/29902
const socketWrapper = new SocketWrapper(socket);
copyAddressDetails(socket, socketWrapper);
server.emit('connection', socketWrapper);
socket.__timingInfo.tunnelSetup = Date.now();
server.emit('connection', socket);
});

@@ -209,2 +220,3 @@ }

copyAddressDetails(res.socket, res.stream);
copyTimingDetails(res.socket, res.stream);
server.emit('connection', res.stream);

@@ -216,2 +228,8 @@ }

exports.createComboServer = createComboServer;
function getParentSocket(socket) {
if (socket._parent)
return socket._parent; // TLS wrapper
else
return socket.stream; // SocketWrapper
}
// Update the target socket(-ish) with the address details from the source socket,

@@ -228,2 +246,8 @@ // iff the target has no details of its own.

}
function copyTimingDetails(source, target) {
if (!target.__timingInfo) {
// Clone timing info, don't copy it - child sockets get their own independent timing stats
target.__timingInfo = Object.assign({}, source.__timingInfo);
}
}
//# sourceMappingURL=http-combo-server.js.map

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

constructor(debug: boolean, ignoreHostCertificateErrors: string[]);
handleUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise<void>;
handleUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer): void;
private connectUpstream;

@@ -12,0 +12,0 @@ private pipeWebSocket;

@@ -30,29 +30,27 @@ "use strict";

handleUpgrade(req, socket, head) {
return __awaiter(this, void 0, void 0, function* () {
let { protocol: requestedProtocol, hostname, port, path } = url.parse(req.url);
if (this.debug)
console.log(`Handling upgrade for ${req.url}`);
const transparentProxy = !hostname;
if (transparentProxy) {
const hostHeader = req.headers.host;
[hostname, port] = hostHeader.split(':');
// upstreamEncryption is set in http-combo-server, for requests that have explicitly
// CONNECTed upstream (which may then up/downgrade from the current encryption).
let protocol;
if (socket.lastHopEncrypted !== undefined) {
protocol = socket.lastHopEncrypted ? 'wss' : 'ws';
}
else {
protocol = req.connection.encrypted ? 'wss' : 'ws';
}
this.connectUpstream(`${protocol}://${hostname}${port ? ':' + port : ''}${path}`, req, socket, head);
let { protocol: requestedProtocol, hostname, port, path } = url.parse(req.url);
if (this.debug)
console.log(`Handling upgrade for ${req.url}`);
const transparentProxy = !hostname;
if (transparentProxy) {
const hostHeader = req.headers.host;
[hostname, port] = hostHeader.split(':');
// upstreamEncryption is set in http-combo-server, for requests that have explicitly
// CONNECTed upstream (which may then up/downgrade from the current encryption).
let protocol;
if (socket.lastHopEncrypted !== undefined) {
protocol = socket.lastHopEncrypted ? 'wss' : 'ws';
}
else {
// Connect directly according to the specified URL
const protocol = requestedProtocol.replace('http', 'ws');
this.connectUpstream(`${protocol}//${hostname}${port ? ':' + port : ''}${path}`, req, socket, head);
protocol = req.connection.encrypted ? 'wss' : 'ws';
}
});
this.connectUpstream(`${protocol}://${hostname}${port ? ':' + port : ''}${path}`, req, socket, head);
}
else {
// Connect directly according to the specified URL
const protocol = requestedProtocol.replace('http', 'ws');
this.connectUpstream(`${protocol}//${hostname}${port ? ':' + port : ''}${path}`, req, socket, head);
}
}
connectUpstream(wsUrl, req, socket, head) {
connectUpstream(wsUrl, req, incomingSocket, head) {
if (this.debug)

@@ -66,3 +64,5 @@ console.log(`Connecting to upstream websocket at ${wsUrl}`);

rejectUnauthorized: checkServerCertificate,
maxPayload: 0
maxPayload: 0,
headers: _.omitBy(req.headers, (_v, headerName) => headerName.toLowerCase().startsWith('sec-websocket') ||
headerName.toLowerCase() === 'connection') // Simplify to string - doesn't matter though, only used by http module anyway
});

@@ -72,3 +72,4 @@ upstreamSocket.once('open', () => {

console.log(`Websocket connected to ${wsUrl}`);
this.wsServer.handleUpgrade(req, socket, head, (ws) => {
// Presumably the below adds an error handler. But what about before we get here?
this.wsServer.handleUpgrade(req, incomingSocket, head, (ws) => {
ws.upstreamSocket = upstreamSocket;

@@ -81,3 +82,3 @@ this.wsServer.emit('connection', ws);

console.log(`Unexpected websocket response from ${wsUrl}: ${res.statusCode}`);
this.mirrorRejection(socket, res);
this.mirrorRejection(incomingSocket, res);
});

@@ -87,4 +88,5 @@ // If there's some other error, we just kill the socket:

console.warn(e);
socket.end();
incomingSocket.end();
});
incomingSocket.once('error', () => upstreamSocket.close(1011)); // Internal error
}

@@ -91,0 +93,0 @@ pipeWebSocket(inSocket, outSocket) {

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

@@ -5,0 +5,0 @@ "main": "dist/main.js",

@@ -670,4 +670,4 @@ /**

// Use a client cert if it's listed for the host+port or whole hostname
const clientCert =this.clientCertificateHostMap[hostWithPort] ||
// Use a client cert if it's listed for the host+port or whole hostname
const clientCert = this.clientCertificateHostMap[hostWithPort] ||
this.clientCertificateHostMap[hostname!] ||

@@ -783,13 +783,22 @@ {};

clientRes.writeHead(
serverStatusCode,
serverStatusMessage || clientRes.statusMessage
);
try {
clientRes.writeHead(
serverStatusCode,
serverStatusMessage || clientRes.statusMessage
);
} catch (e) {
serverReq.abort();
reject(e);
}
if (resBodyOverride) {
// Return the override data to the client:
clientRes.end(resBodyOverride);
// Dump the real response data:
serverRes.resume();
resolve();
} else {
serverRes.pipe(clientRes);
serverRes.once('end', resolve);
clientRes.once('finish', resolve);
}

@@ -820,4 +829,10 @@ });

clientReq.once('abort', () => serverReq.abort());
clientRes.once('close', () => serverReq.abort());
// If the downstream connection aborts, before the response has been completed,
// we also abort the upstream connection. Important to avoid unnecessary connections,
// and to correctly proxy client connection behaviour to the upstream server.
function abortUpstream() {
serverReq.abort();
}
clientReq.on('aborted', abortUpstream);
clientRes.once('finish', () => clientReq.removeListener('aborted', abortUpstream));

@@ -824,0 +839,0 @@ serverReq.once('error', (e: any) => {

@@ -49,9 +49,16 @@ import _ = require('lodash');

// If you keep it open, you probably trust the TLS connection
// (some clients open unused connections for connection pools etc)
delay(5000).then(resolve);
// If you silently close it very quicky, you probably don't trust us
socket.once('close', reject);
socket.once('end', reject);
// Some clients open later-unused TLS connections for connection pools, preconnect, etc.
// Even if these are shut later on, that doesn't mean they're are rejected connections.
// To differentiate the two cases, we consider connections OK after waiting 10x longer
// than the initial TLS handshake for an unhappy disconnection.
const timing = socket.__timingInfo || {};
const tlsSetupDuration = timing.tlsConnected! - (timing.tunnelSetup! || timing.initialSocket!);
const maxTlsRejectionTime = (tlsSetupDuration !== NaN && tlsSetupDuration !== 0)
? tlsSetupDuration * 10
: 5000;
delay(maxTlsRejectionTime).then(resolve);
})

@@ -136,2 +143,4 @@ .then(() => {

server.on('connection', (socket: net.Socket) => {
socket.__timingInfo = socket.__timingInfo || { initialSocket: Date.now() };
// All sockets are initially marked as using unencrypted upstream connections.

@@ -143,8 +152,14 @@ // If TLS is used, this is upgraded to 'true' by secureConnection below.

server.on('secureConnection', (socket: tls.TLSSocket) => {
if ((socket as { _parent?: net.Socket })._parent) {
const parentSocket = getParentSocket(socket);
if (parentSocket) {
// Sometimes wrapper TLS sockets created by the HTTP/2 server don't include the
// underlying port details, so it's better to make sure we copy them up.
copyAddressDetails((socket as any)._parent, socket);
// underlying socket details, so it's better to make sure we copy them up.
copyAddressDetails(parentSocket, socket);
copyTimingDetails(parentSocket, socket);
} else if (!socket.__timingInfo) {
socket.__timingInfo = { initialSocket: Date.now() };
}
socket.__timingInfo!.tlsConnected = Date.now();
socket.lastHopEncrypted = true;

@@ -208,6 +223,4 @@ ifTlsDropped(socket, () => {

socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'utf-8', () => {
// Required here to avoid https://github.com/nodejs/node/issues/29902
const socketWrapper = new SocketWrapper(socket);
copyAddressDetails(socket, socketWrapper);
server.emit('connection', socketWrapper);
socket.__timingInfo!.tunnelSetup = Date.now();
server.emit('connection', socket);
});

@@ -231,2 +244,4 @@ }

copyAddressDetails(res.socket, res.stream);
copyTimingDetails(res.socket, res.stream);
server.emit('connection', res.stream);

@@ -238,8 +253,15 @@ }

function getParentSocket(socket: net.Socket) {
if (socket._parent) return socket._parent; // TLS wrapper
else return socket.stream; // SocketWrapper
}
type SocketIsh<MinProps extends keyof net.Socket> =
streams.Duplex & Partial<Pick<net.Socket, MinProps>>;
// Update the target socket(-ish) with the address details from the source socket,
// iff the target has no details of its own.
function copyAddressDetails(
source: net.Socket,
target: streams.Duplex &
Partial<Pick<net.Socket, 'localAddress' | 'localPort' | 'remoteAddress' | 'remotePort'>>
source: SocketIsh<'localAddress' | 'localPort' | 'remoteAddress' | 'remotePort'>,
target: SocketIsh<'localAddress' | 'localPort' | 'remoteAddress' | 'remotePort'>
) {

@@ -255,2 +277,12 @@ const fields = ['localAddress', 'localPort', 'remoteAddress', 'remotePort'] as const;

});
}
function copyTimingDetails<T extends SocketIsh<'__timingInfo'>>(
source: SocketIsh<'__timingInfo'>,
target: T
): asserts target is T & { __timingInfo: Required<net.Socket>['__timingInfo'] } {
if (!target.__timingInfo) {
// Clone timing info, don't copy it - child sockets get their own independent timing stats
target.__timingInfo = Object.assign({}, source.__timingInfo);
}
}

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

async handleUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
handleUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
let { protocol: requestedProtocol, hostname, port, path } = url.parse(req.url!);

@@ -58,3 +58,3 @@

private connectUpstream(wsUrl: string, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
private connectUpstream(wsUrl: string, req: http.IncomingMessage, incomingSocket: net.Socket, head: Buffer) {
if (this.debug) console.log(`Connecting to upstream websocket at ${wsUrl}`);

@@ -69,3 +69,7 @@

rejectUnauthorized: checkServerCertificate,
maxPayload: 0
maxPayload: 0,
headers: _.omitBy(req.headers, (_v, headerName) =>
headerName.toLowerCase().startsWith('sec-websocket') ||
headerName.toLowerCase() === 'connection'
) as { [key: string]: string } // Simplify to string - doesn't matter though, only used by http module anyway
});

@@ -75,3 +79,4 @@

if (this.debug) console.log(`Websocket connected to ${wsUrl}`);
this.wsServer.handleUpgrade(req, socket, head, (ws) => {
// Presumably the below adds an error handler. But what about before we get here?
this.wsServer.handleUpgrade(req, incomingSocket, head, (ws) => {
(<InterceptedWebSocket> ws).upstreamSocket = upstreamSocket;

@@ -85,3 +90,3 @@ this.wsServer.emit('connection', ws);

console.log(`Unexpected websocket response from ${wsUrl}: ${res.statusCode}`);
this.mirrorRejection(socket, res);
this.mirrorRejection(incomingSocket, res);
});

@@ -92,4 +97,6 @@

console.warn(e);
socket.end();
incomingSocket.end();
});
incomingSocket.once('error', () => upstreamSocket.close(1011)); // Internal error
}

@@ -96,0 +103,0 @@

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
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc