Home Announcements Schedule Assignments Project Reading Reviews Resources

libmicrohttpd Checkpoint 1: Starting and Stopping the Daemon

Due: Feb 5th, 2007 (not graded)

Implement the following functions:

You will also need to start writing the MHD_Daemon struct. Recall that MHD_Daemon is defined in microhttpd.h as a forward definition. You'll need to fill it out to hold the data necessary for running an instance of a web server.

MHD_start_daemon

First, go to the daemon.c and take a look at the definition of this function. The function takes 6 arguments. Yes 6!:

struct MHD_Daemon * MHD_start_daemon(unsigned int options,
                                     unsigned short port,
                                     MHD_AcceptPolicyCallback apc,
                                     void * apc_cls,
                                     MHD_AccessHandlerCallback dh,
                                     void * dh_cls)

The meaning of the arguments are as follows:

  1. options: A set of options passed to determine how the web server is configured. Look at microhttpd.h and locate the MHD_OPTION enumeration. This lists the possible options that might be passed to the web server.
  2. port: The port number that the web server will listen to for connections.
  3. apc: A pointer to a function that decides if a connection will be accepted or not. The library user writes this function and passes it to the web server. Before we accept a connection, the library calls this function which returns MHD_YES, meaning to accept the connection, or MHD_NO, meaning to reject the connection.
  4. apc_cls: A void * argument that is passed as the first argument to apc, the AcceptPolicyCallback function. Typically, a library user will point to some data structure that holds the information they need to decide whether or not to accept a connection.
  5. dh: A pointer to a function that handles a client request. The library user writes this function and passes it to the web server on creation. When data comes in on a socket, we parse it using the HTTP protocol, determine what method was used, and call this function so that the user of the library can handle it.
  6. dh_cls: A void * argument that is passed as the first argument to dh, the AccessHandlerCallback. As with apc_cls, this typically points to some structure that stores information on behalf of the library user.
As you can see, the arguments to MHD_start_daemon are values that must be stored with the instance of the web server. Thus, at minimum these will be stored in your MHD_Daemon structure.

You should notice from the function definition that MHD_start_daemon returns a pointer to a MHD_Daemon structure. This should be a hint that in this function, you will have to allocate and return an MHD_Daemon structure. How do you allocate in C? While I'm sure you have done this before, here's an example:

struct MHD_Daemon *daemon = 
        (struct MHD_Daemon *) malloc(sizeof(struct MHD_Daemon));

The call to malloc needs to know the number of bytes you need to allocate from the operating system. sizeof will give you the size of something in bytes. However, malloc returns a void *, so we have to typecast this to a struct MHD_Daemon *. One should note that malloc does not initialize the memory allocated in any way. One could use calloc, for example, to allocate the memory and then set all bytes in that memory chunk to 0. However, if you are filling out the values of the MHD_Daemon struct, there's not really a need to clear everything out first.

What else goes inside MHD_start_daemon?

Besides just allocating the MHD structure and returning a pointer to it, you also need to write your first socket code. Recall from the socket tutorial that to write a server we have to to the following:

  1. Make a call to int socket(int domain, int type, int protocol) to create a socket from the operating system. This function returns the socket. To create a TCP socket, we pass (AF_INET, SOCK_STREAM, 0) as the parameters to socket.
  2. Make a call to int bind(int s, const struct sockaddr* name, socklen_t namelen). Prior to this you have to create an address structure that indicates what port you want to listen on.
  3. Make a call to int listen(int s, int backlog). At this point, your socket will actually start accepting connections to it. The first argument is your socket, the second argument is the number of TCP connections to queue. Thus, the socket will not complete the '3-way' handshake that TCP begins with, but it will queue up new TCP connections until you dequeue them with a call to accept. listen takes an argument that determines how many new connections can be queued up before they are rejected. If this value is too small, clients will be rejected before you can dequeue them with accept. A value too large will use up system resources needlessly.

You should not return from MHD_start_daemon until all of these steps have been taken. In addition, from these calls you can see that you will need to add some additional data to your MHD_Daemon struct (Hint: You need to store the socket you listen with somewhere!).

MHD_stop_daemon

In MHD_stop_daemon you simply need to close or shutdown. Typically, when using TCP sockets, we call shutdown so we can determine if any remaining data can be sent or received before the socket is closed. For example, we might call shutdown(listenfd, SHUT_RDWR) which would prevent any further reads or writes on the socket.