1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/xpcshell/node-http2/node_modules/http2-protocol/lib/framer.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,830 @@ 1.4 +// The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]: 1.5 +// the Serializer and the Deserializer 1.6 +// [1]: http://nodejs.org/api/stream.html#stream_class_stream_transform 1.7 +// [2]: http://nodejs.org/api/stream.html#stream_new_stream_readable_options 1.8 +var assert = require('assert'); 1.9 + 1.10 +var Transform = require('stream').Transform; 1.11 + 1.12 +exports.Serializer = Serializer; 1.13 +exports.Deserializer = Deserializer; 1.14 + 1.15 +var logData = Boolean(process.env.HTTP2_LOG_DATA); 1.16 + 1.17 +var MAX_PAYLOAD_SIZE = 16383; 1.18 + 1.19 +// Serializer 1.20 +// ---------- 1.21 +// 1.22 +// Frame Objects 1.23 +// * * * * * * * --+--------------------------- 1.24 +// | | 1.25 +// v v Buffers 1.26 +// [] -----> Payload Ser. --[buffers]--> Header Ser. --> * * * * 1.27 +// empty adds payload adds header 1.28 +// array buffers buffer 1.29 + 1.30 +function Serializer(log, sizeLimit) { 1.31 + this._log = log.child({ component: 'serializer' }); 1.32 + this._sizeLimit = sizeLimit || MAX_PAYLOAD_SIZE; 1.33 + Transform.call(this, { objectMode: true }); 1.34 +} 1.35 +Serializer.prototype = Object.create(Transform.prototype, { constructor: { value: Serializer } }); 1.36 + 1.37 +// When there's an incoming frame object, it first generates the frame type specific part of the 1.38 +// frame (payload), and then then adds the header part which holds fields that are common to all 1.39 +// frame types (like the length of the payload). 1.40 +Serializer.prototype._transform = function _transform(frame, encoding, done) { 1.41 + this._log.trace({ frame: frame }, 'Outgoing frame'); 1.42 + 1.43 + assert(frame.type in Serializer, 'Unknown frame type: ' + frame.type); 1.44 + 1.45 + var buffers = []; 1.46 + Serializer[frame.type](frame, buffers); 1.47 + Serializer.commonHeader(frame, buffers); 1.48 + 1.49 + assert(buffers[0].readUInt16BE(0) <= this._sizeLimit, 'Frame too large!'); 1.50 + 1.51 + for (var i = 0; i < buffers.length; i++) { 1.52 + if (logData) { 1.53 + this._log.trace({ data: buffers[i] }, 'Outgoing data'); 1.54 + } 1.55 + this.push(buffers[i]); 1.56 + } 1.57 + 1.58 + done(); 1.59 +}; 1.60 + 1.61 +// Deserializer 1.62 +// ------------ 1.63 +// 1.64 +// Buffers 1.65 +// * * * * --------+------------------------- 1.66 +// | | 1.67 +// v v Frame Objects 1.68 +// {} -----> Header Des. --{frame}--> Payload Des. --> * * * * * * * 1.69 +// empty adds parsed adds parsed 1.70 +// object header properties payload properties 1.71 + 1.72 +function Deserializer(log, sizeLimit, role) { 1.73 + this._role = role; 1.74 + this._log = log.child({ component: 'deserializer' }); 1.75 + this._sizeLimit = sizeLimit || MAX_PAYLOAD_SIZE; 1.76 + Transform.call(this, { objectMode: true }); 1.77 + this._next(COMMON_HEADER_SIZE); 1.78 +} 1.79 +Deserializer.prototype = Object.create(Transform.prototype, { constructor: { value: Deserializer } }); 1.80 + 1.81 +// The Deserializer is stateful, and it's two main alternating states are: *waiting for header* and 1.82 +// *waiting for payload*. The state is stored in the boolean property `_waitingForHeader`. 1.83 +// 1.84 +// When entering a new state, a `_buffer` is created that will hold the accumulated data (header or 1.85 +// payload). The `_cursor` is used to track the progress. 1.86 +Deserializer.prototype._next = function(size) { 1.87 + this._cursor = 0; 1.88 + this._buffer = new Buffer(size); 1.89 + this._waitingForHeader = !this._waitingForHeader; 1.90 + if (this._waitingForHeader) { 1.91 + this._frame = {}; 1.92 + } 1.93 +}; 1.94 + 1.95 +// Parsing an incoming buffer is an iterative process because it can hold multiple frames if it's 1.96 +// large enough. A `cursor` is used to track the progress in parsing the incoming `chunk`. 1.97 +Deserializer.prototype._transform = function _transform(chunk, encoding, done) { 1.98 + var cursor = 0; 1.99 + 1.100 + if (logData) { 1.101 + this._log.trace({ data: chunk }, 'Incoming data'); 1.102 + } 1.103 + 1.104 + while(cursor < chunk.length) { 1.105 + // The content of an incoming buffer is first copied to `_buffer`. If it can't hold the full 1.106 + // chunk, then only a part of it is copied. 1.107 + var toCopy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor); 1.108 + chunk.copy(this._buffer, this._cursor, cursor, cursor + toCopy); 1.109 + this._cursor += toCopy; 1.110 + cursor += toCopy; 1.111 + 1.112 + // When `_buffer` is full, it's content gets parsed either as header or payload depending on 1.113 + // the actual state. 1.114 + 1.115 + // If it's header then the parsed data is stored in a temporary variable and then the 1.116 + // deserializer waits for the specified length payload. 1.117 + if ((this._cursor === this._buffer.length) && this._waitingForHeader) { 1.118 + var payloadSize = Deserializer.commonHeader(this._buffer, this._frame); 1.119 + if (payloadSize <= this._sizeLimit) { 1.120 + this._next(payloadSize); 1.121 + } else { 1.122 + this.emit('error', 'FRAME_SIZE_ERROR'); 1.123 + return; 1.124 + } 1.125 + } 1.126 + 1.127 + // If it's payload then the the frame object is finalized and then gets pushed out. 1.128 + // Unknown frame types are ignored. 1.129 + // 1.130 + // Note: If we just finished the parsing of a header and the payload length is 0, this branch 1.131 + // will also run. 1.132 + if ((this._cursor === this._buffer.length) && !this._waitingForHeader) { 1.133 + if (this._frame.type) { 1.134 + var error = Deserializer[this._frame.type](this._buffer, this._frame, this._role); 1.135 + if (error) { 1.136 + this._log.error('Incoming frame parsing error: ' + error); 1.137 + this.emit('error', 'PROTOCOL_ERROR'); 1.138 + } else { 1.139 + this._log.trace({ frame: this._frame }, 'Incoming frame'); 1.140 + this.push(this._frame); 1.141 + } 1.142 + } else { 1.143 + this._log.error('Unknown type incoming frame'); 1.144 + this.emit('error', 'PROTOCOL_ERROR'); 1.145 + } 1.146 + this._next(COMMON_HEADER_SIZE); 1.147 + } 1.148 + } 1.149 + 1.150 + done(); 1.151 +}; 1.152 + 1.153 +// [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-4.1) 1.154 +// -------------------------------------------------------------- 1.155 +// 1.156 +// HTTP/2.0 frames share a common base format consisting of an 8-byte header followed by 0 to 65535 1.157 +// bytes of data. 1.158 +// 1.159 +// Additional size limits can be set by specific application uses. HTTP limits the frame size to 1.160 +// 16,383 octets. 1.161 +// 1.162 +// 0 1 2 3 1.163 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.164 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.165 +// | R | Length (14) | Type (8) | Flags (8) | 1.166 +// +-+-+---------------------------+---------------+---------------+ 1.167 +// |R| Stream Identifier (31) | 1.168 +// +-+-------------------------------------------------------------+ 1.169 +// | Frame Data (0...) ... 1.170 +// +---------------------------------------------------------------+ 1.171 +// 1.172 +// The fields of the frame header are defined as: 1.173 +// 1.174 +// * R: 1.175 +// A reserved 2-bit field. The semantics of these bits are undefined and the bits MUST remain 1.176 +// unset (0) when sending and MUST be ignored when receiving. 1.177 +// 1.178 +// * Length: 1.179 +// The length of the frame data expressed as an unsigned 14-bit integer. The 8 bytes of the frame 1.180 +// header are not included in this value. 1.181 +// 1.182 +// * Type: 1.183 +// The 8-bit type of the frame. The frame type determines how the remainder of the frame header 1.184 +// and data are interpreted. Implementations MUST ignore unsupported and unrecognized frame types. 1.185 +// 1.186 +// * Flags: 1.187 +// An 8-bit field reserved for frame-type specific boolean flags. 1.188 +// 1.189 +// Flags are assigned semantics specific to the indicated frame type. Flags that have no defined 1.190 +// semantics for a particular frame type MUST be ignored, and MUST be left unset (0) when sending. 1.191 +// 1.192 +// * R: 1.193 +// A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST remain unset 1.194 +// (0) when sending and MUST be ignored when receiving. 1.195 +// 1.196 +// * Stream Identifier: 1.197 +// A 31-bit stream identifier. The value 0 is reserved for frames that are associated with the 1.198 +// connection as a whole as opposed to an individual stream. 1.199 +// 1.200 +// The structure and content of the remaining frame data is dependent entirely on the frame type. 1.201 + 1.202 +var COMMON_HEADER_SIZE = 8; 1.203 + 1.204 +var frameTypes = []; 1.205 + 1.206 +var frameFlags = {}; 1.207 + 1.208 +var genericAttributes = ['type', 'flags', 'stream']; 1.209 + 1.210 +var typeSpecificAttributes = {}; 1.211 + 1.212 +Serializer.commonHeader = function writeCommonHeader(frame, buffers) { 1.213 + var headerBuffer = new Buffer(COMMON_HEADER_SIZE); 1.214 + 1.215 + var size = 0; 1.216 + for (var i = 0; i < buffers.length; i++) { 1.217 + size += buffers[i].length; 1.218 + } 1.219 + headerBuffer.writeUInt16BE(size, 0); 1.220 + 1.221 + var typeId = frameTypes.indexOf(frame.type); // If we are here then the type is valid for sure 1.222 + headerBuffer.writeUInt8(typeId, 2); 1.223 + 1.224 + var flagByte = 0; 1.225 + for (var flag in frame.flags) { 1.226 + var position = frameFlags[frame.type].indexOf(flag); 1.227 + assert(position !== -1, 'Unknown flag for frame type ' + frame.type + ': ' + flag); 1.228 + if (frame.flags[flag]) { 1.229 + flagByte |= (1 << position); 1.230 + } 1.231 + } 1.232 + headerBuffer.writeUInt8(flagByte, 3); 1.233 + 1.234 + assert((0 <= frame.stream) && (frame.stream < 0x7fffffff), frame.stream); 1.235 + headerBuffer.writeUInt32BE(frame.stream || 0, 4); 1.236 + 1.237 + buffers.unshift(headerBuffer); 1.238 +}; 1.239 + 1.240 +Deserializer.commonHeader = function readCommonHeader(buffer, frame) { 1.241 + var length = buffer.readUInt16BE(0); 1.242 + 1.243 + frame.type = frameTypes[buffer.readUInt8(2)]; 1.244 + 1.245 + frame.flags = {}; 1.246 + var flagByte = buffer.readUInt8(3); 1.247 + var definedFlags = frameFlags[frame.type]; 1.248 + for (var i = 0; i < definedFlags.length; i++) { 1.249 + frame.flags[definedFlags[i]] = Boolean(flagByte & (1 << i)); 1.250 + } 1.251 + 1.252 + frame.stream = buffer.readUInt32BE(4) & 0x7fffffff; 1.253 + 1.254 + return length; 1.255 +}; 1.256 + 1.257 +// Frame types 1.258 +// =========== 1.259 + 1.260 +// Every frame type is registered in the following places: 1.261 +// 1.262 +// * `frameTypes`: a register of frame type codes (used by `commonHeader()`) 1.263 +// * `frameFlags`: a register of valid flags for frame types (used by `commonHeader()`) 1.264 +// * `typeSpecificAttributes`: a register of frame specific frame object attributes (used by 1.265 +// logging code and also serves as documentation for frame objects) 1.266 + 1.267 +// [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.1) 1.268 +// ------------------------------------------------------------ 1.269 +// 1.270 +// DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a 1.271 +// stream. 1.272 +// 1.273 +// The DATA frame defines the following flags: 1.274 +// 1.275 +// * END_STREAM (0x1): 1.276 +// Bit 1 being set indicates that this frame is the last that the endpoint will send for the 1.277 +// identified stream. 1.278 +// * END_SEGMENT (0x2): 1.279 +// Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries 1.280 +// MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when 1.281 +// forwarding frames. 1.282 +// * PAD_LOW (0x10): 1.283 +// Bit 5 being set indicates that the Pad Low field is present. 1.284 +// * PAD_HIGH (0x20): 1.285 +// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless 1.286 +// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW 1.287 +// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. 1.288 + 1.289 +frameTypes[0x0] = 'DATA'; 1.290 + 1.291 +frameFlags.DATA = ['END_STREAM', 'END_SEGMENT', 'RESERVED4', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; 1.292 + 1.293 +typeSpecificAttributes.DATA = ['data']; 1.294 + 1.295 +Serializer.DATA = function writeData(frame, buffers) { 1.296 + buffers.push(frame.data); 1.297 +}; 1.298 + 1.299 +Deserializer.DATA = function readData(buffer, frame) { 1.300 + var dataOffset = 0; 1.301 + var paddingLength = 0; 1.302 + if (frame.flags.PAD_LOW) { 1.303 + if (frame.flags.PAD_HIGH) { 1.304 + paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; 1.305 + dataOffset += 1; 1.306 + } 1.307 + paddingLength += (buffer.readUInt8(dataOffset) & 0xff); 1.308 + dataOffset += 1; 1.309 + } else if (frame.flags.PAD_HIGH) { 1.310 + return 'DATA frame got PAD_HIGH without PAD_LOW'; 1.311 + } 1.312 + if (paddingLength) { 1.313 + frame.data = buffer.slice(dataOffset, -1 * paddingLength); 1.314 + } else { 1.315 + frame.data = buffer.slice(dataOffset); 1.316 + } 1.317 +}; 1.318 + 1.319 +// [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.2) 1.320 +// -------------------------------------------------------------- 1.321 +// 1.322 +// The HEADERS frame (type=0x1) allows the sender to create a stream. 1.323 +// 1.324 +// The HEADERS frame defines the following flags: 1.325 +// 1.326 +// * END_STREAM (0x1): 1.327 +// Bit 1 being set indicates that this frame is the last that the endpoint will send for the 1.328 +// identified stream. 1.329 +// * END_SEGMENT (0x2): 1.330 +// Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries 1.331 +// MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when 1.332 +// forwarding frames. 1.333 +// * END_HEADERS (0x4): 1.334 +// The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide 1.335 +// a complete set of headers. 1.336 +// * PRIORITY (0x8): 1.337 +// Bit 4 being set indicates that the first four octets of this frame contain a single reserved 1.338 +// bit and a 31-bit priority. 1.339 +// * PAD_LOW (0x10): 1.340 +// Bit 5 being set indicates that the Pad Low field is present. 1.341 +// * PAD_HIGH (0x20): 1.342 +// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless 1.343 +// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW 1.344 +// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. 1.345 + 1.346 +frameTypes[0x1] = 'HEADERS'; 1.347 + 1.348 +frameFlags.HEADERS = ['END_STREAM', 'END_SEGMENT', 'END_HEADERS', 'PRIORITY', 'PAD_LOW', 'PAD_HIGH']; 1.349 + 1.350 +typeSpecificAttributes.HEADERS = ['priority', 'headers', 'data']; 1.351 + 1.352 +// 0 1 2 3 1.353 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.354 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.355 +// |X| (Optional) Priority (31) | 1.356 +// +-+-------------------------------------------------------------+ 1.357 +// | Header Block (*) ... 1.358 +// +---------------------------------------------------------------+ 1.359 +// 1.360 +// The payload of a HEADERS frame contains a Headers Block 1.361 + 1.362 +Serializer.HEADERS = function writeHeadersPriority(frame, buffers) { 1.363 + if (frame.flags.PRIORITY) { 1.364 + var buffer = new Buffer(4); 1.365 + assert((0 <= frame.priority) && (frame.priority <= 0xffffffff), frame.priority); 1.366 + buffer.writeUInt32BE(frame.priority, 0); 1.367 + buffers.push(buffer); 1.368 + } 1.369 + buffers.push(frame.data); 1.370 +}; 1.371 + 1.372 +Deserializer.HEADERS = function readHeadersPriority(buffer, frame) { 1.373 + var dataOffset = 0; 1.374 + var paddingLength = 0; 1.375 + if (frame.flags.PAD_LOW) { 1.376 + if (frame.flags.PAD_HIGH) { 1.377 + paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; 1.378 + dataOffset += 1; 1.379 + } 1.380 + paddingLength += (buffer.readUInt8(dataOffset) & 0xff); 1.381 + dataOffset += 1; 1.382 + } else if (frame.flags.PAD_HIGH) { 1.383 + return 'HEADERS frame got PAD_HIGH without PAD_LOW'; 1.384 + } 1.385 + if (frame.flags.PRIORITY) { 1.386 + frame.priority = buffer.readUInt32BE(dataOffset) & 0x7fffffff; 1.387 + dataOffset += 4; 1.388 + } 1.389 + if (paddingLength) { 1.390 + frame.data = buffer.slice(dataOffset, -1 * paddingLength); 1.391 + } else { 1.392 + frame.data = buffer.slice(dataOffset); 1.393 + } 1.394 +}; 1.395 + 1.396 +// [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.3) 1.397 +// ------------------------------------------------------- 1.398 +// 1.399 +// The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream. 1.400 +// 1.401 +// The PRIORITY frame does not define any flags. 1.402 + 1.403 +frameTypes[0x2] = 'PRIORITY'; 1.404 + 1.405 +frameFlags.PRIORITY = []; 1.406 + 1.407 +typeSpecificAttributes.PRIORITY = ['priority']; 1.408 + 1.409 +// 0 1 2 3 1.410 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.411 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.412 +// |X| Priority (31) | 1.413 +// +-+-------------------------------------------------------------+ 1.414 +// 1.415 +// The payload of a PRIORITY frame contains a single reserved bit and a 31-bit priority. 1.416 + 1.417 +Serializer.PRIORITY = function writePriority(frame, buffers) { 1.418 + var buffer = new Buffer(4); 1.419 + buffer.writeUInt32BE(frame.priority, 0); 1.420 + buffers.push(buffer); 1.421 +}; 1.422 + 1.423 +Deserializer.PRIORITY = function readPriority(buffer, frame) { 1.424 + frame.priority = buffer.readUInt32BE(0); 1.425 +}; 1.426 + 1.427 +// [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.4) 1.428 +// ----------------------------------------------------------- 1.429 +// 1.430 +// The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream. 1.431 +// 1.432 +// No type-flags are defined. 1.433 + 1.434 +frameTypes[0x3] = 'RST_STREAM'; 1.435 + 1.436 +frameFlags.RST_STREAM = []; 1.437 + 1.438 +typeSpecificAttributes.RST_STREAM = ['error']; 1.439 + 1.440 +// 0 1 2 3 1.441 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.442 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.443 +// | Error Code (32) | 1.444 +// +---------------------------------------------------------------+ 1.445 +// 1.446 +// The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error 1.447 +// code (see Error Codes). The error code indicates why the stream is being terminated. 1.448 + 1.449 +Serializer.RST_STREAM = function writeRstStream(frame, buffers) { 1.450 + var buffer = new Buffer(4); 1.451 + var code = errorCodes.indexOf(frame.error); 1.452 + assert((0 <= code) && (code <= 0xffffffff), code); 1.453 + buffer.writeUInt32BE(code, 0); 1.454 + buffers.push(buffer); 1.455 +}; 1.456 + 1.457 +Deserializer.RST_STREAM = function readRstStream(buffer, frame) { 1.458 + frame.error = errorCodes[buffer.readUInt32BE(0)]; 1.459 +}; 1.460 + 1.461 +// [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.5) 1.462 +// ------------------------------------------------------- 1.463 +// 1.464 +// The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints 1.465 +// communicate. 1.466 +// 1.467 +// The SETTINGS frame defines the following flag: 1.468 + 1.469 +// * ACK (0x1): 1.470 +// Bit 1 being set indicates that this frame acknowledges receipt and application of the peer's 1.471 +// SETTINGS frame. 1.472 +frameTypes[0x4] = 'SETTINGS'; 1.473 + 1.474 +frameFlags.SETTINGS = ['ACK']; 1.475 + 1.476 +typeSpecificAttributes.SETTINGS = ['settings']; 1.477 + 1.478 +// The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of an 1.479 +// 8-bit identifier, and an unsigned 32-bit value. 1.480 +// 1.481 +// 0 1 2 3 1.482 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.483 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.484 +// | Identifier(8) | Value (32) | 1.485 +// +-----------------+---------------------------------------------+ 1.486 +// ...Value | 1.487 +// +-----------------+ 1.488 +// 1.489 +// Each setting in a SETTINGS frame replaces the existing value for that setting. Settings are 1.490 +// processed in the order in which they appear, and a receiver of a SETTINGS frame does not need to 1.491 +// maintain any state other than the current value of settings. Therefore, the value of a setting 1.492 +// is the last value that is seen by a receiver. This permits the inclusion of the same settings 1.493 +// multiple times in the same SETTINGS frame, though doing so does nothing other than waste 1.494 +// connection capacity. 1.495 + 1.496 +Serializer.SETTINGS = function writeSettings(frame, buffers) { 1.497 + var settings = [], settingsLeft = Object.keys(frame.settings); 1.498 + definedSettings.forEach(function(setting, id) { 1.499 + if (setting.name in frame.settings) { 1.500 + settingsLeft.splice(settingsLeft.indexOf(setting.name), 1); 1.501 + var value = frame.settings[setting.name]; 1.502 + settings.push({ id: id, value: setting.flag ? Boolean(value) : value }); 1.503 + } 1.504 + }); 1.505 + assert(settingsLeft.length === 0, 'Unknown settings: ' + settingsLeft.join(', ')); 1.506 + 1.507 + var buffer = new Buffer(settings.length * 5); 1.508 + for (var i = 0; i < settings.length; i++) { 1.509 + buffer.writeUInt8(settings[i].id & 0xff, i*5); 1.510 + buffer.writeUInt32BE(settings[i].value, i*5 + 1); 1.511 + } 1.512 + 1.513 + buffers.push(buffer); 1.514 +}; 1.515 + 1.516 +Deserializer.SETTINGS = function readSettings(buffer, frame, role) { 1.517 + frame.settings = {}; 1.518 + 1.519 + if (buffer.length % 5 !== 0) { 1.520 + return 'Invalid SETTINGS frame'; 1.521 + } 1.522 + for (var i = 0; i < buffer.length / 5; i++) { 1.523 + var id = buffer.readUInt8(i*5) & 0xff; 1.524 + var setting = definedSettings[id]; 1.525 + if (setting) { 1.526 + if (role == 'CLIENT' && setting.name == 'SETTINGS_ENABLE_PUSH') { 1.527 + return 'SETTINGS frame on client got SETTINGS_ENABLE_PUSH'; 1.528 + } 1.529 + var value = buffer.readUInt32BE(i*5 + 1); 1.530 + frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value; 1.531 + } else { 1.532 + /* Unknown setting, protocol error */ 1.533 + return 'SETTINGS frame got unknown setting type'; 1.534 + } 1.535 + } 1.536 +}; 1.537 + 1.538 +// The following settings are defined: 1.539 +var definedSettings = []; 1.540 + 1.541 +// * SETTINGS_HEADER_TABLE_SIZE (1): 1.542 +// Allows the sender to inform the remote endpoint of the size of the header compression table 1.543 +// used to decode header blocks. 1.544 +definedSettings[1] = { name: 'SETTINGS_HEADER_TABLE_SIZE', flag: false }; 1.545 + 1.546 +// * SETTINGS_ENABLE_PUSH (2): 1.547 +// This setting can be use to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame 1.548 +// if it receives this setting set to a value of 0. The default value is 1, which indicates that 1.549 +// push is permitted. 1.550 +definedSettings[2] = { name: 'SETTINGS_ENABLE_PUSH', flag: true }; 1.551 + 1.552 +// * SETTINGS_MAX_CONCURRENT_STREAMS (4): 1.553 +// indicates the maximum number of concurrent streams that the sender will allow. 1.554 +definedSettings[3] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false }; 1.555 + 1.556 +// * SETTINGS_INITIAL_WINDOW_SIZE (7): 1.557 +// indicates the sender's initial stream window size (in bytes) for new streams. 1.558 +definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false }; 1.559 + 1.560 +// [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.6) 1.561 +// --------------------------------------------------------------- 1.562 +// 1.563 +// The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the 1.564 +// sender intends to initiate. 1.565 +// 1.566 +// The PUSH_PROMISE frame defines the following flags: 1.567 +// 1.568 +// * END_PUSH_PROMISE (0x4): 1.569 +// The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to 1.570 +// provide a complete set of headers. 1.571 + 1.572 +frameTypes[0x5] = 'PUSH_PROMISE'; 1.573 + 1.574 +frameFlags.PUSH_PROMISE = ['RESERVED1', 'RESERVED2', 'END_PUSH_PROMISE']; 1.575 + 1.576 +typeSpecificAttributes.PUSH_PROMISE = ['promised_stream', 'headers', 'data']; 1.577 + 1.578 +// 0 1 2 3 1.579 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.580 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.581 +// |X| Promised-Stream-ID (31) | 1.582 +// +-+-------------------------------------------------------------+ 1.583 +// | Header Block (*) ... 1.584 +// +---------------------------------------------------------------+ 1.585 +// 1.586 +// The PUSH_PROMISE frame includes the unsigned 31-bit identifier of 1.587 +// the stream the endpoint plans to create along with a minimal set of headers that provide 1.588 +// additional context for the stream. 1.589 + 1.590 +Serializer.PUSH_PROMISE = function writePushPromise(frame, buffers) { 1.591 + var buffer = new Buffer(4); 1.592 + 1.593 + var promised_stream = frame.promised_stream; 1.594 + assert((0 <= promised_stream) && (promised_stream <= 0x7fffffff), promised_stream); 1.595 + buffer.writeUInt32BE(promised_stream, 0); 1.596 + 1.597 + buffers.push(buffer); 1.598 + buffers.push(frame.data); 1.599 +}; 1.600 + 1.601 +Deserializer.PUSH_PROMISE = function readPushPromise(buffer, frame) { 1.602 + frame.promised_stream = buffer.readUInt32BE(0) & 0x7fffffff; 1.603 + frame.data = buffer.slice(4); 1.604 +}; 1.605 + 1.606 +// [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.7) 1.607 +// ----------------------------------------------- 1.608 +// 1.609 +// The PING frame (type=0x6) is a mechanism for measuring a minimal round-trip time from the 1.610 +// sender, as well as determining whether an idle connection is still functional. 1.611 +// 1.612 +// The PING frame defines one type-specific flag: 1.613 +// 1.614 +// * ACK (0x1): 1.615 +// Bit 1 being set indicates that this PING frame is a PING response. 1.616 + 1.617 +frameTypes[0x6] = 'PING'; 1.618 + 1.619 +frameFlags.PING = ['ACK']; 1.620 + 1.621 +typeSpecificAttributes.PING = ['data']; 1.622 + 1.623 +// In addition to the frame header, PING frames MUST contain 8 additional octets of opaque data. 1.624 + 1.625 +Serializer.PING = function writePing(frame, buffers) { 1.626 + buffers.push(frame.data); 1.627 +}; 1.628 + 1.629 +Deserializer.PING = function readPing(buffer, frame) { 1.630 + if (buffer.length !== 8) { 1.631 + return 'Invalid size PING frame'; 1.632 + } 1.633 + frame.data = buffer; 1.634 +}; 1.635 + 1.636 +// [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.8) 1.637 +// --------------------------------------------------- 1.638 +// 1.639 +// The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this connection. 1.640 +// 1.641 +// The GOAWAY frame does not define any flags. 1.642 + 1.643 +frameTypes[0x7] = 'GOAWAY'; 1.644 + 1.645 +frameFlags.GOAWAY = []; 1.646 + 1.647 +typeSpecificAttributes.GOAWAY = ['last_stream', 'error']; 1.648 + 1.649 +// 0 1 2 3 1.650 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1.651 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.652 +// |X| Last-Stream-ID (31) | 1.653 +// +-+-------------------------------------------------------------+ 1.654 +// | Error Code (32) | 1.655 +// +---------------------------------------------------------------+ 1.656 +// 1.657 +// The last stream identifier in the GOAWAY frame contains the highest numbered stream identifier 1.658 +// for which the sender of the GOAWAY frame has received frames on and might have taken some action 1.659 +// on. 1.660 +// 1.661 +// The GOAWAY frame also contains a 32-bit error code (see Error Codes) that contains the reason for 1.662 +// closing the connection. 1.663 + 1.664 +Serializer.GOAWAY = function writeGoaway(frame, buffers) { 1.665 + var buffer = new Buffer(8); 1.666 + 1.667 + var last_stream = frame.last_stream; 1.668 + assert((0 <= last_stream) && (last_stream <= 0x7fffffff), last_stream); 1.669 + buffer.writeUInt32BE(last_stream, 0); 1.670 + 1.671 + var code = errorCodes.indexOf(frame.error); 1.672 + assert((0 <= code) && (code <= 0xffffffff), code); 1.673 + buffer.writeUInt32BE(code, 4); 1.674 + 1.675 + buffers.push(buffer); 1.676 +}; 1.677 + 1.678 +Deserializer.GOAWAY = function readGoaway(buffer, frame) { 1.679 + frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff; 1.680 + frame.error = errorCodes[buffer.readUInt32BE(4)]; 1.681 +}; 1.682 + 1.683 +// [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.9) 1.684 +// ----------------------------------------------------------------- 1.685 +// 1.686 +// The WINDOW_UPDATE frame (type=0x8) is used to implement flow control. 1.687 +// 1.688 +// The WINDOW_UPDATE frame does not define any flags. 1.689 + 1.690 +frameTypes[0x8] = 'WINDOW_UPDATE'; 1.691 + 1.692 +frameFlags.WINDOW_UPDATE = []; 1.693 + 1.694 +typeSpecificAttributes.WINDOW_UPDATE = ['window_size']; 1.695 + 1.696 +// The payload of a WINDOW_UPDATE frame is a 32-bit value indicating the additional number of bytes 1.697 +// that the sender can transmit in addition to the existing flow control window. The legal range 1.698 +// for this field is 1 to 2^31 - 1 (0x7fffffff) bytes; the most significant bit of this value is 1.699 +// reserved. 1.700 + 1.701 +Serializer.WINDOW_UPDATE = function writeWindowUpdate(frame, buffers) { 1.702 + var buffer = new Buffer(4); 1.703 + 1.704 + var window_size = frame.window_size; 1.705 + assert((0 <= window_size) && (window_size <= 0x7fffffff), window_size); 1.706 + buffer.writeUInt32BE(window_size, 0); 1.707 + 1.708 + buffers.push(buffer); 1.709 +}; 1.710 + 1.711 +Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) { 1.712 + frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff; 1.713 +}; 1.714 + 1.715 +// [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.10) 1.716 +// ------------------------------------------------------------ 1.717 +// 1.718 +// The CONTINUATION frame (type=0xA) is used to continue a sequence of header block fragments. 1.719 +// 1.720 +// The CONTINUATION frame defines the following flag: 1.721 +// 1.722 +// * END_HEADERS (0x4): 1.723 +// The END_HEADERS bit indicates that this frame ends the sequence of header block fragments 1.724 +// necessary to provide a complete set of headers. 1.725 +// * PAD_LOW (0x10): 1.726 +// Bit 5 being set indicates that the Pad Low field is present. 1.727 +// * PAD_HIGH (0x20): 1.728 +// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless 1.729 +// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW 1.730 +// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. 1.731 + 1.732 +frameTypes[0x9] = 'CONTINUATION'; 1.733 + 1.734 +frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; 1.735 + 1.736 +typeSpecificAttributes.CONTINUATION = ['headers', 'data']; 1.737 + 1.738 +Serializer.CONTINUATION = function writeContinuation(frame, buffers) { 1.739 + buffers.push(frame.data); 1.740 +}; 1.741 + 1.742 +Deserializer.CONTINUATION = function readContinuation(buffer, frame) { 1.743 + var dataOffset = 0; 1.744 + var paddingLength = 0; 1.745 + if (frame.flags.PAD_LOW) { 1.746 + if (frame.flags.PAD_HIGH) { 1.747 + paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; 1.748 + dataOffset += 1; 1.749 + } 1.750 + paddingLength += (buffer.readUInt8(dataOffset) & 0xff); 1.751 + dataOffset += 1; 1.752 + } else if (frame.flags.PAD_HIGH) { 1.753 + return 'CONTINUATION frame got PAD_HIGH without PAD_LOW'; 1.754 + } 1.755 + if (paddingLength) { 1.756 + frame.data = buffer.slice(dataOffset, -1 * paddingLength); 1.757 + } else { 1.758 + frame.data = buffer.slice(dataOffset); 1.759 + } 1.760 +}; 1.761 + 1.762 +// [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-7) 1.763 +// ------------------------------------------------------------ 1.764 + 1.765 +var errorCodes = [ 1.766 + 'NO_ERROR', 1.767 + 'PROTOCOL_ERROR', 1.768 + 'INTERNAL_ERROR', 1.769 + 'FLOW_CONTROL_ERROR', 1.770 + 'SETTINGS_TIMEOUT', 1.771 + 'STREAM_CLOSED', 1.772 + 'FRAME_SIZE_ERROR', 1.773 + 'REFUSED_STREAM', 1.774 + 'CANCEL', 1.775 + 'COMPRESSION_ERROR', 1.776 + 'CONNECT_ERROR' 1.777 +]; 1.778 +errorCodes[420] = 'ENHANCE_YOUR_CALM'; 1.779 + 1.780 +// Logging 1.781 +// ------- 1.782 + 1.783 +// [Bunyan serializers](https://github.com/trentm/node-bunyan#serializers) to improve logging output 1.784 +// for debug messages emitted in this component. 1.785 +exports.serializers = {}; 1.786 + 1.787 +// * `frame` serializer: it transforms data attributes from Buffers to hex strings and filters out 1.788 +// flags that are not present. 1.789 +var frameCounter = 0; 1.790 +exports.serializers.frame = function(frame) { 1.791 + if (!frame) { 1.792 + return null; 1.793 + } 1.794 + 1.795 + if ('id' in frame) { 1.796 + return frame.id; 1.797 + } 1.798 + 1.799 + frame.id = frameCounter; 1.800 + frameCounter += 1; 1.801 + 1.802 + var logEntry = { id: frame.id }; 1.803 + genericAttributes.concat(typeSpecificAttributes[frame.type]).forEach(function(name) { 1.804 + logEntry[name] = frame[name]; 1.805 + }); 1.806 + 1.807 + if (frame.data instanceof Buffer) { 1.808 + if (logEntry.data.length > 50) { 1.809 + logEntry.data = frame.data.slice(0, 47).toString('hex') + '...'; 1.810 + } else { 1.811 + logEntry.data = frame.data.toString('hex'); 1.812 + } 1.813 + 1.814 + if (!('length' in logEntry)) { 1.815 + logEntry.length = frame.data.length; 1.816 + } 1.817 + } 1.818 + 1.819 + if (frame.promised_stream instanceof Object) { 1.820 + logEntry.promised_stream = 'stream-' + frame.promised_stream.id; 1.821 + } 1.822 + 1.823 + logEntry.flags = Object.keys(frame.flags || {}).filter(function(name) { 1.824 + return frame.flags[name] === true; 1.825 + }); 1.826 + 1.827 + return logEntry; 1.828 +}; 1.829 + 1.830 +// * `data` serializer: it simply transforms a buffer to a hex string. 1.831 +exports.serializers.data = function(data) { 1.832 + return data.toString('hex'); 1.833 +};