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