libdorrit(3)           FreeBSD Library Functions Manual           libdorrit(3)

     libdorrit - UNIX-Domain WebSocket Server Library

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

     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.

         o   Fragment frames. Incoming messages must not be fragmented.

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

     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, unsigned 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.

     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.

     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

     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.

     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.

     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().


     Use ws_set_data() to associate a void pointer with a connection.

     void ws_set_data( void *conn, void *data );

     The data argument is a pointer of any kind that you want to associate
     with the connection referenced by the conn argument.

     Use ws_get_data() to retrieve the stored pointer.

     void *ws_get_data( void *conn );

     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.

     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, unsigned char *buffer );

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

     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.  The fourth
     argument points into a buffer that will be reused on further reads of
     client data.  If you want to preserve the incoming data when
     ws_read_callback returns, copy the data to another location.

     Queue an outgoing message for clients with ws_write_conn().

     int ws_write_conn( void **, int conns, int binary, unsigned int len, unsigned 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 no more than one 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

     ws_write_conn() will not allow the outgoing message queue for any
     connection to grow longer than 200 frames.

     ws_write_conn() returns:

     o   -1 on allcation errors.

     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.

     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.

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

     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

     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).

     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_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


     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

     The following command line options are recognized by dorrit servers.  All
     of these are optional.

     -b  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 messages arrive with payloads
         greater than the buffer size less 5 bytes.  Dorrit drops connections
         on which fragment frames arrive. Incoming messages must not be

         You can queue outgoing messages of any size, but if messages are
         greater than the maximum buffer size, the messages are broken into
         fragment frames of the maximum buffer size or less.

     -l  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 fully qualified 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  By default, dorrit maintains no more than 16384 simultaneous
         connections.  The -m option changes this value.


     -g  The -u and the -g options are used to specify the user and group for
         the server.  If the server starts as an unprivileged user and group,
         then these options must be set to the user and group.  For
         MessageServer to change to a different user and group, it must be
         started as root.  Both values default to "nobody".

         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  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.

     James Bailie <>

                                Tue Dec 4, 2018