This commit is contained in:
lalBi94
2023-03-05 13:23:23 +01:00
commit 7bc56c09b5
14034 changed files with 1834369 additions and 0 deletions

25
node_modules/spdy-transport/lib/spdy-transport.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
'use strict'
var transport = exports
// Exports utils
transport.utils = require('./spdy-transport/utils')
// Export parser&framer
transport.protocol = {}
transport.protocol.base = require('./spdy-transport/protocol/base')
transport.protocol.spdy = require('./spdy-transport/protocol/spdy')
transport.protocol.http2 = require('./spdy-transport/protocol/http2')
// Window
transport.Window = require('./spdy-transport/window')
// Priority Tree
transport.Priority = require('./spdy-transport/priority')
// Export Connection and Stream
transport.Stream = require('./spdy-transport/stream').Stream
transport.Connection = require('./spdy-transport/connection').Connection
// Just for `transport.connection.create()`
transport.connection = transport.Connection

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
'use strict'
var transport = require('../spdy-transport')
var utils = transport.utils
var assert = require('assert')
var debug = require('debug')('spdy:priority')
function PriorityNode (tree, options) {
this.tree = tree
this.id = options.id
this.parent = options.parent
this.weight = options.weight
// To be calculated in `addChild`
this.priorityFrom = 0
this.priorityTo = 1
this.priority = 1
this.children = {
list: [],
weight: 0
}
if (this.parent !== null) {
this.parent.addChild(this)
}
}
function compareChildren (a, b) {
return a.weight === b.weight ? a.id - b.id : a.weight - b.weight
}
PriorityNode.prototype.toJSON = function toJSON () {
return {
parent: this.parent,
weight: this.weight,
exclusive: this.exclusive
}
}
PriorityNode.prototype.getPriority = function getPriority () {
return this.priority
}
PriorityNode.prototype.getPriorityRange = function getPriorityRange () {
return { from: this.priorityFrom, to: this.priorityTo }
}
PriorityNode.prototype.addChild = function addChild (child) {
child.parent = this
utils.binaryInsert(this.children.list, child, compareChildren)
this.children.weight += child.weight
this._updatePriority(this.priorityFrom, this.priorityTo)
}
PriorityNode.prototype.remove = function remove () {
assert(this.parent, 'Can\'t remove root node')
this.parent.removeChild(this)
this.tree._removeNode(this)
// Move all children to the parent
for (var i = 0; i < this.children.list.length; i++) {
this.parent.addChild(this.children.list[i])
}
}
PriorityNode.prototype.removeChild = function removeChild (child) {
this.children.weight -= child.weight
var index = utils.binarySearch(this.children.list, child, compareChildren)
if (index !== -1 && this.children.list.length >= index) {
this.children.list.splice(index, 1)
}
}
PriorityNode.prototype.removeChildren = function removeChildren () {
var children = this.children.list
this.children.list = []
this.children.weight = 0
return children
}
PriorityNode.prototype._updatePriority = function _updatePriority (from, to) {
this.priority = to - from
this.priorityFrom = from
this.priorityTo = to
var weight = 0
for (var i = 0; i < this.children.list.length; i++) {
var node = this.children.list[i]
var nextWeight = weight + node.weight
node._updatePriority(
from + this.priority * (weight / this.children.weight),
from + this.priority * (nextWeight / this.children.weight)
)
weight = nextWeight
}
}
function PriorityTree (options) {
this.map = {}
this.list = []
this.defaultWeight = options.defaultWeight || 16
this.count = 0
this.maxCount = options.maxCount
// Root
this.root = this.add({
id: 0,
parent: null,
weight: 1
})
}
module.exports = PriorityTree
PriorityTree.create = function create (options) {
return new PriorityTree(options)
}
PriorityTree.prototype.add = function add (options) {
if (options.id === options.parent) {
return this.addDefault(options.id)
}
var parent = options.parent === null ? null : this.map[options.parent]
if (parent === undefined) {
return this.addDefault(options.id)
}
debug('add node=%d parent=%d weight=%d exclusive=%d',
options.id,
options.parent === null ? -1 : options.parent,
options.weight || this.defaultWeight,
options.exclusive ? 1 : 0)
var children
if (options.exclusive) {
children = parent.removeChildren()
}
var node = new PriorityNode(this, {
id: options.id,
parent: parent,
weight: options.weight || this.defaultWeight
})
this.map[options.id] = node
if (options.exclusive) {
for (var i = 0; i < children.length; i++) {
node.addChild(children[i])
}
}
this.count++
if (this.count > this.maxCount) {
debug('hit maximum remove id=%d', this.list[0].id)
this.list.shift().remove()
}
// Root node is not subject to removal
if (node.parent !== null) {
this.list.push(node)
}
return node
}
// Only for testing, should use `node`'s methods
PriorityTree.prototype.get = function get (id) {
return this.map[id]
}
PriorityTree.prototype.addDefault = function addDefault (id) {
debug('creating default node')
return this.add({ id: id, parent: 0, weight: this.defaultWeight })
}
PriorityTree.prototype._removeNode = function _removeNode (node) {
delete this.map[node.id]
var index = utils.binarySearch(this.list, node, compareChildren)
this.list.splice(index, 1)
this.count--
}

View File

@@ -0,0 +1,4 @@
exports.DEFAULT_METHOD = 'GET'
exports.DEFAULT_HOST = 'localhost'
exports.MAX_PRIORITY_STREAMS = 100
exports.DEFAULT_MAX_CHUNK = 8 * 1024

View File

