├── .gitignore ├── README.md ├── TODO ├── index.js ├── package.json └── tests ├── index.js ├── scrample.js ├── test-close.js ├── test-connect-deferred.js ├── test-connect.js ├── test-end.js ├── test-on-close.js ├── test-scrample-write-big.js ├── test-scrample-write-end.js ├── test-scrample-write-sequence.js ├── test-scrample-write.js ├── test-write-big.js ├── test-write-end.js ├── test-write-sequence.js └── test-write.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | sandbox 3 | docs 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # utp 2 | 3 | utp (micro transport protocol) implementation in node. 4 | It is available through npm 5 | 6 | npm install utp 7 | 8 | ## What is utp? 9 | 10 | utp (micro transport protocol) is a network protocol similar to tcp that runs on top of udp. 11 | Since it build on top of udp it can provide great peer to peer connectivity 12 | through techniques like hole punching and similar while still providing a stream interface. 13 | It is currently the main network protocol powering bittorrent. 14 | 15 | ## BEWARE BEWARE BEWARE 16 | 17 | *This module is a work in progress! So beware of dragons!* 18 | 19 | ## Usage 20 | 21 | utp has the same interface as the net module in node. 22 | 23 | ``` js 24 | var utp = require('utp'); 25 | 26 | var server = utp.createServer(function(socket) { 27 | console.log('new connection!'); 28 | socket.on('data', function(data) { 29 | console.log('client says '+data); 30 | }); 31 | }); 32 | 33 | server.listen(10000, function() { 34 | var client = utp.connect(10000, 'localhost'); 35 | 36 | client.write('hello world'); 37 | }); 38 | ``` 39 | 40 | `server.listen()` also accepts a udp socket to listen on instead of a port. 41 | 42 | 43 | ## License 44 | 45 | MIT 46 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 0.0.4: 2 | ✔ uint overflow safety @done (13-06-16 04:16) 3 | ✔ closing using FIN @done (13-06-16 02:15) 4 | ✔ data packet spliting @done (13-06-20 23:43) 5 | 6 | 0.0.5: 7 | ✔ buffer overflow fixes @done (13-07-03 21:15) 8 | ✔ state packets should NOT inc seq_nr (see docs) @done (13-07-01 07:12) 9 | 10 | 0.1.0: 11 | ☐ keep-alive / timeouts 12 | ☐ complete protocol minus LEDBAT 13 | 14 | investigations: 15 | ☐ naggle algorithm 16 | ☐ selective ack 17 | ☐ delayed ack 18 | ☐ stun 19 | ☐ benchmark parsing 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var dgram = require('dgram'); 2 | var cyclist = require('cyclist'); 3 | var util = require('util'); 4 | var EventEmitter = require('events').EventEmitter; 5 | var Duplex = require('stream').Duplex; 6 | var bufferAlloc = require('buffer-alloc'); 7 | 8 | var EXTENSION = 0; 9 | var VERSION = 1; 10 | var UINT16 = 0xffff; 11 | var ID_MASK = 0xf << 4; 12 | var MTU = 1400; 13 | 14 | var PACKET_DATA = 0 << 4; 15 | var PACKET_FIN = 1 << 4; 16 | var PACKET_STATE = 2 << 4; 17 | var PACKET_RESET = 3 << 4; 18 | var PACKET_SYN = 4 << 4; 19 | 20 | var MIN_PACKET_SIZE = 20; 21 | var DEFAULT_WINDOW_SIZE = 1 << 18; 22 | var CLOSE_GRACE = 5000; 23 | 24 | var BUFFER_SIZE = 512; 25 | 26 | var uint32 = function(n) { 27 | return n >>> 0; 28 | }; 29 | 30 | var uint16 = function(n) { 31 | return n & UINT16; 32 | }; 33 | 34 | var timestamp = function() { 35 | var offset = process.hrtime(); 36 | var then = Date.now() * 1000; 37 | 38 | return function() { 39 | var diff = process.hrtime(offset); 40 | return uint32(then + 1000000 * diff[0] + ((diff[1] / 1000) | 0)); 41 | }; 42 | }(); 43 | 44 | var bufferToPacket = function(buffer) { 45 | var packet = {}; 46 | packet.id = buffer[0] & ID_MASK; 47 | packet.connection = buffer.readUInt16BE(2); 48 | packet.timestamp = buffer.readUInt32BE(4); 49 | packet.timediff = buffer.readUInt32BE(8); 50 | packet.window = buffer.readUInt32BE(12); 51 | packet.seq = buffer.readUInt16BE(16); 52 | packet.ack = buffer.readUInt16BE(18); 53 | packet.data = buffer.length > 20 ? buffer.slice(20) : null; 54 | return packet; 55 | }; 56 | 57 | var packetToBuffer = function(packet) { 58 | var buffer = bufferAlloc(20 + (packet.data ? packet.data.length : 0)); 59 | buffer[0] = packet.id | VERSION; 60 | buffer[1] = EXTENSION; 61 | buffer.writeUInt16BE(packet.connection, 2); 62 | buffer.writeUInt32BE(packet.timestamp, 4); 63 | buffer.writeUInt32BE(packet.timediff, 8); 64 | buffer.writeUInt32BE(packet.window, 12); 65 | buffer.writeUInt16BE(packet.seq, 16); 66 | buffer.writeUInt16BE(packet.ack, 18); 67 | if (packet.data) packet.data.copy(buffer, 20); 68 | return buffer; 69 | }; 70 | 71 | var createPacket = function(connection, id, data) { 72 | return { 73 | id: id, 74 | connection: id === PACKET_SYN ? connection._recvId : connection._sendId, 75 | seq: connection._seq, 76 | ack: connection._ack, 77 | timestamp: timestamp(), 78 | timediff: 0, 79 | window: DEFAULT_WINDOW_SIZE, 80 | data: data, 81 | sent: 0 82 | }; 83 | }; 84 | 85 | var Connection = function(port, host, socket, syn) { 86 | Duplex.call(this); 87 | var self = this; 88 | 89 | this.port = port; 90 | this.host = host; 91 | this.socket = socket; 92 | 93 | this._outgoing = cyclist(BUFFER_SIZE); 94 | this._incoming = cyclist(BUFFER_SIZE); 95 | this._closed = false; 96 | 97 | this._inflightPackets = 0; 98 | this._closed = false; 99 | this._alive = false; 100 | 101 | if (syn) { 102 | this._connecting = false; 103 | this._recvId = uint16(syn.connection+1); 104 | this._sendId = syn.connection; 105 | this._seq = (Math.random() * UINT16) | 0; 106 | this._ack = syn.seq; 107 | this._synack = createPacket(this, PACKET_STATE, null); 108 | 109 | this._transmit(this._synack); 110 | } else { 111 | this._connecting = true; 112 | this._recvId = 0; // tmp value for v8 opt 113 | this._sendId = 0; // tmp value for v8 opt 114 | this._seq = (Math.random() * UINT16) | 0; 115 | this._ack = 0; 116 | this._synack = null; 117 | 118 | socket.on('listening', function() { 119 | self._recvId = socket.address().port; // using the port gives us system wide clash protection 120 | self._sendId = uint16(self._recvId + 1); 121 | self._sendOutgoing(createPacket(self, PACKET_SYN, null)); 122 | }); 123 | 124 | socket.on('error', function(err) { 125 | self.emit('error', err); 126 | }); 127 | 128 | socket.bind(); 129 | } 130 | 131 | var resend = setInterval(this._resend.bind(this), 500); 132 | var keepAlive = setInterval(this._keepAlive.bind(this), 10*1000); 133 | var tick = 0; 134 | 135 | var closed = function() { 136 | if (++tick === 2) self._closing(); 137 | }; 138 | 139 | var sendFin = function() { 140 | if (self._connecting) return self.once('connect', sendFin); 141 | self._sendOutgoing(createPacket(self, PACKET_FIN, null)); 142 | self.once('flush', closed); 143 | }; 144 | 145 | this.once('finish', sendFin); 146 | this.once('close', function() { 147 | if (!syn) setTimeout(socket.close.bind(socket), CLOSE_GRACE); 148 | clearInterval(resend); 149 | clearInterval(keepAlive); 150 | }); 151 | this.once('end', function() { 152 | process.nextTick(closed); 153 | }); 154 | }; 155 | 156 | util.inherits(Connection, Duplex); 157 | 158 | Connection.prototype.setTimeout = function() { 159 | // TODO: impl me 160 | }; 161 | 162 | Connection.prototype.destroy = function() { 163 | this.end(); 164 | }; 165 | 166 | Connection.prototype.address = function() { 167 | return {port:this.port, address:this.host}; 168 | }; 169 | 170 | Connection.prototype._read = function() { 171 | // do nothing... 172 | }; 173 | 174 | Connection.prototype._write = function(data, enc, callback) { 175 | if (this._connecting) return this._writeOnce('connect', data, enc, callback); 176 | 177 | while (this._writable()) { 178 | var payload = this._payload(data); 179 | 180 | this._sendOutgoing(createPacket(this, PACKET_DATA, payload)); 181 | 182 | if (payload.length === data.length) return callback(); 183 | data = data.slice(payload.length); 184 | } 185 | 186 | this._writeOnce('flush', data, enc, callback); 187 | }; 188 | 189 | Connection.prototype._writeOnce = function(event, data, enc, callback) { 190 | this.once(event, function() { 191 | this._write(data, enc, callback); 192 | }); 193 | }; 194 | 195 | Connection.prototype._writable = function() { 196 | return this._inflightPackets < BUFFER_SIZE-1; 197 | }; 198 | 199 | Connection.prototype._payload = function(data) { 200 | if (data.length > MTU) return data.slice(0, MTU); 201 | return data; 202 | }; 203 | 204 | Connection.prototype._resend = function() { 205 | var offset = this._seq - this._inflightPackets; 206 | var first = this._outgoing.get(offset); 207 | if (!first) return; 208 | 209 | var timeout = 500000; 210 | var now = timestamp(); 211 | 212 | if (uint32(first.sent - now) < timeout) return; 213 | 214 | for (var i = 0; i < this._inflightPackets; i++) { 215 | var packet = this._outgoing.get(offset+i); 216 | if (uint32(packet.sent - now) >= timeout) this._transmit(packet); 217 | } 218 | }; 219 | 220 | Connection.prototype._keepAlive = function() { 221 | if (this._alive) return this._alive = false; 222 | this._sendAck(); 223 | }; 224 | 225 | Connection.prototype._closing = function() { 226 | if (this._closed) return; 227 | this._closed = true; 228 | process.nextTick(this.emit.bind(this, 'close')); 229 | }; 230 | 231 | // packet handling 232 | 233 | Connection.prototype._recvAck = function(ack) { 234 | var offset = this._seq - this._inflightPackets; 235 | var acked = uint16(ack - offset)+1; 236 | 237 | if (acked >= BUFFER_SIZE) return; // sanity check 238 | 239 | for (var i = 0; i < acked; i++) { 240 | this._outgoing.del(offset+i); 241 | this._inflightPackets--; 242 | } 243 | 244 | if (!this._inflightPackets) this.emit('flush'); 245 | }; 246 | 247 | Connection.prototype._recvIncoming = function(packet) { 248 | if (this._closed) return; 249 | 250 | if (packet.id === PACKET_SYN && this._connecting) { 251 | this._transmit(this._synack); 252 | return; 253 | } 254 | if (packet.id === PACKET_RESET) { 255 | this.push(null); 256 | this.end(); 257 | this._closing(); 258 | return; 259 | } 260 | if (this._connecting) { 261 | if (packet.id !== PACKET_STATE) return this._incoming.put(packet.seq, packet); 262 | 263 | this._ack = uint16(packet.seq-1); 264 | this._recvAck(packet.ack); 265 | this._connecting = false; 266 | this.emit('connect'); 267 | 268 | packet = this._incoming.del(packet.seq); 269 | if (!packet) return; 270 | } 271 | 272 | if (uint16(packet.seq - this._ack) >= BUFFER_SIZE) return this._sendAck(); // old packet 273 | 274 | this._recvAck(packet.ack); // TODO: other calcs as well 275 | 276 | if (packet.id === PACKET_STATE) return; 277 | this._incoming.put(packet.seq, packet); 278 | 279 | while (packet = this._incoming.del(this._ack+1)) { 280 | this._ack = uint16(this._ack+1); 281 | 282 | if (packet.id === PACKET_DATA) this.push(packet.data); 283 | if (packet.id === PACKET_FIN) this.push(null); 284 | } 285 | 286 | this._sendAck(); 287 | }; 288 | 289 | Connection.prototype._sendAck = function() { 290 | this._transmit(createPacket(this, PACKET_STATE, null)); // TODO: make this delayed 291 | }; 292 | 293 | Connection.prototype._sendOutgoing = function(packet) { 294 | this._outgoing.put(packet.seq, packet); 295 | this._seq = uint16(this._seq + 1); 296 | this._inflightPackets++; 297 | this._transmit(packet); 298 | }; 299 | 300 | Connection.prototype._transmit = function(packet) { 301 | packet.sent = packet.sent === 0 ? packet.timestamp : timestamp(); 302 | var message = packetToBuffer(packet); 303 | this._alive = true; 304 | this.socket.send(message, 0, message.length, this.port, this.host); 305 | }; 306 | 307 | 308 | var Server = function() { 309 | EventEmitter.call(this); 310 | this._socket = null; 311 | this._connections = {}; 312 | }; 313 | 314 | util.inherits(Server, EventEmitter); 315 | 316 | Server.prototype.address = function() { 317 | return this._socket.address(); 318 | }; 319 | 320 | Server.prototype.listenSocket = function(socket, onlistening) { 321 | this._socket = socket; 322 | 323 | var connections = this._connections; 324 | var self = this; 325 | 326 | socket.on('message', function(message, rinfo) { 327 | if (message.length < MIN_PACKET_SIZE) return; 328 | var packet = bufferToPacket(message); 329 | var id = rinfo.address+':'+(packet.id === PACKET_SYN ? uint16(packet.connection+1) : packet.connection); 330 | 331 | if (connections[id]) return connections[id]._recvIncoming(packet); 332 | if (packet.id !== PACKET_SYN || self._closed) return; 333 | 334 | connections[id] = new Connection(rinfo.port, rinfo.address, socket, packet); 335 | connections[id].on('close', function() { 336 | delete connections[id]; 337 | }); 338 | 339 | self.emit('connection', connections[id]); 340 | }); 341 | 342 | socket.once('listening', function() { 343 | self.emit('listening'); 344 | }); 345 | 346 | if (onlistening) self.once('listening', onlistening); 347 | } 348 | 349 | Server.prototype.listen = function(port, onlistening) { 350 | if (typeof port === 'object' && typeof port.on === 'function') return this.listenSocket(port, onlistening); 351 | var socket = dgram.createSocket('udp4'); 352 | this.listenSocket(socket, onlistening); 353 | socket.bind(port); 354 | }; 355 | 356 | Server.prototype.close = function(cb) { 357 | var self = this; 358 | var openConnections = 0; 359 | this._closed = true; 360 | 361 | function onClose() { 362 | if (--openConnections === 0) { 363 | if (self._socket) self._socket.close(); 364 | if (cb) cb(); 365 | } 366 | } 367 | 368 | for (var id in this._connections) { 369 | if (this._connections[id]._closed) continue; 370 | openConnections++; 371 | this._connections[id].once('close', onClose); 372 | this._connections[id].end(); 373 | } 374 | }; 375 | 376 | exports.createServer = function(onconnection) { 377 | var server = new Server(); 378 | if (onconnection) server.on('connection', onconnection); 379 | return server; 380 | }; 381 | 382 | exports.connect = function(port, host) { 383 | var socket = dgram.createSocket('udp4'); 384 | var connection = new Connection(port, host || '127.0.0.1', socket, null); 385 | 386 | socket.on('message', function(message) { 387 | if (message.length < MIN_PACKET_SIZE) return; 388 | var packet = bufferToPacket(message); 389 | 390 | if (packet.id === PACKET_SYN) return; 391 | if (packet.connection !== connection._recvId) return; 392 | 393 | connection._recvIncoming(packet); 394 | }); 395 | 396 | return connection; 397 | }; 398 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "utp", 3 | "version": "0.0.10", 4 | "repository": "git://github.com/mafintosh/utp", 5 | "description": "utp (micro transport protocol) implementation in node", 6 | "dependencies": { 7 | "buffer-alloc": "^1.1.0", 8 | "cyclist": "~0.1.0" 9 | }, 10 | "keywords": [ 11 | "utp", 12 | "micro", 13 | "transport", 14 | "protocol", 15 | "stream", 16 | "p2p", 17 | "udp" 18 | ], 19 | "author": "Mathias Buus Madsen ", 20 | "scripts": { 21 | "test": "node tests" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var exec = require('child_process').exec; 6 | 7 | var TIMEOUT = 20000; 8 | 9 | var tests = fs.readdirSync(__dirname).filter(function(file) { 10 | return !fs.statSync(path.join(__dirname,file)).isDirectory(); 11 | }).filter(function(file) { 12 | return /^test(-|_|\.).*\.js$/i.test(file); 13 | }).sort(); 14 | 15 | var cnt = 0; 16 | var all = tests.length; 17 | 18 | var loop = function() { 19 | var next = tests.shift(); 20 | 21 | if (!next) return console.log('\033[32m[ok]\033[39m all ok'); 22 | 23 | exec('node '+path.join(__dirname,next), {timeout:TIMEOUT}, function(err) { 24 | cnt++; 25 | 26 | if (err) { 27 | console.error('\033[31m[err]\033[39m '+cnt+'/'+all+' - '+next); 28 | console.error('\n '+(''+err.stack).split('\n').join('\n ')+'\n'); 29 | return process.exit(1); 30 | } 31 | 32 | console.log('\033[32m[ok]\033[39m '+cnt+'/'+all+' - '+next); 33 | setTimeout(loop, 100); 34 | }); 35 | }; 36 | 37 | loop(); 38 | -------------------------------------------------------------------------------- /tests/scrample.js: -------------------------------------------------------------------------------- 1 | var on = require('dgram').Socket.prototype.on; 2 | 3 | var LOSS_FACTOR = 5; 4 | var SHUFFLE_INTERVAL = 50; 5 | 6 | require('dgram').Socket.prototype.on = function(type, listener) { 7 | var fn = listener; 8 | 9 | if (type === 'message') { 10 | var i = 0; 11 | fn = function(message, rinfo) { 12 | var action = listener.bind(this, message, rinfo); 13 | 14 | if ((i++ % LOSS_FACTOR) === 0) return; 15 | setTimeout(action, (SHUFFLE_INTERVAL * Math.random()) | 0); 16 | }; 17 | } 18 | 19 | return on.call(this, type, fn); 20 | }; 21 | -------------------------------------------------------------------------------- /tests/test-close.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var onclose = function() { 5 | process.exit(0); 6 | }; 7 | 8 | var server = utp.createServer(function(socket) { 9 | socket.resume(); 10 | socket.end(); 11 | server.close(onclose); 12 | }) 13 | 14 | server.listen(53454, function () { 15 | var socket = utp.connect(53454); 16 | 17 | socket.once('connect', function () { 18 | socket.end(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/test-connect-deferred.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var connected = false; 5 | 6 | setTimeout(function() { 7 | utp.createServer(function() { 8 | connected = true; 9 | }).listen(53454); 10 | }, 100); 11 | 12 | var socket = utp.connect(53454); 13 | socket.on('connect', function() { 14 | assert(connected); 15 | process.exit(0); 16 | }); 17 | 18 | setTimeout(process.exit.bind(process, 1), 5000); -------------------------------------------------------------------------------- /tests/test-connect.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var connected = false; 5 | 6 | utp.createServer(function() { 7 | connected = true; 8 | }).listen(53454); 9 | 10 | var socket = utp.connect(53454); 11 | socket.on('connect', function() { 12 | assert(connected); 13 | process.exit(0); 14 | }); 15 | 16 | setTimeout(process.exit.bind(process, 1), 5000); -------------------------------------------------------------------------------- /tests/test-end.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var ended = false; 5 | 6 | utp.createServer(function(socket) { 7 | socket.resume(); 8 | socket.on('end', function() { 9 | ended = true; 10 | socket.end(); 11 | }); 12 | }).listen(53454); 13 | 14 | var socket = utp.connect(53454); 15 | 16 | socket.resume(); 17 | socket.on('end', function() { 18 | assert(ended); 19 | process.exit(0); 20 | }); 21 | socket.end(); 22 | 23 | setTimeout(process.exit.bind(process, 1), 5000); -------------------------------------------------------------------------------- /tests/test-on-close.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var closed = 0; 5 | var onclose = function() { 6 | closed++; 7 | if (closed === 2) process.exit(0); 8 | }; 9 | 10 | utp.createServer(function(socket) { 11 | socket.resume(); 12 | socket.on('end', function() { 13 | socket.end(); 14 | }); 15 | socket.on('close', onclose); 16 | }).listen(53454); 17 | 18 | var socket = utp.connect(53454); 19 | 20 | socket.resume(); 21 | socket.on('close', onclose); 22 | socket.end(); 23 | -------------------------------------------------------------------------------- /tests/test-scrample-write-big.js: -------------------------------------------------------------------------------- 1 | require('./scrample'); 2 | 3 | var utp = require('../index'); 4 | var assert = require('assert'); 5 | var bufferAlloc = require('buffer-alloc'); 6 | 7 | var big = bufferAlloc(10*1024); 8 | big.fill(1); 9 | 10 | utp.createServer(function(socket) { 11 | socket.on('data', function(data) { 12 | socket.write(data); 13 | }); 14 | socket.on('end', function() { 15 | socket.end(); 16 | }); 17 | }).listen(53454); 18 | 19 | var socket = utp.connect(53454); 20 | var recv = 0; 21 | 22 | socket.write(big); 23 | socket.end(); 24 | 25 | socket.on('data', function(data) { 26 | recv += data.length; 27 | console.log(recv); 28 | }); 29 | socket.on('end', function() { 30 | assert(recv === big.length); 31 | process.exit(0); 32 | }); 33 | 34 | setTimeout(process.exit.bind(process, 1), 15000); -------------------------------------------------------------------------------- /tests/test-scrample-write-end.js: -------------------------------------------------------------------------------- 1 | require('./scrample'); 2 | 3 | var utp = require('../index'); 4 | var assert = require('assert'); 5 | 6 | var ended = false; 7 | var dataed = false; 8 | 9 | utp.createServer(function(socket) { 10 | socket.on('data', function(data) { 11 | assert(data.toString() === 'client'); 12 | socket.write('server'); 13 | }); 14 | socket.on('end', function() { 15 | ended = true; 16 | socket.end(); 17 | }); 18 | }).listen(53454); 19 | 20 | var socket = utp.connect(53454); 21 | 22 | socket.on('data', function(data) { 23 | assert(data.toString() === 'server'); 24 | dataed = true; 25 | }); 26 | socket.on('end', function() { 27 | assert(ended); 28 | assert(dataed); 29 | process.exit(0); 30 | }); 31 | socket.write('client'); 32 | socket.end(); 33 | 34 | setTimeout(process.exit.bind(process, 1), 15000); -------------------------------------------------------------------------------- /tests/test-scrample-write-sequence.js: -------------------------------------------------------------------------------- 1 | require('./scrample'); 2 | 3 | var utp = require('../index'); 4 | var assert = require('assert'); 5 | var max = 10; 6 | 7 | utp.createServer(function(socket) { 8 | var prev = 0; 9 | socket.on('data', function(data) { 10 | assert(''+(prev++) === data.toString()); 11 | socket.write(data); 12 | if (prev === max) socket.end(); 13 | }); 14 | }).listen(53454); 15 | 16 | var socket = utp.connect(53454); 17 | var prev = 0; 18 | 19 | for (var i = 0; i < max; i++) { 20 | socket.write(''+i); 21 | } 22 | 23 | socket.on('data', function(data) { 24 | assert(''+(prev++) === data.toString()); 25 | }); 26 | socket.on('end', function() { 27 | process.exit(0); 28 | }); 29 | 30 | setTimeout(process.exit.bind(process, 1), 15000); -------------------------------------------------------------------------------- /tests/test-scrample-write.js: -------------------------------------------------------------------------------- 1 | require('./scrample'); 2 | 3 | var utp = require('../index'); 4 | var assert = require('assert'); 5 | 6 | utp.createServer(function(socket) { 7 | socket.on('data', function(data) { 8 | assert(data.toString() === 'client'); 9 | socket.write('server'); 10 | }); 11 | }).listen(53454); 12 | 13 | var socket = utp.connect(53454); 14 | socket.write('client'); 15 | socket.on('data', function(data) { 16 | assert(data.toString() === 'server'); 17 | process.exit(0); 18 | }); 19 | 20 | setTimeout(process.exit.bind(process, 1), 15000); -------------------------------------------------------------------------------- /tests/test-write-big.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | var bufferAlloc = require('buffer-alloc'); 4 | 5 | var big = bufferAlloc(100*1024); 6 | big.fill(1); 7 | 8 | utp.createServer(function(socket) { 9 | socket.on('data', function(data) { 10 | socket.write(data); 11 | }); 12 | socket.on('end', function() { 13 | socket.end(); 14 | }); 15 | }).listen(53454); 16 | 17 | var socket = utp.connect(53454); 18 | var recv = 0; 19 | 20 | socket.write(big); 21 | socket.end(); 22 | 23 | socket.on('data', function(data) { 24 | recv += data.length; 25 | }); 26 | socket.on('end', function() { 27 | assert(recv === big.length); 28 | process.exit(0); 29 | }); 30 | 31 | setTimeout(process.exit.bind(process, 1), 5000); -------------------------------------------------------------------------------- /tests/test-write-end.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | var ended = false; 5 | var dataed = false; 6 | 7 | utp.createServer(function(socket) { 8 | socket.on('data', function(data) { 9 | assert(data.toString() === 'client'); 10 | socket.write('server'); 11 | }); 12 | socket.on('end', function() { 13 | ended = true; 14 | socket.end(); 15 | }); 16 | }).listen(53454); 17 | 18 | var socket = utp.connect(53454); 19 | 20 | socket.on('data', function(data) { 21 | assert(data.toString() === 'server'); 22 | dataed = true; 23 | }); 24 | socket.on('end', function() { 25 | assert(ended); 26 | assert(dataed); 27 | process.exit(0); 28 | }); 29 | socket.write('client'); 30 | socket.end(); 31 | 32 | setTimeout(process.exit.bind(process, 1), 5000); -------------------------------------------------------------------------------- /tests/test-write-sequence.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | var max = 1000; 4 | 5 | utp.createServer(function(socket) { 6 | var prev = 0; 7 | socket.on('data', function(data) { 8 | assert(''+(prev++) === data.toString()); 9 | socket.write(data); 10 | if (prev === max) socket.end(); 11 | }); 12 | }).listen(53454); 13 | 14 | var socket = utp.connect(53454); 15 | var prev = 0; 16 | 17 | for (var i = 0; i < max; i++) { 18 | socket.write(''+i); 19 | } 20 | 21 | socket.on('data', function(data) { 22 | assert(''+(prev++) === data.toString()); 23 | }); 24 | socket.on('end', function() { 25 | process.exit(0); 26 | }); 27 | 28 | setTimeout(process.exit.bind(process, 1), 50000); -------------------------------------------------------------------------------- /tests/test-write.js: -------------------------------------------------------------------------------- 1 | var utp = require('../index'); 2 | var assert = require('assert'); 3 | 4 | utp.createServer(function(socket) { 5 | socket.on('data', function(data) { 6 | assert(data.toString() === 'client'); 7 | socket.write('server'); 8 | }); 9 | }).listen(53454); 10 | 11 | var socket = utp.connect(53454); 12 | socket.write('client'); 13 | socket.on('data', function(data) { 14 | assert(data.toString() === 'server'); 15 | process.exit(0); 16 | }); 17 | 18 | setTimeout(process.exit.bind(process, 1), 5000); --------------------------------------------------------------------------------