> ## 4. HTTP Server

> An HTTP (Hypertext Transfer Protocol) server makes HTML (Hypertext Markup Language) documents and other documents available to clients, i.e., web browsers. HTTP is implemented by `HTTP.LIB`.

> ### 4.1 HTTP Server Data Structures

> There are four data structures in `HTTP.LIB` of interest to developers of HTTP servers.

> #### 4.1.1 HttpSpec

> The data structure `HttpSpec` contains all the files, variables, and functions the Web server has access to. The structure `ServerSpec` from `ZSERVER.LIB` may be instead.

>     typedef struct {
>        word type;
>        char name[HTTP_MAXNAME];
>        long data;
>        void* addr;
>        word vartype;
>        char* format;
>        HttpRealm* realm;
>     } HttpSpec;

> #### 4.1.1.1 HttpSpec fields

> #### type

> This field tells the server if the entry is a file, variable or function (`HTTPSPEC_FILE`, `HTTPSPEC_VARIABLE` or `HTTPSPEC_FUNCTION`, respectively).

> #### name

> This field specifies a unique name for referring to the entry. The Web server recognizes "`/index.html`" as the entity that matches "`http://someurl.com/index.html`", and delivers the entry's content based on the value of `type` (the first field).

> #### data

> The third field is the physical address of the entity.

> #### addr

> The fourth field is a short pointer to the entity. Either the third field or the fourth field is valid, not both. All files must use the physical address, variables and functions use the short pointer.

> #### vartype

> This field describes the type of variable. Supported types are : `INT8` `INT16`, `PTR16`, `INT32`, and `FLOAT32`.

> #### format

> The format field describes the `printf` format specifier used to display the variable.

> #### realm

> This field is the name and password required to access the entity.

> #### 4.1.2 HttpType

> The structure `HttpType` associates a file extension with a MIME type (Multipurpose Internet Mail Extension) and a function which handles the MIME type. Users can override `HTTP_MAXNAME` (which defaults to 20 characters) in their source file`.` If the function pointer given is `NULL`, then the default handler (which sends the content verbatim) is used.

>     typedef struct {
>         char extension[10];
>         char type[HTTP_MAXNAME];
>         int  (*fptr)(/* HttpState* */);
>     } HttpType;

> #### 4.1.3 HttpRealm

> The structure `HttpRealm` holds user-ID and password pairs for partitions called realms. These realms allow the protected resources on a server to be partitioned into a set of protection spaces, each with its own authentication scheme and/or authorization database`.`

>     typedef struct {
>         char username[HTTP_MAXNAME];
>         char password[HTTP_MAXNAME];
>         char realm[HTTP_MAXNAME];
>     } HttpRealm;

> HTTP/1.0 Basic authentication is used. This scheme is not a secure method of user authentication across an insecure network (e.g., the Internet). HTTP/1.0 does not, however, prevent additional authentication schemes and encryption mechanisms from being employed to increase security.

> In the `HttpSpec` structure, there is a pointer to a structure of type `HttpRealm`. To password-protect the entity, add the name, password, and realm desired. If you do not want to password-protect the entity, leave the realm pointer in the `HttpSpec` structure `NULL`.

> #### 4.1.4 HttpState

> Use of this structure is necessary for CGI functions. Some of the fields are off-limits to developers. The field that are available for use are described in Section 4.1.4.1 HttpState Fields.

>     typedef struct {
>        tcp_Socket s;

>        /* State information */
>        int state, substate, subsubstate, nextstate, laststate;

>        /* File referenced */
>        HttpSpecAll spec, subspec;
>        HttpType *type;
>        int (*handler)(), (*exec)();

>        /* rx/tx state variables */
>        long offset;
>        long length;
>        long filelength, subfilelength;
>        long pos, subpos;
>        long timeout, main_timeout;
>        char buffer[HTTP_MAXBUFFER];
>        char *p;

>        /* http request and header info */
>        char method;
>        char url[HTTP_MAXURL];
>        char version;
>        char connection;
>        char content_type[40];
>        long content_length;
>        char has_form;
>        char finish_form;
>        char abort_notify;
>        char cancel;
>        char username[HTTP_MAXNAME];
>        char password[HTTP_MAXNAME];
>        char cookie[HTTP_MAXNAME];
>        int headerlen;
>        int headeroff;

>        /* other - don't touch */
>        char tag[HTTP_MAXNAME];
>        char value[HTTP_MAXNAME];

>        /* Support for conditional SSI (error feedback etc) */
>        int cond[HTTP_MAX_COND];     /* Condition numbers (default 0) */

>        /*  Optional User Data. Cleared on every new connection. */
>     #ifdef HTTP_USERDATA_SIZE
>        char userdata[ HTTP_USERDATA_SIZE];
>     #endif
>     } HttpState;

> #### 4.1.4.1 HttpState Fields

> The fields discussed here are available for developers to use in their application programs.

> #### s

> This is the socket associated with the given HTTP server. A developer can use this in a CGI function to output dynamic data. Any of the TCP functions can be used.

> #### substate
> subsubstate

> These are intended to be used to hold the current state of a state machine for a CGI function. That is, if a CGI function relinquishes control back to the HTTP server, then the values in these variables will be preserved for the next `http_handler()` call, in which the CGI function will be called again. These variables are initialized to 0 before the CGI function is called for the first time. Hence, the first state of a state machine using substate should be 0.

> #### timeout

> This value can be used by the CGI function to implement an internal timeout.

> #### main_timeout

> This value holds the timeout that is used by the web server. The web server checks against this timeout on every call of `http_handler()`. When the web server changes states, it resets `main_timeout`. When it has stayed in one state for too long, it cancels the current processing for the server and goes back to the initial state. Hence, a CGI function may want to reset this timeout if it needs more processing time (but care should be taken to make sure that the server is not locked up forever). This can be achieved like this:
> state->main_timeout = set_timeout(HTTP_TIMEOUT);
> `HTTP_TIMEOUT` is the number of seconds until the web server will time out. It is 16 seconds by default.

> #### buffer[]

> A buffer that the developer can use to put data to be transmitted over the socket. It is of size `HTTP_MAXBUFFER`.

> #### p

> Pointer into the buffer given above.

> #### method

> This should be treated as read-only. It holds the method by which the web request was submitted. The value is either `HTTP_METHOD_GET` or `HTTP_METHOD_POST`, for the GET and POST request methods, respectively.

> #### url[]

> This should be treated as read-only. It holds the URL by which the current web request was submitted. . If there is GET-style form information, then that information will follow the first `NULL` byte in the url array. The form information will itself be `NULL`-terminated. If the information in the url array is truncated to `HTTP_MAXURL` bytes, the truncated information is also `NULL`-terminated.

> #### version

> This should be treated as read-only. This holds the version of the HTTP request that was made. It can be `HTTP_VER_09`, `HTTP_VER_10`, or `HTTP_VER_11` for 0.9, 1.0, or 1.1 requests, respectively.

> #### content_type[]

> This should be treated as read-only. This buffer holds the value from the Content-Type header sent by the client.

> #### content_length

> This should be treated as read-only. This variable holds the length of the content sent by the client. It matches the value of the Content-Length header sent by the client.

> #### has_form

> This should be treated as read-only. If the value is 1 there is a GET style form, after the \0 byte in `url[]`.

> #### abort_notify

> Set to `!0` in user-defined formprolog function to indicate that the formepilog function needs to be called on an abort condition. If the epilog function is reached normally, this field must be set to zero. This prevents the formepilog function from being called one more time on a connection abort.

> #### cancel

> This should be treated as read-only. It is intended for when the user-defined functions, which may be called before and after an HTML form is submitted, are used for locking resources.

> If the formprolog function was called and then the connection is aborted before the formepilog function can be called, `cancel` is set to 1 and the formepilog function is called exactly once. If the epilog function was already called but returned zero (not finished yet), then it is called again if the connection is aborted, except if `cgi_redirectto()` has been called from the epilog function. In that case the epilog function is not called after an abort.

> #### username[]

> Read-only buffer has username of the user making the request, if authentication took place.

> #### password[]

> Read-only buffer has password of the user making the request, if authentication took place.

> #### cookie[]

> Read-only buffer contains the value of the cookie "DCRABBIT" (see `http_setcookie()` for more information).

> #### headerlen
> headeroff

> These variables can be used together to cause the web server to flush data from the `buffer[]` array in the `HttpState` structure. `headerlen` should be set to the amount of data in `buffer[]`, and `headeroff` should be set to 0 (to indicate the offset into the array). The next time the CGI function is called the data in `buffer[]` will be flushed to the socket.

> #### cond[]

> Support for conditional SSI (error feedback etc).

> #### userdata[]

> This field is included if `HTTP_USERDATA_SIZE` is defined. It is an optional user data area. The area is cleared to zero when the structure is initialized, otherwise it is not touched. Its size must be greater than zero.

> ### 4.2 Configuration Macros

> The following macros are available in `HTTP.LIB`:

> #### HTTP_MAX_COND

> Support for conditional SSI (error feedback etc). It defaults to 4.

> #### HTTP_MAXNAME

> This is the maximum length for a name in the `HttpSpec` structure. This defaults to 20 characters. Without overriding this value, the maximum length of any name is 19 characters because one character is used for the `NULL` termination.

> #### HTTP_MAXRAMSPEC

> This is the maximum number of `HttpSpec` entries that can be added at runtime. This macro overrides `SSPEC_MAXSPEC`.

> #### HTTP_MAXSERVERS

> This is the maximum number of HTTP servers listening on port 80. The default is two. You may increase this value to the maximum number of independent entities on your page. For example, for a Web page with four pictures, two of which are the same, set `HTTP_MAXSERVERS` to four: one for the page, one for the duplicate images, and one for each of the other two images. By default, each server takes 2500 bytes of RAM. This RAM usage can be changed by the macro `SOCK_BUF_SIZE` (or `tcp_MaxBufSize` which is deprecated as of Dynamic C ver. 6.57). Another option is to use the `tcp_reserveport()` function and a smaller number of sockets.

> #### HTTP_PORT

> This macro allows the user to override the default port. Define it before the line `#use http.lib``.`

> #### HTTP_USERDATA_SIZE

> Defining this macro causes "char userdata[]" to be added to the HttpState structure. Define your structure before `#use http.lib`

>     struct UserStateData { char name[50]; int floor; int model; };
>     #define HTTP_USERDATA_SIZE (sizeof(struct UserStateData))
>     #use http.lib

> In your own code, access it like:

>     mystate = (struct UserStateData *) state->userdata;

> #### TIMEZONE

> This macro specifies the distance in hours you are from Coordinated Universal Time (UTC), which is 5 hours ahead of Eastern Standard Time (EST). The default `TIMEZONE` is -8, which represents Pacific Standard Time. You can use the `tm_wr()`function to set the clock to the correct value. If you lose power and don't have the battery-backup option, the time will need to be reset.

> #### 4.2.1 Customizing HTTP headers

> The callback macro, `HTTP_CUSTOM_HEADERS`, will be called whenever HTTP headers are being sent. To be used, it must be defined as a function with the following prototype:

>     void my_headers(HttpState* state, char* buffer, int bytes);

> #### state

> Pointer to the state structure for the calling web server.

> #### buffer

> The buffer in which the header(s) can be written.

> #### bytes

> The number of bytes available in the buffer.

> Typically, the macro would be defined by the user before `http.lib` is used, like in the following:

>     #define HTTP_CUSTOM_HEADERS(state, buffer, bytes) \
>     my_headers(state, buffer, bytes)

> Then, for the above to work, the `my_headers()` function must be defined by the user, like the following:

>     void my_headers(HttpState* state, char* buffer, int bytes)
>     {
>        strcpy(buffer, "Fake-Header: Hello Z-World!\r\n");
>        printf("bytes: %d\n", bytes);
>     }

> Of course, in the real world, the user may need to check the number of bytes available to be sure they don't overwrite the buffer. The buffer must end with "\r\n", and be `NULL``-terminated.`

> ### 4.3 Sample Programs

> Sample programs demonstrating HTTP are in the `\Samples\Tcpip\Http` directory. There is a configuration block at the beginning of each sample program. Unless you are using BOOTP/DHCP, the macros in this block need to be changed to reflect your network settings. For most HTTP programs, you will be concerned with `TIMEZONE` and the IP address macros: `MY_IPADDRESS`, `MY_NETMASK`, `MY_GATEWAY`.

> #### 4.3.1 Serving Static Web Pages

> The sample program, `Static.c`, initializes `HTTP.LIB` and then sets up a basic static web page. It is assumed you are on the same subnet as the controller. The code for `Static.c` is explained in the following pages.

