code:
'use strict'

var assert = require('assert')
var thing = require('handle-thing')
var httpDeceiver = require('http-deceiver')
var util = require('util')

function Handle (options, stream, socket) {
  var state = {}
  this._spdyState = state

  state.options = options || {}

  state.stream = stream
  state.socket = null
  state.rawSocket = socket || stream.connection.socket
  state.deceiver = null
  state.ending = false

  var self = this
  thing.call(this, stream, {
    getPeerName: function () {
      return self._getPeerName()
    },
    close: function (callback) {
      return self._closeCallback(callback)
    }
  })

  if (!state.stream) {
    this.on('stream', function (stream) {
      state.stream = stream
    })
  }
}
util.inherits(Handle, thing)
module.exports = Handle

Handle.create = function create (options, stream, socket) {
  return new Handle(options, stream, socket)
}

Handle.prototype._getPeerName = function _getPeerName () {
  var state = this._spdyState

  if (state.rawSocket._getpeername) {
    return state.rawSocket._getpeername()
  }

  return null
}

Handle.prototype._closeCallback = function _closeCallback (callback) {
  var state = this._spdyState
  var stream = state.stream

  if (state.ending) {
    // The .end() method of the stream may be called by us or by the
    // .shutdown() method in our super-class. If the latter has already been
    // called, then calling the .end() method below will have no effect, with
    // the result that the callback will never get executed, leading to an ever
    // so subtle memory leak.
    if (stream._writableState.finished) {
      // NOTE: it is important to call `setImmediate` instead of `nextTick`,
      // since this is how regular `handle.close()` works in node.js core.
      //
      // Using `nextTick` will lead to `net.Socket` emitting `close` before
      // `end` on UV_EOF. This results in aborted request without `end` event.
      setImmediate(callback)
    } else if (stream._writableState.ending) {
      stream.once('finish', function () {
        callback(null)
      })
    } else {
      stream.end(callback)
    }
  } else {
    stream.abort(callback)
  }

  // Only a single end is allowed
  state.ending = false
}

Handle.prototype.getStream = function getStream (callback) {
  var state = this._spdyState

  if (!callback) {
    assert(state.stream)
    return state.stream
  }

  if (state.stream) {
    process.nextTick(function () {
      callback(state.stream)
    })
    return
  }

  this.on('stream', callback)
}

Handle.prototype.assignSocket = function assignSocket (socket, options) {
  var state = this._spdyState

  state.socket = socket
  state.deceiver = httpDeceiver.create(socket, options)

  function onStreamError (err) {
    state.socket.emit('error', err)
  }

  this.getStream(function (stream) {
    stream.on('error', onStreamError)
  })
}

Handle.prototype.assignClientRequest = function assignClientRequest (req) {
  var state = this._spdyState
  var oldEnd = req.end
  var oldSend = req._send

  // Catch the headers before request will be sent
  var self = this

  // For old nodes
  if (thing.mode !== 'modern') {
    req.end = function end () {
      this.end = oldEnd

      this._send('')

      return this.end.apply(this, arguments)
    }
  }

  req._send = function send (data) {
    this._headerSent = true

    // for v0.10 and below, otherwise it will set `hot = false` and include
    // headers in first write
    this._header = 'ignore me'

    // To prevent exception
    this.connection = state.socket

    // It is very important to leave this here, otherwise it will be executed
    // on a next tick, after `_send` will perform write
    self.getStream(function (stream) {
      if (!stream.connection._isGoaway(stream.id)) {
        stream.send()
      }
    })

    // We are ready to create stream
    self.emit('needStream')

    // Ensure that the connection is still ok to use
    if (state.stream && state.stream.connection._isGoaway(state.stream.id)) {
      return
    }

    req._send = oldSend

    // Ignore empty writes
    if (req.method === 'GET' && data.length === 0) {
      return
    }

    return req._send.apply(this, arguments)
  }

  // No chunked encoding
  req.useChunkedEncodingByDefault = false

  req.on('finish', function () {
    req.socket.end()
  })
}

Handle.prototype.assignRequest = function assignRequest (req) {
  // Emit trailing headers
  this.getStream(function (stream) {
    stream.on('headers', function (headers) {
      req.emit('trailers', headers)
    })
  })
}

Handle.prototype.assignResponse = function assignResponse (res) {
  var self = this

  res.addTrailers = function addTrailers (headers) {
    self.getStream(function (stream) {
      stream.sendHeaders(headers)
    })
  }
}

Handle.prototype._transformHeaders = function _transformHeaders (kind, headers) {
  var state = this._spdyState

  var res = {}
  var keys = Object.keys(headers)

  if (kind === 'request' && state.options['x-forwarded-for']) {
    var xforwarded = state.stream.connection.getXForwardedFor()
    if (xforwarded !== null) {
      res['x-forwarded-for'] = xforwarded
    }
  }

  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    var value = headers[key]

    if (key === ':authority') {
      res.host = value
    }
    if (/^:/.test(key)) {
      continue
    }

    res[key] = value
  }
  return res
}

Handle.prototype.emitRequest = function emitRequest () {
  var state = this._spdyState
  var stream = state.stream

  state.deceiver.emitRequest({
    method: stream.method,
    path: stream.path,
    headers: this._transformHeaders('request', stream.headers)
  })
}

Handle.prototype.emitResponse = function emitResponse (status, headers) {
  var state = this._spdyState

  state.deceiver.emitResponse({
    status: status,
    headers: this._transformHeaders('response', headers)
  })
}


code:
'use strict'

function attachPush (req) {
  var handle = req.socket._handle

  handle.getStream(function (stream) {
    stream.on('pushPromise', function (push) {
      req.emit('push', push)
    })
  })
}

exports.onNewListener = function onNewListener (type) {
  var req = this

  if (type !== 'push') {
    return
  }

  // Not first listener
  if (req.listeners('push').length !== 0) {
    return
  }

  if (!req.socket) {
    req.on('socket', function () {
      attachPush(req)
    })
    return
  }

  attachPush(req)
}

license: mit


code:
'use strict'

// NOTE: Mostly copy paste from node
exports.writeHead = function writeHead (statusCode, reason, obj) {
  var headers

  if (typeof reason === 'string') {
    // writeHead(statusCode, reasonPhrase[, headers])
    this.statusMessage = reason
  } else {
    // writeHead(statusCode[, headers])
    this.statusMessage =
      this.statusMessage || 'unknown'
    obj = reason
  }
  this.statusCode = statusCode

  if (this._headers) {
    // Slow-case: when progressive API and header fields are passed.
    if (obj) {
      var keys = Object.keys(obj)
      for (var i = 0; i < keys.length; i++) {
        var k = keys[i]
        if (k) this.setHeader(k, obj[k])
      }
    }
    // only progressive api is used
    headers = this._renderHeaders()
  } else {
    // only writeHead() called
    headers = obj
  }

  if (statusCode === 204 || statusCode === 304 ||
      (statusCode >= 100 && statusCode <= 199)) {
    // RFC 2616, 10.2.5:
    // The 204 response MUST NOT include a message-body, and thus is always
    // terminated by the first empty line after the header fields.
    // RFC 2616, 10.3.5:
    // The 304 response MUST NOT contain a message-body, and thus is always
    // terminated by the first empty line after the header fields.
    // RFC 2616, 10.1 Informational 1xx:
    // This class of status code indicates a provisional response,
    // consisting only of the Status-Line and optional headers, and is
    // terminated by an empty line.
    this._hasBody = false
  }

  // don't keep alive connections where the client expects 100 Continue
  // but we sent a final status; they may put extra bytes on the wire.
  if (this._expect_continue && !this._sent100) {
    this.shouldKeepAlive = false
  }

  // Implicit headers sent!
  this._header = true
  this._headerSent = true

  if (this.socket._handle) { this.socket._handle._spdyState.stream.respond(this.statusCode, headers) }
}

exports.end = function end (data, encoding, callback) {
  if (!this._headerSent) {
    this.writeHead(this.statusCode)
  }

  if (!this.socket._handle) {
    return
  }

  // Compatibility with Node.js core
  this.finished = true

  var self = this
  var handle = this.socket._handle
  handle._spdyState.ending = true
  this.socket.end(data, encoding, function () {
    self.constructor.prototype.end.call(self, '', 'utf8', callback)
  })
}

exports.push = function push (path, headers, callback) {
  var frame = {
    path: path,
    method: headers.method ? headers.method.toString() : 'GET',
    status: headers.status ? parseInt(headers.status, 10) : 200,
    host: this._req.headers.host,
    headers: headers.request,
    response: headers.response
  }

  var stream = this.spdyStream
  return stream.pushPromise(frame, callback)
}

exports.writeContinue = function writeContinue (callback) {
  if (this.socket._handle) {
    this.socket._handle._spdyState.stream.respond(100, {}, callback)
  }
}


code:
'use strict'

var assert = require('assert')
var https = require('https')
var http = require('http')
var tls = require('tls')
var net = require('net')
var util = require('util')
var selectHose = require('select-hose')
var transport = require('spdy-transport')
var debug = require('debug')('spdy:server')
var EventEmitter = require('events').EventEmitter

// Node.js 0.8, 0.10 and 0.12 support
Object.assign = process.versions.modules >= 46
  ? Object.assign // eslint-disable-next-line
  : util._extend

var spdy = require('../spdy')

var proto = {}

function instantiate (base) {
  function Server (options, handler) {
    this._init(base, options, handler)
  }
  util.inherits(Server, base)

  Server.create = function create (options, handler) {
    return new Server(options, handler)
  }

  Object.keys(proto).forEach(function (key) {
    Server.prototype[key] = proto[key]
  })

  return Server
}

proto._init = function _init (base, options, handler) {
  var state = {}
  this._spdyState = state

  state.options = options.spdy || {}

  var protocols = state.options.protocols || [
    'h2',
    'spdy/3.1', 'spdy/3', 'spdy/2',
    'http/1.1', 'http/1.0'
  ]

  var actualOptions = Object.assign({
    NPNProtocols: protocols,

    // Future-proof
    ALPNProtocols: protocols
  }, options)

  state.secure = this instanceof tls.Server

  if (state.secure) {
    base.call(this, actualOptions)
  } else {
    base.call(this)
  }

  // Support HEADERS+FIN
  this.httpAllowHalfOpen = true

  var event = state.secure ? 'secureConnection' : 'connection'

  state.listeners = this.listeners(event).slice()
  assert(state.listeners.length > 0, 'Server does not have default listeners')
  this.removeAllListeners(event)

  if (state.options.plain) {
    this.on(event, this._onPlainConnection)
  } else { this.on(event, this._onConnection) }

  if (handler) {
    this.on('request', handler)
  }

  debug('server init secure=%d', state.secure)
}

