1 --- libmicrohttpd-0.9.16/doc/chapters/sessions.inc.orig 1970-01-01 01:00:00.000000000 +0100
2 +++ libmicrohttpd-0.9.16/doc/chapters/sessions.inc 2011-11-06 06:41:40.346690352 +0100
4 +This chapter discusses how one should manage sessions, that is, share state between multiple
5 +HTTP requests from the same user. We use a simple example where the user submits multiple
6 +forms and the server is supposed to accumulate state from all of these forms. Naturally, as
7 +this is a network protocol, our session mechanism must support having many users with
8 +many concurrent sessions at the same time.
10 +In order to track users, we use a simple session cookie. A session cookie expires when the
11 +user closes the browser. Changing from session cookies to persistent cookies only requires
12 +adding an expiration time to the cookie. The server creates a fresh session cookie whenever
13 +a request without a cookie is received, or if the supplied session cookie is not known to
16 +@heading Looking up the cookie
18 +Since MHD parses the HTTP cookie header for us, looking up an existing cookie
25 +Here, FIXME is the name we chose for our session cookie.
28 +@heading Setting the cookie header
30 +MHD requires the user to provide the full cookie format string in order to set
31 +cookies. In order to generate a unique cookie, our example creates a random
32 +64-character text string to be used as the value of the cookie:
38 +Given this cookie value, we can then set the cookie header in our HTTP response
46 +@heading Remark: Session expiration
48 +It is of course possible that clients stop their interaction with the
49 +server at any time. In order to avoid using too much storage, the
50 +server must thus discard inactive sessions at some point. Our example
51 +implements this by discarding inactive sessions after a certain amount
52 +of time. Alternatively, the implementation may limit the total number
53 +of active sessions. Which bounds are used for idle sessions or the
54 +total number of sessions obviously depends largely on the type of
55 +the application and available server resources.
57 +@heading Example code
59 +A sample application implementing a website with multiple
60 +forms (which are dynamically created using values from previous
61 +POST requests from the same session) is available
62 +as the example @code{sessions.c}.
64 +Note that the example uses a simple, $O(n)$ linked list traversal to
65 +look up sessions and to expire old sessions. Using a hash table and a
66 +heap would be more appropriate if a large number of concurrent
67 +sessions is expected.
71 +Naturally, it is quite conceivable to store session data in a database
72 +instead of in memory. Still, having mechanisms to expire data
73 +associated with long-time idle sessions (where the business process
74 +has still not finished) is likely a good idea.
75 --- libmicrohttpd-0.9.16/doc/examples/sessions.c.orig 1970-01-01 01:00:00.000000000 +0100
76 +++ libmicrohttpd-0.9.16/doc/examples/sessions.c 2011-11-06 06:41:35.430023520 +0100
79 + This file is part of libmicrohttpd
80 + (C) 2011 Christian Grothoff (and other contributing authors)
82 + This library is free software; you can redistribute it and/or
83 + modify it under the terms of the GNU Lesser General Public
84 + License as published by the Free Software Foundation; either
85 + version 2.1 of the License, or (at your option) any later version.
87 + This library is distributed in the hope that it will be useful,
88 + but WITHOUT ANY WARRANTY; without even the implied warranty of
89 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
90 + Lesser General Public License for more details.
92 + You should have received a copy of the GNU Lesser General Public
93 + License along with this library; if not, write to the Free Software
94 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
97 + * @file post_example.c
98 + * @brief example for processing POST requests using libmicrohttpd
99 + * @author Christian Grothoff
102 +/* needed for asprintf */
111 +#include <microhttpd.h>
114 + * Invalid method page.
116 +#define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
119 + * Invalid URL page.
121 +#define NOT_FOUND_ERROR "<html><head><title>Not found</title></head><body>Go away.</body></html>"
126 +#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>"
129 + * Second page. (/2)
131 +#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>"
136 +#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>"
141 +#define LAST_PAGE "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
144 + * Name of our cookie.
146 +#define COOKIE_NAME "session"
150 + * State we keep for each user/session/browser.
155 + * We keep all sessions in a linked list.
157 + struct Session *next;
160 + * Unique ID for this session.
165 + * Reference counter giving the number of connections
166 + * currently using this session.
171 + * Time when this session was last active.
176 + * String submitted via form.
181 + * Another value submitted via form.
189 + * Data kept per request.
195 + * Associated session.
197 + struct Session *session;
200 + * Post processor handling form data (IF this is
203 + struct MHD_PostProcessor *pp;
206 + * URL to serve in response to this POST (if this request
209 + const char *post_url;
215 + * Linked list of all active sessions. Yes, O(n) but a
216 + * hash table would be overkill for a simple example...
218 +static struct Session *sessions;
224 + * Return the session handle for this connection, or
225 + * create one if this is a new user.
227 +static struct Session *
228 +get_session (struct MHD_Connection *connection)
230 + struct Session *ret;
231 + const char *cookie;
233 + cookie = MHD_lookup_connection_value (connection,
236 + if (cookie != NULL)
238 + /* find existing session */
240 + while (NULL != ret)
242 + if (0 == strcmp (cookie, ret->sid))
252 + /* create fresh session */
253 + ret = calloc (1, sizeof (struct Session));
256 + fprintf (stderr, "calloc error: %s\n", strerror (errno));
259 + /* not a super-secure way to generate a random session ID,
260 + but should do for a simple example... */
261 + snprintf (ret->sid,
264 + (unsigned int) random (),
265 + (unsigned int) random (),
266 + (unsigned int) random (),
267 + (unsigned int) random ());
269 + ret->start = time (NULL);
270 + ret->next = sessions;
277 + * Type of handler that generates a reply.
279 + * @param cls content for the page (handler-specific)
280 + * @param mime mime type to use
281 + * @param session session information
282 + * @param connection connection to process
283 + * @param MHD_YES on success, MHD_NO on failure
285 +typedef int (*PageHandler)(const void *cls,
287 + struct Session *session,
288 + struct MHD_Connection *connection);
292 + * Entry we generate for each page served.
297 + * Acceptable URL for this page.
302 + * Mime type to set for the page.
307 + * Handler to call to generate response.
309 + PageHandler handler;
312 + * Extra argument to handler.
314 + const void *handler_cls;
319 + * Add header to response to set a session cookie.
321 + * @param session session to use
322 + * @param response response to modify
325 +add_session_cookie (struct Session *session,
326 + struct MHD_Response *response)
335 + MHD_add_response_header (response,
336 + MHD_HTTP_HEADER_SET_COOKIE,
340 + "Failed to set session cookie header!\n");
346 + * Handler that returns a simple static HTTP page that
347 + * is passed in via 'cls'.
349 + * @param cls a 'const char *' with the HTML webpage to return
350 + * @param mime mime type to use
351 + * @param session session handle
352 + * @param connection connection to use
355 +serve_simple_form (const void *cls,
357 + struct Session *session,
358 + struct MHD_Connection *connection)
361 + const char *form = cls;
362 + struct MHD_Response *response;
364 + /* return static form */
365 + response = MHD_create_response_from_buffer (strlen (form),
367 + MHD_RESPMEM_PERSISTENT);
368 + add_session_cookie (session, response);
369 + MHD_add_response_header (response,
370 + MHD_HTTP_HEADER_CONTENT_ENCODING,
372 + ret = MHD_queue_response (connection,
375 + MHD_destroy_response (response);
381 + * Handler that adds the 'v1' value to the given HTML code.
383 + * @param cls a 'const char *' with the HTML webpage to return
384 + * @param mime mime type to use
385 + * @param session session handle
386 + * @param connection connection to use
389 +fill_v1_form (const void *cls,
391 + struct Session *session,
392 + struct MHD_Connection *connection)
395 + const char *form = cls;
397 + struct MHD_Response *response;
399 + if (-1 == asprintf (&reply,
406 + /* return static form */
407 + response = MHD_create_response_from_buffer (strlen (reply),
409 + MHD_RESPMEM_MUST_FREE);
410 + add_session_cookie (session, response);
411 + MHD_add_response_header (response,
412 + MHD_HTTP_HEADER_CONTENT_ENCODING,
414 + ret = MHD_queue_response (connection,
417 + MHD_destroy_response (response);
423 + * Handler that adds the 'v1' and 'v2' values to the given HTML code.
425 + * @param cls a 'const char *' with the HTML webpage to return
426 + * @param mime mime type to use
427 + * @param session session handle
428 + * @param connection connection to use
431 +fill_v1_v2_form (const void *cls,
433 + struct Session *session,
434 + struct MHD_Connection *connection)
437 + const char *form = cls;
439 + struct MHD_Response *response;
441 + if (-1 == asprintf (&reply,
449 + /* return static form */
450 + response = MHD_create_response_from_buffer (strlen (reply),
452 + MHD_RESPMEM_MUST_FREE);
453 + add_session_cookie (session, response);
454 + MHD_add_response_header (response,
455 + MHD_HTTP_HEADER_CONTENT_ENCODING,
457 + ret = MHD_queue_response (connection,
460 + MHD_destroy_response (response);
466 + * Handler used to generate a 404 reply.
468 + * @param cls a 'const char *' with the HTML webpage to return
469 + * @param mime mime type to use
470 + * @param session session handle
471 + * @param connection connection to use
474 +not_found_page (const void *cls,
476 + struct Session *session,
477 + struct MHD_Connection *connection)
480 + struct MHD_Response *response;
482 + /* unsupported HTTP method */
483 + response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR),
484 + (void *) NOT_FOUND_ERROR,
485 + MHD_RESPMEM_PERSISTENT);
486 + ret = MHD_queue_response (connection,
487 + MHD_HTTP_NOT_FOUND,
489 + MHD_add_response_header (response,
490 + MHD_HTTP_HEADER_CONTENT_ENCODING,
492 + MHD_destroy_response (response);
498 + * List of all pages served by this HTTP server.
500 +static struct Page pages[] =
502 + { "/", "text/html", &fill_v1_form, MAIN_PAGE },
503 + { "/2", "text/html", &fill_v1_v2_form, SECOND_PAGE },
504 + { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
505 + { "/F", "text/html", &serve_simple_form, LAST_PAGE },
506 + { NULL, NULL, ¬_found_page, NULL } /* 404 */
512 + * Iterator over key-value pairs where the value
513 + * maybe made available in increments and/or may
514 + * not be zero-terminated. Used for processing
517 + * @param cls user-specified closure
518 + * @param kind type of the value
519 + * @param key 0-terminated key for the value
520 + * @param filename name of the uploaded file, NULL if not known
521 + * @param content_type mime-type of the data, NULL if not known
522 + * @param transfer_encoding encoding of the data, NULL if not known
523 + * @param data pointer to size bytes of data at the
525 + * @param off offset of data in the overall value
526 + * @param size number of bytes in data available
527 + * @return MHD_YES to continue iterating,
528 + * MHD_NO to abort the iteration
531 +post_iterator (void *cls,
532 + enum MHD_ValueKind kind,
534 + const char *filename,
535 + const char *content_type,
536 + const char *transfer_encoding,
537 + const char *data, uint64_t off, size_t size)
539 + struct Request *request = cls;
540 + struct Session *session = request->session;
542 + if (0 == strcmp ("DONE", key))
545 + "Session `%s' submitted `%s', `%s'\n",
551 + if (0 == strcmp ("v1", key))
553 + if (size + off > sizeof(session->value_1))
554 + size = sizeof (session->value_1) - off;
555 + memcpy (&session->value_1[off],
558 + if (size + off < sizeof (session->value_1))
559 + session->value_1[size+off] = '\0';
562 + if (0 == strcmp ("v2", key))
564 + if (size + off > sizeof(session->value_2))
565 + size = sizeof (session->value_2) - off;
566 + memcpy (&session->value_2[off],
569 + if (size + off < sizeof (session->value_2))
570 + session->value_2[size+off] = '\0';
573 + fprintf (stderr, "Unsupported form value `%s'\n", key);
579 + * Main MHD callback for handling requests.
582 + * @param cls argument given together with the function
583 + * pointer when the handler was registered with MHD
584 + * @param url the requested url
585 + * @param method the HTTP method used ("GET", "PUT", etc.)
586 + * @param version the HTTP version string (i.e. "HTTP/1.1")
587 + * @param upload_data the data being uploaded (excluding HEADERS,
588 + * for a POST that fits into memory and that is encoded
589 + * with a supported encoding, the POST data will NOT be
590 + * given in upload_data and is instead available as
591 + * part of MHD_get_connection_values; very large POST
592 + * data *will* be made available incrementally in
594 + * @param upload_data_size set initially to the size of the
595 + * upload_data provided; the method must update this
596 + * value to the number of bytes NOT processed;
597 + * @param con_cls pointer that the callback can set to some
598 + * address and that will be preserved by MHD for future
599 + * calls for this request; since the access handler may
600 + * be called many times (i.e., for a PUT/POST operation
601 + * with plenty of upload data) this allows the application
602 + * to easily associate some request-specific state.
603 + * If necessary, this state can be cleaned up in the
604 + * global "MHD_RequestCompleted" callback (which
605 + * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
606 + * Initially, <tt>*con_cls</tt> will be NULL.
607 + * @return MHS_YES if the connection was handled successfully,
608 + * MHS_NO if the socket must be closed due to a serios
609 + * error while handling the request
612 +create_response (void *cls,
613 + struct MHD_Connection *connection,
615 + const char *method,
616 + const char *version,
617 + const char *upload_data,
618 + size_t *upload_data_size,
621 + struct MHD_Response *response;
622 + struct Request *request;
623 + struct Session *session;
628 + if (NULL == request)
630 + request = calloc (1, sizeof (struct Request));
631 + if (NULL == request)
633 + fprintf (stderr, "calloc error: %s\n", strerror (errno));
637 + if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
639 + request->pp = MHD_create_post_processor (connection, 1024,
640 + &post_iterator, request);
641 + if (NULL == request->pp)
643 + fprintf (stderr, "Failed to setup post processor for `%s'\n",
645 + return MHD_NO; /* internal error */
650 + if (NULL == request->session)
652 + request->session = get_session (connection);
653 + if (NULL == request->session)
655 + fprintf (stderr, "Failed to setup session for `%s'\n",
657 + return MHD_NO; /* internal error */
660 + session = request->session;
661 + session->start = time (NULL);
662 + if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
664 + /* evaluate POST data */
665 + MHD_post_process (request->pp,
667 + *upload_data_size);
668 + if (0 != *upload_data_size)
670 + *upload_data_size = 0;
673 + /* done with POST data, serve response */
674 + MHD_destroy_post_processor (request->pp);
675 + request->pp = NULL;
676 + method = MHD_HTTP_METHOD_GET; /* fake 'GET' */
677 + if (NULL != request->post_url)
678 + url = request->post_url;
681 + if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
682 + (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
684 + /* find out which page to serve */
686 + while ( (pages[i].url != NULL) &&
687 + (0 != strcmp (pages[i].url, url)) )
689 + ret = pages[i].handler (pages[i].handler_cls,
691 + session, connection);
692 + if (ret != MHD_YES)
693 + fprintf (stderr, "Failed to create page for `%s'\n",
697 + /* unsupported HTTP method */
698 + response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
699 + (void *) METHOD_ERROR,
700 + MHD_RESPMEM_PERSISTENT);
701 + ret = MHD_queue_response (connection,
702 + MHD_HTTP_METHOD_NOT_ACCEPTABLE,
704 + MHD_destroy_response (response);
710 + * Callback called upon completion of a request.
711 + * Decrements session reference counter.
713 + * @param cls not used
714 + * @param connection connection that completed
715 + * @param con_cls session handle
716 + * @param toe status code
719 +request_completed_callback (void *cls,
720 + struct MHD_Connection *connection,
722 + enum MHD_RequestTerminationCode toe)
724 + struct Request *request = *con_cls;
726 + if (NULL == request)
728 + if (NULL != request->session)
729 + request->session->rc--;
730 + if (NULL != request->pp)
731 + MHD_destroy_post_processor (request->pp);
737 + * Clean up handles of sessions that have been idle for
743 + struct Session *pos;
744 + struct Session *prev;
745 + struct Session *next;
751 + while (NULL != pos)
754 + if (now - pos->start > 60 * 60)
756 + /* expire sessions after 1h */
758 + sessions = pos->next;
771 + * Call with the port number as the only argument.
772 + * Never terminates (other than by signals, such as CTRL-C).
775 +main (int argc, char *const *argv)
777 + struct MHD_Daemon *d;
779 + struct timeval *tvp;
784 + unsigned MHD_LONG_LONG mhd_timeout;
788 + printf ("%s PORT\n", argv[0]);
791 + /* initialize PRNG */
792 + srandom ((unsigned int) time (NULL));
793 + d = MHD_start_daemon (MHD_USE_DEBUG,
796 + &create_response, NULL,
797 + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
798 + MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
804 + expire_sessions ();
809 + if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
810 + break; /* fatal internal error */
811 + if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES)
813 + tv.tv_sec = mhd_timeout / 1000;
814 + tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
819 + select (max + 1, &rs, &ws, &es, tvp);
822 + MHD_stop_daemon (d);