|
Packit |
875988 |
The previous chapters already have demonstrated a variety of possibilities to send information
|
|
Packit |
875988 |
to the HTTP server, but it is not recommended that the @emph{GET} method is used to alter the way
|
|
Packit |
875988 |
the server operates. To induce changes on the server, the @emph{POST} method is preferred over
|
|
Packit |
875988 |
and is much more powerful than @emph{GET} and will be introduced in this chapter.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
We are going to write an application that asks for the visitor's name and, after the user has posted it,
|
|
Packit |
875988 |
composes an individual response text. Even though it was not mandatory to use the @emph{POST} method here,
|
|
Packit |
875988 |
as there is no permanent change caused by the POST, it is an illustrative example on how to share data
|
|
Packit |
875988 |
between different functions for the same connection. Furthermore, the reader should be able to extend
|
|
Packit |
875988 |
it easily.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@heading GET request
|
|
Packit |
875988 |
When the first @emph{GET} request arrives, the server shall respond with a HTML page containing an
|
|
Packit |
875988 |
edit field for the name.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
const char* askpage = "<html><body>\
|
|
Packit |
875988 |
What's your name, Sir? \
|
|
Packit |
875988 |
<form action=\"/namepost\" method=\"post\">\
|
|
Packit |
875988 |
|
|
Packit |
875988 |
<input type=\"submit\" value=\" Send \"></form>\
|
|
Packit |
875988 |
</body></html>";
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
The @code{action} entry is the @emph{URI} to be called by the browser when posting, and the
|
|
Packit |
875988 |
@code{name} will be used later to be sure it is the editbox's content that has been posted.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
We also prepare the answer page, where the name is to be filled in later, and an error page
|
|
Packit |
875988 |
as the response for anything but proper @emph{GET} and @emph{POST} requests:
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
const char* greatingpage="<html><body>Welcome, %s!</center></body></html>";
|
|
Packit |
875988 |
|
|
Packit |
875988 |
const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
Whenever we need to send a page, we use an extra function
|
|
Packit |
875988 |
@code{int send_page(struct MHD_Connection *connection, const char* page)}
|
|
Packit |
875988 |
for this, which does not contain anything new and whose implementation is therefore
|
|
Packit |
875988 |
not discussed further in the tutorial.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@heading POST request
|
|
Packit |
875988 |
Posted data can be of arbitrary and considerable size; for example, if a user uploads a big
|
|
Packit |
875988 |
image to the server. Similar to the case of the header fields, there may also be different streams
|
|
Packit |
875988 |
of posted data, such as one containing the text of an editbox and another the state of a button.
|
|
Packit |
875988 |
Likewise, we will have to register an iterator function that is going to be called maybe several times
|
|
Packit |
875988 |
not only if there are different POSTs but also if one POST has only been received partly yet and
|
|
Packit |
875988 |
needs processing before another chunk can be received.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
Such an iterator function is called by a @emph{postprocessor}, which must be created upon arriving
|
|
Packit |
875988 |
of the post request. We want the iterator function to read the first post data which is tagged
|
|
Packit |
875988 |
@code{name} and to create an individual greeting string based on the template and the name.
|
|
Packit |
875988 |
But in order to pass this string to other functions and still be able to differentiate different
|
|
Packit |
875988 |
connections, we must first define a structure to share the information, holding the most import entries.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
struct connection_info_struct
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
int connectiontype;
|
|
Packit |
875988 |
char *answerstring;
|
|
Packit |
875988 |
struct MHD_PostProcessor *postprocessor;
|
|
Packit |
875988 |
};
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
With these information available to the iterator function, it is able to fulfill its task.
|
|
Packit |
875988 |
Once it has composed the greeting string, it returns @code{MHD_NO} to inform the post processor
|
|
Packit |
875988 |
that it does not need to be called again. Note that this function does not handle processing
|
|
Packit |
875988 |
of data for the same @code{key}. If we were to expect that the name will be posted in several
|
|
Packit |
875988 |
chunks, we had to expand the namestring dynamically as additional parts of it with the same @code{key}
|
|
Packit |
875988 |
came in. But in this example, the name is assumed to fit entirely inside one single packet.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
static int
|
|
Packit |
875988 |
iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
|
|
Packit |
875988 |
const char *filename, const char *content_type,
|
|
Packit |
875988 |
const char *transfer_encoding, const char *data,
|
|
Packit |
875988 |
uint64_t off, size_t size)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
struct connection_info_struct *con_info = coninfo_cls;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
if (0 == strcmp (key, "name"))
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
if ((size > 0) && (size <= MAXNAMESIZE))
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
char *answerstring;
|
|
Packit |
875988 |
answerstring = malloc (MAXANSWERSIZE);
|
|
Packit |
875988 |
if (!answerstring) return MHD_NO;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
snprintf (answerstring, MAXANSWERSIZE, greatingpage, data);
|
|
Packit |
875988 |
con_info->answerstring = answerstring;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
else con_info->answerstring = NULL;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
return MHD_NO;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
|
|
Packit |
875988 |
return MHD_YES;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
Once a connection has been established, it can be terminated for many reasons. As these
|
|
Packit |
875988 |
reasons include unexpected events, we have to register another function that cleans up any resources
|
|
Packit |
875988 |
that might have been allocated for that connection by us, namely the post processor and the greetings
|
|
Packit |
875988 |
string. This cleanup function must take into account that it will also be called for finished
|
|
Packit |
875988 |
requests other than @emph{POST} requests.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
void request_completed (void *cls, struct MHD_Connection *connection,
|
|
Packit |
875988 |
void **con_cls,
|
|
Packit |
875988 |
enum MHD_RequestTerminationCode toe)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
struct connection_info_struct *con_info = *con_cls;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
if (NULL == con_info) return;
|
|
Packit |
875988 |
if (con_info->connectiontype == POST)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
MHD_destroy_post_processor (con_info->postprocessor);
|
|
Packit |
875988 |
if (con_info->answerstring) free (con_info->answerstring);
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
|
|
Packit |
875988 |
free (con_info);
|
|
Packit |
875988 |
*con_cls = NULL;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@emph{GNU libmicrohttpd} is informed that it shall call the above function when the daemon is started
|
|
Packit |
875988 |
in the main function.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
...
|
|
Packit |
875988 |
daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
|
|
Packit |
875988 |
&answer_to_connection, NULL,
|
|
Packit |
875988 |
MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL,
|
|
Packit |
875988 |
MHD_OPTION_END);
|
|
Packit |
875988 |
...
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@heading Request handling
|
|
Packit |
875988 |
With all other functions prepared, we can now discuss the actual request handling.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
On the first iteration for a new request, we start by allocating a new instance of a
|
|
Packit |
875988 |
@code{struct connection_info_struct} structure, which will store all necessary information for later
|
|
Packit |
875988 |
iterations and other functions.
|
|
Packit |
875988 |
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
static int
|
|
Packit |
875988 |
answer_to_connection (void *cls, struct MHD_Connection *connection,
|
|
Packit |
875988 |
const char *url,
|
|
Packit |
875988 |
const char *method, const char *version,
|
|
Packit |
875988 |
const char *upload_data,
|
|
Packit |
875988 |
size_t *upload_data_size, void **con_cls)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
if(NULL == *con_cls)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
struct connection_info_struct *con_info;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
con_info = malloc (sizeof (struct connection_info_struct));
|
|
Packit |
875988 |
if (NULL == con_info) return MHD_NO;
|
|
Packit |
875988 |
con_info->answerstring = NULL;
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
If the new request is a @emph{POST}, the postprocessor must be created now. In addition, the type
|
|
Packit |
875988 |
of the request is stored for convenience.
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
if (0 == strcmp (method, "POST"))
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
con_info->postprocessor
|
|
Packit |
875988 |
= MHD_create_post_processor (connection, POSTBUFFERSIZE,
|
|
Packit |
875988 |
iterate_post, (void*) con_info);
|
|
Packit |
875988 |
|
|
Packit |
875988 |
if (NULL == con_info->postprocessor)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
free (con_info);
|
|
Packit |
875988 |
return MHD_NO;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
con_info->connectiontype = POST;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
else con_info->connectiontype = GET;
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
The address of our structure will both serve as the indicator for successive iterations and to remember
|
|
Packit |
875988 |
the particular details about the connection.
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
*con_cls = (void*) con_info;
|
|
Packit |
875988 |
return MHD_YES;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
The rest of the function will not be executed on the first iteration. A @emph{GET} request is easily
|
|
Packit |
875988 |
satisfied by sending the question form.
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
if (0 == strcmp (method, "GET"))
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
return send_page (connection, askpage);
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
In case of @emph{POST}, we invoke the post processor for as long as data keeps incoming, setting
|
|
Packit |
875988 |
@code{*upload_data_size} to zero in order to indicate that we have processed---or at least have
|
|
Packit |
875988 |
considered---all of it.
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
if (0 == strcmp (method, "POST"))
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
struct connection_info_struct *con_info = *con_cls;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
if (*upload_data_size != 0)
|
|
Packit |
875988 |
{
|
|
Packit |
875988 |
MHD_post_process (con_info->postprocessor, upload_data,
|
|
Packit |
875988 |
*upload_data_size);
|
|
Packit |
875988 |
*upload_data_size = 0;
|
|
Packit |
875988 |
|
|
Packit |
875988 |
return MHD_YES;
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
else if (NULL != con_info->answerstring)
|
|
Packit |
875988 |
return send_page (connection, con_info->answerstring);
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
Finally, if they are neither @emph{GET} nor @emph{POST} requests, the error page is returned.
|
|
Packit |
875988 |
@verbatim
|
|
Packit |
875988 |
return send_page(connection, errorpage);
|
|
Packit |
875988 |
}
|
|
Packit |
875988 |
@end verbatim
|
|
Packit |
875988 |
@noindent
|
|
Packit |
875988 |
|
|
Packit |
875988 |
These were the important parts of the program @code{simplepost.c}.
|