Socket
Socket
Sign inDemoInstall

mockttp

Package Overview
Dependencies
149
Maintainers
1
Versions
119
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.12.6 to 0.13.0

typedoc/interfaces/internal.requestheaders.html

55

dist/client/mocked-endpoint-client.js

@@ -13,53 +13,18 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var MockedEndpointClient = /** @class */ (function () {
function MockedEndpointClient(id, getMockedEndpointData) {
class MockedEndpointClient {
constructor(id, getMockedEndpointData) {
this.id = id;
this.getMockedEndpointData = getMockedEndpointData;
}
MockedEndpointClient.prototype.getSeenRequests = function () {
return __awaiter(this, void 0, void 0, function () {
var mockedEndpointData;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.getMockedEndpointData()];
case 1:
mockedEndpointData = _a.sent();
if (mockedEndpointData === null)
throw new Error("Can't get seen requests for unknown mocked endpoint");
return [2 /*return*/, mockedEndpointData.seenRequests];
}
});
getSeenRequests() {
return __awaiter(this, void 0, void 0, function* () {
const mockedEndpointData = yield this.getMockedEndpointData();
if (mockedEndpointData === null)
throw new Error("Can't get seen requests for unknown mocked endpoint");
return mockedEndpointData.seenRequests;
});
};
return MockedEndpointClient;
}());
}
}
exports.MockedEndpointClient = MockedEndpointClient;
//# sourceMappingURL=mocked-endpoint-client.js.map
/**
* @module Mockttp
*/
import TypedError = require('typed-error');
import { TypedError } from 'typed-error';
import { MockedEndpoint } from "../types";

@@ -6,0 +6,0 @@ import { MockRuleData } from "../rules/mock-rule-types";

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

