aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2008-08-12 19:49:42 +0000
committerChristian Grothoff <christian@grothoff.org>2008-08-12 19:49:42 +0000
commitc5969a735ab86f8c461b9188853e7bf4dd42d588 (patch)
tree8065294f958340462b0ab3239daa4339332dc89c /doc
parent53c25f932f5bd45c26af675b4c89d71eb99a9b8c (diff)
downloadlibmicrohttpd-c5969a735ab86f8c461b9188853e7bf4dd42d588.tar.gz
libmicrohttpd-c5969a735ab86f8c461b9188853e7bf4dd42d588.zip
tutorial
Diffstat (limited to 'doc')
-rw-r--r--doc/basicauthentication.inc208
-rw-r--r--doc/bibliography.inc30
-rw-r--r--doc/examples/basicauthentication.c152
-rw-r--r--doc/examples/hellobrowser.c34
-rw-r--r--doc/examples/logging.c39
-rw-r--r--doc/examples/responseheaders.c94
-rw-r--r--doc/examples/simplepost.c157
-rw-r--r--doc/exploringrequests.inc104
-rw-r--r--doc/hellobrowser.inc201
-rw-r--r--doc/introduction.inc19
-rw-r--r--doc/processingpost.inc225
-rw-r--r--doc/responseheaders.inc171
-rw-r--r--doc/tutorial.texi119
13 files changed, 1553 insertions, 0 deletions
diff --git a/doc/basicauthentication.inc b/doc/basicauthentication.inc
new file mode 100644
index 00000000..6daf0756
--- /dev/null
+++ b/doc/basicauthentication.inc
@@ -0,0 +1,208 @@
1With the small exception of IP address based access control,
2requests from all connecting clients where served equally until now.
3This chapter discusses a first method of client's authentication and
4its limits.
5
6A very simple approach feasible with the means already discussed would
7be to expect the password in the @emph{URI} string before granting access to
8the secured areas. The password could be separated from the actual resource identifier
9by a certain character, thus the request line might look like
10@verbatim
11GET /picture.png?mypassword
12@end verbatim
13@noindent
14
15In a situation, where the client is customized enough and the connection occurs
16through secured lines (e.g., a embedded device directly attached to another via wire),
17this can be a reasonable choice.
18
19But when it is assumed that the user connecting does so with an ordinary Internet browser,
20this implementation brings some problems about. For example, the URI including the password
21stays in the address field or at least in the history of the browser for anybody near enough to see.
22It will also be inconvenient to add the password manually to any new URI when the browser does
23not know how to compose this automatically.
24
25At least the convenience issue can be addressed by employing the simplest built-in password
26facilities of HTTP compliant browsers, hence we want to start there. It will however turn out
27to have still severe weaknesses in terms of security which need consideration.
28
29Before we will start implementing @emph{Basic Authentication} as described in @emph{RFC 2617},
30we should finally abandon the bad practice of responding every request the first time our callback
31is called for a given connection. This is becoming more important now because the client and
32the server will have to talk in a more bi-directional way than before to
33
34But how can we tell whether the callback has been called before for the particular connection?
35Initially, the pointer this parameter references is set by @emph{MHD} in the callback. But it will
36also be "remembered" on the next call (for the same connection).
37Thus, we will generate no response until the parameter is non-null---implying the callback was
38called before at least once. We do not need to share information between different calls of the callback,
39so we can set the parameter to any adress that is assured to be not null. The pointer to the
40@code{connection} structure will be pointing to a legal adress, so we take this.
41
42Not even the headers will be looked at on the first iteration.
43
44@verbatim
45int AnswerToConnection(void *cls, struct MHD_Connection *connection,
46 const char *url, const char *method, const char *version,
47 const char *upload_data, unsigned int *upload_data_size, void **con_cls)
48{
49 if (0 != strcmp(method, "GET")) return MHD_NO;
50 if(*con_cls==NULL) {*con_cls=connection; return MHD_YES;}
51
52 ...
53 /* else respond accordingly */
54 ...
55}
56@end verbatim
57@noindent
58
59Note how we lop off the connection on the first condition, but return asking for more on
60the other one with @code{MHD_YES}.
61With the framework improved, we can proceed to implement the actual authentication process.
62
63@heading Request for authentication
64
65Let us assume we had only files not intended to be handed out without the correct username/password,
66so every "GET" request will be challenged.
67@emph{RFC 2617} describes how the server shall ask for authentication by adding a
68@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected.
69
70We let an extra function function do this.
71@verbatim
72int AskForAuthentication(struct MHD_Connection *connection, const char *realm)
73{
74 int ret;
75 struct MHD_Response *response;
76 char *headervalue;
77 const char *strbase = "Basic realm=";
78
79 response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
80 if (!response) return MHD_NO;
81
82 headervalue = malloc( strlen(strbase) + strlen(realm) + 1);
83 if (!headervalue) return MHD_NO;
84
85 strcpy(headervalue, strbase);
86 strcat(headervalue, realm);
87
88 ret = MHD_add_response_header(response, "WWW-Authenticate", headervalue);
89 free(headervalue);
90 if (!ret) {MHD_destroy_response (response); return MHD_NO;}
91
92 ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
93
94 MHD_destroy_response (response);
95
96 return ret;
97}
98@end verbatim
99@noindent
100
101@code{#define} the realm name according to your own taste, e.g. "Maintenance" or "Area51" but
102it will need to have extra quotes.
103
104But the client may send the authentication right away, so it would be wrong to ask for
105it without checking the request's header--where the authentication is expected to be found.
106
107@heading Authentication in detail
108Checking @emph{RFC 2617} again, we find that the client will pack the username and password, by
109whatever means he might have obtained them, in a line separated by a colon---and then encodes
110them to @emph{Base64}. The actual implementation of this encoding are not within the scope of
111this tutorial although a working function is included in the complete source file of the example.
112
113An unencoded word describing the authentication method (here "Basic") will precede the code
114and the resulting line is the value of a request header of the type "Authorization".
115
116This header line thus is of interest to the function checking a connection for a given username/password:
117@verbatim
118int IsAuthenticated(struct MHD_Connection *connection,
119 const char *username, const char *password)
120{
121 const char *headervalue;
122 ...
123
124 headervalue = MHD_lookup_connection_value (connection,
125 MHD_HEADER_KIND, "Authorization");
126
127 if(headervalue == NULL) return 0;
128@end verbatim
129@noindent
130
131where, firstly, the authentication method will be checked.
132@verbatim
133const char *strbase = "Basic ";
134...
135if (strncmp(headervalue, strbase, strlen(strbase))!=0) return 0;
136@end verbatim
137@noindent
138
139Of course, we could decode the passed credentials in the next step and compare them directly to
140the given strings. But as this would involve string parsing, which is more complicated then string
141composing, it is done the other way around---the clear text credentials will be encoded to @emph{Base64}
142and then compared against the headerline. The authentication method string will be left out here as
143it has been checked already at this point.
144@verbatim
145 char *expected_b64, *expected;
146 int authenticated;
147
148 ...
149 strcpy(expected, username);
150 strcat(expected, ":");
151 strcat(expected, password);
152
153 expected_b64 = StringToBase64(expected);
154 if(expected_b64 == NULL) return 0;
155
156 strcpy(expected, strbase);
157
158 authenticated = (strcmp(headervalue+strlen(strbase), expected_b64) == 0);
159
160 free(expected_b64);
161
162 return authenticated;
163}
164@end verbatim
165@noindent
166
167These two functions---together with a response function in case of positive authentication doing little
168new---allow the rest of the callback function to be rather short.
169@verbatim
170 if (!IsAuthenticated(connection, USER, PASSWORD))
171 return AskForAuthentication(connection, REALM);
172
173 return SecretPage(connection);
174}
175@end verbatim
176@noindent
177
178See the @code{examples} directory for the complete example file.
179
180@heading Remarks
181For a proper server, the conditional statements leading to a return of @code{MHD_NO} should yield a
182response with a more precise status code instead of silently closing the connection. For example,
183failures of memory allocation are best reported as @emph{internal server error} and unexpected
184authentication methods as @emph{400 bad request}.
185
186@heading Exercises
187@itemize @bullet
188@item
189Make the server respond to wrong credentials (but else correct requests) with the recommended
190@emph{401 unauthorized} status code. If the client still does not authenticate correctly within the
191same connection, close it and store the client's IP address for a certain time. (It is OK to check for
192expiration not until the main thread wakes up again on the next connection.) If the client fails
193authenticating three times during this period, add it to another list whose entries the
194@code{AcceptPolicyCallback} function denies connection (temporally).
195
196@item
197With the network utility @emph{netcat} connect and log the response of a "GET" request as you
198did in the exercise of the first example, this time to a file. Now stop the server and let @emph{netcat}
199listen on the same port the server used to listen on and have it fake being the proper server by giving
200the file's content as the response (e.g. @code{cat log | nc -l -p 8888}). Pretending to think your were
201connecting to the actual server, browse to the eavesdropper and give the correct credentials.
202
203Copy and paste the encoded string you see in netcat's output to some of the Base64 decode tools available online
204and see how both the user's name and password could be completely restored.
205
206@end itemize
207
208
diff --git a/doc/bibliography.inc b/doc/bibliography.inc
new file mode 100644
index 00000000..547028f5
--- /dev/null
+++ b/doc/bibliography.inc
@@ -0,0 +1,30 @@
1@itemize @bullet
2@heading API reference
3@item
4The @emph{GNU libmicrohttpd} manual by Christian Grothoff 2008
5@uref{http://gnunet.org/libmicrohttpd/microhttpd.html}
6
7@heading Requests for comments
8All referenced RFCs can be found on the website of @emph{The Internet Engineering Task Force}
9@uref{http://www.ietf.org/}
10
11@item
12@emph{RFC 2616}: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T. Berners-Lee,
13"Hypertext Transfer Protocol -- HTTP/1.1", RFC 2016, January 1997.
14
15@item
16@emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P.,
17Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Authentication", RFC 2617, June 1999.
18
19
20@heading Recommended readings
21@item
22A well--structured @emph{HTML} reference can be found on
23@uref{http://www.echoecho.com/html.htm}
24
25For those readers understanding German or French, there is an excellent document both for learning
26@emph{HTML} and for reference, whose English version unfortunately has been discontinued.
27@uref{http://de.selfhtml.org/} and @uref{http://fr.selfhtml.org/}
28
29
30@end itemize
diff --git a/doc/examples/basicauthentication.c b/doc/examples/basicauthentication.c
new file mode 100644
index 00000000..eecaaf05
--- /dev/null
+++ b/doc/examples/basicauthentication.c
@@ -0,0 +1,152 @@
1#include <microhttpd.h>
2#include <string.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <time.h>
6
7#define PORT 8888
8
9#define REALM "\"Maintenance\""
10#define USER "a legitimate user"
11#define PASSWORD "and his password"
12
13
14char* StringToBase64(const char *message);
15
16
17int AskForAuthentication(struct MHD_Connection *connection, const char *realm)
18{
19 int ret;
20 struct MHD_Response *response;
21 char *headervalue;
22 const char *strbase = "Basic realm=";
23
24 response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
25 if (!response) return MHD_NO;
26
27 headervalue = malloc( strlen(strbase) + strlen(realm) + 1);
28 if (!headervalue) return MHD_NO;
29
30 strcpy(headervalue, strbase);
31 strcat(headervalue, realm);
32
33 ret = MHD_add_response_header(response, "WWW-Authenticate", headervalue);
34 free(headervalue);
35 if (!ret) {MHD_destroy_response (response); return MHD_NO;}
36
37 ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
38
39 MHD_destroy_response (response);
40
41 return ret;
42}
43
44int IsAuthenticated(struct MHD_Connection *connection, const char *username,
45 const char *password)
46{
47 const char *headervalue;
48 char *expected_b64, *expected;
49 const char *strbase = "Basic ";
50 int authenticated;
51
52 headervalue = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Authorization");
53 if(headervalue == NULL) return 0;
54 if (strncmp(headervalue, strbase, strlen(strbase))!=0) return 0;
55
56 expected = malloc(strlen(username) + 1 + strlen(password) + 1);
57 if(expected == NULL) return 0;
58
59 strcpy(expected, username);
60 strcat(expected, ":");
61 strcat(expected, password);
62
63 expected_b64 = StringToBase64(expected);
64 if(expected_b64 == NULL) return 0;
65
66 strcpy(expected, strbase);
67
68 authenticated = (strcmp(headervalue+strlen(strbase), expected_b64) == 0);
69
70 free(expected_b64);
71
72 return authenticated;
73}
74
75
76int SecretPage(struct MHD_Connection *connection)
77{
78 int ret;
79 struct MHD_Response *response;
80 const char *page = "<html><body>A secret.</body></html>";
81
82 response = MHD_create_response_from_data(strlen(page), (void*)page, MHD_NO, MHD_NO);
83 if (!response) return MHD_NO;
84
85 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
86
87 MHD_destroy_response (response);
88
89 return ret;
90}
91
92
93int AnswerToConnection(void *cls, struct MHD_Connection *connection,
94 const char *url, const char *method, const char *version,
95 const char *upload_data, unsigned int *upload_data_size, void **con_cls)
96{
97 if (0 != strcmp(method, "GET")) return MHD_NO;
98 if(*con_cls==NULL) {*con_cls=connection; return MHD_YES;}
99
100 if (!IsAuthenticated(connection, USER, PASSWORD))
101 return AskForAuthentication(connection, REALM);
102
103 return SecretPage(connection);
104}
105
106
107int main ()
108{
109 struct MHD_Daemon *daemon;
110
111 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
112 &AnswerToConnection, NULL, MHD_OPTION_END);
113
114 if (daemon == NULL) return 1;
115
116 getchar();
117
118 MHD_stop_daemon(daemon);
119 return 0;
120}
121
122
123char* StringToBase64(const char *message)
124{
125 const char *lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
126 unsigned long l;
127 int i;
128 char *tmp;
129 size_t length = strlen(message);
130
131 tmp = malloc(length*2);
132 if (tmp==NULL) return tmp;
133 tmp[0]=0;
134
135 for(i=0; i<length; i+=3)
136 {
137 l = ( ((unsigned long)message[i])<<16 ) |
138 (((i+1) < length) ? (((unsigned long)message[i+1])<<8 ) : 0 ) |
139 (((i+2) < length) ? ( (unsigned long)message[i+2] ) : 0 );
140
141
142 strncat(tmp, &lookup[(l>>18) & 0x3F], 1);
143 strncat(tmp, &lookup[(l>>12) & 0x3F], 1);
144
145 if (i+1 < length) strncat(tmp, &lookup[(l>> 6) & 0x3F], 1);
146 if (i+2 < length) strncat(tmp, &lookup[(l ) & 0x3F], 1);
147 }
148
149 if (length%3) strncat(tmp, "===", 3-length%3) ;
150
151 return tmp;
152}
diff --git a/doc/examples/hellobrowser.c b/doc/examples/hellobrowser.c
new file mode 100644
index 00000000..a5aa64d6
--- /dev/null
+++ b/doc/examples/hellobrowser.c
@@ -0,0 +1,34 @@
1#include <microhttpd.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdio.h>
5
6#define PORT 8888
7
8int AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url,
9 const char *method, const char *version, const char *upload_data,
10 unsigned int *upload_data_size, void **con_cls)
11{
12 const char *page = "<html><body>Hello, browser!</body></html>";
13 struct MHD_Response *response;
14 int ret;
15
16 response = MHD_create_response_from_data (strlen (page), (void*) page, MHD_NO, MHD_NO);
17 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
18 MHD_destroy_response (response);
19 return ret;
20}
21
22int main ()
23{
24 struct MHD_Daemon *daemon;
25
26 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
27 &AnswerToConnection, NULL, MHD_OPTION_END);
28 if (daemon == NULL) return 1;
29
30 getchar();
31
32 MHD_stop_daemon(daemon);
33 return 0;
34}
diff --git a/doc/examples/logging.c b/doc/examples/logging.c
new file mode 100644
index 00000000..4fc45d12
--- /dev/null
+++ b/doc/examples/logging.c
@@ -0,0 +1,39 @@
1#include <microhttpd.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdio.h>
5
6#define PORT 8888
7
8
9int PrintOutKey(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
10{
11 printf("%s = %s\n", key, value);
12 return MHD_YES;
13}
14
15int AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url,
16 const char *method, const char *version, const char *upload_data,
17 unsigned int *upload_data_size, void **con_cls)
18{
19
20 printf("New request %s for %s using version %s\n", method, url, version);
21
22 MHD_get_connection_values(connection, MHD_HEADER_KIND, PrintOutKey, NULL);
23
24 return MHD_NO;
25}
26
27int main ()
28{
29 struct MHD_Daemon *daemon;
30
31 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
32 &AnswerToConnection, NULL, MHD_OPTION_END);
33 if (daemon == NULL) return 1;
34
35 getchar();
36
37 MHD_stop_daemon(daemon);
38 return 0;
39}
diff --git a/doc/examples/responseheaders.c b/doc/examples/responseheaders.c
new file mode 100644
index 00000000..73d545a7
--- /dev/null
+++ b/doc/examples/responseheaders.c
@@ -0,0 +1,94 @@
1#include <microhttpd.h>
2#include <string.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <time.h>
6
7#define PORT 8888
8#define FILENAME "picture.png"
9#define MIMETYPE "image/png"
10
11
12long GetFileSize(const char *filename)
13{
14 FILE *fp;
15
16 fp = fopen(filename, "rb");
17 if (fp)
18 {
19 long size;
20
21 if ( (0!=fseek(fp, 0, SEEK_END)) || (-1==(size=ftell(fp))) )
22 size = 0;
23
24 fclose(fp);
25 return size;
26 } else return 0;
27}
28
29
30int AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url,
31 const char *method, const char *version, const char *upload_data,
32 unsigned int *upload_data_size, void **con_cls)
33{
34 unsigned char *buffer;
35 struct MHD_Response *response;
36 long size;
37 FILE *fp;
38 int ret=0;
39
40 if (0 != strcmp(method, "GET")) return MHD_NO;
41
42 size = GetFileSize(FILENAME);
43 if (size != 0)
44 {
45 fp = fopen(FILENAME, "rb");
46 if (fp)
47 {
48 buffer = malloc(size);
49 if (buffer)
50 if (size == fread(buffer, 1, size, fp)) ret=1;
51 }
52
53 fclose(fp);
54 }
55
56 if (!ret)
57 {
58 const char *errorstr = "<html><body>An internal server error has occured!\
59 </body></html>";
60
61 if (buffer) free(buffer);
62 response = MHD_create_response_from_data(strlen(errorstr), (void*)errorstr,
63 MHD_NO, MHD_NO);
64
65 ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
66 MHD_destroy_response (response);
67 return MHD_YES;
68 }
69
70 response = MHD_create_response_from_data(size, (void*)buffer, MHD_YES, MHD_NO);
71
72 MHD_add_response_header(response, "Content-Type", MIMETYPE);
73
74 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
75 MHD_destroy_response (response);
76 return ret;
77}
78
79
80int main ()
81{
82 struct MHD_Daemon *daemon;
83
84 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
85 &AnswerToConnection, NULL, MHD_OPTION_END);
86
87 if (daemon == NULL) return 1;
88
89 getchar();
90
91 MHD_stop_daemon(daemon);
92 return 0;
93}
94
diff --git a/doc/examples/simplepost.c b/doc/examples/simplepost.c
new file mode 100644
index 00000000..935fe1e3
--- /dev/null
+++ b/doc/examples/simplepost.c
@@ -0,0 +1,157 @@
1#include <microhttpd.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdio.h>
5
6#define PORT 8888
7#define POSTBUFFERSIZE 512
8#define MAXNAMESIZE 20
9#define MAXANSWERSIZE 512
10
11#define GET 0
12#define POST 1
13
14struct ConnectionInfoStruct
15{
16 int connectiontype;
17 char *answerstring;
18 struct MHD_PostProcessor *postprocessor;
19};
20
21const char* askpage="<html><body>\
22 What's your name, Sir?<br>\
23 <form action=\"/namepost\" method=\"post\">\
24 <input name=\"name\" type=\"text\"\
25 <input type=\"submit\" value=\" Send \"></form>\
26 </body></html>";
27
28const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>";
29
30const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
31
32
33int SendPage(struct MHD_Connection *connection, const char* page)
34{
35 int ret;
36 struct MHD_Response *response;
37
38
39 response = MHD_create_response_from_data(strlen(page), (void*)page, MHD_NO, MHD_NO);
40 if (!response) return MHD_NO;
41
42 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
43
44 MHD_destroy_response(response);
45
46 return ret;
47}
48
49
50int IteratePost(void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
51 const char *filename, const char *content_type,
52 const char *transfer_encoding, const char *data, size_t off, size_t size)
53{
54 struct ConnectionInfoStruct *con_info = (struct ConnectionInfoStruct*)(coninfo_cls);
55
56
57 if (0 == strcmp(key, "name"))
58 {
59 if ((size>0) && (size<=MAXNAMESIZE))
60 {
61 char *answerstring;
62 answerstring = malloc(MAXANSWERSIZE);
63 if (!answerstring) return MHD_NO;
64
65 snprintf(answerstring, MAXANSWERSIZE, greatingpage, data);
66 con_info->answerstring = answerstring;
67 } else con_info->answerstring=NULL;
68
69 return MHD_NO;
70 }
71
72 return MHD_YES;
73}
74
75void RequestCompleted(void *cls, struct MHD_Connection *connection, void **con_cls,
76 enum MHD_RequestTerminationCode toe)
77{
78 struct ConnectionInfoStruct *con_info = (struct ConnectionInfoStruct*)(*con_cls);
79
80
81 if (NULL == con_info) return;
82
83 if (con_info->connectiontype == POST)
84 {
85 MHD_destroy_post_processor(con_info->postprocessor);
86 if (con_info->answerstring) free(con_info->answerstring);
87 }
88
89 free(con_info);
90}
91
92
93int AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url,
94 const char *method, const char *version, const char *upload_data,
95 unsigned int *upload_data_size, void **con_cls)
96{
97 if(*con_cls==NULL)
98 {
99 struct ConnectionInfoStruct *con_info;
100
101 con_info = malloc(sizeof(struct ConnectionInfoStruct));
102 if (NULL == con_info) return MHD_NO;
103
104 if (0 == strcmp(method, "POST"))
105 {
106 con_info->postprocessor = MHD_create_post_processor(connection, POSTBUFFERSIZE,
107 IteratePost, (void*)con_info);
108
109 if (NULL == con_info->postprocessor)
110 {
111 free(con_info);
112 return MHD_NO;
113 }
114
115 con_info->connectiontype = POST;
116 } else con_info->connectiontype = GET;
117
118 *con_cls = (void*)con_info;
119 return MHD_YES;
120 }
121
122 if (0 == strcmp(method, "GET"))
123 {
124 return SendPage(connection, askpage);
125 }
126
127 if (0 == strcmp(method, "POST"))
128 {
129 struct ConnectionInfoStruct *con_info = *con_cls;
130
131 if (*upload_data_size != 0)
132 {
133 MHD_post_process(con_info->postprocessor, upload_data, *upload_data_size);
134 *upload_data_size = 0;
135 return MHD_YES;
136 } else return SendPage(connection, con_info->answerstring);
137 }
138
139 return SendPage(connection, errorpage);
140}
141
142int main ()
143{
144 struct MHD_Daemon *daemon;
145
146
147 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
148 &AnswerToConnection, NULL, MHD_OPTION_NOTIFY_COMPLETED,
149 RequestCompleted, NULL, MHD_OPTION_END);
150
151 if (NULL == daemon) return 1;
152
153 getchar();
154
155 MHD_stop_daemon(daemon);
156 return 0;
157}
diff --git a/doc/exploringrequests.inc b/doc/exploringrequests.inc
new file mode 100644
index 00000000..25a0cd8b
--- /dev/null
+++ b/doc/exploringrequests.inc
@@ -0,0 +1,104 @@
1This chapter will deal with the information which the client sends to the
2server at every request. We are going to examine the most useful fields of such an request
3and print them out in a readable manner. This could be useful for logging facilities.
4
5The starting point is the @emph{hellobrowser} program with the former response removed.
6
7This time, we just want to collect information in the callback function, thus we will
8just return MHD_NO after we have probed the request. This way, the connection is closed
9without much ado by the server.
10
11@verbatim
12int AnswerToConnection(void *cls, struct MHD_Connection *connection,
13 const char *url, const char *method, const char *version,
14 const char *upload_data, unsigned int *upload_data_size, void **con_cls)
15{
16 ...
17 return MHD_NO;
18}
19@end verbatim
20@noindent
21The ellipsis marks the position where the following instructions shall be inserted.
22
23
24We begin with the most obvious information available to the server, the request line. You should
25already have noted that a request consists of a command (or "method") and a URI (e.g. a filename).
26It also contains a string for the version of the protocol which can be found in @code{version}.
27To call it a "new request" is justified because we return only @code{MHD_NO}, thus ensuring the
28function will not be called again for this connection.
29@verbatim
30printf("New request %s for %s using version %s\n", method, url, version);
31@end verbatim
32@noindent
33
34The rest of the information is a bit more hidden. Nevertheless, there is lot of it sent from common
35Internet browsers. It is stored in "key-name" pairs and we want to list what we find in the header.
36As there is no mandatory set of keys a client has to send, each key--name pair is printed out one by
37one until there are no more left. We do this by writing a separate function which will be called for
38each pair just like the above function is called for each HTTP request.
39It can then print out the content of this pair.
40@verbatim
41int PrintOutKey(void *cls, enum MHD_ValueKind kind, const char *key,
42 const char *value)
43{
44 printf("%s = %s\n", key, value);
45 return MHD_YES;
46}
47@end verbatim
48@noindent
49
50To start the iteration process that calls our new function for every key, the line
51@verbatim
52MHD_get_connection_values(connection, MHD_HEADER_KIND, PrintOutKey, NULL);
53@end verbatim
54@noindent
55needs to be inserted in the connection callback function too. The second parameter tells the function
56that we are only interested in keys from the general HTTP header of the request. Our iterating
57function @code{PrintOutKey} does not rely on any additional information to fulfill its duties
58so the last parameter can be NULL.
59
60All in all, this constitutes the complete @code{logger.c} program for this chapter which can be
61found in the @code{examples} section.
62
63Connecting with any modern Internet browser should yield a handful of keys. You should try to
64interpret them with the aid of @emph{RFC 2616}.
65Especially worth mentioning is the host key which is often used to serve several different websites
66hosted under one single IP address but reachable by different domain names.
67
68@heading Conclusion
69The introduced capabilities to itemize the content of a simple GET request---especially the
70URI---should already allow the server to satisfy clients' requests for small specific resources
71(e.g. files) or even induce alteration of how the server operates. However, the latter is not
72recommended as the GET method (including its header data) is by convention considered a "SAFE"
73operation, which should not change the server's state in a significant way, but temporally actions
74like searching for a passed string is fine.
75
76Of course, no transmission can occur while the return value is still set to @code{MHD_NO} in the
77callback function.
78
79@heading Exercises
80@itemize @bullet
81@item
82By parsing the @code{url} string and delivering responses accordingly, implement a small server for
83"virtual" files. When asked for @code{/index.htm@{l@}}, let the response consist of a HTML page
84containing a link to @code{/another.html} page which is also to be created "on the fly" in case of
85being requested. If neither of these two pages are requested, @code{MHD_HTTP_NOT_FOUND} shall be
86returned accompanied by an informative message.
87
88@item
89A very interesting information has still been ignored by our logger---the client's IP address.
90Implement a callback function
91@verbatim
92int OnClientConnect(void *cls,
93 const struct sockaddr *addr,socklen_t addrlen)
94@end verbatim
95@noindent
96that prints out the IP address in an appropriate format. You might want to use the posix function
97@code{inet_ntoa} but bear in mind that @code{addr} is actually just a structure containing other
98substructures and is @emph{not} the variable this function expects.
99Make sure to return @code{MHD_YES} so that the library knows the client is allowed to connect
100(and to request). If one wanted to limit access basing on IP addresses, this would be the place
101to do it. The address of your function will then be passed as the third parameter of the
102@code{MHD_start_daemon} call.
103
104@end itemize
diff --git a/doc/hellobrowser.inc b/doc/hellobrowser.inc
new file mode 100644
index 00000000..422162af
--- /dev/null
+++ b/doc/hellobrowser.inc
@@ -0,0 +1,201 @@
1The most basic task for a HTTP server is to deliver a static text message to any client connecting to it.
2Given that this is also very easy to implement, it is an excellent problem to start with.
3
4For now, the particular filename the client asks for shall have no effect on the message that will
5be returned. In addition, the server shall end the connection after the message has been sent so that
6the client will know there is nothing more to expect.
7
8The C program @code{hellobrowser.c}, which is to be found in the examples section, does just that.
9If you are very eager, you can compile and start it right away but it is advisable to type the
10lines in by yourself as they will be discussed and explained in detail.
11
12After the unexciting includes and the definition of the port which our server should listen on
13@verbatim
14#include <microhttpd.h>
15#include <string.h>
16#include <stdlib.h>
17#include <stdio.h>
18@end verbatim
19@noindent
20the desired behaviour of our server when HTTP request arrive have to be implemented. We already have
21agreed that it should not care about the particular details of the request, such as who is requesting
22what. The server will respond merely with the same small HTML page to every request.
23
24The function we are going to write now will be called by @emph{GNU libmicrohttpd} every time an
25appropriate request comes in. While the name of this callback function is arbitrary, its parameter
26list has to follow a certain layout. So please, ignore the lot of parameters for now, they will be
27explained at the point they are needed. We have to use only one of them,
28@code{struct MHD_Connection *connection}, for the minimalistic functionality we want to archive at the moment.
29
30This parameter is set by the @emph{libmicrohttpd} daemon and holds the necessary information to
31relate the call with a certain connection. Keep in mind that a server might have to satisfy hundreds
32of concurrent connections and we have to make sure that the correct data is sent to the destined
33client. Therefore, this variable is a means to refer to a particular connection if we ask the
34daemon to sent the reply.
35
36Talking about the reply, it is defined as a string right after the function header
37@verbatim
38int AnswerToConnection(void *cls, struct MHD_Connection *connection,
39 const char *url, const char *method, const char *version,
40 const char *upload_data, unsigned int *upload_data_size, void **con_cls)
41{
42 const char *page = "<html><body>Hello, browser!</body></html>";
43@end verbatim
44@noindent
45HTTP is a rather strict protocol and the client would certainly consider it "inappropriate" if we
46just sent the answer string "as is". Instead, it has to be wrapped in certain layers, called headers,
47of additional information. Luckily, most of the work in this area is done by the library for us---we
48just have to ask. Our reply string packed in the necessary layers will be called a "response".
49To obtain such a response we hand our data (the reply--string) and its size over to the
50@code{MHD_create_response_from_data} function. The last two parameters basically tell @emph{MHD}
51that we do not want it to dispose the message data for us when it has been sent and there also needs
52no internal copy to be done because the @emph{constant} string won't change anyway.
53
54@verbatim
55 struct MHD_Response *response;
56 int ret;
57
58 response = MHD_create_response_from_data(strlen(page),
59 (void*)page, MHD_NO, MHD_NO);
60@end verbatim
61@noindent
62Now that the the response has been laced up, it is ready for delivery and can be queued for sending.
63This is done by passing it to another @emph{GNU libmicrohttpd} function. As all our work was done in
64the scope of one function, the recipient is without doubt the one associated with the
65local variable @code{connection} and consequently this variable is given to the queue function.
66Every HTTP response is accompanied by a status code, here "OK", so that the client knows
67this response is the intended result of his request and not due to some error or malfunction.
68
69Finally, the packet is destroyed and the return value from the queue returned,
70already being set at this point to either MHD_YES or MHD_NO in case of success or failure.
71
72@verbatim
73 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
74 MHD_destroy_response (response);
75 return ret;
76}
77@end verbatim
78@noindent
79With the primary task of our server implemented, we can start the actual server daemon which will listen
80on @code{PORT} for connections. This is done in the main function.
81@verbatim
82int main ()
83{
84 struct MHD_Daemon *d;
85
86 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
87 &AnswerToConnection, NULL, MHD_OPTION_END);
88 if (d == NULL) return 1;
89@end verbatim
90@noindent
91The first parameter is one of three possible modes of operation. Here we want the daemon to run in
92a separate thread and to manage all incoming connections in the same thread. This means that while
93producing the response for one connection, the other connections will be put on hold. In this
94chapter, where the reply is already known and therefore the request is served quickly, this poses no problem.
95
96We will allow all clients to connect regardless of their name or location, therefore we do not check
97them on connection and set the forth and fifth parameter to NULL.
98
99Parameter six is the address of the function we want to be called whenever a new connection has been
100established. Our @code{AnswerToConnection} knows best what the client wants and needs no additional
101information (which could be passed via the next parameter) so the next parameter is NULL. Likewise,
102we do not need to pass extra options to the daemon so we just write the MHD_OPTION_END as the last parameter.
103
104As the server daemon runs in the background in its own thread, the execution flow in our main
105function will contine right after the call. Because of this, we must delay the execution flow in the
106main thread or else the program will terminate prematurely. We let it pause in a processing-time
107friendly manner by waiting for the enter key to be pressed. In the end, we stop the daemon so it can
108do its cleanup tasks.
109@verbatim
110 getchar();
111
112 MHD_stop_daemon(d);
113 return 0;
114}
115@end verbatim
116@noindent
117The first example is now complete.
118
119Compile it with
120@verbatim
121cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
122 -L$PATH_TO_LIBMHD_INCLUDES -static -lmicrohttpd -pthread
123@end verbatim
124with the two paths set accordingly and run it.
125
126Now open your favorite Internet browser and go to the address @code{localhost:8888}, provided that
127is the port you chose. If everything works as expected, the browser will present the message of the
128static HTML page it got from our minimal server.
129
130@heading Remarks
131To keep this first example as small as possible, some drastic shortcuts were taken and are to be
132discussed now.
133
134Firstly, there is no distinction made between the kinds of requests a client could send. We implied
135that the client sends a GET request, that means, that he actually asked for some data. Even when
136it is not intended to accept POST requests, a good server should at least recognize that this
137request does not constitute a legal request and answer with an error code. This can be easily
138implemented by checking if the parameter @code{method} equals the string "GET" and returning a
139@code{MHD_NO} if not so.
140
141Secondly, the above practice of queuing a response upon the first call of the callback function
142brings with it some limitations. This is because the content of the message body will not be
143received if a response is queued in the first iteration. Furthermore, the connection will be closed
144right after the response has been transferred then.
145
146Both of these issues you will find addressed in the official @code{minimal_example.c} residing in
147the @code{src/examples} directory of the @emph{GNU libmicrohttpd} package. The source code of this
148program should look very familiar to you by now and easy to understand.
149
150For our example, the @code{must_copy} and @code{must_free} parameter at the response construction
151function could be set to @code{MHD_NO}. In the usual case, responses cannot be sent immediately
152after being queued. For example, there might be other data on the system that needs to be sent with
153a higher priority. Nevertheless, the queue function will return successfully---raising the problem
154that the data we have pointed to may be invalid by the time it is about being sent. This is not an
155issue here because we can expect the @code{page} string, which is a constant @emph{string literal}
156here, to be static. That means it will be present and unchanged for as long as the program runs.
157For dynamic data, one could choose to either have @emph{MHD} free the memory @code{page} points
158to itself when it is not longer needed or, alternatively, have the library to make and manage
159its own copy of it.
160
161@heading Exercises
162@itemize @bullet
163@item
164While the server is running, use a program like telnet or netcat to connect to it. Try to form a
165valid HTTP1.1 request yourself like
166@verbatim
167GET /dontcare HTTP1.1
168Host: itsme
169<enter>
170@end verbatim
171@noindent
172and see what the server returns to you.
173
174
175@item
176Also, try other requests, like POST, and see how our server does not mind and why.
177How far in malforming a request can you go before the builtin functionality of @emph{MHD} intervenes
178and an altered response is sent? Make sure you read about the status codes in the @emph{RFC}.
179
180
181@item
182Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the daemon in @code{main}.
183Mind the special format of the parameter list here which is described in the manual. How indulgent
184is the server now to your input?
185
186
187@item
188Let the main function take a string as the first command line argument and pass @code{argv[1]} to
189the @code{MHD_start_daemon} function as the sixth parameter. The address of this string will be
190passed to the callback function via the @code{cls} variable. Decorate the text given at the command
191line when the server is started with proper HTML tags and send it as the response instead of the
192former static string.
193
194
195@item
196@emph{Demanding:} Write a separate function returning a string containing some useful information,
197for example, the time. Pass the function's address as the sixth parameter and evaluate this function
198on every request anew in @code{AnswerToConnection}. Remember to free the memory of the string
199every time after satisfying the request.
200
201@end itemize
diff --git a/doc/introduction.inc b/doc/introduction.inc
new file mode 100644
index 00000000..3eb1248d
--- /dev/null
+++ b/doc/introduction.inc
@@ -0,0 +1,19 @@
1This tutorial is aimed at developers who want to learn how they can add HTTP serving
2capabilities to their applications with the @emph{GNU libmicrohttpd} library,
3abbreviated @emph{MHD}, and who do not know how to start. It tries to help these
4developers to implement common basic HTTP serving tasks by discussing executable
5sample programs implementing different features.
6
7The text is supposed to be a supplement to the API reference manual of
8@emph{GNU libmicrohttpd} and for that reason does not explain many of the parameters.
9Therefore, the reader should always consult the manual to find the exact meaning
10of the functions used in the tutorial. In the same sense, the tutorial seeks to
11encourage the use of the @emph{RFCs}, which document the conventions the Internet
12is built upon.
13
14@emph{GNU libmicrohttpd} is assumed to be already installed and it has been
15written with respect to version @value{VERSION}. As the library is still in its
16beta stages, later versions may show different behaviour. At the time being,
17this tutorial has only been tested on @emph{GNU/Linux} machines even though
18efforts were made not to rely on anything that would prevent the samples from being
19built on similar systems.
diff --git a/doc/processingpost.inc b/doc/processingpost.inc
new file mode 100644
index 00000000..42160970
--- /dev/null
+++ b/doc/processingpost.inc
@@ -0,0 +1,225 @@
1The previous chapters already have demonstrated a variety of possibilities to send information
2to the HTTP server, but it is not recommended that the @emph{GET} method is used to alter the way
3the server operates. To induce changes on the server, the @emph{POST} method is preferred over
4and is much more powerful than @emph{GET} and will be introduced in this chapter.
5
6We are going to write an application that asks for the visitor's name and, after the user has posted it,
7composes an individual response text. Even though it was not mandatory to use the @emph{post} method here,
8as there is no permanent change caused by the post, it is an illustrative example on how to share data
9between different functions for the same connection. Furthermore, the reader should be able to extend
10it easily.
11
12@heading GET request
13When the first @emph{GET} request arrives, the server shall respond with a HTML page containing an
14edit field for the name.
15
16@verbatim
17const char* askpage="<html><body>\
18 What's your name, Sir?<br>\
19 <form action=\"/namepost\" method=\"post\">\
20 <input name=\"name\" type=\"text\"\
21 <input type=\"submit\" value=\" Send \"></form>\
22 </body></html>";
23@end verbatim
24@noindent
25
26The @code{action} entry is the @emph{URI} to be called by the browser when posting, and the
27@code{name} will be used later to be sure it is the editbox's content that has been posted.
28
29We also prepare the answer page, where the name is to be filled in later, and an error page
30as the response for anything but proper @emph{GET} and @emph{POST} requests:
31
32@verbatim
33const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>";
34
35const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
36@end verbatim
37@noindent
38
39Whenever we need to send a page, we use an extra function
40@code{int SendPage(struct MHD_Connection *connection, const char* page)}
41for this, which does not contain anything new and whose implementation is therefore left out here.
42
43
44@heading POST request
45Posted data can be of arbitrary and considerable size; for example, if a user uploads a big
46image to the server. Similar to the case of the header fields, there may also be different streams
47of posted data, such as one containing the text of an editbox and another the state of a button.
48Likewise, we will have to register an iterator function that is going to be called maybe several times
49not only if there are different POSTs but also if one POST has only been received partly yet and
50needs processing before another chunk can be received.
51
52Such an iterator function is called by a @emph{postprocessor}, which must be created upon arriving
53of the post request. We want the iterator function to read the first post data which is tagged
54@code{name} and to create an individual greeting string based on the template and the name.
55But in order to pass this string to other functions and still be able to differentiate different
56connections, we must first define a structure to share the information, holding the most import entries.
57
58@verbatim
59struct ConnectionInfoStruct
60{
61 int connectiontype;
62 char *answerstring;
63 struct MHD_PostProcessor *postprocessor;
64};
65@end verbatim
66@noindent
67
68With these information available to the iterator function, it is able to fulfill its task.
69Once it has composed the greeting string, it returns @code{MHD_NO} to inform the post processor
70that it does not need to be called again. Note that this function does not handle processing
71of data for the same @code{key}. If we were to expect that the name will be posted in several
72chunks, we had to expand the namestring dynamically as additional parts of it with the same @code{key}
73came in. But in this example, the name is assumed to fit entirely inside one single packet.
74
75@verbatim
76int IteratePost(void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
77 const char *filename, const char *content_type,
78 const char *transfer_encoding, const char *data, size_t off, size_t size)
79{
80 struct ConnectionInfoStruct *con_info = (struct ConnectionInfoStruct*)(coninfo_cls);
81
82
83 if (0 == strcmp(key, "name"))
84 {
85 if ((size>0) && (size<=MAXNAMESIZE))
86 {
87 char *answerstring;
88 answerstring = malloc(MAXANSWERSIZE);
89 if (!answerstring) return MHD_NO;
90
91 snprintf(answerstring, MAXANSWERSIZE, greatingpage, data);
92 con_info->answerstring = answerstring;
93 } else con_info->answerstring=NULL;
94
95 return MHD_NO;
96 }
97
98 return MHD_YES;
99}
100@end verbatim
101@noindent
102
103Once a connection has been established, it can be terminated for many reasons. As these
104reasons include unexpected events, we have to register another function that cleans up any resources
105that might have been allocated for that connection by us, namely the post processor and the greetings
106string. This cleanup function must take into account that it will also be called for finished
107requests other than @emph{POST} requests.
108
109@verbatim
110void RequestCompleted(void *cls, struct MHD_Connection *connection, void **con_cls,
111 enum MHD_RequestTerminationCode toe)
112{
113 struct ConnectionInfoStruct *con_info = (struct ConnectionInfoStruct*)(*con_cls);
114
115
116 if (NULL == con_info) return;
117
118 if (con_info->connectiontype == POST)
119 {
120 MHD_destroy_post_processor(con_info->postprocessor);
121 if (con_info->answerstring) free(con_info->answerstring);
122 }
123
124 free(con_info);
125}
126@end verbatim
127@noindent
128
129@emph{GNU libmicrohttpd} is informed that it shall call the above function when the daemon is started
130in the main function.
131
132@verbatim
133...
134daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
135 &AnswerToConnection, NULL, MHD_OPTION_NOTIFY_COMPLETED,
136 RequestCompleted, NULL, MHD_OPTION_END);
137...
138@end verbatim
139@noindent
140
141@heading Request handling
142With all other functions prepared, we can now discuss the actual request handling.
143
144On the first iteration for a new request, we start by allocating a new instance of a
145@code{ConnectionInfoStruct} structure, which will store all necessary information for later
146iterations and other functions.
147
148@verbatim
149int AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url,
150 const char *method, const char *version, const char *upload_data,
151 unsigned int *upload_data_size, void **con_cls)
152{
153 if(*con_cls==NULL)
154 {
155 struct ConnectionInfoStruct *con_info;
156
157 con_info = malloc(sizeof(struct ConnectionInfoStruct));
158 if (NULL == con_info) return MHD_NO;
159@end verbatim
160@noindent
161
162If the new request is a @emph{POST}, the postprocessor must be created now. In addition, the type
163of the request is stored for convenience.
164@verbatim
165 if (0 == strcmp(method, "POST"))
166 {
167 con_info->postprocessor = MHD_create_post_processor(connection, POSTBUFFERSIZE,
168 IteratePost, (void*)con_info);
169
170 if (NULL == con_info->postprocessor)
171 {
172 free(con_info);
173 return MHD_NO;
174 }
175
176 con_info->connectiontype = POST;
177 } else con_info->connectiontype = GET;
178@end verbatim
179@noindent
180
181The address of our structure will both serve as the indicator for successive iterations and to remember
182the particular details about the connection.
183@verbatim
184 *con_cls = (void*)con_info;
185 return MHD_YES;
186 }
187@end verbatim
188@noindent
189
190The rest of the function will not be executed on the first iteration. A @emph{GET} request is easily
191satisfied by sending the question form.
192@verbatim
193 if (0 == strcmp(method, "GET"))
194 {
195 return SendPage(connection, askpage);
196 }
197@end verbatim
198@noindent
199
200In case of @emph{POST}, we invoke the post processor for as long as data keeps incoming, setting
201@code{*upload_data_size} to zero in order to indicate that we have processed---or at least have
202considered---all of it.
203@verbatim
204 if (0 == strcmp(method, "POST"))
205 {
206 struct ConnectionInfoStruct *con_info = *con_cls;
207
208 if (*upload_data_size != 0)
209 {
210 MHD_post_process(con_info->postprocessor, upload_data, *upload_data_size);
211 *upload_data_size = 0;
212 return MHD_YES;
213 } else return SendPage(connection, con_info->answerstring);
214 }
215@end verbatim
216@noindent
217
218If they are neither @emph{GET} nor @emph{POST} requests, the error page is returned finally.
219@verbatim
220 return SendPage(connection, errorpage);
221}
222@end verbatim
223@noindent
224
225These were the important parts of the program @code{simplepost.c}.
diff --git a/doc/responseheaders.inc b/doc/responseheaders.inc
new file mode 100644
index 00000000..1e4fdac8
--- /dev/null
+++ b/doc/responseheaders.inc
@@ -0,0 +1,171 @@
1Now that we are able to inspect the incoming request in great detail,
2this chapter discusses the means to enrich the outgoing responses likewise.
3
4As you have learned in the @emph{Hello, Browser} chapter, some obligatory
5header fields are added and set automatically for simple responses by the library
6itself but if more advanced features are desired, additional fields have to be created.
7One of the possible fields is the content type field and an example will be developed around it.
8This will lead to an application capable of correctly serving different types of files.
9
10
11When we responded with HTML page packed in the static string previously, the client had no choice
12but guessing about how to handle the response, because the server hadn't told him.
13What if we had sent a picture or a sound file? Would the message have been understood
14or merely been displayed as an endless stream of random characters in the browser?
15This is what the mime content types are for. The header of the response is extended
16by certain information about how the data is to be interpreted.
17
18To introduce the concept, a picture of the format @emph{PNG} will be sent to the client
19and labeled accordingly with @code{image/png}.
20Once again, we can base the new example on the @code{hellobrowser} program.
21
22@verbatim
23#define FILENAME "picture.png"
24#define MIMETYPE "image/png"
25
26int AnswerToConnection(void *cls, struct MHD_Connection *connection,
27 const char *url, const char *method, const char *version,
28 const char *upload_data, unsigned int *upload_data_size, void **con_cls)
29{
30 struct MHD_Response *response;
31 int ret=0;
32@end verbatim
33@noindent
34
35We want the program to load the graphics file into memory:
36@verbatim
37long size;
38 FILE *fp;
39 int ret=0;
40
41 if (0 != strcmp(method, "GET")) return MHD_NO;
42
43 size = GetFileSize(FILENAME);
44 if (size != 0)
45 {
46 fp = fopen(FILENAME, "rb");
47 if (fp)
48 {
49 buffer = malloc(size);
50 if (buffer)
51 if (size == fread(buffer, 1, size, fp)) ret=1;
52 }
53
54 fclose(fp);
55 }
56@end verbatim
57@noindent
58
59The @code{GetFileSize} function, which returns a size of zero if the file could not be opened or
60found, is left out on this page for tidiness.
61
62When dealing with files and allocating memory, there is a lot that could go wrong on the
63sider side and if so, the client should be informed with @code{MHD_HTTP_INTERNAL_SERVER_ERROR}.
64
65@verbatim
66 if (!ret)
67 {
68 const char *errorstr = "<html><body>An internal server error\
69 has occured!</body></html>";
70
71 if (buffer) free(buffer);
72 response = MHD_create_response_from_data(strlen(errorstr),
73 (void*)errorstr, MHD_NO, MHD_NO);
74
75 ret = MHD_queue_response (connection,
76 MHD_HTTP_INTERNAL_SERVER_ERROR, response);
77
78 return MHD_YES;
79 }
80@end verbatim
81@noindent
82
83Note that we nevertheless have to create an response object even for sending a simple error code.
84Otherwise, the connection would just be closed without comment, leaving the client curious about
85what has happened.
86
87But in the case of success a response will be constructed that contains the buffer filled with the
88file's content.
89
90@verbatim
91response = MHD_create_response_from_data(size, (void*)buffer, MHD_YES, MHD_NO);
92@end verbatim
93@noindent
94
95Contrary to the above case where a static string will be sent, this time we have to
96keep track of the dynamically allocated buffer. As discussed in the @ref{Hello browser example},
97the buffer cannot be safely freed as soon as the function call returns. Instead, we ask the function
98to keep charge of freeing the buffer itself when it is not longer needed. Thus, no further @code{free}
99command is invoked by us.
100
101Up to this point, there was little new. The actual novelty is that we enhance the header with the
102meta data about the content. Aware of the field's name we want to add, it is as easy as that:
103@verbatim
104MHD_add_response_header(response, "Content-Type", MIMETYPE);
105@end verbatim
106@noindent
107We do not have to append a colon expected by the protocol hehind the first
108field---@emph{GNU libhttpdmicro} will take care of this.
109
110The function finishes with the well-known lines
111@verbatim
112 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
113 MHD_destroy_response (response);
114 return ret;
115}
116@end verbatim
117@noindent
118
119The complete program @code{responseheaders.c} is in the @code{examples} section as usual.
120Find a @emph{PNG} file you like and save it to the directory the example is run from under the name
121@code{picture.png}. You should find the image displayed on your browser if everything worked well.
122
123@heading Remarks
124The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616}
125already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead
126of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could
127add custom response headers without violated the protocol. Whether and how the client would react
128to these custom header is up to the receiver. Likewise, the client is allowed to send custom request
129headers to the server as well, opening up yet more possibilities how client and server could
130communicate with each other.
131
132The method of creating the response from one big chunk of data is only feasible for smaller files.
133A public file server satisfying many request at the same time would be choking under these high
134demands on memory very soon. Serving responses in smaller parts would be more adequate here and
135will be a topic of a future chapter.
136
137@heading Exercises
138@itemize @bullet
139
140@item
141Remember that the original program was written under a few assumptions---a small, static response
142being one of them. In order to simulate a very large or hard to reach file that cannot be provided
143instantly, postpone the queuing in the callback with the @code{sleep} function for 30 seconds
144@emph{if} the file @code{/big.png} is requested (but deliver the same as above). A request for
145@code{/picture.png} should provide just the same but without any artificial delays.
146
147Now start two instances of your browser (or even use two machines) and see how the second client
148is put on hold while the first waits for his request on the slow file to be fulfilled.
149
150Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is
151started and try again.
152
153
154@item
155Did you succeed in implementing the clock exercise yet? This time, let the server save the
156program's start time @code{t} and implement a response simulating a countdown that reaches 0 at
157@code{t+60}. Returning a message saying on which point the countdown is, the response should
158ultimately be to reply "Done" if the program has been running long enough,
159
160A non official, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with
161the uppercase words substituted to tell the client it should request the given resource after
162the given delay again. Improve your program in that the browser (any modern browser should work)
163automatically reconnects and asks for the status again every 5 seconds or so. The URL would have
164to be composed so that it begins with "http://", followed by the @emph{URI} the server is reachable
165from the client's point of view.
166
167Maybe you want also to visualize the countdown as a status bar by creating a
168@code{<table>} consisting of one row and @code{n} columns whose fields contain small images of either
169a red or a green light.
170
171@end itemize
diff --git a/doc/tutorial.texi b/doc/tutorial.texi
new file mode 100644
index 00000000..fed834f8
--- /dev/null
+++ b/doc/tutorial.texi
@@ -0,0 +1,119 @@
1\input texinfo @c -*-texinfo-*-
2@finalout
3@setfilename libmicrohttpdtutorial
4@settitle A tutorial for GNU libmicrohttpd
5@afourpaper
6
7@set VERSION 0.3.1 beta
8
9@titlepage
10@title A Tutorial for GNU libmicrohttpd
11@subtitle written for version @value{VERSION}
12@author Sebastian Gerhardt (@email{sebgerhardt@@gmx.net})
13@page
14@vskip 0pt plus 1filll
15@end titlepage
16
17@verbatim
18Copyright (c) 2008 Sebastian Gerhardt.
19Permission is granted to copy, distribute and/or modify this document
20under the terms of the GNU Free Documentation License, Version 1.2
21or any later version published by the Free Software Foundation;
22with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
23Texts. A copy of the license is included in the section entitled "GNU
24Free Documentation License".
25@end verbatim
26
27@contents
28
29@ifnottex
30@node Top
31@top Top
32@end ifnottex
33
34@menu
35* Introduction::
36* Hello browser example::
37* Exploring requests::
38* Response headers::
39* A basic authentication::
40* Processing post data::
41* Bibliography::
42* License text::
43* Example programs::
44@end menu
45
46@node Introduction
47@chapter Introduction
48@include introduction.inc
49
50@node Hello browser example
51@chapter Hello browser example
52@include hellobrowser.inc
53
54@node Exploring requests
55@chapter Exploring requests
56@include exploringrequests.inc
57
58@node Response headers
59@chapter Response headers
60@include responseheaders.inc
61
62@node A basic authentication
63@chapter A basic authentication
64@include basicauthentication.inc
65
66@node Processing post data
67@chapter Processing post data
68@include processingpost.inc
69
70
71@node Bibliography
72@appendix Bibliography
73@include bibliography.inc
74
75@node License text
76@appendix GNU Free Documentation License
77@include fdl-1.2.texi
78
79@node Example programs
80@appendix Example programs
81@menu
82* hellobrowser.c::
83* logging.c::
84* responseheaders.c::
85* basicauthentication.c::
86* simplepost.c::
87@end menu
88
89@node hellobrowser.c
90@section hellobrowser.c
91@smalldisplay
92@verbatiminclude examples/hellobrowser.c
93@end smalldisplay
94
95@node logging.c
96@section logging.c
97@smalldisplay
98@verbatiminclude examples/logging.c
99@end smalldisplay
100
101@node responseheaders.c
102@section responseheaders.c
103@smalldisplay
104@verbatiminclude examples/responseheaders.c
105@end smalldisplay
106
107@node basicauthentication.c
108@section basicauthentication.c
109@smalldisplay
110@verbatiminclude examples/basicauthentication.c
111@end smalldisplay
112
113@node simplepost.c
114@section simplepost.c
115@smalldisplay
116@verbatiminclude examples/simplepost.c
117@end smalldisplay
118
119@bye