Socket
Socket
Sign inDemoInstall

http2

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

http2 - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

doc/compressor.html

502

lib/framer.js

@@ -5,6 +5,6 @@ // The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]:

// [2]: http://nodejs.org/api/stream.html#stream_new_stream_readable_options
var Transform = require('stream').Transform
var Transform = require('stream').Transform;
exports.Serializer = Serializer
exports.Deserializer = Deserializer
exports.Serializer = Serializer;
exports.Deserializer = Deserializer;

@@ -14,34 +14,47 @@ // Serializer

//
// Frame Objects +-----------------------------+ Buffers
// * * * * * * * ---> | Serializer Transform Stream | ---> * * * *
// +-----------------------------+
// Frame Objects
// * * * * * * * --+---------------------------
// | |
// v v Buffers
// [] -----> Payload Ser. --[buffers]--> Header Ser. --> * * * *
// empty adds payload adds header
// array buffers buffer
function Serializer() {
Transform.call(this, { objectMode: true })
Transform.call(this, { objectMode: true });
}
Serializer.prototype = Object.create(Transform.prototype, { constructor: { value: Serializer } })
Serializer.prototype = Object.create(Transform.prototype, { constructor: { value: Serializer } });
// When there's an incoming frame object, it first generates the frame type specific part of the
// frame (payload), and then then header part which holds fields that are common to all frame types
// (like the length of the payload).
// 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) {
var payload = Serializer[frame.type](frame)
frame.length = payload.length
var header = Serializer.commonHeader(frame)
if (!(frame.type in Serializer)) {
throw new Error('Unknown frame type: ' + frame.type);
}
this.push(header)
this.push(payload)
done()
}
var buffers = [];
Serializer[frame.type](frame, buffers);
Serializer.commonHeader(frame, buffers);
for (var i = 0; i < buffers.length; i++) {
this.push(buffers[i]);
}
done();
};
// Deserializer
// ------------
//
// Buffers +-------------------------------+ Frame Objects
// * * * * ---> | Deserializer Transform Stream | ---> * * * * * * *
// +-------------------------------+
// Buffers
// * * * * --------+-------------------------
// | |
// v v Frame Objects
// {} -----> Header Des. --{frame}--> Payload Des. --> * * * * * * *
// empty adds parsed adds parsed
// object header properties payload properties
function Deserializer() {
Transform.call(this, { objectMode: true })
this._next(8)
Transform.call(this, { objectMode: true });
this._next(8);
}

@@ -56,6 +69,9 @@ Deserializer.prototype = Object.create(Transform.prototype, { constructor: { value: Deserializer } })

Deserializer.prototype._next = function(size) {
this._cursor = 0
this._buffer = new Buffer(size)
this._waiting_for_header = !this._waiting_for_header
}
this._cursor = 0;
this._buffer = new Buffer(size);
this._waiting_for_header = !this._waiting_for_header;
if (this._waiting_for_header) {
this._frame = {};
}
};

@@ -65,3 +81,3 @@ // Parsing an incoming buffer is an iterative process because it can hold multiple frames if it's

Deserializer.prototype._transform = function _transform(chunk, encoding, done) {
var cursor = 0
var cursor = 0;

@@ -71,6 +87,6 @@ while(cursor < chunk.length) {

// chunk, then only a part of it is copied.
var to_copy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor)
chunk.copy(this._buffer, this._cursor, cursor, cursor + to_copy)
this._cursor += to_copy
cursor += to_copy
var to_copy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor);
chunk.copy(this._buffer, this._cursor, cursor, cursor + to_copy);
this._cursor += to_copy;
cursor += to_copy;

@@ -83,16 +99,17 @@ // When `_buffer` is full, it's content gets parsed either as header or payload depending on

// deserializer waits for the specified length payload.
this._header = Deserializer.commonHeader(this._buffer)
this._next(this._header.length)
Deserializer.commonHeader(this._buffer, this._frame);
this._next(this._frame.length);
} else {
// If it's payload then the the frame object is assembled and then gets pushed out.
// If it's payload then the the frame object is finalized and then gets pushed out.
// Unknown frame types are ignored.
if (this._header.type) {
var frame = Deserializer[this._header.type](this._buffer)
frame.type = this._header.type
frame.flags = this._header.flags
frame.stream = this._header.stream
this.push(frame)
if (this._frame.type) {
try {
Deserializer[this._frame.type](this._buffer, this._frame);
this.push(this._frame);
} catch(error) {
this.emit('error', error);
}
}
this._next(8)
this._next(8);
}

@@ -102,4 +119,4 @@ }

done()
}
done();
};

