I'm building a rudimentary web server in Rebol 2. I'm trying in particular to understand the flow and logic behind the asynchronous aspects of the TCP scheme.
As far as I can understand it, you create a listening port, place it on the wait list. As requests come in, clones of the listening port is created which are then added to the wait list until their function is complete.
In the meantime, the state of all ports concerned need to be managed (via their ASYNC-MODES) in order for the correct flow of data.
Things I'm unclear on:
My port logic is as minimal as I could make it—is this sufficient to be somewhat reliable?
ASYNC-MODES are undocumented and apparently a temporary measure that calcified. Does any explanation exist as to how they should be set over the course of operations? (the only usage examples I've been able to locate are the UniServe Engine and the Async Protocol, however both are intended as base protocols and have incorporate layers of code management that obscure the elementary network operations)
When reading the request, the only way to ensure the entire request is read is to push READ-IO until it returns a 'WOULD-BLOCK error. Is this the only way to do this?
The client handler reads the request, sends the response and closes the client port. In HTTP, there is more dialog that this simple request->response operation. Is it enough to keep the client port open in order to be able to continue, e.g. in response to a
client-awake: func [client-port][ ... read client port ... ... send response ... ... if decisive response, close client port ... false ]
Minimal Async Web Server
#!/usr/local/bin/rebol -cs Rebol [ Title: "Minimal Web Server for Rebol 2" Author: "Christopher Ross-Gill" Date: 15-Jan-2017 File: %mini-webserver.r Version: 0.1.0 Purpose: "An elementary Web Server" Rights: http://opensource.org/licenses/Apache-2.0 ] buffer: make binary! 2 + buffer-size: 1024 start: func [port [port!]][ append system/ports/wait-list port ] stop: func [port [port!]][ remove find system/ports/wait-list port close port ] wake-server: func [server-port [port!] /local client-port][ client-port: first server-port net-utils/net-log reform ["HTTP request from" client-port/host] ; set-modes client-port [no-wait: true] client-port/async-modes: 'read client-port/awake: :wake-client client-port/locals: make object! [ request: make binary! 1024 response: "HTTP/1.0 200 OK^/Content-type: text/html^/^/<pre>I'm Alive!^/^/" ] start client-port false ; returns false to perpetuate the WAIT function ] wake-client: func [client-port [port!] /local state request response][ request: client-port/locals/request response: client-port/locals/response while [ case [ error? state: try [ read-io client-port clear buffer buffer-size ][ state: disarm :state either state/code = 517 [ ; would-block ; end of input? net-utils/net-log "End of Stream" ][ net-utils/net-log "Read Error" ? state ] false ] state <= 0 [ net-utils/net-log join "Client closed connection: " state false ] state [true] ] ][ net-utils/net-log "Reading stream..." append request buffer ] client-port/async-modes: 'write write-io client-port response: join response request length? response stop client-port false ; returns false to perpetuate the WAIT function ] server-port: make port! [ scheme: 'tcp port-id: 8888 async-modes: 'connect awake: :wake-server ] trace/net on net-utils/net-log "Opening HTTP listening port 8888" start server-port open/direct/binary server-port browse http://127.0.0.1:8888/test wait  net-utils/net-log "Closing HTTP listening port 8888" stop server-port