dtudury/Http2Lite

View on GitHub
lib/utils/HpackContexts/HpackEncodingContext.js

Summary

Maintainability
A
3 hrs
Test Coverage
const { hpackStaticTable } = require('../../constants')
const { concat, ui8aFromString } = require('../ui8aHelpers')
const { encodeHeader, encodeStringLiteral, encodeHuffman } = require('../codecs')
const { INDEXED, INCREMENTAL, SIZE_UPDATE } = require('../../constants')
const HpackContext = require('./HpackContext')

/**
 * @typedef {Object} HeaderField
 * @property {number} kind
 * @property {string} name
 * @property {string} value
 * @property {number} maxSize
 */

module.exports = class HpackEncodingContext extends HpackContext {
  /**
   * @private
   * @param {HeaderField} headerField
   * @returns {Uint8Array}
   */
  _encodeHeaderField (headerField) {
    let kind = headerField.kind
    if (kind == null) {
      kind = INCREMENTAL
    }
    let header
    if (kind === SIZE_UPDATE) {
      this.maximumTableSize = headerField.maxSize
      return encodeHeader(headerField.kind, headerField.maxSize)
    }
    let index = _findInTables(headerField.name, headerField.value, [hpackStaticTable, this._dynamicTable])
    if (index.indexed === 'both') {
      header = encodeHeader(INDEXED, index.index)
    } else {
      if (kind === INCREMENTAL) {
        this._addEntry(headerField.name, headerField.value)
      }
      if (index.indexed === 'name') {
        header = encodeHeader(kind, index.index, encodeString(headerField.value))
      } else {
        header = encodeHeader(kind, 0, encodeString(headerField.name), encodeString(headerField.value))
      }
    }
    return header
  }

  /**
   * @param {Array.<HeaderField>} headerFieldList
   * @returns {Uint8Array}
   */
  encode (headerFieldList) {
    return concat(headerFieldList.map(this._encodeHeaderField.bind(this)))
  }
}

function _findInTables (name, value, tables) {
  let tableOffset = 1
  let nameMatch
  for (let i = 0; i < tables.length; i++) {
    let table = tables[i]
    for (let j = 0; j < table.length; j++) {
      let row = table[j]
      if (row[0] === name) {
        if (row[1] === value) {
          return { indexed: 'both', index: j + tableOffset }
        }
        if (nameMatch == null) {
          nameMatch = j + tableOffset
        }
      }
    }
    tableOffset += table.length
  }
  if (nameMatch != null) {
    return { indexed: 'name', index: nameMatch }
  } else {
    return { indexed: 'none' }
  }
}

function encodeString (string) {
  let ui8a = ui8aFromString(string)
  let huffmanEncoded = encodeHuffman(ui8a)
  if (huffmanEncoded.length < ui8a.length) {
    return encodeStringLiteral(true, huffmanEncoded)
  } else {
    return encodeStringLiteral(false, ui8a)
  }
}