Comparing version 1.1.0 to 1.2.0
@@ -16,2 +16,4 @@ /** | ||
ca: fs.readFileSync(__dirname + '/keys/twitlog-csr.pem'), | ||
ciphers: '!aNULL:!ADH:!eNull:!LOW:!EXP:RC4+RSA:MEDIUM:HIGH', | ||
maxStreams: 15 | ||
}; | ||
@@ -18,0 +20,0 @@ |
!function() { | ||
var socket = io.connect(), | ||
container = $('#tweets'), | ||
tweets = []; | ||
container = document.getElementById('tweets'), | ||
tweets = [], | ||
first; | ||
@@ -9,3 +10,3 @@ socket.on('tweet', function(tweet) { | ||
// Remove first tweet | ||
tweets.shift().remove(); | ||
container.removeChild(tweets.shift()); | ||
} | ||
@@ -16,22 +17,37 @@ tweets.push(createTweet(tweet)); | ||
function createTweet(tweet) { | ||
var elem = $('<article class="tweet" />'); | ||
var elem = document.createElement('article'), | ||
avatar = document.createElement('img'), | ||
textContainer = document.createElement('div'), | ||
text = document.createElement('div'), | ||
clear = document.createElement('div'); | ||
elem.append( | ||
$('<img class="avatar"/>').attr({ | ||
src: tweet.user.image, | ||
title: tweet.user.name, | ||
alt: tweet.user.name | ||
}) | ||
avatar.className = 'avatar'; | ||
avatar.src = tweet.user.image.replace( | ||
/^http:\/\/a2\.twimg\.com\//, | ||
'//twimg0-a.akamaihd.net/' | ||
); | ||
avatar.title = avatar.alt = tweet.user.name; | ||
elem.append( | ||
$('<div class=text-container />').html( | ||
$('<div class=text />').html(tweet.text) | ||
) | ||
); | ||
elem.append('<div class=clear />'); | ||
container.prepend(elem); | ||
text.className = 'text'; | ||
text.innerHTML = tweet.text; | ||
clear.className = 'clear'; | ||
textContainer.className = 'text-container'; | ||
textContainer.appendChild(text); | ||
textContainer.appendChild(clear); | ||
elem.className = 'tweet'; | ||
elem.appendChild(avatar); | ||
elem.appendChild(textContainer); | ||
if (first) { | ||
container.insertBefore(elem, first); | ||
} else { | ||
container.appendChild(elem); | ||
} | ||
first = elem; | ||
return elem; | ||
}; | ||
}(); |
@@ -10,9 +10,16 @@ var twitter = new (require('ntwitter'))({ | ||
var tweets = []; | ||
twitter.search('#spdytwitlog', function(err, result) { | ||
if (err) return console.error(err); | ||
result.results.sort(function(a, b) { | ||
return (+new Date(a.created_at)) - | ||
(+new Date(b.created_at)); | ||
}).forEach(receive); | ||
}); | ||
twitter.search( | ||
'#spdytwitlog', | ||
{ | ||
result_type: 'recent', | ||
include_entities: 1 | ||
}, | ||
function(err, result) { | ||
if (err) return console.error(err); | ||
result.results.sort(function(a, b) { | ||
return (+new Date(a.created_at)) - | ||
(+new Date(b.created_at)); | ||
}).forEach(receive); | ||
} | ||
); | ||
@@ -37,5 +44,18 @@ function watchStream(method, query) { | ||
} | ||
watchStream('statuses/filter', { track: '#spdytwitlog' }); | ||
watchStream('statuses/filter', { | ||
track: '#spdytwitlog', | ||
include_entities: 1 | ||
}); | ||
function receive(tweet) { | ||
if (tweet.entities && tweet.entities.urls) { | ||
tweet.entities.urls.sort(function(a, b) { | ||
return b.indices[0] - a.indices[0]; | ||
}).forEach(function(url) { | ||
tweet.text = tweet.text.slice(0, url.indices[0]) + | ||
url.display_url.link(url.expanded_url || url.url) + | ||
tweet.text.slice(url.indices[1]); | ||
}); | ||
} | ||
tweet = { | ||
@@ -42,0 +62,0 @@ text: tweet.text, |
@@ -7,4 +7,4 @@ var spdy = exports; | ||
// Export parser&framer | ||
spdy.framer = require('./spdy/framer'); | ||
spdy.parser = require('./spdy/parser'); | ||
spdy.framer = require('./spdy/framer'); | ||
@@ -11,0 +11,0 @@ // Export ServerResponse |
@@ -6,5 +6,26 @@ var framer = exports; | ||
// | ||
// ### function execute (connection, header, body, callback) | ||
// #### @connection {Connection} SPDY connection | ||
// ### function Framer (inflate, deflate) | ||
// #### @inflate {zlib.Inflate} Inflate stream | ||
// #### @deflate {zlib.Deflate} Deflate stream | ||
// Framer constructor | ||
// | ||
function Framer(inflate, deflate) { | ||
this.inflate = inflate; | ||
this.deflate = deflate; | ||
}; | ||
// | ||
// ### function create (inflate, deflate) | ||
// #### @inflate {zlib.Inflate} Inflate stream | ||
// #### @deflate {zlib.Deflate} Deflate stream | ||
// Framer constructor wrapper | ||
// | ||
framer.create = function create(inflate, deflate) { | ||
return new Framer(inflate, deflate); | ||
}; | ||
// | ||
// ### function execute (header, body, callback) | ||
// #### @header {Object} Frame headers | ||
@@ -15,3 +36,3 @@ // #### @body {Buffer} Frame's body | ||
// | ||
framer.execute = function execute(connection, header, body, callback) { | ||
Framer.prototype.execute = function execute(header, body, callback) { | ||
// Data frame | ||
@@ -32,3 +53,2 @@ if (!header.control) { | ||
var syn_stream = header.type === 0x01, | ||
inflate = connection.inflate, | ||
id = body.readUInt32BE(0) & 0x7fffffff, | ||
@@ -39,3 +59,3 @@ associated = syn_stream ? body.readUInt32BE(4) & 0x7fffffff : 0, | ||
body = body.slice(syn_stream ? 10 : 6); | ||
spdy.utils.zstream(inflate, body, function(err, chunks, length) { | ||
spdy.utils.zstream(this.inflate, body, function(err, chunks, length) { | ||
var pairs = new Buffer(length); | ||
@@ -100,14 +120,14 @@ for (var i = 0, offset = 0; i < chunks.length; i++) { | ||
// | ||
// ### function sendSynReply (stream, code, reason, headers) | ||
// #### @stream {Stream} SPDY Stream | ||
// ### function replyFrame (id, code, reason, headers, callback) | ||
// #### @id {Number} Stream ID | ||
// #### @code {Number} HTTP Status Code | ||
// #### @reason {String} (optional) | ||
// #### @headers {Object|Array} (optional) HTTP headers | ||
// #### @callback {Function} Continuation function | ||
// Sends SYN_REPLY frame | ||
// | ||
framer.sendSynReply = function sendSynReply(stream, code, reason, headers) { | ||
// Do not send data to new connections after GOAWAY | ||
if (stream.connection.goaway && stream.id > stream.connection.goaway) return; | ||
var pairs = headers || {}, | ||
Framer.prototype.replyFrame = function replyFrame(id, code, reason, headers, | ||
callback) { | ||
var self = this, | ||
pairs = headers || {}, | ||
size = 2; | ||
@@ -154,23 +174,16 @@ | ||
stream.lock(function() { | ||
// Compress headers | ||
spdy.utils.zstream(stream.deflate, pairsBuf, function (err, chunks, size) { | ||
var header = new Buffer(14); | ||
header.writeUInt16BE(0x8002, 0); // Control + Version | ||
header.writeUInt16BE(0x0002, 2); // SYN_REPLY | ||
header.writeUInt32BE((size + 6) & 0x00ffffff, 4); // No flag support | ||
// Compress headers | ||
spdy.utils.zstream(self.deflate, pairsBuf, function (err, chunks, size) { | ||
if (err) return callback(err); | ||
// Write body start | ||
header.writeUInt32BE(stream.id & 0x7fffffff, 8); // Stream-ID | ||
header.writeUInt16BE(0, 12); // Unused | ||
var header = new Buffer(14); | ||
header.writeUInt16BE(0x8002, 0); // Control + Version | ||
header.writeUInt16BE(0x0002, 2); // SYN_REPLY | ||
header.writeUInt32BE((size + 6) & 0x00ffffff, 4); // No flag support | ||
stream.connection.write(header); | ||
// Write body start | ||
header.writeUInt32BE(id & 0x7fffffff, 8); // Stream-ID | ||
header.writeUInt16BE(0, 12); // Unused | ||
// Write body end | ||
chunks.forEach(function(chunk) { | ||
stream.connection.write(chunk); | ||
}); | ||
stream.unlock(); | ||
}); | ||
callback(null, [header].concat(chunks)); | ||
}); | ||
@@ -180,4 +193,4 @@ }; | ||
// | ||
// ### function sendData (stream, fin, data) | ||
// #### @stream {Stream} SPDY Stream | ||
// ### function dataFrame (id, fin, data) | ||
// #### @id {Number} Stream id | ||
// #### @fin {Bool} Is this data frame last frame | ||
@@ -187,30 +200,19 @@ // #### @data {Buffer} Response data | ||
// | ||
framer.sendData = function sendData(stream, fin, data) { | ||
// Do not send data to new connections after GOAWAY | ||
if (stream.connection.goaway && stream.id > stream.connection.goaway) return; | ||
Framer.prototype.dataFrame = function dataFrame(id, fin, data) { | ||
if (!fin && data.length === 0) return []; | ||
var header = new Buffer(8); | ||
if (!fin && data.length === 0) return; | ||
stream.lock(function() { | ||
var header = new Buffer(8); | ||
header.writeUInt32BE(id & 0x7fffffff, 0); | ||
header.writeUInt32BE(data.length & 0x00ffffff, 4); | ||
header.writeUInt8(fin ? 0x01 : 0x0, 4); | ||
header.writeUInt32BE(stream.id & 0x7fffffff, 0); | ||
header.writeUInt32BE(data.length & 0x00ffffff, 4); | ||
header.writeUInt8(fin ? 0x01 : 0x0, 4); | ||
stream.connection.write(header); | ||
if (data.length > 0) stream.connection.write(data); | ||
if (fin) stream.close(); | ||
stream.unlock(); | ||
}); | ||
return data.length > 0 ? [header, data] : [header]; | ||
}; | ||
// | ||
// ### function sendPing (connection, id) | ||
// #### @connection {Connection} SPDY Connection | ||
// ### function pingFrame (id) | ||
// #### @id {Buffer} Ping ID | ||
// Sends PING frame | ||
// | ||
framer.sendPing = function sendPing(connection, id) { | ||
Framer.prototype.pingFrame = function pingFrame(id) { | ||
var header = new Buffer(12); | ||
@@ -222,20 +224,51 @@ | ||
connection.write(header); | ||
return header; | ||
}; | ||
// | ||
// ### function sendRst (stream, code) | ||
// #### @stream {Stream} SPDY Stream | ||
// ### function rstFrame (id, code) | ||
// #### @id {Number} Stream ID | ||
// #### @code {NUmber} RST Code | ||
// Sends PING frame | ||
// | ||
framer.sendRst = function sendRst(stream, code) { | ||
var header = new Buffer(16); | ||
Framer.prototype.rstFrame = function rstFrame(id, code) { | ||
var header; | ||
header.writeUInt32BE(0x80020003, 0); // Version and type | ||
header.writeUInt32BE(0x00000008, 4); // Length | ||
header.writeUInt32BE(stream.id & 0x7fffffff, 8); // Stream ID | ||
header.writeUInt32BE(code, 12); // Status Code | ||
if (!(header = Framer.rstCache[code])) { | ||
header = new Buffer(16); | ||
stream.write(header); | ||
header.writeUInt32BE(0x80020003, 0); // Version and type | ||
header.writeUInt32BE(0x00000008, 4); // Length | ||
header.writeUInt32BE(id & 0x7fffffff, 8); // Stream ID | ||
header.writeUInt32BE(code, 12); // Status Code | ||
Framer.rstCache[code] = header; | ||
} | ||
return header; | ||
}; | ||
Framer.rstCache = {}; | ||
// | ||
// ### function maxStreamsFrame (count) | ||
// #### @count {Number} Max Concurrent Streams count | ||
// Sends SETTINGS frame with MAX_CONCURRENT_STREAMS | ||
// | ||
Framer.prototype.maxStreamsFrame = function maxStreamsFrame(count) { | ||
var settings; | ||
if (!(settings = Framer.settingsCache[count])) { | ||
settings = new Buffer(20); | ||
settings.writeUInt32BE(0x80020004, 0); // Version and type | ||
settings.writeUInt32BE(0x0000000C, 4); // length | ||
settings.writeUInt32BE(0x00000001, 8); // Count of entries | ||
settings.writeUInt32LE(0x01000004, 12); // Entry ID and Persist flag | ||
settings.writeUInt32BE(count, 16); // 100 Streams | ||
Framer.settingsCache[count] = settings; | ||
} | ||
return settings; | ||
}; | ||
Framer.settingsCache = {}; |
@@ -9,7 +9,8 @@ var parser = exports; | ||
// | ||
// ### function Parser (connection) | ||
// #### @connection {Connection} SPDY connection | ||
// ### function Parser (inflate, framer) | ||
// #### @inflate {zlib.Inflate} Inflate stream | ||
// #### @framer {spdy.Framer} Framer instance | ||
// SPDY protocol frames parser's @constructor | ||
// | ||
function Parser(connection) { | ||
function Parser(inflate, framer) { | ||
stream.Stream.call(this); | ||
@@ -23,3 +24,4 @@ | ||
this.state = { type: 'frame-head' }; | ||
this.connection = connection; | ||
this.inflate = inflate; | ||
this.framer = framer; | ||
@@ -31,8 +33,9 @@ this.readable = this.writable = true; | ||
// | ||
// ### function create (connection) | ||
// #### @connection {Connection} SPDY connection | ||
// ### function create (inflate, framer) | ||
// #### @inflate {zlib.Inflate} Inflate stream | ||
// #### @framer {spdy.Framer} Framer instance | ||
// @constructor wrapper | ||
// | ||
parser.create = function create(connection) { | ||
return new Parser(connection); | ||
parser.create = function create(inflate, framer) { | ||
return new Parser(inflate, framer); | ||
}; | ||
@@ -91,3 +94,3 @@ | ||
this.paused = true; | ||
this.execute(this.connection, this.state, buffer, function (err, waiting) { | ||
this.execute(this.state, buffer, function (err, waiting) { | ||
// And unpause once execution finished | ||
@@ -116,4 +119,3 @@ self.paused = false; | ||
// | ||
// ### function execute (connection, state, data, callback) | ||
// #### @connection {Connection} SPDY connection | ||
// ### function execute (state, data, callback) | ||
// #### @state {Object} Parser's state | ||
@@ -124,3 +126,3 @@ // #### @data {Buffer} Incoming data | ||
// | ||
Parser.prototype.execute = function execute(connection, state, data, callback) { | ||
Parser.prototype.execute = function execute(state, data, callback) { | ||
if (state.type === 'frame-head') { | ||
@@ -148,3 +150,3 @@ var header = state.header = { | ||
spdy.framer.execute(connection, state.header, data, function(err, frame) { | ||
this.framer.execute(state.header, data, function(err, frame) { | ||
if (err) return callback(err); | ||
@@ -151,0 +153,0 @@ |
@@ -48,3 +48,22 @@ var spdy = require('../spdy'), | ||
spdy.framer.sendSynReply(this.socket, statusCode, reasonPhrase, headers); | ||
// Do not send data to new connections after GOAWAY | ||
if (this.socket.isGoaway()) return; | ||
this.socket.lock(function() { | ||
var socket = this; | ||
this.framer.replyFrame( | ||
this.id, | ||
statusCode, | ||
reasonPhrase, | ||
headers, | ||
function (err, chunks) { | ||
// TODO: Handle err | ||
chunks.forEach(function(chunk) { | ||
socket.connection.write(chunk); | ||
}); | ||
socket.unlock(); | ||
} | ||
); | ||
}); | ||
}; |
@@ -22,2 +22,3 @@ var spdy = require('../spdy'), | ||
options.NPNProtocols = ['spdy/2', 'http/1.1', 'http/1.0'] | ||
options.maxStreams || (options.maxStreams = 100); | ||
@@ -41,3 +42,3 @@ HTTPSServer.call(this, options, requestListener); | ||
// Wrap incoming socket into abstract class | ||
var connection = new Connection(socket); | ||
var connection = new Connection(socket, options); | ||
@@ -49,4 +50,4 @@ // Emulate each stream like connection | ||
res.writeHead = spdy.response.writeHead; | ||
req.streamID = req.socket.id; | ||
req.isSpdy = true; | ||
res.streamID = req.streamID = req.socket.id; | ||
res.isSpdy = req.isSpdy = true; | ||
@@ -63,4 +64,6 @@ res.on('finish', function() { | ||
// Send SETTINGS frame (MAX_CONCURRENT_STREAMS = 100) | ||
connection.write(spdy.utils.settings); | ||
// Generate custom settings frame and send | ||
connection.write( | ||
connection.framer.maxStreamsFrame(options.maxStreams) | ||
); | ||
}); | ||
@@ -101,7 +104,8 @@ } | ||
// | ||
// ### function Connection (socket) | ||
// ### function Connection (socket, options) | ||
// #### @socket {net.Socket} server's connection | ||
// #### @options {Object} server's options | ||
// Abstract connection @constructor | ||
// | ||
function Connection(socket) { | ||
function Connection(socket, options) { | ||
process.EventEmitter.call(this); | ||
@@ -119,4 +123,7 @@ | ||
// Initialize framer | ||
this.framer = spdy.framer.create(this.inflate, this.deflate); | ||
// Initialize parser | ||
this.parser = spdy.parser.create(this); | ||
this.parser = spdy.parser.create(this.inflate, this.framer); | ||
this.parser.on('frame', function (frame) { | ||
@@ -131,5 +138,13 @@ var stream; | ||
self.emit('stream', stream); | ||
// If we reached stream limit | ||
if (self.streamsCount > options.maxStreams) { | ||
stream.once('error', function() {}); | ||
// REFUSED_STREAM | ||
stream.rstCode = 3; | ||
stream.destroy(true); | ||
} else { | ||
self.emit('stream', stream); | ||
stream.init(); | ||
stream.init(); | ||
} | ||
} else { | ||
@@ -169,3 +184,3 @@ if (frame.id) { | ||
} else if (frame.type === 'PING') { | ||
spdy.framer.sendPing(self, frame.pingId); | ||
self.write(self.framer.pingFrame(frame.pingId)); | ||
// Ignore SETTINGS for now | ||
@@ -198,2 +213,8 @@ } else if (frame.type === 'SETTINGS' || frame.type === 'NOOP') { | ||
// 2 minutes socket timeout | ||
this.socket.setTimeout(2 * 60 * 1000); | ||
this.socket.once('timeout', function() { | ||
socket.destroy(); | ||
}); | ||
this.socket.on('drain', function () { | ||
@@ -245,2 +266,3 @@ self.emit('drain'); | ||
this.connection = connection; | ||
this.framer = connection.framer; | ||
@@ -281,2 +303,10 @@ this.ondata = this.onend = null; | ||
// | ||
// ### function isGoaway () | ||
// Returns true if any writes to that stream should be ignored | ||
// | ||
Stream.prototype.isGoaway = function isGoaway() { | ||
return this.connection.goaway && this.id > this.connection.goaway; | ||
}; | ||
// | ||
// ### function init () | ||
@@ -316,3 +346,3 @@ // Initialize stream, internal | ||
this.locked = true; | ||
callback(null); | ||
callback.call(this, null); | ||
} | ||
@@ -369,3 +399,5 @@ }; | ||
if (error) { | ||
if (this.rstCode) spdy.framer.sendRst(this, this.rstCode); | ||
if (this.rstCode) { | ||
this.connection.write(this.framer.rstFrame(this.id, this.rstCode)); | ||
} | ||
} | ||
@@ -381,2 +413,23 @@ | ||
// | ||
// ### function _writeData (fin, buffer) | ||
// #### @fin {Boolean} | ||
// #### @buffer {Buffer} | ||
// Internal function | ||
// | ||
Stream.prototype._writeData = function _writeData(fin, buffer) { | ||
this.lock(function() { | ||
var stream = this, | ||
chunks = this.framer.dataFrame(this.id, fin, buffer); | ||
chunks.forEach(function(chunk) { | ||
stream.connection.write(chunk); | ||
}); | ||
if (fin) this.close(); | ||
this.unlock(); | ||
}); | ||
}; | ||
// | ||
// ### function write (data, encoding) | ||
@@ -390,2 +443,5 @@ // #### @data {Buffer|String} data | ||
// Do not send data to new connections after GOAWAY | ||
if (this.isGoaway()) return; | ||
// Send DATA | ||
@@ -398,3 +454,3 @@ if (typeof data === 'string') { | ||
spdy.framer.sendData(this, false, buffer); | ||
this._writeData(false, buffer); | ||
}; | ||
@@ -407,3 +463,6 @@ | ||
Stream.prototype.end = function end() { | ||
spdy.framer.sendData(this, true, new Buffer(0)); | ||
// Do not send data to new connections after GOAWAY | ||
if (this.isGoaway()) return; | ||
this._writeData(true, []); | ||
this.closedBy.server = true; | ||
@@ -410,0 +469,0 @@ this.handleClose(); |
@@ -89,9 +89,1 @@ var utils = exports; | ||
}; | ||
// Default SPDY connection settings | ||
var settings = utils.settings = new Buffer(20); | ||
settings.writeUInt32BE(0x80020004, 0); // Version and type | ||
settings.writeUInt32BE(0x0000000C, 4); // length | ||
settings.writeUInt32BE(0x00000001, 8); // Count of entries | ||
settings.writeUInt32LE(0x01000004, 12); // Entry ID and Persist flag | ||
settings.writeUInt32BE(0x00000064, 16); // 100 Streams |
{ | ||
"name": "spdy", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Implementation of the SPDY protocol on node.js.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -36,5 +36,5 @@ # SPDY Server for node.js [![Build Status](https://secure.travis-ci.org/indutny/node-spdy.png)](http://travis-ci.org/indutny/node-spdy) | ||
var options = { | ||
key: fs.readFileSync(__dirname + '/../keys/spdy-key.pem'), | ||
cert: fs.readFileSync(__dirname + '/../keys/spdy-cert.pem'), | ||
ca: fs.readFileSync(__dirname + '/../keys/spdy-csr.pem') | ||
key: fs.readFileSync(__dirname + '/keys/spdy-key.pem'), | ||
cert: fs.readFileSync(__dirname + '/keys/spdy-cert.pem'), | ||
ca: fs.readFileSync(__dirname + '/keys/spdy-csr.pem') | ||
}; | ||
@@ -50,6 +50,24 @@ | ||
## Helping project | ||
### API | ||
Node-spdy is open for donations, please feel free to contact me for any futher information: fedor@indutny.com | ||
API is compatible with `http` and `https` module, but you can use another | ||
function as base class for SPDYServer. For example, | ||
`require('express').HTTPSServer` given that as base class you'll get a server | ||
compatible with [express](https://github.com/visionmedia/express) API. | ||
```javascript | ||
spdy.createServer( | ||
[base class constructor, i.e. https.Server or express.HTTPSServer], | ||
{ /* keys and options */ }, // <- the only one required argument | ||
[request listener] | ||
).listen([port], [host], [callback]); | ||
``` | ||
Request listener will receive two arguments: `request` and `response`. They're | ||
both instances of `http`'s `IncomingMessage` and `OutgoingMessage`. But two | ||
custom properties are added to both of them: `streamID` and `isSpdy`. The first | ||
one indicates on which spdy stream are sitting request and response. Latter one | ||
is always true and can be checked to ensure that incoming request wasn't | ||
received by HTTPS callback. | ||
#### Contributors | ||
@@ -56,0 +74,0 @@ |
@@ -7,11 +7,15 @@ var assert = require('assert'), | ||
suite('A Framer of SPDY module', function() { | ||
var connection; | ||
var inflate, | ||
deflate, | ||
framer; | ||
setup(function() { | ||
connection = new spdy.server.Connection(new Stream()); | ||
inflate = spdy.utils.createInflate(); | ||
deflate = spdy.utils.createDeflate(); | ||
framer = spdy.framer.create(inflate, deflate); | ||
}); | ||
/* | ||
connection.deflate.on('data', function(b) {console.log(b)}); | ||
connection.deflate.write(new Buffer([ | ||
deflate.on('data', function(b) {console.log(b)}); | ||
deflate.write(new Buffer([ | ||
0x00, 0x02, // Number of name+value | ||
@@ -27,107 +31,154 @@ 0, 0x04, // Name length | ||
])); | ||
connection.deflate.flush(); | ||
deflate.flush(); | ||
*/ | ||
test('given a SYN_STREAM should return correct frame', function(done) { | ||
var body = new Buffer([ | ||
0x00, 0x00, 0x00, 0x01, // Stream ID | ||
0x00, 0x00, 0x00, 0x00, // Associated Stream ID | ||
0x00, 0x00, // Priority + Unused | ||
0x78, 0xbb, 0xdf, 0xa2, 0x51, 0xb2, // Deflated Name/Value pairs | ||
0x62, 0x60, 0x62, 0x60, 0x01, 0xe5, 0x12, | ||
0x06, 0x4e, 0x50, 0x50, 0xe6, 0x80, 0x99, | ||
0x6c, 0xc9, 0xa5, 0xc5, 0x25, 0xf9, 0xb9, | ||
0x0c, 0x8c, 0x86, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff | ||
]); | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 1, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'SYN_STREAM'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.associated, 0); | ||
assert.equal(frame.headers.host, 'localhost'); | ||
assert.equal(frame.headers.custom, '1'); | ||
done(); | ||
suite('frame parsing', function() { | ||
test('given a SYN_STREAM should return correct frame', function(done) { | ||
var body = new Buffer([ | ||
0x00, 0x00, 0x00, 0x01, // Stream ID | ||
0x00, 0x00, 0x00, 0x00, // Associated Stream ID | ||
0x00, 0x00, // Priority + Unused | ||
0x78, 0xbb, 0xdf, 0xa2, 0x51, 0xb2, // Deflated Name/Value pairs | ||
0x62, 0x60, 0x62, 0x60, 0x01, 0xe5, 0x12, | ||
0x06, 0x4e, 0x50, 0x50, 0xe6, 0x80, 0x99, | ||
0x6c, 0xc9, 0xa5, 0xc5, 0x25, 0xf9, 0xb9, | ||
0x0c, 0x8c, 0x86, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff | ||
]); | ||
framer.execute({ | ||
control: true, | ||
type: 1, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'SYN_STREAM'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.associated, 0); | ||
assert.equal(frame.headers.host, 'localhost'); | ||
assert.equal(frame.headers.custom, '1'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test('given a SYN_REPLY should return correct frame', function(done) { | ||
var body = new Buffer([ | ||
0x00, 0x00, 0x00, 0x01, // Stream ID | ||
0x00, 0x00, // Unused | ||
0x78, 0xbb, 0xdf, 0xa2, 0x51, 0xb2, // Deflated Name/Value pairs | ||
0x62, 0x60, 0x62, 0x60, 0x01, 0xe5, 0x12, | ||
0x06, 0x4e, 0x50, 0x50, 0xe6, 0x80, 0x99, | ||
0x6c, 0xc9, 0xa5, 0xc5, 0x25, 0xf9, 0xb9, | ||
0x0c, 0x8c, 0x86, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff | ||
]); | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 2, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'SYN_REPLY'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.headers.host, 'localhost'); | ||
assert.equal(frame.headers.custom, '1'); | ||
done(); | ||
test('given a SYN_REPLY should return correct frame', function(done) { | ||
var body = new Buffer([ | ||
0x00, 0x00, 0x00, 0x01, // Stream ID | ||
0x00, 0x00, // Unused | ||
0x78, 0xbb, 0xdf, 0xa2, 0x51, 0xb2, // Deflated Name/Value pairs | ||
0x62, 0x60, 0x62, 0x60, 0x01, 0xe5, 0x12, | ||
0x06, 0x4e, 0x50, 0x50, 0xe6, 0x80, 0x99, | ||
0x6c, 0xc9, 0xa5, 0xc5, 0x25, 0xf9, 0xb9, | ||
0x0c, 0x8c, 0x86, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff | ||
]); | ||
framer.execute({ | ||
control: true, | ||
type: 2, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'SYN_REPLY'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.headers.host, 'localhost'); | ||
assert.equal(frame.headers.custom, '1'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test('given a RST_STREAM should return correct frame', function(done) { | ||
var body = new Buffer([0, 0, 0, 1, 0, 0, 0, 2]); | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 3, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'RST_STREAM'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.status, 2); | ||
done(); | ||
test('given a RST_STREAM should return correct frame', function(done) { | ||
var body = new Buffer([0, 0, 0, 1, 0, 0, 0, 2]); | ||
framer.execute({ | ||
control: true, | ||
type: 3, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'RST_STREAM'); | ||
assert.equal(frame.id, 1); | ||
assert.equal(frame.status, 2); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test('given a NOOP frame should return correct frame', function(done) { | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 5, | ||
length: 0 | ||
}, new Buffer(0), function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'NOOP'); | ||
done(); | ||
test('given a NOOP frame should return correct frame', function(done) { | ||
framer.execute({ | ||
control: true, | ||
type: 5, | ||
length: 0 | ||
}, new Buffer(0), function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'NOOP'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test('given a PING frame should return correct frame', function(done) { | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 6, | ||
length: 0 | ||
}, new Buffer(0), function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'PING'); | ||
done(); | ||
test('given a PING frame should return correct frame', function(done) { | ||
framer.execute({ | ||
control: true, | ||
type: 6, | ||
length: 0 | ||
}, new Buffer(0), function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'PING'); | ||
done(); | ||
}); | ||
}); | ||
test('given a GOAWAY frame should return correct frame', function(done) { | ||
var body = new Buffer([0, 0, 0, 1]); | ||
framer.execute({ | ||
control: true, | ||
type: 7, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'GOAWAY'); | ||
assert.equal(frame.lastId, 1); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test('given a GOAWAY frame should return correct frame', function(done) { | ||
var body = new Buffer([0, 0, 0, 1]); | ||
spdy.framer.execute(connection, { | ||
control: true, | ||
type: 7, | ||
length: body.length | ||
}, body, function(err, frame) { | ||
assert.ok(!err); | ||
assert.equal(frame.type, 'GOAWAY'); | ||
assert.equal(frame.lastId, 1); | ||
done(); | ||
suite('frame generation', function() { | ||
test('.replyFrame() should generate correct frame', function(done) { | ||
framer.replyFrame(1, 200, 'ok', {}, function(err, chunks) { | ||
assert.equal(err, null); | ||
assert.ok(chunks.length > 1); | ||
done(); | ||
}); | ||
}); | ||
test('.dataFrame() w/o fin should generate correct frame', function() { | ||
var chunks = framer.dataFrame(1, false, new Buffer(123)); | ||
assert.equal(chunks[0][4], 0); | ||
assert.equal(chunks.length, 2); | ||
}); | ||
test('.dataFrame() with fin should generate correct frame', function() { | ||
var chunks = framer.dataFrame(1, true, new Buffer(123)); | ||
assert.equal(chunks[0][4], 1); | ||
assert.equal(chunks.length, 2); | ||
}); | ||
test('.pingFrame() should generate correct frame', function() { | ||
var frame = framer.pingFrame(new Buffer([0, 1, 2, 3])); | ||
assert.ok(frame.length > 0); | ||
}); | ||
test('.rstFrame() should generate correct frame', function() { | ||
var frame = framer.rstFrame(1, 2); | ||
assert.ok(frame.length > 0); | ||
// Verify that cache works | ||
var frame = framer.rstFrame(1, 2); | ||
assert.ok(frame.length > 0); | ||
}); | ||
test('.maxStreamsFrame() should generate correct frame', function() { | ||
var frame = framer.maxStreamsFrame(13); | ||
assert.ok(frame.length > 0); | ||
// Verify that cache works | ||
var frame = framer.maxStreamsFrame(13); | ||
assert.ok(frame.length > 0); | ||
}); | ||
}); | ||
}); |
@@ -6,10 +6,9 @@ var assert = require('assert'), | ||
suite('A Parser of SPDY module', function() { | ||
var parser, | ||
framer; | ||
var parser; | ||
setup(function() { | ||
parser = new spdy.parser.create(); | ||
framer = spdy.framer; | ||
spdy.framer = { | ||
execute: function(conn, header, data, callback) { | ||
var inflate = spdy.utils.createInflate(); | ||
parser = new spdy.parser.create(inflate, { | ||
execute: function(header, data, callback) { | ||
callback(null, { | ||
@@ -20,9 +19,5 @@ header: header, | ||
} | ||
}; | ||
}); | ||
}); | ||
teardown(function() { | ||
spdy.framer = framer; | ||
}); | ||
test('should wait for headers initially', function() { | ||
@@ -29,0 +24,0 @@ assert.equal(parser.waiting, 8); |
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
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
101
66029
1545