proto._onConnection = function _onConnection (socket) {
  var state = this._spdyState

  var protocol
  if (state.secure) {
    protocol = socket.npnProtocol || socket.alpnProtocol
  }

  this._handleConnection(socket, protocol)
}

proto._handleConnection = function _handleConnection (socket, protocol) {
  var state = this._spdyState

  if (!protocol) {
    protocol = state.options.protocol
  }

  debug('incoming socket protocol=%j', protocol)

  // No way we can do anything with the socket
  if (!protocol || protocol === 'http/1.1' || protocol === 'http/1.0') {
    debug('to default handler it goes')
    return this._invokeDefault(socket)
  }

  socket.setNoDelay(true)

  var connection = transport.connection.create(socket, Object.assign({
    protocol: /spdy/.test(protocol) ? 'spdy' : 'http2',
    isServer: true
  }, state.options.connection || {}))

  // Set version when we are certain
  if (protocol === 'http2') { connection.start(4) } else if (protocol === 'spdy/3.1') {
    connection.start(3.1)
  } else if (protocol === 'spdy/3') { connection.start(3) } else if (protocol === 'spdy/2') {
    connection.start(2)
  }

  connection.on('error', function () {
    socket.destroy()
  })

  var self = this
  connection.on('stream', function (stream) {
    self._onStream(stream)
  })
}

// HTTP2 preface
var PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
var PREFACE_BUFFER = Buffer.from(PREFACE)

function hoseFilter (data, callback) {
  if (data.length < 1) {
    return callback(null, null)
  }

  // SPDY!
  if (data[0] === 0x80) { return callback(null, 'spdy') }

  var avail = Math.min(data.length, PREFACE_BUFFER.length)
  for (var i = 0; i < avail; i++) {
    if (data[i] !== PREFACE_BUFFER[i]) { return callback(null, 'http/1.1') }
  }

  // Not enough bytes to be sure about HTTP2
  if (avail !== PREFACE_BUFFER.length) { return callback(null, null) }

  return callback(null, 'h2')
}

proto._onPlainConnection = function _onPlainConnection (socket) {
  var hose = selectHose.create(socket, {}, hoseFilter)

  var self = this
  hose.on('select', function (protocol, socket) {
    self._handleConnection(socket, protocol)
  })

  hose.on('error', function (err) {
    debug('hose error %j', err.message)
    socket.destroy()
  })
}

proto._invokeDefault = function _invokeDefault (socket) {
  var state = this._spdyState

  for (var i = 0; i < state.listeners.length; i++) { state.listeners[i].call(this, socket) }
}

proto._onStream = function _onStream (stream) {
  var state = this._spdyState

  var handle = spdy.handle.create(this._spdyState.options, stream)

  var socketOptions = {
    handle: handle,
    allowHalfOpen: true
  }

  var socket
  if (state.secure) {
    socket = new spdy.Socket(stream.connection.socket, socketOptions)
  } else {
    socket = new net.Socket(socketOptions)
  }

  // This is needed because the `error` listener, added by the default
  // `connection` listener, no longer has bound arguments. It relies instead
  // on the `server` property of the socket. See https://github.com/nodejs/node/pull/11926
  // for more details.
  // This is only done for Node.js >= 4 in order to not break compatibility
  // with older versions of the platform.
  if (process.versions.modules >= 46) { socket.server = this }

  handle.assignSocket(socket)

  // For v0.8
  socket.readable = true
  socket.writable = true

  this._invokeDefault(socket)

  // For v0.8, 0.10 and 0.12
  if (process.versions.modules < 46) {
    // eslint-disable-next-line
    this.listenerCount = EventEmitter.listenerCount.bind(this)
  }

  // Add lazy `checkContinue` listener, otherwise `res.writeContinue` will be
  // called before the response object was patched by us.
  if (stream.headers.expect !== undefined &&
      /100-continue/i.test(stream.headers.expect) &&
      this.listenerCount('checkContinue') === 0) {
    this.once('checkContinue', function (req, res) {
      res.writeContinue()

      this.emit('request', req, res)
    })
  }

  handle.emitRequest()
}

proto.emit = function emit (event, req, res) {
  if (event !== 'request' && event !== 'checkContinue') {
    return EventEmitter.prototype.emit.apply(this, arguments)
  }

  if (!(req.socket._handle instanceof spdy.handle)) {
    debug('not spdy req/res')
    req.isSpdy = false
    req.spdyVersion = 1
    res.isSpdy = false
    res.spdyVersion = 1
    return EventEmitter.prototype.emit.apply(this, arguments)
  }

  var handle = req.connection._handle

  req.isSpdy = true
  req.spdyVersion = handle.getStream().connection.getVersion()
  res.isSpdy = true
  res.spdyVersion = req.spdyVersion
  req.spdyStream = handle.getStream()

  debug('override req/res')
  res.writeHead = spdy.response.writeHead
  res.end = spdy.response.end
  res.push = spdy.response.push
  res.writeContinue = spdy.response.writeContinue
  res.spdyStream = handle.getStream()

  res._req = req

  handle.assignRequest(req)
  handle.assignResponse(res)

  return EventEmitter.prototype.emit.apply(this, arguments)
}

exports.Server = instantiate(https.Server)
exports.PlainServer = instantiate(http.Server)

exports.create = function create (base, options, handler) {
  if (typeof base === 'object') {
    handler = options
    options = base
    base = null
  }

  if (base) {
    return instantiate(base).create(options, handler)
  }

  if (options.spdy && options.spdy.plain) { return exports.PlainServer.create(options, handler) } else {
    return exports.Server.create(options, handler)
  }
}


code:
'use strict'

var util = require('util')
var net = require('net')

function Socket (parent, options) {
  net.Socket.call(this, options)

  var state = {}

  this._spdyState = state

  state.parent = parent

  this.servername = parent.servername
  this.npnProtocol = parent.npnProtocol
  this.alpnProtocol = parent.alpnProtocol
  this.authorized = parent.authorized
  this.authorizationError = parent.authorizationError
  this.encrypted = true
  this.allowHalfOpen = true
}

util.inherits(Socket, net.Socket)

module.exports = Socket

var methods = [
  'renegotiate', 'setMaxSendFragment', 'getTLSTicket', 'setServername',
  'setSession', 'getPeerCertificate', 'getSession', 'isSessionReused',
  'getCipher', 'getEphemeralKeyInfo'
]

methods.forEach(function (method) {
  Socket.prototype[method] = function methodWrap () {
    var parent = this._spdyState.parent
    return parent[method].apply(parent, arguments)
  }
})


spdy

description: Implementation of the SPDY protocol on node.js.

license: MIT

lint: standard

test: mocha --reporter=spec test/*-test.js

coverage: istanbul cover node_modules/.bin/_mocha -- --reporter=spec test/**/*-test.js

pre-commit: lint test

type: git

author: Fedor Indutny <fedor.indutny@gmail.com>

contributors:
Chris Storm <github@eeecooks.com>
François de Metz <francois@2metz.fr>
Ilya Grigorik <ilya@igvita.com>
Roberto Peon
Tatsuhiro Tsujikawa
Jesse Cravens <jesse.cravens@gmail.com>


code:
/* eslint-env mocha */

var assert = require('assert')
var https = require('https')
var http = require('http')
var util = require('util')

var fixtures = require('./fixtures')
var spdy = require('../')

// Node.js 0.10 and 0.12 support
Object.assign = process.versions.modules >= 46
  ? Object.assign // eslint-disable-next-line
  : util._extend

describe('SPDY Client', function () {
  describe('regular', function () {
    fixtures.everyConfig(function (protocol, alpn, version, plain) {
      var server
      var agent
      var hmodule

      beforeEach(function (done) {
        hmodule = plain ? http : https

        var options = Object.assign({
          spdy: {
            plain: plain
          }
        }, fixtures.keys)
        server = spdy.createServer(options, function (req, res) {
          var body = ''
          req.on('data', function (chunk) {
            body += chunk
          })
          req.on('end', function () {
            res.writeHead(200, req.headers)
            res.addTrailers({ trai: 'ler' })

            var push = res.push('/push', {
              request: {
                push: 'yes'
              }
            }, function (err) {
              assert(!err)

              push.end('push')
              push.on('error', function () {
              })

              res.end(body || 'okay')
            })
          })
        })

        server.listen(fixtures.port, function () {
          agent = spdy.createAgent({
            rejectUnauthorized: false,
            port: fixtures.port,
            spdy: {
              plain: plain,
              protocol: plain ? alpn : null,
              protocols: [alpn]
            }
          })

          done()
        })
      })

      afterEach(function (done) {
        var waiting = 2
        agent.close(next)
        server.close(next)

        function next () {
          if (--waiting === 0) {
            done()
          }
        }
      })

      it('should send GET request', function (done) {
        var req = hmodule.request({
          agent: agent,

          method: 'GET',
          path: '/get',
          headers: {
            a: 'b'
          }
        }, function (res) {
          assert.strictEqual(res.statusCode, 200)
          assert.strictEqual(res.headers.a, 'b')

          fixtures.expectData(res, 'okay', done)
        })
        req.end()
      })

      it('should send POST request', function (done) {
        var req = hmodule.request({
          agent: agent,

          method: 'POST',
          path: '/post',

          headers: {
            post: 'headers'
          }
        }, function (res) {
          assert.strictEqual(res.statusCode, 200)
          assert.strictEqual(res.headers.post, 'headers')

          fixtures.expectData(res, 'post body', done)
        })

        agent._spdyState.socket.once(plain ? 'connect' : 'secureConnect',
          function () {
            req.end('post body')
          })
      })

      it('should receive PUSH_PROMISE', function (done) {
        var req = hmodule.request({
          agent: agent,

          method: 'GET',
          path: '/get'
        }, function (res) {
          assert.strictEqual(res.statusCode, 200)

          res.resume()
        })
        req.on('push', function (push) {
          assert.strictEqual(push.path, '/push')
          assert.strictEqual(push.headers.push, 'yes')

          push.resume()
          push.once('end', done)
        })
        req.end()
      })

      it('should receive trailing headers', function (done) {
        var req = hmodule.request({
          agent: agent,

          method: 'GET',
          path: '/get'
        }, function (res) {
          assert.strictEqual(res.statusCode, 200)

          res.on('trailers', function (headers) {
            assert.strictEqual(headers.trai, 'ler')
            fixtures.expectData(res, 'okay', done)
          })
        })
        req.end()
      })
    })
  })

  describe('x-forwarded-for', function () {
    fixtures.everyConfig(function (protocol, alpn, version, plain) {
      var server
      var agent
      var hmodule
      // The underlying spdy Connection created by the agent.
      var connection

      beforeEach(function (done) {
        hmodule = plain ? http : https

        var options = Object.assign({
          spdy: {
            plain: plain,
            'x-forwarded-for': true
          }
        }, fixtures.keys)
        server = spdy.createServer(options, function (req, res) {
          res.writeHead(200, req.headers)
          res.end()
        })

        server.listen(fixtures.port, function () {
          agent = spdy.createAgent({
            rejectUnauthorized: false,
            port: fixtures.port,
            spdy: {
              'x-forwarded-for': '1.2.3.4',
              plain: plain,
              protocol: plain ? alpn : null,
              protocols: [alpn]
            }
          })
          // Once aagent has connection, keep a copy for testing.
          agent.once('_connect', function () {
            connection = agent._spdyState.connection
            done()
          })
        })
      })

      afterEach(function (done) {
        var waiting = 2
        agent.close(next)
        server.close(next)

        function next () {
          if (--waiting === 0) {
            done()
          }
        }
      })

      it('should send x-forwarded-for', function (done) {
        var req = hmodule.request({
          agent: agent,

          method: 'GET',
          path: '/get'
        }, function (res) {
          assert.strictEqual(res.statusCode, 200)
          assert.strictEqual(res.headers['x-forwarded-for'], '1.2.3.4')

          res.resume()
          res.once('end', done)
        })
        req.end()
      })

      it('agent should emit connection level errors', function (done) {
        agent.once('error', function (err) {
          assert.strictEqual(err.message, 'mock error')
          done()
        })
        connection.emit('error', new Error('mock error'))
      })
    })
  })
})


