aboutsummaryrefslogtreecommitdiff
path: root/doc/chapters/processingpost.inc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/chapters/processingpost.inc')
-rw-r--r--doc/chapters/processingpost.inc231
1 files changed, 231 insertions, 0 deletions
diff --git a/doc/chapters/processingpost.inc b/doc/chapters/processingpost.inc
new file mode 100644
index 00000000..0677c7e8
--- /dev/null
+++ b/doc/chapters/processingpost.inc
@@ -0,0 +1,231 @@
1The previous chapters already have demonstrated a variety of possibilities to send information
2to the HTTP server, but it is not recommended that the @emph{GET} method is used to alter the way
3the server operates. To induce changes on the server, the @emph{POST} method is preferred over
4and is much more powerful than @emph{GET} and will be introduced in this chapter.
5
6We are going to write an application that asks for the visitor's name and, after the user has posted it,
7composes an individual response text. Even though it was not mandatory to use the @emph{post} method here,
8as there is no permanent change caused by the post, it is an illustrative example on how to share data
9between different functions for the same connection. Furthermore, the reader should be able to extend
10it easily.
11
12@heading GET request
13When the first @emph{GET} request arrives, the server shall respond with a HTML page containing an
14edit field for the name.
15
16@verbatim
17const char* askpage = "<html><body>\
18 What's your name, Sir?<br>\
19 <form action=\"/namepost\" method=\"post\">\
20 <input name=\"name\" type=\"text\"\
21 <input type=\"submit\" value=\" Send \"></form>\
22 </body></html>";
23@end verbatim
24@noindent
25
26The @code{action} entry is the @emph{URI} to be called by the browser when posting, and the
27@code{name} will be used later to be sure it is the editbox's content that has been posted.
28
29We also prepare the answer page, where the name is to be filled in later, and an error page
30as the response for anything but proper @emph{GET} and @emph{POST} requests:
31
32@verbatim
33const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>";
34
35const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
36@end verbatim
37@noindent
38
39Whenever we need to send a page, we use an extra function
40@code{int SendPage(struct MHD_Connection *connection, const char* page)}
41for this, which does not contain anything new and whose implementation is therefore left out here.
42
43
44@heading POST request
45Posted data can be of arbitrary and considerable size; for example, if a user uploads a big
46image to the server. Similar to the case of the header fields, there may also be different streams
47of posted data, such as one containing the text of an editbox and another the state of a button.
48Likewise, we will have to register an iterator function that is going to be called maybe several times
49not only if there are different POSTs but also if one POST has only been received partly yet and
50needs processing before another chunk can be received.
51
52Such an iterator function is called by a @emph{postprocessor}, which must be created upon arriving
53of the post request. We want the iterator function to read the first post data which is tagged
54@code{name} and to create an individual greeting string based on the template and the name.
55But in order to pass this string to other functions and still be able to differentiate different
56connections, we must first define a structure to share the information, holding the most import entries.
57
58@verbatim
59struct connection_info_struct
60{
61 int connectiontype;
62 char *answerstring;
63 struct MHD_PostProcessor *postprocessor;
64};
65@end verbatim
66@noindent
67
68With these information available to the iterator function, it is able to fulfill its task.
69Once it has composed the greeting string, it returns @code{MHD_NO} to inform the post processor
70that it does not need to be called again. Note that this function does not handle processing
71of data for the same @code{key}. If we were to expect that the name will be posted in several
72chunks, we had to expand the namestring dynamically as additional parts of it with the same @code{key}
73came in. But in this example, the name is assumed to fit entirely inside one single packet.
74
75@verbatim
76int iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
77 const char *filename, const char *content_type,
78 const char *transfer_encoding, const char *data, size_t off, size_t size)
79{
80 struct connection_info_struct *con_info = (struct connection_info_struct*) coninfo_cls;
81
82
83 if (0 == strcmp (key, "name"))
84 {
85 if ((size > 0) && (size <= MAXNAMESIZE))
86 {
87 char *answerstring;
88 answerstring = malloc (MAXANSWERSIZE);
89 if (!answerstring) return MHD_NO;
90
91 snprintf (answerstring, MAXANSWERSIZE, greatingpage, data);
92 con_info->answerstring = answerstring;
93 }
94 else con_info->answerstring = NULL;
95
96 return MHD_NO;
97 }
98
99 return MHD_YES;
100}
101@end verbatim
102@noindent
103
104Once a connection has been established, it can be terminated for many reasons. As these
105reasons include unexpected events, we have to register another function that cleans up any resources
106that might have been allocated for that connection by us, namely the post processor and the greetings
107string. This cleanup function must take into account that it will also be called for finished
108requests other than @emph{POST} requests.
109
110@verbatim
111void request_completed (void *cls, struct MHD_Connection *connection, void **con_cls,
112 enum MHD_RequestTerminationCode toe)
113{
114 struct connection_info_struct *con_info = (struct connection_info_struct*) *con_cls;
115
116
117 if (NULL == con_info) return;
118
119 if (con_info->connectiontype == POST)
120 {
121 MHD_destroy_post_processor (con_info->postprocessor);
122 if (con_info->answerstring) free (con_info->answerstring);
123 }
124
125 free (con_info);
126 *con_cls = NULL;
127}
128@end verbatim
129@noindent
130
131@emph{GNU libmicrohttpd} is informed that it shall call the above function when the daemon is started
132in the main function.
133
134@verbatim
135...
136daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
137 &answer_to_connection, NULL, MHD_OPTION_NOTIFY_COMPLETED,
138 request_completed, NULL, MHD_OPTION_END);
139...
140@end verbatim
141@noindent
142
143@heading Request handling
144With all other functions prepared, we can now discuss the actual request handling.
145
146On the first iteration for a new request, we start by allocating a new instance of a
147@code{ConnectionInfoStruct} structure, which will store all necessary information for later
148iterations and other functions.
149
150@verbatim
151int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url,
152 const char *method, const char *version, const char *upload_data,
153 unsigned int *upload_data_size, void **con_cls)
154{
155 if(NULL == *con_cls)
156 {
157 struct connection_info_struct *con_info;
158
159 con_info = malloc (sizeof (struct connection_info_struct));
160 if (NULL == con_info) return MHD_NO;
161 con_info->answerstring = NULL;
162@end verbatim
163@noindent
164
165If the new request is a @emph{POST}, the postprocessor must be created now. In addition, the type
166of the request is stored for convenience.
167@verbatim
168 if (0 == strcmp (method, "POST"))
169 {
170 con_info->postprocessor = MHD_create_post_processor (connection, POSTBUFFERSIZE,
171 iterate_post, (void*) con_info);
172
173 if (NULL == con_info->postprocessor)
174 {
175 free (con_info);
176 return MHD_NO;
177 }
178
179 con_info->connectiontype = POST;
180 }
181 else con_info->connectiontype = GET;
182@end verbatim
183@noindent
184
185The address of our structure will both serve as the indicator for successive iterations and to remember
186the particular details about the connection.
187@verbatim
188 *con_cls = (void*) con_info;
189 return MHD_YES;
190 }
191@end verbatim
192@noindent
193
194The rest of the function will not be executed on the first iteration. A @emph{GET} request is easily
195satisfied by sending the question form.
196@verbatim
197 if (0 == strcmp (method, "GET"))
198 {
199 return send_page (connection, askpage);
200 }
201@end verbatim
202@noindent
203
204In case of @emph{POST}, we invoke the post processor for as long as data keeps incoming, setting
205@code{*upload_data_size} to zero in order to indicate that we have processed---or at least have
206considered---all of it.
207@verbatim
208 if (0 == strcmp (method, "POST"))
209 {
210 struct connection_info_struct *con_info = *con_cls;
211
212 if (*upload_data_size != 0)
213 {
214 MHD_post_process(con_info->postprocessor, upload_data, *upload_data_size);
215 *upload_data_size = 0;
216
217 return MHD_YES;
218 }
219 else if (NULL != con_info->answerstring) return send_page (connection, con_info->answerstring);
220 }
221@end verbatim
222@noindent
223
224If they are neither @emph{GET} nor @emph{POST} requests, the error page is returned finally.
225@verbatim
226 return send_page(connection, errorpage);
227}
228@end verbatim
229@noindent
230
231These were the important parts of the program @code{simplepost.c}.