Blame doc/chapters/responseheaders.inc

Packit 875988
Now that we are able to inspect the incoming request in great detail,
Packit 875988
this chapter discusses the means to enrich the outgoing responses likewise.
Packit 875988
Packit 875988
As you have learned in the @emph{Hello, Browser} chapter, some obligatory 
Packit 875988
header fields are added and set automatically for simple responses by the library
Packit 875988
itself but if more advanced features are desired, additional fields have to be created.
Packit 875988
One of the possible fields is the content type field and an example will be developed around it.
Packit 875988
This will lead to an application capable of correctly serving different types of files.
Packit 875988
Packit 875988
Packit 875988
When we responded with HTML page packed in the static string previously, the client had no choice
Packit 875988
but guessing about how to handle the response, because the server had not told him. 
Packit 875988
What if we had sent a picture or a sound file?  Would the message have been understood
Packit 875988
or merely been displayed as an endless stream of random characters in the browser?
Packit 875988
This is what the mime content types are for. The header of the response is extended
Packit 875988
by certain information about how the data is to be interpreted. 
Packit 875988
Packit 875988
To introduce the concept, a picture of the format @emph{PNG} will be sent to the client 
Packit 875988
and labeled accordingly with @code{image/png}.
Packit 875988
Once again, we can base the new example on the @code{hellobrowser} program.
Packit 875988
Packit 875988
@verbatim
Packit 875988
#define FILENAME "picture.png"
Packit 875988
#define MIMETYPE "image/png"
Packit 875988
Packit 875988
static int 
Packit 875988
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
  unsigned char *buffer = NULL;
Packit 875988
  struct MHD_Response *response;
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
 
Packit 875988
We want the program to open the file for reading and determine its size:
Packit 875988
@verbatim
Packit 875988
  int fd;
Packit 875988
  int ret;
Packit 875988
  struct stat sbuf;
Packit 875988
Packit 875988
  if (0 != strcmp (method, "GET"))
Packit 875988
    return MHD_NO;
Packit 875988
  if ( (-1 == (fd = open (FILENAME, O_RDONLY))) ||
Packit 875988
       (0 != fstat (fd, &sbuf)) )
Packit 875988
    {
Packit 875988
     /* error accessing file */
Packit 875988
      /* ... (see below) */
Packit 875988
    }
Packit 875988
 /* ... (see below) */
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
When dealing with files, there is a lot that could go wrong on the
Packit 875988
server side and if so, the client should be informed with @code{MHD_HTTP_INTERNAL_SERVER_ERROR}.
Packit 875988
Packit 875988
@verbatim 
Packit 875988
      /* error accessing file */
Packit 875988
     if (fd != -1) close (fd);
Packit 875988
      const char *errorstr =
Packit 875988
        "<html><body>An internal server error has occured!\
Packit 875988
                              </body></html>";
Packit 875988
      response =
Packit 875988
	MHD_create_response_from_buffer (strlen (errorstr), 
Packit 875988
				         (void *) errorstr, 
Packit 875988
				         MHD_RESPMEM_PERSISTENT);
Packit 875988
      if (response)
Packit 875988
        {
Packit 875988
          ret =
Packit 875988
            MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
Packit 875988
                                response);
Packit 875988
          MHD_destroy_response (response);
Packit 875988
Packit 875988
          return MHD_YES;
Packit 875988
        }
Packit 875988
      else
Packit 875988
        return MHD_NO;
Packit 875988
  if (!ret) 
Packit 875988
    {
Packit 875988
      const char *errorstr = "<html><body>An internal server error has occured!\
Packit 875988
                              </body></html>";
Packit 875988
Packit 875988
      if (buffer) free(buffer);
Packit 875988
    
Packit 875988
      response = MHD_create_response_from_buffer (strlen(errorstr), (void*) errorstr,
Packit 875988
                                                  MHD_RESPMEM_PERSISTENT);
Packit 875988
Packit 875988
      if (response)
Packit 875988
        {     
Packit 875988
          ret = MHD_queue_response (connection, 
Packit 875988
	      			    MHD_HTTP_INTERNAL_SERVER_ERROR, 
Packit 875988
				    response);
Packit 875988
          MHD_destroy_response (response);
Packit 875988
Packit 875988
          return MHD_YES;    
Packit 875988
        } 
Packit 875988
      else return MHD_NO;
Packit 875988
    }
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
Note that we nevertheless have to create a response object even for sending a simple error code.
Packit 875988
Otherwise, the connection would just be closed without comment, leaving the client curious about
Packit 875988
what has happened.
Packit 875988
Packit 875988
But in the case of success a response will be constructed directly from the file descriptor:
Packit 875988
Packit 875988
@verbatim
Packit 875988
     /* error accessing file */