@@ -0,0 +1,58 @@
'use strict'
var util = require('util')
var transport = require('../../../spdy-transport')
var base = require('./')
var Scheduler = base.Scheduler
function Framer (options) {
Scheduler.call(this)
this.version = null
this.compress = null
this.window = options.window
this.timeout = options.timeout
// Wait for `enablePush`
this.pushEnabled = null
}
util.inherits(Framer, Scheduler)
module.exports = Framer
Framer.prototype.setVersion = function setVersion (version) {
this.version = version
this.emit('version')
}
Framer.prototype.setCompression = function setCompresion (pair) {
this.compress = new transport.utils.LockStream(pair.compress)
}
Framer.prototype.enablePush = function enablePush (enable) {
this.pushEnabled = enable
this.emit('_pushEnabled')
}
Framer.prototype._checkPush = function _checkPush (callback) {
if (this.pushEnabled === null) {
this.once('_pushEnabled', function () {
this._checkPush(callback)
})
return
}
var err = null
if (!this.pushEnabled) {
err = new Error('PUSH_PROMISE disabled by other side')
}
process.nextTick(function () {
return callback(err)
})
}
Framer.prototype._resetTimeout = function _resetTimeout () {
if (this.timeout) {
this.timeout.reset()
}
}

View File

@@ -0,0 +1,7 @@
'use strict'
exports.utils = require('./utils')
exports.constants = require('./constants')
exports.Scheduler = require('./scheduler')
exports.Parser = require('./parser')
exports.Framer = require('./framer')

View File

@@ -0,0 +1,106 @@
'use strict'
var transport = require('../../../spdy-transport')
var util = require('util')
var utils = require('./').utils
var OffsetBuffer = require('obuf')
var Transform = require('readable-stream').Transform
function Parser (options) {
Transform.call(this, {
readableObjectMode: true
})
this.buffer = new OffsetBuffer()
this.partial = false
this.waiting = 0
this.window = options.window
this.version = null
this.decompress = null
this.dead = false
}
module.exports = Parser
util.inherits(Parser, Transform)
Parser.prototype.error = utils.error
Parser.prototype.kill = function kill () {
this.dead = true
}
Parser.prototype._transform = function transform (data, encoding, cb) {
if (!this.dead) { this.buffer.push(data) }
this._consume(cb)
}
Parser.prototype._consume = function _consume (cb) {
var self = this
function next (err, frame) {
if (err) {
return cb(err)
}
if (Array.isArray(frame)) {
for (var i = 0; i < frame.length; i++) {
self.push(frame[i])
}
} else if (frame) {
self.push(frame)
}
// Consume more packets
if (!sync) {
return self._consume(cb)
}
process.nextTick(function () {
self._consume(cb)
})
}
if (this.dead) {
return cb()
}
if (this.buffer.size < this.waiting) {
// No data at all
if (this.buffer.size === 0) {
return cb()
}
// Partial DATA frame or something that we can process partially
if (this.partial) {
var partial = this.buffer.clone(this.buffer.size)
this.buffer.skip(partial.size)
this.waiting -= partial.size
this.executePartial(partial, next)
return
}
// We shall not do anything until we get all expected data
return cb()
}
var sync = true
var content = this.buffer.clone(this.waiting)
this.buffer.skip(this.waiting)
this.execute(content, next)
sync = false
}
Parser.prototype.setVersion = function setVersion (version) {
this.version = version
this.emit('version', version)
}
Parser.prototype.setCompression = function setCompresion (pair) {
this.decompress = new transport.utils.LockStream(pair.decompress)
}

View File

@@ -0,0 +1,216 @@
'use strict'
var transport = require('../../../spdy-transport')
var utils = transport.utils
var assert = require('assert')
var util = require('util')
var debug = require('debug')('spdy:scheduler')
var Readable = require('readable-stream').Readable
/*
* We create following structure in `pending`:
* [ [ id = 0 ], [ id = 1 ], [ id = 2 ], [ id = 0 ] ]
* chunks chunks chunks chunks
* chunks chunks
* chunks
*
* Then on the `.tick()` pass we pick one chunks from each item and remove the
* item if it is empty:
*
* [ [ id = 0 ], [ id = 2 ] ]
* chunks chunks
* chunks
*
* Writing out: chunks for 0, chunks for 1, chunks for 2, chunks for 0
*
* This way data is interleaved between the different streams.
*/
function Scheduler (options) {
Readable.call(this)
// Pretty big window by default
this.window = 0.25
if (options && options.window) { this.window = options.window }
this.sync = []
this.list = []
this.count = 0
this.pendingTick = false
}
util.inherits(Scheduler, Readable)
module.exports = Scheduler
// Just for testing, really
Scheduler.create = function create (options) {
return new Scheduler(options)
}
function insertCompare (a, b) {
return a.priority === b.priority
? a.stream - b.stream
: b.priority - a.priority
}
Scheduler.prototype.schedule = function schedule (data) {
var priority = data.priority
var stream = data.stream
var chunks = data.chunks
// Synchronous frames should not be interleaved
if (priority === false) {
debug('queue sync', chunks)
this.sync.push(data)
this.count += chunks.length
this._read()
return
}
debug('queue async priority=%d stream=%d', priority, stream, chunks)
var item = new SchedulerItem(stream, priority)
var index = utils.binaryLookup(this.list, item, insertCompare)
// Push new item
if (index >= this.list.length || insertCompare(this.list[index], item) !== 0) {
this.list.splice(index, 0, item)
} else { // Coalesce
item = this.list[index]
}
item.push(data)
this.count += chunks.length
this._read()
}
Scheduler.prototype._read = function _read () {
if (this.count === 0) {
return
}
if (this.pendingTick) {
return
}
this.pendingTick = true
var self = this
process.nextTick(function () {
self.pendingTick = false
self.tick()
})
}
Scheduler.prototype.tick = function tick () {
// No luck for async frames
if (!this.tickSync()) { return false }
return this.tickAsync()
}
Scheduler.prototype.tickSync = function tickSync () {
// Empty sync queue first
var sync = this.sync
var res = true
this.sync = []
for (var i = 0; i < sync.length; i++) {
var item = sync[i]
debug('tick sync pending=%d', this.count, item.chunks)
for (var j = 0; j < item.chunks.length; j++) {
this.count--
// TODO: handle stream backoff properly
try {
res = this.push(item.chunks[j])
} catch (err) {
this.emit('error', err)
return false
}
}
debug('after tick sync pending=%d', this.count)
// TODO(indutny): figure out the way to invoke callback on actual write
if (item.callback) {
item.callback(null)
}
}
return res
}
Scheduler.prototype.tickAsync = function tickAsync () {
var res = true
var list = this.list
if (list.length === 0) {
return res
}
var startPriority = list[0].priority
for (var index = 0; list.length > 0; index++) {
// Loop index
index %= list.length
if (startPriority - list[index].priority > this.window) { index = 0 }
debug('tick async index=%d start=%d', index, startPriority)
var current = list[index]
var item = current.shift()
if (current.isEmpty()) {
list.splice(index, 1)
if (index === 0 && list.length > 0) {
startPriority = list[0].priority
}
index--
}
debug('tick async pending=%d', this.count, item.chunks)
for (var i = 0; i < item.chunks.length; i++) {
this.count--
// TODO: handle stream backoff properly
try {
res = this.push(item.chunks[i])
} catch (err) {
this.emit('error', err)
return false
}
}
debug('after tick pending=%d', this.count)
// TODO(indutny): figure out the way to invoke callback on actual write
if (item.callback) {
item.callback(null)
}
if (!res) { break }
}
return res
}
Scheduler.prototype.dump = function dump () {
this.tickSync()
// Write everything out
while (!this.tickAsync()) {
// Intentional no-op
}
assert.strictEqual(this.count, 0)
}
function SchedulerItem (stream, priority) {
this.stream = stream
this.priority = priority
this.queue = []
}
SchedulerItem.prototype.push = function push (chunks) {
this.queue.push(chunks)
}
SchedulerItem.prototype.shift = function shift () {
return this.queue.shift()
}
SchedulerItem.prototype.isEmpty = function isEmpty () {
return this.queue.length === 0
}