code:
/* eslint-env mocha */
'use strict'

var assert = require('assert')

exports.port = 23433

exports.keys = {
  key: '-----BEGIN RSA PRIVATE KEY-----\n' +
       'MIIEogIBAAKCAQEA1ARXSoyizYSnHDYickxX4x2UG/8uNWnQWKlWR97NAwRsspN6\n' +
       'aFF1+LnyN9bvLNnhxIowcYy68+LpZ7pYAQgBZSyAhnF1S4qz2w/rxH4CNn96B/je\n' +
       'vQGo3e8vIQ8ChhfuYvGAtTEYJzW8aRoxWSPcukZZdxPQ1Wgbhd9DSXhgkUnkEEET\n' +
       'owyn8ufQFRnQHfc9Fn5DrJilI7vD+ZyRU3gZoBj2GVMQuxJLqQEHy2XsJ6ZWTea/\n' +
       'EfK93XfDyY7ZxyeK0ZdWCVoTqw9QNJSkGjesCBkcY4Rjxi9LbLJwW3Es4wgW4N4Y\n' +
       'cltfygjltSis+RVKJyGeDqTWAxceih3mlkdGIQIDAQABAoIBAB6akc8dBdMMtuKH\n' +
       'nelJw9XwyxRPfWgQYhaqOt4c9xLcbKRKTX0JZTIGBUSyLcwXl1M7b0q0ubfCpVZn\n' +
       'u5RKh4kHJ3ZAomHJH7UbUzkFx2P+eqrz7ZLyzmFayT7IX+DjS3HU0nNVJttiElRJ\n' +
       'h54KYy4wQXHC1n43jOGCHMBaM/ZEpO3xA7PLfVD/BpYLzL+FAYoFBb/x2buLv8Mg\n' +
       'D6QAWkS70mu8ER13NapKjg6PUsYPxHYU30BjGMTXw95Iz5PSAK8+/xQ6YaW7MEVM\n' +
       'twxxfJfZ+9u9nJMfJANqxCi6iZ6ft/e5cbhvNhV/X97XeoPWxqSpx98M6BC/vvBc\n' +
       'UjSmaRECgYEA4NH8Y20zC8zF4ALcBgqgrx+U9upov2UGa+kjS1/So4r/dpG4T8pT\n' +
       'T2tMW6zR5qe7g11kgJm0oI/I6x9P2qwFJONO3MdLYVKd2mSxG2fniCktLg2j6BAX\n' +
       'QTt5zjIEWvhRP2vkrS8gAaJbVMLTMg4s374bE/IdKT+c59tYpcVaXXMCgYEA8WvJ\n' +
       'dfPXoagEgaHRd++R2COMG19euOTFRle0MSq+S9ZeeSe9ejb9CIpWYZ3WVviKvf+E\n' +
       'zksmKTZJnig5pGEgU+2ka1C9PthCGlTlQagD6Ey4hblQgi+pOFgBjE9Yn3FxfppH\n' +
       '25ICXNY89EF6klEqKV67E/5O+nBZo+Y2TM4YKRsCgYAaEV8RbEUB9kFvYwV+Eddl\n' +
       '1uSf6LgykRU4h/TWtYqn+eL7LZRQdCZKzCczbgt8kjBU4AxaOPhPsbxbPus0cMO7\n' +
       '7jtjsBwWcczp2MkMY3TePeAGOgCqVMtNfgb2mKgWoDpTf0ApsJAmgFvUrS5t3GTp\n' +
       'oJJlMqqc8MpRvAZAWmzK7wKBgEVBFlmvyXumJyTItr4hC0VlbRutEA8aET1Mi3RP\n' +
       'Pqeipxc6PzB/9bYtePonvQTV53b5ha9n/1pzKEsmXuK4uf1ZfoEKeD8+6jeDgwCC\n' +
       'ohxRZd12e5Hc+j4fgNIvMM0MTfJzb4mdKPBYxMOMxQyUG/QiKKhjm2RcNlq9/3Wo\n' +
       '6WVhAoGAG4QPWoE4ccFECp8eyGw8rjE45y5uqUI/f/RssX7bnKbCRY0otDsPlJd6\n' +
       'Kf0XFssLnYsCXO+ua03gw2N+2mrcsuA5FXHmQMrbfnuojHIVY05nt4Wa5iqV/gqH\n' +
       'PJXWyOgD+Kd6eR/cih/SCoKl4tSGCSJG5TDEpMt+r8EJkCXJ7Fw=\n' +
       '-----END RSA PRIVATE KEY-----',
  cert: '-----BEGIN CERTIFICATE-----\n' +
        'MIICuTCCAaOgAwIBAgIDAQABMAsGCSqGSIb3DQEBCzAUMRIwEAYDVQQDFglub2Rl\n' +
        'LnNwZHkwHhcNNjkwMTAxMDAwMDAwWhcNMjUwNzA2MDUwMzQzWjAUMRIwEAYDVQQD\n' +
        'Fglub2RlLnNwZHkwggEgMAsGCSqGSIb3DQEBAQOCAQ8AMIIBCgKCAQEA1ARXSoyi\n' +
        'zYSnHDYickxX4x2UG/8uNWnQWKlWR97NAwRsspN6aFF1+LnyN9bvLNnhxIowcYy6\n' +
        '8+LpZ7pYAQgBZSyAhnF1S4qz2w/rxH4CNn96B/jevQGo3e8vIQ8ChhfuYvGAtTEY\n' +
        'JzW8aRoxWSPcukZZdxPQ1Wgbhd9DSXhgkUnkEEETowyn8ufQFRnQHfc9Fn5DrJil\n' +
        'I7vD+ZyRU3gZoBj2GVMQuxJLqQEHy2XsJ6ZWTea/EfK93XfDyY7ZxyeK0ZdWCVoT\n' +
        'qw9QNJSkGjesCBkcY4Rjxi9LbLJwW3Es4wgW4N4YcltfygjltSis+RVKJyGeDqTW\n' +
        'Axceih3mlkdGIQIDAQABoxowGDAWBgNVHREEDzANggsqLm5vZGUuc3BkeTALBgkq\n' +
        'hkiG9w0BAQsDggEBALn2FQSDMsyu+oqUnJgTVdGpnzKmfXoBPlQuznRdibri8ABO\n' +
        'kOo8FC72Iy6leVSsB26KtAdhpURZ3mv1Oyt4cGeeyQln2Olzp5flIos+GqYSztAq\n' +
        '5ZnrzTLLlip7KHkmastYRXhEwTLmo2JCU8RkRP1X/m1xONF/YkURxmqj6cQTahPY\n' +
        'FzzLP1clW3arJwPlUcKKby6WpxO5MihYEliheBr7fL2TDUA96eG+B/SKxvwaGF2v\n' +
        'gWF8rg5prjPaLW8HH3Efq59AimFqUVQ4HtcJApjLJDYUKlvsMNMvBqh/pQRRPafj\n' +
        '0Cp8dyS45sbZ2RgXdyfl6gNEj+DiPbaFliIuFmM=\n' +
        '-----END CERTIFICATE-----'
}

function expectData (stream, expected, callback) {
  var actual = ''

  stream.on('data', function (chunk) {
    actual += chunk
  })
  stream.on('end', function () {
    assert.strictEqual(actual, expected)
    callback()
  })
}
exports.expectData = expectData

exports.everyProtocol = function everyProtocol (body) {
  var protocols = [
    { protocol: 'http2', alpn: 'h2', version: 4 },
    { protocol: 'spdy', alpn: 'spdy/3.1', version: 3.1 },
    { protocol: 'spdy', alpn: 'spdy/3', version: 3 },
    { protocol: 'spdy', alpn: 'spdy/2', version: 2 }
  ]

  protocols.forEach(function (protocol) {
    describe(protocol.alpn, function () {
      body(protocol.protocol, protocol.alpn, protocol.version)
    })
  })
}

exports.everyConfig = function everyConfig (body) {
  exports.everyProtocol(function (protocol, alpn, version) {
    if (alpn === 'spdy/2') {
      return
    }

    [false, true].forEach(function (plain) {
      describe(plain ? 'plain mode' : 'ssl mode', function () {
        body(protocol, alpn, version, plain)
      })
    })
  })
}


code:
/* eslint-env mocha */

var assert = require('assert')
var tls = require('tls')
var net = require('net')
var https = require('https')
var transport = require('spdy-transport')
var util = require('util')

var fixtures = require('./fixtures')
var spdy = require('../')