@@ -148,49 +165,54 @@ // [Frame Header](http://http2.github.io/http2-spec/#FrameHeader)

var frame_types = []
var frame_types = [];
var frame_flags = {}
var frame_flags = {};
Serializer.commonHeader = function writeCommonHeader(frame) {
var data = new Buffer(8)
Serializer.commonHeader = function writeCommonHeader(frame, buffers) {
var header_buffer = new Buffer(8);
if (frame.length > 65535) throw new Error('Too large frame: ' + frame.length + ' bytes')
data.writeUInt16BE(frame.length, 0)
var size = 0;
for (var i = 0; i < buffers.length; i++) size += buffers[i].length;
if (size > 65535) {
throw new Error('Too large frame: ' + size + ' bytes');
}
header_buffer.writeUInt16BE(size, 0);
var type_id = frame_types.indexOf(frame.type)
if (type_id === -1) throw new Error('Unknown frame type: ' + frame.type)
data.writeUInt8(type_id, 2)
var type_id = frame_types.indexOf(frame.type); // If we are here then the type is valid for sure
header_buffer.writeUInt8(type_id, 2);
var flag_byte = 0
var flag_byte = 0;
for (var flag in frame.flags) {
var position = frame_flags[frame.type].indexOf(flag)
if (position === -1) throw new Error('Unknown flag for frame type ' + frame.type + ': ' + flag)
if (frame.flags[flag]) flag_byte |= (1 << position)
var position = frame_flags[frame.type].indexOf(flag);
if (position === -1) {
throw new Error('Unknown flag for frame type ' + frame.type + ': ' + flag);
}
if (frame.flags[flag]) {
flag_byte |= (1 << position);
}
}
data.writeUInt8(flag_byte, 3)
header_buffer.writeUInt8(flag_byte, 3);
if (frame.stream > 0x7fffffff) throw new Error('Too large stream ID: ' + frame.stream)
data.writeUInt32BE(frame.stream || 0, 4)
if (frame.stream > 0x7fffffff) {
throw new Error('Too large stream ID: ' + frame.stream);
}
header_buffer.writeUInt32BE(frame.stream || 0, 4);
return data
}
buffers.unshift(header_buffer);
};
Deserializer.commonHeader = function readCommonHeader(buffer) {
var frame = {}
Deserializer.commonHeader = function readCommonHeader(buffer, frame) {
frame.length = buffer.readUInt16BE(0);
frame.length = buffer.readUInt16BE(0)
frame.type = frame_types[buffer.readUInt8(2)];
frame.type = frame_types[buffer.readUInt8(2)]
frame.flags = {}
var flag_byte = buffer.readUInt8(3)
var defined_flags = frame_flags[frame.type]
frame.flags = {};
var flag_byte = buffer.readUInt8(3);
var defined_flags = frame_flags[frame.type];
for (var i = 0; i < defined_flags.length; i++) {
frame.flags[defined_flags[i]] = Boolean(flag_byte & (1 << i))
frame.flags[defined_flags[i]] = Boolean(flag_byte & (1 << i));
}
frame.stream = buffer.readUInt32BE(4) & 0x7fffffff
frame.stream = buffer.readUInt32BE(4) & 0x7fffffff;
};
return frame
}
// Frame types

@@ -205,27 +227,44 @@ // ===========

//
// The DATA frame does not define any type-specific flags.
// 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.
// * RESERVED (0x2):
// Bit 2 is reserved for future use.
frame_types[0x0] = 'DATA'
frame_types[0x0] = 'DATA';
frame_flags['DATA'] = []
frame_flags.DATA = ['END_STREAM', 'RESERVED'];
Serializer['DATA'] = function writeData(frame) {
return frame.data
}
Serializer.DATA = function writeData(frame, buffers) {
buffers.push(frame.data);
};
Deserializer['DATA'] = function readData(buffer) {
return { data: buffer }
}
Deserializer.DATA = function readData(buffer, frame) {
frame.data = buffer;
};
// [HEADERS+PRIORITY](http://http2.github.io/http2-spec/#HEADERS)
// [HEADERS](http://http2.github.io/http2-spec/#HEADERS)
// --------------------------------------------------------------
//
// The HEADERS+PRIORITY frame (type=0x1) allows the sender to set header fields and stream priority
// at the same time.
// The HEADERS frame (type=0x1) allows the sender to create a stream.
//
// HEADERS+PRIORITY uses the same flags as the HEADERS frame.
// 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.
// * RESERVED (0x2):
// Bit 2 is reserved for future use.
// * END_HEADERS (0x4):
// The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide
// a complete set of headers.
// * PRIORITY (0x8):
// Bit 4 being set indicates that the first four octets of this frame contain a single reserved
// bit and a 31-bit priority.
frame_types[0x1] = 'HEADERS+PRIORITY'
frame_types[0x1] = 'HEADERS';
frame_flags['HEADERS+PRIORITY'] = ['CONTINUES']
frame_flags.HEADERS = ['END_STREAM', 'RESERVED', 'END_HEADERS', 'PRIORITY'];

