testing/xpcshell/node-http2/node_modules/http2-protocol/lib/framer.js

changeset 0
6474c204b198
     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 +};

mercurial