1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /* This module does a lot of monkeypatching, but unfortunately that appears to be the only way to
|
8 | * accomplish this kind of stuff in Express.
|
9 | *
|
10 | * Here be dragons. */
|
11 |
|
12 | exports.expressWs = expressWs;
|
13 |
|
14 | var _http = require('http');
|
15 |
|
16 | var _http2 = _interopRequireDefault(_http);
|
17 |
|
18 | var _express = require('express');
|
19 |
|
20 | var _express2 = _interopRequireDefault(_express);
|
21 |
|
22 | var _ws = require('ws');
|
23 |
|
24 | var _ws2 = _interopRequireDefault(_ws);
|
25 |
|
26 | var _trailingSlash = require('./trailing-slash');
|
27 |
|
28 | var _trailingSlash2 = _interopRequireDefault(_trailingSlash);
|
29 |
|
30 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
31 |
|
32 | /* The following fixes HenningM/express-ws#17, correctly. */
|
33 | function websocketUrl(url) {
|
34 | if (url.indexOf('?') !== -1) {
|
35 | var _url$split = url.split('?');
|
36 |
|
37 | var _url$split2 = _slicedToArray(_url$split, 2);
|
38 |
|
39 | var baseUrl = _url$split2[0];
|
40 | var query = _url$split2[1];
|
41 |
|
42 |
|
43 | return (0, _trailingSlash2.default)(baseUrl) + '.websocket?' + query;
|
44 | }
|
45 | return (0, _trailingSlash2.default)(url) + '.websocket';
|
46 | }
|
47 |
|
48 | function expressWs(app, httpServer) {
|
49 | var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
|
50 |
|
51 | var server = httpServer;
|
52 |
|
53 | if (!server) {
|
54 | /* No HTTP server was explicitly provided, create one for our Express application. */
|
55 | server = _http2.default.createServer(app);
|
56 |
|
57 | app.listen = function serverListen() {
|
58 | server.listen.apply(server, arguments);
|
59 | };
|
60 | }
|
61 |
|
62 | function wrapMiddleware(middleware) {
|
63 | return function (req, res, next) {
|
64 | if (req.ws !== null) {
|
65 | req.wsHandled = true;
|
66 | /* Unpack the `.ws` property and call the actual handler. */
|
67 | middleware(req.ws, req, next);
|
68 | } else {
|
69 | /* This wasn't a WebSocket request, so skip this middleware. */
|
70 | next();
|
71 | }
|
72 | };
|
73 | }
|
74 |
|
75 | function addWsMethod(target) {
|
76 | if (!target.ws) {
|
77 | /* This prevents conflict with other things setting `.ws`. */
|
78 | target.ws = function addWsRoute(route) {
|
79 | var middlewares = Array.prototype.slice.call(arguments, 1); // deopt!
|
80 | var wrappedMiddlewares = middlewares.map(wrapMiddleware);
|
81 |
|
82 | /* We append `/.websocket` to the route path here. Why? To prevent conflicts when
|
83 | * a non-WebSocket request is made to the same GET route - after all, we are only
|
84 | * interested in handling WebSocket requests.
|
85 | *
|
86 | * Whereas the original `express-ws` prefixed this path segment, we suffix it -
|
87 | * this makes it possible to let requests propagate through Routers like normal,
|
88 | * which allows us to specify WebSocket routes on Routers as well \o/! */
|
89 | var wsRoute = websocketUrl(route);
|
90 |
|
91 | /* Here we configure our new GET route. It will never get called by a client
|
92 | * directly, it's just to let our request propagate internally, so that we can
|
93 | * leave the regular middleware execution and error handling to Express. */
|
94 | target.get.apply(target, [wsRoute].concat(wrappedMiddlewares));
|
95 | };
|
96 | }
|
97 | }
|
98 |
|
99 | /* Make our custom `.ws` method available directly on the Express application. You should
|
100 | * really be using Routers, though. */
|
101 | addWsMethod(app);
|
102 |
|
103 | /* Monkeypatch our custom `.ws` method into Express' Router prototype. This makes it possible,
|
104 | * when using the standard Express Router, to use the `.ws` method without any further calls
|
105 | * to `makeRouter`. When using a custom router, the use of `makeRouter` may still be necessary.
|
106 | *
|
107 | * This approach works, because Express does a strange mixin hack - the Router factory
|
108 | * function is simultaneously the prototype that gets assigned to the resulting Router
|
109 | * object. */
|
110 | if (!options.leaveRouterUntouched) {
|
111 | addWsMethod(_express2.default.Router);
|
112 | }
|
113 |
|
114 | var wsServer = new _ws2.default.Server({ server: server });
|
115 |
|
116 | wsServer.on('connection', function (socket) {
|
117 | var request = socket.upgradeReq;
|
118 |
|
119 | request.ws = socket;
|
120 | request.wsHandled = false;
|
121 |
|
122 | /* By setting this fake `.url` on the request, we ensure that it will end up in the fake
|
123 | * `.get` handler that we defined above - where the wrapper will then unpack the `.ws`
|
124 | * property, indicate that the WebSocket has been handled, and call the actual handler. */
|
125 | request.url = websocketUrl(request.url);
|
126 |
|
127 | var dummyResponse = new _http2.default.ServerResponse(request);
|
128 |
|
129 | dummyResponse.writeHead = function (statusCode) {
|
130 | if (statusCode > 200) {
|
131 | /* Something in the middleware chain signalled an error. */
|
132 | socket.close();
|
133 | }
|
134 | };
|
135 |
|
136 | app.handle(request, dummyResponse, function () {
|
137 | if (!request.wsHandled) {
|
138 | /* There was no matching WebSocket-specific route for this request. We'll close
|
139 | * the connection, as no endpoint was able to handle the request anyway... */
|
140 | socket.close();
|
141 | }
|
142 | });
|
143 | });
|
144 |
|
145 | return {
|
146 | app: app,
|
147 | getWss: function getWss() {
|
148 | return wsServer;
|
149 | },
|
150 | applyTo: function applyTo(router) {
|
151 | return addWsMethod(router);
|
152 | }
|
153 | };
|
154 | }; |
\ | No newline at end of file |