| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2018 Christian Grothoff (and other contributing authors) |
| Copyright (C) 2022 Evgeny Grin (Karlson2k) |
| |
| 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 |
| * @author Karlson2k (Evgeny Grin) |
| */ |
| #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 enum MHD_Result |
| 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 **req_cls) |
| { |
| struct MHD_Response *response; |
| enum MHD_Result ret; |
| struct Request *req; |
| struct itimerspec ts; |
| |
| (void) cls; |
| (void) method; |
| (void) version; /* Unused. Silence compiler warning. */ |
| (void) upload_data; /* Unused. Silence compiler warning. */ |
| (void) upload_data_size; /* Unused. Silence compiler warning. */ |
| req = *req_cls; |
| if (NULL == req) |
| { |
| |
| req = malloc (sizeof(struct Request)); |
| if (NULL == req) |
| return MHD_NO; |
| req->connection = connection; |
| req->timerfd = -1; |
| *req_cls = req; |
| return MHD_YES; |
| } |
| |
| if (-1 != req->timerfd) |
| { |
| /* send response (echo request url) */ |
| response = MHD_create_response_from_buffer_copy (strlen (url), |
| (const void *) url); |
| if (NULL == response) |
| return MHD_NO; |
| ret = MHD_queue_response (connection, |
| MHD_HTTP_OK, |
| response); |
| MHD_destroy_response (response); |
| return ret; |
| } |
| /* 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 void |
| connection_done (void *cls, |
| struct MHD_Connection *connection, |
| void **req_cls, |
| enum MHD_RequestTerminationCode toe) |
| { |
| struct Request *req = *req_cls; |
| |
| (void) cls; |
| (void) connection; |
| (void) toe; |
| if (-1 != req->timerfd) |
| if (0 != close (req->timerfd)) |
| abort (); |
| free (req); |
| } |
| |
| |
| 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; |
| int port; |
| |
| if (argc != 2) |
| { |
| printf ("%s PORT\n", argv[0]); |
| return 1; |
| } |
| port = atoi (argv[1]); |
| if ( (1 > port) || (port > 65535) ) |
| { |
| fprintf (stderr, |
| "Port must be a number between 1 and 65535.\n"); |
| return 1; |
| } |
| d = MHD_start_daemon (MHD_USE_EPOLL | MHD_ALLOW_SUSPEND_RESUME, |
| (uint16_t) port, |
| 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) |
| { |
| current_event_count = epoll_wait (epfd, events_list, 1, |
| MHD_get_timeout_i (d)); |
| |
| 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; |
| } |