View File

@@ -0,0 +1,94 @@
'use strict'
var utils = exports
var util = require('util')
function ProtocolError (code, message) {
this.code = code
this.message = message
}
util.inherits(ProtocolError, Error)
utils.ProtocolError = ProtocolError
utils.error = function error (code, message) {
return new ProtocolError(code, message)
}
utils.reverse = function reverse (object) {
var result = []
Object.keys(object).forEach(function (key) {
result[object[key] | 0] = key
})
return result
}
// weight [1, 36] <=> priority [0, 7]
// This way weight=16 is preserved and has priority=3
utils.weightToPriority = function weightToPriority (weight) {
return ((Math.min(35, (weight - 1)) / 35) * 7) | 0
}
utils.priorityToWeight = function priorityToWeight (priority) {
return (((priority / 7) * 35) | 0) + 1
}
// Copy-Paste from node
exports.addHeaderLine = function addHeaderLine (field, value, dest) {
field = field.toLowerCase()
if (/^:/.test(field)) {
dest[field] = value
return
}
switch (field) {
// Array headers:
case 'set-cookie':
if (dest[field] !== undefined) {
dest[field].push(value)
} else {
dest[field] = [ value ]
}
break
/* eslint-disable max-len */
// list is taken from:
/* eslint-enable max-len */
case 'content-type':
case 'content-length':
case 'user-agent':
case 'referer':
case 'host':
case 'authorization':
case 'proxy-authorization':
case 'if-modified-since':
case 'if-unmodified-since':
case 'from':
case 'location':
case 'max-forwards':
// drop duplicates
if (dest[field] === undefined) {
dest[field] = value
}
break
case 'cookie':
// make semicolon-separated list
if (dest[field] !== undefined) {
dest[field] += '; ' + value
} else {
dest[field] = value
}
break
default:
// make comma-separated list
if (dest[field] !== undefined) {
dest[field] += ', ' + value
} else {
dest[field] = value
}
}
}

View File

@@ -0,0 +1,93 @@
'use strict'
var transport = require('../../../spdy-transport')
var base = transport.protocol.base
exports.PREFACE_SIZE = 24
exports.PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
exports.PREFACE_BUFFER = Buffer.from(exports.PREFACE)
exports.PING_OPAQUE_SIZE = 8
exports.FRAME_HEADER_SIZE = 9
exports.INITIAL_MAX_FRAME_SIZE = 16384
exports.ABSOLUTE_MAX_FRAME_SIZE = 16777215
exports.HEADER_TABLE_SIZE = 4096
exports.DEFAULT_MAX_HEADER_LIST_SIZE = 80 * 1024 // as in http_parser
exports.MAX_INITIAL_WINDOW_SIZE = 2147483647
exports.DEFAULT_WEIGHT = 16
exports.MAX_CONCURRENT_STREAMS = Infinity
exports.frameType = {
DATA: 0,
HEADERS: 1,
PRIORITY: 2,
RST_STREAM: 3,
SETTINGS: 4,
PUSH_PROMISE: 5,
PING: 6,
GOAWAY: 7,
WINDOW_UPDATE: 8,
CONTINUATION: 9,
// Custom
X_FORWARDED_FOR: 0xde
}
exports.flags = {
ACK: 0x01, // SETTINGS-only
END_STREAM: 0x01,
END_HEADERS: 0x04,
PADDED: 0x08,
PRIORITY: 0x20
}
exports.settings = {
SETTINGS_HEADER_TABLE_SIZE: 0x01,
SETTINGS_ENABLE_PUSH: 0x02,
SETTINGS_MAX_CONCURRENT_STREAMS: 0x03,
SETTINGS_INITIAL_WINDOW_SIZE: 0x04,
SETTINGS_MAX_FRAME_SIZE: 0x05,
SETTINGS_MAX_HEADER_LIST_SIZE: 0x06
}
exports.settingsIndex = [
null,
'header_table_size',
'enable_push',
'max_concurrent_streams',
'initial_window_size',
'max_frame_size',
'max_header_list_size'
]
exports.error = {
OK: 0,
NO_ERROR: 0,
PROTOCOL_ERROR: 1,
INTERNAL_ERROR: 2,
FLOW_CONTROL_ERROR: 3,
SETTINGS_TIMEOUT: 4,
STREAM_CLOSED: 5,
INVALID_STREAM: 5,
FRAME_SIZE_ERROR: 6,
REFUSED_STREAM: 7,
CANCEL: 8,
COMPRESSION_ERROR: 9,
CONNECT_ERROR: 10,
ENHANCE_YOUR_CALM: 11,
INADEQUATE_SECURITY: 12,
HTTP_1_1_REQUIRED: 13
}
exports.errorByCode = base.utils.reverse(exports.error)
exports.DEFAULT_WINDOW = 64 * 1024 - 1
exports.goaway = exports.error
exports.goawayByCode = Object.assign({}, exports.errorByCode)
exports.goawayByCode[0] = 'OK'

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
'use strict'
var constants = require('./').constants
var hpack = require('hpack.js')
function Pool () {
}
module.exports = Pool
Pool.create = function create () {
return new Pool()
}
Pool.prototype.get = function get (version) {
var options = {
table: {
maxSize: constants.HEADER_TABLE_SIZE
}
}
var compress = hpack.compressor.create(options)
var decompress = hpack.decompressor.create(options)
return {
version: version,
compress: compress,
decompress: decompress
}
}
Pool.prototype.put = function put () {
}

