aboutsummaryrefslogtreecommitdiff
path: root/doc/server.md
blob: 2f1062ffd99bf0358dff3c50b14f22ac812b20a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
Server Algorithms
=================

As a convenience for coding TCP/IP network servers some algorithms
are provided.

All these server templates work by first creating an object, then
configuring it via various setters, most importantly setting the
event handler for new connections and finally starting the event
loop. So far, all event loops are based on the epoll(7) facility.

net .alg .epollServer
---------------------

    net .alg .epollServer "+" via
     { 8000 } +port
     { ":" via
       # : provides methods to communicate with the new connection
       # :read, :write, :close, :ctl (as in EPOLL_CTL)
    
       "Welcome to server\n" :write
    
       <
         { 4096 :read _ dump "" eq { :close } rep } =*in
         { "Did't :ctl for outgoing data" die } =*out
         { "Error" die } =*err
       >
     } +accept
     +run

`net .alg .epollServer` creates a new server, on which the following options
can be set, and `accept` and `port` must be set before starting the server.

`port`: Takes a function which returns the port number to bind.

`accept`: Takes a function which takes a scope containing capabilities
to communicate over the newly accepted connection and returns a scope
which provides functions to call on `in`coming data, `out`going kernel
buffer space and `err`ors in the connection.

The capabilities of the connection are `read` which takes the maximal number of
bytes to read and returns a string of the actually read bytes or the empty string
if the connection ended. The member `write` takes a string and writes it to the
connection - returning the number of bytes written. `close` immediately closes
the connection and `ctl` takes a new set of flags `bor`'ed together from
`sys .linux .epoll .EPOLLIN`, `.EPOLLOUT`, and `.EPOLLERR`.

`interval`: Takes a function which provides the number of microseconds to sleep
during the next epoll_wait(2) syscall.

    net .alg .epollServer "+" via { 1000 } +interval
    # server will abort epoll_wait after 1ms

`reuseAddr`: Takes a function which takes a socket file descriptor and performs
whatever shenanigans it deems appropriate before the socket is passed to bind(2).
The default for this option applies SO_REUSEADDR, hence the separate option, if you
whish to disable it set `reuseAddr` to `{ -- }`, i.e. just discard the socket descriptor.

    net .alg .epollServer "+" via { -- } +reuseAddr
    # server will not set SO_REUSEADDR

FIXME: There should be a general option setting option as well (and only the default-active
ones should be available for disabling)


net .alg .bufferedEpollServer (what you actually want to use)
-------------------------------------------------------------

Just as `net .alg .epollServer` and actually based on it, but provides buffering for all
in- and output so you don't have to track the number of bytes read and written and can
instead handle stuff on a higher level. To this end, `accept` works slightly different.

The capabilities `write`, `read`, `close` are provided as in `net .alg .epollServer`,
where `close` still immediately closes the connection, whereas the new capability
`finish` will first write the remaining output buffer bytes before actually closing
the connection.

The members of the `accept` returned scope are `in`, `err`, and `end`. The `in`
handler gets the current input buffer and is expected to return whatever can not
yet be handled (i.e. pars whatever prefix you like and return the rest), `err` is
called when an error occurs on the connection and `end` is called when the remote
end closes the connection.

    net .alg .bufferedEpollServer "+" via
     { 8000 } +port
     { ":" via
       # available methods are :read, :write, :close, :finish
    
       "Welcome to server\n" :write
    
       <
         { ==s
           { s len 4 ge } { "4 chars by you: " 4 s str .prefix cat "\n" cat :write 4 s str .postfix =s } loop
           s
         } =*in
         { "Bye..." :write :finish } =*end
         { "Error" die } =*err
       >
     } +accept
     +run

FIXME: Fix the bug which produces two "Bye..." messages.

`net .alg .bufferedEpollServer` takes the same options as `net .alg .epollServer`
and additionally provides `outputBufferLimit` which takes a function which
returns the maximum number of output buffer bytes you are willing to keep
around for slow reading clients.

    net .alg .bufferedEpollServer "+" via { 16 } +outputBufferLimit
    # server will raise an error if output buffer grows above 16 bytes


net .alg .httpServer
--------------------

This is not yet a full featured HTTP server, but works a little bit.
It is based on `net .alg .bufferedEpollServer` and takes the same options.
However, you are not supposed to provide an `accept`, but instead
call `request` to provide a function which handles the requests.

    net .alg .httpServer "+" via
      { 8080 } +port
      { ":" via
        # available methods are :ok, :fail, :close, :write, :finish
        # additional members are :headers, :method, :url, :args
        :headers keys dump
        :method dump
        :url dump
        :args keys dump
        "<html><body>O hi!</body></html>" "text/html" :ok
      } +request
      +run