Comparing version 0.2.0 to 0.2.1
@@ -20,2 +20,3 @@ /// <reference types="node" /> | ||
toString(): string; | ||
isRetransmittable(): boolean; | ||
abstract byteLen(): number; | ||
@@ -22,0 +23,0 @@ abstract writeTo(bufv: BufferVisitor): BufferVisitor; |
@@ -92,2 +92,5 @@ 'use strict'; | ||
} | ||
isRetransmittable() { | ||
return this.name !== 'ACK' && this.name !== 'STOP_WAITING'; | ||
} | ||
[util_1.inspect.custom](_depth, _options) { | ||
@@ -176,9 +179,9 @@ return `<${this.constructor.name} ${this.toString()}>`; | ||
super(0b10000000, 'STREAM'); | ||
if (data != null && data.length === 0) { | ||
data = null; | ||
} | ||
this.streamID = streamID; | ||
this.offset = offset; | ||
if (!isFIN && (!Buffer.isBuffer(data) || data.length === 0)) { | ||
throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); | ||
} | ||
this.data = data; | ||
this.isFIN = isFIN || data == null || data.length === 0; | ||
this.isFIN = isFIN || data == null; | ||
} | ||
@@ -185,0 +188,0 @@ valueOf() { |
@@ -66,2 +66,3 @@ /// <reference types="node" /> | ||
frames: Frame[]; | ||
isRetransmittable: boolean; | ||
constructor(connectionID: ConnectionID, packetNumber: PacketNumber, nonce?: Buffer | null); | ||
@@ -68,0 +69,0 @@ valueOf(): { |
@@ -293,2 +293,3 @@ 'use strict'; | ||
this.frames = []; | ||
this.isRetransmittable = true; | ||
} | ||
@@ -295,0 +296,0 @@ valueOf() { |
@@ -41,3 +41,3 @@ /// <reference types="node" /> | ||
_sendStopWaitingFrame(leastUnacked: number): void; | ||
_retransmit(frame: AckFrame): void; | ||
_retransmit(frame: AckFrame): number; | ||
_sendPacket(packet: Packet, callback: (...args: any[]) => void): void; | ||
@@ -82,4 +82,4 @@ _trySendAckFrame(): void; | ||
keepAlivePingSent: boolean; | ||
maxIncomingByteOffset: Offset; | ||
outgoingWindowByteOffset: Offset; | ||
maxIncomingByteOffset: number; | ||
outgoingWindowByteOffset: number; | ||
constructor(); | ||
@@ -86,0 +86,0 @@ } |
@@ -15,3 +15,3 @@ 'use strict'; | ||
const common_1 = require("./internal/common"); | ||
const debug = util_1.debuglog('quic'); | ||
const debug = util_1.debuglog('quic:session'); | ||
// | ||
@@ -69,2 +69,3 @@ // *************** Session *************** | ||
regularPacket.addFrames(frame); | ||
regularPacket.isRetransmittable = frame.isRetransmittable(); | ||
this._sendPacket(regularPacket, callback); | ||
@@ -75,5 +76,6 @@ } | ||
this[symbol_1.kNextPacketNumber] = packetNumber.nextNumber(); | ||
const frame = new frame_1.StopWaitingFrame(packetNumber, leastUnacked); | ||
const regularPacket = new packet_1.RegularPacket(this[symbol_1.kID], packetNumber); | ||
const frame = new frame_1.StopWaitingFrame(packetNumber, leastUnacked); | ||
regularPacket.addFrames(frame); | ||
regularPacket.isRetransmittable = false; | ||
debug(`session %s - write StopWaitingFrame, packetNumber: %d, leastUnacked: %d`, this.id, packetNumber.valueOf(), leastUnacked); | ||
@@ -93,3 +95,3 @@ this._sendPacket(regularPacket, (err) => { | ||
if (packetNumber > frame.largestAcked) { | ||
return; | ||
return 0; | ||
} | ||
@@ -113,2 +115,3 @@ if (packetNumber <= frame.lowestAcked || frame.acksPacket(packetNumber)) { | ||
debug(`session %s - retransmit, count: %d`, this.id, count); | ||
return count; | ||
} | ||
@@ -127,5 +130,7 @@ _sendPacket(packet, callback) { | ||
} | ||
this[symbol_1.kUnackedPackets].push(packet); | ||
if (this[symbol_1.kUnackedPackets].length > 1000) { | ||
return callback(error_1.QuicError.fromError(error_1.QuicError.QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS)); | ||
if (packet.isRetransmittable) { | ||
this[symbol_1.kUnackedPackets].push(packet); | ||
if (this[symbol_1.kUnackedPackets].length > 1000) { | ||
return callback(error_1.QuicError.fromError(error_1.QuicError.QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS)); | ||
} | ||
} | ||
@@ -164,6 +169,6 @@ } | ||
break; | ||
case 'ACK': // TODO | ||
case 'ACK': | ||
this._handleACKFrame(frame); | ||
break; | ||
case 'STOP_WAITING': // TODO | ||
case 'STOP_WAITING': | ||
// The STOP_WAITING frame is sent to inform the peer that it should not continue to | ||
@@ -240,3 +245,5 @@ // wait for packets with packet numbers lower than a specified value. | ||
// it has received in an ack as the stop waiting frame’s least unacked value. | ||
this._sendStopWaitingFrame(frame.largestAcked); | ||
if (frame.hasMissingRanges()) { | ||
this._sendStopWaitingFrame(frame.largestAcked); | ||
} | ||
this._retransmit(frame); | ||
@@ -251,5 +258,7 @@ } | ||
const streamID = frame.streamID.valueOf(); | ||
const offset = frame.offset.valueOf(); | ||
debug(`session %s - received WindowUpdateFrame, streamID: %d, offset: %d`, this.id, streamID, offset); | ||
if (streamID === 0) { | ||
if (frame.offset.gt(this[symbol_1.kState].outgoingWindowByteOffset)) { | ||
this[symbol_1.kState].outgoingWindowByteOffset = frame.offset; | ||
if (offset > this[symbol_1.kState].outgoingWindowByteOffset) { | ||
this[symbol_1.kState].outgoingWindowByteOffset = offset; | ||
} | ||
@@ -260,4 +269,4 @@ } | ||
if (stream != null && !stream.destroyed) { | ||
if (frame.offset.gt(stream[symbol_1.kState].outgoingWindowByteOffset)) { | ||
stream[symbol_1.kState].outgoingWindowByteOffset = frame.offset; | ||
if (offset > stream[symbol_1.kState].outgoingWindowByteOffset) { | ||
stream[symbol_1.kState].outgoingWindowByteOffset = offset; | ||
stream._tryFlushCallbacks(); | ||
@@ -432,4 +441,4 @@ } | ||
// Both stream and session windows start with a default value of 16 KB | ||
this.maxIncomingByteOffset = new protocol_1.Offset(16 * 1024); | ||
this.outgoingWindowByteOffset = new protocol_1.Offset(16 * 1024); | ||
this.maxIncomingByteOffset = 16 * 1024; | ||
this.outgoingWindowByteOffset = 16 * 1024; | ||
} | ||
@@ -465,2 +474,6 @@ } | ||
} | ||
// if there is no missed packet number, we do not need ack as quickly as possible | ||
if (numbersAcked.length < 256 && numbersAcked.length >= (this.largestAcked - this.lowestAcked + 1)) { | ||
return null; | ||
} | ||
numbersAcked.sort((a, b) => b - a); | ||
@@ -477,2 +490,3 @@ if (numbersAcked[0] <= this.lowestAcked) { | ||
let range = new frame_1.AckRange(this.largestAcked, this.largestAcked); | ||
// numbersAcked should include largestAcked and lowestAcked for this AGL | ||
for (let i = 1, l = numbersAcked.length; i < l; i++) { | ||
@@ -491,3 +505,3 @@ const num = numbersAcked[i]; | ||
range = new frame_1.AckRange(num, num); | ||
} | ||
} // else ingnore | ||
} | ||
@@ -505,3 +519,3 @@ if (range.first > frame.lowestAcked) { | ||
else { | ||
this.lowestAcked = frame.ackRanges[frame.ackRanges.length - 1].first; | ||
this.lowestAcked = frame.ackRanges[frame.ackRanges.length - 1].first; // update by StopWaiting | ||
} | ||
@@ -513,2 +527,3 @@ // if ackRanges.length > 256, ignore some ranges between | ||
} | ||
debug(`after build AckFrame, largestAcked: %d, lowestAcked: %d, numbersAcked %j`, this.largestAcked, this.lowestAcked, numbersAcked); | ||
return frame; | ||
@@ -515,0 +530,0 @@ } |
@@ -19,2 +19,3 @@ /// <reference types="node" /> | ||
_handleRstFrame(frame: RstStreamFrame, rcvTime: number): void; | ||
_tryUpdateWindow(offset: number): void; | ||
_tryFlushCallbacks(): void; | ||
@@ -21,0 +22,0 @@ _flushData(callback: (...args: any[]) => void): void; |
@@ -12,3 +12,3 @@ 'use strict'; | ||
const symbol_1 = require("./internal/symbol"); | ||
const debug = util_1.debuglog('quic'); | ||
const debug = util_1.debuglog('quic:stream'); | ||
class Stream extends stream_1.Duplex { | ||
@@ -56,8 +56,14 @@ constructor(streamID, session, options) { | ||
this[symbol_1.kState].lastActivityTime = rcvTime; | ||
debug(`stream %s - received StreamFrame, offset: %d, data size: %d, isFIN: %s`, this.id, frame.offset.valueOf(), frame.data == null ? 0 : frame.data.length, frame.isFIN); | ||
const offset = frame.offset.valueOf(); | ||
const byteLen = frame.data == null ? 0 : frame.data.length; | ||
debug(`stream %s - received StreamFrame, offset: %d, data size: %d, isFIN: %s`, this.id, offset, byteLen, frame.isFIN); | ||
if (frame.isFIN) { | ||
this[symbol_1.kState].remoteFIN = true; | ||
this[symbol_1.kState].readQueue.setEndOffset(offset + byteLen); | ||
} | ||
if (frame.data != null) { | ||
if (this[symbol_1.kState].readQueue.hasOffset(frame.offset.valueOf())) { | ||
if (this[symbol_1.kState].readQueue.hasOffset(offset)) { | ||
return; // duplicated frame | ||
} | ||
if (frame.offset.gt(this[symbol_1.kState].maxIncomingByteOffset)) { | ||
if (offset > this[symbol_1.kState].maxIncomingByteOffset) { | ||
this.emit('error', new Error('The window of byte offset overflowed')); | ||
@@ -67,9 +73,8 @@ this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_ERROR_PROCESSING_STREAM)); | ||
} | ||
this[symbol_1.kState].bytesRead += frame.data.length; | ||
this[symbol_1.kState].bytesRead += byteLen; | ||
this[symbol_1.kState].readQueue.push(frame); | ||
if (!frame.isFIN) { | ||
this._tryUpdateWindow(offset); | ||
} | ||
} | ||
if (frame.isFIN && !this[symbol_1.kState].remoteFIN) { | ||
this[symbol_1.kState].remoteFIN = true; | ||
this[symbol_1.kState].readQueue.setEndOffset(frame.offset.valueOf()); | ||
} | ||
this._read(protocol_1.MaxStreamBufferSize * 10); // try to read all | ||
@@ -96,2 +101,8 @@ if (this[symbol_1.kState].readQueue.byteLen > protocol_1.MaxStreamReadCacheSize) { | ||
} | ||
_tryUpdateWindow(offset) { | ||
if (offset * 2 > this[symbol_1.kState].maxIncomingByteOffset) { | ||
this[symbol_1.kState].maxIncomingByteOffset *= 2; | ||
this[symbol_1.kSession]._windowUpdate(new protocol_1.Offset(this[symbol_1.kState].maxIncomingByteOffset), this[symbol_1.kID]); | ||
} | ||
} | ||
_tryFlushCallbacks() { | ||
@@ -103,19 +114,18 @@ if (this[symbol_1.kState].writeCallbacks.length === 0 || this[symbol_1.kState].flushing) { | ||
if ((nextByteLen > 0) && | ||
(this[symbol_1.kState].writeOffset.valueOf() + nextByteLen > this[symbol_1.kState].outgoingWindowByteOffset.valueOf())) { | ||
(this[symbol_1.kState].writeOffset.valueOf() + nextByteLen > this[symbol_1.kState].outgoingWindowByteOffset)) { | ||
// should wait for WINDOW_UPDATE | ||
debug(`stream %s - wait for WINDOW_UPDATE, writeOffset: %d, outgoingOffset: %d, to write size: %d`, this.id, this[symbol_1.kState].writeOffset.valueOf(), this[symbol_1.kState].outgoingWindowByteOffset.valueOf(), this[symbol_1.kState].bufferList.byteLen); | ||
debug(`stream %s - wait for WINDOW_UPDATE, writeOffset: %d, outgoingOffset: %d, to write size: %d`, this.id, this[symbol_1.kState].writeOffset.valueOf(), this[symbol_1.kState].outgoingWindowByteOffset, this[symbol_1.kState].bufferList.byteLen); | ||
return; | ||
} | ||
this[symbol_1.kState].flushing = true; | ||
this._flushData((err, finish) => { | ||
this._flushData((err, shouldContinue) => { | ||
this[symbol_1.kState].flushing = false; | ||
if (err != null || finish === true) { | ||
for (const cb of this[symbol_1.kState].writeCallbacks) { | ||
cb(err); | ||
} | ||
this[symbol_1.kState].writeCallbacks.length = 0; | ||
// continue to send data or send FIN | ||
if (err == null && (shouldContinue || (this[symbol_1.kState].shouldFIN && !this[symbol_1.kState].localFIN))) { | ||
return this._tryFlushCallbacks(); | ||
} | ||
else { | ||
this._tryFlushCallbacks(); | ||
for (const cb of this[symbol_1.kState].writeCallbacks) { | ||
cb(err); | ||
} | ||
this[symbol_1.kState].writeCallbacks.length = 0; | ||
}); | ||
@@ -132,4 +142,3 @@ } | ||
} | ||
const isFIN = this[symbol_1.kState].shouldFIN && this[symbol_1.kState].bufferList.length === 0; | ||
const streamFrame = new frame_1.StreamFrame(this[symbol_1.kID], offet, buf, isFIN); | ||
const streamFrame = new frame_1.StreamFrame(this[symbol_1.kID], offet, buf, this[symbol_1.kState].shouldFIN && this[symbol_1.kState].bufferList.byteLen === 0); | ||
if (streamFrame.isFIN) { | ||
@@ -143,3 +152,3 @@ this[symbol_1.kState].localFIN = true; | ||
} | ||
callback(null, this[symbol_1.kState].bufferList.byteLen === 0); | ||
callback(null, this[symbol_1.kState].bufferList.byteLen > 0); | ||
}); | ||
@@ -214,3 +223,3 @@ } | ||
this.aborted = false; | ||
this.destroyed = true; | ||
this.destroyed = false; | ||
this.finished = false; | ||
@@ -224,4 +233,4 @@ this.bytesRead = 0; | ||
// Both stream and session windows start with a default value of 16 KB | ||
this.maxIncomingByteOffset = new protocol_1.Offset(16 * 1024 * 1024); // TODO | ||
this.outgoingWindowByteOffset = new protocol_1.Offset(16 * 1024 * 1024); | ||
this.maxIncomingByteOffset = 16 * 1024; | ||
this.outgoingWindowByteOffset = 16 * 1024; | ||
this.writeCallbacks = []; | ||
@@ -307,3 +316,3 @@ } | ||
hasOffset(offset) { | ||
if (offset <= this.readOffset) { | ||
if (offset < this.readOffset) { | ||
return true; | ||
@@ -310,0 +319,0 @@ } |
@@ -7,3 +7,3 @@ { | ||
], | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"main": "dist/index.js", | ||
@@ -40,2 +40,3 @@ "license": "MIT", | ||
"dist", | ||
"LICENSE", | ||
"README.md", | ||
@@ -42,0 +43,0 @@ "CHANGELOG.md" |
@@ -13,6 +13,16 @@ # QUIC (WIP) | ||
## Demo | ||
### QUIC without TLS | ||
https://github.com/toajs/quic/blob/master/example/echo.js | ||
```sh | ||
node -r ts-node/register example/echo.js | ||
``` | ||
## License | ||
QUIC for Node.js is licensed under the [MIT](https://github.com/toajs/quic/blob/master/LICENSE) license. | ||
Copyright © 2016-2017 Toajs. | ||
Copyright © 2016-2018 Toajs. | ||
@@ -19,0 +29,0 @@ [npm-url]: https://npmjs.org/package/quic |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
322113
5335
36