describe('SPDY Server', function () {
  fixtures.everyConfig(function (protocol, alpn, version, plain) {
    var server
    var client

    beforeEach(function (done) {
      server = spdy.createServer(Object.assign({
        spdy: {
          'x-forwarded-for': true,
          plain: plain
        }
      }, fixtures.keys))

      server.listen(fixtures.port, function () {
        var socket = (plain ? net : tls).connect({
          rejectUnauthorized: false,
          port: fixtures.port,
          ALPNProtocols: [alpn]
        }, function () {
          client = transport.connection.create(socket, {
            protocol: protocol,
            isServer: false
          })
          client.start(version)
          done()
        })
      })
    })

    afterEach(function (done) {
      client.socket.destroy()
      server.close(done)
    })

    it('should process GET request', function (done) {
      var stream = client.request({
        method: 'GET',
        path: '/get',
        headers: {
          a: 'b'
        }
      }, function (err) {
        assert(!err)

        stream.on('error', (err) => {
          done(err)
        })

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 200)
          assert.strictEqual(headers.ok, 'yes')

          fixtures.expectData(stream, 'response', done)
        })

        stream.end()
      })

      server.on('request', function (req, res) {
        assert.strictEqual(req.isSpdy, res.isSpdy)
        assert.strictEqual(req.spdyVersion, res.spdyVersion)
        assert(req.isSpdy)
        if (!plain) {
          assert(req.socket.encrypted)
          assert(req.socket.getPeerCertificate())
        }

        // Auto-detection
        if (version === 3.1) {
          assert(req.spdyVersion >= 3 && req.spdyVersion <= 3.1)
        } else {
          assert.strictEqual(req.spdyVersion, version)
        }
        assert(req.spdyStream)
        assert(res.spdyStream)

        assert.strictEqual(req.method, 'GET')
        assert.strictEqual(req.url, '/get')
        assert.deepStrictEqual(req.headers, { a: 'b', host: 'localhost' })

        req.on('end', function () {
          res.writeHead(200, {
            ok: 'yes'
          })
          res.end('response')
          assert(res.finished, 'res.finished should be set')
        })
        req.resume()
      })
    })

    it('should process POST request', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 200)
          assert.strictEqual(headers.ok, 'yes')

          fixtures.expectData(stream, 'response', next)
        })

        stream.end('request')
      })

      server.on('request', function (req, res) {
        assert.strictEqual(req.method, 'POST')
        assert.strictEqual(req.url, '/post')

        res.writeHead(200, {
          ok: 'yes'
        })
        res.end('response')

        fixtures.expectData(req, 'request', next)
      })

      var waiting = 2
      function next () {
        if (--waiting === 0) {
          return done()
        }
      }
    })

    it('should process expect-continue request', function (done) {
      var stream = client.request({
        method: 'GET',
        path: '/get',
        headers: {
          Expect: '100-continue'
        }
      }, function (err) {
        assert(!err)

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 100)

          fixtures.expectData(stream, 'response', done)
        })

        stream.end()
      })

      server.on('request', function (req, res) {
        req.on('end', function () {
          res.end('response')
        })
        req.resume()
      })
    })

    it('should emit `checkContinue` request', function (done) {
      var stream = client.request({
        method: 'GET',
        path: '/get',
        headers: {
          Expect: '100-continue'
        }
      }, function (err) {
        assert(!err)

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 100)

          fixtures.expectData(stream, 'response', done)
        })

        stream.end()
      })

      server.on('checkContinue', function (req, res) {
        req.on('end', function () {
          res.writeContinue()
          res.end('response')
        })
        req.resume()
      })
    })

    it('should send PUSH_PROMISE', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/page'
      }, function (err) {
        assert(!err)

        stream.on('pushPromise', function (push) {
          assert.strictEqual(push.path, '/push')
          assert.strictEqual(push.headers.yes, 'push')

          fixtures.expectData(push, 'push', next)
          fixtures.expectData(stream, 'response', next)
        })

        stream.end('request')
      })

      server.on('request', function (req, res) {
        assert.strictEqual(req.method, 'POST')
        assert.strictEqual(req.url, '/page')

        res.writeHead(200, {
          ok: 'yes'
        })

        var push = res.push('/push', {
          request: {
            yes: 'push'
          }
        })
        push.end('push')

        res.end('response')

        fixtures.expectData(req, 'request', next)
      })

      var waiting = 3
      function next () {
        if (--waiting === 0) {
          return done()
        }
      }
    })

    it('should receive trailing headers', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)

        stream.sendHeaders({ trai: 'ler' })
        stream.end()

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 200)
          assert.strictEqual(headers.ok, 'yes')

          fixtures.expectData(stream, 'response', done)
        })
      })

      server.on('request', function (req, res) {
        var gotHeaders = false
        req.on('trailers', function (headers) {
          gotHeaders = true
          assert.strictEqual(headers.trai, 'ler')
        })

        req.on('end', function () {
          assert(gotHeaders)

          res.writeHead(200, {
            ok: 'yes'
          })
          res.end('response')
        })
        req.resume()
      })
    })

    it('should call .writeHead() automatically', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 300)

          fixtures.expectData(stream, 'response', done)
        })
        stream.end()
      })

      server.on('request', function (req, res) {
        req.on('end', function () {
          res.statusCode = 300
          res.end('response')
        })
        req.resume()
      })
    })

    it('should not crash on .writeHead() after socket close', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)

        setTimeout(function () {
          client.socket.destroy()
        }, 50)
        stream.on('error', function () {})
        stream.end()
      })

      server.on('request', function (req, res) {
        req.connection.on('close', function () {
          assert.doesNotThrow(function () {
            res.writeHead(200)
            res.end('response')
          })
          done()
        })
      })
    })

    it('should not crash on .push() after socket close', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)

        setTimeout(function () {
          client.socket.destroy()
        }, 50)
        stream.on('error', function () {})
        stream.end()
      })

      server.on('request', function (req, res) {
        req.connection.on('close', function () {
          assert.doesNotThrow(function () {
            assert.strictEqual(res.push('/push', {}), undefined)
            res.end('response')
          })
          done()
        })
      })
    })

    it('should end response after writing everything down', function (done) {
      var stream = client.request({
        method: 'GET',
        path: '/post'
      }, function (err) {
        assert(!err)

        stream.on('response', function (status, headers) {
          assert.strictEqual(status, 200)

          fixtures.expectData(stream, 'hello world, what\'s up?', done)
        })

        stream.end()
      })

      server.on('request', function (req, res) {
        req.resume()
        res.writeHead(200)
        res.write('hello ')
        res.write('world')
        res.write(', what\'s')
        res.write(' up?')
        res.end()
      })
    })

    it('should handle x-forwarded-for', function (done) {
      client.sendXForwardedFor('1.2.3.4')

      var stream = client.request({
        method: 'GET',
        path: '/post'
      }, function (err) {
        assert(!err)

        stream.resume()
        stream.on('end', done)
        stream.end()
      })

      server.on('request', function (req, res) {
        assert.strictEqual(req.headers['x-forwarded-for'], '1.2.3.4')
        req.resume()
        res.end()
      })
    })

    it('should destroy request after end', function (done) {
      var stream = client.request({
        method: 'POST',
        path: '/post'
      }, function (err) {
        assert(!err)
      })
      stream.end()
      stream.on('error', function () {})

      server.on('request', function (req, res) {
        res.end()
        res.destroy()
        res.socket.on('close', function () {
          done()
        })
      })
    })
  })

  it('should respond to http/1.1', function (done) {
    var server = spdy.createServer(fixtures.keys, function (req, res) {
      assert.strictEqual(req.isSpdy, res.isSpdy)
      assert.strictEqual(req.spdyVersion, res.spdyVersion)
      assert(!req.isSpdy)
      assert.strictEqual(req.spdyVersion, 1)

      res.writeHead(200)
      res.end()
    })

    server.listen(fixtures.port, function () {
      var req = https.request({
        agent: false,
        rejectUnauthorized: false,
        NPNProtocols: ['http/1.1'],
        port: fixtures.port,
        method: 'GET',
        path: '/'
      }, function (res) {
        assert.strictEqual(res.statusCode, 200)
        res.resume()
        res.on('end', function () {
          server.close(done)
        })
      })

      req.end()
    })
  })

  it('should support custom base', function (done) {
    function Pseuver (options, listener) {
      https.Server.call(this, options, listener)
    }
    util.inherits(Pseuver, https.Server)

    var server = spdy.createServer(Pseuver, fixtures.keys, function (req, res) {
      assert.strictEqual(req.isSpdy, res.isSpdy)
      assert.strictEqual(req.spdyVersion, res.spdyVersion)
      assert(!req.isSpdy)
      assert.strictEqual(req.spdyVersion, 1)

      res.writeHead(200)
      res.end()
    })

    server.listen(fixtures.port, function () {
      var req = https.request({
        agent: false,
        rejectUnauthorized: false,
        NPNProtocols: ['http/1.1'],
        port: fixtures.port,
        method: 'GET',
        path: '/'
      }, function (res) {
        assert.strictEqual(res.statusCode, 200)
        res.resume()
        res.on('end', function () {
          server.close(done)
        })
      })

      req.end()
    })
  })
})


code:
# sprintf.js
**sprintf.js** is a complete open source JavaScript sprintf implementation for the *browser* and *node.js*.

Its prototype is simple:

    string sprintf(string format , [mixed arg1 [, mixed arg2 [ ,...]]])

The placeholders in the format string are marked by `%` and are followed by one or more of these elements, in this order:

* An optional number followed by a `$` sign that selects which argument index to use for the value. If not specified, arguments will be placed in the same order as the placeholders in the input string.
* An optional `+` sign that forces to preceed the result with a plus or minus sign on numeric values. By default, only the `-` sign is used on negative numbers.
* An optional padding specifier that says what character to use for padding (if specified). Possible values are `0` or any other character precedeed by a `'` (single quote). The default is to pad with *spaces*.
* An optional `-` sign, that causes sprintf to left-align the result of this placeholder. The default is to right-align the result.
* An optional number, that says how many characters the result should have. If the value to be returned is shorter than this number, the result will be padded. When used with the `j` (JSON) type specifier, the padding length specifies the tab size used for indentation.
* An optional precision modifier, consisting of a `.` (dot) followed by a number, that says how many digits should be displayed for floating point numbers. When used with the `g` type specifier, it specifies the number of significant digits. When used on a string, it causes the result to be truncated.
* A type specifier that can be any of:
    * `%` — yields a literal `%` character
    * `b` — yields an integer as a binary number
    * `c` — yields an integer as the character with that ASCII value
    * `d` or `i` — yields an integer as a signed decimal number
    * `e` — yields a float using scientific notation
    * `u` — yields an integer as an unsigned decimal number
    * `f` — yields a float as is; see notes on precision above
    * `g` — yields a float as is; see notes on precision above
    * `o` — yields an integer as an octal number
    * `s` — yields a string as is
    * `x` — yields an integer as a hexadecimal number (lower-case)
    * `X` — yields an integer as a hexadecimal number (upper-case)
    * `j` — yields a JavaScript object or array as a JSON encoded string

## JavaScript `vsprintf`
`vsprintf` is the same as `sprintf` except that it accepts an array of arguments, rather than a variable number of arguments:

    vsprintf("The first 4 letters of the english alphabet are: %s, %s, %s and %s", ["a", "b", "c", "d"])

