# tadsnet.t | documentation

    #charset "us-ascii"

    #include <tads.h>
    #include <tadsnet.h>

    /*
     *   Copyright (c) 1999, 2006 Michael J. Roberts
     *
     *   This file is part of TADS 3
     *
     *   This file defines classes and properties used with the tads-net
     *   intrinsic function set.  If you're using this function set, you should
     *   include this source file in your build by adding it to your project
     *   makefile.
     */

    /* include the tads-net intrinsic function set interface definition */
    #include <tadsnet.h>

    /* ------------------------------------------------------------------------ */
    /*
     *   A NetEvent instance describes an event read via the getNetEvent()
     *   function.
     *
     *   In most cases, this base class will not be instantiated directly.
     *   getNetEvent() will always construct the appropriate subclass for the
     *   specific type of event being generated, if that subclass is defined in
     *   the game program.  However, it's possible that the game won't define
     *   all necessary subclasses.  For example, a game written for version 1
     *   of the networking package wouldn't include new subclasses added in
     *   version 2, because those subclasses weren't defined at the time the
     *   game was written.  When getNetEvent() needs to instantiate a subclass
     *   that isn't defined in the game program, it will instead create a base
     *   NetEvent object, which will simply store the subclass-specific
     *   arguments as a list.  This could be useful for debugging purposes,
     *   because it will at least let the programmer inspect the event details
     *   with the interactive debugger.
     */
    class NetEvent: object
        /*
         *   The event type.  This is a NetEvXxx value (see tadsnet.h)
         *   indicating which type of event this is.
         */
        evType = nil

        /*
         *   Construction.  getNetEvent() only constructs this object directly
         *   when the subclass it's looking for isn't defined in the game
         *   program.
         */
        construct(t, [args])
        {
            evType = t;
            evArgs = args;
        }

        /*
         *   Extra event-specific arguments.  This is primarily for debugging
         *   purposes, since it's only used when getNetEvent() needs to
         *   construct a NetEvent subclass that isn't defined in the game.  In
         *   this case, the absence of a subclass definition in the game
         *   presumably means that the game isn't written to handle the type of
         *   event generated (for example, because it was written for an older
         *   interpreter version that didn't have the event type).
         */
        evArgs = nil
    ;

    /*
     *   Network Request Event.  This type of event occurs when a server (such
     *   as an HTTPServer object) receives a request from a network client.
     *
     *   The evRequest member contains a request object describing the network
     *   request.  The class of this object depends on the type of server that
     *   received the request.  For example, for an HTTP server, this will be
     *   an HTTPRequest object.  To reply to the request, use the appropriate
     *   method(s) in the request object - for details, see the specific
     *   request classes for the server types you create in your program.
     */
    class NetRequestEvent: NetEvent
        /* construction */
        construct(t, req)
        {
            inherited(t, req);
            evRequest = req;
        }

        evType = NetEvRequest

        /*
         *   The request object.  When the event type is NetEvRequest, this
         *   contains a request object describing the request.  The class of
         *   the request object varies according to the server type; you can
         *   use ofKind() to check which type of request it is.  For example,
         *   for an HTTP request, this will be an object of class HTTPRequest.
         */
        evRequest = nil
    ;

    /*
     *   Network Timeout Event.  getNetEvent() returns this type of event when
     *   the timeout interval expires before any actual event occurs.
     */
    class NetTimeoutEvent: NetEvent
        evType = NetEvTimeout
    ;

    /*
     *   Network Reply event.  This type of event occurs when we receive a
     *   reply to a network request made with sendNetRequest().
     */
    class NetReplyEvent: NetEvent
        /* construction */
        construct(t, id, status, body, headers, loc)
        {
            inherited(t, id, body, headers, loc);
            statusCode = status;
            requestID = id;
            replyBody = body;
            replyHeadersRaw = headers;
            redirectLoc = loc;

            /* parse the headers into a lookup table keyed by header name */
            if (headers != nil)
            {
                /* create the lookup table */
                local ht = replyHeaders = new LookupTable();

                /* split the headers at the CR-LF separators */
                headers = headers.split('\r\n');

                /* the first line of the headers is actually the HTTP status */
                if (headers.length() > 1)
                {
                    /* save the status line */
                    httpStatusLine = headers[1];
                    headers = headers.sublist(2);
                }

                /* process the rest of the headers */
                for (local h in headers)
                {
                    /* split the header at the ":", and trim spaces */
                    h = h.split(':', 2).mapAll(
                        { s: rexReplace('^<space>+|<space>+$', s, '') });

                    /*
                     *   If it looks like a header, add it to the table.  If
                     *   the header is repeated, append it to the previous
                     *   value with a comma delimiter.
                     */
                    if (h.length() == 2)
                    {
                        local name = h[1].toLower(), val = h[2];
                        if (ht.isKeyPresent(name))
                            val = '<<ht[name]>>, <<val>>';
                        ht[name] = val;
                    }
                }
            }
        }

        /* our default event type is NetEvReply */
        evType = NetEvReply

        /*
         *   The request identifier.  This is the ID value provided by the
         *   caller in the call to sendNetRequest(), so that the caller can
         *   relate the reply back to the corresponding request.
         */
        requestID = nil

        /*
         *   The network status code.  This is an integer value indicating
         *   whether the request was successful or failed with an error.  A
         *   negative value is a low-level TADS error indicating that the
         *   request couldn't be sent to the server, or that a network error
         *   occurred receiving the reply:
         *
         *.    -1    - out of memory
         *.    -2    - couldn't connect to host
         *.    -3    - other network/socket error
         *.    -4    - invalid parameters
         *.    -5    - error reading the content data to send to the server
         *.    -6    - error saving the reply data received from the server
         *.    -7    - error retrieving reply headers
         *.    -8    - error starting background thread
         *.    -100  - other TADS/network error
         *
         *   A positive value means that the network transaction itself was
         *   successful, and reflects the status information returned by the
         *   network server that handled the request.  This must be interpreted
         *   according to the protocol used to send the request:
         *
         *   - For HTTP requests, the value is an HTTP status code.  A code in
         *   the 200 range generally indicates success, while other ranges
         *   generally indicate errors.
         */
        statusCode = nil

        /* the content body from the reply */
        replyBody = nil

        /*
         *   the HTTP headers from the reply, as a lookup table indexed by
         *   header name
         */
        replyHeaders = nil

        /* the HTTP status string (the first line of the headers) */
        httpStatusLine = nil

        /*
         *   the HTTP headers from the reply, in the raw text format - this is
         *   simply a string of all the headers, separated by CR-LF (\r\n)
         *   sequences
         */
        replyHeadersRaw = nil

        /*
         *   Redirect location, if applicable.  By default, this will be nil
         *   whether or not a redirection took place, because sendNetRequest()
         *   normally follows redirection links transparently, returning only
         *   the final result from the final server we're redirected to.
         *   However, you can override automatic redirection with an option
         *   flag (NetReqNoRedirect) when calling sendNetRequest().  When that
         *   option is selected, the function won't follow redirection links at
         *   all, but will instead simply return the redirect information as
         *   the result from the request.  When that happens, this property is
         *   set to a string giving the target of the redirect.  You can then
         *   follow the redirect manually, if desired, by sending a new request
         *   to the target given here.
         */
        redirectLoc = nil
    ;

    /*
     *   Network Reply Done event.  This type of event occurs when an
     *   asynchronous network reply (such as HTTPRequest.sendReplyAsync())
     *   completes.
     */
    class NetReplyDoneEvent: NetEvent
        /* construction */
        construct(t, req, err, msg)
        {
            inherited(t, req, err, msg);
            requestObj = req;
            socketErr = err;
            errMsg = msg;
        }

        /* our default event type is NetEvReplyDone */
        evType = NetEvReplyDone

        /*
         *   The object representing the request we replied to.  For HTTP
         *   requests, this is an HTTPRequest object.
         */
        requestObj = nil

        /* was the reply successfully sent? */
        isSuccessful() { return errMsg == nil; }

        /*
         *   The socket error, if any.  If the reply failed due to a network
         *   error, this contains the error number.  If no network error
         *   occurred, this is zero.
         */
        socketErr = 0

        /*
         *   Error message, if any.  If the reply failed, this contains a
         *   string with a description of the error that occurred.  If the
         *   reply was sent successfully, this is nil.
         */
        errMsg = nil
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   A FileUpload represents a file uploaded by a network client via a
     *   protocol server, such as an HTTPServer.
     *
     *   When your program is acting as a network server, a FileUpload object
     *   represents a file received from the client.  For example,
     *   HTTPRequest.getFormFields() returns a FileUpload object to represent
     *   each <INPUT TYPE=FILE> field in the posted form.
     *
     *   When your program acts as a network client (via sendNetRequest), you
     *   can create use FileUpload to post file attachments to posted forms.
     */
    class FileUpload: object
        construct(file, contentType, filename)
        {
            self.file = file;
            self.contentType = contentType;
            self.filename = filename;
        }

        /*
         *   The file data.
         *
         *   When you create the FileUpload object for use with
         *   sendNetRequest() to post form data, you must use a string or
         *   ByteArray value for this property.
         *
         *   When the FileUpload is created by HTTPRequest.getFormFields(),
         *   this property contains a File object with the uploaded content.
         *   This is open for read-only access.  If the contentType parameter
         *   is a text type ("text/html", "text/plain", etc), and the
         *   interpreter recognizes the character set parameter in the
         *   contentType, the file is in Text mode (FileModeText) with the
         *   appropriate character mapper in effect.  Otherwise, the file is in
         *   raw binary mode (FileModeRaw).  If you need the file to be opened
         *   in a different mode, you can use setFileMode() on the file to
         *   change the mode.
         */
        file = nil

        /*
         *   The content type.  This a string giving the MIME type specified by
         *   the client with the upload.  This is the full content-type string,
         *   including any attributes, such "charset" for a text type.  This
         *   can be nil if the client doesn't specify a content-type at all.
         *
         *   It's important to recognize that this information is supplied by
         *   the client, and is NOT validated by the protocol server.  At best
         *   you should consider it a suggestion, and at worst a malicious lie.
         *   The client could be innocently mistaken about the type, or could
         *   even be intentionally misrepresenting it.  You should always
         *   validate the actual contents, rather than relying on the client's
         *   description of the format; in particular, be careful not to assume
         *   that expected data fields are present, in the valid range, etc.
         */
        contentType = nil

        /*
         *   The client-side filename, if specified.  This is a string giving
         *   the name of the file on the client machine.  This generally has no
         *   particular meaning to the server, since we can't infer anything
         *   about the directory structure or naming conventions on an
         *   arbitrary client.  However, this might be useful for reference,
         *   such as showing information about the upload in a user interface.
         *   It's sometimes also marginally useful to know the suffix
         *   (extension) for making further guesses about the content type -
         *   although as with the content-type, you can't rely upon this, but
         *   can only use it as a suggestion from the client.
         *
         *   The client won't necessarily specify a filename at all, in which
         *   case this will be nil.
         */
        filename = nil
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   A NetException is the base class for network errors.
     */
    class NetException: Exception
        construct(msg?, errno?)
        {
            if (errMsg != nil)
                errMsg = 'Network error: <<msg>>';
            if (errno != nil)
                errMsg += ' (system error code <<errno>>)';
        }
        displayException() { "<<errMsg>>"; }

        /* a descriptive error message provided by the system */
        errMsg = 'Network error'
    ;

    /*
     *   A NetSafetyException is thrown when the program attempts to perform a
     *   network operation that isn't allowed by the current network safety
     *   level settings.  The user controls the safety level; the program can't
     *   override this.
     */
    class NetSafetyException: NetException
        errMsg = 'Network operation prohibited by user-specified '
                 + 'network safety level'
    ;

    /*
     *   A SocketDisconnectException is thrown when attempting to read or write
     *   a network socket that's been closed, either by us or by the peer (the
     *   computer on the other end of the network connection).  If we didn't
     *   close the socket on this side, this error usually means simply that
     *   the peer program has terminated or otherwise disconnected, so we
     *   should consider the conversation terminated.
     */
    class SocketDisconnectException: NetException
        errMsg = 'Network socket disconnected by peer or closed'
    ;

    /* export the objects and properties used in the tads-net function set */
    export NetEvent 'TadsNet.NetEvent';
    export NetRequestEvent 'TadsNet.NetRequestEvent';
    export NetTimeoutEvent 'TadsNet.NetTimeoutEvent';
    export NetReplyEvent 'TadsNet.NetReplyEvent';
    export NetReplyDoneEvent 'TadsNet.NetReplyDoneEvent';
    export NetException 'TadsNet.NetException';
    export SocketDisconnectException 'TadsNet.SocketDisconnectException';
    export NetSafetyException 'TadsNet.NetSafetyException';
    export FileUpload 'TadsNet.FileUpload';
    export file 'TadsNet.FileUpload.file';
    export contentType 'TadsNet.FileUpload.contentType';
    export filename 'TadsNet.FileUpload.filename';

TADS 3 Library Manual
Generated on 8/21/2012 from TADS version 3.1.2