Comparing version 0.2.2 to 0.4.0
@@ -7,2 +7,6 @@ import { kClientState } from './internal/symbol'; | ||
_resendPacketsForNegotiation(): void; | ||
setKeepAlive(enable: boolean, _initialDelay?: number): void; | ||
ref(): void; | ||
unref(): void; | ||
spawn(port: number, address?: string): Promise<Client>; | ||
connect(port: number, address?: string): Promise<any>; | ||
@@ -9,0 +13,0 @@ } |
@@ -7,2 +7,3 @@ 'use strict'; | ||
const util_1 = require("util"); | ||
const constant_1 = require("./internal/constant"); | ||
const common_1 = require("./internal/common"); | ||
@@ -34,3 +35,3 @@ const error_1 = require("./internal/error"); | ||
this._intervalCheck(time); | ||
}, 1024); | ||
}, 512); | ||
} | ||
@@ -48,11 +49,55 @@ _resendPacketsForNegotiation() { | ||
} | ||
async connect(port, address = 'localhost') { | ||
if (this[symbol_1.kSocket] != null) { | ||
throw new Error('Client connecting duplicated'); | ||
setKeepAlive(enable, _initialDelay) { | ||
this[symbol_1.kState].keepAlivePingSent = enable; | ||
// initialDelay TODO | ||
} | ||
ref() { | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket == null) { | ||
throw new Error('Client not connect'); | ||
} | ||
socket.ref(); | ||
} | ||
unref() { | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket == null) { | ||
throw new Error('Client not connect'); | ||
} | ||
socket.unref(); | ||
} | ||
async spawn(port, address = 'localhost') { | ||
if (this[symbol_1.kState].destroyed) { | ||
throw new Error('Client destroyed'); | ||
} | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket == null || socket[symbol_1.kState].destroyed) { | ||
throw new Error('the underlying socket destroyed'); | ||
} | ||
const addr = await common_1.lookup(address); | ||
debug(`client connect: %s, %d, %j`, address, port, addr); | ||
const client = new Client(); | ||
socket[symbol_1.kState].conns.set(client.id, client); | ||
socket[symbol_1.kState].exclusive = false; | ||
client[symbol_1.kSocket] = socket; | ||
client[symbol_1.kState].localFamily = this[symbol_1.kState].localFamily; | ||
client[symbol_1.kState].localAddress = this[symbol_1.kState].localAddress; | ||
client[symbol_1.kState].localPort = this[symbol_1.kState].localPort; | ||
client[symbol_1.kState].localAddr = new protocol_1.SocketAddress(socket.address()); | ||
client[symbol_1.kState].remotePort = port; | ||
client[symbol_1.kState].remoteAddress = addr.address; | ||
client[symbol_1.kState].remoteFamily = 'IPv' + addr.family; | ||
client[symbol_1.kState].remoteAddr = | ||
new protocol_1.SocketAddress({ port, address: addr.address, family: `IPv${addr.family}` }); | ||
client[symbol_1.kState].maxPacketSize = this[symbol_1.kState].maxPacketSize; | ||
return client; | ||
} | ||
async connect(port, address = 'localhost') { | ||
if (this[symbol_1.kState].destroyed) { | ||
throw new Error('Client destroyed'); | ||
} | ||
if (this[symbol_1.kSocket] != null) { | ||
throw new Error('Client connecting duplicated'); | ||
} | ||
const addr = await common_1.lookup(address); | ||
debug(`client connect: %s, %d, %j`, address, port, addr); | ||
this[symbol_1.kState].remotePort = port; | ||
@@ -62,7 +107,10 @@ this[symbol_1.kState].remoteAddress = addr.address; | ||
this[symbol_1.kState].remoteAddr = new protocol_1.SocketAddress({ port, address: addr.address, family: `IPv${addr.family}` }); | ||
this[symbol_1.kState].maxPacketSize = | ||
this[symbol_1.kState].localFamily === protocol_1.FamilyType.IPv6 ? constant_1.MaxPacketSizeIPv6 : constant_1.MaxPacketSizeIPv4; | ||
const socket = this[symbol_1.kSocket] = socket_1.createSocket(addr.family); | ||
socket[symbol_1.kState].conns.set(this.id, this); | ||
socket | ||
.on('error', (err) => this.emit('error', err)) | ||
.on('close', () => this.destroy(new Error('the underlying socket closed'))) | ||
.on('message', (msg, rinfo) => clientOnMessage(this, msg, rinfo)); | ||
.on('message', socketOnMessage); | ||
const res = new Promise((resolve, reject) => { | ||
@@ -92,4 +140,4 @@ socket.once('listening', () => { | ||
exports.ClientState = ClientState; | ||
function clientOnMessage(client, msg, rinfo) { | ||
if (msg.length === 0 || client.destroyed) { | ||
function socketOnMessage(msg, rinfo) { | ||
if (msg.length === 0 || this[symbol_1.kState].destroyed) { | ||
return; | ||
@@ -99,3 +147,3 @@ } | ||
// If it does, we only read a truncated packet, which will then end up undecryptable | ||
if (msg.length > protocol_1.MaxReceivePacketSize) { | ||
if (msg.length > constant_1.MaxReceivePacketSize) { | ||
debug(`client message - receive too large data: %d bytes`, msg.length); | ||
@@ -106,17 +154,23 @@ // msg = msg.slice(0, MaxReceivePacketSize) | ||
const rcvTime = Date.now(); | ||
const bufv = common_1.Visitor.wrap(msg); | ||
const bufv = new common_1.BufferVisitor(msg); | ||
let packet = null; | ||
try { | ||
packet = packet_1.parsePacket(bufv, protocol_1.SessionType.SERVER, client[symbol_1.kVersion]); | ||
packet = packet_1.parsePacket(bufv, protocol_1.SessionType.SERVER); | ||
} | ||
catch (err) { | ||
debug(`session %s - parsing packet error: %o`, client.id, err); | ||
debug(`client message - parsing packet error: %o`, err); | ||
// drop this packet if we can't parse the Public Header | ||
return; | ||
} | ||
// reject packets with the wrong connection ID | ||
if (!client[symbol_1.kID].equals(packet.connectionID)) { | ||
debug(`session %s - received a spoofed packet with wrong ID: %s`, client.id, packet.connectionID); | ||
const connectionID = packet.connectionID.valueOf(); | ||
const client = this[symbol_1.kState].conns.get(connectionID); | ||
if (client == null) { | ||
// reject packets with the wrong connection ID | ||
debug(`client message - received a spoofed packet with wrong ID: %s`, connectionID); | ||
return; | ||
} | ||
else if (client.destroyed) { | ||
// Late packet for closed session | ||
return; | ||
} | ||
if (packet.isReset()) { | ||
@@ -123,0 +177,0 @@ // check if the remote address and the connection ID match |
export { Stream } from './stream'; | ||
export { Session } from './session'; | ||
export { ServerSession } from './server'; | ||
export { ServerSession, Server } from './server'; | ||
export { Client } from './client'; | ||
export { Server } from './server'; |
@@ -12,6 +12,5 @@ 'use strict'; | ||
exports.ServerSession = server_1.ServerSession; | ||
exports.Server = server_1.Server; | ||
var client_1 = require("./client"); | ||
exports.Client = client_1.Client; | ||
var server_2 = require("./server"); | ||
exports.Server = server_2.Server; | ||
//# sourceMappingURL=index.js.map |
/// <reference types="node" /> | ||
import { lookup as dnsLookup } from 'dns'; | ||
export declare const lookup: typeof dnsLookup.__promisify__; | ||
export interface BufferVisitor extends Buffer { | ||
v: Visitor; | ||
} | ||
/** Visitor representing a Buffer visitor. */ | ||
export declare class Visitor { | ||
static wrap(buf: Buffer): BufferVisitor; | ||
start: number; | ||
@@ -16,2 +11,8 @@ end: number; | ||
} | ||
export declare class BufferVisitor extends Visitor { | ||
buf: Buffer; | ||
constructor(buf: Buffer, start?: number, end?: number); | ||
readonly length: number; | ||
isOutside(): boolean; | ||
} | ||
export interface ToBuffer { | ||
@@ -21,6 +22,8 @@ byteLen(): number; | ||
} | ||
export declare function toBuffer(obj: ToBuffer): BufferVisitor; | ||
export declare function toBuffer(obj: ToBuffer): Buffer; | ||
export declare const Float16MaxValue = 4396972769280; | ||
export declare function readUFloat16(buf: Buffer, offset?: number): number; | ||
export declare function writeUFloat16(buf: Buffer, value: number, offset: number): Buffer; | ||
export declare function readUnsafeUIntLE(buf: Buffer, offset: number, len: number): number; | ||
export declare function writeUnsafeUIntLE(buf: Buffer, val: number, offset: number, len: number): Buffer; | ||
export declare class Queue<T> { | ||
@@ -27,0 +30,0 @@ private tail; |
@@ -9,8 +9,3 @@ 'use strict'; | ||
exports.lookup = util_1.promisify(dns_1.lookup); | ||
/** Visitor representing a Buffer visitor. */ | ||
class Visitor { | ||
static wrap(buf) { | ||
Object.assign(buf, { v: new Visitor() }); | ||
return buf; | ||
} | ||
constructor(start = 0, end = 0) { | ||
@@ -37,6 +32,18 @@ this.start = start; | ||
exports.Visitor = Visitor; | ||
class BufferVisitor extends Visitor { | ||
constructor(buf, start = 0, end = 0) { | ||
super(start, end); | ||
this.buf = buf; | ||
} | ||
get length() { | ||
return this.buf.length; | ||
} | ||
isOutside() { | ||
return this.end > this.buf.length; | ||
} | ||
} | ||
exports.BufferVisitor = BufferVisitor; | ||
function toBuffer(obj) { | ||
const bufv = obj.writeTo(Visitor.wrap(Buffer.alloc(obj.byteLen()))); | ||
bufv.v.reset(0, 0); | ||
return bufv; | ||
const bufv = obj.writeTo(new BufferVisitor(Buffer.alloc(obj.byteLen()))); | ||
return bufv.buf; | ||
} | ||
@@ -92,2 +99,36 @@ exports.toBuffer = toBuffer; | ||
exports.writeUFloat16 = writeUFloat16; | ||
const unsafeUIntRadix = 0xffffffffffff + 1; | ||
function readUnsafeUIntLE(buf, offset, len) { | ||
let val = 0; | ||
if (len > 6) { | ||
val = buf.readUIntLE(offset, 6); | ||
const high = buf.readUIntLE(offset + 6, len - 6); | ||
if (high > 0) { | ||
val += high * unsafeUIntRadix; | ||
} | ||
} | ||
else if (len > 0) { | ||
val = buf.readUIntLE(offset, len); | ||
} | ||
return val; | ||
} | ||
exports.readUnsafeUIntLE = readUnsafeUIntLE; | ||
function writeUnsafeUIntLE(buf, val, offset, len) { | ||
if (len > 6) { | ||
if (val <= 0xffffffffffff) { | ||
buf.writeUIntLE(val, offset, 6); | ||
buf.writeUIntLE(0, offset + 6, len - 6); // clear cached bits | ||
} | ||
else { | ||
const high = Math.floor(val / unsafeUIntRadix); | ||
buf.writeUIntLE(val - high * unsafeUIntRadix, offset, 6); | ||
buf.writeUIntLE(high, offset + 6, len - 6); | ||
} | ||
} | ||
else if (len > 0) { | ||
buf.writeUIntLE(val, offset, len); | ||
} | ||
return buf; | ||
} | ||
exports.writeUnsafeUIntLE = writeUnsafeUIntLE; | ||
class Queue { | ||
@@ -94,0 +135,0 @@ constructor() { |
@@ -6,3 +6,3 @@ import { BufferVisitor } from './common'; | ||
static fromBuffer(bufv: BufferVisitor): QUICError; | ||
static checkAny(err?: any): QUICError | null; | ||
static checkAny(err?: any): QUICError | QUICStreamError | null; | ||
name: string; | ||
@@ -24,3 +24,3 @@ code: number; | ||
static fromBuffer(bufv: BufferVisitor): QUICStreamError; | ||
static checkAny(err?: any): QUICError | null; | ||
static checkAny(err?: any): QUICError | QUICStreamError | null; | ||
name: string; | ||
@@ -27,0 +27,0 @@ code: number; |
@@ -485,7 +485,7 @@ 'use strict'; | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new QUICError('INVALID_ERROR_CODE'); | ||
} | ||
const code = bufv.readUInt32LE(bufv.v.start); | ||
const code = bufv.buf.readUInt32LE(bufv.start); | ||
return new QUICError(code); | ||
@@ -497,2 +497,5 @@ } | ||
} | ||
if (err instanceof QUICStreamError) { | ||
return err; | ||
} | ||
return QUICError.fromError(err); | ||
@@ -521,4 +524,7 @@ } | ||
writeTo(bufv) { | ||
bufv.v.walk(4); | ||
bufv.writeUInt32LE(this.code, bufv.v.start); | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new QUICError('INVALID_ERROR_CODE'); | ||
} | ||
bufv.buf.writeUInt32LE(this.code, bufv.start); | ||
return bufv; | ||
@@ -543,7 +549,7 @@ } | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new QUICError('INVALID_ERROR_CODE'); | ||
} | ||
const code = bufv.readUInt32LE(bufv.v.start); | ||
const code = bufv.buf.readUInt32LE(bufv.start); | ||
return new QUICStreamError(code); | ||
@@ -555,2 +561,5 @@ } | ||
} | ||
if (err instanceof QUICError) { | ||
return err; | ||
} | ||
return QUICStreamError.fromError(err); | ||
@@ -579,4 +588,7 @@ } | ||
writeTo(bufv) { | ||
bufv.v.walk(4); | ||
bufv.writeUInt32LE(this.code, bufv.v.start); | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new QUICError('INVALID_ERROR_CODE'); | ||
} | ||
bufv.buf.writeUInt32LE(this.code, bufv.start); | ||
return bufv; | ||
@@ -583,0 +595,0 @@ } |
@@ -29,5 +29,6 @@ /// <reference types="node" /> | ||
offset: Offset; | ||
isFIN: boolean; | ||
data: Buffer | null; | ||
isFIN: boolean; | ||
constructor(streamID: StreamID, offset: Offset, data?: Buffer | null, isFIN?: boolean); | ||
constructor(streamID: StreamID, offset: Offset, isFIN?: boolean); | ||
setData(data: Buffer | null): this; | ||
valueOf(): { | ||
@@ -41,2 +42,3 @@ name: string; | ||
}; | ||
headerLen(hasDataLen: boolean): number; | ||
byteLen(): number; | ||
@@ -47,4 +49,4 @@ writeTo(bufv: BufferVisitor): BufferVisitor; | ||
export declare class AckRange { | ||
last: number; | ||
first: number; | ||
last: number; | ||
constructor(firstPacketNumberValue: number, lastPacketNumberValue: number); | ||
@@ -51,0 +53,0 @@ len(): number; |
@@ -41,4 +41,4 @@ 'use strict'; | ||
function parseFrame(bufv, headerPacketNumber) { | ||
bufv.v.walk(0); // align start and end | ||
const type = bufv.readUInt8(bufv.v.start, true); | ||
bufv.walk(0); // align start and end | ||
const type = bufv.buf.readUInt8(bufv.start); | ||
if (type >= 128) { | ||
@@ -144,4 +144,4 @@ return StreamFrame.fromBuffer(bufv); | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (!isStreamType(type)) { | ||
@@ -156,33 +156,43 @@ throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); | ||
// a Data Length is present in the STREAM header | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); | ||
} | ||
const len = bufv.readUInt16LE(bufv.v.start, true); | ||
const len = bufv.buf.readUInt16LE(bufv.start); | ||
if (len > 0) { | ||
bufv.v.walk(len); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); | ||
} | ||
data = bufv.slice(bufv.v.start, bufv.v.end); | ||
data = Buffer.allocUnsafe(len); // should copy to release socket buffer | ||
bufv.buf.copy(data, 0, bufv.start, bufv.end); | ||
} | ||
} | ||
else if (bufv.length > bufv.v.end) { | ||
else if (bufv.length > bufv.end) { | ||
// the STREAM frame extends to the end of the Packet. | ||
bufv.v.walk(bufv.length - bufv.v.end); | ||
data = bufv.slice(bufv.v.start, bufv.v.end); | ||
bufv.walk(bufv.length - bufv.end); | ||
data = Buffer.allocUnsafe(bufv.end - bufv.start); // should copy to release socket buffer | ||
bufv.buf.copy(data, 0, bufv.start, bufv.end); | ||
} | ||
const frame = new StreamFrame(streamID, offset, data, isFIN); | ||
const frame = new StreamFrame(streamID, offset, isFIN); | ||
frame.setData(data); | ||
frame.type = type; | ||
return frame; | ||
} | ||
constructor(streamID, offset, data = null, isFIN = false) { | ||
constructor(streamID, offset, isFIN = false) { | ||
super(0b10000000, 'STREAM'); | ||
this.streamID = streamID; | ||
this.offset = offset; | ||
this.isFIN = isFIN; | ||
this.data = null; | ||
} | ||
setData(data) { | ||
if (data != null && data.length === 0) { | ||
data = null; | ||
} | ||
this.streamID = streamID; | ||
this.offset = offset; | ||
if (data == null) { | ||
this.isFIN = true; | ||
} | ||
this.data = data; | ||
this.isFIN = isFIN || data == null; | ||
return this; | ||
} | ||
@@ -199,5 +209,9 @@ valueOf() { | ||
} | ||
headerLen(hasDataLen) { | ||
const len = hasDataLen ? 2 : 0; | ||
return 1 + this.streamID.byteLen() + this.offset.byteLen() + len; | ||
} | ||
byteLen() { | ||
const dataLen = this.data != null ? this.data.length : 0; | ||
return 1 + this.streamID.byteLen() + this.offset.byteLen() + (dataLen > 0 ? (dataLen + 2) : 0); | ||
return this.headerLen(dataLen > 0) + dataLen; | ||
} | ||
@@ -213,11 +227,11 @@ writeTo(bufv) { | ||
this.type |= this.streamID.flagBits(); | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.streamID.writeTo(bufv); | ||
this.offset.writeTo(bufv); | ||
if (this.data != null) { | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(this.data.length, bufv.v.start, true); | ||
bufv.v.walk(this.data.length); | ||
this.data.copy(bufv, bufv.v.start, 0, this.data.length); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(this.data.length, bufv.start); | ||
bufv.walk(this.data.length); | ||
this.data.copy(bufv.buf, bufv.start, 0, this.data.length); | ||
} | ||
@@ -231,4 +245,4 @@ return bufv; | ||
constructor(firstPacketNumberValue, lastPacketNumberValue) { | ||
this.last = lastPacketNumberValue; // last >= first | ||
this.first = firstPacketNumberValue; // PacketNumber value | ||
this.last = lastPacketNumberValue; // last >= first | ||
} | ||
@@ -319,4 +333,4 @@ len() { | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (!isACKType(type)) { | ||
@@ -330,14 +344,14 @@ throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
frame.largestAcked = largestAckedNumber.valueOf(); | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
frame.delayTime = common_1.readUFloat16(bufv, bufv.v.start); | ||
frame.delayTime = common_1.readUFloat16(bufv.buf, bufv.start); | ||
let numAckBlocks = 0; | ||
if (hasMissingRanges) { | ||
bufv.v.walk(1); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(1); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
numAckBlocks = bufv.readUInt8(bufv.v.start, true); | ||
numAckBlocks = bufv.buf.readUInt8(bufv.start); | ||
} | ||
@@ -347,7 +361,7 @@ if (hasMissingRanges && numAckBlocks === 0) { | ||
} | ||
bufv.v.walk(missingNumberDeltaLen); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(missingNumberDeltaLen); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
let ackBlockLength = bufv.readUIntLE(bufv.v.start, missingNumberDeltaLen, true); | ||
let ackBlockLength = bufv.buf.readUIntLE(bufv.start, missingNumberDeltaLen); | ||
if ((frame.largestAcked > 0 && ackBlockLength < 1) || ackBlockLength > frame.largestAcked) { | ||
@@ -362,12 +376,12 @@ throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
for (let i = 0; i < numAckBlocks; i++) { | ||
bufv.v.walk(1); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(1); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
const gap = bufv.readUInt8(bufv.v.start, true); | ||
bufv.v.walk(missingNumberDeltaLen); | ||
if (bufv.length < bufv.v.end) { | ||
const gap = bufv.buf.readUInt8(bufv.start); | ||
bufv.walk(missingNumberDeltaLen); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
ackBlockLength = bufv.readUIntLE(bufv.v.start, missingNumberDeltaLen, true); | ||
ackBlockLength = bufv.buf.readUIntLE(bufv.start, missingNumberDeltaLen); | ||
const lastAckRange = frame.ackRanges[frame.ackRanges.length - 1]; | ||
@@ -407,33 +421,33 @@ if (inLongBlock) { | ||
} | ||
bufv.v.walk(1); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(1); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
const numTimestamp = bufv.readUInt8(bufv.v.start, true); | ||
const numTimestamp = bufv.buf.readUInt8(bufv.start); | ||
if (numTimestamp > 0) { // TODO | ||
// Delta Largest acked | ||
bufv.v.walk(1); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(1); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
// buf.readUInt8(v.start, true) | ||
// buf.readUInt8(v.start) | ||
// First Timestamp | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
// buf.readUInt32LE(v.start, true) | ||
// buf.readUInt32LE(v.start) | ||
for (let i = 0; i < numTimestamp - 1; i++) { | ||
// Delta Largest acked | ||
bufv.v.walk(1); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(1); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
// buf.readUInt8(v.start, true) | ||
// buf.readUInt8(v.start) | ||
// Time Since Previous Timestamp | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_ACK_DATA'); | ||
} | ||
// buf.readUInt16LE(v.start, true) | ||
// buf.readUInt16LE(v.start) | ||
} | ||
@@ -591,4 +605,4 @@ } | ||
let numRanges = 0; | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
largestAckedNum.writeTo(bufv); | ||
@@ -598,4 +612,4 @@ if (this.delayTime === 0) { | ||
} | ||
bufv.v.walk(2); | ||
common_1.writeUFloat16(bufv, this.delayTime, bufv.v.start); | ||
bufv.walk(2); | ||
common_1.writeUFloat16(bufv.buf, this.delayTime, bufv.start); | ||
let numRangesWritten = 0; | ||
@@ -607,4 +621,4 @@ if (hasMissingRanges) { | ||
} | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(numRanges - 1, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(numRanges - 1, bufv.start); | ||
} | ||
@@ -625,4 +639,4 @@ let firstAckBlockLength = 0; | ||
} | ||
bufv.v.walk(missingNumberDeltaLen); | ||
bufv.writeUIntLE(firstAckBlockLength, bufv.v.start, missingNumberDeltaLen); | ||
bufv.walk(missingNumberDeltaLen); | ||
bufv.buf.writeUIntLE(firstAckBlockLength, bufv.start, missingNumberDeltaLen); | ||
for (let i = 1, l = this.ackRanges.length; i < l; i++) { | ||
@@ -636,6 +650,6 @@ const length = this.ackRanges[i].len(); | ||
if (num === 1) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(gap, bufv.v.start); | ||
bufv.v.walk(missingNumberDeltaLen); | ||
bufv.writeUIntLE(length, bufv.v.start, missingNumberDeltaLen); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(gap, bufv.start); | ||
bufv.walk(missingNumberDeltaLen); | ||
bufv.buf.writeUIntLE(length, bufv.start, missingNumberDeltaLen); | ||
numRangesWritten++; | ||
@@ -655,6 +669,6 @@ } | ||
} | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(gapWritten, bufv.v.start); | ||
bufv.v.walk(missingNumberDeltaLen); | ||
bufv.writeUIntLE(lengthWritten, bufv.v.start, missingNumberDeltaLen); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(gapWritten, bufv.start); | ||
bufv.walk(missingNumberDeltaLen); | ||
bufv.buf.writeUIntLE(lengthWritten, bufv.start, missingNumberDeltaLen); | ||
numRangesWritten++; | ||
@@ -671,4 +685,4 @@ } | ||
} | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(0, bufv.v.start); // no timestamps | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(0, bufv.start); // no timestamps | ||
return bufv; | ||
@@ -700,4 +714,4 @@ } | ||
static fromBuffer(bufv, packetNumber) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x06) { | ||
@@ -707,7 +721,7 @@ throw new error_1.QuicError('QUIC_INVALID_STOP_WAITING_DATA'); | ||
const len = packetNumber.byteLen(); | ||
bufv.v.walk(len); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_STOP_WAITING_DATA'); | ||
} | ||
const delta = bufv.readIntLE(bufv.v.start, len, false); | ||
const delta = bufv.buf.readIntLE(bufv.start, len, false); | ||
return new StopWaitingFrame(packetNumber, packetNumber.valueOf() - delta); | ||
@@ -733,6 +747,6 @@ } | ||
const len = this.packetNumber.byteLen(); | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.v.walk(len); | ||
bufv.writeUIntLE(this.packetNumber.valueOf() - this.leastUnacked, bufv.v.start, len); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
bufv.walk(len); | ||
bufv.buf.writeUIntLE(this.packetNumber.valueOf() - this.leastUnacked, bufv.start, len); | ||
return bufv; | ||
@@ -762,4 +776,4 @@ } | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x04) { | ||
@@ -789,4 +803,4 @@ throw new error_1.QuicError('QUIC_INVALID_WINDOW_UPDATE_DATA'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.streamID.writeTo(bufv, true); | ||
@@ -817,4 +831,4 @@ this.offset.writeTo(bufv, true); | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x05) { | ||
@@ -841,4 +855,4 @@ throw new error_1.QuicError('QUIC_INVALID_BLOCKED_DATA'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.streamID.writeTo(bufv, true); | ||
@@ -857,4 +871,4 @@ return bufv; | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (!isCongestionType(type)) { | ||
@@ -872,4 +886,4 @@ throw new error_1.QuicError('QUIC_INVALID_FRAME_DATA'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
return bufv; | ||
@@ -887,4 +901,4 @@ } | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type > 0) { | ||
@@ -902,4 +916,4 @@ throw new error_1.QuicError('QUIC_INVALID_FRAME_DATA'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(0, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(0, bufv.start); | ||
return bufv; | ||
@@ -928,5 +942,5 @@ } | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
if (type !== 0x01 || bufv.length < (bufv.v.end + 16)) { | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x01 || bufv.length < (bufv.end + 16)) { | ||
throw new error_1.QuicError('QUIC_INVALID_RST_STREAM_DATA'); | ||
@@ -958,4 +972,4 @@ } | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.streamID.writeTo(bufv, true); | ||
@@ -979,4 +993,4 @@ this.offset.writeTo(bufv, true); | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x07) { | ||
@@ -994,4 +1008,4 @@ throw new error_1.QuicError('QUIC_INVALID_FRAME_DATA'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
return bufv; | ||
@@ -1021,16 +1035,16 @@ } | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
if (type !== 0x02 || bufv.length < (bufv.v.end + 6)) { | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x02 || bufv.length < (bufv.end + 6)) { | ||
throw new error_1.QuicError('QUIC_INVALID_CONNECTION_CLOSE_DATA'); | ||
} | ||
const error = error_1.QuicError.fromBuffer(bufv); | ||
bufv.v.walk(2); | ||
const reasonPhraseLen = bufv.readUInt16LE(bufv.v.start, true); | ||
bufv.walk(2); | ||
const reasonPhraseLen = bufv.buf.readUInt16LE(bufv.start); | ||
if (reasonPhraseLen > 0) { | ||
bufv.v.walk(reasonPhraseLen); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(reasonPhraseLen); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_CONNECTION_CLOSE_DATA'); | ||
} | ||
error.message = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
error.message = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
} | ||
@@ -1058,10 +1072,10 @@ return new ConnectionCloseFrame(error); | ||
const reasonPhraseLen = reasonPhrase !== '' ? Buffer.byteLength(reasonPhrase) : 0; | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.error.writeTo(bufv); | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(reasonPhraseLen, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(reasonPhraseLen, bufv.start); | ||
if (reasonPhrase !== '') { | ||
bufv.v.walk(reasonPhraseLen); | ||
bufv.write(reasonPhrase, bufv.v.start, reasonPhraseLen); | ||
bufv.walk(reasonPhraseLen); | ||
bufv.buf.write(reasonPhrase, bufv.start, reasonPhraseLen); | ||
} | ||
@@ -1099,4 +1113,4 @@ return bufv; | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); | ||
const type = bufv[bufv.v.start]; | ||
bufv.walk(1); | ||
const type = bufv.buf[bufv.start]; | ||
if (type !== 0x03) { | ||
@@ -1107,13 +1121,13 @@ throw new error_1.QuicError('QUIC_INVALID_GOAWAY_DATA'); | ||
const streamID = protocol_1.StreamID.fromBuffer(bufv, 4); | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_GOAWAY_DATA'); | ||
} | ||
const reasonPhraseLen = bufv.readUInt16LE(bufv.v.start, true); | ||
const reasonPhraseLen = bufv.buf.readUInt16LE(bufv.start); | ||
if (reasonPhraseLen > 0) { | ||
bufv.v.walk(reasonPhraseLen); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(reasonPhraseLen); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_GOAWAY_DATA'); | ||
} | ||
error.message = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
error.message = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
} | ||
@@ -1143,11 +1157,11 @@ return new GoAwayFrame(streamID, error); | ||
const reasonPhraseLen = reasonPhrase !== '' ? Buffer.byteLength(reasonPhrase) : 0; | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.type, bufv.v.start); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.type, bufv.start); | ||
this.error.writeTo(bufv); | ||
this.streamID.writeTo(bufv, true); | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(reasonPhraseLen, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(reasonPhraseLen, bufv.start); | ||
if (reasonPhrase !== '') { | ||
bufv.v.walk(reasonPhraseLen); | ||
bufv.write(reasonPhrase, bufv.v.start, reasonPhraseLen); | ||
bufv.walk(reasonPhraseLen); | ||
bufv.buf.write(reasonPhrase, bufv.start, reasonPhraseLen); | ||
} | ||
@@ -1154,0 +1168,0 @@ return bufv; |
@@ -5,3 +5,3 @@ /// <reference types="node" /> | ||
import { ConnectionID, PacketNumber, SocketAddress, QuicTag, SessionType } from './protocol'; | ||
export declare function parsePacket(bufv: BufferVisitor, packetSentBy: SessionType, _version?: string): Packet; | ||
export declare function parsePacket(bufv: BufferVisitor, packetSentBy: SessionType): Packet; | ||
/** Packet representing a base Packet. */ | ||
@@ -12,2 +12,3 @@ export declare abstract class Packet { | ||
connectionID: ConnectionID; | ||
sentTime: number; | ||
constructor(connectionID: ConnectionID, flag: number); | ||
@@ -82,9 +83,8 @@ isReset(): boolean; | ||
setVersion(version: string): void; | ||
/** | ||
* @param {Array<Frame>} frames | ||
* @return {this} | ||
*/ | ||
setPacketNumber(packetNumber: PacketNumber): void; | ||
needAck(): boolean; | ||
addFrames(...frames: Frame[]): this; | ||
headerLen(): number; | ||
byteLen(): number; | ||
writeTo(bufv: BufferVisitor): BufferVisitor; | ||
} |
@@ -73,5 +73,5 @@ 'use strict'; | ||
// 0x80 is currently unused, and must be set to 0. | ||
function parsePacket(bufv, packetSentBy, _version) { | ||
bufv.v.walk(0); // align start and end | ||
const flag = bufv.readUIntLE(bufv.v.start, 1, true); | ||
function parsePacket(bufv, packetSentBy) { | ||
bufv.walk(0); // align start and end | ||
const flag = bufv.buf.readUIntLE(bufv.start, 1); | ||
// 0x80, currently unused | ||
@@ -103,2 +103,3 @@ if (flag >= 127) { | ||
this.connectionID = connectionID; | ||
this.sentTime = 0; // timestamp, ms | ||
} | ||
@@ -143,3 +144,3 @@ isReset() { | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); // flag | ||
bufv.walk(1); // flag | ||
const connectionID = protocol_1.ConnectionID.fromBuffer(bufv); | ||
@@ -164,7 +165,7 @@ const quicTag = protocol_1.QuicTag.fromBuffer(bufv); | ||
if (rseq != null) { | ||
this.packetNumber = protocol_1.PacketNumber.fromBuffer(common_1.Visitor.wrap(rseq), rseq.length); | ||
this.packetNumber = protocol_1.PacketNumber.fromBuffer(new common_1.BufferVisitor(rseq), rseq.length); | ||
} | ||
const cadr = tags.getTag('CADR'); | ||
if (cadr != null) { | ||
this.socketAddress = protocol_1.SocketAddress.fromBuffer(common_1.Visitor.wrap(cadr)); | ||
this.socketAddress = protocol_1.SocketAddress.fromBuffer(new common_1.BufferVisitor(cadr)); | ||
} | ||
@@ -185,4 +186,4 @@ } | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.flag, bufv.v.start, true); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.flag, bufv.start); | ||
this.connectionID.writeTo(bufv); | ||
@@ -212,8 +213,8 @@ this.tags.writeTo(bufv); | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(1); // flag | ||
bufv.walk(1); // flag | ||
const connectionID = protocol_1.ConnectionID.fromBuffer(bufv); | ||
const versions = []; | ||
while (bufv.length > bufv.v.end) { | ||
bufv.v.walk(4); | ||
const version = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
while (bufv.length > bufv.end) { | ||
bufv.walk(4); | ||
const version = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
if (!protocol_1.isSupportedVersion(version)) { | ||
@@ -241,8 +242,8 @@ throw new error_1.QuicError('QUIC_INVALID_VERSION'); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.flag, bufv.v.start, true); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.flag, bufv.start); | ||
this.connectionID.writeTo(bufv); | ||
for (const version of this.versions) { | ||
bufv.v.walk(4); | ||
bufv.write(version, bufv.v.start, 4); | ||
bufv.walk(4); | ||
bufv.buf.write(version, bufv.start, 4); | ||
} | ||
@@ -260,3 +261,3 @@ return bufv; | ||
static fromBuffer(bufv, flag) { | ||
bufv.v.walk(1); // flag | ||
bufv.walk(1); // flag | ||
const connectionID = protocol_1.ConnectionID.fromBuffer(bufv); | ||
@@ -266,4 +267,4 @@ let version = ''; | ||
if (hasVersion) { | ||
bufv.v.walk(4); | ||
version = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
bufv.walk(4); | ||
version = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
if (!protocol_1.isSupportedVersion(version)) { | ||
@@ -275,4 +276,4 @@ throw new error_1.QuicError('QUIC_INVALID_VERSION'); | ||
if ((flag & 0b100) > 0) { | ||
bufv.v.walk(32); | ||
nonce = bufv.slice(bufv.v.start, bufv.v.end); | ||
bufv.walk(32); | ||
nonce = bufv.buf.slice(bufv.start, bufv.end); | ||
if (nonce.length !== 32) { | ||
@@ -287,3 +288,3 @@ throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
while (bufv.v.end < bufv.length) { | ||
while (bufv.end < bufv.length) { | ||
packet.addFrames(frame_1.parseFrame(bufv, packetNumber)); | ||
@@ -320,6 +321,15 @@ } | ||
} | ||
/** | ||
* @param {Array<Frame>} frames | ||
* @return {this} | ||
*/ | ||
setPacketNumber(packetNumber) { | ||
this.packetNumber = packetNumber; | ||
this.flag &= 0b11001111; | ||
this.flag |= (packetNumber.flagBits() << 4); | ||
} | ||
needAck() { | ||
for (const frame of this.frames) { | ||
if (frame.isRetransmittable()) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
addFrames(...frames) { | ||
@@ -329,3 +339,3 @@ this.frames.push(...frames); | ||
} | ||
byteLen() { | ||
headerLen() { | ||
let len = 9; | ||
@@ -339,2 +349,6 @@ if (this.version !== '') { | ||
len += this.packetNumber.byteLen(); | ||
return len; | ||
} | ||
byteLen() { | ||
let len = this.headerLen(); | ||
for (const frame of this.frames) { | ||
@@ -346,12 +360,12 @@ len += frame.byteLen(); | ||
writeTo(bufv) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(this.flag, bufv.v.start, true); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(this.flag, bufv.start); | ||
this.connectionID.writeTo(bufv); | ||
if (this.version !== '') { | ||
bufv.v.walk(4); | ||
bufv.write(this.version, bufv.v.start, 4); | ||
bufv.walk(4); | ||
bufv.buf.write(this.version, bufv.start, 4); | ||
} | ||
if (this.nonce != null) { | ||
bufv.v.walk(32); | ||
this.nonce.copy(bufv, bufv.v.start, 0, 32); | ||
bufv.walk(32); | ||
this.nonce.copy(bufv.buf, bufv.start, 0, 32); | ||
} | ||
@@ -358,0 +372,0 @@ this.packetNumber.writeTo(bufv); |
@@ -13,11 +13,2 @@ /// <reference types="node" /> | ||
} | ||
export declare const DefaultIdleTimeout: number; | ||
export declare const MaxIdleTimeout: number; | ||
export declare const MaxIdleTimeoutServer: number; | ||
export declare const MaxStreamWaitingTimeout: number; | ||
export declare const PingFrameDelay: number; | ||
export declare const MaxOffset: number; | ||
export declare const MaxReceivePacketSize = 1350; | ||
export declare const MaxStreamBufferSize = 1280; | ||
export declare const MaxStreamReadCacheSize: number; | ||
/** | ||
@@ -116,3 +107,2 @@ * Returns supported version. | ||
toString(): string; | ||
nextOffset(byteLen: number): Offset; | ||
} | ||
@@ -119,0 +109,0 @@ /** SocketAddress representing a socket address. */ |
@@ -22,63 +22,2 @@ 'use strict'; | ||
})(FamilyType = exports.FamilyType || (exports.FamilyType = {})); | ||
// MaxPacketSize is the maximum packet size, including the public header, that we use for sending packets | ||
// This is the value used by Chromium for a QUIC packet sent using IPv6 (for IPv4 it would be 1370) | ||
// export const MaxPacketSize = 1350 | ||
// MaxFrameAndPublicHeaderSize is the maximum size of a QUIC frame plus PublicHeader | ||
// const MaxFrameAndPublicHeaderSize = exports.MaxFrameAndPublicHeaderSize = MaxPacketSize - 12 /*crypto signature*/ | ||
// DefaultMaxCongestionWindow is the default for the max congestion window | ||
// const DefaultMaxCongestionWindow = exports.DefaultMaxCongestionWindow = 1000 | ||
// InitialCongestionWindow is the initial congestion window in QUIC packets | ||
// const InitialCongestionWindow = exports.InitialCongestionWindow = 32 | ||
// MaxUndecryptablePackets limits the number of undecryptable packets that a | ||
// session queues for later until it sends a public reset. | ||
// const MaxUndecryptablePackets = exports.MaxUndecryptablePackets = 10 | ||
// PublicResetTimeout is the time to wait before sending a Public Reset when receiving | ||
// too many undecryptable packets during the handshake | ||
// const PublicResetTimeout = exports.PublicResetTimeout = 500 // ms | ||
// AckSendDelay is the maximum delay that can be applied to an ACK for a retransmittable packet | ||
// This is the value Chromium is using | ||
// const AckSendDelay = exports.AckSendDelay = 25 // ms | ||
// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection | ||
// const MaxStreamsPerConnection = exports.MaxStreamsPerConnection = 100 | ||
// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames | ||
// prevents DoS attacks against the streamFrameSorter | ||
// const MaxStreamFrameSorterGaps = exports.MaxStreamFrameSorterGaps = 1000 | ||
// CryptoMaxParams is the upper limit for the number of parameters in a crypto message. | ||
// Value taken from Chrome. | ||
// const CryptoMaxParams = exports.CryptoMaxParams = 128 | ||
// CryptoParameterMaxLength is the upper limit for the length of a parameter in a crypto message. | ||
// const CryptoParameterMaxLength = exports.CryptoParameterMaxLength = 4000 | ||
// InitialIdleTimeout is the timeout before the handshake succeeds. | ||
// const InitialIdleTimeout = exports.InitialIdleTimeout = 5 * 1000 // ms | ||
// DefaultIdleTimeout is the default idle timeout, for the server | ||
exports.DefaultIdleTimeout = 30 * 1000; | ||
// MaxIdleTimeout is the max idle timeout | ||
exports.MaxIdleTimeout = 10 * 60 * 1000; | ||
// MaxIdleTimeoutServer is the maximum idle timeout that can be negotiated, for the server | ||
exports.MaxIdleTimeoutServer = 1 * 60 * 1000; | ||
exports.MaxStreamWaitingTimeout = 30 * 1000; | ||
// The PING frame should be used to keep a connection alive when a stream is open. | ||
// The default is to do this after 15 seconds of quiescence, which is much shorter than most NATs time out. | ||
exports.PingFrameDelay = 15 * 1000; | ||
// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds. | ||
// const DefaultHandshakeTimeout = exports.DefaultHandshakeTimeout = 10 * 1000 | ||
// ClosedSessionDeleteTimeout the server ignores packets arriving on a connection that is already closed | ||
// after this time all information about the old connection will be deleted | ||
// const ClosedSessionDeleteTimeout = exports.ClosedSessionDeleteTimeout = 60 * 1000 | ||
// NumCachedCertificates is the number of cached compressed certificate chains, each taking ~1K space | ||
// const NumCachedCertificates = exports.NumCachedCertificates = 128 | ||
// MaxOffset is the maximum value of a ByteCount | ||
exports.MaxOffset = Number.MAX_SAFE_INTEGER; | ||
// MaxReceivePacketSize maximum packet size of any QUIC packet, based on | ||
// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header, | ||
// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes. | ||
// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452. | ||
// the current QUIC implementation uses a 1350-byte maximum QUIC packet size for IPv6, | ||
// 1370 for IPv4. Both sizes are without IP and UDP overhead. | ||
exports.MaxReceivePacketSize = 1350; | ||
exports.MaxStreamBufferSize = 1280; // todo | ||
exports.MaxStreamReadCacheSize = 1024 * 256; // todo | ||
// DefaultTCPMSS is the default maximum packet size used in the Linux TCP implementation. | ||
// Used in QUIC for congestion window computations in bytes. | ||
// const DefaultTCPMSS = exports.DefaultTCPMSS = 1460 | ||
/** | ||
@@ -134,7 +73,7 @@ * Returns supported version. | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(8); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(8); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
return new ConnectionID(bufv.toString('hex', bufv.v.start, bufv.v.end)); | ||
return new ConnectionID(bufv.buf.toString('hex', bufv.start, bufv.end)); | ||
} | ||
@@ -163,4 +102,4 @@ static random() { | ||
writeTo(bufv) { | ||
bufv.v.walk(8); | ||
bufv.write(this[symbol_1.kVal], bufv.v.start, 8, 'hex'); | ||
bufv.walk(8); | ||
bufv.buf.write(this[symbol_1.kVal], bufv.start, 8, 'hex'); | ||
return bufv; | ||
@@ -188,7 +127,7 @@ } | ||
static fromBuffer(bufv, len) { | ||
bufv.v.walk(len); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
return new PacketNumber(bufv.readUIntLE(bufv.v.start, len, true)); | ||
return new PacketNumber(bufv.buf.readUIntLE(bufv.start, len)); | ||
} | ||
@@ -249,4 +188,4 @@ constructor(val) { | ||
const len = isFull ? 6 : this.byteLen(); | ||
bufv.v.walk(len); | ||
bufv.writeUIntLE(this[symbol_1.kVal], bufv.v.start, len, true); | ||
bufv.walk(len); | ||
bufv.buf.writeUIntLE(this[symbol_1.kVal], bufv.start, len); | ||
return bufv; | ||
@@ -274,7 +213,7 @@ } | ||
static fromBuffer(bufv, len) { | ||
bufv.v.walk(len); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); | ||
} | ||
return new StreamID(bufv.readUIntLE(bufv.v.start, len, true)); | ||
return new StreamID(bufv.buf.readUIntLE(bufv.start, len)); | ||
} | ||
@@ -321,4 +260,4 @@ constructor(id) { | ||
const len = isFull ? 4 : this.byteLen(); | ||
bufv.v.walk(len); | ||
bufv.writeUIntLE(this[symbol_1.kVal], bufv.v.start, len, true); | ||
bufv.walk(len); | ||
bufv.buf.writeUIntLE(this[symbol_1.kVal], bufv.start, len); | ||
return bufv; | ||
@@ -343,10 +282,10 @@ } | ||
static fromBuffer(bufv, len) { | ||
bufv.v.walk(len); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
return new Offset(len > 0 ? bufv.readUIntLE(bufv.v.start, len, true) : 0); | ||
return new Offset(common_1.readUnsafeUIntLE(bufv.buf, bufv.start, len)); | ||
} | ||
constructor(offset) { | ||
if (!Number.isInteger(offset) || offset < 0 || offset > exports.MaxOffset) { | ||
if (!Number.isSafeInteger(offset) || offset < 0) { | ||
throw new Error(`invalid Offset ${offset}`); | ||
@@ -386,3 +325,3 @@ } | ||
} | ||
return 7; | ||
return 7; // value should small than 0xffffffffffffff | ||
} | ||
@@ -403,6 +342,7 @@ return 8; | ||
const len = isFull ? 8 : this.byteLen(); | ||
if (len > 0) { | ||
bufv.v.walk(len); | ||
bufv.writeUIntLE(this[symbol_1.kVal], bufv.v.start, len, true); | ||
bufv.walk(len); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
common_1.writeUnsafeUIntLE(bufv.buf, this[symbol_1.kVal], bufv.start, len); | ||
return bufv; | ||
@@ -413,6 +353,2 @@ } | ||
} | ||
nextOffset(byteLen) { | ||
const value = this[symbol_1.kVal] + byteLen; | ||
return new Offset(value); | ||
} | ||
} | ||
@@ -428,46 +364,46 @@ exports.Offset = Offset; | ||
}; | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
const family = bufv.readUInt16LE(bufv.v.start, true); | ||
const family = bufv.buf.readUInt16LE(bufv.start); | ||
if (family === 0x02) { | ||
obj.family = FamilyType.IPv4; | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
obj.address = [ | ||
bufv.readUInt8(bufv.v.start, true), | ||
bufv.readUInt8(bufv.v.start + 1, true), | ||
bufv.readUInt8(bufv.v.start + 2, true), | ||
bufv.readUInt8(bufv.v.start + 3, true), | ||
bufv.buf.readUInt8(bufv.start), | ||
bufv.buf.readUInt8(bufv.start + 1), | ||
bufv.buf.readUInt8(bufv.start + 2), | ||
bufv.buf.readUInt8(bufv.start + 3), | ||
].join('.'); | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
obj.port = bufv.readUInt16LE(bufv.v.start, true); | ||
obj.port = bufv.buf.readUInt16LE(bufv.start); | ||
} | ||
else if (family === 0x0a) { | ||
obj.family = FamilyType.IPv6; | ||
bufv.v.walk(16); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(16); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
obj.address = [ | ||
bufv.readUInt16BE(bufv.v.start, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 2, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 4, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 6, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 8, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 10, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 12, true).toString(16), | ||
bufv.readUInt16BE(bufv.v.start + 14, true).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 2).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 4).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 6).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 8).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 10).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 12).toString(16), | ||
bufv.buf.readUInt16BE(bufv.start + 14).toString(16), | ||
].join(':'); | ||
bufv.v.walk(2); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(2); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
obj.port = bufv.readUInt16LE(bufv.v.start, true); | ||
obj.port = bufv.buf.readUInt16LE(bufv.start); | ||
} | ||
@@ -520,20 +456,20 @@ else { | ||
if (this.family === FamilyType.IPv4) { | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(0x02, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(0x02, bufv.start); | ||
for (const val of address.split('.')) { | ||
bufv.v.walk(1); | ||
bufv.writeUInt8(parseInt(val, 10), bufv.v.start, true); | ||
bufv.walk(1); | ||
bufv.buf.writeUInt8(parseInt(val, 10), bufv.start); | ||
} | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(this.port, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(this.port, bufv.start); | ||
} | ||
else { | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(0x0a, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(0x0a, bufv.start); | ||
for (const val of address.split(':')) { | ||
bufv.v.walk(2); | ||
bufv.writeUInt16BE(parseInt(val, 16), bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16BE(parseInt(val, 16), bufv.start); | ||
} | ||
bufv.v.walk(2); | ||
bufv.writeUInt16LE(this.port, bufv.v.start, true); | ||
bufv.walk(2); | ||
bufv.buf.writeUInt16LE(this.port, bufv.start); | ||
} | ||
@@ -550,31 +486,31 @@ return bufv; | ||
static fromBuffer(bufv) { | ||
bufv.v.walk(4); | ||
const name = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
bufv.walk(4); | ||
const name = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
const quicTag = new QuicTag(name); | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
let count = bufv.readInt32LE(bufv.v.start); | ||
const baseOffset = bufv.v.end + 8 * count; | ||
let count = bufv.buf.readInt32LE(bufv.start); | ||
const baseOffset = bufv.end + 8 * count; | ||
const v2 = new common_1.Visitor(baseOffset); | ||
while (count-- > 0) { | ||
bufv.v.walk(4); | ||
if (bufv.length < bufv.v.end) { | ||
bufv.walk(4); | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
const key = bufv.toString('utf8', bufv.v.start, bufv.v.end); | ||
bufv.v.walk(4); | ||
const key = bufv.buf.toString('utf8', bufv.start, bufv.end); | ||
bufv.walk(4); | ||
v2.walk(0); | ||
if (bufv.length < bufv.v.end) { | ||
if (bufv.isOutside()) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
v2.end = baseOffset + bufv.readInt32LE(bufv.v.start); | ||
v2.end = baseOffset + bufv.buf.readInt32LE(bufv.start); | ||
if (bufv.length < v2.end) { | ||
throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); | ||
} | ||
const val = bufv.slice(v2.start, v2.end); | ||
const val = bufv.buf.slice(v2.start, v2.end); | ||
quicTag.setTag(key, val); | ||
} | ||
bufv.v.reset(v2.end, v2.end); | ||
bufv.reset(v2.end, v2.end); | ||
return quicTag; | ||
@@ -631,9 +567,9 @@ } | ||
writeTo(bufv) { | ||
bufv.v.walk(4); | ||
bufv.write(this.name, bufv.v.start, 4); | ||
bufv.v.walk(4); | ||
bufv.walk(4); | ||
bufv.buf.write(this.name, bufv.start, 4); | ||
bufv.walk(4); | ||
const size = this.tags.size; | ||
bufv.writeUInt32LE(size, bufv.v.start, true); | ||
bufv.buf.writeUInt32LE(size, bufv.start); | ||
let baseOffset = 0; | ||
const v = new common_1.Visitor(bufv.v.end + 8 * size); | ||
const v = new common_1.Visitor(bufv.end + 8 * size); | ||
for (const key of this.keys) { | ||
@@ -644,11 +580,11 @@ const val = this.tags.get(key); | ||
} | ||
bufv.v.walk(4); | ||
bufv.write(key, bufv.v.start, 4); | ||
bufv.v.walk(4); | ||
bufv.walk(4); | ||
bufv.buf.write(key, bufv.start, 4); | ||
bufv.walk(4); | ||
baseOffset += val.length; | ||
bufv.writeUInt32LE(baseOffset, bufv.v.start, true); | ||
bufv.buf.writeUInt32LE(baseOffset, bufv.start); | ||
v.walk(val.length); | ||
val.copy(bufv, v.start, 0, val.length); | ||
val.copy(bufv.buf, v.start, 0, val.length); | ||
} | ||
bufv.v.reset(v.end, v.end); | ||
bufv.reset(v.end, v.end); | ||
return bufv; | ||
@@ -655,0 +591,0 @@ } |
@@ -11,2 +11,3 @@ export declare const kID: unique symbol; | ||
export declare const kServer: unique symbol; | ||
export declare const kConns: unique symbol; | ||
export declare const kClientState: unique symbol; | ||
@@ -17,2 +18,5 @@ export declare const kNextStreamID: unique symbol; | ||
export declare const kIntervalCheck: unique symbol; | ||
export declare const kDelayAckTimer: unique symbol; | ||
export declare const kUnackedPackets: unique symbol; | ||
export declare const kFC: unique symbol; | ||
export declare const kRTT: unique symbol; |
@@ -16,2 +16,3 @@ 'use strict'; | ||
exports.kServer = Symbol('server'); | ||
exports.kConns = Symbol('conns'); | ||
exports.kClientState = Symbol('clientState'); | ||
@@ -22,3 +23,6 @@ exports.kNextStreamID = Symbol('nextStreamID'); | ||
exports.kIntervalCheck = Symbol('intervalCheck'); | ||
exports.kDelayAckTimer = Symbol('delayAckTimer'); | ||
exports.kUnackedPackets = Symbol('unackedPackets'); | ||
exports.kFC = Symbol('flowController'); | ||
exports.kRTT = Symbol('RTTStats'); | ||
//# sourceMappingURL=symbol.js.map |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { kSocket, kState, kServer, kIntervalCheck } from './internal/symbol'; | ||
import { kConns, kSocket, kState, kServer, kIntervalCheck } from './internal/symbol'; | ||
import { ConnectionID } from './internal/protocol'; | ||
import { Socket, AddressInfo } from './socket'; | ||
import { Session } from './session'; | ||
export declare const kConns: unique symbol; | ||
export declare class ServerSession extends Session { | ||
@@ -9,0 +8,0 @@ [kServer]: Server; |
@@ -8,2 +8,3 @@ 'use strict'; | ||
const events_1 = require("events"); | ||
const constant_1 = require("./internal/constant"); | ||
const common_1 = require("./internal/common"); | ||
@@ -17,3 +18,2 @@ const error_1 = require("./internal/error"); | ||
const debug = util_1.debuglog('quic'); | ||
exports.kConns = Symbol('conns'); | ||
class ServerSession extends session_1.Session { | ||
@@ -28,2 +28,4 @@ constructor(id, socket, server) { | ||
this[symbol_1.kState].localAddr = new protocol_1.SocketAddress(server.address()); | ||
this[symbol_1.kState].maxPacketSize = | ||
server.localFamily === protocol_1.FamilyType.IPv6 ? constant_1.MaxPacketSizeIPv6 : constant_1.MaxPacketSizeIPv4; | ||
} | ||
@@ -52,3 +54,3 @@ get server() { | ||
this.listening = false; | ||
this[exports.kConns] = new Map(); | ||
this[symbol_1.kConns] = new Map(); | ||
this[symbol_1.kState] = new ServerState(); | ||
@@ -93,3 +95,3 @@ this[symbol_1.kIntervalCheck] = setInterval(() => { | ||
_intervalCheck(time) { | ||
for (const session of this[exports.kConns].values()) { | ||
for (const session of this[symbol_1.kConns].values()) { | ||
// server session idle timeout | ||
@@ -103,3 +105,3 @@ if (time - session[symbol_1.kState].lastNetworkActivityTime > session[symbol_1.kState].idleTimeout) { | ||
} | ||
this[exports.kConns].delete(session.id); | ||
this[symbol_1.kConns].delete(session.id); | ||
return; | ||
@@ -120,3 +122,3 @@ } | ||
this[symbol_1.kState].destroyed = true; | ||
for (const session of this[exports.kConns].values()) { | ||
for (const session of this[symbol_1.kConns].values()) { | ||
await session.close(err); | ||
@@ -128,12 +130,25 @@ } | ||
} | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket != null && !socket[symbol_1.kState].destroyed) { | ||
socket.close(); | ||
socket[symbol_1.kState].destroyed = true; | ||
} | ||
process.nextTick(() => this.emit('close')); | ||
} | ||
getConnections() { | ||
return Promise.resolve(this[exports.kConns].size); | ||
return Promise.resolve(this[symbol_1.kConns].size); // TODO | ||
} | ||
ref() { | ||
return; | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket == null) { | ||
throw new Error('Server not listen'); | ||
} | ||
socket.ref(); | ||
} | ||
unref() { | ||
return; | ||
const socket = this[symbol_1.kSocket]; | ||
if (socket == null) { | ||
throw new Error('Server not listen'); | ||
} | ||
socket.unref(); | ||
} | ||
@@ -143,7 +158,11 @@ } | ||
function serverOnClose(server) { | ||
for (const session of server[exports.kConns].values()) { | ||
for (const session of server[symbol_1.kConns].values()) { | ||
session.destroy(new Error('the underlying socket closed')); | ||
} | ||
server[exports.kConns].clear(); | ||
// server[kConns].clear() | ||
if (!server[symbol_1.kState].destroyed) { | ||
const timer = server[symbol_1.kIntervalCheck]; | ||
if (timer != null) { | ||
clearInterval(timer); | ||
} | ||
server[symbol_1.kState].destroyed = true; | ||
@@ -154,3 +173,3 @@ server.emit('close'); | ||
function serverOnMessage(server, socket, msg, rinfo) { | ||
if (msg.length === 0) { | ||
if (msg.length === 0 || server[symbol_1.kState].destroyed) { | ||
return; | ||
@@ -160,3 +179,3 @@ } | ||
// If it does, we only read a truncated packet, which will then end up undecryptable | ||
if (msg.length > protocol_1.MaxReceivePacketSize) { | ||
if (msg.length > constant_1.MaxReceivePacketSize) { | ||
debug(`server message - receive too large data: $d bytes`, msg.length); | ||
@@ -167,6 +186,6 @@ // msg = msg.slice(0, MaxReceivePacketSize) | ||
const rcvTime = Date.now(); | ||
const bufv = common_1.Visitor.wrap(msg); | ||
const bufv = new common_1.BufferVisitor(msg); | ||
let packet = null; | ||
try { | ||
packet = packet_1.parsePacket(bufv, protocol_1.SessionType.CLIENT, ''); | ||
packet = packet_1.parsePacket(bufv, protocol_1.SessionType.CLIENT); | ||
} | ||
@@ -183,3 +202,3 @@ catch (err) { | ||
const connectionID = packet.connectionID.valueOf(); | ||
let session = server[exports.kConns].get(connectionID); | ||
let session = server[symbol_1.kConns].get(connectionID); | ||
const newSession = session == null; | ||
@@ -191,3 +210,3 @@ if (session == null) { | ||
session = new ServerSession(packet.connectionID, socket, server); | ||
server[exports.kConns].set(connectionID, session); | ||
server[symbol_1.kConns].set(connectionID, session); | ||
debug(`server message - new session: %s`, connectionID); | ||
@@ -194,0 +213,0 @@ } |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { Offset, SessionType, StreamID, PacketNumber, ConnectionID, SocketAddress } from './internal/protocol'; | ||
import { kID, kStreams, kSocket, kState, kType, kVersion, kACKHandler, kNextStreamID, kNextPacketNumber, kIntervalCheck, kUnackedPackets } from './internal/symbol'; | ||
import { Frame, StreamFrame, RstStreamFrame, AckFrame, WindowUpdateFrame, StopWaitingFrame } from './internal/frame'; | ||
import { kID, kFC, kRTT, kStreams, kSocket, kState, kType, kVersion, kACKHandler, kNextStreamID, kNextPacketNumber, kIntervalCheck, kUnackedPackets } from './internal/symbol'; | ||
import { Frame, StreamFrame, RstStreamFrame, AckFrame, WindowUpdateFrame, StopWaitingFrame, BlockedFrame } from './internal/frame'; | ||
import { Packet, RegularPacket } from './internal/packet'; | ||
import { ConnectionFlowController } from './internal/flowcontrol'; | ||
import { RTTStats } from './internal/congestion'; | ||
import { Socket } from './socket'; | ||
@@ -22,2 +24,4 @@ import { Stream } from './stream'; | ||
protected [kUnackedPackets]: Queue<RegularPacket>; | ||
protected [kRTT]: RTTStats; | ||
protected [kFC]: ConnectionFlowController; | ||
constructor(id: ConnectionID, type: SessionType); | ||
@@ -40,6 +44,8 @@ readonly id: string; | ||
}; | ||
_sendFrame(frame: Frame, callback: (...args: any[]) => void): void; | ||
_newRegularPacket(): RegularPacket; | ||
_sendFrame(frame: Frame, callback?: (...args: any[]) => void): void; | ||
_sendStopWaitingFrame(leastUnacked: number): void; | ||
_retransmit(frame: AckFrame): number; | ||
_sendPacket(packet: Packet, callback: (...args: any[]) => void): void; | ||
_retransmit(frame: AckFrame, rcvTime: number): number; | ||
_sendPacket(packet: Packet, callback?: (...args: any[]) => void): void; | ||
_sendWindowUpdate(offset: Offset, streamID?: StreamID): void; | ||
_trySendAckFrame(): void; | ||
@@ -49,7 +55,7 @@ _handleRegularPacket(packet: RegularPacket, rcvTime: number, _bufv: BufferVisitor): void; | ||
_handleRstStreamFrame(frame: RstStreamFrame, rcvTime: number): void; | ||
_handleACKFrame(frame: AckFrame): void; | ||
_handleACKFrame(frame: AckFrame, rcvTime: number): void; | ||
_handleStopWaitingFrame(frame: StopWaitingFrame): void; | ||
_handleWindowUpdateFrame(frame: WindowUpdateFrame): void; | ||
_handleBlockedFrame(frame: BlockedFrame, rcvTime: number): void; | ||
_intervalCheck(time: number): void; | ||
_windowUpdate(offset: Offset, streamID?: StreamID): void; | ||
request(options?: any): Stream; | ||
@@ -62,4 +68,2 @@ goaway(err: any): Promise<void>; | ||
destroy(err: any): void; | ||
ref(): void; | ||
unref(): void; | ||
} | ||
@@ -75,6 +79,7 @@ export declare class SessionState { | ||
remoteAddr: SocketAddress | null; | ||
pendingAck: number; | ||
maxPacketSize: number; | ||
bytesRead: number; | ||
bytesWritten: number; | ||
idleTimeout: number; | ||
liveStreamCount: number; | ||
lastNetworkActivityTime: number; | ||
@@ -86,7 +91,6 @@ destroyed: boolean; | ||
keepAlivePingSent: boolean; | ||
maxIncomingByteOffset: number; | ||
outgoingWindowByteOffset: number; | ||
constructor(); | ||
} | ||
export declare class ACKHandler { | ||
misshit: number; | ||
lowestAcked: number; | ||
@@ -96,6 +100,7 @@ largestAcked: number; | ||
largestAckedTime: number; | ||
lastAckedTime: number; | ||
constructor(); | ||
lowest(packetNumber: number): void; | ||
ack(packetNumber: number, rcvTime: number): number; | ||
ack(packetNumber: number, rcvTime: number, needAck: boolean): boolean; | ||
toFrame(): AckFrame | null; | ||
} |
@@ -8,2 +8,4 @@ 'use strict'; | ||
const events_1 = require("events"); | ||
const crypto_1 = require("crypto"); | ||
const constant_1 = require("./internal/constant"); | ||
const protocol_1 = require("./internal/protocol"); | ||
@@ -14,2 +16,4 @@ const symbol_1 = require("./internal/symbol"); | ||
const error_1 = require("./internal/error"); | ||
const flowcontrol_1 = require("./internal/flowcontrol"); | ||
const congestion_1 = require("./internal/congestion"); | ||
const socket_1 = require("./socket"); | ||
@@ -36,3 +40,6 @@ const stream_1 = require("./stream"); | ||
this[symbol_1.kUnackedPackets] = new common_1.Queue(); // up to 1000 | ||
this.setMaxListeners((2 ** 31) - 1); | ||
this[symbol_1.kRTT] = new congestion_1.RTTStats(); | ||
this[symbol_1.kFC] = this.isClient ? // TODO | ||
new flowcontrol_1.ConnectionFlowController(constant_1.ReceiveConnectionWindow, constant_1.DefaultMaxReceiveConnectionWindowClient) : | ||
new flowcontrol_1.ConnectionFlowController(constant_1.ReceiveConnectionWindow, constant_1.DefaultMaxReceiveConnectionWindowServer); | ||
} | ||
@@ -67,6 +74,9 @@ get id() { | ||
} | ||
_sendFrame(frame, callback) { | ||
_newRegularPacket() { | ||
const packetNumber = this[symbol_1.kNextPacketNumber]; | ||
this[symbol_1.kNextPacketNumber] = packetNumber.nextNumber(); | ||
const regularPacket = new packet_1.RegularPacket(this[symbol_1.kID], packetNumber); | ||
return new packet_1.RegularPacket(this[symbol_1.kID], packetNumber, crypto_1.randomBytes(32)); | ||
} | ||
_sendFrame(frame, callback) { | ||
const regularPacket = this._newRegularPacket(); | ||
regularPacket.addFrames(frame); | ||
@@ -77,25 +87,23 @@ regularPacket.isRetransmittable = frame.isRetransmittable(); | ||
_sendStopWaitingFrame(leastUnacked) { | ||
const packetNumber = this[symbol_1.kNextPacketNumber]; | ||
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 regularPacket = this._newRegularPacket(); | ||
const frame = new frame_1.StopWaitingFrame(regularPacket.packetNumber, leastUnacked); | ||
regularPacket.addFrames(frame); | ||
regularPacket.isRetransmittable = false; | ||
debug(`session %s - write StopWaitingFrame, packetNumber: %d, leastUnacked: %d`, this.id, packetNumber.valueOf(), leastUnacked); | ||
this._sendPacket(regularPacket, (err) => { | ||
if (err != null) { | ||
this.destroy(err); | ||
} | ||
}); | ||
debug(`%s session %s - write StopWaitingFrame, packetNumber: %d, leastUnacked: %d`, protocol_1.SessionType[this[symbol_1.kType]], this.id, frame.packetNumber.valueOf(), leastUnacked); | ||
this._sendPacket(regularPacket); | ||
} | ||
_retransmit(frame) { | ||
_retransmit(frame, rcvTime) { | ||
const unackedPackets = this[symbol_1.kUnackedPackets]; | ||
debug(`%s session %s - start retransmit, count: %d, ackFrame: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, unackedPackets.length, frame.valueOf()); | ||
let count = 0; | ||
let packet = unackedPackets.first(); | ||
let count = 0; | ||
while (packet != null) { | ||
const packetNumber = packet.packetNumber.valueOf(); | ||
if (packetNumber > frame.largestAcked) { | ||
return 0; | ||
break; // wait for newest ack | ||
} | ||
if (packetNumber <= frame.lowestAcked || frame.acksPacket(packetNumber)) { | ||
else if (packetNumber === frame.largestAcked) { | ||
this[symbol_1.kRTT].updateRTT(packet.sentTime, rcvTime, frame.delayTime); | ||
} | ||
if (frame.acksPacket(packetNumber)) { | ||
unackedPackets.shift(); | ||
@@ -106,13 +114,9 @@ packet = unackedPackets.first(); | ||
unackedPackets.shift(); | ||
packet.packetNumber = this[symbol_1.kNextPacketNumber]; | ||
packet.setPacketNumber(this[symbol_1.kNextPacketNumber]); | ||
this[symbol_1.kNextPacketNumber] = packet.packetNumber.nextNumber(); | ||
this._sendPacket(packet, (err) => { | ||
if (err != null) { | ||
this.destroy(err); | ||
} | ||
}); | ||
this._sendPacket(packet); | ||
count += 1; | ||
packet = unackedPackets.first(); | ||
count += 1; | ||
} | ||
debug(`session %s - retransmit, count: %d`, this.id, count); | ||
debug(`%s session %s - finish retransmit, count: %d`, protocol_1.SessionType[this[symbol_1.kType]], this.id, count); | ||
return count; | ||
@@ -122,2 +126,9 @@ } | ||
const socket = this[symbol_1.kSocket]; | ||
if (callback == null) { | ||
callback = (err) => { | ||
if (err != null) { | ||
this.destroy(err); | ||
} | ||
}; | ||
} | ||
if (socket == null) { | ||
@@ -130,6 +141,7 @@ return callback(error_1.QuicError.fromError(error_1.QuicError.QUIC_PACKET_WRITE_ERROR)); | ||
if (packet.isRegular()) { | ||
const _packet = packet; | ||
if (this.isClient && !this[symbol_1.kState].versionNegotiated) { | ||
packet.setVersion(this[symbol_1.kVersion]); | ||
_packet.setVersion(this[symbol_1.kVersion]); | ||
} | ||
if (packet.isRetransmittable) { | ||
if (_packet.isRetransmittable) { | ||
this[symbol_1.kUnackedPackets].push(packet); | ||
@@ -140,7 +152,21 @@ if (this[symbol_1.kUnackedPackets].length > 4096) { | ||
} | ||
debug(`%s session %s - write RegularPacket, packetNumber: %d, frames: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, _packet.packetNumber.valueOf(), _packet.frames.map((frame) => frame.name)); | ||
} | ||
socket_1.sendPacket(socket, packet, this[symbol_1.kState].remotePort, this[symbol_1.kState].remoteAddress, callback); | ||
// debug(`%s session %s - write packet: %j`, this.id, packet.valueOf()) | ||
// const buf = toBuffer(packet) | ||
// socket.send(buf, this[kState].remotePort, this[kState].remoteAddress, callback) | ||
} | ||
_sendWindowUpdate(offset, streamID) { | ||
if (streamID == null) { | ||
// update for session | ||
streamID = new protocol_1.StreamID(0); | ||
} | ||
debug(`%s session %s - write WindowUpdateFrame, streamID: %d, offset: %d`, protocol_1.SessionType[this[symbol_1.kType]], this.id, streamID.valueOf(), offset); | ||
this._sendFrame(new frame_1.WindowUpdateFrame(streamID, offset), (err) => { | ||
if (err != null) { | ||
this.emit('error', err); | ||
} | ||
}); | ||
} | ||
_trySendAckFrame() { | ||
@@ -151,3 +177,3 @@ const frame = this[symbol_1.kACKHandler].toFrame(); | ||
} | ||
debug(`session %s - write AckFrame, lowestAcked: %d, largestAcked: %d, ackRanges: %j`, this.id, frame.lowestAcked, frame.largestAcked, frame.ackRanges); | ||
debug(`%s session %s - write AckFrame, lowestAcked: %d, largestAcked: %d, ackRanges: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, frame.lowestAcked, frame.largestAcked, frame.ackRanges); | ||
this._sendFrame(frame, (err) => { | ||
@@ -164,8 +190,8 @@ if (err != null) { | ||
} | ||
const packetNumber = packet.packetNumber.valueOf(); | ||
this[symbol_1.kState].lastNetworkActivityTime = rcvTime; | ||
this[symbol_1.kState].keepAlivePingSent = false; | ||
if (this[symbol_1.kACKHandler].ack(packet.packetNumber.valueOf(), rcvTime) >= 511) { // 256 blocks + 255 gaps | ||
if (this[symbol_1.kACKHandler].ack(packetNumber, rcvTime, packet.needAck())) { | ||
this._trySendAckFrame(); | ||
} | ||
debug(`session %s - received RegularPacket, packetNumber: %d, frames: %j`, this.id, packet.packetNumber.valueOf(), packet.frames.map((frame) => frame.name)); | ||
debug(`%s session %s - received RegularPacket, packetNumber: %d, frames: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, packetNumber, packet.frames.map((frame) => frame.name)); | ||
for (const frame of packet.frames) { | ||
@@ -177,3 +203,3 @@ switch (frame.name) { | ||
case 'ACK': | ||
this._handleACKFrame(frame); | ||
this._handleACKFrame(frame, rcvTime); | ||
break; | ||
@@ -195,2 +221,3 @@ case 'STOP_WAITING': | ||
// It is a purely informational frame. | ||
this._handleBlockedFrame(frame, rcvTime); | ||
break; | ||
@@ -228,3 +255,8 @@ case 'CONGESTION_FEEDBACK': | ||
stream = new stream_1.Stream(frame.streamID, this, {}); | ||
if (this[symbol_1.kState].liveStreamCount >= constant_1.DefaultMaxIncomingStreams) { | ||
stream.close(error_1.QuicError.fromError(error_1.QuicError.QUIC_TOO_MANY_AVAILABLE_STREAMS)); | ||
return; | ||
} | ||
this[symbol_1.kStreams].set(streamID, stream); | ||
this[symbol_1.kState].liveStreamCount += 1; | ||
this.emit('stream', stream); | ||
@@ -245,3 +277,3 @@ } | ||
} | ||
_handleACKFrame(frame) { | ||
_handleACKFrame(frame, rcvTime) { | ||
// The sender must always close the connection if an unsent packet number is acked, | ||
@@ -258,3 +290,3 @@ // so this mechanism automatically defeats any potential attackers. | ||
} | ||
this._retransmit(frame); | ||
this._retransmit(frame, rcvTime); | ||
} | ||
@@ -269,7 +301,5 @@ _handleStopWaitingFrame(frame) { | ||
const offset = frame.offset.valueOf(); | ||
debug(`session %s - received WindowUpdateFrame, streamID: %d, offset: %d`, this.id, streamID, offset); | ||
debug(`%s session %s - received WindowUpdateFrame, streamID: %d, offset: %d`, protocol_1.SessionType[this[symbol_1.kType]], this.id, streamID, offset); | ||
if (streamID === 0) { | ||
if (offset > this[symbol_1.kState].outgoingWindowByteOffset) { | ||
this[symbol_1.kState].outgoingWindowByteOffset = offset; | ||
} | ||
this[symbol_1.kFC].updateMaxSendOffset(offset); | ||
} | ||
@@ -279,4 +309,3 @@ else { | ||
if (stream != null && !stream.destroyed) { | ||
if (offset > stream[symbol_1.kState].outgoingWindowByteOffset) { | ||
stream[symbol_1.kState].outgoingWindowByteOffset = offset; | ||
if (stream[symbol_1.kFC].updateMaxSendOffset(offset)) { | ||
stream._tryFlushCallbacks(); | ||
@@ -287,2 +316,5 @@ } | ||
} | ||
_handleBlockedFrame(frame, rcvTime) { | ||
this[symbol_1.kFC].updateBlockedFrame(frame.streamID.valueOf(), rcvTime); | ||
} | ||
_intervalCheck(time) { | ||
@@ -293,3 +325,3 @@ if (this.destroyed) { | ||
// The PING frame should be used to keep a connection alive when a stream is open. | ||
if (this[symbol_1.kState].keepAlivePingSent && this[symbol_1.kStreams].size > 0 && (time - this[symbol_1.kState].lastNetworkActivityTime >= protocol_1.PingFrameDelay)) { | ||
if (this[symbol_1.kState].keepAlivePingSent && this[symbol_1.kStreams].size > 0 && (time - this[symbol_1.kState].lastNetworkActivityTime >= constant_1.PingFrameDelay)) { | ||
this.ping().catch((err) => this.emit('error', err)); | ||
@@ -304,3 +336,3 @@ } | ||
} | ||
else if (time - stream[symbol_1.kState].lastActivityTime > protocol_1.MaxStreamWaitingTimeout) { | ||
else if (time - stream[symbol_1.kState].lastActivityTime > constant_1.MaxStreamWaitingTimeout) { | ||
stream.emit('timeout'); | ||
@@ -312,14 +344,2 @@ } | ||
} | ||
_windowUpdate(offset, streamID) { | ||
if (streamID == null) { | ||
// update for session | ||
streamID = new protocol_1.StreamID(0); | ||
} | ||
debug(`session %s - write WindowUpdateFrame, streamID: %d, offset: %d`, this.id, streamID.valueOf(), offset); | ||
this._sendFrame(new frame_1.WindowUpdateFrame(streamID, offset), (err) => { | ||
if (err != null) { | ||
this.emit('error', err); | ||
} | ||
}); | ||
} | ||
request(options) { | ||
@@ -329,7 +349,10 @@ if (this[symbol_1.kState].shuttingDown) { | ||
} | ||
if (this[symbol_1.kState].liveStreamCount >= constant_1.DefaultMaxIncomingStreams) { | ||
throw error_1.QuicError.fromError(error_1.QuicError.QUIC_TOO_MANY_OPEN_STREAMS); | ||
} | ||
const streamID = this[symbol_1.kNextStreamID]; | ||
this[symbol_1.kNextStreamID] = streamID.nextID(); | ||
const stream = new stream_1.Stream(streamID, this, (options == null ? {} : options)); | ||
const _streamID = streamID.valueOf(); | ||
this[symbol_1.kStreams].set(_streamID, stream); | ||
this[symbol_1.kStreams].set(streamID.valueOf(), stream); | ||
this[symbol_1.kState].liveStreamCount += 1; | ||
return stream; | ||
@@ -344,3 +367,3 @@ } | ||
const frame = new frame_1.GoAwayFrame(this[symbol_1.kNextStreamID].prevID(), error_1.QuicError.fromError(err)); | ||
debug(`session %s - write GoAwayFrame, streamID: %d, error: %j`, this.id, frame.streamID.valueOf(), frame.error); | ||
debug(`%s session %s - write GoAwayFrame, streamID: %d, error: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, frame.streamID.valueOf(), frame.error); | ||
this._sendFrame(frame, (_e) => { | ||
@@ -353,3 +376,3 @@ resolve(); | ||
return new Promise((resolve, reject) => { | ||
debug(`session %s - write PingFrame`, this.id); | ||
debug(`%s session %s - write PingFrame`, protocol_1.SessionType[this[symbol_1.kType]], this.id); | ||
this._sendFrame(new frame_1.PingFrame(), (err) => { | ||
@@ -374,3 +397,3 @@ if (err != null) { | ||
const frame = new frame_1.ConnectionCloseFrame(error_1.QuicError.fromError(err)); | ||
debug(`session %s - write ConnectionCloseFrame, error: %j`, this.id, frame.error); | ||
debug(`%s session %s - write ConnectionCloseFrame, error: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, frame.error); | ||
this._sendFrame(frame, (e) => { | ||
@@ -395,3 +418,3 @@ this.destroy(e); | ||
const packet = new packet_1.ResetPacket(this[symbol_1.kID], tags); | ||
debug(`session %s - write ResetPacket, packet: %j`, this.id, packet); | ||
debug(`%s session %s - write ResetPacket, packet: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, packet); | ||
this._sendPacket(packet, (e) => { | ||
@@ -404,6 +427,6 @@ this.destroy(e); | ||
destroy(err) { | ||
debug(`session %s - session destroyed, error: %j`, this.id, err); | ||
if (this[symbol_1.kState].destroyed) { | ||
return; | ||
} | ||
debug(`%s session %s - session destroyed, error: %j`, protocol_1.SessionType[this[symbol_1.kType]], this.id, err); | ||
err = error_1.QuicError.checkAny(err); | ||
@@ -415,3 +438,4 @@ if (err != null && err.isNoError) { | ||
if (socket != null) { | ||
if (socket[symbol_1.kState].exclusive && !socket[symbol_1.kState].destroyed) { | ||
socket[symbol_1.kState].conns.delete(this.id); | ||
if (this.isClient && !socket[symbol_1.kState].destroyed && (socket[symbol_1.kState].exclusive || socket[symbol_1.kState].conns.size === 0)) { | ||
socket.close(); | ||
@@ -440,8 +464,2 @@ socket[symbol_1.kState].destroyed = true; | ||
} | ||
ref() { | ||
return; | ||
} | ||
unref() { | ||
return; | ||
} | ||
} | ||
@@ -459,6 +477,7 @@ exports.Session = Session; | ||
this.remoteAddr = null; // SocketAddress | ||
this.pendingAck = 0; | ||
this.maxPacketSize = 0; | ||
this.bytesRead = 0; | ||
this.bytesWritten = 0; | ||
this.idleTimeout = protocol_1.DefaultIdleTimeout; | ||
this.idleTimeout = constant_1.DefaultIdleTimeout; | ||
this.liveStreamCount = 0; | ||
this.lastNetworkActivityTime = Date.now(); | ||
@@ -470,5 +489,2 @@ this.destroyed = false; | ||
this.keepAlivePingSent = false; | ||
// Both stream and session windows start with a default value of 16 KB | ||
this.maxIncomingByteOffset = 16 * 1024; | ||
this.outgoingWindowByteOffset = 16 * 1024; | ||
} | ||
@@ -479,6 +495,8 @@ } | ||
constructor() { | ||
this.lowestAcked = 1; | ||
this.largestAcked = 1; | ||
this.misshit = 0; | ||
this.lowestAcked = 0; | ||
this.largestAcked = 0; | ||
this.numbersAcked = []; | ||
this.largestAckedTime = 0; | ||
this.lastAckedTime = Date.now(); | ||
} | ||
@@ -490,11 +508,35 @@ lowest(packetNumber) { | ||
} | ||
ack(packetNumber, rcvTime) { | ||
ack(packetNumber, rcvTime, needAck) { | ||
if (packetNumber < this.lowestAcked) { | ||
return this.numbersAcked.length; // ignore | ||
return false; // ignore | ||
} | ||
else if (packetNumber > this.largestAcked) { | ||
if (packetNumber > this.largestAcked) { | ||
if (packetNumber - this.largestAcked > 1) { | ||
this.misshit += 1; | ||
} | ||
this.largestAcked = packetNumber; | ||
this.largestAckedTime = rcvTime; | ||
} | ||
return this.numbersAcked.unshift(packetNumber); | ||
else if (Math.abs(packetNumber - this.numbersAcked[0]) > 1) { | ||
this.misshit += 1; | ||
} | ||
let shouldAck = this.numbersAcked.unshift(packetNumber) >= 511; // 256 blocks + 255 gaps, too many packets, should ack | ||
if (!needAck && this.largestAcked - this.lowestAcked <= 1) { | ||
// ACK frame | ||
this.lowestAcked = this.largestAcked; | ||
this.numbersAcked.length = 1; | ||
return false; | ||
} | ||
if (this.misshit > 16) { | ||
shouldAck = true; | ||
} | ||
const timeSpan = rcvTime - this.lastAckedTime; | ||
if (timeSpan >= 512) { | ||
shouldAck = true; | ||
} | ||
if (shouldAck) { | ||
debug(`should ACK, largestAcked: %d, lowestAcked: %d, misshit: %d, numbersAcked: %d, timeSpan: %d`, this.largestAcked, this.lowestAcked, this.misshit, this.numbersAcked.length, timeSpan); | ||
this.lastAckedTime = rcvTime; | ||
} | ||
return shouldAck; | ||
} | ||
@@ -506,6 +548,2 @@ toFrame() { | ||
} | ||
// 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); | ||
@@ -519,3 +557,2 @@ if (numbersAcked[0] <= this.lowestAcked) { | ||
frame.largestAcked = this.largestAcked; | ||
frame.lowestAcked = this.lowestAcked; | ||
frame.largestAckedTime = this.largestAckedTime; | ||
@@ -539,9 +576,6 @@ let range = new frame_1.AckRange(this.largestAcked, this.largestAcked); | ||
} | ||
if (range.first > frame.lowestAcked) { | ||
frame.lowestAcked = range.first; | ||
if (range.last < frame.largestAcked) { | ||
frame.ackRanges.push(range); | ||
frame.ackRanges.push(new frame_1.AckRange(frame.lowestAcked, frame.lowestAcked)); | ||
} | ||
else if (range.first === frame.lowestAcked && frame.ackRanges.length > 0) { | ||
frame.ackRanges.push(range); | ||
} | ||
if (frame.ackRanges.length === 0) { | ||
@@ -551,11 +585,9 @@ this.lowestAcked = this.largestAcked; | ||
} | ||
else { | ||
this.lowestAcked = frame.ackRanges[frame.ackRanges.length - 1].first; // update by StopWaiting | ||
} | ||
// if ackRanges.length > 256, ignore some ranges between | ||
if (frame.ackRanges.length > 256) { | ||
else if (frame.ackRanges.length > 256) { | ||
// if ackRanges.length > 256, ignore some ranges between | ||
frame.ackRanges[255] = frame.ackRanges[frame.ackRanges.length - 1]; | ||
frame.ackRanges.length = 256; | ||
} | ||
debug(`after build AckFrame, largestAcked: %d, lowestAcked: %d, numbersAcked %j`, this.largestAcked, this.lowestAcked, numbersAcked); | ||
debug(`after build AckFrame, largestAcked: %d, lowestAcked: %d, numbersAcked: %j`, this.largestAcked, this.lowestAcked, numbersAcked); | ||
this.misshit = 0; | ||
return frame; | ||
@@ -562,0 +594,0 @@ } |
@@ -5,2 +5,3 @@ /// <reference types="node" /> | ||
import { Packet } from './internal/packet'; | ||
import { Client } from './client'; | ||
export interface AddressInfo { | ||
@@ -17,2 +18,3 @@ address: string; | ||
destroyed: boolean; | ||
conns: Map<string, Client>; | ||
constructor(); | ||
@@ -19,0 +21,0 @@ } |
@@ -8,3 +8,5 @@ 'use strict'; | ||
const symbol_1 = require("./internal/symbol"); | ||
const error_1 = require("./internal/error"); | ||
const common_1 = require("./internal/common"); | ||
const constant_1 = require("./internal/constant"); | ||
class SocketState { | ||
@@ -14,2 +16,3 @@ constructor() { | ||
this.destroyed = false; | ||
this.conns = new Map(); | ||
} | ||
@@ -23,2 +26,3 @@ } | ||
state.destroyed = true; | ||
socket.removeAllListeners(); | ||
}); | ||
@@ -32,14 +36,20 @@ Object.assign(socket, { [symbol_1.kState]: state }); | ||
const byteLen = packet.byteLen(); | ||
if (byteLen > 1500) { | ||
return callback(new Error('packet size too large!')); | ||
if (byteLen > constant_1.MaxReceivePacketSize) { | ||
return callback(new error_1.QuicError('packet size too large!')); | ||
} | ||
if (socket[symbol_1.kState].destroyed) { | ||
return callback(new error_1.QuicError('socket destroyed!')); | ||
} | ||
let bufv = bufferPool.shift(); | ||
if (bufv == null) { | ||
bufv = common_1.Visitor.wrap(Buffer.alloc(1500)); // MTU | ||
bufv = new common_1.BufferVisitor(Buffer.alloc(constant_1.MaxReceivePacketSize)); | ||
} | ||
bufv.v.reset(0, 0); | ||
else { | ||
bufv.reset(); | ||
} | ||
packet.writeTo(bufv); | ||
socket.send(bufv.slice(0, bufv.v.end), remotePort, remoteAddr, (err) => { | ||
socket.send(bufv.buf, 0, bufv.end, remotePort, remoteAddr, (err) => { | ||
packet.sentTime = Date.now(); | ||
bufferPool.push(bufv); | ||
callback(err); | ||
callback(error_1.QuicError.checkAny(err)); | ||
}); | ||
@@ -46,0 +56,0 @@ } |
@@ -5,3 +5,3 @@ /// <reference types="node" /> | ||
import { StreamFrame, RstStreamFrame } from './internal/frame'; | ||
import { kID, kSession, kState } from './internal/symbol'; | ||
import { kID, kFC, kSession, kState } from './internal/symbol'; | ||
import { Session } from './session'; | ||
@@ -12,2 +12,3 @@ export declare class Stream extends Duplex { | ||
private [kState]; | ||
private [kFC]; | ||
constructor(streamID: StreamID, session: Session, options: any); | ||
@@ -21,7 +22,2 @@ readonly id: number; | ||
close(err: any): Promise<any>; | ||
_handleFrame(frame: StreamFrame, rcvTime: number): void; | ||
_handleRstFrame(frame: RstStreamFrame, rcvTime: number): void; | ||
_tryUpdateWindow(offset: number): void; | ||
_tryFlushCallbacks(): void; | ||
_flushData(callback: (err: any) => void): void; | ||
_write(chunk: Buffer, encoding: string, callback: (...args: any[]) => void): void; | ||
@@ -32,2 +28,9 @@ _writev(chunks: any[], callback: (...args: any[]) => void): void; | ||
_destroy(err: any, callback: (...args: any[]) => void): void; | ||
_sendBlockFrame(): void; | ||
_trySendUpdateWindow(): void; | ||
_handleFrame(frame: StreamFrame, rcvTime: number): void; | ||
_handleRstFrame(frame: RstStreamFrame, rcvTime: number): void; | ||
_tryFlushCallbacks(): void; | ||
private _isRemoteWriteable(byteLen); | ||
private _flushData(bufv, callback); | ||
} |
@@ -9,4 +9,7 @@ 'use strict'; | ||
const error_1 = require("./internal/error"); | ||
const constant_1 = require("./internal/constant"); | ||
const protocol_1 = require("./internal/protocol"); | ||
const frame_1 = require("./internal/frame"); | ||
const flowcontrol_1 = require("./internal/flowcontrol"); | ||
const common_1 = require("./internal/common"); | ||
const symbol_1 = require("./internal/symbol"); | ||
@@ -19,6 +22,8 @@ const debug = util_1.debuglog('quic:stream'); | ||
super(options); | ||
// this.cork() | ||
this[symbol_1.kID] = streamID; | ||
this[symbol_1.kSession] = session; | ||
this[symbol_1.kState] = new StreamState(); | ||
this[symbol_1.kFC] = session.isClient ? // TODO: small window will make "packets loss" test failure | ||
new flowcontrol_1.StreamFlowController(constant_1.ReceiveStreamWindow, constant_1.DefaultMaxReceiveStreamWindowClient, session[symbol_1.kFC]) : | ||
new flowcontrol_1.StreamFlowController(constant_1.ReceiveStreamWindow, constant_1.DefaultMaxReceiveStreamWindowServer, session[symbol_1.kFC]); | ||
this.once('close', () => this[symbol_1.kState].lastActivityTime = Date.now()); | ||
@@ -41,6 +46,6 @@ debug(`session %s - new stream: %d`, session.id, streamID.valueOf()); | ||
get bytesRead() { | ||
return this[symbol_1.kState].bytesRead; | ||
return this[symbol_1.kFC].consumedOffset; | ||
} | ||
get bytesWritten() { | ||
return this[symbol_1.kState].bytesWritten; | ||
return this[symbol_1.kFC].writtenOffset; | ||
} | ||
@@ -50,3 +55,3 @@ // close closes the stream with an error. | ||
this[symbol_1.kState].localFIN = true; | ||
const offset = this[symbol_1.kState].writeOffset; | ||
const offset = new protocol_1.Offset(this[symbol_1.kFC].writtenOffset); | ||
const rstStreamFrame = new frame_1.RstStreamFrame(this[symbol_1.kID], offset, error_1.StreamError.fromError(err)); | ||
@@ -63,99 +68,4 @@ debug(`stream %s - close stream, offset: %d, error: %j`, this.id, offset.valueOf(), err); | ||
} | ||
_handleFrame(frame, rcvTime) { | ||
this[symbol_1.kState].lastActivityTime = rcvTime; | ||
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(offset)) { | ||
return; // duplicated frame | ||
} | ||
if (offset > this[symbol_1.kState].maxIncomingByteOffset) { | ||
this.emit('error', new Error('The window of byte offset overflowed')); | ||
this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_ERROR_PROCESSING_STREAM)); | ||
return; | ||
} | ||
this[symbol_1.kState].bytesRead += byteLen; | ||
this[symbol_1.kState].readQueue.push(frame); | ||
if (!frame.isFIN) { | ||
this._tryUpdateWindow(offset); | ||
} | ||
} | ||
this._read(protocol_1.MaxStreamBufferSize * 10); // try to read all | ||
if (this[symbol_1.kState].readQueue.byteLen > protocol_1.MaxStreamReadCacheSize) { | ||
this.emit('error', new Error('Too large caching, stream data maybe lost')); | ||
this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_ERROR_PROCESSING_STREAM)); | ||
return; | ||
} | ||
} | ||
_handleRstFrame(frame, rcvTime) { | ||
this[symbol_1.kState].lastActivityTime = rcvTime; | ||
this[symbol_1.kState].remoteFIN = true; | ||
this[symbol_1.kState].readQueue.setEndOffset(frame.offset.valueOf()); | ||
debug(`stream %s - received RstStreamFrame, offset: %d, error: %j`, this.id, frame.offset.valueOf(), frame.error); | ||
_write(chunk, encoding, callback) { | ||
if (this[symbol_1.kState].localFIN) { | ||
this.destroy(frame.error); | ||
} | ||
else { | ||
this.emit('error', frame.error); | ||
this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_RST_ACKNOWLEDGEMENT)); | ||
} | ||
return; | ||
} | ||
_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() { | ||
if (this[symbol_1.kState].writeCallbacks.length === 0 || this[symbol_1.kState].flushing) { | ||
return; | ||
} | ||
const nextByteLen = Math.min(this[symbol_1.kState].bufferList.byteLen, protocol_1.MaxStreamBufferSize); | ||
if ((nextByteLen > 0) && | ||
(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, this[symbol_1.kState].bufferList.byteLen); | ||
return; | ||
} | ||
this[symbol_1.kState].flushing = true; | ||
this._flushData((err) => { | ||
this[symbol_1.kState].flushing = false; | ||
const shouldConsume = this[symbol_1.kState].bufferList.byteLen > 0; // should send data | ||
const shouldFIN = this[symbol_1.kState].shouldFIN && !this[symbol_1.kState].localFIN; // should send FIN | ||
if (err == null && (shouldConsume || shouldFIN)) { | ||
return this._tryFlushCallbacks(); | ||
} | ||
for (const cb of this[symbol_1.kState].writeCallbacks) { | ||
cb(err); | ||
} | ||
this[symbol_1.kState].writeCallbacks.length = 0; | ||
}); | ||
} | ||
_flushData(callback) { | ||
const byteLen = this[symbol_1.kState].bufferList.read(this[symbol_1.kState].flushBuffer, 0); | ||
if (byteLen === 0 && !this[symbol_1.kState].shouldFIN) { | ||
return callback(null); | ||
} | ||
const offet = this[symbol_1.kState].writeOffset; | ||
const buf = byteLen > 0 ? this[symbol_1.kState].flushBuffer.slice(0, byteLen) : null; | ||
const shouldFIN = this[symbol_1.kState].shouldFIN && this[symbol_1.kState].bufferList.byteLen === 0; | ||
if (byteLen > 0) { | ||
this[symbol_1.kState].writeOffset = offet.nextOffset(byteLen); | ||
this[symbol_1.kState].bytesWritten += byteLen; | ||
} | ||
const streamFrame = new frame_1.StreamFrame(this[symbol_1.kID], offet, buf, shouldFIN); | ||
if (streamFrame.isFIN) { | ||
this[symbol_1.kState].localFIN = true; | ||
} | ||
debug(`stream %s - write streamFrame, isFIN: %s, offset: %d, data size: %d`, this.id, streamFrame.isFIN, streamFrame.offset.valueOf(), byteLen); | ||
this[symbol_1.kSession]._sendFrame(streamFrame, callback); | ||
} | ||
_write(chunk, encoding, callback) { | ||
if (this[symbol_1.kState].localFIN || this[symbol_1.kState].shouldFIN) { | ||
return callback(new error_1.StreamError('QUIC_RST_ACKNOWLEDGEMENT')); | ||
@@ -166,10 +76,14 @@ } | ||
} | ||
this[symbol_1.kState].bufferList.write(chunk); | ||
this[symbol_1.kState].writeCallbacks.push(callback); | ||
if (chunk.length === 0) { | ||
return callback(null); | ||
} | ||
this[symbol_1.kState].outgoingChunksList.push(chunk, callback); | ||
this._tryFlushCallbacks(); | ||
} | ||
_writev(chunks, callback) { | ||
if (this[symbol_1.kState].localFIN || this[symbol_1.kState].shouldFIN) { | ||
if (this[symbol_1.kState].localFIN) { | ||
return callback(new error_1.StreamError('QUIC_RST_ACKNOWLEDGEMENT')); | ||
} | ||
let len = 0; | ||
const list = []; | ||
for (const item of chunks) { | ||
@@ -181,18 +95,21 @@ // { chunk: ..., encoding: ... } | ||
} | ||
this[symbol_1.kState].bufferList.write(chunk); | ||
len += chunk.length; | ||
list.push(chunk); | ||
} | ||
this[symbol_1.kState].writeCallbacks.push(callback); | ||
if (len === 0) { | ||
return callback(null); | ||
} | ||
this[symbol_1.kState].outgoingChunksList.push(Buffer.concat(list, len), callback); | ||
this._tryFlushCallbacks(); | ||
} | ||
_final(callback) { | ||
this[symbol_1.kState].shouldFIN = true; | ||
this[symbol_1.kState].writeCallbacks.push(callback); | ||
this[symbol_1.kState].outgoingChunksList.push(null, callback); | ||
this._tryFlushCallbacks(); | ||
} | ||
_read(size = 0) { | ||
let data = this[symbol_1.kState].readQueue.read(); | ||
let data = this[symbol_1.kState].incomingSequencer.read(); | ||
while (data != null) { | ||
if (this.push(data) && size > data.length) { | ||
size -= data.length; | ||
data = this[symbol_1.kState].readQueue.read(); | ||
data = this[symbol_1.kState].incomingSequencer.read(); | ||
continue; | ||
@@ -202,3 +119,7 @@ } | ||
} | ||
if (!this[symbol_1.kState].ended && this[symbol_1.kState].readQueue.isEnd()) { | ||
this[symbol_1.kFC].updateConsumedOffset(this[symbol_1.kState].incomingSequencer.consumedOffset); | ||
if (!this[symbol_1.kState].remoteFIN) { | ||
process.nextTick(() => this._trySendUpdateWindow()); | ||
} | ||
if (!this[symbol_1.kState].ended && this[symbol_1.kState].incomingSequencer.isFIN()) { | ||
this[symbol_1.kState].ended = true; | ||
@@ -210,11 +131,11 @@ this.push(null); | ||
debug(`stream %s - stream destroyed, error: %j`, this.id, err); | ||
this[symbol_1.kSession][symbol_1.kState].liveStreamCount -= 1; | ||
const state = this[symbol_1.kState]; | ||
state.localFIN = true; | ||
state.remoteFIN = true; | ||
state.shouldFIN = true; | ||
state.aborted = true; | ||
state.destroyed = true; | ||
state.finished = true; | ||
state.readQueue.reset(); | ||
state.bufferList.reset(); | ||
state.incomingSequencer.reset(); | ||
state.outgoingChunksList.reset(); | ||
err = error_1.StreamError.checkAny(err); | ||
@@ -226,2 +147,109 @@ if (err != null && err.isNoError) { | ||
} | ||
_sendBlockFrame() { | ||
this[symbol_1.kSession]._sendFrame(new frame_1.BlockedFrame(this[symbol_1.kID])); | ||
} | ||
_trySendUpdateWindow() { | ||
if (this[symbol_1.kFC].shouldUpdateWindow()) { | ||
const offset = this[symbol_1.kFC].updateWindowOffset(this[symbol_1.kSession][symbol_1.kRTT].msRTT); | ||
this[symbol_1.kSession]._sendWindowUpdate(new protocol_1.Offset(offset), this[symbol_1.kID]); | ||
} | ||
} | ||
_handleFrame(frame, rcvTime) { | ||
this[symbol_1.kState].lastActivityTime = rcvTime; | ||
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); | ||
this[symbol_1.kFC].updateHighestReceived(offset + byteLen); | ||
if (this[symbol_1.kFC].isBlocked()) { | ||
this.emit('error', new Error('The window of byte offset overflowed')); | ||
this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_ERROR_PROCESSING_STREAM)); | ||
return; | ||
} | ||
if (frame.isFIN) { | ||
this[symbol_1.kState].remoteFIN = true; | ||
this[symbol_1.kState].incomingSequencer.setFinalOffset(offset + byteLen); | ||
} | ||
if (frame.data != null) { | ||
if (this[symbol_1.kState].incomingSequencer.hasOffset(offset)) { | ||
return; // duplicated frame | ||
} | ||
this[symbol_1.kState].incomingSequencer.push(frame); | ||
} | ||
this._read(); | ||
if (this[symbol_1.kState].incomingSequencer.byteLen > constant_1.MaxStreamReadCacheSize) { | ||
this.emit('error', new Error('Too large caching, stream data maybe lost')); | ||
this.destroy(error_1.StreamError.fromError(error_1.StreamError.QUIC_ERROR_PROCESSING_STREAM)); | ||
} | ||
} | ||
_handleRstFrame(frame, rcvTime) { | ||
this[symbol_1.kState].lastActivityTime = rcvTime; | ||
this[symbol_1.kState].remoteFIN = true; | ||
this[symbol_1.kState].incomingSequencer.setFinalOffset(frame.offset.valueOf()); | ||
debug(`stream %s - received RstStreamFrame, offset: %d, error: %j`, this.id, frame.offset.valueOf(), frame.error); | ||
if (this[symbol_1.kState].localFIN) { | ||
this.destroy(frame.error); | ||
} | ||
else { | ||
this.emit('error', frame.error); | ||
this.close(error_1.StreamError.fromError(error_1.StreamError.QUIC_RST_ACKNOWLEDGEMENT)); | ||
} | ||
return; | ||
} | ||
_tryFlushCallbacks() { | ||
const entry = this[symbol_1.kState].outgoingChunksList.first(); | ||
if (entry == null || this[symbol_1.kState].flushing) { | ||
return; | ||
} | ||
if (entry.data != null && !this._isRemoteWriteable(this[symbol_1.kSession][symbol_1.kState].maxPacketSize)) { | ||
return; | ||
} | ||
const callback = entry.callback; | ||
this[symbol_1.kState].flushing = true; | ||
this._flushData(entry.data, (err) => { | ||
this[symbol_1.kState].flushing = false; | ||
if (entry.checkConsumed()) { | ||
this[symbol_1.kState].outgoingChunksList.shift(); | ||
callback(err); | ||
} | ||
if (err == null && this[symbol_1.kState].outgoingChunksList.pendingCb > 0) { | ||
return this._tryFlushCallbacks(); | ||
} | ||
}); | ||
} | ||
_isRemoteWriteable(byteLen) { | ||
if (this[symbol_1.kFC].willBlocked(byteLen)) { | ||
// should wait for WINDOW_UPDATE | ||
debug(`stream %s - wait for WINDOW_UPDATE, writtenOffset: %d, maxSendOffset: %d, to write size: %d`, this.id, this[symbol_1.kFC].writtenOffset, this[symbol_1.kFC].maxSendOffset, byteLen); | ||
this._sendBlockFrame(); | ||
return false; | ||
} | ||
return true; | ||
} | ||
_flushData(bufv, callback) { | ||
let byteLen = 0; // bytes to write | ||
let nextByteLen = 0; // bytes for next write | ||
const offet = new protocol_1.Offset(this[symbol_1.kFC].writtenOffset); | ||
const streamFrame = new frame_1.StreamFrame(this[symbol_1.kID], offet, bufv == null); | ||
const packet = this[symbol_1.kSession]._newRegularPacket(); | ||
if (bufv != null) { | ||
byteLen = Math.min(bufv.length - bufv.end, this[symbol_1.kSession][symbol_1.kState].maxPacketSize - packet.headerLen() - streamFrame.headerLen(true)); | ||
bufv.walk(byteLen); | ||
nextByteLen = Math.min(byteLen, bufv.length - bufv.end); | ||
streamFrame.setData(bufv.buf.slice(bufv.start, bufv.end)); | ||
this[symbol_1.kFC].updateWrittenOffset(byteLen); | ||
} | ||
if (streamFrame.isFIN) { | ||
this[symbol_1.kState].localFIN = true; | ||
} | ||
debug(`stream %s - write streamFrame, isFIN: %s, offset: %d, data size: %d`, this.id, streamFrame.isFIN, streamFrame.offset.valueOf(), byteLen); | ||
packet.addFrames(streamFrame); | ||
packet.isRetransmittable = true; | ||
this[symbol_1.kSession]._sendPacket(packet, (err) => { | ||
// Packet Number length maybe increase 1 byte | ||
if (err != null || nextByteLen === 0 || !this._isRemoteWriteable(nextByteLen + 1)) { | ||
return callback(err); | ||
} | ||
this._flushData(bufv, callback); | ||
}); | ||
} | ||
} | ||
@@ -233,3 +261,2 @@ exports.Stream = Stream; | ||
this.remoteFIN = false; // remote endpoint should not send data | ||
this.shouldFIN = false; | ||
this.flushing = false; | ||
@@ -240,20 +267,19 @@ this.ended = false; | ||
this.finished = false; | ||
this.bytesRead = 0; | ||
this.bytesWritten = 0; | ||
this.lastActivityTime = Date.now(); | ||
this.readQueue = new StreamFramesSorter(); | ||
this.bufferList = new StreamDataList(); | ||
this.writeOffset = new protocol_1.Offset(0); | ||
this.flushBuffer = Buffer.alloc(protocol_1.MaxStreamBufferSize); | ||
// Both stream and session windows start with a default value of 16 KB | ||
this.maxIncomingByteOffset = 16 * 1024; | ||
this.outgoingWindowByteOffset = 16 * 1024; | ||
this.writeCallbacks = []; | ||
this.incomingSequencer = new StreamSequencer(); | ||
this.outgoingChunksList = new StreamDataList(); | ||
} | ||
} | ||
class StreamDataEntry { | ||
constructor(buf, entry) { | ||
this.data = buf; | ||
this.next = entry; | ||
constructor(callback, buf) { | ||
this.callback = callback; | ||
this.next = null; | ||
this.data = buf == null ? null : new common_1.BufferVisitor(buf); | ||
} | ||
get byteLen() { | ||
return this.data == null ? 0 : this.data.length; | ||
} | ||
checkConsumed() { | ||
return this.data == null || this.data.end === this.data.length; | ||
} | ||
} | ||
@@ -264,3 +290,3 @@ class StreamDataList { | ||
this.tail = null; | ||
this.length = 0; | ||
this.pendingCb = 0; | ||
this.byteLen = 0; | ||
@@ -271,7 +297,7 @@ } | ||
this.tail = null; | ||
this.length = 0; | ||
this.pendingCb = 0; | ||
this.byteLen = 0; | ||
} | ||
write(buf) { | ||
const entry = new StreamDataEntry(buf, null); | ||
push(buf, callback) { | ||
const entry = new StreamDataEntry(callback, buf); | ||
if (this.tail != null) { | ||
@@ -284,11 +310,14 @@ this.tail.next = entry; | ||
this.tail = entry; | ||
this.length += 1; | ||
this.byteLen += buf.length; | ||
this.pendingCb += 1; | ||
this.byteLen += entry.byteLen; | ||
} | ||
_shift() { | ||
first() { | ||
return this.head; | ||
} | ||
shift() { | ||
if (this.head == null) { | ||
return null; | ||
} | ||
const ret = this.head.data; | ||
if (this.length === 1) { | ||
const entry = this.head; | ||
if (this.pendingCb === 1) { | ||
this.head = this.tail = null; | ||
@@ -299,22 +328,6 @@ } | ||
} | ||
this.length -= 1; | ||
return ret; | ||
this.pendingCb -= 1; | ||
this.byteLen -= entry.byteLen; | ||
return entry; | ||
} | ||
read(buf, offset) { | ||
if (this.head == null) { | ||
return 0; | ||
} | ||
const n = buf.length - offset; | ||
const ret = this.head.data; | ||
if (ret.length >= n) { | ||
ret.copy(buf, offset, 0, n); | ||
this.head.data = ret.slice(n); | ||
this.byteLen -= n; | ||
return n; | ||
} | ||
ret.copy(buf, offset, 0, ret.length); | ||
this._shift(); | ||
this.byteLen -= ret.length; | ||
return ret.length + this.read(buf, offset + ret.length); | ||
} | ||
} | ||
@@ -328,12 +341,13 @@ class StreamFrameEntry { | ||
} | ||
class StreamFramesSorter { | ||
// sequencer | ||
class StreamSequencer { | ||
constructor() { | ||
this.head = null; | ||
this.byteLen = 0; | ||
this.readOffset = 0; | ||
this.endOffset = -1; | ||
this.consumedOffset = 0; | ||
this.finalOffset = -1; | ||
this.pendingOffsets = new Set(); | ||
} | ||
hasOffset(offset) { | ||
if (offset < this.readOffset) { | ||
if (offset < this.consumedOffset) { | ||
return true; | ||
@@ -346,11 +360,11 @@ } | ||
this.byteLen = 0; | ||
this.readOffset = 0; | ||
this.endOffset = -1; | ||
this.consumedOffset = 0; | ||
this.finalOffset = -1; | ||
this.pendingOffsets.clear(); | ||
} | ||
setEndOffset(offset) { | ||
this.endOffset = offset; | ||
setFinalOffset(offset) { | ||
this.finalOffset = offset; | ||
} | ||
isEnd() { | ||
return this.readOffset === this.endOffset; | ||
isFIN() { | ||
return this.consumedOffset === this.finalOffset; | ||
} | ||
@@ -392,8 +406,8 @@ /** | ||
let data = null; | ||
if (this.head != null && this.readOffset === this.head.offset) { | ||
if (this.head != null && this.consumedOffset === this.head.offset) { | ||
data = this.head.data; | ||
if (data != null) { | ||
this.pendingOffsets.delete(this.consumedOffset); | ||
this.byteLen -= data.length; | ||
this.readOffset += data.length; | ||
this.pendingOffsets.delete(this.readOffset); | ||
this.consumedOffset += data.length; | ||
} | ||
@@ -400,0 +414,0 @@ this.head = this.head.next; |
@@ -7,3 +7,3 @@ { | ||
], | ||
"version": "0.2.2", | ||
"version": "0.4.0", | ||
"main": "dist/index.js", | ||
@@ -24,4 +24,4 @@ "license": "MIT", | ||
"devDependencies": { | ||
"@types/node": "^9.6.5", | ||
"ilog": "^1.2.2", | ||
"@types/node": "^9.6.6", | ||
"ilog": "^1.2.3", | ||
"thunks": "^4.9.2", | ||
@@ -32,3 +32,3 @@ "tman": "^1.7.4", | ||
"tslint-eslint-rules": "^5.1.0", | ||
"typescript": "^2.8.1" | ||
"typescript": "^2.8.3" | ||
}, | ||
@@ -35,0 +35,0 @@ "scripts": { |
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
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
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
361889
51
5921