## Argument swapping
You can also swap the arguments. That is, the order of the placeholders doesn't have to match the order of the arguments. You can do that by simply indicating in the format string which arguments the placeholders refer to:

    sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants")
And, of course, you can repeat the placeholders without having to increase the number of arguments.

## Named arguments
Format strings may contain replacement fields rather than positional placeholders. Instead of referring to a certain argument, you can now refer to a certain key within an object. Replacement fields are surrounded by rounded parentheses - `(` and `)` - and begin with a keyword that refers to a key:

    var user = {
        name: "Dolly"
    }
    sprintf("Hello %(name)s", user) // Hello Dolly
Keywords in replacement fields can be optionally followed by any number of keywords or indexes:

    var users = [
        {name: "Dolly"},
        {name: "Molly"},
        {name: "Polly"}
    ]
    sprintf("Hello %(users[0].name)s, %(users[1].name)s and %(users[2].name)s", {users: users}) // Hello Dolly, Molly and Polly
Note: mixing positional and named placeholders is not (yet) supported

## Computed values
You can pass in a function as a dynamic value and it will be invoked (with no arguments) in order to compute the value on-the-fly.

    sprintf("Current timestamp: %d", Date.now) // Current timestamp: 1398005382890
    sprintf("Current date and time: %s", function() { return new Date().toString() })

# AngularJS
You can now use `sprintf` and `vsprintf` (also aliased as `fmt` and `vfmt` respectively) in your AngularJS projects. See `demo/`.

# Installation

## Via Bower

    bower install sprintf

## Or as a node.js module

    npm install sprintf-js

### Usage

    var sprintf = require("sprintf-js").sprintf,
        vsprintf = require("sprintf-js").vsprintf

    sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants")
    vsprintf("The first 4 letters of the english alphabet are: %s, %s, %s and %s", ["a", "b", "c", "d"])

# License

**sprintf.js** is licensed under the terms of the 3-clause BSD license.


sprintf

description: JavaScript sprintf implementation

keywords: sprintf string formatting

authors: Alexandru Marasteanu <hello@alexei.ro> (http://alexei.ro/)


code:
<!doctype html>

{{ "%+010d"|sprintf:-123 }}

{{ "%+010d"|vsprintf:[-123] }}

{{ "%+010d"|fmt:-123 }}

{{ "%+010d"|vfmt:[-123] }}

{{ "I've got %2$d apples and %1$d oranges."|fmt:4:2 }}

{{ "I've got %(apples)d apples and %(oranges)d oranges."|fmt:{apples: 2, oranges: 4} }}


code:
/*! sprintf-js | Alexandru Marasteanu <hello@alexei.ro> (http://alexei.ro/) | BSD-3-Clause */

angular.module("sprintf",[]).filter("sprintf",function(){return function(){return sprintf.apply(null,arguments)}}).filter("fmt",["$filter",function(a){return a("sprintf")}]).filter("vsprintf",function(){return function(a,b){return vsprintf(a,b)}}).filter("vfmt",["$filter",function(a){return a("vsprintf")}]);
//# sourceMappingURL=angular-sprintf.min.map


names: angular module filter sprintf apply arguments $filter format argv vsprintf


code:
module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),

        uglify: {
            options: {
                banner: "/*! <%= pkg.name %> | <%= pkg.author %> | <%= pkg.license %> */\n",
                sourceMap: true
            },
            build: {
                files: [
                    {
                        src: "src/sprintf.js",
                        dest: "dist/sprintf.min.js"
                    },
                    {
                        src: "src/angular-sprintf.js",
                        dest: "dist/angular-sprintf.min.js"
                    }
                ]
            }
        },

        watch: {
            js: {
                files: "src/*.js",
                tasks: ["uglify"]
            }
        }
    })

    grunt.loadNpmTasks("grunt-contrib-uglify")
    grunt.loadNpmTasks("grunt-contrib-watch")

    grunt.registerTask("default", ["uglify", "watch"])
}


sprintf-js

description: JavaScript sprintf implementation

author: Alexandru Marasteanu <hello@alexei.ro> (http://alexei.ro/)

test: mocha test/test.js


code:
angular.
    module("sprintf", []).
    filter("sprintf", function() {
        return function() {
            return sprintf.apply(null, arguments)
        }
    }).
    filter("fmt", ["$filter", function($filter) {
        return $filter("sprintf")
    }]).
    filter("vsprintf", function() {
        return function(format, argv) {
            return vsprintf(format, argv)
        }
    }).
    filter("vfmt", ["$filter", function($filter) {
        return $filter("vsprintf")
    }])


code:
(function(window) {
    var re = {
        not_string: /[^s]/,
        number: /[diefg]/,
        json: /[j]/,
        not_json: /[^j]/,
        text: /^[^\x25]+/,
        modulo: /^\x25{2}/,
        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/,
        key: /^([a-z_][a-z_\d]*)/i,
        key_access: /^\.([a-z_][a-z_\d]*)/i,
        index_access: /^\[(\d+)\]/,
        sign: /^[\+\-]/
    }

    function sprintf() {
        var key = arguments[0], cache = sprintf.cache
        if (!(cache[key] && cache.hasOwnProperty(key))) {
            cache[key] = sprintf.parse(key)
        }
        return sprintf.format.call(null, cache[key], arguments)
    }

    sprintf.format = function(parse_tree, argv) {
        var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ""
        for (i = 0; i < tree_length; i++) {
            node_type = get_type(parse_tree[i])
            if (node_type === "string") {
                output[output.length] = parse_tree[i]
            }
            else if (node_type === "array") {
                match = parse_tree[i] // convenience purposes only
                if (match[2]) { // keyword argument
                    arg = argv[cursor]
                    for (k = 0; k < match[2].length; k++) {
                        if (!arg.hasOwnProperty(match[2][k])) {
                            throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k]))
                        }
                        arg = arg[match[2][k]]
                    }
                }
                else if (match[1]) { // positional argument (explicit)
                    arg = argv[match[1]]
                }
                else { // positional argument (implicit)
                    arg = argv[cursor++]
                }

                if (get_type(arg) == "function") {
                    arg = arg()
                }

                if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) {
                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
                }

                if (re.number.test(match[8])) {
                    is_positive = arg >= 0
                }

                switch (match[8]) {
                    case "b":
                        arg = arg.toString(2)
                    break
                    case "c":
                        arg = String.fromCharCode(arg)
                    break
                    case "d":
                    case "i":
                        arg = parseInt(arg, 10)
                    break
                    case "j":
                        arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
                    break
                    case "e":
                        arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential()
                    break
                    case "f":
                        arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
                    break
                    case "g":
                        arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
                    break
                    case "o":
                        arg = arg.toString(8)
                    break
                    case "s":
                        arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg)
                    break
                    case "u":
                        arg = arg >>> 0
                    break
                    case "x":
                        arg = arg.toString(16)
                    break
                    case "X":
                        arg = arg.toString(16).toUpperCase()
                    break
                }
                if (re.json.test(match[8])) {
                    output[output.length] = arg
                }
                else {
                    if (re.number.test(match[8]) && (!is_positive || match[3])) {
                        sign = is_positive ? "+" : "-"
                        arg = arg.toString().replace(re.sign, "")
                    }
                    else {
                        sign = ""
                    }
                    pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " "
                    pad_length = match[6] - (sign + arg).length
                    pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : ""
                    output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg)
                }
            }
        }
        return output.join("")
    }

    sprintf.cache = {}

    sprintf.parse = function(fmt) {
        var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
        while (_fmt) {
            if ((match = re.text.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = match[0]
            }
            else if ((match = re.modulo.exec(_fmt)) !== null) {
                parse_tree[parse_tree.length] = "%"
            }
            else if ((match = re.placeholder.exec(_fmt)) !== null) {
                if (match[2]) {
                    arg_names |= 1
                    var field_list = [], replacement_field = match[2], field_match = []
                    if ((field_match = re.key.exec(replacement_field)) !== null) {
                        field_list[field_list.length] = field_match[1]
                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") {
                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
                                field_list[field_list.length] = field_match[1]
                            }
                            else {
                                throw new SyntaxError("[sprintf] failed to parse named argument key")
                            }
                        }
                    }
                    else {
                        throw new SyntaxError("[sprintf] failed to parse named argument key")
                    }
                    match[2] = field_list
                }
                else {
                    arg_names |= 2
                }
                if (arg_names === 3) {
                    throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
                }
                parse_tree[parse_tree.length] = match
            }
            else {
                throw new SyntaxError("[sprintf] unexpected placeholder")
            }
            _fmt = _fmt.substring(match[0].length)
        }
        return parse_tree
    }

    var vsprintf = function(fmt, argv, _argv) {
        _argv = (argv || []).slice(0)
        _argv.splice(0, 0, fmt)
        return sprintf.apply(null, _argv)
    }

    /**
     * helpers
     */
    function get_type(variable) {
        return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
    }

    function str_repeat(input, multiplier) {
        return Array(multiplier + 1).join(input)
    }

    /**
     * export to either browser or node.js
     */
    if (typeof exports !== "undefined") {
        exports.sprintf = sprintf
        exports.vsprintf = vsprintf
    }
    else {
        window.sprintf = sprintf
        window.vsprintf = vsprintf

        if (typeof define === "function" && define.amd) {
            define(function() {
                return {
                    sprintf: sprintf,
                    vsprintf: vsprintf
                }
            })
        }
    }
})(typeof window === "undefined" ? this : window);


code:
var assert = require("assert"),
    sprintfjs = require("../src/sprintf.js"),
    sprintf = sprintfjs.sprintf,
    vsprintf = sprintfjs.vsprintf