View File

@@ -0,0 +1,8 @@
'use strict'
exports.name = 'h2'
exports.constants = require('./constants')
exports.parser = require('./parser')
exports.framer = require('./framer')
exports.compressionPool = require('./hpack-pool')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
'use strict'
var transport = require('../../../spdy-transport')
var base = transport.protocol.base
exports.FRAME_HEADER_SIZE = 8
exports.PING_OPAQUE_SIZE = 4
exports.MAX_CONCURRENT_STREAMS = Infinity
exports.DEFAULT_MAX_HEADER_LIST_SIZE = Infinity
exports.DEFAULT_WEIGHT = 16
exports.frameType = {
SYN_STREAM: 1,
SYN_REPLY: 2,
RST_STREAM: 3,
SETTINGS: 4,
PING: 6,
GOAWAY: 7,
HEADERS: 8,
WINDOW_UPDATE: 9,
// Custom
X_FORWARDED_FOR: 0xf000
}
exports.flags = {
FLAG_FIN: 0x01,
FLAG_COMPRESSED: 0x02,
FLAG_UNIDIRECTIONAL: 0x02
}
exports.error = {
PROTOCOL_ERROR: 1,
INVALID_STREAM: 2,
REFUSED_STREAM: 3,
UNSUPPORTED_VERSION: 4,
CANCEL: 5,
INTERNAL_ERROR: 6,
FLOW_CONTROL_ERROR: 7,
STREAM_IN_USE: 8,
// STREAM_ALREADY_CLOSED: 9
STREAM_CLOSED: 9,
INVALID_CREDENTIALS: 10,
FRAME_TOO_LARGE: 11
}
exports.errorByCode = base.utils.reverse(exports.error)
exports.settings = {
FLAG_SETTINGS_PERSIST_VALUE: 1,
FLAG_SETTINGS_PERSISTED: 2,
SETTINGS_UPLOAD_BANDWIDTH: 1,
SETTINGS_DOWNLOAD_BANDWIDTH: 2,
SETTINGS_ROUND_TRIP_TIME: 3,
SETTINGS_MAX_CONCURRENT_STREAMS: 4,
SETTINGS_CURRENT_CWND: 5,
SETTINGS_DOWNLOAD_RETRANS_RATE: 6,
SETTINGS_INITIAL_WINDOW_SIZE: 7,
SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: 8
}
exports.settingsIndex = [
null,
'upload_bandwidth',
'download_bandwidth',
'round_trip_time',
'max_concurrent_streams',
'current_cwnd',
'download_retrans_rate',
'initial_window_size',
'client_certificate_vector_size'
]
exports.DEFAULT_WINDOW = 64 * 1024
exports.MAX_INITIAL_WINDOW_SIZE = 2147483647
exports.goaway = {
OK: 0,
PROTOCOL_ERROR: 1,
INTERNAL_ERROR: 2
}
exports.goawayByCode = base.utils.reverse(exports.goaway)
exports.statusReason = {
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing', // RFC 2518, obsoleted by RFC 4918
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non-Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status', // RFC 4918
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Moved Temporarily',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
307: 'Temporary Redirect',
308: 'Permanent Redirect', // RFC 7238
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Time-out',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Request Entity Too Large',
414: 'Request-URI Too Large',
415: 'Unsupported Media Type',
416: 'Requested Range Not Satisfiable',
417: 'Expectation Failed',
418: 'I\'m a teapot', // RFC 2324
422: 'Unprocessable Entity', // RFC 4918
423: 'Locked', // RFC 4918
424: 'Failed Dependency', // RFC 4918
425: 'Unordered Collection', // RFC 4918
426: 'Upgrade Required', // RFC 2817
428: 'Precondition Required', // RFC 6585
429: 'Too Many Requests', // RFC 6585
431: 'Request Header Fields Too Large', // RFC 6585
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Time-out',
505: 'HTTP Version Not Supported',
506: 'Variant Also Negotiates', // RFC 2295
507: 'Insufficient Storage', // RFC 4918
509: 'Bandwidth Limit Exceeded',
510: 'Not Extended', // RFC 2774
511: 'Network Authentication Required' // RFC 6585
}

View File

