This commit is contained in:
Evgeny Grin (Karlson2k)
2024-07-21 07:36:40 +02:00
parent 706092798a
commit e7eea331b1
30 changed files with 1173 additions and 368 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ SUBDIRS += microhttpd_ws
endif
if BUILD_EXAMPLES
#SUBDIRS += examples
SUBDIRS += examples
endif
if BUILD_TOOLS
+10 -245
View File
@@ -3,10 +3,9 @@ SUBDIRS = .
AM_CPPFLAGS = \
-I$(top_srcdir)/src/include \
$(CPPFLAGS_ac) \
-DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
$(CPPFLAGS_ac)
AM_CFLAGS = $(CFLAGS_ac) @LIBGCRYPT_CFLAGS@
AM_CFLAGS = $(CFLAGS_ac)
AM_LDFLAGS = $(LDFLAGS_ac)
@@ -14,254 +13,20 @@ MHD_CPU_COUNT_DEF = -DMHD_CPU_COUNT=$(CPU_COUNT)
AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la
if USE_COVERAGE
AM_CFLAGS += --coverage
endif
$(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/Makefile
@echo ' cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la'; \
$(am__cd) $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la
$(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile
@echo ' cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la'; \
$(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la
# example programs
noinst_PROGRAMS = \
benchmark \
benchmark_https \
chunked_example \
minimal_example \
minimal_example_empty \
dual_stack_example \
minimal_example_comet \
querystring_example \
timeout \
fileserver_example \
fileserver_example_dirs \
fileserver_example_external_select \
refuse_post_example
minimal_example2
if HAVE_EXPERIMENTAL
noinst_PROGRAMS += \
websocket_chatserver_example
endif
if MHD_USE_EPOLL
noinst_PROGRAMS += \
suspend_resume_epoll
endif
EXTRA_DIST = msgs_i18n.c
noinst_EXTRA_DIST = msgs_i18n.c
if ENABLE_HTTPS
noinst_PROGRAMS += \
https_fileserver_example \
minimal_example_empty_tls
endif
if HAVE_POSTPROCESSOR
noinst_PROGRAMS += \
post_example
if HAVE_POSIX_THREADS
noinst_PROGRAMS += demo
if ENABLE_HTTPS
noinst_PROGRAMS += demo_https
endif
endif
endif
if ENABLE_DAUTH
noinst_PROGRAMS += \
digest_auth_example \
digest_auth_example_adv
endif
if ENABLE_BAUTH
noinst_PROGRAMS += \
authorization_example
endif
if HAVE_POSIX_THREADS
if ENABLE_UPGRADE
noinst_PROGRAMS += \
upgrade_example \
websocket_threaded_example
endif
endif
if HAVE_ZLIB
noinst_PROGRAMS += \
http_compression \
http_chunked_compression
endif
if HAVE_W32
AM_CFLAGS += -DWINDOWS
endif
minimal_example_SOURCES = \
minimal_example.c
minimal_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
minimal_example_empty_SOURCES = \
minimal_example_empty.c
minimal_example_empty_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
minimal_example_empty_tls_SOURCES = \
minimal_example_empty_tls.c
minimal_example_empty_tls_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
upgrade_example_SOURCES = \
upgrade_example.c
upgrade_example_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
upgrade_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
websocket_threaded_example_SOURCES = \
websocket_threaded_example.c
websocket_threaded_example_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
websocket_threaded_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
timeout_SOURCES = \
timeout.c
timeout_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
chunked_example_SOURCES = \
chunked_example.c
chunked_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
websocket_chatserver_example_SOURCES = \
websocket_chatserver_example.c
websocket_chatserver_example_LDADD = \
$(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
demo_SOURCES = \
demo.c
demo_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
demo_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_CPU_COUNT_DEF)
demo_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
if MHD_HAVE_LIBMAGIC
demo_LDADD += -lmagic
endif
demo_https_SOURCES = \
demo_https.c
demo_https_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
demo_https_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_CPU_COUNT_DEF)
demo_https_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
if MHD_HAVE_LIBMAGIC
demo_https_LDADD += -lmagic
endif
benchmark_SOURCES = \
benchmark.c
benchmark_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_CPU_COUNT_DEF)
benchmark_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
suspend_resume_epoll_SOURCES = \
suspend_resume_epoll.c
suspend_resume_epoll_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_CPU_COUNT_DEF)
suspend_resume_epoll_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
benchmark_https_SOURCES = \
benchmark_https.c
benchmark_https_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_CPU_COUNT_DEF)
benchmark_https_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
dual_stack_example_SOURCES = \
dual_stack_example.c
dual_stack_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
post_example_SOURCES = \
post_example.c
post_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
minimal_example_comet_SOURCES = \
minimal_example_comet.c
minimal_example_comet_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
authorization_example_SOURCES = \
authorization_example.c
authorization_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
digest_auth_example_SOURCES = \
digest_auth_example.c
digest_auth_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
digest_auth_example_adv_SOURCES = \
digest_auth_example_adv.c
digest_auth_example_adv_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
refuse_post_example_SOURCES = \
refuse_post_example.c
refuse_post_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
querystring_example_SOURCES = \
querystring_example.c
querystring_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
fileserver_example_SOURCES = \
fileserver_example.c
fileserver_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
fileserver_example_dirs_SOURCES = \
fileserver_example_dirs.c
fileserver_example_dirs_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
fileserver_example_external_select_SOURCES = \
fileserver_example_external_select.c
fileserver_example_external_select_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
https_fileserver_example_SOURCES = \
https_fileserver_example.c
https_fileserver_example_CPPFLAGS = \
$(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
https_fileserver_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
http_compression_SOURCES = \
http_compression.c
http_chunked_compression_SOURCES = \
http_chunked_compression.c
http_compression_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
http_chunked_compression_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
if HAVE_ZLIB
http_compression_LDADD += -lz
http_chunked_compression_LDADD += -lz
endif
minimal_example2_SOURCES = \
minimal_example2.c
+96
View File
@@ -0,0 +1,96 @@
/*
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 minimal_example2.c
* @brief Minimal example for libmicrohttpd v2
* @author Karlson2k (Evgeny Grin)
*/
#include <stdio.h>
#include <microhttpd2.h>
static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3)
const struct MHD_Action *
req_cb (void *cls,
struct MHD_Request *MHD_RESTRICT request,
const struct MHD_String *MHD_RESTRICT path,
enum MHD_HTTP_Method method,
uint_fast64_t upload_size)
{
static const char res_msg[] = "Hello there!";
(void) cls; (void) path; (void) method; (void) upload_size; /* Unused */
return MHD_action_from_response (
request,
MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK,
sizeof(res_msg) / sizeof(char) - 1,
res_msg));
}
int
main (int argc,
char *const *argv)
{
struct MHD_Daemon *d;
int port;
if (argc != 2)
{
fprintf (stderr,"Usage:\n%s PORT\n", argv[0]);
return 1;
}
port = atoi (argv[1]);
if ( (1 > port) || (port > 65535) )
{
fprintf (stderr,
"The port must be a number between 1 and 65535.\n");
return 2;
}
d = MHD_daemon_create (&req_cb, NULL);
if (NULL != d)
{
if (MHD_SC_OK ==
MHD_DAEMON_SET_OPTIONS (d,
MHD_D_OPTION_WM_WORKER_THREADS (1),
MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
(uint_least16_t) port)))
{
if (MHD_SC_OK == MHD_daemon_start (d))
{
printf ("The MHD daemon is listening on port %d\n"
"Press ENTER to stop.\n", port);
(void) fgetc (stdin);
}
else
fprintf (stderr, "Failed to start MHD daemon.\n");
}
else
fprintf (stderr, "Failed to set MHD daemon run parameters.\n");
printf ("Stopping... ");
MHD_daemon_destroy (d);
printf ("OK\n");
}
else
fprintf (stderr, "Failed to create MHD daemon.\n");
return 0;
}
+16 -1
View File
@@ -637,6 +637,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REQ_COOKIE_INVALID = 40163
,
/**
* The request cannot be processed. Sending error reply.
*/
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 40200
,
/* 50000-level errors are because of an error internal
to the MHD logic, possibly including our interaction
@@ -921,7 +926,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
MHD_SC_ITC_STATUS_ERROR = 500104
,
/**
* We failed to add a socket to the epoll() set.
* Failed to add a socket to the epoll set.
*/
MHD_SC_EPOLL_CTL_ADD_FAILED = 500110
,
@@ -963,6 +968,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_UNEXPECTED_SELECT_ERROR = 50116
,
/**
* Failed to remove a socket to the epoll set.
*/
MHD_SC_EPOLL_CTL_REMOVE_FAILED = 50117
,
/**
* poll() is not supported.
*/
@@ -1072,6 +1082,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REPLY_POOL_ALLOCATION_FAILURE = 50231
,
/**
* Failed to allocate memory in connection's pool for the reply.
*/
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
,
/**
* The feature is not supported by this MHD build (either
* disabled by configure parameters or build platform
+1 -1
View File
@@ -71,7 +71,7 @@ libmicrohttpd2_la_SOURCES = \
conn_data_send.c conn_data_send.h \
request_funcs.c request_funcs.h \
request_get_value.c request_get_value.h \
respond_with_error.h \
respond_with_error.c respond_with_error.h \
response_from.c response_from.h \
response_add_header.c response_add_header.h \
response_destroy.c response_destroy.h \
+9 -1
View File
@@ -28,9 +28,13 @@
#include "mhd_action.h"
#include "mhd_request.h"
#include "mhd_public_api.h"
#include "daemon_logger.h"
#include "response_funcs.h"
#include "mhd_public_api.h"
MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
const struct MHD_Action *
@@ -56,6 +60,8 @@ MHD_action_from_response (struct MHD_Request *request,
if (NULL == response)
return (const struct MHD_Action *) NULL;
mhd_response_check_frozen_freeze (response);
head_act->act = mhd_ACTION_RESPONSE;
head_act->data.response = response;
@@ -155,6 +161,8 @@ MHD_upload_action_from_response (struct MHD_Request *request,
if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
return (const struct MHD_UploadAction *) NULL;
mhd_response_check_frozen_freeze (response);
upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
upl_act->data.response = response;
+5
View File
@@ -65,6 +65,11 @@ mhd_conn_data_recv (struct MHD_Connection *restrict c,
if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
{
/* Handle errors */
if ((mhd_SOCKET_ERR_NO_ERROR == res) && (0 == received))
{
c->sk_rmt_shut_wr = true;
res = mhd_SOCKET_ERR_REMT_DISCONN;
}
if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
{
/* Re-try last time to detect the error */
+14 -9
View File
@@ -148,8 +148,10 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
(! c->rp.props.send_reply_body)),
&sent);
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
if (mhd_SOCKET_ERR_NO_ERROR == res)
{
mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state);
if (sent > wb_ready)
{
/* The complete header and some response data have been sent,
@@ -157,17 +159,20 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
mhd_assert (0 == c->rp.rsp_cntn_read_pos);
mhd_assert (! c->rp.props.chunked);
mhd_assert (c->rp.props.send_reply_body);
c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY;
c->write_buffer_send_offset += wb_ready;
c->rp.rsp_cntn_read_pos = sent - wb_ready;
if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size)
c->state = MHD_CONNECTION_FULL_REPLY_SENT;
}
else
{
c->write_buffer_send_offset += sent;
// TODO: move it to data processing
check_write_done (c,
MHD_CONNECTION_HEADERS_SENT);
}
mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state);
// TODO: move it to data processing
check_write_done (c,
MHD_CONNECTION_HEADERS_SENT);
}
@@ -226,7 +231,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
res = mhd_SOCKET_ERR_INTERNAL;
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
if (mhd_SOCKET_ERR_NO_ERROR == res)
{
if (mhd_REPLY_CNTN_LOC_CONN_BUF != c->rp.cntn_loc)
c->rp.rsp_cntn_read_pos += sent;
@@ -253,7 +258,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
+ c->write_buffer_send_offset,
true,
&sent);
if (mhd_SOCKET_ERR_NO_ERROR != res)
if (mhd_SOCKET_ERR_NO_ERROR == res)
{
c->write_buffer_send_offset += sent;
// TODO: move it to data processing
@@ -267,7 +272,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c)
res = mhd_SOCKET_ERR_INTERNAL;
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
if (mhd_SOCKET_ERR_NO_ERROR == res)
{
mhd_stream_update_activity_mark (c); // TODO: centralise activity mark updates
}
+37 -14
View File
@@ -41,6 +41,7 @@
#include "sys_base_types.h"
#include "sys_sockets_types.h"
#include "sys_sockets_headers.h"
#include "sys_ip_headers.h"
#include <string.h>
#ifdef MHD_USE_EPOLL
@@ -49,16 +50,15 @@
#include "compat_calloc.h"
#include "mhd_sockets_macros.h"
#include "mhd_sockets_funcs.h"
#include "mhd_panic.h"
#include "mhd_daemon.h"
#include "mhd_connection.h"
#include "mhd_public_api.h"
#include "daemon_logger.h"
#include "sys_sockets_headers.h"
#include "sys_ip_headers.h"
#include "mhd_sockets_macros.h"
#include "mhd_sockets_funcs.h"
#include "mhd_mono_clock.h"
#include "mhd_mempool.h"
#include "events_process.h"
@@ -66,6 +66,8 @@
#include "response_from.h"
#include "response_destroy.h"
#include "mhd_public_api.h"
/**
* Set initial internal states for the connection to start reading and
@@ -146,6 +148,9 @@ connection_clean_destroy (struct MHD_Connection *restrict c,
/* Connection must not be in 'process ready' list */
mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, proc_ready));
mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(d->events), proc_ready));
mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(d->events), proc_ready));
#ifdef MHD_USE_EPOLL
if (mhd_D_IS_USING_EPOLL (d))
@@ -402,7 +407,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon,
&event))
{
mhd_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED,
"Failed to add connection socket .");
"Failed to add connection socket to epoll.");
res = MHD_SC_EPOLL_CTL_ADD_FAILED;
}
else
@@ -897,7 +902,7 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
return mhd_DAEMON_ACCEPT_FAILED;
}
if (mhd_FD_FITS_DAEMON (daemon, s))
if (! mhd_FD_FITS_DAEMON (daemon, s))
{
mhd_LOG_MSG (daemon, MHD_SC_ACCEPT_OUTSIDE_OF_SET_RANGE, \
"The accepted socket has value outside of allowed range.");
@@ -984,18 +989,36 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon)
sk_nonbl,
sk_spipe_supprs,
sk_non_ip)) ?
mhd_DAEMON_ACCEPT_FAILED : mhd_DAEMON_ACCEPT_SUCCESS;
mhd_DAEMON_ACCEPT_SUCCESS : mhd_DAEMON_ACCEPT_FAILED;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_close_final (struct MHD_Connection *restrict c)
{
if ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) ||
(NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) ||
(c == mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready)))
mhd_DLINKEDL_DEL (&(c->daemon->events), c, proc_ready);
mhd_assert (c->dbg.pre_closed);
mhd_assert (c->dbg.pre_cleaned);
mhd_assert (NULL == c->rp.response);
mhd_assert (! c->rq.app_aware);
mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, proc_ready));
mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, proc_ready));
mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready));
mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(c->daemon->events), proc_ready));
mhd_assert (0 && "Not finished yet");
// TODO: finish
if (mhd_D_HAS_THR_PER_CONN (c->daemon))
{
mhd_assert (0 && "Not implemented yet");
// TODO: Support "thread per connection"
}
mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (c, by_timeout));
mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, by_timeout));
mhd_assert (NULL == c->pool);
mhd_DLINKEDL_DEL (&(c->daemon->conns), c, all_conn);
// TODO: update per-IP limits
if (NULL != c->addr)
free (c->addr);
mhd_socket_close (c->socket_fd);
free (c);
}
+2
View File
@@ -84,6 +84,8 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon);
* Finally close and clean-up connection.
* Must be performed only when connection thread (for thread-per-connection)
* has stopped.
* The connection data deallocated by this function and cannot be used anymore.
* The function must be the last function called for connection object.
* @param c the connection to close
*/
MHD_INTERNAL void
+13 -10
View File
@@ -67,12 +67,11 @@ update_conn_net_status (struct MHD_Daemon *restrict d,
(sk_state | (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY);
c->sk_ready = sk_state;
if (NULL != mhd_DLINKEDL_GET_PREV (c,proc_ready))
if ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) ||
(NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) ||
(c == mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready)))
return; /* In the 'proc_ready' list already */
mhd_assert (0 != (((unsigned int) c->event_loop_info) \
& MHD_EVENT_LOOP_INFO_PROCESS));
if ((0 !=
(((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info)
& (MHD_EVENT_LOOP_INFO_READ | MHD_EVENT_LOOP_INFO_WRITE))) ||
@@ -209,7 +208,7 @@ daemon_accept_new_conns (struct MHD_Daemon *restrict d)
static bool
daemon_process_all_act_coons (struct MHD_Daemon *restrict d)
daemon_process_all_act_conns (struct MHD_Daemon *restrict d)
{
struct MHD_Connection *c;
mhd_assert (! mhd_D_HAS_WORKERS (d));
@@ -264,10 +263,11 @@ poll_update_fds (struct MHD_Daemon *restrict d,
{
unsigned short events; /* 'unsigned' for correct bits manipulations */
mhd_assert ((i_c - i_s) < d->conns.cfg.count_limit);
mhd_assert (MHD_CONNECTION_CLOSED != c->state);
d->events.data.poll.fds[i_c].fd = c->socket_fd;
d->events.data.poll.rel[i_c].connection = c;
events = POLLHUP; /* Actually, not needed and should be ignored in 'events' */
events = 0;
if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_READ))
events |= MHD_POLL_IN;
if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_WRITE))
@@ -506,8 +506,8 @@ process_all_events_and_data (struct MHD_Daemon *restrict d)
else if (! d->net.listen.non_block)
d->events.act_req.accept = false;
}
daemon_process_all_act_coons (d);
return false;
daemon_process_all_act_conns (d);
return ! d->threading.stop_requested;
}
@@ -519,6 +519,7 @@ mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
mhd_worker_all_events (void *cls)
{
struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls;
mhd_thread_handle_ID_set_current_thread_ID (&(d->threading.tid));
mhd_assert (d->dbg.net_inited);
mhd_assert (! d->dbg.net_deinited);
mhd_assert (mhd_D_TYPE_IS_VALID (d->threading.d_type));
@@ -584,6 +585,8 @@ mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
mhd_worker_listening_only (void *cls)
{
struct MHD_Daemon *const restrict d = (struct MHD_Daemon *) cls;
mhd_thread_handle_ID_set_current_thread_ID (&(d->threading.tid));
mhd_assert (d->dbg.net_inited);
mhd_assert (! d->dbg.net_deinited);
mhd_assert (mhd_DAEMON_TYPE_LISTEN_ONLY == d->threading.d_type);
@@ -599,8 +602,8 @@ mhd_worker_listening_only (void *cls)
if (! d->threading.stop_requested)
{
mhd_LOG_MSG (d, MHD_SC_DAEMON_THREAD_STOP_UNEXPECTED, \
"The daemon thread is stopping, but termination has not been " \
"requested by the daemon.");
"The daemon thread is stopping, but termination has " \
"not been requested by the daemon.");
}
return (mhd_THRD_RTRN_TYPE) 0;
}
+2 -2
View File
@@ -161,7 +161,7 @@ static const struct MHD_String five_hundred[] = {
struct mhd_HttpStatusesBlock
{
size_t max;
size_t num_elmnts;
const struct MHD_String *const data;
};
@@ -185,7 +185,7 @@ MHD_HTTP_status_code_to_string (enum MHD_HTTP_StatusCode code)
return NULL;
if (600 < code)
return NULL;
if (statuses[code_i / 100].max > (code_i % 100))
if (statuses[code_i / 100].num_elmnts <= (code_i % 100))
return NULL;
res = statuses[code_i / 100].data + (code_i % 100);
if (NULL == res->cstr)
+7
View File
@@ -78,4 +78,11 @@
# endif /* ! HAVE_ASSERT */
#endif /* NDEBUG */
#ifdef _DEBUG
# ifdef MHD_UNREACHABLE_
# undef MHD_UNREACHABLE_
# endif
# define MHD_UNREACHABLE_ ((void) 0)
#endif
#endif /* ! MHD_ASSERT_H */
+12
View File
@@ -335,6 +335,11 @@ enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
};
struct mhd_ConnDebugData
{
bool pre_closed;
bool pre_cleaned;
};
/**
* Ability to use same connection for next request
@@ -603,6 +608,13 @@ struct MHD_Connection
* What is this connection waiting for?
*/
enum MHD_ConnectionEventLoopInfo event_loop_info;
#ifndef NDEBUG
/**
* Debugging data
*/
struct mhd_ConnDebugData dbg;
#endif
};
+6 -1
View File
@@ -741,10 +741,15 @@ struct mhd_DaemonConnections
mhd_DLNKDL_LIST (MHD_Connection,all_conn);
/**
* The list of all daemon's connections
* The list of connections sorted by last activity
*/
mhd_DLNKDL_LIST (MHD_Connection,def_timeout);
/**
* The list of connections with custom timeouts
*/
mhd_DLNKDL_LIST (MHD_Connection,cust_timeout);
/**
* The list of all daemon's connections
*/
-17
View File
@@ -339,23 +339,6 @@ struct MHD_Request
*/
uint_fast64_t current_chunk_offset;
/**
* Indicate that some of the upload payload data (from the currently
* processed chunk for chunked uploads) have been processed by the
* last call of the connection handler.
* If any data have been processed, but some data left in the buffer
* for further processing, then MHD will use zero timeout before the
* next data processing round. This allow the application handler
* process the data by the fixed portions or other way suitable for
* application developer.
* If no data have been processed, than MHD will wait for more data
* to come (as it makes no sense to call the same connection handler
* under the same conditions). However this is dangerous as if buffer
* is completely used then connection is aborted. Connection
* suspension should be used in such case.
*/
bool some_payload_processed;
/**
* We allow the main application to associate some pointer with the
* HTTP request, which is passed to each #MHD_AccessHandlerCallback
+23 -1
View File
@@ -266,6 +266,23 @@ struct mhd_ResponseConfiguration
bool int_err_resp;
};
/**
* Special data for internal error responses
*/
struct mhd_ResponseInternalErrData
{
/**
* The length of the @a spec_hdr
*/
size_t spec_hdr_len;
/**
* The special header string.
* The final CRLF is not included.
* Must be deallocated if not NULL.
*/
char *spec_hdr;
};
#ifndef NDEBUG
struct mhd_ResponseDebug
{
@@ -332,7 +349,12 @@ struct MHD_Response
*/
mhd_DLNKDL_LIST (mhd_ResponseHeader,headers);
#ifndef NDEBUG
/**
* Special data for internal error responses
*/
struct mhd_ResponseInternalErrData special_resp;
#ifndef NDEBUG
struct mhd_ResponseDebug dbg;
#endif
};
+6 -1
View File
@@ -57,6 +57,11 @@ enum MHD_FIXED_ENUM_ mhd_SocketError
*/
mhd_SOCKET_ERR_NOMEM
,
/**
* The connection has been gracefully closed by remote peer
*/
mhd_SOCKET_ERR_REMT_DISCONN
,
/**
* The connection has been hard-closed by remote peer.
*/
@@ -128,7 +133,7 @@ enum MHD_FIXED_ENUM_ mhd_SocketError
/**
* Check whether the socket error is unrecoverable
*/
#define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_CONNRESET <= (err))
#define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_REMT_DISCONN <= (err))
/**
* Check whether the socket error is unexpected
+123
View File
@@ -0,0 +1,123 @@
/*
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/respond_with_error.c
* @brief The implementation of error response functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "respond_with_error.h"
#include "sys_base_types.h"
#include "sys_null_macro.h"
#include "mhd_str_macros.h"
#include "sys_malloc.h"
#include "mhd_connection.h"
#include "mhd_mempool.h"
#include "response_from.h"
#include "daemon_logger.h"
#include "response_destroy.h"
#include "stream_funcs.h"
#include "daemon_funcs.h"
#include "mhd_public_api.h"
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) void
respond_with_error_len (struct MHD_Connection *c,
unsigned int http_code,
size_t msg_len,
const char *msg,
size_t add_hdr_line_len,
char *add_hdr_line)
{
struct MHD_Response *err_res;
mhd_assert (! c->stop_with_error); /* Do not send error twice */
mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state);
/* Discard most of the request data */
if (NULL != c->rq.cntn.lbuf.buf)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.buf = NULL;
c->write_buffer = NULL;
c->write_buffer_size = 0;
c->write_buffer_send_offset = 0;
c->write_buffer_append_offset = 0;
mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
c->rq.version = NULL;
c->rq.method = NULL;
c->rq.url = NULL;
c->continue_message_write_offset = 0;
if (0 != c->read_buffer_size)
{
mhd_pool_deallocate (c->pool,
c->read_buffer,
c->read_buffer_size);
c->read_buffer = NULL;
c->read_buffer_size = 0;
c->read_buffer_offset = 0;
}
c->stop_with_error = true;
c->discard_request = true;
if ((MHD_HTTP_STATUS_CONTENT_TOO_LARGE == http_code) ||
(MHD_HTTP_STATUS_URI_TOO_LONG == http_code) ||
(MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE == http_code))
c->rq.too_large = true;
mhd_LOG_PRINT (c->daemon,
MHD_SC_REQ_PROCCESSING_ERR_REPLY,
mhd_LOG_FMT ("Error processing request. Sending %u " \
"error reply: %s"),
(unsigned int) http_code, msg);
if (NULL != c->rp.response)
{
mhd_response_dec_use_count (c->rp.response);
c->rp.response = NULL;
}
err_res = mhd_response_special_for_error (http_code,
msg_len,
msg,
add_hdr_line_len,
add_hdr_line);
if (NULL == err_res)
{
if (NULL != add_hdr_line)
free (add_hdr_line);
mhd_STREAM_ABORT (c, \
mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE, \
"No memory to create error response.");
return;
}
c->rp.response = err_res;
c->state = MHD_CONNECTION_START_REPLY;
}
+22 -13
View File
@@ -34,17 +34,28 @@
struct MHD_Connection; /* forward declaration */
/**
* Respond with provided error response.
* Current request will be aborted, stream will be closed after sending
* error response.
* @param c the connection to use
* @param http_code the reply HTTP status code
* @param msg_len the length of the @a msg
* @param msg the reply content, could be NULL
* @param add_hdr_line_len the length the @a add_hdr_line
* @param add_hdr_line the additional special header line, could be NULL,
* if not NULL it will be deallocated by free().
*
*/
MHD_INTERNAL void
respond_with_error_len (struct MHD_Connection *c,
unsigned int http_code,
size_t msg_len,
const char *msg,
size_t add_hdr_name_len,
const char *add_hdr_name,
size_t add_hdr_value_len,
const char *add_hdr_value)
size_t add_hdr_line_len,
char *add_hdr_line)
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (8);
MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6);
#ifdef HAVE_HTTP_AUTO_MESSAGES_BODIES
/**
@@ -53,16 +64,15 @@ MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (8);
# define mhd_RESPOND_WITH_ERROR_STATIC(c, code, msg) \
respond_with_error_len ((c), (code), \
mhd_SSTR_LEN (msg), (msg), \
0, NULL, 0, NULL)
0, NULL)
/**
* Transmit static string as error response and add specified header
*/
# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hd_n_l,hd_n,hd_v_l,hd_v) \
# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hdrl_l,hdrl) \
respond_with_error_len ((c), (code), \
mhd_SSTR_LEN (m), (m), \
(hd_n_l), (hd_n), \
(hd_v_l), (hd_v))
(hdrl_l), (hdrl))
#else
/**
@@ -71,16 +81,15 @@ MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_CSTR_ (8);
# define mhd_RESPOND_WITH_ERROR_STATIC(c, code, msg) \
respond_with_error_len ((c), (code), \
0, NULL, \
0, NULL, 0, NULL)
0, NULL)
/**
* Transmit static string as error response and add specified header
*/
# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hd_n_l,hd_n,hd_v_l,hd_v) \
# define mhd_RESPOND_WITH_ERROR_HEADER(c,code,m,hdrl_l,hdrl) \
respond_with_error_len ((c), (code), \
0, NULL, \
(hd_n_l), (hd_n), \
(hd_v_l), (hd_v))
(hdrl_l), (hdrl))
#endif
#endif /* ! MHD_RESPOND_WITH_ERROR_H */
+2
View File
@@ -49,6 +49,8 @@ static MHD_FN_PAR_NONNULL_ (1) void
response_full_detinit (struct MHD_Response *restrict r)
{
mhd_response_remove_all_headers (r);
if (NULL != r->special_resp.spec_hdr)
free (r->special_resp.spec_hdr);
if (r->reuse.reusable)
mhd_response_deinit_reusable (r);
mhd_response_deinit_content_data (r);
+52
View File
@@ -68,6 +68,9 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
{
#ifndef HAVE_NULL_PTR_ALL_ZEROS
mhd_DLINKEDL_INIT_LIST (r, headers);
r->free.cb = NULL;
r->free.cls = NULL;
r->special_resp.spec_hdr = NULL;
s->termination_callback.v_term_cb = NULL;
s->termination_callback.v_term_cb_cls = NULL;
@@ -353,3 +356,52 @@ MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
return res;
}
/**
* Create special internal response for sending error reply
* @param sc the HTTP status code
* @param cntn_len the length of the @a cntn
* @param cntn the content of the response, could be NULL
* @param spec_hdr_len the length of the @a spec_hdr
* @param spec_hdr the special header line, without last CRLF,
* if not NULL it will be deallocated by free().
* @return
*/
MHD_INTERNAL
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response *
mhd_response_special_for_error (unsigned int sc,
size_t cntn_len,
const char *cntn,
size_t spec_hdr_len,
char *spec_hdr)
{
struct MHD_Response *restrict res;
mhd_assert (100 <= sc);
mhd_assert (600 > sc);
mhd_assert ((NULL != cntn) || (0 == cntn_len));
mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len));
res = mhd_calloc (1, sizeof(struct MHD_Response));
if (NULL == res)
return NULL;
#ifndef HAVE_NULL_PTR_ALL_ZEROS
mhd_DLINKEDL_INIT_LIST (res, headers);
res->free.cb = NULL;
res->free.cls = NULL;
res->special_resp.spec_hdr = NULL;
#endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
res->sc = (enum MHD_HTTP_StatusCode) sc;
res->cntn_size = cntn_len;
res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : "");
res->cfg.close_forced = true;
res->cfg.int_err_resp = true;
res->special_resp.spec_hdr_len = spec_hdr_len;
res->special_resp.spec_hdr = spec_hdr;
res->frozen = true;
return res;
}
+9
View File
@@ -29,6 +29,7 @@
#define MHD_RESPONSE_FROM_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
struct MHD_Response; /* forward declaration */
@@ -40,5 +41,13 @@ MHD_INTERNAL void
mhd_response_deinit_content_data (struct MHD_Response *restrict r)
MHD_FN_PAR_NONNULL_ (1);
MHD_INTERNAL struct MHD_Response *
mhd_response_special_for_error (unsigned int sc,
size_t cntn_len,
const char *cntn,
size_t spec_hdr_len,
char *spec_hdr)
MHD_FN_PAR_CSTR_(3) MHD_FN_PAR_CSTR_(5);
#endif /* ! MHD_RESPONSE_FROM_H */
+2 -1
View File
@@ -76,7 +76,6 @@ response_set_properties (struct MHD_Response *restrict r)
{
struct ResponseOptions *restrict const s = r->settings;
mhd_assert (NULL != s);
r->frozen = true;
r->cfg.head_only = s->head_only_response;
if (s->http_1_0_compatible_strict)
@@ -102,6 +101,8 @@ response_set_properties (struct MHD_Response *restrict r)
// TODO: calculate size of the headers and the "Connection:" header
r->frozen = true;
r->settings = NULL;
free (s);
}
+88 -11
View File
@@ -32,6 +32,7 @@
#include "mhd_daemon.h"
#include "mhd_connection.h"
#include "mhd_response.h"
#include "mhd_assert.h"
#include "mhd_mempool.h"
#include "mhd_str.h"
@@ -43,6 +44,7 @@
#include "response_destroy.h"
#include "mhd_mono_clock.h"
#include "daemon_logger.h"
#include "daemon_funcs.h"
#include "mhd_public_api.h"
@@ -275,6 +277,8 @@ mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
mhd_assert ((0 == add_element_size) || (NULL != add_element));
c->rq.too_large = true;
if (MHD_CONNECTION_HEADERS_RECEIVED > c->state)
{
mhd_assert (NULL != c->rq.field_lines.start);
@@ -484,16 +488,15 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
if (! reuse)
{
mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \
(c->rp.response->cfg.int_err_resp));
/* Next function will destroy response, notify client,
* destroy memory pool, and set connection state to "CLOSED" */
mhd_conn_pre_close_req_finished (c);
c->read_buffer = NULL;
c->read_buffer_size = 0;
c->read_buffer_offset = 0;
c->write_buffer = NULL;
c->write_buffer_size = 0;
c->write_buffer_send_offset = 0;
c->write_buffer_append_offset = 0;
* destroy memory pool and set connection state to "CLOSED" */
mhd_conn_pre_close (c,
c->stop_with_error ?
mhd_CONN_CLOSE_ERR_REPLY_SENT :
mhd_CONN_CLOSE_HTTP_COMPLETED,
NULL);
}
else
{
@@ -501,6 +504,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
size_t new_read_buf_size;
mhd_assert (! c->stop_with_error);
mhd_assert (! c->discard_request);
mhd_assert (NULL == c->rq.cntn.lbuf.buf);
#if 0 // TODO: notification callback
if ( (NULL != d->notify_completed) &&
@@ -635,6 +639,7 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
const char *log_msg)
{
bool close_hard;
bool use_local_lingering;
enum MHD_RequestTerminationCode term_code;
enum MHD_StatusCode sc;
@@ -657,7 +662,17 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
break;
case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY:
close_hard = true;
term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
term_code = (! c->stop_with_error || c->rq.too_large) ?
MHD_REQUEST_TERMINATED_NO_RESOURCES :
MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
break;
case mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE:
close_hard = true;
term_code = c->rq.too_large ?
MHD_REQUEST_TERMINATED_NO_RESOURCES :
MHD_REQUEST_TERMINATED_HTTP_PROTOCOL_ERROR;
sc = MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE;
break;
case mhd_CONN_CLOSE_APP_ERROR:
close_hard = true;
@@ -680,6 +695,10 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
case mhd_SOCKET_ERR_NOMEM:
term_code = MHD_REQUEST_TERMINATED_NO_RESOURCES;
break;
case mhd_SOCKET_ERR_REMT_DISCONN:
close_hard = false;
term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
break;
case mhd_SOCKET_ERR_CONNRESET:
term_code = MHD_REQUEST_TERMINATED_CLIENT_ABORT;
break;
@@ -716,7 +735,7 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
term_code = MHD_REQUEST_TERMINATED_TIMEOUT_REACHED;
break;
case mhd_CONN_CLOSE_CLIENT_HTTP_ERR_SENT_ERR:
case mhd_CONN_CLOSE_ERR_REPLY_SENT:
close_hard = false;
term_code = c->rq.too_large ?
MHD_REQUEST_TERMINATED_NO_RESOURCES :
@@ -736,6 +755,7 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
use_local_lingering = false;
/* Make changes on the socket early to let the kernel and the remote
* to process the changes in parallel. */
if (close_hard)
@@ -752,6 +772,7 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
(void) 0; // TODO: start local lingering phase
c->state = MHD_CONNECTION_CLOSED; // TODO: start local lingering phase
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase
// use_local_lingering = true;
}
else
{ /* No need / not possible to linger */
@@ -780,6 +801,38 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
(void) term_code;
#endif
if (! mhd_D_HAS_THR_PER_CONN (c->daemon))
{
if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout)
mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \
c, by_timeout);
else
mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \
c, by_timeout);
}
#ifndef NDEBUG
c->dbg.pre_closed = true;
#endif
if (! use_local_lingering)
mhd_conn_pre_clean (c);
}
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1) void
mhd_conn_pre_clean (struct MHD_Connection *restrict c)
{
// TODO: support suspended connections
if ((NULL != mhd_DLINKEDL_GET_NEXT (c, proc_ready)) ||
(NULL != mhd_DLINKEDL_GET_PREV (c, proc_ready)) ||
(c == mhd_DLINKEDL_GET_FIRST (&(c->daemon->events), proc_ready)))
mhd_DLINKEDL_DEL (&(c->daemon->events), c, proc_ready);
if (NULL != c->rq.cntn.lbuf.buf)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.buf = NULL;
if (NULL != c->rp.response)
mhd_response_dec_use_count (c->rp.response);
c->rp.response = NULL;
@@ -792,6 +845,30 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
c->write_buffer_append_offset = 0;
c->write_buffer_size = 0;
c->write_buffer = NULL;
// TODO: call in the thread where it was allocated for thread-per-connection
mhd_pool_destroy (c->pool);
c->pool = NULL;
#ifdef MHD_USE_EPOLL
if (mhd_POLL_TYPE_EPOLL == daemon->events.poll_type)
{
struct epoll_event event;
event.events = 0;
event.data.ptr = NULL;
if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd,
EPOLL_CTL_DEL,
c->socket_fd,
&event))
{
mhd_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED,
"Failed to remove connection socket from epoll.");
}
}
#endif /* MHD_USE_EPOLL */
#ifndef NDEBUG
c->dbg.pre_cleaned = true;
#endif
}
+21 -7
View File
@@ -174,6 +174,11 @@ enum mhd_ConnCloseReason
*/
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY
,
/**
* No memory to create error response
*/
mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE
,
/**
* Application behaves incorrectly
*/
@@ -211,7 +216,7 @@ enum mhd_ConnCloseReason
* The connection must be closed after error response as the client
* violates HTTP specification
*/
mhd_CONN_CLOSE_CLIENT_HTTP_ERR_SENT_ERR
mhd_CONN_CLOSE_ERR_REPLY_SENT
,
/* Graceful closing */
@@ -224,9 +229,10 @@ enum mhd_ConnCloseReason
/**
* Perform initial clean-up and mark for closing.
* Set the reason to "request finished"
* Prepare connection for closing.
* @param c the connection for pre-closing
* @param reason the reason for closing
* @param log_msg the message for the log
*/
MHD_INTERNAL void
mhd_conn_pre_close (struct MHD_Connection *restrict c,
@@ -249,7 +255,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_app_abort(c) \
mhd_conn_pre_close (c, mhd_CONN_CLOSE_APP_ABORTED, NULL)
mhd_conn_pre_close ((c), mhd_CONN_CLOSE_APP_ABORTED, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -257,7 +263,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_skt_err(c) \
mhd_conn_pre_close (c, mhd_CONN_CLOSE_SOCKET_ERR, NULL)
mhd_conn_pre_close ((c), mhd_CONN_CLOSE_SOCKET_ERR, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -265,7 +271,7 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_req_finished(c) \
mhd_conn_pre_close (c, mhd_CONN_CLOSE_HTTP_COMPLETED, NULL)
mhd_conn_pre_close ((c), mhd_CONN_CLOSE_HTTP_COMPLETED, NULL)
/**
* Perform initial clean-up and mark for closing.
@@ -273,7 +279,15 @@ MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3);
* @param c the connection for pre-closing
*/
#define mhd_conn_pre_close_timedout(c) \
mhd_conn_pre_close (c, mhd_CONN_CLOSE_TIMEDOUT, NULL)
mhd_conn_pre_close ((c), mhd_CONN_CLOSE_TIMEDOUT, NULL)
/**
* Perform initial connection cleanup.
* The connection must be prepared for closing.
* @param c the connection for pre-closing
*/
MHD_INTERNAL void
mhd_conn_pre_clean (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ (1);
#endif /* ! MHD_STREAM_FUNCS_H */
+15 -2
View File
@@ -749,6 +749,19 @@ build_header_response_inn (struct MHD_Connection *restrict c)
/* * The headers * */
/* A special custom header */
if (0 != r->special_resp.spec_hdr_len)
{
mhd_assert (r->cfg.int_err_resp);
if (buf_size < pos + r->special_resp.spec_hdr_len + 2)
return false;
memcpy (buf + pos,
r->special_resp.spec_hdr,
r->special_resp.spec_hdr_len);
buf[pos++] = '\r';
buf[pos++] = '\n';
}
/* Main automatic headers */
/* The "Date:" header */
@@ -809,8 +822,8 @@ build_header_response_inn (struct MHD_Connection *restrict c)
(! r->cfg.chunked) &&
(! r->cfg.head_only))
{ /* The size is known and can be indicated by the header */
if (r->cfg.cnt_len_by_app)
{ /* The response does not have "Content-Length" header */
if (! r->cfg.cnt_len_by_app)
{ /* The response does not have app-defined "Content-Length" header */
if (! buffer_append_s (buf, &pos, buf_size,
MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
return false;
+439 -25
View File
@@ -45,18 +45,22 @@
#include "mhd_daemon.h"
#include "mhd_connection.h"
#include "mhd_public_api.h"
#include "daemon_logger.h"
#include "mhd_assert.h"
#include "mhd_panic.h"
#include "mhd_mempool.h"
#include "request_funcs.h"
#include "request_get_value.h"
#include "respond_with_error.h"
#include "stream_funcs.h"
#include "daemon_funcs.h"
#include "mhd_public_api.h"
/**
* Response text used when the request (http header) is
* malformed.
@@ -294,6 +298,35 @@
"<body>Request HTTP header is too big for the memory constraints " \
"of this webserver.</body>" \
"</html>"
/**
* Response text used when the request chunk size line with chunk extension
* cannot fit the buffer.
*/
#define ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
"<html>" \
"<head><title>Request too big</title></head>" \
"<body><p>The total size of the request target, the request field lines " \
"and the chunk size line exceeds the memory constraints of this web " \
"server.</p>" \
"<p>The request could be re-tried without chunk extensions, with a smaller " \
"chunk size, shorter field lines, a shorter request target or a shorter " \
"request method token.</p></body>" \
"</html>"
/**
* Response text used when the request chunk size line without chunk extension
* cannot fit the buffer.
*/
#define ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG \
"<html>" \
"<head><title>Request too big</title></head>" \
"<body><p>The total size of the request target, the request field lines " \
"and the chunk size line exceeds the memory constraints of this web " \
"server.</p>" \
"<p>The request could be re-tried with a smaller " \
"chunk size, shorter field lines, a shorter request target or a shorter " \
"request method token.</p></body>" \
"</html>"
/**
* Response text used when the request (http header) does not
@@ -378,6 +411,17 @@
"<body>The chunk size used in HTTP chunked encoded " \
"request is too large.</body></html>"
/**
* The reasonable length of the upload chunk "header" (the size specifier
* with optional chunk extension).
* MHD tries to keep the space in the read buffer large enough to read
* the chunk "header" in one step.
* The real "header" could be much larger, it will be handled correctly
* anyway, however it may require several rounds of buffer grow.
*/
#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
/**
* Get whether bare LF in HTTP header and other protocol elements
* should be treated as the line termination depending on the configured
@@ -1229,7 +1273,10 @@ process_request_target (struct MHD_Connection *c)
mhd_RESPOND_WITH_ERROR_STATIC (
c,
mhd_stream_get_no_space_err_status_code (c,MHD_PROC_RECV_URI, 0, NULL),
mhd_stream_get_no_space_err_status_code (c,
MHD_PROC_RECV_URI,
0,
NULL),
ERR_RSP_MSG_REQUEST_TOO_BIG);
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING != c->state);
return false;
@@ -1273,12 +1320,14 @@ process_request_target (struct MHD_Connection *c)
static void
send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
{
static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": ";
static const size_t hdr_prefix_len =
mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": ");
char *hdr_line;
char *b;
size_t fixed_uri_len;
size_t i;
size_t o;
char *hdr_name;
size_t hdr_name_len;
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri);
@@ -1288,7 +1337,7 @@ send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
+ 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
if ( (fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) ||
(fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
(NULL == (b = malloc (fixed_uri_len + 1))) )
(NULL == (hdr_line = malloc (fixed_uri_len + 1 + hdr_prefix_len))) )
{
mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \
"The request has whitespace character is " \
@@ -1296,6 +1345,8 @@ send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
"send automatic redirect to fixed URI.");
return;
}
memcpy (hdr_line, hdr_prefix, hdr_prefix_len);
b = hdr_line + hdr_prefix_len;
i = 0;
o = 0;
@@ -1336,26 +1387,12 @@ send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
mhd_assert (fixed_uri_len == o);
b[o] = 0; /* Zero-terminate the result */
hdr_name_len = mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION);
hdr_name = malloc (hdr_name_len + 1);
if (NULL != hdr_name)
{
memcpy (hdr_name,
MHD_HTTP_HEADER_LOCATION,
hdr_name_len + 1);
/* hdr_name and b are free()d within this call */
mhd_RESPOND_WITH_ERROR_HEADER (c,
MHD_HTTP_STATUS_MOVED_PERMANENTLY,
ERR_RSP_RQ_TARGET_INVALID_CHAR,
hdr_name_len,
hdr_name,
o,
b);
return;
}
free (b);
mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN,
"The request has whitespace character is in the URI.");
mhd_RESPOND_WITH_ERROR_HEADER (c,
MHD_HTTP_STATUS_MOVED_PERMANENTLY,
ERR_RSP_RQ_TARGET_INVALID_CHAR,
o + hdr_prefix_len,
hdr_line);
return;
}
@@ -3410,9 +3447,386 @@ mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
{
if (NULL != c->rq.cntn.lbuf.buf)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.buf = NULL;
if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size)
c->discard_request = true;
mhd_assert (NULL != c->rp.response);
c->state = MHD_CONNECTION_START_REPLY;
return true;
}
/**
* Send error reply when receive buffer space exhausted while receiving
* the chunk size line.
* @param c the connection to handle
* @param add_header the optional pointer to the partially received
* the current chunk size line.
* Could be not zero-terminated and can contain binary zeros.
* Can be NULL.
* @param add_header_size the size of the @a add_header
*/
static void
handle_req_chunk_size_line_no_space (struct MHD_Connection *c,
const char *chunk_size_line,
size_t chunk_size_line_size)
{
unsigned int err_code;
if (NULL != chunk_size_line)
{
const char *semicol;
/* Check for chunk extension */
semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
if (NULL != semicol)
{ /* Chunk extension present. It could be removed without any loss of the
details of the request. */
mhd_RESPOND_WITH_ERROR_STATIC (c,
MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG);
}
}
err_code = mhd_stream_get_no_space_err_status_code (c,
MHD_PROC_RECV_BODY_CHUNKED,
chunk_size_line_size,
chunk_size_line);
mhd_RESPOND_WITH_ERROR_STATIC (c,
err_code,
ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG);
}
/**
* Handle situation with read buffer exhaustion.
* Must be called when no more space left in the read buffer, no more
* space left in the memory pool to grow the read buffer, but more data
* need to be received from the client.
* Could be called when the result of received data processing cannot be
* stored in the memory pool (like some header).
* @param c the connection to process
* @param stage the receive stage where the exhaustion happens.
*/
static MHD_FN_PAR_NONNULL_ALL_ void
handle_recv_no_space (struct MHD_Connection *c,
enum MHD_ProcRecvDataStage stage)
{
mhd_assert (MHD_PROC_RECV_INIT <= stage);
mhd_assert (MHD_PROC_RECV_FOOTERS >= stage);
mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state);
mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
(MHD_CONNECTION_INIT == c->state));
mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
(MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_URI != stage) || \
(MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
(MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
(MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state));
mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
(MHD_CONNECTION_BODY_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
(MHD_CONNECTION_BODY_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
(MHD_CONNECTION_FOOTERS_RECEIVING == c->state));
mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
(! c->rq.have_chunked_upload));
mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
(c->rq.have_chunked_upload));
switch (stage)
{
case MHD_PROC_RECV_INIT:
case MHD_PROC_RECV_METHOD:
/* Some data has been received, but it is not clear yet whether
* the received data is an valid HTTP request */
mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
"No space left in the read buffer when " \
"receiving the initial part of " \
"the request line.");
return;
case MHD_PROC_RECV_URI:
case MHD_PROC_RECV_HTTPVER:
/* Some data has been received, but the request line is incomplete */
mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd);
mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver);
/* A quick simple check whether the incomplete line looks
* like an HTTP request */
if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
(mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
{
mhd_RESPOND_WITH_ERROR_STATIC (c,
MHD_HTTP_STATUS_URI_TOO_LONG,
ERR_RSP_MSG_REQUEST_TOO_BIG);
return;
}
mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
"No space left in the read buffer when " \
"receiving the URI in " \
"the request line. " \
"The request uses non-standard HTTP request " \
"method token.");
return;
case MHD_PROC_RECV_HEADERS:
handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset);
return;
case MHD_PROC_RECV_BODY_NORMAL:
/* A header probably has been added to a suspended connection and
it took precisely all the space in the buffer.
Very low probability. */
mhd_assert (! c->rq.have_chunked_upload);
handle_req_headers_no_space (c, NULL, 0); // FIXME: check
return;
case MHD_PROC_RECV_BODY_CHUNKED:
mhd_assert (c->rq.have_chunked_upload);
if (c->rq.current_chunk_offset != c->rq.current_chunk_size)
{ /* Receiving content of the chunk */
/* A header probably has been added to a suspended connection and
it took precisely all the space in the buffer.
Very low probability. */
handle_req_headers_no_space (c, NULL, 0); // FIXME: check
}
else
{
if (0 != c->rq.current_chunk_size)
{ /* Waiting for chunk-closing CRLF */
/* Not really possible as some payload should be
processed and the space used by payload should be available. */
handle_req_headers_no_space (c, NULL, 0); // FIXME: check
}
else
{ /* Reading the line with the chunk size */
handle_req_chunk_size_line_no_space (c,
c->read_buffer,
c->read_buffer_offset);
}
}
return;
case MHD_PROC_RECV_FOOTERS:
handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset);
return;
/* The next cases should not be possible */
case MHD_PROC_RECV_COOKIE:
default:
break;
}
mhd_assert (0 && "Should be unreachable");
}
/**
* Try growing the read buffer. We initially claim half the available
* buffer space for the read buffer (the other half being left for
* management data structures; the write buffer can in the end take
* virtually everything as the read buffer can be reduced to the
* minimum necessary at that point.
*
* @param connection the connection
* @param required set to 'true' if grow is required, i.e. connection
* will fail if no additional space is granted
* @return 'true' on success, 'false' on failure
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
try_grow_read_buffer (struct MHD_Connection *restrict connection,
bool required)
{
size_t new_size;
size_t avail_size;
const size_t def_grow_size = 1536; // TODO: remove hardcoded increment
void *rb;
avail_size = mhd_pool_get_free (connection->pool);
if (0 == avail_size)
return false; /* No more space available */
if (0 == connection->read_buffer_size)
new_size = avail_size / 2; /* Use half of available buffer for reading */
else
{
size_t grow_size;
grow_size = avail_size / 8;
if (def_grow_size > grow_size)
{ /* Shortage of space */
const size_t left_free =
connection->read_buffer_size - connection->read_buffer_offset;
mhd_assert (connection->read_buffer_size >= \
connection->read_buffer_offset);
if ((def_grow_size <= grow_size + left_free)
&& (left_free < def_grow_size))
grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
else if (! required)
return false; /* Grow is not mandatory, leave some space in pool */
else
{
/* Shortage of space, but grow is mandatory */
const size_t small_inc =
((mhd_BUF_INC_SIZE > def_grow_size) ?
def_grow_size : mhd_BUF_INC_SIZE) / 8;
if (small_inc < avail_size)
grow_size = small_inc;
else
grow_size = avail_size;
}
}
new_size = connection->read_buffer_size + grow_size;
}
/* Make sure that read buffer will not be moved */
if ((NULL != connection->read_buffer) &&
! mhd_pool_is_resizable_inplace (connection->pool,
connection->read_buffer,
connection->read_buffer_size))
{
mhd_assert (0);
return false;
}
/* we can actually grow the buffer, do it! */
rb = mhd_pool_reallocate (connection->pool,
connection->read_buffer,
connection->read_buffer_size,
new_size);
if (NULL == rb)
{
/* This should NOT be possible: we just computed 'new_size' so that
it should fit. If it happens, somehow our read buffer is not in
the right position in the pool, say because someone called
mhd_pool_allocate() without 'from_end' set to 'true'? Anyway,
should be investigated! (Ideally provide all data from
*pool and connection->read_buffer and new_size for debugging). */
mhd_assert (0);
return false;
}
mhd_assert (connection->read_buffer == rb);
connection->read_buffer = rb;
mhd_assert (NULL != connection->read_buffer);
connection->read_buffer_size = new_size;
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
{
/**
* The increase of read buffer size is desirable.
*/
bool rbuff_grow_desired;
/**
* The increase of read buffer size is a hard requirement.
*/
bool rbuff_grow_required;
mhd_assert (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info));
mhd_assert (! c->discard_request);
rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
if (rbuff_grow_required)
rbuff_grow_desired = true;
else
{
rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size
c->read_buffer_size);
if ((rbuff_grow_desired) &&
(MHD_CONNECTION_BODY_RECEIVING == c->state))
{
if (! c->rq.have_chunked_upload)
{
mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size);
/* Do not grow read buffer more than necessary to process the current
request. */
rbuff_grow_desired =
(c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME
}
else
{
mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
if (0 == c->rq.current_chunk_size)
rbuff_grow_desired = /* Reading value of the next chunk size */
(MHD_CHUNK_HEADER_REASONABLE_LEN >
c->read_buffer_size);
else
{
const uint_fast64_t cur_chunk_left =
c->rq.current_chunk_size - c->rq.current_chunk_offset;
/* Do not grow read buffer more than necessary to process the current
chunk with terminating CRLF. */
mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size);
rbuff_grow_desired =
((cur_chunk_left + 2) > (uint_fast64_t) (c->read_buffer_size));
}
}
}
}
if (! rbuff_grow_desired)
return true; /* No need to increase the buffer */
if (try_grow_read_buffer (c, rbuff_grow_required))
return true; /* Buffer increase succeed */
if (! rbuff_grow_required)
return true; /* Can continue without buffer increase */
/* Failed to increase the read buffer size, but need to read the data
from the network.
No more space left in the buffer, no more space to increase the buffer. */
if (1)
{
enum MHD_ProcRecvDataStage stage;
switch (c->state)
{
case MHD_CONNECTION_INIT:
stage = MHD_PROC_RECV_INIT;
break;
case MHD_CONNECTION_REQ_LINE_RECEIVING:
if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd)
stage = MHD_PROC_RECV_METHOD;
else if (0 == c->rq.req_target_len)
stage = MHD_PROC_RECV_URI;
else
stage = MHD_PROC_RECV_HTTPVER;
break;
case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
stage = MHD_PROC_RECV_HEADERS;
break;
case MHD_CONNECTION_BODY_RECEIVING:
stage = c->rq.have_chunked_upload ?
MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
break;
case MHD_CONNECTION_FOOTERS_RECEIVING:
stage = MHD_PROC_RECV_FOOTERS;
break;
case MHD_CONNECTION_REQ_LINE_RECEIVED:
case MHD_CONNECTION_HEADERS_RECEIVED:
case MHD_CONNECTION_HEADERS_PROCESSED:
case MHD_CONNECTION_CONTINUE_SENDING:
case MHD_CONNECTION_BODY_RECEIVED:
case MHD_CONNECTION_FOOTERS_RECEIVED:
case MHD_CONNECTION_FULL_REQ_RECEIVED:
case MHD_CONNECTION_REQ_RECV_FINISHED:
case MHD_CONNECTION_START_REPLY:
case MHD_CONNECTION_HEADERS_SENDING:
case MHD_CONNECTION_HEADERS_SENT:
case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
case MHD_CONNECTION_UNCHUNKED_BODY_READY:
case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
case MHD_CONNECTION_CHUNKED_BODY_READY:
case MHD_CONNECTION_CHUNKED_BODY_SENT:
case MHD_CONNECTION_FOOTERS_SENDING:
case MHD_CONNECTION_FULL_REPLY_SENT:
case MHD_CONNECTION_CLOSED:
#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
case MHD_CONNECTION_UPGRADE:
#endif
default:
mhd_assert (0);
MHD_UNREACHABLE_;
stage = MHD_PROC_RECV_BODY_NORMAL;
}
handle_recv_no_space (c, stage);
}
return false;
}
+16
View File
@@ -180,5 +180,21 @@ MHD_INTERNAL bool
mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Check whether enough space is available in the read buffer for the next
* operation.
* Handles grow of the buffer if required and error conditions (when buffer
* grow is required but not possible).
* Must be called only when processing the event loop states and when
* reading is required for the next phase.
* @param c the connection to check
* @return true if connection handled successfully and enough buffer
* is available,
* false if not enough buffer is available and the loop's states
* must be processed again as connection is in the error state.
*/
MHD_INTERNAL bool
mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_STREAM_PROCESS_REQUEST_H */
+124 -5
View File
@@ -44,7 +44,122 @@
static MHD_FN_PAR_NONNULL_ALL_ bool
update_active_state (struct MHD_Connection *restrict c)
{
mhd_assert (0); (void) c;
/* Do not update states of suspended connection */
mhd_assert (! c->suspended);
#if 0 // def HTTPS_SUPPORT // TODO: implement TLS support
if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
{ /* HTTPS connection. */
switch (connection->tls_state)
{
}
}
#endif /* HTTPS_SUPPORT */
while (1)
{
switch (c->state)
{
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
break;
case MHD_CONNECTION_REQ_LINE_RECEIVED:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
break;
case MHD_CONNECTION_HEADERS_RECEIVED:
case MHD_CONNECTION_HEADERS_PROCESSED:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_CONTINUE_SENDING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_BODY_RECEIVING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
break;
case MHD_CONNECTION_BODY_RECEIVED:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_FOOTERS_RECEIVING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
break;
case MHD_CONNECTION_FOOTERS_RECEIVED:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_FULL_REQ_RECEIVED:
mhd_assert (0 && "Should not be possible");
c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
break;
case MHD_CONNECTION_REQ_RECV_FINISHED:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_START_REPLY:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_HEADERS_SENDING:
/* headers in buffer, keep writing */
c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_HEADERS_SENT:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY:
mhd_assert (0 && "Should not be possible");
c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
break;
case MHD_CONNECTION_UNCHUNKED_BODY_READY:
c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
mhd_assert (0 && "Should not be possible");
c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
break;
case MHD_CONNECTION_CHUNKED_BODY_READY:
c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_CHUNKED_BODY_SENT:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_FOOTERS_SENDING:
c->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
break;
case MHD_CONNECTION_FULL_REPLY_SENT:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
case MHD_CONNECTION_CLOSED:
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
return false; /* do nothing, not even reading */
#if 0 // def UPGRADE_SUPPORT // TODO: Upgrade support
case MHD_CONNECTION_UPGRADE:
mhd_assert (0);
break;
#endif /* UPGRADE_SUPPORT */
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
}
if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
{
/* Check whether the space is available to receive data */
if (! mhd_stream_check_and_grow_read_buffer_space (c))
{
mhd_assert (c->discard_request);
continue;
}
}
break; /* Everything was processed. */
}
return true;
}
@@ -67,7 +182,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
if ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) ||
(0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
{
mhd_assert ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) || \
mhd_assert ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) || \
mhd_SOCKET_ERR_IS_HARD (c->sk_discnt_err));
if ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) ||
(mhd_SOCKET_ERR_NOT_CHECKED == c->sk_discnt_err))
@@ -280,23 +395,27 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
&& ! c->sk_rmt_shut_wr);
continue;
case MHD_CONNECTION_CLOSED:
mhd_assert (0 && "Should be unreachable");
MHD_UNREACHABLE_;
return false;
break;
#if 0 // def UPGRADE_SUPPORT
case MHD_CONNECTION_UPGRADE:
return MHD_YES; /* keep open */
#endif /* UPGRADE_SUPPORT */
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
break;
}
break;
}
if (MHD_CONNECTION_CLOSED == c->state)
return false;
if (c->suspended)
{
// TODO: process
mhd_assert (0 && "Not implemented yet");
return true;
}
if (mhd_stream_check_timedout (c)) // TODO: centralise timeout checks