diff options
Diffstat (limited to 'doc/chapters/processingpost.inc')
-rw-r--r-- | doc/chapters/processingpost.inc | 231 |
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 @@ | |||
1 | The previous chapters already have demonstrated a variety of possibilities to send information | ||
2 | to the HTTP server, but it is not recommended that the @emph{GET} method is used to alter the way | ||
3 | the server operates. To induce changes on the server, the @emph{POST} method is preferred over | ||
4 | and is much more powerful than @emph{GET} and will be introduced in this chapter. | ||
5 | |||
6 | We are going to write an application that asks for the visitor's name and, after the user has posted it, | ||
7 | composes an individual response text. Even though it was not mandatory to use the @emph{post} method here, | ||
8 | as there is no permanent change caused by the post, it is an illustrative example on how to share data | ||
9 | between different functions for the same connection. Furthermore, the reader should be able to extend | ||
10 | it easily. | ||
11 | |||
12 | @heading GET request | ||
13 | When the first @emph{GET} request arrives, the server shall respond with a HTML page containing an | ||
14 | edit field for the name. | ||
15 | |||
16 | @verbatim | ||
17 | const 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 | |||
26 | The @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 | |||
29 | We also prepare the answer page, where the name is to be filled in later, and an error page | ||
30 | as the response for anything but proper @emph{GET} and @emph{POST} requests: | ||
31 | |||
32 | @verbatim | ||
33 | const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>"; | ||
34 | |||
35 | const char* errorpage="<html><body>This doesn't seem to be right.</body></html>"; | ||
36 | @end verbatim | ||
37 | @noindent | ||
38 | |||
39 | Whenever we need to send a page, we use an extra function | ||
40 | @code{int SendPage(struct MHD_Connection *connection, const char* page)} | ||
41 | for this, which does not contain anything new and whose implementation is therefore left out here. | ||
42 | |||
43 | |||
44 | @heading POST request | ||
45 | Posted data can be of arbitrary and considerable size; for example, if a user uploads a big | ||
46 | image to the server. Similar to the case of the header fields, there may also be different streams | ||
47 | of posted data, such as one containing the text of an editbox and another the state of a button. | ||
48 | Likewise, we will have to register an iterator function that is going to be called maybe several times | ||
49 | not only if there are different POSTs but also if one POST has only been received partly yet and | ||
50 | needs processing before another chunk can be received. | ||
51 | |||
52 | Such an iterator function is called by a @emph{postprocessor}, which must be created upon arriving | ||
53 | of 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. | ||
55 | But in order to pass this string to other functions and still be able to differentiate different | ||
56 | connections, we must first define a structure to share the information, holding the most import entries. | ||
57 | |||
58 | @verbatim | ||
59 | struct connection_info_struct | ||
60 | { | ||
61 | int connectiontype; | ||
62 | char *answerstring; | ||
63 | struct MHD_PostProcessor *postprocessor; | ||
64 | }; | ||
65 | @end verbatim | ||
66 | @noindent | ||
67 | |||
68 | With these information available to the iterator function, it is able to fulfill its task. | ||
69 | Once it has composed the greeting string, it returns @code{MHD_NO} to inform the post processor | ||
70 | that it does not need to be called again. Note that this function does not handle processing | ||
71 | of data for the same @code{key}. If we were to expect that the name will be posted in several | ||
72 | chunks, we had to expand the namestring dynamically as additional parts of it with the same @code{key} | ||
73 | came in. But in this example, the name is assumed to fit entirely inside one single packet. | ||
74 | |||
75 | @verbatim | ||
76 | int 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 | |||
104 | Once a connection has been established, it can be terminated for many reasons. As these | ||
105 | reasons include unexpected events, we have to register another function that cleans up any resources | ||
106 | that might have been allocated for that connection by us, namely the post processor and the greetings | ||
107 | string. This cleanup function must take into account that it will also be called for finished | ||
108 | requests other than @emph{POST} requests. | ||
109 | |||
110 | @verbatim | ||
111 | void 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 | ||
132 | in the main function. | ||
133 | |||
134 | @verbatim | ||
135 | ... | ||
136 | daemon = 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 | ||
144 | With all other functions prepared, we can now discuss the actual request handling. | ||
145 | |||
146 | On 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 | ||
148 | iterations and other functions. | ||
149 | |||
150 | @verbatim | ||
151 | int 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 | |||
165 | If the new request is a @emph{POST}, the postprocessor must be created now. In addition, the type | ||
166 | of 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 | |||
185 | The address of our structure will both serve as the indicator for successive iterations and to remember | ||
186 | the particular details about the connection. | ||
187 | @verbatim | ||
188 | *con_cls = (void*) con_info; | ||
189 | return MHD_YES; | ||
190 | } | ||
191 | @end verbatim | ||
192 | @noindent | ||
193 | |||
194 | The rest of the function will not be executed on the first iteration. A @emph{GET} request is easily | ||
195 | satisfied 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 | |||
204 | In 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 | ||
206 | considered---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 | |||
224 | If 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 | |||
231 | These were the important parts of the program @code{simplepost.c}. | ||