TRIVIAL-SOCKETS: a socket interface for scripting and interactive use
*********************************************************************

This manual describes TRIVIAL-SOCKETS, a simple socket interface for Common Lisp programs and libraries.

Copyright © 2004 Daniel Barlow and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Introduction
************

TRIVIAL-SOCKETS is a portable socket interface that allows CL programs to open connected (client) stream sockets to network services (e.g. HTTP, FTP, SMTP servers) and communicate with them.  It's intended mostly for use by small ``script'' programs and for interactive use where the effort involved in writing one's own portable wrapper layer for several Lisp implementations would outweigh that spent on the actual application.

In the interests of simplicity and ease of porting, the functionality available through TRIVIAL-SOCKETS has been deliberately restricted. For a more general sockets interface which may allow access to more functionality, the reader is encouraged to consult his Lisp implementation's documentation.

Installation
************

TRIVIAL-SOCKETS is distributed via asdf-install.  If you are on the Internet and your Lisp implementation has asdf-install available, you may download and compile this package with an invocation like

     (asdf-install:install 'trivial-sockets)

The trivial-sockets package has been PGP-signed by Daniel Barlow, and asdf-install will by default check that the signature is good and that a trust path exists between you and him.  If not, you will be prompted for a decision on whether to install anyway.  See asdf-install documentation for more details on how this works.

Once you have installed trivial-sockets, the next time you wish to load it you need only evaluate

     (asdf:operate 'asdf:load-op 'trivial-sockets)

or if you have an asdf system that uses it, add trivial-sockets to the :depends-on clause of that system and it will be loaded whenever your system is.

API
***

Types
=====

A host designator is one of the following:

 1. A string, which is resolved as a hostname by the system resolver, typically using DNS or YP or some other implementation-defined mechanism.  For example, "www.google.com"

 2. An IPv4 address in "dotted quad" notation: e.g. "127.0.0.1"

 3. (Implementation-defined): An IPv4 address in whatever ``native'' format the implementation uses to represent same, if applicable. For example, #(127 0 0 1) or 2130706433

 4. The keyword :ANY, which corresponds to INADDR_ANY or "0.0.0.0"

A protocol specifier is a keyword naming an IANA protocol number (as typically found in /etc/protocols on Unix-like systems) or the corresponding number.  Implementations must support :TCP at a minimum.

Functions
=========

 -- Function: open-stream peer-host peer-port &key local-host local-port external-format element-type protocol
     => stream

     Arguments and Values:

     PEER-HOST--a host designator.

     PEER-PORT--an integer.

     LOCAL-HOST--a host designator.  The default is :any.

     LOCAL-PORT--an integer.  The default is 0.

     EXTERNAL-FORMAT--an external file format designator.  The default is :default.

     ELEMENT-TYPE--a type specifier; see the Common Lisp function open for valid values.  The default is 'character.

     PROTOCOL--a protocol specifier.  The default is :tcp.

     Description:

     Return a stream to the named service, open for both reading and writing. The stream is usually buffered, so be sure to use force-output where necessary.

     If the stream cannot be created for any reason, an error of type socket-error is signaled.

     The stream should be closed in the usual way when no longer needed: see the Common Lisp functions close, with-open-stream

 -- Function: open-server &key host port reuse-address backlog protocol
     => server socket

     Arguments and Values:

     HOST--a host designator.  The default is :any.

     PORT--an integer.  The default is 0.

     REUSE-ADDRESS--t or nil.  The default is t.

     BACKLOG--an integer.  The default is 1.

     PROTOCOL--a protocol specifier.  The default is :tcp.

     Description:

     Create a listening server socket.  If PORT is 0, an unused port will be chosen by the implementation/operating system.  HOST may be set to the address of any local network interface to restrict the socket to that interface.

     If REUSE-ADDRESS is true (the default, as recommended by Stevens) then the SO_REUSEADDR socket option will be set, which allows the the port to be reused immediately after it has been closed, without waiting for a timeout (``2*MSL'') to expire.

     BACKLOG sets how many pending connections are queued by the operating system.

     If the socket cannot be created for any reason, an error of type socket-error is signaled.

     The nature of the object returned is implementation-dependent.  When the socket is no longer needed it should be closed with close-server.

     See Macro with-server.

 -- Function: close-server server
     => result

     Arguments and Values:

     SERVER--a server socket.

     RESULT--implementation-dependent.

     Description:

     Close SERVER and release all resources associated with it. Note that opening a new server on the same address/port will not be immediately possible unless the earlier server was created with the :reuse-address argument.

 -- Macro: with-server (server args) declaration* form*
     => results

     Arguments and Values:

     SERVER--a variable.

     ARGS--a list of arguments.

     DECLARATION--a declare expression.

     FORMS--an implicit progn.

     RESULTS--the values returned by the FORMS.

     Description:

     with-server uses open-server to create a server socket named by SERVER.  ARGS are used as keyword arguments to open-server.

     with-server evaluates the FORMS as an implicit progn with SERVER bound to the value returned by open-server.

     When control leaves the body, either normally or abnormally (such as by use of throw), the server socket is automatically closed.

     The consequences are undefined if an attempt is made to assign to the variable SERVER within the body forms.

 -- Function: accept-connection server &key external-format element-type
     => stream

     Arguments and Values:

     SERVER--a server socket.

     EXTERNAL-FORMAT--an external file format designator.  The default is :default.

     ELEMENT-TYPE--a type specifier; see the Common Lisp function open for valid values.  The default is 'character.

     Description:

     Accept a connection to SERVER, returning a stream connected to the client which is open for both reading and writing.  The stream is usually buffered, so be sure to use force-output where necessary.

     If no connection is pending, accept-connection waits until one arrives.

     If anything goes wrong, an error of type socket-error is signaled.

Examples
========

Simple client
-------------

     ;; this is not HTTP compliant, really.  But it's good enough
     ;; for a demonstration
     (with-open-stream (s (trivial-sockets:open-stream "www.google.com" 80))
       (format s "HEAD / HTTP/1.0~%Host: www.google.com~%~%")
       (force-output s)
       (loop
         (let ((l (read-line s nil nil)))
           (unless l (return))
           (princ l) (terpri))))

Simple (single-threaded) server
-------------------------------

     (trivial-sockets:with-server (s (:port 8913 :reuse-address t))
        (loop
         (with-open-stream (c (trivial-sockets:accept-connection s))
             (read-line c)
             (format c "This is a compliant though pointless implementation ~
     of the finger protocol~%"))))

Errors
======

 -- Condition: unsupported
     Class precedence list: error

     This exists so that partial implementations of this interface may be created for environments which are incapable of supporting the full API.  An unsupported error is signaled if the user requests functionality that is not implemented for the Lisp environment in use.

 -- Condition: socket-error
     Class precedence list: error

     A socket-error error is signaled when an error situation occurs during opening of the stream.  If you need more detail, this is probably a sign that you have outgrown this interface and will have to resort to unportable code (error codes vary between systems:were you expecting HOST_UNREACH or NET_UNREACH?).  With that in mind, you can access the implementation-specific error using

          (socket-nested-error condition)

Implementation-dependent
************************

Not all features in this interface are supported on all platforms, owing to deficiencies in the underlying socket layers that it uses.

Many implementations signal socket-related errors using non-specific error classes such as ERROR or SIMPLE-ERROR.  (Some others, perhaps, signal more specific errors but the code in trivial-sockets does not know that.  Patches welcome).  Where we don't know of a specific error, we catch the general ones and resignal SOCKET-ERROR, so it's possible sometimes that errors shich are nothing at all to do with sockets (e.g. keyboard interrupts or external signals) also get presented as SOCKET-ERRORs.  This applies in all implementations listed except where noted.

  * Armed Bear CL currently supports only client sockets, and only for TCP, with unspecified local endpoint, and with the default external-format.

  * Allegro CL (tested in Allegro 6.2. trial) has no support for protocols other than :tcp or non-default external-formats. Allegro sockets are multivalent, so it ignores the :element-type.

  * CLISP has no support for protocols that are not :tcp, or for binding the local address/port.  Its streams are unbuffered, as CLISP buffered streams do not return any data at all on reads until the buffer is full - making them no use for any protocol in which one side sends less than 4k at a time.  (CLISP ``interactively buffered'' streams are likely to fix this, but as of October 2004 have not yet been implemented).

  * CMUCL has no support for external-formats other than :default, for protocols that are not :tcp, or for binding the local address/port.

  * Lispworks supports TCP only,  It doesn't do non-default local address in server sockets, or listen backlog length. It doesn't do non-default external-formats.  If the local port is 0, open-server doesn't return the real port number.  It also uses an odd construction involving multiple threads for server sockets which in principle should be transparent but don't say we didn't warn you.

  * OpenMCL socket support is very similar to that of Allegro: all implementation notes applicable to Allegro also hold for OpenMCL. Additionally, errors signaled by instances of ccl:socket-error are caught and resignaled as socket-error.

  * SBCL has no support for external-formats other than :default. Errors signaled by sb-bsd-sockets:socket-error and sb-bsd-sockets:name-service-error are caught and resignaled as socket-error.

Patches to improve per-implementation support for this interface are welcome.  Patches which include an appropriate update for the manual are doubly if not sevenfoldly so.