Contents

Overview

With HTML forms you can use the GET method to process user input. This is quite simple to use, particularly because Nut/OS provides the three API functions NutHttpGetParameterCount, NutHttpGetParameterName and NutHttpGetParameterValue to extract the parameters and its values from the request string.

However, the total size of the request string is limited with most browsers and HTTP servers. In Nut/OS the maximum size is specified by HTTP_MAX_REQUEST_SIZE, which is 256 bytes only, by default. As an alternative, the POST method may be used instead.

I will present a simple web-based chat application to demonstrate how to use the POST method in Nut/OS.

The Chat Server

This application allows any number of users to chat online, using a webbrowser to connect to a chat server running on an Ethernut board.

Header Files

System configurations are stored in

<source lang="c">

include <cfg/os.h>

</source>

and can be used to set up the right thread stack size, based on the CPU architecture and the compile mode. When compiling with debug information, more stack is required and NUT_THREAD_STACK_MULT and NUT_THREAD_STACK_ADD will have higher values.

The second variable chat_len keeps track of the total number of bytes contained in all lines.

main

When the system is started, Nut/OS will do some internal initialization and then call function main in the application code. Our chat application is implemented by an HTTP server and the main routine looks quite similar to the main routine of the HTTP daemon sample included in the Nut/OS distribution.

HTTP Server Threads

As you may know, Nut/OS doesn't provide a backlog for incoming connections. If a TCP connection is established, incoming request for additional connections to the same port are rejected. The typical solution is to start more than one thread to service a specific TCP port. This is done in the main function on lines 355-357, where 4 additional threads are started.

The thread function itself does nothing else but calling ProcessNextClient in an endless loop.

The same loop is used at the end of the main function (lines 359-361), so in total 5 threads are running to server up to 5 concurrent client requests. Each thread calls ProcessNextClient over and over again.

CGIs

In function ProcessNextClient the Nut/OS API function NutHttpProcessRequest is called to process the HTTP request. No further effort is required to handle static content like HTML files or images.

Dynamic content may be created by CGI routines. Two such CGI routines are registered in lines 351 and 352 of the main function, SendForm and ProcessForm, registered for the URLs /cgi-bin/form.cgi and /cgi-bin/post.cgi, resp. If a client requests one of these, then NutHttpProcessRequest will call its registered routine.

The first CGI routine SendForm will send an HTML page containing a simple form.

<source lang="c" line start="160">

/*!

* \brief Send HTML page with a chat form to the client.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL '/cgi-bin/form.cgi'.
*
* \param stream Stream of the HTTP connection.
* \param req HTTP request information.
*
* \return Always 0.
*/

/* Append the new chat line to the chat line array. Note,
that we add 11 to the size of a chat line to reflect
the size of the surrounding list tags. These tags are
not stored in the line buffer. */
if (chat_lines[0]) {
chat_len -= strlen(chat_lines[0]) + 11;
free(chat_lines[0]);
}
for (i = 0; i < MAX_CHAT_LINES - 1; i++) {
chat_lines[i] = chat_lines[i + 1];
}
chat_lines[i] = strdup(buf);
chat_len += strlen(buf) + 11;
}
}
free(buf);
}

To access the form, the user must request the URL http://mynutip/cgi-bin/form.cgi, which is a bit complicated. Just entering http://mynutip will result in error 404, because in this case the API will look for index.html, index.shtml or similar.

A nice solution is to create index.html containing a redirection to our CGI.

<source lang="html4strict">
<html>
<head>

<meta http-equiv="refresh" content="0; URL=/cgi-bin/form.cgi">

</head>
</html>
</source>

This file will be placed in subdirectory www, which will be converted by crurom into urom.c.

Another way of processing POSTed form data

Current Nut/OS directly support the POST method when processing a form submit. Instead of manually parsing the form data like in the example above you can just use the function "NutHttpProcessPostQuery(stream, req);". The same CGI would look like this:

<source lang="c" line start="206">

/*!

* \brief Process a POST of the chat form in the same way as using a GET request
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL '/cgi-bin/post_new_style.cgi'.
*
* \param stream Stream of the HTTP connection.
* \param req HTTP request information.
*
* \return Always 0.
*/

/* Append the new chat line to the chat line array. Note,
that we add 11 to the size of a chat line to reflect
the size of the surrounding list tags. These tags are
not stored in the line buffer. */
if (chat_lines[0]) {
chat_len -= strlen(chat_lines[0]) + 11;
free(chat_lines[0]);
}
for (i = 0; i < MAX_CHAT_LINES - 1; i++) {
chat_lines[i] = chat_lines[i + 1];
}
chat_lines[i] = strdup(value);
chat_len += strlen(value) + 11;
}
}
}

POST Method Processing in Application Code

Early versions of the Nut/OS API did not directly support the POST method when processing a form submit. This has to be done by the application. This is _not_ needed if you implement your CGIs using the NutHttpProcessPostQuery(stream, req); method as shown in the above sample.

* \brief Read encoded string from stream.
*
* This function reads a specified number of characters from a stream,
* but not beyond a given end-of-string character.
*
* \param stream The stream to read from.
* \param str Pointer to a buffer that receives the decoded string.
* The string will be terminated with ASCII 0.
* \param siz Maximum number of characters to read.
* \param eos End of string character.
*
* \return Number of characters read or -1 on errors.
*/