httpreq.h | documentation |
#charset "us-ascii" #pragma once /* * 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. */ getServer(); /* * 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. */ getVerb(); /* * 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 * string. */ getQuery(); /* * Parse the query string. This returns a LookupTable containing the * parsed elements of the query. The element table[1] 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: * *. http://www.tads.org:1234/path/resource?a=one&b=two&c=three&d * * we'd generate this table: * *. table[1] = '/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). */ parseQuery(); /* * 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. */ getQueryParam(name); /* * 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 * 'application/x-www-form-urlencoded'. * * The element table[1] 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. */ getHeaders(); /* * 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. */ getCookie(name); /* * 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. */ getCookies(); /* * 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 * browser. * * 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. */ getFormFields(); /* * 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. */ getBody(); /* * 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. */ getClientAddress(); /* * 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 *. httponly - hide cookie from Javascript *. 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. */ setCookie(name, value); /* * 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. */ sendReplyChunk(body); /* * 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(). */ endChunkedReply(headers?); /* * 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
Generated on 5/16/2013 from TADS version 3.1.3