From 1c4f23db7458d79a04c8e68022ade67cb5ee006b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 26 Jul 2020 14:15:50 +0200 Subject: add ability to serve files from pipe --- ChangeLog | 4 +++ doc/libmicrohttpd.texi | 15 +++++++++++ src/include/microhttpd.h | 20 ++++++++++++--- src/microhttpd/connection.c | 3 +++ src/microhttpd/internal.h | 5 ++++ src/microhttpd/response.c | 61 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index ebbe7994..a2e24c02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sun 26 Jul 2020 01:56:54 PM CEST + Add MHD_create_response_from_pipe() to allow creating a response based + on data read from a pipe. -CG + Fri Jul 10 15:04:51 CEST 2020 Fixed Postprocessor URL-encoded parsing if '%' fell on boundary. -CG/MD diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi index 150dd048..fd776028 100644 --- a/doc/libmicrohttpd.texi +++ b/doc/libmicrohttpd.texi @@ -2018,6 +2018,21 @@ Return @code{NULL} on error (i.e. invalid arguments, out of memory). @end deftypefun +@deftypefun {struct MHD_Response *} MHD_create_response_from_pipe (uint64_t size, int fd) +Create a response object. The response object can be extended with +header information and then it can be used ONLY ONCE. + +@table @var +@item fd +file descriptor of the read-end of the pipe; will be +closed when response is destroyed. +The descriptor should be in blocking-IO mode. +@end table + +Return @code{NULL} on error (i.e. out of memory). +@end deftypefun + + @deftypefun {struct MHD_Response *} MHD_create_response_from_fd_at_offset (size_t size, int fd, off_t offset) Create a response object. The response object can be extended with header information and then it can be used any number of times. diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 09983ab7..7aff40f5 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h @@ -180,7 +180,7 @@ enum MHD_Result #define _MHD_EXTERN extern #elif defined (_WIN32) && defined(MHD_W32DLL) /* Define MHD_W32DLL when using MHD as W32 .DLL to speed up linker a little */ -#define _MHD_EXTERN __declspec (dllimport) +#define _MHD_EXTERN __declspec(dllimport) #else #define _MHD_EXTERN extern #endif @@ -262,10 +262,10 @@ typedef SOCKET MHD_socket; #ifndef _MHD_DEPR_FUNC #if defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1400 /* VS 2005 or later */ -#define _MHD_DEPR_FUNC(msg) __declspec (deprecated (msg)) +#define _MHD_DEPR_FUNC(msg) __declspec(deprecated (msg)) #elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1310 /* VS .NET 2003 deprecation do not support custom messages */ -#define _MHD_DEPR_FUNC(msg) __declspec (deprecated) +#define _MHD_DEPR_FUNC(msg) __declspec(deprecated) #elif (__GNUC__ + 0 >= 5) || (defined (__clang__) && \ (__clang_major__ + 0 > 2 || (__clang_major__ + 0 == 2 && __clang_minor__ >= \ 9))) /* FIXME: earlier versions not tested */ @@ -3125,6 +3125,20 @@ MHD_create_response_from_fd (size_t size, int fd); +/** + * Create a response object. The response object can be extended with + * header information and then be used ONLY ONCE. + * + * @param fd file descriptor referring to a read-end of a pipe with the + * data; will be closed when response is destroyed; + * fd should be in 'blocking' mode + * @return NULL on error (i.e. invalid arguments, out of memory) + * @ingroup response + */ +_MHD_EXTERN struct MHD_Response * +MHD_create_response_from_pipe (int fd); + + /** * Create a response object. The response object can be extended with * header information and then be used any number of times. diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 7a17c628..ceae1cf8 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c @@ -3957,11 +3957,14 @@ MHD_queue_response (struct MHD_Connection *connection, connection->responseCode = status_code; #if defined(_MHD_HAVE_SENDFILE) if ( (response->fd == -1) || + (response->is_pipe) || (0 != (connection->daemon->options & MHD_USE_TLS)) ) connection->resp_sender = MHD_resp_sender_std; else connection->resp_sender = MHD_resp_sender_sendfile; #endif /* _MHD_HAVE_SENDFILE */ + /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice() + to avoid two user-space copies... */ if ( ( (NULL != connection->method) && (MHD_str_equal_caseless_ (connection->method, diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h index 3fdc7e5c..8f0e821a 100644 --- a/src/microhttpd/internal.h +++ b/src/microhttpd/internal.h @@ -400,6 +400,11 @@ struct MHD_Response */ enum MHD_ResponseFlags flags; + /** + * If the @e fd is a pipe (no sendfile()). + */ + bool is_pipe; + }; diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index 25c05163..27b3ee3f 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c @@ -526,6 +526,37 @@ file_reader (void *cls, } +/** + * Given a pipe descriptor, read data from the pipe + * to generate the response. + * + * @param cls pointer to the response + * @param pos offset in the pipe to access (ignored) + * @param buf where to write the data + * @param max number of bytes to write at most + * @return number of bytes written + */ +static ssize_t +pipe_reader (void *cls, + uint64_t pos, + char *buf, + size_t max) +{ + struct MHD_Response *response = cls; + ssize_t n; + + (void) pos; + n = read (response->fd, + buf, + max); + if (0 == n) + return MHD_CONTENT_READER_END_OF_STREAM; + if (n < 0) + return MHD_CONTENT_READER_END_WITH_ERROR; + return n; +} + + /** * Destroy file reader context. Closes the file * descriptor. @@ -614,12 +645,42 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size, if (NULL == response) return NULL; response->fd = fd; + response->is_pipe = false; response->fd_off = offset; response->crc_cls = response; return response; } +/** + * Create a response object. The response object can be extended with + * header information and then be used ONLY ONCE. + * + * @param fd file descriptor referring to a read-end of a pipe with the + * data; will be closed when response is destroyed; + * fd should be in 'blocking' mode + * @return NULL on error (i.e. invalid arguments, out of memory) + * @ingroup response + */ +_MHD_EXTERN struct MHD_Response * +MHD_create_response_from_pipe (int fd) +{ + struct MHD_Response *response; + + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, + MHD_FILE_READ_BLOCK_SIZE, + &pipe_reader, + NULL, + &free_callback); + if (NULL == response) + return NULL; + response->fd = fd; + response->is_pipe = true; + response->crc_cls = response; + return response; +} + + /** * Create a response object. The response object can be extended with * header information and then be used any number of times. -- cgit v1.2.3