@@ -0,0 +1,203 @@
'use strict'
var dictionary = {}
module.exports = dictionary
dictionary[2] = Buffer.from([
'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-',
'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi',
'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser',
'-agent10010120020120220320420520630030130230330430530630740040140240340440',
'5406407408409410411412413414415416417500501502503504505accept-rangesageeta',
'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic',
'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran',
'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati',
'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo',
'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe',
'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic',
'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1',
'.1statusversionurl\x00'
].join(''))
dictionary[3] = Buffer.from([
0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti
0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h
0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p
0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de
0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete....
0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace...
0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept.
0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse
0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc
0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding....
0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage.
0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges
0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age.
0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow
0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio
0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac
0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr
0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection
0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base
0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding....
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content-
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language
0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng
0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation..
0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5...
0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content
0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range..
0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type..
0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date..
0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag..
0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect
0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi
0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f
0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i
0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match.
0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo
0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s
0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince....
0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none-
0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match...
0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang
0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if-
0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi
0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since
0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last
0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie
0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation...
0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for
0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards...
0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma.
0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy
0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent
0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate...
0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion....
0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range...
0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer
0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr
0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after.
0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve
0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te.
0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail
0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e
0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding.
0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra
0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us
0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent
0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary
0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via.
0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni
0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww
0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen
0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate..
0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method
0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get.
0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu
0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200
0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion..
0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1
0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur
0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub
0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s
0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki
0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee
0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive.
0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi
0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012
0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205
0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030
0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043
0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307
0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540
0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084
0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411
0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341
0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164
0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504
0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N
0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho
0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative
0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa
0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204.
0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte
0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo
0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm
0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4
0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40
0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40
0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid
0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N
0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found
0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte
0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser
0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro
0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not
0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme
0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503.
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service.
0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila
0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F
0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A
0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J
0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A
0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept.
0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov.
0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0
0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon
0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W
0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu.
0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa
0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun..
0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk
0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text.
0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima
0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i
0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg
0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g
0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml
0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl
0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text
0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr
0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ
0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat
0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age
0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de
0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd
0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i
0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859-
0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-..
0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0.
])
dictionary[3.1] = dictionary[3]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
'use strict'
exports.name = 'spdy'
exports.dictionary = require('./dictionary')
exports.constants = require('./constants')
exports.parser = require('./parser')
exports.framer = require('./framer')
exports.compressionPool = require('./zlib-pool')

View File