describe("sprintfjs", function() {
    var pi = 3.141592653589793

    it("should return formated strings for simple placeholders", function() {
        assert.equal("%", sprintf("%%"))
        assert.equal("10", sprintf("%b", 2))
        assert.equal("A", sprintf("%c", 65))
        assert.equal("2", sprintf("%d", 2))
        assert.equal("2", sprintf("%i", 2))
        assert.equal("2", sprintf("%d", "2"))
        assert.equal("2", sprintf("%i", "2"))
        assert.equal('{"foo":"bar"}', sprintf("%j", {foo: "bar"}))
        assert.equal('["foo","bar"]', sprintf("%j", ["foo", "bar"]))
        assert.equal("2e+0", sprintf("%e", 2))
        assert.equal("2", sprintf("%u", 2))
        assert.equal("4294967294", sprintf("%u", -2))
        assert.equal("2.2", sprintf("%f", 2.2))
        assert.equal("3.141592653589793", sprintf("%g", pi))
        assert.equal("10", sprintf("%o", 8))
        assert.equal("%s", sprintf("%s", "%s"))
        assert.equal("ff", sprintf("%x", 255))
        assert.equal("FF", sprintf("%X", 255))
        assert.equal("Polly wants a cracker", sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants"))
        assert.equal("Hello world!", sprintf("Hello %(who)s!", {"who": "world"}))
    })

    it("should return formated strings for complex placeholders", function() {
        // sign
        assert.equal("2", sprintf("%d", 2))
        assert.equal("-2", sprintf("%d", -2))
        assert.equal("+2", sprintf("%+d", 2))
        assert.equal("-2", sprintf("%+d", -2))
        assert.equal("2", sprintf("%i", 2))
        assert.equal("-2", sprintf("%i", -2))
        assert.equal("+2", sprintf("%+i", 2))
        assert.equal("-2", sprintf("%+i", -2))
        assert.equal("2.2", sprintf("%f", 2.2))
        assert.equal("-2.2", sprintf("%f", -2.2))
        assert.equal("+2.2", sprintf("%+f", 2.2))
        assert.equal("-2.2", sprintf("%+f", -2.2))
        assert.equal("-2.3", sprintf("%+.1f", -2.34))
        assert.equal("-0.0", sprintf("%+.1f", -0.01))
        assert.equal("3.14159", sprintf("%.6g", pi))
        assert.equal("3.14", sprintf("%.3g", pi))
        assert.equal("3", sprintf("%.1g", pi))
        assert.equal("-000000123", sprintf("%+010d", -123))
        assert.equal("______-123", sprintf("%+'_10d", -123))
        assert.equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2))

        // padding
        assert.equal("-0002", sprintf("%05d", -2))
        assert.equal("-0002", sprintf("%05i", -2))
        assert.equal("    <", sprintf("%5s", "<"))
        assert.equal("0000<", sprintf("%05s", "<"))
        assert.equal("____<", sprintf("%'_5s", "<"))
        assert.equal(">    ", sprintf("%-5s", ">"))
        assert.equal(">0000", sprintf("%0-5s", ">"))
        assert.equal(">____", sprintf("%'_-5s", ">"))
        assert.equal("xxxxxx", sprintf("%5s", "xxxxxx"))
        assert.equal("1234", sprintf("%02u", 1234))
        assert.equal(" -10.235", sprintf("%8.3f", -10.23456))
        assert.equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx"))
        assert.equal('{\n  "foo": "bar"\n}', sprintf("%2j", {foo: "bar"}))
        assert.equal('[\n  "foo",\n  "bar"\n]', sprintf("%2j", ["foo", "bar"]))

        // precision
        assert.equal("2.3", sprintf("%.1f", 2.345))
        assert.equal("xxxxx", sprintf("%5.5s", "xxxxxx"))
        assert.equal("    x", sprintf("%5.1s", "xxxxxx"))

    })

    it("should return formated strings for callbacks", function() {
        assert.equal("foobar", sprintf("%s", function() { return "foobar" }))
        assert.equal(Date.now(), sprintf("%s", Date.now)) // should pass...
    })
})


