Blame doc/chapters/hellobrowser.inc

Packit 875988
The most basic task for a HTTP server is to deliver a static text message to any client connecting to it.
Packit 875988
Given that this is also easy to implement, it is an excellent problem to start with.
Packit 875988
Packit 875988
For now, the particular URI the client asks for shall have no effect on the message that will
Packit 875988
be returned. In addition, the server shall end the connection after the message has been sent so that
Packit 875988
the client will know there is nothing more to expect.
Packit 875988
Packit 875988
The C program @code{hellobrowser.c}, which is to be found in the examples section, does just that.
Packit 875988
If you are very eager, you can compile and start it right away but it is advisable to type the
Packit 875988
lines in by yourself as they will be discussed and explained in detail.
Packit 875988
Packit 875988
After the necessary includes and the definition of the port which our server should listen on
Packit 875988
@verbatim
Packit 875988
#include <sys/types.h>
Packit 875988
#include <sys/select.h>
Packit 875988
#include <sys/socket.h>
Packit 875988
#include <microhttpd.h>
Packit 875988
Packit 875988
#define PORT 8888
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
the desired behaviour of our server when HTTP request arrive has to be implemented. We already have
Packit 875988
agreed that it should not care about the particular details of the request, such as who is requesting
Packit 875988
what. The server will respond merely with the same small HTML page to every request.
Packit 875988
Packit 875988
The function we are going to write now will be called by @emph{GNU libmicrohttpd} every time an
Packit 875988
appropriate request comes in. While the name of this callback function is arbitrary, its parameter
Packit 875988
list has to follow a certain layout. So please, ignore the lot of parameters for now, they will be
Packit 875988
explained at the point they are needed. We have to use only one of them,
Packit 875988
@code{struct MHD_Connection *connection}, for the minimalistic functionality we want to achieve at the moment.
Packit 875988
Packit 875988
This parameter is set by the @emph{libmicrohttpd} daemon and holds the necessary information to
Packit 875988
relate the call with a certain connection. Keep in mind that a server might have to satisfy hundreds
Packit 875988
of concurrent connections and we have to make sure that the correct data is sent to the destined
Packit 875988
client. Therefore, this variable is a means to refer to a particular connection if we ask the
Packit 875988
daemon to sent the reply.
Packit 875988
Packit 875988
Talking about the reply, it is defined as a string right after the function header
Packit 875988
@verbatim
Packit 875988
int answer_to_connection (void *cls, struct MHD_Connection *connection,
Packit 875988
                          const char *url,
Packit 875988
                          const char *method, const char *version,
Packit 875988
                          const char *upload_data,
Packit 875988
                          size_t *upload_data_size, void **con_cls)
Packit 875988
{
Packit 875988
  const char *page  = "<html><body>Hello, browser!</body></html>";
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
HTTP is a rather strict protocol and the client would certainly consider it "inappropriate" if we
Packit 875988
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
Packit 875988
just have to ask. Our reply string packed in the necessary layers will be called a "response".
Packit 875988
To obtain such a response we hand our data (the reply--string) and its size over to the
Packit 875988
@code{MHD_create_response_from_buffer} function. The last two parameters basically tell @emph{MHD}
Packit 875988
that we do not want it to dispose the message data for us when it has been sent and there also needs
Packit 875988
no internal copy to be done because the @emph{constant} string won't change anyway.
Packit 875988
Packit 875988
@verbatim
Packit 875988
  struct MHD_Response *response;
Packit 875988
  int ret;
Packit 875988
Packit 875988
  response = MHD_create_response_from_buffer (strlen (page),
Packit 875988
                                            (void*) page, MHD_RESPMEM_PERSISTENT);
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
Now that the the response has been laced up, it is ready for delivery and can be queued for sending.
Packit 875988
This is done by passing it to another @emph{GNU libmicrohttpd} function. As all our work was done in
Packit 875988
the scope of one function, the recipient is without doubt the one associated with the
Packit 875988
local variable @code{connection} and consequently this variable is given to the queue function.
Packit 875988
Every HTTP response is accompanied by a status code, here "OK", so that the client knows
Packit 875988
this response is the intended result of his request and not due to some error or malfunction.
Packit 875988
Packit 875988
Finally, the packet is destroyed and the return value from the queue returned,
Packit 875988
already being set at this point to either MHD_YES or MHD_NO in case of success or failure.
Packit 875988
Packit 875988
@verbatim
Packit 875988
  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
Packit 875988
  MHD_destroy_response (response);
Packit 875988
Packit 875988
  return ret;
Packit 875988
}
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
With the primary task of our server implemented, we can start the actual server daemon which will listen
Packit 875988
on @code{PORT} for connections. This is done in the main function.
Packit 875988
@verbatim
Packit 875988
int main ()
Packit 875988
{
Packit 875988
  struct MHD_Daemon *daemon;
Packit 875988
Packit 875988
  daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
Packit 875988
                             &answer_to_connection, NULL, MHD_OPTION_END);
