PREV NEXT INDEX



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:

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 Ssi.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 FormVars 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
PREV NEXT INDEX