浏览代码

Implemented fallback file reader for the responses

Evgeny Grin (Karlson2k) 1 年之前
父节点
当前提交
7aa923019d

+ 21 - 3
configure.ac

@@ -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
src/incl_priv/mhd_sys_options.h

@@ -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
src/include/microhttpd2.h

@@ -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
src/include/microhttpd2_preamble.h.in

@@ -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 - 0
src/mhd2/Makefile.am

@@ -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 - 0
src/mhd2/mhd_read_file.c

@@ -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 - 0
src/mhd2/mhd_read_file.h

@@ -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 - 0
src/mhd2/stream_funcs.c

@@ -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 - 0
src/mhd2/stream_funcs.h

@@ -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
src/mhd2/stream_process_reply.c

@@ -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");