@@ -0,0 +1,485 @@
'use strict'
var parser = exports
var transport = require('../../../spdy-transport')
var base = transport.protocol.base
var utils = base.utils
var constants = require('./constants')
var assert = require('assert')
var util = require('util')
var OffsetBuffer = require('obuf')
function Parser (options) {
base.Parser.call(this, options)
this.isServer = options.isServer
this.waiting = constants.FRAME_HEADER_SIZE
this.state = 'frame-head'
this.pendingHeader = null
}
util.inherits(Parser, base.Parser)
parser.create = function create (options) {
return new Parser(options)
}
Parser.prototype.setMaxFrameSize = function setMaxFrameSize (size) {
// http2-only
}
Parser.prototype.setMaxHeaderListSize = function setMaxHeaderListSize (size) {
// http2-only
}
// Only for testing
Parser.prototype.skipPreface = function skipPreface () {
}
Parser.prototype.execute = function execute (buffer, callback) {
if (this.state === 'frame-head') { return this.onFrameHead(buffer, callback) }
assert(this.state === 'frame-body' && this.pendingHeader !== null)
var self = this
var header = this.pendingHeader
this.pendingHeader = null
this.onFrameBody(header, buffer, function (err, frame) {
if (err) {
return callback(err)
}
self.state = 'frame-head'
self.waiting = constants.FRAME_HEADER_SIZE
self.partial = false
callback(null, frame)
})
}
Parser.prototype.executePartial = function executePartial (buffer, callback) {
var header = this.pendingHeader
if (this.window) {
this.window.recv.update(-buffer.size)
}
// DATA frame
callback(null, {
type: 'DATA',
id: header.id,
// Partial DATA can't be FIN
fin: false,
data: buffer.take(buffer.size)
})
}
Parser.prototype.onFrameHead = function onFrameHead (buffer, callback) {
var header = {
control: (buffer.peekUInt8() & 0x80) === 0x80,
version: null,
type: null,
id: null,
flags: null,
length: null
}
if (header.control) {
header.version = buffer.readUInt16BE() & 0x7fff
header.type = buffer.readUInt16BE()
} else {
header.id = buffer.readUInt32BE(0) & 0x7fffffff
}
header.flags = buffer.readUInt8()
header.length = buffer.readUInt24BE()
if (this.version === null && header.control) {
// TODO(indutny): do ProtocolError here and in the rest of errors
if (header.version !== 2 && header.version !== 3) {
return callback(new Error('Unsupported SPDY version: ' + header.version))
}
this.setVersion(header.version)
}
this.state = 'frame-body'
this.waiting = header.length
this.pendingHeader = header
this.partial = !header.control
callback(null, null)
}
Parser.prototype.onFrameBody = function onFrameBody (header, buffer, callback) {
// Data frame
if (!header.control) {
// Count received bytes
if (this.window) {
this.window.recv.update(-buffer.size)
}
// No support for compressed DATA
if ((header.flags & constants.flags.FLAG_COMPRESSED) !== 0) {
return callback(new Error('DATA compression not supported'))
}
if (header.id === 0) {
return callback(this.error(constants.error.PROTOCOL_ERROR,
'Invalid stream id for DATA'))
}
return callback(null, {
type: 'DATA',
id: header.id,
fin: (header.flags & constants.flags.FLAG_FIN) !== 0,
data: buffer.take(buffer.size)
})
}
if (header.type === 0x01 || header.type === 0x02) { // SYN_STREAM or SYN_REPLY
this.onSynHeadFrame(header.type, header.flags, buffer, callback)
} else if (header.type === 0x03) { // RST_STREAM
this.onRSTFrame(buffer, callback)
} else if (header.type === 0x04) { // SETTINGS
this.onSettingsFrame(buffer, callback)
} else if (header.type === 0x05) {
callback(null, { type: 'NOOP' })
} else if (header.type === 0x06) { // PING
this.onPingFrame(buffer, callback)
} else if (header.type === 0x07) { // GOAWAY
this.onGoawayFrame(buffer, callback)
} else if (header.type === 0x08) { // HEADERS
this.onHeaderFrames(buffer, callback)
} else if (header.type === 0x09) { // WINDOW_UPDATE
this.onWindowUpdateFrame(buffer, callback)
} else if (header.type === 0xf000) { // X-FORWARDED
this.onXForwardedFrame(buffer, callback)
} else {
callback(null, { type: 'unknown: ' + header.type })
}
}
Parser.prototype._filterHeader = function _filterHeader (headers, name) {
var res = {}
var keys = Object.keys(headers)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
if (key !== name) {
res[key] = headers[key]
}
}
return res
}
Parser.prototype.onSynHeadFrame = function onSynHeadFrame (type,
flags,
body,
callback) {
var self = this
var stream = type === 0x01
var offset = stream ? 10 : this.version === 2 ? 6 : 4
if (!body.has(offset)) {
return callback(new Error('SynHead OOB'))
}
var head = body.clone(offset)
body.skip(offset)
this.parseKVs(body, function (err, headers) {
if (err) {
return callback(err)
}
if (stream &&
(!headers[':method'] || !headers[':path'])) {
return callback(new Error('Missing `:method` and/or `:path` header'))
}
var id = head.readUInt32BE() & 0x7fffffff
if (id === 0) {
return callback(self.error(constants.error.PROTOCOL_ERROR,
'Invalid stream id for HEADERS'))
}
var associated = stream ? head.readUInt32BE() & 0x7fffffff : 0
var priority = stream
? head.readUInt8() >> 5
: utils.weightToPriority(constants.DEFAULT_WEIGHT)
var fin = (flags & constants.flags.FLAG_FIN) !== 0
var unidir = (flags & constants.flags.FLAG_UNIDIRECTIONAL) !== 0
var path = headers[':path']
var isPush = stream && associated !== 0
var weight = utils.priorityToWeight(priority)
var priorityInfo = {
weight: weight,
exclusive: false,
parent: 0
}
if (!isPush) {
callback(null, {
type: 'HEADERS',
id: id,
priority: priorityInfo,
fin: fin,
writable: !unidir,
headers: headers,
path: path
})
return
}
if (stream && !headers[':status']) {
return callback(new Error('Missing `:status` header'))
}
var filteredHeaders = self._filterHeader(headers, ':status')
callback(null, [ {
type: 'PUSH_PROMISE',
id: associated,
fin: false,
promisedId: id,
headers: filteredHeaders,
path: path
}, {
type: 'HEADERS',
id: id,
fin: fin,
priority: priorityInfo,
writable: true,
path: undefined,
headers: {
':status': headers[':status']
}
}])
})
}
Parser.prototype.onHeaderFrames = function onHeaderFrames (body, callback) {
var offset = this.version === 2 ? 6 : 4
if (!body.has(offset)) {
return callback(new Error('HEADERS OOB'))
}
var streamId = body.readUInt32BE() & 0x7fffffff
if (this.version === 2) { body.skip(2) }
this.parseKVs(body, function (err, headers) {
if (err) { return callback(err) }
callback(null, {
type: 'HEADERS',
priority: {
parent: 0,
exclusive: false,
weight: constants.DEFAULT_WEIGHT
},
id: streamId,
fin: false,
writable: true,
path: undefined,
headers: headers
})
})
}
Parser.prototype.parseKVs = function parseKVs (buffer, callback) {
var self = this
this.decompress.write(buffer.toChunks(), function (err, chunks) {
if (err) {
return callback(err)
}
var buffer = new OffsetBuffer()
for (var i = 0; i < chunks.length; i++) {
buffer.push(chunks[i])
}
var size = self.version === 2 ? 2 : 4
if (!buffer.has(size)) { return callback(new Error('KV OOB')) }
var count = self.version === 2
? buffer.readUInt16BE()
: buffer.readUInt32BE()
var headers = {}
function readString () {
if (!buffer.has(size)) { return null }
var len = self.version === 2
? buffer.readUInt16BE()
: buffer.readUInt32BE()
if (!buffer.has(len)) { return null }
var value = buffer.take(len)
return value.toString()
}
while (count > 0) {
var key = readString()
var value = readString()
if (key === null || value === null) {
return callback(new Error('Headers OOB'))
}
if (self.version < 3) {
var isInternal = /^(method|version|url|host|scheme|status)$/.test(key)
if (key === 'url') {
key = 'path'
}
if (isInternal) {
key = ':' + key
}
}
// Compatibility with HTTP2
if (key === ':status') {
value = value.split(/ /g, 2)[0]
}
count--
if (key === ':host') {
key = ':authority'
}
// Skip version, not present in HTTP2
if (key === ':version') {
continue
}
value = value.split(/\0/g)
for (var j = 0; j < value.length; j++) {
utils.addHeaderLine(key, value[j], headers)
}
}
callback(null, headers)
})
}
Parser.prototype.onRSTFrame = function onRSTFrame (body, callback) {
if (!body.has(8)) { return callback(new Error('RST OOB')) }
var frame = {
type: 'RST',
id: body.readUInt32BE() & 0x7fffffff,
code: constants.errorByCode[body.readUInt32BE()]
}
if (frame.id === 0) {
return callback(this.error(constants.error.PROTOCOL_ERROR,
'Invalid stream id for RST'))
}
if (body.size !== 0) {
frame.extra = body.take(body.size)
}
callback(null, frame)
}
Parser.prototype.onSettingsFrame = function onSettingsFrame (body, callback) {
if (!body.has(4)) {
return callback(new Error('SETTINGS OOB'))
}
var settings = {}
var number = body.readUInt32BE()
var idMap = {
1: 'upload_bandwidth',
2: 'download_bandwidth',
3: 'round_trip_time',
4: 'max_concurrent_streams',
5: 'current_cwnd',
6: 'download_retrans_rate',
7: 'initial_window_size',
8: 'client_certificate_vector_size'
}
if (!body.has(number * 8)) {
return callback(new Error('SETTINGS OOB#2'))
}
for (var i = 0; i < number; i++) {
var id = this.version === 2
? body.readUInt32LE()
: body.readUInt32BE()
var flags = (id >> 24) & 0xff
id = id & 0xffffff
// Skip persisted settings
if (flags & 0x2) { continue }
var name = idMap[id]
settings[name] = body.readUInt32BE()
}
callback(null, {
type: 'SETTINGS',
settings: settings
})
}
Parser.prototype.onPingFrame = function onPingFrame (body, callback) {
if (!body.has(4)) {
return callback(new Error('PING OOB'))
}
var isServer = this.isServer
var opaque = body.clone(body.size).take(body.size)
var id = body.readUInt32BE()
var ack = isServer ? (id % 2 === 0) : (id % 2 === 1)
callback(null, { type: 'PING', opaque: opaque, ack: ack })
}
Parser.prototype.onGoawayFrame = function onGoawayFrame (body, callback) {
if (!body.has(8)) {
return callback(new Error('GOAWAY OOB'))
}
callback(null, {
type: 'GOAWAY',
lastId: body.readUInt32BE() & 0x7fffffff,
code: constants.goawayByCode[body.readUInt32BE()]
})
}
Parser.prototype.onWindowUpdateFrame = function onWindowUpdateFrame (body,
callback) {
if (!body.has(8)) {
return callback(new Error('WINDOW_UPDATE OOB'))
}
callback(null, {
type: 'WINDOW_UPDATE',
id: body.readUInt32BE() & 0x7fffffff,
delta: body.readInt32BE()
})
}
Parser.prototype.onXForwardedFrame = function onXForwardedFrame (body,
callback) {
if (!body.has(4)) {
return callback(new Error('X_FORWARDED OOB'))
}
var len = body.readUInt32BE()
if (!body.has(len)) { return callback(new Error('X_FORWARDED host length OOB')) }
callback(null, {
type: 'X_FORWARDED_FOR',
host: body.take(len).toString()
})
}

