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