[REBOL] Re: The REBOL async:// tutorial - take 1
From: maarten:vrijheid at: 3-Mar-2004 19:35
First, realize that we didn't knock this off in two afternoons. So it is
OK to hit your head against the wall.
>> handler: func [port [port!] state [word! error!] /local tmp cmd] [
>> if error? :state [print mold disarm state return true]
>> switch state [
>> connect [
>> ; do HTTP request
>> insert port {GET /fg/anen.jpg HTTP/1.0^M^JHost:
>>www.3dwallpaper.com^M^J^M^J}
>>
>>
>>
>>
>That one - raw tcp stream, right? I wonder if some kind of dialect (set
>of functions) could be produced to handle that ugly MJMJMJ and GET
>commands etc., as e.g. read/custom allows. Just a theoretical question,
>if it even would be worth it, nothing more
>
You could use the builtin crlf as a replacement.
>> false
>> ]
>> read [false]
>> write [false]
>> close [
>> ; get data
>> data: copy port
>>
>>
>>
>>
>OK, so that one does not block right? And it is so just because we are
>inside handler function, which is being called once some event happens
>on port, so theoretically some data should be awaiting us. I just wonder
>it comes in 'close switch part. We get here once other side closed
>connection? So if I understand it correctly, we read it in parts, but
>'read part does nothing, rebol is internally buffering data (how large
>data is rebol able to buffer easily that way?) and once other side
>closes connection, we can read it by copy (which will not block, even if
>no-wait was not used), and whole data is being read out of the port
>buffer at once? Well, I hope I am still on track :-)
>
That copy doesn't block, because it gets diverted to the copy in the
async:// handler IIRC. Also, async:// does all low-level input and
output buffering.
And async opening + async dns://
>>Now for a simple server:
>>
>>First we add a listening server port to the system/ports/wait-list, like:
>>
>>
>>
>>
>Why? Is that needed? Am I right thinking it is because of View? Once
>View starts, it adds event-port into wait-list and if we want to process
>all various events properly, we have to go via wait-list?
>
It can be done many ways indeed. Pick your own example.
>> either error? try [listen: open/no-wait tcp://:8000] [
>> port: open async://localhost:8000
>> port/awake: do handler
>>
>>
>>
>>
>Above code somehow escapes my understanding :-) So if we are not able to
>open listen port (because e.g. we are already listening), we open
>connection on localhost to that port? What is that good for?
>
Gabriele? I missed it, it looks like setting up a tunnel to me.
>> ] [
>> listen/awake: func [l /local p] [
>> print "Got connection."
>> p: first listen
>> remove find system/ports/wait-list listen
>> port: make port! [scheme: 'async sub-port: p]
>>
>>
>>
>>
>that is something I never understood. That is why I was not able to
>further more deeply adapt Sterling's proxy.r script. It contained way
>too much port subport and proxy (as a port :-) stuff for my brain to
>swallow :-) 'p is assigned first connected client. It does not contain
>any sub-port, yet it can communicate. IIRC someone said, that sub-port
>contains real communication port. But I don't understand the difference,
>even without sub-port, I am able to send data here and there and I can
>see it buffered in port/state. What is sub-port then?
>
The real connecttion :-) Anyway... this is the way to go. If you take a
look at the renewed xml-rpc you will find an add-server function that
abstract this away nicely.
>> open port
>> port/awake: do handler
>> false
>> ]
>> insert tail system/ports/wait-list listen
>> port: none
>> ]
>>
>>
>>
>>
>>
>so overall - it is clever - once first event happens on listening port,
>we remove it from wait-list, reassign handler and insert it back into
>wait-list. That sounds like nice constructor/init method in OOP :-)
>
Go clean your mouth!
>>As you can see, its awake function convert the accepted port to an async
>>one and sets the handler. So what is the handler then?
>>
>> handler: [ use [ buffer ][
>>
>> buffer: copy []
>>
>> func [port [port!] state [word! error!] /local tmp cmd] [
>> if error? :state [print mold disarm state return true]
>> switch state [
>> connect [print "Connected." false]
>> read [
>> append buffer copy port
>> while [tmp: find buffer newline] [
>> cmd: copy/part buffer tmp
>> remove/part buffer next tmp
>> do-cmd cmd
>> ]
>> false
>> ]
>> write [false]
>> close [print "Peer closed connection." close port true]
>> ]
>> ]
>> ]
>> ]
>>
>>The first thing to notice is the fact that we use 'use to create a context
>>that returns a function! value. This function (and only this particluar
>>value) has access to its buffer. By doing the handler block in the server
>>part above, every accepted port gets a copy of theis function value with
>>its own "static" buffer space. A very simple but effective trick.
>>
>>
>>
>>
>cool!
>
I know! That's why I thought I'd share it with you all.
>Thanks for your tutorial, very educative!
>
Put it to work. async:// is so clever. Carl explained it Eastern 2003
in a private chat, then I started experimenting and Gabriele turned that
into async://
It took some more experimenting, and finally we have a tutorial.
--Maarten