PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/index.js

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