💾 Archived View for rawtext.club › ~sloum › geminilist › 002189.gmi captured on 2020-11-07 at 02:44:20. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2020-09-24)
-=-=-=-=-=-=-
Sean Conner sean at conman.org
Fri Jul 10 21:05:23 BST 2020
- - - - - - - - - - - - - - - - - - -
It was thus said that the Great Gary Johnson once stated:
Yes, these are CGI scripts in practice. I'm just trying not to shell out
to subprocesses in the interest of server responsiveness. I'm currently
just thinking through how to provide as simple and intuitive an
interface as possible for the script writer. Probably it will amount to
some kind of handler function API that takes a request map and returns a
response map in the classic tradition of all ring-based Clojure web
apps.
Scripts are currently run within their own auto-generated temporary
namespaces, which prevents them from accidentally polluting the server's
namespaces with def* forms. Any I/O to stdout is captured in a string
and currently returned as the body of a "20 text/gemini; charset=utf-8"
response. This is fine as a starting point, but I think I want to create
something that feels more functional and less state-oriented pretty
soon.
GLV-1.12556 [1] uses handlers to generate content. The builtin handlersinclude ones to serve up a file (yup--just one file), a filesystem (handlesserving up data from a directory) and user directories [2]. The API I useis simple. The optional functions are:
okay,err = init(conf)
This function does any intialization for the handler. It is given theconfiguration parameters from the configuration file and returns 'true' ifsuccess, or 'false' and an error message if it fails. Typically, if I havethis function, it verifies the configuration passed in to make sure it willhandle requests. This configuratio block can also be used for stateinformation for the handler if need be.
okay,err = fini(conf)
This function is called when the server is being shut down, and isintended to do any cleanup (that isn't not already handled by the garbagecollector) or saving any persistent data [3].
The mandatory function is:
status,mime,data = handler(conf,auth,location,match)
This handles a request. It's given the configuration block, the clientcertificate (if any [4]), the parsed URL and the 'match' array, which Idon't know how to explain other than an example, in this case, thefilesystem handler (from my development system configuration file):
{ path = "^(/cgi%-bin/)(.*)", module = "GLV-1.handlers.filesystem", directory = "/home/spc/projects/gemini/non-checkin/cgi-bin", cgi = true, }
The path component is a Lua pattern (a type of regex) to determine whichhandler to call for a given request, and it has two parts, the first (whichis captured in ()) determines the "base" path of the request, and the secondis the part the handler is reponsible for. It's these two captures that arepassed in the 'match' section.
This function returns a status code, meta information (named mime here)and a body (which for errors, has to be "") as a string. Handlers do nothave direct access to the socket. And a given handler can be instantiatedmultiple times, each with its own configuration block.
As handlers in GLV-1.12556 are also Lua modules, that means they get theirown "global" namespace. It works and I've had no issue with this structure. It also means it's easier for me to write a handler than it is a CGI (orSCGI) script, although it does mean I have to add a configuration block andrestart the server when adding a new handler.
-spc
[1] https://github.com/spc476/GLV-1.12556
[2] I run more custom handlers than I do builtin ones.
[3] My "quote of the day" handler will save which quote was last served in order to pick up from where it left off.
[4] Authorization has already been done by the time the request is handled so the handler doesn't need to do it, but some handlers (like the filesystem handler, which deals with CGI/SCGi) can do some additional processing.