mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-27 04:09:31 +03:00
WIP-5
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@ SUBDIRS += microhttpd_ws
|
||||
endif
|
||||
|
||||
if BUILD_EXAMPLES
|
||||
#SUBDIRS += examples
|
||||
SUBDIRS += examples
|
||||
endif
|
||||
|
||||
if BUILD_TOOLS
|
||||
|
||||
+10
-245
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user