qiwi/qorsproxy

View on GitHub
src/main/js/container/container.js

Summary

Maintainability
A
0 mins
Test Coverage
A
99%
import { find } from '../base/index.js'
import log from '../log/index.js'
import { HttpServer, HttpsServer, ServerHelper } from './server.js'

export class Container {
  constructor () {
    this.online = false
    this.initServer()
    this.initSecureServer()
  }

  shouldRestart ({ host, port, secure }) {
    return this.online && (
      this.port !== port ||
      this.host !== host ||
      this.secure.port !== secure.port ||
      this.secure.cert !== secure.cert ||
      this.secure.key !== secure.key
    )
  }

  async configure ({ host, port, secure, servlets }) {
    if (servlets) {
      this.servlets = servlets
    }

    port = port | 0

    const restart = this.shouldRestart({ host, port, secure })

    this.secure = secure
    this.port = port
    this.host = host

    restart &&
    await this.restart()

    log.info('Container configured.')

    return this
  }

  collector (req, res) {
    const body = []

    req
      .on('data', chunk => {
        chunk && body.push(chunk)
      })
      .on('end', () => {
        if (body.length > 0) {
          req.body = Buffer.concat(body)
        }
        this.handler(req, res)
      })
  }

  handler (req, res) {
    const url = req.url
    let route

    if (!this.online) {
      res.end()
      return
    }
    const servlet = find(this.servlets, (servlet, r) => {
      route = r // TODO add servlet name prop
      return !url.indexOf(r)
    })

    if (!servlet) {
      log.error(`Servlet not found. url=${url}`)
      ServerHelper.notFound(req, res)
      return
    }
    try {
      servlet.handler(req, res)
    } catch (e) {
      log.error(`Servlet unhandled exception. route=${route} url=${url}`, e)
      ServerHelper.internalError(req, res)
    }
  }

  initServer () {
    this.server = new HttpServer()
    this.server.on('request', this.collector.bind(this))
  }

  initSecureServer () {
    this.httpsServer = new HttpsServer(this.secure)
    this.httpsServer.on('request', this.collector.bind(this))
  }

  async start () {
    if (!this.online) {
      this.initSecureServer()
      await Promise.all([
        this.server.listen(this.port, this.host),
        this.secure ? this.httpsServer.listen(this.secure.port, this.host) : Promise.resolve()
      ])

      const entrypoints = [
        `http://${this.host}:${this.port}`,
        this.secure ? `https://${this.host}:${this.secure.port}` : ''
      ].filter(Boolean)

      log.info(`Container is online: ${entrypoints.join(', ')}`)
      this.online = true
    }

    return this
  }

  async stop () {
    if (this.online) {
      // NOTE Stops the server from accepting new connections and keeps existing connections.
      // This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event.
      this.online = false
      await Promise.all([
        this.server.close(),
        this.secure && this.httpsServer.close()
      ])
      log.warn('Container stopped.')
    }

    return this
  }

  async restart () {
    log.warn('Container restart required.')
    await this.stop()
    await this.start()

    return this
  }
}

export default Container