Packit 875988
  if (NULL == daemon) return 1;
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
The first parameter is one of three possible modes of operation. Here we want the daemon to run in
Packit 875988
a separate thread and to manage all incoming connections in the same thread. This means that while
Packit 875988
producing the response for one connection, the other connections will be put on hold. In this
Packit 875988
example, where the reply is already known and therefore the request is served quickly, this poses no problem.
Packit 875988
Packit 875988
We will allow all clients to connect regardless of their name or location, therefore we do not check
Packit 875988
them on connection and set the third and fourth parameter to NULL.
Packit 875988
Packit 875988
Parameter five is the address of the function we want to be called whenever a new connection has been
Packit 875988
established. Our @code{answer_to_connection} knows best what the client wants and needs no additional
Packit 875988
information (which could be passed via the next parameter) so the next (sixth) parameter is NULL. Likewise,
Packit 875988
we do not need to pass extra options to the daemon so we just write the MHD_OPTION_END as the last parameter.
Packit 875988
Packit 875988
As the server daemon runs in the background in its own thread, the execution flow in our main
Packit 875988
function will contine right after the call. Because of this, we must delay the execution flow in the
Packit 875988
main thread or else the program will terminate prematurely. We let it pause in a processing-time
Packit 875988
friendly manner by waiting for the enter key to be pressed. In the end, we stop the daemon so it can
Packit 875988
do its cleanup tasks.
Packit 875988
@verbatim
Packit 875988
  getchar ();
Packit 875988
Packit 875988
  MHD_stop_daemon (daemon);
Packit 875988
  return 0;
Packit 875988
}
Packit 875988
Packit 875988
@end verbatim
Packit 875988
Packit 875988
@noindent
Packit 875988
The first example is now complete.
Packit 875988
Packit 875988
Compile it with
Packit 875988
@verbatim
Packit 875988
cc hellobrowser.c -o hellobrowser -I$PATH_TO_LIBMHD_INCLUDES
Packit 875988
  -L$PATH_TO_LIBMHD_LIBS -lmicrohttpd