@@ -235,3 +274,3 @@ // 0 1 2 3

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |X| Priority (31) |
// |X| (Optional) Priority (31) |
// +-+-------------------------------------------------------------+

@@ -241,18 +280,21 @@ // | Header Block (*) ...

//
// The HEADERS+PRIORITY frame is identical to the HEADERS frame, preceded by a
// single reserved bit and a 31-bit priority.
// The payload of a HEADERS frame contains a Headers Block
Serializer['HEADERS+PRIORITY'] = function writeHeadersPriority(frame) {
var data = new Buffer(4 + frame.data.length)
data.writeUInt32BE(frame.priority & 0x7fffffff, 0)
frame.data.copy(data, 4)
return data
}
Serializer.HEADERS = function writeHeadersPriority(frame, buffers) {
if (frame.flags.PRIORITY) {
var buffer = new Buffer(4);
buffer.writeUInt32BE(frame.priority & 0x7fffffff, 0);
buffers.push(buffer);
}
buffers.push(frame.data);
};
Deserializer['HEADERS+PRIORITY'] = function readHeadersPriority(buffer) {
return {
priority: buffer.readUInt32BE(0) & 0x7fffffff,
data: buffer.slice(4)
Deserializer.HEADERS = function readHeadersPriority(buffer, frame) {
if (frame.flags.PRIORITY) {
frame.priority = buffer.readUInt32BE(0) & 0x7fffffff;
frame.data = buffer.slice(4);
} else {
frame.data = buffer;
}
}
};

@@ -266,5 +308,5 @@ // [PRIORITY](http://http2.github.io/http2-spec/#PRIORITY)

frame_types[0x2] = 'PRIORITY'
frame_types[0x2] = 'PRIORITY';
frame_flags['PRIORITY'] = []
frame_flags.PRIORITY = [];

@@ -279,13 +321,11 @@ // 0 1 2 3

Serializer['PRIORITY'] = function writePriority(frame) {
var data = new Buffer(4)
data.writeUInt32BE(frame.priority, 0)
return data
}
Serializer.PRIORITY = function writePriority(frame, buffers) {
var buffer = new Buffer(4);
buffer.writeUInt32BE(frame.priority, 0);
buffers.push(buffer);
};
Deserializer['PRIORITY'] = function readPriority(buffer) {
return {
priority: buffer.readUInt32BE(0)
}
}
Deserializer.PRIORITY = function readPriority(buffer, frame) {
frame.priority = buffer.readUInt32BE(0);
};

@@ -299,5 +339,5 @@ // [RST_STREAM](http://http2.github.io/http2-spec/#RST_STREAM)

frame_types[0x3] = 'RST_STREAM'
frame_types[0x3] = 'RST_STREAM';
frame_flags['RST_STREAM'] = []
frame_flags.RST_STREAM = [];

@@ -313,13 +353,11 @@ // 0 1 2 3

Serializer['RST_STREAM'] = function writeRstStream(frame) {
var data = new Buffer(4)
data.writeUInt32BE(error_codes.indexOf(frame.error), 0)
return data
}
Serializer.RST_STREAM = function writeRstStream(frame, buffers) {
var buffer = new Buffer(4);
buffer.writeUInt32BE(error_codes.indexOf(frame.error), 0);
buffers.push(buffer);
};
Deserializer['RST_STREAM'] = function readRstStream(buffer) {
return {
error: error_codes[buffer.readUInt32BE(0)]
}
}
Deserializer.RST_STREAM = function readRstStream(buffer, frame) {
frame.error = error_codes[buffer.readUInt32BE(0)];
};

@@ -332,11 +370,7 @@ // [SETTINGS](http://http2.github.io/http2-spec/#SETTINGS)

//
// The SETTINGS frame defines the following flag:
//
// * CLEAR_PERSISTED (0x2):
// Bit 2 being set indicates a request to clear any previously persisted settings before
// processing the settings.
// The SETTINGS frame does not define any flags.
frame_types[0x4] = 'SETTINGS'
frame_types[0x4] = 'SETTINGS';
frame_flags['SETTINGS'] = ['CLEAR_PERSISTED']
frame_flags.SETTINGS = [];

@@ -361,46 +395,53 @@ // The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of an

Serializer['SETTINGS'] = function writeSettings(frame) {
var settings = []
Serializer.SETTINGS = function writeSettings(frame, buffers) {
var settings = [], settings_left = Object.keys(frame.settings);
defined_settings.forEach(function(setting, id) {
if (setting.name in frame.settings) {
var value = frame.settings[setting.name]
settings.push({ id: id, value: setting.flag ? value & 0x1 : value })
settings_left.splice(settings_left.indexOf(setting.name), 1);
var value = frame.settings[setting.name];
settings.push({ id: id, value: setting.flag ? Boolean(value) : value });
}
})
});
if (settings_left.length !== 0) {
throw new Error('Unknown settings: ' + settings_left.join(', '))
}
var buffer = new Buffer(settings.length * 8)
var buffer = new Buffer(settings.length * 8);
for (var i = 0; i < settings.length; i++) {
buffer.writeUInt32BE(settings[i].id & 0xffffff, i*8)
buffer.writeUInt32BE(settings[i].value, i*8 + 4)
buffer.writeUInt32BE(settings[i].id & 0xffffff, i*8);
buffer.writeUInt32BE(settings[i].value, i*8 + 4);
}
return buffer
}
buffers.push(buffer);
};
Deserializer['SETTINGS'] = function readSettings(buffer) {
var settings = {}
Deserializer.SETTINGS = function readSettings(buffer, frame) {
frame.settings = {};
if (buffer.length % 8 !== 0) {
throw new Error('Invalid SETTINGS frame.');
}
for (var i = 0; i < buffer.length / 8; i++) {
var id = buffer.readUInt32BE(i*8) & 0xffffff
var setting = defined_settings[id]
var value = buffer.readUInt32BE(i*8 + 4)
if (setting.name in settings) continue
settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value
var id = buffer.readUInt32BE(i*8) & 0xffffff;
var setting = defined_settings[id];
var value = buffer.readUInt32BE(i*8 + 4);
if (!setting || setting.name in frame.settings) {
continue;
}
frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value;
}
return {
settings: settings
}
}
return frame;
};
// The following settings are defined:
var defined_settings = []
var defined_settings = [];
// * SETTINGS_MAX_CONCURRENT_STREAMS (4):
// indicates the maximum number of concurrent streams that the sender will allow.
defined_settings[4] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false }
defined_settings[4] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false };
// * SETTINGS_INITIAL_WINDOW_SIZE (7):
// indicates the sender's initial stream window size (in bytes) for new streams.
defined_settings[7] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false }
defined_settings[7] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false };

