hellobrowser.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. For now, the particular URI the client asks for shall have no effect on the message that will
  4. be returned. In addition, the server shall end the connection after the message has been sent so that
  5. the client will know there is nothing more to expect.
  6. The C program @code{hellobrowser.c}, which is to be found in the examples section, does just that.
  7. If you are very eager, you can compile and start it right away but it is advisable to type the
  8. lines in by yourself as they will be discussed and explained in detail.
  9. After the necessary includes and the definition of the port which our server should listen on
  10. @verbatim
  11. #include <sys/types.h>
  12. #include <sys/select.h>
  13. #include <sys/socket.h>
  14. #include <microhttpd.h>
  15. #define PORT 8888
  16. @end verbatim
  17. @noindent
  18. the desired behaviour of our server when HTTP request arrive has to be implemented. We already have
  19. agreed that it should not care about the particular details of the request, such as who is requesting
  20. what. The server will respond merely with the same small HTML page to every request.
  21. The function we are going to write now will be called by @emph{GNU libmicrohttpd} every time an
  22. appropriate request comes in. While the name of this callback function is arbitrary, its parameter
  23. list has to follow a certain layout. So please, ignore the lot of parameters for now, they will be
  24. explained at the point they are needed. We have to use only one of them,
  25. @code{struct MHD_Connection *connection}, for the minimalistic functionality we want to achieve at the moment.
  26. This parameter is set by the @emph{libmicrohttpd} daemon and holds the necessary information to
  27. relate the call with a certain connection. Keep in mind that a server might have to satisfy hundreds
  28. of concurrent connections and we have to make sure that the correct data is sent to the destined
  29. client. Therefore, this variable is a means to refer to a particular connection if we ask the
  30. daemon to sent the reply.
  31. Talking about the reply, it is defined as a string right after the function header
  32. @verbatim
  33. int answer_to_connection (void *cls, struct MHD_Connection *connection,
  34. const char *url,
  35. const char *method, const char *version,
  36. const char *upload_data,
  37. size_t *upload_data_size, void **req_cls)
  38. {
  39. const char *page = "<html><body>Hello, browser!</body></html>";
  40. @end verbatim
  41. @noindent
  42. HTTP is a rather strict protocol and the client would certainly consider it "inappropriate" if we
  43. just sent the answer string "as is". Instead, it has to be wrapped with additional information stored in so-called headers and footers. Most of the work in this area is done by the library for us---we
  44. just have to ask. Our reply string packed in the necessary layers will be called a "response".
  45. To obtain such a response we hand our data (the reply--string) and its size over to the
  46. @code{MHD_create_response_from_buffer} function. The last two parameters basically tell @emph{MHD}
  47. that we do not want it to dispose the message data for us when it has been sent and there also needs
  48. no internal copy to be done because the @emph{constant} string won't change anyway.
  49. @verbatim
  50. struct MHD_Response *response;
  51. int ret;
  52. response = MHD_create_response_from_buffer (strlen (page),
  53. (void*) page, MHD_RESPMEM_PERSISTENT);
  54. @end verbatim
  55. @noindent
  56. Now that the the response has been laced up, it is ready for delivery and can be queued for sending.
  57. This is done by passing it to another @emph{GNU libmicrohttpd} function. As all our work was done in
  58. the scope of one function, the recipient is without doubt the one associated with the
  59. local variable @code{connection} and consequently this variable is given to the queue function.
  60. Every HTTP response is accompanied by a status code, here "OK", so that the client knows
  61. this response is the intended result of his request and not due to some error or malfunction.
  62. Finally, the packet is destroyed and the return value from the queue returned,
  63. already being set at this point to either MHD_YES or MHD_NO in case of success or failure.
  64. @verbatim
  65. ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  66. MHD_destroy_response (response);
  67. return ret;
  68. }
  69. @end verbatim
  70. @noindent
  71. With the primary task of our server implemented, we can start the actual server daemon which will listen
  72. on @code{PORT} for connections. This is done in the main function.
  73. @verbatim
  74. int main ()
  75. {
  76. struct MHD_Daemon *daemon;
  77. daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
  78. &answer_to_connection, NULL, MHD_OPTION_END);
  79. if (NULL == daemon) return 1;
  80. @end verbatim
  81. @noindent
  82. The first parameter is one of three possible modes of operation. Here we want the daemon to run in
  83. a separate thread and to manage all incoming connections in the same thread. This means that while
  84. producing the response for one connection, the other connections will be put on hold. In this
  85. example, where the reply is already known and therefore the request is served quickly, this poses no problem.
  86. We will allow all clients to connect regardless of their name or location, therefore we do not check
  87. them on connection and set the third and fourth parameter to NULL.
  88. Parameter five is the address of the function we want to be called whenever a new connection has been
  89. established. Our @code{answer_to_connection} knows best what the client wants and needs no additional
  90. information (which could be passed via the next parameter) so the next (sixth) parameter is NULL. Likewise,
  91. we do not need to pass extra options to the daemon so we just write the MHD_OPTION_END as the last parameter.
  92. As the server daemon runs in the background in its own thread, the execution flow in our main
  93. function will continue right after the call. Because of this, we must delay the execution flow in the
  94. main thread or else the program will terminate prematurely. We let it pause in a processing-time
  95. friendly manner by waiting for the enter key to be pressed. In the end, we stop the daemon so it can
  96. do its cleanup tasks.
  97. @verbatim
  98. getchar ();
  99. MHD_stop_daemon (daemon);
  100. return 0;
  101. }
  102. @end verbatim
  103. @noindent
  104. The first example is now complete.
  105. Compile it with
  106. @verbatim
  107. cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
  108. -L$PATH_TO_LIBMHD_LIBS -lmicrohttpd
  109. @end verbatim
  110. with the two paths set accordingly and run it.
  111. Now open your favorite Internet browser and go to the address @code{http://localhost:8888/}, provided that 8888
  112. is the port you chose. If everything works as expected, the browser will present the message of the
  113. static HTML page it got from our minimal server.
  114. @heading Remarks
  115. To keep this first example as small as possible, some drastic shortcuts were taken and are to be
  116. discussed now.
  117. Firstly, there is no distinction made between the kinds of requests a client could send. We implied
  118. that the client sends a GET request, that means, that he actually asked for some data. Even when
  119. it is not intended to accept POST requests, a good server should at least recognize that this
  120. request does not constitute a legal request and answer with an error code. This can be easily
  121. implemented by checking if the parameter @code{method} equals the string "GET" and returning a
  122. @code{MHD_NO} if not so.
  123. Secondly, the above practice of queuing a response upon the first call of the callback function
  124. brings with it some limitations. This is because the content of the message body will not be
  125. received if a response is queued in the first iteration. Furthermore, the connection will be closed
  126. right after the response has been transferred then. This is typically not what you want as it
  127. disables HTTP pipelining. The correct approach is to simply not queue a message on the first
  128. callback unless there is an error. The @code{void**} argument to the callback provides a location
  129. for storing information about the history of the connection; for the first call, the pointer
  130. will point to NULL. A simplistic way to differentiate the first call from others is to check
  131. if the pointer is NULL and set it to a non-NULL value during the first call.
  132. Both of these issues you will find addressed in the official @code{minimal_example.c} residing in
  133. the @code{src/examples} directory of the @emph{MHD} package. The source code of this
  134. program should look very familiar to you by now and easy to understand.
  135. For our example, we create the response from a static (persistent) buffer in memory and thus pass @code{MHD_RESPMEM_PERSISTENT} to the response construction
  136. function. In the usual case, responses are not transmitted immediately
  137. after being queued. For example, there might be other data on the system that needs to be sent with
  138. a higher priority. Nevertheless, the queue function will return successfully---raising the problem
  139. that the data we have pointed to may be invalid by the time it is about being sent. This is not an
  140. issue here because we can expect the @code{page} string, which is a constant @emph{string literal}
  141. here, to be static. That means it will be present and unchanged for as long as the program runs.
  142. For dynamic data, one could choose to either have @emph{MHD} free the memory @code{page} points
  143. to itself when it is not longer needed (by passing @code{MHD_RESPMEM_MUST_FREE}) or, alternatively, have the library to make and manage
  144. its own copy of it (by passing @code{MHD_RESPMEM_MUST_COPY}). Naturally, this last option is the most expensive.
  145. @heading Exercises
  146. @itemize @bullet
  147. @item
  148. While the server is running, use a program like @code{telnet} or @code{netcat} to connect to it. Try to form a
  149. valid HTTP 1.1 request yourself like
  150. @verbatim
  151. GET /dontcare HTTP/1.1
  152. Host: itsme
  153. <enter>
  154. @end verbatim
  155. @noindent
  156. and see what the server returns to you.
  157. @item
  158. Also, try other requests, like POST, and see how our server does not mind and why.
  159. How far in malforming a request can you go before the builtin functionality of @emph{MHD} intervenes
  160. and an altered response is sent? Make sure you read about the status codes in the @emph{RFC}.
  161. @item
  162. Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the daemon in @code{main}.
  163. Mind the special format of the parameter list here which is described in the manual. How indulgent
  164. is the server now to your input?
  165. @item
  166. Let the main function take a string as the first command line argument and pass @code{argv[1]} to
  167. the @code{MHD_start_daemon} function as the sixth parameter. The address of this string will be
  168. passed to the callback function via the @code{cls} variable. Decorate the text given at the command
  169. line when the server is started with proper HTML tags and send it as the response instead of the
  170. former static string.
  171. @item
  172. @emph{Demanding:} Write a separate function returning a string containing some useful information,
  173. for example, the time. Pass the function's address as the sixth parameter and evaluate this function
  174. on every request anew in @code{answer_to_connection}. Remember to free the memory of the string
  175. every time after satisfying the request.
  176. @end itemize