[null,{"project":"httptools","version":"0.0.0","unit":"struct","info":"# TODO\n\n* The methods `HttpServer::listens()`, `HttpServer::start()` and `HttpServer::stop()` don't work properly, especially the `stop()` function does not work.\n\n\n# Overview\n\nThis script/module provides three kinds of HTTP servers:\n\n* A [__file server__](#HttpServer-fileServer), that displays the content of the directory or file specified by the URL path.\n\n 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.\n If that path is a directory, then content directory content is displayed.\n And if it is a file, e.g. a `.html` or `.txt` file, the text itself is displayed.\n\n* 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.\n\n The document producer `dp` can be defined arbitrarily.\n Below, we provide a `defaultDocProducer`, that returns a HTML page that displays the input record `rec`.\n\n 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' }`.\n\n* Or a mix of both, called [__file and document producer server__](#HttpServer-fileAndDocProducerServer), which acts depending on the provided URL:\n\n * If the combined query and data record `rec` is empty, it acts as the file server and displays the content from the URL path.\n * If the record is not empty though, the page produced by `dp(rec)` is displayed instead.\n\n-------------------------------------------------------------------------------------------------\n\n# The HTML/HTTP wrapper for a functional approach to a GUI with JavaScript\n\n.....\n\n-------------------------------------------------------------------------------------------------\n\n# Anatomy of a server request\n\nOur HTTP servers respond to two kind of [HTTP requests](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods):\nGET and POST. In our API we essentially abstract from this distinction and only care for two things:\n\n1. the requested [URL](http://en.wikipedia.org/wiki/Uniform_resource_locator) and\n2. any additional _data_ that is sent with a POST request.\n\n## (i) The URL of a server request\n\nFor every possible URL, for example,\n\n http://localhost:55555/aaa/bbb/ccc/ddd?one=123&two=234&three=345#hello-there\n\nwe distinguish the following parts:\n\n* `http://localhost:55555`\n is the mandatory __base part__, as we call it.\n `55555` is the __port number__ and that is specified and fixed with the creation of the server.\n* `aaa/bbb/ccc/ddd`\n is the __path__\n* `one=123&two=234&three=345`\n is the __query string__.\n In its parsed form this turns into the __query record__ `{\"one\": \"123\", \"two\": \"234\", \"three\": \"345\"}`.\n* `hello-there`\n is the __fragment__ part of the URL.\n\n## (ii) The additional data of a POST request\n\nOriginally this data comes as a __data string__, but we further parse an process it as a __data record__.\n\nFor example, if the browser shows a page containing this formula\n\n
\n\nand the user inputs `institution: United Nations` and `established: 1945` and then submits, the data record of this post request is\n\n { \"institution\": \"United Nations\", \"established\": \"1945\" }\n\n## Summary of a server request anatomy\n\nAs 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:\n\n* The __base part__ `http://localhost:55555` which is constant once the server is created.\n* The __path__, say `aaa/bbb/ccc/ddd`. This path may be empty.\n* The combined __(query and data) record__, e.g.\n `{\"one\": \"123\", \"two\": \"234\", \"three\": \"345\", \"institution\": \"United Nations\", \"established\": \"1945\"}`.\n This record is always well-defined, although it may be empty `{}`.\n\n-------------------------------------------------------------------------------------------------\n\n# The three kinds of servers\n\nWe provide three kinds of servers here:\n\n1. A __file server__, that returns or displays the file or directory specified by the path `aaa/bbb/ccc/ddd`.\n\n2. 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`.\n\n3. A __file and document producer server__, which is essentially the combination of the previous two and responds as follows:\n\n * 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`\n\n * otherwise, the record `rec` is not empty, and as a result `docProducer(rec)` is the resulting HTML document.\n\nThe according functions are:\n\n1. `HttpServer.fileServer(port)` creates a _file server_.\n\n2. `HttpServer.docuProducerServer(port)(docProducer)` creates a file server, which acts as HTTP wrapper around the specified `docProducer` function.\n In particular, we can use the predefined `HttpServer.defaultDocProducer`, which returns for each given record `rec` a simple HTML page that displays this record.\n\n3. `HttpServer.fileAndDocProducerServer(port)(docProducer)` creates the _file and document producer server_.\n\n-------------------------------------------------------------------------------------------------\n\n...\n\n-------------------------------------------------------------------------------------------------\n\n# Workflow\n\n1. Create a `server` with either one of the following statements:\n\n var server = HttpServer.fileServer(port)\n var server = HttpServer.docProducerServer(port)(docProducer)\n var server = HttpServer.fileAndDocProducerServer(port)(docProducer)\n\n where `port` is a `$HttpPortNumber`, say `55555`, and `docProducer` is a function that turns each record `rec` into a HTML document `docProducer(rec)`.\n\n2. Start the `server` with\n\n server.start()\n\n As a response you get a log message saying that the server starts listening to the specified `port`, say `55555`.\n\n3. Now you can use the browser and point it to\n\n http://localhost:55555\n\n4. .....\n\n5. .....\n\n-------------------------------------------------------------------------------------------------\n\n# Example REPL session\n\n## Preparations\n\nNode.js and, after starting the REPL, do this\n\n > .load type.tofg.js\n > .load value.tofg.js\n > .load html.tofg.js\n > .load HttpTools.tofg.js\n\n## Creating, starting and stopping the document producer server\n\nFirst, create the server with a port number and a document producer (e.g. the `defaultDocProducer`):\n\n > var server = HttpServer.docProducerServer (55555) (HttpServer.defaultDocProducer)\n undefined\n\nIn this initial state, the `server` exists, but does not listen, yet\n\n > server.listens()\n false\n\nWe now `start()` the `server()`\n\n > server.start()\n Server started listening on port `55555` ...\n null\n\nOnce started, the `server` does listen, as we can see by asking\n\n > server.listens()\n true\n\nAt any time, we can `stop()` the `server` again:\n\n > server.stop()\n Server stopped listening at port `55555`.\n null\n\n## Requests and responses for the running server\n\nWhen the `server` is listening, we can point the browser (say Firefox) to\n\n http://localhost:55555/?one=123&two=234\n\nand the browser will display\n\n +-----------------------------------------------------------------------------------------+\n | http://localhost:55555/?one=123&two=234 |\n +-----------------------------------------------------------------------------------------+\n | { one: '123', two: '234' } |\n | |\n | |\n | |\n | |\n | |\n | |\n | |\n | |\n | |\n | |\n | |\n +-----------------------------------------------------------------------------------------+\n\nAt the terminal/REPL we obtain this log message\n\n `GET` request for `http://localhost:55555` at 4/1/2015, 2:43:38 AM:\n Path: `/`\n Query record: `{\"one\":\"123\",\"two\":\"234\"}`\n Data record: `{}`\n","comment":"# Debugging required for:\n\n* `HttpServer.stop()` does not word as expected.\n* The original `unit` property also had a `system_target` flag; which is deleted.","$unit_flags$":{"kind_of_unit":"value","kind_of_value_unit":"static_value","type_of_value_unit":"struct","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","target_platform":"universal_target","target_dialect":"es5_target_dialect","overall_source_dialect":"all_es6","overall_value_scope":"custom_value_scope","overall_augmentation":"custom_augmentation","overall_value_existence":"all_unit_implemented","static_type_check":"type_check","session_verification":"verify_sessions","document_executability":"no_executable_document","target_minification":"no_minification"},"$name$":"","$value$":"Object.create (null)","$info$":"HttpServer::listens()
, HttpServer::start()
and HttpServer::stop()
don't work properly, especially the stop()
function does not work.This script/module provides three kinds of HTTP servers:
\nA file server, that displays the content of the directory or file specified by the URL path.
\nFor 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 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, which acts depending on the provided URL:
\nrec
is empty, it acts as the file server and displays the content from the URL path.dp(rec)
is displayed instead......
\nOur HTTP servers respond to two kind of HTTP requests: GET and POST. In our API we essentially abstract from this distinction and only care for two things:
\nFor every possible URL, for example,
\nhttp://localhost:55555/aaa/bbb/ccc/ddd?one=123&two=234&three=345#hello-there
\nwe distinguish the following parts:
\nhttp://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 pathone=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.Originally this data comes as a data string, but we further parse an process it as a data record.
\nFor example, if the browser shows a page containing this formula
\n<form action="localhost:55555/aaa/bbb/ccc/ddd" method="POST">\n institution: <input type="text" name="institution" />\n established: <input type="text" name="established" />\n <input type="submit">\n</form>
\nand 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" }
\nAs 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:
\nhttp://localhost:55555
which is constant once the server is created.aaa/bbb/ccc/ddd
. This path may be empty.{"one": "123", "two": "234", "three": "345", "institution": "United Nations", "established": "1945"}
. This record is always well-defined, although it may be empty {}
.We provide three kinds of servers here:
\nA file server, that returns or displays the file or directory specified by the path aaa/bbb/ccc/ddd
.
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
.
A file and document producer server, which is essentially the combination of the previous two and responds as follows:
\nif 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:
\nHttpServer.fileServer(port)
creates a file server.
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.
HttpServer.fileAndDocProducerServer(port)(docProducer)
creates the file and document producer server.
...
\nCreate a server
with either one of the following statements:
var server = HttpServer.fileServer(port)\nvar server = HttpServer.docProducerServer(port)(docProducer)\nvar 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)
.
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
.
Now you can use the browser and point it to
\nhttp://localhost:55555
.....
.....
Node.js and, after starting the REPL, do this
\n> .load type.tofg.js\n> .load value.tofg.js\n> .load html.tofg.js\n> .load HttpTools.tofg.js
\nFirst, create the server with a port number and a document producer (e.g. the defaultDocProducer
):
> var server = HttpServer.docProducerServer (55555) (HttpServer.defaultDocProducer)\nundefined
\nIn this initial state, the server
exists, but does not listen, yet
> server.listens()\nfalse
\nWe now start()
the server()
> server.start()\nServer started listening on port `55555` ...\nnull
\nOnce started, the server
does listen, as we can see by asking
> server.listens()\ntrue
\nAt any time, we can stop()
the server
again:
> server.stop()\nServer stopped listening at port `55555`.\nnull
\nWhen the server
is listening, we can point the browser (say Firefox) to
http://localhost:55555/?one=123&two=234
\nand the browser will display
\n+-----------------------------------------------------------------------------------------+\n| http://localhost:55555/?one=123&two=234 |\n+-----------------------------------------------------------------------------------------+\n| { one: '123', two: '234' } |\n| |\n| |\n| |\n| |\n| |\n| |\n| |\n| |\n| |\n| |\n| |\n+-----------------------------------------------------------------------------------------+
\nAt the terminal/REPL we obtain this log message
\n`GET` request for `http://localhost:55555` at 4/1/2015, 2:43:38 AM:\nPath: `/`\nQuery record: `{"one":"123","two":"234"}`\nData record: `{}`
\n","$comment$":"HttpServer.stop()
does not word as expected.unit
property also had a system_target
flag; which is deleted.A HTTP port number is an integer between between 0
and 2^16-1
.
port number range | \nkind of ports | \n
---|---|
0-1023 | \nWell-known or system ports, used by system processes that provide widely used types of network services. | \n
1024-49151 | \nRegistered ports, assigned by IANA for specific service upon application by a requesting entity. | \n
49152-65535 | \nDynamic, private or ephemeral ports that cannot be registered with IANA, for custom or temporary purposes. | \n
See the List of TCP and UDP port numbers.
\nIn my examples, I use 55555
(five times five) and its successors 55556
, 55557
, 55558
, ... as defaults.
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,
\nHttpServer.directoryContentPage ('/home/myself/myproject') ('node_modules/markdown')
\nreturns this content
\nCall
\nHttpServer.directoryContentPage ('/home/myself/myproject') ('aaa/bbb/ccc/ddd')
\nso that
\nrootDir === '/home/myself/myproject/'\nbranchDir === 'aaa/bbb/ccc/ddd'
\nthen
\nlocalPath === '/home/myself/myproject/aaa/bbb/ccc/ddd'\nbranchSteps === ['aaa', 'bbb', 'ccc', 'ddd']
\nFurthermore, suppose that
\nfileList === ['xxx.txt', 'yyy.markdown', 'zzz'] // the content of the `localPath` directory
\nHttpServer.ContentType
is a record that maps file extensions, like '.html'
, '.txt'
or '.js'
, to their MIME type string, e.g. "text/html"
, "text/plain"
or "application/javascript; charset=UTF-8"
.
' + recStr + '\\n'\n + '\\n'\n + '\\n';\n return html;\n}","info":"A __document producer__ is here defined as a lambda function that takes a record argument and returns a HTML document.\nIn other words, it is a _dynamic_ or _parameterized_ document generator.\n\nThis `defaultDocProducer` takes a record `rec` and just displays this record in the returned HTML document.\nFor example,\n\n HttpServer.defaultDocProducer ({name: \"Sindbad\", password: \"SESAME\"})\n\nreturns this string, which is a well-defined HTML document:\n\n ╭───────────────────────────────────────────────────╮\n │↵ │\n │↵ │\n │↵ │\n │ ↵ │\n │↵ │\n │↵ │\n │
{ name: 'Sindbad', password: 'SESAME' }↵│\n │↵ │\n │↵ │\n │ │\n ╰───────────────────────────────────────────────────╯\n","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"HttpServer.defaultDocProducer","$value$":"function (rec) {\n var recStr = require('util').inspect (rec, {showHidden: true, depth: null});\n var recStr = Html.entityify (recStr);\n var html\n = '\\n'\n + '\\n'\n + '\\n'\n + ' \\n'\n + '\\n'\n + '\\n'\n + '
' + recStr + '\\n'\n + '\\n'\n + '\\n';\n return html;\n}","$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.
\nThis defaultDocProducer
takes a record rec
and just displays this record in the returned HTML document. For example,
HttpServer.defaultDocProducer ({name: "Sindbad", password: "SESAME"})
\nreturns this string, which is a well-defined HTML document:
\n╭───────────────────────────────────────────────────╮\n│<!DOCTYPE html>↵ │\n│<html>↵ │\n│<head>↵ │\n│ <meta charset="utf-8">↵ │\n│</head>↵ │\n│<body>↵ │\n│<pre>{ name: 'Sindbad', password: 'SESAME' }</pre>↵│\n│</body>↵ │\n│</html>↵ │\n│ │\n╰───────────────────────────────────────────────────╯
\n","$markdown_caption$":"global constant extensible HttpServer.defaultDocProducer : Type.lambda ([RECORD, Data.Html.HTML_DOC])","$html_caption$":"global constant extensible HttpServer.defaultDocProducer : Type.lambda ([RECORD, Data.Html.HTML_DOC])"}],["doc_3",{"unit":"doc","name":"HttpServer","header":"HTTP Servers","$unit_flags$":{"kind_of_unit":"doc"},"$name$":"HttpServer.doc_3","$header$":"HTTP Servers","$markdown_caption$":"3. HTTP Servers","$html_caption$":"3. HTTP Servers"}],["fileServer",{"unit":"value","name":"HttpServer.fileServer","type":"$prod ([HTTP_PORT_NUMBER, Type.object (HttpServer)])","value":"function (port) {\n\n // reconstruct the involved directories and addresses\n var serverRoot = process.cwd();\n var host = 'localhost';\n var serverUrl = 'http://' + host + \":\" + port;\n\n // create the server\n var server = new require('http').Server();\n\n server.addListener ('request', function (req, res) {\n\n // reconstruct some request components\n var urlString = req.url; // the requested URL\n var urlRec = require ('url').parse(urlString); // the requested URL parsed as a record\n var method = req.method; // usually 'GET'\n var path = urlRec.pathname; // e.g. `'/one/two/three.txt'`\n var localPath = System.Path.joinList ([serverRoot, path]); // translate the `path` into the `localPath` on the server\n\n // print the log message\n var date = new Date();\n console.log ( \"`%s` request `%s` at %s:\", method, urlString, date.toLocaleString() );\n\n // // // BEGIN // // // (this block is the same in `fileServer` and `docProducerServer`)\n try {\n if (System.Path.exists (localPath)) {\n if (System.Path.isFile (localPath)) {\n var extension = System.Path.extname (path); // e.g. `'.html'` or `'.js'` or `'.txt'` or `''`\n console.log (\" Responding with the `%s`-file `%s`.\", extension, localPath);\n if (extension in ContentType) {\n res.writeHead (200, {\"Content-Type\": ContentType[extension]});\n } else {\n res.writeHead (200, {\"Content-Type\": \"application/octet-stream\"});\n }\n res.write (System.File.read (localPath));\n res.end ();\n } else if (System.Path.isDirectory (localPath)) {\n console.log (\" Responding with the listed directory at root `%s` and branch `%s`.\", serverRoot, path);\n res.writeHead (200, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write (directoryContentPage (serverRoot) (path));\n res.end ();\n } else {\n console.log (\" Responding with a 404 page, `%s` is neither a file nor directory.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '' + localPath + '\\n' );\n res.end ();\n }\n } else {\n console.log (\" Responding with a 404 page, `%s` does not exits on this system.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } catch (e) {\n console.log (\" Responding with a 404 page due to error: \" + e);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + e + '\\n' );\n res.end ();\n }\n // // // END // // //\n\n });\n\n // add the `port` and `listening` properties to the `server`\n server.port = port; // a `$HttpPortnumber` property\n server.listening = false; // a `$Boolean` property, toggled on `start()` and `stop()` calls\n\n // return the `server`\n return server;\n\n}","info":"`HttpServer.fileServer(port)` creates a file server that listens to the specified port, i.e. to HTTP requests starting with\n`http://localhost:port/`.","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"HttpServer.fileServer","$value$":"function (port) {\n\n // reconstruct the involved directories and addresses\n var serverRoot = process.cwd();\n var host = 'localhost';\n var serverUrl = 'http://' + host + \":\" + port;\n\n // create the server\n var server = new require('http').Server();\n\n server.addListener ('request', function (req, res) {\n\n // reconstruct some request components\n var urlString = req.url; // the requested URL\n var urlRec = require ('url').parse(urlString); // the requested URL parsed as a record\n var method = req.method; // usually 'GET'\n var path = urlRec.pathname; // e.g. `'/one/two/three.txt'`\n var localPath = System.Path.joinList ([serverRoot, path]); // translate the `path` into the `localPath` on the server\n\n // print the log message\n var date = new Date();\n console.log ( \"`%s` request `%s` at %s:\", method, urlString, date.toLocaleString() );\n\n // // // BEGIN // // // (this block is the same in `fileServer` and `docProducerServer`)\n try {\n if (System.Path.exists (localPath)) {\n if (System.Path.isFile (localPath)) {\n var extension = System.Path.extname (path); // e.g. `'.html'` or `'.js'` or `'.txt'` or `''`\n console.log (\" Responding with the `%s`-file `%s`.\", extension, localPath);\n if (extension in ContentType) {\n res.writeHead (200, {\"Content-Type\": ContentType[extension]});\n } else {\n res.writeHead (200, {\"Content-Type\": \"application/octet-stream\"});\n }\n res.write (System.File.read (localPath));\n res.end ();\n } else if (System.Path.isDirectory (localPath)) {\n console.log (\" Responding with the listed directory at root `%s` and branch `%s`.\", serverRoot, path);\n res.writeHead (200, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write (directoryContentPage (serverRoot) (path));\n res.end ();\n } else {\n console.log (\" Responding with a 404 page, `%s` is neither a file nor directory.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } else {\n console.log (\" Responding with a 404 page, `%s` does not exits on this system.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } catch (e) {\n console.log (\" Responding with a 404 page due to error: \" + e);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + e + '\\n' );\n res.end ();\n }\n // // // END // // //\n\n });\n\n // add the `port` and `listening` properties to the `server`\n server.port = port; // a `$HttpPortnumber` property\n server.listening = false; // a `$Boolean` property, toggled on `start()` and `stop()` calls\n\n // return the `server`\n return server;\n\n}","$info$":"
HttpServer.fileServer(port)
creates a file server that listens to the specified port, i.e. to HTTP requests starting with http://localhost:port/
.
Let docProducer
be a document producer, e.g. the defaultDocProducer
, i.e. a lambda function that takes a record and returns a HTML document. Furthermore, let port
be a HTTP port number, e.g. 55555
.
var server = HttpServer.docuProducerServer(port)(docProducer)
\ngenerates 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)
.
' + localPath + '\\n' );\n res.end ();\n }\n } else {\n console.log (\" Responding with a 404 page, `%s` does not exits on this system.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } catch (e) {\n console.log (\" Responding with a 404 page due to error: \" + e);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + e + '\\n' );\n res.end ();\n }\n // // // END // // //\n\n } else { // `rec` is not empty, so return `docProducer(rec)`\n\n var htmlDoc = docProducer (rec);\n res.writeHead (200, {\"Content-Type\": \"text/html\"});\n res.write (htmlDoc);\n res.end();\n\n }\n\n });\n\n }); // end of `request` event listener\n\n // add the `port` and `listening` properties to the `server`\n server.port = port; // a `$HttpPortnumber` property\n server.listening = false; // a `$Boolean` property, toggled on `start()` and `stop()` calls\n\n // return the `server`\n return server;\n\n }\n}","info":"......","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"HttpServer.fileAndDocProducerServer","$value$":"function (port) {\n return function (docProducer) {\n\n // reconstruct the involved directories and addresses\n var serverRoot = process.cwd (); // current working directory/path of the server\n var host = 'localhost'; // host will always be `localhost`\n var serverUrl = 'http://' + host + \":\" + port; // e.g. `http://localhost:55555`\n\n // create the `server`\n var server = new require('http').Server();\n\n // implement the `request` event listener\n server.addListener ('request', function (req, res) {\n\n var urlRec = require ('url').parse (req.url); // the requested URL, parsed as a record\n var queryRec = require ('querystring').parse (urlRec.query); // the parsed query record of the URL\n var path = urlRec.pathname; // e.g. '/one/two/three.txt'\n var localPath = System.Path.joinList ([serverRoot, path]); // translate the `path` into the `localPath` on the server\n var method = req.method; // usually, either 'GET' or 'POST'\n var dataString = ''; // the POST data as a string\n var dataRec; // the POST data as a record\n\n // action for the the next incoming POST `data`\n req.on ('data', function (data) {\n\n // add the next `data` chunk to `dataString`\n dataString += data;\n\n });\n\n // action if the POST data stream has terminated\n req.on ('end', function () {\n\n // convert the\n dataRec = require('querystring').parse (dataString);\n\n // print the log message\n var date = new Date();\n console.log (\n \"`%s` request for `%s` at %s:\\n Path: `%s`\\n Query record: `%j`\\n Data record: `%s`\",\n method, serverUrl, date.toLocaleString(), path, queryRec, Any.ellipsis (dataRec)\n );\n\n // combine `queryRec` and `dataRec` into a single record `rec`\n var rec = {};\n for (var k in queryRec) { rec[k] = queryRec[k]; }\n for (var k in dataRec) { rec[k] = dataRec[k]; }\n\n // check if `rec` is empty; if so, return the file requested in `path`, otherwise return `docProducer(rec)`\n if (Object.keys(rec).length === 0) {\n\n // // // BEGIN // // // (this block is the same in `fileServer` and `docProducerServer`)\n try {\n if (System.Path.exists (localPath)) {\n if (System.Path.isFile (localPath)) {\n var extension = System.Path.extname (path); // e.g. `'.html'` or `'.js'` or `'.txt'` or `''`\n console.log (\" Responding with the `%s`-file `%s`.\", extension, localPath);\n if (extension in ContentType) {\n res.writeHead (200, {\"Content-Type\": ContentType[extension]});\n } else {\n res.writeHead (200, {\"Content-Type\": \"application/octet-stream\"});\n }\n res.write (System.File.read (localPath));\n res.end ();\n } else if (System.Path.isDirectory (localPath)) {\n console.log (\" Responding with the listed directory at root `%s` and branch `%s`.\", serverRoot, path);\n res.writeHead (200, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write (directoryContentPage (serverRoot) (path));\n res.end ();\n } else {\n console.log (\" Responding with a 404 page, `%s` is neither a file nor directory.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } else {\n console.log (\" Responding with a 404 page, `%s` does not exits on this system.\", localPath);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + localPath + '\\n' );\n res.end ();\n }\n } catch (e) {\n console.log (\" Responding with a 404 page due to error: \" + e);\n res.writeHead (404, {\"Content-Type\": \"text/html; charset=UTF-8\"});\n res.write ( '
' + e + '\\n' );\n res.end ();\n }\n // // // END // // //\n\n } else { // `rec` is not empty, so return `docProducer(rec)`\n\n var htmlDoc = docProducer (rec);\n res.writeHead (200, {\"Content-Type\": \"text/html\"});\n res.write (htmlDoc);\n res.end();\n\n }\n\n });\n\n }); // end of `request` event listener\n\n // add the `port` and `listening` properties to the `server`\n server.port = port; // a `$HttpPortnumber` property\n server.listening = false; // a `$Boolean` property, toggled on `start()` and `stop()` calls\n\n // return the `server`\n return server;\n\n }\n}","$info$":"
......
\n","$markdown_caption$":"global constant extensible HttpServer.fileAndDocProducerServer : Type.fun ([HTTP_PORT_NUMBER, Type.lambda ([RECORD, Data.Html.HTML_DOC]), Type.object (HttpServer)])","$html_caption$":"global constant extensible HttpServer.fileAndDocProducerServer : Type.fun ([HTTP_PORT_NUMBER, Type.lambda ([RECORD, Data.Html.HTML_DOC]), Type.object (HttpServer)])"}],["doc_4",{"unit":"doc","name":"HttpServer","header":"Server Prototype Properties","$unit_flags$":{"kind_of_unit":"doc"},"$name$":"HttpServer.doc_4","$header$":"Server Prototype Properties","$markdown_caption$":"4. Server Prototype Properties","$html_caption$":"4. Server Prototype Properties"}],["listens",{"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_flags$":{"kind_of_value_unit":"proto","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"HttpServer.listens","$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.
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.
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.
.....
\n","$markdown_caption$":"global constant extensible HttpServer::stop","$html_caption$":"global constant extensible HttpServer::stop"}]]]