libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 761267757c03f8c05c21d377a7fd24a70d7a0363
parent 8157744d45ee018ab325ff5a16be1212cd7e8f58
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 26 Jul 2010 16:57:47 +0000

ready for 0.9.0

Diffstat:
MAUTHORS | 5+++--
MChangeLog | 6+++++-
MREADME | 4----
Mdoc/chapters/basicauthentication.inc | 44+++++++++++++++++++++++---------------------
Mdoc/chapters/exploringrequests.inc | 52+++++++++++++++++++++++++++++-----------------------
Mdoc/chapters/hellobrowser.inc | 33++++++++++++++++++---------------
Mdoc/chapters/introduction.inc | 7+++++++
Mdoc/chapters/largerpost.inc | 72++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mdoc/chapters/processingpost.inc | 57++++++++++++++++++++++++++++++++-------------------------
Mdoc/chapters/responseheaders.inc | 104++++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mdoc/chapters/tlsauthentication.inc | 6+++---
Mdoc/examples/basicauthentication.c | 12+++++++-----
Mdoc/examples/hellobrowser.c | 6++++--
Mdoc/examples/largepost.c | 25++++++++++++-------------
Mdoc/examples/logging.c | 12+++++++-----
Mdoc/examples/responseheaders.c | 72+++++++++++++++---------------------------------------------------------
Mdoc/examples/simplepost.c | 21+++++++++------------
Mdoc/examples/tlsauthentication.c | 16+++++++++-------
Mdoc/libmicrohttpd.3 | 12++++++------
Mdoc/microhttpd.texi | 86++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mdoc/tutorial.texi | 35+++++++++++++++++++++++------------
Msrc/daemon/EXPORT.sym | 1+
Msrc/daemon/connection.c | 7+++++--
Msrc/daemon/connection_https.c | 2+-
Msrc/daemon/connection_https.h | 2+-
Msrc/daemon/daemon.c | 30++++++++++++++++++++----------
Msrc/daemon/internal.h | 34+++++++++++++++++-----------------
Msrc/daemon/response.c | 22+++++++++++++---------
Msrc/include/microhttpd.h | 2+-
Msrc/testcurl/Makefile.am | 14++++++++++++++
Asrc/testcurl/daemontest_get_sendfile.c | 463+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/https/mhds_get_test.c | 36++++++++++++++++++------------------
Msrc/testcurl/https/tls_daemon_options_test.c | 44++++++++++++++++++--------------------------
Msymbian/MHD_config.h | 2+-
34 files changed, 932 insertions(+), 414 deletions(-)