View File

@@ -0,0 +1,65 @@
'use strict'
var zlibpool = exports
var zlib = require('zlib')
var transport = require('../../../spdy-transport')
// TODO(indutny): think about it, why has it always been Z_SYNC_FLUSH here.
// It should be possible to manually flush stuff after the write instead
function createDeflate (version, compression) {
var deflate = zlib.createDeflate({
dictionary: transport.protocol.spdy.dictionary[version],
flush: zlib.Z_SYNC_FLUSH,
windowBits: 11,
level: compression ? zlib.Z_DEFAULT_COMPRESSION : zlib.Z_NO_COMPRESSION
})
// For node.js v0.8
deflate._flush = zlib.Z_SYNC_FLUSH
return deflate
}
function createInflate (version) {
var inflate = zlib.createInflate({
dictionary: transport.protocol.spdy.dictionary[version],
flush: zlib.Z_SYNC_FLUSH
})
// For node.js v0.8
inflate._flush = zlib.Z_SYNC_FLUSH
return inflate
}
function Pool (compression) {
this.compression = compression
this.pool = {
2: [],
3: [],
3.1: []
}
}
zlibpool.create = function create (compression) {
return new Pool(compression)
}
Pool.prototype.get = function get (version) {
if (this.pool[version].length > 0) {
return this.pool[version].pop()
} else {
var id = version
return {
version: version,
compress: createDeflate(id, this.compression),
decompress: createInflate(id)
}
}
}
Pool.prototype.put = function put (pair) {
this.pool[pair.version].push(pair)
}

File diff suppressed because it is too large Load Diff

196
node_modules/spdy-transport/lib/spdy-transport/utils.js generated vendored Normal file
View File

