Ethernut Home Hardware Firmware Tools Download Community
 
 
Search | Legals | Deutsch

MicroHTTP Library: Common Gateway Interface

Like its predecessor, the MicroHTTP library supports CGI functions. This still works a in most simple way.

HTML Forms With GET Method

CGI is most often used to request user input via HTML forms. The following form lets the user to enter his first and his family name:

<html>
<head>
<title>Form Sample</title>
</head>
<body>
<h1>A Form</h1>
<form action="getform.cgi" method="get">
  <p>First name:<br><input name="firstname" type="text" size="30" maxlength="30"></p>
  <p>Family name:<br><input name="familyname" type="text" size="30" maxlength="40"></p>
  <p><input type="submit" value="Send"></p>
</form>
</body>
</html>

When the Send button is clicked, the browser will request the CGI script getform.cgi with the GET method. This means, that the values of the input fields are appended as arguments to the requested URL as name=value pairs. The webserver then passes this URL to the CGI function.

To simplify CGI function programming, the MicroHTTP library provides a set of useful functions to parse the URL for parameters.

When using these functions, processing the HTML form given above is quite simple. Here is the complete code:

int CgiGetForm(HTTPD_SESSION *hs)
{
    char *arg;
    char *val;
    char *first = NULL;
    char *last = NULL;

    for (arg = HttpArgParseFirst(&hs->s_req); arg; arg = HttpArgParseNext(&hs->s_req)) {
        val = HttpArgValue(&hs->s_req);
        if (val) {
            if (strcmp(arg, "firstname") == 0) {
                first = strdup(val);
            }
            else if (strcmp(arg, "familyname") == 0) {
                last = strdup(val);
            }
        }
    }
    SendResult(hs, first, last);
    free(first);
    free(last);

    return 0;
}

The original Nut/OS HTTP library called CGI functions with a FILE stream pointer and a pointer to a request structure. MicroHTTP instead passes a single pointer to a HTTPD_SESSION structure, which, among other things, contains a HTTP_STREAM pointer and a slightly different request structure. Nevertheless, it shouldn't be too hard to port existing CGI functions to the new library.

Please note, that the parser functions simply return pointers to a static buffer, which will be overridden when parsing the next argument. Therefore, the CGI function must make a local copy (strdup) of each string which will be used afterwards.

Some of you may already have noticed, that the code is not as complete as it pretends to be. Indeed, the response to the webbrowser is missing. The MicroHTTP webserver expects, that the complete response is sent by the CGI function, including all header lines. This is done in the function SendResult that is called near the end of the CGI function. To simplify this task, two functions are provided by the library to cover standard responses.

Splitting HTTP header line transmission into two functions enables the CGI to send additional lines between both calls. The latter function will also send an empty line to terminate the HTTP header. The CGI may directly append the content thereafter. Here is the source code of the missing function:

int SendResult(HTTPD_SESSION *hs, char *first, char *last)
{
    static const char head[] =
        "<html>"
        "<head>"
        "<title>Form Result</title>"
        "</head>";
    static const char body[] =
        "<body>"
        "<p>Hello %s %s!</p>"
        "<a href=\"/index.html\">back</a>"
        "</body>"
        "<html>";

    HttpSendHeaderTop(hs, 200);
    HttpSendHeaderBottom(hs, "text", "html", -1);

    s_puts(head, hs->s_stream);
    s_printf(hs->s_stream, body, first, last);
    s_flush(hs->s_stream);

    return 0;
}

Of course, this code could have been included into the CGI function. We use a separate function here, so we can reuse it when explaining the POST method in the next chapter.

HTML Forms With POST Method

The first problem you may encounter with the GET method is its size limitation. MicroHTTP is designed to run on tiny embedded systems with a 32 kilobytes of RAM or less. As it is often required to process all HTTP headers before parsing the URL, which always comes in the first line, there may not be enough memory space for large forms. Furthermore, even webbrowsers, which typically occupy megabytes or gigabytes of memory, do have a tight limit here. After the first release of HTTP routines in Nut/OS with POST method capabilities, application developers missed this feature immediately.

To instruct the browser to use POST, we simply need to replace a single line in our HTML document.

<form action="getform.cgi" method="get">

must be replaced by

<form action="postform.cgi" method="post">

Actually, the CGI function doesn't require many modifications. The most obvious one is, that it needs to keep track of the content length. Futhermore, HttpArgParseFirst() is not required, HttpArgParseNext() is replaced by HttpArgReadNext(). The value is retrieved by the same function as used with the GET method, HttpArgValue().

static int CgiPostForm(HTTPD_SESSION *hs)
{
    char *arg;
    char *val;
    char *first = NULL;
    char *last = NULL;
    long avail;

    avail = hs->s_req.req_length;
    while (avail) {
        arg = HttpArgReadNext(hs, &avail);
        if (arg) {
            val = HttpArgValue(&hs->s_req);
            if (val) {
                if (strcmp(arg, "firstname") == 0) {
                    first = strdup(val);
                }
                else if (strcmp(arg, "familyname") == 0) {
                    last = strdup(val);
                }
            }
        }
    }
    SendResult(hs, first, last);
    free(first);
    free(last);

    return 0;
}

For the response we can use the same function SendResult(), that had been used in the previous chapter above.

Webserver Code

The webserver is started in the same way as in the minimal sample:

StreamInit();
MediaTypeInitDefaults();
HttpRegisterCgiFunction("getform.cgi", CgiGetForm);
HttpRegisterCgiFunction("postform.cgi", CgiPostForm);
HttpRegisterMediaType("cgi", NULL, NULL, HttpCgiFunctionHandler);
StreamClientAccept(HttpdClientHandler, NULL);

Two additional calls have been added to register the two CGI functions, one is used for the GET and another one of the POST method. In addition, we need to register a CGI media type, using the standard handler HttpCgiFunctionHandler of the MicroHTTP library. This handler will call any registered CGI function when the browser requests an URL with the extension cgi. Further details about media type handlers will be discussed later.

Next Step

We can use CGIs to create all kind of dynamic content. However, in many cases only parts of a web page need to updated. Creating complete HTML pages within CGI functions may be an annoying job and requires to create a new runtime binary each time the web content is changed. Server side includes provide a smarter solution.