Next: , Previous: , Up: Writing servers   [Contents][Index]


3.2.2 Guile servers

This section describes the Guile interface to Serveez which provides the ability to write servers with Guile. Of course, you could do this without any help from Serveez, but it makes the task a lot easier. This interface reduces the Guile implementation of an Internet server to a simple data processor.

3.2.2.1 Underlying libserveez

Scheme Procedure: libserveez-features

Return a list of symbols representing the features of the underlying libserveez. For details, See Library features.

3.2.2.2 Special Data Types

Serveez extends Guile by various new data types which represent internal data structures of Serveez’s core API.

3.2.2.3 Passing Binary Data

The new binary data type (#<svz-binary>) provides access to any kind of unstructured data. It manages the data exchange between Guile and Serveez. There are some conversion procedures for strings and lists which help to process this binary data in a more complex (guile’ish) way.

Scheme Procedure: binary->string binary

Convert the given binary smob binary into a string. Return the string itself.

Scheme Procedure: string->binary string

Convert the given string into a binary smob. The data pointer of the binary smob is marked as garbage which must be free’d in the sweep phase of the garbage collector.

Scheme Procedure: binary? obj

Return #t if obj is an instance of the binary smob type.

Scheme Procedure: list->binary list

Convert the scheme list list into a binary smob. Each of the elements of list is checked for validity. The elements can be either exact numbers in a byte’s range or characters.

Scheme Procedure: binary->list binary

Convert the given binary smob binary into a scheme list. The list is empty if the size of binary is zero.

Scheme Procedure: binary-search binary needle

Search through the binary smob binary for needle, which can be an exact number, character, string or another binary smob. Return #f if the needle could not be found, or a positive number indicating the position of the first occurrence of needle in the binary smob binary.

Scheme Procedure: binary-set! binary index value

Set the byte at position index of the binary smob binary to the value given in value which can be either a character or an exact number.

Scheme Procedure: binary-ref binary index

Obtain the byte at position index of the binary smob binary.

Scheme Procedure: binary-length binary

Return the size in bytes of the binary smob binary.

Scheme Procedure: binary-concat! binary append

Append either the binary smob or string append onto the binary smob binary. If binary has been a simple data pointer reference it is then a standalone binary smob as returned by string->binary.

Scheme Procedure: binary-subset binary start [end]

Create a subset binary smob from the given binary smob binary. The range of this subset is specified by start and end both inclusive (thus the resulting size is end - start + 1). With a single exception: If end is not given or specified with -1, return all data until the end of binary.

Scheme Procedure: binary-reverse binary

Return a new binary smob with the reverse byte order of the given binary smob binary.

Scheme Procedure: binary-reverse! binary

Perform an in-place reversal of the given binary smob binary and return it.

Scheme Procedure: binary-long-ref binary index

Return the long value of the binary smob binary at the array index index.

Scheme Procedure: binary-long-set! binary index value

Set the long value of the binary smob binary at the array index index to the given value value. Return the previous (overridden) value.

Scheme Procedure: binary-int-ref binary index

Return the int value of the binary smob binary at the array index index.

Scheme Procedure: binary-int-set! binary index value

Set the int value of the binary smob binary at the array index index to the given value value. Return the previous (overridden) value.

Scheme Procedure: binary-short-ref binary index

Return the short value of the binary smob binary at the array index index.

Scheme Procedure: binary-short-set! binary index value

Set the short value of the binary smob binary at the array index index to the given value value. Return the previous (overridden) value.

Scheme Procedure: binary-char-ref binary index

Return the char value of the binary smob binary at the array index index.

Scheme Procedure: binary-char-set! binary index value

Set the char value of the binary smob binary at the array index index to the given value value. Return the previous (overridden) value.

3.2.2.4 Server Definition

In order to set up a new server type, you use the procedure define-servertype!. This procedure takes one argument which must be an associative list specifying the server type in detail. There are optional and mandatory elements you can set up in this alist.

The following example shows the overall syntax of this procedure:

(define-servertype! '(

  ;; Mandatory: server type prefix for later use in (define-server!)
  (prefix          . "foo")

  ;; Mandatory: server type description
  (description     . "guile foo server")

  ;; Mandatory for TCP and PIPE servers: protocol detection
  (detect-proto    . foo-detect-proto)

  ;; Optional: global server type initialisation
  (global-init     . foo-global-init)

  ;; Optional: server instance initialisation
  (init            . foo-init)

  ;; Optional: server instance finalisation
  (finalize        . foo-finalize)

  ;; Optional: global server type finalisation
  (global-finalize . foo-global-finalize)

  ;; Mandatory for TCP and PIPE servers: socket connection
  (connect-socket  . foo-connect-socket)

  ;; Optional: server instance info
  (info-server     . foo-info-server)

  ;; Optional: client info
  (info-client     . foo-info-client)

  ;; Optional: server instance reset callback
  (reset           . foo-reset)

  ;; Optional: server instance notifier
  (notify          . foo-notify)

  ;; Mandatory for UDP and ICMP servers: packet handler
  (handle-request  . foo-handle-request)

  ;; Mandatory: server type configuration (may be an empty list)
  (configuration   . (

    ;; The server configuration is an alist (associative list) again.
    ;; Each item consists of an item name and a list describing the
    ;; item itself.
    ;; Syntax: (key . (type defaultable default))
    (foo-integer       . (integer  #t 0))
    (foo-integer-array . (intarray #t (1 2 3 4 5)))
    (foo-string        . (string   #t "default-foo-string"))
    (foo-string-array  . (strarray #t ("guile" "foo" "server")))
    (foo-hash          . (hash     #t (("foo" . "bar"))))
    (foo-port          . (portcfg  #t foo-port))
    (foo-boolean       . (boolean  #t #t))
  ))))
Scheme Procedure: define-servertype! args

Define a new server type based on args. (If everything works fine you have a freshly registered server type afterwards.) Return #t on success.

3.2.2.5 Predefined Procedures

The following subset of procedures may be used to implement a Guile server. They should be used within the callbacks defined in the define-servertype! procedure. Each of these callbacks gets passed the appropriate arguments needed to stuff into the following procedures. Please have a look at the example Guile servers for the details.

Scheme Procedure: svz:sock? sock

Return #t if the given cell sock is an instance of a valid #<svz-socket>, otherwise #f.

Scheme Procedure: svz:sock:check-request sock [proc]

Set the check-request member of the socket structure sock to proc. Return the previously handler if there is any.

Scheme Procedure: svz:sock:check-oob-request sock [proc]

Set the check-oob-request callback of the given socket structure sock to proc, returning the previous callback (if there was any set before). The callback is run whenever urgent data (out-of-band) has been detected on the socket.

Scheme Procedure: svz:sock:send-oob sock oob

Send byte oob as urgent (out-of-band) data through the underlying TCP stream of TCP sock. Return #t on successful completion and otherwise (either it failed to send the byte or the passed socket is not a TCP socket) #f.

Scheme Procedure: svz:sock:handle-request sock [proc]

Set the handle-request member of the socket structure sock to proc. Return the previously set handler if there is any.

Scheme Procedure: svz:sock:boundary sock boundary

Setup the packet boundary of the socket sock. The given string value boundary can contain any kind of data. If boundary is an exact number, set up the socket to parse fixed sized packets. More precisely, set the check-request callback of the given socket structure sock to an internal routine which runs the socket’s handle-request callback when it detects a complete packet specified by boundary.

For instance, you can arrange for Serveez to pass the handle-request procedure lines of text by calling (svz:sock:boundary sock "\n").

Scheme Procedure: svz:sock:floodprotect sock [flag]

Set or unset the flood protection bit of the given socket sock. Return the previous value of this bit (#t or #f). The flag argument must be either boolean or an exact number and is optional.

Scheme Procedure: svz:sock:print sock buffer

Write buffer (string or binary smob) to the socket sock. Return #t on success and #f on failure.

Scheme Procedure: svz:sock:final-print sock

Schedule the socket sock for shutdown after all data within the send buffer queue has been sent. You should call this right before the last call to svz:sock:print.

Scheme Procedure: svz:sock:no-delay sock [enable]

Turn the Nagle algorithm for the TCP socket sock on or off depending on the optional enable argument. Return the previous state of this flag (#f if Nagle is active, #t otherwise). By default this flag is switched off. This socket option is useful when dealing with small packet transfer in order to disable unnecessary delays.

Scheme Procedure: svz:sock:send-buffer sock

Return the send buffer of the socket sock as a binary smob.

Scheme Procedure: svz:sock:send-buffer-size sock [size]

Return the current send buffer size and fill status in bytes of the socket sock as a pair of exact numbers. If the optional argument size is given, set the send buffer to the specified size in bytes.

Scheme Procedure: svz:sock:receive-buffer sock

Return the receive buffer of the socket sock as a binary smob.

Scheme Procedure: svz:sock:receive-buffer-size sock [size]

Return the current receive buffers size and fill status in bytes of the socket sock as a pair of exact numbers. If the optional argument size is given, set the receive buffer to the specified size in bytes.

Scheme Procedure: svz:sock:receive-buffer-reduce sock [length]

Dequeue length bytes from the receive buffer of the socket sock, or all bytes if length is omitted. Return the number of bytes actually shuffled away.

Scheme Procedure: svz:sock:connect host proto [port]

Establish a network connection to the given host [ :port ]. If proto equals PROTO_ICMP the port argument is ignored. Valid identifiers for proto are PROTO_TCP, PROTO_UDP and PROTO_ICMP. The host argument must be either a string in dotted decimal form, a valid hostname or an exact number in host byte order. When giving a hostname this operation might block. The port argument must be an exact number in the range from 0 to 65535, also in host byte order. Return a valid #<svz-socket> or #f on failure.

Scheme Procedure: svz:sock:disconnected sock [proc]

Set the disconnected-socket member of the socket structure sock to proc. The given callback runs whenever the socket is lost for some external reason. Return the previously set handler if there is one.

Scheme Procedure: svz:sock:kicked sock [proc]

Set the kicked-socket callback of the given socket structure sock to proc and return any previously set procedure. This callback gets called whenever the socket gets closed by Serveez intentionally.

Scheme Procedure: svz:sock:trigger sock [proc]

Set the trigger callback of the socket structure sock to proc and return any previously set procedure. The callback is run when the trigger-condition callback returns #t.

Scheme Procedure: svz:sock:trigger-condition sock [proc]

Set the trigger-condition callback for the socket structure sock to proc. Return the previously set procedure if available. The callback is run once every server loop indicating whether the trigger callback should be run or not.

Scheme Procedure: svz:sock:idle sock [proc]

Set the idle callback of the socket structure sock to proc. Return any previously set procedure. The callback is run by the periodic task scheduler when the idle-counter of the socket structure drops to zero. If this counter is not zero it gets decremented once a second. The idle callback can reset idle-counter to some value and thus can re-schedule itself for a later task.

Scheme Procedure: svz:sock:idle-counter sock [counter]

Return the socket structure sock’s current idle-counter value. If the optional argument counter is given, the set the idle-counter. Please have a look at the svz:sock:idle procedure for the exact meaning of this value.

Scheme Procedure: svz:sock:parent sock [parent]

Return the given socket’s sock parent and optionally set it to the socket parent. Return either a valid #<svz-socket> object or an empty list.

Scheme Procedure: svz:sock:referrer sock [referrer]

Return the given socket’s sock referrer and optionally set it to the socket referrer. Return either a valid #<svz-socket> or an empty list.

Scheme Procedure: svz:sock:server sock [server]

Return the #<svz-server> object associated with the given argument sock. The optional argument server can be used to redefine this association and must be a valid #<svz-server> object. For a usual socket callback like connect-socket or handle-request, the association is already in place. But for sockets created by svz:sock:connect, you can use it in order to make the returned socket object part of a server.

Scheme Procedure: svz:sock:local-address sock [address]

Return the current local address as a pair like (host . port) with both entries in network byte order. If you pass the optional argument address, you can set the local address of the socket sock.

Scheme Procedure: svz:sock:remote-address sock [address]

Return the current remote address as a pair like (host . port) with both entries in network byte order. If you pass the optional argument address, you can set the remote address of the socket sock.

Scheme Procedure: svz:sock:find ident

Return the #<svz-socket> specified by ident, a pair of integers in the form (identification . version). If that socket no longer exists, return #f.

Scheme Procedure: svz:sock:ident sock

Return a pair of numbers identifying the given #<svz-socket> sock, which can be passed to svz:sock:find. This may be necessary when you are passing a #<svz-socket> through coserver callback arguments in order to verify that the passed #<svz-socket> is still valid when the coserver callback runs.

Scheme Procedure: svz:sock:protocol sock

Return one of the PROTO_TCP, PROTO_UDP, PROTO_ICMP, PROTO_RAW or PROTO_PIPE constants indicating the type of the socket structure sock. If there is no protocol information available, return #f.

Scheme Procedure: svz:read-file port size

Return either a binary smob containing a data block read from the open input port port with a maximum number of size bytes, or the end-of-file object if the underlying ports end has been reached. The size of the returned binary smob may be less than the requested size size if it exceed the current size of the given port port. Throw an exception if an error occurred while reading from the port.

Scheme Procedure: svz:server? server

Return #t if the given cell server is an instance of a valid #<svz-server>, otherwise #f.

Scheme Procedure: svz:server:listeners server

Return a list of listening #<svz-socket> smobs to which the given server instance server is currently bound, or an empty list if there is no such binding yet.

Scheme Procedure: svz:server:clients server

Return a list of #<svz-socket> client smobs associated with the given server instance server in arbitrary order, or an empty list if there is no such client.

Scheme Procedure: svz:server:config-ref server key

Return the configuration item specified by key of the given server instance server. You can pass this procedure a socket, too, in which case the appropriate server instance is looked up. If the given string key is invalid (not defined in the configuration alist in define-servertype!), then return an empty list.

Scheme Procedure: serveez-port? name

Return #t if the given string name corresponds with a registered port configuration, otherwise #f.

Scheme Procedure: serveez-server? name

Check whether the given string name corresponds with an instantiated server name and return #t if so.

Scheme Procedure: serveez-servertype? name

Check whether the given string name is a valid server type prefix known in Serveez and return #t if so. Otherwise return #f.

Scheme Procedure: serveez-exceptions [enable]

Control the use of exception handlers for the Guile procedure calls of Guile server callbacks. If the optional argument enable is #t, enable exception handling; if #f, disable it. Return the current (boolean) setting.

Scheme Procedure: serveez-nuke [exit-value]

Shutdown all network connections and terminate after the next event loop. You should use this instead of calling quit. Optional arg exit-value specifies an exit value for the serveez program. It is mapped to a number via scm_exit_value.

Scheme Procedure: serveez-loadpath [args]

Make the search path for the Serveez core library accessible to Scheme. Return a list a each path as previously defined. If args is specified, override the current definition of this load path with it. The load path is used to tell Serveez where it can find additional server modules.

Scheme Procedure: serveez-interfaces [args]

Make the list of local interfaces accessible to Scheme. Return the local interfaces as a list of ip addresses in dotted decimal form. If args are specified, they are added as additional local interfaces.

Scheme Procedure: getrpc [arg]

Lookup a network rpc service arg (name or service number), and return a network rpc service object. If given no arguments, it behave like getrpcent.

Scheme Procedure: setrpc [stayopen]

Open and rewind the file /etc/rpc. If the stayopen flag is non-zero, the net data base will not be closed after each call to getrpc. If stayopen is omitted, this is equivalent to calling endrpcent. Otherwise it is equivalent to calling setrpcent with arg 1.

Scheme Procedure: portmap prognum versnum [protocol [port]]

Establish a (portmap service) mapping between the triple [prognum,versnum,protocol] and port on the machine’s portmap service. The value of protocol is most likely IPPROTO_UDP or IPPROTO_TCP. If instead protocol and port are omitted, destroy all mapping between the triple [prognum,versnum,*] and ports on the machine’s portmap service.

Scheme Procedure: portmap-list [address]

Return a list of the current RPC program-to-port mappings on the host located at IP address address, which defaults to the local machine’s IP address. Return an empty list if either there is no such list available or an error occurred while fetching the list.

Scheme Procedure: svz:coserver:dns host callback

Enqueue the host string argument into the internal DNS coserver queue. When the coserver responds, the procedure callback is run as (callback addr). The addr argument passed to the callback is a string representing the appropriate IP address for the given hostname host.

Scheme Procedure: svz:coserver:reverse-dns addr callback

Enqueue the given addr argument, which must be an IP address in network byte order, into the internal reverse DNS coserver queue. When the coserver responds, the procedure callback is run as (callback host) where host is the hostname of the requested IP address addr.

Scheme Procedure: svz:coserver:ident sock callback

Enqueue the given #<svz-socket> sock into the internal ident coserver queue. When the coserver responds, it runs the procedure callback as (callback user), where user is the corresponding username for the client connection sock.

3.2.2.6 Callback Prototypes

The Guile interface of Serveez is completely callback driven. Callbacks can be set up in the associative list passed to define-servertype!, or by using the predefined procedures described in the previous section. Each of the callbacks is passed certain arguments and is meant to return specific values to indicate success or failure. This section describes each of these callbacks.

Scheme Procedure: global-init servertype

This callback is invoked once for every type of server right after the define-servertype! statement. Here you can initialise resources shared between all instances of your server type. The callback is optional and can be set up in define-servertype!. It should return zero to indicate success and non-zero to indicate failure. If the global initialiser fails, Serveez will refuse to register the server type.

Scheme Procedure: global-finalize servertype

If you want to free shared resources, which were possibly allocated within the global initialiser, you can do so here. The callback is invoked when Serveez shuts down (issued by serveez-nuke) or the server type gets unregistered for some reason. It should return zero to signal success. The callback can be set up in define-servertype! and is optional.

Scheme Procedure: init server

Within this callback you can initialise everything you might need for a single instance of your server. The callback is invoked for each server instance which has been created by define-server! and should return zero to indicate success, otherwise Serveez rejects the server instance. The callback can be set up in define-servertype! and is optional.

Scheme Procedure: finalize server

The server instance finaliser gets its instance representation passed as argument. You need to free all resources used by this server instance which might have been allocated within the server instance initialiser or consumed while running. You can set this callback in the define-servertype! statement. The callback is optional and should return zero to indicate success.

Scheme Procedure: detect-proto server socket

Connection oriented protocols like TCP and PIPE allow more than one server to be listening on the same network port. Therefore, it is necessary to be able to detect the type of client connecting to a port.

This callback takes two arguments; the first is the server instance and the second is the client socket object containing the client connection information. You can set up this callback in the define-servertype! statement.

Serveez may invoke this callback several times as data becomes available from the client until one of the servers recognises it. The servers can retrieve the data received so far using the svz:sock:receive-buffer call.

To indicate successful client detection, you need to return a non-zero value. (Note that for historical reasons, this is inconsistent with other procedures which return zero on successful completion.) Once the server has indicated success, Serveez invokes any further callbacks for the connection only on that server.

If no server has recognised the client after the first 16 bytes, Serveez will close the connection. The connection will also be closed if the client has not sent enough data for a server to recognise it within 30 seconds of connecting.

If multiple servers are listening on the same network port, Serveez invokes this callback for each of them in an arbitrary order. Only one server at most should indicate successful detection.

This callback is mandatory for servers which get bound to connection oriented protocol (TCP and PIPE) port configurations by bind-server!.

Scheme Procedure: connect-socket server socket

If the client detection signalled success, this callback is invoked to assign the client connection to a server instance. The arguments are the same as the detection callback. In this callback you can assign all the connection specific callbacks for your server and perform some initial tasks. Basically you should specify the handle-request and/or check-request callback. This can be achieved by calling svz:sock:handle-request and svz:sock:check-request. The connect-socket callback is also mandatory for connection oriented protocols and must be defined in define-servertype!. On success you should return zero, otherwise the connection will be shutdown by Serveez.

Scheme Procedure: info-server server

This callback gets invoked when requested by the builtin Control Protocol Server. The callback is optional and can be set up in define-servertype!. The returned character string can be multiple lines separated by \r\n (but without a trailing separator). Usually you will return information about the server instance configuration and/or state.

Scheme Procedure: info-client server socket

This callback is optional. You can set it up in the define-servertype! procedure. It is meant to provide socket structure specific information. (The socket structure is a client/child of the given server instance.) You need to return a single line character string without trailing newlines. The information provided can be requested by the builtin Control Protocol Server.

Scheme Procedure: notify server

The server instance notifier callback will be invoked whenever there is idle time available. In practice, it is run approximately once a second. A server instance can use it to perform periodic tasks. The callback is optional and can be set up in define-servertype!.

Scheme Procedure: reset server

This callback is invoked when the Serveez process receives a SIGHUP signal which can be issued via ‘killall -HUP serveez’ from user land. If the underlying operating system does not provide SIGHUP there is no use for this callback. It provides the possibility to perform asynchronous tasks scheduled from outside Serveez. You can optionally set it up in the define-servertype! procedure.

Scheme Procedure: handle-request socket binary size

This callback is invoked whenever a complete packet has been detected in the receive buffer. The packet data is passed to the callback as a #<svz-binary>. The size argument is passed for convenience and specifies the length of the packet in bytes.

The detection, and therefore the invocation, can be made in one of two ways. When Serveez can determine itself when a packet is complete, the callback will be invoked directly. Serveez can make this determination for connections with packet oriented protocols such as UDP and ICMP, or if you tell Serveez how to parse the packet using svz:sock:boundary sock delimiter or svz:sock:boundary sock size and do not specify a check-request callback.

Whenever you specify a check-request callback to determine when a packet is complete, it becomes the responsiblity of that callback to invoke handle-request itself.

Serveez recognises two different return value meanings. For connection oriented protocols (TCP and PIPE), zero indicates success and non-zero failure; on failure, Serveez will shutdown the connection. For packet oriented protocols (UDP and ICMP), a non-zero return value indicates that your server was able to process the passed packet data, otherwise (zero return value) the packet can be passed to other servers listening on the same port configuration.

This callback must be specified in define-servertype! for packet oriented protocols (UDP and ICMP) but is optional otherwise. You can modify the callback by calling svz:sock:handle-request.

Scheme Procedure: check-request socket

This callback is invoked whenever new data has arrived in the receive buffer. The receive buffer of the given #<svz-socket> can be obtained using svz:sock:receive-buffer. The callback is initially not set and can be set up with svz:sock:check-request. Its purpose is to check whether a complete request was received. If so, it should be handled (by running the handle-request callback) and removed from the receive buffer (using svz:sock:receive-buffer-reduce). The callback is for connection oriented protocols (TCP and PIPE) only. You should return zero to indicate success and non-zero to indicate failure. On failure Serveez shuts the connection down.

Scheme Procedure: disconnected socket

The disconnected callback gets invoked whenever the socket is lost for some external reason and is going to be shutdown by Serveez. It can be set up with svz:sock:disconnected.

Scheme Procedure: kicked socket reason

This callback gets invoked whenever the socket gets closed by Serveez intentionally. It can be set up with svz:sock:kicked. The reason argument can be either KICK_FLOOD, indicating the socket is a victim of the builtin flood protection, or KICK_QUEUE which indicates a send buffer overflow.

Scheme Procedure: idle socket

The idle callback gets invoked from the periodic task scheduler, which maintains a idle-counter for each socket structure. This counter is decremented whenever Serveez becomes idle and the callback is invoked when it drops to zero. The idle callback can set its socket’s idle-counter to some value with the procedure svz:sock:idle-counter and thus re-schedule itself for a later task. You can set up this callback with svz:sock:idle.

Scheme Procedure: trigger-condition socket

This callback is invoked once every server loop for the socket structure. If you return #f nothing else is happening. Otherwise the trigger callback will be invoked immediately. You can set up the callback using the procedure svz:sock:trigger-condition.

Scheme Procedure: trigger socket

The trigger callback is invoked when the trigger-condition returns #t. The callback can be set up with the procedure svz:sock:trigger. Returning a non-zero value shuts the connection down. A zero return value indicates success. This callback can be used to perform connection related updates, e.g., you can ensure a certain send buffer fill.

Scheme Procedure: check-oob-request socket oob-byte

This callback is invoked whenever urgent data (out-of-band) has been detected on a socket. Initially this event is ignored and the callback can be set up with the procedure svz:sock:check-oob-request. The oob-byte argument is a number containing the received out-of-band data byte ranging from 0 to 255. If the callback returns non-zero the connection will be shutdown. A zero return value indicates success. You can use svz:sock:send-oob to send a single out-of-band data byte.

Please note: The urgent data is not supported by all operating systems. Also it does not work for all types of network protocols. We verified it to be working for TCP streams on GNU/Linux 2.x.x and Windows 95; let us know if/how it works on other platforms.


Next: Builtin servers, Previous: Embedded servers, Up: Writing servers   [Contents][Index]