@@ -411,3 +452,3 @@ // * SETTINGS_FLOW_CONTROL_OPTIONS (10):

// bits are reserved.
defined_settings[10] = { name: 'SETTINGS_FLOW_CONTROL_OPTIONS', flag: true }
defined_settings[10] = { name: 'SETTINGS_FLOW_CONTROL_OPTIONS', flag: true };

@@ -420,7 +461,11 @@ // [PUSH_PROMISE](http://http2.github.io/http2-spec/#PUSH_PROMISE)

//
// PUSH_PROMISE uses the same flags as the HEADERS frame.
// The PUSH_PROMISE frame defines the following flags:
//
// * END_PUSH_PROMISE (0x1):
// The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to
// provide a complete set of headers.
frame_types[0x5] = 'PUSH_PROMISE'
frame_types[0x5] = 'PUSH_PROMISE';
frame_flags['PUSH_PROMISE'] = ['CONTINUES']
frame_flags.PUSH_PROMISE = ['END_PUSH_PROMISE'];

@@ -439,15 +484,13 @@ // 0 1 2 3

Serializer['PUSH_PROMISE'] = function writePushPromise(frame) {
var data = new Buffer(4 + frame.data.length)
data.writeUInt32BE(frame.promised_stream & 0x7fffffff, 0)
frame.data.copy(data, 4)
return data
}
Serializer.PUSH_PROMISE = function writePushPromise(frame, buffers) {
var buffer = new Buffer(4);
buffer.writeUInt32BE(frame.promised_stream & 0x7fffffff, 0);
buffers.push(buffer);
buffers.push(frame.data);
};
Deserializer['PUSH_PROMISE'] = function readPushPromise(buffer) {
return {
promised_stream: buffer.readUInt32BE(0) & 0x7fffffff,
data: buffer.slice(4)
}
}
Deserializer.PUSH_PROMISE = function readPushPromise(buffer, frame) {
frame.promised_stream = buffer.readUInt32BE(0) & 0x7fffffff;
frame.data = buffer.slice(4);
};

