Yrkki/cv-generator-fe

View on GitHub
listener.js

Summary

Maintainability
A
0 mins
Test Coverage
#!/usr/bin/env node

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2018 Georgi Marinov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/*eslint no-fallthrough: ["error", { "commentPattern": "break[\\s\\w]*omitted" }]*/

'use strict';

/**
 * Server configuration, instantiation, and listener invokation.
 */

/**
 * Module dependencies.
 */

var http = require('http');
var fs = require('fs');
var path = require('path');

/**
 * Get port from environment and store in Express.
 */

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Bind pipe or port.
 */

 function bindPipeOrPort(item, pipe, port) {
  return typeof item === 'string'
    ? 'pipe ' + pipe
    : 'port ' + port;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error, port) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = bindPipeOrPort(port, port, port);

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      // eslint-disable-next-line no-console
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
    // break; // break omitted

    case 'EADDRINUSE':
      // eslint-disable-next-line no-console
      console.error(bind + ' is already in use');
      process.exit(1);
    // break; // break omitted

    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening(server) {
  var addr = server.address();
  var bind = bindPipeOrPort(addr, addr, addr.port);
  // eslint-disable-next-line no-console
  console.debug('Listening on ' + bind + '.');
  // eslint-disable-next-line no-console
  console.debug();
}

/**
 * Show connection message.
 */

function showConnectionMessage(error, app) {
  if (error) {
    // eslint-disable-next-line no-console
    console.error(error);
    return process.exit(1);
  } else {
    // eslint-disable-next-line no-console
    console.log(`Welcome to the ${app.get('appName')}.`);
    // eslint-disable-next-line no-console
    console.log();
  }
}

/**
 * Get the name of the node from the basename of the current working directory.
 */

function nodeName() {
  let dirName;
  for (const iterator of [
    path.join(__dirname),
    path.join(__dirname, '..')
  ]) {
    dirName = path.parse(path.normalize(iterator)).base;
    if (!['bin', 'dist'].includes(dirName)) {
      break;
    }
  }
  return dirName;
}

/**
 * Get the application name.
 */

function appPackageName(app) {
  return app.get('appPackageName') || nodeName();
}

/**
 * Check synchronously if file exist in file system.
 * ~security: codacy: path traversal: ESLint_security_detect-non-literal-fs-filename
 */

function existsSync(dirnameCertPath, targetPath) {
  // ~security: codacy: unsafe: ESLint_security_detect-non-literal-fs-filename
  return fs.existsSync(path.join(dirnameCertPath, targetPath));
}

/**
 * Get spdy options.
 */

function getSpdyOptions(certName, certPath) {
  // Prepare http/2 options
  let dirnameCertPath;
  for (const iterator of [
    path.join(__dirname, 'bin'),
    path.join(__dirname),
    path.join(__dirname, '..')
  ]) {
    dirnameCertPath = path.join(iterator, certPath);
    if (existsSync(dirnameCertPath, `${certName}.key`)) {
      break;
    }
  }
  const spdyOptions = {
    key: existsSync(dirnameCertPath, `${certName}.key`),
    cert: existsSync(dirnameCertPath, `${certName}.crt`)
  };

  return spdyOptions;
}

/**
 * Create HTTP server.
 */

function createServer(app, config, certName, certPath = 'cert') {
  var server;

  if (config.useSpdy) {
    // Install http/2
    const spdy = require('spdy');
    // Get certificate name.
    if (!certName) { certName = appPackageName(app); }
    // Prepare spdy options
    var spdyOptions = getSpdyOptions(certName, certPath);
    // Serve http/2
    server = spdy.createServer(spdyOptions, app);
    // eslint-disable-next-line no-console
    console.log('Using HTTP/2.');
  } else if (config.useHttp) {
    // Create HTTP server
    server = http.createServer(app);
    // eslint-disable-next-line no-console
    console.log('Using HTTP.');
  } else {
    server = app;
    // eslint-disable-next-line no-console
    console.log('Using Express.');
  }

  return server;
}

/**
 * Connect to the server or create it.
 */

function connectServer(app, options) {
  let { welcome, server, config, certPath, certName } = options;

  // Config HTTP server.
  if (!config) {
    // config = { useSpdy: false, useHttp: true }; // use http
    config = { useSpdy: true, useHttp: false }; // use spdy
  }

  // Create HTTP server.
  if (!server) {
    server = createServer(app, config, certName, certPath);
  }

  // Show connection message.
  if (!welcome) {
    welcome = (error) => showConnectionMessage(error, app);
  }

  return { welcome, server, config };
}

/**
 * Start the app by listening on the default port provided, on all network interfaces.
 */

function listen(app, port, options) {
  // connect to server
  const { welcome, server, config } = connectServer(app, options);

  if (config.useSpdy || config.useHttp) {
    // Serve http/2 or
    // Listen on provided port, on all network interfaces
    server.listen(port, welcome);
    server.on('error', (error) => onError(error, port));
    server.on('listening', () => onListening(server));
  } else {
    // Start the app by listening on the default port
    app.listen(port, welcome);
    app.on('error', (error) => onError(error, port));
    app.on('listening', () => onListening(app));
  }
}

module.exports = { normalizePort: normalizePort, listen: listen };