@@ -0,0 +1,196 @@
'use strict'
var util = require('util')
var isNode = require('detect-node')
// Node.js 0.8, 0.10 and 0.12 support
Object.assign = (process.versions.modules >= 46 || !isNode)
? Object.assign // eslint-disable-next-line
: util._extend
function QueueItem () {
this.prev = null
this.next = null
}
exports.QueueItem = QueueItem
function Queue () {
QueueItem.call(this)
this.prev = this
this.next = this
}
util.inherits(Queue, QueueItem)
exports.Queue = Queue
Queue.prototype.insertTail = function insertTail (item) {
item.prev = this.prev
item.next = this
item.prev.next = item
item.next.prev = item
}
Queue.prototype.remove = function remove (item) {
var next = item.next
var prev = item.prev
item.next = item
item.prev = item
next.prev = prev
prev.next = next
}
Queue.prototype.head = function head () {
return this.next
}
Queue.prototype.tail = function tail () {
return this.prev
}
Queue.prototype.isEmpty = function isEmpty () {
return this.next === this
}
Queue.prototype.isRoot = function isRoot (item) {
return this === item
}
function LockStream (stream) {
this.locked = false
this.queue = []
this.stream = stream
}
exports.LockStream = LockStream
LockStream.prototype.write = function write (chunks, callback) {
var self = this
// Do not let it interleave
if (this.locked) {
this.queue.push(function () {
return self.write(chunks, callback)
})
return
}
this.locked = true
function done (err, chunks) {
self.stream.removeListener('error', done)
self.locked = false
if (self.queue.length > 0) { self.queue.shift()() }
callback(err, chunks)
}
this.stream.on('error', done)
// Accumulate all output data
var output = []
function onData (chunk) {
output.push(chunk)
}
this.stream.on('data', onData)
function next (err) {
self.stream.removeListener('data', onData)
if (err) {
return done(err)
}
done(null, output)
}
for (var i = 0; i < chunks.length - 1; i++) { this.stream.write(chunks[i]) }
if (chunks.length > 0) {
this.stream.write(chunks[i], next)
} else { process.nextTick(next) }
if (this.stream.execute) {
this.stream.execute(function (err) {
if (err) { return done(err) }
})
}
}
// Just finds the place in array to insert
function binaryLookup (list, item, compare) {
var start = 0
var end = list.length
while (start < end) {
var pos = (start + end) >> 1
var cmp = compare(item, list[pos])
if (cmp === 0) {
start = pos
end = pos
break
} else if (cmp < 0) {
end = pos
} else {
start = pos + 1
}
}
return start
}
exports.binaryLookup = binaryLookup
function binaryInsert (list, item, compare) {
var index = binaryLookup(list, item, compare)
list.splice(index, 0, item)
}
exports.binaryInsert = binaryInsert
function binarySearch (list, item, compare) {
var index = binaryLookup(list, item, compare)
if (index >= list.length) {
return -1
}
if (compare(item, list[index]) === 0) {
return index
}
return -1
}
exports.binarySearch = binarySearch
function Timeout (object) {
this.delay = 0
this.timer = null
this.object = object
}
exports.Timeout = Timeout
Timeout.prototype.set = function set (delay, callback) {
this.delay = delay
this.reset()
if (!callback) { return }
if (this.delay === 0) {
this.object.removeListener('timeout', callback)
} else {
this.object.once('timeout', callback)
}
}
Timeout.prototype.reset = function reset () {
if (this.timer !== null) {
clearTimeout(this.timer)
this.timer = null
}
if (this.delay === 0) { return }
var self = this
this.timer = setTimeout(function () {
self.timer = null
self.object.emit('timeout')
}, this.delay)
}

View File

@@ -0,0 +1,174 @@
'use strict'
var util = require('util')
var EventEmitter = require('events').EventEmitter
var debug = {
server: require('debug')('spdy:window:server'),
client: require('debug')('spdy:window:client')
}
function Side (window, name, options) {
EventEmitter.call(this)
this.name = name
this.window = window
this.current = options.size
this.max = options.size
this.limit = options.max
this.lowWaterMark = options.lowWaterMark === undefined
? this.max / 2
: options.lowWaterMark
this._refilling = false
this._refillQueue = []
}
util.inherits(Side, EventEmitter)
Side.prototype.setMax = function setMax (max) {
this.window.debug('id=%d side=%s setMax=%d',
this.window.id,
this.name,
max)
this.max = max
this.lowWaterMark = this.max / 2
}
Side.prototype.updateMax = function updateMax (max) {
var delta = max - this.max
this.window.debug('id=%d side=%s updateMax=%d delta=%d',
this.window.id,
this.name,
max,
delta)
this.max = max
this.lowWaterMark = max / 2
this.update(delta)
}
Side.prototype.setLowWaterMark = function setLowWaterMark (lwm) {
this.lowWaterMark = lwm
}
Side.prototype.update = function update (size, callback) {
// Not enough space for the update, wait for refill
if (size <= 0 && callback && this.isEmpty()) {
this.window.debug('id=%d side=%s wait for refill=%d [%d/%d]',
this.window.id,
this.name,
-size,
this.current,
this.max)
this._refillQueue.push({
size: size,
callback: callback
})
return
}
this.current += size
if (this.current > this.limit) {
this.emit('overflow')
return
}
this.window.debug('id=%d side=%s update by=%d [%d/%d]',
this.window.id,
this.name,
size,
this.current,
this.max)
// Time to send WINDOW_UPDATE
if (size < 0 && this.isDraining()) {
this.window.debug('id=%d side=%s drained', this.window.id, this.name)
this.emit('drain')
}
// Time to write
if (size > 0 && this.current > 0 && this.current <= size) {
this.window.debug('id=%d side=%s full', this.window.id, this.name)
this.emit('full')
}
this._processRefillQueue()
if (callback) { process.nextTick(callback) }
}
Side.prototype.getCurrent = function getCurrent () {
return this.current
}
Side.prototype.getMax = function getMax () {
return this.max
}
Side.prototype.getDelta = function getDelta () {
return this.max - this.current
}
Side.prototype.isDraining = function isDraining () {
return this.current <= this.lowWaterMark
}
Side.prototype.isEmpty = function isEmpty () {
return this.current <= 0
}
// Private
Side.prototype._processRefillQueue = function _processRefillQueue () {
// Prevent recursion
if (this._refilling) {
return
}
this._refilling = true
while (this._refillQueue.length > 0) {
var item = this._refillQueue[0]
if (this.isEmpty()) {
break
}
this.window.debug('id=%d side=%s refilled for size=%d',
this.window.id,
this.name,
-item.size)
this._refillQueue.shift()
this.update(item.size, item.callback)
}
this._refilling = false
}
function Window (options) {
this.id = options.id
this.isServer = options.isServer
this.debug = this.isServer ? debug.server : debug.client
this.recv = new Side(this, 'recv', options.recv)
this.send = new Side(this, 'send', options.send)
}
module.exports = Window
Window.prototype.clone = function clone (id) {
return new Window({
id: id,
isServer: this.isServer,
recv: {
size: this.recv.max,
max: this.recv.limit,
lowWaterMark: this.recv.lowWaterMark
},
send: {
size: this.send.max,
max: this.send.limit,
lowWaterMark: this.send.lowWaterMark
}
})
}