================================================================ ____ _ _ __ / ___|__ _ ___| | _____| |_ __ __/ /_ | | / _` / __| |/ / _ \ __| \ \ / / '_ \ | |__| (_| \__ \ < __/ |_ \ V /| (_) | \____\__,_|___/_|\_\___|\__| \_/ \___/
================================================================
Casket is a small HTTP/1.1 server written in RetroForth.
Some notes on this:
• runs under inetd
• use w/stunnel if you need HTTPS
• I recommend also using timelimit(1) with this
Changes from v5:
• removed support for running .fsp files
• now using file:read/bytes and file:write/bytes
• support for HEAD requests
• reject POST, PUT, DELETE requests
• various refactorings
================================================================
First, some configuration options. Since this will run under inetd there's no need to specify the port. But the path to the files to serve is needed, so define it here.
The server supports virtual servers. For this, create a separate directory for each under the WEBROOT. E.g., I use a setup like
/www/retroforth.org /www/retroforth.org:443 /www/ilo.retroforth.org
Note the port number: if using a port other than 80 this is needed. I recommend just using a symlink.
================================================================
Next, I need to handle the incoming requests. HTTP allows for a large number of header fields, but I really only care about two: GET and Host.
An incoming request will look like:
GET / HTTP/1.1 Host: retroforth.org
With the lines ending in a CR,LF sequence.
I need to allocate space for the data I care about. There are three items:
• The Requested file
• The desired virtual Host
• The query string (if any)
The header processor will read each item and store the Host and Requested file. Everything else is ignored.
I implement eot? to decide if a line (or field) indicator has been reached. This is used by s:get to decide when the input should stop. s:get records the characters read into the specified buffer. And finally, read-request reads the input.
================================================================
Next is reading in the desired file. An initial request may be just a /. In this case, Casket will replace the Requested filename with /index.html. In the odd case that a file is requested without a leading /, I have a word to add this. And then a word that constructs a filename.
This also has a word check-for-params that is used to separate the requested file from any query string that may be present.
Next, I need to determine the file type. I'll do this by taking a look at the extension, and mapping this to a MIME type.
================================================================
Using these, I can construct a word to read in the file and send it to the client.
Reading files is now a bit more involved, since images and other formats have binary data.
transfer performs the actual process of reading the requested file and sending it to the client. This makes use of the file:read/bytes and file:write/bytes to do the actual i/o. These are used as they are faster than reading & writing on a per-byte basis.
Adjust the FileBuffer size (and matching size constant) as desired. The requsted file will be read in chunks of this size, so matching the splits better for te files you are using can aid in performance.
In the above, eol will send an end of line sequence.
================================================================
The last support word is a handler for 404 errors. This will send the 404 status code and a human readable error message.
================================================================
I have a stub setup for unsupported (PUT/POST/DELETE) methods.
And now for the top level server.
Receive a request:
If invalid, reject:
See if the file exists:
Send an "200 OK" response and the file (or a 404 if the file wasn't found):
And the code for Casket is done.
================================================================
Casket requires [Retro](http://forthworks.com/retro) and a Unix system with inetd.
Install Retro and put the casket.forth somewhere. Then add a configuration line to your /etc/inetd.conf to run it. Restart inetd.
Edit the WEBROOT in casket.forth to point to your web directory. Then go to the web directory and create a directory for each domain. E.g.,
/home/crc/www/casket.forthworks.com
Put your index.html and other files here and try accessing your website.
================================================================
Copyright (c) 2018 - 2023, Charles Childers
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.