libmicrohttpd

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

commit 7f9b98e25631731afd8b853b9f0163dc793a0a57
parent 5bbf69f67d4d1c386d7ec4cd0b359f038a73fee2
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sun, 27 Nov 2016 23:15:49 +0300

Fixed thread safety with responses with same file FD,
added enum value MHD_FEATURE_RESPONSES_SHARED_FD

Diffstat:
Mconfigure.ac | 2+-
Msrc/include/microhttpd.h | 15++++++++++++++-
Msrc/microhttpd/daemon.c | 12+++++++++++-
Msrc/microhttpd/response.c | 63+++++++++++++++++++++++++++++++++++++++++++--------------------
4 files changed, 69 insertions(+), 23 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -1043,7 +1043,7 @@ AM_CONDITIONAL([HAVE_MAGIC], [[test "x$mhd_have_magic_open" = "xyes"]]) # large file support (> 4 GB) AC_SYS_LARGEFILE AC_FUNC_FSEEKO -AC_CHECK_FUNCS([_lseeki64 lseek64 sendfile64]) +AC_CHECK_FUNCS([lseek64 sendfile64 pread64 pread]) # optional: have error messages ? AC_MSG_CHECKING([[whether to generate error messages]]) diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -2952,7 +2952,20 @@ enum MHD_FEATURE * If supported then #MHD_ALLOW_UPGRADE, #MHD_upgrade_action() and * #MHD_create_response_for_upgrade() can be used. */ - MHD_FEATURE_UPGRADE = 17 + MHD_FEATURE_UPGRADE = 17, + + /** + * Get whether it's safe to use same FD for multiple calls of + * #MHD_create_response_from_fd() and whether it's safe to use single + * response generated by #MHD_create_response_from_fd() with multiple + * connections at same time. + * If #MHD_is_feature_supported() return #MHD_NO for this feature then + * usage of responses with same file FD in multiple parallel threads may + * results in incorrect data sent to remote client. + * It's always safe to use same file FD in multiple responses if MHD + * is run in any single thread mode. + */ + MHD_FEATURE_RESPONSES_SHARED_FD = 18 }; diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -6018,7 +6018,11 @@ MHD_is_feature_supported(enum MHD_FEATURE feature) return MHD_NO; #endif /* !HTTPS_SUPPORT || GNUTLS_VERSION_NUMBER < 0x030111 */ case MHD_FEATURE_LARGE_FILE: -#if defined(HAVE___LSEEKI64) || defined(HAVE_LSEEK64) +#if defined(HAVE_PREAD64) || defined(_WIN32) + return MHD_YES; +#elif defined(HAVE_PREAD) + return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES; +#elif defined(HAVE_LSEEK64) return MHD_YES; #else return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES; @@ -6035,6 +6039,12 @@ MHD_is_feature_supported(enum MHD_FEATURE feature) #else return MHD_NO; #endif + case MHD_FEATURE_RESPONSES_SHARED_FD: +#if defined(HAVE_PREAD64) || defined(HAVE_PREAD) || defined(_WIN32) + return MHD_YES; +#else + return MHD_NO; +#endif } return MHD_NO; } diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -30,6 +30,9 @@ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif /* HAVE_SYS_IOCTL_H */ +#ifdef _WIN32 +#include <windows.h> +#endif /* _WIN32 */ #include "internal.h" #include "response.h" @@ -341,23 +344,35 @@ file_reader (void *cls, size_t max) { struct MHD_Response *response = cls; +#ifndef _WIN32 ssize_t n; +#else /* _WIN32 */ + const HANDLE fh = (HANDLE) _get_osfhandle (response->fd); +#endif /* _WIN32 */ const int64_t offset64 = (int64_t)(pos + response->fd_off); if (offset64 < 0) return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ +#ifndef _WIN32 + if (max > SSIZE_MAX) + max = SSIZE_MAX; /* Clamp to maximum return value. */ + +#if defined(HAVE_PREAD64) + n = pread64(response->fd, buf, max, offset64); +#elif defined(HAVE_PREAD) + if ( (sizeof(off_t) < sizeof (uint64_t)) && + (offset64 > (uint64_t)INT32_MAX) ) + return MHD_CONTENT_READER_END_WITH_ERROR; /* Read at required position is not possible. */ + + n = pread(response->fd, buf, max, (off_t) offset64); +#else /* ! HAVE_PREAD */ #if defined(HAVE_LSEEK64) if (lseek64 (response->fd, offset64, SEEK_SET) != offset64) return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ -#elif defined(HAVE___LSEEKI64) - if (_lseeki64 (response->fd, - offset64, - SEEK_SET) != offset64) - return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ -#else /* !HAVE___LSEEKI64 */ +#else /* ! HAVE_LSEEK64 */ if ( (sizeof(off_t) < sizeof (uint64_t)) && (offset64 > (uint64_t)INT32_MAX) ) return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ @@ -366,29 +381,37 @@ file_reader (void *cls, (off_t) offset64, SEEK_SET) != (off_t) offset64) return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ -#endif - -#ifndef _WIN32 - if (max > SSIZE_MAX) - max = SSIZE_MAX; - +#endif /* ! HAVE_LSEEK64 */ n = read (response->fd, buf, max); -#else /* _WIN32 */ - if (max > INT32_MAX) - max = INT32_MAX; - - n = read (response->fd, - buf, - (unsigned int) max); -#endif /* _WIN32 */ +#endif /* ! HAVE_PREAD */ if (0 == n) return MHD_CONTENT_READER_END_OF_STREAM; if (n < 0) return MHD_CONTENT_READER_END_WITH_ERROR; return n; +#else /* _WIN32 */ + if (INVALID_HANDLE_VALUE == fh) + return MHD_CONTENT_READER_END_WITH_ERROR; /* Value of 'response->fd' is not valid. */ + else + { + OVERLAPPED f_ol = {0, 0, 0, 0}; /* Initialize to zero. */ + ULARGE_INTEGER pos_uli; + DWORD toRead = (max > INT32_MAX) ? INT32_MAX : (DWORD) max; + DWORD resRead; + + pos_uli.QuadPart = (uint64_t) offset64; /* Simple transformation 64bit -> 2x32bit. */ + f_ol.Offset = pos_uli.LowPart; + f_ol.OffsetHigh = pos_uli.HighPart; + if (! ReadFile(fh, (void*)buf, toRead, &resRead, &f_ol)) + return MHD_CONTENT_READER_END_WITH_ERROR; /* Read error. */ + if (0 == resRead) + return MHD_CONTENT_READER_END_OF_STREAM; + return (ssize_t) resRead; + } +#endif /* _WIN32 */ }