diff --git a/AUTHORS b/AUTHORS @@ -1,5 +1,6 @@ Primary developers: -Christian Grothoff <christian@grothoff.org> +Christian Grothoff <christian@grothoff.org> (maintainer) +Nils Durner <durner@gnunet.org> (W32 port) Sagie Amir (TLS/SSL support using GNUtls) Richard Alimi <rich@velvetsea.net> (performance) @@ -8,7 +9,6 @@ Chris GauthierDickey <chrisg@cs.du.edu> Elliot Glaysher Daniel Pittman <depittman@gmail.com> John Popplewell <john@johnnypops.demon.co.uk> -Nils Durner <durner@gnunet.org> Heikki Lindholm <holin@iki.fi> Alex Sadovsky <alexeysad@gmail.com> Greg Schohn <greg.schohn@gmail.com> @@ -27,3 +27,4 @@ Geoffrey McRae <geoff@spacevs.com> Documentation contributions also came from: Marco Maggi <marco.maggi-ipsu@poste.it> Sebastian Gerhardt <sebgerhardt@gmx.net> + diff --git a/ChangeLog b/ChangeLog @@ -1,7 +1,11 @@ +Mon Jul 26 10:46:57 CEST 2010 + Releasing libmicrohttpd 0.9.0. -CG + Sun Jul 25 14:57:47 CEST 2010 Adding support for sendfile on Linux. Adding support for systemd-style passing of an existing listen socket - as an option. -CG + as an option. IPv6 sockets now only bind to IPv6 + (if platform supports this). -CG Sun Jul 25 11:10:45 CEST 2010 Changed code to use external libgnutls code instead of diff --git a/README b/README @@ -95,7 +95,6 @@ Functions not covered by "make check": - MHD_del_response_header - MHD_get_response_headers - MHD_tls_connection_close -- MHD_create_response_from_fd & sendfile support (!) Missing documentation: @@ -104,7 +103,4 @@ Missing documentation: - manual: * document configuration options * document details on porting MHD (plibc, z/OS) -- tutorial: - * clean up English - * make sure everything is accurate diff --git a/doc/chapters/basicauthentication.inc b/doc/chapters/basicauthentication.inc @@ -12,9 +12,10 @@ GET /picture.png?mypassword @end verbatim @noindent -In a 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), -this can be a reasonable choice. +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 @@ -37,15 +38,16 @@ 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 -@code{connection} structure will be pointing to a legal adress, so we take this. +@code{connection} structure will be pointing to a legal address, so we take this. -Not even the headers will be looked at on the first iteration. +The first time @code{answer_to_connection} is called, we will not even look at the headers. @verbatim -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) +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;} @@ -57,9 +59,9 @@ int answer_to_connection (void *cls, struct MHD_Connection *connection, @end verbatim @noindent -Note how we lop off the connection on the first condition, but return asking for more on +Note how we lop off the connection on the first condition (no "GET" request), but return asking for more on the other one with @code{MHD_YES}. -With the framework improved, we can proceed to implement the actual authentication process. +With this minor change, we can proceed to implement the actual authentication process. @heading Request for authentication @@ -70,7 +72,8 @@ so every "GET" request will be challenged. We let an extra function function do this. @verbatim -int ask_for_authentication (struct MHD_Connection *connection, const char *realm) +static int +ask_for_authentication (struct MHD_Connection *connection, const char *realm) { int ret; struct MHD_Response *response; @@ -91,9 +94,7 @@ int ask_for_authentication (struct MHD_Connection *connection, const char *realm if (!ret) {MHD_destroy_response (response); return MHD_NO;} ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); - MHD_destroy_response (response); - return ret; } @end verbatim @@ -102,7 +103,7 @@ int ask_for_authentication (struct MHD_Connection *connection, const char *realm @code{#define} the realm name according to your own taste, e.g. "Maintenance" or "Area51" but it will need to have extra quotes. -But the client may send the authentication right away, so it would be wrong to ask for +Since the client may send the authentication right away, it would be wrong to ask for it without checking the request's header--where the authentication is expected to be found. @heading Authentication in detail @@ -116,8 +117,9 @@ and the resulting line is the value of a request header of the type "Authorizati This header line thus is of interest to the function checking a connection for a given username/password: @verbatim -int is_authenticated (struct MHD_Connection *connection, - const char *username, const char *password) +static int +is_authenticated (struct MHD_Connection *connection, + const char *username, const char *password) { const char *headervalue; ... @@ -185,21 +187,21 @@ authentication methods as @emph{400 bad request}. @heading Exercises @itemize @bullet @item -Make the server respond to wrong credentials (but else correct requests) with the recommended +Make the server respond to wrong credentials (but otherwise well-formed requests) with the recommended @emph{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 whose entries the +authenticating three times during this period, add it to another list for which the @code{AcceptPolicyCallback} function denies connection (temporally). @item -With the network utility @emph{netcat} connect and log the response of a "GET" request as you +With the network utility @code{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 @emph{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. @code{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 +Copy and paste the encoded string you see in @code{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. @end itemize diff --git a/doc/chapters/exploringrequests.inc b/doc/chapters/exploringrequests.inc @@ -9,9 +9,12 @@ just return MHD_NO after we have probed the request. This way, the connection is without much ado by the server. @verbatim -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) +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; @@ -22,25 +25,26 @@ The ellipsis marks the position where the following instructions shall be insert 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 "method") and a URI (e.g. a filename). +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 @code{version}. To call it a "new request" is justified because we return only @code{MHD_NO}, thus ensuring the function will not be called again for this connection. @verbatim -printf ("New request %s for %s using version %s\n", method, url, version); +printf ("New %s request for %s using version %s\n", method, url, version); @end verbatim @noindent 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-name" 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--name pair is printed out one by +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. @verbatim -int print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +int print_out_key (void *cls, enum MHD_ValueKind kind, + const char *key, const char *value) { - printf ("%s = %s\n", key, value); + printf ("%s: %s\n", key, value); return MHD_YES; } @end verbatim @@ -48,29 +52,30 @@ int print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, const ch To start the iteration process that calls our new function for every key, the line @verbatim -MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, NULL); +MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL); @end verbatim @noindent 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 @code{PrintOutKey} does not rely on any additional information to fulfill its duties +function @code{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 @code{logger.c} program for this chapter which can be +All in all, this constitutes the complete @code{logging.c} program for this chapter which can be found in the @code{examples} section. Connecting with any modern Internet browser should yield a handful of keys. You should try to interpret them with the aid of @emph{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. +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). @heading 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 how the server operates. 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, but temporally actions -like searching for a passed string is fine. +(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 @code{MHD_NO} in the callback function. @@ -88,16 +93,17 @@ 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 @verbatim -int on_client_connect (void *cls, - const struct sockaddr *addr,socklen_t addrlen) +static int on_client_connect (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) @end verbatim @noindent -that prints out the IP address in an appropriate format. You might want to use the posix function +that prints out the IP address in an appropriate format. You might want to use the POSIX function @code{inet_ntoa} but bear in mind that @code{addr} is actually just a structure containing other substructures and is @emph{not} the variable this function expects. Make sure to return @code{MHD_YES} so that the library knows the client is allowed to connect -(and to request). If one wanted to limit access basing on IP addresses, this would be the place -to do it. The address of your function will then be passed as the third parameter of the +(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 @code{on_client_connect} function must be passed as the third parameter to the @code{MHD_start_daemon} call. @end itemize diff --git a/doc/chapters/hellobrowser.inc b/doc/chapters/hellobrowser.inc @@ -1,7 +1,7 @@ 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 filename the client asks for shall have no effect on the message that will +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. @@ -11,7 +11,9 @@ 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 @verbatim -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 @@ -35,16 +37,17 @@ daemon to sent the reply. Talking about the reply, it is defined as a string right after the function header @verbatim -int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, - const char *method, const char *version, const char *upload_data, +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>"; @end verbatim @noindent 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 in certain layers, called headers, -of additional information. Luckily, most of the work in this area is done by the library for us---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 @code{MHD_create_response_from_data} function. The last two parameters basically tell @emph{MHD} @@ -92,13 +95,13 @@ int main () 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 -chapter, where the reply is already known and therefore the request is served quickly, this poses no problem. +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 @code{AnswerToConnection} knows best what the client wants and needs no additional +established. Our @code{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. @@ -121,11 +124,11 @@ The first example is now complete. Compile it with @verbatim cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES - -L$PATH_TO_LIBMHD_INCLUDES -static -lmicrohttpd -pthread + -L$PATH_TO_LIBMHD_INCLUDES -lmicrohttpd @end verbatim with the two paths set accordingly and run it. -Now open your favorite Internet browser and go to the address @code{localhost:8888}, provided that +Now open your favorite Internet browser and go to the address @code{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. @@ -146,7 +149,7 @@ received if a response is queued in the first iteration. Furthermore, the connec right after the response has been transferred then. Both of these issues you will find addressed in the official @code{minimal_example.c} residing in -the @code{src/examples} directory of the @emph{GNU libmicrohttpd} package. The source code of this +the @code{src/examples} directory of the @emph{MHD} package. The source code of this program should look very familiar to you by now and easy to understand. For our example, the @code{must_copy} and @code{must_free} parameter at the response construction @@ -163,10 +166,10 @@ its own copy of it. @heading Exercises @itemize @bullet @item -While the server is running, use a program like telnet or netcat to connect to it. Try to form a -valid HTTP1.1 request yourself like +While the server is running, use a program like @code{telnet} or @code{netcat} to connect to it. Try to form a +valid HTTP 1.1 request yourself like @verbatim -GET /dontcare HTTP1.1 +GET /dontcare HTTP/1.1 Host: itsme <enter> @end verbatim @@ -197,7 +200,7 @@ former static string. @item @emph{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 @code{AnswerToConnection}. Remember to free the memory of the string +on every request anew in @code{answer_to_connection}. Remember to free the memory of the string every time after satisfying the request. @end itemize diff --git a/doc/chapters/introduction.inc b/doc/chapters/introduction.inc @@ -15,3 +15,9 @@ is written for version @value{VERSION}. At the time being, this tutorial has only been tested on @emph{GNU/Linux} machines even though efforts were made not to rely on anything that would prevent the samples from being built on similar systems. + +@section 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. +\ No newline at end of file diff --git a/doc/chapters/largerpost.inc b/doc/chapters/largerpost.inc @@ -13,14 +13,15 @@ total number of clients that are uploading. @verbatim #define MAXCLIENTS 2 -static unsigned char nr_of_uploading_clients = 0; +static unsigned int nr_of_uploading_clients = 0; @end verbatim @noindent If there are too many clients uploading, we want the server to respond to all requests with a busy message. @verbatim -const char* busypage = "<html><body>This server is busy, please try again later.</body></html>"; +const char* busypage = + "<html><body>This server is busy, please try again later.</body></html>"; @end verbatim @noindent @@ -29,8 +30,9 @@ and ask her to pick a file on her local filesystem which is to be uploaded. @verbatim const char* askpage = "<html><body>\n\ Upload a file, please!<br>\n\ - There are %d clients uploading at the moment.<br>\n\ - <form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\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>"; @@ -46,8 +48,10 @@ const char* completepage = "<html><body>The upload has been completed.</body></h We want the server to report internal errors, such as memory shortage or file access problems, adequately. @verbatim -const char* servererrorpage = "<html><body>An internal server error has occured.</body></html>"; -const char* fileexistspage = "<html><body>This file already exists.</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>"; @end verbatim @noindent @@ -56,13 +60,15 @@ status code but in order to improve the @code{HTTP} conformance of our server a @code{send_page} function so that it accepts individual status codes. @verbatim -int send_page (struct MHD_Connection *connection, const char* page, int status_code) +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_data (strlen (page), (void*) page, MHD_NO, MHD_YES); + response = MHD_create_response_from_data (strlen (page), (void*) page, + MHD_NO, MHD_YES); if (!response) return MHD_NO; ret = MHD_queue_response (connection, status_code, response); @@ -84,9 +90,12 @@ queued at this point, and @code{MHD_YES} returned, @emph{MHD} will not sent any a postprocessor has been created and the post iterator is called at least once. @verbatim -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) +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) { @@ -127,8 +136,9 @@ the structure is initialized to "no error". @verbatim if (0 == strcmp (method, "POST")) { - con_info->postprocessor = MHD_create_post_processor (connection, POSTBUFFERSIZE, - iterate_post, (void*) con_info); + con_info->postprocessor + = MHD_create_post_processor (connection, POSTBUFFERSIZE, + iterate_post, (void*) con_info); if (NULL == con_info->postprocessor) { @@ -153,7 +163,7 @@ own copy of it for as long as it is needed. if (0 == strcmp (method, "GET")) { int ret; - char buffer[1024] = {0}; + char buffer[1024]; sprintf (buffer, askpage, nr_of_uploading_clients); return send_page (connection, buffer, MHD_HTTP_OK); @@ -173,12 +183,15 @@ constituted no expected request method. if (0 != *upload_data_size) { - MHD_post_process(con_info->postprocessor, upload_data, *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); + else + return send_page (connection, con_info->answerstring, + con_info->answercode); } return send_page(connection, errorpage, MHD_HTTP_BAD_REQUEST); @@ -193,11 +206,14 @@ several times now. This means that for any given connection (there might be seve the posted data has to be written to the correct file. That is why we store a file handle in every @code{connection_info}, so that the it is preserved between successive iterations. @verbatim -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) +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 = (struct connection_info_struct*) coninfo_cls; + struct connection_info_struct *con_info = coninfo_cls; @end verbatim @noindent @@ -221,7 +237,8 @@ would be an error. If the iterator is called for the first time, no file will have been opened yet. The @code{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. +(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). @verbatim if (!con_info->fp) { @@ -245,7 +262,8 @@ server only needs to write data to the file if there is some. @verbatim if (size > 0) { - if (!fwrite (data, size, sizeof(char), con_info->fp)) return MHD_NO; + if (!fwrite (data, size, sizeof(char), con_info->fp)) + return MHD_NO; } @end verbatim @noindent @@ -265,10 +283,11 @@ be set to success again. If the upload has finished, this iterator function will The new client was registered when the postprocessor was created. Likewise, we unregister the client on destroying the postprocessor when the request is completed. @verbatim -void request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, +void request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe) { - struct connection_info_struct *con_info = (struct connection_info_struct*) *con_cls; + struct connection_info_struct *con_info = *con_cls; if (NULL == con_info) return; @@ -296,4 +315,5 @@ This is essentially the whole example @code{largepost.c}. @heading 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. +explicitly that no files will be created outside of a dedicated upload directory. In particular, +filenames must be checked to not contain strings like "../". diff --git a/doc/chapters/processingpost.inc b/doc/chapters/processingpost.inc @@ -4,8 +4,8 @@ the server operates. To induce changes on the server, the @emph{POST} method is and is much more powerful than @emph{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 @emph{post} method here, -as there is no permanent change caused by the post, it is an illustrative example on how to share data +composes an individual response text. Even though it was not mandatory to use the @emph{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. @@ -37,8 +37,9 @@ const char* errorpage="<html><body>This doesn't seem to be right.</body></html>" @noindent Whenever we need to send a page, we use an extra function -@code{int SendPage(struct MHD_Connection *connection, const char* page)} -for this, which does not contain anything new and whose implementation is therefore left out here. +@code{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. @heading POST request @@ -73,12 +74,13 @@ chunks, we had to expand the namestring dynamically as additional parts of it wi came in. But in this example, the name is assumed to fit entirely inside one single packet. @verbatim -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) +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 = (struct connection_info_struct*) coninfo_cls; - + struct connection_info_struct *con_info = coninfo_cls; if (0 == strcmp (key, "name")) { @@ -108,14 +110,13 @@ string. This cleanup function must take into account that it will also be called requests other than @emph{POST} requests. @verbatim -void request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, +void request_completed (void *cls, struct MHD_Connection *connection, + void **con_cls, enum MHD_RequestTerminationCode toe) { - struct connection_info_struct *con_info = (struct connection_info_struct*) *con_cls; - + 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); @@ -134,8 +135,9 @@ in the main function. @verbatim ... 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); + &answer_to_connection, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL, + MHD_OPTION_END); ... @end verbatim @noindent @@ -144,13 +146,16 @@ daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL, 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 -@code{ConnectionInfoStruct} structure, which will store all necessary information for later +@code{struct connection_info_struct} structure, which will store all necessary information for later iterations and other functions. @verbatim -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) +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) { @@ -167,15 +172,15 @@ of the request is stored for convenience. @verbatim if (0 == strcmp (method, "POST")) { - con_info->postprocessor = MHD_create_post_processor (connection, POSTBUFFERSIZE, - iterate_post, (void*) con_info); + 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; @@ -211,17 +216,19 @@ considered---all of it. if (*upload_data_size != 0) { - MHD_post_process(con_info->postprocessor, upload_data, *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->answerstring) return send_page (connection, con_info->answerstring); + else if (NULL != con_info->answerstring) + return send_page (connection, con_info->answerstring); } @end verbatim @noindent -If they are neither @emph{GET} nor @emph{POST} requests, the error page is returned finally. +Finally, if they are neither @emph{GET} nor @emph{POST} requests, the error page is returned. @verbatim return send_page(connection, errorpage); } diff --git a/doc/chapters/responseheaders.inc b/doc/chapters/responseheaders.inc @@ -9,7 +9,7 @@ This will lead to an application capable of correctly serving different types of 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 hadn't told him. +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 @@ -23,47 +23,60 @@ Once again, we can base the new example on the @code{hellobrowser} program. #define FILENAME "picture.png" #define MIMETYPE "image/png" -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) +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; @end verbatim @noindent -We want the program to load the graphics file into memory: +We want the program to open the file for reading and determine its size: @verbatim - long size; - FILE *fp; - int ret = 0; - - if (0 != strcmp(method, "GET")) return MHD_NO; - - size = get_file_size (FILENAME); - if (size != 0) + 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)) ) { - fp = fopen (FILENAME, "rb"); - if (fp) - { - buffer = malloc (size); - - if (buffer) - if (size == fread (buffer, 1, size, fp)) ret = 1; - - fclose(fp); - } + /* error accessing file */ + /* ... (see below) */ } + /* ... (see below) */ @end verbatim @noindent -The @code{GetFileSize} function, which returns a size of zero if the file could not be opened or -found, is left out on this page for tidiness. - -When dealing with files and allocating memory, there is a lot that could go wrong on the +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 @code{MHD_HTTP_INTERNAL_SERVER_ERROR}. -@verbatim +@verbatim + /* 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_data (strlen (errorstr), + (void *) errorstr, + MHD_NO, MHD_NO); + 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!\ @@ -76,7 +89,9 @@ server side and if so, the client should be informed with @code{MHD_HTTP_INTERNA if (response) { - ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); + ret = MHD_queue_response (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + response); MHD_destroy_response (response); return MHD_YES; @@ -90,19 +105,22 @@ Note that we nevertheless have to create a response object even for sending a si 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 that contains the buffer filled with the -file's content. +But in the case of success a response will be constructed directly from the file descriptor: @verbatim -response = MHD_create_response_from_data (size, (void*)buffer, MHD_YES, MHD_NO); + /* error accessing file */ + /* ... (see above) */ + } + + response = + MHD_create_response_from_fd (fd, sbuf.st_size); + MHD_add_response_header (response, "Content-Type", MIMETYPE); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); @end verbatim @noindent -Contrary to the above case where a static string will be sent, this time we have to -keep track of the dynamically allocated buffer. As discussed in the @ref{Hello browser example}, -the buffer cannot be safely freed as soon as the function call returns. Instead, we ask the function -to keep charge of freeing the buffer itself when it is not longer needed. Thus, no further @code{free} -command is invoked by us. +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: @@ -135,17 +153,15 @@ to these custom header is up to the receiver. Likewise, the client is allowed to 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 one big chunk of data is only feasible for smaller files. -A public file server satisfying many request at the same time would be choking under these high -demands on memory very soon. Serving responses in smaller parts would be more adequate here and -will be a topic of a future chapter. +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. @heading Exercises @itemize @bullet @item -Remember that the original program was written under a few assumptions---a small, static response -being one of them. In order to simulate a very large or hard to reach file that cannot be provided +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 @code{sleep} function for 30 seconds @emph{if} the file @code{/big.png} is requested (but deliver the same as above). A request for @code{/picture.png} should provide just the same but without any artificial delays. @@ -163,7 +179,7 @@ program's start time @code{t} and implement a response simulating a countdown th @code{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, -A non official, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with +An unofficial, but widely understood, response header line is @code{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 diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc @@ -68,7 +68,8 @@ main () and then we point the @emph{MHD} daemon to it upon initalization. @verbatim - daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL, + 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, @@ -125,6 +126,5 @@ hardcode certificates in embedded devices. The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}. -@end itemize - +@end itemize diff --git a/doc/examples/basicauthentication.c b/doc/examples/basicauthentication.c @@ -1,4 +1,6 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #include <time.h> @@ -12,7 +14,7 @@ char *string_to_base64 (const char *message); -int +static int ask_for_authentication (struct MHD_Connection *connection, const char *realm) { int ret; @@ -46,7 +48,7 @@ ask_for_authentication (struct MHD_Connection *connection, const char *realm) return ret; } -int +static int is_authenticated (struct MHD_Connection *connection, const char *username, const char *password) { @@ -84,7 +86,7 @@ is_authenticated (struct MHD_Connection *connection, } -int +static int secret_page (struct MHD_Connection *connection) { int ret; @@ -104,7 +106,7 @@ secret_page (struct MHD_Connection *connection) } -int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, diff --git a/doc/examples/hellobrowser.c b/doc/examples/hellobrowser.c @@ -1,9 +1,11 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 -int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, diff --git a/doc/examples/largepost.c b/doc/examples/largepost.c @@ -1,4 +1,6 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 @@ -8,7 +10,7 @@ #define GET 0 #define POST 1 -static unsigned char nr_of_uploading_clients = 0; +static unsigned int nr_of_uploading_clients = 0; struct connection_info_struct { @@ -21,7 +23,7 @@ struct connection_info_struct const char *askpage = "<html><body>\n\ Upload a file, please!<br>\n\ - There are %d clients uploading at the moment.<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\ @@ -41,14 +43,13 @@ const char *fileexistspage = "<html><body>This file already exists.</body></html>"; -int +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_data (strlen (page), (void *) page, MHD_NO, MHD_YES); @@ -62,15 +63,14 @@ send_page (struct MHD_Connection *connection, const char *page, } -int +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; - struct connection_info_struct *con_info = - (struct connection_info_struct *) coninfo_cls; con_info->answerstring = servererrorpage; con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR; @@ -105,12 +105,11 @@ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, return MHD_YES; } -void +static void request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { - struct connection_info_struct *con_info = - (struct connection_info_struct *) *con_cls; + struct connection_info_struct *con_info = *con_cls; if (NULL == con_info) return; @@ -132,7 +131,7 @@ request_completed (void *cls, struct MHD_Connection *connection, } -int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, @@ -180,7 +179,7 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, if (0 == strcmp (method, "GET")) { int ret; - char buffer[1024] = { 0 }; + char buffer[1024]; sprintf (buffer, askpage, nr_of_uploading_clients); return send_page (connection, buffer, MHD_HTTP_OK); diff --git a/doc/examples/logging.c b/doc/examples/logging.c @@ -1,24 +1,26 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 -int +static int print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { - printf ("%s = %s\n", key, value); + printf ("%s: %s\n", key, value); return MHD_YES; } -int +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 request %s for %s using version %s\n", method, url, version); + 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); diff --git a/doc/examples/responseheaders.c b/doc/examples/responseheaders.c @@ -1,4 +1,6 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #include <time.h> @@ -6,73 +8,32 @@ #define FILENAME "picture.png" #define MIMETYPE "image/png" - -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; -} - - -int +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; - long size; - FILE *fp; - int ret = 0; + int fd; + int ret; + struct stat sbuf; if (0 != strcmp (method, "GET")) return MHD_NO; - size = get_file_size (FILENAME); - if (size != 0) - { - fp = fopen (FILENAME, "rb"); - if (fp) - { - buffer = malloc (size); - - if (buffer) - if (size == fread (buffer, 1, size, fp)) - ret = 1; - - fclose (fp); - } - } - - if (!ret) + if ( (-1 == (fd = open (FILENAME, O_RDONLY))) || + (0 != fstat (fd, &sbuf)) ) { + /* error accessing file */ + if (fd != -1) close (fd); const char *errorstr = "<html><body>An internal server error has occured!\ </body></html>"; - - if (buffer) - free (buffer); - response = - MHD_create_response_from_data (strlen (errorstr), (void *) errorstr, - MHD_NO, MHD_NO); - + MHD_create_response_from_data (strlen (errorstr), + (void *) errorstr, + MHD_NO, MHD_NO); if (response) { ret = @@ -85,12 +46,9 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, else return MHD_NO; } - response = - MHD_create_response_from_data (size, (void *) buffer, MHD_YES, MHD_NO); - + MHD_create_response_from_fd (fd, sbuf.st_size); MHD_add_response_header (response, "Content-Type", MIMETYPE); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); diff --git a/doc/examples/simplepost.c b/doc/examples/simplepost.c @@ -1,4 +1,6 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 @@ -30,7 +32,7 @@ const char *errorpage = "<html><body>This doesn't seem to be right.</body></html>"; -int +static int send_page (struct MHD_Connection *connection, const char *page) { int ret; @@ -50,15 +52,13 @@ send_page (struct MHD_Connection *connection, const char *page) } -int +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 = - (struct connection_info_struct *) coninfo_cls; - + struct connection_info_struct *con_info = coninfo_cls; if (0 == strcmp (key, "name")) { @@ -81,13 +81,11 @@ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key, return MHD_YES; } -void +static void request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { - struct connection_info_struct *con_info = - (struct connection_info_struct *) *con_cls; - + struct connection_info_struct *con_info = *con_cls; if (NULL == con_info) return; @@ -104,7 +102,7 @@ request_completed (void *cls, struct MHD_Connection *connection, } -int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, @@ -170,7 +168,6 @@ 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, diff --git a/doc/examples/tlsauthentication.c b/doc/examples/tlsauthentication.c @@ -1,4 +1,6 @@ -#include <platform.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> #include <microhttpd.h> #define PORT 8888 @@ -50,7 +52,7 @@ string_to_base64 (const char *message) } -long +static long get_file_size (const char *filename) { FILE *fp; @@ -71,7 +73,7 @@ get_file_size (const char *filename) return 0; } -char * +static char * load_file (const char *filename) { FILE *fp; @@ -103,7 +105,7 @@ load_file (const char *filename) return buffer; } -int +static int ask_for_authentication (struct MHD_Connection *connection, const char *realm) { int ret; @@ -137,7 +139,7 @@ ask_for_authentication (struct MHD_Connection *connection, const char *realm) return ret; } -int +static int is_authenticated (struct MHD_Connection *connection, const char *username, const char *password) { @@ -176,7 +178,7 @@ is_authenticated (struct MHD_Connection *connection, } -int +static int secret_page (struct MHD_Connection *connection) { int ret; @@ -196,7 +198,7 @@ secret_page (struct MHD_Connection *connection) } -int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, diff --git a/doc/libmicrohttpd.3 b/doc/libmicrohttpd.3 @@ -3,10 +3,10 @@ GNU libmicrohttpd \- library for embedding HTTP servers .SH "SYNOPSIS" -\fB#include <sys/types.h> -\fB#include <sys/select.h> -\fB#include <sys/socket.h> -\fB#include <microhttpd.h> + \fB#include <sys/types.h> + \fB#include <sys/select.h> + \fB#include <sys/socket.h> + \fB#include <microhttpd.h> .SH "DESCRIPTION" .P @@ -31,10 +31,10 @@ libmicrohttpd.so libmicrohttpd library .SH "REPORTING BUGS" -Report bugs by using mantis <https://gnunet.org/mantis/>. +Report bugs by using mantis <https://gnunet.org/bugs/>. .SH "AUTHORS" -GNU libmicrohttpd was originally designed by Christian Grothoff <christian@grothoff.org> and Chris GauthierDickey <chrisg@cs.du.edu>. The original implementation was done by Daniel Pittman <depittman@gmail.com> and Christian Grothoff. SSL/TLS support was added by Sagie Amir using code from gnutls. See the AUTHORS file in the distribution for a more detailed list of contributors. +GNU libmicrohttpd was originally designed by Christian Grothoff <christian@grothoff.org> and Chris GauthierDickey <chrisg@cs.du.edu>. The original implementation was done by Daniel Pittman <depittman@gmail.com> and Christian Grothoff. SSL/TLS support was added by Sagie Amir using code from GnuTLS. See the AUTHORS file in the distribution for a more detailed list of contributors. .SH "AVAILABILITY" You can obtain the latest version from http://www.gnu.org/software/libmicrohttpd/. diff --git a/doc/microhttpd.texi b/doc/microhttpd.texi @@ -2,8 +2,6 @@ @setfilename microhttpd.info @include version.texi @settitle The GNU libmicrohttpd Reference Manual -@c Unify some of the indices. -@c %**end of header @copying This manual documents GNU libmicrohttpd version @value{VERSION}, last updated @value{UPDATED}. It is built upon the documentation in the @@ -21,6 +19,7 @@ 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". @end quotation +GNU libmicrohttpd is a GNU package. @end copying @dircategory GNU Libraries @@ -31,7 +30,6 @@ Free Documentation License". @c @c Titlepage @c -@setchapternewpage odd @titlepage @title The GNU libmicrohttpd Reference Manual @subtitle Version @value{VERSION} @@ -46,7 +44,6 @@ Free Documentation License". @summarycontents @contents -@page @macro gnu{} @@ -111,8 +108,6 @@ Free Documentation License". @insertcopying @end ifnottex -GNU libmicrohttpd is a GNU package. - @menu * microhttpd-intro:: Introduction. * microhttpd-const:: Constants. @@ -188,6 +183,44 @@ Additionally, clients can specify resource limits on the overall number of connections, number of connections per IP address and memory used per connection to avoid resource exhaustion. +@section SIGPIPE +@cindex signals +@mhd{} does not install a signal handler for SIGPIPE. On platforms +where this is possible (such as GNU/Linux), it disables SIGPIPE for +its I/O operations (by passing MSG_NOSIGNAL). On other platforms, +SIGPIPE signals may be generated from network operations by +@mhd{} and will cause the process to die unless the developer +explicitly installs a signal handler for SIGPIPE. + +Hence portable code using MHD must install a SIGPIPE handler or +explicitly block the SIGPIPE signal. MHD does not do so in order +to avoid messing with other parts of the application that may +need to handle SIGPIPE in a particular way. You can make your application handle SIGPIPE by calling the following function in @code{main}: + +@verbatim +static void +catcher (int sig) +{ +} + +static void +ignore_sigpipe () +{ + struct sigaction oldsig; + struct sigaction sig; + + sig.sa_handler = &catcher; + sigemptyset (&sig.sa_mask); +#ifdef SA_INTERRUPT + sig.sa_flags = SA_INTERRUPT; /* SunOS */ +#else + sig.sa_flags = SA_RESTART; +#endif + if (0 != sigaction (SIGPIPE, &sig, &oldsig)) + fprintf (stderr, + "Failed to install SIGPIPE handler: %s\n", strerror (errno)); +} +@end verbatim @c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -357,12 +390,12 @@ HTTPS daemon. This option should be followed by an "const char*" argument. This should be used in conjunction with 'MHD_OPTION_HTTPS_MEM_KEY'. -@item MHD_OPTION_CRED_TYPE +@item MHD_OPTION_HTTPS_CRED_TYPE @cindex SSL @cindex TLS Daemon credentials type. Either certificate or anonymous, this option should be followed by one of the values listed in -"enum MHD_GNUTLS_CredentialsType". +"enum gnutls_credentials_type_t". @item MHD_OPTION_HTTPS_PRIORITIES @cindex SSL @@ -503,43 +536,6 @@ We had to close the session since @mhd{} was being shut down. @end table @end deftp -@deftp {Enumeration} MHD_GNUTLS_Protocol -SSL/TLS Protocol types. Note that not all listed algorithms are -necessarily supported by all builds of MHD. - -@table @code -@item MHD_GNUTLS_PROTOCOL_END -@item MHD_GNUTLS_PROTOCOL_SSL3 -@item MHD_GNUTLS_PROTOCOL_TLS1_0 -@item MHD_GNUTLS_PROTOCOL_TLS1_1 -@item MHD_GNUTLS_PROTOCOL_TLS1_2 -@item MHD_GNUTLS_PROTOCOL_UNKNOWN - -@end table -@end deftp - - - -@deftp {Enumeration} MHD_GNUTLS_CipherAlgorithm -List of symmetric ciphers. -Note that not all listed algorithms are necessarily supported by -all builds of MHD. - -@table @code -@item MHD_GNUTLS_CIPHER_UNKNOWN - -@item MHD_GNUTLS_CIPHER_NULL - -@item MHD_GNUTLS_CIPHER_ARCFOUR_128 - -@item MHD_GNUTLS_CIPHER_3DES_CBC - -@item MHD_GNUTLS_CIPHER_AES_128_CBC - -@item MHD_GNUTLS_CIPHER_AES_256_CBC - -@end table -@end deftp @deftp {Enumeration} MHD_ConnectionInfoType diff --git a/doc/tutorial.texi b/doc/tutorial.texi @@ -1,33 +1,44 @@ \input texinfo @c -*-texinfo-*- @finalout @setfilename microhttpd-tutorial.info +@include version.texi @settitle A tutorial for GNU libmicrohttpd -@afourpaper - -@set VERSION 0.4.0 @dircategory GNU Libraries @direntry * libmicrohttpdtutorial: (microhttpd). A tutorial for GNU libmicrohttpd. @end direntry -@titlepage -@title A Tutorial for GNU libmicrohttpd -@subtitle written for version @value{VERSION} -@author Sebastian Gerhardt (@email{sebgerhardt@@gmx.net}) -@page -@vskip 0pt plus 1filll -@end titlepage +@copying +This tutorial documents GNU libmicrohttpd version @value{VERSION}, last +updated @value{UPDATED}. -@verbatim Copyright (c) 2008 Sebastian Gerhardt. + +Copyright (c) 2010 Christian Grothoff. +@quotation 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". -@end verbatim +@end quotation +@end copying + +@titlepage +@title A Tutorial for GNU libmicrohttpd +@subtitle Version @value{VERSION} +@subtitle @value{UPDATED} +@author Sebastian Gerhardt (@email{sebgerhardt@@gmx.net}) +@author Christian Grothoff (@email{christian@@grothoff.org}) + +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + + @contents diff --git a/src/daemon/EXPORT.sym b/src/daemon/EXPORT.sym @@ -10,6 +10,7 @@ MHD_lookup_connection_value MHD_queue_response MHD_create_response_from_callback MHD_create_response_from_data +MHD_create_response_from_fd MHD_destroy_response MHD_add_response_header MHD_del_response_header diff --git a/src/daemon/connection.c b/src/daemon/connection.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007, 2008 Daniel Pittman and Christian Grothoff + (C) 2007, 2008, 2009, 2010 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -336,7 +336,10 @@ try_ready_normal_body (struct MHD_Connection *connection) #if LINUX if ( (response->fd != -1) && (0 == (connection->daemon->options & MHD_USE_SSL)) ) - return MHD_YES; /* will use sendfile */ + { + /* will use sendfile, no need to bother response crc */ + return MHD_YES; + } #endif ret = response->crc (response->crc_cls, connection->response_write_position, diff --git a/src/daemon/connection_https.c b/src/daemon/connection_https.c @@ -198,7 +198,7 @@ MHD_tls_connection_handle_write (struct MHD_Connection *connection) * the processing of this secure connection. */ void -MHD_set_https_calbacks (struct MHD_Connection *connection) +MHD_set_https_callbacks (struct MHD_Connection *connection) { connection->read_handler = &MHD_tls_connection_handle_read; connection->write_handler = &MHD_tls_connection_handle_write; diff --git a/src/daemon/connection_https.h b/src/daemon/connection_https.h @@ -29,7 +29,7 @@ #include "internal.h" #if HTTPS_SUPPORT -void MHD_set_https_calbacks (struct MHD_Connection *connection); +void MHD_set_https_callbacks (struct MHD_Connection *connection); #endif #endif diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007, 2008, 2009 Daniel Pittman and Christian Grothoff + (C) 2007, 2008, 2009, 2010 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -387,8 +387,8 @@ MHD_init_daemon_certificate (struct MHD_Daemon *daemon) cert.size = strlen (daemon->https_mem_cert); return gnutls_certificate_set_x509_key_mem (daemon->x509_cred, - &cert, &key, - GNUTLS_X509_FMT_PEM); + &cert, &key, + GNUTLS_X509_FMT_PEM); } #if HAVE_MESSAGES MHD_DLOG (daemon, "You need to specify a certificate and key location\n"); @@ -625,21 +625,28 @@ send_param_adapter (struct MHD_Connection *connection, #if LINUX int fd; off_t offset; + int ret; #endif if (connection->socket_fd == -1) return -1; if (0 != (connection->daemon->options & MHD_USE_SSL)) return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL); #if LINUX - if ( (NULL != connection->response) && + if ( (connection->write_buffer_append_offset == + connection->write_buffer_send_offset) && + (NULL != connection->response) && (-1 != (fd = connection->response->fd)) ) { /* can use sendfile */ offset = (off_t) connection->response_write_position; - return sendfile (connection->socket_fd, - fd, - &offset, - i); + ret = sendfile (connection->socket_fd, + fd, + &offset, + connection->response->total_size - offset); + if ( (ret == -1) && + (errno == EINTR) ) + return 0; + return ret; } #endif return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL | MSG_DONTWAIT); @@ -819,7 +826,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon) connection->recv_cls = &recv_tls_adapter; connection->send_cls = &send_tls_adapter; connection->state = MHD_TLS_CONNECTION_INIT; - MHD_set_https_calbacks (connection); + MHD_set_https_callbacks (connection); gnutls_init (&connection->tls_session, GNUTLS_SERVER); gnutls_priority_set (connection->tls_session, daemon->priority_cache); @@ -1664,7 +1671,10 @@ MHD_start_daemon_va (unsigned int options, return NULL; } } - + else + { + socket_fd = retVal->socket_fd; + } #ifndef WINDOWS if ( (socket_fd >= FD_SETSIZE) && (0 == (options & MHD_USE_POLL)) ) diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -1,21 +1,21 @@ /* - This file is part of libmicrohttpd - (C) 2007 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ + This file is part of libmicrohttpd + (C) 2007, 2008, 2009, 2010 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ /** * @file internal.h diff --git a/src/daemon/response.c b/src/daemon/response.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007, 2009 Daniel Pittman and Christian Grothoff + (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -210,7 +210,7 @@ MHD_create_response_from_callback (uint64_t size, * Given a file descriptor, read data from the file * to generate the response. * - * @param cls pointer to the file descriptor + * @param cls pointer to the response * @param pos offset in the file to access * @param buf where to write the data * @param max number of bytes to write at most @@ -219,10 +219,14 @@ MHD_create_response_from_callback (uint64_t size, static int file_reader (void *cls, uint64_t pos, char *buf, int max) { - int *fd = cls; + struct MHD_Response *response = cls; + int ret; - (void) lseek (*fd, pos, SEEK_SET); - return read (*fd, buf, max); + pthread_mutex_lock (&response->mutex); + (void) lseek (response->fd, pos, SEEK_SET); + ret = read (response->fd, buf, max); + pthread_mutex_unlock (&response->mutex); + return ret; } @@ -235,9 +239,9 @@ file_reader (void *cls, uint64_t pos, char *buf, int max) static void free_callback (void *cls) { - int *fd = cls; - close (*fd); - *fd = -1; + struct MHD_Response *response = cls; + (void) close (response->fd); + response->fd = -1; } @@ -262,7 +266,7 @@ struct MHD_Response *MHD_create_response_from_fd (size_t size, if (ret == NULL) return NULL; ret->fd = fd; - ret->crc_cls = &ret->fd; + ret->crc_cls = ret; return ret; } diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -81,7 +81,7 @@ extern "C" /** * Current version of the library. */ -#define MHD_VERSION 0x00040600 +#define MHD_VERSION 0x00090000 /** * MHD-internal return code for "YES". diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am @@ -16,6 +16,7 @@ $(LIBCURL_CPPFLAGS) check_PROGRAMS = \ daemontest_get \ + daemontest_get_sendfile \ daemontest_post \ daemontest_postform \ daemontest_post_loop \ @@ -25,6 +26,7 @@ check_PROGRAMS = \ daemontest_parse_cookies \ daemontest_large_put \ daemontest_get11 \ + daemontest_get_sendfile11 \ daemontest_post11 \ daemontest_postform11 \ daemontest_post_loop11 \ @@ -57,6 +59,12 @@ daemontest_get_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la \ @LIBCURL@ +daemontest_get_sendfile_SOURCES = \ + daemontest_get_sendfile.c +daemontest_get_sendfile_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la \ + @LIBCURL@ + daemontest_get_chunked_SOURCES = \ daemontest_get_chunked.c daemontest_get_chunked_LDADD = \ @@ -117,6 +125,12 @@ daemontest_get11_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la \ @LIBCURL@ +daemontest_get_sendfile11_SOURCES = \ + daemontest_get_sendfile.c +daemontest_get_sendfile11_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la \ + @LIBCURL@ + daemontest_post11_SOURCES = \ daemontest_post.c daemontest_post11_LDADD = \ diff --git a/src/testcurl/daemontest_get_sendfile.c b/src/testcurl/daemontest_get_sendfile.c @@ -0,0 +1,463 @@ +/* DO NOT CHANGE THIS LINE */ +/* + This file is part of libmicrohttpd + (C) 2007, 2009 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file daemontest_get_sendfile.c + * @brief Testcase for libmicrohttpd response from FD + * @author Christian Grothoff + */ + +#include "MHD_config.h" +#include "platform.h" +#include <curl/curl.h> +#include <microhttpd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <fcntl.h> + +#ifndef WINDOWS +#include <unistd.h> +#endif + +#define TESTSTR "/* DO NOT CHANGE THIS LINE */" + +static int oneone; + +struct CBC +{ + char *buf; + size_t pos; + size_t size; +}; + +static size_t +copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) +{ + struct CBC *cbc = ctx; + + if (cbc->pos + size * nmemb > cbc->size) + return 0; /* overflow */ + memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); + cbc->pos += size * nmemb; + return size * nmemb; +} + + +static int +ahc_echo (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 **unused) +{ + static int ptr; + const char *me = cls; + struct MHD_Response *response; + int ret; + int fd; + + if (0 != strcmp (me, method)) + return MHD_NO; /* unexpected method */ + if (&ptr != *unused) + { + *unused = &ptr; + return MHD_YES; + } + *unused = NULL; + fd = open ("daemontest_get_sendfile.c", O_RDONLY); + if (fd == -1) + abort (); + response = MHD_create_response_from_fd (strlen (TESTSTR), fd); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + if (ret == MHD_NO) + abort (); + return ret; +} + + +static int +testInternalGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + if (d == NULL) + return 1; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 2; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen (TESTSTR)) + return 4; + if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR))) + return 8; + return 0; +} + +static int +testMultithreadedGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen (TESTSTR)) + return 64; + if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR))) + return 128; + return 0; +} + +static int +testMultithreadedPoolGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen (TESTSTR)) + return 64; + if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR))) + return 128; + return 0; +} + +static int +testExternalGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLM *multi; + CURLMcode mret; + fd_set rs; + fd_set ws; + fd_set es; + int max; + int running; + struct CURLMsg *msg; + time_t start; + struct timeval tv; + + multi = NULL; + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_DEBUG, + 1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + if (d == NULL) + return 256; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + + + multi = curl_multi_init (); + if (multi == NULL) + { + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 512; + } + mret = curl_multi_add_handle (multi, c); + if (mret != CURLM_OK) + { + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 1024; + } + start = time (NULL); + while ((time (NULL) - start < 5) && (multi != NULL)) + { + max = 0; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + curl_multi_perform (multi, &running); + mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); + if (mret != CURLM_OK) + { + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 2048; + } + if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) + { + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 4096; + } + tv.tv_sec = 0; + tv.tv_usec = 1000; + select (max + 1, &rs, &ws, &es, &tv); + curl_multi_perform (multi, &running); + if (running == 0) + { + msg = curl_multi_info_read (multi, &running); + if (msg == NULL) + break; + if (msg->msg == CURLMSG_DONE) + { + if (msg->data.result != CURLE_OK) + printf ("%s failed at %s:%d: `%s'\n", + "curl_multi_perform", + __FILE__, + __LINE__, curl_easy_strerror (msg->data.result)); + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + c = NULL; + multi = NULL; + } + } + MHD_run (d); + } + if (multi != NULL) + { + curl_multi_remove_handle (multi, c); + curl_easy_cleanup (c); + curl_multi_cleanup (multi); + } + MHD_stop_daemon (d); + if (cbc.pos != strlen (TESTSTR)) + return 8192; + if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR))) + return 16384; + return 0; +} + +static int +testUnknownPortGet () +{ + struct MHD_Daemon *d; + const union MHD_DaemonInfo *di; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_SOCK_ADDR, &addr, + MHD_OPTION_END); + if (d == NULL) + return 32768; + + di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); + if (di == NULL) + return 65536; + + if (0 != getsockname(di->listen_fd, &addr, &addr_len)) + return 131072; + + if (addr.sin_family != AF_INET) + return 26214; + + snprintf(buf, sizeof(buf), "http://localhost:%hu/", + ntohs(addr.sin_port)); + + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, buf); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 524288; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen (TESTSTR)) + return 1048576; + if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR))) + return 2097152; + return 0; +} + + +int +main (int argc, char *const *argv) +{ + unsigned int errorCount = 0; + + oneone = NULL != strstr (argv[0], "11"); + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + return 2; + errorCount += testInternalGet (); + errorCount += testMultithreadedGet (); + errorCount += testMultithreadedPoolGet (); + errorCount += testExternalGet (); + errorCount += testUnknownPortGet (); + if (errorCount != 0) + fprintf (stderr, "Error (code: %u)\n", errorCount); + curl_global_cleanup (); + return errorCount != 0; /* 0 == pass */ +} diff --git a/src/testcurl/https/mhds_get_test.c b/src/testcurl/https/mhds_get_test.c @@ -1,22 +1,22 @@ /* - This file is part of libmicrohttpd - (C) 2007 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - libmicrohttpd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ + This file is part of libmicrohttpd + (C) 2007 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ /** * @file mhds_get_test.c diff --git a/src/testcurl/https/tls_daemon_options_test.c b/src/testcurl/https/tls_daemon_options_test.c @@ -1,22 +1,22 @@ /* - This file is part of libmicrohttpd - (C) 2007 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - libmicrohttpd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ + This file is part of libmicrohttpd + (C) 2007, 2010 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ /** * @file tls_daemon_options_test.c @@ -103,7 +103,6 @@ main (int argc, char *const *argv) fprintf (stderr, "Error: %s\n", strerror (errno)); return -1; } -#if 0 errorCount += test_wrap ("TLS1.0-AES-SHA1", &test_https_transfer, test_fd, daemon_flags, @@ -113,8 +112,6 @@ main (int argc, char *const *argv) MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, MHD_OPTION_HTTPS_PRIORITIES, "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL", MHD_OPTION_END); -#endif -#if 0 errorCount += test_wrap ("TLS1.0-AES-SHA1", &test_https_transfer, test_fd, daemon_flags, @@ -134,8 +131,6 @@ main (int argc, char *const *argv) MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, MHD_OPTION_HTTPS_PRIORITIES, "NONE:+VERS-SSL3.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL", MHD_OPTION_END); -#endif - #if 0 /* manual inspection of the handshake suggests that CURL will request TLSv1, we send back "SSL3" and CURL takes it *despite* @@ -152,7 +147,6 @@ main (int argc, char *const *argv) MHD_OPTION_CIPHER_ALGORITHM, "SSL3", MHD_OPTION_END); #endif -#if 1 errorCount += test_wrap ("TLS1.0 vs SSL3", &test_unmatching_ssl_version, test_fd, daemon_flags, @@ -162,8 +156,6 @@ main (int argc, char *const *argv) MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, MHD_OPTION_HTTPS_PRIORITIES, "NONE:+VERS-TLS1.0:+AES-256-CBC:+SHA1:+RSA:+COMP-NULL", MHD_OPTION_END); -#endif - curl_global_cleanup (); fclose (test_fd); remove (TEST_FILE_NAME); diff --git a/symbian/MHD_config.h b/symbian/MHD_config.h @@ -240,7 +240,7 @@ #define STDC_HEADERS 1 /* Version number of package */ -#define VERSION "0.4.2" +#define VERSION "0.9.0" /* This is a Windows system */ /* #undef WINDOWS */