UNPKG

yakbak

Version:

Record and playback HTTP responses

100 lines (80 loc) 2.72 kB
// Copyright 2019 SmugMug, Inc. // Licensed under the terms of the MIT license. Please see LICENSE file in the project root for terms. var Promise = require('bluebird'); var messageHash = require('incoming-message-hash'); var assert = require('assert'); var mkdirp = require('mkdirp'); var path = require('path'); var buffer = require('./lib/buffer'); var proxy = require('./lib/proxy'); var record = require('./lib/record'); var curl = require('./lib/curl'); var debug = require('debug')('yakbak:server'); /** * Returns a new yakbak proxy middleware. * @param {String} host The hostname to proxy to * @param {Object} opts * @param {String} opts.dirname The tapes directory * @param {Boolean} opts.noRecord if true, requests will return a 404 error if the tape doesn't exist * @returns {Function} */ module.exports = function (host, opts) { assert(opts.dirname, 'You must provide opts.dirname'); return function (req, res) { mkdirp.sync(opts.dirname); debug('req', req.url); return buffer(req).then(function (body) { var file = path.join(opts.dirname, tapename(req, body)); return Promise.try(function () { return require.resolve(file); }).catch(ModuleNotFoundError, function (/* err */) { if (opts.noRecord) { throw new RecordingDisabledError('Recording Disabled'); } else { return proxy(req, body, host).then(function (pres) { return record(pres.req, pres, file); }); } }); }).then(function (file) { return require(file); }).then(function (tape) { return tape(req, res); }).catch(RecordingDisabledError, function (err) { /* eslint-disable no-console */ console.log('An HTTP request has been made that yakbak does not know how to handle'); console.log(curl.request(req)); /* eslint-enable no-console */ res.statusCode = err.status; res.end(err.message); }); }; /** * Returns the tape name for `req`. * @param {http.IncomingMessage} req * @param {Array.<Buffer>} body * @returns {String} */ function tapename(req, body) { var hash = opts.hash || messageHash.sync; return hash(req, Buffer.concat(body)) + '.js'; } }; /** * Bluebird error predicate for matching module not found errors. * @param {Error} err * @returns {Boolean} */ function ModuleNotFoundError(err) { return err.code === 'MODULE_NOT_FOUND'; } /** * Error class that is thrown when an unmatched request * is encountered in noRecord mode * @constructor */ function RecordingDisabledError(message) { this.message = message; this.status = 404; } RecordingDisabledError.prototype = Object.create(Error.prototype);