Source: defrag.js

/**
struct {
  ContentType type;         # 1 byte
  ProtocolVersion version;  # 2 bytes
  uint16 length;            # 2 bytes
  opaque fragment[TLSPlaintext.length];
} TLSPlaintext;

struct {
  ContentType type;         # same as TLSPlaintext.type
  ProtocolVersion version;  # same as TLSPlaintext.version
  uint16 length;
  opaque fragment[TLSCompressed.length];
} TLSCompressed;

struct {
  ContentType type;
  ProtocolVersion version;
  uint16 length;
  select (SecurityParameters.cipher_type) {
    case stream: GenericStreamCipher;
    case block:  GenericBlockCipher;
    case aead:   GenericAEADCipher;
  } fragment;
} TLSCiphertext;

length: from 1 
  2^14 (plain text), 
  2^14 + 1024 (compressed)
  2^14 + 2048 (ciphered)
*/


/**
 * @typedef {Object} Fragment
 * @property {Number} type - content type
 * @property {Buffer} data - fragment data
 */

/**
 * Defragger generates fragments from raw (socket) data
 */
class Defragger {
  /**
   * constructs a defragger
   */
  contructor () {
    /**
     * input buffer
     * @member
     * @type {Buffer}
     */
    this.data = alloc(0)

    /**
     * @member
     * @type {Decipher}
     */
    this.decipher = null
  }

  // inclusive
  maxLength () {
    if (this.decipher) {
      return Math.pow(2, 14) + 2048
    } else {
      return Math.pow(2, 14)
    }
  }

  /**
  append input data
  */
  append (data) {
    this.data = Buffer.concat([this.data, data])
  }

  /**
  @return {Fragment|null} 
  */
  read () {
    if (this.data.length < 1) return null
    const type = this.data[0]
    if (type < 20 || type > 23) {
      // TODO add error code in this function
      const err = new Error('bad content type')
      throw err
    }

    if (this.data.length < 3) return null
    const version = this.data.readUInt16BE(1)
    if (version !== 0x0303) {
      const err = new Error('bad protocol version')
      throw err
    }
    
    if (this.data.length < 5) return null
    const length = this.data.readUInt16BE(3) 

    if (length === 0 || length > this.maxLength) {
      const err = new Error('bad content length')   
      throw err
    }
    
    if (this.data.length < 5 + length) return null

    const data = this.data.slice(5, 5 + length)
    this.data = this.data.slice(5 + length)
    return { type, data }
  }
}

module.exports = Defragger