[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r7545 - in libmicrohttpd: . doc doc/examples
From: |
gnunet |
Subject: |
[GNUnet-SVN] r7545 - in libmicrohttpd: . doc doc/examples |
Date: |
Tue, 12 Aug 2008 13:49:42 -0600 (MDT) |
Author: grothoff
Date: 2008-08-12 13:49:42 -0600 (Tue, 12 Aug 2008)
New Revision: 7545
Added:
libmicrohttpd/doc/basicauthentication.inc
libmicrohttpd/doc/bibliography.inc
libmicrohttpd/doc/examples/
libmicrohttpd/doc/examples/basicauthentication.c
libmicrohttpd/doc/examples/hellobrowser.c
libmicrohttpd/doc/examples/logging.c
libmicrohttpd/doc/examples/responseheaders.c
libmicrohttpd/doc/examples/simplepost.c
libmicrohttpd/doc/exploringrequests.inc
libmicrohttpd/doc/hellobrowser.inc
libmicrohttpd/doc/introduction.inc
libmicrohttpd/doc/processingpost.inc
libmicrohttpd/doc/responseheaders.inc
libmicrohttpd/doc/tutorial.texi
Modified:
libmicrohttpd/AUTHORS
Log:
tutorial
Modified: libmicrohttpd/AUTHORS
===================================================================
--- libmicrohttpd/AUTHORS 2008-08-12 19:37:49 UTC (rev 7544)
+++ libmicrohttpd/AUTHORS 2008-08-12 19:49:42 UTC (rev 7545)
@@ -13,3 +13,4 @@
Documentation contributions also came from:
Marco Maggi <address@hidden>
+Sebastian Gerhardt <address@hidden>
Added: libmicrohttpd/doc/basicauthentication.inc
===================================================================
--- libmicrohttpd/doc/basicauthentication.inc (rev 0)
+++ libmicrohttpd/doc/basicauthentication.inc 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,208 @@
+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 @emph{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
address@hidden
+GET /picture.png?mypassword
address@hidden verbatim
address@hidden
+
+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.
+
+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 @emph{Basic Authentication} as described in
@emph{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 @emph{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
address@hidden structure will be pointing to a legal adress, so we take this.
+
+Not even the headers will be looked at on the first iteration.
+
address@hidden
+int AnswerToConnection(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, unsigned int *upload_data_size, void **con_cls)
+{
+ if (0 != strcmp(method, "GET")) return MHD_NO;
+ if(*con_cls==NULL) {*con_cls=connection; return MHD_YES;}
+
+ ...
+ /* else respond accordingly */
+ ...
+}
address@hidden verbatim
address@hidden
+
+Note how we lop off the connection on the first condition, 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.
+
address@hidden 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.
address@hidden 2617} describes how the server shall ask for authentication by
adding a
address@hidden response header with the name of the @emph{realm} protected.
+
+We let an extra function function do this.
address@hidden
+int AskForAuthentication(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_data(0, NULL, MHD_NO, MHD_NO);
+ 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;
+}
address@hidden verbatim
address@hidden
+
address@hidden 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
+it without checking the request's header--where the authentication is expected
to be found.
+
address@hidden Authentication in detail
+Checking @emph{RFC 2617} again, we find that the client will pack the username
and password, by
+whatever means he might have obtained them, in a line separated by a
colon---and then encodes
+them to @emph{Base64}. The actual implementation of this encoding are not
within the scope of
+this tutorial although a working function is included in the complete source
file of the example.
+
+An unencoded word describing the authentication method (here "Basic") will
precede the code
+and the resulting line is the value of a request header of the type
"Authorization".
+
+This header line thus is of interest to the function checking a connection for
a given username/password:
address@hidden
+int IsAuthenticated(struct MHD_Connection *connection,
+ const char *username, const char *password)
+{
+ const char *headervalue;
+ ...
+
+ headervalue = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND, "Authorization");
+
+ if(headervalue == NULL) return 0;
address@hidden verbatim
address@hidden
+
+where, firstly, the authentication method will be checked.
address@hidden
+const char *strbase = "Basic ";
+...
+if (strncmp(headervalue, strbase, strlen(strbase))!=0) return 0;
address@hidden verbatim
address@hidden
+
+Of course, we could decode the passed credentials in the next step and compare
them directly to
+the given strings. But as this would involve string parsing, which is more
complicated then string
+composing, it is done the other way around---the clear text credentials will
be encoded to @emph{Base64}
+and then compared against the headerline. The authentication method string
will be left out here as
+it has been checked already at this point.
address@hidden
+ char *expected_b64, *expected;
+ int authenticated;
+
+ ...
+ strcpy(expected, username);
+ strcat(expected, ":");
+ strcat(expected, password);
+
+ expected_b64 = StringToBase64(expected);
+ if(expected_b64 == NULL) return 0;
+
+ strcpy(expected, strbase);
+
+ authenticated = (strcmp(headervalue+strlen(strbase), expected_b64) == 0);
+
+ free(expected_b64);
+
+ return authenticated;
+}
address@hidden verbatim
address@hidden
+
+These two functions---together with a response function in case of positive
authentication doing little
+new---allow the rest of the callback function to be rather short.
address@hidden
+ if (!IsAuthenticated(connection, USER, PASSWORD))
+ return AskForAuthentication(connection, REALM);
+
+ return SecretPage(connection);
+}
address@hidden verbatim
address@hidden
+
+See the @code{examples} directory for the complete example file.
+
address@hidden Remarks
+For a proper server, the conditional statements leading to a return of
@code{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 @emph{internal server
error} and unexpected
+authentication methods as @emph{400 bad request}.
+
address@hidden Exercises
address@hidden @bullet
address@hidden
+Make the server respond to wrong credentials (but else correct requests) with
the recommended
address@hidden 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
address@hidden function denies connection (temporally).
+
address@hidden
+With the network utility @emph{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
+and see how both the user's name and password could be completely restored.
+
address@hidden itemize
+
+
Added: libmicrohttpd/doc/bibliography.inc
===================================================================
--- libmicrohttpd/doc/bibliography.inc (rev 0)
+++ libmicrohttpd/doc/bibliography.inc 2008-08-12 19:49:42 UTC (rev 7545)
@@ -0,0 +1,30 @@
address@hidden @bullet
address@hidden API reference
address@hidden
+The @emph{GNU libmicrohttpd} manual by Christian Grothoff 2008
address@hidden://gnunet.org/libmicrohttpd/microhttpd.html}
+
address@hidden Requests for comments
+All referenced RFCs can be found on the website of @emph{The Internet
Engineering Task Force}
address@hidden://www.ietf.org/}
+
address@hidden
address@hidden 2616}: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T.
Berners-Lee,
+"Hypertext Transfer Protocol -- HTTP/1.1", RFC 2016, January 1997.
+
address@hidden
address@hidden 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.
+
+
address@hidden Recommended readings
address@hidden
+A well--structured @emph{HTML} reference can be found on
address@hidden://www.echoecho.com/html.htm}
+
+For those readers understanding German or French, there is an excellent
document both for learning
address@hidden and for reference, whose English version unfortunately has been
discontinued.
address@hidden://de.selfhtml.org/} and @uref{http://fr.selfhtml.org/}
+
+
address@hidden itemize
Added: libmicrohttpd/doc/examples/basicauthentication.c
===================================================================
--- libmicrohttpd/doc/examples/basicauthentication.c
(rev 0)
+++ libmicrohttpd/doc/examples/basicauthentication.c 2008-08-12 19:49:42 UTC
(rev 7545)
@@ -0,0 +1,152 @@
+#include <microhttpd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define PORT 8888
+
+#define REALM "\"Maintenance\""
+#define USER "a legitimate user"
+#define PASSWORD "and his password"
+
+
+char* StringToBase64(const char *message);
+
+
+int AskForAuthentication(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_data(0, NULL, MHD_NO, MHD_NO);
+ 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;
+}
+
+int IsAuthenticated(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(headervalue == NULL) return 0;
+ if (strncmp(headervalue, strbase, strlen(strbase))!=0) return 0;
+
+ expected = malloc(strlen(username) + 1 + strlen(password) + 1);
+ if(expected == NULL) return 0;
+
+ strcpy(expected, username);
+ strcat(expected, ":");
+ strcat(expected, password);
+
+ expected_b64 = StringToBase64(expected);
+ if(expected_b64 == NULL) return 0;
+
+ strcpy(expected, strbase);
+
+ authenticated = (strcmp(headervalue+strlen(strbase), expected_b64) == 0);
+
+ free(expected_b64);
+
+ return authenticated;
+}
+
+
+int SecretPage(struct MHD_Connection *connection)
+{
+ int ret;
+ struct MHD_Response *response;
+ const char *page = "<html><body>A secret.</body></html>";
+
+ response = MHD_create_response_from_data(strlen(page), (void*)page, MHD_NO,
MHD_NO);
+ if (!response) return MHD_NO;
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+int AnswerToConnection(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, unsigned int *upload_data_size, void **con_cls)
+{
+ if (0 != strcmp(method, "GET")) return MHD_NO;
+ if(*con_cls==NULL) {*con_cls=connection; return MHD_YES;}
+
+ if (!IsAuthenticated(connection, USER, PASSWORD))
+ return AskForAuthentication(connection, REALM);
+
+ return SecretPage(connection);
+}
+
+
+int main ()
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &AnswerToConnection, NULL, MHD_OPTION_END);
+
+ if (daemon == NULL) return 1;
+
+ getchar();
+
+ MHD_stop_daemon(daemon);
+ return 0;
+}
+
+
+char* StringToBase64(const char *message)
+{
+ const char *lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned long l;
+ int i;
+ char *tmp;
+ size_t length = strlen(message);
+
+ tmp = malloc(length*2);
+ if (tmp==NULL) 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;
+}
Added: libmicrohttpd/doc/examples/hellobrowser.c
===================================================================
--- libmicrohttpd/doc/examples/hellobrowser.c (rev 0)
+++ libmicrohttpd/doc/examples/hellobrowser.c 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,34 @@
+#include <microhttpd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PORT 8888
+
+int AnswerToConnection(void *cls, struct MHD_Connection *connection, const
char *url,
+ const char *method, const char *version, const char *upload_data,
+ unsigned int *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_data (strlen (page), (void*) page,
MHD_NO, MHD_NO);
+ 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,
+ &AnswerToConnection, NULL, MHD_OPTION_END);
+ if (daemon == NULL) return 1;
+
+ getchar();
+
+ MHD_stop_daemon(daemon);
+ return 0;
+}
Added: libmicrohttpd/doc/examples/logging.c
===================================================================
--- libmicrohttpd/doc/examples/logging.c (rev 0)
+++ libmicrohttpd/doc/examples/logging.c 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,39 @@
+#include <microhttpd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PORT 8888
+
+
+int PrintOutKey(void *cls, enum MHD_ValueKind kind, const char *key, const
char *value)
+{
+ printf("%s = %s\n", key, value);
+ return MHD_YES;
+}
+
+int AnswerToConnection(void *cls, struct MHD_Connection *connection, const
char *url,
+ const char *method, const char *version, const char *upload_data,
+ unsigned int *upload_data_size, void **con_cls)
+{
+
+ printf("New request %s for %s using version %s\n", method, url, version);
+
+ MHD_get_connection_values(connection, MHD_HEADER_KIND, PrintOutKey, NULL);
+
+ return MHD_NO;
+}
+
+int main ()
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &AnswerToConnection, NULL, MHD_OPTION_END);
+ if (daemon == NULL) return 1;
+
+ getchar();
+
+ MHD_stop_daemon(daemon);
+ return 0;
+}
Added: libmicrohttpd/doc/examples/responseheaders.c
===================================================================
--- libmicrohttpd/doc/examples/responseheaders.c
(rev 0)
+++ libmicrohttpd/doc/examples/responseheaders.c 2008-08-12 19:49:42 UTC
(rev 7545)
@@ -0,0 +1,94 @@
+#include <microhttpd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#define PORT 8888
+#define FILENAME "picture.png"
+#define MIMETYPE "image/png"
+
+
+long GetFileSize(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 AnswerToConnection(void *cls, struct MHD_Connection *connection, const
char *url,
+ const char *method, const char *version, const char *upload_data,
+ unsigned int *upload_data_size, void **con_cls)
+{
+ unsigned char *buffer;
+ struct MHD_Response *response;
+ long size;
+ FILE *fp;
+ int ret=0;
+
+ if (0 != strcmp(method, "GET")) return MHD_NO;
+
+ size = GetFileSize(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)
+ {
+ 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);
+
+ ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
response);
+ MHD_destroy_response (response);
+ return MHD_YES;
+ }
+
+ response = MHD_create_response_from_data(size, (void*)buffer, MHD_YES,
MHD_NO);
+
+ 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,
+ &AnswerToConnection, NULL, MHD_OPTION_END);
+
+ if (daemon == NULL) return 1;
+
+ getchar();
+
+ MHD_stop_daemon(daemon);
+ return 0;
+}
+
Added: libmicrohttpd/doc/examples/simplepost.c
===================================================================
--- libmicrohttpd/doc/examples/simplepost.c (rev 0)
+++ libmicrohttpd/doc/examples/simplepost.c 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,157 @@
+#include <microhttpd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PORT 8888
+#define POSTBUFFERSIZE 512
+#define MAXNAMESIZE 20
+#define MAXANSWERSIZE 512
+
+#define GET 0
+#define POST 1
+
+struct ConnectionInfoStruct
+{
+ 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* greatingpage="<html><body><h1>Welcome,
%s!</center></h1></body></html>";
+
+const char* errorpage="<html><body>This doesn't seem to be
right.</body></html>";
+
+
+int SendPage(struct MHD_Connection *connection, const char* page)
+{
+ int ret;
+ struct MHD_Response *response;
+
+
+ response = MHD_create_response_from_data(strlen(page), (void*)page, MHD_NO,
MHD_NO);
+ if (!response) return MHD_NO;
+
+ ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+
+ MHD_destroy_response(response);
+
+ return ret;
+}
+
+
+int IteratePost(void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data, size_t off,
size_t size)
+{
+ struct ConnectionInfoStruct *con_info = (struct
ConnectionInfoStruct*)(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;
+}
+
+void RequestCompleted(void *cls, struct MHD_Connection *connection, void
**con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct ConnectionInfoStruct *con_info = (struct
ConnectionInfoStruct*)(*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);
+}
+
+
+int AnswerToConnection(void *cls, struct MHD_Connection *connection, const
char *url,
+ const char *method, const char *version, const char *upload_data,
+ unsigned int *upload_data_size, void **con_cls)
+{
+ if(*con_cls==NULL)
+ {
+ struct ConnectionInfoStruct *con_info;
+
+ con_info = malloc(sizeof(struct ConnectionInfoStruct));
+ if (NULL == con_info) return MHD_NO;
+
+ if (0 == strcmp(method, "POST"))
+ {
+ con_info->postprocessor = MHD_create_post_processor(connection,
POSTBUFFERSIZE,
+ IteratePost,
(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 SendPage(connection, askpage);
+ }
+
+ if (0 == strcmp(method, "POST"))
+ {
+ struct ConnectionInfoStruct *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 return SendPage(connection, con_info->answerstring);
+ }
+
+ return SendPage(connection, errorpage);
+}
+
+int main ()
+{
+ struct MHD_Daemon *daemon;
+
+
+ daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &AnswerToConnection, NULL,
MHD_OPTION_NOTIFY_COMPLETED,
+ RequestCompleted, NULL, MHD_OPTION_END);
+
+ if (NULL == daemon) return 1;
+
+ getchar();
+
+ MHD_stop_daemon(daemon);
+ return 0;
+}
Added: libmicrohttpd/doc/exploringrequests.inc
===================================================================
--- libmicrohttpd/doc/exploringrequests.inc (rev 0)
+++ libmicrohttpd/doc/exploringrequests.inc 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,104 @@
+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 @emph{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.
+
address@hidden
+int AnswerToConnection(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, unsigned int *upload_data_size, void **con_cls)
+{
+ ...
+ return MHD_NO;
+}
address@hidden verbatim
address@hidden
+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 "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.
address@hidden
+printf("New request %s for %s using version %s\n", method, url, version);
address@hidden verbatim
address@hidden
+
+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
+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.
address@hidden
+int PrintOutKey(void *cls, enum MHD_ValueKind kind, const char *key,
+ const char *value)
+{
+ printf("%s = %s\n", key, value);
+ return MHD_YES;
+}
address@hidden verbatim
address@hidden
+
+To start the iteration process that calls our new function for every key, the
line
address@hidden
+MHD_get_connection_values(connection, MHD_HEADER_KIND, PrintOutKey, NULL);
address@hidden verbatim
address@hidden
+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
+so the last parameter can be NULL.
+
+All in all, this constitutes the complete @code{logger.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.
+
address@hidden 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.
+
+Of course, no transmission can occur while the return value is still set to
@code{MHD_NO} in the
+callback function.
+
address@hidden Exercises
address@hidden @bullet
address@hidden
+By parsing the @code{url} string and delivering responses accordingly,
implement a small server for
+"virtual" files. When asked for @code{/address@hidden@}}, let the response
consist of a HTML page
+containing a link to @code{/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,
@code{MHD_HTTP_NOT_FOUND} shall be
+returned accompanied by an informative message.
+
address@hidden
+A very interesting information has still been ignored by our logger---the
client's IP address.
+Implement a callback function
address@hidden
+int OnClientConnect(void *cls,
+ const struct sockaddr *addr,socklen_t addrlen)
address@hidden verbatim
address@hidden
+that prints out the IP address in an appropriate format. You might want to use
the posix function
address@hidden 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
address@hidden call.
+
address@hidden itemize
Added: libmicrohttpd/doc/hellobrowser.inc
===================================================================
--- libmicrohttpd/doc/hellobrowser.inc (rev 0)
+++ libmicrohttpd/doc/hellobrowser.inc 2008-08-12 19:49:42 UTC (rev 7545)
@@ -0,0 +1,201 @@
+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 very 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
+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 @code{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 unexciting includes and the definition of the port which our server
should listen on
address@hidden
+#include <microhttpd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
address@hidden verbatim
address@hidden
+the desired behaviour of our server when HTTP request arrive have 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 @emph{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,
address@hidden MHD_Connection *connection}, for the minimalistic functionality
we want to archive at the moment.
+
+This parameter is set by the @emph{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
address@hidden
+int AnswerToConnection(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, unsigned int *upload_data_size, void **con_cls)
+{
+ const char *page = "<html><body>Hello, browser!</body></html>";
address@hidden verbatim
address@hidden
+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 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
address@hidden function. The last two parameters basically tell @emph{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 @emph{constant} string won't change
anyway.
+
address@hidden
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_data(strlen(page),
+ (void*)page, MHD_NO, MHD_NO);
address@hidden verbatim
address@hidden
+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 @emph{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 @code{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.
+
address@hidden
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+ return ret;
+}
address@hidden verbatim
address@hidden
+With the primary task of our server implemented, we can start the actual
server daemon which will listen
+on @code{PORT} for connections. This is done in the main function.
address@hidden
+int main ()
+{
+ struct MHD_Daemon *d;
+
+ d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &AnswerToConnection, NULL, MHD_OPTION_END);
+ if (d == NULL) return 1;
address@hidden verbatim
address@hidden
+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.
+
+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
+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.
address@hidden
+ getchar();
+
+ MHD_stop_daemon(d);
+ return 0;
+}
address@hidden verbatim
address@hidden
+The first example is now complete.
+
+Compile it with
address@hidden
+cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
+ -L$PATH_TO_LIBMHD_INCLUDES -static -lmicrohttpd -pthread
address@hidden 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
+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.
+
address@hidden 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 @code{method} equals the string "GET"
and returning a
address@hidden 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.
+
+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
+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
+function could be set to @code{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 @code{page} string, which is a constant
@emph{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 @emph{MHD} free the memory
@code{page} points
+to itself when it is not longer needed or, alternatively, have the library to
make and manage
+its own copy of it.
+
address@hidden Exercises
address@hidden @bullet
address@hidden
+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
address@hidden
+GET /dontcare HTTP1.1
+Host: itsme
+<enter>
address@hidden verbatim
address@hidden
+and see what the server returns to you.
+
+
address@hidden
+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
@emph{MHD} intervenes
+and an altered response is sent? Make sure you read about the status codes in
the @emph{RFC}.
+
+
address@hidden
+Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the
daemon in @code{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?
+
+
address@hidden
+Let the main function take a string as the first command line argument and
pass @code{argv[1]} to
+the @code{MHD_start_daemon} function as the sixth parameter. The address of
this string will be
+passed to the callback function via the @code{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.
+
+
address@hidden
address@hidden:} 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
+every time after satisfying the request.
+
address@hidden itemize
Added: libmicrohttpd/doc/introduction.inc
===================================================================
--- libmicrohttpd/doc/introduction.inc (rev 0)
+++ libmicrohttpd/doc/introduction.inc 2008-08-12 19:49:42 UTC (rev 7545)
@@ -0,0 +1,19 @@
+This tutorial is aimed at developers who want to learn how they can add HTTP
serving
+capabilities to their applications with the @emph{GNU libmicrohttpd} library,
+abbreviated @emph{MHD}, and who do not know how to start. It tries to help
these
+developers to implement common basic HTTP serving tasks by discussing
executable
+sample programs implementing different features.
+
+The text is supposed to be a supplement to the API reference manual of
address@hidden 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. In the same sense, the tutorial seeks to
+encourage the use of the @emph{RFCs}, which document the conventions the
Internet
+is built upon.
+
address@hidden libmicrohttpd} is assumed to be already installed and it has been
+written with respect to version @value{VERSION}. As the library is still in
its
+beta stages, later versions may show different behaviour. 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.
Added: libmicrohttpd/doc/processingpost.inc
===================================================================
--- libmicrohttpd/doc/processingpost.inc (rev 0)
+++ libmicrohttpd/doc/processingpost.inc 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,225 @@
+The previous chapters already have demonstrated a variety of possibilities to
send information
+to the HTTP server, but it is not recommended that the @emph{GET} method is
used to alter the way
+the server operates. To induce changes on the server, the @emph{POST} method
is preferred over
+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
+between different functions for the same connection. Furthermore, the reader
should be able to extend
+it easily.
+
address@hidden GET request
+When the first @emph{GET} request arrives, the server shall respond with a
HTML page containing an
+edit field for the name.
+
address@hidden
+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>";
address@hidden verbatim
address@hidden
+
+The @code{action} entry is the @emph{URI} to be called by the browser when
posting, and the
address@hidden 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 @emph{GET} and @emph{POST} requests:
+
address@hidden
+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>";
address@hidden verbatim
address@hidden
+
+Whenever we need to send a page, we use an extra function
address@hidden SendPage(struct MHD_Connection *connection, const char* page)}
+for this, which does not contain anything new and whose implementation is
therefore left out here.
+
+
address@hidden 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 @emph{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
address@hidden 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.
+
address@hidden
+struct ConnectionInfoStruct
+{
+ int connectiontype;
+ char *answerstring;
+ struct MHD_PostProcessor *postprocessor;
+};
address@hidden verbatim
address@hidden
+
+With these information available to the iterator function, it is able to
fulfill its task.
+Once it has composed the greeting string, it returns @code{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 @code{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 @code{key}
+came in. But in this example, the name is assumed to fit entirely inside one
single packet.
+
address@hidden
+int IteratePost(void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data, size_t off,
size_t size)
+{
+ struct ConnectionInfoStruct *con_info = (struct
ConnectionInfoStruct*)(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;
+}
address@hidden verbatim
address@hidden
+
+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 @emph{POST} requests.
+
address@hidden
+void RequestCompleted(void *cls, struct MHD_Connection *connection, void
**con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct ConnectionInfoStruct *con_info = (struct
ConnectionInfoStruct*)(*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);
+}
address@hidden verbatim
address@hidden
+
address@hidden libmicrohttpd} is informed that it shall call the above function
when the daemon is started
+in the main function.
+
address@hidden
+...
+daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
+ &AnswerToConnection, NULL,
MHD_OPTION_NOTIFY_COMPLETED,
+ RequestCompleted, NULL, MHD_OPTION_END);
+...
address@hidden verbatim
address@hidden
+
address@hidden 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
address@hidden structure, which will store all necessary information for later
+iterations and other functions.
+
address@hidden
+int AnswerToConnection(void *cls, struct MHD_Connection *connection, const
char *url,
+ const char *method, const char *version, const char *upload_data,
+ unsigned int *upload_data_size, void **con_cls)
+{
+ if(*con_cls==NULL)
+ {
+ struct ConnectionInfoStruct *con_info;
+
+ con_info = malloc(sizeof(struct ConnectionInfoStruct));
+ if (NULL == con_info) return MHD_NO;
address@hidden verbatim
address@hidden
+
+If the new request is a @emph{POST}, the postprocessor must be created now. In
addition, the type
+of the request is stored for convenience.
address@hidden
+ if (0 == strcmp(method, "POST"))
+ {
+ con_info->postprocessor = MHD_create_post_processor(connection,
POSTBUFFERSIZE,
+ IteratePost,
(void*)con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free(con_info);
+ return MHD_NO;
+ }
+
+ con_info->connectiontype = POST;
+ } else con_info->connectiontype = GET;
address@hidden verbatim
address@hidden
+
+The address of our structure will both serve as the indicator for successive
iterations and to remember
+the particular details about the connection.
address@hidden
+ *con_cls = (void*)con_info;
+ return MHD_YES;
+ }
address@hidden verbatim
address@hidden
+
+The rest of the function will not be executed on the first iteration. A
@emph{GET} request is easily
+satisfied by sending the question form.
address@hidden
+ if (0 == strcmp(method, "GET"))
+ {
+ return SendPage(connection, askpage);
+ }
address@hidden verbatim
address@hidden
+
+In case of @emph{POST}, we invoke the post processor for as long as data keeps
incoming, setting
address@hidden to zero in order to indicate that we have processed---or at
least have
+considered---all of it.
address@hidden
+ if (0 == strcmp(method, "POST"))
+ {
+ struct ConnectionInfoStruct *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 return SendPage(connection, con_info->answerstring);
+ }
address@hidden verbatim
address@hidden
+
+If they are neither @emph{GET} nor @emph{POST} requests, the error page is
returned finally.
address@hidden
+ return SendPage(connection, errorpage);
+}
address@hidden verbatim
address@hidden
+
+These were the important parts of the program @code{simplepost.c}.
Added: libmicrohttpd/doc/responseheaders.inc
===================================================================
--- libmicrohttpd/doc/responseheaders.inc (rev 0)
+++ libmicrohttpd/doc/responseheaders.inc 2008-08-12 19:49:42 UTC (rev
7545)
@@ -0,0 +1,171 @@
+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 @emph{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 hadn't 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 @emph{PNG} will be sent to
the client
+and labeled accordingly with @code{image/png}.
+Once again, we can base the new example on the @code{hellobrowser} program.
+
address@hidden
+#define FILENAME "picture.png"
+#define MIMETYPE "image/png"
+
+int AnswerToConnection(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, unsigned int *upload_data_size, void **con_cls)
+{
+ struct MHD_Response *response;
+ int ret=0;
address@hidden verbatim
address@hidden
+
+We want the program to load the graphics file into memory:
address@hidden
+long size;
+ FILE *fp;
+ int ret=0;
+
+ if (0 != strcmp(method, "GET")) return MHD_NO;
+
+ size = GetFileSize(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);
+ }
address@hidden verbatim
address@hidden
+
+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
+sider side and if so, the client should be informed with
@code{MHD_HTTP_INTERNAL_SERVER_ERROR}.
+
address@hidden
+ if (!ret)
+ {
+ 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);
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+
+ return MHD_YES;
+ }
address@hidden verbatim
address@hidden
+
+Note that we nevertheless have to create an 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 that contains the
buffer filled with the
+file's content.
+
address@hidden
+response = MHD_create_response_from_data(size, (void*)buffer, MHD_YES, MHD_NO);
address@hidden verbatim
address@hidden
+
+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.
+
+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:
address@hidden
+MHD_add_response_header(response, "Content-Type", MIMETYPE);
address@hidden verbatim
address@hidden
+We do not have to append a colon expected by the protocol hehind the first
address@hidden libhttpdmicro} will take care of this.
+
+The function finishes with the well-known lines
address@hidden
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+ return ret;
+}
address@hidden verbatim
address@hidden
+
+The complete program @code{responseheaders.c} is in the @code{examples}
section as usual.
+Find a @emph{PNG} file you like and save it to the directory the example is
run from under the name
address@hidden You should find the image displayed on your browser if
everything worked well.
+
address@hidden Remarks
+The include file of the @emph{MHD} library comes with the header types
mentioned in @emph{RFC 2616}
+already defined as macros. Thus, we could have written
@code{MHD_HTTP_HEADER_CONTENT_TYPE} instead
+of @code{"Content-Type"} as well. However, one is not limited to these
standard headers and could
+add custom response headers without violated 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 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.
+
address@hidden Exercises
address@hidden @bullet
+
address@hidden
+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
+instantly, postpone the queuing in the callback with the @code{sleep} function
for 30 seconds
address@hidden the file @code{/big.png} is requested (but deliver the same as
above). A request for
address@hidden/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 @code{MHD_USE_THREAD_PER_CONNECTION}
when the daemon is
+started and try again.
+
+
address@hidden
+Did you succeed in implementing the clock exercise yet? This time, let the
server save the
+program's start time @code{t} and implement a response simulating a countdown
that reaches 0 at
address@hidden 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
+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 @emph{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
address@hidden<table>} consisting of one row and @code{n} columns whose fields
contain small images of either
+a red or a green light.
+
address@hidden itemize
Added: libmicrohttpd/doc/tutorial.texi
===================================================================
--- libmicrohttpd/doc/tutorial.texi (rev 0)
+++ libmicrohttpd/doc/tutorial.texi 2008-08-12 19:49:42 UTC (rev 7545)
@@ -0,0 +1,119 @@
+\input texinfo @c -*-texinfo-*-
address@hidden
address@hidden libmicrohttpdtutorial
address@hidden A tutorial for GNU libmicrohttpd
address@hidden
+
address@hidden VERSION 0.3.1 beta
+
address@hidden
address@hidden A Tutorial for GNU libmicrohttpd
address@hidden written for version @value{VERSION}
address@hidden Sebastian Gerhardt (@email{sebgerhardt@@gmx.net})
address@hidden
address@hidden 0pt plus 1filll
address@hidden titlepage
+
address@hidden
+Copyright (c) 2008 Sebastian Gerhardt.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2
+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".
address@hidden verbatim
+
address@hidden
+
address@hidden
address@hidden Top
address@hidden Top
address@hidden ifnottex
+
address@hidden
+* Introduction::
+* Hello browser example::
+* Exploring requests::
+* Response headers::
+* A basic authentication::
+* Processing post data::
+* Bibliography::
+* License text::
+* Example programs::
address@hidden menu
+
address@hidden Introduction
address@hidden Introduction
address@hidden introduction.inc
+
address@hidden Hello browser example
address@hidden Hello browser example
address@hidden hellobrowser.inc
+
address@hidden Exploring requests
address@hidden Exploring requests
address@hidden exploringrequests.inc
+
address@hidden Response headers
address@hidden Response headers
address@hidden responseheaders.inc
+
address@hidden A basic authentication
address@hidden A basic authentication
address@hidden basicauthentication.inc
+
address@hidden Processing post data
address@hidden Processing post data
address@hidden processingpost.inc
+
+
address@hidden Bibliography
address@hidden Bibliography
address@hidden bibliography.inc
+
address@hidden License text
address@hidden GNU Free Documentation License
address@hidden fdl-1.2.texi
+
address@hidden Example programs
address@hidden Example programs
address@hidden
+* hellobrowser.c::
+* logging.c::
+* responseheaders.c::
+* basicauthentication.c::
+* simplepost.c::
address@hidden menu
+
address@hidden hellobrowser.c
address@hidden hellobrowser.c
address@hidden
address@hidden examples/hellobrowser.c
address@hidden smalldisplay
+
address@hidden logging.c
address@hidden logging.c
address@hidden
address@hidden examples/logging.c
address@hidden smalldisplay
+
address@hidden responseheaders.c
address@hidden responseheaders.c
address@hidden
address@hidden examples/responseheaders.c
address@hidden smalldisplay
+
address@hidden basicauthentication.c
address@hidden basicauthentication.c
address@hidden
address@hidden examples/basicauthentication.c
address@hidden smalldisplay
+
address@hidden simplepost.c
address@hidden simplepost.c
address@hidden
address@hidden examples/simplepost.c
address@hidden smalldisplay
+
address@hidden
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r7545 - in libmicrohttpd: . doc doc/examples,
gnunet <=