*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -27,75 +14,37 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var TypedError = require("typed-error");
var getFetchPonyfill = require("fetch-ponyfill");
var _ = require("lodash");
var WebSocket = require("universal-websocket-client");
var connectWebSocketStream = require("websocket-stream");
var subscriptions_transport_ws_1 = require("subscriptions-transport-ws");
var _a = getFetchPonyfill(),
const typed_error_1 = require("typed-error");
const getFetchPonyfill = require("fetch-ponyfill");
const _ = require("lodash");
const WebSocket = require("universal-websocket-client");
const connectWebSocketStream = require("websocket-stream");
const subscriptions_transport_ws_1 = require("subscriptions-transport-ws");
const {
/** @hidden */
fetch = _a.fetch,
fetch,
/** @hidden */
Headers = _a.Headers;
var mockttp_1 = require("../mockttp");
var mock_rule_1 = require("../rules/mock-rule");
var types_1 = require("../types");
var mocked_endpoint_client_1 = require("./mocked-endpoint-client");
var request_utils_1 = require("../server/request-utils");
var ConnectionError = /** @class */ (function (_super) {
__extends(ConnectionError, _super);
function ConnectionError() {
return _super !== null && _super.apply(this, arguments) || this;
}
return ConnectionError;
}(TypedError));
Headers } = getFetchPonyfill();
const mockttp_1 = require("../mockttp");
const mock_rule_1 = require("../rules/mock-rule");
const types_1 = require("../types");
const mocked_endpoint_client_1 = require("./mocked-endpoint-client");
const request_utils_1 = require("../server/request-utils");
class ConnectionError extends typed_error_1.TypedError {
}
exports.ConnectionError = ConnectionError;
var RequestError = /** @class */ (function (_super) {
__extends(RequestError, _super);
function RequestError(message, response) {
var _this = _super.call(this, message) || this;
_this.response = response;
return _this;
class RequestError extends typed_error_1.TypedError {
constructor(message, response) {
super(message);
this.response = response;
}
return RequestError;
}(TypedError));
}
exports.RequestError = RequestError;
var GraphQLError = /** @class */ (function (_super) {
__extends(GraphQLError, _super);
function GraphQLError(error, errors) {
var _this = _super.call(this, "GraphQL request failed, with errors:\n" + errors.map(function (e) { return e.message; }).join('\n'), error.response) || this;
_this.errors = errors;
return _this;
class GraphQLError extends RequestError {
constructor(error, errors) {
super(`GraphQL request failed, with errors:\n${errors.map((e) => e.message).join('\n')}`, error.response);
this.errors = errors;
}
return GraphQLError;
}(RequestError));
}
exports.GraphQLError = GraphQLError;
var SUBSCRIBABLE_EVENTS = [
const SUBSCRIBABLE_EVENTS = [
'request',

@@ -111,249 +60,183 @@ 'response',

*/
var MockttpClient = /** @class */ (function (_super) {
__extends(MockttpClient, _super);
function MockttpClient(mockServerOptions) {
if (mockServerOptions === void 0) { mockServerOptions = {}; }
var _this = _super.call(this, _.defaults(mockServerOptions, {
class MockttpClient extends mockttp_1.AbstractMockttp {
constructor(mockServerOptions = {}) {
super(_.defaults(mockServerOptions, {
// Browser clients generally want cors enabled. For other clients, it doesn't hurt.
// TODO: Maybe detect whether we're in a browser in future
cors: true,
standaloneServerUrl: "http://localhost:" + types_1.DEFAULT_STANDALONE_PORT
})) || this;
_this.reset = function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.queryMockServer("mutation Reset {\n reset\n }")];
case 1: return [2 /*return*/, (_a.sent()).data];
standaloneServerUrl: `http://localhost:${types_1.DEFAULT_STANDALONE_PORT}`
}));
this.reset = () => __awaiter(this, void 0, void 0, function* () {
return (yield this.queryMockServer(`mutation Reset {
reset
}`)).data;
});
this.addRule = (rule) => __awaiter(this, void 0, void 0, function* () {
let ruleId = (yield this.queryMockServer(`mutation AddRule($newRule: MockRule!) {
addRule(input: $newRule) {
id
}
}`, {
newRule: mock_rule_1.serializeRuleData(rule, { clientStream: this.mockServerStream })
})).data.addRule.id;
return new mocked_endpoint_client_1.MockedEndpointClient(ruleId, this.getEndpointData(ruleId));
});
this.getEndpointData = (ruleId) => () => __awaiter(this, void 0, void 0, function* () {
let result = yield this.queryMockServer(`query GetEndpointData($id: ID!) {
mockedEndpoint(id: $id) {
seenRequests {
protocol,
method,
url,
path,
hostname,
headers,
body
}
}
}`, {
id: ruleId
});
}); };
_this.addRule = function (rule) { return __awaiter(_this, void 0, void 0, function () {
var ruleId;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.queryMockServer("mutation AddRule($newRule: MockRule!) {\n addRule(input: $newRule) {\n id\n }\n }", {
newRule: mock_rule_1.serializeRuleData(rule, { clientStream: this.mockServerStream })
})];
case 1:
ruleId = (_a.sent()).data.addRule.id;
return [2 /*return*/, new mocked_endpoint_client_1.MockedEndpointClient(ruleId, this.getEndpointData(ruleId))];
}
const mockedEndpoint = result.data.mockedEndpoint;
if (!mockedEndpoint)
return null;
mockedEndpoint.seenRequests.forEach((request) => {
request.body = request_utils_1.buildBodyReader(Buffer.from(request.body, 'base64'), request.headers);
});
}); };
_this.getEndpointData = function (ruleId) { return function () { return __awaiter(_this, void 0, void 0, function () {
var result, mockedEndpoint;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.queryMockServer("query GetEndpointData($id: ID!) {\n mockedEndpoint(id: $id) {\n seenRequests {\n protocol,\n method,\n url,\n path,\n hostname,\n headers,\n body\n }\n }\n }", {
id: ruleId
})];
case 1:
result = _a.sent();
mockedEndpoint = result.data.mockedEndpoint;
if (!mockedEndpoint)
return [2 /*return*/, null];
mockedEndpoint.seenRequests.forEach(function (request) {
request.body = request_utils_1.buildBodyReader(Buffer.from(request.body, 'base64'), request.headers);
});
return [2 /*return*/, mockedEndpoint];
}
});
}); }; };
return mockedEndpoint;
});
// Note that 'defaults' above mutates this, so this includes
// the default parameter values too (and thus the type assertion)
_this.mockServerOptions = mockServerOptions;
return _this;
this.mockServerOptions = mockServerOptions;
}
MockttpClient.prototype.requestFromStandalone = function (path, options) {
return __awaiter(this, void 0, void 0, function () {
var url, response, e_1, body, jsonBody;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
url = "" + this.mockServerOptions.standaloneServerUrl + path;
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, fetch(url, options)];
case 2:
response = _a.sent();
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
if (e_1.code === 'ECONNREFUSED') {
throw new ConnectionError("Failed to connect to standalone server at " + this.mockServerOptions.standaloneServerUrl);
}
else
throw e_1;
return [3 /*break*/, 4];
case 4:
if (!(response.status >= 400)) return [3 /*break*/, 6];
return [4 /*yield*/, response.text()];
case 5:
body = _a.sent();
jsonBody = null;
try {
jsonBody = JSON.parse(body);
}
catch (e) { }
if (jsonBody && jsonBody.error) {
throw new RequestError(jsonBody.error, response);
}
else {
throw new RequestError("Request to " + url + " failed, with status " + response.status + " and response body: " + body, response);
}
return [3 /*break*/, 7];
case 6: return [2 /*return*/, response.json()];
case 7: return [2 /*return*/];
requestFromStandalone(path, options) {
return __awaiter(this, void 0, void 0, function* () {
const url = `${this.mockServerOptions.standaloneServerUrl}${path}`;
let response;
try {
response = yield fetch(url, options);
}
catch (e) {
if (e.code === 'ECONNREFUSED') {
throw new ConnectionError(`Failed to connect to standalone server at ${this.mockServerOptions.standaloneServerUrl}`);
}
});
else
throw e;
}
if (response.status >= 400) {
let body = yield response.text();
let jsonBody = null;
try {
jsonBody = JSON.parse(body);
}
catch (e) { }
if (jsonBody && jsonBody.error) {
throw new RequestError(jsonBody.error, response);
}
else {
throw new RequestError(`Request to ${url} failed, with status ${response.status} and response body: ${body}`, response);
}
}
else {
return response.json();
}
});
};
MockttpClient.prototype.openStreamToMockServer = function (config) {
var standaloneStreamServer = this.mockServerOptions.standaloneServerUrl.replace(/^http/, 'ws');
var stream = connectWebSocketStream(standaloneStreamServer + "/server/" + config.port + "/stream", {
}
openStreamToMockServer(config) {
const standaloneStreamServer = this.mockServerOptions.standaloneServerUrl.replace(/^http/, 'ws');
const stream = connectWebSocketStream(`${standaloneStreamServer}/server/${config.port}/stream`, {
objectMode: true
});
return new Promise(function (resolve, reject) {
stream.once('connect', function () { return resolve(stream); });
return new Promise((resolve, reject) => {
stream.once('connect', () => resolve(stream));
stream.once('error', reject);
});
};
MockttpClient.prototype.requestFromMockServer = function (path, options) {
return __awaiter(this, void 0, void 0, function () {
var url, response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.mockServerConfig)
throw new Error('Not connected to mock server');
url = this.mockServerOptions.standaloneServerUrl + "/server/" + this.mockServerConfig.port + path;
return [4 /*yield*/, fetch(url, options)];
case 1:
response = _a.sent();
if (response.status >= 400) {
throw new RequestError("Request to " + url + " failed, with status " + response.status, response);
}
else {
return [2 /*return*/, response.json()];
}
return [2 /*return*/];
}
});
}
requestFromMockServer(path, options) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.mockServerConfig)
throw new Error('Not connected to mock server');
let url = `${this.mockServerOptions.standaloneServerUrl}/server/${this.mockServerConfig.port}${path}`;
let response = yield fetch(url, options);
if (response.status >= 400) {
throw new RequestError(`Request to ${url} failed, with status ${response.status}`, response);
}
else {
return response.json();
}
});
};
MockttpClient.prototype.queryMockServer = function (query, variables) {
return __awaiter(this, void 0, void 0, function () {
var e_2, graphQLErrors, e2_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 7]);
return [4 /*yield*/, this.requestFromMockServer('/', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify({ query: query, variables: variables })
})];
case 1: return [2 /*return*/, _a.sent()];
case 2:
e_2 = _a.sent();
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, e_2.response.json()];
case 4:
graphQLErrors = (_a.sent()).errors;
throw new GraphQLError(e_2, graphQLErrors);
case 5:
e2_1 = _a.sent();
// If we fail to get a proper JSON graphql error, just throw the
// underlying exception without decoration
throw e_2;
case 6: return [3 /*break*/, 7];
case 7: return [2 /*return*/];
}
queryMockServer(query, variables) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield this.requestFromMockServer('/', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify({ query, variables })
});
}
catch (e) {
try {
let graphQLErrors = (yield e.response.json()).errors;
throw new GraphQLError(e, graphQLErrors);
}
});
catch (e2) {
// If we fail to get a proper JSON graphql error, just throw the
// underlying exception without decoration
throw e;
}
}
});
};
MockttpClient.prototype.start = function (port) {
return __awaiter(this, void 0, void 0, function () {
var path, mockServerConfig, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this.mockServerConfig)
throw new Error('Server is already started');
path = port ? "/start?port=" + port : '/start';
return [4 /*yield*/, this.requestFromStandalone(path, {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify(this.mockServerOptions)
})];
case 1:
mockServerConfig = _b.sent();
// Also open a stream connection, for 2-way communication we might need later.
_a = this;
return [4 /*yield*/, this.openStreamToMockServer(mockServerConfig)];
case 2:
// Also open a stream connection, for 2-way communication we might need later.
_a.mockServerStream = _b.sent();
// We don't persist the config or resolve this promise until everything is set up
this.mockServerConfig = mockServerConfig;
return [2 /*return*/];
}
}
start(port) {
return __awaiter(this, void 0, void 0, function* () {
if (this.mockServerConfig)
throw new Error('Server is already started');
const path = port ? `/start?port=${port}` : '/start';
let mockServerConfig = yield this.requestFromStandalone(path, {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify(this.mockServerOptions)
});
// Also open a stream connection, for 2-way communication we might need later.
this.mockServerStream = yield this.openStreamToMockServer(mockServerConfig);
// We don't persist the config or resolve this promise until everything is set up
this.mockServerConfig = mockServerConfig;
});
};
MockttpClient.prototype.stop = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.mockServerConfig)
return [2 /*return*/];
this.mockServerStream.end();
return [4 /*yield*/, this.requestFromMockServer('/stop', {
method: 'POST'
})];
case 1:
_a.sent();
this.mockServerConfig = this.mockServerStream = undefined;
return [2 /*return*/];
}
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.mockServerConfig)
return;
this.mockServerStream.end();
yield this.requestFromMockServer('/stop', {
method: 'POST'
});
this.mockServerConfig = this.mockServerStream = undefined;
});
};
MockttpClient.prototype.enableDebug = function () {
}
enableDebug() {
throw new Error("Client-side debug info not implemented.");
};
Object.defineProperty(MockttpClient.prototype, "url", {
get: function () {
if (!this.mockServerConfig)
throw new Error('Cannot get url before server is started');
return this.mockServerConfig.mockRoot;
},
enumerable: true,
configurable: true
});
Object.defineProperty(MockttpClient.prototype, "port", {
get: function () {
if (!this.mockServerConfig)
throw new Error('Cannot get port before server is started');
return this.mockServerConfig.port;
},
enumerable: true,
configurable: true
});
MockttpClient.prototype.on = function (event, callback) {
var _this = this;
}
get url() {
if (!this.mockServerConfig)
throw new Error('Cannot get url before server is started');
return this.mockServerConfig.mockRoot;
}
get port() {
if (!this.mockServerConfig)
throw new Error('Cannot get port before server is started');
return this.mockServerConfig.port;
}
on(event, callback) {
if (!_.includes(SUBSCRIBABLE_EVENTS, event))
return Promise.resolve();
var standaloneStreamServer = this.mockServerOptions.standaloneServerUrl.replace(/^http/, 'ws');
var url = standaloneStreamServer + "/server/" + this.port + "/subscription";
var client = new subscriptions_transport_ws_1.SubscriptionClient(url, {}, WebSocket);
var queryResultName = {
const standaloneStreamServer = this.mockServerOptions.standaloneServerUrl.replace(/^http/, 'ws');
const url = `${standaloneStreamServer}/server/${this.port}/subscription`;
const client = new subscriptions_transport_ws_1.SubscriptionClient(url, {}, WebSocket);
const queryResultName = {
request: 'requestReceived',

@@ -363,24 +246,62 @@ response: 'responseCompleted',

}[event];
var query = {
const query = {
request: {
operationName: 'OnRequest',
query: "subscription OnRequest {\n " + queryResultName + " {\n id,\n protocol,\n method,\n url,\n path,\n hostname,\n\n headers,\n body\n }\n }"
query: `subscription OnRequest {
${queryResultName} {
id,
protocol,
method,
url,
path,
hostname,
headers,
body,
timingEvents
}
}`
},
response: {
operationName: 'OnResponse',
query: "subscription OnResponse {\n " + queryResultName + " {\n id,\n statusCode,\n statusMessage,\n headers,\n body\n }\n }"
query: `subscription OnResponse {
${queryResultName} {
id,
statusCode,
statusMessage,
headers,
body,
timingEvents
}
}`
},
abort: {
operationName: 'OnAbort',
query: "subscription OnAbort {\n " + queryResultName + " {\n id,\n protocol,\n method,\n url,\n path,\n hostname,\n\n headers,\n body\n }\n }"
query: `subscription OnAbort {
${queryResultName} {
id,
protocol,
method,
url,
path,
hostname,
headers,
body,
timingEvents
}
}`
},
}[event];
client.request(query).subscribe({
next: function (value) {
next: (value) => {
if (value.data) {
var data = value.data[queryResultName];
const data = value.data[queryResultName];
// TODO: Get a proper graphql client that does all this automatically from the schema itself
if (data.headers) {
// TODO: Get a proper graphql client that does this automatically from the schema itself
data.headers = JSON.parse(data.headers);
}
if (data.timingEvents) {
data.timingEvents = JSON.parse(data.timingEvents);
}
if (data.body) {

@@ -395,12 +316,11 @@ data.body = request_utils_1.buildBodyReader(Buffer.from(data.body, 'base64'), data.headers);

},
error: function (e) { return _this.debug && console.warn('Error in remote subscription:', e); }
error: (e) => this.debug && console.warn('Error in remote subscription:', e)
});
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
client.onConnected(resolve);
client.onDisconnected(reject);
});
};
return MockttpClient;
}(mockttp_1.AbstractMockttp));
}
}
exports.default = MockttpClient;
//# sourceMappingURL=mockttp-client.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var mockttp_client_1 = require("./client/mockttp-client");
function getLocal(options) {
if (options === void 0) { options = {}; }
const mockttp_client_1 = require("./client/mockttp-client");
function getLocal(options = {}) {
return new mockttp_client_1.default(options);
}
exports.getLocal = getLocal;
function getRemote(options) {
if (options === void 0) { options = {}; }
function getRemote(options = {}) {
return new mockttp_client_1.default(options);
}
exports.getRemote = getRemote;
function getStandalone(options) {
if (options === void 0) { options = {}; }
function getStandalone(options = {}) {
throw new Error('Cannot set up a standalone server within a browser');

@@ -17,0 +14,0 @@ }

@@ -6,5 +6,5 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
var mockttp_server_1 = require("./server/mockttp-server");
var mockttp_client_1 = require("./client/mockttp-client");
var mockttp_standalone_1 = require("./standalone/mockttp-standalone");
const mockttp_server_1 = require("./server/mockttp-server");
const mockttp_client_1 = require("./client/mockttp-client");
const mockttp_standalone_1 = require("./standalone/mockttp-standalone");
var tls_1 = require("./util/tls");

@@ -25,4 +25,3 @@ exports.generateCACertificate = tls_1.generateCACertificate;

*/
function getLocal(options) {
if (options === void 0) { options = {}; }
function getLocal(options = {}) {
return new mockttp_server_1.default(options);

@@ -37,4 +36,3 @@ }

*/
function getRemote(options) {
if (options === void 0) { options = {}; }
function getRemote(options = {}) {
return new mockttp_client_1.default(options);

@@ -54,4 +52,3 @@ }

*/
function getStandalone(options) {
if (options === void 0) { options = {}; }
function getStandalone(options = {}) {
return new mockttp_standalone_1.MockttpStandalone(options);

@@ -58,0 +55,0 @@ }

"use strict";
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -10,59 +6,55 @@ /**

*/
var common_tags_1 = require("common-tags");
var mock_rule_builder_1 = require("./rules/mock-rule-builder");
var types_1 = require("./types");
const common_tags_1 = require("common-tags");
const mock_rule_builder_1 = require("./rules/mock-rule-builder");
const types_1 = require("./types");
/**
* @hidden
*/
var AbstractMockttp = /** @class */ (function () {
function AbstractMockttp(options) {
class AbstractMockttp {
constructor(options) {
this.debug = options.debug || false;
this.cors = options.cors || false;
}
Object.defineProperty(AbstractMockttp.prototype, "proxyEnv", {
get: function () {
return {
HTTP_PROXY: this.url,
HTTPS_PROXY: this.url
};
},
enumerable: true,
configurable: true
});
AbstractMockttp.prototype.urlFor = function (path) {
get proxyEnv() {
return {
HTTP_PROXY: this.url,
HTTPS_PROXY: this.url
};
}
urlFor(path) {
return this.url + path;
};
AbstractMockttp.prototype.anyRequest = function () {
}
anyRequest() {
return new mock_rule_builder_1.default(this.addRule);
};
AbstractMockttp.prototype.get = function (url) {
}
get(url) {
return new mock_rule_builder_1.default(types_1.Method.GET, url, this.addRule);
};
AbstractMockttp.prototype.post = function (url) {
}
post(url) {
return new mock_rule_builder_1.default(types_1.Method.POST, url, this.addRule);
};
AbstractMockttp.prototype.put = function (url) {
}
put(url) {
return new mock_rule_builder_1.default(types_1.Method.PUT, url, this.addRule);
};
AbstractMockttp.prototype.delete = function (url) {
}
delete(url) {
return new mock_rule_builder_1.default(types_1.Method.DELETE, url, this.addRule);
};
AbstractMockttp.prototype.patch = function (url) {
}
patch(url) {
return new mock_rule_builder_1.default(types_1.Method.PATCH, url, this.addRule);
};
AbstractMockttp.prototype.head = function (url) {
}
head(url) {
return new mock_rule_builder_1.default(types_1.Method.HEAD, url, this.addRule);
};
AbstractMockttp.prototype.options = function (url) {
}
options(url) {
if (this.cors) {
throw new Error(common_tags_1.stripIndent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n Cannot mock OPTIONS requests with CORS enabled.\n\n You can disable CORS by passing { cors: false } to getLocal/getRemote, but this may cause issues ", "connecting to your mock server from browsers, unless you mock all required OPTIONS preflight ", "responses by hand.\n "], ["\n Cannot mock OPTIONS requests with CORS enabled.\n\n You can disable CORS by passing { cors: false } to getLocal/getRemote, but this may cause issues ",
"connecting to your mock server from browsers, unless you mock all required OPTIONS preflight ",
"responses by hand.\n "])), '', ''));
throw new Error(common_tags_1.stripIndent `
Cannot mock OPTIONS requests with CORS enabled.
You can disable CORS by passing { cors: false } to getLocal/getRemote, but this may cause issues ${''}connecting to your mock server from browsers, unless you mock all required OPTIONS preflight ${''}responses by hand.
`);
}
return new mock_rule_builder_1.default(types_1.Method.OPTIONS, url, this.addRule);
};
return AbstractMockttp;
}());
}
}
exports.AbstractMockttp = AbstractMockttp;
var templateObject_1;
//# sourceMappingURL=mockttp.js.map

@@ -5,93 +5,65 @@ "use strict";

*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var serialization_1 = require("../util/serialization");
var AlwaysData = /** @class */ (function (_super) {
__extends(AlwaysData, _super);
function AlwaysData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'always';
return _this;
const serialization_1 = require("../util/serialization");
class AlwaysData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'always';
}
AlwaysData.prototype.buildCompletionChecker = function () {
return withExplanation(function () { return false; }, function () {
buildCompletionChecker() {
return withExplanation(() => false, function () {
return explainUntil(this.requests, Infinity, 'always');
});
};
return AlwaysData;
}(serialization_1.Serializable));
}
}
exports.AlwaysData = AlwaysData;
var OnceData = /** @class */ (function (_super) {
__extends(OnceData, _super);
function OnceData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'once';
return _this;
class OnceData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'once';
}
OnceData.prototype.buildCompletionChecker = function () {
buildCompletionChecker() {
return withExplanation(checkTimes(1), function () {
return explainUntil(this.requests, 1, 'once');
});
};
return OnceData;
}(serialization_1.Serializable));
}
}
exports.OnceData = OnceData;
var TwiceData = /** @class */ (function (_super) {
__extends(TwiceData, _super);
function TwiceData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'twice';
return _this;
class TwiceData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'twice';
}
TwiceData.prototype.buildCompletionChecker = function () {
buildCompletionChecker() {
return withExplanation(checkTimes(2), function () {
return explainUntil(this.requests, 2, 'twice');
});
};
return TwiceData;
}(serialization_1.Serializable));
}
}
exports.TwiceData = TwiceData;
var ThriceData = /** @class */ (function (_super) {
__extends(ThriceData, _super);
function ThriceData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'thrice';
return _this;
class ThriceData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'thrice';
}
ThriceData.prototype.buildCompletionChecker = function () {
buildCompletionChecker() {
return withExplanation(checkTimes(3), function () {
return explainUntil(this.requests, 3, 'thrice');
});
};
return ThriceData;
}(serialization_1.Serializable));
}
}
exports.ThriceData = ThriceData;
var TimesData = /** @class */ (function (_super) {
__extends(TimesData, _super);
function TimesData(count) {
var _this = _super.call(this) || this;
_this.count = count;
_this.type = 'times';
return _this;
class TimesData extends serialization_1.Serializable {
constructor(count) {
super();
this.count = count;
this.type = 'times';
}
TimesData.prototype.buildCompletionChecker = function () {
var count = this.count;
buildCompletionChecker() {
let count = this.count;
return withExplanation(checkTimes(count), function () {
return explainUntil(this.requests, count, count + " times");
return explainUntil(this.requests, count, `${count} times`);
});
};
return TimesData;
}(serialization_1.Serializable));
}
}
exports.TimesData = TimesData;

@@ -120,4 +92,4 @@ exports.CompletionCheckerDataLookup = {

function explainUntil(requests, n, name) {
var seen = requests.length;
return name + " " + (seen < n ? "(seen " + seen + ")" : "(done)");
const seen = requests.length;
return name + " " + (seen < n ? `(seen ${seen})` : "(done)");
}

@@ -124,0 +96,0 @@ function withExplanation(functionToExplain, explainer) {

@@ -28,5 +28,3 @@ /**

body?: string;
headers?: {
[key: string]: string;
};
headers?: Headers;
}

@@ -33,0 +31,0 @@ export interface SerializedStreamBackedHandlerData {

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

*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -31,184 +14,117 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var url = require("url");
var os = require("os");
var http = require("http");
var https = require("https");
var uuid = require("uuid/v4");
var base64_arraybuffer_1 = require("base64-arraybuffer");
var stream_1 = require("stream");
var common_tags_1 = require("common-tags");
var request_utils_1 = require("../server/request-utils");
var serialization_1 = require("../util/serialization");
const _ = require("lodash");
const url = require("url");
const os = require("os");
const http = require("http");
const https = require("https");
const uuid = require("uuid/v4");
const base64_arraybuffer_1 = require("base64-arraybuffer");
const stream_1 = require("stream");
const common_tags_1 = require("common-tags");
const request_utils_1 = require("../server/request-utils");
const serialization_1 = require("../util/serialization");
function isSerializedBuffer(obj) {
return obj && obj.type === 'Buffer' && !!obj.data;
}
var SimpleHandlerData = /** @class */ (function (_super) {
__extends(SimpleHandlerData, _super);
function SimpleHandlerData(status, data, headers) {
var _this = _super.call(this) || this;
_this.status = status;
_this.data = data;
_this.headers = headers;
_this.type = 'simple';
return _this;
class SimpleHandlerData extends serialization_1.Serializable {
constructor(status, data, headers) {
super();
this.status = status;
this.data = data;
this.headers = headers;
this.type = 'simple';
}
SimpleHandlerData.prototype.buildHandler = function () {
var _this = this;
return _.assign(function (_request, response) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (this.headers) {
request_utils_1.setHeaders(response, this.headers);
}
response.writeHead(this.status);
if (isSerializedBuffer(this.data)) {
this.data = new Buffer(this.data);
}
response.end(this.data || "");
return [2 /*return*/];
});
}); }, { explain: function () {
return "respond with status " + _this.status +
(_this.headers ? ", headers " + JSON.stringify(_this.headers) : "") +
(_this.data ? " and body \"" + _this.data + "\"" : "");
buildHandler() {
return _.assign((_request, response) => __awaiter(this, void 0, void 0, function* () {
if (this.headers) {
request_utils_1.setHeaders(response, this.headers);
}
response.writeHead(this.status);
if (isSerializedBuffer(this.data)) {
this.data = new Buffer(this.data);
}
response.end(this.data || "");
}), { explain: () => `respond with status ${this.status}` +
(this.headers ? `, headers ${JSON.stringify(this.headers)}` : "") +
(this.data ? ` and body "${this.data}"` : "")
});
};
return SimpleHandlerData;
}(serialization_1.Serializable));
}
}
exports.SimpleHandlerData = SimpleHandlerData;
;
var CallbackHandlerData = /** @class */ (function (_super) {
__extends(CallbackHandlerData, _super);
function CallbackHandlerData(callback) {
var _this = _super.call(this) || this;
_this.callback = callback;
_this.type = 'callback';
return _this;
class CallbackHandlerData extends serialization_1.Serializable {
constructor(callback) {
super();
this.callback = callback;
this.type = 'callback';
}
CallbackHandlerData.prototype.buildHandler = function () {
var _this = this;
return _.assign(function (request, response) { return __awaiter(_this, void 0, void 0, function () {
var req, outResponse, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, request_utils_1.waitForCompletedRequest(request)];
case 1:
req = _a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, this.callback(req)];
case 3:
outResponse = _a.sent();
return [3 /*break*/, 5];
case 4:
error_1 = _a.sent();
response.writeHead(500, 'Callback handler threw an exception');
response.end(error_1.toString());
return [2 /*return*/];
case 5:
if (outResponse.json !== undefined) {
outResponse.headers = _.assign(outResponse.headers || {}, { 'Content-Type': 'application/json' });
outResponse.body = JSON.stringify(outResponse.json);
delete outResponse.json;
}
if (outResponse.headers) {
request_utils_1.setHeaders(response, outResponse.headers);
}
response.writeHead(outResponse.status || 200);
response.end(outResponse.body || "");
return [2 /*return*/];
}
});
}); }, { explain: function () {
return 'respond using provided callback' +
(_this.callback.name ? " (" + _this.callback.name + ")" : '');
buildHandler() {
return _.assign((request, response) => __awaiter(this, void 0, void 0, function* () {
let req = yield request_utils_1.waitForCompletedRequest(request);
let outResponse;
try {
outResponse = yield this.callback(req);
}
catch (error) {
response.writeHead(500, 'Callback handler threw an exception');
response.end(error.toString());
return;
}
if (outResponse.json !== undefined) {
outResponse.headers = _.assign(outResponse.headers || {}, { 'Content-Type': 'application/json' });
outResponse.body = JSON.stringify(outResponse.json);
delete outResponse.json;
}
if (outResponse.headers) {
request_utils_1.setHeaders(response, outResponse.headers);
}
response.writeHead(outResponse.status || 200);
response.end(outResponse.body || "");
}), { explain: () => 'respond using provided callback' +
(this.callback.name ? ` (${this.callback.name})` : '')
});
};
CallbackHandlerData.prototype.serialize = function (options) {
var _this = this;
}
serialize(options) {
if (!options || !options.clientStream) {
throw new Error('Client-side callback handlers require a streaming client connection.');
}
var clientStream = options.clientStream;
const { clientStream } = options;
// The topic id is used to identify the client-side source rule, so when a message comes
// across we know which callback needs to be run.
var topicId = uuid();
const topicId = uuid();
// When we receive a request from the server: check it's us, call the callback, stream back the result.
clientStream.on('data', function (streamMsg) { return __awaiter(_this, void 0, void 0, function () {
var serverRequest, requestId, requestTopicId, result, error_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
serverRequest = JSON.parse(streamMsg.toString());
requestId = serverRequest.requestId, requestTopicId = serverRequest.topicId;
// This message isn't meant for us.
if (topicId !== requestTopicId)
return [2 /*return*/];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.callback.apply(null, serverRequest.args)];
case 2:
result = _a.sent();
clientStream.write(JSON.stringify({
topicId: topicId,
requestId: requestId,
result: result
}));
return [3 /*break*/, 4];
case 3:
error_2 = _a.sent();
clientStream.write(JSON.stringify({
topicId: topicId,
requestId: requestId,
error: error_2
}));
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
return { type: this.type, topicId: topicId, name: this.callback.name };
};
CallbackHandlerData.deserialize = function (_a, options) {
var topicId = _a.topicId, name = _a.name;
clientStream.on('data', (streamMsg) => __awaiter(this, void 0, void 0, function* () {
let serverRequest = JSON.parse(streamMsg.toString());
let { requestId, topicId: requestTopicId } = serverRequest;
// This message isn't meant for us.
if (topicId !== requestTopicId)
return;
try {
let result = yield this.callback.apply(null, serverRequest.args);
clientStream.write(JSON.stringify({
topicId,
requestId,
result
}));
}
catch (error) {
clientStream.write(JSON.stringify({
topicId,
requestId,
error
}));
}
}));
return { type: this.type, topicId, name: this.callback.name };
}
static deserialize({ topicId, name }, options) {
if (!options || !options.clientStream) {
throw new Error('Client-side callback handlers require a streaming client connection.');
}
var clientStream = options.clientStream;
var outstandingRequests = {};
var responseListener = function (streamMsg) {
var clientResponse = JSON.parse(streamMsg.toString());
var requestId = clientResponse.requestId;
const { clientStream } = options;
let outstandingRequests = {};
const responseListener = (streamMsg) => {
let clientResponse = JSON.parse(streamMsg.toString());
let { requestId } = clientResponse;
if (outstandingRequests[requestId]) {

@@ -221,6 +137,6 @@ outstandingRequests[requestId](clientResponse.error, clientResponse.result);

clientStream.on('data', responseListener);
var rpcCallback = function (request) {
return new Promise(function (resolve, reject) {
var requestId = uuid();
outstandingRequests[requestId] = function (error, result) {
const rpcCallback = (request) => {
return new Promise((resolve, reject) => {
let requestId = uuid();
outstandingRequests[requestId] = (error, result) => {
if (error) {

@@ -235,4 +151,4 @@ reject(error);

clientStream.write(JSON.stringify({
topicId: topicId,
requestId: requestId,
topicId,
requestId,
args: [request]

@@ -247,53 +163,50 @@ }));

return new CallbackHandlerData(rpcCallback);
};
return CallbackHandlerData;
}(serialization_1.Serializable));
}
}
exports.CallbackHandlerData = CallbackHandlerData;
;
var StreamHandlerData = /** @class */ (function (_super) {
__extends(StreamHandlerData, _super);
function StreamHandlerData(status, stream, headers) {
var _this = _super.call(this) || this;
_this.status = status;
_this.stream = stream;
_this.headers = headers;
_this.type = 'stream';
return _this;
class StreamHandlerData extends serialization_1.Serializable {
constructor(status, stream, headers) {
super();
this.status = status;
this.stream = stream;
this.headers = headers;
this.type = 'stream';
}
StreamHandlerData.prototype.buildHandler = function () {
var _this = this;
return _.assign(function (_request, response) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (!this.stream.done) {
if (this.headers) {
request_utils_1.setHeaders(response, this.headers);
}
response.writeHead(this.status);
this.stream.pipe(response);
this.stream.done = true;
buildHandler() {
return _.assign((_request, response) => __awaiter(this, void 0, void 0, function* () {
if (!this.stream.done) {
if (this.headers) {
request_utils_1.setHeaders(response, this.headers);
}
else {
throw new Error(common_tags_1.stripIndent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n Stream request handler called more than once - this is not supported.\n\n Streams can typically only be read once, so all subsequent requests would be empty.\n To mock repeated stream requests, call 'thenStream' repeatedly with multiple streams.\n\n (Have a better way to handle this? Open an issue at ", ")\n "], ["\n Stream request handler called more than once - this is not supported.\n\n Streams can typically only be read once, so all subsequent requests would be empty.\n To mock repeated stream requests, call 'thenStream' repeatedly with multiple streams.\n\n (Have a better way to handle this? Open an issue at ", ")\n "])), require('../../package.json').bugs.url));
}
return [2 /*return*/];
});
}); }, { explain: function () {
return "respond with status " + _this.status +
(_this.headers ? ", headers " + JSON.stringify(_this.headers) + "," : "") +
' and a stream of response data';
response.writeHead(this.status);
this.stream.pipe(response);
this.stream.done = true;
}
else {
throw new Error(common_tags_1.stripIndent `
Stream request handler called more than once - this is not supported.
Streams can typically only be read once, so all subsequent requests would be empty.
To mock repeated stream requests, call 'thenStream' repeatedly with multiple streams.
(Have a better way to handle this? Open an issue at ${require('../../package.json').bugs.url})
`);
}
}), { explain: () => `respond with status ${this.status}` +
(this.headers ? `, headers ${JSON.stringify(this.headers)},` : "") +
' and a stream of response data'
});
};
StreamHandlerData.prototype.serialize = function (options) {
var _this = this;
}
serialize(options) {
if (!options || !options.clientStream) {
throw new Error('Client-side stream handlers require a streaming client connection.');
}
var clientStream = options.clientStream;
const { clientStream } = options;
// The topic id is used to identify the client-side source rule, so when a message comes
// across we know which handler should handle it.
var topicId = uuid();
var serializationStream = new stream_1.Transform({
const topicId = uuid();
const serializationStream = new stream_1.Transform({
transform: function (chunk, encoding, callback) {
var serializedEventData = _.isString(chunk) ? { type: 'string', value: chunk } :
let serializedEventData = _.isString(chunk) ? { type: 'string', value: chunk } :
_.isBuffer(chunk) ? { type: 'buffer', value: chunk.toString('base64') } :

@@ -303,6 +216,6 @@ (_.isArrayBuffer(chunk) || _.isTypedArray(chunk)) ? { type: 'arraybuffer', value: base64_arraybuffer_1.encode(chunk) } :

if (!serializedEventData) {
callback(new Error("Can't serialize streamed value: " + chunk.toString() + ". Streaming must output strings, buffers or array buffers"));
callback(new Error(`Can't serialize streamed value: ${chunk.toString()}. Streaming must output strings, buffers or array buffers`));
}
callback(undefined, JSON.stringify({
topicId: topicId,
topicId,
event: 'data',

@@ -314,3 +227,3 @@ content: serializedEventData

this.push(JSON.stringify({
topicId: topicId,
topicId,
event: 'end'

@@ -324,26 +237,26 @@ }));

// but we haven't, so we can't.
var startStreamListener = function (streamMsg) {
var serverRequest = JSON.parse(streamMsg.toString());
var requestTopicId = serverRequest.topicId;
const startStreamListener = (streamMsg) => {
let serverRequest = JSON.parse(streamMsg.toString());
let { topicId: requestTopicId } = serverRequest;
// This message isn't meant for us.
if (topicId !== requestTopicId)
return;
_this.stream.pipe(serializationStream).pipe(clientStream, { end: false });
this.stream.pipe(serializationStream).pipe(clientStream, { end: false });
clientStream.removeListener('data', startStreamListener);
};
clientStream.on('data', startStreamListener);
return { type: this.type, topicId: topicId, status: this.status, headers: this.headers };
};
StreamHandlerData.deserialize = function (handlerData, options) {
return { type: this.type, topicId, status: this.status, headers: this.headers };
}
static deserialize(handlerData, options) {
if (!options || !options.clientStream) {
throw new Error('Client-side stream handlers require a streaming client connection.');
}
var clientStream = options.clientStream;
var handlerStream = new stream_1.Transform({
const { clientStream } = options;
const handlerStream = new stream_1.Transform({
transform: function (chunk, encoding, callback) {
var clientMessage = JSON.parse(chunk.toString());
var topicId = clientMessage.topicId, event = clientMessage.event, content = clientMessage.content;
let clientMessage = JSON.parse(chunk.toString());
const { topicId, event, content } = clientMessage;
if (handlerData.topicId !== topicId)
return;
var deserializedEventData = content && (content.type === 'string' ? content.value :
let deserializedEventData = content && (content.type === 'string' ? content.value :
content.type === 'buffer' ? Buffer.from(content.value, 'base64') :

@@ -362,3 +275,3 @@ content.type === 'arraybuffer' ? Buffer.from(base64_arraybuffer_1.decode(content.value)) :

// When we get piped (i.e. to a live request), ping upstream to start streaming
handlerStream.once('resume', function () {
handlerStream.once('resume', () => {
clientStream.pipe(handlerStream);

@@ -370,141 +283,121 @@ clientStream.write(JSON.stringify({

return new StreamHandlerData(handlerData.status, handlerStream, handlerData.headers);
};
return StreamHandlerData;
}(serialization_1.Serializable));
}
}
exports.StreamHandlerData = StreamHandlerData;
var PassThroughHandlerData = /** @class */ (function (_super) {
__extends(PassThroughHandlerData, _super);
function PassThroughHandlerData(options) {
if (options === void 0) { options = {}; }
var _this = _super.call(this) || this;
_this.type = 'passthrough';
_this.ignoreHostCertificateErrors = [];
_this.forwardToLocation = options.forwardToLocation;
_this.ignoreHostCertificateErrors = options.ignoreHostCertificateErrors || [];
return _this;
class PassThroughHandlerData extends serialization_1.Serializable {
constructor(options = {}) {
super();
this.type = 'passthrough';
this.ignoreHostCertificateErrors = [];
this.forwardToLocation = options.forwardToLocation;
this.ignoreHostCertificateErrors = options.ignoreHostCertificateErrors || [];
}
PassThroughHandlerData.prototype.buildHandler = function () {
var _this = this;
return _.assign(function (clientReq, clientRes) { return __awaiter(_this, void 0, void 0, function () {
var _a, _b, method, originalUrl, headers, _c, protocol, hostname, port, path, socket, remoteAddress, remotePort, hostHeader, checkServerCertificate, makeRequest, outgoingPort;
return __generator(this, function (_d) {
method = clientReq.method, originalUrl = clientReq.originalUrl, headers = clientReq.headers;
_c = url.parse(originalUrl), protocol = _c.protocol, hostname = _c.hostname, port = _c.port, path = _c.path;
if (this.forwardToLocation) {
(_a = url.parse(this.forwardToLocation), protocol = _a.protocol, hostname = _a.hostname, port = _a.port);
}
socket = clientReq.socket;
remoteAddress = socket.remoteAddress.replace(/^::ffff:/, '');
remotePort = port ? Number.parseInt(port) : socket.remotePort;
if (isRequestLoop(remoteAddress, remotePort)) {
throw new Error(common_tags_1.stripIndent(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n Passthrough loop detected. This probably means you're sending a request directly ", "to a passthrough endpoint, which is forwarding it to the target URL, which is a ", "passthrough endpoint, which is forwarding it to the target URL, which is a ", "passthrough endpoint...\n\n You should either explicitly mock a response for this URL (", "), or use ", "the server as a proxy, instead of making requests to it directly\n "], ["\n Passthrough loop detected. This probably means you're sending a request directly ",
"to a passthrough endpoint, which is forwarding it to the target URL, which is a ",
"passthrough endpoint, which is forwarding it to the target URL, which is a ",
"passthrough endpoint...\n\n You should either explicitly mock a response for this URL (", "), or use ",
"the server as a proxy, instead of making requests to it directly\n "])), '', '', '', originalUrl, ''));
}
if (!hostname) {
hostHeader = headers.host;
_b = hostHeader.split(':'), hostname = _b[0], port = _b[1];
protocol = clientReq.protocol + ':';
}
checkServerCertificate = !_.includes(this.ignoreHostCertificateErrors, hostname);
makeRequest = protocol === 'https:' ? https.request : http.request;
outgoingPort = null;
return [2 /*return*/, new Promise(function (resolve, reject) {
var serverReq = makeRequest({
protocol: protocol,
method: method,
hostname: hostname,
port: port,
path: path,
headers: headers,
rejectUnauthorized: checkServerCertificate
}, function (serverRes) {
Object.keys(serverRes.headers).forEach(function (header) {
try {
clientRes.setHeader(header, serverRes.headers[header]);
}
catch (e) {
// A surprising number of real sites have slightly invalid headers (e.g. extra spaces)
// If we hit any, just drop that header and print a message.
console.log("Error setting header on passthrough response: " + e.message);
}
});
clientRes.status(serverRes.statusCode);
serverRes.pipe(clientRes);
serverRes.once('end', resolve);
serverRes.once('error', reject);
});
serverReq.once('socket', function (socket) {
// We want the local port - it's not available until we actually connect
socket.once('connect', function () {
// Add this port to our list of active ports
outgoingPort = socket.localPort;
currentlyForwardingPorts.push(outgoingPort);
});
socket.once('close', function () {
// Remove this port from our list of active ports
currentlyForwardingPorts = currentlyForwardingPorts.filter(function (port) { return port !== outgoingPort; });
outgoingPort = null;
});
});
clientReq.body.rawStream.pipe(serverReq);
clientReq.body.rawStream.once('error', function () { return serverReq.abort(); });
clientRes.once('close', function () { return serverReq.abort(); });
serverReq.once('error', function (e) {
if (serverReq.aborted)
return;
e.statusCode = 502;
e.statusMessage = 'Error communicating with upstream server';
reject(e);
});
})];
buildHandler() {
return _.assign((clientReq, clientRes) => __awaiter(this, void 0, void 0, function* () {
const { method, originalUrl, headers } = clientReq;
let { protocol, hostname, port, path } = url.parse(originalUrl);
if (this.forwardToLocation) {
({ protocol, hostname, port } = url.parse(this.forwardToLocation));
}
const socket = clientReq.socket;
// If it's ipv4 masquerading as v6, strip back to ipv4
const remoteAddress = socket.remoteAddress.replace(/^::ffff:/, '');
const remotePort = port ? Number.parseInt(port) : socket.remotePort;
if (isRequestLoop(remoteAddress, remotePort)) {
throw new Error(common_tags_1.stripIndent `
Passthrough loop detected. This probably means you're sending a request directly ${''}to a passthrough endpoint, which is forwarding it to the target URL, which is a ${''}passthrough endpoint, which is forwarding it to the target URL, which is a ${''}passthrough endpoint...
You should either explicitly mock a response for this URL (${originalUrl}), or use ${''}the server as a proxy, instead of making requests to it directly
`);
}
if (!hostname) {
const hostHeader = headers.host;
[hostname, port] = hostHeader.split(':');
protocol = clientReq.protocol + ':';
}
const checkServerCertificate = !_.includes(this.ignoreHostCertificateErrors, hostname);
let makeRequest = protocol === 'https:' ? https.request : http.request;
let outgoingPort = null;
return new Promise((resolve, reject) => {
let serverReq = makeRequest({
protocol,
method,
hostname,
port,
path,
headers,
rejectUnauthorized: checkServerCertificate
}, (serverRes) => {
Object.keys(serverRes.headers).forEach((header) => {
try {
clientRes.setHeader(header, serverRes.headers[header]);
}
catch (e) {
// A surprising number of real sites have slightly invalid headers (e.g. extra spaces)
// If we hit any, just drop that header and print a message.
console.log(`Error setting header on passthrough response: ${e.message}`);
}
});
clientRes.status(serverRes.statusCode);
serverRes.pipe(clientRes);
serverRes.once('end', resolve);
serverRes.once('error', reject);
});
serverReq.once('socket', (socket) => {
// We want the local port - it's not available until we actually connect
socket.once('connect', () => {
// Add this port to our list of active ports
outgoingPort = socket.localPort;
currentlyForwardingPorts.push(outgoingPort);
});
socket.once('close', () => {
// Remove this port from our list of active ports
currentlyForwardingPorts = currentlyForwardingPorts.filter((port) => port !== outgoingPort);
outgoingPort = null;
});
});
clientReq.body.rawStream.pipe(serverReq);
clientReq.body.rawStream.once('error', () => serverReq.abort());
clientRes.once('close', () => serverReq.abort());
serverReq.once('error', (e) => {
if (serverReq.aborted)
return;
e.statusCode = 502;
e.statusMessage = 'Error communicating with upstream server';
reject(e);
});
});
}); }, { explain: function () { return _this.forwardToLocation ? 'forward the request to the specified url' : 'pass the request through to the real server'; } });
};
return PassThroughHandlerData;
}(serialization_1.Serializable));
}), { explain: () => this.forwardToLocation ? 'forward the request to the specified url' : 'pass the request through to the real server' });
}
}
exports.PassThroughHandlerData = PassThroughHandlerData;
var CloseConnectionHandlerData = /** @class */ (function (_super) {
__extends(CloseConnectionHandlerData, _super);
function CloseConnectionHandlerData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'close-connection';
return _this;
class CloseConnectionHandlerData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'close-connection';
}
CloseConnectionHandlerData.prototype.buildHandler = function () {
buildHandler() {
return _.assign(function (request, response) {
return __awaiter(this, void 0, void 0, function () {
var socket;
return __generator(this, function (_a) {
socket = request.socket;
socket.end();
return [2 /*return*/];
});
return __awaiter(this, void 0, void 0, function* () {
const socket = request.socket;
socket.end();
});
}, { explain: function () { return 'close the connection'; } });
};
return CloseConnectionHandlerData;
}(serialization_1.Serializable));
}, { explain: () => 'close the connection' });
}
}
exports.CloseConnectionHandlerData = CloseConnectionHandlerData;
var TimeoutHandlerData = /** @class */ (function (_super) {
__extends(TimeoutHandlerData, _super);
function TimeoutHandlerData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'timeout';
return _this;
class TimeoutHandlerData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'timeout';
}
TimeoutHandlerData.prototype.buildHandler = function () {
buildHandler() {
return _.assign(function (request, response) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
// Do nothing, leaving the socket open, but never sending a response.
return [2 /*return*/];
});
return __awaiter(this, void 0, void 0, function* () {
// Do nothing, leaving the socket open, but never sending a response.
return;
});
}, { explain: function () { return 'timeout (never respond)'; } });
};
return TimeoutHandlerData;
}(serialization_1.Serializable));
}, { explain: () => 'timeout (never respond)' });
}
}
exports.TimeoutHandlerData = TimeoutHandlerData;

@@ -523,12 +416,11 @@ exports.HandlerDataLookup = {

// we might want to listen for events/periodically update this list some time in future.
var localAddresses = _(os.networkInterfaces())
.map(function (interfaceAddresses) { return interfaceAddresses.map(function (addressDetails) { return addressDetails.address; }); })
const localAddresses = _(os.networkInterfaces())
.map((interfaceAddresses) => interfaceAddresses.map((addressDetails) => addressDetails.address))
.flatten()
.valueOf();
// Track currently live ports for forwarded connections, so we can spot requests from them later.
var currentlyForwardingPorts = [];
var isRequestLoop = function (remoteAddress, remotePort) {
// If the request is local, and from a port we're sending a request on right now, we have a loop
return _.includes(localAddresses, remoteAddress) && _.includes(currentlyForwardingPorts, remotePort);
};
let currentlyForwardingPorts = [];
const isRequestLoop = (remoteAddress, remotePort) =>
// If the request is local, and from a port we're sending a request on right now, we have a loop
_.includes(localAddresses, remoteAddress) && _.includes(currentlyForwardingPorts, remotePort);
function buildHandler(handlerData) {

@@ -538,3 +430,2 @@ return handlerData.buildHandler();

exports.buildHandler = buildHandler;
var templateObject_1, templateObject_2;
//# sourceMappingURL=handlers.js.map

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

*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -31,221 +14,134 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var url = require("url");
var types_1 = require("../types");
var serialization_1 = require("../util/serialization");
var normalize_url_1 = require("../util/normalize-url");
var common_tags_1 = require("common-tags");
var WildcardMatcherData = /** @class */ (function (_super) {
__extends(WildcardMatcherData, _super);
function WildcardMatcherData() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = 'wildcard';
return _this;
const _ = require("lodash");
const url = require("url");
const types_1 = require("../types");
const serialization_1 = require("../util/serialization");
const normalize_url_1 = require("../util/normalize-url");
const common_tags_1 = require("common-tags");
class WildcardMatcherData extends serialization_1.Serializable {
constructor() {
super(...arguments);
this.type = 'wildcard';
}
WildcardMatcherData.prototype.buildMatcher = function () {
return _.assign(function () { return true; }, { explain: function () { return 'for anything'; } });
};
return WildcardMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
return _.assign(() => true, { explain: () => 'for anything' });
}
}
exports.WildcardMatcherData = WildcardMatcherData;
var MethodMatcherData = /** @class */ (function (_super) {
__extends(MethodMatcherData, _super);
function MethodMatcherData(method) {
var _this = _super.call(this) || this;
_this.method = method;
_this.type = 'method';
return _this;
class MethodMatcherData extends serialization_1.Serializable {
constructor(method) {
super();
this.method = method;
this.type = 'method';
}
MethodMatcherData.prototype.buildMatcher = function () {
var methodName = types_1.Method[this.method];
return _.assign(function (request) {
return request.method === methodName;
}, { explain: function () { return "making " + methodName + "s"; } });
};
return MethodMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
let methodName = types_1.Method[this.method];
return _.assign((request) => request.method === methodName, { explain: () => `making ${methodName}s` });
}
}
exports.MethodMatcherData = MethodMatcherData;
var SimplePathMatcherData = /** @class */ (function (_super) {
__extends(SimplePathMatcherData, _super);
function SimplePathMatcherData(path) {
var _this = _super.call(this) || this;
_this.path = path;
_this.type = 'simple-path';
var _a = url.parse(_this.path, true), search = _a.search, query = _a.query;
class SimplePathMatcherData extends serialization_1.Serializable {
constructor(path) {
super();
this.path = path;
this.type = 'simple-path';
let { search, query } = url.parse(this.path, true);
if (search) {
throw new Error(common_tags_1.stripIndent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n Tried to match a path that contained a query (", "). ", "To match query parameters, add .withQuery(", ") instead.\n "], ["\n Tried to match a path that contained a query (", "). ",
"To match query parameters, add .withQuery(", ") instead.\n "])), search, '', JSON.stringify(query)));
throw new Error(common_tags_1.stripIndent `
Tried to match a path that contained a query (${search}). ${''}To match query parameters, add .withQuery(${JSON.stringify(query)}) instead.
`);
}
return _this;
}
SimplePathMatcherData.prototype.buildMatcher = function () {
var _this = this;
var url = normalize_url_1.default(this.path);
return _.assign(function (request) {
return normalize_url_1.default(request.url) === url;
}, { explain: function () { return "for " + _this.path; } });
};
return SimplePathMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
let url = normalize_url_1.default(this.path);
return _.assign((request) => normalize_url_1.default(request.url) === url, { explain: () => `for ${this.path}` });
}
}
exports.SimplePathMatcherData = SimplePathMatcherData;
var RegexPathMatcherData = /** @class */ (function (_super) {
__extends(RegexPathMatcherData, _super);
function RegexPathMatcherData(regex) {
var _this = _super.call(this) || this;
_this.type = 'regex-path';
_this.regexString = regex.source;
return _this;
class RegexPathMatcherData extends serialization_1.Serializable {
constructor(regex) {
super();
this.type = 'regex-path';
this.regexString = regex.source;
}
RegexPathMatcherData.prototype.buildMatcher = function () {
var _this = this;
var url = new RegExp(this.regexString);
return _.assign(function (request) {
return url.test(normalize_url_1.default(request.url));
}, { explain: function () { return "for paths matching /" + _this.regexString + "/"; } });
};
return RegexPathMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
let url = new RegExp(this.regexString);
return _.assign((request) => url.test(normalize_url_1.default(request.url)), { explain: () => `for paths matching /${this.regexString}/` });
}
}
exports.RegexPathMatcherData = RegexPathMatcherData;
var HeaderMatcherData = /** @class */ (function (_super) {
__extends(HeaderMatcherData, _super);
function HeaderMatcherData(headers) {
var _this = _super.call(this) || this;
_this.headers = headers;
_this.type = 'header';
return _this;
class HeaderMatcherData extends serialization_1.Serializable {
constructor(headers) {
super();
this.headers = headers;
this.type = 'header';
}
HeaderMatcherData.prototype.buildMatcher = function () {
var _this = this;
var lowerCasedHeaders = _.mapKeys(this.headers, function (_value, key) { return key.toLowerCase(); });
return _.assign(function (request) { return _.isMatch(request.headers, lowerCasedHeaders); }, { explain: function () { return "with headers including " + JSON.stringify(_this.headers); } });
};
return HeaderMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
let lowerCasedHeaders = _.mapKeys(this.headers, (_value, key) => key.toLowerCase());
return _.assign((request) => _.isMatch(request.headers, lowerCasedHeaders), { explain: () => `with headers including ${JSON.stringify(this.headers)}` });
}
}
exports.HeaderMatcherData = HeaderMatcherData;
var QueryMatcherData = /** @class */ (function (_super) {
__extends(QueryMatcherData, _super);
function QueryMatcherData(queryObject) {
var _this = _super.call(this) || this;
_this.queryObject = queryObject;
_this.type = 'query';
return _this;
class QueryMatcherData extends serialization_1.Serializable {
constructor(queryObject) {
super();
this.queryObject = queryObject;
this.type = 'query';
}
QueryMatcherData.prototype.buildMatcher = function () {
var _this = this;
var expectedQuery = _.mapValues(this.queryObject, function (v) { return v.toString(); });
return _.assign(function (request) {
var query = url.parse(request.url, true).query;
buildMatcher() {
const expectedQuery = _.mapValues(this.queryObject, (v) => v.toString());
return _.assign((request) => {
let { query } = url.parse(request.url, true);
return _.isMatch(query, expectedQuery);
}, { explain: function () { return "with a query including " + JSON.stringify(_this.queryObject); } });
};
return QueryMatcherData;
}(serialization_1.Serializable));
}, { explain: () => `with a query including ${JSON.stringify(this.queryObject)}` });
}
}
exports.QueryMatcherData = QueryMatcherData;
var FormDataMatcherData = /** @class */ (function (_super) {
__extends(FormDataMatcherData, _super);
function FormDataMatcherData(formData) {
var _this = _super.call(this) || this;
_this.formData = formData;
_this.type = 'form-data';
return _this;
class FormDataMatcherData extends serialization_1.Serializable {
constructor(formData) {
super();
this.formData = formData;
this.type = 'form-data';
}
FormDataMatcherData.prototype.buildMatcher = function () {
var _this = this;
return _.assign(function (request) { return __awaiter(_this, void 0, void 0, function () {
var _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_a = !!request.headers["content-type"] &&
request.headers["content-type"].indexOf("application/x-www-form-urlencoded") !== -1;
if (!_a) return [3 /*break*/, 2];
_c = (_b = _).isMatch;
return [4 /*yield*/, request.body.asFormData()];
case 1:
_a = _c.apply(_b, [_d.sent(), this.formData]);
_d.label = 2;
case 2: return [2 /*return*/, _a];
}
});
}); }, { explain: function () { return "with form data including " + JSON.stringify(_this.formData); } });
};
return FormDataMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
return _.assign((request) => __awaiter(this, void 0, void 0, function* () {
const contentType = request.headers['content-type'];
return !!contentType &&
contentType.indexOf("application/x-www-form-urlencoded") !== -1 &&
_.isMatch(yield request.body.asFormData(), this.formData);
}), { explain: () => `with form data including ${JSON.stringify(this.formData)}` });
}
}
exports.FormDataMatcherData = FormDataMatcherData;
var RawBodyMatcherData = /** @class */ (function (_super) {
__extends(RawBodyMatcherData, _super);
function RawBodyMatcherData(content) {
var _this = _super.call(this) || this;
_this.content = content;
_this.type = 'raw-body';
return _this;
class RawBodyMatcherData extends serialization_1.Serializable {
constructor(content) {
super();
this.content = content;
this.type = 'raw-body';
}
RawBodyMatcherData.prototype.buildMatcher = function () {
var _this = this;
return _.assign(function (request) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, request.body.asText()];
case 1: return [2 /*return*/, (_a.sent()) === this.content];
}
}); }); }, { explain: function () { return "with body '" + _this.content + "'"; } });
};
return RawBodyMatcherData;
}(serialization_1.Serializable));
buildMatcher() {
return _.assign((request) => __awaiter(this, void 0, void 0, function* () { return (yield request.body.asText()) === this.content; }), { explain: () => `with body '${this.content}'` });
}
}
exports.RawBodyMatcherData = RawBodyMatcherData;
var CookieMatcherData = /** @class */ (function (_super) {
__extends(CookieMatcherData, _super);
function CookieMatcherData(cookie) {
var _this = _super.call(this) || this;
_this.cookie = cookie;
_this.type = 'cookie';
return _this;
class CookieMatcherData extends serialization_1.Serializable {
constructor(cookie) {
super();
this.cookie = cookie;
this.type = 'cookie';
}
CookieMatcherData.prototype.buildMatcher = function () {
var _this = this;
return _.assign(function (request) { return __awaiter(_this, void 0, void 0, function () {
var cookies;
var _this = this;
return __generator(this, function (_a) {
if (!request.headers || !request.headers.cookie) {
return [2 /*return*/];
}
cookies = request.headers.cookie.split(';').map(function (cookie) {
var _a;
var _b = cookie.split('='), key = _b[0], value = _b[1];
return _a = {}, _a[key.trim()] = (value || '').trim(), _a;
});
return [2 /*return*/, cookies.some(function (element) { return _.isEqual(element, _this.cookie); })];
buildMatcher() {
return _.assign((request) => __awaiter(this, void 0, void 0, function* () {
if (!request.headers || !request.headers.cookie) {
return;
}
const cookies = request.headers.cookie.split(';').map(cookie => {
const [key, value] = cookie.split('=');
return { [key.trim()]: (value || '').trim() };
});
}); }, { explain: function () { return "with cookies including " + JSON.stringify(_this.cookie); } });
};
return CookieMatcherData;
}(serialization_1.Serializable));
return cookies.some(element => _.isEqual(element, this.cookie));
}), { explain: () => `with cookies including ${JSON.stringify(this.cookie)}` });
}
}
exports.CookieMatcherData = CookieMatcherData;

@@ -264,17 +160,8 @@ exports.MatcherDataLookup = {

function buildMatchers(matcherPartData) {
var matchers = matcherPartData.map(function (m) { return m.buildMatcher(); });
const matchers = matcherPartData.map(m => m.buildMatcher());
return _.assign(function matchRequest(req) {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_b = (_a = _).every;
return [4 /*yield*/, Promise.all(matchers.map(function (m) { return m(req); }))];
case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
}
});
return __awaiter(this, void 0, void 0, function* () {
return _.every(yield Promise.all(matchers.map((m) => m(req))));
});
}, { explain: function () {
var _this = this;
if (matchers.length === 1)

@@ -284,7 +171,7 @@ return matchers[0].explain.apply(this);

// With just two explanations, you can just combine them
return matchers[0].explain.apply(this) + " " + matchers[1].explain.apply(this);
return `${matchers[0].explain.apply(this)} ${matchers[1].explain.apply(this)}`;
}
// With 3+, we need to oxford comma separate explanations to make them readable
return matchers.slice(0, -1)
.map(function (m) { return m.explain.apply(_this); })
.map((m) => m.explain.apply(this))
.join(', ') + ', and ' + matchers.slice(-1)[0].explain.apply(this);

@@ -294,3 +181,2 @@ } });

exports.buildMatchers = buildMatchers;
var templateObject_1;
//# sourceMappingURL=matchers.js.map

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

*/
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -18,36 +14,9 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var url = require("url");
var lodash_1 = require("lodash");
var common_tags_1 = require("common-tags");
var completion_checkers_1 = require("./completion-checkers");
var matchers_1 = require("./matchers");
var handlers_1 = require("./handlers");
const url = require("url");
const lodash_1 = require("lodash");
const common_tags_1 = require("common-tags");
const completion_checkers_1 = require("./completion-checkers");
const matchers_1 = require("./matchers");
const handlers_1 = require("./handlers");
/**

@@ -71,4 +40,4 @@ * @class MockRuleBuilder

*/
var MockRuleBuilder = /** @class */ (function () {
function MockRuleBuilder(methodOrAddRule, path, addRule) {
class MockRuleBuilder {
constructor(methodOrAddRule, path, addRule) {
this.matchers = [];

@@ -93,69 +62,69 @@ if (methodOrAddRule instanceof Function) {

*/
MockRuleBuilder.prototype.withHeaders = function (headers) {
withHeaders(headers) {
this.matchers.push(new matchers_1.HeaderMatcherData(headers));
return this;
};
}
/**
* Match only requests that include the given query parameters
*/
MockRuleBuilder.prototype.withQuery = function (query) {
withQuery(query) {
this.matchers.push(new matchers_1.QueryMatcherData(query));
return this;
};
}
/**
* Match only requests whose bodies include the given form data
*/
MockRuleBuilder.prototype.withForm = function (formData) {
withForm(formData) {
this.matchers.push(new matchers_1.FormDataMatcherData(formData));
return this;
};
}
/**
* Match only requests whose bodies exactly match the given string
*/
MockRuleBuilder.prototype.withBody = function (content) {
withBody(content) {
this.matchers.push(new matchers_1.RawBodyMatcherData(content));
return this;
};
}
/**
* Match only requests that include the given cookies
*/
MockRuleBuilder.prototype.withCookie = function (cookie) {
withCookie(cookie) {
this.matchers.push(new matchers_1.CookieMatcherData(cookie));
return this;
};
}
/**
* Run this rule forever, for all matching requests
*/
MockRuleBuilder.prototype.always = function () {
always() {
this.isComplete = new completion_checkers_1.AlwaysData();
return this;
};
}
/**
* Run this rule only once, for the first matching request
*/
MockRuleBuilder.prototype.once = function () {
once() {
this.isComplete = new completion_checkers_1.OnceData();
return this;
};
}
/**
* Run this rule twice, for the first two matching requests
*/
MockRuleBuilder.prototype.twice = function () {
twice() {
this.isComplete = new completion_checkers_1.TwiceData();
return this;
};
}
/**
* Run this rule three times, for the first three matching requests
*/
MockRuleBuilder.prototype.thrice = function () {
thrice() {
this.isComplete = new completion_checkers_1.ThriceData();
return this;
};
}
/**
* Run this rule the given number of times, for the first matching requests
*/
MockRuleBuilder.prototype.times = function (n) {
times(n) {
this.isComplete = new completion_checkers_1.TimesData(n);
return this;
};
}
/**

@@ -173,4 +142,4 @@ * Reply to matched with with given status and (optionally) body

*/
MockRuleBuilder.prototype.thenReply = function (status, data, headers) {
var rule = {
thenReply(status, data, headers) {
const rule = {
matchers: this.matchers,

@@ -181,3 +150,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -198,7 +167,6 @@ * Reply to matched requests with the given status & JSON and (optionally)

*/
MockRuleBuilder.prototype.thenJSON = function (status, data, headers) {
if (headers === void 0) { headers = {}; }
var defaultHeaders = { 'Content-Type': 'application/json' };
thenJSON(status, data, headers = {}) {
const defaultHeaders = { 'Content-Type': 'application/json' };
lodash_1.merge(defaultHeaders, headers);
var rule = {
const rule = {
matchers: this.matchers,

@@ -209,3 +177,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -237,4 +205,4 @@ * Call the given callback for any matched requests that are received,

*/
MockRuleBuilder.prototype.thenCallback = function (callback) {
var rule = {
thenCallback(callback) {
const rule = {
matchers: this.matchers,

@@ -245,3 +213,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -265,4 +233,4 @@ * Respond immediately with the given status (and optionally, headers),

*/
MockRuleBuilder.prototype.thenStream = function (status, stream, headers) {
var rule = {
thenStream(status, stream, headers) {
const rule = {
matchers: this.matchers,

@@ -273,3 +241,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -293,4 +261,4 @@ * Pass matched requests through to their real destination. This works

*/
MockRuleBuilder.prototype.thenPassThrough = function (options) {
var rule = {
thenPassThrough(options) {
const rule = {
matchers: this.matchers,

@@ -301,3 +269,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -321,21 +289,19 @@ * Forward matched requests on to the specified forwardToUrl. The url

*/
MockRuleBuilder.prototype.thenForwardTo = function (forwardToLocation, options) {
return __awaiter(this, void 0, void 0, function () {
var _a, protocol, hostname, port, path, suggestion, rule;
return __generator(this, function (_b) {
_a = url.parse(forwardToLocation), protocol = _a.protocol, hostname = _a.hostname, port = _a.port, path = _a.path;
if (path && path.trim() !== "/") {
suggestion = url.format({ protocol: protocol, hostname: hostname, port: port });
throw new Error(common_tags_1.stripIndent(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n URLs passed to thenForwardTo cannot include a path, but \"", "\" does. ", "Did you mean ", "?\n "], ["\n URLs passed to thenForwardTo cannot include a path, but \"", "\" does. ",
"Did you mean ", "?\n "])), forwardToLocation, '', suggestion));
}
rule = {
matchers: this.matchers,
completionChecker: this.isComplete,
handler: new handlers_1.PassThroughHandlerData(lodash_1.defaults({ forwardToLocation: forwardToLocation }, options))
};
return [2 /*return*/, this.addRule(rule)];
});
thenForwardTo(forwardToLocation, options) {
return __awaiter(this, void 0, void 0, function* () {
const { protocol, hostname, port, path } = url.parse(forwardToLocation);
if (path && path.trim() !== "/") {
const suggestion = url.format({ protocol, hostname, port });
throw new Error(common_tags_1.stripIndent `
URLs passed to thenForwardTo cannot include a path, but "${forwardToLocation}" does. ${''}Did you mean ${suggestion}?
`);
}
const rule = {
matchers: this.matchers,
completionChecker: this.isComplete,
handler: new handlers_1.PassThroughHandlerData(lodash_1.defaults({ forwardToLocation }, options))
};
return this.addRule(rule);
});
};
}
/**

@@ -353,4 +319,4 @@ * Close connections that match this rule immediately, without

*/
MockRuleBuilder.prototype.thenCloseConnection = function () {
var rule = {
thenCloseConnection() {
const rule = {
matchers: this.matchers,

@@ -361,3 +327,3 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
}
/**

@@ -375,4 +341,4 @@ * Hold open connections that match this rule, but never respond

*/
MockRuleBuilder.prototype.thenTimeout = function () {
var rule = {
thenTimeout() {
const rule = {
matchers: this.matchers,

@@ -383,7 +349,5 @@ completionChecker: this.isComplete,

return this.addRule(rule);
};
return MockRuleBuilder;
}());
}
}
exports.default = MockRuleBuilder;
var templateObject_1;
//# sourceMappingURL=mock-rule-builder.js.map

@@ -13,40 +13,13 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var uuid = require("uuid/v4");
var serialization_1 = require("../util/serialization");
var request_utils_1 = require("../server/request-utils");
var matching = require("./matchers");
var handling = require("./handlers");
var completion = require("./completion-checkers");
const _ = require("lodash");
const uuid = require("uuid/v4");
const serialization_1 = require("../util/serialization");
const request_utils_1 = require("../server/request-utils");
const matching = require("./matchers");
const handling = require("./handlers");
const completion = require("./completion-checkers");
function serializeRuleData(data, options) {
return {
matchers: data.matchers.map(function (m) { return m.serialize(options); }),
matchers: data.matchers.map(m => m.serialize(options)),
handler: data.handler.serialize(options),

@@ -60,5 +33,3 @@ completionChecker: data.completionChecker && data.completionChecker.serialize(options)

return {
matchers: data.matchers.map(function (m) {
return serialization_1.deserialize(m, matching.MatcherDataLookup, options);
}),
matchers: data.matchers.map((m) => serialization_1.deserialize(m, matching.MatcherDataLookup, options)),
handler: serialization_1.deserialize(data.handler, handling.HandlerDataLookup, options),

@@ -69,5 +40,4 @@ completionChecker: data.completionChecker && serialization_1.deserialize(data.completionChecker, completion.CompletionCheckerDataLookup, options)

exports.deserializeRuleData = deserializeRuleData;
var MockRule = /** @class */ (function () {
function MockRule(_a) {
var matchers = _a.matchers, handler = _a.handler, completionChecker = _a.completionChecker;
class MockRule {
constructor({ matchers, handler, completionChecker }) {
this.id = uuid();

@@ -80,23 +50,15 @@ this.requests = [];

// Wrap the handler, to add the request to this.requests when it's done
MockRule.prototype.recordRequests = function (handler) {
var thisRule = this;
var recordRequest = _.assign(function recordRequest(req) {
var _this = this;
var handlerArgs = arguments;
var completedAndRecordedPromise = (function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
var buffer;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
buffer = req.body.asBuffer();
return [4 /*yield*/, handler.apply(this, handlerArgs)];
case 1:
_a.sent();
return [2 /*return*/, request_utils_1.waitForCompletedRequest(req)];
}
});
}); })();
recordRequests(handler) {
const thisRule = this;
const recordRequest = _.assign(function recordRequest(req) {
const handlerArgs = arguments;
let completedAndRecordedPromise = ((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
// Start recording before the data starts piping, so we don't miss anything.
req.body.asBuffer();
yield handler.apply(this, handlerArgs);
return request_utils_1.waitForCompletedRequest(req);
}))();
// Requests are added to rule.requests as soon as they start being handled.
thisRule.requests.push(completedAndRecordedPromise);
return completedAndRecordedPromise.then(function () { });
return completedAndRecordedPromise.then(() => { });
}, {

@@ -106,8 +68,8 @@ explain: handler.explain

return recordRequest;
};
MockRule.prototype.explain = function () {
var explanation = "Match requests " + this.matches.explain.apply(this) + ", " +
("and then " + this.handleRequest.explain.apply(this));
}
explain() {
let explanation = `Match requests ${this.matches.explain.apply(this)}, ` +
`and then ${this.handleRequest.explain.apply(this)}`;
if (this.isComplete) {
explanation += ", " + this.isComplete.explain.apply(this) + ".";
explanation += `, ${this.isComplete.explain.apply(this)}.`;
}

@@ -118,6 +80,5 @@ else {

return explanation;
};
return MockRule;
}());
}
}
exports.MockRule = MockRule;
//# sourceMappingURL=mock-rule.js.map

@@ -10,36 +10,9 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var tls = require("tls");
var http = require("http");
var httpolyglot = require("httpolyglot");
var destroyable_server_1 = require("../util/destroyable-server");
var tls_1 = require("../util/tls");
var socket_util_1 = require("../util/socket-util");
const tls = require("tls");
const http = require("http");
const httpolyglot = require("httpolyglot");
const destroyable_server_1 = require("../util/destroyable-server");
const tls_1 = require("../util/tls");
const socket_util_1 = require("../util/socket-util");
// The low-level server that handles all the sockets & TLS. The server will correctly call the

@@ -49,6 +22,55 @@ // given handler for both HTTP & HTTPS direct connections, or connections when used as an

function createComboServer(options, requestListener) {
return __awaiter(this, void 0, void 0, function () {
return __awaiter(this, void 0, void 0, function* () {
if (!options.https) {
return destroyable_server_1.default(http.createServer(requestListener));
}
const ca = yield tls_1.getCA(options.https);
const defaultCert = ca.generateCertificate('localhost');
const server = httpolyglot.createServer({
key: defaultCert.key,
cert: defaultCert.cert,
ca: [defaultCert.ca],
SNICallback: (domain, cb) => {
if (options.debug)
console.log(`Generating certificate for ${domain}`);
try {
const generatedCert = ca.generateCertificate(domain);
cb(null, tls.createSecureContext({
key: generatedCert.key,
cert: generatedCert.cert,
ca: generatedCert.ca
}));
}
catch (e) {
console.error('Cert generation error', e);
cb(e);
}
}
}, requestListener);
// If the server receives a HTTP/HTTPS CONNECT request, do some magic to proxy & intercept it
server.addListener('connect', (req, socket) => {
const [targetHost, port] = req.url.split(':');
if (options.debug)
console.log(`Proxying CONNECT to ${targetHost}`);
socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'utf-8', () => __awaiter(this, void 0, void 0, function* () {
const firstByte = yield socket_util_1.peekFirstByte(socket);
// Tell later handlers whether the socket wants an insecure upstream
socket.upstreamEncryption = socket_util_1.mightBeTLSHandshake(firstByte);
if (socket.upstreamEncryption) {
if (options.debug)
console.log(`Unwrapping TLS connection to ${targetHost}`);
unwrapTLS(targetHost, port, socket);
}
else {
// Non-TLS CONNECT, probably a plain HTTP websocket. Pass it through untouched.
if (options.debug)
console.log(`Passing through connection to ${targetHost}`);
server.emit('connection', socket);
socket.resume();
}
}));
});
function unwrapTLS(targetHost, port, socket) {
var generatedCert = ca.generateCertificate(targetHost);
var tlsSocket = new tls.TLSSocket(socket, {
const generatedCert = ca.generateCertificate(targetHost);
let tlsSocket = new tls.TLSSocket(socket, {
isServer: true,

@@ -66,80 +88,14 @@ server: server,

// this is the easiest way I can see to put targetHost into the URL, without reimplementing HTTP.
var innerServer = http.createServer(function (req, res) {
req.url = "https://" + targetHost + ":" + port + req.url;
const innerServer = http.createServer((req, res) => {
req.url = `https://${targetHost}:${port}${req.url}`;
return requestListener(req, res);
});
innerServer.addListener('upgrade', function (req, socket, head) {
req.url = "https://" + targetHost + ":" + port + req.url;
innerServer.addListener('upgrade', (req, socket, head) => {
req.url = `https://${targetHost}:${port}${req.url}`;
server.emit('upgrade', req, socket, head);
});
innerServer.addListener('connect', function (req, res) { return server.emit('connect', req, res); });
innerServer.addListener('connect', (req, res) => server.emit('connect', req, res));
innerServer.emit('connection', tlsSocket);
}
var ca, defaultCert, server;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!options.https) {
return [2 /*return*/, destroyable_server_1.default(http.createServer(requestListener))];
}
return [4 /*yield*/, tls_1.getCA(options.https)];
case 1:
ca = _a.sent();
defaultCert = ca.generateCertificate('localhost');
server = httpolyglot.createServer({
key: defaultCert.key,
cert: defaultCert.cert,
ca: [defaultCert.ca],
SNICallback: function (domain, cb) {
if (options.debug)
console.log("Generating certificate for " + domain);
try {
var generatedCert = ca.generateCertificate(domain);
cb(null, tls.createSecureContext({
key: generatedCert.key,
cert: generatedCert.cert,
ca: generatedCert.ca
}));
}
catch (e) {
console.error('Cert generation error', e);
cb(e);
}
}
}, requestListener);
// If the server receives a HTTP/HTTPS CONNECT request, do some magic to proxy & intercept it
server.addListener('connect', function (req, socket) {
var _a = req.url.split(':'), targetHost = _a[0], port = _a[1];
if (options.debug)
console.log("Proxying CONNECT to " + targetHost);
socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'utf-8', function () { return __awaiter(_this, void 0, void 0, function () {
var firstByte;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, socket_util_1.peekFirstByte(socket)];
case 1:
firstByte = _a.sent();
// Tell later handlers whether the socket wants an insecure upstream
socket.upstreamEncryption = socket_util_1.mightBeTLSHandshake(firstByte);
if (socket.upstreamEncryption) {
if (options.debug)
console.log("Unwrapping TLS connection to " + targetHost);
unwrapTLS(targetHost, port, socket);
}
else {
// Non-TLS CONNECT, probably a plain HTTP websocket. Pass it through untouched.
if (options.debug)
console.log("Passing through connection to " + targetHost);
server.emit('connection', socket);
socket.resume();
}
return [2 /*return*/];
}
});
}); });
});
return [2 /*return*/, destroyable_server_1.default(server)];
}
});
return destroyable_server_1.default(server);
});

@@ -146,0 +102,0 @@ }

@@ -6,4 +6,4 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
var MockedEndpoint = /** @class */ (function () {
function MockedEndpoint(rule) {
class MockedEndpoint {
constructor(rule) {
this.rule = rule;

@@ -13,16 +13,11 @@ this.getSeenRequests.bind(this);

;
Object.defineProperty(MockedEndpoint.prototype, "id", {
get: function () {
return this.rule.id;
},
enumerable: true,
configurable: true
});
MockedEndpoint.prototype.getSeenRequests = function () {
get id() {
return this.rule.id;
}
getSeenRequests() {
// Wait for all completed running requests to have all their details available
return Promise.all(this.rule.requests);
};
return MockedEndpoint;
}());
}
}
exports.MockedEndpoint = MockedEndpoint;
//# sourceMappingURL=mocked-endpoint.js.map

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

*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -27,43 +14,17 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var events_1 = require("events");
var portfinder = require("portfinder");
var express = require("express");
var uuid = require("uuid/v4");
var cors = require("cors");
var _ = require("lodash");
var mockttp_1 = require("../mockttp");
var mock_rule_1 = require("../rules/mock-rule");
var mocked_endpoint_1 = require("./mocked-endpoint");
var http_combo_server_1 = require("./http-combo-server");
var promise_1 = require("../util/promise");
var request_utils_1 = require("./request-utils");
var websocket_handler_1 = require("./websocket-handler");
const events_1 = require("events");
const portfinder = require("portfinder");
const express = require("express");
const uuid = require("uuid/v4");
const cors = require("cors");
const now = require("performance-now");
const _ = require("lodash");
const mockttp_1 = require("../mockttp");
const mock_rule_1 = require("../rules/mock-rule");
const mocked_endpoint_1 = require("./mocked-endpoint");
const http_combo_server_1 = require("./http-combo-server");
const promise_1 = require("../util/promise");
const request_utils_1 = require("./request-utils");
const websocket_handler_1 = require("./websocket-handler");
/**

@@ -75,14 +36,12 @@ * A in-process Mockttp implementation. This starts servers on the local machine in the

*/
var MockttpServer = /** @class */ (function (_super) {
__extends(MockttpServer, _super);
function MockttpServer(options) {
if (options === void 0) { options = {}; }
var _this = _super.call(this, options) || this;
_this.rules = [];
_this.addRule = function (ruleData) {
var rule = new mock_rule_1.MockRule(ruleData);
_this.rules.push(rule);
class MockttpServer extends mockttp_1.AbstractMockttp {
constructor(options = {}) {
super(options);
this.rules = [];
this.addRule = (ruleData) => {
const rule = new mock_rule_1.MockRule(ruleData);
this.rules.push(rule);
return Promise.resolve(new mocked_endpoint_1.MockedEndpoint(rule));
};
_this.isComplete = function (rule, matchingRules) {
this.isComplete = (rule, matchingRules) => {
if (rule.isComplete) {

@@ -98,328 +57,231 @@ return rule.isComplete();

};
_this.initialDebugSetting = _this.debug;
_this.httpsOptions = options.https;
_this.eventEmitter = new events_1.EventEmitter();
_this.app = express();
_this.app.disable('x-powered-by');
if (_this.cors) {
if (_this.debug)
this.initialDebugSetting = this.debug;
this.httpsOptions = options.https;
this.eventEmitter = new events_1.EventEmitter();
this.app = express();
this.app.disable('x-powered-by');
if (this.cors) {
if (this.debug)
console.log('Enabling CORS');
_this.app.use(cors({
this.app.use(cors({
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
}));
}
_this.app.use(request_utils_1.parseBody);
_this.app.use(_this.handleRequest.bind(_this));
return _this;
this.app.use(request_utils_1.parseBody);
this.app.use(this.handleRequest.bind(this));
}
MockttpServer.prototype.start = function (portParam) {
return __awaiter(this, void 0, void 0, function () {
var port, _a, _b, webSocketHander;
var _this = this;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!_.isInteger(portParam) && !_.isUndefined(portParam)) {
throw new Error("Cannot start server with port " + portParam + ". If passed, the port must be an integer");
}
_a = portParam;
if (_a) return [3 /*break*/, 2];
return [4 /*yield*/, new Promise(function (resolve, reject) {
portfinder.getPort(function (err, port) {
if (err)
reject(err);
else
resolve(port);
});
})];
case 1:
_a = (_c.sent());
_c.label = 2;
case 2:
port = (_a);
start(portParam) {
return __awaiter(this, void 0, void 0, function* () {
if (!_.isInteger(portParam) && !_.isUndefined(portParam)) {
throw new Error(`Cannot start server with port ${portParam}. If passed, the port must be an integer`);
}
const port = (portParam || (yield portfinder.getPortPromise()));
if (this.debug)
console.log(`Starting mock server on port ${port}`);
this.server = yield http_combo_server_1.createComboServer({
debug: this.debug,
https: this.httpsOptions
}, this.app);
this.server.listen(port);
// Handle websocket connections too (ignore for now, just forward on)
const webSocketHander = new websocket_handler_1.WebSocketHandler(this.debug);
this.server.on('upgrade', webSocketHander.handleUpgrade.bind(webSocketHander));
return new Promise((resolve, reject) => {
this.server.on('listening', resolve);
this.server.on('error', (e) => {
// Although we try to pick a free port, we may have race conditions, if something else
// takes the same port at the same time. If you haven't explicitly picked a port, and
// we do have a collision, simply try again.
if (e.code === 'EADDRINUSE' && !portParam) {
if (this.debug)
console.log("Starting mock server on port " + port);
_b = this;
return [4 /*yield*/, http_combo_server_1.createComboServer({
debug: this.debug,
https: this.httpsOptions
}, this.app)];
case 3:
_b.server = _c.sent();
this.server.listen(port);
webSocketHander = new websocket_handler_1.WebSocketHandler(this.debug);
this.server.on('upgrade', webSocketHander.handleUpgrade.bind(webSocketHander));
return [2 /*return*/, new Promise(function (resolve) {
_this.server.on('listening', resolve);
_this.server.on('error', function (e) {
// Although we try to pick a free port, we may have race conditions, if something else
// takes the same port at the same time. If you haven't explicitly picked a port, and
// we do have a collision, simply try again.
if (e.code === 'EADDRINUSE' && !portParam) {
if (_this.debug)
console.log('Address in use, retrying...');
_this.server.destroy(); // Don't bother waiting for this, it can stop on its own time
resolve(_this.start());
}
else {
throw e;
}
});
})];
}
console.log('Address in use, retrying...');
this.server.destroy(); // Don't bother waiting for this, it can stop on its own time
resolve(this.start());
}
else {
reject(e);
}
});
});
});
};
MockttpServer.prototype.stop = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.debug)
console.log("Stopping server at " + this.url);
if (!this.server) return [3 /*break*/, 2];
return [4 /*yield*/, this.server.destroy()];
case 1:
_a.sent();
_a.label = 2;
case 2:
this.reset();
return [2 /*return*/];
}
});
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
if (this.debug)
console.log(`Stopping server at ${this.url}`);
if (this.server)
yield this.server.destroy();
this.reset();
});
};
MockttpServer.prototype.enableDebug = function () {
}
enableDebug() {
this.debug = true;
};
MockttpServer.prototype.reset = function () {
}
reset() {
this.rules = [];
this.debug = this.initialDebugSetting;
};
Object.defineProperty(MockttpServer.prototype, "mockedEndpoints", {
get: function () {
return this.rules.map(function (rule) { return new mocked_endpoint_1.MockedEndpoint(rule); });
},
enumerable: true,
configurable: true
});
Object.defineProperty(MockttpServer.prototype, "address", {
get: function () {
if (!this.server)
throw new Error('Cannot get address before server is started');
return this.server.address();
},
enumerable: true,
configurable: true
});
Object.defineProperty(MockttpServer.prototype, "url", {
get: function () {
if (!this.server)
throw new Error('Cannot get url before server is started');
if (this.httpsOptions) {
return "https://localhost:" + this.address.port;
}
else {
return "http://localhost:" + this.address.port;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(MockttpServer.prototype, "port", {
get: function () {
if (!this.server)
throw new Error('Cannot get port before server is started');
return this.address.port;
},
enumerable: true,
configurable: true
});
MockttpServer.prototype.on = function (event, callback) {
}
get mockedEndpoints() {
return this.rules.map((rule) => new mocked_endpoint_1.MockedEndpoint(rule));
}
get address() {
if (!this.server)
throw new Error('Cannot get address before server is started');
return this.server.address();
}
get url() {
if (!this.server)
throw new Error('Cannot get url before server is started');
if (this.httpsOptions) {
return "https://localhost:" + this.address.port;
}
else {
return "http://localhost:" + this.address.port;
}
}
get port() {
if (!this.server)
throw new Error('Cannot get port before server is started');
return this.address.port;
}
on(event, callback) {
this.eventEmitter.on(event, callback);
return Promise.resolve();
};
MockttpServer.prototype.announceRequestAsync = function (request) {
var _this = this;
setImmediate(function () {
}
announceRequestAsync(request) {
setImmediate(() => {
request_utils_1.waitForCompletedRequest(request)
.then(function (req) {
_this.eventEmitter.emit('request', req);
.then((req) => {
this.eventEmitter.emit('request', Object.assign(req, {
timingEvents: _.clone(req.timingEvents)
}));
})
.catch(console.error);
});
};
MockttpServer.prototype.announceResponseAsync = function (response) {
var _this = this;
setImmediate(function () {
}
announceResponseAsync(response) {
setImmediate(() => {
request_utils_1.waitForCompletedResponse(response)
.then(function (res) {
_this.eventEmitter.emit('response', res);
.then((res) => {
this.eventEmitter.emit('response', Object.assign(res, {
timingEvents: _.clone(res.timingEvents)
}));
})
.catch(console.error);
});
};
MockttpServer.prototype.announceAbortAsync = function (request) {
return __awaiter(this, void 0, void 0, function () {
var req;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, request_utils_1.waitForCompletedRequest(request)];
case 1:
req = _a.sent();
this.eventEmitter.emit('abort', req);
return [2 /*return*/];
}
});
}
announceAbortAsync(request) {
return __awaiter(this, void 0, void 0, function* () {
const req = yield request_utils_1.waitForCompletedRequest(request);
this.eventEmitter.emit('abort', Object.assign(req, {
timingEvents: _.clone(req.timingEvents)
}));
});
};
MockttpServer.prototype.handleRequest = function (request, rawResponse) {
return __awaiter(this, void 0, void 0, function () {
var response, id, result, matchingRules_1, nextRule, e_1;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.debug)
console.log("Handling request for " + request.url);
response = request_utils_1.trackResponse(rawResponse);
id = uuid();
request.id = id;
response.id = id;
this.announceRequestAsync(request);
result = null;
response.once('close', function () {
// Aborted is only defined in new node. We use it where it's explicitly false though.
if (result === null && (request.aborted !== false)) {
_this.announceAbortAsync(request);
result = 'aborted';
}
});
_a.label = 1;
case 1:
_a.trys.push([1, 7, , 8]);
return [4 /*yield*/, promise_1.filter(this.rules, function (r) { return r.matches(request); })];
case 2:
matchingRules_1 = _a.sent();
nextRule = matchingRules_1.filter(function (r) { return !_this.isComplete(r, matchingRules_1); })[0];
if (!nextRule) return [3 /*break*/, 4];
if (this.debug)
console.log("Request matched rule: " + nextRule.explain());
return [4 /*yield*/, nextRule.handleRequest(request, response)];
case 3:
_a.sent();
return [3 /*break*/, 6];
case 4: return [4 /*yield*/, this.sendUnmatchedRequestError(request, response)];
case 5:
_a.sent();
_a.label = 6;
case 6:
result = result || 'responded';
return [3 /*break*/, 8];
case 7:
e_1 = _a.sent();
if (this.debug) {
console.error("Failed to handle request:", e_1);
}
else {
console.error("Failed to handle request:", e_1.message);
}
// Make sure any errors here don't kill the process
response.on('error', function (e) { });
// Do whatever we can to tell the client we broke
try {
response.writeHead(e_1.statusCode || 500, e_1.statusMessage || 'Server error');
}
catch (e) { }
try {
response.end(e_1.toString());
result = result || 'responded';
}
catch (e) {
this.announceAbortAsync(request);
}
return [3 /*break*/, 8];
case 8:
if (result === 'responded') {
this.announceResponseAsync(response);
}
return [2 /*return*/];
}
handleRequest(rawRequest, rawResponse) {
return __awaiter(this, void 0, void 0, function* () {
if (this.debug)
console.log(`Handling request for ${rawRequest.url}`);
const timingEvents = { startTime: Date.now(), startTimestamp: now() };
const response = request_utils_1.trackResponse(rawResponse, timingEvents);
const id = uuid();
const request = Object.assign(rawRequest, { id: id, timingEvents });
response.id = id;
this.announceRequestAsync(request);
let result = null;
response.once('close', () => {
// Aborted is only defined in new node. We use it where it's explicitly false though.
if (result === null && (request.aborted !== false)) {
request.timingEvents.abortedTimestamp = now();
this.announceAbortAsync(request);
result = 'aborted';
}
});
});
};
MockttpServer.prototype.sendUnmatchedRequestError = function (request, response) {
return __awaiter(this, void 0, void 0, function () {
var requestExplanation, _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, this.explainRequest(request)];
case 1:
requestExplanation = _c.sent();
if (this.debug)
console.warn("Unmatched request received: " + requestExplanation);
response.setHeader('Content-Type', 'text/plain');
response.writeHead(503, "Request for unmocked endpoint");
response.write("No rules were found matching this request.\n");
response.write("This request was: " + requestExplanation + "\n\n");
if (this.rules.length > 0) {
response.write("The configured rules are:\n");
this.rules.forEach(function (rule) { return response.write(rule.explain() + "\n"); });
}
else {
response.write("There are no rules configured.\n");
}
_b = (_a = response).end;
return [4 /*yield*/, this.suggestRule(request)];
case 2:
_b.apply(_a, [_c.sent()]);
return [2 /*return*/];
try {
let matchingRules = yield promise_1.filter(this.rules, (r) => r.matches(request));
let nextRule = matchingRules.filter((r) => !this.isComplete(r, matchingRules))[0];
if (nextRule) {
if (this.debug)
console.log(`Request matched rule: ${nextRule.explain()}`);
yield nextRule.handleRequest(request, response);
}
});
});
};
MockttpServer.prototype.explainRequest = function (request) {
return __awaiter(this, void 0, void 0, function () {
var msg, bodyText;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
msg = request.method + " request to " + request.url;
return [4 /*yield*/, request.body.asText()];
case 1:
bodyText = _a.sent();
if (bodyText)
msg += " with body `" + bodyText + "`";
if (!_.isEmpty(request.headers)) {
msg += " with headers:\n" + JSON.stringify(request.headers, null, 2);
}
return [2 /*return*/, msg];
else {
yield this.sendUnmatchedRequestError(request, response);
}
});
});
};
MockttpServer.prototype.suggestRule = function (request) {
return __awaiter(this, void 0, void 0, function () {
var msg, isFormRequest, formBody;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
msg = "You can fix this by adding a rule to match this request, for example:\n";
msg += "mockServer." + request.method.toLowerCase() + "(\"" + request.path + "\")";
isFormRequest = !!request.headers["content-type"] && request.headers["content-type"].indexOf("application/x-www-form-urlencoded") > -1;
return [4 /*yield*/, request.body.asFormData().catch(function () { return undefined; })];
case 1:
formBody = _a.sent();
if (isFormRequest && !!formBody) {
msg += ".withForm(" + JSON.stringify(formBody) + ")";
}
msg += '.thenReply(200, "your response");';
return [2 /*return*/, msg];
result = result || 'responded';
}
catch (e) {
if (this.debug) {
console.error("Failed to handle request:", e);
}
});
else {
console.error("Failed to handle request:", e.message);
}
// Make sure any errors here don't kill the process
response.on('error', (e) => { });
// Do whatever we can to tell the client we broke
try {
response.writeHead(e.statusCode || 500, e.statusMessage || 'Server error');
}
catch (e) { }
try {
response.end(e.toString());
result = result || 'responded';
}
catch (e) {
this.announceAbortAsync(request);
}
}
if (result === 'responded') {
this.announceResponseAsync(response);
}
});
};
return MockttpServer;
}(mockttp_1.AbstractMockttp));
}
sendUnmatchedRequestError(request, response) {
return __awaiter(this, void 0, void 0, function* () {
let requestExplanation = yield this.explainRequest(request);
if (this.debug)
console.warn(`Unmatched request received: ${requestExplanation}`);
response.setHeader('Content-Type', 'text/plain');
response.writeHead(503, "Request for unmocked endpoint");
response.write("No rules were found matching this request.\n");
response.write(`This request was: ${requestExplanation}\n\n`);
if (this.rules.length > 0) {
response.write("The configured rules are:\n");
this.rules.forEach((rule) => response.write(rule.explain() + "\n"));
}
else {
response.write("There are no rules configured.\n");
}
response.end(yield this.suggestRule(request));
});
}
explainRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
let msg = `${request.method} request to ${request.url}`;
let bodyText = yield request.body.asText();
if (bodyText)
msg += ` with body \`${bodyText}\``;
if (!_.isEmpty(request.headers)) {
msg += ` with headers:\n${JSON.stringify(request.headers, null, 2)}`;
}
return msg;
});
}
suggestRule(request) {
return __awaiter(this, void 0, void 0, function* () {
let msg = "You can fix this by adding a rule to match this request, for example:\n";
msg += `mockServer.${request.method.toLowerCase()}("${request.path}")`;
const contentType = request.headers['content-type'];
let isFormRequest = !!contentType && contentType.indexOf("application/x-www-form-urlencoded") > -1;
let formBody = yield request.body.asFormData().catch(() => undefined);
if (isFormRequest && !!formBody) {
msg += `.withForm(${JSON.stringify(formBody)})`;
}
msg += '.thenReply(200, "your response");';
return msg;
});
}
}
exports.default = MockttpServer;
//# sourceMappingURL=mockttp-server.js.map

@@ -6,9 +6,9 @@ /**

import * as express from 'express';
import { Headers, OngoingRequest, CompletedRequest, OngoingResponse, CompletedResponse, CompletedBody } from "../types";
import { Headers, OngoingRequest, CompletedRequest, OngoingResponse, CompletedResponse, CompletedBody, TimingEvents } from "../types";
export declare const setHeaders: (response: express.Response, headers: Headers) => void;
export declare const handleContentEncoding: (body: Buffer, encoding?: string | undefined) => Buffer;
export declare const handleContentEncoding: (body: Buffer, encoding?: string | string[] | undefined) => Buffer;
export declare const buildBodyReader: (body: Buffer, headers: Headers) => CompletedBody;
export declare const parseBody: (req: express.Request, _res: express.Response, next: express.NextFunction) => void;
export declare function waitForCompletedRequest(request: OngoingRequest): Promise<CompletedRequest>;
export declare function trackResponse(response: express.Response): OngoingResponse;
export declare function trackResponse(response: express.Response, timingEvents: TimingEvents): OngoingResponse;
export declare function waitForCompletedResponse(response: OngoingResponse): Promise<CompletedResponse>;

@@ -13,39 +13,12 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var _this = this;
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var stream = require("stream");
var querystring = require("querystring");
var zlib = require("zlib");
var brotliDecompress = require("brotli/decompress");
exports.setHeaders = function (response, headers) {
Object.keys(headers).forEach(function (header) {
var value = headers[header];
const _ = require("lodash");
const stream = require("stream");
const querystring = require("querystring");
const zlib = require("zlib");
const brotliDecompress = require("brotli/decompress");
const now = require("performance-now");
exports.setHeaders = (response, headers) => {
Object.keys(headers).forEach((header) => {
let value = headers[header];
if (!value)

@@ -56,15 +29,15 @@ return;

};
var streamToBuffer = function (input) {
return new Promise(function (resolve, reject) {
var chunks = [];
input.on('data', function (d) { return chunks.push(d); });
input.on('end', function () { return resolve(Buffer.concat(chunks)); });
const streamToBuffer = (input) => {
return new Promise((resolve, reject) => {
let chunks = [];
input.on('data', (d) => chunks.push(d));
input.on('end', () => resolve(Buffer.concat(chunks)));
input.on('error', reject);
});
};
var parseBodyStream = function (bodyStream) {
var buffer = null;
var body = {
const parseBodyStream = (bodyStream) => {
let buffer = null;
let body = {
rawStream: bodyStream,
asBuffer: function () {
asBuffer() {
if (!buffer) {

@@ -75,11 +48,10 @@ buffer = streamToBuffer(bodyStream);

},
asText: function (encoding) {
if (encoding === void 0) { encoding = 'utf8'; }
return body.asBuffer().then(function (b) { return b.toString(encoding); });
asText(encoding = 'utf8') {
return body.asBuffer().then((b) => b.toString(encoding));
},
asJson: function () {
return body.asText().then(function (t) { return JSON.parse(t); });
asJson() {
return body.asText().then((t) => JSON.parse(t));
},
asFormData: function () {
return body.asText().then(function (t) { return querystring.parse(t); });
asFormData() {
return body.asText().then((t) => querystring.parse(t));
},

@@ -97,14 +69,13 @@ };

}
var waitForBody = function (body, headers) { return __awaiter(_this, void 0, void 0, function () {
var bufferBody;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, body.asBuffer()];
case 1:
bufferBody = _a.sent();
return [2 /*return*/, exports.buildBodyReader(bufferBody, headers)];
}
});
}); };
exports.handleContentEncoding = function (body, encoding) {
const waitForBody = (body, headers) => __awaiter(this, void 0, void 0, function* () {
const bufferBody = yield body.asBuffer();
return exports.buildBodyReader(bufferBody, headers);
});
exports.handleContentEncoding = (body, encoding) => {
if (_.isArray(encoding) || (typeof encoding === 'string' && encoding.indexOf(', ') >= 0)) {
const encodings = typeof encoding === 'string' ? encoding.split(', ').reverse() : encoding;
return encodings.reduce((content, nextEncoding) => {
return exports.handleContentEncoding(content, nextEncoding);
}, body);
}
if (encoding === 'gzip' || encoding === 'x-gzip') {

@@ -117,3 +88,3 @@ return zlib.gunzipSync(body);

// https://stackoverflow.com/a/37528114/68051
var lowNibble = body[0] & 0xF;
const lowNibble = body[0] & 0xF;
if (lowNibble === 8) {

@@ -133,28 +104,19 @@ return zlib.inflateSync(body);

else {
throw new Error("Unknown encoding: " + encoding);
throw new Error(`Unknown encoding: ${encoding}`);
}
};
exports.buildBodyReader = function (body, headers) {
var completedBody = {
exports.buildBodyReader = (body, headers) => {
const completedBody = {
buffer: body,
get decodedBuffer() {
return runOrUndefined(function () {
return exports.handleContentEncoding(body, headers['content-encoding']);
});
return runOrUndefined(() => exports.handleContentEncoding(body, headers['content-encoding']));
},
get text() {
var _this = this;
return runOrUndefined(function () {
return _this.decodedBuffer.toString('utf8');
});
return runOrUndefined(() => this.decodedBuffer.toString('utf8'));
},
get json() {
return runOrUndefined(function () {
return JSON.parse(completedBody.text);
});
return runOrUndefined(() => JSON.parse(completedBody.text));
},
get formData() {
return runOrUndefined(function () {
return completedBody.text ? querystring.parse(completedBody.text) : undefined;
});
return runOrUndefined(() => completedBody.text ? querystring.parse(completedBody.text) : undefined);
}

@@ -164,5 +126,5 @@ };

};
exports.parseBody = function (req, _res, next) {
var transformedRequest = req;
var bodyStream = new stream.PassThrough();
exports.parseBody = (req, _res, next) => {
let transformedRequest = req;
let bodyStream = new stream.PassThrough();
req.pipe(bodyStream);

@@ -173,27 +135,22 @@ transformedRequest.body = parseBodyStream(bodyStream);

function waitForCompletedRequest(request) {
return __awaiter(this, void 0, void 0, function () {
var _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_b = (_a = _(request).pick([
'id',
'protocol',
'method',
'url',
'path',
'hostname',
'headers'
])).assign;
_c = {};
return [4 /*yield*/, waitForBody(request.body, request.headers)];
case 1: return [2 /*return*/, _b.apply(_a, [(_c.body = _d.sent(),
_c)]).valueOf()];
}
});
return __awaiter(this, void 0, void 0, function* () {
const body = yield waitForBody(request.body, request.headers);
const bodyReceivedTimestamp = request.timingEvents.bodyReceivedTimestamp || now();
return _(request).pick([
'id',
'protocol',
'method',
'url',
'path',
'hostname',
'headers'
]).assign({
body: body,
timingEvents: Object.assign(request.timingEvents, { bodyReceivedTimestamp })
}).valueOf();
});
}
exports.waitForCompletedRequest = waitForCompletedRequest;
function trackResponse(response) {
var trackedResponse = response;
function trackResponse(response, timingEvents) {
let trackedResponse = response;
if (!trackedResponse.getHeaders) {

@@ -203,10 +160,15 @@ // getHeaders was added in 7.7. - if it's not available, polyfill it

}
var trackingStream = new stream.PassThrough();
var originalWrite = trackedResponse.write;
var originalEnd = trackedResponse.end;
var trackingWrite = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
trackedResponse.timingEvents = timingEvents;
// Headers are sent when .writeHead or .write() are first called
const trackingStream = new stream.PassThrough();
const originalWriteHeader = trackedResponse.writeHead;
const originalWrite = trackedResponse.write;
const originalEnd = trackedResponse.end;
trackedResponse.writeHead = function (...args) {
if (!timingEvents.headersSentTimestamp) {
timingEvents.headersSentTimestamp = now();
}
return originalWriteHeader.apply(this, args);
};
const trackingWrite = function (...args) {
trackingStream.write.apply(trackingStream, args);

@@ -216,3 +178,3 @@ return originalWrite.apply(this, args);

trackedResponse.write = trackingWrite;
trackedResponse.end = function () {
trackedResponse.end = function (...args) {
// We temporarily disable write tracking here, as .end

@@ -222,9 +184,5 @@ // can call this.write, but that write should not be

// calls it on itself too.
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
trackedResponse.write = originalWrite;
trackingStream.end.apply(trackingStream, args);
var result = originalEnd.apply(this, args);
let result = originalEnd.apply(this, args);
trackedResponse.write = trackingWrite;

@@ -238,20 +196,14 @@ return result;

function waitForCompletedResponse(response) {
return __awaiter(this, void 0, void 0, function () {
var _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_b = (_a = _(response).pick([
'id',
'statusCode',
'statusMessage'
])).assign;
_c = {
headers: response.getHeaders()
};
return [4 /*yield*/, waitForBody(response.body, response.getHeaders())];
case 1: return [2 /*return*/, _b.apply(_a, [(_c.body = _d.sent(),
_c)]).valueOf()];
}
});
return __awaiter(this, void 0, void 0, function* () {
const body = yield waitForBody(response.body, response.getHeaders());
response.timingEvents.responseSentTimestamp = response.timingEvents.responseSentTimestamp || now();
return _(response).pick([
'id',
'statusCode',
'statusMessage',
'timingEvents'
]).assign({
headers: response.getHeaders(),
body: body
}).valueOf();
});

@@ -258,0 +210,0 @@ }

@@ -10,98 +10,67 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var WebSocket = require("ws");
var url = require("url");
const WebSocket = require("ws");
const url = require("url");
// Pile of hacks to blindly forward all WS connections upstream untouched
var WebSocketHandler = /** @class */ (function () {
function WebSocketHandler(debug) {
var _this = this;
class WebSocketHandler {
constructor(debug) {
this.debug = debug;
this.wsServer = new WebSocket.Server({ noServer: true });
this.wsServer.on('connection', function (ws) {
if (_this.debug)
this.wsServer.on('connection', (ws) => {
if (this.debug)
console.log('Successfully proxying websocket streams');
_this.pipeWebSocket(ws, ws.upstreamSocket);
_this.pipeWebSocket(ws.upstreamSocket, ws);
this.pipeWebSocket(ws, ws.upstreamSocket);
this.pipeWebSocket(ws.upstreamSocket, ws);
});
}
WebSocketHandler.prototype.handleUpgrade = function (req, socket, head) {
return __awaiter(this, void 0, void 0, function () {
var _a, _b, requestedProtocol, hostname, port, path, transparentProxy, hostHeader, protocol, protocol;
return __generator(this, function (_c) {
_b = url.parse(req.url), requestedProtocol = _b.protocol, hostname = _b.hostname, port = _b.port, path = _b.path;
if (this.debug)
console.log("Handling upgrade for " + req.url);
transparentProxy = !hostname;
if (transparentProxy) {
hostHeader = req.headers.host;
_a = hostHeader.split(':'), hostname = _a[0], port = _a[1];
protocol = void 0;
if (socket.upstreamEncryption !== undefined) {
protocol = socket.upstreamEncryption ? 'wss' : 'ws';
}
else {
protocol = req.connection.encrypted ? 'wss' : 'ws';
}
this.connectUpstream(protocol + "://" + hostname + (port ? ':' + port : '') + path, req, socket, head);
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.upstreamEncryption !== undefined) {
protocol = socket.upstreamEncryption ? 'wss' : 'ws';
}
else {
protocol = requestedProtocol.replace('http', 'ws');
this.connectUpstream(protocol + "//" + hostname + (port ? ':' + port : '') + path, req, socket, head);
protocol = req.connection.encrypted ? 'wss' : 'ws';
}
return [2 /*return*/];
});
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);
}
});
};
WebSocketHandler.prototype.connectUpstream = function (url, req, socket, head) {
var _this = this;
}
connectUpstream(url, req, socket, head) {
if (this.debug)
console.log("Connecting to upstream websocket at " + url);
var upstreamSocket = new WebSocket(url);
upstreamSocket.once('open', function () {
_this.wsServer.handleUpgrade(req, socket, head, function (ws) {
console.log(`Connecting to upstream websocket at ${url}`);
const upstreamSocket = new WebSocket(url);
upstreamSocket.once('open', () => {
this.wsServer.handleUpgrade(req, socket, head, (ws) => {
ws.upstreamSocket = upstreamSocket;
_this.wsServer.emit('connection', ws);
this.wsServer.emit('connection', ws);
});
});
upstreamSocket.once('error', function (e) { return console.warn(e); });
};
WebSocketHandler.prototype.pipeWebSocket = function (inSocket, outSocket) {
var _this = this;
var onPipeFailed = function (op) { return function (err) {
upstreamSocket.once('error', (e) => console.warn(e));
}
pipeWebSocket(inSocket, outSocket) {
const onPipeFailed = (op) => (err) => {
if (!err)
return;
inSocket.close();
console.error("Websocket " + op + " failed", err);
}; };
inSocket.on('message', function (msg) { return outSocket.send(msg, onPipeFailed('message')); });
inSocket.on('close', function (num, reason) {
console.error(`Websocket ${op} failed`, err);
};
inSocket.on('message', (msg) => outSocket.send(msg, onPipeFailed('message')));
inSocket.on('close', (num, reason) => {
if (num >= 1000 && num <= 1004) {
if (_this.debug)
if (this.debug)
console.log('Successfully proxying websocket streams');

@@ -115,8 +84,7 @@ outSocket.close(num, reason);

});
inSocket.on('ping', function (data) { return outSocket.ping(data, undefined, onPipeFailed('ping')); });
inSocket.on('pong', function (data) { return outSocket.pong(data, undefined, onPipeFailed('pong')); });
};
return WebSocketHandler;
}());
inSocket.on('ping', (data) => outSocket.ping(data, undefined, onPipeFailed('ping')));
inSocket.on('pong', (data) => outSocket.pong(data, undefined, onPipeFailed('pong')));
}
}
exports.WebSocketHandler = WebSocketHandler;
//# sourceMappingURL=websocket-handler.js.map

@@ -13,52 +13,23 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference path="../../custom-typings/asynciterator.d.ts" />
var path = require("path");
var fs = require("../util/fs");
var _ = require("lodash");
var express = require("express");
var cors = require("cors");
var bodyParser = require("body-parser");
var ws = require("ws");
var apollo_server_express_1 = require("apollo-server-express");
var graphql_1 = require("graphql");
var graphql_tools_1 = require("graphql-tools");
var subscriptions_transport_ws_1 = require("subscriptions-transport-ws");
var connectWebSocketStream = require("websocket-stream");
var DuplexPassthrough = require("duplex-passthrough");
var destroyable_server_1 = require("../util/destroyable-server");
var mockttp_server_1 = require("../server/mockttp-server");
var standalone_model_1 = require("./standalone-model");
var types_1 = require("../types");
var MockttpStandalone = /** @class */ (function () {
function MockttpStandalone(options) {
if (options === void 0) { options = {}; }
var _this = this;
const path = require("path");
const fs = require("../util/fs");
const _ = require("lodash");
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const ws = require("ws");
const apollo_server_express_1 = require("apollo-server-express");
const graphql_1 = require("graphql");
const graphql_tools_1 = require("graphql-tools");
const subscriptions_transport_ws_1 = require("subscriptions-transport-ws");
const connectWebSocketStream = require("websocket-stream");
const DuplexPassthrough = require("duplex-passthrough");
const destroyable_server_1 = require("../util/destroyable-server");
const mockttp_server_1 = require("../server/mockttp-server");
const standalone_model_1 = require("./standalone-model");
const types_1 = require("../types");
class MockttpStandalone {
constructor(options = {}) {
this.app = express();

@@ -75,174 +46,133 @@ this.server = null;

this.app.use(bodyParser.json());
this.app.post('/start', function (req, res) { return __awaiter(_this, void 0, void 0, function () {
var port, mockServerOptions, _a, mockPort, mockServer, config, e_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this.debug)
console.log('Standalone starting mock server on port', req.query.port);
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
port = req.query.port ?
parseInt(req.query.port, 10) : undefined;
mockServerOptions = _.defaults({}, req.body, options.serverDefaults);
if (port != null && this.routers[port] != null) {
res.status(409).json({
error: "Cannot start: mock server is already running on port " + port
});
return [2 /*return*/];
}
return [4 /*yield*/, this.startMockServer(mockServerOptions, port)];
case 2:
_a = _b.sent(), mockPort = _a.mockPort, mockServer = _a.mockServer;
config = {
port: mockPort,
mockRoot: mockServer.url
};
res.json(config);
return [3 /*break*/, 4];
case 3:
e_1 = _b.sent();
res.status(500).json({ error: "Failed to start server: " + (e_1.message || e_1) });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
this.app.post('/start', (req, res) => __awaiter(this, void 0, void 0, function* () {
if (this.debug)
console.log('Standalone starting mock server on port', req.query.port);
try {
const port = req.query.port ?
parseInt(req.query.port, 10) : undefined;
const mockServerOptions = _.defaults({}, req.body, options.serverDefaults);
if (port != null && this.routers[port] != null) {
res.status(409).json({
error: `Cannot start: mock server is already running on port ${port}`
});
return;
}
});
}); });
const { mockPort, mockServer } = yield this.startMockServer(mockServerOptions, port);
const config = {
port: mockPort,
mockRoot: mockServer.url
};
res.json(config);
}
catch (e) {
res.status(500).json({ error: `Failed to start server: ${e.message || e}` });
}
}));
// Dynamically route to admin servers ourselves, so we can easily add/remove
// servers as we see fit later on.
this.app.use('/server/:port/', function (req, res, next) {
if (!_this.routers[req.params.port]) {
this.app.use('/server/:port/', (req, res, next) => {
if (!this.routers[req.params.port]) {
res.status(404).send('Unknown mock server');
throw new Error("Request for unknown mock server port: " + req.params.port);
throw new Error(`Request for unknown mock server port: ${req.params.port}`);
}
_this.routers[req.params.port](req, res, next);
this.routers[req.params.port](req, res, next);
});
}
MockttpStandalone.prototype.loadSchema = function (schemaFilename, mockServer, stream) {
loadSchema(schemaFilename, mockServer, stream) {
return fs.readFile(path.join(__dirname, schemaFilename), 'utf8')
.then(function (schemaString) { return graphql_tools_1.makeExecutableSchema({
.then((schemaString) => graphql_tools_1.makeExecutableSchema({
typeDefs: schemaString,
resolvers: standalone_model_1.buildStandaloneModel(mockServer, stream)
}); });
};
MockttpStandalone.prototype.start = function (listenOptions) {
if (listenOptions === void 0) { listenOptions = types_1.DEFAULT_STANDALONE_PORT; }
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.server)
throw new Error('Standalone server already running');
return [4 /*yield*/, new Promise(function (resolve, reject) {
_this.server = destroyable_server_1.default(_this.app.listen(listenOptions, resolve));
_this.server.on('upgrade', function (req, socket, head) {
var isSubscriptionRequest = req.url.match(/^\/server\/(\d+)\/subscription$/);
var isStreamRequest = req.url.match(/^\/server\/(\d+)\/stream$/);
var isMatch = isSubscriptionRequest || isStreamRequest;
if (isMatch) {
var port = parseInt(isMatch[1], 10);
var wsServer_1 = isSubscriptionRequest ?
_this.subscriptionServers[port].wsServer :
_this.streamServers[port];
if (wsServer_1) {
wsServer_1.handleUpgrade(req, socket, head, function (ws) {
wsServer_1.emit('connection', ws, req);
});
}
else {
console.warn("Websocket request for unrecognized mock server: " + port);
socket.destroy();
}
}
else {
console.warn("Unrecognized websocket request for " + req.url);
socket.destroy();
}
});
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
MockttpStandalone.prototype.startMockServer = function (options, port) {
return __awaiter(this, void 0, void 0, function () {
var mockServer, mockPort, mockServerRouter, serverSideStream, clientSideStream, schema;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockServer = new mockttp_server_1.default(_.defaults({
debug: this.debug // Use debug mode if the client requests it, or if the standalone has it set
}, options));
this.mockServers.push(mockServer);
return [4 /*yield*/, mockServer.start(port)];
case 1:
_a.sent();
mockPort = mockServer.port;
mockServerRouter = express.Router();
this.routers[mockPort] = mockServerRouter;
mockServerRouter.post('/stop', function (req, res) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, mockServer.stop()];
case 1:
_a.sent();
this.mockServers = _.reject(this.mockServers, mockServer);
delete this.routers[mockPort];
delete this.subscriptionServers[mockPort];
this.streamServers[mockPort].close();
this.streamServers[mockPort].emit('close');
delete this.streamServers[mockPort];
res.status(200).send(JSON.stringify({
success: true
}));
return [2 /*return*/];
}
}));
}
start(listenOptions = types_1.DEFAULT_STANDALONE_PORT) {
return __awaiter(this, void 0, void 0, function* () {
if (this.server)
throw new Error('Standalone server already running');
yield new Promise((resolve, reject) => {
this.server = destroyable_server_1.default(this.app.listen(listenOptions, resolve));
this.server.on('upgrade', (req, socket, head) => {
let isSubscriptionRequest = req.url.match(/^\/server\/(\d+)\/subscription$/);
let isStreamRequest = req.url.match(/^\/server\/(\d+)\/stream$/);
let isMatch = isSubscriptionRequest || isStreamRequest;
if (isMatch) {
let port = parseInt(isMatch[1], 10);
let wsServer = isSubscriptionRequest ?
this.subscriptionServers[port].wsServer :
this.streamServers[port];
if (wsServer) {
wsServer.handleUpgrade(req, socket, head, (ws) => {
wsServer.emit('connection', ws, req);
});
}); });
serverSideStream = new DuplexPassthrough(null, { objectMode: true });
clientSideStream = new DuplexPassthrough(null, { objectMode: true });
serverSideStream._writer.pipe(clientSideStream._reader);
clientSideStream._writer.pipe(serverSideStream._reader);
if (this.debug) {
clientSideStream._writer.on('data', function (d) {
console.debug('Streaming data to clients:', d.toString());
});
clientSideStream._reader.on('data', function (d) {
console.debug('Streaming data from clients:', d.toString());
});
}
this.streamServers[mockPort] = new ws.Server({ noServer: true });
this.streamServers[mockPort].on('connection', function (ws) {
var newClientStream = connectWebSocketStream(ws, { objectMode: true });
newClientStream.pipe(clientSideStream, { end: false });
clientSideStream.pipe(newClientStream);
});
this.streamServers[mockPort].on('close', function () { return clientSideStream.end(); });
return [4 /*yield*/, this.loadSchema('schema.gql', mockServer, serverSideStream)];
case 2:
schema = _a.sent();
this.subscriptionServers[mockPort] = subscriptions_transport_ws_1.SubscriptionServer.create({
schema: schema, execute: graphql_1.execute, subscribe: graphql_1.subscribe
}, {
noServer: true
});
mockServerRouter.use(bodyParser.json(), apollo_server_express_1.graphqlExpress({
schema: schema
}));
return [2 /*return*/, {
mockPort: mockPort,
mockServer: mockServer
}];
}
else {
console.warn(`Websocket request for unrecognized mock server: ${port}`);
socket.destroy();
}
}
else {
console.warn(`Unrecognized websocket request for ${req.url}`);
socket.destroy();
}
});
});
});
};
MockttpStandalone.prototype.stop = function () {
var _this = this;
}
startMockServer(options, port) {
return __awaiter(this, void 0, void 0, function* () {
const mockServer = new mockttp_server_1.default(_.defaults({
debug: this.debug // Use debug mode if the client requests it, or if the standalone has it set
}, options));
yield mockServer.start(port);
this.mockServers.push(mockServer);
const mockPort = mockServer.port;
const mockServerRouter = express.Router();
this.routers[mockPort] = mockServerRouter;
mockServerRouter.post('/stop', (req, res) => __awaiter(this, void 0, void 0, function* () {
yield mockServer.stop();
this.mockServers = _.reject(this.mockServers, mockServer);
delete this.routers[mockPort];
delete this.subscriptionServers[mockPort];
this.streamServers[mockPort].close();
this.streamServers[mockPort].emit('close');
delete this.streamServers[mockPort];
res.status(200).send(JSON.stringify({
success: true
}));
}));
const serverSideStream = new DuplexPassthrough(null, { objectMode: true });
const clientSideStream = new DuplexPassthrough(null, { objectMode: true });
serverSideStream._writer.pipe(clientSideStream._reader);
clientSideStream._writer.pipe(serverSideStream._reader);
if (this.debug) {
clientSideStream._writer.on('data', (d) => {
console.debug('Streaming data to clients:', d.toString());
});
clientSideStream._reader.on('data', (d) => {
console.debug('Streaming data from clients:', d.toString());
});
}
this.streamServers[mockPort] = new ws.Server({ noServer: true });
this.streamServers[mockPort].on('connection', (ws) => {
let newClientStream = connectWebSocketStream(ws, { objectMode: true });
newClientStream.pipe(clientSideStream, { end: false });
clientSideStream.pipe(newClientStream);
});
this.streamServers[mockPort].on('close', () => clientSideStream.end());
const schema = yield this.loadSchema('schema.gql', mockServer, serverSideStream);
this.subscriptionServers[mockPort] = subscriptions_transport_ws_1.SubscriptionServer.create({
schema, execute: graphql_1.execute, subscribe: graphql_1.subscribe
}, {
noServer: true
});
mockServerRouter.use(bodyParser.json(), apollo_server_express_1.graphqlExpress({
schema
}));
return {
mockPort,
mockServer
};
});
}
stop() {
if (!this.server)

@@ -252,9 +182,8 @@ return Promise.resolve();

this.server.destroy(),
].concat(this.mockServers.map(function (s) { return s.stop(); }))).then(function () {
_this.server = null;
].concat(this.mockServers.map((s) => s.stop()))).then(() => {
this.server = null;
});
};
return MockttpStandalone;
}());
}
}
exports.MockttpStandalone = MockttpStandalone;
//# sourceMappingURL=mockttp-standalone.js.map

@@ -11,34 +11,7 @@ #!/usr/bin/env node

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var childProcess = require("child_process");
var Mockttp = require("../main");
handleArgs(process.argv).catch(function (e) {
const _ = require("lodash");
const childProcess = require("child_process");
const Mockttp = require("../main");
handleArgs(process.argv).catch((e) => {
console.error(e);

@@ -48,74 +21,48 @@ process.exit(1);

function handleArgs(args) {
return __awaiter(this, void 0, void 0, function () {
var debug, _i, _a, i, remainingArgs;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
debug = false;
_i = 0, _a = _.range(2, args.length);
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 5];
i = _a[_i];
if (!(args[i] === '-c')) return [3 /*break*/, 3];
remainingArgs = args.slice(i + 1);
return [4 /*yield*/, runCommandWithServer(remainingArgs.join(' '), debug)];
case 2:
_b.sent();
return [2 /*return*/];
case 3:
if (args[i] === '-d') {
debug = true;
}
else {
return [3 /*break*/, 5];
}
_b.label = 4;
case 4:
_i++;
return [3 /*break*/, 1];
case 5:
console.log("Usage: mockttp -c <test command>");
process.exit(1);
return [2 /*return*/];
return __awaiter(this, void 0, void 0, function* () {
let debug = false;
for (let i of _.range(2, args.length)) {
if (args[i] === '-c') {
let remainingArgs = args.slice(i + 1);
yield runCommandWithServer(remainingArgs.join(' '), debug);
return;
}
});
else if (args[i] === '-d') {
debug = true;
}
else {
break;
}
}
console.log("Usage: mockttp -c <test command>");
process.exit(1);
});
}
function runCommandWithServer(command, debug) {
return __awaiter(this, void 0, void 0, function () {
var server, realProcess;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
server = Mockttp.getStandalone({ debug: debug });
return [4 /*yield*/, server.start()];
case 1:
_a.sent();
realProcess = childProcess.spawn(command, [], {
shell: true,
stdio: 'inherit'
});
realProcess.on('error', function (error) {
server.stop().then(function () {
console.error(error);
process.exit(1);
});
});
realProcess.on('exit', function (code, signal) {
server.stop().then(function () {
if (code == null) {
console.error('Executed process exited due to signal: ' + signal);
process.exit(1);
}
else {
process.exit(code);
}
});
});
return [2 /*return*/];
}
return __awaiter(this, void 0, void 0, function* () {
const server = Mockttp.getStandalone({ debug });
yield server.start();
let realProcess = childProcess.spawn(command, [], {
shell: true,
stdio: 'inherit'
});
realProcess.on('error', (error) => {
server.stop().then(function () {
console.error(error);
process.exit(1);
});
});
realProcess.on('exit', (code, signal) => {
server.stop().then(function () {
if (code == null) {
console.error('Executed process exited due to signal: ' + signal);
process.exit(1);
}
else {
process.exit(code);
}
});
});
});
}
//# sourceMappingURL=standalone-bin.js.map

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

*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -25,39 +14,12 @@ return new (P || (P = Promise))(function (resolve, reject) {

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var graphql_1 = require("graphql");
var graphql_subscriptions_1 = require("graphql-subscriptions");
var mock_rule_1 = require("../rules/mock-rule");
var REQUEST_RECEIVED_TOPIC = 'request-received';
var RESPONSE_COMPLETED_TOPIC = 'response-completed';
var REQUEST_ABORTED_TOPIC = 'request-aborted';
const _ = require("lodash");
const graphql_1 = require("graphql");
const graphql_subscriptions_1 = require("graphql-subscriptions");
const mock_rule_1 = require("../rules/mock-rule");
const REQUEST_RECEIVED_TOPIC = 'request-received';
const RESPONSE_COMPLETED_TOPIC = 'response-completed';
const REQUEST_ABORTED_TOPIC = 'request-aborted';
function astToObject(ast) {
return _.zipObject(ast.fields.map(function (f) { return f.name.value; }), ast.fields.map(function (f) { return parseAnyAst(f.value); }));
return _.zipObject(ast.fields.map((f) => f.name.value), ast.fields.map((f) => parseAnyAst(f.value)));
}

@@ -83,26 +45,18 @@ function parseAnyAst(ast) {

function buildMockedEndpointData(endpoint) {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = {
id: endpoint.id
};
return [4 /*yield*/, endpoint.getSeenRequests()];
case 1: return [2 /*return*/, (_a.seenRequests = _b.sent(),
_a)];
}
});
return __awaiter(this, void 0, void 0, function* () {
return {
id: endpoint.id,
seenRequests: yield endpoint.getSeenRequests()
};
});
}
var ScalarResolvers = {
const ScalarResolvers = {
RequestMatcher: new graphql_1.GraphQLScalarType({
name: 'RequestMatcher',
description: 'Matcher for requests',
serialize: function (value) {
serialize: (value) => {
throw new Error('Matchers are input only values');
},
parseValue: function (v) { return v; },
parseLiteral: function (ast) {
parseValue: (v) => v,
parseLiteral(ast) {
if (ast.kind === graphql_1.Kind.OBJECT) {

@@ -118,7 +72,7 @@ return astToObject(ast);

description: 'Handler for requests',
serialize: function (value) {
serialize: (value) => {
throw new Error('Handlers are input only values');
},
parseValue: function (v) { return v; },
parseLiteral: function (ast) {
parseValue: (v) => v,
parseLiteral(ast) {
if (ast.kind === graphql_1.Kind.OBJECT) {

@@ -134,7 +88,7 @@ return astToObject(ast);

description: 'Completion checkers for requests',
serialize: function (value) {
serialize: (value) => {
throw new Error('Completion checkers are input only values');
},
parseValue: function (v) { return v; },
parseLiteral: function (ast) {
parseValue: (v) => v,
parseLiteral(ast) {
if (ast.kind === graphql_1.Kind.OBJECT) {

@@ -150,4 +104,4 @@ return astToObject(ast);

description: 'A JSON entity, serialized as a simple JSON string',
serialize: function (value) { return JSON.stringify(value); },
parseValue: function (input) { return JSON.parse(input); },
serialize: (value) => JSON.stringify(value),
parseValue: (input) => JSON.parse(input),
parseLiteral: parseAnyAst

@@ -158,4 +112,4 @@ }),

description: 'Wildcard Anything! Here be dragons',
serialize: function (value) { return JSON.stringify(value); },
parseValue: function (input) { return JSON.parse(input); },
serialize: (value) => JSON.stringify(value),
parseValue: (input) => JSON.parse(input),
parseLiteral: parseAnyAst

@@ -166,6 +120,6 @@ }),

description: 'A buffer',
serialize: function (value) {
serialize: (value) => {
return value.toString('base64');
},
parseValue: function (input) {
parseValue: (input) => {
return Buffer.from(input, 'base64');

@@ -177,5 +131,4 @@ },

function buildStandaloneModel(mockServer, stream) {
var _this = this;
var pubsub = new graphql_subscriptions_1.PubSub();
mockServer.on('request', function (request) {
const pubsub = new graphql_subscriptions_1.PubSub();
mockServer.on('request', (request) => {
pubsub.publish(REQUEST_RECEIVED_TOPIC, {

@@ -185,3 +138,3 @@ requestReceived: request

});
mockServer.on('response', function (response) {
mockServer.on('response', (response) => {
pubsub.publish(RESPONSE_COMPLETED_TOPIC, {

@@ -191,3 +144,3 @@ responseCompleted: response

});
mockServer.on('abort', function (request) {
mockServer.on('abort', (request) => {
pubsub.publish(REQUEST_ABORTED_TOPIC, {

@@ -197,9 +150,8 @@ requestAborted: request

});
return __assign({ Query: {
mockedEndpoints: function () {
return Object.assign({ Query: {
mockedEndpoints: () => {
return Promise.all(mockServer.mockedEndpoints.map(buildMockedEndpointData));
},
mockedEndpoint: function (__, _a) {
var id = _a.id;
var endpoint = _.find(mockServer.mockedEndpoints, function (endpoint) {
mockedEndpoint: (__, { id }) => {
let endpoint = _.find(mockServer.mockedEndpoints, (endpoint) => {
return endpoint.id === id;

@@ -212,11 +164,6 @@ });

}, Mutation: {
addRule: function (__, _a) {
var input = _a.input;
return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_b) {
return [2 /*return*/, mockServer.addRule(mock_rule_1.deserializeRuleData(input, { clientStream: stream }))];
});
});
},
reset: function () {
addRule: (__, { input }) => __awaiter(this, void 0, void 0, function* () {
return mockServer.addRule(mock_rule_1.deserializeRuleData(input, { clientStream: stream }));
}),
reset: () => {
mockServer.reset();

@@ -227,16 +174,16 @@ return true;

requestReceived: {
subscribe: function () { return pubsub.asyncIterator(REQUEST_RECEIVED_TOPIC); }
subscribe: () => pubsub.asyncIterator(REQUEST_RECEIVED_TOPIC)
},
responseCompleted: {
subscribe: function () { return pubsub.asyncIterator(RESPONSE_COMPLETED_TOPIC); }
subscribe: () => pubsub.asyncIterator(RESPONSE_COMPLETED_TOPIC)
},
requestAborted: {
subscribe: function () { return pubsub.asyncIterator(REQUEST_ABORTED_TOPIC); }
subscribe: () => pubsub.asyncIterator(REQUEST_ABORTED_TOPIC)
},
}, Request: {
body: function (request) {
body: (request) => {
return request.body.buffer;
}
}, Response: {
body: function (response) {
body: (response) => {
return response.body.buffer;

@@ -243,0 +190,0 @@ }

@@ -18,4 +18,10 @@ /**

export interface Headers {
[key: string]: string;
'content-type'?: string;
'user-agent'?: string;
[key: string]: undefined | string | string[];
}
interface RequestHeaders extends Headers {
host: string;
cookie?: string;
}
export interface Request {

@@ -27,3 +33,3 @@ protocol: string;

hostname: string;
headers: Headers;
headers: RequestHeaders;
}

@@ -34,2 +40,3 @@ export interface OngoingRequest extends Request {

body: ParsedBody;
timingEvents: TimingEvents;
}

@@ -57,3 +64,12 @@ export interface ParsedBody {

body: CompletedBody;
timingEvents: TimingEvents;
}
export interface TimingEvents {
startTime: number;
startTimestamp: number;
bodyReceivedTimestamp?: number;
headersSentTimestamp?: number;
responseSentTimestamp?: number;
abortedTimestamp?: number;
}
export interface OngoingResponse extends express.Response {

@@ -63,2 +79,3 @@ id: string;

body: ParsedBody;
timingEvents: TimingEvents;
}

@@ -71,2 +88,3 @@ export interface CompletedResponse {

body: CompletedBody;
timingEvents: TimingEvents;
}

@@ -100,1 +118,2 @@ /**

}
export {};

@@ -8,5 +8,5 @@ "use strict";

function destroyable(server) {
var connections = {};
const connections = {};
server.on('connection', function (conn) {
var key = conn.remoteAddress + ':' + conn.remotePort;
const key = conn.remoteAddress + ':' + conn.remotePort;
connections[key] = conn;

@@ -18,3 +18,3 @@ conn.on('close', function () {

server.on('secureConnection', function (conn) {
var key = conn.remoteAddress + ':' + conn.remotePort;
const key = conn.remoteAddress + ':' + conn.remotePort;
connections[key] = conn;

@@ -26,4 +26,4 @@ conn.on('close', function () {

server.destroy = function () {
return new Promise(function (resolve, reject) {
server.close(function (err) {
return new Promise((resolve, reject) => {
server.close((err) => {
if (err)

@@ -34,3 +34,3 @@ reject(err);

});
for (var key in connections) {
for (let key in connections) {
connections[key].destroy();

@@ -37,0 +37,0 @@ }

@@ -6,6 +6,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
var fs = require("fs");
const fs = require("fs");
function readFile(filename, encoding) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, encoding, function (err, contents) {
return new Promise((resolve, reject) => {
fs.readFile(filename, encoding, (err, contents) => {
if (err)

@@ -12,0 +12,0 @@ reject(err);

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

Object.defineProperty(exports, "__esModule", { value: true });
var normalizeUrl = require("normalize-url");
const normalizeUrl = require("normalize-url");
function normalize(url) {

@@ -9,0 +9,0 @@ return normalizeUrl(url, {

@@ -13,41 +13,7 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
function filter(array, test) {
return __awaiter(this, void 0, void 0, function () {
var testResults;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, Promise.all(array.map(test))];
case 1:
testResults = _a.sent();
return [2 /*return*/, array.filter(function (v, i) { return testResults[i]; })];
}
});
return __awaiter(this, void 0, void 0, function* () {
let testResults = yield Promise.all(array.map(test));
return array.filter((v, i) => testResults[i]);
});

@@ -54,0 +20,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var Serializable = /** @class */ (function () {
function Serializable() {
}
Serializable.prototype.serialize = function () {
const _ = require("lodash");
class Serializable {
serialize() {
// By default, we assume data is transferrable as-is
return this;
};
Serializable.deserialize = function (data) {
}
static deserialize(data) {
// By default, we assume we just need to assign the right prototype
return _.create(this.prototype, data);
};
return Serializable;
}());
}
}
exports.Serializable = Serializable;
function deserialize(data, lookup, options) {
if (options === void 0) { options = {}; }
var type = data.type;
function deserialize(data, lookup, options = {}) {
let type = data.type;
return lookup[type].deserialize(data, options);

@@ -22,0 +18,0 @@ }

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

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -43,11 +16,9 @@ // Grab the first byte of a stream

function peekFirstByte(socket) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve) {
socket.once('data', function (data) {
socket.pause();
socket.unshift(data);
resolve(data[0]);
});
})];
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => {
socket.once('data', (data) => {
socket.pause();
socket.unshift(data);
resolve(data[0]);
});
});

@@ -54,0 +25,0 @@ });

@@ -13,35 +13,8 @@ "use strict";

};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var uuid = require("uuid/v4");
var forge = require("node-forge");
var pki = forge.pki, md = forge.md, encode64 = forge.util.encode64;
var fs = require("./fs");
const _ = require("lodash");
const uuid = require("uuid/v4");
const forge = require("node-forge");
const { pki, md, util: { encode64 } } = forge;
const fs = require("./fs");
/**

@@ -56,4 +29,3 @@ * Generate a CA certificate for mocking HTTPS.

*/
function generateCACertificate(options) {
if (options === void 0) { options = {}; }
function generateCACertificate(options = {}) {
options = _.defaults({}, options, {

@@ -63,8 +35,11 @@ commonName: 'Mockttp Testing CA - DO NOT TRUST - TESTING ONLY',

});
var keyPair = pki.rsa.generateKeyPair(options.bytes);
var cert = pki.createCertificate();
const keyPair = pki.rsa.generateKeyPair(options.bytes);
const cert = pki.createCertificate();
cert.publicKey = keyPair.publicKey;
cert.serialNumber = uuid().replace(/-/g, '');
cert.validity.notBefore = new Date();
// Make it valid for the last 24h - helps in cases where clocks slightly disagree
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
cert.validity.notAfter = new Date();
// Valid for the next year by default.
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

@@ -87,3 +62,3 @@ cert.setSubject([{ name: 'commonName', value: options.commonName }]);

function generateSPKIFingerprint(certPem) {
var cert = pki.certificateFromPem(certPem);
let cert = pki.certificateFromPem(certPem);
return encode64(pki.getPublicKeyFingerprint(cert.publicKey, {

@@ -96,31 +71,22 @@ type: 'SubjectPublicKeyInfo',

function getCA(options) {
return __awaiter(this, void 0, void 0, function () {
var httpsOptions, pathOptions_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(options.key && options.cert)) return [3 /*break*/, 1];
httpsOptions = options;
return [3 /*break*/, 4];
case 1:
if (!(options.keyPath && options.certPath)) return [3 /*break*/, 3];
pathOptions_1 = options;
return [4 /*yield*/, Promise.all([
fs.readFile(pathOptions_1.keyPath, 'utf8'),
fs.readFile(pathOptions_1.certPath, 'utf8')
]).then(function (_a) {
var keyContents = _a[0], certContents = _a[1];
return ({
key: keyContents,
cert: certContents,
keyLength: pathOptions_1.keyLength
});
})];
case 2:
httpsOptions = _a.sent();
return [3 /*break*/, 4];
case 3: throw new Error('Unrecognized https options: you need to provide either a keyPath & certPath, or a key & cert.');
case 4: return [2 /*return*/, new CA(httpsOptions.key, httpsOptions.cert, httpsOptions.keyLength || 1024)];
}
});
return __awaiter(this, void 0, void 0, function* () {
let httpsOptions;
if (options.key && options.cert) {
httpsOptions = options;
}
else if (options.keyPath && options.certPath) {
let pathOptions = options;
httpsOptions = yield Promise.all([
fs.readFile(pathOptions.keyPath, 'utf8'),
fs.readFile(pathOptions.certPath, 'utf8')
]).then(([keyContents, certContents]) => ({
key: keyContents,
cert: certContents,
keyLength: pathOptions.keyLength
}));
}
else {
throw new Error('Unrecognized https options: you need to provide either a keyPath & certPath, or a key & cert.');
}
return new CA(httpsOptions.key, httpsOptions.cert, httpsOptions.keyLength || 1024);
});

@@ -134,5 +100,5 @@ }

// it's ok - if anybody can steal this, they can steal the CA cert anyway.
var KEY_PAIR;
var CA = /** @class */ (function () {
function CA(caKey, caCert, keyLength) {
let KEY_PAIR;
class CA {
constructor(caKey, caCert, keyLength) {
this.caKey = pki.privateKeyFromPem(caKey);

@@ -147,12 +113,14 @@ this.caCert = pki.certificateFromPem(caCert);

}
CA.prototype.generateCertificate = function (domain) {
generateCertificate(domain) {
// TODO: Expire domains from the cache? Based on their actual expiry?
if (this.certCache[domain])
return this.certCache[domain];
var cert = pki.createCertificate();
let cert = pki.createCertificate();
cert.publicKey = KEY_PAIR.publicKey;
cert.serialNumber = uuid().replace(/-/g, '');
cert.validity.notBefore = new Date();
// Make it valid for the last 24h - helps in cases where clocks slightly disagree.
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
cert.validity.notAfter = new Date();
// TODO: shorten this expiry
// Valid for the next year by default. TODO: Shorten (and expire the cache) automatically.
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

@@ -179,3 +147,3 @@ cert.setSubject([

cert.sign(this.caKey, md.sha256.create());
var generatedCertificate = {
const generatedCertificate = {
key: pki.privateKeyToPem(KEY_PAIR.privateKey),

@@ -187,6 +155,5 @@ cert: pki.certificateToPem(cert),

return generatedCertificate;
};
return CA;
}());
}
}
exports.CA = CA;
//# sourceMappingURL=tls.js.map
{
"name": "mockttp",
"version": "0.12.6",
"version": "0.13.0",
"description": "Mock HTTP server for testing HTTP clients and stubbing webservices",

@@ -100,3 +100,3 @@ "main": "dist/main.js",

"typedoc-plugin-external-module-name": "^1.1.1",
"typescript": "3.1.6",
"typescript": "3.3.3333",
"url-search-params": "^0.10.0",

@@ -128,5 +128,6 @@ "webpack": "^3.7.1",

"normalize-url": "^1.9.1",
"portfinder": "^1.0.9",
"performance-now": "^2.1.0",
"portfinder": "^1.0.13",
"subscriptions-transport-ws": "^0.9.4",
"typed-error": "^2.0.0",
"typed-error": "^3.0.2",
"universal-websocket-client": "^1.0.2",

@@ -133,0 +134,0 @@ "uuid": "^3.1.0",

@@ -5,3 +5,3 @@ /**

import TypedError = require('typed-error');
import { TypedError } from 'typed-error';
import getFetchPonyfill = require('fetch-ponyfill');

@@ -272,3 +272,4 @@ import _ = require('lodash');

headers,
body
body,
timingEvents
}

@@ -285,3 +286,4 @@ }`

headers,
body
body,
timingEvents
}

@@ -302,3 +304,4 @@ }`

headers,
body
body,
timingEvents
}

@@ -313,6 +316,9 @@ }`

const data = (<any> value.data)[queryResultName];
// TODO: Get a proper graphql client that does all this automatically from the schema itself
if (data.headers) {
// TODO: Get a proper graphql client that does this automatically from the schema itself
data.headers = JSON.parse(data.headers);
}
if (data.timingEvents) {
data.timingEvents = JSON.parse(data.timingEvents);
}
if (data.body) {

@@ -319,0 +325,0 @@ data.body = buildBodyReader(Buffer.from(data.body, 'base64'), data.headers);

@@ -64,5 +64,3 @@ /**

body?: string;
headers?: {
[key: string]: string;
};
headers?: Headers;
}

@@ -69,0 +67,0 @@

@@ -136,7 +136,9 @@ /**

buildMatcher() {
return _.assign(async (request: OngoingRequest) =>
!!request.headers["content-type"] &&
request.headers["content-type"].indexOf("application/x-www-form-urlencoded") !== -1 &&
_.isMatch(await request.body.asFormData(), this.formData)
, { explain: () => `with form data including ${JSON.stringify(this.formData)}` });
return _.assign(async (request: OngoingRequest) => {
const contentType = request.headers['content-type'];
return !!contentType &&
contentType.indexOf("application/x-www-form-urlencoded") !== -1 &&
_.isMatch(await request.body.asFormData(), this.formData)
}, { explain: () => `with form data including ${JSON.stringify(this.formData)}` });
}

@@ -143,0 +145,0 @@ }

@@ -73,5 +73,5 @@ /**

// Start recording before the data starts piping, so we don't miss anything.
let buffer = req.body.asBuffer();
req.body.asBuffer();
await handler.apply(this, handlerArgs);
await handler.apply(this, <any> handlerArgs);

@@ -78,0 +78,0 @@ return waitForCompletedRequest(req);

@@ -11,2 +11,3 @@ /**

import cors = require("cors");
import now = require("performance-now");
import _ = require("lodash");

@@ -77,8 +78,3 @@

const port = (portParam || await new Promise<number>((resolve, reject) => {
portfinder.getPort((err, port) => {
if (err) reject(err);
else resolve(port);
});
}));
const port = (portParam || await portfinder.getPortPromise());

@@ -98,3 +94,3 @@ if (this.debug) console.log(`Starting mock server on port ${port}`);

return new Promise<void>((resolve) => {
return new Promise<void>((resolve, reject) => {
this.server!.on('listening', resolve);

@@ -111,3 +107,3 @@ this.server!.on('error', (e: any) => {

} else {
throw e;
reject(e);
}

@@ -179,3 +175,5 @@ });

.then((req: CompletedRequest) => {
this.eventEmitter.emit('request', req);
this.eventEmitter.emit('request', Object.assign(req, {
timingEvents: _.clone(req.timingEvents)
}));
})

@@ -190,3 +188,5 @@ .catch(console.error);

.then((res: CompletedResponse) => {
this.eventEmitter.emit('response', res);
this.eventEmitter.emit('response', Object.assign(res, {
timingEvents: _.clone(res.timingEvents)
}));
})

@@ -199,13 +199,17 @@ .catch(console.error);

const req = await waitForCompletedRequest(request);
this.eventEmitter.emit('abort', req);
this.eventEmitter.emit('abort', Object.assign(req, {
timingEvents: _.clone(req.timingEvents)
}));
}
private async handleRequest(request: OngoingRequest, rawResponse: express.Response) {
if (this.debug) console.log(`Handling request for ${request.url}`);
private async handleRequest(rawRequest: express.Request, rawResponse: express.Response) {
if (this.debug) console.log(`Handling request for ${rawRequest.url}`);
const response = trackResponse(rawResponse);
const timingEvents = { startTime: Date.now(), startTimestamp: now() };
const response = trackResponse(rawResponse, timingEvents);
const id = uuid();
request.id = id;
const request = <OngoingRequest>Object.assign(rawRequest, { id: id, timingEvents });
response.id = id;

@@ -219,2 +223,3 @@

if (result === null && ((request as any).aborted !== false)) {
request.timingEvents.abortedTimestamp = now();
this.announceAbortAsync(request);

@@ -309,3 +314,4 @@ result = 'aborted';

let isFormRequest = !!request.headers["content-type"] && request.headers["content-type"].indexOf("application/x-www-form-urlencoded") > -1;
const contentType = request.headers['content-type'];
let isFormRequest = !!contentType && contentType.indexOf("application/x-www-form-urlencoded") > -1;
let formBody = await request.body.asFormData().catch(() => undefined);

@@ -312,0 +318,0 @@

@@ -11,2 +11,3 @@ /**

import * as brotliDecompress from 'brotli/decompress';
import now = require("performance-now");

@@ -20,3 +21,4 @@ import {

ParsedBody,
CompletedBody
CompletedBody,
TimingEvents
} from "../types";

@@ -81,3 +83,10 @@

export const handleContentEncoding = (body: Buffer, encoding?: string) => {
export const handleContentEncoding = (body: Buffer, encoding?: string | string[]): Buffer => {
if (_.isArray(encoding) || (typeof encoding === 'string' && encoding.indexOf(', ') >= 0)) {
const encodings = typeof encoding === 'string' ? encoding.split(', ').reverse() : encoding;
return encodings.reduce((content, nextEncoding) => {
return handleContentEncoding(content, nextEncoding);
}, body);
}
if (encoding === 'gzip' || encoding === 'x-gzip') {

@@ -148,2 +157,5 @@ return zlib.gunzipSync(body);

export async function waitForCompletedRequest(request: OngoingRequest): Promise<CompletedRequest> {
const body = await waitForBody(request.body, request.headers);
const bodyReceivedTimestamp = request.timingEvents.bodyReceivedTimestamp || now();
return _(request).pick([

@@ -158,7 +170,8 @@ 'id',

]).assign({
body: await waitForBody(request.body, request.headers)
body: body,
timingEvents: Object.assign(request.timingEvents, { bodyReceivedTimestamp })
}).valueOf();
}
export function trackResponse(response: express.Response): OngoingResponse {
export function trackResponse(response: express.Response, timingEvents: TimingEvents): OngoingResponse {
let trackedResponse = <OngoingResponse> response;

@@ -170,8 +183,20 @@ if (!trackedResponse.getHeaders) {

trackedResponse.timingEvents = timingEvents;
// Headers are sent when .writeHead or .write() are first called
const trackingStream = new stream.PassThrough();
const originalWriteHeader = trackedResponse.writeHead;
const originalWrite = trackedResponse.write;
const originalEnd = trackedResponse.end;
const trackingWrite = function (this: typeof trackedResponse, ...args: any[]) {
trackedResponse.writeHead = function (this: typeof trackedResponse, ...args: any) {
if (!timingEvents.headersSentTimestamp) {
timingEvents.headersSentTimestamp = now();
}
return originalWriteHeader.apply(this, args);
}
const trackingWrite = function (this: typeof trackedResponse, ...args: any) {
trackingStream.write.apply(trackingStream, args);

@@ -183,3 +208,3 @@ return originalWrite.apply(this, args);

trackedResponse.end = function (...args: any[]) {
trackedResponse.end = function (...args: any) {
// We temporarily disable write tracking here, as .end

@@ -205,10 +230,14 @@ // can call this.write, but that write should not be

export async function waitForCompletedResponse(response: OngoingResponse): Promise<CompletedResponse> {
const body = await waitForBody(response.body, response.getHeaders());
response.timingEvents.responseSentTimestamp = response.timingEvents.responseSentTimestamp || now();
return _(response).pick([
'id',
'statusCode',
'statusMessage'
'statusMessage',
'timingEvents'
]).assign({
headers: response.getHeaders(),
body: await waitForBody(response.body, response.getHeaders())
body: body
}).valueOf();
}

@@ -151,4 +151,4 @@ /**

}, options));
await mockServer.start(port);
this.mockServers.push(mockServer);
await mockServer.start(port);

@@ -155,0 +155,0 @@ const mockPort = mockServer.port!;

@@ -21,5 +21,18 @@ /**

export interface Headers {
[key: string]: string;
// An arbitrary set of headers that are known to
// only ever appear once (for valid requests).
'content-type'?: string;
'user-agent'?: string;
// In general there may be 0+ of any header
[key: string]: undefined | string | string[];
}
interface RequestHeaders extends Headers {
// An arbitrary set of headers that are known to
// only ever appear once (for legal requests).
host: string;
cookie?: string;
}
export interface Request {

@@ -32,3 +45,3 @@ protocol: string;

headers: Headers;
headers: RequestHeaders;
}

@@ -41,2 +54,3 @@

body: ParsedBody;
timingEvents: TimingEvents;
}

@@ -64,4 +78,18 @@

body: CompletedBody;
timingEvents: TimingEvents;
}
export interface TimingEvents {
// Milliseconds since unix epoch
startTime: number;
// High-precision floating-point monotonically increasing timestamps.
// Comparable and precise, but not related to specific current time.
startTimestamp: number; // When the request was initially received
bodyReceivedTimestamp?: number; // When the request body was fully received
headersSentTimestamp?: number; // When the response headers were sent
responseSentTimestamp?: number; // When the response was fully completed
abortedTimestamp?: number; // When the request was aborted
}
export interface OngoingResponse extends express.Response {

@@ -71,2 +99,3 @@ id: string;

body: ParsedBody;
timingEvents: TimingEvents;
}

@@ -80,2 +109,3 @@

body: CompletedBody;
timingEvents: TimingEvents;
}

@@ -82,0 +112,0 @@

@@ -56,3 +56,7 @@ /**

cert.validity.notBefore = new Date();
// Make it valid for the last 24h - helps in cases where clocks slightly disagree
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
cert.validity.notAfter = new Date();
// Valid for the next year by default.
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

@@ -151,4 +155,7 @@

cert.validity.notBefore = new Date();
// Make it valid for the last 24h - helps in cases where clocks slightly disagree.
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
cert.validity.notAfter = new Date();
// TODO: shorten this expiry
// Valid for the next year by default. TODO: Shorten (and expire the cache) automatically.
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

@@ -155,0 +162,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

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

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 too big to display

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

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

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

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

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc