<< Previous | Index | Next >>

4. HTTP Server

This chapter is intended to be a detailed description of the HTTP server, and how it interfaces to other libraries, such as Zserver and TCP/IP. For an overview of how these libraries interface with one another and with your application, please see Chapter 2. "Web-Enabling Your Application."

An HTTP (Hypertext Transfer Protocol) server makes HTML (Hypertext Markup Language) pages and other resources available to clients (that is, web browsers). HTTP is implemented by HTTP.LIB, thus you need to write #use "http.lib" near the top of your program. HTTP depends on the Dynamic C networking suite, which is included in your program by writing #use "dcrtcp.lib".

Setting up the network subsystem is a necessary pre-requisite for use of HTTP. This is described in the Dynamic C TCP/IP User's Manual, Vol. 1. However, it can be quite simple for test applications and samples to initialize the network subsystem. In the file tcp_config.lib are predefined configurations that may be accessed by a #define of the macro TCPCONFIG. For instructions on how to set up different configurations, please see the Dynamic C TCP/IP User's Manual, Vol. 1 or look in the file \LIB\TCPIP\TCP_CONFIG.LIB.

HTTP makes use of the Zserver library to manage resources and access control. The previous chapter discusses Zserver. When reading this chapter on the HTTP server, it will help if you are familiar with Zserver, its interfaces and capabilities.

Much of this chapter contains material that could be considered advanced usage. There is also some material of a historical nature, with relevant sections marked as such.

4.1 HTTP Server Data Structures

The single data structure in HTTP.LIB of interest to developers of CGI functions is discussed in this section.

4.1.1 HttpState

Use of the HttpState structure is necessary for CGI functions (whether or not they were written prior to Dynamic C 8.50). Some of the fields are off-limits to developers. The field that are available for use are described in the next section.

Historical note: prior to Dynamic C 8.50, it was sometimes necessary for CGI functions to access directly the fields of this structure. New programs should not directly access the fields, since it reduces the chance of upward compatibility. There is a new suite of macros (see http_getAction() and related macros) that should be used instead. Where applicable, the equivalent macro is documented with the field. Some fields do not have an equivalent macro (such as the cookie field); for now, use read-only access to such fields.

A pointer to HttpState is the first (and only) parameter to all CGI functions. Most of the time, this pointer should be passed on to other HTTP library functions.

Note that the HttpState structure is only valid within a CGI function that has been called from the HTTP server. Outside of this (for example, in your main() function) none of the fields are guaranteed to be meaningful or consistent.

4.1.1.1 HttpState Fields

The fields discussed here are available for developers to use in their CGI functions.

s

This is the socket associated with the given HTTP server. A developer can use this in a CGI function to output dynamic data (although there are better, safer ways of doing this: see the section on "Writing a CGI Function"). Any of the TCP functions can be used; however, you should not use any functions that may wait for long periods, or may change the state or mode of the socket (since the HTTP server depends on it being a normal ASCII mode TCP socket).
It is recommended that you use the http_getSocket() macro instead of directly accessing this field.

substate
subsubstate

Intended for holding 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.
It is recommended that you use the macros http_getState() and http_setState() to manipulate the substate field instead of directly accessing it. subsubstate is not accessible via these macros, but there are better alternatives.

timeout

This value can be used by the CGI function to implement an internal time-out.

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 (defaults to 256 bytes).
Note: It is not recommended to directly access "buffer" or "p" (below). Use the new-style CGI functions and the http_write(), http_getData() and http_getDataLength() functions instead. These create a much easier-to-use and safer method of reading/writing data to the client.

p

Pointer into the buffer given above. See above note.

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.
Use http_getHTTPMethod() for new code.

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.

Use http_getURL() for new code.

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.
Use http_getHTTPVersion() for new code.

content_type[]

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

Use http_getContentType() for new code.

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.

Use http_getContentLength() for new code.

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.

Note: New code should use the http_getContext() macro, then use the results to look up the user details using the sauth_* functions. See the documentation for the ServerContext Structure in the previous chapter.

password[]

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

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.

For new code, consider writing a new-style CGI function, which obviates the need to manipulate these fields.

cond[]

Support for conditional SSI (error feedback etc.).

New code should use the macros http_getCond()and http_setCond().

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.

New code should use the http_getUserData() macro to obtain a pointer to user-defined storage in this structure.

4.2 Configuration Macros

The following macros are specified in HTTP.LIB. Unless otherwise noted, you can override the default values by defining the macro (same name, different value) before you #use "http.lib".

HTTP_HOMEDIR

Specify the "home directory" for the server. This is the root directory to which all URLs are appended. The default is "/", which means that all resources are accessible. If this is set to, say, "/htdocs", then an incoming URL of "foo/bar.html" gets turned into "/htdocs/foo/bar.html". You can use this to restrict the HTTP server's access to all but a specific "branch" of resources.

Note: the string value for this macro must start and end with a "/" character.

HTTP_DFLTFILE

Specify the default file name to append to the URL if the URL refers to a directory. This is only applicable if the URL is "/", or is in a filesystem (not the static or dynamic resource tables). The default setting is "index.html". The value must not start or end with a "/" character.

HTTP_SOCK_BUF_SIZE

This macro is not defined by default. If you define it, then it specifies the amount of extended memory to allocate (xalloc()) for each HTTP server instance. If you do not define it, then socket buffers are allocated from the usual pool. See tcp_extopen() for more details.

HTTP_DIGEST_NONCE_TIMEOUT

This macro is used when USE_HTTP_DIGEST_AUTHENTICATION is set to one. Nonces that are generated by the server are valid for this many seconds (900 by default). If set to 0, nonces are good forever. Setting this to a smaller value can possibly result in higher security, although internal use of the nonce-count facility offsets this. Setting it to a larger value reduces the negotiation between the browser and the server, since when a nonce times out, the browser must be told that it is using a stale nonce value and provided with a new one. Since Mozilla and Netscape ignore the stale parameter, the user must reenter the username and password when a nonce times out. Internet Explorer and Opera respect the stale parameter, so they automatically try the username and password with the new nonce without asking the user.

HTTP_MAXBUFFER

This is the size of the buffer accessible through the HttpSpec structure. It defaults to 256 bytes. The size of this buffer affects the speed of the HTTP server; the larger the buffer (up to a point), the faster the server will run. The buffer size is also important for use in CGI functions because it is a work space the programmer can use. HTTP_MAXBUFFER must be at least 180 bytes for CGI functionality.

HTTP_MAX_COND

Support for conditional SSI (error feedback etc.). It defaults to 4. This is the maximum number of state variables that may be accessed using the http_getCond() or http_setCond() macros.

HTTP_MAX_NONCES

This macro is used when USE_HTTP_DIGEST_AUTHENTICATION is set to one. Defined to 5 by default, it specifies the number of nonces the HTTP server will allow as valid at any one time. This value should be somewhat larger than the maximum number of clients expected to be accessing the server simultaneously. Otherwise performance could suffer as clients are forced to retry authorization in order to acquire a fresh nonce.

HTTP_MAXSERVERS

This is the maximum number of HTTP servers listening on port 80. The default is 2. 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 4: 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_MAXURL

This macro defines the maximum incoming URL. This could be important if someone is allowing GET requests with a large number of parameters.

HTTP_PORT

This macro allows the user to override the default port of 80.

HTTP_IFACE

This macro allows the user to override the default listening network interface. The default is IF_ANY, meaning that the HTTP server(s) will listen for incoming network connections on all interfaces which are up. You can restrict the HTTP servers to a single interface by overriding this macro to the specific interface number (for example, IF_ETH0).

HTTP_TIMEOUT

Defines the number of seconds of no activity that can elapse before the HTTP server closes a connection. The default is 16 seconds.

HTTP_USERDATA_SIZE

This macro causes "char userdata[]" to be added to the HttpState structure. Define your structure before the statement #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 CGI function code, access it using:


mystate = (struct UserStateData *)http_getUserData(state);
USE_HTTP_DIGEST_AUTHENTICATION

Set to 1 to enable digest authentication, 0 to disable digest authentication. Set to 0 by default.

USE_HTTP_BASIC_AUTHENTICATION

Set to 1 to enable basic authentication, 0 to disable basic authentication. Set to 1 by default.

4.2.1 Sending Customized HTTP Headers to the Client

The callback macro, HTTP_CUSTOM_HEADERS, will be called whenever HTTP headers are being sent. 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 the #use "http.lib" statement, like in the following:


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

Then, for the above to work, my_headers() must be defined by the user, like so:


void my_headers(HttpState *state, char *buffer, int bytes)
{
strcpy(buffer, Hello Rabbit!\r\n");
printf("bytes: %d\n", bytes);
}

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.2.2 Saving Custom Headers from the Client

Customers may want to save some specific headers that a web client sends to the server as part of a request. One possibility for this is to check the browser version of the client and display a different page depending on that value. This is mostly useful for CGI functions.

The user can create a structure like the following to indicate to the web server that it should save the specified tags:


const HttpHeader http_headers[] = {
"Host",
"Content-Length",
"User-Agent",
END_HTTP_HEADERS
};

END_HTTP_HEADERS is simply a macro (NULL) that indicates the end of the structure. These headers will be saved in an internal buffer of a user-specified size:


#define HTTP_CUSTOM_HEADERS_SIZE 1024

By default, HTTP_CUSTOM_HEADERS_SIZE is undefined, which disables the custom header functionality (since, in most cases, it will not need to be used). This buffer will be located in xmem, and there will be one per HTTP server. A define will also be provided to limit the maximum size of a single header (to keep one very long header from monopolizing all of the buffer space):


#define HTTP_CUSTOM_HEADER_MAX_SIZE 128

By default, this is undefined and there is no limit.

The user will also need functions that look up the data:


int http_getheader(HttpState *state, char *header, char *dest, int destlen);

int http_xgetheader(HttpState *state, char *header, long *destptr);

The first function requires the user to provide a root buffer to place the header. The HttpState state structure must be passed so that the server knows which set of headers to access. The header parameter is, of course, the name of the header the user wants to retrieve. dest is a pointer to the destination buffer. destlen is the length of the destination buffer (provided by the user). The function returns -1 on error, and the number of bytes in the header on success.

The second function, http_xgetheader(), simply returns a long pointer into the internal header buffer for the given header. It returns -1 on error, and the number of bytes in the header on success.

Note that some headers are saved by the HTTP server by default into the HTTP state structure, such as "Content-Length." We will also begin saving the "Host" header, which is useful in performing CGI redirection. Hence, we can change the semantics of the cgi_redirectto() function:


int cgi_redirectto(HttpState *state, char *url);

such that the url parameter no longer needs to be an absolute URL.

4.3 Authentication Methods

HTTP/1.0 Basic Authentication is used by default. 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.

Starting with Dynamic C version 8.01, HTTP Digest Authentication as specified in RFC 2617 is supported. Instead of sending the password in cleartext as is done using Basic Authentication, MD5 is used to perform a cryptographic hash.

In general, adding a query string to the end of a GET request is poorly supported by digest authentication. Users should be aware that older browsers (e.g., IE6 and earlier) do not consider the query string to be part of the URL that is included in the cryptographic hash, whereas newer browsers (e.g., IE7 and later) are just the opposite. This means that digest authentication can work with one or the other, but not both. Therefore, it is only "safe" to use digest authentication without a query string at the end of the URL.

To use HTTP Digest Authentication, define USE_HTTP_DIGEST_AUTHENTICATION as 1. When this USE_* macro is defined, the macros HTTP_MAX_NONCES and HTTP_DIGEST_NONCE_TIMEOUT are available; they affect negotiation time between server and client. For more details see Section 4.2 "Configuration Macros."

In either case (basic or digest), you will need to add the appropriate rules and/or permissions to the appropriate tables. See the previous chapter for details on protecting resources. The HTTP server applies the strongest applicable authentication mechanism depending on the information it retrieves from the resource manager. Typically, in addition to defining user IDs and groups, you also need to associate an authentication mechanism with the resource using e.g. the SSPEC_MM_RULE macro, or the sspec_setpermissions() function.

Starting with Dynamic C 8.50, Secure Socket Layer (SSL) as specified in RFC 2818, is supported. It is also known by its newer official name, TLS (Transport Layer Security). To use SSL, you must create a secure HTTP server, known as an HTTPS server. To do this you must define some macros and import the SSL certificate.


#define USE_HTTP_SSL
#define HTTP_SSL_SOCKETS 1

#ximport "cert\mycert.dcc" SSL_CERTIFICATE

For complete documentation on the Dynamic C implementation of SSL, see the Dynamic C Module document entitled, "Rabbit Embedded Security Pack." Another good source of information are the sample programs that demonstrate using SSL. They are located in the /Samples/tcpip/ssl folder that will be created when the Rabbit Embedded Security Pack is installed.

4.4 Setting the Time Zone

The HTTP specification requires the server to indicate its current clock time in the response to any request. The HTTP implementation performs this function by consulting the rtc_timezone() library function (in RTCLOCK.LIB). The server uses the returned time zone to adjust the local real-time clock (RTC) value so that it is always returned to the client in UTC (Co-ordinated Universal Time).

There are several macros which you can set to define

The local timezone offset may be defined using the TIMEZONE macro, or it may be obtained automatically from a DHCP server if you are using DHCP to configure the network interface. Failing that, it defaults to zero.

If the RTC is already set to UTC (not local time), then you must define the macro RTC_IS_UTC, in which case the local timezone offset will be ignored.

For many reasons, including the fact that daylight savings transitions are more manageable, it is better to set the RTC to UTC, however some users prefer the clock to run in local time.

See the documentation for rtc_timezone() for more details. To do this, use the function lookup feature in Dynamic C or refer to the Dynamic C Function Reference Manual.

4.5 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. The macros in this block need to be changed to reflect your network settings.

Starting with Dynamic C 7.30, setting up the network addresses is both more complex and more simple. The complexity lies in the added support for multiple interfaces. Luckily for us, the simplicity is in the interface to this more intricate implementation. In the file tcp_config.lib are predefined configurations that may be accessed by a #define of the macro TCPCONFIG. For instructions on how to set the configuration, please see volume 1 of the manual or LIB\TCPIP\TCP_CONFIG.LIB.

4.5.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: \Samples\tcpip\http\Static.c

#define TCPCONFIG 1

#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

SSPEC_MIMETABLE_START
SSPEC_MIME(".html", "text/html"),
SSPEC_MIME(".gif", "image/gif")
SSPEC_MIMETABLE_END

SSPEC_RESOURCETABLE_START
SSPEC_RESOURCE_XMEMFILE("/index.html", index_html),
SSPEC_RESOURCE_XMEMFILE("/rabbit1.gif", rabbit1_gif)
SSPEC_RESOURCETABLE_END

main()
{
sock_init(); //
Initializes the TCP/IP stack
http_init(); // Initializes the web server

  tcp_reserveport(80);

  while (1) {
http_handler();
}
}

The 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, find and modify this line in Static.c:

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

Replace static.html with the name of the file you want the controller to serve.

4.5.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:

SSPEC_RESOURCETABLE_START
SSPEC_RESOURCE_XMEMFILE("/index.html", index_html),
SSPEC_RESOURCE_XMEMFILE("/rabbit1.gif", rabbit1_gif)
SSPEC_RESOURCETABLE_END

Insert the name of your new file, preceded by "/", into this structure, using the same format as the other lines.

SSPEC_RESOURCETABLE_START
SSPEC_RESOURCE_XMEMFILE("/index.html", index_html),
SSPEC_RESOURCE_XMEMFILE("/newfile.html", newfile_html),
SSPEC_RESOURCE_XMEMFILE("/rabbit1.gif", rabbit1_gif)
SSPEC_RESOURCETABLE_END

Compile and run the program. Open up your browser to the new page (for example, "http://10.10.6.100/newfile.html"), and your new page will be displayed by the browser.

4.5.1.2 Adding Files with Different Extensions

If you are adding a file with an extension that is not html or gif, you need to use the appropriate macros to make an entry in the MIMETypeMap 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.


SSPEC_MIMETABLE_START
SSPEC_MIME(".html", "text/html"),
SSPEC_MIME(".pdf", "application/pdf"), //
added this one
SSPEC_MIME(".gif", "image/gif")
SSPEC_MIMETABLE_END

4.5.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 the MIME table. If you use the SSPEC_MIME macro, the default handler is used. It passes the information verbatim. You can also use the macro SSPEC_MIME_FUNC to specify a non-default text processor; this is necessary for SSI and RabbitWeb scripts (described later).

4.5.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.

The sample code follows, but it has been edited for brevity. Open ssi.c in Dynamic C to see the fully-commented source.

#define TCPCONFIG 1
#define HTTP_MAXSERVERS 1
#define MAX_TCP_SOCKET_BUFFERS 1

// This is the address that the browser uses to access your server
#define REDIRECTHOST _PRIMARY_STATIC_IP

//
Used by the cgi of each ledxtoggle function to tell the browser which page to hit next.
#define REDIRECTTO "http://" REDIRECTHOST "/index.shtml"

#memmap xmem

#use "dcrtcp.lib"
#use "http.lib"

#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

SSPEC_MIMETABLE_START
SSPEC_MIME_FUNC(".shtml", "text/html", shtml_handler),
SSPEC_MIME(".html", "text/html"),
SSPEC_MIME(".gif", "image/gif"),
SSPEC_MIME(".cgi", "")
SSPEC_MIMETABLE_END


char led1[15];
char led2[15];
char led3[15];
char led4[15];

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){
//
Entirely analogous to led1toggle
}
int led3toggle(HttpState* state){
//
Entirely analogous to led1toggle
}
int led4toggle(HttpState* state){
//
Entirely analogous to led1toggle
}

SSPEC_RESOURCETABLE_START
SSPEC_RESOURCE_XMEMFILE("/", index_html),
SSPEC_RESOURCE_XMEMFILE("/index.shtml", index_html),
SSPEC_RESOURCE_XMEMFILE("/showsrc.shtml", showsrc_shtml),
SSPEC_RESOURCE_XMEMFILE("/rabbit1.gif", rabbit1_gif),
SSPEC_RESOURCE_XMEMFILE("/ledon.gif", ledon_gif),
SSPEC_RESOURCE_XMEMFILE("/ledoff.gif", ledoff_gif),
SSPEC_RESOURCE_XMEMFILE("/button.gif", button_gif),
SSPEC_RESOURCE_XMEMFILE("/ssi.c", ssi_c),

  SSPEC_RESOURCE_ROOTVAR("led1", led1, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led2", led2, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led3", led3, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led4", led4, PTR16, "%s"),

  SSPEC_RESOURCE_FUNCTION("/led1tog.cgi", led1toggle),
SSPEC_RESOURCE_FUNCTION("/led2tog.cgi", led2toggle),
SSPEC_RESOURCE_FUNCTION("/led3tog.cgi", led3toggle),
SSPEC_RESOURCE_FUNCTION("/led4tog.cgi", led4toggle)
SSPEC_RESOURCETABLE_END


void 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 old style of CGI (SSPEC_RESOURCE_FUNCTION). Use of SSI is explained in greater detail below.

4.5.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 conventionally recognized by the HTTP server by the resource name extension .shtml.1

The supported SSI commands are:

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 i.e. and old-style CGI and replaces the SSI command with the output.

Dynamically Changing the Display of a Variable on a Web Page

The Ssi.shtml file, located in \Samples\Tcpip\Http\Pages, 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 static resource table.


SSPEC_RESOURCETABLE_START
...
SSPEC_RESOURCE_ROOTVAR("led1", led1, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led2", led2, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led3", led3, PTR16, "%s"),
SSPEC_RESOURCE_ROOTVAR("led4", led4, PTR16, "%s"),
...
SSPEC_RESOURCETABLE_END

shtml_handler (which is the built-in script processor for SSI) 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.

SSI string variables are only appropriate for relatively short strings. (In the above example, the SSI string variables are "ledon.gif" and "ledoff.gif.") The size that can be output is limited to the size HTTP_MAXBUFFER. If you need larger strings, you should either increase HTTP_MAXBUFFER (which will use more root RAM) or switch to using a CGI function.

4.5.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/

and many other web sites and books. In the Ssi.shtml file, the following 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.

This sample demonstrates the so-called "old-style" CGI. New-style CGIs are easier to write (especially when they are doing something non-trivial). They are described in Section 4.6 "HTTP File Upload."

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 a non-zero value 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.5.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.5.3.1, 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.

Starting with Dynamic C 8.50, you can also use the enctype option inside the FORM tag. This specifies a return encoding type for the form's information. If you did not specify this option, then you can use old-style CGIs (as described in this section). If you specify enctype="multipart/form-data" then you should specify a new-style CGI instead. See Section 4.6 describing the HTTP upload feature for more details on writing a new-style CGI.

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.5.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 shown here:

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 examine the new values, and assign them to the appropriate C variables if they are valid.

4.5.3.2 POST-Style Form Submission

If an HTML file specifies a POST-style form submission (that is, METHOD="POST"), the form will still be waiting on the socket when the old-style 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 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.5.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() is called from the CGI function (submit()) to read URL-encoded data off the network. It calls http_scanpost() to store the data in FORMSpec[]. These code snippets are from Samples\tcpip\http\post.c.


int parse_post(HttpState *state) {
auto int retval;
auto int i;

  retval = sock_aread(&state->s, state->p,\
(state->content_length < HTTP_MAXBUFFER-1)?\
(int)state->content_length:HTTP_MAXBUFFER-1);

  if (retval < 0)
return 1;

  state->subsubstate += retval;

  if (state->subsubstate >= state->content_length) {
state->buffer[(int)state->content_length] = '\0';
for(i=0; i<(sizeof(FORMSpec)/sizeof(FORMType)); i++) {
http_scanpost(FORMSpec[i].name, state->buffer,\
FORMSpec[i].value, MAX_FORMSIZE);
}
return 1;
}
return 0;
}

4.5.3.4 Sample of a CGI Handler

This next function is the CGI handler that calls parse_post(). 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:                     // initialize 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.5.4 HTML Forms Using Zserver.lib

In this section, we will step through a sample 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 clear. (As of Dynamic C 8.50, you have the option of using the RabbitWeb server, with its easier-to-use interface and completely flexible ZHTML page layout capabilities.

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 that 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 static resource table, we can define the following macro, to remove some code for handling this table from Zserver.


#define HTTP_NO_FLASHSPEC

These lines are part of the standard TCP/IP and MIME table configuration.


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

SSPEC_MIMETABLE_START
SSPEC_MIME(".html", "text/html")
SSPEC_MIMETABLE_END

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 dynamic resource table. The first parameter gives the name of the form. 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 dynamic resource table.


  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 resource table. It must be added to this table 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 handle of the variable in the resource table.


  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 resource table ("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 dynamic resource table 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 resource table 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.6 HTTP File Upload

This section describes the HTTP file upload feature available starting with Dynamic C 8.50. The enhanced CGI capabilities of this version of Dynamic C allow files of unlimited size to be uploaded using a web interface. It has always been possible to upload files using FTP; however, it is usually more convenient to use a browser-based upload.

4.6.1 What is a CGI Function and Why is It Useful?

The HTTP library provided with Dynamic C allows the association of C functions with web page URLs. When the user, via their web browser, retrieves a specified resource, the C function may be called from the HTTP server. Such a function is called a Common Gateway Interface (CGI) function, and it is responsible for generating a response to the user's request.

The advantage of using a CGI is that it can generate web page content on-the-fly, and cause the browser to display or do anything that it is capable of. In addition, the CGI is able to read data that was sent by the browser.

Previous to this release of Dynamic C, the CGI was limited to handling relatively small amounts of data sent from the browser. This is satisfactory for processing simple forms, but does not allow large data sets to be uploaded. This release of Dynamic C supports upload of one or more files from the browser. The files can be of unlimited size. In conjunction with the latest Zserver (resource manager) enhancements introduced in Dynamic C 8.50, the uploaded files may be stored in the FS2 or FAT file systems, or even processed dynamically.

The new CGI file upload facility enables a range of convenient firmware features. Possibilities include:

4.6.2 How Do I Use the New CGI Facility?

There are a number of steps, some of which will be familiar to users of CGIs in previous releases. They are listed here and described in more detail in the following pages. The steps, if coding from scratch, are:

  1. #use "dcrtcp.lib", and specify network configuration options.

  2. #use <filesystem(s) of choice>, and specify the file system configuration.

  3. #define USE_HTTP_UPLOAD

  4. #use "http.lib"

  5. Create an initial web page with a form asking for the file(s) to be uploaded. The main requirement is that you specify enctype="multipart/form-data" inside the <FORM> tag(s).

  6. Write a CGI function (if not using the default one provided).

  7. Create an initial resource table containing at least an entry for each of the above two resources (the web page and the CGI).

  8. Create a list of content type mappings, i.e., the MIME table.

  9. Create rules which limit the upload facility to select user groups.

  10. Create a set of user IDs

  11. In the main program, call http_handler() in a loop.

Step 1: Specify Network Configuration

To make use of HTTP upload, you need to perform the usual inclusion and configuration of the networking library, dcrtcp.lib. At its simplest, it is two lines of code at the top of your main program:


#define TCPCONFIG 1
#use "dcrtcp.lib"

This specifies that the default TCP (networking) configuration is to be used. If you want to change the default networking configuration, first read the comments at the top of tcp_config.lib.

HTTP upload usually requires at least two additional libraries to be included: a file system library, and http.lib itself. A file system is required, otherwise the uploaded file has nowhere to go (although you can write a CGI which processes the file as it is uploaded, in which case you do not need to store it permanently, and thus you do not need to include a file system; the following discussion assumes that you are using a file system).

Steps 2, 3 and 4: Specify File system and Web Server

You need to include the file system library (or libraries) before including http.lib. This is because the HTTP library needs to know about the filesystem(s) it is going to support. In addition, you need to tell the HTTP library to use the upload facility. For example, if you want to use the FAT file system, then you would write the following:


#define TCPCONFIG 1
#use "dcrtcp.lib"
#use "fat.lib"               //
Step 2: the filesystem
#define USE_HTTP_UPLOAD      // Step 3: enable upload feature
#use "http.lib"              // Step 4: HTTP server code

The order of the above statements is important. A possible exception is that the order of dcrtcp.lib and fat.lib may be interchanged, since these libraries are independent. However, it is recommended you use the given ordering since future releases of the FAT may be able to use networking services.

Step 5: Create a Web Page

When using HTTP upload, there needs to be a way to prompt the user (web browser) to enter a file name to upload. This is done by using an HTML form. The form specifies input fields that may be filled out by the user, and one or more "submit" buttons that the user presses to start the upload process.

If you have an existing web-based application to which you want to add a file upload facility, you probably already have a web page with a form on it; in this case, you can add an extra input field to an existing form on that page, or create a new form on the same page. You may already have a CGI function that processes the results of the form submission. This will need to be rewritten to process data that is not URL encoded.

If you are creating a new application, you need to construct an initial page to contain the necessary form elements. As a starting point you can use the sample page in samples\tcpip\http\pages\upload.html. Click on upload.html and the browser will display something like this:


The construction of this page is outlined below, but it has been simplified and reformatted slightly. A blow-by-blow description of each line is added in italics.


<html>

This introduces the page as an HTML document.


<head><title>HTTP Upload Form</title></head>

This ("HTTP Upload Form") gets displayed at the top of the browser window. You can change this to whatever is appropriate for describing the overall purpose of this page.


<body>

Introduce the main content of this page.


<FORM ACTION="upload.cgi" METHOD="POST" enctype="multipart/form-data">

Start a form definition. The parameters are
action="upload.cgi": this refers to the CGI function that will process the results of the form submission. This is a URL name, which is mapped to a C function on the server.

method=post: this is required, since a post-type request must be sent to the server.

enctype="multipart/form-data": this is also required, and is the part that is different from the old style of processing. The old style did not specify an encoding type, thus the default of "URL encoded" was used.


   <TABLE BORDER=0 CELLSPACING=2 CELLPADDING=1>

For neatness of screen layout, we put everything in an HTML table. The following <TR>...</TR> sections delimit each row of the table, and the data for each cell is delimited by <TD>...</TD>.


   <TR>
<TD WIDTH=130 ALIGN=RIGHT><B>Name</B></TD>
<TD WIDTH=500><INPUT TYPE="TEXT" NAME="user_name" SIZE=50></TD>

This is the first input field. It is not a file to upload, but it is information that the server may nevertheless be interested in. This shows that not every form field needs to be a file to upload. The order is important. Browsers will send back the form fields in the same order that they are defined in the HTML, however it is probably best not to rely on this if you can help it.


   </TR>
<TR>
<TD ALIGN=RIGHT><B>File to upload<BR>(to /A/new.htm)</B></TD>
<TD><INPUT TYPE="FILE" NAME="/A/new.htm" SIZE=50></TD>

This is the file-to-upload input field. The browser displays this as a text input field, with an additional "browse" button so that the user can easily navigate his local filesystem to find the appropriate file. The critical distinction is that it contains a type=file parameter (as opposed to, for example, type=text in the previous field). The name="/A/new.htm" parameter specifies the name of the input field, not the name of the file on the user's system! As it happens, this looks like a file name, and indeed the server may use it as the name of a local file, but this is a convention only. The size=50 parameter specifies the number of characters that the browser will display for file name selection.


   <TR>
</TABLE>
<INPUT TYPE="SUBMIT" VALUE="Upload">

It is necessary to supply a type=submit form element. The user presses this button to start to post (upload) process. Note that this is another input field, however if you leave out the name= parameter (as in this example) then the browser will not send the value of this button back with the form submission. If there is only one submit button, then there is no need to name it.


</FORM></body></html>

Close and complete the form, body, and entire page.

If you have an existing application, you can take out the relevant parts of the above, and insert them in your existing web page. The relevant parts are the enctype="multipart/form-data" parameter in the <FORM> element, and the <INPUT type=file> element.

If you have an existing application that processes the form data submission, you will need to rewrite the CGI function that handles the submitted data. This is because the enctype parameter changes the syntax that the browser uses to encode the data. In short, you will need to rewrite the CGI as a "new-style" CGI as described in Step 6: Writing a CGI Function.

Having created the HTML file with the upload form, it is necessary to import it into your main program, so that the HTTP server can present it to the user. This can be done using #ximport, or you can write it directly to the filesystem (although, initially at least, this presents a chicken-and-egg type problem since you might not have established an upload procedure in the first place!)

Step 6: Writing a CGI Function

The CGI function is responsible for processing the form submission data as it comes in from the client (browser). In addition, it generally needs to write some sort of response back to the client indicating whether or not the submission was acceptable.

If you start reading the following, and start feeling somewhat overwhelmed, please be aware that there is a default CGI function in the HTTP library that is very useful. The default CGI, called http_defaultCGI(), automatically saves uploaded files into the filesystem. If that is all you need to do, then you do not need to fully comprehend this section on first reading.

Note that all of this section is describing new-style CGIs. Old-style CGIs are covered in Section 4.5.3.

CGI Syntax

All CGI functions are C functions with the following prototype:


int my_CGI(HttpState * s);

The HttpState parameter is a pointer to the internal state variables of the HTTP server instance that is handling the current request. You can have one or more server instances. If there is more than one, the same CGI may be invoked at the same time for more than one client (if both happen to press the submit button at about the same time). Thus, it is important to write the CGI function so that it is re-entrant. This basically means that the function should not update global or static variables. The CGI should not attempt to modify directly any of the fields in the HttpState structure, otherwise the server may become inoperable.

API Functions

The HTTP library provides a set of API functions that can be called safely from the CGI. The list of safe functions is in the index under "Function Reference, CGI."

It is unwise to make direct calls to TCP/IP functions, especially functions that may not return for a long time such as sock_read().

How to Transfer Form Submission Data

To understand how to write a CGI function, it is necessary to have some understanding of the protocol used to transfer the form submission data. Since the data can consist of one or more files and/or form fields, there needs to be a way of separating them within the one, sequential, stream of data that is sent by the client.

The way this is done is that the client specifies a unique string that separates each item of data. The following text is a dump of the actual data sent by a client (with some irrelevant details omitted, and with comments added in italics):


POST /upload.cgi HTTP/1.1

This indicates that it is POSTed form data, and the target handler is upload.cgi.


Content-length: 277

This gives the total number of bytes of data following the initial header.


Content-Type: multipart/form-data; boundary=3vAL1QsFOUg2GsY3p6n3YQ

The multipart/form-data type indicates that this is a multipart form data submission. The boundary parameter specifies a unique character sequence that separates each part. The boundary is deliberately chosen as a long, random, string of characters so that it is unlikely to be confused with the actual data content.



The above blank line is significant; it indicates the end of the initial header lines, and the start of data.


--3vAL1QsFOUg2GsY3p6n3YQ

This is the first boundary. Boundary strings are always prefixed by an additional -- sequence. The following lines are header lines for the individual part. The actual data follows the first empty line.


Content-Disposition: form-data; name="/A/new.htm"; filename="test.txt"

The Content-Disposition header indicates the presentation of the data. The only type which is relevant is "form-data". The name= parameter indicates the field name (which was originally part of the name= parameter of the <input> element). The filename= parameter is only set if this is an uploaded file. It gives the name of the file on the remote (client) side. This is not usually relevant to the server. The name of the file as it is stored on the server is not specified (since the browser does not know it or have control over where the file is stored). We are using the convention that the field name indicates the local file name, but this is just a convention!


Content-Type: text/plain

Content-Type indicates the type of information. The default is plain (i.e. ascii) text, however it could also be set to image/gif for a GIF file, text/html for HTML etc. The following blank line indicates the end of headers for this part.


test file contents, first line

This is the actual file or form field content.


--3vAL1QsFOUg2GsY3p6n3YQ

The boundary string terminates the data for the previous part. Headers for the next part immediately follow.


Content-Disposition: form-data; name="submit"

This is form field data, in this case the submit button itself.

<