|
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
|