@@ -465,18 +508,20 @@ // [PING](http://http2.github.io/http2-spec/#PING)

frame_types[0x6] = 'PING'
frame_types[0x6] = 'PING';
frame_flags['PING'] = ['PONG']
frame_flags.PING = ['PONG'];
// In addition to the frame header, PING frames MUST contain 8 additional octets of opaque data.
Serializer['PING'] = function writePing(frame) {
var payload = frame.data
if (!payload || payload.length !== 8) throw new Error('PING frames must carry an 8 byte payload.')
return payload
Serializer.PING = function writePing(frame, buffers) {
if (!frame.data || frame.data.length !== 8) {
throw new Error('PING frames must carry an 8 byte payload.');
}
buffers.push(frame.data);
}
Deserializer['PING'] = function readPing(buffer) {
return {
data: buffer
Deserializer.PING = function readPing(buffer, frame) {
if (buffer.length !== 8) {
throw new Error('Invalid size PING frame.');
}
frame.data = buffer;
}

@@ -489,7 +534,7 @@

//
// The GOAWAY frame does not define any type-specific flags.
// The GOAWAY frame does not define any flags.
frame_types[0x7] = 'GOAWAY'
frame_types[0x7] = 'GOAWAY';
frame_flags['GOAWAY'] = []
frame_flags.GOAWAY = [];

@@ -511,41 +556,14 @@ // 0 1 2 3

Serializer['GOAWAY'] = function writeGoaway(frame) {
var data = new Buffer(8)
data.writeUInt32BE(frame.last_stream & 0x7fffffff, 0)
data.writeUInt32BE(error_codes.indexOf(frame.error), 4)
return data
}
Serializer.GOAWAY = function writeGoaway(frame, buffers) {
var buffer = new Buffer(8);
buffer.writeUInt32BE(frame.last_stream & 0x7fffffff, 0);
buffer.writeUInt32BE(error_codes.indexOf(frame.error), 4);
buffers.push(buffer);
};
Deserializer['GOAWAY'] = function readGoaway(buffer) {
return {
last_stream: buffer.readUInt32BE(0) & 0x7fffffff,
error: error_codes[buffer.readUInt32BE(4)]
}
}
Deserializer.GOAWAY = function readGoaway(buffer, frame) {
frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff;
frame.error = error_codes[buffer.readUInt32BE(4)];
};
// [HEADERS](http://http2.github.io/http2-spec/#HEADERS)
// -----------------------------------------------------
//
// The HEADERS frame (type=0x8) provides header fields for a stream.
//
// Additional type-specific flags for the HEADERS frame are:
//
// * CONTINUES (0x2):
// The CONTINUES bit indicates that this frame does not contain the entire payload necessary to
// provide a complete set of headers.
frame_types[0x8] = 'HEADERS'
frame_flags['HEADERS'] = ['CONTINUES']
// The payload of a HEADERS frame contains a Headers Block (Section 3.7).
Serializer['HEADERS'] = function writeHeaders(frame) {
return frame.data
}
Deserializer['HEADERS'] = function readHeaders(buffer) {
return { data: buffer }
}
// [WINDOW_UPDATE](http://http2.github.io/http2-spec/#WINDOW_UPDATE)

@@ -556,11 +574,11 @@ // -----------------------------------------------------------------

//
// The following additional flags are defined for the WINDOW_UPDATE frame:
// The WINDOW_UPDATE frame defines the following flags:
//
// * END_FLOW_CONTROL (0x2):
// Bit 2 being set indicates that flow control for the identified stream
// * END_FLOW_CONTROL (0x1):
// Bit 1 being set indicates that flow control for the identified stream
// or connection has been ended; subsequent frames do not need to be flow controlled.
frame_types[0x9] = 'WINDOW_UPDATE'
frame_types[0x9] = 'WINDOW_UPDATE';
frame_flags['WINDOW_UPDATE'] = ['END_FLOW_CONTROL']
frame_flags.WINDOW_UPDATE = ['END_FLOW_CONTROL'];

@@ -572,22 +590,12 @@ // The payload of a WINDOW_UPDATE frame is a 32-bit value indicating the additional number of bytes

Serializer['WINDOW_UPDATE'] = function writeWindowUpdate(frame) {
var data = new Buffer(4)
data.writeUInt32BE(frame.window_size & 0x7fffffff, 0)
return data
}
Serializer.WINDOW_UPDATE = function writeWindowUpdate(frame, buffers) {
var buffer = new Buffer(4);
buffer.writeUInt32BE(frame.window_size & 0x7fffffff, 0);
buffers.push(buffer);
};
Deserializer['WINDOW_UPDATE'] = function readWindowUpdate(buffer) {
return {
window_size: buffer.readUInt32BE(0) & 0x7fffffff
}
}
Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) {
frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff;
};
// Common Flags
// ------------
//
// The least significant bit (0x1) - the FINAL bit - is defined for all frame types as an indication
// that this frame is the last the endpoint will send for the identified stream.
for (var type in frame_flags) frame_flags[type].unshift('FINAL')
// [Error Codes](http://http2.github.io/http2-spec/#ErrorCodes)

@@ -607,2 +615,2 @@ // ------------------------------------------------------------

'COMPRESSION_ERROR'
]
];
{
"name": "http2",
"version": "0.0.1",
"version": "0.0.2",
"description": "An HTTP/2 server implementation",

@@ -30,4 +30,4 @@ "main": "index.js",

"author": "Gábor Molnár <gabor@molnar.es> (http://gabor.molnar.es)",
"license": "BSD",
"license": "MIT",
"readmeFilename": "README.md"
}
node-http2
==========
An HTTP/2 server implementation for node.js
An HTTP/2 server implementation for node.js, developed as a [Google Summer of Code project](https://google-melange.appspot.com/gsoc/project/google/gsoc2013/molnarg/5001).
Status
======
I post weekly status updates [on my blog](http://gabor.molnar.es/blog/categories/google-summer-of-code/). Short version: framing layer 70% ready.
Installation
============
Using npm:
```
npm install http2
```
Documentation
=============
The developer documentation is generated using [docco](http://jashkenas.github.io/docco/), and is located in the `doc` directory. API documentation is coming later.
Running the tests
=================
To run the tests, first install [mocha](http://visionmedia.github.io/mocha/) and [chai](http://visionmedia.github.io/mocha/) (`npm install mocha chai`) and then run `npm test`.
The tests are written in BDD style, so they are a good starting point to understand the code.
License
=======
The MIT License
Copyright (C) 2013 Gábor Molnár <gabor@molnar.es>

@@ -1,19 +0,18 @@

var expect = require('chai').expect
var expect = require('chai').expect;
var framer = require('../lib/framer')
, Serializer = framer.Serializer
, Deserializer = framer.Deserializer
, Deserializer = framer.Deserializer;
var frame_types = {
'DATA': ['data'],
'HEADERS+PRIORITY': ['priority', 'data'],
'PRIORITY': ['priority'],
'RST_STREAM': ['error'],
'SETTINGS': ['settings'],
'PUSH_PROMISE': ['promised_stream', 'data'],
'PING': ['data'],
'GOAWAY': ['last_stream', 'error'],
'HEADERS': ['data'],
'WINDOW_UPDATE': ['window_size']
}
DATA: ['data'],
HEADERS: ['priority', 'data'],
PRIORITY: ['priority'],
RST_STREAM: ['error'],
SETTINGS: ['settings'],
PUSH_PROMISE: ['promised_stream', 'data'],
PING: ['data'],
GOAWAY: ['last_stream', 'error'],
WINDOW_UPDATE: ['window_size']
};

@@ -23,3 +22,3 @@ var test_frames = [{

type: 'DATA',
flags: { 'FINAL': false },
flags: { END_STREAM: false, RESERVED: false },
stream: 10,

@@ -32,7 +31,19 @@ length: 4,

buffer: new Buffer('0004' + '00' + '00' + '0000000A' + '12345678', 'hex')
}, {
frame: {
type: 'HEADERS+PRIORITY',
flags: { 'FINAL': false, 'CONTINUES': false },
type: 'HEADERS',
flags: { END_STREAM: false, RESERVED: false, END_HEADERS: false, PRIORITY: false },
stream: 15,
length: 4,
data: new Buffer('12345678', 'hex')
},
buffer: new Buffer('0004' + '01' + '00' + '0000000F' + '12345678', 'hex')
}, {
frame: {
type: 'HEADERS',
flags: { END_STREAM: false, RESERVED: false, END_HEADERS: false, PRIORITY: true },
stream: 15,
length: 8,

@@ -43,7 +54,8 @@

},
buffer: new Buffer('0008' + '01' + '00' + '0000000F' + '00000003' + '12345678', 'hex')
buffer: new Buffer('0008' + '01' + '08' + '0000000F' + '00000003' + '12345678', 'hex')
}, {
frame: {
type: 'PRIORITY',
flags: { 'FINAL': false },
flags: { },
stream: 10,

@@ -55,6 +67,7 @@ length: 4,

buffer: new Buffer('0004' + '02' + '00' + '0000000A' + '00000003', 'hex')
}, {
frame: {
type: 'RST_STREAM',
flags: { 'FINAL': false },
flags: { },
stream: 10,

@@ -66,6 +79,7 @@ length: 4,

buffer: new Buffer('0004' + '03' + '00' + '0000000A' + '00000002', 'hex')
}, {
frame: {
type: 'SETTINGS',
flags: { 'FINAL': false, 'CLEAR_PERSISTED': false },
flags: { },
stream: 10,

@@ -83,6 +97,7 @@ length: 24,

'00' + '00000A' + '00000001', 'hex')
}, {
frame: {
type: 'PUSH_PROMISE',
flags: { 'FINAL': false, 'CONTINUES': false },
flags: { END_PUSH_PROMISE: false },
stream: 15,

@@ -95,6 +110,7 @@ length: 8,

buffer: new Buffer('0008' + '05' + '00' + '0000000F' + '00000003' + '12345678', 'hex')
}, {
frame: {
type: 'PING',
flags: { 'FINAL': false, 'PONG': false },
flags: { PONG: false },
stream: 15,

@@ -106,6 +122,7 @@ length: 8,

buffer: new Buffer('0008' + '06' + '00' + '0000000F' + '1234567887654321', 'hex')
}, {
frame: {
type: 'GOAWAY',
flags: { 'FINAL': false },
flags: { },
stream: 10,

@@ -118,16 +135,7 @@ length: 8,

buffer: new Buffer('0008' + '07' + '00' + '0000000A' + '12345678' + '00000001', 'hex')
}, {
frame: {
type: 'HEADERS',
flags: { 'FINAL': false, 'CONTINUES': false },
stream: 10,
length: 4,
data: new Buffer('12345678', 'hex')
},
buffer: new Buffer('0004' + '08' + '00' + '0000000A' + '12345678', 'hex')
}, {
frame: {
type: 'WINDOW_UPDATE',
flags: { 'FINAL': false, 'END_FLOW_CONTROL': false },
flags: { END_FLOW_CONTROL: false },
stream: 10,

@@ -139,10 +147,17 @@ length: 4,

buffer: new Buffer('0004' + '09' + '00' + '0000000A' + '12345678', 'hex')
}]
}];
// Concatenate two buffer into a new buffer
function concat(buffer1, buffer2) {
var concatenated = new Buffer(buffer1.length + buffer2.length)
buffer1.copy(concatenated)
buffer2.copy(concatenated, buffer1.length)
return concatenated
function concat(buffers) {
var size = 0;
for (var i = 0; i < buffers.length; i++) {
size += buffers[i].length;
}
var concatenated = new Buffer(size);
for (var cursor = 0, j = 0; j < buffers.length; cursor += buffers[j].length, j++) {
buffers[j].copy(concatenated, cursor);
}
return concatenated;
}

@@ -152,60 +167,65 @@

function shuffle_buffers(buffers) {
var concatenated = new Buffer(0)
for (var i = 0; i < buffers.length; i++) concatenated = concat(concatenated, buffers[i])
var concatenated = concat(buffers), output = [], written = 0;
var output = []
var written = 0
while (written < concatenated.length) {
var chunk_size = Math.min(concatenated.length - written, Math.ceil(Math.random()*20))
output.push(concatenated.slice(written, written + chunk_size))
written += chunk_size
var chunk_size = Math.min(concatenated.length - written, Math.ceil(Math.random()*20));
output.push(concatenated.slice(written, written + chunk_size));
written += chunk_size;
}
return output
return output;
}
describe('Framer', function() {
describe('framer.js', function() {
describe('Serializer', function() {
describe('static method .commonHeader({ length, type, flags, stream })', function() {
it('should return the appropriate 8 byte header buffer', function() {
describe('static method .commonHeader({ type, flags, stream }, buffer_array)', function() {
it('should add the appropriate 8 byte header buffer in front of the others', function() {
for (var i = 0; i < test_frames.length; i++) {
var test = test_frames[i]
expect(Serializer.commonHeader(test.frame)).to.deep.equal(test.buffer.slice(0,8))
, buffers = [test.buffer.slice(8)]
, header_buffer = test.buffer.slice(0,8);
Serializer.commonHeader(test.frame, buffers);
expect(buffers[0]).to.deep.equal(header_buffer);
}
})
})
});
});
Object.keys(frame_types).forEach(function(type) {
var tests = test_frames.filter(function(test) { return test.frame.type === type })
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'
describe('static method [\'' + type + '\'](' + frame_shape + ')', function() {
it('should return a ' + type + ' type payload buffer', function() {
var tests = test_frames.filter(function(test) { return test.frame.type === type });
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }';
describe('static method .' + type + '(' + frame_shape + ', buffer_array)', function() {
it('should push buffers to the array that make up a ' + type + ' type payload', function() {
for (var i = 0; i < tests.length; i++) {
var test = tests[i]
expect(Serializer[type](test.frame)).to.deep.equal(test.buffer.slice(8))
, buffers = [];
Serializer[type](test.frame, buffers);
expect(concat(buffers)).to.deep.equal(test.buffer.slice(8));
}
})
})
})
});
});
});
describe('transform stream', function() {
it('should transform frame objects to appropriate buffers', function() {
var stream = new Serializer()
var stream = new Serializer();
for (var i = 0; i < test_frames.length; i++) {
var test = test_frames[i]
stream.write(test.frame)
var chunk, buffer = new Buffer(0)
while (chunk = stream.read()) buffer = concat(buffer, chunk)
expect(buffer).to.be.deep.equal(test.buffer)
var test = test_frames[i];
stream.write(test.frame);
var chunk, buffer = new Buffer(0);
while (chunk = stream.read()) {
buffer = concat([buffer, chunk]);
}
expect(buffer).to.be.deep.equal(test.buffer);
}
})
})
})
});
});
});
describe('Deserializer', function() {
describe('static method .commonHeader(header_buffer)', function() {
it('should return the appropriate header object', function() {
describe('static method .commonHeader(header_buffer, frame)', function() {
it('should augment the frame object with these properties: { length, type, flags, stream })', function() {
for (var i = 0; i < test_frames.length; i++) {
var test = test_frames[i]
expect(Deserializer.commonHeader(test.buffer.slice(0,8))).to.deep.equal({
var test = test_frames[i], frame = {};
Deserializer.commonHeader(test.buffer.slice(0,8), frame);
expect(frame).to.deep.equal({
length: test.frame.length,

@@ -215,66 +235,42 @@ type: test.frame.type,

stream: test.frame.stream
})
});
}
})
})
});
});
Object.keys(frame_types).forEach(function(type) {
var tests = test_frames.filter(function(test) { return test.frame.type === type })
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'
describe('static method [\'' + type + '\'](payload_buffer)', function() {
it('should return the parsed frame object with these properties: ' + frame_shape, function() {
var tests = test_frames.filter(function(test) { return test.frame.type === type });
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }';
describe('static method .' + type + '(payload_buffer, frame)', function() {
it('should augment the frame object with these properties: ' + frame_shape, function() {
for (var i = 0; i < tests.length; i++) {
var test = tests[i]
var parsed = Deserializer[type](test.buffer.slice(8))
parsed.length = test.frame.length
parsed.type = test.frame.type
parsed.flags = test.frame.flags
parsed.stream = test.frame.stream
expect(parsed).to.deep.equal(test.frame)
var test = tests[i];
var frame = {
length: test.frame.length,
type: test.frame.type,
flags: test.frame.flags,
stream: test.frame.stream
};
Deserializer[type](test.buffer.slice(8), frame);
expect(frame).to.deep.equal(test.frame);
}
})
})
})
});
});
});
describe('transform stream', function() {
it('should transform buffers to appropriate frame object', function() {
var stream = new Deserializer()
var stream = new Deserializer();
shuffle_buffers(test_frames.map(function(test) { return test.buffer }))
.forEach(stream.write.bind(stream))
.forEach(stream.write.bind(stream));
for (var j = 0; j < test_frames.length; j++) {
var parsed_frame = stream.read()
parsed_frame.length = test_frames[j].frame.length
expect(parsed_frame).to.be.deep.equal(test_frames[j].frame)
var parsed_frame = stream.read();
parsed_frame.length = test_frames[j].frame.length;
expect(parsed_frame).to.be.deep.equal(test_frames[j].frame);
}
})
})
})
describe('invariant', function() {
describe('header === Deserializer.commonHeader(Serializer.commonHeader(header))', function() {
it('should always be true for well formed header objects', function() {
for (var i = 0; i < test_frames.length; i++) {
var frame = test_frames[i].frame
var header = {
length: frame.length,
type: frame.type,
flags: frame.flags,
stream: frame.stream
}
expect(Deserializer.commonHeader(Serializer.commonHeader(header))).to.deep.equal(header)
}
})
})
describe('buffer === Serializer.commonHeader(Deserializer.commonHeader(buffer))', function() {
it('should always be true for well formed header buffers', function() {
for (var i = 0; i < test_frames.length; i++) {
var buffer = test_frames[i].buffer.slice(0,8)
expect(Serializer.commonHeader(Deserializer.commonHeader(buffer))).to.deep.equal(buffer)
}
})
})
})
})
});
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc