diff options
author | Christian Grothoff <christian@grothoff.org> | 2011-03-03 18:15:23 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2011-03-03 18:15:23 +0000 |
commit | dc2697790f0802dc10704483bc552d98e9575231 (patch) | |
tree | be91739419ef5dd2a48e732dceae89c75eff14a1 | |
parent | 260dd93fdabe89e8c715286c20e23124a79c2b54 (diff) | |
download | libmicrohttpd-dc2697790f0802dc10704483bc552d98e9575231.tar.gz libmicrohttpd-dc2697790f0802dc10704483bc552d98e9575231.zip |
example
-rw-r--r-- | src/examples/Makefile.am | 6 | ||||
-rw-r--r-- | src/examples/post_example.c | 748 |
2 files changed, 754 insertions, 0 deletions
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index fb467ed9..f3f2772e 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am | |||
@@ -18,6 +18,7 @@ noinst_PROGRAMS = \ | |||
18 | authorization_example \ | 18 | authorization_example \ |
19 | minimal_example \ | 19 | minimal_example \ |
20 | minimal_example_comet \ | 20 | minimal_example_comet \ |
21 | post_example \ | ||
21 | querystring_example \ | 22 | querystring_example \ |
22 | fileserver_example \ | 23 | fileserver_example \ |
23 | fileserver_example_dirs \ | 24 | fileserver_example_dirs \ |
@@ -37,6 +38,11 @@ minimal_example_SOURCES = \ | |||
37 | minimal_example_LDADD = \ | 38 | minimal_example_LDADD = \ |
38 | $(top_builddir)/src/daemon/libmicrohttpd.la | 39 | $(top_builddir)/src/daemon/libmicrohttpd.la |
39 | 40 | ||
41 | post_example_SOURCES = \ | ||
42 | post_example.c | ||
43 | post_example_LDADD = \ | ||
44 | $(top_builddir)/src/daemon/libmicrohttpd.la | ||
45 | |||
40 | minimal_example_comet_SOURCES = \ | 46 | minimal_example_comet_SOURCES = \ |
41 | minimal_example_comet.c | 47 | minimal_example_comet.c |
42 | minimal_example_comet_LDADD = \ | 48 | minimal_example_comet_LDADD = \ |
diff --git a/src/examples/post_example.c b/src/examples/post_example.c new file mode 100644 index 00000000..6bb81a0b --- /dev/null +++ b/src/examples/post_example.c | |||
@@ -0,0 +1,748 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | (C) 2011 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | /** | ||
20 | * @file post_example.c | ||
21 | * @brief example for processing POST requests using libmicrohttpd | ||
22 | * @author Christian Grothoff | ||
23 | */ | ||
24 | |||
25 | /* needed for asprintf */ | ||
26 | #define _GNU_SOURCE | ||
27 | |||
28 | |||
29 | #include <stdlib.h> | ||
30 | #include <string.h> | ||
31 | #include <stdio.h> | ||
32 | #include <errno.h> | ||
33 | #include <time.h> | ||
34 | #include <microhttpd.h> | ||
35 | |||
36 | /** | ||
37 | * Invalid method page. | ||
38 | */ | ||
39 | #define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>" | ||
40 | |||
41 | /** | ||
42 | * Invalid URL page. | ||
43 | */ | ||
44 | #define NOT_FOUND_ERROR "<html><head><title>Not found</title></head><body>Go away.</body></html>" | ||
45 | |||
46 | /** | ||
47 | * Front page. (/) | ||
48 | */ | ||
49 | #define MAIN_PAGE "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>" | ||
50 | |||
51 | /** | ||
52 | * Second page. (/2) | ||
53 | */ | ||
54 | #define SECOND_PAGE "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>" | ||
55 | |||
56 | /** | ||
57 | * Second page (/S) | ||
58 | */ | ||
59 | #define SUBMIT_PAGE "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></body></html>" | ||
60 | |||
61 | /** | ||
62 | * Last page. | ||
63 | */ | ||
64 | #define LAST_PAGE "<html><head><title>Thank you</title></head><body>Thank you.</body></html>" | ||
65 | |||
66 | /** | ||
67 | * Name of our cookie. | ||
68 | */ | ||
69 | #define COOKIE_NAME "session" | ||
70 | |||
71 | |||
72 | /** | ||
73 | * State we keep for each user/session/browser. | ||
74 | */ | ||
75 | struct Session | ||
76 | { | ||
77 | /** | ||
78 | * We keep all sessions in a linked list. | ||
79 | */ | ||
80 | struct Session *next; | ||
81 | |||
82 | /** | ||
83 | * Unique ID for this session. | ||
84 | */ | ||
85 | char sid[33]; | ||
86 | |||
87 | /** | ||
88 | * Reference counter giving the number of connections | ||
89 | * currently using this session. | ||
90 | */ | ||
91 | unsigned int rc; | ||
92 | |||
93 | /** | ||
94 | * Time when this session was last active. | ||
95 | */ | ||
96 | time_t start; | ||
97 | |||
98 | /** | ||
99 | * String submitted via form. | ||
100 | */ | ||
101 | char value_1[64]; | ||
102 | |||
103 | /** | ||
104 | * Another value submitted via form. | ||
105 | */ | ||
106 | char value_2[64]; | ||
107 | |||
108 | }; | ||
109 | |||
110 | |||
111 | /** | ||
112 | * Data kept per request. | ||
113 | */ | ||
114 | struct Request | ||
115 | { | ||
116 | |||
117 | /** | ||
118 | * Associated session. | ||
119 | */ | ||
120 | struct Session *session; | ||
121 | |||
122 | /** | ||
123 | * Post processor handling form data (IF this is | ||
124 | * a POST request). | ||
125 | */ | ||
126 | struct MHD_PostProcessor *pp; | ||
127 | |||
128 | /** | ||
129 | * URL to serve in response to this POST (if this request | ||
130 | * was a 'POST') | ||
131 | */ | ||
132 | const char *post_url; | ||
133 | |||
134 | }; | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Linked list of all active sessions. Yes, O(n) but a | ||
139 | * hash table would be overkill for a simple example... | ||
140 | */ | ||
141 | static struct Session *sessions; | ||
142 | |||
143 | |||
144 | |||
145 | |||
146 | /** | ||
147 | * Return the session handle for this connection, or | ||
148 | * create one if this is a new user. | ||
149 | */ | ||
150 | static struct Session * | ||
151 | get_session (struct MHD_Connection *connection) | ||
152 | { | ||
153 | struct Session *ret; | ||
154 | const char *cookie; | ||
155 | |||
156 | cookie = MHD_lookup_connection_value (connection, | ||
157 | MHD_COOKIE_KIND, | ||
158 | COOKIE_NAME); | ||
159 | if (cookie != NULL) | ||
160 | { | ||
161 | /* find existing session */ | ||
162 | ret = sessions; | ||
163 | while (NULL != ret) | ||
164 | { | ||
165 | if (0 == strcmp (cookie, ret->sid)) | ||
166 | break; | ||
167 | ret = ret->next; | ||
168 | } | ||
169 | if (NULL != ret) | ||
170 | { | ||
171 | ret->rc++; | ||
172 | return ret; | ||
173 | } | ||
174 | } | ||
175 | /* create fresh session */ | ||
176 | ret = calloc (1, sizeof (struct Session)); | ||
177 | if (NULL == ret) | ||
178 | { | ||
179 | fprintf (stderr, "calloc error: %s\n", strerror (errno)); | ||
180 | return NULL; | ||
181 | } | ||
182 | /* not a super-secure way to generate a random session ID, | ||
183 | but should do for a simple example... */ | ||
184 | snprintf (ret->sid, | ||
185 | sizeof (ret->sid), | ||
186 | "%X%X%X%X", | ||
187 | (unsigned int) random (), | ||
188 | (unsigned int) random (), | ||
189 | (unsigned int) random (), | ||
190 | (unsigned int) random ()); | ||
191 | ret->rc++; | ||
192 | ret->start = time (NULL); | ||
193 | ret->next = sessions; | ||
194 | sessions = ret; | ||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | |||
199 | /** | ||
200 | * Type of handler that generates a reply. | ||
201 | * | ||
202 | * @param cls content for the page (handler-specific) | ||
203 | * @param mime mime type to use | ||
204 | * @param session session information | ||
205 | * @param connection connection to process | ||
206 | * @param MHD_YES on success, MHD_NO on failure | ||
207 | */ | ||
208 | typedef int (*PageHandler)(const void *cls, | ||
209 | const char *mime, | ||
210 | struct Session *session, | ||
211 | struct MHD_Connection *connection); | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Entry we generate for each page served. | ||
216 | */ | ||
217 | struct Page | ||
218 | { | ||
219 | /** | ||
220 | * Acceptable URL for this page. | ||
221 | */ | ||
222 | const char *url; | ||
223 | |||
224 | /** | ||
225 | * Mime type to set for the page. | ||
226 | */ | ||
227 | const char *mime; | ||
228 | |||
229 | /** | ||
230 | * Handler to call to generate response. | ||
231 | */ | ||
232 | PageHandler handler; | ||
233 | |||
234 | /** | ||
235 | * Extra argument to handler. | ||
236 | */ | ||
237 | const void *handler_cls; | ||
238 | }; | ||
239 | |||
240 | |||
241 | /** | ||
242 | * Add header to response to set a session cookie. | ||
243 | * | ||
244 | * @param session session to use | ||
245 | * @param response response to modify | ||
246 | */ | ||
247 | static void | ||
248 | add_session_cookie (struct Session *session, | ||
249 | struct MHD_Response *response) | ||
250 | { | ||
251 | char cstr[256]; | ||
252 | snprintf (cstr, | ||
253 | sizeof (cstr), | ||
254 | "%s=%s", | ||
255 | COOKIE_NAME, | ||
256 | session->sid); | ||
257 | if (MHD_NO == | ||
258 | MHD_add_response_header (response, | ||
259 | MHD_HTTP_HEADER_SET_COOKIE, | ||
260 | cstr)) | ||
261 | { | ||
262 | fprintf (stderr, | ||
263 | "Failed to set session cookie header!\n"); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | |||
268 | /** | ||
269 | * Handler that returns a simple static HTTP page that | ||
270 | * is passed in via 'cls'. | ||
271 | * | ||
272 | * @param cls a 'const char *' with the HTML webpage to return | ||
273 | * @param mime mime type to use | ||
274 | * @param session session handle | ||
275 | * @param connection connection to use | ||
276 | */ | ||
277 | static int | ||
278 | serve_simple_form (const void *cls, | ||
279 | const char *mime, | ||
280 | struct Session *session, | ||
281 | struct MHD_Connection *connection) | ||
282 | { | ||
283 | int ret; | ||
284 | const char *form = cls; | ||
285 | struct MHD_Response *response; | ||
286 | |||
287 | /* return static form */ | ||
288 | response = MHD_create_response_from_buffer (strlen (form), | ||
289 | (void *) form, | ||
290 | MHD_RESPMEM_PERSISTENT); | ||
291 | add_session_cookie (session, response); | ||
292 | MHD_add_response_header (response, | ||
293 | MHD_HTTP_HEADER_CONTENT_ENCODING, | ||
294 | mime); | ||
295 | ret = MHD_queue_response (connection, | ||
296 | MHD_HTTP_OK, | ||
297 | response); | ||
298 | MHD_destroy_response (response); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | |||
303 | /** | ||
304 | * Handler that adds the 'v1' value to the given HTML code. | ||
305 | * | ||
306 | * @param cls a 'const char *' with the HTML webpage to return | ||
307 | * @param mime mime type to use | ||
308 | * @param session session handle | ||
309 | * @param connection connection to use | ||
310 | */ | ||
311 | static int | ||
312 | fill_v1_form (const void *cls, | ||
313 | const char *mime, | ||
314 | struct Session *session, | ||
315 | struct MHD_Connection *connection) | ||
316 | { | ||
317 | int ret; | ||
318 | const char *form = cls; | ||
319 | char *reply; | ||
320 | struct MHD_Response *response; | ||
321 | |||
322 | if (-1 == asprintf (&reply, | ||
323 | form, | ||
324 | session->value_1)) | ||
325 | { | ||
326 | /* oops */ | ||
327 | return MHD_NO; | ||
328 | } | ||
329 | /* return static form */ | ||
330 | response = MHD_create_response_from_buffer (strlen (reply), | ||
331 | (void *) reply, | ||
332 | MHD_RESPMEM_MUST_FREE); | ||
333 | add_session_cookie (session, response); | ||
334 | MHD_add_response_header (response, | ||
335 | MHD_HTTP_HEADER_CONTENT_ENCODING, | ||
336 | mime); | ||
337 | ret = MHD_queue_response (connection, | ||
338 | MHD_HTTP_OK, | ||
339 | response); | ||
340 | MHD_destroy_response (response); | ||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | |||
345 | /** | ||
346 | * Handler that adds the 'v1' and 'v2' values to the given HTML code. | ||
347 | * | ||
348 | * @param cls a 'const char *' with the HTML webpage to return | ||
349 | * @param mime mime type to use | ||
350 | * @param session session handle | ||
351 | * @param connection connection to use | ||
352 | */ | ||
353 | static int | ||
354 | fill_v1_v2_form (const void *cls, | ||
355 | const char *mime, | ||
356 | struct Session *session, | ||
357 | struct MHD_Connection *connection) | ||
358 | { | ||
359 | int ret; | ||
360 | const char *form = cls; | ||
361 | char *reply; | ||
362 | struct MHD_Response *response; | ||
363 | |||
364 | if (-1 == asprintf (&reply, | ||
365 | form, | ||
366 | session->value_1, | ||
367 | session->value_2)) | ||
368 | { | ||
369 | /* oops */ | ||
370 | return MHD_NO; | ||
371 | } | ||
372 | /* return static form */ | ||
373 | response = MHD_create_response_from_buffer (strlen (reply), | ||
374 | (void *) reply, | ||
375 | MHD_RESPMEM_MUST_FREE); | ||
376 | add_session_cookie (session, response); | ||
377 | MHD_add_response_header (response, | ||
378 | MHD_HTTP_HEADER_CONTENT_ENCODING, | ||
379 | mime); | ||
380 | ret = MHD_queue_response (connection, | ||
381 | MHD_HTTP_OK, | ||
382 | response); | ||
383 | MHD_destroy_response (response); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Handler used to generate a 404 reply. | ||
390 | * | ||
391 | * @param cls a 'const char *' with the HTML webpage to return | ||
392 | * @param mime mime type to use | ||
393 | * @param session session handle | ||
394 | * @param connection connection to use | ||
395 | */ | ||
396 | static int | ||
397 | not_found_page (const void *cls, | ||
398 | const char *mime, | ||
399 | struct Session *session, | ||
400 | struct MHD_Connection *connection) | ||
401 | { | ||
402 | int ret; | ||
403 | struct MHD_Response *response; | ||
404 | |||
405 | /* unsupported HTTP method */ | ||
406 | response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR), | ||
407 | (void *) NOT_FOUND_ERROR, | ||
408 | MHD_RESPMEM_PERSISTENT); | ||
409 | ret = MHD_queue_response (connection, | ||
410 | MHD_HTTP_NOT_FOUND, | ||
411 | response); | ||
412 | MHD_add_response_header (response, | ||
413 | MHD_HTTP_HEADER_CONTENT_ENCODING, | ||
414 | mime); | ||
415 | MHD_destroy_response (response); | ||
416 | return ret; | ||
417 | } | ||
418 | |||
419 | |||
420 | /** | ||
421 | * List of all pages served by this HTTP server. | ||
422 | */ | ||
423 | static struct Page pages[] = | ||
424 | { | ||
425 | { "/", "text/html", &fill_v1_form, MAIN_PAGE }, | ||
426 | { "/2", "text/html", &fill_v1_v2_form, SECOND_PAGE }, | ||
427 | { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE }, | ||
428 | { "/F", "text/html", &serve_simple_form, LAST_PAGE }, | ||
429 | { NULL, NULL, ¬_found_page, NULL } /* 404 */ | ||
430 | }; | ||
431 | |||
432 | |||
433 | |||
434 | /** | ||
435 | * Iterator over key-value pairs where the value | ||
436 | * maybe made available in increments and/or may | ||
437 | * not be zero-terminated. Used for processing | ||
438 | * POST data. | ||
439 | * | ||
440 | * @param cls user-specified closure | ||
441 | * @param kind type of the value | ||
442 | * @param key 0-terminated key for the value | ||
443 | * @param filename name of the uploaded file, NULL if not known | ||
444 | * @param content_type mime-type of the data, NULL if not known | ||
445 | * @param transfer_encoding encoding of the data, NULL if not known | ||
446 | * @param data pointer to size bytes of data at the | ||
447 | * specified offset | ||
448 | * @param off offset of data in the overall value | ||
449 | * @param size number of bytes in data available | ||
450 | * @return MHD_YES to continue iterating, | ||
451 | * MHD_NO to abort the iteration | ||
452 | */ | ||
453 | static int | ||
454 | post_iterator (void *cls, | ||
455 | enum MHD_ValueKind kind, | ||
456 | const char *key, | ||
457 | const char *filename, | ||
458 | const char *content_type, | ||
459 | const char *transfer_encoding, | ||
460 | const char *data, uint64_t off, size_t size) | ||
461 | { | ||
462 | struct Request *request = cls; | ||
463 | struct Session *session = request->session; | ||
464 | |||
465 | if (0 == strcmp ("DONE", key)) | ||
466 | { | ||
467 | fprintf (stdout, | ||
468 | "Session `%s' submitted `%s', `%s'\n", | ||
469 | session->sid, | ||
470 | session->value_1, | ||
471 | session->value_2); | ||
472 | return MHD_YES; | ||
473 | } | ||
474 | if (0 == strcmp ("v1", key)) | ||
475 | { | ||
476 | if (size + off > sizeof(session->value_1)) | ||
477 | size = sizeof (session->value_1) - off; | ||
478 | memcpy (&session->value_1[off], | ||
479 | data, | ||
480 | size); | ||
481 | if (size + off < sizeof (session->value_1)) | ||
482 | session->value_1[size+off] = '\0'; | ||
483 | return MHD_YES; | ||
484 | } | ||
485 | if (0 == strcmp ("v2", key)) | ||
486 | { | ||
487 | if (size + off > sizeof(session->value_2)) | ||
488 | size = sizeof (session->value_2) - off; | ||
489 | memcpy (&session->value_2[off], | ||
490 | data, | ||
491 | size); | ||
492 | if (size + off < sizeof (session->value_2)) | ||
493 | session->value_2[size+off] = '\0'; | ||
494 | return MHD_YES; | ||
495 | } | ||
496 | fprintf (stderr, "Unsupported form value `%s'\n", key); | ||
497 | return MHD_YES; | ||
498 | } | ||
499 | |||
500 | |||
501 | /** | ||
502 | * Main MHD callback for handling requests. | ||
503 | * | ||
504 | * | ||
505 | * @param cls argument given together with the function | ||
506 | * pointer when the handler was registered with MHD | ||
507 | * @param url the requested url | ||
508 | * @param method the HTTP method used ("GET", "PUT", etc.) | ||
509 | * @param version the HTTP version string (i.e. "HTTP/1.1") | ||
510 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
511 | * for a POST that fits into memory and that is encoded | ||
512 | * with a supported encoding, the POST data will NOT be | ||
513 | * given in upload_data and is instead available as | ||
514 | * part of MHD_get_connection_values; very large POST | ||
515 | * data *will* be made available incrementally in | ||
516 | * upload_data) | ||
517 | * @param upload_data_size set initially to the size of the | ||
518 | * upload_data provided; the method must update this | ||
519 | * value to the number of bytes NOT processed; | ||
520 | * @param con_cls pointer that the callback can set to some | ||
521 | * address and that will be preserved by MHD for future | ||
522 | * calls for this request; since the access handler may | ||
523 | * be called many times (i.e., for a PUT/POST operation | ||
524 | * with plenty of upload data) this allows the application | ||
525 | * to easily associate some request-specific state. | ||
526 | * If necessary, this state can be cleaned up in the | ||
527 | * global "MHD_RequestCompleted" callback (which | ||
528 | * can be set with the MHD_OPTION_NOTIFY_COMPLETED). | ||
529 | * Initially, <tt>*con_cls</tt> will be NULL. | ||
530 | * @return MHS_YES if the connection was handled successfully, | ||
531 | * MHS_NO if the socket must be closed due to a serios | ||
532 | * error while handling the request | ||
533 | */ | ||
534 | static int | ||
535 | create_response (void *cls, | ||
536 | struct MHD_Connection *connection, | ||
537 | const char *url, | ||
538 | const char *method, | ||
539 | const char *version, | ||
540 | const char *upload_data, | ||
541 | size_t *upload_data_size, | ||
542 | void **ptr) | ||
543 | { | ||
544 | struct MHD_Response *response; | ||
545 | struct Request *request; | ||
546 | struct Session *session; | ||
547 | int ret; | ||
548 | unsigned int i; | ||
549 | |||
550 | request = *ptr; | ||
551 | if (NULL == request) | ||
552 | { | ||
553 | request = calloc (1, sizeof (struct Request)); | ||
554 | if (NULL == request) | ||
555 | { | ||
556 | fprintf (stderr, "calloc error: %s\n", strerror (errno)); | ||
557 | return MHD_NO; | ||
558 | } | ||
559 | *ptr = request; | ||
560 | if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) | ||
561 | { | ||
562 | request->pp = MHD_create_post_processor (connection, 1024, | ||
563 | &post_iterator, request); | ||
564 | if (NULL == request->pp) | ||
565 | { | ||
566 | fprintf (stderr, "Failed to setup post processor for `%s'\n", | ||
567 | url); | ||
568 | return MHD_NO; /* internal error */ | ||
569 | } | ||
570 | } | ||
571 | return MHD_YES; | ||
572 | } | ||
573 | if (NULL == request->session) | ||
574 | { | ||
575 | request->session = get_session (connection); | ||
576 | if (NULL == request->session) | ||
577 | { | ||
578 | fprintf (stderr, "Failed to setup session for `%s'\n", | ||
579 | url); | ||
580 | return MHD_NO; /* internal error */ | ||
581 | } | ||
582 | } | ||
583 | session = request->session; | ||
584 | session->start = time (NULL); | ||
585 | if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) | ||
586 | { | ||
587 | /* evaluate POST data */ | ||
588 | MHD_post_process (request->pp, | ||
589 | upload_data, | ||
590 | *upload_data_size); | ||
591 | if (0 != *upload_data_size) | ||
592 | { | ||
593 | *upload_data_size = 0; | ||
594 | return MHD_YES; | ||
595 | } | ||
596 | /* done with POST data, serve response */ | ||
597 | MHD_destroy_post_processor (request->pp); | ||
598 | request->pp = NULL; | ||
599 | method = MHD_HTTP_METHOD_GET; /* fake 'GET' */ | ||
600 | if (NULL != request->post_url) | ||
601 | url = request->post_url; | ||
602 | } | ||
603 | |||
604 | if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || | ||
605 | (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) | ||
606 | { | ||
607 | /* find out which page to serve */ | ||
608 | i=0; | ||
609 | while ( (pages[i].url != NULL) && | ||
610 | (0 != strcmp (pages[i].url, url)) ) | ||
611 | i++; | ||
612 | ret = pages[i].handler (pages[i].handler_cls, | ||
613 | pages[i].mime, | ||
614 | session, connection); | ||
615 | if (ret != MHD_YES) | ||
616 | fprintf (stderr, "Failed to create page for `%s'\n", | ||
617 | url); | ||
618 | return ret; | ||
619 | } | ||
620 | /* unsupported HTTP method */ | ||
621 | response = MHD_create_response_from_buffer (strlen (METHOD_ERROR), | ||
622 | (void *) METHOD_ERROR, | ||
623 | MHD_RESPMEM_PERSISTENT); | ||
624 | ret = MHD_queue_response (connection, | ||
625 | MHD_HTTP_METHOD_NOT_ACCEPTABLE, | ||
626 | response); | ||
627 | MHD_destroy_response (response); | ||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | |||
632 | /** | ||
633 | * Callback called upon completion of a request. | ||
634 | * Decrements session reference counter. | ||
635 | * | ||
636 | * @param cls not used | ||
637 | * @param connection connection that completed | ||
638 | * @param con_cls session handle | ||
639 | * @param toe status code | ||
640 | */ | ||
641 | static void | ||
642 | request_completed_callback (void *cls, | ||
643 | struct MHD_Connection *connection, | ||
644 | void **con_cls, | ||
645 | enum MHD_RequestTerminationCode toe) | ||
646 | { | ||
647 | struct Request *request = *con_cls; | ||
648 | |||
649 | if (NULL == request) | ||
650 | return; | ||
651 | if (NULL != request->session) | ||
652 | request->session->rc--; | ||
653 | if (NULL != request->pp) | ||
654 | MHD_destroy_post_processor (request->pp); | ||
655 | free (request); | ||
656 | } | ||
657 | |||
658 | |||
659 | /** | ||
660 | * Clean up handles of sessions that have been idle for | ||
661 | * too long. | ||
662 | */ | ||
663 | static void | ||
664 | expire_sessions () | ||
665 | { | ||
666 | struct Session *pos; | ||
667 | struct Session *prev; | ||
668 | struct Session *next; | ||
669 | time_t now; | ||
670 | |||
671 | now = time (NULL); | ||
672 | prev = NULL; | ||
673 | pos = sessions; | ||
674 | while (NULL != pos) | ||
675 | { | ||
676 | next = pos->next; | ||
677 | if (now - pos->start > 60 * 60) | ||
678 | { | ||
679 | /* expire sessions after 1h */ | ||
680 | if (NULL == prev) | ||
681 | sessions = pos->next; | ||
682 | else | ||
683 | prev->next = next; | ||
684 | free (pos); | ||
685 | } | ||
686 | else | ||
687 | prev = pos; | ||
688 | pos = next; | ||
689 | } | ||
690 | } | ||
691 | |||
692 | |||
693 | /** | ||
694 | * Call with the port number as the only argument. | ||
695 | * Never terminates (other than by signals, such as CTRL-C). | ||
696 | */ | ||
697 | int | ||
698 | main (int argc, char *const *argv) | ||
699 | { | ||
700 | struct MHD_Daemon *d; | ||
701 | struct timeval tv; | ||
702 | struct timeval *tvp; | ||
703 | fd_set rs; | ||
704 | fd_set ws; | ||
705 | fd_set es; | ||
706 | int max; | ||
707 | unsigned MHD_LONG_LONG mhd_timeout; | ||
708 | |||
709 | if (argc != 2) | ||
710 | { | ||
711 | printf ("%s PORT\n", argv[0]); | ||
712 | return 1; | ||
713 | } | ||
714 | /* initialize PRNG */ | ||
715 | srandom ((unsigned int) time (NULL)); | ||
716 | d = MHD_start_daemon (MHD_USE_DEBUG, | ||
717 | atoi (argv[1]), | ||
718 | NULL, NULL, | ||
719 | &create_response, NULL, | ||
720 | MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15, | ||
721 | MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL, | ||
722 | MHD_OPTION_END); | ||
723 | if (NULL == d) | ||
724 | return 1; | ||
725 | while (1) | ||
726 | { | ||
727 | expire_sessions (); | ||
728 | max = 0; | ||
729 | FD_ZERO (&rs); | ||
730 | FD_ZERO (&ws); | ||
731 | FD_ZERO (&es); | ||
732 | if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) | ||
733 | break; /* fatal internal error */ | ||
734 | if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES) | ||
735 | { | ||
736 | tv.tv_sec = mhd_timeout / 1000; | ||
737 | tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000; | ||
738 | tvp = &tv; | ||
739 | } | ||
740 | else | ||
741 | tvp = NULL; | ||
742 | select (max + 1, &rs, &ws, &es, tvp); | ||
743 | MHD_run (d); | ||
744 | } | ||
745 | MHD_stop_daemon (d); | ||
746 | return 0; | ||
747 | } | ||
748 | |||