ws-incoming.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. var http = require('http'),
  2. https = require('https'),
  3. common = require('../common');
  4. /*!
  5. * Array of passes.
  6. *
  7. * A `pass` is just a function that is executed on `req, socket, options`
  8. * so that you can easily add new checks while still keeping the base
  9. * flexible.
  10. */
  11. /*
  12. * Websockets Passes
  13. *
  14. */
  15. module.exports = {
  16. /**
  17. * WebSocket requests must have the `GET` method and
  18. * the `upgrade:websocket` header
  19. *
  20. * @param {ClientRequest} Req Request object
  21. * @param {Socket} Websocket
  22. *
  23. * @api private
  24. */
  25. checkMethodAndHeader : function checkMethodAndHeader(req, socket) {
  26. if (req.method !== 'GET' || !req.headers.upgrade) {
  27. socket.destroy();
  28. return true;
  29. }
  30. if (req.headers.upgrade.toLowerCase() !== 'websocket') {
  31. socket.destroy();
  32. return true;
  33. }
  34. },
  35. /**
  36. * Sets `x-forwarded-*` headers if specified in config.
  37. *
  38. * @param {ClientRequest} Req Request object
  39. * @param {Socket} Websocket
  40. * @param {Object} Options Config object passed to the proxy
  41. *
  42. * @api private
  43. */
  44. XHeaders : function XHeaders(req, socket, options) {
  45. if(!options.xfwd) return;
  46. var values = {
  47. for : req.connection.remoteAddress || req.socket.remoteAddress,
  48. port : common.getPort(req),
  49. proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws'
  50. };
  51. ['for', 'port', 'proto'].forEach(function(header) {
  52. req.headers['x-forwarded-' + header] =
  53. (req.headers['x-forwarded-' + header] || '') +
  54. (req.headers['x-forwarded-' + header] ? ',' : '') +
  55. values[header];
  56. });
  57. },
  58. /**
  59. * Does the actual proxying. Make the request and upgrade it
  60. * send the Switching Protocols request and pipe the sockets.
  61. *
  62. * @param {ClientRequest} Req Request object
  63. * @param {Socket} Websocket
  64. * @param {Object} Options Config object passed to the proxy
  65. *
  66. * @api private
  67. */
  68. stream : function stream(req, socket, options, head, server, clb) {
  69. var createHttpHeader = function(line, headers) {
  70. return Object.keys(headers).reduce(function (head, key) {
  71. var value = headers[key];
  72. if (!Array.isArray(value)) {
  73. head.push(key + ': ' + value);
  74. return head;
  75. }
  76. for (var i = 0; i < value.length; i++) {
  77. head.push(key + ': ' + value[i]);
  78. }
  79. return head;
  80. }, [line])
  81. .join('\r\n') + '\r\n\r\n';
  82. }
  83. common.setupSocket(socket);
  84. if (head && head.length) socket.unshift(head);
  85. var proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request(
  86. common.setupOutgoing(options.ssl || {}, options, req)
  87. );
  88. // Enable developers to modify the proxyReq before headers are sent
  89. if (server) { server.emit('proxyReqWs', proxyReq, req, socket, options, head); }
  90. // Error Handler
  91. proxyReq.on('error', onOutgoingError);
  92. proxyReq.on('response', function (res) {
  93. // if upgrade event isn't going to happen, close the socket
  94. if (!res.upgrade) {
  95. socket.write(createHttpHeader('HTTP/' + res.httpVersion + ' ' + res.statusCode + ' ' + res.statusMessage, res.headers));
  96. res.pipe(socket);
  97. }
  98. });
  99. proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
  100. proxySocket.on('error', onOutgoingError);
  101. // Allow us to listen when the websocket has completed
  102. proxySocket.on('end', function () {
  103. server.emit('close', proxyRes, proxySocket, proxyHead);
  104. });
  105. // The pipe below will end proxySocket if socket closes cleanly, but not
  106. // if it errors (eg, vanishes from the net and starts returning
  107. // EHOSTUNREACH). We need to do that explicitly.
  108. socket.on('error', function () {
  109. proxySocket.end();
  110. });
  111. common.setupSocket(proxySocket);
  112. if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead);
  113. //
  114. // Remark: Handle writing the headers to the socket when switching protocols
  115. // Also handles when a header is an array
  116. //
  117. socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers));
  118. proxySocket.pipe(socket).pipe(proxySocket);
  119. server.emit('open', proxySocket);
  120. server.emit('proxySocket', proxySocket); //DEPRECATED.
  121. });
  122. return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
  123. function onOutgoingError(err) {
  124. if (clb) {
  125. clb(err, req, socket);
  126. } else {
  127. server.emit('error', err, req, socket);
  128. }
  129. socket.end();
  130. }
  131. }
  132. };