text: //
  //  Created by Mingliang Chen on 17/8/1.
  //  illuspas[a]gmail.com
  //  Copyright (c) 2018 Nodemedia. All rights reserved.
  //
  
  
  const Fs = require('fs');
  const path = require('path');
  const Http = require('http');
  const Https = require('https');
  const WebSocket = require('ws');
  const Express = require('express');
  const bodyParser = require('body-parser');
  const basicAuth = require('basic-auth-connect');
  const NodeFlvSession = require('./node_flv_session');
  const HTTP_PORT = 80;
  const HTTPS_PORT = 443;
  const HTTP_MEDIAROOT = './media';
  const Logger = require('./node_core_logger');
  const context = require('./node_core_ctx');
  
  const streamsRoute = require('./api/routes/streams');
  const serverRoute = require('./api/routes/server');
  const relayRoute = require('./api/routes/relay');
  
  class NodeHttpServer {
    constructor(config) {
      this.port = config.http.port || HTTP_PORT;
      this.mediaroot = config.http.mediaroot || HTTP_MEDIAROOT;
      this.config = config;
  
      let app = Express();
      app.use(bodyParser.json());
  
      app.use(bodyParser.urlencoded({ extended: true }));
  
      app.set('view engine', 'ejs');
      app.set('views', path.join(__dirname, 'views'));
  
      app.all('*', (req, res, next) => {
        res.header("Access-Control-Allow-Origin", this.config.http.allow_origin);
        res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
        res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
        res.header("Access-Control-Allow-Credentials", true);
        req.method === "OPTIONS" ? res.sendStatus(200) : next();
      });
  
      app.get('/live/:roomId', (req, res) => {
        res.render('live.ejs', {roomId: req.params.roomId, liveHost: process.env.WS_LIVE_HOST});
      })
  
      app.get('*.flv', (req, res, next) => {
        req.nmsConnectionType = 'http';
        this.onConnect(req, res);
      });
  
      let adminEntry = path.join(__dirname + '/public/admin/index.html');
      if (Fs.existsSync(adminEntry)) {
        app.get('/admin/*', (req, res) => {
          res.sendFile(adminEntry);
        });
      }
  
      if (this.config.http.api !== false) {
        if (this.config.auth && this.config.auth.api) {
          app.use(['/api/*', '/static/*', '/admin/*'], basicAuth(this.config.auth.api_user, this.config.auth.api_pass));
        }
        app.use('/api/streams', streamsRoute(context));
        app.use('/api/server', serverRoute(context));
        app.use('/api/relay', relayRoute(context));
      }
  
      app.use(Express.static(path.join(__dirname + '/public')));
      app.use(Express.static(this.mediaroot));
      if (config.http.webroot) {
        app.use(Express.static(config.http.webroot));
      }
  
      this.httpServer = Http.createServer(app);
  
      /**
       * ~ openssl genrsa -out privatekey.pem 1024
       * ~ openssl req -new -key privatekey.pem -out certrequest.csr
       * ~ openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
       */
      if (this.config.https) {
        let options = {
          key: Fs.readFileSync(this.config.https.key),
          cert: Fs.readFileSync(this.config.https.cert)
        };
        this.sport = config.https.port ? config.https.port : HTTPS_PORT;
        this.httpsServer = Https.createServer(options, app);
      }
    }
  
    run() {
      this.httpServer.listen(this.port, () => {
        Logger.log(`Node Media Http Server started on port: ${this.port}`);
      });
  
      this.httpServer.on('error', (e) => {
        Logger.error(`Node Media Http Server ${e}`);
      });
  
      this.httpServer.on('close', () => {
        Logger.log('Node Media Http Server Close.');
      });
  
      this.wsServer = new WebSocket.Server({ server: this.httpServer });
  
      this.wsServer.on('connection', (ws, req) => {
        req.nmsConnectionType = 'ws';
        this.onConnect(req, ws);
      });
  
      this.wsServer.on('listening', () => {
        Logger.log(`Node Media WebSocket Server started on port: ${this.port}`);
      });
      this.wsServer.on('error', (e) => {
        Logger.error(`Node Media WebSocket Server ${e}`);
      });
  
      if (this.httpsServer) {
        this.httpsServer.listen(this.sport, () => {
          Logger.log(`Node Media Https Server started on port: ${this.sport}`);
        });
  
        this.httpsServer.on('error', (e) => {
          Logger.error(`Node Media Https Server ${e}`);
        });
  
        this.httpsServer.on('close', () => {
          Logger.log('Node Media Https Server Close.');
        });
  
        this.wssServer = new WebSocket.Server({ server: this.httpsServer });
  
        this.wssServer.on('connection', (ws, req) => {
          req.nmsConnectionType = 'ws';
          this.onConnect(req, ws);
        });
  
        this.wssServer.on('listening', () => {
          Logger.log(`Node Media WebSocketSecure Server started on port: ${this.sport}`);
        });
        this.wssServer.on('error', (e) => {
          Logger.error(`Node Media WebSocketSecure Server ${e}`);
        });
      }
  
      context.nodeEvent.on('postPlay', (id, args) => {
        context.stat.accepted++;
      });
  
      context.nodeEvent.on('postPublish', (id, args) => {
        context.stat.accepted++;
      });
  
      context.nodeEvent.on('doneConnect', (id, args) => {
        let session = context.sessions.get(id);
        let socket = session instanceof NodeFlvSession ? session.req.socket : session.socket;
        context.stat.inbytes += socket.bytesRead;
        context.stat.outbytes += socket.bytesWritten;
      });
    }
  
    stop() {
      this.httpServer.close();
      if (this.httpsServer) {
        this.httpsServer.close();
      }
      context.sessions.forEach((session, id) => {
        if (session instanceof NodeFlvSession) {
          session.req.destroy();
          context.sessions.delete(id);
        }
      });
    }
  
    onConnect(req, res) {
      let session = new NodeFlvSession(this.config, req, res);
      session.run();
  
    }
  }
  
  module.exports = NodeHttpServer;
  