libdorrit(8)            FreeBSD System Manager's Manual           libdorrit(8)

NAME
     libdorrit - UNIX-Domain WebSocket Server Library

SYNOPSIS
     #include <dorrit.h>
     -I/usr/local/include -L/usr/local/lib -ldorrit

DESCRIPTION
     Libdorrit implements a generic event-driven WebSocket server that works
     with the prospero(8) web server.  Dorrit performs network and concurrency
     tasks.  You supply code to service connections.

     o   Dorrit accepts only local UNIX-domain connections.

     o   Dorrit automatically responds to pings with pongs.

     o   Dorrit drops connections with clients that send:

         o   Frames with payloads and no masking keys.

         o   Frames with payloads larger than a specified length, which can be
             no greater than 63535.

     An Example echo server is included in the dorrit source distribution.

   USAGE
     The libraries provide your server's "main" function.  You define 5
     functions to match the following prototypes.

     void ws_init_func();
     void ws_exit_func();

     int ws_open_callback( void *, char * );
     void ws_read_callback( void *, int, int, char * );
     void ws_close_callback( void * );

     Do not define any other global symbol beginning with the 3 characters
     'ws_' because the library reserves that namespace.

   WS_INIT_FUNC()
     In ws_init_func() perform the initialization tasks your server needs to
     do once at server start-up.

     Dorrit calls ws_init_func():

     o   before attempting to change the server's user and group to the values
         specified by the command line options.  If the server starts as root
         ws_init_func(), executes as root.

     o   before the server becomes a daemon and starts listening for
         connections.  The standard streams are connected to the terminal from
         which the server was started.  Error and informative messages should
         be sent to the terminal.

   WS_EXIT_FUNC()
     If you have exit handlers you would like to register to run before the
     server exits, do not invoke atexit() from ws_init_func().  If you do so,
     your handlers are run when the server becomes a daemon.  The server forks
     to create a new session.  The dying parent process will call your
     handlers when it exits.  Instead, call your cleanup code from
     ws_exit_func().

   WS_SET_NAME()
     Call ws_set_name() inside ws_init_func() to set the server's name.

     void ws_set_name( char * );

     If not set the server's name defaults to "server".  The server's name is
     used in 3 ways:

     o   When the server is running, stderr is connected to /dev/null.  Errors
         must are reported with syslog(3).  Dorrit calls openlog() with the
         server's name as argument to ensure that log entries are identified
         by the server's name.

     o   The server's pidfile is written to /var/run/ if the server is started
         as root.  The filename is the server's name with ".pid" appended to
         it.  This file is used by rc.d scripts to stop the server.  A sample
         script is included in the dorrit distribution.

     o   The server's listening socket is created in /var/run/ unless you
         override this with the -l option.  If you do not use the -l option,
         the name defaults to dorrit.socket.  With the server's name
         explicitly set, the name is <name>.socket.

   WS_SET_PERIODIC()
     Install a function to be invoked periodically with:

     void ws_set_periodic( void (*)(), int );

     Dorrit calls the function pointed to by first argument when the number of
     seconds specified by the second argument have elapsed and then again
     repeatedly when that number of seconds has elapsed since the last call.

   WS_OPEN_CALLBACK()
     Dorrit calls ws_open_callback() when a new connection is received.

     int ws_open_callback( void *, char *cookie );

     The first argument is an opaque pointer that uniquely identifies the
     connection.  You pass the pointer to other library functions as
     necessary.  The second argument points to the content of the client's
     Cookie header line presented in the WebSocket upgrade request.

     Return 0 from ws_open_callback() to indicate that the connection should
     be accepted.  Return a non-zero value to indicate that the connection
     should be dropped.

     If the protocol your server implements requires the server to speak
     first, send the initial data with ws_write_conn() in ws_open_callback().

   WS_CLOSE_CALLBACK()
     Dorrit calls ws_close_callback() when a connection is closed.

     void ws_close_callback( void *data );

     The function's argument is the opaque pointer identifying the connection.
     Dorrit frees any outgoing queued data when ws_close_callback() returns.

   WS_READ_CALLBACK()
     Dorrit calls ws_read_callback() when a complete WebSocket message has
     been read from the client.  Dorrit unframes and unmasks the payload and
     passes it to this function.

     void ws_read_callback( void *, int binary, int len, char *buffer );

     The second argument is a flag indicating if the payload data is binary
     data.

     The third argument is the length of the payload data in bytes.  The
     length will never be greater than the maximum buffer size less 5 bytes
     (Defaults to 65535 bytes.  See the description of the -b option).

     The fourth argument is a character pointer to the payload data.  This
     data is always zero-terminated.  The terminator is not part of the
     incoming message and is not counted in the length argument.

   WS_WRITE_CONN()
     Queue an outgoing message for clients with ws_write_conn().

     int ws_write_conn( void **, int conns, int binary, int len, char *data );

     The first argument is a pointer to an array of opaque pointers
     representing the connections to which the data will be written.

     The second argument is the number of elements in the first argument.

     The third argument is a flag indicating whether or not the content is
     binary data.  If this argument is non-zero, the opcode for the outgoing
     frames is set to binary (2).  Otherwise, the opcode is set to text (1).

     The fourth argument is the length of the payload data in bytes.

     The fifth argument is a character pointer to the payload data.  The data
     is copied to an internal buffer and does not have to be zero-terminated.

     ws_write_conn() does not write the data but frames it and queues the
     frames for delivery.  Payloads greater in size than the maximum buffer
     size - 5 are broken into fragment frames to facilitate smoother
     multiplexing.  Each time a write event for a connection is generated, the
     library will send up to one complete frame to the client.

     All queued data is reference counted to avoid unnecessary copying.  Each
     connection's queue of outgoing data points to the same copy of the data.
     When a connection writes the data or closes, the reference count for the
     data is decremented.  When the reference count becomes zero, the data is
     freed.

     ws_write_conn() will not queue more than 200 frames for any connection.

     ws_write_conn() returns:

     o   -1 if the queue could not be enlarged.

     o   -2 if len is less than 1.

     o   0 on success.

     If the function returns -1, the server cannot allocate memory.  There is
     no point continuing to service this connection.  Perform any clean up
     operations necessary, and call ws_close_conn() to drop the connection.

   WS_CLOSE_CONN()
     To close a connection, invoke ws_close_conn().

     void ws_close_conn( void *, int force );

     ws_close_conn() closes a connection immediately if the second "force"
     argument is non-zero.  If "force" is zero, the connection is closed when
     the outgoing queue has been written to the client.  In that situation,
     further incoming data is not read from the client.

     If a client closes its connection, the library calls ws_close_callback().

   SIGNALS
     Dorrit needs to catch SIGTERM, so do not change the disposition of that
     signal.

     Upon receipt of SIGBUS or SIGSEGV, dorrit restarts servers with a call to
     execv(3).  If you want to do something else, install your own handler.

     If your server starts as root and changes user and group, the library
     will be unable to restart if your server is not executable by the user or
     group.

     Dorrit will be unable to perform the operations that require root
     privileges after restart unless you turn on the setuid bit of the server
     (chmod u=+s).

   CONFIGURATION
     Dorrit writes its pidfile into /var/run/ if is started as root.  The
     library is stopped with SIGTERM.  A sample control script is provided in
     the dorrit distribution.  To use the script, you must replace all
     occurrences of "dorrit" with the value you pass to ws_set_name().  The
     script must be renamed as the value you passed to ws_set_name() and
     installed in /usr/local/etc/rc.d.

     Two variables must be added to /etc/rc.conf to use the script.
     Substitute your server's name for "server":

     server_enable="YES"
     server_flags="-u www -g www

     If the "enable" variable is set to "YES", the server is started at system
     start.  Use the following rc commands:

     /usr/local/etc/rc.d/dorrit start
     /usr/local/etc/rc.d/dorrit stop
     /usr/local/etc/rc.d/dorrit restart
     /usr/local/etc/rc.d/dorrit status

     If you do not want the server started on system start, then set

     dorrit_enable="NO"

     and use the following commands:

     /usr/local/etc/rc.d/dorrit forcestart
     /usr/local/etc/rc.d/dorrit forcestop
     /usr/local/etc/rc.d/dorrit forcerestart
     /usr/local/etc/rc.d/dorrit forcestatus

   COMMAND-LINE OPTIONS
     The following command line options are recognized by dorrit servers.  All
     of these are optional.

     -b <number>          By default, dorrit uses buffers of up to 65540 bytes
                          to contain WebSocket frames. 65535 is the largest
                          size that can be specified in 16 bits.  Dorrit does
                          not support incoming WebSocket frames with larger
                          payloads.  4 bytes are added to the buffer size for
                          the WebSocket header. 1 byte is added for a zero
                          terminator.  65535 + 4 + 1 = 65540.   The mask for
                          incoming frames is stored elsewhere.

                          To reduce memory consumption, you can specify a
                          smaller maximum buffer size with the -b option, but
                          you cannot set the maximum buffer size to less than
                          10 bytes or to more than 65540 bytes.

                          Dorrit drops connections on which frames arrive with
                          payloads greater than the buffer size less 5 bytes.
                          If fragment frames arrive on a connection, each
                          fragment's payload must fit into the maximum buffer
                          size less 5 bytes, and the complete coelesced
                          message must also fit into the maximum buffer size
                          less 5 bytes.

                          Support for incoming fragments is so limited because
                          although browsers do not fragment outgoing frames,
                          RFC6455 specifies that intermediaries can change the
                          fragmentation of messages.  If your clients do not
                          send messages larger than those that fit into the
                          buffer size less 5 bytes, any fragments created by
                          intermediaries will fit into the buffer size, and
                          the reconstituted message will also fit into the
                          buffer size.

                          You can queue outgoing messages of any size, but
                          they are broken into fragments of the maximum buffer
                          size or less.

     -l <path/to/socket>  By default, dorrit listens on
                          /var/run/dorrit.socket.  If you set the server's
                          name with ws_set_name(), then "dorrit" in the
                          preceding path is replaced with the name you set.
                          To create the socket, dorrit must start as root.
                          The -l option instructs the library to listen on
                          another socket instead.  Specify the full path to
                          the socket as argument.

                          The server creates the listening socket when it
                          starts, unlinking it first if it already exists in
                          the filesystem.  The owner and group of the socket
                          are changed to the values of the -u and -g options.
                          The permisssions of the socket are set to
                          srwxrwx---.

     -m <number>          By default, dorrit maintains no more than 16384
                          simultaneous connections.  The -m option changes
                          this value.

     -u <string>

     -g <string>          The -u and the -g options are used to specify the
                          user and group for the server.  Both values default
                          to "nobody".  For dorrit to change user, it must be
                          started as root.

                          Dorrit restarts servers on receipt of SIGSEGV or
                          SIGBUS.

                          If your server starts as root and changes user and
                          group, the library will be unable to restart if your
                          server is not executable by the user or group.

                          Dorrit will be unable to perform the operations that
                          require root privileges after restart unless you
                          turn on the setuid bit of the server (chmod +s).

     -x                   The -x option prevents Dorrit from becoming a daemon
                          and writing its pidfile to /var/run/.  Dorrit runs
                          in the foreground.  Stderr is connected to the
                          terminal so that diagnostic output can be sent
                          there.  The option takes no argument.

     -f <path/to/file>    The -f option takes a filename as argument.  Dorrit
                          assigns the filename to the global character pointer
                          ws_config_file.  This enables code in ws_init_func()
                          and to access a configuration file.

AUTHORS
     James Bailie <jimmy@mammothcheese.ca>
     http://www.mammothcheese.ca

                                 June 29, 2017