libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit a1afe5d1c253dd3382ac5b2591d8eb3ea3ed181f
parent c06b590afb3be1e876be1fcb887fd3eca9b1d65e
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 17 Nov 2010 11:24:22 +0000

allowing error signalling for chunked responses

Diffstat:
MChangeLog | 7+++++++
Mdoc/microhttpd.texi | 25++++++++++++++++++++++++-
Msrc/daemon/connection.c | 20+++++++++++++++++---
Msrc/examples/fileserver_example_dirs.c | 2+-
Msrc/examples/minimal_example_comet.c | 2+-
Msrc/include/microhttpd.h | 40+++++++++++++++++++++++++++++++---------
Msrc/testcurl/daemontest_get_chunked.c | 4++--
Msrc/testzzuf/daemontest_get_chunked.c | 4++--
8 files changed, 85 insertions(+), 19 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,10 @@ +Wed Nov 17 12:16:53 CET 2010 + Allowing signalling of errors in generating chunked responses to + clients (by closing connectins) using the new + MHD_CONTENT_READER_END_WITH_ERROR ((size_t)-2) return value. Also + introducing MHD_CONTENT_READER_END_OF_STREAM constant instead + of (size_t) -1 / SIZE_MAX. + Sun Nov 14 20:45:45 CET 2010 Adding API call to generate HTTP footers in response. -CG diff --git a/doc/microhttpd.texi b/doc/microhttpd.texi @@ -857,7 +857,7 @@ iteration. @end deftypefn -@deftypefn {Function Pointer} int {*MHD_ContentReaderCallback} (void *cls, uint64_t pos, char *buf, int max) +@deftypefn {Function Pointer} int {*MHD_ContentReaderCallback} (void *cls, uint64_t pos, char *buf, size_t max) Callback used by @mhd{} in order to obtain content. The callback has to copy at most @var{max} bytes of content into @var{buf}. The total number of bytes that has been placed into @var{buf} should be returned. @@ -870,6 +870,29 @@ that runs in internal @cfunction{select} mode is an error (since it would result in busy waiting) and cause the program to be aborted (@cfunction{abort}). +While usually the callback simply returns the number of bytes written +into @var{buf}, there are two special return value: + +@code{MHD_CONTENT_READER_END_OF_STREAM} (-1) should be returned +for the regular end of transmission (with chunked encoding, MHD will then +terminate the chunk and send any HTTP footers that might be +present; without chunked encoding and given an unknown +response size, @mhd{} will simply close the connection; note +that while returning @code{MHD_CONTENT_READER_END_OF_STREAM} is not technically +legal if a response size was specified, MHD accepts this +and treats it just as @code{MHD_CONTENT_READER_END_WITH_ERROR}. + +@code{MHD_CONTENT_READER_END_WITH_ERROR} (-2) is used to indicate a server +error generating the response; this will cause @mhd{} to simply +close the connection immediately. If a response size was +given or if chunked encoding is in use, this will indicate +an error to the client. Note, however, that if the client +does not know a response size and chunked encoding is not in +use, then clients will not be able to tell the difference between +@code{MHD_CONTENT_READER_END_WITH_ERROR} and +@code{MHD_CONTENT_READER_END_OF_STREAM}. +This is not a limitation of @mhd{} but rather of the HTTP protocol. + @table @var @item cls custom value selected at callback registration time; diff --git a/src/daemon/connection.c b/src/daemon/connection.c @@ -357,14 +357,15 @@ try_ready_normal_body (struct MHD_Connection *connection) NULL #endif ); - if (ret == -1) + if ( (ret == MHD_CONTENT_READER_END_OF_STREAM) || + (ret == MHD_CONTENT_READER_END_WITH_ERROR) ) { /* either error or http 1.0 transfer, close socket! */ #if DEBUG_CLOSE #if HAVE_MESSAGES MHD_DLOG (connection->daemon, - "Closing connection (end of response)\n"); + "Closing connection (end of response or error)\n"); #endif #endif response->total_size = connection->response_write_position; @@ -444,7 +445,20 @@ try_ready_chunked_body (struct MHD_Connection *connection) &connection->write_buffer[sizeof (cbuf)], connection->write_buffer_size - sizeof (cbuf) - 2); } - if (ret == -1) + if (ret == MHD_CONTENT_READER_END_WITH_ERROR) + { + /* error, close socket! */ +#if DEBUG_CLOSE +#if HAVE_MESSAGES + MHD_DLOG (connection->daemon, + "Closing connection (error generating response)\n"); +#endif +#endif + response->total_size = connection->response_write_position; + connection_close_error (connection); + return MHD_NO; + } + if (ret == MHD_CONTENT_READER_END_OF_STREAM) { /* end of message, signal other side! */ strcpy (connection->write_buffer, "0\r\n"); diff --git a/src/examples/fileserver_example_dirs.c b/src/examples/fileserver_example_dirs.c @@ -66,7 +66,7 @@ dir_reader (void *cls, uint64_t pos, char *buf, size_t max) { e = readdir (dir); if (e == NULL) - return -1; + return MHD_CONTENT_READER_END_OF_STREAM; } while (e->d_name[0] == '.'); return snprintf (buf, max, "<a href=\"/%s\">%s</a><br>", diff --git a/src/examples/minimal_example_comet.c b/src/examples/minimal_example_comet.c @@ -56,7 +56,7 @@ ahc_echo (void *cls, return MHD_YES; } *ptr = NULL; /* reset when done */ - response = MHD_create_response_from_callback (-1, + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 80, &data_generator, NULL, NULL); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -133,6 +133,15 @@ extern "C" #define MHD_SIZE_UNKNOWN ((uint64_t) -1LL) #endif +#ifdef SIZE_MAX +#define MHD_CONTENT_READER_END_OF_STREAM SIZE_MAX +#define MHD_CONTENT_READER_END_WITH_ERROR (SIZE_MAX - 1) +#else +#define MHD_CONTENT_READER_END ((size_t) -1LL) +#define MHD_CONTENT_READER_END_WITH_ERROR (((size_t) -LL) - 1) +#endif + + /** * HTTP response codes. */ @@ -868,15 +877,28 @@ typedef int * obtained from the content reader so far. * @param buf where to copy the data * @param max maximum number of bytes to copy to buf (size of buf) - * @return -1 for the end of transmission (or on error); - * if a content transfer size was pre-set and the callback - * has provided fewer than that amount of data, - * MHD will close the connection with the client; - * if no content size was specified and this is an - * http 1.1 connection using chunked encoding, MHD will - * interpret "-1" as the normal end of the transfer - * (possibly allowing the client to perform additional - * requests using the same TCP connection). + * @return number of bytes written to 'buf'; + * 0 is legal unless we are running in internal select mode (since + * this would cause busy-waiting); 0 in external select mode + * will cause this function to be called again once the external + * select calls MHD again; + * MHD_CONTENT_READER_END_OF_STREAM (-1) for the regular + * end of transmission (with chunked encoding, MHD will then + * terminate the chunk and send any HTTP footers that might be + * present; without chunked encoding and given an unknown + * response size, MHD will simply close the connection; note + * that while returning END_OF_STREAM is not technically + * legal if a response size was specified, MHD accepts this + * and treats it just as MHD_CONTENT_READER_END_WITH_ERROR; + * MHD_CONTENT_READER_END_WITH_ERROR (-2) to indicate a server + * error generating the response; this will cause MHD to simply + * close the connection immediately. If a response size was + * given or if chunked encoding is in use, this will indicate + * an error to the client. Note, however, that if the client + * does not know a response size and chunked encoding is not in + * use, then clients will not be able to tell the difference between + * MHD_CONTENT_READER_END_WITH_ERROR and MHD_CONTENT_READER_END_OF_STREAM. + * This is not a limitation of MHD but rather of the HTTP protocol. */ typedef ssize_t (*MHD_ContentReaderCallback) (void *cls, diff --git a/src/testcurl/daemontest_get_chunked.c b/src/testcurl/daemontest_get_chunked.c @@ -71,7 +71,7 @@ crc (void *cls, uint64_t pos, char *buf, size_t max) if (pos == 128 * 10) { MHD_add_response_header (*responseptr, "Footer", "working"); - return -1; /* end of stream */ + return MHD_CONTENT_READER_END_OF_STREAM; } if (max < 128) abort (); /* should not happen in this testcase... */ @@ -113,7 +113,7 @@ ahc_echo (void *cls, responseptr = malloc (sizeof (struct MHD_Response *)); if (responseptr == NULL) return MHD_NO; - response = MHD_create_response_from_callback (-1, + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &crc, responseptr, &crcf); *responseptr = response; diff --git a/src/testzzuf/daemontest_get_chunked.c b/src/testzzuf/daemontest_get_chunked.c @@ -69,7 +69,7 @@ crc (void *cls, uint64_t pos, char *buf, size_t max) if (pos == 128 * 10) { MHD_add_response_header (*responseptr, "Footer", "working"); - return -1; /* end of stream */ + return MHD_CONTENT_READER_END_OF_STREAM; } if (max < 128) abort (); /* should not happen in this testcase... */ @@ -109,7 +109,7 @@ ahc_echo (void *cls, return MHD_YES; } responseptr = malloc (sizeof (struct MHD_Response *)); - response = MHD_create_response_from_callback (-1, + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &crc, responseptr, &crcf); *responseptr = response;