> From Dynamic C, compile and run the program. You will see the LNK light on the board come on after a couple of seconds. Point your internet browser at the controller (e.g., http://10.10.6.100/). The ACT light will flash a couple of times and your browser will display the page.

> #### Program Name: Static.c

>     #define MY_IP_ADDRESS   "10.10.6.100"
>     #define MY_NETMASK      "255.255.255.0"
>     #define TIMEZONE        -8

>     #memmap xmem
>     #use "dcrtcp.lib"
>     #use "http.lib"

>     #ximport "samples/tcpip/http/pages/static.html" index_html
>     #ximport "samples/tcpip/http/pages/rabbit1.gif" rabbit1_gif

>     const HttpType http_types[] =
>     {
>        { ".html", "text/html", NULL},
>        { ".gif", "image/gif", NULL}
>     };
>     const HttpSpec http_flashspec[] =
>     {
>        {HTTPSPEC_FILE, "/", index_html, NULL, 0, NULL, NULL},
>        {HTTPSPEC_FILE, "/index.html", index_html, NULL, 0, NULL, NULL},
>        {HTTPSPEC_FILE, "/rabbit1.gif", rabbit1_gif, NULL, 0, NULL, NULL},
>     };
>     main()
>     {
>        sock_init(); // Initializes the TCP/IP stack
>        http_init(); // Initializes the web server

>        tcp_reserveport(80);
>        while (1) {
>           http_handler();
>        }
>     }

> This program serves the `static.html` file and the `rabbit1.gif` file to any user contacting the controller. If you want to change the file that is served by the controller, modify this line in `Static.c`:

>     #ximport "samples/tcpip/http/pages/static.html" index_html

> #### 4.3.1.1 Adding Files to Display

> Adding additional files to the controller to serve as web pages is slightly more complicated. First, add an `#ximport` line with the filename as the first parameter, and a symbol that references it in Dynamic C as the second parameter.

>     #ximport "samples/tcpip/http/pages/static.html" index_html
>     #ximport "samples/tcpip/http/pages/newfile.html" newfile_html

> Next, find these lines in `Static.c`:

>     HttpSpec http_flashspec[] =
>     {
>        {HTTPSPEC_FILE, "/", index_html, NULL, 0, NULL, NULL},
>        {HTTPSPEC_FILE, "/index.html", index_html,NULL,0,NULL, NULL},
>        {HTTPSPEC_FILE, "/newfile.html", index_html, NULL,0, NULL, NULL},
>        {HTTPSPEC_FILE, "/rabbit1.gif", rabbit1_gif, NULL,0, NULL, NULL},
>     };

> Insert the name of your new file, preceded by "/", into this structure, using the same format as the other lines. Compile and run the program. Open up your browser to the new page (e.g. "http://10.10.6.100/newfile.html"), and your new page will be displayed by the browser.

> #### 4.3.1.2 Adding Files with Different Extensions

> If you are adding a file with an extension that is not html or gif, you will need to make an entry in the `HttpType` structure for the new extension. The first field is the extension and the second field describes the MIME type for that extension. You can find a list of MIME types at:

>     ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/media-types

> In the media-types document located there, the text in the type column would precede the "/", and the subtype column would directly follow. Find the type subtype entry that matches your extension and add it to the `http_types` table.

>     HttpType http_types[] =
>     {
>        { ".html", "text/html", NULL},
>        { ".gif", "image/gif", NULL}
>     };

> #### 4.3.1.3 Handling of Files With No Extension

> The entry "/" and files without an extension are dealt with by the handler specified in the first entry in `http_types[]`.

> #### 4.3.2 Dynamic Web Pages Without HTML Forms

> Serving a dynamic web page without the use of HTML forms is done by sample program `ssi.c`. This program displays four 'lights' and four buttons to toggle them. Users can browse to the device and change the status of the lights.

> #### Program Name: samples/tcpip/http/ssi.c

>     #define MY_GATEWAY "10.10.6.19"
>     #define MY_IP_ADDRESS   "10.10.6.100"
>     #define MY_NETMASK      "255.255.255.0"

>     #define SOCK_BUF_SIZE 2048
>     #define HTTP_MAXSERVERS 1
>     #define MAX_SOCKETS 1

>     #define REDIRECTHOST MY_IP_ADDRESS
>     #define REDIRECTTO "http: //" REDIRECTHOST "/index.shtml"

>     #memmap xmem
>     #use "dcrtcp.lib"
>     #use "http.lib"

>     /*
>      * The source code for this program is ximported. This allows
>      * us to put the line <!--#include file="ssi.c" --> in the
>      * file Samples/Tcpip/Http/Pages/Showsrc.shtml.
>      */

>     #ximport "samples/tcpip/http/pages/ssi.shtml" index_html
>     #ximport "samples/tcpip/http/pages/rabbit1.gif" rabbit1_gif
>     #ximport "samples/tcpip/http/pages/ledon.gif" ledon_gif
>     #ximport "samples/tcpip/http/pages/ledoff.gif" ledoff_gif
>     #ximport "samples/tcpip/http/pages/button.gif" button_gif
>     #ximport "samples/tcpip/http/pages/showsrc.shtml" showsrc_shtml
>     #ximport "samples/tcpip/http/ssi.c" ssi_c

>     /*
>      *  In this case the extension .shtml is the first type in
>      *  the type table. This causes the default (no extension)
>      *  to assume the shtml_handler.
>      */

>     const HttpType http_types[] = {
>        { ".shtml", "text/html", shtml_handler}, // ssi
>        { ".html", "text/html", NULL},           // html
>        { ".cgi", "", NULL},                     // cgi
>        { ".gif", "image/gif", NULL}
>     };
>     char led1[15];
>     char led2[15];
>     char led3[15];
>     char led4[15];

> #### Program Name: ssi.c (continued)

>     int led1toggle(HttpState* state)
>     {
>        if (strcmp(led1,"ledon.gif")==0)
>           strcpy(led1,"ledoff.gif");
>        else
>           strcpy(led1,"ledon.gif");
>        cgi_redirectto(state,REDIRECTTO);
>        return 0;
>     }
>     int led2toggle(HttpState* state)
>     {
>        if (strcmp(led2,"ledon.gif")==0)
>           strcpy(led2,"ledoff.gif");
>        else
>           strcpy(led2,"ledon.gif");
>        cgi_redirectto(state,REDIRECTTO);
>        return 0;
>     }
>     int led3toggle(HttpState* state)
>     {
>        if (strcmp(led3,"ledon.gif")==0)
>           strcpy(led3,"ledoff.gif");
>        else
>           strcpy(led3,"ledon.gif");
>        cgi_redirectto(state,REDIRECTTO);
>        return 0;
>     }
>     int led4toggle(HttpState* state)
>     {
>        if (strcmp(led4,"ledon.gif")==0)
>           strcpy(led4,"ledoff.gif");
>        else
>           strcpy(led4,"ledon.gif");
>        cgi_redirectto(state,REDIRECTTO);
>        return 0;
>     }

> #### Program Name: ssi.c (continued)

>     const HttpSpec http_flashspec[] = {

>       {HTTPSPEC_FILE, "/", index_html, NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE, "/index.shtml", index_html, NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE,"/showsrc.shtml", showsrc_shtml, NULL,0,NULL, NULL},
>       {HTTPSPEC_FILE,"/rabbit1.gif", rabbit1_gif, NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE, "/ledon.gif",ledon_gif, NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE,"/ledoff.gif",ledoff_gif, NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE,"/button.gif",button_gif,NULL, 0, NULL, NULL},
>       {HTTPSPEC_FILE,     "ssi.c", ssi_c,  NULL, 0, NULL, NULL},
>       {HTTPSPEC_VARIABLE, "led1",  0,    led1, PTR16,   "%s", NULL},
>       {HTTPSPEC_VARIABLE, "led2",  0,    led2, PTR16,   "%s", NULL},
>       {HTTPSPEC_VARIABLE, "led3",  0,    led3, PTR16,   "%s", NULL},
>       {HTTPSPEC_VARIABLE, "led4",  0,    led4, PTR16,   "%s", NULL},
>       {HTTPSPEC_FUNCTION, "/led1tog.cgi", 0, led1toggle, 0, NULL, NULL},
>       {HTTPSPEC_FUNCTION, "/led2tog.cgi", 0, led2toggle, 0, NULL, NULL},
>       {HTTPSPEC_FUNCTION, "/led3tog.cgi", 0, led3toggle, 0, NULL, NULL},
>       {HTTPSPEC_FUNCTION, "/led4tog.cgi", 0, led4toggle, 0, NULL, NULL},
>     };

>     main()
>     {
>        strcpy(led1,"ledon.gif");
>        strcpy(led2,"ledon.gif");
>        strcpy(led3,"ledoff.gif");
>        strcpy(led4,"ledon.gif");

>        sock_init();
>        http_init();
>        tcp_reserveport(80);

>        while (1) {
>           http_handler();
>        }
>     }

> When you compile and run `ssi.c`, you see the LNK light on the board come on. Point your browser at the controller (e.g., http://10.10.6.100/). The ACT light will flash a couple of times and your browser will display the page.

> This program displays pictures of LEDs. Their state is toggled by pressing the image of a BUTTON. This program uses Server Side Includes (SSI) and the Common Gateway Interface (CGI).

> #### 4.3.2.1 SSI Feature

> SSI commands are an extension of the HTML comment command (<!--This is a comment -->). They allow dynamic changes to HTML files and are resolved at the server side, so the client never sees them. HTML files that need to be parsed because they contain SSI commands, are recognized by the HTTP server by the file extension shtml.

> The supported SSI commands are:

> - `#echo var`
> - `#exec cmd`
> - `#include file`

> They are used by inserting the command into an HTML file:

>     <!--#include file="anyfile" -->

> The server replaces the command, `#include file`, with the contents of `anyfile`.

> `#exec cmd` executes a command and replaces the SSI command with the output.

> ### Dynamically changing a variable on a web page

> The `S` `si.shtml` file, located in the `/``Samples/Tcpip/Http/Pages` folder, gives an example of dynamically changing a variable on a web page using `#echo var`.

>     <img SRC="<!--#echo var="led1" -->">

> In an shtml file, the "`<!--#echo var="led1" -->`" is replaced by the value of the variable `led1` from the `http_flashspec` structure.

>     HttpSpec http_flashspec[] =
>     {
>        //...
>        { HTTPSPEC_VARIABLE, "led1", 0, led1, PTR16, "%s", NULL}
>        //...
>     };

> `shtml_handler` looks up `led1` and replaces it with the text output from:

>     printf("%s",(char*)led1);

> The `led1` variable is either `ledon.gif` or `ledoff.gif``.` When the browser loads the page, it replaces

>     <img SRC="<!--#echo var="led1"-->">

> with

>     <img SRC="ledon.gif">

> or

>     <img SRC="ledoff.gif">

> This causes the browser to load the appropriate image file.

> #### 4.3.2.2 CGI Feature

> `Ssi.c` also demonstrates the Common Gateway Interface. CGI is a standard for interfacing external applications with HTTP servers. Each time a client requests an URL corresponding to a CGI program, the server will execute the CGI program in real-time.

> For increased flexibility, a CGI function is responsible for outputting its own HTTP headers. Information about HTTP headers can be found at:

>     http://deesse.univ-lemans.fr:8003/Connected/RFC/1945/

> In the `Ssi.shtml` file, this line creates the clickable button viewable from the browser.

>     <TD> <A HREF="/led1tog.cgi"> <img SRC="button.gif"> </A> </TD>

> When the user clicks on the button, the browser will request the `/led1tog.cgi` entity. This causes the HTTP server to examine the contents of the `http_flashspec` structure looking for `/led1tog.cgi`. It finds it and notices that `led1toggle()` needs to be called.

> The `led1toggle` function changes the value of the `led1` variable, then redirects the browser back to the original page. When the original page is reloaded by the browser, the LED image will have changed states to reflect the user's action.

> ##### 4.3.2.2.1 Connection Abort Condition

> There are two fields in the `HttpState` structure that allow a CGI function to appropriately respond to a connection abort condition. The user may set the field `abort_notify` to `!0` in a CGI function to request that the CGI function be called one more time with the `cancel` field set to one if a connection abort occurs.

> #### 4.3.3 Web Pages With HTML Forms

> With a web browser, HTML forms enable users to input values. With a CGI program, those values can be sent back to the server and processed. The FORM and INPUT tags are used to create forms in HTML.

> The FORM tag specifies which elements constitute a single form and what CGI program to call when the form is submitted. The FORM tag has an option called ACTION. This option defines what CGI program is called when the form is submitted (when the "Submit" button is pressed). The FORM tag also has an option called METHOD that defines the method used to return the form information to the web server. In Section 4.3.3.1, "Sample HTML Page," on page 187, the POST method is used, which will be described later. All of the HTML between the <FORM> and </FORM> tags define what is contained within a form.

> The INPUT tag defines a specific form element, the individual input fields in a form. For example, a text box in which the user may type in a value, or a pull-down menu from which the user may choose an item. The TYPE parameter defines what type of input field is being used. In the following example, in the first two cases , it is the text input field, which is a single-line text entry box. The NAME parameter defines what the name of that particular input variable is, so that when the information is returned to the server, then the server can associate it with a particular variable. The VALUE parameter defines the current value of the parameter. The SIZE parameter defines how long the text entry box is (in characters).

> At the end of the HTML page in our example, the Submit and Reset buttons are defined with the INPUT tag. These use the special types "submit" and "reset", since these buttons have special purposes. When the submit button is pressed, the form is submitted by calling the CGI program "`myform`".

> #### 4.3.3.1 Sample HTML Page

> An HTML page that includes a form may look like the following:

>     <HTML>
>     <HEAD><TITLE>ACME Thermostat Settings</TITLE></HEAD>
>     <BODY>
>     <H1>ACME Thermostat Settings</H1>
>     <FORM ACTION="myform.html" METHOD="POST">
>        <TABLE BORDER>
>           <TR>
>              <TD>Name</TD>
>              <TD>Value</TD>
>              <TD>Description</TD>
>           </TR>

>           <TR>
>              <TD>High Temp</TD>
>              <TD><INPUT TYPE="text" NAME="temphi" VALUE="80"
>                                                  SIZE="5"></TD>
>              <TD>Maximum in temperature range (&deg;F)</TD>
>           </TR>

>           <TR>
>              <TD>Low Temp</TD>
>              <TD><INPUT TYPE="text" NAME="templo" VALUE="65"
>                                                  SIZE="5"></TD>
>              <TD>Minimum in temperature range (&deg;F)</TD>
>           </TR>

>        </TABLE>
>        <P>
>           <INPUT TYPE="submit" VALUE="Submit">
>           <INPUT TYPE="reset" Value="Reset">

>     </FORM></BODY>
>     </HTML>

> The form might display as follows:

> When the form is displayed by a browser, the user can change values in the form. But how does this changed data get back to the HTTP server? By using the HTTP `POST` command. When the user presses the "Submit" button, the browser connects to the HTTP server and makes the following request:

>     POST myform HTTP/1.0
>     .
>     . (some header information)
>     .
>     Content-Length: 19

> where "`myform`" is the CGI program that was specified in the ACTION attribute of the FORM tag and `POST` is the METHOD attribute of the FORM tag. "Content-Length" defines how many bytes of information are being sent to the server (not including the request line and the headers).

> Then, the browser sends a blank line followed by the form information in the following manner:

>     temphi=80&templo=65

> That is, it sends back name and value pairs, separated by the `&' character. (There can be some further encoding done here to represent special characters, but we will ignore that in this explanation.) The server must read in the information, decode it, parse it, and then handle it in some fashion. It will check the validity of the new values, and then assign them to the appropriate C variable if they are valid.

> #### 4.3.3.2 POST-style form submission

> If an HTML file specifies a POST-style form submission (i.e., `METHOD="POST"`), the form will still be waiting on the socket when the CGI handler is called. Therefore, it is the job of the CGI handler to read this data off the socket and parse it in a meaningful way. The sample files `Post.c` and `Post2.c` in the `\Samples\Tcpip\Http` folder show how to do this.

> The HTTP `POST` command can put any kind of data onto the network. There are many known encoding schemes currently used, but we will only look at URL-encoded data in this document. Other encoding schemes can be handled in a similar manner.

> #### 4.3.3.3 URL-encoded Data

> URL-encoded data is of the form "name1=value1&name2=value2," and is similar to the CGI form submission type passed in normal URLs. This has to be parsed to `name=value` pairs. The rest of this section details an extensible way to do this.

>     #define MAX_FORMSIZE   64
>     typedef struct {
>         char *name;
>         char value[MAX_FORMSIZE];
>     } FORMType;
>     FORMType FORMSpec[2];

>     void init_forms(void) {
>         FORMSpec[0].name = "user_name";
>         FORMSpec[1].name = "user_email";
>     }

> This initializes two possible HTML form entries to be received, and a place to store the results.

> ### Reading & Storing URL-encoded Data

> `parse_post()` reads URL-encoded data off the network. and calls `parse_token()` to store the data in `FORMSpec``[]`. These code snippets are from `/samples/tcpip/http/post.c`.

>     /* Parse one token 'foo=bar', matching 'foo' to the name field in */
>     /* the struct, and store 'bar' into the value */

>     void parse_token(HttpState* state) {
>        int i, len;
>        for(i=0; i<HTTP_MAXBUFFER; i++) {
>           if(state->buffer[i] == '=')
>              state->buffer[i] = '\0';
>        }
>        state->p = state->buffer + strlen(state->buffer) + 1;
>        for(i=0; i<(sizeof(FORMSpec)/sizeof(FORMType)); i++) {
>           if(!strcmp(FORMSpec[i].name,state->buffer)) {

>              len = (strlen(state->p)>MAX_FORMSIZE) ? MAX_FORMSIZE - 1: strlen(state->p);

>              strncpy(FORMSpec[i].value,state->p,1+len);
>              FORMSpec[i].value[MAX_FORMSIZE - 1] = '\0';
>           }
>        }

>     }

>     /* Parse the url-encoded POST data into FORMSpec structure */
>     /* e.g., parse 'foo=bar&baz=qux' into the struct) */

>     int parse_post(HttpState* state) {
>        auto int retval;
>        while(1) {
>           retval = sock_fastread(&state->s, state->p, 1);
>           if(0 == retval) {
>              *state->p = '\0';
>              parse_token(state);
>              return 1
>           }

>           /* should this only be '&'? (allow the eoln as valid text?) */

>           if((*state->p == '&') || (*state->p == '\r') || (*state->p == '\n')) {

>              /* found one token */
>              *state->p = '\0';
>              parse_token(state);
>              state->p = state->buffer;
>           } else {
>              state->p++;
>           }

>           if((state->p - state->buffer) > HTTP_MAXBUFFER)
>              return 1;        // input too long
>        }
>        return 0;              // end of data - loop again to give it time to write more
>     }

> #### 4.3.3.4 Sample of a CGI Handler

> This next function is the CGI handler. It is a state-machine-based handler that generates the page. It calls `parse_post()` and references the structure that is now filled with the parsed data we wanted. This function is from `/samples/tcpip/http/post.c`.

>     int submit(HttpState* state){

>        auto int i;

>        if(state->length) {           // buffer to write out
>           if(state->offset < state->length) {

>              state->offset += sock_fastwrite(&state->s, state->buffer + (int)state->offset,(int)state->length - (int)state -> offset);

>           } else {
>              state->offset = 0;
>              state->length = 0;
>           }
>        } else {
>           switch(state->substate) {
>           case 0:
>              strcpy(state->buffer, "HTTP/1.0 200 OK\r\n\r\n");
>              state->length = strlen(state->buffer);
>              state->offset = 0;
>              state->substate++;
>              break;
>           case 1:

>              strcpy(state->buffer,"<html><head><title>Results</title> </head><body>\r\n");

>              state->length = strlen(state->buffer);
>              state->substate++;
>              break;
>           case 2:                     // init the FORMSpec data
>              FORMSpec[0].value[0] = '\0';
>              FORMSpec[1].value[0] = '\0';
>              state->p = state->buffer;
>              state->substate++;
>              break;
>           case 3:                     // parse the POST information
>              if(parse_post(state)) {

>                 sprintf(state->buffer, "<p>Username: %s<p>\r\n<p>Email: %s<p>\r\n", FORMSpec[0].value, FORMSpec[1].value);

>                 state->length = strlen(state->buffer);
>                 state->substate++;
>              }
>              break;
>           case 4:

>              strcpy(state->buffer,"<p>Go <a href=\"/\">home</a></body> </html>\r\n");

>              state->length = strlen(state->buffer);
>              state->substate++;
>              break;
>           default:
>              state->substate = 0;
>              return 1;
>           }

>        }

>        return 0;

>     }

> #### 4.3.4 HTML Forms Using Zserver.lib

> In this section, we will step through an example program,`/samples/tcpip/http/form1.c`, that uses HTML forms. Through this step-by-step explanation, the method of using the functions in `zserver.lib` will become clearer.

> These lines are part of the standard TCP/IP configuration. You must change them to whatever your local IP address and netmask are. Contact your network administrator for these numbers.

>     #define MY_IP_ADDRESS   "10.10.6.112"
>     #define MY_NETMASK      "255.255.255.0"

> Defining `FORM_ERROR_BUF` is required in order to use the HTML form functionality in `Zserver.lib`. The value represents the number of bytes that will be reserved in root memory for the buffer which will be used for form processing. This buffer must be large enough to hold the name and value for each variable, plus four bytes for each variable. Since we are building a small form, 256 bytes is sufficient.

>     #define FORM_ERROR_BUF 256

> Since we will not be using the `http_flashspec` array, then we can define the following macro, which removes some code for handling this array from the web server.

>     #define HTTP_NO_FLASHSPEC

> These lines are part of the standard TCP/IP configuration.

>     #memmap xmem
>     #use "dcrtcp.lib"
>     #use "http.lib"

>     const HttpType http_types[] =
>     {
>        { ".html", "text/html", NULL}
>     };

> These are the declarations of the variables that will be included in the form.

>     int temphi;
>     int tempnow;
>     int templo;
>     float humidity;
>     char fail[21];

>     void main(void)
>     {

> An array of type `FormVar` must be declared to hold information about the form variables. Be sure to allocate enough entries in the array to hold all of the variables that will go in the form. If more forms are needed, then more of these arrays can be allocated.

>      FormVar myform[5];

> These variables will hold the indices in the TCP/IP servers' object list for the form and the form variables.

>     int var;
>     int form;

> This array holds the possible values for the fail variable. The fail variable will be used to make a pulldown menu in the HTML form.

>     const char* const fail_options[] = {
>           "Email",
>           "Page",
>           "Email and page",
>           "Nothing"
>        };

> These lines initialize the form variables.

>     temphi = 80;
>     tempnow = 72;
>     templo = 65;
>     humidity = 0.3;
>     strcpy(fail, "Page");

> The next line adds a form to the TCP/IP servers' object list. The first parameter gives the name of the form. Hence, when a browser requests the page "`myform.html`", the HTML form is generated and presented to the browser. The second parameter gives the developer-declared array in which form information will be saved. The third parameter gives the number of entries in the `myform` array (this number should match the one given in the `myform` declaration above). The fourth parameter indicates that this form should only be accessible to the HTTP server, and not the FTP server. `SERVER_HTTP` should always be given for HTML forms. The return value is the index of the newly created form in the TCP/IP servers' object list.

>      form = sspec_addform("myform.html", myform, 5, SERVER_HTTP);

> This line sets the title of the form. The first parameter is the form index ( the return value of `sspec_addform()`), and the second parameter is the form title. This title will be displayed as the title of the HTML page and as a large heading in the HTML page.

>      sspec_setformtitle(form, "ACME Thermostat Settings");

> The following line adds a variable to the TCP/IP servers' object list. It must be added to the TCP/IP servers' object list before being added to the form. The first parameter is the name to be given to the variable, the second is the address of the variable, the third is the type of variable (this can be `INT8`, `INT16`, `INT32`, `FLOAT32`, or `PTR16`), the fourth is a printf-style format specifier that indicates how the variable should be printed, and the fifth is the server for which this variable is accessible. The return value is the index of the variable in the TCP/IP servers' object list.

>     var = sspec_addvariable("temphi", &temphi, INT16, "%d", SERVER_HTTP);

> The following line adds a variable to a form. The first parameter is the index of the form to add the variable to ( the return value of `sspec_addform()`), and the second parameter is the index of the variable ( the return value of `sspec_addvariable()`). The return value is the index of the variable within the developer-declared `FormVar` array, `myform`.

>     var = sspec_addfv(form, var);

> This function sets the name of a form variable that will be displayed in the first column of the form table. If this name is not set, it defaults to the name for the variable in the TCP/IP servers' object list ("temphi", in this case). The first parameter is the form in which the variable is located, the second parameter is the variable index within the form, and the third parameter is the name for the form variable.

>     sspec_setfvname(form, var, "High Temp");

> This function sets the description of the form variable, which is displayed in the third column of the form table.

>     sspec_setfvdesc(form, var, "Maximum in temperature range
>                       (60 - 90 &deg;F)");

> This function sets the length of the string representation of the form variable. In this case, the text box for the form variable in the HTML form will be 5 characters long. If the user enters a value longer than 5 characters, the extra characters will be ignored.

>     sspec_setfvlen(form, var, 5);

> This function sets the range of values for the given form variable. The variable must be within the range of 60 to 90, inclusive, or an error will be generated when the form is submitted.

>     sspec_setfvrange(form, var, 60, 90);

> This concludes setting up the first variable. The next five lines set up the second variable, which represents the current temperature.

>     var = sspec_addvariable("tempnow", &tempnow, INT16, "%d",SERVER_HTTP);
>     var = sspec_addfv(form, var);
>     sspec_setfvname(form, var, "Current Temp");
>     sspec_setfvdesc(form, var, "Current temperature in &deg;F");
>     sspec_setfvlen(form, var, 5);

> Since the value of the second variable should not be modifiable via the HTML form (by default variables are modifiable,) the following line is necessary and makes the given form variable read-only when the third parameter is 1. The variable will be displayed in the form table, but can not be modified within the form.

>     sspec_setfvreadonly(form, var, 1);

> These lines set up the low temperature variable. It is set up in much the same way as the high temperature variable.

>     var = sspec_addvariable("templo", &templo, INT16, "%d", SERVER_HTTP);
>     var = sspec_addfv(form, var);
>     sspec_setfvname(form, var, "Low Temp");
>     sspec_setfvdesc(form, var, "Minimum in temperature range
>                     (50 - 80 &deg;F)");
>     sspec_setfvlen(form, var, 5);
>     sspec_setfvrange(form, var, 50, 80);

> This code begins setting up the string variable that specifies what to do in case of air conditioning failure. Note that the variable is of type `PTR16`, and that the address of the variable is not given to `sspec_addvariable()`, since the variable `fail` already represents an address.

>     var = sspec_addvariable("failure", fail, PTR16, "%s", SERVER_HTTP);
>     var = sspec_addfv(form, var);
>     sspec_setfvname(form, var, "Failure Action");
>     sspec_setfvdesc(form, var, "Action to take in case of air-conditioning
>                      failure");
>     sspec_setfvlen(form, var, 20);

> This line associates an option list with a form variable. The third parameter gives the developer-defined option array, and the fourth parameter gives the length of the array. The form variable can now only take on values listed in the option list.

>     sspec_setfvoptlist(form, var, fail_options, 4);

> This function sets the type of form element that is used to represent the variable. The default is `HTML_FORM_TEXT`, which is a standard text entry box. This line sets the type to `HTML_FORM_PULLDOWN`, which is a pull-down menu.

>     sspec_setfventrytype(form, var, HTML_FORM_PULLDOWN);

> Finally, this code sets up the last variable. Note that it is a float, so `FLOAT32` is given in the `sspec_addvariable()` call. The last function call is `sspec_setfvfloatrange()` instead of `sspec_setfvrange()`, since this is a floating point variable.

>     var = sspec_addvariable("humidity", &humidity, FLOAT32, "%.2f",
>                              SERVER_HTTP);
>     var = sspec_addfv(form, var);
>     sspec_setfvname(form, var, "Humidity");
>     sspec_setfvdesc(form, var, "Target humidity (between 0.0 and 1.0)");
>     sspec_setfvlen(form, var, 8);
>     sspec_setfvfloatrange(form, var, 0.0, 1.0);

> These calls create aliases in the TCP/IP servers' object list for the HTML form. That is, the same form can now be generated by requesting "`index.html`" or "`/`". Note that `sspec_aliasspec()` should be called after the form has already been set up. The aliasing is done by creating a new entry in the TCP/IP servers' object list and copying the original entry into the new entry. Note that aliasing can also be done for files and other types of server objects.

>     sspec_aliasspec(form, "index.html");
>     sspec_aliasspec(form, "/");

> These lines complete the sample program. They initialize the TCP/IP stack and web server, and run the web server.

>       sock_init();
>       http_init();
>       while (1) {
>           http_handler();
>       }
>     }

> This is the form that is generated:

> ### 4.4 Function Reference

> ## cgi_redirectto

>     void cgi_redirectto(HttpState* state, char* url);

> ### Description

> This utility function may be called in a CGI function to redirect the user to another page. It sends a user to the URL stored in `url`. You should immediately issue a "`return 0;`" after calling this function. The CGI is considered finished when you call this, and will be in an undefined state.

> ### Parameters

> #### state

> Current server struct, as received by the CGI function.

> #### url

> Fully qualified URL to redirect to.

> ### Return value

> None - sets the state, so the CGI must immediately return with a value of `0`.

> ### Library

> HTTP.LIB

> ### See also

> cgi_sendstring

> ## cgi_sendstring

>     void cgi_sendstring(HttpState* state, char* str);

> ### Description

> Sends a string to the user. You should immediately issue a "`return 0;`" after calling this function. The CGI is considered finished when you call this, and will be in an undefined state. This function greatly simplifies a CGI handler because it allows you to generate your page in a buffer, and then let the library handle writing it to the network.

> ### Parameters

> #### state

> Current server struct, as received by the CGI function.

> #### str

> String to send.

> ### Return value

> None - sets the state, so the CGI must immediately return with a value of `0`.

> ### Library

> HTTP.LIB

> ### See also

> `cgi_redirectto`

> ## http_addfile

>     int http_addfile(char* name, long location);

> ### Description

> Adds a file to the TCP/IP servers' object list.

> ### Parameters

> #### name

> Name of the file (e.g., "`/index.html`").

> #### location

> Address of the file data. (from`#ximport`)

> ### Return value

> `0`: Success.
> `1`: Failure.

> ### Library

> HTTP.LIB

> ### See also

> http_delfile

> ## http_contentencode

>      char *http_contentencode(char *dest, const char *src, int len);

> ### Description

> Converts a string to include HTTP transfer-coding ''tokens'' (such as &#64; (decimal) for at-sign) where appropriate. Encodes these characters: ''<>@%#&''

> Source string is `NULL-`byte terminated. Destination buffer is bounded by `len`. This function is reentrant.

> ### Parameters

> #### dest

> Buffer where encoded string is stored.

> #### src

> Buffer holding original string (not changed)

> #### len

> Size of destination buffer.

> ### Return value

> `dest`: There was room for all conversions.
> `NULL`: Not enough room.

> ### Library

> HTTP.LIB

> ### See also

> http_urldecode

> ## http_date_str

>     char *http_date_str(char *buf);

> ### Description

> Print the date (time zone adjusted) into the given buffer. This assumes there is room!

> ### Parameters

> #### buf

> The buffer to write the date into. This requires at least 30 bytes in the destination buffer.

> ### Return Value

> A pointer to the string.

> ### Library

> HTTP.LIB

> ### See Also

> http_handler

> ## http_delfile

>     int http_delfile(char* name);

> ### Description

> Deletes a file from TCP/IP servers' object list.

> ### Parameters

> #### name

> Name of the file, as passed to `http_addfile`.

> ### Return value

> `0`: Success;
> `1`: Failure (not found).

> ### Library

> HTTP.LIB

> ### See also

> http_addfile

> ## http_finderrbuf

>     char* http_finderrbuf(char* name);

> ### Description

> Finds the occurrence of the given variable in the HTML form error buffer, and returns its location.

> ### Parameters

> #### name

> Name of the variable.

> ### Return value

> `NULL`: Failure.
> `!NULL`: Success, location of the variable in the error buffer.

> ### Library

> HTTP.LIB

> ## http_findname

>     HttpSpecAll http_findname(char* name);

> ### Description

> Finds a spec entry, searching first in RAM, then in FLASH.

> ### Parameters

> #### name

> Name, in text, of the spec to find.

> ### Return Value

> The spec entry.

> ### Library

> HTTP.LIB

> ## http_handler

>     void http_handler();

> ### Description

> This is the basic control function for the HTTP server, a tick function to run the HTTP daemon. It must be called periodically for the daemon to work. It parses the requests and passes control to the other handlers, either `html_handler`, `shtml_handler`, or to the developer-defined CGI handler based on the request's extension.

> ### Library

> HTTP.LIB

> ### See also

> http_init

> ## http_init

>     int http_init(void);

> ### Description

> Initializes the HTTP daemon.

> ### Return value

> `0`: Success.

> ### Library

> HTTP.LIB

> ### See also

> http_handler

> ## http_nextfverr

>     void http_nextfverr( char* start, char** name, char** value, int* error, char** next );

> ### Description

> Gets the information for the next variable in the HTML form error buffer. If any of the last four parameters in the function call are `NULL`, then those parameters will not have a value returned. This is useful if you are only interested in certain variable information.

> ### Parameters

> #### start

> Pointer to the variable in the buffer for which we want to get information.

> #### name

> Return location for the name of the variable.

> #### value

> Return location for the value of the variable.

> #### error

> Return location for whether or not the variable is in error (0 if it is not, 1 if it is).

> #### next

> Return location for a pointer to the variable after this one.

> ### Return Value

> None, although information is returned in the last four parameters.

> ### Library

> HTTP.LIB

> ## http_parseform

>     int http_parseform(int form, HttpState* state);

> ### Description

> Parses the returned form information`.` It expects a POST submission. This function is useful for a developer who only wants the parsing functionality and wishes to generate forms herself. Note that the developer must still build the array of `FormVar`s and use the `server_spec` table. This function will not, however, automatically display the form when used by itself. If all variables satisfy all integrity checks, then the variables' values are updated. If any variables fail, then none of the values are updated, and error information is written into the error buffer If this function is used directly, the developer must process errors.

> ### Parameters

> #### form

> `server_spec` index of the form (i.e., location in TCP/IP servers' object list).

> #### state

> The HTTP server with which to parse the POSTed data.

> ### Return value

> `0` if there is more processing to do;
> `1` form processing has been completed.

> ### Library

> HTTP.LIB

> ## http_setcookie

>     void http_setcookie(char* buf, char* value);

> ### Description

> This utility generates a cookie on the client. This will store the text in `value` into a cookie-generation header that will be written to `buf`. This will not be written out to the client, and it is still the responsibility of the client to write out. Also, this utility will generate an HTTP header line that must be written along with any other headers that are written before the HTML file itself is written out. When a page is requested from the client, and the cookie is already set, the text of the cookie will be stored in `state->cookie[]`. This is a `char*`, and`state->cookie[0]` will equal `'``\0``'` if no cookie was available.

> ### Parameters

> #### buf

> Buffer to store cookie-generation header.

> #### value

> Text to store in cookie-generation header.

> ### Library

> HTTP.LIB

> ## http_urldecode

>     char *http_urldecode(char *dest, const char *src, int len);

> ### Description

> Converts a string with URL-escaped ''tokens'' (such as %20 (hex) for space) into actual values. Changes "+" into a space. String can be `NULL` terminated; it is also bounded by a specified string length. This function is reentrant.

> ### Parameters

> #### dest

> Buffer where decoded string is stored.

> #### src

> Buffer holding original string (not changed).

> #### len

> Maximum size of string (`NULL` terminated strings can be shorter).

> ### Return value

> `dest`: If all conversion was good.
> `NULL`: If some conversion had troubles.

> ### Library

> HTTP.LIB

> ### See also

> http_contentencode

> ## shtml_addfunction

>     int shtml_addfunction(char* name, void (*fptr()));

> ### Description

> Adds a CGI/SSI-exec function for making dynamic web pages to the TCP/IP servers' object list.

> ### Parameters

> #### name

> Name of the function (e.g., "`/foo.cgi`").

> #### fptr

> Function pointer to the handler, that must take `HttpState*` as an argument. This function should return an `int` (`0` while still pending, `1` when finished).

> ### Return value

> `0`: Success;
> `1`: Failure (no room).

> ### Library

> HTTP.LIB

> ### See also

> shtml_delfunction

> ## shtml_addvariable

>     int shtml_addvariable(char* name, void* variable, word type, char* format);

> ### Description

> This function adds a variable so it can be recognized by the `shtml_handler`.

> ### Parameters

> #### name

> Name of the variable.

> #### variable

> Pointer to the variable.

> #### type

> Type of variable. The following types are supported: `INT8``,` `INT16``,` `INT32``,` `PTR16, FLOAT32`

> #### format

> Standard printf format string. (e.g., "%d")

> ### Return value

> `0`: Success.
> `1`: Failure (no room).

> ### Library

> HTTP.LIB

> ### See also

> shtml_delvariable

> ## shtml_delfunction

>     int shtml_delfunction(char* name);

> ### Description

> Deletes a function from the TCP/IP servers' object list.

> ### Parameters

> #### name

> Name of the function as given to `shtml_addfunction`.

> ### Return value

> `0`: Success;
> `1`: Failure (not found).

> ### Library

> HTTP.LIB

> ### See also

> shtml_addfunction

> ## shtml_delvariable

>     int shtml_delvariable(char* name);

> ### Description

> Deletes a variable from the TCP/IP servers' object list.

> ### Parameters

> #### name

> Name of the variable, as given to `shtml_addvariable`.

> ### Return value

> `0`: Success;
> `1`: Failure (not found).

> ### Library

> HTTP.LIB

> ### See also

> shtml_addvariable

Z-World http://www.zworld.com Voice: 530.757.3737 Fax: 530.757.3792 or 530.753.5141 sales@zworld.com