Packit 875988
@end verbatim
Packit 875988
with the two paths set accordingly and run it.
Packit 875988
Packit 875988
Now open your favorite Internet browser and go to the address @code{http://localhost:8888/}, provided that 8888
Packit 875988
is the port you chose. If everything works as expected, the browser will present the message of the
Packit 875988
static HTML page it got from our minimal server.
Packit 875988
Packit 875988
@heading Remarks
Packit 875988
To keep this first example as small as possible, some drastic shortcuts were taken and are to be
Packit 875988
discussed now.
Packit 875988
Packit 875988
Firstly, there is no distinction made between the kinds of requests a client could send. We implied
Packit 875988
that the client sends a GET request, that means, that he actually asked for some data. Even when
Packit 875988
it is not intended to accept POST requests, a good server should at least recognize that this
Packit 875988
request does not constitute a legal request and answer with an error code. This can be easily
Packit 875988
implemented by checking if the parameter @code{method} equals the string "GET" and returning a
Packit 875988
@code{MHD_NO} if not so.
Packit 875988
Packit 875988
Secondly, the above practice of queuing a response upon the first call of the callback function
Packit 875988
brings with it some limitations.  This is because the content of the message body will not be
Packit 875988
received if a response is queued in the first iteration.  Furthermore, the connection will be closed
Packit 875988
right after the response has been transferred then.  This is typically not what you want as it
Packit 875988
disables HTTP pipelining.  The correct approach is to simply not queue a message on the first
Packit 875988
callback unless there is an error.  The @code{void**} argument to the callback provides a location
Packit 875988
for storing information about the history of the connection; for the first call, the pointer
Packit 875988
will point to NULL.  A simplistic way to differenciate the first call from others is to check
Packit 875988
if the pointer is NULL and set it to a non-NULL value during the first call.
Packit 875988
Packit 875988
Both of these issues you will find addressed in the official @code{minimal_example.c} residing in
Packit 875988
the @code{src/examples} directory of the @emph{MHD} package.  The source code of this
Packit 875988
program should look very familiar to you by now and easy to understand.
Packit 875988
Packit 875988
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
Packit 875988
function. In the usual case, responses are not transmitted immediately
Packit 875988
after being queued. For example, there might be other data on the system that needs to be sent with
Packit 875988
a higher priority. Nevertheless, the queue function will return successfully---raising the problem
Packit 875988
that the data we have pointed to may be invalid by the time it is about being sent. This is not an
Packit 875988
issue here because we can expect the @code{page} string, which is a constant @emph{string literal}
Packit 875988
here, to be static. That means it will be present and unchanged for as long as the program runs.
Packit 875988
For dynamic data, one could choose to either have @emph{MHD} free the memory @code{page} points
Packit 875988
to itself when it is not longer needed (by passing @code{MHD_RESPMEM_MUST_FREE}) or, alternatively, have the library to make and manage
Packit 875988
its own copy of it (by passing @code{MHD_RESPMEM_MUST_COPY}).  Naturally, this last option is the most expensive.
Packit 875988
Packit 875988
@heading Exercises
Packit 875988
@itemize @bullet
Packit 875988
@item
Packit 875988
While the server is running, use a program like @code{telnet} or @code{netcat} to connect to it. Try to form a
Packit 875988
valid HTTP 1.1 request yourself like
Packit 875988
@verbatim
Packit 875988
GET /dontcare HTTP/1.1
Packit 875988
Host: itsme
Packit 875988
<enter>
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
and see what the server returns to you.
Packit 875988
Packit 875988
Packit 875988
@item
Packit 875988
Also, try other requests, like POST, and see how our server does not mind and why.
Packit 875988
How far in malforming a request can you go before the builtin functionality of @emph{MHD} intervenes
Packit 875988
and an altered response is sent? Make sure you read about the status codes in the @emph{RFC}.
Packit 875988
Packit 875988
Packit 875988
@item
Packit 875988
Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the daemon in @code{main}.
Packit 875988
Mind the special format of the parameter list here which is described in the manual. How indulgent
Packit 875988
is the server now to your input?
Packit 875988
Packit 875988
Packit 875988
@item
Packit 875988
Let the main function take a string as the first command line argument and pass @code{argv[1]} to
Packit 875988
the @code{MHD_start_daemon} function as the sixth parameter. The address of this string will be
Packit 875988
passed to the callback function via the @code{cls} variable. Decorate the text given at the command
Packit 875988
line when the server is started with proper HTML tags and send it as the response instead of the
Packit 875988
former static string.
Packit 875988
Packit 875988
Packit 875988
@item
Packit 875988
@emph{Demanding:} Write a separate function returning a string containing some useful information,
Packit 875988
for example, the time. Pass the function's address as the sixth parameter and evaluate this function
Packit 875988
on every request anew in @code{answer_to_connection}. Remember to free the memory of the string
Packit 875988
every time after satisfying the request.
Packit 875988
Packit 875988
@end itemize