(c) Software Lab. Mattias Sundblad
This document describes the httpGate
utility which is included
in the PicoLisp distribution.
For basic information about the PicoLisp system please look at the PicoLisp Reference and the PicoLisp Tutorial.
httpGate
is a central element of the PicoLisp application server
architecture. Its purpose is to perform the following tasks:
A HTTP request to port 80, respectively 443, of the form
http[s]://server.org/12345/path/file
is forwarded to a server on localhost listening on port 12345, to ask for the resource "path/file".
If httpGate was started with a config file, and that file contains an entry for "app", then also the following request is accepted:
http[s]://server.org/app/path/file
In that case, the "app" server process is started automatically (if it is not already running) listening on port 12345, and the request is forwarded as above.
Only GET and POST requests will be forwarded, and only to ports >= 1024. The main httpGate process then forks two child processes, one for each direction. These child processes terminate automatically if the connection is idle for more than 7 minutes.
The simplest way to run httpGate is to start it with an explicit port argument:
bin/httpGate 80 8080 bin/httpGate 443 8080 pem/www.domain.key,pem/domain.crt
When started in this way, httpGate forwards requests from port 80 and 443 respectively to a PicoLisp application on port 8080. This form has a drawback though, since it only allows for a single application to be handled. Usually, there are many PicoLisp applications running on the same machine, and we need httpGate to forward requests to all of them.
To handle several applications, start httpGate with a "names" config file:
bin/httpGate 80 names bin/httpGate 443 names pem/www.domain.key,pem/domain.crt
httpGate needs to be started as root, but application servers should run under normal user accounts. The easiest way to start httpGate automatically is to add lines like the ones above to '/etc/rc.local'.
The "names" config file contains one line per application server. Each line holds six whitespace separated tokens, for example:
app 12345 tom /home/tom log/app ./pil app/main.l lib/app.l -main -go -wait
Empty lines, and lines starting with a "#", are ignored. If the key in a config file record is the special name "@", then it denotes the default application for this machine. URLs without name will be forwarded to that port. If the key contains a slash followed by a string (e.g. "app/foo") then this string is inserted in front of URLs.
Optional tokens (e.g. log files) or empty arguments to the commands must be written as single caret (^) characters to denote empty strings. Double or single quotes are not parsed.
If the port is zero, then a single additional token is expected which should denote an URL to redirect the request to:
app 0 https://domain/foo/barThis will cause httpGate to respnd with "302 Found" and "Location: https://domain/foo/bar".
If the config file contains many (hundreds or thousands) entries, then it is recommended to sort it with the 'balance' utility. This may greatly accelerate name (key) lookup at runtime. For that, put the above config lines into a file "config". The tool 'balance' can be built - together with httpGate - with
(cd src; make tools gate)
The following command will create a balanced "names" file:
cat config | bin/balance -sort > names
If the local application server cannot be connected on the requested port (typically because a session timed out), and a file with the name "void" exists in the current working directory (token 4 in the config line), then the contents of that file (normally HTML) are sent as response to the client.
When the config file is modified, it can be reloaded by sending SIGHUP to all running top-level httpGate processes:
$ sudo pkill -HUP -P1 httpGate
Another possibility is to restart httpGate(s). This is not a problem, and can be done also while the server is in production.
Just kill the top level httpGate parent process. This is not harmful, because existing user sessions are handled by pairs of child processes, which continue to run (until they terminate normally) even if their parent is stopped. Note that this is different from PicoLisp DB applications, where the parent should *never* be hard-stopped (eg. with 'kill -9 <pid>') while child processes are running ('kill <pid>' is OK though, because the parent takes care of stopping the children).
An example for stopping and restarting a running httpGate is:
(let L # Build list of all httpGate parents (i.e. on 80 and 443) (make (in '("sudo" "pgrep" "-P1" "httpGate") (while (read) (link @) ) ) ) # Stop them (for P L (call "sudo" "kill" P) ) # Wait until all are gone (while (find '((P) (kill P 0)) L) (wait 200) ) # Start new (call "sudo" "bin/httpGate" 80 "names") (call "sudo" "bin/httpGate" 443 "names" "pem/...") )
Applications should call
(retire 20)
before they call 'server'. This causes the parent server process to terminate automatically 20 minutes after the last child process (user session) terminated. It will be started by httpGate again on demand. User sessions in turn terminate automatically after 5 minutes (if nobody logged in) or 1 hour (if a user is logged in), unless JavaScript is enabled in the client browser and the application calls
(<ping> 7)
in its main 'action' function. In that case, the user session will not terminate until the user closes the last window or tab to this application.