| 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, ¬_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 |