suspend_resume_epoll.c (5701B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2018 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /** 21 * @file suspend_resume_epoll.c 22 * @brief example for how to use libmicrohttpd with epoll() and 23 * resume a suspended connection 24 * @author Robert D Kocisko 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 #include "platform.h" 29 #include <microhttpd.h> 30 #include <sys/epoll.h> 31 #include <sys/timerfd.h> 32 #include <limits.h> 33 #include <errno.h> 34 35 #define TIMEOUT_INFINITE -1 36 37 struct Request 38 { 39 struct MHD_Connection *connection; 40 int timerfd; 41 }; 42 43 44 static int epfd; 45 46 static struct epoll_event evt; 47 48 49 static enum MHD_Result 50 ahc_echo (void *cls, 51 struct MHD_Connection *connection, 52 const char *url, 53 const char *method, 54 const char *version, 55 const char *upload_data, size_t *upload_data_size, void **req_cls) 56 { 57 struct MHD_Response *response; 58 enum MHD_Result ret; 59 struct Request *req; 60 struct itimerspec ts; 61 62 (void) cls; 63 (void) method; 64 (void) version; /* Unused. Silence compiler warning. */ 65 (void) upload_data; /* Unused. Silence compiler warning. */ 66 (void) upload_data_size; /* Unused. Silence compiler warning. */ 67 req = *req_cls; 68 if (NULL == req) 69 { 70 71 req = malloc (sizeof(struct Request)); 72 if (NULL == req) 73 return MHD_NO; 74 req->connection = connection; 75 req->timerfd = -1; 76 *req_cls = req; 77 return MHD_YES; 78 } 79 80 if (-1 != req->timerfd) 81 { 82 /* send response (echo request url) */ 83 response = MHD_create_response_from_buffer_copy (strlen (url), 84 (const void *) url); 85 if (NULL == response) 86 return MHD_NO; 87 ret = MHD_queue_response (connection, 88 MHD_HTTP_OK, 89 response); 90 MHD_destroy_response (response); 91 return ret; 92 } 93 /* create timer and suspend connection */ 94 req->timerfd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK); 95 if (-1 == req->timerfd) 96 { 97 printf ("timerfd_create: %s", strerror (errno)); 98 return MHD_NO; 99 } 100 evt.events = EPOLLIN; 101 evt.data.ptr = req; 102 if (-1 == epoll_ctl (epfd, EPOLL_CTL_ADD, req->timerfd, &evt)) 103 { 104 printf ("epoll_ctl: %s", strerror (errno)); 105 return MHD_NO; 106 } 107 ts.it_value.tv_sec = 1; 108 ts.it_value.tv_nsec = 0; 109 ts.it_interval.tv_sec = 0; 110 ts.it_interval.tv_nsec = 0; 111 if (-1 == timerfd_settime (req->timerfd, 0, &ts, NULL)) 112 { 113 printf ("timerfd_settime: %s", strerror (errno)); 114 return MHD_NO; 115 } 116 MHD_suspend_connection (connection); 117 return MHD_YES; 118 } 119 120 121 static void 122 connection_done (void *cls, 123 struct MHD_Connection *connection, 124 void **req_cls, 125 enum MHD_RequestTerminationCode toe) 126 { 127 struct Request *req = *req_cls; 128 129 (void) cls; 130 (void) connection; 131 (void) toe; 132 if (-1 != req->timerfd) 133 if (0 != close (req->timerfd)) 134 abort (); 135 free (req); 136 } 137 138 139 int 140 main (int argc, 141 char *const *argv) 142 { 143 struct MHD_Daemon *d; 144 const union MHD_DaemonInfo *info; 145 int current_event_count; 146 struct epoll_event events_list[1]; 147 struct Request *req; 148 uint64_t timer_expirations; 149 int port; 150 151 if (argc != 2) 152 { 153 printf ("%s PORT\n", argv[0]); 154 return 1; 155 } 156 port = atoi (argv[1]); 157 if ( (1 > port) || (port > 65535) ) 158 { 159 fprintf (stderr, 160 "Port must be a number between 1 and 65535.\n"); 161 return 1; 162 } 163 d = MHD_start_daemon (MHD_USE_EPOLL | MHD_ALLOW_SUSPEND_RESUME, 164 (uint16_t) port, 165 NULL, NULL, &ahc_echo, NULL, 166 MHD_OPTION_NOTIFY_COMPLETED, &connection_done, NULL, 167 MHD_OPTION_END); 168 if (d == NULL) 169 return 1; 170 171 info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_EPOLL_FD); 172 if (info == NULL) 173 return 1; 174 175 epfd = epoll_create1 (EPOLL_CLOEXEC); 176 if (-1 == epfd) 177 return 1; 178 179 evt.events = EPOLLIN; 180 evt.data.ptr = NULL; 181 if (-1 == epoll_ctl (epfd, EPOLL_CTL_ADD, info->epoll_fd, &evt)) 182 return 1; 183 184 while (1) 185 { 186 current_event_count = epoll_wait (epfd, events_list, 1, 187 MHD_get_timeout_i (d)); 188 189 if (1 == current_event_count) 190 { 191 if (events_list[0].data.ptr) 192 { 193 /* A timer has timed out */ 194 req = events_list[0].data.ptr; 195 /* read from the fd so the system knows we heard the notice */ 196 if (-1 == read (req->timerfd, &timer_expirations, 197 sizeof(timer_expirations))) 198 { 199 return 1; 200 } 201 /* Now resume the connection */ 202 MHD_resume_connection (req->connection); 203 } 204 } 205 else if (0 == current_event_count) 206 { 207 /* no events: continue */ 208 } 209 else 210 { 211 /* error */ 212 return 1; 213 } 214 if (! MHD_run (d)) 215 return 1; 216 } 217 218 return 0; 219 }