Index: frontend/node_modules/spdy/.travis.yml
===================================================================
--- frontend/node_modules/spdy/.travis.yml	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/.travis.yml	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+sudo: false
+
+language: node_js
+
+node_js:
+  - "6"
+  - "8"
+  - "10"
+
+script:
+  - npm run lint
+  - npm test
+  - npm run coverage
Index: frontend/node_modules/spdy/README.md
===================================================================
--- frontend/node_modules/spdy/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,267 @@
+# SPDY Server for node.js
+
+[![Build Status](https://travis-ci.org/spdy-http2/node-spdy.svg?branch=master)](http://travis-ci.org/spdy-http2/node-spdy)
+[![NPM version](https://badge.fury.io/js/spdy.svg)](http://badge.fury.io/js/spdy)
+[![dependencies Status](https://david-dm.org/spdy-http2/node-spdy/status.svg?style=flat-square)](https://david-dm.org/spdy-http2/node-spdy)
+[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](http://standardjs.com/)
+[![Waffle](https://img.shields.io/badge/track-waffle-blue.svg?style=flat-square)](https://waffle.io/spdy-http2/node-spdy)
+
+With this module you can create [HTTP2][0] / [SPDY][1] servers
+in node.js with natural http module interface and fallback to regular https
+(for browsers that don't support neither HTTP2, nor SPDY yet).
+
+This module named `spdy` but it [provides](https://github.com/indutny/node-spdy/issues/269#issuecomment-239014184) support for both http/2 (h2) and spdy (2,3,3.1). Also, `spdy` is compatible with Express.
+
+## Usage
+
+### Examples
+
+Server:
+```javascript
+var spdy = require('spdy'),
+    fs = require('fs');
+
+var options = {
+  // Private key
+  key: fs.readFileSync(__dirname + '/keys/spdy-key.pem'),
+
+  // Fullchain file or cert file (prefer the former)
+  cert: fs.readFileSync(__dirname + '/keys/spdy-fullchain.pem'),
+
+  // **optional** SPDY-specific options
+  spdy: {
+    protocols: [ 'h2', 'spdy/3.1', ..., 'http/1.1' ],
+    plain: false,
+
+    // **optional**
+    // Parse first incoming X_FORWARDED_FOR frame and put it to the
+    // headers of every request.
+    // NOTE: Use with care! This should not be used without some proxy that
+    // will *always* send X_FORWARDED_FOR
+    'x-forwarded-for': true,
+
+    connection: {
+      windowSize: 1024 * 1024, // Server's window size
+
+      // **optional** if true - server will send 3.1 frames on 3.0 *plain* spdy
+      autoSpdy31: false
+    }
+  }
+};
+
+var server = spdy.createServer(options, function(req, res) {
+  res.writeHead(200);
+  res.end('hello world!');
+});
+
+server.listen(3000);
+```
+
+Client:
+```javascript
+var spdy = require('spdy');
+var https = require('https');
+
+var agent = spdy.createAgent({
+  host: 'www.google.com',
+  port: 443,
+
+  // Optional SPDY options
+  spdy: {
+    plain: false,
+    ssl: true,
+
+    // **optional** send X_FORWARDED_FOR
+    'x-forwarded-for': '127.0.0.1'
+  }
+});
+
+https.get({
+  host: 'www.google.com',
+  agent: agent
+}, function(response) {
+  console.log('yikes');
+  // Here it goes like with any other node.js HTTP request
+  // ...
+  // And once we're done - we may close TCP connection to server
+  // NOTE: All non-closed requests will die!
+  agent.close();
+}).end();
+```
+
+Please note that if you use a custom agent, by default all connection-level
+errors will result in an uncaught exception. To handle these errors subscribe
+to the `error` event and re-emit the captured error:
+
+```javascript
+var agent = spdy.createAgent({
+  host: 'www.google.com',
+  port: 443
+}).once('error', function (err) {
+  this.emit(err);
+});
+```
+
+#### Push streams
+
+It is possible to initiate [PUSH_PROMISE][5] to send content to clients _before_
+the client requests it.
+
+```javascript
+spdy.createServer(options, function(req, res) {
+  var stream = res.push('/main.js', {
+    status: 200, // optional
+    method: 'GET', // optional
+    request: {
+      accept: '*/*'
+    },
+    response: {
+      'content-type': 'application/javascript'
+    }
+  });
+  stream.on('error', function() {
+  });
+  stream.end('alert("hello from push stream!");');
+
+  res.end('<script src="/main.js"></script>');
+}).listen(3000);
+```
+
+[PUSH_PROMISE][5] may be sent using the `push()` method on the current response
+object.  The signature of the `push()` method is:
+
+`.push('/some/relative/url', { request: {...}, response: {...} }, callback)`
+
+Second argument contains headers for both PUSH_PROMISE and emulated response.
+`callback` will receive two arguments: `err` (if any error is happened) and a
+[Duplex][4] stream as the second argument.
+
+Client usage:
+```javascript
+var agent = spdy.createAgent({ /* ... */ });
+var req = http.get({
+  host: 'www.google.com',
+  agent: agent
+}, function(response) {
+});
+req.on('push', function(stream) {
+  stream.on('error', function(err) {
+    // Handle error
+  });
+  // Read data from stream
+});
+```
+
+NOTE: You're responsible for the `stream` object once given it in `.push()`
+callback or `push` event. Hence ignoring `error` event on it will result in
+uncaught exception and crash your program.
+
+#### Trailing headers
+
+Server usage:
+```javascript
+function (req, res) {
+  // Send trailing headers to client
+  res.addTrailers({ header1: 'value1', header2: 'value2' });
+
+  // On client's trailing headers
+  req.on('trailers', function(headers) {
+    // ...
+  });
+}
+```
+
+Client usage:
+```javascript
+var req = http.request({ agent: spdyAgent, /* ... */ }).function (res) {
+  // On server's trailing headers
+  res.on('trailers', function(headers) {
+    // ...
+  });
+});
+req.write('stuff');
+req.addTrailers({ /* ... */ });
+req.end();
+```
+
+#### Options
+
+All options supported by [tls][2] work with node-spdy.
+
+Additional options may be passed via `spdy` sub-object:
+
+* `plain` - if defined, server will ignore NPN and ALPN data and choose whether
+  to use spdy or plain http by looking at first data packet.
+* `ssl` - if `false` and `options.plain` is `true`, `http.Server` will be used
+  as a `base` class for created server.
+* `maxChunk` - if set and non-falsy, limits number of bytes sent in one DATA
+  chunk. Setting it to non-zero value is recommended if you care about
+  interleaving of outgoing data from multiple different streams.
+  (defaults to 8192)
+* `protocols` - list of NPN/ALPN protocols to use (default is:
+  `['h2','spdy/3.1', 'spdy/3', 'spdy/2','http/1.1', 'http/1.0']`)
+* `protocol` - use specific protocol if no NPN/ALPN ex In addition,
+* `maxStreams` - set "[maximum concurrent streams][3]" protocol option
+
+### API
+
+API is compatible with `http` and `https` module, but you can use another
+function as base class for SPDYServer.
+
+```javascript
+spdy.createServer(
+  [base class constructor, i.e. https.Server],
+  { /* keys and options */ }, // <- the only one required argument
+  [request listener]
+).listen([port], [host], [callback]);
+```
+
+Request listener will receive two arguments: `request` and `response`. They're
+both instances of `http`'s `IncomingMessage` and `OutgoingMessage`. But three
+custom properties are added to both of them: `isSpdy`, `spdyVersion`. `isSpdy`
+is `true` when the request was processed using HTTP2/SPDY protocols, it is
+`false` in case of HTTP/1.1 fallback. `spdyVersion` is either of: `2`, `3`,
+`3.1`, or `4` (for HTTP2).
+
+
+#### Contributors
+
+* [Fedor Indutny](https://github.com/indutny)
+* [Chris Strom](https://github.com/eee-c)
+* [François de Metz](https://github.com/francois2metz)
+* [Ilya Grigorik](https://github.com/igrigorik)
+* [Roberto Peon](https://github.com/grmocg)
+* [Tatsuhiro Tsujikawa](https://github.com/tatsuhiro-t)
+* [Jesse Cravens](https://github.com/jessecravens)
+
+#### LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2015.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[0]: https://http2.github.io/
+[1]: http://www.chromium.org/spdy
+[2]: http://nodejs.org/docs/latest/api/tls.html#tls.createServer
+[3]: https://httpwg.github.io/specs/rfc7540.html#SETTINGS_MAX_CONCURRENT_STREAMS
+[4]: https://iojs.org/api/stream.html#stream_class_stream_duplex
+[5]: https://httpwg.github.io/specs/rfc7540.html#PUSH_PROMISE
Index: frontend/node_modules/spdy/lib/spdy.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+'use strict'
+
+var spdy = exports
+
+// Export tools
+spdy.handle = require('./spdy/handle')
+spdy.request = require('./spdy/request')
+spdy.response = require('./spdy/response')
+spdy.Socket = require('./spdy/socket')
+
+// Export client
+spdy.agent = require('./spdy/agent')
+spdy.Agent = spdy.agent.Agent
+spdy.createAgent = spdy.agent.create
+
+// Export server
+spdy.server = require('./spdy/server')
+spdy.Server = spdy.server.Server
+spdy.PlainServer = spdy.server.PlainServer
+spdy.createServer = spdy.server.create
Index: frontend/node_modules/spdy/lib/spdy/agent.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/agent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/agent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,295 @@
+'use strict'
+
+var assert = require('assert')
+var http = require('http')
+var https = require('https')
+var net = require('net')
+var util = require('util')
+var transport = require('spdy-transport')
+var debug = require('debug')('spdy:client')
+
+// Node.js 0.10 and 0.12 support
+Object.assign = process.versions.modules >= 46
+  ? Object.assign // eslint-disable-next-line
+  : util._extend
+
+var EventEmitter = require('events').EventEmitter
+
+var spdy = require('../spdy')
+
+var mode = /^v0\.8\./.test(process.version)
+  ? 'rusty'
+  : /^v0\.(9|10)\./.test(process.version)
+    ? 'old'
+    : /^v0\.12\./.test(process.version)
+      ? 'normal'
+      : 'modern'
+
+var proto = {}
+
+function instantiate (base) {
+  function Agent (options) {
+    this._init(base, options)
+  }
+  util.inherits(Agent, base)
+
+  Agent.create = function create (options) {
+    return new Agent(options)
+  }
+
+  Object.keys(proto).forEach(function (key) {
+    Agent.prototype[key] = proto[key]
+  })
+
+  return Agent
+}
+
+proto._init = function _init (base, options) {
+  base.call(this, options)
+
+  var state = {}
+  this._spdyState = state
+
+  state.host = options.host
+  state.options = options.spdy || {}
+  state.secure = this instanceof https.Agent
+  state.fallback = false
+  state.createSocket = this._getCreateSocket()
+  state.socket = null
+  state.connection = null
+
+  // No chunked encoding
+  this.keepAlive = false
+
+  var self = this
+  this._connect(options, function (err, connection) {
+    if (err) {
+      return self.emit('error', err)
+    }
+
+    state.connection = connection
+    self.emit('_connect')
+  })
+}
+
+proto._getCreateSocket = function _getCreateSocket () {
+  // Find super's `createSocket` method
+  var createSocket
+  var cons = this.constructor.super_
+  do {
+    createSocket = cons.prototype.createSocket
+
+    if (cons.super_ === EventEmitter || !cons.super_) {
+      break
+    }
+    cons = cons.super_
+  } while (!createSocket)
+  if (!createSocket) {
+    createSocket = http.Agent.prototype.createSocket
+  }
+
+  assert(createSocket, '.createSocket() method not found')
+
+  return createSocket
+}
+
+proto._connect = function _connect (options, callback) {
+  var self = this
+  var state = this._spdyState
+
+  var protocols = state.options.protocols || [
+    'h2',
+    'spdy/3.1', 'spdy/3', 'spdy/2',
+    'http/1.1', 'http/1.0'
+  ]
+
+  // TODO(indutny): reconnect automatically?
+  var socket = this.createConnection(Object.assign({
+    NPNProtocols: protocols,
+    ALPNProtocols: protocols,
+    servername: options.servername || options.host
+  }, options))
+  state.socket = socket
+
+  socket.setNoDelay(true)
+
+  function onError (err) {
+    return callback(err)
+  }
+  socket.on('error', onError)
+
+  socket.on(state.secure ? 'secureConnect' : 'connect', function () {
+    socket.removeListener('error', onError)
+
+    var protocol
+    if (state.secure) {
+      protocol = socket.npnProtocol ||
+                 socket.alpnProtocol ||
+                 state.options.protocol
+    } else {
+      protocol = state.options.protocol
+    }
+
+    // HTTP server - kill socket and switch to the fallback mode
+    if (!protocol || protocol === 'http/1.1' || protocol === 'http/1.0') {
+      debug('activating fallback')
+      socket.destroy()
+      state.fallback = true
+      return
+    }
+
+    debug('connected protocol=%j', protocol)
+    var connection = transport.connection.create(socket, Object.assign({
+      protocol: /spdy/.test(protocol) ? 'spdy' : 'http2',
+      isServer: false
+    }, state.options.connection || {}))
+
+    // Pass connection level errors are passed to the agent.
+    connection.on('error', function (err) {
+      self.emit('error', err)
+    })
+
+    // Set version when we are certain
+    if (protocol === 'h2') {
+      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)
+    } else {
+      socket.destroy()
+      callback(new Error('Unexpected protocol: ' + protocol))
+      return
+    }
+
+    if (state.options['x-forwarded-for'] !== undefined) {
+      connection.sendXForwardedFor(state.options['x-forwarded-for'])
+    }
+
+    callback(null, connection)
+  })
+}
+
+proto._createSocket = function _createSocket (req, options, callback) {
+  var state = this._spdyState
+  if (state.fallback) { return state.createSocket(req, options) }
+
+  var handle = spdy.handle.create(null, null, state.socket)
+
+  var socketOptions = {
+    handle: handle,
+    allowHalfOpen: true
+  }
+
+  var socket
+  if (state.secure) {
+    socket = new spdy.Socket(state.socket, socketOptions)
+  } else {
+    socket = new net.Socket(socketOptions)
+  }
+
+  handle.assignSocket(socket)
+  handle.assignClientRequest(req)
+
+  // Create stream only once `req.end()` is called
+  var self = this
+  handle.once('needStream', function () {
+    if (state.connection === null) {
+      self.once('_connect', function () {
+        handle.setStream(self._createStream(req, handle))
+      })
+    } else {
+      handle.setStream(self._createStream(req, handle))
+    }
+  })
+
+  // Yes, it is in reverse
+  req.on('response', function (res) {
+    handle.assignRequest(res)
+  })
+  handle.assignResponse(req)
+
+  // Handle PUSH
+  req.addListener('newListener', spdy.request.onNewListener)
+
+  // For v0.8
+  socket.readable = true
+  socket.writable = true
+
+  if (callback) {
+    return callback(null, socket)
+  }
+
+  return socket
+}
+
+if (mode === 'modern' || mode === 'normal') {
+  proto.createSocket = proto._createSocket
+} else {
+  proto.createSocket = function createSocket (name, host, port, addr, req) {
+    var state = this._spdyState
+    if (state.fallback) {
+      return state.createSocket(name, host, port, addr, req)
+    }
+
+    return this._createSocket(req, {
+      host: host,
+      port: port
+    })
+  }
+}
+
+proto._createStream = function _createStream (req, handle) {
+  var state = this._spdyState
+
+  var self = this
+  return state.connection.reserveStream({
+    method: req.method,
+    path: req.path,
+    headers: req._headers,
+    host: state.host
+  }, function (err, stream) {
+    if (err) {
+      return self.emit('error', err)
+    }
+
+    stream.on('response', function (status, headers) {
+      handle.emitResponse(status, headers)
+    })
+  })
+}
+
+// Public APIs
+
+proto.close = function close (callback) {
+  var state = this._spdyState
+
+  if (state.connection === null) {
+    this.once('_connect', function () {
+      this.close(callback)
+    })
+    return
+  }
+
+  state.connection.end(callback)
+}
+
+exports.Agent = instantiate(https.Agent)
+exports.PlainAgent = instantiate(http.Agent)
+
+exports.create = function create (base, options) {
+  if (typeof base === 'object') {
+    options = base
+    base = null
+  }
+
+  if (base) {
+    return instantiate(base).create(options)
+  }
+
+  if (options.spdy && options.spdy.plain) {
+    return exports.PlainAgent.create(options)
+  } else { return exports.Agent.create(options) }
+}
Index: frontend/node_modules/spdy/lib/spdy/handle.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/handle.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/handle.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,247 @@
+'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)
+  })
+}
Index: frontend/node_modules/spdy/lib/spdy/request.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/request.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/request.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,33 @@
+'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)
+}
Index: frontend/node_modules/spdy/lib/spdy/response.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/response.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/response.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,100 @@
+'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)
+  }
+}
Index: frontend/node_modules/spdy/lib/spdy/server.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/server.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/server.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,288 @@
+'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)
+  }
+}
Index: frontend/node_modules/spdy/lib/spdy/socket.js
===================================================================
--- frontend/node_modules/spdy/lib/spdy/socket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/lib/spdy/socket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,39 @@
+'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)
+  }
+})
Index: frontend/node_modules/spdy/package.json
===================================================================
--- frontend/node_modules/spdy/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,54 @@
+{
+  "name": "spdy",
+  "version": "4.0.2",
+  "description": "Implementation of the SPDY protocol on node.js.",
+  "license": "MIT",
+  "scripts": {
+    "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"
+  ],
+  "keywords": [
+    "spdy"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/indutny/node-spdy.git"
+  },
+  "homepage": "https://github.com/indutny/node-spdy",
+  "bugs": {
+    "email": "node-spdy+bugs@indutny.com",
+    "url": "https://github.com/spdy-http2/node-spdy/issues"
+  },
+  "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>"
+  ],
+  "dependencies": {
+    "debug": "^4.1.0",
+    "handle-thing": "^2.0.0",
+    "http-deceiver": "^1.2.7",
+    "select-hose": "^2.0.0",
+    "spdy-transport": "^3.0.0"
+  },
+  "devDependencies": {
+    "istanbul": "^0.4.5",
+    "mocha": "^6.2.3",
+    "pre-commit": "^1.2.2",
+    "standard": "^13.1.0"
+  },
+  "engines": {
+    "node": ">=6.0.0"
+  },
+  "main": "./lib/spdy",
+  "optionalDependencies": {}
+}
Index: frontend/node_modules/spdy/test/client-test.js
===================================================================
--- frontend/node_modules/spdy/test/client-test.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/test/client-test.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,242 @@
+/* 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'))
+      })
+    })
+  })
+})
Index: frontend/node_modules/spdy/test/fixtures.js
===================================================================
--- frontend/node_modules/spdy/test/fixtures.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/test/fixtures.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,95 @@
+/* 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)
+      })
+    })
+  })
+}
Index: frontend/node_modules/spdy/test/server-test.js
===================================================================
--- frontend/node_modules/spdy/test/server-test.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/spdy/test/server-test.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,487 @@
+/* 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()
+    })
+  })
+})
