UNPKG

postmailer

Version:

HTTP POST -> SMTP proxy, as Express middleware

142 lines (126 loc) 3.84 kB
var crypto = require('crypto'); var WebSocketServer = require('ws').Server; function parseHeaders(headers) { var matches = headers.match(/(^|\n)[a-z\-]+:([^\r\n]|\r?\n[ \t])+/ig); if (!matches) return null; var result = {}; matches.forEach(function (match) { var key = match.split(':', 1)[0]; var value = match.substring(key.length + 1).replace(/(^\s+)/, '').replace(/\s+$/, ''); key = key.toLowerCase().replace('\n', ''); value = value.replace(/\r?\n[ \t]+/g, ' '); result[key] = value; }); return result; } function headersToString(headers) { var result = ''; var keys = Object.keys(headers); keys.sort(); keys.forEach(function (key) { var value = headers[key].replace(/(^\s+|\s+$)/g, ''); key = key.replace(/(^|\-)./g, function (s) { return s.toUpperCase(); }); result += key + ': ' + value + '\r\n'; }); return result; } function matchRequest(hub, request) { // TODO: additionally add 'wss' if it's secure var url = 'ws://' + (request.headers['host']) + request.url.replace(/\?.*/, ''); // Reconstruct (without query) return hub.match(url); return url; } function handleChat(hub, options) { options = options || {}; options.verifyClient = function (info) { return !!matchRequest(hub, info.req); }; var wsServer = new WebSocketServer(options); var connectionIdMap = {}; hub.handle('chat', function (chat, next) { var targetUrls = chat.target.urls; var connection = null; for (var i = 0; i < targetUrls.length; i++) { connection = connectionIdMap[targetUrls[i]]; if (connection) break; } if (!connection) return next(); var headers = headersToString(chat.headers); var prefix = ''; var message = chat.message; if (headers && headers !== connection.sentHeaders) { connection.sentHeaders = headers; prefix = headers + '\r\n'; } else if (/^([^\r\n]+\r?\n)*\r?\n/.test(message.toString('ascii'))) { prefix = '\r\n'; } if (prefix) { if (typeof message === 'string') { message = prefix + message; } else { message = Buffer.concat([new Buffer(prefix, 'ascii'), message]); } } connection.socket.send(message); }); wsServer.on('connection', function (ws) { // TODO: assemble ws:// URL from host and match on that var target = matchRequest(hub, ws.upgradeReq); if (!target) return ws.close(); // Events, and management within the map var connectionId = 'chat:' + crypto.randomBytes(24).toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); connectionIdMap[connectionId] = { socket: ws, sentHeaders: 'Content-Type: text/plain' }; var source = { url: connectionId, name: 'WebSocket chat' }; hub.emit('chat-open', { source: source, target: target }); ws.on('close', function () { hub.emit('chat-close', { source: source, target: target }); delete connectionIdMap[connectionId]; }); ws.on('error', function (error) { hub.emit('chat-error', { source: source, target: target, error: error }); hub.emit('error', error); delete connectionIdMap[connectionId]; }); var latestHeaders = parseHeaders('Content-Type: text/plain'); var messageCounter = 0; ws.on('message', function (message) { var stringMessage = (typeof message === 'string') ? message : message.toString('ascii'); var headerMatch = stringMessage.match(/^([^\r\n]+\r?\n)*\r?\n/); if (headerMatch) { latestHeaders = parseHeaders(headerMatch[0]) || latestHeaders; if (typeof message === 'string') { message = message.substring(headerMatch[0].length); } else { message = message.slice(headerMatch[0].length); } } stringMessage = null; hub.emit('chat', { target: target, source: source, headers: JSON.parse(JSON.stringify(latestHeaders)), message: message }); }); }); } module.exports = handleChat; return;