tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

framer.js (39565B)


      1 // The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]:
      2 // the Serializer and the Deserializer
      3 // [1]: https://nodejs.org/api/stream.html#stream_class_stream_transform
      4 // [2]: https://nodejs.org/api/stream.html#stream_new_stream_readable_options
      5 var assert = require('assert');
      6 
      7 var Transform = require('stream').Transform;
      8 
      9 exports.Serializer = Serializer;
     10 exports.Deserializer = Deserializer;
     11 
     12 var logData = Boolean(process.env.HTTP2_LOG_DATA);
     13 
     14 var MAX_PAYLOAD_SIZE = 16384;
     15 var WINDOW_UPDATE_PAYLOAD_SIZE = 4;
     16 
     17 // Serializer
     18 // ----------
     19 //
     20 //     Frame Objects
     21 //     * * * * * * * --+---------------------------
     22 //                     |                          |
     23 //                     v                          v           Buffers
     24 //      [] -----> Payload Ser. --[buffers]--> Header Ser. --> * * * *
     25 //     empty      adds payload                adds header
     26 //     array        buffers                     buffer
     27 
     28 function Serializer(log) {
     29  this._log = log.child({ component: 'serializer' });
     30  Transform.call(this, { objectMode: true });
     31 }
     32 Serializer.prototype = Object.create(Transform.prototype, { constructor: { value: Serializer } });
     33 
     34 // When there's an incoming frame object, it first generates the frame type specific part of the
     35 // frame (payload), and then then adds the header part which holds fields that are common to all
     36 // frame types (like the length of the payload).
     37 Serializer.prototype._transform = function _transform(frame, encoding, done) {
     38  this._log.trace({ frame: frame }, 'Outgoing frame');
     39 
     40  assert(frame.type in Serializer, 'Unknown frame type: ' + frame.type);
     41 
     42  var buffers = [];
     43  Serializer[frame.type](frame, buffers);
     44  var length = Serializer.commonHeader(frame, buffers);
     45 
     46  assert(length <= MAX_PAYLOAD_SIZE, 'Frame too large!');
     47 
     48  for (var i = 0; i < buffers.length; i++) {
     49    if (logData) {
     50      this._log.trace({ data: buffers[i] }, 'Outgoing data');
     51    }
     52    this.push(buffers[i]);
     53  }
     54 
     55  done();
     56 };
     57 
     58 // Deserializer
     59 // ------------
     60 //
     61 //     Buffers
     62 //     * * * * --------+-------------------------
     63 //                     |                        |
     64 //                     v                        v           Frame Objects
     65 //      {} -----> Header Des. --{frame}--> Payload Des. --> * * * * * * *
     66 //     empty      adds parsed              adds parsed
     67 //     object  header properties        payload properties
     68 
     69 function Deserializer(log, role) {
     70  this._role = role;
     71  this._log = log.child({ component: 'deserializer' });
     72  Transform.call(this, { objectMode: true });
     73  this._next(COMMON_HEADER_SIZE);
     74 }
     75 Deserializer.prototype = Object.create(Transform.prototype, { constructor: { value: Deserializer } });
     76 
     77 // The Deserializer is stateful, and it's two main alternating states are: *waiting for header* and
     78 // *waiting for payload*. The state is stored in the boolean property `_waitingForHeader`.
     79 //
     80 // When entering a new state, a `_buffer` is created that will hold the accumulated data (header or
     81 // payload). The `_cursor` is used to track the progress.
     82 Deserializer.prototype._next = function(size) {
     83  this._cursor = 0;
     84  this._buffer = Buffer.alloc(size);
     85  this._waitingForHeader = !this._waitingForHeader;
     86  if (this._waitingForHeader) {
     87    this._frame = {};
     88  }
     89 };
     90 
     91 // Parsing an incoming buffer is an iterative process because it can hold multiple frames if it's
     92 // large enough. A `cursor` is used to track the progress in parsing the incoming `chunk`.
     93 Deserializer.prototype._transform = function _transform(chunk, encoding, done) {
     94  var cursor = 0;
     95 
     96  if (logData) {
     97    this._log.trace({ data: chunk }, 'Incoming data');
     98  }
     99 
    100  while(cursor < chunk.length) {
    101    // The content of an incoming buffer is first copied to `_buffer`. If it can't hold the full
    102    // chunk, then only a part of it is copied.
    103    var toCopy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor);
    104    chunk.copy(this._buffer, this._cursor, cursor, cursor + toCopy);
    105    this._cursor += toCopy;
    106    cursor += toCopy;
    107 
    108    // When `_buffer` is full, it's content gets parsed either as header or payload depending on
    109    // the actual state.
    110 
    111    // If it's header then the parsed data is stored in a temporary variable and then the
    112    // deserializer waits for the specified length payload.
    113    if ((this._cursor === this._buffer.length) && this._waitingForHeader) {
    114      var payloadSize = Deserializer.commonHeader(this._buffer, this._frame);
    115      if (payloadSize <= MAX_PAYLOAD_SIZE) {
    116        this._next(payloadSize);
    117      } else {
    118        this.emit('error', 'FRAME_SIZE_ERROR');
    119        return;
    120      }
    121    }
    122 
    123    // If it's payload then the the frame object is finalized and then gets pushed out.
    124    // Unknown frame types are ignored.
    125    //
    126    // Note: If we just finished the parsing of a header and the payload length is 0, this branch
    127    // will also run.
    128    if ((this._cursor === this._buffer.length) && !this._waitingForHeader) {
    129      if (this._frame.type) {
    130        var error = Deserializer[this._frame.type](this._buffer, this._frame, this._role);
    131        if (error) {
    132          this._log.error('Incoming frame parsing error: ' + error);
    133          this.emit('error', error);
    134        } else {
    135          this._log.trace({ frame: this._frame }, 'Incoming frame');
    136          this.push(this._frame);
    137        }
    138      } else {
    139        this._log.error('Unknown type incoming frame');
    140        // Ignore it other than logging
    141      }
    142      this._next(COMMON_HEADER_SIZE);
    143    }
    144  }
    145 
    146  done();
    147 };
    148 
    149 // [Frame Header](https://tools.ietf.org/html/rfc7540#section-4.1)
    150 // --------------------------------------------------------------
    151 //
    152 // HTTP/2 frames share a common base format consisting of a 9-byte header followed by 0 to 2^24 - 1
    153 // bytes of data.
    154 //
    155 // Additional size limits can be set by specific application uses. HTTP limits the frame size to
    156 // 16,384 octets by default, though this can be increased by a receiver.
    157 //
    158 //      0                   1                   2                   3
    159 //      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
    160 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    161 //     |                 Length (24)                   |
    162 //     +---------------+---------------+---------------+
    163 //     |   Type (8)    |   Flags (8)   |
    164 //     +-+-----------------------------+---------------+---------------+
    165 //     |R|                 Stream Identifier (31)                      |
    166 //     +-+-------------------------------------------------------------+
    167 //     |                     Frame Data (0...)                       ...
    168 //     +---------------------------------------------------------------+
    169 //
    170 // The fields of the frame header are defined as:
    171 //
    172 // * Length:
    173 //   The length of the frame data expressed as an unsigned 24-bit integer. The 9 bytes of the frame
    174 //   header are not included in this value.
    175 //
    176 // * Type:
    177 //   The 8-bit type of the frame. The frame type determines how the remainder of the frame header
    178 //   and data are interpreted. Implementations MUST ignore unsupported and unrecognized frame types.
    179 //
    180 // * Flags:
    181 //   An 8-bit field reserved for frame-type specific boolean flags.
    182 //
    183 //   Flags are assigned semantics specific to the indicated frame type. Flags that have no defined
    184 //   semantics for a particular frame type MUST be ignored, and MUST be left unset (0) when sending.
    185 //
    186 // * R:
    187 //   A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST remain unset
    188 //   (0) when sending and MUST be ignored when receiving.
    189 //
    190 // * Stream Identifier:
    191 //   A 31-bit stream identifier. The value 0 is reserved for frames that are associated with the
    192 //   connection as a whole as opposed to an individual stream.
    193 //
    194 // The structure and content of the remaining frame data is dependent entirely on the frame type.
    195 
    196 var COMMON_HEADER_SIZE = 9;
    197 
    198 var frameTypes = [];
    199 
    200 var frameFlags = {};
    201 
    202 var genericAttributes = ['type', 'flags', 'stream'];
    203 
    204 var typeSpecificAttributes = {};
    205 
    206 Serializer.commonHeader = function writeCommonHeader(frame, buffers) {
    207  var headerBuffer = Buffer.alloc(COMMON_HEADER_SIZE);
    208 
    209  var size = 0;
    210  for (var i = 0; i < buffers.length; i++) {
    211    size += buffers[i].length;
    212  }
    213  headerBuffer.writeUInt8(0, 0);
    214  headerBuffer.writeUInt16BE(size, 1);
    215 
    216  var typeId = frameTypes.indexOf(frame.type);  // If we are here then the type is valid for sure
    217  headerBuffer.writeUInt8(typeId, 3);
    218 
    219  var flagByte = 0;
    220  for (var flag in frame.flags) {
    221    var position = frameFlags[frame.type].indexOf(flag);
    222    assert(position !== -1, 'Unknown flag for frame type ' + frame.type + ': ' + flag);
    223    if (frame.flags[flag]) {
    224      flagByte |= (1 << position);
    225    }
    226  }
    227  headerBuffer.writeUInt8(flagByte, 4);
    228 
    229  assert((0 <= frame.stream) && (frame.stream < 0x7fffffff), frame.stream);
    230  headerBuffer.writeUInt32BE(frame.stream || 0, 5);
    231 
    232  buffers.unshift(headerBuffer);
    233 
    234  return size;
    235 };
    236 
    237 Deserializer.commonHeader = function readCommonHeader(buffer, frame) {
    238  if (buffer.length < 9) {
    239    return 'FRAME_SIZE_ERROR';
    240  }
    241 
    242  var totallyWastedByte = buffer.readUInt8(0);
    243  var length = buffer.readUInt16BE(1);
    244  // We do this just for sanity checking later on, to make sure no one sent us a
    245  // frame that's super large.
    246  length += totallyWastedByte << 16;
    247 
    248  frame.type = frameTypes[buffer.readUInt8(3)];
    249  if (!frame.type) {
    250    // We are required to ignore unknown frame types
    251    return length;
    252  }
    253 
    254  frame.flags = {};
    255  var flagByte = buffer.readUInt8(4);
    256  var definedFlags = frameFlags[frame.type];
    257  for (var i = 0; i < definedFlags.length; i++) {
    258    frame.flags[definedFlags[i]] = Boolean(flagByte & (1 << i));
    259  }
    260 
    261  frame.stream = buffer.readUInt32BE(5) & 0x7fffffff;
    262 
    263  return length;
    264 };
    265 
    266 // Frame types
    267 // ===========
    268 
    269 // Every frame type is registered in the following places:
    270 //
    271 // * `frameTypes`: a register of frame type codes (used by `commonHeader()`)
    272 // * `frameFlags`: a register of valid flags for frame types (used by `commonHeader()`)
    273 // * `typeSpecificAttributes`: a register of frame specific frame object attributes (used by
    274 //   logging code and also serves as documentation for frame objects)
    275 
    276 // [DATA Frames](https://tools.ietf.org/html/rfc7540#section-6.1)
    277 // ------------------------------------------------------------
    278 //
    279 // DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a
    280 // stream.
    281 //
    282 // The DATA frame defines the following flags:
    283 //
    284 // * END_STREAM (0x1):
    285 //   Bit 1 being set indicates that this frame is the last that the endpoint will send for the
    286 //   identified stream.
    287 // * PADDED (0x08):
    288 //   Bit 4 being set indicates that the Pad Length field is present.
    289 
    290 frameTypes[0x0] = 'DATA';
    291 
    292 frameFlags.DATA = ['END_STREAM', 'RESERVED2', 'RESERVED4', 'PADDED'];
    293 
    294 typeSpecificAttributes.DATA = ['data'];
    295 
    296 Serializer.DATA = function writeData(frame, buffers) {
    297  buffers.push(frame.data);
    298 };
    299 
    300 Deserializer.DATA = function readData(buffer, frame) {
    301  var dataOffset = 0;
    302  var paddingLength = 0;
    303  if (frame.flags.PADDED) {
    304    if (buffer.length < 1) {
    305      // We must have at least one byte for padding control, but we don't. Bad peer!
    306      return 'FRAME_SIZE_ERROR';
    307    }
    308    paddingLength = (buffer.readUInt8(dataOffset) & 0xff);
    309    dataOffset = 1;
    310  }
    311 
    312  if (paddingLength) {
    313    if (paddingLength >= (buffer.length - 1)) {
    314      // We don't have enough room for the padding advertised - bad peer!
    315      return 'FRAME_SIZE_ERROR';
    316    }
    317    frame.data = buffer.slice(dataOffset, -1 * paddingLength);
    318  } else {
    319    frame.data = buffer.slice(dataOffset);
    320  }
    321 };
    322 
    323 // [HEADERS](https://tools.ietf.org/html/rfc7540#section-6.2)
    324 // --------------------------------------------------------------
    325 //
    326 // The HEADERS frame (type=0x1) allows the sender to create a stream.
    327 //
    328 // The HEADERS frame defines the following flags:
    329 //
    330 // * END_STREAM (0x1):
    331 //   Bit 1 being set indicates that this frame is the last that the endpoint will send for the
    332 //   identified stream.
    333 // * END_HEADERS (0x4):
    334 //   The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide
    335 //   a complete set of headers.
    336 // * PADDED (0x08):
    337 //   Bit 4 being set indicates that the Pad Length field is present.
    338 // * PRIORITY (0x20):
    339 //   Bit 6 being set indicates that the Exlusive Flag (E), Stream Dependency, and Weight fields are
    340 //   present.
    341 
    342 frameTypes[0x1] = 'HEADERS';
    343 
    344 frameFlags.HEADERS = ['END_STREAM', 'RESERVED2', 'END_HEADERS', 'PADDED', 'RESERVED5', 'PRIORITY'];
    345 
    346 typeSpecificAttributes.HEADERS = ['priorityDependency', 'priorityWeight', 'exclusiveDependency', 'headers', 'data'];
    347 
    348 //      0                   1                   2                   3
    349 //      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
    350 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    351 //     |Pad Length? (8)|
    352 //     +-+-------------+---------------+-------------------------------+
    353 //     |E|                 Stream Dependency? (31)                     |
    354 //     +-+-------------+-----------------------------------------------+
    355 //     |  Weight? (8)  |
    356 //     +-+-------------+-----------------------------------------------+
    357 //     |                   Header Block Fragment (*)                 ...
    358 //     +---------------------------------------------------------------+
    359 //     |                           Padding (*)                       ...
    360 //     +---------------------------------------------------------------+
    361 //
    362 // The payload of a HEADERS frame contains a Headers Block
    363 
    364 Serializer.HEADERS = function writeHeadersPriority(frame, buffers) {
    365  if (frame.flags.PRIORITY) {
    366    var buffer = Buffer.alloc(5);
    367    assert((0 <= frame.priorityDependency) && (frame.priorityDependency <= 0x7fffffff), frame.priorityDependency);
    368    buffer.writeUInt32BE(frame.priorityDependency, 0);
    369    if (frame.exclusiveDependency) {
    370      buffer[0] |= 0x80;
    371    }
    372    assert((0 <= frame.priorityWeight) && (frame.priorityWeight <= 0xff), frame.priorityWeight);
    373    buffer.writeUInt8(frame.priorityWeight, 4);
    374    buffers.push(buffer);
    375  }
    376  buffers.push(frame.data);
    377 };
    378 
    379 Deserializer.HEADERS = function readHeadersPriority(buffer, frame) {
    380  var minFrameLength = 0;
    381  if (frame.flags.PADDED) {
    382    minFrameLength += 1;
    383  }
    384  if (frame.flags.PRIORITY) {
    385    minFrameLength += 5;
    386  }
    387  if (buffer.length < minFrameLength) {
    388    // Peer didn't send enough data - bad peer!
    389    return 'FRAME_SIZE_ERROR';
    390  }
    391 
    392  var dataOffset = 0;
    393  var paddingLength = 0;
    394  if (frame.flags.PADDED) {
    395    paddingLength = (buffer.readUInt8(dataOffset) & 0xff);
    396    dataOffset = 1;
    397  }
    398 
    399  if (frame.flags.PRIORITY) {
    400    var dependencyData = Buffer.alloc(4);
    401    buffer.copy(dependencyData, 0, dataOffset, dataOffset + 4);
    402    dataOffset += 4;
    403    frame.exclusiveDependency = !!(dependencyData[0] & 0x80);
    404    dependencyData[0] &= 0x7f;
    405    frame.priorityDependency = dependencyData.readUInt32BE(0);
    406    frame.priorityWeight = buffer.readUInt8(dataOffset);
    407    dataOffset += 1;
    408  }
    409 
    410  if (paddingLength) {
    411    if ((buffer.length - dataOffset) < paddingLength) {
    412      // Not enough data left to satisfy the advertised padding - bad peer!
    413      return 'FRAME_SIZE_ERROR';
    414    }
    415    frame.data = buffer.slice(dataOffset, -1 * paddingLength);
    416  } else {
    417    frame.data = buffer.slice(dataOffset);
    418  }
    419 };
    420 
    421 // [PRIORITY](https://tools.ietf.org/html/rfc7540#section-6.3)
    422 // -------------------------------------------------------
    423 //
    424 // The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream.
    425 //
    426 // The PRIORITY frame does not define any flags.
    427 
    428 frameTypes[0x2] = 'PRIORITY';
    429 
    430 frameFlags.PRIORITY = [];
    431 
    432 typeSpecificAttributes.PRIORITY = ['priorityDependency', 'priorityWeight', 'exclusiveDependency'];
    433 
    434 //      0                   1                   2                   3
    435 //      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
    436 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    437 //     |E|                 Stream Dependency? (31)                     |
    438 //     +-+-------------+-----------------------------------------------+
    439 //     |  Weight? (8)  |
    440 //     +-+-------------+
    441 //
    442 // The payload of a PRIORITY frame contains an exclusive bit, a 31-bit dependency, and an 8-bit weight
    443 
    444 Serializer.PRIORITY = function writePriority(frame, buffers) {
    445  var buffer = Buffer.alloc(5);
    446  assert((0 <= frame.priorityDependency) && (frame.priorityDependency <= 0x7fffffff), frame.priorityDependency);
    447  buffer.writeUInt32BE(frame.priorityDependency, 0);
    448  if (frame.exclusiveDependency) {
    449    buffer[0] |= 0x80;
    450  }
    451  assert((0 <= frame.priorityWeight) && (frame.priorityWeight <= 0xff), frame.priorityWeight);
    452  buffer.writeUInt8(frame.priorityWeight, 4);
    453 
    454  buffers.push(buffer);
    455 };
    456 
    457 Deserializer.PRIORITY = function readPriority(buffer, frame) {
    458  if (buffer.length < 5) {
    459    // PRIORITY frames are 5 bytes long. Bad peer!
    460    return 'FRAME_SIZE_ERROR';
    461  }
    462  var dependencyData = Buffer.alloc(4);
    463  buffer.copy(dependencyData, 0, 0, 4);
    464  frame.exclusiveDependency = !!(dependencyData[0] & 0x80);
    465  dependencyData[0] &= 0x7f;
    466  frame.priorityDependency = dependencyData.readUInt32BE(0);
    467  frame.priorityWeight = buffer.readUInt8(4);
    468 };
    469 
    470 // [RST_STREAM](https://tools.ietf.org/html/rfc7540#section-6.4)
    471 // -----------------------------------------------------------
    472 //
    473 // The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream.
    474 //
    475 // No type-flags are defined.
    476 
    477 frameTypes[0x3] = 'RST_STREAM';
    478 
    479 frameFlags.RST_STREAM = [];
    480 
    481 typeSpecificAttributes.RST_STREAM = ['error'];
    482 
    483 //      0                   1                   2                   3
    484 //      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
    485 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    486 //     |                         Error Code (32)                       |
    487 //     +---------------------------------------------------------------+
    488 //
    489 // The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error
    490 // code (see Error Codes). The error code indicates why the stream is being terminated.
    491 
    492 Serializer.RST_STREAM = function writeRstStream(frame, buffers) {
    493  var buffer = Buffer.alloc(4);
    494  var code = errorCodes.indexOf(frame.error);
    495  assert((0 <= code) && (code <= 0xffffffff), code);
    496  buffer.writeUInt32BE(code, 0);
    497  buffers.push(buffer);
    498 };
    499 
    500 Deserializer.RST_STREAM = function readRstStream(buffer, frame) {
    501  if (buffer.length < 4) {
    502    // RST_STREAM is 4 bytes long. Bad peer!
    503    return 'FRAME_SIZE_ERROR';
    504  }
    505  frame.error = errorCodes[buffer.readUInt32BE(0)];
    506  if (!frame.error) {
    507    // Unknown error codes are considered equivalent to INTERNAL_ERROR
    508    frame.error = 'INTERNAL_ERROR';
    509  }
    510 };
    511 
    512 // [SETTINGS](https://tools.ietf.org/html/rfc7540#section-6.5)
    513 // -------------------------------------------------------
    514 //
    515 // The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints
    516 // communicate.
    517 //
    518 // The SETTINGS frame defines the following flag:
    519 
    520 // * ACK (0x1):
    521 //   Bit 1 being set indicates that this frame acknowledges receipt and application of the peer's
    522 //   SETTINGS frame.
    523 frameTypes[0x4] = 'SETTINGS';
    524 
    525 frameFlags.SETTINGS = ['ACK'];
    526 
    527 typeSpecificAttributes.SETTINGS = ['settings'];
    528 
    529 // The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of a
    530 // 16-bit identifier, and an unsigned 32-bit value.
    531 //
    532 //      0                   1                   2                   3
    533 //      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
    534 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    535 //     |         Identifier(16)          |        Value (32)           |
    536 //     +-----------------+---------------------------------------------+
    537 //     ...Value                          |
    538 //     +---------------------------------+
    539 //
    540 // Each setting in a SETTINGS frame replaces the existing value for that setting.  Settings are
    541 // processed in the order in which they appear, and a receiver of a SETTINGS frame does not need to
    542 // maintain any state other than the current value of settings.  Therefore, the value of a setting
    543 // is the last value that is seen by a receiver. This permits the inclusion of the same settings
    544 // multiple times in the same SETTINGS frame, though doing so does nothing other than waste
    545 // connection capacity.
    546 
    547 Serializer.SETTINGS = function writeSettings(frame, buffers) {
    548  var settings = [], settingsLeft = Object.keys(frame.settings);
    549  definedSettings.forEach(function(setting, id) {
    550    if (setting.name in frame.settings) {
    551      settingsLeft.splice(settingsLeft.indexOf(setting.name), 1);
    552      var value = frame.settings[setting.name];
    553      settings.push({ id: id, value: setting.flag ? Boolean(value) : value });
    554    }
    555  });
    556  assert(settingsLeft.length === 0, 'Unknown settings: ' + settingsLeft.join(', '));
    557 
    558  var buffer = Buffer.alloc(settings.length * 6);
    559  for (var i = 0; i < settings.length; i++) {
    560    buffer.writeUInt16BE(settings[i].id & 0xffff, i*6);
    561    buffer.writeUInt32BE(settings[i].value, i*6 + 2);
    562  }
    563 
    564  buffers.push(buffer);
    565 };
    566 
    567 Deserializer.SETTINGS = function readSettings(buffer, frame, role) {
    568  frame.settings = {};
    569 
    570  // Receipt of a SETTINGS frame with the ACK flag set and a length
    571  // field value other than 0 MUST be treated as a connection error
    572  // (Section 5.4.1) of type FRAME_SIZE_ERROR.
    573  if(frame.flags.ACK && buffer.length != 0) {
    574    return 'FRAME_SIZE_ERROR';
    575  }
    576 
    577  if (buffer.length % 6 !== 0) {
    578    return 'PROTOCOL_ERROR';
    579  }
    580  for (var i = 0; i < buffer.length / 6; i++) {
    581    var id = buffer.readUInt16BE(i*6) & 0xffff;
    582    var setting = definedSettings[id];
    583    if (setting) {
    584      if (role == 'CLIENT' && setting.name == 'SETTINGS_ENABLE_PUSH') {
    585        return 'SETTINGS frame on client got SETTINGS_ENABLE_PUSH';
    586      }
    587      var value = buffer.readUInt32BE(i*6 + 2);
    588      frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value;
    589    }
    590  }
    591 };
    592 
    593 // The following settings are defined:
    594 var definedSettings = [];
    595 
    596 // * SETTINGS_HEADER_TABLE_SIZE (1):
    597 //   Allows the sender to inform the remote endpoint of the size of the header compression table
    598 //   used to decode header blocks.
    599 definedSettings[1] = { name: 'SETTINGS_HEADER_TABLE_SIZE', flag: false };
    600 
    601 // * SETTINGS_ENABLE_PUSH (2):
    602 //   This setting can be use to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame
    603 //   if it receives this setting set to a value of 0. The default value is 1, which indicates that
    604 //   push is permitted.
    605 definedSettings[2] = { name: 'SETTINGS_ENABLE_PUSH', flag: true };
    606 
    607 // * SETTINGS_MAX_CONCURRENT_STREAMS (3):
    608 //   indicates the maximum number of concurrent streams that the sender will allow.
    609 definedSettings[3] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false };
    610 
    611 // * SETTINGS_INITIAL_WINDOW_SIZE (4):
    612 //   indicates the sender's initial stream window size (in bytes) for new streams.
    613 definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false };
    614 
    615 // * SETTINGS_MAX_FRAME_SIZE (5):
    616 //   indicates the maximum size of a frame the receiver will allow.
    617 definedSettings[5] = { name: 'SETTINGS_MAX_FRAME_SIZE', flag: false };
    618 
    619 // [PUSH_PROMISE](https://tools.ietf.org/html/rfc7540#section-6.6)
    620 // ---------------------------------------------------------------
    621 //
    622 // The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the
    623 // sender intends to initiate.
    624 //
    625 // The PUSH_PROMISE frame defines the following flags:
    626 //
    627 // * END_PUSH_PROMISE (0x4):
    628 //   The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to
    629 //   provide a complete set of headers.
    630 
    631 frameTypes[0x5] = 'PUSH_PROMISE';
    632 
    633 frameFlags.PUSH_PROMISE = ['RESERVED1', 'RESERVED2', 'END_PUSH_PROMISE', 'PADDED'];
    634 
    635 typeSpecificAttributes.PUSH_PROMISE = ['promised_stream', 'headers', 'data'];
    636 
    637 //      0                   1                   2                   3
    638 //      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
    639 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    640 //     |Pad Length? (8)|
    641 //     +-+-------------+-----------------------------------------------+
    642 //     |X|                Promised-Stream-ID (31)                      |
    643 //     +-+-------------------------------------------------------------+
    644 //     |                 Header Block Fragment (*)                   ...
    645 //     +---------------------------------------------------------------+
    646 //     |                         Padding (*)                         ...
    647 //     +---------------------------------------------------------------+
    648 //
    649 // The PUSH_PROMISE frame includes the unsigned 31-bit identifier of
    650 // the stream the endpoint plans to create along with a minimal set of headers that provide
    651 // additional context for the stream.
    652 
    653 Serializer.PUSH_PROMISE = function writePushPromise(frame, buffers) {
    654  var buffer = Buffer.alloc(4);
    655 
    656  var promised_stream = frame.promised_stream;
    657  assert((0 <= promised_stream) && (promised_stream <= 0x7fffffff), promised_stream);
    658  buffer.writeUInt32BE(promised_stream, 0);
    659 
    660  buffers.push(buffer);
    661  buffers.push(frame.data);
    662 };
    663 
    664 Deserializer.PUSH_PROMISE = function readPushPromise(buffer, frame) {
    665  if (buffer.length < 4) {
    666    return 'FRAME_SIZE_ERROR';
    667  }
    668  var dataOffset = 0;
    669  var paddingLength = 0;
    670  if (frame.flags.PADDED) {
    671    if (buffer.length < 5) {
    672      return 'FRAME_SIZE_ERROR';
    673    }
    674    paddingLength = (buffer.readUInt8(dataOffset) & 0xff);
    675    dataOffset = 1;
    676  }
    677  frame.promised_stream = buffer.readUInt32BE(dataOffset) & 0x7fffffff;
    678  dataOffset += 4;
    679  if (paddingLength) {
    680    if ((buffer.length - dataOffset) < paddingLength) {
    681      return 'FRAME_SIZE_ERROR';
    682    }
    683    frame.data = buffer.slice(dataOffset, -1 * paddingLength);
    684  } else {
    685    frame.data = buffer.slice(dataOffset);
    686  }
    687 };
    688 
    689 // [PING](https://tools.ietf.org/html/rfc7540#section-6.7)
    690 // -----------------------------------------------
    691 //
    692 // The PING frame (type=0x6) is a mechanism for measuring a minimal round-trip time from the
    693 // sender, as well as determining whether an idle connection is still functional.
    694 //
    695 // The PING frame defines one type-specific flag:
    696 //
    697 // * ACK (0x1):
    698 //   Bit 1 being set indicates that this PING frame is a PING response.
    699 
    700 frameTypes[0x6] = 'PING';
    701 
    702 frameFlags.PING = ['ACK'];
    703 
    704 typeSpecificAttributes.PING = ['data'];
    705 
    706 // In addition to the frame header, PING frames MUST contain 8 additional octets of opaque data.
    707 
    708 Serializer.PING = function writePing(frame, buffers) {
    709  buffers.push(frame.data);
    710 };
    711 
    712 Deserializer.PING = function readPing(buffer, frame) {
    713  if (buffer.length !== 8) {
    714    return 'FRAME_SIZE_ERROR';
    715  }
    716  frame.data = buffer;
    717 };
    718 
    719 // [GOAWAY](https://tools.ietf.org/html/rfc7540#section-6.8)
    720 // ---------------------------------------------------
    721 //
    722 // The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this connection.
    723 //
    724 // The GOAWAY frame does not define any flags.
    725 
    726 frameTypes[0x7] = 'GOAWAY';
    727 
    728 frameFlags.GOAWAY = [];
    729 
    730 typeSpecificAttributes.GOAWAY = ['last_stream', 'error'];
    731 
    732 //      0                   1                   2                   3
    733 //      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
    734 //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    735 //     |X|                  Last-Stream-ID (31)                        |
    736 //     +-+-------------------------------------------------------------+
    737 //     |                      Error Code (32)                          |
    738 //     +---------------------------------------------------------------+
    739 //
    740 // The last stream identifier in the GOAWAY frame contains the highest numbered stream identifier
    741 // for which the sender of the GOAWAY frame has received frames on and might have taken some action
    742 // on.
    743 //
    744 // The GOAWAY frame also contains a 32-bit error code (see Error Codes) that contains the reason for
    745 // closing the connection.
    746 
    747 Serializer.GOAWAY = function writeGoaway(frame, buffers) {
    748  var buffer = Buffer.alloc(8);
    749 
    750  var last_stream = frame.last_stream;
    751  assert((0 <= last_stream) && (last_stream <= 0x7fffffff), last_stream);
    752  buffer.writeUInt32BE(last_stream, 0);
    753 
    754  var code = errorCodes.indexOf(frame.error);
    755  assert((0 <= code) && (code <= 0xffffffff), code);
    756  buffer.writeUInt32BE(code, 4);
    757 
    758  buffers.push(buffer);
    759 };
    760 
    761 Deserializer.GOAWAY = function readGoaway(buffer, frame) {
    762  if (buffer.length !== 8) {
    763    // GOAWAY must have 8 bytes
    764    return 'FRAME_SIZE_ERROR';
    765  }
    766  frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff;
    767  frame.error = errorCodes[buffer.readUInt32BE(4)];
    768  if (!frame.error) {
    769    // Unknown error types are to be considered equivalent to INTERNAL ERROR
    770    frame.error = 'INTERNAL_ERROR';
    771  }
    772 };
    773 
    774 // [WINDOW_UPDATE](https://tools.ietf.org/html/rfc7540#section-6.9)
    775 // -----------------------------------------------------------------
    776 //
    777 // The WINDOW_UPDATE frame (type=0x8) is used to implement flow control.
    778 //
    779 // The WINDOW_UPDATE frame does not define any flags.
    780 
    781 frameTypes[0x8] = 'WINDOW_UPDATE';
    782 
    783 frameFlags.WINDOW_UPDATE = [];
    784 
    785 typeSpecificAttributes.WINDOW_UPDATE = ['window_size'];
    786 
    787 // The payload of a WINDOW_UPDATE frame is a 32-bit value indicating the additional number of bytes
    788 // that the sender can transmit in addition to the existing flow control window. The legal range
    789 // for this field is 1 to 2^31 - 1 (0x7fffffff) bytes; the most significant bit of this value is
    790 // reserved.
    791 
    792 Serializer.WINDOW_UPDATE = function writeWindowUpdate(frame, buffers) {
    793  var buffer = Buffer.alloc(4);
    794 
    795  var window_size = frame.window_size;
    796  assert((0 < window_size) && (window_size <= 0x7fffffff), window_size);
    797  buffer.writeUInt32BE(window_size, 0);
    798 
    799  buffers.push(buffer);
    800 };
    801 
    802 Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) {
    803  if (buffer.length !== WINDOW_UPDATE_PAYLOAD_SIZE) {
    804    return 'FRAME_SIZE_ERROR';
    805  }
    806  frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff;
    807  if (frame.window_size === 0) {
    808    return 'PROTOCOL_ERROR';
    809  }
    810 };
    811 
    812 // [CONTINUATION](https://tools.ietf.org/html/rfc7540#section-6.10)
    813 // ------------------------------------------------------------
    814 //
    815 // The CONTINUATION frame (type=0x9) is used to continue a sequence of header block fragments.
    816 //
    817 // The CONTINUATION frame defines the following flag:
    818 //
    819 // * END_HEADERS (0x4):
    820 //   The END_HEADERS bit indicates that this frame ends the sequence of header block fragments
    821 //   necessary to provide a complete set of headers.
    822 
    823 frameTypes[0x9] = 'CONTINUATION';
    824 
    825 frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS'];
    826 
    827 typeSpecificAttributes.CONTINUATION = ['headers', 'data'];
    828 
    829 Serializer.CONTINUATION = function writeContinuation(frame, buffers) {
    830  buffers.push(frame.data);
    831 };
    832 
    833 Deserializer.CONTINUATION = function readContinuation(buffer, frame) {
    834  frame.data = buffer;
    835 };
    836 
    837 // [ALTSVC](https://tools.ietf.org/html/rfc7838#section-4)
    838 // ------------------------------------------------------------
    839 //
    840 // The ALTSVC frame (type=0xA) advertises the availability of an alternative service to the client.
    841 //
    842 // The ALTSVC frame does not define any flags.
    843 
    844 frameTypes[0xA] = 'ALTSVC';
    845 
    846 frameFlags.ALTSVC = [];
    847 
    848 //     0                   1                   2                   3
    849 //     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
    850 //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    851 //    |         Origin-Len (16)       | Origin? (*)                 ...
    852 //    +-------------------------------+----------------+--------------+
    853 //    |                   Alt-Svc-Field-Value (*)                   ...
    854 //    +---------------------------------------------------------------+
    855 //
    856 // The ALTSVC frame contains the following fields:
    857 //
    858 // Origin-Len: An unsigned, 16-bit integer indicating the length, in
    859 //    octets, of the Origin field.
    860 //
    861 // Origin: An OPTIONAL sequence of characters containing ASCII
    862 //    serialisation of an origin ([RFC6454](https://tools.ietf.org/html/rfc6454),
    863 //    Section 6.2) that the alternate service is applicable to.
    864 //
    865 // Alt-Svc-Field-Value: A sequence of octets (length determined by
    866 //    subtracting the length of all preceding fields from the frame
    867 //    length) containing a value identical to the Alt-Svc field value
    868 //    defined in (Section 3)[https://tools.ietf.org/html/rfc7838#section-3]
    869 //    (ABNF production "Alt-Svc").
    870 
    871 typeSpecificAttributes.ALTSVC = ['maxAge', 'port', 'protocolID', 'host',
    872                                 'origin'];
    873 
    874 function istchar(c) {
    875  return ('!#$&\'*+-.^_`|~1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.indexOf(c) > -1);
    876 }
    877 
    878 function hexencode(s) {
    879  var t = '';
    880  for (var i = 0; i < s.length; i++) {
    881    if (!istchar(s[i])) {
    882      t += '%';
    883      t += Buffer.from(s[i]).toString('hex');
    884    } else {
    885      t += s[i];
    886    }
    887  }
    888  return t;
    889 }
    890 
    891 Serializer.ALTSVC = function writeAltSvc(frame, buffers) {
    892  var buffer = Buffer.alloc(2);
    893  buffer.writeUInt16BE(frame.origin.length, 0);
    894  buffers.push(buffer);
    895  buffers.push(Buffer.from(frame.origin, 'ascii'));
    896 
    897  var fieldValue = hexencode(frame.protocolID) + '="' + frame.host + ':' + frame.port + '"';
    898  if (frame.maxAge !== 86400) { // 86400 is the default
    899    fieldValue += "; ma=" + frame.maxAge;
    900  }
    901 
    902  buffers.push(Buffer.from(fieldValue, 'ascii'));
    903 };
    904 
    905 function stripquotes(s) {
    906  var start = 0;
    907  var end = s.length;
    908  while ((start < end) && (s[start] === '"')) {
    909    start++;
    910  }
    911  while ((end > start) && (s[end - 1] === '"')) {
    912    end--;
    913  }
    914  if (start >= end) {
    915    return "";
    916  }
    917  return s.substring(start, end);
    918 }
    919 
    920 function splitNameValue(nvpair) {
    921  var eq = -1;
    922  var inQuotes = false;
    923 
    924  for (var i = 0; i < nvpair.length; i++) {
    925    if (nvpair[i] === '"') {
    926      inQuotes = !inQuotes;
    927      continue;
    928    }
    929    if (inQuotes) {
    930      continue;
    931    }
    932    if (nvpair[i] === '=') {
    933      eq = i;
    934      break;
    935    }
    936  }
    937 
    938  if (eq === -1) {
    939    return {'name': nvpair, 'value': null};
    940  }
    941 
    942  var name = stripquotes(nvpair.substring(0, eq).trim());
    943  var value = stripquotes(nvpair.substring(eq + 1).trim());
    944  return {'name': name, 'value': value};
    945 }
    946 
    947 function splitHeaderParameters(hv) {
    948  return parseHeaderValue(hv, ';', splitNameValue);
    949 }
    950 
    951 function parseHeaderValue(hv, separator, callback) {
    952  var start = 0;
    953  var inQuotes = false;
    954  var values = [];
    955 
    956  for (var i = 0; i < hv.length; i++) {
    957    if (hv[i] === '"') {
    958      inQuotes = !inQuotes;
    959      continue;
    960    }
    961    if (inQuotes) {
    962      // Just skip this
    963      continue;
    964    }
    965    if (hv[i] === separator) {
    966      var newValue = hv.substring(start, i).trim();
    967      if (newValue.length > 0) {
    968        newValue = callback(newValue);
    969        values.push(newValue);
    970      }
    971      start = i + 1;
    972    }
    973  }
    974 
    975  var newValue = hv.substring(start).trim();
    976  if (newValue.length > 0) {
    977    newValue = callback(newValue);
    978    values.push(newValue);
    979  }
    980 
    981  return values;
    982 }
    983 
    984 function rsplit(s, delim, count) {
    985  var nsplits = 0;
    986  var end = s.length;
    987  var rval = [];
    988  for (var i = s.length - 1; i >= 0; i--) {
    989    if (s[i] === delim) {
    990      var t = s.substring(i + 1, end);
    991      end = i;
    992      rval.unshift(t);
    993      nsplits++;
    994      if (nsplits === count) {
    995        break;
    996      }
    997    }
    998  }
    999  if (end !== 0) {
   1000    rval.unshift(s.substring(0, end));
   1001  }
   1002  return rval;
   1003 }
   1004 
   1005 function ishex(c) {
   1006  return ('0123456789ABCDEFabcdef'.indexOf(c) > -1);
   1007 }
   1008 
   1009 function unescape(s) {
   1010  var i = 0;
   1011  var t = '';
   1012  while (i < s.length) {
   1013    if (s[i] != '%' || !ishex(s[i + 1]) || !ishex(s[i + 2])) {
   1014      t += s[i];
   1015    } else {
   1016      ++i;
   1017      var hexvalue = '';
   1018      if (i < s.length) {
   1019        hexvalue += s[i];
   1020        ++i;
   1021      }
   1022      if (i < s.length) {
   1023        hexvalue += s[i];
   1024      }
   1025      if (hexvalue.length > 0) {
   1026        t += Buffer.from(hexvalue, 'hex').toString();
   1027      } else {
   1028        t += '%';
   1029      }
   1030    }
   1031 
   1032    ++i;
   1033  }
   1034  return t;
   1035 }
   1036 
   1037 Deserializer.ALTSVC = function readAltSvc(buffer, frame) {
   1038  if (buffer.length < 2) {
   1039    return 'FRAME_SIZE_ERROR';
   1040  }
   1041  var originLength = buffer.readUInt16BE(0);
   1042  if ((buffer.length - 2) < originLength) {
   1043    return 'FRAME_SIZE_ERROR';
   1044  }
   1045  frame.origin = buffer.toString('ascii', 2, 2 + originLength);
   1046  var fieldValue = buffer.toString('ascii', 2 + originLength);
   1047  var values = parseHeaderValue(fieldValue, ',', splitHeaderParameters);
   1048  if (values.length > 1) {
   1049    // TODO - warn that we only use one here
   1050  }
   1051  if (values.length === 0) {
   1052    // Well that's a malformed frame. Just ignore it.
   1053    return;
   1054  }
   1055 
   1056  var chosenAltSvc = values[0];
   1057  frame.maxAge = 86400; // Default
   1058  for (var i = 0; i < chosenAltSvc.length; i++) {
   1059    if (i === 0) {
   1060      // This corresponds to the protocolID="<host>:<port>" item
   1061      frame.protocolID = unescape(chosenAltSvc[i].name);
   1062      var hostport = rsplit(chosenAltSvc[i].value, ':', 1);
   1063      frame.host = hostport[0];
   1064      frame.port = parseInt(hostport[1], 10);
   1065    } else if (chosenAltSvc[i].name == 'ma') {
   1066      frame.maxAge = parseInt(chosenAltSvc[i].value, 10);
   1067    }
   1068    // Otherwise, we just ignore this
   1069  }
   1070 };
   1071 
   1072 // frame 0xB was BLOCKED and some versions of chrome will
   1073 // throw PROTOCOL_ERROR upon seeing it with non 0 payload
   1074 
   1075 frameTypes[0xC] = 'ORIGIN';
   1076 frameFlags.ORIGIN = [];
   1077 typeSpecificAttributes.ORIGIN = ['originList'];
   1078 
   1079 Serializer.ORIGIN = function writeOrigin(frame, buffers) {
   1080  for (var i = 0; i < frame.originList.length; i++) {
   1081    var buffer = Buffer.alloc(2);
   1082    buffer.writeUInt16BE(frame.originList[i].length, 0);
   1083    buffers.push(buffer);
   1084    buffers.push(Buffer.from(frame.originList[i], 'ascii'));
   1085  }
   1086 };
   1087 
   1088 Deserializer.ORIGIN = function readOrigin(buffer, frame) {
   1089    // ignored
   1090 };
   1091 
   1092 
   1093 // [Error Codes](https://tools.ietf.org/html/rfc7540#section-7)
   1094 // ------------------------------------------------------------
   1095 
   1096 var errorCodes = [
   1097  'NO_ERROR',
   1098  'PROTOCOL_ERROR',
   1099  'INTERNAL_ERROR',
   1100  'FLOW_CONTROL_ERROR',
   1101  'SETTINGS_TIMEOUT',
   1102  'STREAM_CLOSED',
   1103  'FRAME_SIZE_ERROR',
   1104  'REFUSED_STREAM',
   1105  'CANCEL',
   1106  'COMPRESSION_ERROR',
   1107  'CONNECT_ERROR',
   1108  'ENHANCE_YOUR_CALM',
   1109  'INADEQUATE_SECURITY',
   1110  'HTTP_1_1_REQUIRED'
   1111 ];
   1112 
   1113 // Logging
   1114 // -------
   1115 
   1116 // [Bunyan serializers](https://github.com/trentm/node-bunyan#serializers) to improve logging output
   1117 // for debug messages emitted in this component.
   1118 exports.serializers = {};
   1119 
   1120 // * `frame` serializer: it transforms data attributes from Buffers to hex strings and filters out
   1121 //   flags that are not present.
   1122 var frameCounter = 0;
   1123 exports.serializers.frame = function(frame) {
   1124  if (!frame) {
   1125    return null;
   1126  }
   1127 
   1128  if ('id' in frame) {
   1129    return frame.id;
   1130  }
   1131 
   1132  frame.id = frameCounter;
   1133  frameCounter += 1;
   1134 
   1135  var logEntry = { id: frame.id };
   1136  genericAttributes.concat(typeSpecificAttributes[frame.type]).forEach(function(name) {
   1137    logEntry[name] = frame[name];
   1138  });
   1139 
   1140  if (frame.data instanceof Buffer) {
   1141    if (logEntry.data.length > 50) {
   1142      logEntry.data = frame.data.slice(0, 47).toString('hex') + '...';
   1143    } else {
   1144      logEntry.data = frame.data.toString('hex');
   1145    }
   1146 
   1147    if (!('length' in logEntry)) {
   1148      logEntry.length = frame.data.length;
   1149    }
   1150  }
   1151 
   1152  if (frame.promised_stream instanceof Object) {
   1153    logEntry.promised_stream = 'stream-' + frame.promised_stream.id;
   1154  }
   1155 
   1156  logEntry.flags = Object.keys(frame.flags || {}).filter(function(name) {
   1157    return frame.flags[name] === true;
   1158  });
   1159 
   1160  return logEntry;
   1161 };
   1162 
   1163 // * `data` serializer: it simply transforms a buffer to a hex string.
   1164 exports.serializers.data = function(data) {
   1165  return data.toString('hex');
   1166 };