| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 | 'use strict'const assert = require('assert')const Buffer = require('buffer').Bufferconst realZlib = require('zlib')const constants = exports.constants = require('./constants.js')const Minipass = require('minipass')const OriginalBufferConcat = Buffer.concatconst _superWrite = Symbol('_superWrite')class ZlibError extends Error {  constructor (err) {    super('zlib: ' + err.message)    this.code = err.code    this.errno = err.errno    /* istanbul ignore if */    if (!this.code)      this.code = 'ZLIB_ERROR'    this.message = 'zlib: ' + err.message    Error.captureStackTrace(this, this.constructor)  }  get name () {    return 'ZlibError'  }}// the Zlib class they all inherit from// This thing manages the queue of requests, and returns// true or false if there is anything in the queue when// you call the .write() method.const _opts = Symbol('opts')const _flushFlag = Symbol('flushFlag')const _finishFlushFlag = Symbol('finishFlushFlag')const _fullFlushFlag = Symbol('fullFlushFlag')const _handle = Symbol('handle')const _onError = Symbol('onError')const _sawError = Symbol('sawError')const _level = Symbol('level')const _strategy = Symbol('strategy')const _ended = Symbol('ended')const _defaultFullFlush = Symbol('_defaultFullFlush')class ZlibBase extends Minipass {  constructor (opts, mode) {    if (!opts || typeof opts !== 'object')      throw new TypeError('invalid options for ZlibBase constructor')    super(opts)    this[_sawError] = false    this[_ended] = false    this[_opts] = opts    this[_flushFlag] = opts.flush    this[_finishFlushFlag] = opts.finishFlush    // this will throw if any options are invalid for the class selected    try {      this[_handle] = new realZlib[mode](opts)    } catch (er) {      // make sure that all errors get decorated properly      throw new ZlibError(er)    }    this[_onError] = (err) => {      // no sense raising multiple errors, since we abort on the first one.      if (this[_sawError])        return      this[_sawError] = true      // there is no way to cleanly recover.      // continuing only obscures problems.      this.close()      this.emit('error', err)    }    this[_handle].on('error', er => this[_onError](new ZlibError(er)))    this.once('end', () => this.close)  }  close () {    if (this[_handle]) {      this[_handle].close()      this[_handle] = null      this.emit('close')    }  }  reset () {    if (!this[_sawError]) {      assert(this[_handle], 'zlib binding closed')      return this[_handle].reset()    }  }  flush (flushFlag) {    if (this.ended)      return    if (typeof flushFlag !== 'number')      flushFlag = this[_fullFlushFlag]    this.write(Object.assign(Buffer.alloc(0), { [_flushFlag]: flushFlag }))  }  end (chunk, encoding, cb) {    if (chunk)      this.write(chunk, encoding)    this.flush(this[_finishFlushFlag])    this[_ended] = true    return super.end(null, null, cb)  }  get ended () {    return this[_ended]  }  write (chunk, encoding, cb) {    // process the chunk using the sync process    // then super.write() all the outputted chunks    if (typeof encoding === 'function')      cb = encoding, encoding = 'utf8'    if (typeof chunk === 'string')      chunk = Buffer.from(chunk, encoding)    if (this[_sawError])      return    assert(this[_handle], 'zlib binding closed')    // _processChunk tries to .close() the native handle after it's done, so we    // intercept that by temporarily making it a no-op.    const nativeHandle = this[_handle]._handle    const originalNativeClose = nativeHandle.close    nativeHandle.close = () => {}    const originalClose = this[_handle].close    this[_handle].close = () => {}    // It also calls `Buffer.concat()` at the end, which may be convenient    // for some, but which we are not interested in as it slows us down.    Buffer.concat = (args) => args    let result    try {      const flushFlag = typeof chunk[_flushFlag] === 'number'        ? chunk[_flushFlag] : this[_flushFlag]      result = this[_handle]._processChunk(chunk, flushFlag)      // if we don't throw, reset it back how it was      Buffer.concat = OriginalBufferConcat    } catch (err) {      // or if we do, put Buffer.concat() back before we emit error      // Error events call into user code, which may call Buffer.concat()      Buffer.concat = OriginalBufferConcat      this[_onError](new ZlibError(err))    } finally {      if (this[_handle]) {        // Core zlib resets `_handle` to null after attempting to close the        // native handle. Our no-op handler prevented actual closure, but we        // need to restore the `._handle` property.        this[_handle]._handle = nativeHandle        nativeHandle.close = originalNativeClose        this[_handle].close = originalClose        // `_processChunk()` adds an 'error' listener. If we don't remove it        // after each call, these handlers start piling up.        this[_handle].removeAllListeners('error')        // make sure OUR error listener is still attached tho      }    }    if (this[_handle])      this[_handle].on('error', er => this[_onError](new ZlibError(er)))    let writeReturn    if (result) {      if (Array.isArray(result) && result.length > 0) {        // The first buffer is always `handle._outBuffer`, which would be        // re-used for later invocations; so, we always have to copy that one.        writeReturn = this[_superWrite](Buffer.from(result[0]))        for (let i = 1; i < result.length; i++) {          writeReturn = this[_superWrite](result[i])        }      } else {        writeReturn = this[_superWrite](Buffer.from(result))      }    }    if (cb)      cb()    return writeReturn  }  [_superWrite] (data) {    return super.write(data)  }}class Zlib extends ZlibBase {  constructor (opts, mode) {    opts = opts || {}    opts.flush = opts.flush || constants.Z_NO_FLUSH    opts.finishFlush = opts.finishFlush || constants.Z_FINISH    super(opts, mode)    this[_fullFlushFlag] = constants.Z_FULL_FLUSH    this[_level] = opts.level    this[_strategy] = opts.strategy  }  params (level, strategy) {    if (this[_sawError])      return    if (!this[_handle])      throw new Error('cannot switch params when binding is closed')    // no way to test this without also not supporting params at all    /* istanbul ignore if */    if (!this[_handle].params)      throw new Error('not supported in this implementation')    if (this[_level] !== level || this[_strategy] !== strategy) {      this.flush(constants.Z_SYNC_FLUSH)      assert(this[_handle], 'zlib binding closed')      // .params() calls .flush(), but the latter is always async in the      // core zlib. We override .flush() temporarily to intercept that and      // flush synchronously.      const origFlush = this[_handle].flush      this[_handle].flush = (flushFlag, cb) => {        this.flush(flushFlag)        cb()      }      try {        this[_handle].params(level, strategy)      } finally {        this[_handle].flush = origFlush      }      /* istanbul ignore else */      if (this[_handle]) {        this[_level] = level        this[_strategy] = strategy      }    }  }}// minimal 2-byte headerclass Deflate extends Zlib {  constructor (opts) {    super(opts, 'Deflate')  }}class Inflate extends Zlib {  constructor (opts) {    super(opts, 'Inflate')  }}// gzip - bigger header, same deflate compressionconst _portable = Symbol('_portable')class Gzip extends Zlib {  constructor (opts) {    super(opts, 'Gzip')    this[_portable] = opts && !!opts.portable  }  [_superWrite] (data) {    if (!this[_portable])      return super[_superWrite](data)    // we'll always get the header emitted in one first chunk    // overwrite the OS indicator byte with 0xFF    this[_portable] = false    data[9] = 255    return super[_superWrite](data)  }}class Gunzip extends Zlib {  constructor (opts) {    super(opts, 'Gunzip')  }}// raw - no headerclass DeflateRaw extends Zlib {  constructor (opts) {    super(opts, 'DeflateRaw')  }}class InflateRaw extends Zlib {  constructor (opts) {    super(opts, 'InflateRaw')  }}// auto-detect header.class Unzip extends Zlib {  constructor (opts) {    super(opts, 'Unzip')  }}class Brotli extends ZlibBase {  constructor (opts, mode) {    opts = opts || {}    opts.flush = opts.flush || constants.BROTLI_OPERATION_PROCESS    opts.finishFlush = opts.finishFlush || constants.BROTLI_OPERATION_FINISH    super(opts, mode)    this[_fullFlushFlag] = constants.BROTLI_OPERATION_FLUSH  }}class BrotliCompress extends Brotli {  constructor (opts) {    super(opts, 'BrotliCompress')  }}class BrotliDecompress extends Brotli {  constructor (opts) {    super(opts, 'BrotliDecompress')  }}exports.Deflate = Deflateexports.Inflate = Inflateexports.Gzip = Gzipexports.Gunzip = Gunzipexports.DeflateRaw = DeflateRawexports.InflateRaw = InflateRawexports.Unzip = Unzip/* istanbul ignore else */if (typeof realZlib.BrotliCompress === 'function') {  exports.BrotliCompress = BrotliCompress  exports.BrotliDecompress = BrotliDecompress} else {  exports.BrotliCompress = exports.BrotliDecompress = class {    constructor () {      throw new Error('Brotli is not supported in this version of Node.js')    }  }}
 |