| var http = require('http'), |
| https = require('https'), |
| common = require('../common'); |
| |
| /*! |
| * Array of passes. |
| * |
| * A `pass` is just a function that is executed on `req, socket, options` |
| * so that you can easily add new checks while still keeping the base |
| * flexible. |
| */ |
| |
| /* |
| * Websockets Passes |
| * |
| */ |
| |
| |
| module.exports = { |
| /** |
| * WebSocket requests must have the `GET` method and |
| * the `upgrade:websocket` header |
| * |
| * @param {ClientRequest} Req Request object |
| * @param {Socket} Websocket |
| * |
| * @api private |
| */ |
| |
| checkMethodAndHeader : function checkMethodAndHeader(req, socket) { |
| if (req.method !== 'GET' || !req.headers.upgrade) { |
| socket.destroy(); |
| return true; |
| } |
| |
| if (req.headers.upgrade.toLowerCase() !== 'websocket') { |
| socket.destroy(); |
| return true; |
| } |
| }, |
| |
| /** |
| * Sets `x-forwarded-*` headers if specified in config. |
| * |
| * @param {ClientRequest} Req Request object |
| * @param {Socket} Websocket |
| * @param {Object} Options Config object passed to the proxy |
| * |
| * @api private |
| */ |
| |
| XHeaders : function XHeaders(req, socket, options) { |
| if(!options.xfwd) return; |
| |
| var values = { |
| for : req.connection.remoteAddress || req.socket.remoteAddress, |
| port : common.getPort(req), |
| proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws' |
| }; |
| |
| ['for', 'port', 'proto'].forEach(function(header) { |
| req.headers['x-forwarded-' + header] = |
| (req.headers['x-forwarded-' + header] || '') + |
| (req.headers['x-forwarded-' + header] ? ',' : '') + |
| values[header]; |
| }); |
| }, |
| |
| /** |
| * Does the actual proxying. Make the request and upgrade it |
| * send the Switching Protocols request and pipe the sockets. |
| * |
| * @param {ClientRequest} Req Request object |
| * @param {Socket} Websocket |
| * @param {Object} Options Config object passed to the proxy |
| * |
| * @api private |
| */ |
| stream : function stream(req, socket, options, head, server, clb) { |
| |
| var createHttpHeader = function(line, headers) { |
| return Object.keys(headers).reduce(function (head, key) { |
| var value = headers[key]; |
| |
| if (!Array.isArray(value)) { |
| head.push(key + ': ' + value); |
| return head; |
| } |
| |
| for (var i = 0; i < value.length; i++) { |
| head.push(key + ': ' + value[i]); |
| } |
| return head; |
| }, [line]) |
| .join('\r\n') + '\r\n\r\n'; |
| } |
| |
| common.setupSocket(socket); |
| |
| if (head && head.length) socket.unshift(head); |
| |
| |
| var proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request( |
| common.setupOutgoing(options.ssl || {}, options, req) |
| ); |
| |
| // Enable developers to modify the proxyReq before headers are sent |
| if (server) { server.emit('proxyReqWs', proxyReq, req, socket, options, head); } |
| |
| // Error Handler |
| proxyReq.on('error', onOutgoingError); |
| proxyReq.on('response', function (res) { |
| // if upgrade event isn't going to happen, close the socket |
| if (!res.upgrade) { |
| socket.write(createHttpHeader('HTTP/' + res.httpVersion + ' ' + res.statusCode + ' ' + res.statusMessage, res.headers)); |
| res.pipe(socket); |
| } |
| }); |
| |
| proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { |
| proxySocket.on('error', onOutgoingError); |
| |
| // Allow us to listen when the websocket has completed |
| proxySocket.on('end', function () { |
| server.emit('close', proxyRes, proxySocket, proxyHead); |
| }); |
| |
| // The pipe below will end proxySocket if socket closes cleanly, but not |
| // if it errors (eg, vanishes from the net and starts returning |
| // EHOSTUNREACH). We need to do that explicitly. |
| socket.on('error', function () { |
| proxySocket.end(); |
| }); |
| |
| common.setupSocket(proxySocket); |
| |
| if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); |
| |
| // |
| // Remark: Handle writing the headers to the socket when switching protocols |
| // Also handles when a header is an array |
| // |
| socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers)); |
| |
| proxySocket.pipe(socket).pipe(proxySocket); |
| |
| server.emit('open', proxySocket); |
| server.emit('proxySocket', proxySocket); //DEPRECATED. |
| }); |
| |
| return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT |
| |
| function onOutgoingError(err) { |
| if (clb) { |
| clb(err, req, socket); |
| } else { |
| server.emit('error', err, req, socket); |
| } |
| socket.end(); |
| } |
| } |
| }; |