Implemented fallback file reader for the responses

This commit is contained in:
Evgeny Grin (Karlson2k)
2024-11-25 20:58:26 +03:00
parent 21d78eec58
commit 7aa923019d
10 changed files with 465 additions and 20 deletions
+21 -3
View File
@@ -4526,10 +4526,16 @@ AM_CONDITIONAL([MHD_HAVE_LIBMAGIC], [[test "x$mhd_cv_have_func_magic_open" = "xy
# large file support (> 4 GB)
MHD_CHECK_FUNC([lseek64],
[[
#ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE 1
#endif
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
]],
[[
i][f (((off64_t) -1) == lseek64(0, (off64_t) 0, SEEK_SET))
@@ -4538,10 +4544,16 @@ MHD_CHECK_FUNC([lseek64],
)
MHD_CHECK_FUNC([pread64],
[[
#ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE 1
#endif
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
]],
[[
char buf[5];
@@ -4554,7 +4566,10 @@ MHD_CHECK_FUNC([pread],
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
]],
[[
char buf[5];
@@ -4608,6 +4623,9 @@ static void empty_func(void)
AC_MSG_RESULT([[yes]])
MHD_CHECK_FUNC([sendfile64],
[[
#ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE 1
#endif
#include <sys/sendfile.h>
]],
[[
+12 -3
View File
@@ -332,6 +332,13 @@
# endif
#endif /* mhd_THREADS_KIND_POSIX || mhd_THREADS_KIND_W32 */
#if defined(_WIN32) && ! defined(__CYGWIN__)
/**
* Defined if the platform is native W32.
* Not define on Cygwin.
*/
# define mhd_W32_NATIVE 1
#endif
/**
* Macro to drop 'const' qualifier from pointer.
* Try to avoid compiler warning.
@@ -401,10 +408,12 @@
#define RESTRICT __restrict__
#endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */
#if defined(LINUX) && (defined(HAVE_SENDFILE64) || defined(HAVE_LSEEK64)) && \
! defined(_LARGEFILE64_SOURCE)
#if defined(__linux__) && \
(defined(HAVE_SENDFILE64) || defined(HAVE_LSEEK64) || defined(HAVE_PREAD64))
# if ! defined(_LARGEFILE64_SOURCE)
/* On Linux, special macro is required to enable definitions of some xxx64 functions */
#define _LARGEFILE64_SOURCE 1
# define _LARGEFILE64_SOURCE 1
# endif
#endif
#ifdef HAVE_C11_GMTIME_S
+26 -3
View File
@@ -1152,6 +1152,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REPLY_POOL_ALLOCATION_FAILURE = 50231
,
/**
* Failed to read the file for file-backed response.
*/
MHD_SC_REPLY_FILE_READ_ERROR = 50232
,
/**
* Failed to allocate memory in connection's pool for the reply.
*/
@@ -1457,6 +1462,18 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REPLY_HEADERS_TOO_LARGE = 60103
,
/**
* Specified offset in file-backed response is too large and not supported
* by the platform
*/
MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE = 60104
,
/**
* File-backed response has file smaller than specified combination of
* the file offset and the response size.
*/
MHD_SC_REPLY_FILE_TOO_SHORT = 60105
,
/**
* The new connection cannot be used because the FD number is higher than
* the limit set by FD_SETSIZE (if internal polling with select is used) or
@@ -3734,8 +3751,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_CLIENT_ABORT = 30
,
/**
* The request is not valid according to
* HTTP specifications.
* The request is not valid according to HTTP specifications.
* @ingroup request
*/
MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31
@@ -3747,7 +3763,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_BY_APP_ABORT = 40
,
/**
* The application aborted request without response.
* The request was aborted due to the application failed to provide a valid
* resonse.
* @ingroup request
*/
MHD_REQUEST_ENDED_BY_APP_ERROR = 41
@@ -3758,6 +3775,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
*/
MHD_REQUEST_ENDED_NO_RESOURCES = 50
,
/**
* The request was aborted due to error reading file for file-backed response
* @ingroup request
*/
MHD_REQUEST_ENDED_FILE_ERROR = 51
,
/**
* Closing the session since MHD is being shut down.
* @ingroup request
+26 -3
View File
@@ -1152,6 +1152,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REPLY_POOL_ALLOCATION_FAILURE = 50231
,
/**
* Failed to read the file for file-backed response.
*/
MHD_SC_REPLY_FILE_READ_ERROR = 50232
,
/**
* Failed to allocate memory in connection's pool for the reply.
*/
@@ -1457,6 +1462,18 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REPLY_HEADERS_TOO_LARGE = 60103
,
/**
* Specified offset in file-backed response is too large and not supported
* by the platform
*/
MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE = 60104
,
/**
* File-backed response has file smaller than specified combination of
* the file offset and the response size.
*/
MHD_SC_REPLY_FILE_TOO_SHORT = 60105
,
/**
* The new connection cannot be used because the FD number is higher than
* the limit set by FD_SETSIZE (if internal polling with select is used) or
@@ -3734,8 +3751,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_CLIENT_ABORT = 30
,
/**
* The request is not valid according to
* HTTP specifications.
* The request is not valid according to HTTP specifications.
* @ingroup request
*/
MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31
@@ -3747,7 +3763,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
MHD_REQUEST_ENDED_BY_APP_ABORT = 40
,
/**
* The application aborted request without response.
* The request was aborted due to the application failed to provide a valid
* resonse.
* @ingroup request
*/
MHD_REQUEST_ENDED_BY_APP_ERROR = 41
@@ -3758,6 +3775,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
*/
MHD_REQUEST_ENDED_NO_RESOURCES = 50
,
/**
* The request was aborted due to error reading file for file-backed response
* @ingroup request
*/
MHD_REQUEST_ENDED_FILE_ERROR = 51
,
/**
* Closing the session since MHD is being shut down.
* @ingroup request
+1
View File
@@ -55,6 +55,7 @@ libmicrohttpd2_la_SOURCES = \
mhd_threads.c mhd_threads.h sys_thread_entry_type.h \
mhd_mono_clock.c mhd_mono_clock.h \
mhd_mempool.c mhd_mempool.h \
mhd_read_file.c mhd_read_file.h \
mhd_recv.c mhd_recv.h \
mhd_send.c mhd_send.h \
mhd_daemon.h \
+177
View File
@@ -0,0 +1,177 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_read_file.c
* @brief The implementation of mhd_read_file() function
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "mhd_read_file.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if ! defined(mhd_W32_NATIVE)
# include "mhd_limits.h"
#else
/* Native W32 */
# include <windows.h>
# include <string.h> /* for memset() */
#endif
/**
* Read data from the file to the provided buffer
*
* @param file_fd the FD of file to read
* @param buf_size the size of the @a buf buffer
* @param[out] buf the buffer to fill with the read data
* @param[out] size_filled the pointer to variable to get the size of the data
* actually put to the @a buffer
* @return #mhd_FILE_READ_OK if succeed (the @a size_filled gets the actual
* read size),
* error otherwise
*/
MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) enum mhd_FileReadResult
mhd_read_file (int file_fd,
uint_fast64_t offset,
size_t buf_size,
char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
size_t *restrict size_filled)
{
#if (defined(HAVE_PREAD64) || defined(HAVE_PREAD)) && ! defined(mhd_W32_NATIVE)
# ifdef HAVE_PREAD64
const off64_t pos_off = (off64_t) offset;
# else /* HAVE_PREAD */
const off_t pos_off = (off_t) offset;
# endif /* HAVE_PREAD */
ssize_t res;
*size_filled = 0;
if ((0 > pos_off) ||
(offset != (uint_fast64_t) pos_off))
return mhd_FILE_READ_OFFSET_TOO_LARGE;
if (0 > (ssize_t) buf_size)
buf_size = SSIZE_MAX; /* Larger sizes may result in undefined behaviour */
# ifdef HAVE_PREAD64
res = pread64 (file_fd,
buf,
buf_size,
pos_off);
# else /* HAVE_PREAD */
res = pread (file_fd,
buf,
buf_size,
pos_off);
# endif /* HAVE_PREAD */
if (0 > res)
return mhd_FILE_READ_ERROR;
if (0 == res)
return mhd_FILE_READ_EOF;
*size_filled = (size_t) res;
return mhd_FILE_READ_OK;
#elif ! defined(mhd_W32_NATIVE)
/* Multithread-unsafe emulation */
# ifdef HAVE_LSEEK64
const off64_t pos_off = (off64_t) offset;
# else
const off_t pos_off = (off_t) offset;
# endif
ssize_t res;
*size_filled = 0;
if ((0 > pos_off) ||
(offset != (uint_fast64_t) pos_off))
return mhd_FILE_READ_OFFSET_TOO_LARGE;
if (0 > (ssize_t) buf_size)
buf_size = SSIZE_MAX; /* Larger sizes may result in undefined behaviour */
# ifdef HAVE_LSEEK64
if (pos_off != lseek64 (file_fd,
pos_off,
SEEK_SET))
return mhd_FILE_READ_ERROR;
# else
if (pos_off != lseek (file_fd,
pos_off,
SEEK_SET))
return mhd_FILE_READ_ERROR;
# endif
res = read (file_fd,
buf,
buf_size);
if (0 > res)
return mhd_FILE_READ_ERROR;
if (0 == res)
return mhd_FILE_READ_EOF;
*size_filled = (size_t) res;
return mhd_FILE_READ_OK;
#else /* Native W32 */
const intptr_t sys_fd = _get_osfhandle (file_fd);
const HANDLE w_hndl = (HANDLE) sys_fd;
OVERLAPPED ovrlp;
DWORD reqReadSize;
DWORD resReadSize;
*size_filled = 0;
if (INVALID_HANDLE_VALUE == w_hndl)
return mhd_FILE_READ_ERROR;
memset (&ovrlp, 0, sizeof(ovrlp));
reqReadSize = (DWORD) buf_size;
if (reqReadSize != buf_size)
reqReadSize = (DWORD) (~((DWORD) 0));
ovrlp.Offset = (DWORD) offset;
offset >>= 32;
ovrlp.OffsetHigh = (DWORD) offset;
if (0 != (offset >> 32))
return mhd_FILE_READ_OFFSET_TOO_LARGE;
if (! ReadFile (w_hndl,
buf,
reqReadSize,
&resReadSize,
&ovrlp))
return mhd_FILE_READ_ERROR;
if (0 == resReadSize)
return mhd_FILE_READ_EOF;
*size_filled = resReadSize;
return mhd_FILE_READ_OK;
#endif /* Native W32 */
}
+83
View File
@@ -0,0 +1,83 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_read_file.h
* @brief The declaration of mhd_read_file() function
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_READ_FILE_H
#define MHD_READ_FILE_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
/**
* Results of file reading
*/
enum mhd_FileReadResult
{
/**
* File read succeed
*/
mhd_FILE_READ_OK = 0
,
/**
* File read failed
*/
mhd_FILE_READ_ERROR
,
/**
* The requested offset is too large
*/
mhd_FILE_READ_OFFSET_TOO_LARGE
,
/**
* Got "end of file"
*/
mhd_FILE_READ_EOF
};
/**
* Read data from the file to the provided buffer
*
* @param file_fd the FD of file to read
* @param buf_size the size of the @a buf buffer
* @param[out] buf the buffer to fill with the read data
* @param[out] size_filled the pointer to variable to get the size of the data
* actually put to the @a buffer
* @return #mhd_FILE_READ_OK if succeed (the @a size_filled gets the actual
* read size),
* error otherwise
*/
MHD_INTERNAL enum mhd_FileReadResult
mhd_read_file (int file_fd,
uint_fast64_t offset,
size_t buf_size,
char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
size_t *restrict size_filled)
MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_OUT_SIZE_(4, 3) MHD_FN_PAR_OUT_ (5);
#endif /* ! MHD_READ_FILE_H */
+15
View File
@@ -720,6 +720,21 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
end_code = MHD_REQUEST_ENDED_BY_APP_ABORT;
sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION;
break;
case mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE:
close_hard = true;
end_code = MHD_REQUEST_ENDED_FILE_ERROR;
sc = MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE;
break;
case mhd_CONN_CLOSE_FILE_READ_ERROR:
close_hard = true;
end_code = MHD_REQUEST_ENDED_FILE_ERROR;
sc = MHD_SC_REPLY_FILE_READ_ERROR;
break;
case mhd_CONN_CLOSE_FILE_TOO_SHORT:
close_hard = true;
end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
sc = MHD_SC_REPLY_FILE_TOO_SHORT;
break;
case mhd_CONN_CLOSE_INT_ERROR:
close_hard = true;
end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
+15
View File
@@ -198,6 +198,21 @@ enum mhd_ConnCloseReason
*/
mhd_CONN_CLOSE_APP_ABORTED
,
/**
* File-backed response too large (unsupported by OS) file offset
*/
mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE
,
/**
* Error reading file-backed response
*/
mhd_CONN_CLOSE_FILE_READ_ERROR
,
/**
* File-backed response has file smaller than specified by application
*/
mhd_CONN_CLOSE_FILE_TOO_SHORT
,
/* Hard problem while receiving or sending */
/**
+89 -8
View File
@@ -53,6 +53,8 @@
#include "stream_funcs.h"
#include "request_get_value.h"
#include "mhd_read_file.h"
#include "mhd_public_api.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
@@ -964,6 +966,67 @@ preprocess_dcc_action (struct MHD_Connection *restrict c,
}
/**
* Read next portion of the response file to the buffer, based on information
* about amount of response content position.
* Handle file read errors, update response position.
* @param c the stream to use
* @param r the response to use
* @param buf_size the size of the @a buf buffer
* @param[out] buf the buffer to fill with the file data
* @param[out] size_filled the pointer to variable to get the size of the data
* actually put to the @a buffer
* @return 'true' if succeed (size_filled and response position are updated),
* 'false' if failed (the stream is closed)
*/
static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool
read_response_file (struct MHD_Connection *restrict c,
struct MHD_Response *const restrict r,
size_t buf_size,
char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
size_t *restrict size_filled)
{
mhd_assert (r == c->rp.response);
mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype);
switch (mhd_read_file (r->cntn.file.fd,
r->cntn.file.offset + c->rp.rsp_cntn_read_pos,
buf_size,
buf,
size_filled))
{
case mhd_FILE_READ_OK:
break;
case mhd_FILE_READ_ERROR:
mhd_STREAM_ABORT (c, \
mhd_CONN_CLOSE_FILE_READ_ERROR, \
"Error reading file for file-backed response.");
return false;
case mhd_FILE_READ_OFFSET_TOO_LARGE:
mhd_STREAM_ABORT (c, \
mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE, \
"The offset for file-backed response is too large " \
"for this platform.");
return false;
case mhd_FILE_READ_EOF:
mhd_STREAM_ABORT (c, \
mhd_CONN_CLOSE_FILE_TOO_SHORT, \
"The file for file-backed response is smaller " \
"than specified by application.");
return false;
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
return false;
}
c->rp.rsp_cntn_read_pos += *size_filled;
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
{
@@ -1049,10 +1112,21 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
}
else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
{
// TODO: implement fallback
mhd_assert (0 && "Not implemented yet");
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
c->rp.rsp_cntn_read_pos = r->cntn_size;
size_t size_to_fill =
c->write_buffer_size - c->write_buffer_append_offset;
size_t filled;
if (size_to_fill > left_to_send)
size_to_fill = (size_t) left_to_send;
if (! read_response_file (c,
r,
size_to_fill,
c->write_buffer + c->write_buffer_append_offset,
&filled))
return true; /* Error, the stream is closed */
c->write_buffer_append_offset += filled;
}
else
{
@@ -1061,10 +1135,6 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
c->rp.rsp_cntn_read_pos = r->cntn_size;
}
mhd_assert (0 && "Not implemented yet");
c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
c->rp.rsp_cntn_read_pos = r->cntn_size;
}
else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
{
@@ -1215,6 +1285,17 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
c->rp.rsp_cntn_read_pos += filled;
c->write_buffer_append_offset += filled;
}
else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
{
if (! read_response_file (c,
r,
size_to_fill,
c->write_buffer + max_chunk_hdr_len,
&filled))
return true; /* Error, the stream is closed */
c->write_buffer_append_offset += filled;
}
else
{
mhd_assert (0 && "Not implemented yet");