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