aboutsummaryrefslogtreecommitdiff
path: root/doc/libmicrohttpd-tutorial.info
diff options
context:
space:
mode:
Diffstat (limited to 'doc/libmicrohttpd-tutorial.info')
-rw-r--r--doc/libmicrohttpd-tutorial.info4231
1 files changed, 4231 insertions, 0 deletions
diff --git a/doc/libmicrohttpd-tutorial.info b/doc/libmicrohttpd-tutorial.info
new file mode 100644
index 00000000..79632a40
--- /dev/null
+++ b/doc/libmicrohttpd-tutorial.info
@@ -0,0 +1,4231 @@
+This is libmicrohttpd-tutorial.info, produced by makeinfo version 4.13
+from libmicrohttpd-tutorial.texi.
+
+INFO-DIR-SECTION Software libraries
+START-INFO-DIR-ENTRY
+* libmicrohttpdtutorial: (libmicrohttpd). A tutorial for GNU libmicrohttpd.
+END-INFO-DIR-ENTRY
+
+ This tutorial documents GNU libmicrohttpd version 0.9.23, last
+updated 17 November 2013.
+
+ Copyright (c) 2008 Sebastian Gerhardt.
+
+ Copyright (c) 2010, 2011, 2012, 2013 Christian Grothoff.
+
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, no Front-Cover Texts, and
+ no Back-Cover Texts. A copy of the license is included in the
+ section entitled "GNU Free Documentation License".
+
+
+File: libmicrohttpd-tutorial.info, Node: Top, Next: Introduction, Up: (dir)
+
+A Tutorial for GNU libmicrohttpd
+********************************
+
+This tutorial documents GNU libmicrohttpd version 0.9.23, last updated
+17 November 2013.
+
+ Copyright (c) 2008 Sebastian Gerhardt.
+
+ Copyright (c) 2010, 2011, 2012, 2013 Christian Grothoff.
+
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, no Front-Cover Texts, and
+ no Back-Cover Texts. A copy of the license is included in the
+ section entitled "GNU Free Documentation License".
+
+* Menu:
+
+* Introduction::
+* Hello browser example::
+* Exploring requests::
+* Response headers::
+* Supporting basic authentication::
+* Processing POST data::
+* Improved processing of POST data::
+* Session management::
+* Adding a layer of security::
+* Bibliography::
+* License text::
+* Example programs::
+
+
+File: libmicrohttpd-tutorial.info, Node: Introduction, Next: Hello browser example, Prev: Top, Up: Top
+
+1 Introduction
+**************
+
+This tutorial is for developers who want to learn how they can add HTTP
+serving capabilities to their applications with the _GNU libmicrohttpd_
+library, abbreviated _MHD_. The reader will learn how to implement
+basic HTTP functions from simple executable sample programs that
+implement various features.
+
+ The text is supposed to be a supplement to the API reference manual
+of _GNU libmicrohttpd_ and for that reason does not explain many of the
+parameters. Therefore, the reader should always consult the manual to
+find the exact meaning of the functions used in the tutorial.
+Furthermore, the reader is encouraged to study the relevant _RFCs_,
+which document the HTTP standard.
+
+ _GNU libmicrohttpd_ is assumed to be already installed. This
+tutorial is written for version 0.9.23. At the time being, this
+tutorial has only been tested on _GNU/Linux_ machines even though
+efforts were made not to rely on anything that would prevent the
+samples from being built on similar systems.
+
+1.1 History
+===========
+
+This tutorial was originally written by Sebastian Gerhardt for MHD
+0.4.0. It was slighly polished and updated to MHD 0.9.0 by Christian
+Grothoff.
+
+
+File: libmicrohttpd-tutorial.info, Node: Hello browser example, Next: Exploring requests, Prev: Introduction, Up: Top
+
+2 Hello browser example
+***********************
+
+The most basic task for a HTTP server is to deliver a static text
+message to any client connecting to it. Given that this is also easy
+to implement, it is an excellent problem to start with.
+
+ For now, the particular URI the client asks for shall have no effect
+on the message that will be returned. In addition, the server shall end
+the connection after the message has been sent so that the client will
+know there is nothing more to expect.
+
+ The C program `hellobrowser.c', which is to be found in the examples
+section, does just that. If you are very eager, you can compile and
+start it right away but it is advisable to type the lines in by
+yourself as they will be discussed and explained in detail.
+
+ After the necessary includes and the definition of the port which
+our server should listen on
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <microhttpd.h>
+
+#define PORT 8888
+
+the desired behaviour of our server when HTTP request arrive has to be
+implemented. We already have agreed that it should not care about the
+particular details of the request, such as who is requesting what. The
+server will respond merely with the same small HTML page to every
+request.
+
+ The function we are going to write now will be called by _GNU
+libmicrohttpd_ every time an appropriate request comes in. While the
+name of this callback function is arbitrary, its parameter list has to
+follow a certain layout. So please, ignore the lot of parameters for
+now, they will be explained at the point they are needed. We have to
+use only one of them, `struct MHD_Connection *connection', for the
+minimalistic functionality we want to archive at the moment.
+
+ This parameter is set by the _libmicrohttpd_ daemon and holds the
+necessary information to relate the call with a certain connection.
+Keep in mind that a server might have to satisfy hundreds of concurrent
+connections and we have to make sure that the correct data is sent to
+the destined client. Therefore, this variable is a means to refer to a
+particular connection if we ask the daemon to sent the reply.
+
+ Talking about the reply, it is defined as a string right after the
+function header
+int answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url,
+ const char *method, const char *version,
+ const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ const char *page = "<html><body>Hello, browser!</body></html>";
+
+HTTP is a rather strict protocol and the client would certainly
+consider it "inappropriate" if we just sent the answer string "as is".
+Instead, it has to be wrapped with additional information stored in
+so-called headers and footers. Most of the work in this area is done
+by the library for us--we just have to ask. Our reply string packed in
+the necessary layers will be called a "response". To obtain such a
+response we hand our data (the reply-string) and its size over to the
+`MHD_create_response_from_buffer' function. The last two parameters
+basically tell _MHD_ that we do not want it to dispose the message data
+for us when it has been sent and there also needs no internal copy to
+be done because the _constant_ string won't change anyway.
+
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_buffer (strlen (page),
+ (void*) page, MHD_RESPMEM_PERSISTENT);
+
+Now that the the response has been laced up, it is ready for delivery
+and can be queued for sending. This is done by passing it to another
+_GNU libmicrohttpd_ function. As all our work was done in the scope of
+one function, the recipient is without doubt the one associated with the
+local variable `connection' and consequently this variable is given to
+the queue function. Every HTTP response is accompanied by a status
+code, here "OK", so that the client knows this response is the intended
+result of his request and not due to some error or malfunction.
+
+ Finally, the packet is destroyed and the return value from the queue
+returned, already being set at this point to either MHD_YES or MHD_NO
+in case of success or failure.
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+With the primary task of our server implemented, we can start the
+actual server daemon which will listen on `PORT' for connections. This
+is done in the main function.
+int main ()
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon) return 1;
+
+The first parameter is one of three possible modes of operation. Here
+we want the daemon to run in a separate thread and to manage all
+incoming connections in the same thread. This means that while
+producing the response for one connection, the other connections will
+be put on hold. In this example, where the reply is already known and
+therefore the request is served quickly, this poses no problem.
+
+ We will allow all clients to connect regardless of their name or
+location, therefore we do not check them on connection and set the
+forth and fifth parameter to NULL.
+
+ Parameter six is the address of the function we want to be called
+whenever a new connection has been established. Our
+`answer_to_connection' knows best what the client wants and needs no
+additional information (which could be passed via the next parameter)
+so the next parameter is NULL. Likewise, we do not need to pass extra
+options to the daemon so we just write the MHD_OPTION_END as the last
+parameter.
+
+ As the server daemon runs in the background in its own thread, the
+execution flow in our main function will contine right after the call.
+Because of this, we must delay the execution flow in the main thread or
+else the program will terminate prematurely. We let it pause in a
+processing-time friendly manner by waiting for the enter key to be
+pressed. In the end, we stop the daemon so it can do its cleanup tasks.
+ getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+}
+
+The first example is now complete.
+
+ Compile it with
+cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
+ -L$PATH_TO_LIBMHD_LIBS -lmicrohttpd
+ with the two paths set accordingly and run it.
+
+ Now open your favorite Internet browser and go to the address
+`http://localhost:8888/', provided that 8888 is the port you chose. If
+everything works as expected, the browser will present the message of
+the static HTML page it got from our minimal server.
+
+Remarks
+=======
+
+To keep this first example as small as possible, some drastic shortcuts
+were taken and are to be discussed now.
+
+ Firstly, there is no distinction made between the kinds of requests
+a client could send. We implied that the client sends a GET request,
+that means, that he actually asked for some data. Even when it is not
+intended to accept POST requests, a good server should at least
+recognize that this request does not constitute a legal request and
+answer with an error code. This can be easily implemented by checking
+if the parameter `method' equals the string "GET" and returning a
+`MHD_NO' if not so.
+
+ Secondly, the above practice of queuing a response upon the first
+call of the callback function brings with it some limitations. This is
+because the content of the message body will not be received if a
+response is queued in the first iteration. Furthermore, the connection
+will be closed right after the response has been transferred then.
+This is typically not what you want as it disables HTTP pipelining.
+The correct approach is to simply not queue a message on the first
+callback unless there is an error. The `void**' argument to the
+callback provides a location for storing information about the history
+of the connection; for the first call, the pointer will point to NULL.
+A simplistic way to differenciate the first call from others is to check
+if the pointer is NULL and set it to a non-NULL value during the first
+call.
+
+ Both of these issues you will find addressed in the official
+`minimal_example.c' residing in the `src/examples' directory of the
+_MHD_ package. The source code of this program should look very
+familiar to you by now and easy to understand.
+
+ For our example, the `must_copy' and `must_free' parameter at the
+response construction function could be set to `MHD_NO'. In the usual
+case, responses cannot be sent immediately after being queued. For
+example, there might be other data on the system that needs to be sent
+with a higher priority. Nevertheless, the queue function will return
+successfully--raising the problem that the data we have pointed to may
+be invalid by the time it is about being sent. This is not an issue
+here because we can expect the `page' string, which is a constant
+_string literal_ here, to be static. That means it will be present and
+unchanged for as long as the program runs. For dynamic data, one could
+choose to either have _MHD_ free the memory `page' points to itself
+when it is not longer needed or, alternatively, have the library to
+make and manage its own copy of it.
+
+Exercises
+=========
+
+ * While the server is running, use a program like `telnet' or
+ `netcat' to connect to it. Try to form a valid HTTP 1.1 request
+ yourself like GET /dontcare HTTP/1.1
+ Host: itsme
+ <enter>
+ and see what the server returns to you.
+
+ * Also, try other requests, like POST, and see how our server does
+ not mind and why. How far in malforming a request can you go
+ before the builtin functionality of _MHD_ intervenes and an
+ altered response is sent? Make sure you read about the status
+ codes in the _RFC_.
+
+ * Add the option `MHD_USE_PEDANTIC_CHECKS' to the start function of
+ the daemon in `main'. Mind the special format of the parameter
+ list here which is described in the manual. How indulgent is the
+ server now to your input?
+
+ * Let the main function take a string as the first command line
+ argument and pass `argv[1]' to the `MHD_start_daemon' function as
+ the sixth parameter. The address of this string will be passed to
+ the callback function via the `cls' variable. Decorate the text
+ given at the command line when the server is started with proper
+ HTML tags and send it as the response instead of the former static
+ string.
+
+ * _Demanding:_ Write a separate function returning a string
+ containing some useful information, for example, the time. Pass
+ the function's address as the sixth parameter and evaluate this
+ function on every request anew in `answer_to_connection'. Remember
+ to free the memory of the string every time after satisfying the
+ request.
+
+
+
+File: libmicrohttpd-tutorial.info, Node: Exploring requests, Next: Response headers, Prev: Hello browser example, Up: Top
+
+3 Exploring requests
+********************
+
+This chapter will deal with the information which the client sends to
+the server at every request. We are going to examine the most useful
+fields of such an request and print them out in a readable manner. This
+could be useful for logging facilities.
+
+ The starting point is the _hellobrowser_ program with the former
+response removed.
+
+ This time, we just want to collect information in the callback
+function, thus we will just return MHD_NO after we have probed the
+request. This way, the connection is closed without much ado by the
+server.
+
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url,
+ const char *method, const char *version,
+ const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ ...
+ return MHD_NO;
+}
+ The ellipsis marks the position where the following instructions shall
+be inserted.
+
+ We begin with the most obvious information available to the server,
+the request line. You should already have noted that a request consists
+of a command (or "HTTP method") and a URI (e.g. a filename). It also
+contains a string for the version of the protocol which can be found in
+`version'. To call it a "new request" is justified because we return
+only `MHD_NO', thus ensuring the function will not be called again for
+this connection.
+printf ("New %s request for %s using version %s\n", method, url, version);
+ The rest of the information is a bit more hidden. Nevertheless, there
+is lot of it sent from common Internet browsers. It is stored in
+"key-value" pairs and we want to list what we find in the header. As
+there is no mandatory set of keys a client has to send, each key-value
+pair is printed out one by one until there are no more left. We do this
+by writing a separate function which will be called for each pair just
+like the above function is called for each HTTP request. It can then
+print out the content of this pair.
+int print_out_key (void *cls, enum MHD_ValueKind kind,
+ const char *key, const char *value)
+{
+ printf ("%s: %s\n", key, value);
+ return MHD_YES;
+}
+ To start the iteration process that calls our new function for every
+key, the line
+MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL);
+ needs to be inserted in the connection callback function too. The
+second parameter tells the function that we are only interested in keys
+from the general HTTP header of the request. Our iterating function
+`print_out_key' does not rely on any additional information to fulfill
+its duties so the last parameter can be NULL.
+
+ All in all, this constitutes the complete `logging.c' program for
+this chapter which can be found in the `examples' section.
+
+ Connecting with any modern Internet browser should yield a handful
+of keys. You should try to interpret them with the aid of _RFC 2616_.
+Especially worth mentioning is the "Host" key which is often used to
+serve several different websites hosted under one single IP address but
+reachable by different domain names (this is called virtual hosting).
+
+Conclusion
+==========
+
+The introduced capabilities to itemize the content of a simple GET
+request--especially the URI--should already allow the server to satisfy
+clients' requests for small specific resources (e.g. files) or even
+induce alteration of server state. However, the latter is not
+recommended as the GET method (including its header data) is by
+convention considered a "safe" operation, which should not change the
+server's state in a significant way. By convention, GET operations can
+thus be performed by crawlers and other automatic software. Naturally
+actions like searching for a passed string are fine.
+
+ Of course, no transmission can occur while the return value is still
+set to `MHD_NO' in the callback function.
+
+Exercises
+=========
+
+ * By parsing the `url' string and delivering responses accordingly,
+ implement a small server for "virtual" files. When asked for
+ `/index.htm{l}', let the response consist of a HTML page
+ containing a link to `/another.html' page which is also to be
+ created "on the fly" in case of being requested. If neither of
+ these two pages are requested, `MHD_HTTP_NOT_FOUND' shall be
+ returned accompanied by an informative message.
+
+ * A very interesting information has still been ignored by our
+ logger--the client's IP address. Implement a callback function static int on_client_connect (void *cls,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+ that prints out the IP address in an appropriate format. You
+ might want to use the POSIX function `inet_ntoa' but bear in mind
+ that `addr' is actually just a structure containing other
+ substructures and is _not_ the variable this function expects.
+ Make sure to return `MHD_YES' so that the library knows the client
+ is allowed to connect (and to then process the request). If one
+ wanted to limit access basing on IP addresses, this would be the
+ place to do it. The address of your `on_client_connect' function
+ must be passed as the third parameter to the `MHD_start_daemon'
+ call.
+
+
+
+File: libmicrohttpd-tutorial.info, Node: Response headers, Next: Supporting basic authentication, Prev: Exploring requests, Up: Top
+
+4 Response headers
+******************
+
+Now that we are able to inspect the incoming request in great detail,
+this chapter discusses the means to enrich the outgoing responses
+likewise.
+
+ As you have learned in the _Hello, Browser_ chapter, some obligatory
+header fields are added and set automatically for simple responses by
+the library itself but if more advanced features are desired,
+additional fields have to be created. One of the possible fields is
+the content type field and an example will be developed around it.
+This will lead to an application capable of correctly serving different
+types of files.
+
+ When we responded with HTML page packed in the static string
+previously, the client had no choice but guessing about how to handle
+the response, because the server had not told him. What if we had sent
+a picture or a sound file? Would the message have been understood or
+merely been displayed as an endless stream of random characters in the
+browser? This is what the mime content types are for. The header of
+the response is extended by certain information about how the data is
+to be interpreted.
+
+ To introduce the concept, a picture of the format _PNG_ will be sent
+to the client and labeled accordingly with `image/png'. Once again, we
+can base the new example on the `hellobrowser' program.
+
+#define FILENAME "picture.png"
+#define MIMETYPE "image/png"
+
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url,
+ const char *method, const char *version,
+ const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ unsigned char *buffer = NULL;
+ struct MHD_Response *response;
+ We want the program to open the file for reading and determine its
+size:
+ int fd;
+ int ret;
+ struct stat sbuf;
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+ if ( (-1 == (fd = open (FILENAME, O_RDONLY))) ||
+ (0 != fstat (fd, &sbuf)) )
+ {
+ /* error accessing file */
+ /* ... (see below) */
+ }
+ /* ... (see below) */
+ When dealing with files, there is a lot that could go wrong on the
+server side and if so, the client should be informed with
+`MHD_HTTP_INTERNAL_SERVER_ERROR'.
+
+ /* error accessing file */
+ if (fd != -1) close (fd);
+ const char *errorstr =
+ "<html><body>An internal server error has occured!\
+ </body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (errorstr),
+ (void *) errorstr,
+ MHD_RESPMEM_PERSISTENT);
+ if (response)
+ {
+ ret =
+ MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ response);
+ MHD_destroy_response (response);
+
+ return MHD_YES;
+ }
+ else
+ return MHD_NO;
+ if (!ret)
+ {
+ const char *errorstr = "<html><body>An internal server error has occured!\
+ </body></html>";
+
+ if (buffer) free(buffer);
+
+ response = MHD_create_response_from_buffer (strlen(errorstr), (void*) errorstr,
+ MHD_RESPMEM_PERSISTENT);
+
+ if (response)
+ {
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ response);
+ MHD_destroy_response (response);
+
+ return MHD_YES;
+ }
+ else return MHD_NO;
+ }
+ Note that we nevertheless have to create a response object even for
+sending a simple error code. Otherwise, the connection would just be
+closed without comment, leaving the client curious about what has
+happened.
+
+ But in the case of success a response will be constructed directly
+from the file descriptor:
+
+ /* error accessing file */
+ /* ... (see above) */
+ }
+
+ response =
+ MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0);
+ MHD_add_response_header (response, "Content-Type", MIMETYPE);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+ Note that the response object will take care of closing the file
+desciptor for us.
+
+ Up to this point, there was little new. The actual novelty is that
+we enhance the header with the meta data about the content. Aware of
+the field's name we want to add, it is as easy as that:
+MHD_add_response_header(response, "Content-Type", MIMETYPE);
+ We do not have to append a colon expected by the protocol behind the
+first field--_GNU libhttpdmicro_ will take care of this.
+
+ The function finishes with the well-known lines
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+ return ret;
+}
+ The complete program `responseheaders.c' is in the `examples' section
+as usual. Find a _PNG_ file you like and save it to the directory the
+example is run from under the name `picture.png'. You should find the
+image displayed on your browser if everything worked well.
+
+Remarks
+=======
+
+The include file of the _MHD_ library comes with the header types
+mentioned in _RFC 2616_ already defined as macros. Thus, we could have
+written `MHD_HTTP_HEADER_CONTENT_TYPE' instead of `"Content-Type"' as
+well. However, one is not limited to these standard headers and could
+add custom response headers without violating the protocol. Whether,
+and how, the client would react to these custom header is up to the
+receiver. Likewise, the client is allowed to send custom request
+headers to the server as well, opening up yet more possibilities how
+client and server could communicate with each other.
+
+ The method of creating the response from a file on disk only works
+for static content. Serving dynamically created responses will be a
+topic of a future chapter.
+
+Exercises
+=========
+
+ * Remember that the original program was written under a few
+ assumptions--a static response using a local file being one of
+ them. In order to simulate a very large or hard to reach file that
+ cannot be provided instantly, postpone the queuing in the callback
+ with the `sleep' function for 30 seconds _if_ the file `/big.png'
+ is requested (but deliver the same as above). A request for
+ `/picture.png' should provide just the same but without any
+ artificial delays.
+
+ Now start two instances of your browser (or even use two machines)
+ and see how the second client is put on hold while the first waits
+ for his request on the slow file to be fulfilled.
+
+ Finally, change the sourcecode to use
+ `MHD_USE_THREAD_PER_CONNECTION' when the daemon is started and try
+ again.
+
+ * Did you succeed in implementing the clock exercise yet? This time,
+ let the server save the program's start time `t' and implement a
+ response simulating a countdown that reaches 0 at `t+60'.
+ Returning a message saying on which point the countdown is, the
+ response should ultimately be to reply "Done" if the program has
+ been running long enough,
+
+ An unofficial, but widely understood, response header line is
+ `Refresh: DELAY; url=URL' with the uppercase words substituted to
+ tell the client it should request the given resource after the
+ given delay again. Improve your program in that the browser (any
+ modern browser should work) automatically reconnects and asks for
+ the status again every 5 seconds or so. The URL would have to be
+ composed so that it begins with "http://", followed by the _URI_
+ the server is reachable from the client's point of view.
+
+ Maybe you want also to visualize the countdown as a status bar by
+ creating a `<table>' consisting of one row and `n' columns whose
+ fields contain small images of either a red or a green light.
+
+
+
+File: libmicrohttpd-tutorial.info, Node: Supporting basic authentication, Next: Processing POST data, Prev: Response headers, Up: Top
+
+5 Supporting basic authentication
+*********************************
+
+With the small exception of IP address based access control, requests
+from all connecting clients where served equally until now. This
+chapter discusses a first method of client's authentication and its
+limits.
+
+ A very simple approach feasible with the means already discussed
+would be to expect the password in the _URI_ string before granting
+access to the secured areas. The password could be separated from the
+actual resource identifier by a certain character, thus the request
+line might look like
+GET /picture.png?mypassword
+ In the rare situation where the client is customized enough and the
+connection occurs through secured lines (e.g., a embedded device
+directly attached to another via wire) and where the ability to embedd
+a password in the URI or to pass on a URI with a password are desired,
+this can be a reasonable choice.
+
+ But when it is assumed that the user connecting does so with an
+ordinary Internet browser, this implementation brings some problems
+about. For example, the URI including the password stays in the address
+field or at least in the history of the browser for anybody near enough
+to see. It will also be inconvenient to add the password manually to
+any new URI when the browser does not know how to compose this
+automatically.
+
+ At least the convenience issue can be addressed by employing the
+simplest built-in password facilities of HTTP compliant browsers, hence
+we want to start there. It will however turn out to have still severe
+weaknesses in terms of security which need consideration.
+
+ Before we will start implementing _Basic Authentication_ as
+described in _RFC 2617_, we should finally abandon the bad practice of
+responding every request the first time our callback is called for a
+given connection. This is becoming more important now because the
+client and the server will have to talk in a more bi-directional way
+than before to
+
+ But how can we tell whether the callback has been called before for
+the particular connection? Initially, the pointer this parameter
+references is set by _MHD_ in the callback. But it will also be
+"remembered" on the next call (for the same connection). Thus, we will
+generate no response until the parameter is non-null--implying the
+callback was called before at least once. We do not need to share
+information between different calls of the callback, so we can set the
+parameter to any adress that is assured to be not null. The pointer to
+the `connection' structure will be pointing to a legal address, so we
+take this.
+
+ The first time `answer_to_connection' is called, we will not even
+look at the headers.
+
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 != strcmp(method, "GET")) return MHD_NO;
+ if (NULL == *con_cls) {*con_cls = connection; return MHD_YES;}
+
+ ...
+ /* else respond accordingly */
+ ...
+}
+ Note how we lop off the connection on the first condition (no "GET"
+request), but return asking for more on the other one with `MHD_YES'.
+With this minor change, we can proceed to implement the actual
+authentication process.
+
+Request for authentication
+==========================
+
+Let us assume we had only files not intended to be handed out without
+the correct username/password, so every "GET" request will be
+challenged. _RFC 2617_ describes how the server shall ask for
+authentication by adding a _WWW-Authenticate_ response header with the
+name of the _realm_ protected. MHD can generate and queue such a
+failure response for you using the `MHD_queue_basic_auth_fail_response'
+API. The only thing you need to do is construct a response with the
+error page to be shown to the user if he aborts basic authentication.
+But first, you should check if the proper credentials were already
+supplied using the `MHD_basic_auth_get_username_password' call.
+
+ Your code would then look like this:
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ char *user;
+ char *pass;
+ int fail;
+ struct MHD_Response *response;
+
+ if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+ return MHD_NO;
+ if (NULL == *con_cls)
+ {
+ *con_cls = connection;
+ return MHD_YES;
+ }
+ pass = NULL;
+ user = MHD_basic_auth_get_username_password (connection, &pass);
+ fail = ( (user == NULL) ||
+ (0 != strcmp (user, "root")) ||
+ (0 != strcmp (pass, "pa$$w0rd") ) );
+ if (user != NULL) free (user);
+ if (pass != NULL) free (pass);
+ if (fail)
+ {
+ const char *page = "<html><body>Go away.</body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_basic_auth_fail_response (connection,
+ "my realm",
+ response);
+ }
+ else
+ {
+ const char *page = "<html><body>A secret.</body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ }
+ MHD_destroy_response (response);
+ return ret;
+}
+
+ See the `examples' directory for the complete example file.
+
+Remarks
+=======
+
+For a proper server, the conditional statements leading to a return of
+`MHD_NO' should yield a response with a more precise status code
+instead of silently closing the connection. For example, failures of
+memory allocation are best reported as _internal server error_ and
+unexpected authentication methods as _400 bad request_.
+
+Exercises
+=========
+
+ * Make the server respond to wrong credentials (but otherwise
+ well-formed requests) with the recommended _401 unauthorized_
+ status code. If the client still does not authenticate correctly
+ within the same connection, close it and store the client's IP
+ address for a certain time. (It is OK to check for expiration not
+ until the main thread wakes up again on the next connection.) If
+ the client fails authenticating three times during this period,
+ add it to another list for which the `AcceptPolicyCallback'
+ function denies connection (temporally).
+
+ * With the network utility `netcat' connect and log the response of
+ a "GET" request as you did in the exercise of the first example,
+ this time to a file. Now stop the server and let _netcat_ listen
+ on the same port the server used to listen on and have it fake
+ being the proper server by giving the file's content as the
+ response (e.g. `cat log | nc -l -p 8888'). Pretending to think
+ your were connecting to the actual server, browse to the
+ eavesdropper and give the correct credentials.
+
+ Copy and paste the encoded string you see in `netcat''s output to
+ some of the Base64 decode tools available online and see how both
+ the user's name and password could be completely restored.
+
+
+
+File: libmicrohttpd-tutorial.info, Node: Processing POST data, Next: Improved processing of POST data, Prev: Supporting basic authentication, Up: Top
+
+6 Processing POST data
+**********************
+
+The previous chapters already have demonstrated a variety of
+possibilities to send information to the HTTP server, but it is not
+recommended that the _GET_ method is used to alter the way the server
+operates. To induce changes on the server, the _POST_ method is
+preferred over and is much more powerful than _GET_ and will be
+introduced in this chapter.
+
+ We are going to write an application that asks for the visitor's
+name and, after the user has posted it, composes an individual response
+text. Even though it was not mandatory to use the _POST_ method here,
+as there is no permanent change caused by the POST, it is an
+illustrative example on how to share data between different functions
+for the same connection. Furthermore, the reader should be able to
+extend it easily.
+
+GET request
+===========
+
+When the first _GET_ request arrives, the server shall respond with a
+HTML page containing an edit field for the name.
+
+const char* askpage = "<html><body>\
+ What's your name, Sir?<br>\
+ <form action=\"/namepost\" method=\"post\">\
+ <input name=\"name\" type=\"text\"\
+ <input type=\"submit\" value=\" Send \"></form>\
+ </body></html>";
+ The `action' entry is the _URI_ to be called by the browser when
+posting, and the `name' will be used later to be sure it is the
+editbox's content that has been posted.
+
+ We also prepare the answer page, where the name is to be filled in
+later, and an error page as the response for anything but proper _GET_
+and _POST_ requests:
+
+const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>";
+
+const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
+ Whenever we need to send a page, we use an extra function `int
+send_page(struct MHD_Connection *connection, const char* page)' for
+this, which does not contain anything new and whose implementation is
+therefore not discussed further in the tutorial.
+
+POST request
+============
+
+Posted data can be of arbitrary and considerable size; for example, if
+a user uploads a big image to the server. Similar to the case of the
+header fields, there may also be different streams of posted data, such
+as one containing the text of an editbox and another the state of a
+button. Likewise, we will have to register an iterator function that
+is going to be called maybe several times not only if there are
+different POSTs but also if one POST has only been received partly yet
+and needs processing before another chunk can be received.
+
+ Such an iterator function is called by a _postprocessor_, which must
+be created upon arriving of the post request. We want the iterator
+function to read the first post data which is tagged `name' and to
+create an individual greeting string based on the template and the name.
+But in order to pass this string to other functions and still be able
+to differentiate different connections, we must first define a
+structure to share the information, holding the most import entries.
+
+struct connection_info_struct
+{
+ int connectiontype;
+ char *answerstring;
+ struct MHD_PostProcessor *postprocessor;
+};
+ With these information available to the iterator function, it is able
+to fulfill its task. Once it has composed the greeting string, it
+returns `MHD_NO' to inform the post processor that it does not need to
+be called again. Note that this function does not handle processing of
+data for the same `key'. If we were to expect that the name will be
+posted in several chunks, we had to expand the namestring dynamically
+as additional parts of it with the same `key' came in. But in this
+example, the name is assumed to fit entirely inside one single packet.
+
+static int
+iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data,
+ uint64_t off, size_t size)
+{
+ struct connection_info_struct *con_info = coninfo_cls;
+
+ if (0 == strcmp (key, "name"))
+ {
+ if ((size > 0) && (size <= MAXNAMESIZE))
+ {
+ char *answerstring;
+ answerstring = malloc (MAXANSWERSIZE);
+ if (!answerstring) return MHD_NO;
+
+ snprintf (answerstring, MAXANSWERSIZE, greatingpage, data);
+ con_info->answerstring = answerstring;
+ }
+ else con_info->answerstring = NULL;
+
+ return MHD_NO;
+ }
+
+ return MHD_YES;
+}
+ Once a connection has been established, it can be terminated for many
+reasons. As these reasons include unexpected events, we have to
+register another function that cleans up any resources that might have
+been allocated for that connection by us, namely the post processor and
+the greetings string. This cleanup function must take into account that
+it will also be called for finished requests other than _POST_ requests.
+
+void request_completed (void *cls, struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (NULL == con_info) return;
+ if (con_info->connectiontype == POST)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ if (con_info->answerstring) free (con_info->answerstring);
+ }
+
+ free (con_info);
+ *con_cls = NULL;
+}
+ _GNU libmicrohttpd_ is informed that it shall call the above function
+when the daemon is started in the main function.
+
+...
+daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL,
+ MHD_OPTION_END);
+...
+
+Request handling
+================
+
+With all other functions prepared, we can now discuss the actual
+request handling.
+
+ On the first iteration for a new request, we start by allocating a
+new instance of a `struct connection_info_struct' structure, which will
+store all necessary information for later iterations and other
+functions.
+
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url,
+ const char *method, const char *version,
+ const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ if(NULL == *con_cls)
+ {
+ struct connection_info_struct *con_info;
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info) return MHD_NO;
+ con_info->answerstring = NULL;
+ If the new request is a _POST_, the postprocessor must be created now.
+In addition, the type of the request is stored for convenience.
+ if (0 == strcmp (method, "POST"))
+ {
+ con_info->postprocessor
+ = MHD_create_post_processor (connection, POSTBUFFERSIZE,
+ iterate_post, (void*) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+ con_info->connectiontype = POST;
+ }
+ else con_info->connectiontype = GET;
+ The address of our structure will both serve as the indicator for
+successive iterations and to remember the particular details about the
+connection.
+ *con_cls = (void*) con_info;
+ return MHD_YES;
+ }
+ The rest of the function will not be executed on the first iteration.
+A _GET_ request is easily satisfied by sending the question form.
+ if (0 == strcmp (method, "GET"))
+ {
+ return send_page (connection, askpage);
+ }
+ In case of _POST_, we invoke the post processor for as long as data
+keeps incoming, setting `*upload_data_size' to zero in order to
+indicate that we have processed--or at least have considered--all of it.
+ if (0 == strcmp (method, "POST"))
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (*upload_data_size != 0)
+ {
+ MHD_post_process (con_info->postprocessor, upload_data,
+ *upload_data_size);
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ else if (NULL != con_info->answerstring)
+ return send_page (connection, con_info->answerstring);
+ }
+ Finally, if they are neither _GET_ nor _POST_ requests, the error page
+is returned.
+ return send_page(connection, errorpage);
+}
+ These were the important parts of the program `simplepost.c'.
+
+
+File: libmicrohttpd-tutorial.info, Node: Improved processing of POST data, Next: Session management, Prev: Processing POST data, Up: Top
+
+7 Improved processing of POST data
+**********************************
+
+The previous chapter introduced a way to upload data to the server, but
+the developed example program has some shortcomings, such as not being
+able to handle larger chunks of data. In this chapter, we are going to
+discuss a more advanced server program that allows clients to upload a
+file in order to have it stored on the server's filesystem. The server
+shall also watch and limit the number of clients concurrently
+uploading, responding with a proper busy message if necessary.
+
+Prepared answers
+================
+
+We choose to operate the server with the `SELECT_INTERNALLY' method.
+This makes it easier to synchronize the global states at the cost of
+possible delays for other connections if the processing of a request is
+too slow. One of these variables that needs to be shared for all
+connections is the total number of clients that are uploading.
+
+#define MAXCLIENTS 2
+static unsigned int nr_of_uploading_clients = 0;
+ If there are too many clients uploading, we want the server to respond
+to all requests with a busy message.
+const char* busypage =
+ "<html><body>This server is busy, please try again later.</body></html>";
+ Otherwise, the server will send a _form_ that informs the user of the
+current number of uploading clients, and ask her to pick a file on her
+local filesystem which is to be uploaded.
+const char* askpage = "<html><body>\n\
+ Upload a file, please!<br>\n\
+ There are %u clients uploading at the moment.<br>\n\
+ <form action=\"/filepost\" method=\"post\" \
+ enctype=\"multipart/form-data\">\n\
+ <input name=\"file\" type=\"file\">\n\
+ <input type=\"submit\" value=\" Send \"></form>\n\
+ </body></html>";
+ If the upload has succeeded, the server will respond with a message
+saying so.
+const char* completepage = "<html><body>The upload has been completed.</body></html>";
+ We want the server to report internal errors, such as memory shortage
+or file access problems, adequately.
+const char* servererrorpage
+ = "<html><body>An internal server error has occured.</body></html>";
+const char* fileexistspage
+ = "<html><body>This file already exists.</body></html>";
+ It would be tolerable to send all these responses undifferentiated
+with a `200 HTTP_OK' status code but in order to improve the `HTTP'
+conformance of our server a bit, we extend the `send_page' function so
+that it accepts individual status codes.
+
+static int
+send_page (struct MHD_Connection *connection,
+ const char* page, int status_code)
+{
+ int ret;
+ struct MHD_Response *response;
+
+ response = MHD_create_response_from_buffer (strlen (page), (void*) page,
+ MHD_RESPMEM_MUST_COPY);
+ if (!response) return MHD_NO;
+
+ ret = MHD_queue_response (connection, status_code, response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+ Note how we ask _MHD_ to make its own copy of the message data. The
+reason behind this will become clear later.
+
+Connection cycle
+================
+
+The decision whether the server is busy or not is made right at the
+beginning of the connection. To do that at this stage is especially
+important for _POST_ requests because if no response is queued at this
+point, and `MHD_YES' returned, _MHD_ will not sent any queued messages
+until a postprocessor has been created and the post iterator is called
+at least once.
+
+static int
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url,
+ const char *method, const char *version,
+ const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+{
+ if (NULL == *con_cls)
+ {
+ struct connection_info_struct *con_info;
+
+ if (nr_of_uploading_clients >= MAXCLIENTS)
+ return send_page(connection, busypage, MHD_HTTP_SERVICE_UNAVAILABLE);
+ If the server is not busy, the `connection_info' structure is
+initialized as usual, with the addition of a filepointer for each
+connection.
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info) return MHD_NO;
+ con_info->fp = 0;
+
+ if (0 == strcmp (method, "POST"))
+ {
+ ...
+ }
+ else con_info->connectiontype = GET;
+
+ *con_cls = (void*) con_info;
+
+ return MHD_YES;
+ }
+ For _POST_ requests, the postprocessor is created and we register a
+new uploading client. From this point on, there are many possible
+places for errors to occur that make it necessary to interrupt the
+uploading process. We need a means of having the proper response
+message ready at all times. Therefore, the `connection_info' structure
+is extended to hold the most current response message so that whenever
+a response is sent, the client will get the most informative message.
+Here, the structure is initialized to "no error".
+ if (0 == strcmp (method, "POST"))
+ {
+ con_info->postprocessor
+ = MHD_create_post_processor (connection, POSTBUFFERSIZE,
+ iterate_post, (void*) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+
+ nr_of_uploading_clients++;
+
+ con_info->connectiontype = POST;
+ con_info->answercode = MHD_HTTP_OK;
+ con_info->answerstring = completepage;
+ }
+ else con_info->connectiontype = GET;
+ If the connection handler is called for the second time, _GET_
+requests will be answered with the _form_. We can keep the buffer under
+function scope, because we asked _MHD_ to make its own copy of it for
+as long as it is needed.
+ if (0 == strcmp (method, "GET"))
+ {
+ int ret;
+ char buffer[1024];
+
+ sprintf (buffer, askpage, nr_of_uploading_clients);
+ return send_page (connection, buffer, MHD_HTTP_OK);
+ }
+ The rest of the `answer_to_connection' function is very similar to the
+`simplepost.c' example, except the more flexible content of the
+responses. The _POST_ data is processed until there is none left and
+the execution falls through to return an error page if the connection
+constituted no expected request method.
+ if (0 == strcmp (method, "POST"))
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (0 != *upload_data_size)
+ {
+ MHD_post_process (con_info->postprocessor,
+ upload_data, *upload_data_size);
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ else
+ return send_page (connection, con_info->answerstring,
+ con_info->answercode);
+ }
+
+ return send_page(connection, errorpage, MHD_HTTP_BAD_REQUEST);
+}
+
+Storing to data
+===============
+
+Unlike the `simplepost.c' example, here it is to be expected that post
+iterator will be called several times now. This means that for any
+given connection (there might be several concurrent of them) the posted
+data has to be written to the correct file. That is why we store a file
+handle in every `connection_info', so that the it is preserved between
+successive iterations.
+static int
+iterate_post (void *coninfo_cls, enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data,
+ uint64_t off, size_t size)
+{
+ struct connection_info_struct *con_info = coninfo_cls;
+ Because the following actions depend heavily on correct file
+processing, which might be error prone, we default to reporting
+internal errors in case anything will go wrong.
+
+con_info->answerstring = servererrorpage;
+con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ In the "askpage" _form_, we told the client to label its post data
+with the "file" key. Anything else would be an error.
+
+ if (0 != strcmp (key, "file")) return MHD_NO;
+ If the iterator is called for the first time, no file will have been
+opened yet. The `filename' string contains the name of the file
+(without any paths) the user selected on his system. We want to take
+this as the name the file will be stored on the server and make sure no
+file of that name exists (or is being uploaded) before we create one
+(note that the code below technically contains a race between the two
+"fopen" calls, but we will overlook this for portability sake).
+ if (!con_info->fp)
+ {
+ if (NULL != (fp = fopen (filename, "rb")) )
+ {
+ fclose (fp);
+ con_info->answerstring = fileexistspage;
+ con_info->answercode = MHD_HTTP_FORBIDDEN;
+ return MHD_NO;
+ }
+
+ con_info->fp = fopen (filename, "ab");
+ if (!con_info->fp) return MHD_NO;
+ }
+ Occasionally, the iterator function will be called even when there are
+0 new bytes to process. The server only needs to write data to the file
+if there is some.
+if (size > 0)
+ {
+ if (!fwrite (data, size, sizeof(char), con_info->fp))
+ return MHD_NO;
+ }
+ If this point has been reached, everything worked well for this
+iteration and the response can be set to success again. If the upload
+has finished, this iterator function will not be called again.
+ con_info->answerstring = completepage;
+ con_info->answercode = MHD_HTTP_OK;
+
+ return MHD_YES;
+}
+ The new client was registered when the postprocessor was created.
+Likewise, we unregister the client on destroying the postprocessor when
+the request is completed.
+void request_completed (void *cls, struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (NULL == con_info) return;
+
+ if (con_info->connectiontype == POST)
+ {
+ if (NULL != con_info->postprocessor)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ nr_of_uploading_clients--;
+ }
+
+ if (con_info->fp) fclose (con_info->fp);
+ }
+
+ free (con_info);
+ *con_cls = NULL;
+}
+ This is essentially the whole example `largepost.c'.
+
+Remarks
+=======
+
+Now that the clients are able to create files on the server, security
+aspects are becoming even more important than before. Aside from proper
+client authentication, the server should always make sure explicitly
+that no files will be created outside of a dedicated upload directory.
+In particular, filenames must be checked to not contain strings like
+"../".
+
+
+File: libmicrohttpd-tutorial.info, Node: Session management, Next: Adding a layer of security, Prev: Improved processing of POST data, Up: Top
+
+8 Session management
+********************
+
+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.
+
+Looking up the cookie
+=====================
+
+Since MHD parses the HTTP cookie header for us, looking up an existing
+cookie is straightforward:
+
+FIXME.
+
+ Here, FIXME is the name we chose for our session cookie.
+
+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 64-character text string to be used as the value of
+the cookie:
+
+FIXME.
+
+ Given this cookie value, we can then set the cookie header in our
+HTTP response as follows:
+
+FIXME.
+
+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.
+
+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 `sessions.c'.
+
+ Note that the example uses a simple, $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.
+
+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.
+
+
+File: libmicrohttpd-tutorial.info, Node: Adding a layer of security, Next: Bibliography, Prev: Session management, Up: Top
+
+9 Adding a layer of security
+****************************
+
+We left the basic authentication chapter with the unsatisfactory
+conclusion that any traffic, including the credentials, could be
+intercepted by anyone between the browser client and the server.
+Protecting the data while it is sent over unsecured lines will be the
+goal of this chapter.
+
+ Since version 0.4, the _MHD_ library includes support for encrypting
+the traffic by employing SSL/TSL. If _GNU libmicrohttpd_ has been
+configured to support these, encryption and decryption can be applied
+transparently on the data being sent, with only minimal changes to the
+actual source code of the example.
+
+Preparation
+===========
+
+First, a private key for the server will be generated. With this key,
+the server will later be able to authenticate itself to the
+client--preventing anyone else from stealing the password by faking its
+identity. The _OpenSSL_ suite, which is available on many operating
+systems, can generate such a key. For the scope of this tutorial, we
+will be content with a 1024 bit key:
+> openssl genrsa -out server.key 1024
+ In addition to the key, a certificate describing the server in human
+readable tokens is also needed. This certificate will be attested with
+our aforementioned key. In this way, we obtain a self-signed
+certificate, valid for one year.
+
+> openssl req -days 365 -out server.pem -new -x509 -key server.key
+ To avoid unnecessary error messages in the browser, the certificate
+needs to have a name that matches the _URI_, for example, "localhost"
+or the domain. If you plan to have a publicly reachable server, you
+will need to ask a trusted third party, called _Certificate Authority_,
+or _CA_, to attest the certificate for you. This way, any visitor can
+make sure the server's identity is real.
+
+ Whether the server's certificate is signed by us or a third party,
+once it has been accepted by the client, both sides will be
+communicating over encrypted channels. From this point on, it is the
+client's turn to authenticate itself. But this has already been
+implemented in the basic authentication scheme.
+
+Changing the source code
+========================
+
+We merely have to extend the server program so that it loads the two
+files into memory,
+
+int
+main ()
+{
+ struct MHD_Daemon *daemon;
+ char *key_pem;
+ char *cert_pem;
+
+ key_pem = load_file (SERVERKEYFILE);
+ cert_pem = load_file (SERVERCERTFILE);
+
+ if ((key_pem == NULL) || (cert_pem == NULL))
+ {
+ printf ("The key/certificate files could not be read.\n");
+ return 1;
+ }
+ and then we point the _MHD_ daemon to it upon initalization.
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+ MHD_OPTION_END);
+
+ if (NULL == daemon)
+ {
+ printf ("%s\n", cert_pem);
+
+ free (key_pem);
+ free (cert_pem);
+
+ return 1;
+ }
+ The rest consists of little new besides some additional memory
+cleanups.
+
+ getchar ();
+
+ MHD_stop_daemon (daemon);
+ free (key_pem);
+ free (cert_pem);
+
+ return 0;
+}
+ The rather unexciting file loader can be found in the complete example
+`tlsauthentication.c'.
+
+Remarks
+=======
+
+ * While the standard _HTTP_ port is 80, it is 443 for _HTTPS_. The
+ common internet browsers assume standard _HTTP_ if they are asked
+ to access other ports than these. Therefore, you will have to type
+ `https://localhost:8888' explicitly when you test the example, or
+ the browser will not know how to handle the answer properly.
+
+ * The remaining weak point is the question how the server will be
+ trusted initially. Either a _CA_ signs the certificate or the
+ client obtains the key over secure means. Anyway, the clients have
+ to be aware (or configured) that they should not accept
+ certificates of unknown origin.
+
+ * The introduced method of certificates makes it mandatory to set an
+ expiration date--making it less feasible to hardcode certificates
+ in embedded devices.
+
+ * The cryptographic facilities consume memory space and computing
+ time. For this reason, websites usually consists both of
+ uncritically _HTTP_ parts and secured _HTTPS_.
+
+
+Client authentication
+=====================
+
+You can also use MHD to authenticate the client via SSL/TLS certificates
+(as an alternative to using the password-based Basic or Digest
+authentication). To do this, you will need to link your application
+against _gnutls_. Next, when you start the MHD daemon, you must
+specify the root CA that you're willing to trust:
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+ MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem,
+ MHD_OPTION_END);
+
+ With this, you can then obtain client certificates for each session.
+In order to obtain the identity of the client, you first need to obtain
+the raw GnuTLS session handle from _MHD_ using
+`MHD_get_connection_info'.
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+gnutls_session_t tls_session;
+union MHD_ConnectionInfo *ci;
+
+ci = MHD_get_connection_info (connection,
+ MHD_CONNECTION_INFO_GNUTLS_SESSION);
+tls_session = ci->tls_session;
+
+ You can then extract the client certificate:
+
+/**
+ * Get the client's certificate
+ *
+ * @param tls_session the TLS session
+ * @return NULL if no valid client certificate could be found, a pointer
+ * to the certificate if found
+ */
+static gnutls_x509_crt_t
+get_client_certificate (gnutls_session_t tls_session)
+{
+ unsigned int listsize;
+ const gnutls_datum_t * pcert;
+ gnutls_certificate_status_t client_cert_status;
+ gnutls_x509_crt_t client_cert;
+
+ if (tls_session == NULL)
+ return NULL;
+ if (gnutls_certificate_verify_peers2(tls_session,
+ &client_cert_status))
+ return NULL;
+ pcert = gnutls_certificate_get_peers(tls_session,
+ &listsize);
+ if ( (pcert == NULL) ||
+ (listsize == 0))
+ {
+ fprintf (stderr,
+ "Failed to retrieve client certificate chain\n");
+ return NULL;
+ }
+ if (gnutls_x509_crt_init(&client_cert))
+ {
+ fprintf (stderr,
+ "Failed to initialize client certificate\n");
+ return NULL;
+ }
+ /* Note that by passing values between 0 and listsize here, you
+ can get access to the CA's certs */
+ if (gnutls_x509_crt_import(client_cert,
+ &pcert[0],
+ GNUTLS_X509_FMT_DER))
+ {
+ fprintf (stderr,
+ "Failed to import client certificate\n");
+ gnutls_x509_crt_deinit(client_cert);
+ return NULL;
+ }
+ return client_cert;
+}
+
+ Using the client certificate, you can then get the client's
+distinguished name and alternative names:
+
+/**
+ * Get the distinguished name from the client's certificate
+ *
+ * @param client_cert the client certificate
+ * @return NULL if no dn or certificate could be found, a pointer
+ * to the dn if found
+ */
+char *
+cert_auth_get_dn(gnutls_x509_crt_c client_cert)
+{
+ char* buf;
+ size_t lbuf;
+
+ lbuf = 0;
+ gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf);
+ buf = malloc(lbuf);
+ if (buf == NULL)
+ {
+ fprintf (stderr,
+ "Failed to allocate memory for certificate dn\n");
+ return NULL;
+ }
+ gnutls_x509_crt_get_dn(client_cert, buf, &lbuf);
+ return buf;
+}
+
+
+/**
+ * Get the alternative name of specified type from the client's certificate
+ *
+ * @param client_cert the client certificate
+ * @param nametype The requested name type
+ * @param index The position of the alternative name if multiple names are
+ * matching the requested type, 0 for the first matching name
+ * @return NULL if no matching alternative name could be found, a pointer
+ * to the alternative name if found
+ */
+char *
+MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
+ int nametype,
+ unsigned int index)
+{
+ char* buf;
+ size_t lbuf;
+ unsigned int seq;
+ unsigned int subseq;
+ unsigned int type;
+ int result;
+
+ subseq = 0;
+ for (seq=0;;seq++)
+ {
+ lbuf = 0;
+ result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf,
+ &type, NULL);
+ if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ return NULL;
+ if (nametype != (int) type)
+ continue;
+ if (subseq == index)
+ break;
+ subseq++;
+ }
+ buf = malloc(lbuf);
+ if (buf == NULL)
+ {
+ fprintf (stderr,
+ "Failed to allocate memory for certificate alt name\n");
+ return NULL;
+ }
+ result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
+ seq,
+ buf,
+ &lbuf,
+ NULL, NULL);
+ if (result != nametype)
+ {
+ fprintf (stderr,
+ "Unexpected return value from gnutls: %d\n",
+ result);
+ free (buf);
+ return NULL;
+ }
+ return buf;
+}
+
+ Finally, you should release the memory associated with the client
+certificate:
+
+gnutls_x509_crt_deinit (client_cert);
+
+Using TLS Server Name Indication (SNI)
+======================================
+
+SNI enables hosting multiple domains under one IP address with TLS. So
+SNI is the TLS-equivalent of virtual hosting. To use SNI with MHD, you
+need at least GnuTLS 3.0. The main change compared to the simple
+hosting of one domain is that you need to provide a callback instead of
+the key and certificate. For example, when you start the MHD daemon,
+you could do this:
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
+ MHD_OPTION_END);
+ Here, `sni_callback' is the name of a function that you will have to
+implement to retrieve the X.509 certificate for an incoming connection.
+The callback has type `gnutls_certificate_retrieve_function2' and is
+documented in the GnuTLS API for the
+`gnutls_certificate_set_retrieve_function2' as follows:
+
+ -- Function Pointer: int *gnutls_certificate_retrieve_function2
+ (gnutls_session_t, const gnutls_datum_t* req_ca_dn, int
+ nreqs, const gnutls_pk_algorithm_t* pk_algos, int
+ pk_algos_length, gnutls_pcert_st** pcert, unsigned int
+ *pcert_length, gnutls_privkey_t * pkey)
+ REQ_CA_CERT
+ is only used in X.509 certificates. Contains a list with the
+ CA names that the server considers trusted. Normally we
+ should send a certificate that is signed by one of these CAs.
+ These names are DER encoded. To get a more meaningful value
+ use the function `gnutls_x509_rdn_get()'.
+
+ PK_ALGOS
+ contains a list with server’s acceptable signature
+ algorithms. The certificate returned should support the
+ server’s given algorithms.
+
+ PCERT
+ should contain a single certificate and public or a list of
+ them.
+
+ PCERT_LENGTH
+ is the size of the previous list.
+
+ PKEY
+ is the private key.
+
+ A possible implementation of this callback would look like this:
+
+struct Hosts
+{
+ struct Hosts *next;
+ const char *hostname;
+ gnutls_pcert_st pcrt;
+ gnutls_privkey_t key;
+};
+
+static struct Hosts *hosts;
+
+int
+sni_callback (gnutls_session_t session,
+ const gnutls_datum_t* req_ca_dn,
+ int nreqs,
+ const gnutls_pk_algorithm_t* pk_algos,
+ int pk_algos_length,
+ gnutls_pcert_st** pcert,
+ unsigned int *pcert_length,
+ gnutls_privkey_t * pkey)
+{
+ char name[256];
+ size_t name_len;
+ struct Hosts *host;
+ unsigned int type;
+
+ name_len = sizeof (name);
+ if (GNUTLS_E_SUCCESS !=
+ gnutls_server_name_get (session,
+ name,
+ &name_len,
+ &type,
+ 0 /* index */))
+ return -1;
+ for (host = hosts; NULL != host; host = host->next)
+ if (0 == strncmp (name, host->hostname, name_len))
+ break;
+ if (NULL == host)
+ {
+ fprintf (stderr,
+ "Need certificate for %.*s\n",
+ (int) name_len,
+ name);
+ return -1;
+ }
+ fprintf (stderr,
+ "Returning certificate for %.*s\n",
+ (int) name_len,
+ name);
+ *pkey = host->key;
+ *pcert_length = 1;
+ *pcert = &host->pcrt;
+ return 0;
+}
+
+ Note that MHD cannot offer passing a closure or any other additional
+information to this callback, as the GnuTLS API unfortunately does not
+permit this at this point.
+
+ The `hosts' list can be initialized by loading the private keys and
+X.509 certificats from disk as follows:
+
+static void
+load_keys(const char *hostname,
+ const char *CERT_FILE,
+ const char *KEY_FILE)
+{
+ int ret;
+ gnutls_datum_t data;
+ struct Hosts *host;
+
+ host = malloc (sizeof (struct Hosts));
+ host->hostname = hostname;
+ host->next = hosts;
+ hosts = host;
+
+ ret = gnutls_load_file (CERT_FILE, &data);
+ if (ret < 0)
+ {
+ fprintf (stderr,
+ "*** Error loading certificate file %s.\n",
+ CERT_FILE);
+ exit(1);
+ }
+ ret =
+ gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
+ 0);
+ if (ret < 0)
+ {
+ fprintf(stderr,
+ "*** Error loading certificate file: %s\n",
+ gnutls_strerror (ret));
+ exit(1);
+ }
+ gnutls_free (data.data);
+
+ ret = gnutls_load_file (KEY_FILE, &data);
+ if (ret < 0)
+ {
+ fprintf (stderr,
+ "*** Error loading key file %s.\n",
+ KEY_FILE);
+ exit(1);
+ }
+
+ gnutls_privkey_init (&host->key);
+ ret =
+ gnutls_privkey_import_x509_raw (host->key,
+ &data, GNUTLS_X509_FMT_PEM,
+ NULL, 0);
+ if (ret < 0)
+ {
+ fprintf (stderr,
+ "*** Error loading key file: %s\n",
+ gnutls_strerror (ret));
+ exit(1);
+ }
+ gnutls_free (data.data);
+}
+
+ The code above was largely lifted from GnuTLS. You can find other
+methods for initializing certificates and keys in the GnuTLS manual and
+source code.
+
+
+File: libmicrohttpd-tutorial.info, Node: Bibliography, Next: License text, Prev: Adding a layer of security, Up: Top
+
+Appendix A Bibliography
+***********************
+
+API reference
+=============
+
+ * The _GNU libmicrohttpd_ manual by Marco Maggi and Christian
+ Grothoff 2008 `http://gnunet.org/libmicrohttpd/microhttpd.html'
+
+ * All referenced RFCs can be found on the website of _The Internet
+ Engineering Task Force_ `http://www.ietf.org/'
+
+ * _RFC 2616_: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and
+ T. Berners-Lee, "Hypertext Transfer Protocol - HTTP/1.1", RFC
+ 2016, January 1997.
+
+ * _RFC 2617_: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence,
+ S., Leach, P., Luotonen, A., and L. Stewart, "HTTP Authentication:
+ Basic and Digest Access Authentication", RFC 2617, June 1999.
+
+ * A well-structured _HTML_ reference can be found on
+ `http://www.echoecho.com/html.htm'
+
+ For those readers understanding German or French, there is an
+ excellent document both for learning _HTML_ and for reference,
+ whose English version unfortunately has been discontinued.
+ `http://de.selfhtml.org/' and `http://fr.selfhtml.org/'
+
+
+
+File: libmicrohttpd-tutorial.info, Node: License text, Next: Example programs, Prev: Bibliography, Up: Top
+
+Appendix B GNU Free Documentation License
+*****************************************
+
+ Version 1.3, 3 November 2008
+
+ Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+ `http://fsf.org/'
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ 0. PREAMBLE
+
+ The purpose of this License is to make a manual, textbook, or other
+ functional and useful document "free" in the sense of freedom: to
+ assure everyone the effective freedom to copy and redistribute it,
+ with or without modifying it, either commercially or
+ noncommercially. Secondarily, this License preserves for the
+ author and publisher a way to get credit for their work, while not
+ being considered responsible for modifications made by others.
+
+ This License is a kind of "copyleft", which means that derivative
+ works of the document must themselves be free in the same sense.
+ It complements the GNU General Public License, which is a copyleft
+ license designed for free software.
+
+ We have designed this License in order to use it for manuals for
+ free software, because free software needs free documentation: a
+ free program should come with manuals providing the same freedoms
+ that the software does. But this License is not limited to
+ software manuals; it can be used for any textual work, regardless
+ of subject matter or whether it is published as a printed book.
+ We recommend this License principally for works whose purpose is
+ instruction or reference.
+
+ 1. APPLICABILITY AND DEFINITIONS
+
+ This License applies to any manual or other work, in any medium,
+ that contains a notice placed by the copyright holder saying it
+ can be distributed under the terms of this License. Such a notice
+ grants a world-wide, royalty-free license, unlimited in duration,
+ to use that work under the conditions stated herein. The
+ "Document", below, refers to any such manual or work. Any member
+ of the public is a licensee, and is addressed as "you". You
+ accept the license if you copy, modify or distribute the work in a
+ way requiring permission under copyright law.
+
+ A "Modified Version" of the Document means any work containing the
+ Document or a portion of it, either copied verbatim, or with
+ modifications and/or translated into another language.
+
+ A "Secondary Section" is a named appendix or a front-matter section
+ of the Document that deals exclusively with the relationship of the
+ publishers or authors of the Document to the Document's overall
+ subject (or to related matters) and contains nothing that could
+ fall directly within that overall subject. (Thus, if the Document
+ is in part a textbook of mathematics, a Secondary Section may not
+ explain any mathematics.) The relationship could be a matter of
+ historical connection with the subject or with related matters, or
+ of legal, commercial, philosophical, ethical or political position
+ regarding them.
+
+ The "Invariant Sections" are certain Secondary Sections whose
+ titles are designated, as being those of Invariant Sections, in
+ the notice that says that the Document is released under this
+ License. If a section does not fit the above definition of
+ Secondary then it is not allowed to be designated as Invariant.
+ The Document may contain zero Invariant Sections. If the Document
+ does not identify any Invariant Sections then there are none.
+
+ The "Cover Texts" are certain short passages of text that are
+ listed, as Front-Cover Texts or Back-Cover Texts, in the notice
+ that says that the Document is released under this License. A
+ Front-Cover Text may be at most 5 words, and a Back-Cover Text may
+ be at most 25 words.
+
+ A "Transparent" copy of the Document means a machine-readable copy,
+ represented in a format whose specification is available to the
+ general public, that is suitable for revising the document
+ straightforwardly with generic text editors or (for images
+ composed of pixels) generic paint programs or (for drawings) some
+ widely available drawing editor, and that is suitable for input to
+ text formatters or for automatic translation to a variety of
+ formats suitable for input to text formatters. A copy made in an
+ otherwise Transparent file format whose markup, or absence of
+ markup, has been arranged to thwart or discourage subsequent
+ modification by readers is not Transparent. An image format is
+ not Transparent if used for any substantial amount of text. A
+ copy that is not "Transparent" is called "Opaque".
+
+ Examples of suitable formats for Transparent copies include plain
+ ASCII without markup, Texinfo input format, LaTeX input format,
+ SGML or XML using a publicly available DTD, and
+ standard-conforming simple HTML, PostScript or PDF designed for
+ human modification. Examples of transparent image formats include
+ PNG, XCF and JPG. Opaque formats include proprietary formats that
+ can be read and edited only by proprietary word processors, SGML or
+ XML for which the DTD and/or processing tools are not generally
+ available, and the machine-generated HTML, PostScript or PDF
+ produced by some word processors for output purposes only.
+
+ The "Title Page" means, for a printed book, the title page itself,
+ plus such following pages as are needed to hold, legibly, the
+ material this License requires to appear in the title page. For
+ works in formats which do not have any title page as such, "Title
+ Page" means the text near the most prominent appearance of the
+ work's title, preceding the beginning of the body of the text.
+
+ The "publisher" means any person or entity that distributes copies
+ of the Document to the public.
+
+ A section "Entitled XYZ" means a named subunit of the Document
+ whose title either is precisely XYZ or contains XYZ in parentheses
+ following text that translates XYZ in another language. (Here XYZ
+ stands for a specific section name mentioned below, such as
+ "Acknowledgements", "Dedications", "Endorsements", or "History".)
+ To "Preserve the Title" of such a section when you modify the
+ Document means that it remains a section "Entitled XYZ" according
+ to this definition.
+
+ The Document may include Warranty Disclaimers next to the notice
+ which states that this License applies to the Document. These
+ Warranty Disclaimers are considered to be included by reference in
+ this License, but only as regards disclaiming warranties: any other
+ implication that these Warranty Disclaimers may have is void and
+ has no effect on the meaning of this License.
+
+ 2. VERBATIM COPYING
+
+ You may copy and distribute the Document in any medium, either
+ commercially or noncommercially, provided that this License, the
+ copyright notices, and the license notice saying this License
+ applies to the Document are reproduced in all copies, and that you
+ add no other conditions whatsoever to those of this License. You
+ may not use technical measures to obstruct or control the reading
+ or further copying of the copies you make or distribute. However,
+ you may accept compensation in exchange for copies. If you
+ distribute a large enough number of copies you must also follow
+ the conditions in section 3.
+
+ You may also lend copies, under the same conditions stated above,
+ and you may publicly display copies.
+
+ 3. COPYING IN QUANTITY
+
+ If you publish printed copies (or copies in media that commonly
+ have printed covers) of the Document, numbering more than 100, and
+ the Document's license notice requires Cover Texts, you must
+ enclose the copies in covers that carry, clearly and legibly, all
+ these Cover Texts: Front-Cover Texts on the front cover, and
+ Back-Cover Texts on the back cover. Both covers must also clearly
+ and legibly identify you as the publisher of these copies. The
+ front cover must present the full title with all words of the
+ title equally prominent and visible. You may add other material
+ on the covers in addition. Copying with changes limited to the
+ covers, as long as they preserve the title of the Document and
+ satisfy these conditions, can be treated as verbatim copying in
+ other respects.
+
+ If the required texts for either cover are too voluminous to fit
+ legibly, you should put the first ones listed (as many as fit
+ reasonably) on the actual cover, and continue the rest onto
+ adjacent pages.
+
+ If you publish or distribute Opaque copies of the Document
+ numbering more than 100, you must either include a
+ machine-readable Transparent copy along with each Opaque copy, or
+ state in or with each Opaque copy a computer-network location from
+ which the general network-using public has access to download
+ using public-standard network protocols a complete Transparent
+ copy of the Document, free of added material. If you use the
+ latter option, you must take reasonably prudent steps, when you
+ begin distribution of Opaque copies in quantity, to ensure that
+ this Transparent copy will remain thus accessible at the stated
+ location until at least one year after the last time you
+ distribute an Opaque copy (directly or through your agents or
+ retailers) of that edition to the public.
+
+ It is requested, but not required, that you contact the authors of
+ the Document well before redistributing any large number of
+ copies, to give them a chance to provide you with an updated
+ version of the Document.
+
+ 4. MODIFICATIONS
+
+ You may copy and distribute a Modified Version of the Document
+ under the conditions of sections 2 and 3 above, provided that you
+ release the Modified Version under precisely this License, with
+ the Modified Version filling the role of the Document, thus
+ licensing distribution and modification of the Modified Version to
+ whoever possesses a copy of it. In addition, you must do these
+ things in the Modified Version:
+
+ A. Use in the Title Page (and on the covers, if any) a title
+ distinct from that of the Document, and from those of
+ previous versions (which should, if there were any, be listed
+ in the History section of the Document). You may use the
+ same title as a previous version if the original publisher of
+ that version gives permission.
+
+ B. List on the Title Page, as authors, one or more persons or
+ entities responsible for authorship of the modifications in
+ the Modified Version, together with at least five of the
+ principal authors of the Document (all of its principal
+ authors, if it has fewer than five), unless they release you
+ from this requirement.
+
+ C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+
+ D. Preserve all the copyright notices of the Document.
+
+ E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+
+ F. Include, immediately after the copyright notices, a license
+ notice giving the public permission to use the Modified
+ Version under the terms of this License, in the form shown in
+ the Addendum below.
+
+ G. Preserve in that license notice the full lists of Invariant
+ Sections and required Cover Texts given in the Document's
+ license notice.
+
+ H. Include an unaltered copy of this License.
+
+ I. Preserve the section Entitled "History", Preserve its Title,
+ and add to it an item stating at least the title, year, new
+ authors, and publisher of the Modified Version as given on
+ the Title Page. If there is no section Entitled "History" in
+ the Document, create one stating the title, year, authors,
+ and publisher of the Document as given on its Title Page,
+ then add an item describing the Modified Version as stated in
+ the previous sentence.
+
+ J. Preserve the network location, if any, given in the Document
+ for public access to a Transparent copy of the Document, and
+ likewise the network locations given in the Document for
+ previous versions it was based on. These may be placed in
+ the "History" section. You may omit a network location for a
+ work that was published at least four years before the
+ Document itself, or if the original publisher of the version
+ it refers to gives permission.
+
+ K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the
+ section all the substance and tone of each of the contributor
+ acknowledgements and/or dedications given therein.
+
+ L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section
+ titles.
+
+ M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+
+ N. Do not retitle any existing section to be Entitled
+ "Endorsements" or to conflict in title with any Invariant
+ Section.
+
+ O. Preserve any Warranty Disclaimers.
+
+ If the Modified Version includes new front-matter sections or
+ appendices that qualify as Secondary Sections and contain no
+ material copied from the Document, you may at your option
+ designate some or all of these sections as invariant. To do this,
+ add their titles to the list of Invariant Sections in the Modified
+ Version's license notice. These titles must be distinct from any
+ other section titles.
+
+ You may add a section Entitled "Endorsements", provided it contains
+ nothing but endorsements of your Modified Version by various
+ parties--for example, statements of peer review or that the text
+ has been approved by an organization as the authoritative
+ definition of a standard.
+
+ You may add a passage of up to five words as a Front-Cover Text,
+ and a passage of up to 25 words as a Back-Cover Text, to the end
+ of the list of Cover Texts in the Modified Version. Only one
+ passage of Front-Cover Text and one of Back-Cover Text may be
+ added by (or through arrangements made by) any one entity. If the
+ Document already includes a cover text for the same cover,
+ previously added by you or by arrangement made by the same entity
+ you are acting on behalf of, you may not add another; but you may
+ replace the old one, on explicit permission from the previous
+ publisher that added the old one.
+
+ The author(s) and publisher(s) of the Document do not by this
+ License give permission to use their names for publicity for or to
+ assert or imply endorsement of any Modified Version.
+
+ 5. COMBINING DOCUMENTS
+
+ You may combine the Document with other documents released under
+ this License, under the terms defined in section 4 above for
+ modified versions, provided that you include in the combination
+ all of the Invariant Sections of all of the original documents,
+ unmodified, and list them all as Invariant Sections of your
+ combined work in its license notice, and that you preserve all
+ their Warranty Disclaimers.
+
+ The combined work need only contain one copy of this License, and
+ multiple identical Invariant Sections may be replaced with a single
+ copy. If there are multiple Invariant Sections with the same name
+ but different contents, make the title of each such section unique
+ by adding at the end of it, in parentheses, the name of the
+ original author or publisher of that section if known, or else a
+ unique number. Make the same adjustment to the section titles in
+ the list of Invariant Sections in the license notice of the
+ combined work.
+
+ In the combination, you must combine any sections Entitled
+ "History" in the various original documents, forming one section
+ Entitled "History"; likewise combine any sections Entitled
+ "Acknowledgements", and any sections Entitled "Dedications". You
+ must delete all sections Entitled "Endorsements."
+
+ 6. COLLECTIONS OF DOCUMENTS
+
+ You may make a collection consisting of the Document and other
+ documents released under this License, and replace the individual
+ copies of this License in the various documents with a single copy
+ that is included in the collection, provided that you follow the
+ rules of this License for verbatim copying of each of the
+ documents in all other respects.
+
+ You may extract a single document from such a collection, and
+ distribute it individually under this License, provided you insert
+ a copy of this License into the extracted document, and follow
+ this License in all other respects regarding verbatim copying of
+ that document.
+
+ 7. AGGREGATION WITH INDEPENDENT WORKS
+
+ A compilation of the Document or its derivatives with other
+ separate and independent documents or works, in or on a volume of
+ a storage or distribution medium, is called an "aggregate" if the
+ copyright resulting from the compilation is not used to limit the
+ legal rights of the compilation's users beyond what the individual
+ works permit. When the Document is included in an aggregate, this
+ License does not apply to the other works in the aggregate which
+ are not themselves derivative works of the Document.
+
+ If the Cover Text requirement of section 3 is applicable to these
+ copies of the Document, then if the Document is less than one half
+ of the entire aggregate, the Document's Cover Texts may be placed
+ on covers that bracket the Document within the aggregate, or the
+ electronic equivalent of covers if the Document is in electronic
+ form. Otherwise they must appear on printed covers that bracket
+ the whole aggregate.
+
+ 8. TRANSLATION
+
+ Translation is considered a kind of modification, so you may
+ distribute translations of the Document under the terms of section
+ 4. Replacing Invariant Sections with translations requires special
+ permission from their copyright holders, but you may include
+ translations of some or all Invariant Sections in addition to the
+ original versions of these Invariant Sections. You may include a
+ translation of this License, and all the license notices in the
+ Document, and any Warranty Disclaimers, provided that you also
+ include the original English version of this License and the
+ original versions of those notices and disclaimers. In case of a
+ disagreement between the translation and the original version of
+ this License or a notice or disclaimer, the original version will
+ prevail.
+
+ If a section in the Document is Entitled "Acknowledgements",
+ "Dedications", or "History", the requirement (section 4) to
+ Preserve its Title (section 1) will typically require changing the
+ actual title.
+
+ 9. TERMINATION
+
+ You may not copy, modify, sublicense, or distribute the Document
+ except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense, or distribute it is void,
+ and will automatically terminate your rights under this License.
+
+ However, if you cease all violation of this License, then your
+ license from a particular copyright holder is reinstated (a)
+ provisionally, unless and until the copyright holder explicitly
+ and finally terminates your license, and (b) permanently, if the
+ copyright holder fails to notify you of the violation by some
+ reasonable means prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+ reinstated permanently if the copyright holder notifies you of the
+ violation by some reasonable means, this is the first time you have
+ received notice of violation of this License (for any work) from
+ that copyright holder, and you cure the violation prior to 30 days
+ after your receipt of the notice.
+
+ Termination of your rights under this section does not terminate
+ the licenses of parties who have received copies or rights from
+ you under this License. If your rights have been terminated and
+ not permanently reinstated, receipt of a copy of some or all of
+ the same material does not give you any rights to use it.
+
+ 10. FUTURE REVISIONS OF THIS LICENSE
+
+ The Free Software Foundation may publish new, revised versions of
+ the GNU Free Documentation License from time to time. Such new
+ versions will be similar in spirit to the present version, but may
+ differ in detail to address new problems or concerns. See
+ `http://www.gnu.org/copyleft/'.
+
+ Each version of the License is given a distinguishing version
+ number. If the Document specifies that a particular numbered
+ version of this License "or any later version" applies to it, you
+ have the option of following the terms and conditions either of
+ that specified version or of any later version that has been
+ published (not as a draft) by the Free Software Foundation. If
+ the Document does not specify a version number of this License,
+ you may choose any version ever published (not as a draft) by the
+ Free Software Foundation. If the Document specifies that a proxy
+ can decide which future versions of this License can be used, that
+ proxy's public statement of acceptance of a version permanently
+ authorizes you to choose that version for the Document.
+
+ 11. RELICENSING
+
+ "Massive Multiauthor Collaboration Site" (or "MMC Site") means any
+ World Wide Web server that publishes copyrightable works and also
+ provides prominent facilities for anybody to edit those works. A
+ public wiki that anybody can edit is an example of such a server.
+ A "Massive Multiauthor Collaboration" (or "MMC") contained in the
+ site means any set of copyrightable works thus published on the MMC
+ site.
+
+ "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+ license published by Creative Commons Corporation, a not-for-profit
+ corporation with a principal place of business in San Francisco,
+ California, as well as future copyleft versions of that license
+ published by that same organization.
+
+ "Incorporate" means to publish or republish a Document, in whole or
+ in part, as part of another Document.
+
+ An MMC is "eligible for relicensing" if it is licensed under this
+ License, and if all works that were first published under this
+ License somewhere other than this MMC, and subsequently
+ incorporated in whole or in part into the MMC, (1) had no cover
+ texts or invariant sections, and (2) were thus incorporated prior
+ to November 1, 2008.
+
+ The operator of an MMC Site may republish an MMC contained in the
+ site under CC-BY-SA on the same site at any time before August 1,
+ 2009, provided the MMC is eligible for relicensing.
+
+
+ADDENDUM: How to use this License for your documents
+====================================================
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and license
+notices just after the title page:
+
+ Copyright (C) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. A copy of the license is included in the section entitled ``GNU
+ Free Documentation License''.
+
+ If you have Invariant Sections, Front-Cover Texts and Back-Cover
+Texts, replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with
+ the Front-Cover Texts being LIST, and with the Back-Cover Texts
+ being LIST.
+
+ If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+ If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License, to
+permit their use in free software.
+
+
+File: libmicrohttpd-tutorial.info, Node: Example programs, Prev: License text, Up: Top
+
+Appendix C Example programs
+***************************
+
+* Menu:
+
+* hellobrowser.c::
+* logging.c::
+* responseheaders.c::
+* basicauthentication.c::
+* simplepost.c::
+* largepost.c::
+* sessions.c::
+* tlsauthentication.c::
+
+
+File: libmicrohttpd-tutorial.info, Node: hellobrowser.c, Next: logging.c, Up: Example programs
+
+C.1 hellobrowser.c
+==================
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <string.h>
+ #include <microhttpd.h>
+ #include <stdio.h>
+
+ #define PORT 8888
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ const char *page = "<html><body>Hello, browser!</body></html>";
+ struct MHD_Response *response;
+ int ret;
+
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: logging.c, Next: responseheaders.c, Prev: hellobrowser.c, Up: Example programs
+
+C.2 logging.c
+=============
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <microhttpd.h>
+ #include <stdio.h>
+
+ #define PORT 8888
+
+
+ static int
+ print_out_key (void *cls, enum MHD_ValueKind kind, const char *key,
+ const char *value)
+ {
+ printf ("%s: %s\n", key, value);
+ return MHD_YES;
+ }
+
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ printf ("New %s request for %s using version %s\n", method, url, version);
+
+ MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key,
+ NULL);
+
+ return MHD_NO;
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: responseheaders.c, Next: basicauthentication.c, Prev: logging.c, Up: Example programs
+
+C.3 responseheaders.c
+=====================
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <microhttpd.h>
+ #include <time.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <string.h>
+ #include <stdio.h>
+
+ #define PORT 8888
+ #define FILENAME "picture.png"
+ #define MIMETYPE "image/png"
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ struct MHD_Response *response;
+ int fd;
+ int ret;
+ struct stat sbuf;
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+
+ if ( (-1 == (fd = open (FILENAME, O_RDONLY))) ||
+ (0 != fstat (fd, &sbuf)) )
+ {
+ /* error accessing file */
+ if (fd != -1)
+ (void) close (fd);
+ const char *errorstr =
+ "<html><body>An internal server error has occured!\
+ </body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (errorstr),
+ (void *) errorstr,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL != response)
+ {
+ ret =
+ MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+ else
+ return MHD_NO;
+ }
+ response =
+ MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0);
+ MHD_add_response_header (response, "Content-Type", MIMETYPE);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: basicauthentication.c, Next: simplepost.c, Prev: responseheaders.c, Up: Example programs
+
+C.4 basicauthentication.c
+=========================
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <microhttpd.h>
+ #include <time.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ #define PORT 8888
+
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ char *user;
+ char *pass;
+ int fail;
+ int ret;
+ struct MHD_Response *response;
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+ if (NULL == *con_cls)
+ {
+ *con_cls = connection;
+ return MHD_YES;
+ }
+ pass = NULL;
+ user = MHD_basic_auth_get_username_password (connection, &pass);
+ fail = ( (user == NULL) ||
+ (0 != strcmp (user, "root")) ||
+ (0 != strcmp (pass, "pa$$w0rd") ) );
+ if (user != NULL) free (user);
+ if (pass != NULL) free (pass);
+ if (fail)
+ {
+ const char *page = "<html><body>Go away.</body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_basic_auth_fail_response (connection,
+ "my realm",
+ response);
+ }
+ else
+ {
+ const char *page = "<html><body>A secret.</body></html>";
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ }
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: simplepost.c, Next: largepost.c, Prev: basicauthentication.c, Up: Example programs
+
+C.5 simplepost.c
+================
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <microhttpd.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ #define PORT 8888
+ #define POSTBUFFERSIZE 512
+ #define MAXNAMESIZE 20
+ #define MAXANSWERSIZE 512
+
+ #define GET 0
+ #define POST 1
+
+ struct connection_info_struct
+ {
+ int connectiontype;
+ char *answerstring;
+ struct MHD_PostProcessor *postprocessor;
+ };
+
+ const char *askpage = "<html><body>\
+ What's your name, Sir?<br>\
+ <form action=\"/namepost\" method=\"post\">\
+ <input name=\"name\" type=\"text\"\
+ <input type=\"submit\" value=\" Send \"></form>\
+ </body></html>";
+
+ const char *greetingpage =
+ "<html><body><h1>Welcome, %s!</center></h1></body></html>";
+
+ const char *errorpage =
+ "<html><body>This doesn't seem to be right.</body></html>";
+
+
+ static int
+ send_page (struct MHD_Connection *connection, const char *page)
+ {
+ int ret;
+ struct MHD_Response *response;
+
+
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ if (!response)
+ return MHD_NO;
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+
+ static int
+ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data, uint64_t off,
+ size_t size)
+ {
+ struct connection_info_struct *con_info = coninfo_cls;
+
+ if (0 == strcmp (key, "name"))
+ {
+ if ((size > 0) && (size <= MAXNAMESIZE))
+ {
+ char *answerstring;
+ answerstring = malloc (MAXANSWERSIZE);
+ if (!answerstring)
+ return MHD_NO;
+
+ snprintf (answerstring, MAXANSWERSIZE, greetingpage, data);
+ con_info->answerstring = answerstring;
+ }
+ else
+ con_info->answerstring = NULL;
+
+ return MHD_NO;
+ }
+
+ return MHD_YES;
+ }
+
+ static void
+ request_completed (void *cls, struct MHD_Connection *connection,
+ void **con_cls, enum MHD_RequestTerminationCode toe)
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (NULL == con_info)
+ return;
+
+ if (con_info->connectiontype == POST)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ if (con_info->answerstring)
+ free (con_info->answerstring);
+ }
+
+ free (con_info);
+ *con_cls = NULL;
+ }
+
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ if (NULL == *con_cls)
+ {
+ struct connection_info_struct *con_info;
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info)
+ return MHD_NO;
+ con_info->answerstring = NULL;
+
+ if (0 == strcmp (method, "POST"))
+ {
+ con_info->postprocessor =
+ MHD_create_post_processor (connection, POSTBUFFERSIZE,
+ iterate_post, (void *) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+
+ con_info->connectiontype = POST;
+ }
+ else
+ con_info->connectiontype = GET;
+
+ *con_cls = (void *) con_info;
+
+ return MHD_YES;
+ }
+
+ if (0 == strcmp (method, "GET"))
+ {
+ return send_page (connection, askpage);
+ }
+
+ if (0 == strcmp (method, "POST"))
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (*upload_data_size != 0)
+ {
+ MHD_post_process (con_info->postprocessor, upload_data,
+ *upload_data_size);
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ else if (NULL != con_info->answerstring)
+ return send_page (connection, con_info->answerstring);
+ }
+
+ return send_page (connection, errorpage);
+ }
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, request_completed,
+ NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: largepost.c, Next: sessions.c, Prev: simplepost.c, Up: Example programs
+
+C.6 largepost.c
+===============
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <microhttpd.h>
+
+ #define PORT 8888
+ #define POSTBUFFERSIZE 512
+ #define MAXCLIENTS 2
+
+ #define GET 0
+ #define POST 1
+
+ static unsigned int nr_of_uploading_clients = 0;
+
+ struct connection_info_struct
+ {
+ int connectiontype;
+ struct MHD_PostProcessor *postprocessor;
+ FILE *fp;
+ const char *answerstring;
+ int answercode;
+ };
+
+ const char *askpage = "<html><body>\n\
+ Upload a file, please!<br>\n\
+ There are %u clients uploading at the moment.<br>\n\
+ <form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n\
+ <input name=\"file\" type=\"file\">\n\
+ <input type=\"submit\" value=\" Send \"></form>\n\
+ </body></html>";
+
+ const char *busypage =
+ "<html><body>This server is busy, please try again later.</body></html>";
+
+ const char *completepage =
+ "<html><body>The upload has been completed.</body></html>";
+
+ const char *errorpage =
+ "<html><body>This doesn't seem to be right.</body></html>";
+ const char *servererrorpage =
+ "<html><body>An internal server error has occured.</body></html>";
+ const char *fileexistspage =
+ "<html><body>This file already exists.</body></html>";
+
+
+ static int
+ send_page (struct MHD_Connection *connection, const char *page,
+ int status_code)
+ {
+ int ret;
+ struct MHD_Response *response;
+
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_MUST_COPY);
+ if (!response)
+ return MHD_NO;
+ MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
+ ret = MHD_queue_response (connection, status_code, response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+
+ static int
+ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data, uint64_t off,
+ size_t size)
+ {
+ struct connection_info_struct *con_info = coninfo_cls;
+ FILE *fp;
+
+ con_info->answerstring = servererrorpage;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+
+ if (0 != strcmp (key, "file"))
+ return MHD_NO;
+
+ if (!con_info->fp)
+ {
+ if (NULL != (fp = fopen (filename, "rb")))
+ {
+ fclose (fp);
+ con_info->answerstring = fileexistspage;
+ con_info->answercode = MHD_HTTP_FORBIDDEN;
+ return MHD_NO;
+ }
+
+ con_info->fp = fopen (filename, "ab");
+ if (!con_info->fp)
+ return MHD_NO;
+ }
+
+ if (size > 0)
+ {
+ if (!fwrite (data, size, sizeof (char), con_info->fp))
+ return MHD_NO;
+ }
+
+ con_info->answerstring = completepage;
+ con_info->answercode = MHD_HTTP_OK;
+
+ return MHD_YES;
+ }
+
+
+ static void
+ request_completed (void *cls, struct MHD_Connection *connection,
+ void **con_cls, enum MHD_RequestTerminationCode toe)
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (NULL == con_info)
+ return;
+
+ if (con_info->connectiontype == POST)
+ {
+ if (NULL != con_info->postprocessor)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ nr_of_uploading_clients--;
+ }
+
+ if (con_info->fp)
+ fclose (con_info->fp);
+ }
+
+ free (con_info);
+ *con_cls = NULL;
+ }
+
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ if (NULL == *con_cls)
+ {
+ struct connection_info_struct *con_info;
+
+ if (nr_of_uploading_clients >= MAXCLIENTS)
+ return send_page (connection, busypage, MHD_HTTP_SERVICE_UNAVAILABLE);
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info)
+ return MHD_NO;
+
+ con_info->fp = NULL;
+
+ if (0 == strcmp (method, "POST"))
+ {
+ con_info->postprocessor =
+ MHD_create_post_processor (connection, POSTBUFFERSIZE,
+ iterate_post, (void *) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+
+ nr_of_uploading_clients++;
+
+ con_info->connectiontype = POST;
+ con_info->answercode = MHD_HTTP_OK;
+ con_info->answerstring = completepage;
+ }
+ else
+ con_info->connectiontype = GET;
+
+ *con_cls = (void *) con_info;
+
+ return MHD_YES;
+ }
+
+ if (0 == strcmp (method, "GET"))
+ {
+ char buffer[1024];
+
+ snprintf (buffer, sizeof (buffer), askpage, nr_of_uploading_clients);
+ return send_page (connection, buffer, MHD_HTTP_OK);
+ }
+
+ if (0 == strcmp (method, "POST"))
+ {
+ struct connection_info_struct *con_info = *con_cls;
+
+ if (0 != *upload_data_size)
+ {
+ MHD_post_process (con_info->postprocessor, upload_data,
+ *upload_data_size);
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ else
+ {
+ if (NULL != con_info->fp)
+ {
+ fclose (con_info->fp);
+ con_info->fp = NULL;
+ }
+ /* Now it is safe to open and inspect the file before calling send_page with a response */
+ return send_page (connection, con_info->answerstring,
+ con_info->answercode);
+ }
+
+ }
+
+ return send_page (connection, errorpage, MHD_HTTP_BAD_REQUEST);
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, request_completed,
+ NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+ (void) getchar ();
+ MHD_stop_daemon (daemon);
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: sessions.c, Next: tlsauthentication.c, Prev: largepost.c, Up: Example programs
+
+C.7 sessions.c
+==============
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ /* needed for asprintf */
+ #define _GNU_SOURCE
+
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <time.h>
+ #include <microhttpd.h>
+
+ #if defined _WIN32 && !defined(__MINGW64_VERSION_MAJOR)
+ static int
+ asprintf (char **resultp, const char *format, ...)
+ {
+ va_list argptr;
+ char *result = NULL;
+ int len = 0;
+
+ if (format == NULL)
+ return -1;
+
+ va_start (argptr, format);
+
+ len = _vscprintf ((char *) format, argptr);
+ if (len >= 0)
+ {
+ len += 1;
+ result = (char *) malloc (sizeof (char *) * len);
+ if (result != NULL)
+ {
+ int len2 = _vscprintf ((char *) format, argptr);
+ if (len2 != len - 1 || len2 <= 0)
+ {
+ free (result);
+ result = NULL;
+ len = -1;
+ }
+ else
+ {
+ len = len2;
+ if (resultp)
+ *resultp = result;
+ }
+ }
+ }
+ va_end (argptr);
+ return len;
+ }
+ #endif
+
+ /**
+ * Invalid method page.
+ */
+ #define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
+
+ /**
+ * Invalid URL page.
+ */
+ #define NOT_FOUND_ERROR "<html><head><title>Not found</title></head><body>Go away.</body></html>"
+
+ /**
+ * Front page. (/)
+ */
+ #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>"
+
+ /**
+ * Second page. (/2)
+ */
+ #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>"
+
+ /**
+ * Second page (/S)
+ */
+ #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>"
+
+ /**
+ * Last page.
+ */
+ #define LAST_PAGE "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
+
+ /**
+ * Name of our cookie.
+ */
+ #define COOKIE_NAME "session"
+
+
+ /**
+ * State we keep for each user/session/browser.
+ */
+ struct Session
+ {
+ /**
+ * We keep all sessions in a linked list.
+ */
+ struct Session *next;
+
+ /**
+ * Unique ID for this session.
+ */
+ char sid[33];
+
+ /**
+ * Reference counter giving the number of connections
+ * currently using this session.
+ */
+ unsigned int rc;
+
+ /**
+ * Time when this session was last active.
+ */
+ time_t start;
+
+ /**
+ * String submitted via form.
+ */
+ char value_1[64];
+
+ /**
+ * Another value submitted via form.
+ */
+ char value_2[64];
+
+ };
+
+
+ /**
+ * Data kept per request.
+ */
+ struct Request
+ {
+
+ /**
+ * Associated session.
+ */
+ struct Session *session;
+
+ /**
+ * Post processor handling form data (IF this is
+ * a POST request).
+ */
+ struct MHD_PostProcessor *pp;
+
+ /**
+ * URL to serve in response to this POST (if this request
+ * was a 'POST')
+ */
+ const char *post_url;
+
+ };
+
+
+ /**
+ * Linked list of all active sessions. Yes, O(n) but a
+ * hash table would be overkill for a simple example...
+ */
+ static struct Session *sessions;
+
+
+
+
+ /**
+ * Return the session handle for this connection, or
+ * create one if this is a new user.
+ */
+ static struct Session *
+ get_session (struct MHD_Connection *connection)
+ {
+ struct Session *ret;
+ const char *cookie;
+
+ cookie = MHD_lookup_connection_value (connection,
+ MHD_COOKIE_KIND,
+ COOKIE_NAME);
+ if (cookie != NULL)
+ {
+ /* find existing session */
+ ret = sessions;
+ while (NULL != ret)
+ {
+ if (0 == strcmp (cookie, ret->sid))
+ break;
+ ret = ret->next;
+ }
+ if (NULL != ret)
+ {
+ ret->rc++;
+ return ret;
+ }
+ }
+ /* create fresh session */
+ ret = calloc (1, sizeof (struct Session));
+ if (NULL == ret)
+ {
+ fprintf (stderr, "calloc error: %s\n", strerror (errno));
+ return NULL;
+ }
+ /* not a super-secure way to generate a random session ID,
+ but should do for a simple example... */
+ snprintf (ret->sid,
+ sizeof (ret->sid),
+ "%X%X%X%X",
+ (unsigned int) rand (),
+ (unsigned int) rand (),
+ (unsigned int) rand (),
+ (unsigned int) rand ());
+ ret->rc++;
+ ret->start = time (NULL);
+ ret->next = sessions;
+ sessions = ret;
+ return ret;
+ }
+
+
+ /**
+ * Type of handler that generates a reply.
+ *
+ * @param cls content for the page (handler-specific)
+ * @param mime mime type to use
+ * @param session session information
+ * @param connection connection to process
+ * @param MHD_YES on success, MHD_NO on failure
+ */
+ typedef int (*PageHandler)(const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection);
+
+
+ /**
+ * Entry we generate for each page served.
+ */
+ struct Page
+ {
+ /**
+ * Acceptable URL for this page.
+ */
+ const char *url;
+
+ /**
+ * Mime type to set for the page.
+ */
+ const char *mime;
+
+ /**
+ * Handler to call to generate response.
+ */
+ PageHandler handler;
+
+ /**
+ * Extra argument to handler.
+ */
+ const void *handler_cls;
+ };
+
+
+ /**
+ * Add header to response to set a session cookie.
+ *
+ * @param session session to use
+ * @param response response to modify
+ */
+ static void
+ add_session_cookie (struct Session *session,
+ struct MHD_Response *response)
+ {
+ char cstr[256];
+ snprintf (cstr,
+ sizeof (cstr),
+ "%s=%s",
+ COOKIE_NAME,
+ session->sid);
+ if (MHD_NO ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SET_COOKIE,
+ cstr))
+ {
+ fprintf (stderr,
+ "Failed to set session cookie header!\n");
+ }
+ }
+
+
+ /**
+ * Handler that returns a simple static HTTP page that
+ * is passed in via 'cls'.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+ static int
+ serve_simple_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+ {
+ int ret;
+ const char *form = cls;
+ struct MHD_Response *response;
+
+ /* return static form */
+ response = MHD_create_response_from_buffer (strlen (form),
+ (void *) form,
+ MHD_RESPMEM_PERSISTENT);
+ add_session_cookie (session, response);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_ENCODING,
+ mime);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ /**
+ * Handler that adds the 'v1' value to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+ static int
+ fill_v1_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+ {
+ int ret;
+ const char *form = cls;
+ char *reply;
+ struct MHD_Response *response;
+
+ if (-1 == asprintf (&reply,
+ form,
+ session->value_1))
+ {
+ /* oops */
+ return MHD_NO;
+ }
+ /* return static form */
+ response = MHD_create_response_from_buffer (strlen (reply),
+ (void *) reply,
+ MHD_RESPMEM_MUST_FREE);
+ add_session_cookie (session, response);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_ENCODING,
+ mime);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ /**
+ * Handler that adds the 'v1' and 'v2' values to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+ static int
+ fill_v1_v2_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+ {
+ int ret;
+ const char *form = cls;
+ char *reply;
+ struct MHD_Response *response;
+
+ if (-1 == asprintf (&reply,
+ form,
+ session->value_1,
+ session->value_2))
+ {
+ /* oops */
+ return MHD_NO;
+ }
+ /* return static form */
+ response = MHD_create_response_from_buffer (strlen (reply),
+ (void *) reply,
+ MHD_RESPMEM_MUST_FREE);
+ add_session_cookie (session, response);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_ENCODING,
+ mime);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ /**
+ * Handler used to generate a 404 reply.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+ static int
+ not_found_page (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+ {
+ int ret;
+ struct MHD_Response *response;
+
+ /* unsupported HTTP method */
+ response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR),
+ (void *) NOT_FOUND_ERROR,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_ENCODING,
+ mime);
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ /**
+ * List of all pages served by this HTTP server.
+ */
+ static struct Page pages[] =
+ {
+ { "/", "text/html", &fill_v1_form, MAIN_PAGE },
+ { "/2", "text/html", &fill_v1_v2_form, SECOND_PAGE },
+ { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
+ { "/F", "text/html", &serve_simple_form, LAST_PAGE },
+ { NULL, NULL, &not_found_page, NULL } /* 404 */
+ };
+
+
+
+ /**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ */
+ static int
+ post_iterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data, uint64_t off, size_t size)
+ {
+ struct Request *request = cls;
+ struct Session *session = request->session;
+
+ if (0 == strcmp ("DONE", key))
+ {
+ fprintf (stdout,
+ "Session `%s' submitted `%s', `%s'\n",
+ session->sid,
+ session->value_1,
+ session->value_2);
+ return MHD_YES;
+ }
+ if (0 == strcmp ("v1", key))
+ {
+ if (size + off > sizeof(session->value_1))
+ size = sizeof (session->value_1) - off;
+ memcpy (&session->value_1[off],
+ data,
+ size);
+ if (size + off < sizeof (session->value_1))
+ session->value_1[size+off] = '\0';
+ return MHD_YES;
+ }
+ if (0 == strcmp ("v2", key))
+ {
+ if (size + off > sizeof(session->value_2))
+ size = sizeof (session->value_2) - off;
+ memcpy (&session->value_2[off],
+ data,
+ size);
+ if (size + off < sizeof (session->value_2))
+ session->value_2[size+off] = '\0';
+ return MHD_YES;
+ }
+ fprintf (stderr, "Unsupported form value `%s'\n", key);
+ return MHD_YES;
+ }
+
+
+ /**
+ * Main MHD callback for handling requests.
+ *
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param connection handle to connection which is being processed
+ * @param url the requested url
+ * @param method the HTTP method used ("GET", "PUT", etc.)
+ * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * upload_data)
+ * @param upload_data_size set initially to the size of the
+ * upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param ptr pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global "MHD_RequestCompleted" callback (which
+ * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, <tt>*con_cls</tt> will be NULL.
+ * @return MHS_YES if the connection was handled successfully,
+ * MHS_NO if the socket must be closed due to a serios
+ * error while handling the request
+ */
+ static int
+ create_response (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+ {
+ struct MHD_Response *response;
+ struct Request *request;
+ struct Session *session;
+ int ret;
+ unsigned int i;
+
+ request = *ptr;
+ if (NULL == request)
+ {
+ request = calloc (1, sizeof (struct Request));
+ if (NULL == request)
+ {
+ fprintf (stderr, "calloc error: %s\n", strerror (errno));
+ return MHD_NO;
+ }
+ *ptr = request;
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ request->pp = MHD_create_post_processor (connection, 1024,
+ &post_iterator, request);
+ if (NULL == request->pp)
+ {
+ fprintf (stderr, "Failed to setup post processor for `%s'\n",
+ url);
+ return MHD_NO; /* internal error */
+ }
+ }
+ return MHD_YES;
+ }
+ if (NULL == request->session)
+ {
+ request->session = get_session (connection);
+ if (NULL == request->session)
+ {
+ fprintf (stderr, "Failed to setup session for `%s'\n",
+ url);
+ return MHD_NO; /* internal error */
+ }
+ }
+ session = request->session;
+ session->start = time (NULL);
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ /* evaluate POST data */
+ MHD_post_process (request->pp,
+ upload_data,
+ *upload_data_size);
+ if (0 != *upload_data_size)
+ {
+ *upload_data_size = 0;
+ return MHD_YES;
+ }
+ /* done with POST data, serve response */
+ MHD_destroy_post_processor (request->pp);
+ request->pp = NULL;
+ method = MHD_HTTP_METHOD_GET; /* fake 'GET' */
+ if (NULL != request->post_url)
+ url = request->post_url;
+ }
+
+ if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
+ (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
+ {
+ /* find out which page to serve */
+ i=0;
+ while ( (pages[i].url != NULL) &&
+ (0 != strcmp (pages[i].url, url)) )
+ i++;
+ ret = pages[i].handler (pages[i].handler_cls,
+ pages[i].mime,
+ session, connection);
+ if (ret != MHD_YES)
+ fprintf (stderr, "Failed to create page for `%s'\n",
+ url);
+ return ret;
+ }
+ /* unsupported HTTP method */
+ response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
+ (void *) METHOD_ERROR,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+ }
+
+
+ /**
+ * Callback called upon completion of a request.
+ * Decrements session reference counter.
+ *
+ * @param cls not used
+ * @param connection connection that completed
+ * @param con_cls session handle
+ * @param toe status code
+ */
+ static void
+ request_completed_callback (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+ {
+ struct Request *request = *con_cls;
+
+ if (NULL == request)
+ return;
+ if (NULL != request->session)
+ request->session->rc--;
+ if (NULL != request->pp)
+ MHD_destroy_post_processor (request->pp);
+ free (request);
+ }
+
+
+ /**
+ * Clean up handles of sessions that have been idle for
+ * too long.
+ */
+ static void
+ expire_sessions ()
+ {
+ struct Session *pos;
+ struct Session *prev;
+ struct Session *next;
+ time_t now;
+
+ now = time (NULL);
+ prev = NULL;
+ pos = sessions;
+ while (NULL != pos)
+ {
+ next = pos->next;
+ if (now - pos->start > 60 * 60)
+ {
+ /* expire sessions after 1h */
+ if (NULL == prev)
+ sessions = pos->next;
+ else
+ prev->next = next;
+ free (pos);
+ }
+ else
+ prev = pos;
+ pos = next;
+ }
+ }
+
+
+ /**
+ * Call with the port number as the only argument.
+ * Never terminates (other than by signals, such as CTRL-C).
+ */
+ int
+ main (int argc, char *const *argv)
+ {
+ struct MHD_Daemon *d;
+ struct timeval tv;
+ struct timeval *tvp;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max;
+ MHD_UNSIGNED_LONG_LONG mhd_timeout;
+
+ if (argc != 2)
+ {
+ printf ("%s PORT\n", argv[0]);
+ return 1;
+ }
+ /* initialize PRNG */
+ srand ((unsigned int) time (NULL));
+ d = MHD_start_daemon (MHD_USE_DEBUG,
+ atoi (argv[1]),
+ NULL, NULL,
+ &create_response, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
+ MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
+ MHD_OPTION_END);
+ if (NULL == d)
+ return 1;
+ while (1)
+ {
+ expire_sessions ();
+ max = 0;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+ break; /* fatal internal error */
+ if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES)
+ {
+ tv.tv_sec = mhd_timeout / 1000;
+ tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
+ tvp = &tv;
+ }
+ else
+ tvp = NULL;
+ if (-1 == select (max + 1, &rs, &ws, &es, tvp))
+ {
+ if (EINTR != errno)
+ fprintf (stderr,
+ "Aborting due to error during select: %s\n",
+ strerror (errno));
+ break;
+ }
+ MHD_run (d);
+ }
+ MHD_stop_daemon (d);
+ return 0;
+ }
+
+
+File: libmicrohttpd-tutorial.info, Node: tlsauthentication.c, Prev: sessions.c, Up: Example programs
+
+C.8 tlsauthentication.c
+=======================
+
+ /* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+ #include <sys/types.h>
+ #ifndef _WIN32
+ #include <sys/select.h>
+ #include <sys/socket.h>
+ #else
+ #include <winsock2.h>
+ #endif
+ #include <microhttpd.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ #define PORT 8888
+
+ #define REALM "\"Maintenance\""
+ #define USER "a legitimate user"
+ #define PASSWORD "and his password"
+
+ #define SERVERKEYFILE "server.key"
+ #define SERVERCERTFILE "server.pem"
+
+
+ static char *
+ string_to_base64 (const char *message)
+ {
+ const char *lookup =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned long l;
+ int i;
+ char *tmp;
+ size_t length = strlen (message);
+
+ tmp = malloc (length * 2);
+ if (NULL == tmp)
+ return tmp;
+
+ tmp[0] = 0;
+
+ for (i = 0; i < length; i += 3)
+ {
+ l = (((unsigned long) message[i]) << 16)
+ | (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0)
+ | (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0);
+
+
+ strncat (tmp, &lookup[(l >> 18) & 0x3F], 1);
+ strncat (tmp, &lookup[(l >> 12) & 0x3F], 1);
+
+ if (i + 1 < length)
+ strncat (tmp, &lookup[(l >> 6) & 0x3F], 1);
+ if (i + 2 < length)
+ strncat (tmp, &lookup[l & 0x3F], 1);
+ }
+
+ if (length % 3)
+ strncat (tmp, "===", 3 - length % 3);
+
+ return tmp;
+ }
+
+
+ static long
+ get_file_size (const char *filename)
+ {
+ FILE *fp;
+
+ fp = fopen (filename, "rb");
+ if (fp)
+ {
+ long size;
+
+ if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
+ size = 0;
+
+ fclose (fp);
+
+ return size;
+ }
+ else
+ return 0;
+ }
+
+ static char *
+ load_file (const char *filename)
+ {
+ FILE *fp;
+ char *buffer;
+ long size;
+
+ size = get_file_size (filename);
+ if (size == 0)
+ return NULL;
+
+ fp = fopen (filename, "rb");
+ if (!fp)
+ return NULL;
+
+ buffer = malloc (size);
+ if (!buffer)
+ {
+ fclose (fp);
+ return NULL;
+ }
+
+ if (size != fread (buffer, 1, size, fp))
+ {
+ free (buffer);
+ buffer = NULL;
+ }
+
+ fclose (fp);
+ return buffer;
+ }
+
+ static int
+ ask_for_authentication (struct MHD_Connection *connection, const char *realm)
+ {
+ int ret;
+ struct MHD_Response *response;
+ char *headervalue;
+ const char *strbase = "Basic realm=";
+
+ response = MHD_create_response_from_buffer (0, NULL,
+ MHD_RESPMEM_PERSISTENT);
+ if (!response)
+ return MHD_NO;
+
+ headervalue = malloc (strlen (strbase) + strlen (realm) + 1);
+ if (!headervalue)
+ return MHD_NO;
+
+ strcpy (headervalue, strbase);
+ strcat (headervalue, realm);
+
+ ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue);
+ free (headervalue);
+ if (!ret)
+ {
+ MHD_destroy_response (response);
+ return MHD_NO;
+ }
+
+ ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
+
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+ static int
+ is_authenticated (struct MHD_Connection *connection,
+ const char *username, const char *password)
+ {
+ const char *headervalue;
+ char *expected_b64, *expected;
+ const char *strbase = "Basic ";
+ int authenticated;
+
+ headervalue =
+ MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+ "Authorization");
+ if (NULL == headervalue)
+ return 0;
+ if (0 != strncmp (headervalue, strbase, strlen (strbase)))
+ return 0;
+
+ expected = malloc (strlen (username) + 1 + strlen (password) + 1);
+ if (NULL == expected)
+ return 0;
+
+ strcpy (expected, username);
+ strcat (expected, ":");
+ strcat (expected, password);
+
+ expected_b64 = string_to_base64 (expected);
+ free (expected);
+ if (NULL == expected_b64)
+ return 0;
+
+ authenticated =
+ (strcmp (headervalue + strlen (strbase), expected_b64) == 0);
+
+ free (expected_b64);
+
+ return authenticated;
+ }
+
+
+ static int
+ secret_page (struct MHD_Connection *connection)
+ {
+ int ret;
+ struct MHD_Response *response;
+ const char *page = "<html><body>A secret.</body></html>";
+
+ response =
+ MHD_create_response_from_buffer (strlen (page), (void *) page,
+ MHD_RESPMEM_PERSISTENT);
+ if (!response)
+ return MHD_NO;
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+
+
+ static int
+ answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **con_cls)
+ {
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+ if (NULL == *con_cls)
+ {
+ *con_cls = connection;
+ return MHD_YES;
+ }
+
+ if (!is_authenticated (connection, USER, PASSWORD))
+ return ask_for_authentication (connection, REALM);
+
+ return secret_page (connection);
+ }
+
+
+ int
+ main ()
+ {
+ struct MHD_Daemon *daemon;
+ char *key_pem;
+ char *cert_pem;
+
+ key_pem = load_file (SERVERKEYFILE);
+ cert_pem = load_file (SERVERCERTFILE);
+
+ if ((key_pem == NULL) || (cert_pem == NULL))
+ {
+ printf ("The key/certificate files could not be read.\n");
+ return 1;
+ }
+
+ daemon =
+ MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL,
+ NULL, &answer_to_connection, NULL,
+ MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
+ if (NULL == daemon)
+ {
+ printf ("%s\n", cert_pem);
+
+ free (key_pem);
+ free (cert_pem);
+
+ return 1;
+ }
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ free (key_pem);
+ free (cert_pem);
+
+ return 0;
+ }
+
+
+
+Tag Table:
+Node: Top866
+Node: Introduction1917
+Node: Hello browser example3223
+Node: Exploring requests14247
+Node: Response headers19643
+Node: Supporting basic authentication27522
+Node: Processing POST data34913
+Node: Improved processing of POST data43534
+Node: Session management54177
+Node: Adding a layer of security57072
+Node: Bibliography71602
+Node: License text72797
+Node: Example programs97972
+Node: hellobrowser.c98285
+Node: logging.c99828
+Node: responseheaders.c101411
+Node: basicauthentication.c104035
+Node: simplepost.c106574
+Node: largepost.c112254
+Node: sessions.c119619
+Node: tlsauthentication.c141964
+
+End Tag Table