UNPKG

whistle

Version:

HTTP, HTTP2, HTTPS, Websocket debugging proxy

163 lines (155 loc) 4.49 kB
require('../util/patch'); var express = require('express'); var bodyParser = require('body-parser'); var multer = require('multer2'); var fs = require('fs'); var path = require('path'); var Buffer = require('safe-buffer').Buffer; var util = require('./util'); var extractSaz = require('./extract-saz'); var generateSaz = require('./generate-saz'); var getServer = require('hagent').getServer; var SESSIONS_FILE_RE = /\.(txt|json|saz)$/i; var TEMP_FILES_PATH; var LIMIT_SIZE = 1024 * 1024 * 128; var MAX_TEMP_SIZE = 1024 * 1024 * 10; var TEMP_FILE_RE = /^[\da-f]{64}$/; var storage = multer.memoryStorage(); var upload = multer({ storage: storage, fieldSize: LIMIT_SIZE }); function statRetry(filepath, callback, retry) { fs.stat(filepath, function(e, stat) { if (!e || retry) { return callback(e, stat); } statRetry(filepath, callback, true); }); } function writeRetry(filepath, data, callback, retry) { fs.writeFile(filepath, data, function(e) { if (!e || retry) { return callback(e); } writeRetry(filepath, data, callback, true); }); } function writeFile(filepath, data, callback) { statRetry(filepath, function(e, stat) { if (!stat || !stat.isFile()) { return writeRetry(filepath, data, callback); } callback(); }); } function getContent(options) { var value = options.value; if (value && typeof value === 'string') { return value; } var base64 = options.base64; if (base64) { try { return Buffer.from(base64, 'base64'); } catch(e) {} } return ''; } function sessionsHandler() { var app = express(); app.use(function (req, res, next) { req.on('error', abort); res.on('error', abort); function abort() { res.destroy(); } next(); }); app.get('/cgi-bin/sessions/get-temp-file', function(req, res) { var filename = req.query.filename; if (!TEMP_FILE_RE.test(filename)) { return res.json({ ec: 0 }); } filename = path.join(TEMP_FILES_PATH, filename); statRetry(filename, function(err, stat) { if (err ? err.code === 'ENOENT' : !stat.isFile()) { return res.json({ ec: 0 }); } if (err) { return res.json({ ec: 2, em: err.message || 'Error' }); } if (stat.size > MAX_TEMP_SIZE) { return res.json({ ec: 0, em: 'File is too large to load.' }); } fs.readFile(filename, function(e, data) { if (e) { return res.json({ ec: 2, em: e.message || 'Error' }); } res.json({ ec: 0, value: data + '' }); }); }); }); app.use('/cgi-bin/sessions/create-temp-file', bodyParser.json({ limit: 1024 * 1024 * 72 }), function(req, res) { var value = getContent(req.body); var filename = util.getHexHash(value); writeFile(path.join(TEMP_FILES_PATH, filename), value, function(e) { if (e) { return res.json({ ec: 2, em: e.message }); } res.json({ ec: 0, filepath: 'temp/' + filename }); }); } ); app.use( '/cgi-bin/sessions/import', upload.single('importSessions'), function (req, res) { var file = req.file; var suffix; if (file && SESSIONS_FILE_RE.test(file.originalname)) { suffix = RegExp.$1.toLowerCase(); } if (!suffix || !Buffer.isBuffer(file.buffer)) { return res.json([]); } if (suffix !== 'saz') { var sessions = util.parseJSON(file.buffer + ''); return res.json(Array.isArray(sessions) ? sessions : []); } try { extractSaz(file.buffer, res.json.bind(res)); } catch (e) { res.status(500).send(e.stack); } } ); app.use(bodyParser.urlencoded({ extended: true, limit: LIMIT_SIZE })); app.use(bodyParser.json()); app.use('/cgi-bin/sessions/export', function (req, res) { var body = req.body; var type = body.exportFileType; var download = function(content) { res.attachment(util.getFilename(type, body.exportFilename)).send(content || body.sessions); }; if (type !== 'Fiddler') { return download(); } generateSaz(body, function(e, body) { if (e) { return res.status(500).send(e.stack); } download(body); }); }); return app; } module.exports = function (options, callback) { TEMP_FILES_PATH = options.TEMP_FILES_PATH; getServer(function (server, port) { server.on('request', sessionsHandler()); callback(null, { port: port }); }); };