diff options
Diffstat (limited to 'doc/chapters/hellobrowser.inc')
-rw-r--r-- | doc/chapters/hellobrowser.inc | 203 |
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 @@ | |||
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 | ||