* Copyright 2010 Michael J. Roberts.
* This file is part of TADS 3.
* This module defines the HTTPRequest intrinsic class. This class
* represents a request sent from a network client to the byte-code program
* via the program's HTTP server object.
* See the HTTPServer object for details on setting up an HTTP server
* within the program.
* HTTPRequest objects are not created with 'new'. Instead, the system
* creates them when HTTP requests arrive, and returns them to the
* byte-code program via the netEvent() built-in function.
* HTTP Request object. This object represents an HTTP protocol request
* from a client to one of our servers. HTTPRequest objects are created by
* the HTTPServer object as requests arrive, and are passed to the byte
* code program as network events, via the getNetEvent() function. The
* program uses the object to get information on the request and to send
* back the reply.
intrinsic class HTTPRequest 'http-request/030001': Object
* Get the HTTPServer object. This is the server that received the
* network request that 'self' represents.
* Get the verb. This is the HTTP verb that the client sent with the
* request to indicate what action to perform. The standard HTTP verbs
* are GET (this is the most common request type; it simply retrieves a
* resource from the server), POST (used to submit form data), OPTIONS,
* HEAD, PUT, DELETE, TRACE, CONNECT, and PATCH. If the client sends a
* non-standard verb, the system simply passes it through to the
* request, allowing you to write a custom server for a custom client;
* however, this isn't recommended, since proxies and firewalls often
* block what they consider ill-formed requests.
* Get the query string. This is the portion of the URL after the
* server address. For example, if the client web browser navigated to
* the URL "http://www.tads.org:1234/path/resource?a=1&b=2" would
* return "/path/resource?a=1&b=2".
* This is the raw query string, exactly as the client sent it. Any
* '%xx' character sequences are still present in this version of the
* Parse the query string. This returns a LookupTable containing the
* parsed elements of the query. The element table is the base
* resource name: this is the part of the query string up to the first
* '?', if any, or the entire query string if there are no parameters.
* If there are any parameters, they're entered in the table under the
* parameter names as keys. For example, for this query string:
* we'd generate this table:
*. table = '/path/resource'
*. table['a'] = 'one'
*. table['b'] = 'two'
*. table['c'] = 'three'
*. table['d'] = ''
* For all of the strings (including the base resource name, the
* parameter names, and the parameter values), any '%xx' sequences are
* converted into the corresponding character values. For example, if
* the base resource name is '/C%C3%A1fe', the resource name will be
* parsed to 'Cfe' (%C3%A1 is the percent-encoded representation of
* the UTF-8 character small A with acute accent).
* Get a parameter from the query string. This parses the query string
* and returns the parameter with the specified name, if present, or
* nil if not. This does the same parsing as parseQuery(), but it
* returns just the specified parameter value rather than building a
* lookup table with all of the parameters. This is more efficient
* than parseQuery() if you're just looking up one or two parameters,
* since it avoids building the whole table, but it's less efficient if
* you're looking up many parameters because this routine has to
* re-parse the query string on each call.
* Get the headers sent with the request by the client. This returns a
* LookupTable object: the keys are the header names, and the values
* are the corresponding header values. For example, for a POST, there
* might be a 'Content-type' key with the corresponding value
* The element table contains the HTTP request line - the first line
* of the request, which isn't technically a header. This line
* contains the verb, the query string, and (optionally) the HTTP
* version string (in that order, with spaces separating the elements).
* The method includes this so that you can inspect the unparsed
* request line, if desired.
* Look up a cookie. This looks for the given cookie name in the
* cookies sent by the client, and returns a string containing the
* cookie's text if found. If the cookie isn't found, returns nil.
* Get the cookies sent with the request by the client. This returns a
* LookupTable of the cookies, with each key set to a cookie name and
* the corresponding value set to the cookie's text. Cookies are
* assumed to contain only plan ASCII characters; any 8-bit characters
* in a cookie's name or value will be replaced by '?' characters.
* Get the form data-entry field values. This returns a LookupTable
* containing the field values sent with the request. Each key in the
* table is a field name (given by the NAME property of the HTML
* <INPUT> tag for the field), and each corresponding value is a string
* giving the value of the field as entered by the user.
* If the form includes uploaded files (via <INPUT TYPE=FILE>), the
* value of each file field will be a FileUpload object instead of a
* string. The 'file' property of this object contains a File object,
* opened in read-only mode, that can be used to retrieve the contents
* of the uploaded file. Other properties of object provide the
* client-side name of the file and the MIME type specified by the
* This method is a convenience function that parses the message body
* information. You can obtain the raw information on the posted data
* via the getHeader() method.
* This method recognizes form data as a message body with content-type
* application/x-www-form-urlencoded or multipart/form-data. If the
* request doesn't have a message body at all, or has a message body
* with a different content-type, this method assumes that the request
* has no posted form data and returns nil. We return nil rather than
* an empty lookup table so that the caller can tell that this doesn't
* appear to be a form submission request at all.
* Get the body of the request, if any. Some types of HTTP requests,
* such as POST and PUT, contain a message body. This returns the raw,
* unparsed message body. Returns a File object, open with read-only
* access. If there's no message body at all, this returns nil.
* If the body is a text type, the file will be open in text mode, with
* the character mapping set according to the content type passed from
* the client; otherwise it's open in raw binary mode.
* The message body retrieved here is the raw request payload, without
* any processing. There are two standard structured body types that
* are frequently used with POSTs, both of which require further
* parsing. First is MIME type application/x-www-form-urlencoded,
* which is used to represent a basic HTML form, and encodes the data
* entry fields in a form; this type can be parsed via the
* getFormFields() method. Second is multipart/form-data, which is
* used to POST forms that include uploaded files; this can be parsed
* with getFormFields() to retrieve the data fields, and getUploads()
* to get the uploaded file data.
* Get the network address of the client. This returns a list:
* ['ip-address', port], where 'ip-address' is a string with the IP
* address of the client, in decimal notation ('192.168.1.15', for
* example), and 'port' is an integer giving the network port number on
* the client side.
* Set a cookie in the reply. This sets a cookie with the given name
* and value, both given as strings. The value string starts with the
* text to set for the cookie, and can be followed by additional
* parameters, delimited by semicolons:
*. expires=Fri, 31-Dec-2010 23:59:59 GMT - set expiration date
*. domain=.tads.org - scope cookie to domain
*. path=/ - scope cookie within site
*. secure - only send via https://
* (These are all defined by the HTTP protocol, not by TADS. For
* details, refer to any HTTP reference book or web site.)
* A cookie without an expiration date is a session cookie: it
* implicitly expires as soon as the browser application terminates.
* The presence of an expiration date makes the cookie persistent,
* meaning it's to be stored on disk until the given expiration date
* and should survive even after the browser is closed.
* To send a cookie, you must call this BEFORE sending the reply or
* starting to send a chunked reply. This is a limitation of the
* protocol, since the cookies must be sent at the start of the reply
* with the headers. Calling this routine doesn't actually send
* anything immediately to the client, but simply stores the cookie
* with the pending request, to be sent with the reply.
* Send the reply to the request.
* 'body' is the content of the reply. For example, for a request for
* an HTML page, this would contain the HTML text of the page. This
* can be a string or StringBuffer, in which case the string is sent as
* UTF-8 data; a ByteArray, in which case the raw bytes of the byte
* array are sent exactly as given; an integer, in which case the body
* will be a default HTML page automatically generated for the
* corresponding HTML status code; a File object opened with read
* access, in which case the contents of the file will be sent; or nil,
* in which case no message body is sent at all (just the status code
* and the headers). A file open in text mode will be sent as text,
* and raw mode will be sent as binary. "Data" mode isn't allowed.
* Note that if a file is used, this will send the entire contents of
* the file, regardless of the current seek position, and the routine
* will have the side effect of setting the file's seek position to end
* of file upon return.
* 'contentType' is an optional string giving the MIME type of the
* reply body. This is used to generate a Content-type header in the
* reply. If this is omitted, the server generates a default MIME type
* according to the type of the 'body' argument. If 'body' is given as
* a string, and the string starts with <HTML (ignoring any initial
* whitespace), the default content type is "text/html"; if the string
* starts with <?XML, the default type is "text/xml"; otherwise the
* default type is "text/plain". If 'body' is a ByteArray, the server
* checks the first few bytes for signature strings for a few known
* types (JPEG, PNG, MP3, Ogg, MIDI), and uses the corresponding MIME
* type if matched; otherwise the MIME type is set to
* "application/octet-stream". 'contentType' is ignored when the
* 'body' argument is an integer (because in this case the body is
* known to be HTML), or hwen it's nil (since there's no body at all in
* this case).
* When the 'body' is a string, the server automatically adds the
* "charset" parameter to the Content-type header with value "utf-8".
* 'status' is an optional HTTP status code. This can be a string, in
* which case it must have the standard format for an HTTP status code,
* which consists of a decimal status code number followed by the text
* description of the code: for example, "404 Not Found. This argument
* can alternatively be a simple integer giving one of the standard
* HTTP status code numbers, in which case we'll automatically add the
* corresponding status code text. If this argument is omitted, AND
* the 'body' is given as an integer, the 'body' value will be used as
* the status code. If this is omitted and there's a non-integer
* 'body' argument, we use "200 OK" as the default status code.
* 'headers' is an optional list of header strings. Each element of
* the list is a string giving one header, in the standard format
* "Name: Value". If this is omitted, the reply will contain only the
* standard headers synthesized by the server.
* The server automatically generates certain headers with each reply.
* These should not be specified in the 'headers' argument. The
* standard headers are:
*. Content-type: per the 'contentType' argument
*. Content-length: length in bytes of 'body'
* After sending the reply, the request is completed, and no further
* reply can be sent.
sendReply(body, contentType?, status?, headers?);
* Start a chunked reply. This sends the initial headers of a reply
* that will be generated in pieces. This is an alternative to sending
* the entire reply as a single string via sendReply(), for situations
* where you're generating the reply algorithmically and want to send a
* little bit at a time, rather than buffering up the entire reply in a
* string or StringBuffer to send all at once. This is particularly
* useful for cases where the reply will take a while to generate,
* since it allows the client to interpret partial data before the
* entire reply is completed; and for very large reply bodies, where it
* would consume a lot of memory to buffer the whole reply.
* This routine doesn't send any initial data, but simply begins the
* reply process. For this reason, the content type must be specified:
* there's no way for the routine to infer the content type here since
* we don't have any body data to look at. The content type, result
* code, and headers arguments work as they do with sendReply().
* After calling this routine, call sendReplyChunk() as many times as
* needed to send the pieces of the reply. After sending all of the
* pieces, call endChunkedReply() to finish the reply.
startChunkedReply(contentType, resultCode?, headers?);
* Send a piece of a chunked reply. This can be called any number of
* times after calling startChunkedReply() to send the pieces of a
* chunked reply.
* 'body' can be a string or StringBuffer, in which case the string
* data are sent as UTF-8 text; or a ByteArray, in which case the raw
* bytes are sent.
* Finish a chunked reply. This completes a chunked reply started with
* startChunkedReply(). After calling this routine, the request is
* completed, and no further reply can be sent.
* 'headers' is an optional list of additional headers to send with the
* end of the reply. The HTTP chunked reply mechanism allows a server
* to send headers at the beginning of the reply (which corresponds to
* the startChunkedReply() call), at the end of the reply (in this
* routine), or both. This accommodates situations where the server
* might not be able to determine the final value for a header until
* after generating full reply. If this argument is included, it works
* just like the 'headers' argument to sendReply().
* Send the reply to the request asynchronously. This works like
* sendReply(), except that this method starts a new background thread
* to handle the data transfer and then immediately returns. This
* allows the caller to continue servicing other requests while the
* data transfer proceeds, which is important when sending a large file
* (such as a large image or audio file) as the reply body. Most
* browsers allow the user to continue interacting with the displayed
* page while images and audio files are transfered in the background,
* so it's likely that the browser will generate new requests during
* the time it takes to send a single large reply body. When you use
* sendReply(), the server won't be able to service any of those new
* requests until the reply is fully sent, so the browser will appear
* unresponsive for the duration of the reply data transfer. Using
* sendReplyAsync() allows you to service new requests immediately,
* without waiting for the data transfer to complete.
* The parameters are the same as for sendReply(). If 'body' is a
* File, this function opens its own separate handle to the file, so
* you're free to close the File object immediately, or to continue to
* use it for other operations. Note that if you continue writing to
* the file after calling this method, it's unpredictable whether the
* reply data will contain the original or updated data (or a mix of
* new and old data), since the reply data transfer is handled in a
* separate thread that runs in parallel with the main program.
* When the reply data transfer is completed, or if it fails, the
* system posts a NetEvent of type NetEvReplyDone to the network
* message queue. The event contains the original HTTPRequest object,
* to allow you to relate the event back to the request that generated
* the reply, and status information indicating whether or not the
* transfer was successful.
sendReplyAsync(body, contentType?, status?, headers?);
TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3