libmicrohttpd

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

commit 8abd74f3dc9a0482111a2fc8f99b59434f80acd9
parent 7d7ccbcdbdc45e72b30cb180eaf892dc782c408a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 14 Mar 2018 05:31:09 +0100

add suspend_resume_epoll example (from mailinglist)

Diffstat:
M.gitignore | 1+
MAUTHORS | 1+
Mconfigure.ac | 2++
Mpo/Makefile.in.in | 72+++++++++++++++++++++---------------------------------------------------
Msrc/examples/Makefile.am | 12++++++++++++
Asrc/examples/suspend_resume_epoll.c | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 239 insertions(+), 51 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -46,3 +46,4 @@ po/POTFILES po/configargs.stamp **~ doc/libmicrohttpd.log +src/examples/suspend_resume_epoll diff --git a/AUTHORS b/AUTHORS @@ -57,6 +57,7 @@ Denis Dowling <denis.dowling@hsd.com.au> Louis Benoit <louisbenoit@videotron.ca> Flavio Coelin <flavio.ceolin@intel.com> Silvio Clecio <silvioprog@gmail.com> +Robert D Kosisko <rkocisko@gmail.com> Documentation contributions also came from: Marco Maggi <marco.maggi-ipsu@poste.it> diff --git a/configure.ac b/configure.ac @@ -735,6 +735,8 @@ if test "$enable_epoll" != "no"; then fi fi +AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]]) + if test "x$enable_epoll" = "xyes"; then AC_CACHE_CHECK([for epoll_create1()], [mhd_cv_have_epoll_create1], [ AC_LINK_IFELSE([ diff --git a/po/Makefile.in.in b/po/Makefile.in.in @@ -1,19 +1,20 @@ # Makefile for PO directory in any package using GNU gettext. # Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper <drepper@gnu.ai.mit.edu> # -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. This file is offered as-is, -# without any warranty. +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU General Public +# License but which still want to provide support for the GNU gettext +# functionality. +# Please note that the actual code of GNU gettext is covered by the GNU +# General Public License and is *not* in the public domain. # -# Origin: gettext-0.19.8 -GETTEXT_MACRO_VERSION = 0.19 +# Origin: gettext-0.18.2 +GETTEXT_MACRO_VERSION = 0.18 PACKAGE = @PACKAGE@ VERSION = @VERSION@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -SED = @SED@ SHELL = /bin/sh @SET_MAKE@ @@ -43,11 +44,6 @@ install_sh = $(SHELL) @install_sh@ MKDIR_P = @MKDIR_P@ mkdir_p = @mkdir_p@ -# When building gettext-tools, we prefer to use the built programs -# rather than installed programs. However, we can't do that when we -# are cross compiling. -CROSS_COMPILING = @CROSS_COMPILING@ - GMSGFMT_ = @GMSGFMT@ GMSGFMT_no = @GMSGFMT@ GMSGFMT_yes = @GMSGFMT_015@ @@ -80,16 +76,6 @@ POTFILES = \ CATALOGS = @CATALOGS@ -POFILESDEPS_ = $(srcdir)/$(DOMAIN).pot -POFILESDEPS_yes = $(POFILESDEPS_) -POFILESDEPS_no = -POFILESDEPS = $(POFILESDEPS_$(PO_DEPENDS_ON_POT)) - -DISTFILESDEPS_ = update-po -DISTFILESDEPS_yes = $(DISTFILESDEPS_) -DISTFILESDEPS_no = -DISTFILESDEPS = $(DISTFILESDEPS_$(DIST_DEPENDS_ON_UPDATE_PO)) - # Makevars gets inserted here. (Don't remove this line!) .SUFFIXES: @@ -156,25 +142,15 @@ stamp-po: $(srcdir)/$(DOMAIN).pot # heuristic whether some file in the top level directory mentions "GNU xyz". # If GNU 'find' is available, we avoid grepping through monster files. $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed - package_gnu="$(PACKAGE_GNU)"; \ - test -n "$$package_gnu" || { \ - if { if (LC_ALL=C find --version) 2>/dev/null | grep GNU >/dev/null; then \ - LC_ALL=C find -L $(top_srcdir) -maxdepth 1 -type f \ - -size -10000000c -exec grep 'GNU @PACKAGE@' \ - /dev/null '{}' ';' 2>/dev/null; \ - else \ - LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null; \ - fi; \ - } | grep -v 'libtool:' >/dev/null; then \ - package_gnu=yes; \ - else \ - package_gnu=no; \ - fi; \ - }; \ - if test "$$package_gnu" = "yes"; then \ - package_prefix='GNU '; \ + if { if (LC_ALL=C find --version) 2>/dev/null | grep GNU >/dev/null; then \ + LC_ALL=C find -L $(top_srcdir) -maxdepth 1 -type f -size -10000000c -exec grep 'GNU @PACKAGE@' /dev/null '{}' ';' 2>/dev/null; \ + else \ + LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null; \ + fi; \ + } | grep -v 'libtool:' >/dev/null; then \ + package_gnu='GNU '; \ else \ - package_prefix=''; \ + package_gnu=''; \ fi; \ if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ @@ -194,17 +170,12 @@ $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ --files-from=$(srcdir)/POTFILES.in \ --copyright-holder='$(COPYRIGHT_HOLDER)' \ - --package-name="$${package_prefix}@PACKAGE@" \ + --package-name="$${package_gnu}@PACKAGE@" \ --package-version='@VERSION@' \ --msgid-bugs-address="$$msgid_bugs_address" \ ;; \ esac test ! -f $(DOMAIN).po || { \ - if test -f $(srcdir)/$(DOMAIN).pot-header; then \ - sed -e '1,/^#$$/d' < $(DOMAIN).po > $(DOMAIN).1po && \ - cat $(srcdir)/$(DOMAIN).pot-header $(DOMAIN).1po > $(DOMAIN).po; \ - rm -f $(DOMAIN).1po; \ - fi; \ if test -f $(srcdir)/$(DOMAIN).pot; then \ sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ @@ -227,14 +198,13 @@ $(srcdir)/$(DOMAIN).pot: # This target rebuilds a PO file if $(DOMAIN).pot has changed. # Note that a PO file is not touched if it doesn't need to be changed. -$(POFILES): $(POFILESDEPS) +$(POFILES): $(srcdir)/$(DOMAIN).pot @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ if test -f "$(srcdir)/$${lang}.po"; then \ - test -f $(srcdir)/$(DOMAIN).pot || $(MAKE) $(srcdir)/$(DOMAIN).pot; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \ cd $(srcdir) \ - && { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + && { case `$(MSGMERGE_UPDATE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \ *) \ @@ -391,7 +361,7 @@ maintainer-clean: distclean distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) dist distdir: - test -z "$(DISTFILESDEPS)" || $(MAKE) $(DISTFILESDEPS) + $(MAKE) update-po @$(MAKE) dist2 # This is a separate target because 'update-po' must be executed before. dist2: stamp-po $(DISTFILES) @@ -435,7 +405,7 @@ update-po: Makefile .nop.po-update: @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ - if test "$(PACKAGE)" = "gettext-tools" && test "$(CROSS_COMPILING)" != "yes"; then PATH=`pwd`/../src:$$PATH; fi; \ + if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \ tmpdir=`pwd`; \ echo "$$lang:"; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -29,6 +29,11 @@ noinst_PROGRAMS = \ fileserver_example_external_select \ refuse_post_example +if MHD_HAVE_EPOLL +noinst_PROGRAMS += \ + suspend_resume_epoll +endif + EXTRA_DIST = msgs_i18n.c noinst_EXTRA_DIST = msgs_i18n.c @@ -123,6 +128,13 @@ benchmark_CPPFLAGS = \ benchmark_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la +suspend_resume_epoll_SOURCES = \ + suspend_resume_epoll.c +suspend_resume_epoll_CPPFLAGS = \ + $(AM_CPPFLAGS) $(CPU_COUNT_DEF) +suspend_resume_epoll_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la + benchmark_https_SOURCES = \ benchmark_https.c benchmark_https_CPPFLAGS = \ diff --git a/src/examples/suspend_resume_epoll.c b/src/examples/suspend_resume_epoll.c @@ -0,0 +1,202 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2018 Christian Grothoff (and other contributing authors) + + This library 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. + + This library 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 suspend_resume_epoll.c + * @brief example for how to use libmicrohttpd with epoll() and + * resume a suspended connection + * @author Robert D Kocisko + * @author Christian Grothoff + */ +#include "platform.h" +#include <microhttpd.h> +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <limits.h> + +#define TIMEOUT_INFINITE -1 + +struct Request { + struct MHD_Connection *connection; + int timerfd; +}; + + +static int epfd; + +static struct epoll_event evt; + + +static int +ahc_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, size_t *upload_data_size, void **ptr) +{ + struct MHD_Response *response; + int ret; + struct Request* req; + struct itimerspec ts; + (void)url; /* Unused. Silence compiler warning. */ + (void)version; /* Unused. Silence compiler warning. */ + (void)upload_data; /* Unused. Silence compiler warning. */ + (void)upload_data_size; /* Unused. Silence compiler warning. */ + + req = *ptr; + if (!req) + { + + req = malloc(sizeof(struct Request)); + req->connection = connection; + req->timerfd = 0; + *ptr = req; + return MHD_YES; + } + + if (req->timerfd) + { + // send response (echo request url) + response = MHD_create_response_from_buffer (strlen (url), + (void *) url, + MHD_RESPMEM_MUST_COPY); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return ret; + } + else + { + // create timer and suspend connection + req->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (-1 == req->timerfd) + { + printf("timerfd_create: %s", strerror(errno)); + return MHD_NO; + } + evt.events = EPOLLIN; + evt.data.ptr = req; + if (-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, req->timerfd, &evt)) + { + printf("epoll_ctl: %s", strerror(errno)); + return MHD_NO; + } + ts.it_value.tv_sec = 1; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + if (-1 == timerfd_settime(req->timerfd, 0, &ts, NULL)) + { + printf("timerfd_settime: %s", strerror(errno)); + return MHD_NO; + } + MHD_suspend_connection(connection); + return MHD_YES; + } +} + + +static int +connection_done(struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + free(*con_cls); +} + + +int +main (int argc, + char *const *argv) +{ + struct MHD_Daemon *d; + const union MHD_DaemonInfo * info; + int current_event_count; + struct epoll_event events_list[1]; + struct Request *req; + uint64_t timer_expirations; + + if (argc != 2) + { + printf ("%s PORT\n", argv[0]); + return 1; + } + d = MHD_start_daemon (MHD_USE_EPOLL | MHD_ALLOW_SUSPEND_RESUME, + atoi (argv[1]), + NULL, NULL, &ahc_echo, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &connection_done, NULL, + MHD_OPTION_END); + if (d == NULL) + return 1; + + info = MHD_get_daemon_info(d, MHD_DAEMON_INFO_EPOLL_FD); + if (info == NULL) + return 1; + + epfd = epoll_create1(EPOLL_CLOEXEC); + if (-1 == epfd) + return 1; + + evt.events = EPOLLIN; + evt.data.ptr = NULL; + if (-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, info->epoll_fd, &evt)) + return 1; + + while (1) + { + int timeout; + MHD_UNSIGNED_LONG_LONG to; + + if (MHD_YES != + MHD_get_timeout (d, + &to)) + timeout = TIMEOUT_INFINITE; + else + timeout = (to < INT_MAX - 1) ? (int) to : (INT_MAX - 1); + current_event_count = epoll_wait(epfd, events_list, 1, timeout); + + if (1 == current_event_count) + { + if (events_list[0].data.ptr) + { + // A timer has timed out + req = events_list[0].data.ptr; + // read from the fd so the system knows we heard the notice + if (-1 == read(req->timerfd, &timer_expirations, sizeof(timer_expirations))) + { + return 1; + } + // Now resume the connection + MHD_resume_connection(req->connection); + } + } + else if (0 == current_event_count) + { + // no events: continue + } + else + { + // error + return 1; + } + if (! MHD_run(d)) + return 1; + } + + return 0; +}