---
project
  httptools
version
  0.0.0
unit
  struct
info
  # TODO

  * The methods `HttpServer::listens()`, `HttpServer::start()` and `HttpServer::stop()` don't work properly, especially the `stop()` function does not work.


  # Overview

  This script/module provides three kinds of HTTP servers:

  * A [__file server__](#HttpServer-fileServer), that displays the content of the directory or file specified by the URL path.

      For example, if the file server is running and listens to port `12345` and the browser is pointed to `http://localhost:12345/one/two/three/four/five`, then `one/two/three/four/five` is the path.
      If that path is a directory, then content directory content is displayed.
      And if it is a file, e.g. a `.html` or `.txt` file, the text itself is displayed.

  * A [__document producer server__](#HttpServer-docProducerServer) that is a wrapper around a so-called __document producer__ `dp`, a function that returns a HTML document `dp(rec)` depending on the given record `rec` which is provided by the query string of the URL or data of a POST request.

      The document producer `dp` can be defined arbitrarily.
      Below, we provide a `defaultDocProducer`, that returns a HTML page that displays the input record `rec`.

      For example, if the document producer server is started with this default document producer and listens to port `12345`, and if the browser is pointed to `localhost:55555/?name=Sindbad&password=SESAME&year=1001`, the query string is converted into a string record and this record is displayed as `{ name: 'Sindbad', password: 'SESAME', year = '1001' }`.

  * Or a mix of both, called [__file and document producer server__](#HttpServer-fileAndDocProducerServer), which acts depending on the provided URL:

      * If the combined query and data record `rec` is empty, it acts as the file server and displays the content from the URL path.
      * If the record is not empty though, the page produced by `dp(rec)` is displayed instead.

  -------------------------------------------------------------------------------------------------

  # The HTML/HTTP wrapper for a functional approach to a GUI with JavaScript

  .....

  -------------------------------------------------------------------------------------------------

  # Anatomy of a server request

  Our HTTP servers respond to two kind of [HTTP requests](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods):
  GET and POST. In our API we essentially abstract from this distinction and only care for two things:

  1. the requested [URL](http://en.wikipedia.org/wiki/Uniform_resource_locator) and
  2. any additional _data_ that is sent with a POST request.

  ## (i) The URL of a server request

  For every possible URL, for example,

      http://localhost:55555/aaa/bbb/ccc/ddd?one=123&two=234&three=345#hello-there

  we distinguish the following parts:

  * `http://localhost:55555`
    is the mandatory __base part__, as we call it.
    `55555` is the __port number__ and that is specified and fixed with the creation of the server.
  * `aaa/bbb/ccc/ddd`
    is the __path__
  * `one=123&two=234&three=345`
    is the __query string__.
    In its parsed form this turns into the __query record__ `{"one": "123", "two": "234", "three": "345"}`.
  * `hello-there`
    is the __fragment__ part of the URL.

  ## (ii) The additional data of a POST request

  Originally this data comes as a __data string__, but we further parse an process it as a __data record__.

  For example, if the browser shows a page containing this formula

      <form action="localhost:55555/aaa/bbb/ccc/ddd" method="POST">
        institution: <input type="text" name="institution" />
        established: <input type="text" name="established" />
        <input type="submit">
      </form>

  and the user inputs `institution: United Nations` and `established: 1945` and then submits, the data record of this post request is

      { "institution": "United Nations", "established": "1945" }

  ## Summary of a server request anatomy

  As said, we do not really distinguis between GET and POST requests and we combine the query and the data part into a single record, so that we eventually only dinstiguish these well-defined parts of every request:

  * The __base part__ `http://localhost:55555` which is constant once the server is created.
  * The __path__, say `aaa/bbb/ccc/ddd`. This path may be empty.
  * The combined __(query and data) record__, e.g.
    `{"one": "123", "two": "234", "three": "345", "institution": "United Nations", "established": "1945"}`.
    This record is always well-defined, although it may be empty `{}`.

  -------------------------------------------------------------------------------------------------

  # The three kinds of servers

  We provide three kinds of servers here:

  1. A __file server__, that returns or displays the file or directory specified by the path `aaa/bbb/ccc/ddd`.

  2. A __document producer server__, that takes the combined record `rec` and returns a HTML document `docProducer(rec)`, where `docProducer` is a function that returns a HTML document for a given record `rec`.

  3. A __file and document producer server__, which is essentially the combination of the previous two and responds as follows:

      * if the request comes with an empty query and data record `{}`, it acts as a file server and returns the file or directory given by the path `aaa/bbb/ccc/ddd`

      * otherwise, the record `rec` is not empty, and as a result `docProducer(rec)` is the resulting HTML document.

  The according functions are:

  1. `HttpServer.fileServer(port)` creates a _file server_.

  2. `HttpServer.docuProducerServer(port)(docProducer)` creates a file server, which acts as HTTP wrapper around the specified `docProducer` function.
      In particular, we can use the predefined `HttpServer.defaultDocProducer`, which returns for each given record `rec` a simple HTML page that displays this record.

  3. `HttpServer.fileAndDocProducerServer(port)(docProducer)` creates the _file and document producer server_.

  -------------------------------------------------------------------------------------------------

  ...

  -------------------------------------------------------------------------------------------------

  # Workflow

  1. Create a `server` with either one of the following statements:

          var server = HttpServer.fileServer(port)
          var server = HttpServer.docProducerServer(port)(docProducer)
          var server = HttpServer.fileAndDocProducerServer(port)(docProducer)

    where `port` is a `$HttpPortNumber`, say `55555`, and `docProducer` is a function that turns each record `rec` into a HTML document `docProducer(rec)`.

  2. Start the `server` with

          server.start()

    As a response you get a log message saying that the server starts listening to the specified `port`, say `55555`.

  3. Now you can use the browser and point it to

          http://localhost:55555

  4. .....

  5. .....

  -------------------------------------------------------------------------------------------------

  # Example REPL session

  ## Preparations

  Node.js and, after starting the REPL, do this

      > .load type.tofg.js
      > .load value.tofg.js
      > .load html.tofg.js
      > .load HttpTools.tofg.js

  ## Creating, starting and stopping the document producer server

  First, create the server with a port number and a document producer (e.g. the `defaultDocProducer`):

      > var server = HttpServer.docProducerServer (55555) (HttpServer.defaultDocProducer)
      undefined

  In this initial state, the `server` exists, but does not listen, yet

      > server.listens()
      false

  We now `start()` the `server()`

      > server.start()
      Server started listening on port `55555` ...
      null

  Once started, the `server` does listen, as we can see by asking

      > server.listens()
      true

  At any time, we can `stop()` the `server` again:

      > server.stop()
      Server stopped listening at port `55555`.
      null

  ## Requests and responses for the running server

  When the `server` is listening, we can point the browser (say Firefox) to

      http://localhost:55555/?one=123&two=234

  and the browser will display

      +-----------------------------------------------------------------------------------------+
      | http://localhost:55555/?one=123&two=234                                                 |
      +-----------------------------------------------------------------------------------------+
      | { one: '123', two: '234' }                                                              |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      |                                                                                         |
      +-----------------------------------------------------------------------------------------+

  At the terminal/REPL we obtain this log message

      `GET` request for `http://localhost:55555` at 4/1/2015, 2:43:38 AM:
      Path: `/`
      Query record: `{"one":"123","two":"234"}`
      Data record: `{}`

comment
  # Debugging required for:

  * `HttpServer.stop()` does not word as expected.
  * The original `unit` property also had a `system_target` flag; which is deleted.
---






---
// HTTP Server ////////////////////////////////////////////////////////////////////////////////////
---
unit
  construct
name
  HttpServer
type
  Type.CONSTRUCT
value
  require('http').Server
---
unit
  type
name
  HttpServer.HTTP_PORT_NUMBER
value
  Type.synonym (INT)
info
  A __HTTP port number__ is an integer between between `0` and `2^16-1`.

  port number range  kind of ports
  -----------------  ------------------------------------------------------------------------------------------------------------
  0-1023             Well-known or system ports, used by system processes that provide widely used types of network services.
  1024-49151         Registered ports, assigned by IANA for specific service upon application by a requesting entity.
  49152-65535        Dynamic, private or ephemeral ports that cannot be registered with IANA, for custom or temporary purposes.

  See the [List of TCP and UDP port numbers](http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers).

  In my examples, I use `55555` (five times five) and its successors `55556`, `55557`, `55558`, ... as defaults.
---
---
unit
  doc
name
  HttpServer
header
  Content Functions
---
---
unit
  value
name
  HttpServer.directoryContentPage
type
  Type.fun ([System.FILEPATH, System.FILEPATH, Data.HTML_DOC])
value
  function (rootDir) {
    return function (branchDir) {

      // the `localPath` is the combined `rootDir` and `branchDir`
      var localPath  = System.Path.joinList ([rootDir, branchDir]);

      // first, verify that `localPath` is really a directory
      if (! (System.Path.isDirectory (localPath))) {
        throw Error ( "`HttpServer.dictoryContentPage(" + rootDir + ")(" + branchDir + ")` error, `"
                    + localPath + "` is not a directory!" );
      }

      // helper functions for `<a>` tag generation
      function rootLink (href, text) {
        return '<a style="background-color:#555555;color:#CCCCCC;font-weight:bold" href="' + href + '">' + text + '</a>';
      }
      function dirLink (href, text) {
        return '<a style="background-color:#CCCCCC;color:#555555;font-weight:bold" href="' + href + '">' + text + '</a>';
      }
      function fileLink (href, text) {
        return '<a style="background-color:#EEEEEE;color:#444444;" href="' + href + '">' + text + '</a>';
      }
      function noLink (text) {
        return '<span style="background-color:#EEEEEE;color:#888888;">' + text + '</span>';
      }

      // now create the `fileList` with the `localPath` content
      var fileList = System.Dir.list (localPath);     // `fileList` is say `['aaa.txt', 'bbb.js', 'ccc', ...]`

      // create `branchSteps`; which for say `"one/two/three"`  is `['one', 'two', 'three']`
      var branchSteps = branchDir.split('/');
      branchSteps = branchSteps.filter (function (name) { return (name !== ''); });

      // create `multiPath`, the head line with links to each step of `branchDir`
      var href = '/';                                    // always a Path
      var multiPath = rootLink (href, System.Path.normalize(rootDir));
      for (var i = 0; i < branchSteps.length; i++) {
        href += branchSteps[i] + '/';
        multiPath += '/' + dirLink (href, branchSteps[i]);
      }

      // start `html` with `multiPath` wrapped in a `<h1>` tag
      var html = '<h1>' + multiPath + '</h1>\n';

      // add the directory content to `html`
      for (var i = 0; i < fileList.length; i++) {
        var p0 = System.Path.joinList ([localPath, fileList [i]]);  // local path on the machine (Windows or UNIX style) of the i-th entry
        var p1 = System.Path.joinList ([branchDir, fileList [i]]);  // branch path of the i-th entry
        var p2 = p1.replace (/\\/g, '/');                           // URL path, always UNIX/URL style with "/" (rather than "\\")
        if (System.Path.isFile (p0)) {
          html += fileLink (p2, fileList[i]) + '\n';
        } else if (System.Path.isDirectory (p0)) {
          html += dirLink (p2, fileList[i]) + '\n';
        } else { // i.e. `p0` is neither file nor directory
          html += noLink (fileList[i]) + '\n';
        }
      }

      // wrap `html` in a `htmlDoc` and return that
      var title = "`" + localPath + "` content";
      var css = '* { line-height: 200%; }\n'
              + 'a { text-decoration: none; }\n'
              + 'a, span { padding: 3px; margin: 2px }'
              ;
      var htmlDoc = Html.htmlDoc ({title: title, css: css}) (html);
      return htmlDoc;

    }
  }
info
  `HttpServer.directoryContentPage(rootDir)(branchDir)` returns a HTML document that conveniently displays the content of `localPath` on the current system, where `localPath` is the join of `rootDir` and `branchDir`.

  The reason for the separation of `localPath` in `rootDir` and `branchDir` is the fact that in actual use of the HTML document result, `rootDir` is usually replaced by `http://localhost:port`.

  For example,

      HttpServer.directoryContentPage ('/home/myself/myproject') ('node_modules/markdown')

  returns this content

  <div style="border:ridge 8pt black; padding: 20px; margin: 20px; line-height: 200%">
    <h1>
      <span style="padding:3px;margin:2px;background-color:#555555;color:#CCCCCC;font-weight:bold">/home/myself/myproject</span>/
      <span style="padding:3px;margin:2px;background-color:#CCCCCC;color:#555555;font-weight:bold">node_modules</span>/
      <span style="padding:3px;margin:2px;background-color:#CCCCCC;color:#555555;font-weight:bold">markdown</span>
    </h1>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">.npmignore</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">.travis.yml</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">Changes.markdown</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">README.markdown</span>
    <span style="padding:3px;margin:2px;background-color:#CCCCCC;color:#555555;font-weight:bold">bin</span>
    <span style="padding:3px;margin:2px;background-color:#CCCCCC;color:#555555;font-weight:bold">lib</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">markdown-js.sublime-project</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">markdown-js.sublime-workspace</span>
    <span style="padding:3px;margin:2px;background-color:#CCCCCC;color:#555555;font-weight:bold">node_modules</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">package.json</span>
    <span style="padding:3px;margin:2px;background-color:#DDDDDD;color:#444444;">seed.yml</span>
  </div>

  <div style="color:red">..................CONTINUEHERE...............................................</div>

comment

  Call

      HttpServer.directoryContentPage ('/home/myself/myproject') ('aaa/bbb/ccc/ddd')

  so that

      rootDir    === '/home/myself/myproject/'
      branchDir  === 'aaa/bbb/ccc/ddd'

  then

      localPath   === '/home/myself/myproject/aaa/bbb/ccc/ddd'
      branchSteps === ['aaa', 'bbb', 'ccc', 'ddd']

  Furthermore, suppose that

      fileList    === ['xxx.txt', 'yyy.markdown', 'zzz']    // the content of the `localPath` directory

  <div style="color:red">..................CONTINUEHERE...............................................</div>

---
unit
  value
name
  HttpServer.ContentType
type
  Type.record (STRING)
value
  {
    '.html'     : 'text/html',
    '.htm'      : 'text/html',
    '.text'     : 'text/plain',
    '.txt'      : 'text/plain',
    '.text'     : 'text/plain',
    '.markdown' : 'text/plain',
    '.md'       : 'text/plain',
    '.pod'      : 'text/plain',
    '.tex'      : 'text/plain',
    '.css'      : 'text/css',
    '.xml'      : 'text/xml',
    '.xhtml'    : 'text/xhtml',
    '.flatcode' : 'text/plain',   // only in `tof.js`
    '.tex'      : 'text/plain',
    '.json'     : 'application/json',
    '.js'       : 'application/javascript; charset=UTF-8'
  }
info
  `HttpServer.ContentType` is a record that maps file extensions, like `'.html'`, `'.txt'` or `'.js'`, to their [MIME type](http://en.wikipedia.org/wiki/Internet_media_type) string, e.g. `"text/html"`, `"text/plain"` or `"application/javascript; charset=UTF-8"`.

---
unit
  doc
name
  HttpServer
header
  Doc Producers
---
unit
  value
name
  HttpServer.defaultDocProducer
type
  Type.lambda ([RECORD, Data.Html.HTML_DOC])
value
  function (rec) {
    var recStr = require('util').inspect (rec, {showHidden: true, depth: null});
    var recStr = Html.entityify (recStr);
    var html
      = '<!DOCTYPE html>\n'
      + '<html>\n'
      + '<head>\n'
      + '  <meta charset="utf-8">\n'
      + '</head>\n'
      + '<body>\n'
      + '<pre>' + recStr + '</pre>\n'
      + '</body>\n'
      + '</html>\n';
    return html;
  }
info
  A __document producer__ is here defined as a lambda function that takes a record argument and returns a HTML document.
  In other words, it is a _dynamic_ or _parameterized_ document generator.

  This `defaultDocProducer` takes a record `rec` and just displays this record in the returned HTML document.
  For example,

      HttpServer.defaultDocProducer ({name: "Sindbad", password: "SESAME"})

  returns this string, which is a well-defined HTML document:

      ╭───────────────────────────────────────────────────╮
      │<!DOCTYPE html>↵                                   │
      │<html>↵                                            │
      │<head>↵                                            │
      │  <meta charset="utf-8">↵                          │
      │</head>↵                                           │
      │<body>↵                                            │
      │<pre>{ name: 'Sindbad', password: 'SESAME' }</pre>↵│
      │</body>↵                                           │
      │</html>↵                                           │
      │                                                   │
      ╰───────────────────────────────────────────────────╯

---
unit
  doc
name
  HttpServer
header
  HTTP Servers
---
unit
  value
name
  HttpServer.fileServer
type
  $prod ([HTTP_PORT_NUMBER, Type.object (HttpServer)])
value
  function (port) {

    // reconstruct the involved directories and addresses
    var serverRoot = process.cwd();
    var host = 'localhost';
    var serverUrl = 'http://' + host + ":" + port;

    // create the server
    var server = new require('http').Server();

    server.addListener ('request', function (req, res) {

        // reconstruct some request components
        var urlString   = req.url;                                    // the requested URL
        var urlRec      = require ('url').parse(urlString);           // the requested URL parsed as a record
        var method      = req.method;                                 // usually 'GET'
        var path        = urlRec.pathname;                            // e.g. `'/one/two/three.txt'`
        var localPath   = System.Path.joinList ([serverRoot, path]);  // translate the `path` into the `localPath` on the server

        // print the log message
        var date = new Date();
        console.log ( "`%s` request `%s` at %s:", method, urlString, date.toLocaleString() );

        // // // BEGIN // // // (this block is the same in `fileServer` and `docProducerServer`)
        try {
          if (System.Path.exists (localPath)) {
            if (System.Path.isFile (localPath)) {
              var extension = System.Path.extname (path);  // e.g. `'.html'` or `'.js'` or `'.txt'` or `''`
              console.log ("  Responding with the `%s`-file `%s`.", extension, localPath);
              if (extension in ContentType) {
                res.writeHead (200, {"Content-Type": ContentType[extension]});
              } else {
                res.writeHead (200, {"Content-Type": "application/octet-stream"});
              }
              res.write (System.File.read (localPath));
              res.end ();
            } else if (System.Path.isDirectory (localPath)) {
              console.log ("  Responding with the listed directory at root `%s` and branch `%s`.", serverRoot, path);
              res.writeHead (200, {"Content-Type": "text/html; charset=UTF-8"});
              res.write (directoryContentPage (serverRoot) (path));
              res.end ();
            } else {
              console.log ("  Responding with a 404 page, `%s` is neither a file nor directory.", localPath);
              res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
              res.write ( '<h1 style="color:red">This path exists, but is neither a file nor a directory</h1>\n'
                        + '<pre>' + localPath + '</pre>\n' );
              res.end ();
            }
          } else {
            console.log ("  Responding with a 404 page, `%s` does not exits on this system.", localPath);
            res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
            res.write ( '<h1 style="color:red">The path does not exist</h1>\n'
                      + '<pre>' + localPath + '</pre>\n' );
            res.end ();
          }
        } catch (e) {
          console.log ("  Responding with a 404 page due to error: "  + e);
          res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
          res.write ( '<h1 style="color:red">Error</h1>\n'
                    + '<pre>' + e + '</pre>\n' );
          res.end ();
        }
        // // // END // // //

    });

    // add the `port` and `listening` properties to the `server`
    server.port = port;         // a `$HttpPortnumber` property
    server.listening = false;   // a `$Boolean` property, toggled on `start()` and `stop()` calls

    // return the `server`
    return server;

  }
info
  `HttpServer.fileServer(port)` creates a file server that listens to the specified port, i.e. to HTTP requests starting with
  `http://localhost:port/`.
---
unit
  value
name
  HttpServer.docProducerServer
type
  Type.fun ([HTTP_PORT_NUMBER, Type.lambda ([RECORD, Data.Html.HTML_DOC]), Type.object (HttpServer)])
value
  function (port) {
    return function (docProducer) {

      // reconstruct the involved addresses
      var host = 'localhost';
      var serverUrl = 'http://' + host + ":" + port;

      // create the server
      var server = new require('http').Server();

      // the `request` event listener
      server.addListener ('request', function (req, res) {

        var urlRec = require ('url').parse (req.url);                 // the requested URL, parsed as a record
        var queryRec = require ('querystring').parse (urlRec.query);  // the parsed query record of the URL
        var path = urlRec.pathname;                                   // e.g. '/one/two/three.txt'
        var method = req.method;                                      // usually, either 'GET' or 'POST'
        var dataString = '';                                          // the POST data as a string
        var dataRec;                                                  // the POST data as a record

        // action for the the next incoming POST `data`
        req.on ('data', function (data) {

          // add the next `data` chunk to `dataString`
          dataString += data;

        });

        // action if the POST data stream has terminated
        req.on ('end', function () {

          // convert the
          dataRec = require('querystring').parse (dataString);

          // print the log message
          var date = new Date();
          console.log (
            "`%s` request for `%s` at %s:\n  Path: `%s`\n  Query record: `%j`\n  Data record: `%s`",
            method, serverUrl, date.toLocaleString(), path, queryRec, Any.ellipsis (dataRec)
          );

          // combine `queryRec` and `dataRec` into a single record `rec`
          var rec = {};
          for (var k in queryRec) { rec[k] = queryRec[k]; }
          for (var k in  dataRec) { rec[k] = dataRec[k];  }

          // generate the HTML document and return the result
          var htmlDoc = docProducer (rec);
          res.writeHead (200, {"Content-Type": "text/html"});
          res.write (htmlDoc);
          res.end();

        });

      });

      // add the `port` and `listening` properties to the `server`
      server.port = port;         // a `$HttpPortnumber` property
      server.listening = false;   // a `$Boolean` property, toggled on `start()` and `stop()` calls

      // return the `server`
      return server;

    }
  }
info
  Let `docProducer` be a _document producer_, e.g. the [`defaultDocProducer`](#HttpServer-defaultDocProducer), i.e. a lambda function that takes a record and returns a HTML document.
  Furthermore, let `port` be a [HTTP port number](#HttpServer-DOLLARHttpPortNumber), e.g. `55555`.

      var server = HttpServer.docuProducerServer(port)(docProducer)

  generates a HTTP server.
  Once it is started by calling `server.start()`, it responds to each HTTP request by combining the query record and the data record into a single record `rec` and returning the HTML page produced by `docProducer(rec)`.
---
unit
  value
name
  HttpServer.fileAndDocProducerServer
type
  Type.fun ([HTTP_PORT_NUMBER, Type.lambda ([RECORD, Data.Html.HTML_DOC]), Type.object (HttpServer)])
value
  function (port) {
    return function (docProducer) {

      // reconstruct the involved directories and addresses
      var serverRoot = process.cwd ();                  // current working directory/path of the server
      var host = 'localhost';                           // host will always be `localhost`
      var serverUrl = 'http://' + host + ":" + port;    // e.g. `http://localhost:55555`

      // create the `server`
      var server = new require('http').Server();

      // implement the `request` event listener
      server.addListener ('request', function (req, res) {

        var urlRec = require ('url').parse (req.url);                // the requested URL, parsed as a record
        var queryRec = require ('querystring').parse (urlRec.query); // the parsed query record of the URL
        var path = urlRec.pathname;                                  // e.g. '/one/two/three.txt'
        var localPath = System.Path.joinList ([serverRoot, path]);   // translate the `path` into the `localPath` on the server
        var method = req.method;                                     // usually, either 'GET' or 'POST'
        var dataString = '';                                         // the POST data as a string
        var dataRec;                                                 // the POST data as a record

        // action for the the next incoming POST `data`
        req.on ('data', function (data) {

          // add the next `data` chunk to `dataString`
          dataString += data;

        });

        // action if the POST data stream has terminated
        req.on ('end', function () {

          // convert the
          dataRec = require('querystring').parse (dataString);

          // print the log message
          var date = new Date();
          console.log (
            "`%s` request for `%s` at %s:\n  Path: `%s`\n  Query record: `%j`\n  Data record: `%s`",
            method, serverUrl, date.toLocaleString(), path, queryRec, Any.ellipsis (dataRec)
          );

          // combine `queryRec` and `dataRec` into a single record `rec`
          var rec = {};
          for (var k in queryRec) { rec[k] = queryRec[k]; }
          for (var k in  dataRec) { rec[k] = dataRec[k];  }

          // check if `rec` is empty; if so, return the file requested in `path`, otherwise return `docProducer(rec)`
          if (Object.keys(rec).length === 0) {

            // // // BEGIN // // // (this block is the same in `fileServer` and `docProducerServer`)
            try {
              if (System.Path.exists (localPath)) {
                if (System.Path.isFile (localPath)) {
                  var extension = System.Path.extname (path);  // e.g. `'.html'` or `'.js'` or `'.txt'` or `''`
                  console.log ("  Responding with the `%s`-file `%s`.", extension, localPath);
                  if (extension in ContentType) {
                    res.writeHead (200, {"Content-Type": ContentType[extension]});
                  } else {
                    res.writeHead (200, {"Content-Type": "application/octet-stream"});
                  }
                  res.write (System.File.read (localPath));
                  res.end ();
                } else if (System.Path.isDirectory (localPath)) {
                  console.log ("  Responding with the listed directory at root `%s` and branch `%s`.", serverRoot, path);
                  res.writeHead (200, {"Content-Type": "text/html; charset=UTF-8"});
                  res.write (directoryContentPage (serverRoot) (path));
                  res.end ();
                } else {
                  console.log ("  Responding with a 404 page, `%s` is neither a file nor directory.", localPath);
                  res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
                  res.write ( '<h1 style="color:red">This path exists, but is neither a file nor a directory</h1>\n'
                            + '<pre>' + localPath + '</pre>\n' );
                  res.end ();
                }
              } else {
                console.log ("  Responding with a 404 page, `%s` does not exits on this system.", localPath);
                res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
                res.write ( '<h1 style="color:red">The path does not exist</h1>\n'
                          + '<pre>' + localPath + '</pre>\n' );
                res.end ();
              }
            } catch (e) {
              console.log ("  Responding with a 404 page due to error: "  + e);
              res.writeHead (404, {"Content-Type": "text/html; charset=UTF-8"});
              res.write ( '<h1 style="color:red">Error</h1>\n'
                        + '<pre>' + e + '</pre>\n' );
              res.end ();
            }
            // // // END // // //

          } else { // `rec` is not empty, so return `docProducer(rec)`

            var htmlDoc = docProducer (rec);
            res.writeHead (200, {"Content-Type": "text/html"});
            res.write (htmlDoc);
            res.end();

          }

        });

      }); // end of `request` event listener

      // add the `port` and `listening` properties to the `server`
      server.port = port;         // a `$HttpPortnumber` property
      server.listening = false;   // a `$Boolean` property, toggled on `start()` and `stop()` calls

      // return the `server`
      return server;

    }
  }
info
  ......
---
---
unit
  doc
name
  HttpServer
header
  Server Prototype Properties
---
---
unit
  proto
name
  HttpServer.listens
type
  Type.fun ([NULL, BOOLEAN])
value
  function () { return this.listening; }
info
  If `server` is a HTTP server, i.e. a `$inst(HttpServer)` value, then `server.listens()` either returns `true` or `false`, depending on whether the `server` is actively listening.
---
unit
  proto
name
  HttpServer.start
value
  function () {
    try {
      var port = this.port;
      if (this.listening) {
        console.log ("The server is already listening on port `" + port + "`.");
      } else {
        this.listen (port);        // the server now starts listening
        this.listening = true;     // the `listening` property is set to `true`
        console.log ("Server started listening on port `" + port + "` ..." );
      }
      return null;
    } catch (e) {
      throw Error ("`HttpServer::start(port)` error: " + e);
    }
  }
info
  Let `server` be a HTTP server, then `server.start()` makes it listening.
  An according log message like `Server started listening on port 55555 ...` also confirms that.
  If the server was already listening, another log message explains that.
  The return value of `server.start()` is a `null` in any case.
---
unit
  proto
name
  HttpServer.stop
value
  function () {
    try {
      var port = this.port;
      if (this.listening) {
        this.listening = false;
        this.close ();
        console.log ("Server stopped listening at port `" + port + "`.");
      } else {
        console.log ("Server was not listening, anyway.");
      }
      return null;
    } catch (e) {
      throw Error ("`HttpServer.stop()` error: " + e);
    }
  }
info
  Let `server` be a HTTP server, then `server.stop()` stops a running server listening and sends a log message like `Server stopped listening at port 55555.`.
  If the server was not listening, anyway, another log message is explains that, again.
  The return value of `server.stop()` is `null`, in any case.
comment
  .....
---
---
// End HTTP Server ///
---
