/index.js
JavaScript | 859 lines | 608 code | 137 blank | 114 comment | 144 complexity | cc1d2361b5d4672a0b0a3084bd725f0e MD5 | raw file
1var dgram = require('dgram'), 2 socket = dgram.createSocket('udp4'), 3 packet = require('packet'), 4 common = require('./common'), 5 crypto = require('crypto'), 6 dns = require('dns'), 7 __slice = [].slice; 8 9const CONTROL_TYPES = 'handshake keep-alive acknowledgement'.split(/\s+/); 10const MAX_MSG_NO = 0x1FFFFFFF; 11const MAX_SEQ_NO = Math.pow(2, 31) - 1; 12const IMMEDIATE = [ 0, 0 ]; 13 14var socketId = crypto.randomBytes(4).readUInt32BE(0); 15 16function nextSocketId () { 17 if (socketId == 1) socketId = Math.pow(2, 32); 18 return --socketId; 19} 20 21var Stream = require('stream'); 22var util = require('util'); 23var events = require('events'); 24var net = require('net'); 25 26// The start of time used in our high resolution timings. 27var epoch = process.hrtime(); 28 29var heapIndexKeyCounter = 0; 30 31function Heap (before) { 32 this.array = []; 33 this.indexKey = '_Heap_index_key_' + (heapIndexKeyCounter++); 34 this.before = before; 35} 36 37Heap.prototype.__defineGetter__("length", function () { 38 return this.array.length; 39}); 40 41Heap.prototype.peek = function () { 42 return this.array[0]; 43} 44 45Heap.prototype.bubbleUp = function (index) { 46 var before = this.before, array = this.array, indexKey = this.indexKey, node = array[index]; 47 while (index > 0) { 48 var parent = index - 1 >> 1; 49 if (before(node, array[parent])) { 50 array[index] = array[parent]; 51 array[parent] = node; 52 array[index][indexKey] = index; 53 array[parent][indexKey] = parent; 54 index = parent; 55 } else { 56 break; 57 } 58 } 59} 60 61Heap.prototype.sinkDown = function (index) { 62 var array = this.array, indexKey = this.indexKey, 63 length = array.length, node = array[index], 64 left, right, child; 65 for (left = index * 2 + 1; left < length; l = index * 2 + 1) { 66 child = left; 67 right = left + 1; 68 if (right < length && before(array[right], array[left])) { 69 child = right; 70 } 71 if (before(array[child][indexKey], node[indexKey])) { 72 array[index] = array[child]; 73 array[child] = node; 74 array[index][indexKey] = index; 75 array[child][indexKey] = child; 76 index = child; 77 } else { 78 break; 79 } 80 } 81} 82 83Heap.prototype.remove = function (node) { 84 var array = this.array, indexKey = this.indexKey, last = array.pop(), index = node[indexKey]; 85 if (index != array.length) { 86 array[index] = last; 87 if (less(end, node)) { 88 this.bubbleUp(index); 89 } else { 90 this.sinkDown(index); 91 } 92 } 93 delete node[indexKey]; 94} 95 96Heap.prototype.push = function (node, value) { 97 var array = this.array, indexKey = this.indexKey, index = array.length; 98 if (node[indexKey] != null) { 99 this.remove(node); 100 this.push(node); 101 } else { 102 array.push(node); 103 node[indexKey] = index; 104 this.bubbleUp(index); 105 } 106} 107 108Heap.prototype.pop = function (node) { 109 var array = this.array, indexKey = this.indexKey, result = array[0], last = array.pop(); 110 if (array.length) { 111 array[0] = last; 112 last[indexKey] = 0; 113 this.sinkDown(0); 114 } 115 delete result[indexKey]; 116 return result; 117} 118 119// Comparison operator generator for high-resolution time for use with heap. 120function sooner (property) { 121 return function (a, b) { 122 if (a[property][0] < b[property][0]) return true; 123 if (a[property][0] > b[property][0]) return false; 124 return a[property][1] < b[property][1]; 125 } 126} 127 128function die () { 129 console.log.apply(console, __slice.call(arguments, 0)); 130 return process.exit(1); 131} 132 133function say () { return console.log.apply(console, __slice.call(arguments, 0)) } 134 135function extend (to, from) { 136 for (var key in from) to[key] = from[key]; 137 return to; 138} 139 140function parseDotDecimal (quad) { 141 quad = quad.split('.'); 142 for (var i = 3, address = 0; i >= 0; i--) { 143 address = address + quad[i] * Math.pow(256, i); 144 } 145 return address; 146} 147 148// Native control algorithm is an event emitter with certain properties. Ought 149// to be simple enough for the user to implement a native control algorithm as 150// an event emitter. 151function NativeControlAlgorithm () { 152 this.roundTripTime = 0; 153 this.maximumSegmentSize = 0; 154 this.estimatedBandwidth = 0; 155 this.latestPacketSequenceNo = 0; 156 this.windowSize = 16; 157} 158util.inherits(NativeControlAlgorithm, events.EventEmitter); 159 160var sendQueue = new (function () { 161 var before = sooner('_sendTime'), queue = new Heap(before), sending = false; 162 function enqueue (socket, packet, when) { 163 queue.add({ socket: socket, packet: packet, when: when }); 164 if (!sending) poll(); 165 } 166 function schedule (socket, timestamp) { 167 // This gave me a funny feeling, one of violating encapsulation by using a 168 // property in the socket object from the send queue, except that am I 169 // supposed to do? This is what I would have called violating encapsulation 170 // in my Java days, it triggers the creation of a dozen new types to 171 // preserve encapsulation. I've yet to completely de-program myself of this 172 // sort of rote programming. The send queue is within the same capsule as 173 // the socket. They are interdependent. They existing for each other. The 174 // socket object's underscored properties are part of its implementation, in 175 // fact, the socket is not the implementation, the whole API is. 176 socket._sendTime = timestamp; 177 queue.push(socket); 178 if (!sending) poll(); 179 } 180 function poll () { 181 sending = true; 182 if (! queue.length) { 183 sending = false; 184 } else { 185 send(); 186 } 187 } 188 function send () { 189 var socket; 190 if (before(queue.peek(), { _sendTime: process.hrtime() })) { 191 socket = queue.pop(); 192 socket._endPoint.transmit(socket); 193 } 194 process.nextTick(poll); 195 } 196 extend(this, { schedule: schedule }); 197})(); 198 199function Socket (options) { 200 if (!(this instanceof Socket)) return new Socket(); 201 202 if (options === void(0)) options = {}; 203 204 Stream.call(this); 205 206 this._socketId = nextSocketId(); 207 this._messageNumber = 1; 208 this._flowWindowSize = 0; 209 this._ccc = options.ccc || new NativeControlAlgorithm; 210 this._packet = new Buffer(1500); 211 this._pending = []; 212 this._sent = [[]]; 213} 214util.inherits(Socket, Stream); 215 216exports.Socket = Socket; 217 218function exceptional () { return new Error(); } 219 220// Wrapper around an underlying UDP datagram socket. 221function EndPoint (local) { 222 this.listeners = 0; 223 this.dgram = dgram.createSocket('udp4'); 224 this.dgram.on('message', EndPoint.prototype.receive.bind(this)); 225 this.dgram.bind(local.port, local.address); 226 this.local = this.dgram.address(); 227 this.packet = new Buffer(2048); 228 this.sockets = {}; 229} 230EndPoint.prototype.shakeHands = function (socket) { 231 // Stash the socket so we can track it by the socket identifier. 232 this.sockets[socket._socketId] = socket; 233 234 // Start of client handshake. 235 socket._status = "syn"; 236 237 // Send a handshake. Use hard-coded defaults for packet and window size. 238 this.sendHandshake(socket, 239 { control: 1 240 , type: 0 241 , additional: 0 242 , timestamp: 0 243 , destination: 0 244 , version: 4 245 , socketType: 1 246 , sequence: socket._sequence 247 , maxPacketSize: 1500 248 , windowSize: 8192 249 , connectionType: 1 250 , socketId: socket._socketId 251 , synCookie: 0 252 , address: parseDotDecimal(socket._peer.address) 253 }); 254} 255EndPoint.prototype.control = function (socket, pattern, message, callback) { 256 var serializer = common.serializer, dgram = this.dgram, packet = new Buffer(64), peer = socket._peer; 257 258 message.control = 1; 259 message.destination = peer.socketId; 260 // TODO: Implement timestamp. 261 message.timestamp = 0; 262 263 // Format a shutdown packet, simply a header packet of type shutdown. 264 serializer.reset(); 265 serializer.serialize(pattern, message); 266 serializer.write(packet); 267 268 dgram.send(packet, 0, serializer.length, peer.port, peer.address, callback); 269} 270EndPoint.prototype.shutdown = function (socket, send) { 271 // Remove the socket from the stash. 272 delete this.sockets[socket._socketId]; 273 274 // Zero the status. 275 delete socket._status; 276 277 var endPoint = this, dgram = endPoint.dgram; 278 279 if (send) { 280 var serializer = common.serializer, packet = endPoint.packet, peer = socket._peer; 281 282 // Format a shutdown packet, simply a header packet of type shutdown. 283 serializer.reset(); 284 serializer.serialize('header', 285 { control: 1 286 , type: 0x5 287 , additional: 0 288 , timestamp: 0 289 , destination: peer.socketId 290 }); 291 serializer.write(packet); 292 293 dgram.send(packet, 0, serializer.length, peer.port, peer.address, finalize); 294 } else { 295 finalize(); 296 } 297 298 function finalize () { 299 // If we were a bound listening socket, see if we ought to close. 300 if (socket._listener && !--endPoint.listeners && endPoint.server._closing) { 301 // This will unassign `endPoint.server`. 302 endPoint.server.close(); 303 } 304 // Dispose of the end point and UDP socket if it is no longer referenced. 305 if (Object.keys(endPoint.sockets).length == 0) { 306 delete endPoints[endPoint.local.port][endPoint.local.address]; 307 if (Object.keys(endPoints[endPoint.local.port]).length == 0) { 308 delete endPoints[endPoint.local.port]; 309 } 310 dgram.close(); 311 } 312 } 313} 314// Send the handshake four times a second until we get a response, or until four 315// seconds is up. 316EndPoint.prototype.sendHandshake = function (socket, handshake) { 317 var endPoint = this, count = 0, peer = socket._peer; 318 socket._handshakeInterval = setInterval(function () { 319 if (++count == 12) { 320 clearInterval(socket._handshakeInterval); 321 socket.emit('error', new Error('connection timeout')); 322 } else { 323 endPoint.send('handshake', handshake, socket._peer); 324 } 325 }, 250); 326} 327EndPoint.prototype.send = function (packetType, object, peer) { 328 var serializer = common.serializer, 329 packet = this.packet, 330 dgram = this.dgram; 331 332 serializer.reset(); 333 serializer.serialize(packetType, object); 334 serializer.write(packet); 335 336 dgram.send(packet, 0, serializer.length, peer.port, peer.address); 337} 338EndPoint.prototype.receive = function (msg, rinfo) { 339 var endPoint = this, parser = common.parser, handler; 340 parser.reset(); 341 parser.extract('header', function (header) { 342 header.rinfo = rinfo; 343 header.length = msg.length; 344 if (header.control) { 345 if (header.destination) { 346 // TODO: Socket not found... 347 var socket = endPoint.sockets[header.destination]; 348 switch (header.type) { 349 // Keep-alive. 350 case 0x1: 351 break; 352 // Shutdown. 353 case 0x5: 354 endPoint.shutdown(socket, false); 355 break; 356 // Notifications from Bill the Cat. (Ack-ack.) 357 case 0x6: 358 break; 359 default: 360 var name = CONTROL_TYPES[header.type]; 361 console.log(name, header); 362 parser.extract(name, endPoint[name].bind(endPoint, parser, socket, header)) 363 } 364 // Hmm... Do you explicitly enable rendezvous? 365 } else if (header.type == 0 && endPoint.server) { 366 parser.extract('handshake', endPoint.connect.bind(endPoint, rinfo, header)) 367 } 368 } else { 369 } 370 }); 371 parser.parse(msg); 372} 373EndPoint.prototype.handshake = function (parser, socket, header, handshake) { 374 switch (socket._status) { 375 case 'syn': 376 // Only respond to an initial handshake. 377 if (handshake.connectionType != 1) break; 378 379 clearInterval(socket._handshakeInterval); 380 381 socket._status = 'syn-ack'; 382 383 // Unify the packet object for serialization. 384 handshake = extend(handshake, header); 385 386 // Set the destination to nothing. 387 handshake.destination = 0; 388 389 // Select the lesser of the negotiated values. 390 // TODO: Constants are a bad thing... 391 handshake.maxPacketSize = Math.min(handshake.maxPacketSize, 1500); 392 handshake.windowSize = Math.min(handshake.windowSize, 8192); 393 handshake.connectionType = -1; 394 395 this.sendHandshake(socket, handshake); 396 break; 397 case 'syn-ack': 398 // Only respond to an follow-up handshake. 399 if (handshake.connectionType != -1) break; 400 401 clearInterval(socket._handshakeInterval); 402 403 socket._status = 'connected'; 404 socket._handshake = handshake; 405 socket._peer.socketId = handshake.socketId; 406 407 socket.emit('connect'); 408 break; 409 } 410} 411 412// Binary search, implemented, as always, by taking a [peek at 413// Sedgewick](http://algs4.cs.princeton.edu/11model/BinarySearch.java.html). 414function binarySearch (comparator, array, key) { 415 var low = 0, high = array.length - 1, partition, compare; 416 while (low <= high) { 417 partition = Math.floor(low + (high - low) / 2); 418 compare = comparator(key, array[partition]); 419 if (compare < 0) high = partition - 1; 420 else if (compare > 0) low = partition + 1; 421 else return partition; 422 } 423 return low; 424} 425 426// Compare two objects by their sequence property. 427function bySequence (left, right) { return left.sequence - right.sequence } 428 429EndPoint.prototype.acknowledgement = function (parser, socket, header, ack) { 430 // All parsing in one fell swoop so we don't do something that causes a next 431 // tick which might cause the parser to be reused. 432 if (header.length == 40) { 433 parser.extract('statistics', this.fullAcknowledgement.bind(this, socket, header, ack)); 434 } else { 435 this.lightAcknowledgement(socket, header, ack); 436 } 437}; 438 439// Remove the sent packets that have been received. 440EndPoint.prototype.fullAcknowledgement = function (socket, header, ack, stats) { 441 this.lightAcknowledgement(socket, header, ack); 442 say(socket._flowWindowSize, socket._sent[0].length, header, ack, stats); 443} 444 445EndPoint.prototype.lightAcknowledgement = function (socket, header, ack) { 446 var endPoint = this, sent = socket._sent, sequence = sent[0], index; 447 index = binarySearch(bySequence, sequence, ack); 448 if (index != -1 && sent.length == 2) { 449 socket._flowWindowSize -= sent[1].length; 450 sent.length = 1; 451 } 452 if (sent.length == 2) { 453 sequence = sent[1]; 454 index = binarySearch(bySequence, sequence, ack); 455 } 456 socket._flowWindowSize -= sequence.splice(0, index).length; 457 endPoint.control(socket, 'header', { type: 0x6, additional: header.additional }); 458} 459 460EndPoint.prototype.connect = function (rinfo, header, handshake) { 461 var endPoint = this, server = endPoint.server, timestamp = Math.floor(Date.now() / 6e4); 462 463 // Do not accept new connections if the server is closing. 464 if (server._closing) return; 465 466 handshake = extend(handshake, header); 467 468 if (handshake.connectionType == 1) { 469 handshake.destination = handshake.socketId; 470 handshake.synCookie = synCookie(rinfo, timestamp); 471 endPoint.send('handshake', handshake, rinfo); 472 } else if (handshakeWithValidCookie(handshake, timestamp)) { 473 // Create the socket and initialize it as a listener. 474 var socket = new Socket; 475 476 socket._peer = rinfo; 477 socket._endPoint = endPoint; 478 socket._listener = true; 479 socket._status = 'connected'; 480 481 // Increase the count of end point listeners. 482 endPoint.listeners++; 483 484 endPoint.sockets[socket._socketId] = socket; 485 486 handshake.destination = handshake.socketId; 487 handshake.socketId = socket._socketId; 488 489 endPoint.send('handshake', handshake, rinfo); 490 491 endPoint.server.emit('connection', socket); 492 } 493 494 function handshakeWithValidCookie (handshake, timestamp) { 495 if (handshake.connectionType != -1) return false; 496 if (synCookie(rinfo, timestamp) == handshake.synCookie) return true; 497 if (synCookie(rinfo, timestamp - 1) == handshake.synCookie) return true; 498 return false; 499 } 500} 501EndPoint.prototype.transmit = function (socket) { 502 var serializer = common.serializer, dgram = this.dgram, 503 pending = socket._pending, peer = socket._peer, enqueue; 504 505 // If we have data packets to retransmit, they go first, otherwise send a new 506 // data packet. 507 if (false) { 508 509 } else { 510 if (pending.length && !pending[0].length) { 511 pending.shift(); 512 } 513 514 if (pending.length) { 515 // TODO: Is pop faster? 516 message = pending[0].shift(); 517 518 // Set the sequence number. 519 message.sequence = socket._sequence; 520 521 // We will stash the message and increment the seqeunce number. 522 enqueue = true; 523 } 524 } 525 526 if (message) { 527 serializer.reset(); 528 serializer.serialize('header', extend({ control: 0, timestamp: 0 }, message)); 529 serializer.write(message.buffer); 530 531 dgram.send(message.buffer, 0, message.buffer.length, peer.port, peer.address); 532 } 533 534 if (enqueue) { 535 socket._flowWindowSize++; 536 // Advance to the socket's next sequence number. The manipulation of the 537 // sent list occurs in both the `Socket` and the `EndPoint`. 538 socket._sequence = socket._sequence + 1 & MAX_SEQ_NO; 539 // When our sequence number wraps, we use a new array of sent packets. This 540 // helps us handle acknowledgements of packets whose squence number is in 541 // the vicinity of a wrap. 542 if (socket._sequence == 0) { 543 socket._sent.unshift([]); 544 } 545 socket._sent[0].push(message); 546 } 547 548 // TODO: Something like this, but after actually calculating the time of the 549 // next packet using the congestion control algorithm. 550 if (pending.length > 1 || pending[0].length) { 551 sendQueue.schedule(socket, 0); 552 } 553} 554 555// Reference counted cache of UDP datagram sockets. 556var endPoints = {}; 557 558// Create a new UDP datagram socket from the user specified port and address. 559 560// TODO: IP version. 561function createEndPoint (local) { 562 var endPoint = new EndPoint(local), local = endPoint.local; 563 if (!endPoints[local.port]) endPoints[local.port] = {}; 564 return endPoints[local.port][local.address] = endPoint; 565} 566 567// Look up an UDP datagram socket in the cache of bound UDP datagram sockets by 568// the user specified port and address. 569 570// 571function lookupEndPoint (local) { 572 // No interfaces bound by the desired port. Note that this would also work for 573 // zero, which indicates an ephemeral binding, but we check for that case 574 // explicitly before calling this function. 575 if (!endPoints[local.port]) return null; 576 577 // Read datagram socket from cache. 578 var endPoint = endPoints[local.port][local.address]; 579 580 // If no datagram exists, ensure that we'll be able to create one. This only 581 // inspects ports that have been bound by UDT, not by other protocols, so 582 // there is still an opportunity for error when the UDP bind is invoked. 583 if (!endPoint) { 584 if (endPoints[local.port][0]) { 585 throw new Error('Already bound to all interfaces.'); 586 } 587 if (local.address == 0) { 588 throw new Error('Cannot bind to all interfaces because some interfaces are already bound.'); 589 } 590 } 591 592 // Return cached datagram socket or nothing. 593 return endPoint; 594} 595 596function validator (ee) { 597 return function (forward) { return check(ee, forward) } 598} 599 600function check (ee, forward) { 601 return function (error) { 602 if (error) { 603 process.nextTick(function () { 604 ee.emit('error', error); 605 ee._destroy(); 606 }); 607 } else { 608 try { 609 forward.apply(null, __slice.call(arguments, 1)); 610 } catch (error) { 611 ee.emit('error', error); 612 } 613 } 614 } 615} 616 617Socket.prototype._nextSequence = function () { 618 var socket = this; 619 if (socket._sequence == MAX_SEQ_NO) { 620 return socket._sequence = 0; 621 } else { 622 return ++socket._sequence; 623 } 624} 625 626Socket.prototype.connect = function (options) { 627 // Convert legacy 'net' module parameters to an options object. 628 if (typeof options != 'object') { 629 var args = net._normalizeConnectArgs(arguments); 630 return Socket.prototype.connect.apply(this, args); 631 } 632 633 var socket = this; 634 635 // TODO: _endPoint. 636 if (socket._dgram) throw new Error('Already connected'); 637 638 var peer = { address: options.host, port: options.port }; 639 var local = { address: options.localAddress, port: options.localPort }; 640 641 if (options.path) throw new Error('UNIX domain sockets are not supported.'); 642 if (!options.port) throw new Error('Remote port is required.'); 643 644 // Assign reasonable defaults for unspecified connection properties. 645 if (!peer.address) peer.address = '127.0.0.1'; 646 if (!local.address) local.address = '0.0.0.0'; 647 if (!local.port) local.port = 0; 648 649 if (typeof arguments[1] == 'function') { 650 socket.on('connect', arguments[1]); 651 } 652 653 socket._connecting = true; 654 655 var valid = validator(socket); 656 657 // Resolve DNS now to use the ip as cache key. Lookup handles a interprets 658 // local address as 0.0.0.0. 659 dns.lookup(local.address, valid(localResolved)); 660 661 function localResolved (ip, addressType) { 662 local.address = ip; 663 664 // Use an existing datagram socket if one exists. 665 if (local.port == 0) { 666 socket._endPoint = createEndPoint(local); 667 } else if (!(socket._endPoint = lookupEndPoint(local))) { 668 socket._endPoint = createEndPoint(local); 669 } 670 671 dns.lookup(options.address, valid(peerResolved)); 672 } 673 674 // Record the DNS resolved IP address. 675 function peerResolved (ip, addressType) { 676 // Possible cancelation during DNS lookup. 677 if (!socket._connecting) return; 678 679 socket._peer = { address: ip || '127.0.0.1', port: options.port }; 680 681 // Generate random bytes used to set randomized socket properties. 682 // `crypto.randomBytes` calls OpenSSL `RAND_bytes` to generate the bytes. 683 // 684 // * [RAND_bytes](http://www.openssl.org/docs/crypto/RAND_bytes.html). 685 // * [node_crypto.cc](https://github.com/joyent/node/blob/v0.8/src/node_crypto.cc#L4517) 686 crypto.randomBytes(4, valid(randomzied)); 687 } 688 689 // Initialize the randomized socket properies. 690 function randomzied (buffer) { 691 // Randomly generated randomness. 692 socket._sequence = buffer.readUInt32BE(0) % MAX_SEQ_NO; 693 694 // The end point sends a packet on our behalf. 695 socket._endPoint.shakeHands(socket); 696 } 697} 698// There is no way to send the UDP packets without copying the user buffer into 699// new buffers. The UDP packets need a header before a chunk of the user data, 700// so we need to write the header, which means we need a buffer we can alter. We 701// cannot borrow the user's buffer. 702// 703// According to documentation, write returns false if the buffer cannot be 704// written to kernel, if it is queued in user memory, so we can hold onto it for 705// a while if we like. We pushback when the UDT send buffer, as defined by the 706// count of packets, is full. 707// 708// All this copying and allocation is disheartening. This is a place that needs 709// the attention of some benchmarks. If you can think of a way to avoid the 710// copying, please let me know. Nothing's occurring to me. 711 712 713// Total size of UDT data packet overhead, UDP header plus UDT data header. 714const UDP_HEADER_SIZE = 28; 715const UDT_DATA_HEADER_SIZE = 16; 716 717// 718Socket.prototype.write = function (buffer) { 719 var socket = this, 720 handshake = socket._handshake, 721 size = handshake.maxPacketSize - (UDT_DATA_HEADER_SIZE + UDT_DATA_HEADER_SIZE), 722 packet, count, i, length, message = []; 723 724 count = Math.floor(buffer.length / size); 725 if (buffer.length % size) count++; 726 727 for (i = 0; i < count; i++) { 728 packet = { 729 control: 0, 730 position: 0, 731 inOrder: 1, 732 number: socket._messageNumber, 733 destination: handshake.socketId, 734 buffer: new Buffer(UDT_DATA_HEADER_SIZE + Math.min(buffer.length - i * size, size)) 735 }; 736 // TODO: Does `Buffer.copy` choose the lesser of source length and 737 // destination length? 738 buffer.copy(packet.buffer, UDT_DATA_HEADER_SIZE, i * size); 739 if (i == 0) packet.position |= 0x2; 740 if (i == count - 1) packet.position |= 0x1; 741 message.push(packet); 742 } 743 744 socket._messageNumber++; 745 if (socket._messageNumber > MAX_MSG_NO) socket._messageNumber = 1; 746 747 socket._pending.push(message); 748 749 sendQueue.schedule(socket, [ 0, 0 ]); 750 751 return true; 752} 753Socket.prototype.destroy = function () { 754 this._endPoint.shutdown(this); 755} 756Socket.prototype._destroy = Socket.prototype.destroy; 757 758function Server () { 759 if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]); 760 761 events.EventEmitter.call(this); 762 763 var server = this; 764 765 var options; 766 767 if (typeof arguments[0] == 'function') { 768 options = {}; 769 server.on('connection', arguments[0]); 770 } else { 771 options = arguments[0] || {}; 772 if (typeof arguments[1] == 'function') { 773 server.on('connection', arguments[1]); 774 } 775 } 776 777 // The Node.js `net` module uses a property for connections because the 778 // connections property is disabled if the server is running in a 779 // multi-process model, if it has "slaves." UDT does not support multi-process 780 // model, so connections is plain-old property. 781 782 // 783 this.connections = 0; 784} 785util.inherits(Server, events.EventEmitter); 786exports.Server = Server; 787 788// TODO: Consolidate. 789function selectEndPoint (local) { 790 var endPoint; 791 // Use an existing datagram socket if one exists. 792 if (local.port == 0) { 793 endPoint = createEndPoint(local); 794 } else if (!(endPoint = lookupEndPoint(local))) { 795 endPoint = createEndPoint(local); 796 } 797 return endPoint; 798} 799 800Server.prototype.listen = function () { 801 var server = this; 802 803 var lastArg = arguments[arguments.length - 1]; 804 if (typeof lastArg == 'function') { 805 server.once('listening', lastArg); 806 } 807 808 var valid = validator(server); 809 810 var options = { port: arguments[0] || 0 }; 811 dns.lookup(arguments[1], valid(resolved)); 812 813 function resolved (ip, addressType) { 814 options.address = ip || '0.0.0.0'; 815 816 var endPoint = server._endPoint = selectEndPoint(options); 817 818 if (endPoint.server) { 819 throw new Error('already bound to UDT server'); 820 } 821 822 endPoint.server = server; 823 console.log(endPoint.local); 824 825 process.nextTick(function () { 826 server.emit('listening'); 827 }); 828 } 829} 830 831Server.prototype.close = function (callback) { 832 var server = this, endPoint = server._endPoint; 833 834 if (callback) server.once('close', callback); 835 836 server._closing = true; 837 838 if (endPoint.listeners == 0) { 839 endPoint._server = null; 840 server.emit('close'); 841 } 842} 843 844const SYN_COOKIE_SALT = crypto.randomBytes(64).toString('binary'); 845function synCookie (address, timestamp) { 846 var hash = crypto.createHash('sha1'); 847 hash.update(SYN_COOKIE_SALT + ':' + address.host + ':' + address.port + ':' + timestamp); 848 return parseInt(hash.digest('hex').substring(0, 8), 16); 849} 850 851exports.createServer = function () { 852 return new Server(arguments[0], arguments[1]); 853} 854 855function toArray (buffer) { 856 return buffer.toString('hex').replace(/(..)/g, ':$1') 857 .replace(/(.{12})/g, '\n$1') 858 .replace(/\n:/g, '\n'); 859}