Packit 875988
     /* ... (see above) */
Packit 875988
    }
Packit 875988
Packit 875988
  response =
Packit 875988
    MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0);
Packit 875988
  MHD_add_response_header (response, "Content-Type", MIMETYPE);
Packit 875988
  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
Packit 875988
  MHD_destroy_response (response);
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
Note that the response object will take care of closing the file desciptor for us.
Packit 875988
Packit 875988
Up to this point, there was little new. The actual novelty is that we enhance the header with the
Packit 875988
meta data about the content. Aware of the field's name we want to add, it is as easy as that:
Packit 875988
@verbatim
Packit 875988
MHD_add_response_header(response, "Content-Type", MIMETYPE);
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
We do not have to append a colon expected by the protocol behind the first 
Packit 875988
field---@emph{GNU libhttpdmicro} will take care of this. 
Packit 875988
Packit 875988
The function finishes with the well-known lines
Packit 875988
@verbatim
Packit 875988
  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
Packit 875988
  MHD_destroy_response (response);
Packit 875988
  return ret;
Packit 875988
}
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
The complete program @code{responseheaders.c} is in the @code{examples} section as usual.
Packit 875988
Find a @emph{PNG} file you like and save it to the directory the example is run from under the name
Packit 875988
@code{picture.png}. You should find the image displayed on your browser if everything worked well.
Packit 875988
Packit 875988
@heading Remarks
Packit 875988
The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616}
Packit 875988
already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead
Packit 875988
of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could
Packit 875988
add custom response headers without violating the protocol. Whether, and how, the client would react
Packit 875988
to these custom header is up to the receiver. Likewise, the client is allowed to send custom request
Packit 875988
headers to the server as well, opening up yet more possibilities how client and server could 
Packit 875988
communicate with each other.
Packit 875988
Packit 875988
The method of creating the response from a file on disk only works for static content.
Packit 875988
Serving dynamically created responses will be a topic of a future chapter.
Packit 875988
Packit 875988
@heading Exercises
Packit 875988
@itemize @bullet
Packit 875988
Packit 875988
@item
Packit 875988
Remember that the original program was written under a few assumptions---a static response
Packit 875988
using a local file being one of them. In order to simulate a very large or hard to reach file that cannot be provided
Packit 875988
instantly, postpone the queuing in the callback with the @code{sleep} function for 30 seconds 
Packit 875988
@emph{if} the file @code{/big.png} is requested (but deliver the same as above). A request for
Packit 875988
@code{/picture.png} should provide just the same but without any artificial delays.
Packit 875988
Packit 875988
Now start two instances of your browser (or even use two machines) and see how the second client
Packit 875988
is put on hold while the first waits for his request on the slow file to be fulfilled.
Packit 875988
Packit 875988
Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is 
Packit 875988
started and try again.
Packit 875988
Packit 875988
Packit 875988
@item
Packit 875988
Did you succeed in implementing the clock exercise yet? This time, let the server save the 
Packit 875988
program's start time @code{t} and implement a response simulating a countdown that reaches 0 at
Packit 875988
@code{t+60}. Returning a message saying on which point the countdown is, the response should
Packit 875988
ultimately be to reply "Done" if the program has been running long enough,
Packit 875988
Packit 875988
An unofficial, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with
Packit 875988
the uppercase words substituted to tell the client it should request the given resource after 
Packit 875988
the given delay again. Improve your program in that the browser (any modern browser should work)
Packit 875988
automatically reconnects and asks for the status again every 5 seconds or so. The URL would have
Packit 875988
to be composed so that it begins with "http://", followed by the @emph{URI} the server is reachable
Packit 875988
from the client's point of view.
Packit 875988
Packit 875988
Maybe you want also to visualize the countdown as a status bar by creating a 
Packit 875988
@code{} consisting of one row and @code{n} columns whose fields contain small images of either
Packit 875988
a red or a green light.
Packit 875988
Packit 875988
@end itemize