root / HServer / 00.Server / 00.Program / node_modules / uws / uws.js
이력 | 보기 | 이력해설 | 다운로드 (17.5 KB)
1 | 39 | HKM | 'use strict';
|
---|---|---|---|
2 | |||
3 | const http = require('http');
|
||
4 | const EventEmitter = require('events');
|
||
5 | const EE_ERROR = 'Registering more than one listener to a WebSocket is not supported.';
|
||
6 | const DEFAULT_PAYLOAD_LIMIT = 16777216;
|
||
7 | |||
8 | function noop() {} |
||
9 | |||
10 | function abortConnection(socket, code, name) { |
||
11 | socket.end('HTTP/1.1 ' + code + ' ' + name + '\r\n\r\n'); |
||
12 | } |
||
13 | |||
14 | function emitConnection(ws) { |
||
15 | this.emit('connection', ws); |
||
16 | } |
||
17 | |||
18 | function onServerMessage(message, webSocket) { |
||
19 | webSocket.internalOnMessage(message); |
||
20 | } |
||
21 | |||
22 | const native = (() => {
|
||
23 | try {
|
||
24 | try {
|
||
25 | return process.binding('uws_builtin'); |
||
26 | } catch (e) {
|
||
27 | return require(`./uws_${process.platform}_${process.versions.modules}`); |
||
28 | }
|
||
29 | } catch (e) {
|
||
30 | const version = process.version.substring(1).split('.').map(function(n) {
|
||
31 | return parseInt(n);
|
||
32 | });
|
||
33 | const lessThanSixFour = version[0] < 6 || (version[0] === 6 && version[1] < 4);
|
||
34 |
|
||
35 | if (process.platform === 'win32' && lessThanSixFour) {
|
||
36 | throw new Error('µWebSockets requires Node.js 6.4.0 or greater on Windows.');
|
||
37 | } else {
|
||
38 | throw new Error('Compilation of µWebSockets has failed and there is no pre-compiled binary ' +
|
||
39 | 'available for your system. Please install a supported C++11 compiler and reinstall the module \'uws\'.');
|
||
40 | }
|
||
41 | }
|
||
42 | })();
|
||
43 |
|
||
44 | native.setNoop(noop);
|
||
45 |
|
||
46 | var _upgradeReq = null;
|
||
47 |
|
||
48 | const clientGroup = native.client.group.create(0, DEFAULT_PAYLOAD_LIMIT);
|
||
49 |
|
||
50 | native.client.group.onConnection(clientGroup, (external) => {
|
||
51 | const webSocket = native.getUserData(external);
|
||
52 | webSocket.external = external;
|
||
53 | webSocket.internalOnOpen();
|
||
54 | });
|
||
55 |
|
||
56 | native.client.group.onMessage(clientGroup, (message, webSocket) => {
|
||
57 | webSocket.internalOnMessage(message);
|
||
58 | });
|
||
59 |
|
||
60 | native.client.group.onDisconnection(clientGroup, (external, code, message, webSocket) => {
|
||
61 | webSocket.external = null;
|
||
62 |
|
||
63 | process.nextTick(() => {
|
||
64 | webSocket.internalOnClose(code, message);
|
||
65 | });
|
||
66 |
|
||
67 | native.clearUserData(external);
|
||
68 | });
|
||
69 |
|
||
70 | native.client.group.onPing(clientGroup, (message, webSocket) => {
|
||
71 | webSocket.onping(message);
|
||
72 | });
|
||
73 |
|
||
74 | native.client.group.onPong(clientGroup, (message, webSocket) => {
|
||
75 | webSocket.onpong(message);
|
||
76 | });
|
||
77 |
|
||
78 | native.client.group.onError(clientGroup, (webSocket) => {
|
||
79 | process.nextTick(() => {
|
||
80 | webSocket.internalOnError({
|
||
81 | message: 'uWs client connection error',
|
||
82 | stack: 'uWs client connection error'
|
||
83 | });
|
||
84 | });
|
||
85 | });
|
||
86 |
|
||
87 | class WebSocket {
|
||
88 | constructor(external) {
|
||
89 | this.external = external;
|
||
90 | this.internalOnMessage = noop;
|
||
91 | this.internalOnClose = noop;
|
||
92 | this.onping = noop;
|
||
93 | this.onpong = noop;
|
||
94 | }
|
||
95 |
|
||
96 | get upgradeReq() {
|
||
97 | return _upgradeReq;
|
||
98 | }
|
||
99 |
|
||
100 | set onmessage(f) {
|
||
101 | if (f) {
|
||
102 | this.internalOnMessage = (message) => {
|
||
103 | f({data: message});
|
||
104 | };
|
||
105 | } else {
|
||
106 | this.internalOnMessage = noop;
|
||
107 | }
|
||
108 | }
|
||
109 |
|
||
110 | set onopen(f) {
|
||
111 | if (f) {
|
||
112 | this.internalOnOpen = f;
|
||
113 | } else {
|
||
114 | this.internalOnOpen = noop;
|
||
115 | }
|
||
116 | }
|
||
117 |
|
||
118 | set onclose(f) {
|
||
119 | if (f) {
|
||
120 | this.internalOnClose = (code, message) => {
|
||
121 | f({code: code, reason: message});
|
||
122 | };
|
||
123 | } else {
|
||
124 | this.internalOnClose = noop;
|
||
125 | }
|
||
126 | }
|
||
127 |
|
||
128 | set onerror(f) {
|
||
129 | if (f && this instanceof WebSocketClient) {
|
||
130 | this.internalOnError = f;
|
||
131 | } else {
|
||
132 | this.internalOnError = noop;
|
||
133 | }
|
||
134 | }
|
||
135 |
|
||
136 | emit(eventName, arg1, arg2) {
|
||
137 | if (eventName === 'message') {
|
||
138 | this.internalOnMessage(arg1);
|
||
139 | } else if (eventName === 'close') {
|
||
140 | this.internalOnClose(arg1, arg2);
|
||
141 | } else if (eventName === 'ping') {
|
||
142 | this.onping(arg1);
|
||
143 | } else if (eventName === 'pong') {
|
||
144 | this.onpong(arg1);
|
||
145 | }
|
||
146 | return this;
|
||
147 | }
|
||
148 |
|
||
149 | on(eventName, f) {
|
||
150 | if (eventName === 'message') {
|
||
151 | if (this.internalOnMessage !== noop) {
|
||
152 | throw Error(EE_ERROR);
|
||
153 | }
|
||
154 | this.internalOnMessage = f;
|
||
155 | } else if (eventName === 'close') {
|
||
156 | if (this.internalOnClose !== noop) {
|
||
157 | throw Error(EE_ERROR);
|
||
158 | }
|
||
159 | this.internalOnClose = f;
|
||
160 | } else if (eventName === 'ping') {
|
||
161 | if (this.onping !== noop) {
|
||
162 | throw Error(EE_ERROR);
|
||
163 | }
|
||
164 | this.onping = f;
|
||
165 | } else if (eventName === 'pong') {
|
||
166 | if (this.onpong !== noop) {
|
||
167 | throw Error(EE_ERROR);
|
||
168 | }
|
||
169 | this.onpong = f;
|
||
170 | } else if (eventName === 'open') {
|
||
171 | if (this.internalOnOpen !== noop) {
|
||
172 | throw Error(EE_ERROR);
|
||
173 | }
|
||
174 | this.internalOnOpen = f;
|
||
175 | } else if (eventName === 'error' && this instanceof WebSocketClient) {
|
||
176 | if (this.internalOnError !== noop) {
|
||
177 | throw Error(EE_ERROR);
|
||
178 | }
|
||
179 | this.internalOnError = f;
|
||
180 | }
|
||
181 | return this;
|
||
182 | }
|
||
183 |
|
||
184 | once(eventName, f) {
|
||
185 | if (eventName === 'message') {
|
||
186 | if (this.internalOnMessage !== noop) {
|
||
187 | throw Error(EE_ERROR);
|
||
188 | }
|
||
189 | this.internalOnMessage = (message) => {
|
||
190 | this.internalOnMessage = noop;
|
||
191 | f(message);
|
||
192 | };
|
||
193 | } else if (eventName === 'close') {
|
||
194 | if (this.internalOnClose !== noop) {
|
||
195 | throw Error(EE_ERROR);
|
||
196 | }
|
||
197 | this.internalOnClose = (code, message) => {
|
||
198 | this.internalOnClose = noop;
|
||
199 | f(code, message);
|
||
200 | };
|
||
201 | } else if (eventName === 'ping') {
|
||
202 | if (this.onping !== noop) {
|
||
203 | throw Error(EE_ERROR);
|
||
204 | }
|
||
205 | this.onping = () => {
|
||
206 | this.onping = noop;
|
||
207 | f();
|
||
208 | };
|
||
209 | } else if (eventName === 'pong') {
|
||
210 | if (this.onpong !== noop) {
|
||
211 | throw Error(EE_ERROR);
|
||
212 | }
|
||
213 | this.onpong = () => {
|
||
214 | this.onpong = noop;
|
||
215 | f();
|
||
216 | };
|
||
217 | }
|
||
218 | return this;
|
||
219 | }
|
||
220 |
|
||
221 | removeAllListeners(eventName) {
|
||
222 | if (!eventName || eventName === 'message') {
|
||
223 | this.internalOnMessage = noop;
|
||
224 | }
|
||
225 | if (!eventName || eventName === 'close') {
|
||
226 | this.internalOnClose = noop;
|
||
227 | }
|
||
228 | if (!eventName || eventName === 'ping') {
|
||
229 | this.onping = noop;
|
||
230 | }
|
||
231 | if (!eventName || eventName === 'pong') {
|
||
232 | this.onpong = noop;
|
||
233 | }
|
||
234 | return this;
|
||
235 | }
|
||
236 |
|
||
237 | removeListener(eventName, cb) {
|
||
238 | if (eventName === 'message' && this.internalOnMessage === cb) {
|
||
239 | this.internalOnMessage = noop;
|
||
240 | } else if (eventName === 'close' && this.internalOnClose === cb) {
|
||
241 | this.internalOnClose = noop;
|
||
242 | } else if (eventName === 'ping' && this.onping === cb) {
|
||
243 | this.onping = noop;
|
||
244 | } else if (eventName === 'pong' && this.onpong === cb) {
|
||
245 | this.onpong = noop;
|
||
246 | }
|
||
247 | return this;
|
||
248 | }
|
||
249 |
|
||
250 | get OPEN() {
|
||
251 | return WebSocketClient.OPEN;
|
||
252 | }
|
||
253 |
|
||
254 | get CLOSED() {
|
||
255 | return WebSocketClient.CLOSED;
|
||
256 | }
|
||
257 |
|
||
258 | get readyState() {
|
||
259 | return this.external ? WebSocketClient.OPEN : WebSocketClient.CLOSED;
|
||
260 | }
|
||
261 |
|
||
262 | get _socket() {
|
||
263 | const address = this.external ? native.getAddress(this.external) : new Array(3);
|
||
264 | return {
|
||
265 | remotePort: address[0],
|
||
266 | remoteAddress: address[1],
|
||
267 | remoteFamily: address[2]
|
||
268 | };
|
||
269 | }
|
||
270 |
|
||
271 | // from here down, functions are not common between client and server
|
||
272 | |||
273 | ping(message, options, dontFailWhenClosed) { |
||
274 | if (this.external) { |
||
275 | native.server.send(this.external, message, WebSocketClient.OPCODE_PING); |
||
276 | } |
||
277 | } |
||
278 | |||
279 | terminate() { |
||
280 | if (this.external) { |
||
281 | native.server.terminate(this.external); |
||
282 | this.external = null; |
||
283 | } |
||
284 | } |
||
285 | |||
286 | send(message, options, cb) { |
||
287 | if (this.external) { |
||
288 | if (typeof options === 'function') { |
||
289 | cb = options; |
||
290 | options = null;
|
||
291 | } |
||
292 | |||
293 | const binary = options && options.binary || typeof message !== 'string'; |
||
294 | |||
295 | native.server.send(this.external, message, binary ? WebSocketClient.OPCODE_BINARY : WebSocketClient.OPCODE_TEXT, cb ? (() => { |
||
296 | process.nextTick(cb); |
||
297 | }) : undefined);
|
||
298 | } else if (cb) { |
||
299 | cb(new Error('not opened')); |
||
300 | } |
||
301 | } |
||
302 | |||
303 | close(code, data) { |
||
304 | if (this.external) { |
||
305 | native.server.close(this.external, code, data); |
||
306 | this.external = null; |
||
307 | } |
||
308 | } |
||
309 | } |
||
310 | |||
311 | class WebSocketClient extends WebSocket { |
||
312 | constructor(uri) { |
||
313 | super(null); |
||
314 | this.internalOnOpen = noop;
|
||
315 | this.internalOnError = noop;
|
||
316 | native.connect(clientGroup, uri, this); |
||
317 | } |
||
318 | |||
319 | ping(message, options, dontFailWhenClosed) { |
||
320 | if (this.external) { |
||
321 | native.client.send(this.external, message, WebSocketClient.OPCODE_PING); |
||
322 | } |
||
323 | } |
||
324 | |||
325 | terminate() { |
||
326 | if (this.external) { |
||
327 | native.client.terminate(this.external); |
||
328 | this.external = null; |
||
329 | } |
||
330 | } |
||
331 | |||
332 | send(message, options, cb) { |
||
333 | if (this.external) { |
||
334 | if (typeof options === 'function') { |
||
335 | cb = options; |
||
336 | options = null;
|
||
337 | } |
||
338 | |||
339 | const binary = options && options.binary || typeof message !== 'string'; |
||
340 | |||
341 | native.client.send(this.external, message, binary ? WebSocketClient.OPCODE_BINARY : WebSocketClient.OPCODE_TEXT, cb ? (() => { |
||
342 | process.nextTick(cb); |
||
343 | }) : undefined);
|
||
344 | } else if (cb) { |
||
345 | cb(new Error('not opened')); |
||
346 | } |
||
347 | } |
||
348 | |||
349 | close(code, data) { |
||
350 | if (this.external) { |
||
351 | native.client.close(this.external, code, data); |
||
352 | this.external = null; |
||
353 | } |
||
354 | } |
||
355 | } |
||
356 | |||
357 | class Server extends EventEmitter { |
||
358 | constructor(options, callback) { |
||
359 | super();
|
||
360 | |||
361 | if (!options) {
|
||
362 | throw new TypeError('missing options'); |
||
363 | } |
||
364 | |||
365 | if (options.port === undefined && !options.server && !options.noServer) { |
||
366 | throw new TypeError('invalid options'); |
||
367 | } |
||
368 | |||
369 | var nativeOptions = WebSocketClient.PERMESSAGE_DEFLATE;
|
||
370 | |||
371 | if (options.perMessageDeflate !== undefined) { |
||
372 | if (options.perMessageDeflate === false) { |
||
373 | nativeOptions = 0;
|
||
374 | } |
||
375 | } |
||
376 | |||
377 | this.serverGroup = native.server.group.create(nativeOptions, options.maxPayload === undefined ? DEFAULT_PAYLOAD_LIMIT : options.maxPayload); |
||
378 | |||
379 | // can these be made private?
|
||
380 | this._upgradeCallback = noop;
|
||
381 | this._upgradeListener = null; |
||
382 | this._noDelay = options.noDelay === undefined ? true : options.noDelay; |
||
383 | this._lastUpgradeListener = true; |
||
384 | this._passedHttpServer = options.server;
|
||
385 | |||
386 | if (!options.noServer) {
|
||
387 | this.httpServer = options.server ? options.server : http.createServer((request, response) => {
|
||
388 | // todo: default HTTP response
|
||
389 | response.end(); |
||
390 | }); |
||
391 | |||
392 | if (options.path && (!options.path.length || options.path[0] !== '/')) { |
||
393 | options.path = '/' + options.path;
|
||
394 | } |
||
395 | |||
396 | this.httpServer.on('upgrade', this._upgradeListener = ((request, socket, head) => { |
||
397 | if (!options.path || options.path == request.url.split('?')[0].split('#')[0]) { |
||
398 | if (options.verifyClient) {
|
||
399 | const info = { |
||
400 | origin: request.headers.origin,
|
||
401 | secure: request.connection.authorized !== undefined || request.connection.encrypted !== undefined, |
||
402 | req: request
|
||
403 | }; |
||
404 | |||
405 | if (options.verifyClient.length === 2) { |
||
406 | options.verifyClient(info, (result, code, name) => { |
||
407 | if (result) {
|
||
408 | this.handleUpgrade(request, socket, head, emitConnection);
|
||
409 | } else {
|
||
410 | abortConnection(socket, code, name); |
||
411 | } |
||
412 | }); |
||
413 | } else {
|
||
414 | if (options.verifyClient(info)) {
|
||
415 | this.handleUpgrade(request, socket, head, emitConnection);
|
||
416 | } else {
|
||
417 | abortConnection(socket, 400, 'Client verification failed'); |
||
418 | } |
||
419 | } |
||
420 | } else {
|
||
421 | this.handleUpgrade(request, socket, head, emitConnection);
|
||
422 | } |
||
423 | } else {
|
||
424 | if (this._lastUpgradeListener) { |
||
425 | abortConnection(socket, 400, 'URL not supported'); |
||
426 | } |
||
427 | } |
||
428 | })); |
||
429 | |||
430 | this.httpServer.on('newListener', (eventName, listener) => { |
||
431 | if (eventName === 'upgrade') { |
||
432 | this._lastUpgradeListener = false; |
||
433 | } |
||
434 | }); |
||
435 | |||
436 | this.httpServer.on('error', (err) => { |
||
437 | this.emit('error', err); |
||
438 | }); |
||
439 | } |
||
440 | |||
441 | native.server.group.onDisconnection(this.serverGroup, (external, code, message, webSocket) => { |
||
442 | webSocket.external = null;
|
||
443 | |||
444 | process.nextTick(() => { |
||
445 | webSocket.internalOnClose(code, message); |
||
446 | }); |
||
447 | |||
448 | native.clearUserData(external);
|
||
449 | }); |
||
450 | |||
451 | native.server.group.onMessage(this.serverGroup, onServerMessage); |
||
452 | |||
453 | native.server.group.onPing(this.serverGroup, (message, webSocket) => { |
||
454 | webSocket.onping(message); |
||
455 | }); |
||
456 | |||
457 | native.server.group.onPong(this.serverGroup, (message, webSocket) => { |
||
458 | webSocket.onpong(message); |
||
459 | }); |
||
460 | |||
461 | native.server.group.onConnection(this.serverGroup, (external) => { |
||
462 | const webSocket = new WebSocket(external);
|
||
463 | |||
464 | native.setUserData(external, webSocket);
|
||
465 | this._upgradeCallback(webSocket);
|
||
466 | _upgradeReq = null;
|
||
467 | }); |
||
468 | |||
469 | if (options.port !== undefined) { |
||
470 | if (options.host) {
|
||
471 | this.httpServer.listen(options.port, options.host, () => {
|
||
472 | this.emit('listening'); |
||
473 | callback && callback(); |
||
474 | }); |
||
475 | } else {
|
||
476 | this.httpServer.listen(options.port, () => {
|
||
477 | this.emit('listening'); |
||
478 | callback && callback(); |
||
479 | }); |
||
480 | } |
||
481 | } |
||
482 | } |
||
483 | |||
484 | handleUpgrade(request, socket, upgradeHead, callback) { |
||
485 | if (socket._isNative) {
|
||
486 | if (this.serverGroup) { |
||
487 | _upgradeReq = request; |
||
488 | this._upgradeCallback = callback ? callback : noop;
|
||
489 | native.upgrade(this.serverGroup, socket.external, secKey, request.headers['sec-websocket-extensions'], request.headers['sec-websocket-protocol']); |
||
490 | } |
||
491 | } else {
|
||
492 | const secKey = request.headers['sec-websocket-key'];
|
||
493 | const socketHandle = socket.ssl ? socket._parent._handle : socket._handle; |
||
494 | const sslState = socket.ssl ? socket.ssl._external : null;
|
||
495 | if (socketHandle && secKey && secKey.length == 24) { |
||
496 | socket.setNoDelay(this._noDelay);
|
||
497 | const ticket = native.transfer(socketHandle.fd === -1 ? socketHandle : socketHandle.fd, sslState); |
||
498 | socket.on('close', (error) => {
|
||
499 | if (this.serverGroup) { |
||
500 | _upgradeReq = request; |
||
501 | this._upgradeCallback = callback ? callback : noop;
|
||
502 | native.upgrade(this.serverGroup, ticket, secKey, request.headers['sec-websocket-extensions'], request.headers['sec-websocket-protocol']); |
||
503 | } |
||
504 | }); |
||
505 | } |
||
506 | socket.destroy(); |
||
507 | } |
||
508 | } |
||
509 | |||
510 | broadcast(message, options) { |
||
511 | if (this.serverGroup) { |
||
512 | native.server.group.broadcast(this.serverGroup, message, options && options.binary || false); |
||
513 | } |
||
514 | } |
||
515 | |||
516 | startAutoPing(interval, userMessage) { |
||
517 | if (this.serverGroup) { |
||
518 | native.server.group.startAutoPing(this.serverGroup, interval, userMessage); |
||
519 | } |
||
520 | } |
||
521 | |||
522 | close(cb) { |
||
523 | if (this._upgradeListener && this.httpServer) { |
||
524 | this.httpServer.removeListener('upgrade', this._upgradeListener); |
||
525 | |||
526 | if (!this._passedHttpServer) { |
||
527 | this.httpServer.close();
|
||
528 | } |
||
529 | } |
||
530 | |||
531 | if (this.serverGroup) { |
||
532 | native.server.group.close(this.serverGroup); |
||
533 | this.serverGroup = null; |
||
534 | } |
||
535 | |||
536 | if (typeof cb === 'function') { |
||
537 | // compatibility hack, 15 seconds timeout
|
||
538 | setTimeout(cb, 20000);
|
||
539 | } |
||
540 | } |
||
541 | |||
542 | get clients() { |
||
543 | if (this.serverGroup) { |
||
544 | return {
|
||
545 | length: native.server.group.getSize(this.serverGroup), |
||
546 | forEach: ((cb) => {native.server.group.forEach(this.serverGroup, cb)}) |
||
547 | }; |
||
548 | } |
||
549 | } |
||
550 | } |
||
551 | |||
552 | WebSocketClient.PERMESSAGE_DEFLATE = 1;
|
||
553 | WebSocketClient.SERVER_NO_CONTEXT_TAKEOVER = 2;
|
||
554 | WebSocketClient.CLIENT_NO_CONTEXT_TAKEOVER = 4;
|
||
555 | WebSocketClient.OPCODE_TEXT = 1;
|
||
556 | WebSocketClient.OPCODE_BINARY = 2;
|
||
557 | WebSocketClient.OPCODE_PING = 9;
|
||
558 | WebSocketClient.OPEN = 1;
|
||
559 | WebSocketClient.CLOSED = 0;
|
||
560 | WebSocketClient.Server = Server; |
||
561 | WebSocketClient.http = native.httpServer;
|
||
562 | WebSocketClient.native = native; |
||
563 | module.exports = WebSocketClient; |