// When there's an incoming frame object, it first generates the frame type specific part of the // frame (payload), and then then adds the header part which holds fields that are common to all // frame types (like the length of the payload).
Serializer.prototype._transform = function _transform(frame, encoding, done) { this._log.trace({ frame: frame }, 'Outgoing frame');
assert(frame.type in Serializer, 'Unknown frame type: ' + frame.type);
var buffers = [];
Serializer[frame.type](frame, buffers); var length = Serializer.commonHeader(frame, buffers);
assert(length <= MAX_PAYLOAD_SIZE, 'Frame too large!');
for (var i = 0; i < buffers.length; i++) { if (logData) { this._log.trace({ data: buffers[i] }, 'Outgoing data');
} this.push(buffers[i]);
}
// The Deserializer is stateful, and it's two main alternating states are: *waiting for header* and // *waiting for payload*. The state is stored in the boolean property `_waitingForHeader`. // // When entering a new state, a `_buffer` is created that will hold the accumulated data (header or // payload). The `_cursor` is used to track the progress.
Deserializer.prototype._next = function(size) { this._cursor = 0; this._buffer = Buffer.alloc(size); this._waitingForHeader = !this._waitingForHeader; if (this._waitingForHeader) { this._frame = {};
}
};
// Parsing an incoming buffer is an iterative process because it can hold multiple frames if it's // large enough. A `cursor` is used to track the progress in parsing the incoming `chunk`.
Deserializer.prototype._transform = function _transform(chunk, encoding, done) { var cursor = 0;
if (logData) { this._log.trace({ data: chunk }, 'Incoming data');
}
while(cursor < chunk.length) { // The content of an incoming buffer is first copied to `_buffer`. If it can't hold the full // chunk, then only a part of it is copied. var toCopy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor);
chunk.copy(this._buffer, this._cursor, cursor, cursor + toCopy); this._cursor += toCopy;
cursor += toCopy;
// When `_buffer` is full, it's content gets parsed either as header or payload depending on // the actual state.
// If it's header then the parsed data is stored in a temporary variable and then the // deserializer waits for the specified length payload. if ((this._cursor === this._buffer.length) && this._waitingForHeader) { var payloadSize = Deserializer.commonHeader(this._buffer, this._frame); if (payloadSize <= MAX_PAYLOAD_SIZE) { this._next(payloadSize);
} else { this.emit('error', 'FRAME_SIZE_ERROR'); return;
}
}
// If it's payload then the the frame object is finalized and then gets pushed out. // Unknown frame types are ignored. // // Note: If we just finished the parsing of a header and the payload length is 0, this branch // will also run. if ((this._cursor === this._buffer.length) && !this._waitingForHeader) { if (this._frame.type) { var error = Deserializer[this._frame.type](this._buffer, this._frame, this._role); if (error) { this._log.error('Incoming frame parsing error: ' + error); this.emit('error', error);
} else { this._log.trace({ frame: this._frame }, 'Incoming frame'); this.push(this._frame);
}
} else { this._log.error('Unknown type incoming frame'); // Ignore it other than logging
} this._next(COMMON_HEADER_SIZE);
}
}
done();
};
// [Frame Header](https://tools.ietf.org/html/rfc7540#section-4.1) // -------------------------------------------------------------- // // HTTP/2 frames share a common base format consisting of a 9-byte header followed by 0 to 2^24 - 1 // bytes of data. // // Additional size limits can be set by specific application uses. HTTP limits the frame size to // 16,384 octets by default, though this can be increased by a receiver. // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Length (24) | // +---------------+---------------+---------------+ // | Type (8) | Flags (8) | // +-+-----------------------------+---------------+---------------+ // |R| Stream Identifier (31) | // +-+-------------------------------------------------------------+ // | Frame Data (0...) ... // +---------------------------------------------------------------+ // // The fields of the frame header are defined as: // // * Length: // The length of the frame data expressed as an unsigned 24-bit integer. The 9 bytes of the frame // header are not included in this value. // // * Type: // The 8-bit type of the frame. The frame type determines how the remainder of the frame header // and data are interpreted. Implementations MUST ignore unsupported and unrecognized frame types. // // * Flags: // An 8-bit field reserved for frame-type specific boolean flags. // // Flags are assigned semantics specific to the indicated frame type. Flags that have no defined // semantics for a particular frame type MUST be ignored, and MUST be left unset (0) when sending. // // * R: // A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST remain unset // (0) when sending and MUST be ignored when receiving. // // * Stream Identifier: // A 31-bit stream identifier. The value 0 is reserved for frames that are associated with the // connection as a whole as opposed to an individual stream. // // The structure and content of the remaining frame data is dependent entirely on the frame type.
var COMMON_HEADER_SIZE = 9;
var frameTypes = [];
var frameFlags = {};
var genericAttributes = ['type', 'flags', 'stream'];
var typeSpecificAttributes = {};
Serializer.commonHeader = function writeCommonHeader(frame, buffers) { var headerBuffer = Buffer.alloc(COMMON_HEADER_SIZE);
var size = 0; for (var i = 0; i < buffers.length; i++) {
size += buffers[i].length;
}
headerBuffer.writeUInt8(0, 0);
headerBuffer.writeUInt16BE(size, 1);
var typeId = frameTypes.indexOf(frame.type); // If we are here then the type is valid for sure
headerBuffer.writeUInt8(typeId, 3);
var flagByte = 0; for (var flag in frame.flags) { var position = frameFlags[frame.type].indexOf(flag); assert(position !== -1, 'Unknown flag for frame type ' + frame.type + ': ' + flag); if (frame.flags[flag]) {
flagByte |= (1 << position);
}
}
headerBuffer.writeUInt8(flagByte, 4);
Deserializer.commonHeader = function readCommonHeader(buffer, frame) { if (buffer.length < 9) { return'FRAME_SIZE_ERROR';
}
var totallyWastedByte = buffer.readUInt8(0); var length = buffer.readUInt16BE(1); // We do this just for sanity checking later on, to make sure no one sent us a // frame that's super large.
length += totallyWastedByte << 16;
frame.type = frameTypes[buffer.readUInt8(3)]; if (!frame.type) { // We are required to ignore unknown frame types return length;
}
frame.flags = {}; var flagByte = buffer.readUInt8(4); var definedFlags = frameFlags[frame.type]; for (var i = 0; i < definedFlags.length; i++) {
frame.flags[definedFlags[i]] = Boolean(flagByte & (1 << i));
}
// Every frame type is registered in the following places: // // * `frameTypes`: a register of frame type codes (used by `commonHeader()`) // * `frameFlags`: a register of valid flags for frame types (used by `commonHeader()`) // * `typeSpecificAttributes`: a register of frame specific frame object attributes (used by // logging code and also serves as documentation for frame objects)
// [DATA Frames](https://tools.ietf.org/html/rfc7540#section-6.1) // ------------------------------------------------------------ // // DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a // stream. // // The DATA frame defines the following flags: // // * END_STREAM (0x1): // Bit 1 being set indicates that this frame is the last that the endpoint will send for the // identified stream. // * PADDED (0x08): // Bit 4 being set indicates that the Pad Length field is present.
Serializer.DATA = function writeData(frame, buffers) {
buffers.push(frame.data);
};
Deserializer.DATA = function readData(buffer, frame) { var dataOffset = 0; var paddingLength = 0; if (frame.flags.PADDED) { if (buffer.length < 1) { // We must have at least one byte for padding control, but we don't. Bad peer! return'FRAME_SIZE_ERROR';
}
paddingLength = (buffer.readUInt8(dataOffset) & 0xff);
dataOffset = 1;
}
if (paddingLength) { if (paddingLength >= (buffer.length - 1)) { // We don't have enough room for the padding advertised - bad peer! return'FRAME_SIZE_ERROR';
}
frame.data = buffer.slice(dataOffset, -1 * paddingLength);
} else {
frame.data = buffer.slice(dataOffset);
}
};
// [HEADERS](https://tools.ietf.org/html/rfc7540#section-6.2) // -------------------------------------------------------------- // // The HEADERS frame (type=0x1) allows the sender to create a stream. // // The HEADERS frame defines the following flags: // // * END_STREAM (0x1): // Bit 1 being set indicates that this frame is the last that the endpoint will send for the // identified stream. // * END_HEADERS (0x4): // The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide // a complete set of headers. // * PADDED (0x08): // Bit 4 being set indicates that the Pad Length field is present. // * PRIORITY (0x20): // Bit 6 being set indicates that the Exlusive Flag (E), Stream Dependency, and Weight fields are // present.
if (paddingLength) { if ((buffer.length - dataOffset) < paddingLength) { // Not enough data left to satisfy the advertised padding - bad peer! return'FRAME_SIZE_ERROR';
}
frame.data = buffer.slice(dataOffset, -1 * paddingLength);
} else {
frame.data = buffer.slice(dataOffset);
}
};
// [PRIORITY](https://tools.ietf.org/html/rfc7540#section-6.3) // ------------------------------------------------------- // // The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream. // // The PRIORITY frame does not define any flags.
Deserializer.PRIORITY = function readPriority(buffer, frame) { if (buffer.length < 5) { // PRIORITY frames are 5 bytes long. Bad peer! return'FRAME_SIZE_ERROR';
} var dependencyData = Buffer.alloc(4);
buffer.copy(dependencyData, 0, 0, 4);
frame.exclusiveDependency = !!(dependencyData[0] & 0x80);
dependencyData[0] &= 0x7f;
frame.priorityDependency = dependencyData.readUInt32BE(0);
frame.priorityWeight = buffer.readUInt8(4);
};
// [RST_STREAM](https://tools.ietf.org/html/rfc7540#section-6.4) // ----------------------------------------------------------- // // The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream. // // No type-flags are defined.
frameTypes[0x3] = 'RST_STREAM';
frameFlags.RST_STREAM = [];
typeSpecificAttributes.RST_STREAM = ['error'];
// 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Error Code (32) | // +---------------------------------------------------------------+ // // The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error // code (see Error Codes). The error code indicates why the stream is being terminated.
Serializer.RST_STREAM = function writeRstStream(frame, buffers) { var buffer = Buffer.alloc(4); var code = errorCodes.indexOf(frame.error); assert((0 <= code) && (code <= 0xffffffff), code);
buffer.writeUInt32BE(code, 0);
buffers.push(buffer);
};
Deserializer.RST_STREAM = function readRstStream(buffer, frame) { if (buffer.length < 4) { // RST_STREAM is 4 bytes long. Bad peer! return'FRAME_SIZE_ERROR';
}
frame.error = errorCodes[buffer.readUInt32BE(0)]; if (!frame.error) { // Unknown error codes are considered equivalent to INTERNAL_ERROR
frame.error = 'INTERNAL_ERROR';
}
};
// [SETTINGS](https://tools.ietf.org/html/rfc7540#section-6.5) // ------------------------------------------------------- // // The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints // communicate. // // The SETTINGS frame defines the following flag:
// * ACK (0x1): // Bit 1 being set indicates that this frame acknowledges receipt and application of the peer's // SETTINGS frame.
frameTypes[0x4] = 'SETTINGS';
frameFlags.SETTINGS = ['ACK'];
typeSpecificAttributes.SETTINGS = ['settings'];
// The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of a // 16-bit identifier, and an unsigned 32-bit value. // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Identifier(16) | Value (32) | // +-----------------+---------------------------------------------+ // ...Value | // +---------------------------------+ // // Each setting in a SETTINGS frame replaces the existing value for that setting. Settings are // processed in the order in which they appear, and a receiver of a SETTINGS frame does not need to // maintain any state other than the current value of settings. Therefore, the value of a setting // is the last value that is seen by a receiver. This permits the inclusion of the same settings // multiple times in the same SETTINGS frame, though doing so does nothing other than waste // connection capacity.
Serializer.SETTINGS = function writeSettings(frame, buffers) { var settings = [], settingsLeft = Object.keys(frame.settings);
definedSettings.forEach(function(setting, id) { if (setting.name in frame.settings) {
settingsLeft.splice(settingsLeft.indexOf(setting.name), 1); var value = frame.settings[setting.name];
settings.push({ id: id, value: setting.flag ? Boolean(value) : value });
}
}); assert(settingsLeft.length === 0, 'Unknown settings: ' + settingsLeft.join(', '));
var buffer = Buffer.alloc(settings.length * 6); for (var i = 0; i < settings.length; i++) {
buffer.writeUInt16BE(settings[i].id & 0xffff, i*6);
buffer.writeUInt32BE(settings[i].value, i*6 + 2);
}
buffers.push(buffer);
};
Deserializer.SETTINGS = function readSettings(buffer, frame, role) {
frame.settings = {};
// Receipt of a SETTINGS frame with the ACK flag set and a length // field value other than 0 MUST be treated as a connection error // (Section 5.4.1) of type FRAME_SIZE_ERROR. if(frame.flags.ACK && buffer.length != 0) { return'FRAME_SIZE_ERROR';
}
if (buffer.length % 6 !== 0) { return'PROTOCOL_ERROR';
} for (var i = 0; i < buffer.length / 6; i++) { var id = buffer.readUInt16BE(i*6) & 0xffff; var setting = definedSettings[id]; if (setting) { if (role == 'CLIENT' && setting.name == 'SETTINGS_ENABLE_PUSH') { return'SETTINGS frame on client got SETTINGS_ENABLE_PUSH';
} var value = buffer.readUInt32BE(i*6 + 2);
frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value;
}
}
};
// The following settings are defined: var definedSettings = [];
// * SETTINGS_HEADER_TABLE_SIZE (1): // Allows the sender to inform the remote endpoint of the size of the header compression table // used to decode header blocks.
definedSettings[1] = { name: 'SETTINGS_HEADER_TABLE_SIZE', flag: false };
// * SETTINGS_ENABLE_PUSH (2): // This setting can be use to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame // if it receives this setting set to a value of 0. The default value is 1, which indicates that // push is permitted.
definedSettings[2] = { name: 'SETTINGS_ENABLE_PUSH', flag: true };
// * SETTINGS_MAX_CONCURRENT_STREAMS (3): // indicates the maximum number of concurrent streams that the sender will allow.
definedSettings[3] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false };
// * SETTINGS_INITIAL_WINDOW_SIZE (4): // indicates the sender's initial stream window size (in bytes) for new streams.
definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false };
// * SETTINGS_MAX_FRAME_SIZE (5): // indicates the maximum size of a frame the receiver will allow.
definedSettings[5] = { name: 'SETTINGS_MAX_FRAME_SIZE', flag: false };
// [PUSH_PROMISE](https://tools.ietf.org/html/rfc7540#section-6.6) // --------------------------------------------------------------- // // The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the // sender intends to initiate. // // The PUSH_PROMISE frame defines the following flags: // // * END_PUSH_PROMISE (0x4): // The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to // provide a complete set of headers.
Deserializer.PUSH_PROMISE = function readPushPromise(buffer, frame) { if (buffer.length < 4) { return'FRAME_SIZE_ERROR';
} var dataOffset = 0; var paddingLength = 0; if (frame.flags.PADDED) { if (buffer.length < 5) { return'FRAME_SIZE_ERROR';
}
paddingLength = (buffer.readUInt8(dataOffset) & 0xff);
dataOffset = 1;
}
frame.promised_stream = buffer.readUInt32BE(dataOffset) & 0x7fffffff;
dataOffset += 4; if (paddingLength) { if ((buffer.length - dataOffset) < paddingLength) { return'FRAME_SIZE_ERROR';
}
frame.data = buffer.slice(dataOffset, -1 * paddingLength);
} else {
frame.data = buffer.slice(dataOffset);
}
};
// [PING](https://tools.ietf.org/html/rfc7540#section-6.7) // ----------------------------------------------- // // The PING frame (type=0x6) is a mechanism for measuring a minimal round-trip time from the // sender, as well as determining whether an idle connection is still functional. // // The PING frame defines one type-specific flag: // // * ACK (0x1): // Bit 1 being set indicates that this PING frame is a PING response.
frameTypes[0x6] = 'PING';
frameFlags.PING = ['ACK'];
typeSpecificAttributes.PING = ['data'];
// In addition to the frame header, PING frames MUST contain 8 additional octets of opaque data.
Serializer.PING = function writePing(frame, buffers) {
buffers.push(frame.data);
};
Deserializer.PING = function readPing(buffer, frame) { if (buffer.length !== 8) { return'FRAME_SIZE_ERROR';
}
frame.data = buffer;
};
// [GOAWAY](https://tools.ietf.org/html/rfc7540#section-6.8) // --------------------------------------------------- // // The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this connection. // // The GOAWAY frame does not define any flags.
// 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |X| Last-Stream-ID (31) | // +-+-------------------------------------------------------------+ // | Error Code (32) | // +---------------------------------------------------------------+ // // The last stream identifier in the GOAWAY frame contains the highest numbered stream identifier // for which the sender of the GOAWAY frame has received frames on and might have taken some action // on. // // The GOAWAY frame also contains a 32-bit error code (see Error Codes) that contains the reason for // closing the connection.
Serializer.GOAWAY = function writeGoaway(frame, buffers) { var buffer = Buffer.alloc(8);
Deserializer.GOAWAY = function readGoaway(buffer, frame) { if (buffer.length !== 8) { // GOAWAY must have 8 bytes return'FRAME_SIZE_ERROR';
}
frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff;
frame.error = errorCodes[buffer.readUInt32BE(4)]; if (!frame.error) { // Unknown error types are to be considered equivalent to INTERNAL ERROR
frame.error = 'INTERNAL_ERROR';
}
};
// [WINDOW_UPDATE](https://tools.ietf.org/html/rfc7540#section-6.9) // ----------------------------------------------------------------- // // The WINDOW_UPDATE frame (type=0x8) is used to implement flow control. // // The WINDOW_UPDATE frame does not define any flags.
// The payload of a WINDOW_UPDATE frame is a 32-bit value indicating the additional number of bytes // that the sender can transmit in addition to the existing flow control window. The legal range // for this field is 1 to 2^31 - 1 (0x7fffffff) bytes; the most significant bit of this value is // reserved.
Serializer.WINDOW_UPDATE = function writeWindowUpdate(frame, buffers) { var buffer = Buffer.alloc(4);
Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) { if (buffer.length !== WINDOW_UPDATE_PAYLOAD_SIZE) { return'FRAME_SIZE_ERROR';
}
frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff; if (frame.window_size === 0) { return'PROTOCOL_ERROR';
}
};
// [CONTINUATION](https://tools.ietf.org/html/rfc7540#section-6.10) // ------------------------------------------------------------ // // The CONTINUATION frame (type=0x9) is used to continue a sequence of header block fragments. // // The CONTINUATION frame defines the following flag: // // * END_HEADERS (0x4): // The END_HEADERS bit indicates that this frame ends the sequence of header block fragments // necessary to provide a complete set of headers.
Serializer.CONTINUATION = function writeContinuation(frame, buffers) {
buffers.push(frame.data);
};
Deserializer.CONTINUATION = function readContinuation(buffer, frame) {
frame.data = buffer;
};
// [ALTSVC](https://tools.ietf.org/html/rfc7838#section-4) // ------------------------------------------------------------ // // The ALTSVC frame (type=0xA) advertises the availability of an alternative service to the client. // // The ALTSVC frame does not define any flags.
frameTypes[0xA] = 'ALTSVC';
frameFlags.ALTSVC = [];
// 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Origin-Len (16) | Origin? (*) ... // +-------------------------------+----------------+--------------+ // | Alt-Svc-Field-Value (*) ... // +---------------------------------------------------------------+ // // The ALTSVC frame contains the following fields: // // Origin-Len: An unsigned, 16-bit integer indicating the length, in // octets, of the Origin field. // // Origin: An OPTIONAL sequence of characters containing ASCII // serialisation of an origin ([RFC6454](https://tools.ietf.org/html/rfc6454), // Section 6.2) that the alternate service is applicable to. // // Alt-Svc-Field-Value: A sequence of octets (length determined by // subtracting the length of all preceding fields from the frame // length) containing a value identical to the Alt-Svc field value // defined in (Section 3)[https://tools.ietf.org/html/rfc7838#section-3] // (ABNF production "Alt-Svc").
function istchar(c) { return ('!#$&\'*+-.^_`|~1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.indexOf(c) > -1);
}
function hexencode(s) { var t = ''; for (var i = 0; i < s.length; i++) { if (!istchar(s[i])) {
t += '%';
t += Buffer.from(s[i]).toString('hex');
} else {
t += s[i];
}
} return t;
}
Serializer.ALTSVC = function writeAltSvc(frame, buffers) { var buffer = Buffer.alloc(2);
buffer.writeUInt16BE(frame.origin.length, 0);
buffers.push(buffer);
buffers.push(Buffer.from(frame.origin, 'ascii'));
var fieldValue = hexencode(frame.protocolID) + '="' + frame.host + ':' + frame.port + '"'; if (frame.maxAge !== 86400) { // 86400 is the default
fieldValue += "; ma=" + frame.maxAge;
}
var name = stripquotes(nvpair.substring(0, eq).trim()); var value = stripquotes(nvpair.substring(eq + 1).trim()); return {'name': name, 'value': value};
}
function splitHeaderParameters(hv) { return parseHeaderValue(hv, ';', splitNameValue);
}
function parseHeaderValue(hv, separator, callback) { var start = 0; var inQuotes = false; var values = [];
for (var i = 0; i < hv.length; i++) { if (hv[i] === '"') {
inQuotes = !inQuotes; continue;
} if (inQuotes) { // Just skip this continue;
} if (hv[i] === separator) { var newValue = hv.substring(start, i).trim(); if (newValue.length > 0) {
newValue = callback(newValue);
values.push(newValue);
}
start = i + 1;
}
}
var newValue = hv.substring(start).trim(); if (newValue.length > 0) {
newValue = callback(newValue);
values.push(newValue);
}
return values;
}
function rsplit(s, delim, count) { var nsplits = 0; var end = s.length; var rval = []; for (var i = s.length - 1; i >= 0; i--) { if (s[i] === delim) { var t = s.substring(i + 1, end);
end = i;
rval.unshift(t);
nsplits++; if (nsplits === count) { break;
}
}
} if (end !== 0) {
rval.unshift(s.substring(0, end));
} return rval;
}
function ishex(c) { return ('0123456789ABCDEFabcdef'.indexOf(c) > -1);
}
function unescape(s) { var i = 0; var t = ''; while (i < s.length) { if (s[i] != '%' || !ishex(s[i + 1]) || !ishex(s[i + 2])) {
t += s[i];
} else {
++i; var hexvalue = ''; if (i < s.length) {
hexvalue += s[i];
++i;
} if (i < s.length) {
hexvalue += s[i];
} if (hexvalue.length > 0) {
t += Buffer.from(hexvalue, 'hex').toString();
} else {
t += '%';
}
}
++i;
} return t;
}
Deserializer.ALTSVC = function readAltSvc(buffer, frame) { if (buffer.length < 2) { return'FRAME_SIZE_ERROR';
} var originLength = buffer.readUInt16BE(0); if ((buffer.length - 2) < originLength) { return'FRAME_SIZE_ERROR';
}
frame.origin = buffer.toString('ascii', 2, 2 + originLength); var fieldValue = buffer.toString('ascii', 2 + originLength); var values = parseHeaderValue(fieldValue, ',', splitHeaderParameters); if (values.length > 1) { // TODO - warn that we only use one here
} if (values.length === 0) { // Well that's a malformed frame. Just ignore it. return;
}
var chosenAltSvc = values[0];
frame.maxAge = 86400; // Default for (var i = 0; i < chosenAltSvc.length; i++) { if (i === 0) { // This corresponds to the protocolID="<host>:<port>" item
frame.protocolID = unescape(chosenAltSvc[i].name); var hostport = rsplit(chosenAltSvc[i].value, ':', 1);
frame.host = hostport[0];
frame.port = parseInt(hostport[1], 10);
} elseif (chosenAltSvc[i].name == 'ma') {
frame.maxAge = parseInt(chosenAltSvc[i].value, 10);
} // Otherwise, we just ignore this
}
};
// frame 0xB was BLOCKED and some versions of chrome will // throw PROTOCOL_ERROR upon seeing it with non 0 payload
// * `frame` serializer: it transforms data attributes from Buffers to hex strings and filters out // flags that are not present. var frameCounter = 0;
exports.serializers.frame = function(frame) { if (!frame) { returnnull;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.