gopher is a simple interface to the Gopher protocol.
The gopher extension provides the minimum interface required to communicate with Gopher clients. You may create and send records consisting of the five fields (type name selector host port), send text and binary files, even send arbitrary lines; finally, there is a procedure to parse a line sent by the client and pass it to a user-specified handler.
The phricken egg relies on this module, providing a much more high-level interface and a full networked Gopher server.
All transmission is done on (current-input-port) and (current-output-port). This extension does not know or care about the network.
All send-* procedures are specified to return #t.
Procedures[procedure] (make-entry type name selector host port)
Create a record consisting of the five main fields in RFC 1436. The fields may be of any type, as they are converted to strings via ->string before sending.
Example: (make-entry 'I "Picture of me" "/me.jpg" (get-host-name) 70)
Also provided is the record predicate entry?.[procedure] (entry->string e)
Converts an entry record to a string, using the following rules:
- Entry fields are converted to strings via ->string
- CR, LF, TAB and NUL are replaced with SPACE
- Only the first character of the type field string is used
- Output string format is "TypeName<TAB>Selector<TAB>Host<TAB>Port"
Send an entry record to the client. Equivalent to (send-line (entry->string e)).[procedure] (send-line line)
Send a single line to the client, and terminate it with a CRLF.
The constant eol is also provided which is just the string "\r\n".[procedure] (send-lastline)
Send an end-of-transmission indicator to the client, which is simply a period on a line by itself.[procedure] (send-text-file filename)
Read text file FILENAME and send it to the client, with a CRLF terminating each line. A period at the beginning of a line will be escaped (doubled). A lone period is sent at the end, via (send-lastline).[procedure] (send-binary-file filename)
Send a binary file verbatim to the client, using the sendfile module. No lastline is sent.[procedure] (accept handle-request)
[parameter] (max-line-length 2048)
Reads a line from the client (up to CRLF, plain CR, or plain LF), replaces any ASCII NULs with SPACE, and splits the line into fields, using TAB as the delimiter. In general, client lines take 4 forms:
- Selector<TAB>Search terms
- Selector<TAB>Gopher+ data
- Selector<TAB>Gopher+ data<TAB>Search terms
The first field is always the requested selector. Search terms are added for type 7 requests. The Gopher+ protocol unfortunately introduces some context-sensitive ambiguity into the meaning of the fields, and this module is not in the business of figuring that out. Instead, the user is expected to hash out the meaning of any extra fields, and handle-request is therefore called with two arguments:
(handle-request (car fields) (cdr fields)) (handle-request selector extra) ; put another way
Note: the RFC says selectors may not exceed 255 characters, but this check is not enforced; however, to avoid memory overflow, accept will only read up to max-line-length characters. (Special note: line limiting doesn't currently work for TCP ports!)
See the phricken extension for an example.