aboutsummaryrefslogtreecommitdiff
path: root/doc/chapters/hellobrowser.inc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/chapters/hellobrowser.inc')
-rw-r--r--doc/chapters/hellobrowser.inc203
1 files changed, 203 insertions, 0 deletions
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 @@
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 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 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
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
76 return ret;
77}
78@end verbatim
79@noindent
80With the primary task of our server implemented, we can start the actual server daemon which will listen
81on @code{PORT} for connections. This is done in the main function.
82@verbatim
83int 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
92The first parameter is one of three possible modes of operation. Here we want the daemon to run in
93a separate thread and to manage all incoming connections in the same thread. This means that while
94producing the response for one connection, the other connections will be put on hold. In this
95chapter, where the reply is already known and therefore the request is served quickly, this poses no problem.
96
97We will allow all clients to connect regardless of their name or location, therefore we do not check
98them on connection and set the forth and fifth parameter to NULL.
99
100Parameter six is the address of the function we want to be called whenever a new connection has been
101established. Our @code{AnswerToConnection} knows best what the client wants and needs no additional
102information (which could be passed via the next parameter) so the next parameter is NULL. Likewise,
103we do not need to pass extra options to the daemon so we just write the MHD_OPTION_END as the last parameter.
104
105As the server daemon runs in the background in its own thread, the execution flow in our main
106function will contine right after the call. Because of this, we must delay the execution flow in the
107main thread or else the program will terminate prematurely. We let it pause in a processing-time
108friendly manner by waiting for the enter key to be pressed. In the end, we stop the daemon so it can
109do its cleanup tasks.
110@verbatim
111 getchar ();
112
113 MHD_stop_daemon (daemon);
114 return 0;
115}
116
117@end verbatim
118@noindent
119The first example is now complete.
120
121Compile it with
122@verbatim
123cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
124 -L$PATH_TO_LIBMHD_INCLUDES -static -lmicrohttpd -pthread
125@end verbatim
126with the two paths set accordingly and run it.
127
128Now open your favorite Internet browser and go to the address @code{localhost:8888}, provided that
129is the port you chose. If everything works as expected, the browser will present the message of the
130static HTML page it got from our minimal server.
131
132@heading Remarks
133To keep this first example as small as possible, some drastic shortcuts were taken and are to be
134discussed now.
135
136Firstly, there is no distinction made between the kinds of requests a client could send. We implied
137that the client sends a GET request, that means, that he actually asked for some data. Even when
138it is not intended to accept POST requests, a good server should at least recognize that this
139request does not constitute a legal request and answer with an error code. This can be easily
140implemented by checking if the parameter @code{method} equals the string "GET" and returning a
141@code{MHD_NO} if not so.
142
143Secondly, the above practice of queuing a response upon the first call of the callback function
144brings with it some limitations. This is because the content of the message body will not be
145received if a response is queued in the first iteration. Furthermore, the connection will be closed
146right after the response has been transferred then.
147
148Both of these issues you will find addressed in the official @code{minimal_example.c} residing in
149the @code{src/examples} directory of the @emph{GNU libmicrohttpd} package. The source code of this
150program should look very familiar to you by now and easy to understand.
151
152For our example, the @code{must_copy} and @code{must_free} parameter at the response construction
153function could be set to @code{MHD_NO}. In the usual case, responses cannot be sent immediately
154after being queued. For example, there might be other data on the system that needs to be sent with
155a higher priority. Nevertheless, the queue function will return successfully---raising the problem
156that the data we have pointed to may be invalid by the time it is about being sent. This is not an
157issue here because we can expect the @code{page} string, which is a constant @emph{string literal}
158here, to be static. That means it will be present and unchanged for as long as the program runs.
159For dynamic data, one could choose to either have @emph{MHD} free the memory @code{page} points
160to itself when it is not longer needed or, alternatively, have the library to make and manage
161its own copy of it.
162
163@heading Exercises
164@itemize @bullet
165@item
166While the server is running, use a program like telnet or netcat to connect to it. Try to form a
167valid HTTP1.1 request yourself like
168@verbatim
169GET /dontcare HTTP1.1
170Host: itsme
171<enter>
172@end verbatim
173@noindent
174and see what the server returns to you.
175
176
177@item
178Also, try other requests, like POST, and see how our server does not mind and why.
179How far in malforming a request can you go before the builtin functionality of @emph{MHD} intervenes
180and an altered response is sent? Make sure you read about the status codes in the @emph{RFC}.
181
182
183@item
184Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the daemon in @code{main}.
185Mind the special format of the parameter list here which is described in the manual. How indulgent
186is the server now to your input?
187
188
189@item
190Let the main function take a string as the first command line argument and pass @code{argv[1]} to
191the @code{MHD_start_daemon} function as the sixth parameter. The address of this string will be
192passed to the callback function via the @code{cls} variable. Decorate the text given at the command
193line when the server is started with proper HTML tags and send it as the response instead of the
194former static string.
195
196
197@item
198@emph{Demanding:} Write a separate function returning a string containing some useful information,
199for example, the time. Pass the function's address as the sixth parameter and evaluate this function
200on every request anew in @code{AnswerToConnection}. Remember to free the memory of the string
201every time after satisfying the request.
202
203@end itemize