mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
93 lines
3.4 KiB
PHP
93 lines
3.4 KiB
PHP
This chapter discusses how one should manage sessions, that is, share state between multiple
|
|
HTTP requests from the same user. We use a simple example where the user submits multiple
|
|
forms and the server is supposed to accumulate state from all of these forms. Naturally, as
|
|
this is a network protocol, our session mechanism must support having many users with
|
|
many concurrent sessions at the same time.
|
|
|
|
In order to track users, we use a simple session cookie. A session cookie expires when the
|
|
user closes the browser. Changing from session cookies to persistent cookies only requires
|
|
adding an expiration time to the cookie. The server creates a fresh session cookie whenever
|
|
a request without a cookie is received, or if the supplied session cookie is not known to
|
|
the server.
|
|
|
|
@heading Looking up the cookie
|
|
|
|
Since MHD parses the HTTP cookie header for us, looking up an existing cookie
|
|
is straightforward:
|
|
|
|
@verbatim
|
|
const char *value;
|
|
|
|
value = MHD_lookup_connection_value (connection,
|
|
MHD_COOKIE_KIND,
|
|
"KEY");
|
|
@end verbatim
|
|
|
|
Here, "KEY" is the name we chose for our session cookie.
|
|
|
|
|
|
@heading Setting the cookie header
|
|
|
|
MHD requires the user to provide the full cookie format string in order to set
|
|
cookies. In order to generate a unique cookie, our example creates a random
|
|
32-character text string to be used as the value of the cookie:
|
|
|
|
@verbatim
|
|
char value[128];
|
|
char raw_value[33];
|
|
|
|
for (unsigned int i=0;i<sizeof (raw_value) - 1;i++)
|
|
raw_value[i] = 'A' + (rand () % 26); /* bad PRNG! */
|
|
raw_value[sizeof (raw_value) - 1] = '\0';
|
|
snprintf (value, sizeof (value),
|
|
"%s=%s",
|
|
"KEY",
|
|
raw_value);
|
|
@end verbatim
|
|
|
|
Given this cookie value, we can then set the cookie header in our HTTP response
|
|
as follows:
|
|
|
|
@verbatim
|
|
if (MHD_NO ==
|
|
MHD_add_response_header (response,
|
|
MHD_HTTP_HEADER_SET_COOKIE,
|
|
value))
|
|
fprintf (stderr, "Failed to set session cookie header!\n");
|
|
@end verbatim
|
|
|
|
Note that the header has to be added @emph{before} the response is queued with
|
|
@code{MHD_queue_response}; MHD refuses to modify a response that is already
|
|
queued.
|
|
|
|
|
|
@heading Remark: Session expiration
|
|
|
|
It is of course possible that clients stop their interaction with the
|
|
server at any time. In order to avoid using too much storage, the
|
|
server must thus discard inactive sessions at some point. Our example
|
|
implements this by discarding inactive sessions after a certain amount
|
|
of time. Alternatively, the implementation may limit the total number
|
|
of active sessions. Which bounds are used for idle sessions or the
|
|
total number of sessions obviously depends largely on the type of
|
|
the application and available server resources.
|
|
|
|
@heading Example code
|
|
|
|
A sample application implementing a website with multiple
|
|
forms (which are dynamically created using values from previous
|
|
POST requests from the same session) is available
|
|
as the example @code{sessions.c}.
|
|
|
|
Note that the example uses a simple, @math{O(n)} linked list traversal to
|
|
look up sessions and to expire old sessions. Using a hash table and a
|
|
heap would be more appropriate if a large number of concurrent
|
|
sessions is expected.
|
|
|
|
@heading Remarks
|
|
|
|
Naturally, it is quite conceivable to store session data in a database
|
|
instead of in memory. Still, having mechanisms to expire data
|
|
associated with long-time idle sessions (where the business process
|
|
has still not finished) is likely a good idea.
|