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