3
\$\begingroup\$

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 Expect: 100-continue header?

    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
\$\endgroup\$
  • 1
    \$\begingroup\$ This might be a good question for the author of Uniserve. \$\endgroup\$ – HostileFork Jan 18 '17 at 17:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.