code:
# Changelog

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [8.0.1](https://github.com/npm/ssri/compare/v8.0.0...v8.0.1) (2021-01-27)

### Bug Fixes

* simplify regex for strict mode, add tests ([76e2233](https://github.com/npm/ssri/commit/76e223317d971f19e4db8191865bdad5edee40d2))

## [8.0.0](https://github.com/npm/ssri/compare/v7.1.0...v8.0.0) (2020-02-18)

### ⚠ BREAKING CHANGES

* SRI values with `../` in the algorithm name now throw
as invalid (which they always probably should have!)
* adds a new error that will be thrown.  Empty SRIs are
no longer considered valid for checking, only when using integrityStream
to calculate the SRI value.

PR-URL: https://github.com/npm/ssri/pull/12
Credit: @claudiahdz

### Features

* remove figgy-pudding ([0e78fd7](https://github.com/npm/ssri/commit/0e78fd7b754e2d098875eb4c57238709d96d7c27))

### Bug Fixes

* harden SRI parsing against ../ funny business ([4062735](https://github.com/npm/ssri/commit/4062735d1281941fd32ac4320b9f9965fcec278b))
* IntegrityStream responds to mutating opts object mid-stream ([4a963e5](https://github.com/npm/ssri/commit/4a963e5982478c6b07f86848cdb72d142c765195))
* throw null when sri is empty or bad ([a6811cb](https://github.com/npm/ssri/commit/a6811cba71e20ea1fdefa6e50c9ea3c67efc2500)), closes [#12](https://github.com/npm/ssri/issues/12)

## [7.1.0](https://github.com/npm/ssri/compare/v7.0.1...v7.1.0) (2019-10-24)

### Bug Fixes

* Do not blow up if the opts object is mutated ([806e8c8](https://github.com/npm/ssri/commit/806e8c8))

### Features

* Add Integrity#merge method ([0572c1d](https://github.com/npm/ssri/commit/0572c1d)), closes [#4](https://github.com/npm/ssri/issues/4)

### [7.0.1](https://github.com/npm/ssri/compare/v7.0.0...v7.0.1) (2019-09-30)

## [7.0.0](https://github.com/npm/ssri/compare/v6.0.1...v7.0.0) (2019-09-18)

### ⚠ BREAKING CHANGES

* ssri no longer accepts a Promise option, and does not
use, return, or rely on Bluebird promises.
* drop support for Node.js v6.

We knew this was coming, and the Stream changes are breaking anyway.
May as well do this now.
* **streams:** this replaces the Node.js stream with a Minipass
stream.  See http://npm.im/minipass for documentation.

### Bug Fixes

* return super.write() return value ([55b055d](https://github.com/npm/ssri/commit/55b055d))

* Use native promises only ([6d13165](https://github.com/npm/ssri/commit/6d13165))
* update tap, standard, standard-version, travis ([2e54956](https://github.com/npm/ssri/commit/2e54956))
* **streams:** replace transform streams with minipass ([363995e](https://github.com/npm/ssri/commit/363995e))

## [6.0.1](https://github.com/npm/ssri/compare/v6.0.0...v6.0.1) (2018-08-27)

### Bug Fixes

* **opts:** use figgy-pudding to specify consumed opts ([cf86553](https://github.com/npm/ssri/commit/cf86553))

# [6.0.0](https://github.com/npm/ssri/compare/v5.3.0...v6.0.0) (2018-04-09)

### Bug Fixes

* **docs:** minor typo ([b71ef17](https://github.com/npm/ssri/commit/b71ef17))

### meta

* drop support for node@4 ([d9bf359](https://github.com/npm/ssri/commit/d9bf359))

### BREAKING CHANGES

* node@4 is no longer supported

# [5.3.0](https://github.com/npm/ssri/compare/v5.2.4...v5.3.0) (2018-03-13)

### Features

* **checkData:** optionally throw when checkData fails ([bf26b84](https://github.com/npm/ssri/commit/bf26b84))

## [5.2.4](https://github.com/npm/ssri/compare/v5.2.3...v5.2.4) (2018-02-16)

## [5.2.3](https://github.com/npm/ssri/compare/v5.2.2...v5.2.3) (2018-02-16)

### Bug Fixes

* **hashes:** filter hash priority list by available hashes ([2fa30b8](https://github.com/npm/ssri/commit/2fa30b8))
* **integrityStream:** dedupe algorithms to generate ([d56c654](https://github.com/npm/ssri/commit/d56c654))

## [5.2.2](https://github.com/npm/ssri/compare/v5.2.1...v5.2.2) (2018-02-14)

### Bug Fixes

* **security:** tweak strict SRI regex ([#10](https://github.com/npm/ssri/issues/10)) ([d0ebcdc](https://github.com/npm/ssri/commit/d0ebcdc))

## [5.2.1](https://github.com/npm/ssri/compare/v5.2.0...v5.2.1) (2018-02-06)

# [5.2.0](https://github.com/npm/ssri/compare/v5.1.0...v5.2.0) (2018-02-06)

### Features

* **match:** add integrity.match() ([3c49cc4](https://github.com/npm/ssri/commit/3c49cc4))

# [5.1.0](https://github.com/npm/ssri/compare/v5.0.0...v5.1.0) (2018-01-18)

### Bug Fixes

* **checkStream:** integrityStream now takes opts.integrity algos into account ([d262910](https://github.com/npm/ssri/commit/d262910))

### Features

* **sha3:** do some guesswork about upcoming sha3 ([7fdd9df](https://github.com/npm/ssri/commit/7fdd9df))

# [5.0.0](https://github.com/npm/ssri/compare/v4.1.6...v5.0.0) (2017-10-23)

### Features

* **license:** relicense to ISC (#9) ([c82983a](https://github.com/npm/ssri/commit/c82983a))

### BREAKING CHANGES

* **license:** the license has been changed from CC0-1.0 to ISC.

## [4.1.6](https://github.com/npm/ssri/compare/v4.1.5...v4.1.6) (2017-06-07)

### Bug Fixes

* **checkStream:** make sure to pass all opts through ([0b1bcbe](https://github.com/npm/ssri/commit/0b1bcbe))

## [4.1.5](https://github.com/npm/ssri/compare/v4.1.4...v4.1.5) (2017-06-05)

### Bug Fixes

* **integrityStream:** stop crashing if opts.algorithms and opts.integrity have an algo mismatch ([fb1293e](https://github.com/npm/ssri/commit/fb1293e))

## [4.1.4](https://github.com/npm/ssri/compare/v4.1.3...v4.1.4) (2017-05-31)

### Bug Fixes

* **node:** older versions of node[@4](https://github.com/4) do not support base64buffer string parsing ([513df4e](https://github.com/npm/ssri/commit/513df4e))

## [4.1.3](https://github.com/npm/ssri/compare/v4.1.2...v4.1.3) (2017-05-24)

### Bug Fixes

* **check:** handle various bad hash corner cases better ([c2c262b](https://github.com/npm/ssri/commit/c2c262b))

## [4.1.2](https://github.com/npm/ssri/compare/v4.1.1...v4.1.2) (2017-04-18)

### Bug Fixes

* **stream:** _flush can be called multiple times. use on("end") ([b1c4805](https://github.com/npm/ssri/commit/b1c4805))

## [4.1.1](https://github.com/npm/ssri/compare/v4.1.0...v4.1.1) (2017-04-12)

### Bug Fixes

* **pickAlgorithm:** error if pickAlgorithm() is used in an empty Integrity ([fab470e](https://github.com/npm/ssri/commit/fab470e))

# [4.1.0](https://github.com/npm/ssri/compare/v4.0.0...v4.1.0) (2017-04-07)

### Features

* adding ssri.create for a crypto style interface (#2) ([96f52ad](https://github.com/npm/ssri/commit/96f52ad))

# [4.0.0](https://github.com/npm/ssri/compare/v3.0.2...v4.0.0) (2017-04-03)

### Bug Fixes

* **integrity:** should have changed the error code before. oops ([8381afa](https://github.com/npm/ssri/commit/8381afa))

### BREAKING CHANGES

* **integrity:** EBADCHECKSUM -> EINTEGRITY for verification errors

## [3.0.2](https://github.com/npm/ssri/compare/v3.0.1...v3.0.2) (2017-04-03)

## [3.0.1](https://github.com/npm/ssri/compare/v3.0.0...v3.0.1) (2017-04-03)

### Bug Fixes

* **package.json:** really should have these in the keywords because search ([a6ac6d0](https://github.com/npm/ssri/commit/a6ac6d0))

# [3.0.0](https://github.com/npm/ssri/compare/v2.0.0...v3.0.0) (2017-04-03)

### Bug Fixes

* **hashes:** IntegrityMetadata -> Hash ([d04aa1f](https://github.com/npm/ssri/commit/d04aa1f))

### Features

* **check:** return IntegrityMetadata on check success ([2301e74](https://github.com/npm/ssri/commit/2301e74))
* **fromHex:** ssri.fromHex to make it easier to generate them from hex valus ([049b89e](https://github.com/npm/ssri/commit/049b89e))
* **hex:** utility function for getting hex version of digest ([a9f021c](https://github.com/npm/ssri/commit/a9f021c))
* **hexDigest:** added hexDigest method to Integrity objects too ([85208ba](https://github.com/npm/ssri/commit/85208ba))
* **integrity:** add .isIntegrity and .isIntegrityMetadata ([1b29e6f](https://github.com/npm/ssri/commit/1b29e6f))
* **integrityStream:** new stream that can both generate and check streamed data ([fd23e1b](https://github.com/npm/ssri/commit/fd23e1b))
* **parse:** allow parsing straight into a single IntegrityMetadata object ([c8ddf48](https://github.com/npm/ssri/commit/c8ddf48))
* **pickAlgorithm:** Intergrity#pickAlgorithm() added ([b97a796](https://github.com/npm/ssri/commit/b97a796))
* **size:** calculate and update stream sizes ([02ed1ad](https://github.com/npm/ssri/commit/02ed1ad))

### BREAKING CHANGES

* **hashes:** `.isIntegrityMetadata` is now `.isHash`. Also, any references to `IntegrityMetadata` now refer to `Hash`.
* **integrityStream:** createCheckerStream has been removed and replaced with a general-purpose integrityStream.

To convert existing createCheckerStream code, move the `sri` argument into `opts.integrity` in integrityStream. All other options should be the same.
* **check:** `checkData`, `checkStream`, and `createCheckerStream` now yield a whole IntegrityMetadata instance representing the first successful hash match.

# [2.0.0](https://github.com/npm/ssri/compare/v1.0.0...v2.0.0) (2017-03-24)

### Bug Fixes

* **strict-mode:** make regexes more rigid ([122a32c](https://github.com/npm/ssri/commit/122a32c))

### Features

* **api:** added serialize alias for unparse ([999b421](https://github.com/npm/ssri/commit/999b421))
* **concat:** add Integrity#concat() ([cae12c7](https://github.com/npm/ssri/commit/cae12c7))
* **pickAlgo:** pick the strongest algorithm provided, by default ([58c18f7](https://github.com/npm/ssri/commit/58c18f7))
* **strict-mode:** strict SRI support ([3f0b64c](https://github.com/npm/ssri/commit/3f0b64c))
* **stringify:** replaced unparse/serialize with stringify ([4acad30](https://github.com/npm/ssri/commit/4acad30))
* **verification:** add opts.pickAlgorithm ([f72e658](https://github.com/npm/ssri/commit/f72e658))

### BREAKING CHANGES

* **pickAlgo:** ssri will prioritize specific hashes now
* **stringify:** serialize and unparse have been removed. Use ssri.stringify instead.
* **strict-mode:** functions that accepted an optional `sep` argument now expect `opts.sep`.

# 1.0.0 (2017-03-23)

### Features

* **api:** implemented initial api ([4fbb16b](https://github.com/npm/ssri/commit/4fbb16b))

### BREAKING CHANGES

* **api:** Initial API established.


code:
# ssri [![npm version](https://img.shields.io/npm/v/ssri.svg)](https://npm.im/ssri) [![license](https://img.shields.io/npm/l/ssri.svg)](https://npm.im/ssri) [![Travis](https://img.shields.io/travis/npm/ssri.svg)](https://travis-ci.org/npm/ssri) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/npm/ssri?svg=true)](https://ci.appveyor.com/project/npm/ssri) [![Coverage Status](https://coveralls.io/repos/github/npm/ssri/badge.svg?branch=latest)](https://coveralls.io/github/npm/ssri?branch=latest)

[`ssri`](https://github.com/npm/ssri), short for Standard Subresource
Integrity, is a Node.js utility for parsing, manipulating, serializing,
generating, and verifying [Subresource
Integrity](https://w3c.github.io/webappsec/specs/subresourceintegrity/) hashes.

## Install

`$ npm install --save ssri`

## Table of Contents

* [Example](#example)
* [Features](#features)
* [Contributing](#contributing)
* [API](#api)
  * Parsing & Serializing
    * [`parse`](#parse)
    * [`stringify`](#stringify)
    * [`Integrity#concat`](#integrity-concat)
    * [`Integrity#merge`](#integrity-merge)
    * [`Integrity#toString`](#integrity-to-string)
    * [`Integrity#toJSON`](#integrity-to-json)
    * [`Integrity#match`](#integrity-match)
    * [`Integrity#pickAlgorithm`](#integrity-pick-algorithm)
    * [`Integrity#hexDigest`](#integrity-hex-digest)
  * Integrity Generation
    * [`fromHex`](#from-hex)
    * [`fromData`](#from-data)
    * [`fromStream`](#from-stream)
    * [`create`](#create)
  * Integrity Verification
    * [`checkData`](#check-data)
    * [`checkStream`](#check-stream)
    * [`integrityStream`](#integrity-stream)

### Example

```javascript
const ssri = require('ssri')

const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'

// Parsing and serializing
const parsed = ssri.parse(integrity)
ssri.stringify(parsed) // === integrity (works on non-Integrity objects)
parsed.toString() // === integrity

// Async stream functions
ssri.checkStream(fs.createReadStream('./my-file'), integrity).then(...)
ssri.fromStream(fs.createReadStream('./my-file')).then(sri => {
  sri.toString() === integrity
})
fs.createReadStream('./my-file').pipe(ssri.createCheckerStream(sri))

// Sync data functions
ssri.fromData(fs.readFileSync('./my-file')) // === parsed
ssri.checkData(fs.readFileSync('./my-file'), integrity) // => 'sha512'
```

### Features

* Parses and stringifies SRI strings.
* Generates SRI strings from raw data or Streams.
* Strict standard compliance.
* `?foo` metadata option support.
* Multiple entries for the same algorithm.
* Object-based integrity hash manipulation.
* Small footprint: no dependencies, concise implementation.
* Full test coverage.
* Customizable algorithm picker.

### Contributing

The ssri team enthusiastically welcomes contributions and project participation!
There's a bunch of things you can do if you want to contribute! The [Contributor
Guide](CONTRIBUTING.md) has all the information you need for everything from
reporting bugs to contributing entire new features. Please don't hesitate to
jump in if you'd like to, or even ask us questions if something isn't clear.

### API

####  `> ssri.parse(sri, [opts]) -> Integrity`

Parses `sri` into an `Integrity` data structure. `sri` can be an integrity
string, an `Hash`-like with `digest` and `algorithm` fields and an optional
`options` field, or an `Integrity`-like object. The resulting object will be an
`Integrity` instance that has this shape:

```javascript
{
  'sha1': [{algorithm: 'sha1', digest: 'deadbeef', options: []}],
  'sha512': [
    {algorithm: 'sha512', digest: 'c0ffee', options: []},
    {algorithm: 'sha512', digest: 'bad1dea', options: ['foo']}
  ],
}
```

If `opts.single` is truthy, a single `Hash` object will be returned. That is, a
single object that looks like `{algorithm, digest, options}`, as opposed to a
larger object with multiple of these.

If `opts.strict` is truthy, the resulting object will be filtered such that
it strictly follows the Subresource Integrity spec, throwing away any entries
with any invalid components. This also means a restricted set of algorithms
will be used -- the spec limits them to `sha256`, `sha384`, and `sha512`.

Strict mode is recommended if the integrity strings are intended for use in
browsers, or in other situations where strict adherence to the spec is needed.

##### Example

```javascript
ssri.parse('sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo') // -> Integrity object
```

####  `> ssri.stringify(sri, [opts]) -> String`

This function is identical to [`Integrity#toString()`](#integrity-to-string),
except it can be used on _any_ object that [`parse`](#parse) can handle -- that
is, a string, an `Hash`-like, or an `Integrity`-like.

The `opts.sep` option defines the string to use when joining multiple entries
together. To be spec-compliant, this _must_ be whitespace. The default is a
single space (`' '`).

If `opts.strict` is true, the integrity string will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
// Useful for cleaning up input SRI strings:
ssri.stringify('\n\rsha512-foo\n\t\tsha384-bar')
// -> 'sha512-foo sha384-bar'

// Hash-like: only a single entry.
ssri.stringify({
  algorithm: 'sha512',
  digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==',
  options: ['foo']
})
// ->
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'

// Integrity-like: full multi-entry syntax. Similar to output of `ssri.parse`
ssri.stringify({
  'sha512': [
    {
      algorithm: 'sha512',
      digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==',
      options: ['foo']
    }
  ]
})
// ->
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'
```

####  `> Integrity#concat(otherIntegrity, [opts]) -> Integrity`

Concatenates an `Integrity` object with another IntegrityLike, or an integrity
string.

This is functionally equivalent to concatenating the string format of both
integrity arguments, and calling [`ssri.parse`](#ssri-parse) on the new string.

If `opts.strict` is true, the new `Integrity` will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
// This will combine the integrity checks for two different versions of
// your index.js file so you can use a single integrity string and serve
// either of these to clients, from a single `<script>` tag.
const desktopIntegrity = ssri.fromData(fs.readFileSync('./index.desktop.js'))
const mobileIntegrity = ssri.fromData(fs.readFileSync('./index.mobile.js'))

// Note that browsers (and ssri) will succeed as long as ONE of the entries
// for the *prioritized* algorithm succeeds. That is, in order for this fallback
// to work, both desktop and mobile *must* use the same `algorithm` values.
desktopIntegrity.concat(mobileIntegrity)
```

####  `> Integrity#merge(otherIntegrity, [opts])`

Safely merges another IntegrityLike or integrity string into an `Integrity`
object.

If the other integrity value has any algorithms in common with the current
object, then the hash digests must match, or an error is thrown.

Any new hashes will be added to the current object's set.

This is useful when an integrity value may be upgraded with a stronger
algorithm, you wish to prevent accidentally supressing integrity errors by
overwriting the expected integrity value.

##### Example

```javascript
const data = fs.readFileSync('data.txt')

// integrity.txt contains 'sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4='
// because we were young, and didn't realize sha1 would not last
const expectedIntegrity = ssri.parse(fs.readFileSync('integrity.txt', 'utf8'))
const match = ssri.checkData(data, expectedIntegrity, {
  algorithms: ['sha512', 'sha1']
})
if (!match) {
  throw new Error('data corrupted or something!')
}

// get a stronger algo!
if (match && match.algorithm !== 'sha512') {
  const updatedIntegrity = ssri.fromData(data, { algorithms: ['sha512'] })
  expectedIntegrity.merge(updatedIntegrity)
  fs.writeFileSync('integrity.txt', expectedIntegrity.toString())
  // file now contains
  // 'sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4= sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg=='
}
```

####  `> Integrity#toString([opts]) -> String`

Returns the string representation of an `Integrity` object. All hash entries
will be concatenated in the string by `opts.sep`, which defaults to `' '`.

If you want to serialize an object that didn't come from an `ssri` function,
use [`ssri.stringify()`](#stringify).

If `opts.strict` is true, the integrity string will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo'

ssri.parse(integrity).toString() === integrity
```

####  `> Integrity#toJSON() -> String`

Returns the string representation of an `Integrity` object. All hash entries
will be concatenated in the string by `' '`.

This is a convenience method so you can pass an `Integrity` object directly to `JSON.stringify`.
For more info check out [toJSON() behavior on mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior).

##### Example

```javascript
const integrity = '"sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo"'

JSON.stringify(ssri.parse(integrity)) === integrity
```

####  `> Integrity#match(sri, [opts]) -> Hash | false`

Returns the matching (truthy) hash if `Integrity` matches the argument passed as
`sri`, which can be anything that [`parse`](#parse) will accept. `opts` will be
passed through to `parse` and [`pickAlgorithm()`](#integrity-pick-algorithm).

##### Example

```javascript
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A=='

ssri.parse(integrity).match(integrity)
// Hash {
//   digest: '9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A=='
//   algorithm: 'sha512'
// }

ssri.parse(integrity).match('sha1-deadbeef')
// false
```

####  `> Integrity#pickAlgorithm([opts]) -> String`

Returns the "best" algorithm from those available in the integrity object.

If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
arguments. ssri will prioritize whichever of the two algorithms is returned by
this function. Note that the function may be called multiple times, and it
**must** return one of the two algorithms provided. By default, ssri will make
a best-effort to pick the strongest/most reliable of the given algorithms. It
may intentionally deprioritize algorithms with known vulnerabilities.

##### Example

```javascript
ssri.parse('sha1-WEakDigEST sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1').pickAlgorithm() // sha512
```

####  `> Integrity#hexDigest() -> String`

`Integrity` is assumed to be either a single-hash `Integrity` instance, or a
`Hash` instance. Returns its `digest`, converted to a hex representation of the
base64 data.

##### Example

```javascript
ssri.parse('sha1-deadbeef').hexDigest() // '75e69d6de79f'
```

####  `> ssri.fromHex(hexDigest, algorithm, [opts]) -> Integrity`

Creates an `Integrity` object with a single entry, based on a hex-formatted
hash. This is a utility function to help convert existing shasums to the
Integrity format, and is roughly equivalent to something like:

```javascript
algorithm + '-' + Buffer.from(hexDigest, 'hex').toString('base64')
```

`opts.options` may optionally be passed in: it must be an array of option
strings that will be added to all generated integrity hashes generated by
`fromData`. This is a loosely-specified feature of SRIs, and currently has no
specified semantics besides being `?`-separated. Use at your own risk, and
probably avoid if your integrity strings are meant to be used with browsers.

If `opts.strict` is true, the integrity object will be created using strict
parsing rules. See [`ssri.parse`](#parse).

If `opts.single` is true, a single `Hash` object will be returned.

##### Example

```javascript
ssri.fromHex('75e69d6de79f', 'sha1').toString() // 'sha1-deadbeef'
```

####  `> ssri.fromData(data, [opts]) -> Integrity`

Creates an `Integrity` object from either string or `Buffer` data, calculating
all the requested hashes and adding any specified options to the object.

`opts.algorithms` determines which algorithms to generate hashes for. All
results will be included in a single `Integrity` object. The default value for
`opts.algorithms` is `['sha512']`. All algorithm strings must be hashes listed
in `crypto.getHashes()` for the host Node.js platform.

`opts.options` may optionally be passed in: it must be an array of option
strings that will be added to all generated integrity hashes generated by
`fromData`. This is a loosely-specified feature of SRIs, and currently has no
specified semantics besides being `?`-separated. Use at your own risk, and
probably avoid if your integrity strings are meant to be used with browsers.

If `opts.strict` is true, the integrity object will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
const integrityObj = ssri.fromData('foobarbaz', {
  algorithms: ['sha256', 'sha384', 'sha512']
})
integrity.toString('\n')
// ->
// sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0=
// sha384-irnCxQ0CfQhYGlVAUdwTPC9bF3+YWLxlaDGM4xbYminxpbXEq+D+2GCEBTxcjES9
// sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg==
```

####  `> ssri.fromStream(stream, [opts]) -> Promise<Integrity>`

Returns a Promise of an Integrity object calculated by reading data from
a given `stream`.

It accepts both `opts.algorithms` and `opts.options`, which are documented as
part of [`ssri.fromData`](#from-data).

Additionally, `opts.Promise` may be passed in to inject a Promise library of
choice. By default, ssri will use Node's built-in Promises.

If `opts.strict` is true, the integrity object will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
ssri.fromStream(fs.createReadStream('index.js'), {
  algorithms: ['sha1', 'sha512']
}).then(integrity => {
  return ssri.checkStream(fs.createReadStream('index.js'), integrity)
}) // succeeds
```

####  `> ssri.create([opts]) -> <Hash>`

Returns a Hash object with `update(<Buffer or string>[,enc])` and `digest()` methods.

The Hash object provides the same methods as [crypto class Hash](https://nodejs.org/dist/latest-v6.x/docs/api/crypto.html#crypto_class_hash).
`digest()` accepts no arguments and returns an Integrity object calculated by reading data from
calls to update.

It accepts both `opts.algorithms` and `opts.options`, which are documented as
part of [`ssri.fromData`](#from-data).

If `opts.strict` is true, the integrity object will be created using strict
parsing rules. See [`ssri.parse`](#parse).

##### Example

```javascript
const integrity = ssri.create().update('foobarbaz').digest()
integrity.toString()
// ->
// sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg==
```

####  `> ssri.checkData(data, sri, [opts]) -> Hash|false`

Verifies `data` integrity against an `sri` argument. `data` may be either a
`String` or a `Buffer`, and `sri` can be any subresource integrity
representation that [`ssri.parse`](#parse) can handle.

If verification succeeds, `checkData` will return the name of the algorithm that
was used for verification (a truthy value). Otherwise, it will return `false`.

If `opts.pickAlgorithm` is provided, it will be used by
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
the available digests to match against.

If `opts.error` is true, and verification fails, `checkData` will throw either
an `EBADSIZE` or an `EINTEGRITY` error, instead of just returning false.

##### Example

```javascript
const data = fs.readFileSync('index.js')
ssri.checkData(data, ssri.fromData(data)) // -> 'sha512'
ssri.checkData(data, 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0')
ssri.checkData(data, 'sha1-BaDDigEST') // -> false
ssri.checkData(data, 'sha1-BaDDigEST', {error: true}) // -> Error! EINTEGRITY
```

####  `> ssri.checkStream(stream, sri, [opts]) -> Promise<Hash>`

Verifies the contents of `stream` against an `sri` argument. `stream` will be
consumed in its entirety by this process. `sri` can be any subresource integrity
representation that [`ssri.parse`](#parse) can handle.

`checkStream` will return a Promise that either resolves to the
`Hash` that succeeded verification, or, if the verification fails
or an error happens with `stream`, the Promise will be rejected.

If the Promise is rejected because verification failed, the returned error will
have `err.code` as `EINTEGRITY`.

If `opts.size` is given, it will be matched against the stream size. An error
with `err.code` `EBADSIZE` will be returned by a rejection if the expected size
and actual size fail to match.

If `opts.pickAlgorithm` is provided, it will be used by
[`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of
the available digests to match against.

##### Example

```javascript
const integrity = ssri.fromData(fs.readFileSync('index.js'))

ssri.checkStream(
  fs.createReadStream('index.js'),
  integrity
)
// ->
// Promise<{
//   algorithm: 'sha512',
//   digest: 'sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1'
// }>

ssri.checkStream(
  fs.createReadStream('index.js'),
  'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0'
) // -> Promise<Hash>

ssri.checkStream(
  fs.createReadStream('index.js'),
  'sha1-BaDDigEST'
) // -> Promise<Error<{code: 'EINTEGRITY'}>>
```

####  `> integrityStream([opts]) -> IntegrityStream`

Returns a `Transform` stream that data can be piped through in order to generate
and optionally check data integrity for piped data. When the stream completes
successfully, it emits `size` and `integrity` events, containing the total
number of bytes processed and a calculated `Integrity` instance based on stream
data, respectively.

If `opts.algorithms` is passed in, the listed algorithms will be calculated when
generating the final `Integrity` instance. The default is `['sha512']`.

If `opts.single` is passed in, a single `Hash` instance will be returned.

If `opts.integrity` is passed in, it should be an `integrity` value understood
by [`parse`](#parse) that the stream will check the data against. If
verification succeeds, the integrity stream will emit a `verified` event whose
value is a single `Hash` object that is the one that succeeded verification. If
verification fails, the stream will error with an `EINTEGRITY` error code.

If `opts.size` is given, it will be matched against the stream size. An error
with `err.code` `EBADSIZE` will be emitted by the stream if the expected size
and actual size fail to match.

If `opts.pickAlgorithm` is provided, it will be passed two algorithms as
arguments. ssri will prioritize whichever of the two algorithms is returned by
this function. Note that the function may be called multiple times, and it
**must** return one of the two algorithms provided. By default, ssri will make
a best-effort to pick the strongest/most reliable of the given algorithms. It
may intentionally deprioritize algorithms with known vulnerabilities.

##### Example

```javascript
const integrity = ssri.fromData(fs.readFileSync('index.js'))
fs.createReadStream('index.js')
.pipe(ssri.integrityStream({integrity}))
```