diff options
author | lv-426 <oxcafebaby@yahoo.com> | 2008-05-24 13:59:21 +0000 |
---|---|---|
committer | lv-426 <oxcafebaby@yahoo.com> | 2008-05-24 13:59:21 +0000 |
commit | c51d65ffce7b0542a15afa4d7bb252df6a970b5c (patch) | |
tree | 82dcb0f2fb0f3e9b115e77bae74c42a1186b39f5 | |
parent | a0cc83b7c2325dfceec0851c2046148059c84619 (diff) | |
download | libmicrohttpd-c51d65ffce7b0542a15afa4d7bb252df6a970b5c.tar.gz libmicrohttpd-c51d65ffce7b0542a15afa4d7bb252df6a970b5c.zip |
added :
* configure.ac support for --with-gnutls=PFX
* TLS connection handler in daemon.c
* simple TLS echo client server example
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | src/daemon/daemon.c | 1194 | ||||
-rw-r--r-- | src/daemon/internal.h | 1024 | ||||
-rw-r--r-- | src/examples/Makefile.am | 32 | ||||
-rw-r--r-- | src/examples/https_echo_client_example.c | 153 | ||||
-rw-r--r-- | src/examples/https_server_example.c | 182 |
6 files changed, 1528 insertions, 1088 deletions
diff --git a/configure.ac b/configure.ac index 5aacdab1..7fbe5d07 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -141,6 +141,37 @@ AC_CHECK_PROG([HAVE_SOCAT],[socat], 1, 0) | |||
141 | AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF) | 141 | AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF) |
142 | AM_CONDITIONAL(HAVE_SOCAT, test 0 != $HAVE_SOCAT) | 142 | AM_CONDITIONAL(HAVE_SOCAT, test 0 != $HAVE_SOCAT) |
143 | 143 | ||
144 | # GNUtls linkage | ||
145 | AC_ARG_WITH(gnutls, | ||
146 | [ --with-gnutls=PFX Base of GNU TLS installation], | ||
147 | [AC_MSG_RESULT("$with_gnutls") | ||
148 | case $with_gnutls in | ||
149 | no) | ||
150 | ;; | ||
151 | yes) | ||
152 | AC_CHECK_HEADERS(gnutls.h,gnutls=true) | ||
153 | ;; | ||
154 | *) | ||
155 | LDFLAGS="-L$with_gnutls/lib $LDFLAGS" | ||
156 | CPPFLAGS="-I$with_gnutls/include $CPPFLAGS" | ||
157 | AC_CHECK_HEADERS(gnutls/gnutls.h, | ||
158 | # check for 'gnutls_global_init' in gnutls.so | ||
159 | AC_CHECK_LIB(gnutls,gnutls_global_init, | ||
160 | GNUTLS_LIB_PATH="$with_gnutls/lib" | ||
161 | GNUTLS_LDFLAGS="-L$with_gnutls/lib" | ||
162 | GNUTLS_CPPFLAGS="-I$with_gnutls/include" | ||
163 | gnutls=true)) | ||
164 | LDFLAGS=$SAVE_LDFLAGS | ||
165 | CPPFLAGS=$SAVE_CPPFLAGS | ||
166 | ;; | ||
167 | esac | ||
168 | ], | ||
169 | [AC_MSG_RESULT([--with-gnutls not specified])]) | ||
170 | |||
171 | AC_SUBST(GNUTLS_LIB_PATH) | ||
172 | AC_SUBST(GNUTLS_LDFLAGS) | ||
173 | AC_SUBST(GNUTLS_CPPFLAGS) | ||
174 | |||
144 | AC_SUBST(CPPFLAGS) | 175 | AC_SUBST(CPPFLAGS) |
145 | AC_SUBST(LIBS) | 176 | AC_SUBST(LIBS) |
146 | AC_SUBST(LDFLAGS) | 177 | AC_SUBST(LDFLAGS) |
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index 971196f8..ef575dd6 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c | |||
@@ -1,22 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of libmicrohttpd | 2 | This file is part of libmicrohttpd |
3 | (C) 2007 Daniel Pittman and Christian Grothoff | 3 | (C) 2007 Daniel Pittman and Christian Grothoff |
4 | 4 | ||
5 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public | 6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | 7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. | 8 | version 2.1 of the License, or (at your option) any later version. |
9 | 9 | ||
10 | This library is distributed in the hope that it will be useful, | 10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. | 13 | Lesser General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU Lesser General Public | 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 | 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 | 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | 18 | ||
19 | */ | 19 | */ |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file daemon.c | 22 | * @file daemon.c |
@@ -52,7 +52,6 @@ | |||
52 | */ | 52 | */ |
53 | #define DEBUG_CONNECT MHD_NO | 53 | #define DEBUG_CONNECT MHD_NO |
54 | 54 | ||
55 | |||
56 | /** | 55 | /** |
57 | * Obtain the select sets for this daemon. | 56 | * Obtain the select sets for this daemon. |
58 | * | 57 | * |
@@ -60,307 +59,331 @@ | |||
60 | * daemon was not started with the right | 59 | * daemon was not started with the right |
61 | * options for this call. | 60 | * options for this call. |
62 | */ | 61 | */ |
63 | int | 62 | int MHD_get_fdset(struct MHD_Daemon *daemon, |
64 | MHD_get_fdset (struct MHD_Daemon *daemon, | 63 | fd_set * read_fd_set, |
65 | fd_set * read_fd_set, | 64 | fd_set * write_fd_set, |
66 | fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd) | 65 | fd_set * except_fd_set, |
67 | { | 66 | int *max_fd) |
68 | struct MHD_Connection *pos; | 67 | { |
69 | int fd; | 68 | struct MHD_Connection *con_itr; |
70 | 69 | int fd; | |
71 | if ((daemon == NULL) || | 70 | |
72 | (read_fd_set == NULL) || | 71 | if ((daemon == NULL) || (read_fd_set == NULL) || (write_fd_set == NULL) |
73 | (write_fd_set == NULL) || | 72 | || (except_fd_set == NULL) || (max_fd == NULL) || (-1 == (fd = daemon-> |
74 | (except_fd_set == NULL) || | 73 | socket_fd)) || (daemon->shutdown == MHD_YES) || ((daemon->options |
75 | (max_fd == NULL) || | 74 | & MHD_USE_THREAD_PER_CONNECTION) != 0)) |
76 | (-1 == (fd = daemon->socket_fd)) || | 75 | return MHD_NO; |
77 | (daemon->shutdown == MHD_YES) || | 76 | |
78 | ((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0)) | 77 | __FD_SET (fd, read_fd_set); |
79 | return MHD_NO; | 78 | |
80 | FD_SET (fd, read_fd_set); | 79 | /* update max file descriptor */ |
81 | if ((*max_fd) < fd) | 80 | if ((*max_fd) < fd) |
82 | *max_fd = fd; | 81 | *max_fd = fd; |
83 | pos = daemon->connections; | 82 | |
84 | while (pos != NULL) | 83 | con_itr = daemon->connections; |
85 | { | 84 | while (con_itr != NULL) |
86 | if (MHD_YES != MHD_connection_get_fdset (pos, | 85 | { |
87 | read_fd_set, | 86 | if (MHD_YES != MHD_connection_get_fdset (con_itr, |
88 | write_fd_set, | 87 | read_fd_set, |
89 | except_fd_set, max_fd)) | 88 | write_fd_set, |
90 | return MHD_NO; | 89 | except_fd_set, max_fd)) |
91 | pos = pos->next; | 90 | return MHD_NO; |
92 | } | 91 | con_itr = con_itr->next; |
92 | } | ||
93 | #if DEBUG_CONNECT | 93 | #if DEBUG_CONNECT |
94 | MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd); | 94 | MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd); |
95 | #endif | 95 | #endif |
96 | return MHD_YES; | 96 | return MHD_YES; |
97 | } | 97 | } |
98 | 98 | ||
99 | /** | 99 | /** |
100 | * Main function of the thread that handles an individual | 100 | * Main function of the thread that handles an individual |
101 | * connection. | 101 | * connection. |
102 | */ | 102 | */ |
103 | static void * | 103 | static void * MHD_handle_connection(void *data) |
104 | MHD_handle_connection (void *data) | 104 | { |
105 | { | 105 | struct MHD_Connection *con = data; |
106 | struct MHD_Connection *con = data; | 106 | int num_ready; |
107 | int num_ready; | 107 | fd_set rs; |
108 | fd_set rs; | 108 | fd_set ws; |
109 | fd_set ws; | 109 | fd_set es; |
110 | fd_set es; | 110 | int max; |
111 | int max; | 111 | struct timeval tv; |
112 | struct timeval tv; | 112 | unsigned int timeout; |
113 | unsigned int timeout; | 113 | unsigned int now; |
114 | unsigned int now; | 114 | |
115 | 115 | if (con == NULL) | |
116 | if (con == NULL) | 116 | abort(); |
117 | abort (); | 117 | timeout = con->daemon->connection_timeout; |
118 | timeout = con->daemon->connection_timeout; | 118 | while ((!con->daemon->shutdown) && (con->socket_fd != -1)) |
119 | while ((!con->daemon->shutdown) && (con->socket_fd != -1)) | 119 | { |
120 | { | 120 | FD_ZERO (&rs); |
121 | FD_ZERO (&rs); | 121 | FD_ZERO (&ws); |
122 | FD_ZERO (&ws); | 122 | FD_ZERO (&es); |
123 | FD_ZERO (&es); | 123 | max = 0; |
124 | max = 0; | 124 | MHD_connection_get_fdset(con, &rs, &ws, &es, &max); |
125 | MHD_connection_get_fdset (con, &rs, &ws, &es, &max); | 125 | now = time(NULL); |
126 | now = time (NULL); | 126 | tv.tv_usec = 0; |
127 | tv.tv_usec = 0; | 127 | if (timeout > (now - con->last_activity)) |
128 | if (timeout > (now - con->last_activity)) | 128 | tv.tv_sec = timeout - (now - con->last_activity); |
129 | tv.tv_sec = timeout - (now - con->last_activity); | 129 | else |
130 | else | 130 | tv.tv_sec = 0; |
131 | tv.tv_sec = 0; | 131 | num_ready = SELECT (max + 1, |
132 | num_ready = SELECT (max + 1, | 132 | &rs, &ws, &es, (timeout != 0) ? &tv : NULL); |
133 | &rs, &ws, &es, (timeout != 0) ? &tv : NULL); | 133 | if (num_ready < 0) |
134 | if (num_ready < 0) | 134 | { |
135 | { | 135 | if (errno == EINTR) |
136 | if (errno == EINTR) | 136 | continue; |
137 | continue; | ||
138 | #if HAVE_MESSAGES | 137 | #if HAVE_MESSAGES |
139 | MHD_DLOG (con->daemon, "Error during select (%d): `%s'\n", | 138 | MHD_DLOG(con->daemon, "Error during select (%d): `%s'\n", max, |
140 | max, STRERROR (errno)); | 139 | STRERROR (errno)); |
141 | #endif | 140 | #endif |
142 | break; | 141 | break; |
143 | } | 142 | } |
144 | if (FD_ISSET (con->socket_fd, &rs)) | 143 | if (FD_ISSET (con->socket_fd, &rs)) |
145 | MHD_connection_handle_read (con); | 144 | MHD_connection_handle_read(con); |
146 | if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) | 145 | if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) |
147 | MHD_connection_handle_write (con); | 146 | MHD_connection_handle_write(con); |
148 | if (con->socket_fd != -1) | 147 | if (con->socket_fd != -1) |
149 | MHD_connection_handle_idle (con); | 148 | MHD_connection_handle_idle(con); |
150 | } | 149 | } |
151 | if (con->socket_fd != -1) | 150 | if (con->socket_fd != -1) |
152 | { | 151 | { |
153 | #if DEBUG_CLOSE | 152 | #if DEBUG_CLOSE |
154 | #if HAVE_MESSAGES | 153 | #if HAVE_MESSAGES |
155 | MHD_DLOG (con->daemon, | 154 | MHD_DLOG (con->daemon, |
156 | "Processing thread terminating, closing connection\n"); | 155 | "Processing thread terminating, closing connection\n"); |
157 | #endif | 156 | #endif |
158 | #endif | 157 | #endif |
159 | SHUTDOWN (con->socket_fd, SHUT_RDWR); | 158 | SHUTDOWN (con->socket_fd, SHUT_RDWR); |
160 | CLOSE (con->socket_fd); | 159 | CLOSE (con->socket_fd); |
161 | con->socket_fd = -1; | 160 | con->socket_fd = -1; |
162 | } | 161 | } |
163 | return NULL; | 162 | return NULL; |
164 | } | 163 | } |
164 | |||
165 | /** | ||
166 | * Handle an individual TLS connection. | ||
167 | */ | ||
168 | static void * MHDS_handle_connection(void *data) | ||
169 | { | ||
170 | struct MHD_Connection *con = data; | ||
171 | |||
172 | if (con == NULL) | ||
173 | abort(); | ||
174 | |||
175 | /* forward call to handler */ | ||
176 | con->daemon->default_handler(NULL, con, NULL, NULL, NULL, NULL, NULL, | ||
177 | NULL); | ||
165 | 178 | ||
179 | return NULL; | ||
180 | } | ||
166 | 181 | ||
167 | /** | 182 | /** |
168 | * Accept an incoming connection and create the MHD_Connection object for | 183 | * Accept an incoming connection and create the MHD_Connection object for |
169 | * it. This function also enforces policy by way of checking with the | 184 | * it. This function also enforces policy by way of checking with the |
170 | * accept policy callback. | 185 | * accept policy callback. |
171 | */ | 186 | */ |
172 | static int | 187 | static int MHD_accept_connection(struct MHD_Daemon *daemon) |
173 | MHD_accept_connection (struct MHD_Daemon *daemon) | 188 | { |
174 | { | 189 | struct MHD_Connection *pos; |
175 | struct MHD_Connection *pos; | 190 | struct MHD_Connection *connection; |
176 | struct MHD_Connection *connection; | 191 | struct sockaddr_in6 addr6; |
177 | struct sockaddr_in6 addr6; | 192 | struct sockaddr *addr = (struct sockaddr *) &addr6; |
178 | struct sockaddr *addr = (struct sockaddr *) &addr6; | 193 | socklen_t addrlen; |
179 | socklen_t addrlen; | 194 | unsigned int have; |
180 | unsigned int have; | 195 | int s, res_thread_create; |
181 | int s; | ||
182 | #if OSX | 196 | #if OSX |
183 | static int on = 1; | 197 | static int on = 1; |
184 | #endif | 198 | #endif |
185 | 199 | ||
200 | if (sizeof(struct sockaddr) > sizeof(struct sockaddr_in6)) | ||
201 | abort(); /* fatal, serious error */ | ||
202 | addrlen = sizeof(struct sockaddr_in6); | ||
203 | memset(addr, 0, sizeof(struct sockaddr_in6)); | ||
204 | |||
205 | s = ACCEPT (daemon->socket_fd, addr, &addrlen); | ||
186 | 206 | ||
187 | if (sizeof (struct sockaddr) > sizeof (struct sockaddr_in6)) | 207 | if ((s < 0) || (addrlen <= 0)) |
188 | abort (); /* fatal, serious error */ | 208 | { |
189 | addrlen = sizeof (struct sockaddr_in6); | ||
190 | memset (addr, 0, sizeof (struct sockaddr_in6)); | ||
191 | s = ACCEPT (daemon->socket_fd, addr, &addrlen); | ||
192 | if ((s < 0) || (addrlen <= 0)) | ||
193 | { | ||
194 | #if HAVE_MESSAGES | 209 | #if HAVE_MESSAGES |
195 | MHD_DLOG (daemon, "Error accepting connection: %s\n", STRERROR (errno)); | 210 | MHD_DLOG(daemon, "Error accepting connection: %s\n", STRERROR (errno)); |
196 | #endif | 211 | #endif |
197 | if (s != -1) | 212 | if (s != -1) |
198 | { | 213 | { |
199 | SHUTDOWN (s, SHUT_RDWR); | 214 | SHUTDOWN (s, SHUT_RDWR); |
200 | CLOSE (s); /* just in case */ | 215 | CLOSE (s); |
201 | } | 216 | /* just in case */ |
202 | return MHD_NO; | 217 | } |
203 | } | 218 | return MHD_NO; |
219 | } | ||
204 | #if DEBUG_CONNECT | 220 | #if DEBUG_CONNECT |
205 | MHD_DLOG (daemon, "Accepted connection on socket %d\n", s); | 221 | MHD_DLOG (daemon, "Accepted connection on socket %d\n", s); |
206 | #endif | 222 | #endif |
207 | have = 0; | 223 | have = 0; |
208 | if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0)) | 224 | if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0)) |
209 | { | 225 | { |
210 | pos = daemon->connections; | 226 | pos = daemon->connections; |
211 | while (pos != NULL) | 227 | while (pos != NULL) |
212 | { | 228 | { |
213 | if ((pos->addr != NULL) && (pos->addr_len == addrlen)) | 229 | if ((pos->addr != NULL) && (pos->addr_len == addrlen)) |
214 | { | 230 | { |
215 | if (addrlen == sizeof (struct sockaddr_in)) | 231 | if (addrlen == sizeof(struct sockaddr_in)) |
216 | { | 232 | { |
217 | const struct sockaddr_in *a1 = | 233 | const struct sockaddr_in *a1 = |
218 | (const struct sockaddr_in *) &addr; | 234 | (const struct sockaddr_in *) &addr; |
219 | const struct sockaddr_in *a2 = | 235 | const struct sockaddr_in *a2 = |
220 | (const struct sockaddr_in *) pos->addr; | 236 | (const struct sockaddr_in *) pos->addr; |
221 | if (0 == | 237 | if (0 == memcmp(&a1->sin_addr, &a2->sin_addr, |
222 | memcmp (&a1->sin_addr, &a2->sin_addr, | 238 | sizeof(struct in_addr))) |
223 | sizeof (struct in_addr))) | 239 | have++; |
224 | have++; | 240 | } |
225 | } | 241 | if (addrlen == sizeof(struct sockaddr_in6)) |
226 | if (addrlen == sizeof (struct sockaddr_in6)) | 242 | { |
227 | { | 243 | const struct sockaddr_in6 *a1 = |
228 | const struct sockaddr_in6 *a1 = | 244 | (const struct sockaddr_in6 *) &addr; |
229 | (const struct sockaddr_in6 *) &addr; | 245 | const struct sockaddr_in6 *a2 = |
230 | const struct sockaddr_in6 *a2 = | 246 | (const struct sockaddr_in6 *) pos->addr; |
231 | (const struct sockaddr_in6 *) pos->addr; | 247 | if (0 == memcmp(&a1->sin6_addr, &a2->sin6_addr, |
232 | if (0 == | 248 | sizeof(struct in6_addr))) |
233 | memcmp (&a1->sin6_addr, &a2->sin6_addr, | 249 | have++; |
234 | sizeof (struct in6_addr))) | 250 | } |
235 | have++; | 251 | } |
236 | } | 252 | pos = pos->next; |
237 | } | 253 | } |
238 | pos = pos->next; | 254 | } |
239 | } | 255 | |
240 | } | 256 | if ((daemon->max_connections == 0) || ((daemon->per_ip_connection_limit |
241 | 257 | != 0) && (daemon-> | |
242 | if ((daemon->max_connections == 0) || | 258 | per_ip_connection_limit <= have))) |
243 | ((daemon->per_ip_connection_limit != 0) && | 259 | { |
244 | (daemon->per_ip_connection_limit <= have))) | 260 | /* above connection limit - reject */ |
245 | { | ||
246 | /* above connection limit - reject */ | ||
247 | #if HAVE_MESSAGES | 261 | #if HAVE_MESSAGES |
248 | MHD_DLOG (daemon, | 262 | MHD_DLOG(daemon, |
249 | "Server reached connection limit (closing inbound connection)\n"); | 263 | "Server reached connection limit (closing inbound connection)\n"); |
250 | #endif | 264 | #endif |
251 | SHUTDOWN (s, SHUT_RDWR); | 265 | SHUTDOWN (s, SHUT_RDWR); |
252 | CLOSE (s); | 266 | CLOSE (s); |
253 | return MHD_NO; | 267 | return MHD_NO; |
254 | } | 268 | } |
255 | if ((daemon->apc != NULL) && | 269 | if ((daemon->apc != NULL) && (MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen))) |
256 | (MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen))) | 270 | { |
257 | { | ||
258 | #if DEBUG_CLOSE | 271 | #if DEBUG_CLOSE |
259 | #if HAVE_MESSAGES | 272 | #if HAVE_MESSAGES |
260 | MHD_DLOG (daemon, "Connection rejected, closing connection\n"); | 273 | MHD_DLOG (daemon, "Connection rejected, closing connection\n"); |
261 | #endif | 274 | #endif |
262 | #endif | 275 | #endif |
263 | SHUTDOWN (s, SHUT_RDWR); | 276 | SHUTDOWN (s, SHUT_RDWR); |
264 | CLOSE (s); | 277 | CLOSE (s); |
265 | return MHD_YES; | 278 | return MHD_YES; |
266 | } | 279 | } |
267 | #if OSX | 280 | #if OSX |
268 | #ifdef SOL_SOCKET | 281 | #ifdef SOL_SOCKET |
269 | #ifdef SO_NOSIGPIPE | 282 | #ifdef SO_NOSIGPIPE |
270 | setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on)); | 283 | setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on)); |
271 | #endif | 284 | #endif |
272 | #endif | 285 | #endif |
273 | #endif | 286 | #endif |
274 | connection = malloc (sizeof (struct MHD_Connection)); | 287 | connection = malloc(sizeof(struct MHD_Connection)); |
275 | if (connection == NULL) | 288 | if (connection == NULL) |
276 | { | 289 | { |
277 | #if HAVE_MESSAGES | 290 | #if HAVE_MESSAGES |
278 | MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); | 291 | MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno)); |
279 | #endif | 292 | #endif |
280 | SHUTDOWN (s, SHUT_RDWR); | 293 | SHUTDOWN (s, SHUT_RDWR); |
281 | CLOSE (s); | 294 | CLOSE (s); |
282 | return MHD_NO; | 295 | return MHD_NO; |
283 | } | 296 | } |
284 | memset (connection, 0, sizeof (struct MHD_Connection)); | 297 | memset(connection, 0, sizeof(struct MHD_Connection)); |
285 | connection->pool = NULL; | 298 | connection->pool = NULL; |
286 | connection->addr = malloc (addrlen); | 299 | connection->addr = malloc(addrlen); |
287 | if (connection->addr == NULL) | 300 | if (connection->addr == NULL) |
288 | { | 301 | { |
289 | #if HAVE_MESSAGES | 302 | #if HAVE_MESSAGES |
290 | MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); | 303 | MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno)); |
291 | #endif | 304 | #endif |
292 | SHUTDOWN (s, SHUT_RDWR); | 305 | SHUTDOWN (s, SHUT_RDWR); |
293 | CLOSE (s); | 306 | CLOSE (s); |
294 | free (connection); | 307 | free(connection); |
295 | return MHD_NO; | 308 | return MHD_NO; |
296 | } | 309 | } |
297 | memcpy (connection->addr, addr, addrlen); | 310 | memcpy(connection->addr, addr, addrlen); |
298 | connection->addr_len = addrlen; | 311 | connection->addr_len = addrlen; |
299 | connection->socket_fd = s; | 312 | connection->socket_fd = s; |
300 | connection->daemon = daemon; | 313 | connection->daemon = daemon; |
301 | if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | 314 | |
302 | (0 != pthread_create (&connection->pid, | 315 | /* attempt to create handler thread */ |
303 | NULL, &MHD_handle_connection, connection))) | 316 | if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) |
304 | { | 317 | { |
318 | if (daemon->options & MHD_USE_SSL) | ||
319 | res_thread_create= pthread_create(&connection->pid, NULL, | ||
320 | &MHDS_handle_connection, connection); | ||
321 | else | ||
322 | { | ||
323 | res_thread_create= pthread_create(&connection->pid, NULL, | ||
324 | &MHD_handle_connection, connection); | ||
325 | } | ||
326 | if (res_thread_create != 0) | ||
327 | { | ||
305 | #if HAVE_MESSAGES | 328 | #if HAVE_MESSAGES |
306 | MHD_DLOG (daemon, "Failed to create a thread: %s\n", STRERROR (errno)); | 329 | MHD_DLOG(daemon, "Failed to create a thread: %s\n", STRERROR (errno)); |
307 | #endif | 330 | #endif |
308 | SHUTDOWN (s, SHUT_RDWR); | 331 | SHUTDOWN (s, SHUT_RDWR); |
309 | CLOSE (s); | 332 | CLOSE (s); |
310 | free (connection->addr); | 333 | free(connection->addr); |
311 | free (connection); | 334 | free(connection); |
312 | return MHD_NO; | 335 | return MHD_NO; |
313 | } | 336 | } |
314 | connection->last_activity = time (NULL); | 337 | } |
315 | connection->next = daemon->connections; | 338 | |
316 | daemon->connections = connection; | 339 | connection->last_activity = time(NULL); |
317 | daemon->max_connections--; | 340 | connection->next = daemon->connections; |
318 | return MHD_YES; | 341 | daemon->connections = connection; |
319 | } | 342 | daemon->max_connections--; |
320 | 343 | return MHD_YES; | |
344 | } | ||
321 | 345 | ||
322 | /** | 346 | /** |
323 | * Free resources associated with all closed connections. | 347 | * Free resources associated with all closed connections. |
324 | * (destroy responses, free buffers, etc.). A connection | 348 | * (destroy responses, free buffers, etc.). A connection |
325 | * is known to be closed if the socket_fd is -1. | 349 | * is known to be closed if the socket_fd is -1. |
326 | */ | 350 | */ |
327 | static void | 351 | static void MHD_cleanup_connections(struct MHD_Daemon *daemon) |
328 | MHD_cleanup_connections (struct MHD_Daemon *daemon) | 352 | { |
329 | { | 353 | struct MHD_Connection *pos; |
330 | struct MHD_Connection *pos; | 354 | struct MHD_Connection *prev; |
331 | struct MHD_Connection *prev; | 355 | void *unused; |
332 | void *unused; | 356 | |
333 | 357 | pos = daemon->connections; | |
334 | pos = daemon->connections; | 358 | prev = NULL; |
335 | prev = NULL; | 359 | while (pos != NULL) |
336 | while (pos != NULL) | 360 | { |
337 | { | 361 | if (pos->socket_fd == -1) |
338 | if (pos->socket_fd == -1) | 362 | { |
339 | { | 363 | if (prev == NULL) |
340 | if (prev == NULL) | 364 | daemon->connections = pos->next; |
341 | daemon->connections = pos->next; | 365 | else |
342 | else | 366 | prev->next = pos->next; |
343 | prev->next = pos->next; | 367 | if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) |
344 | if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | 368 | { |
345 | { | 369 | pthread_kill(pos->pid, SIGALRM); |
346 | pthread_kill (pos->pid, SIGALRM); | 370 | pthread_join(pos->pid, &unused); |
347 | pthread_join (pos->pid, &unused); | 371 | } |
348 | } | 372 | MHD_destroy_response(pos->response); |
349 | MHD_destroy_response (pos->response); | 373 | MHD_pool_destroy(pos->pool); |
350 | MHD_pool_destroy (pos->pool); | 374 | free(pos->addr); |
351 | free (pos->addr); | 375 | free(pos); |
352 | free (pos); | 376 | daemon->max_connections++; |
353 | daemon->max_connections++; | 377 | if (prev == NULL) |
354 | if (prev == NULL) | 378 | pos = daemon->connections; |
355 | pos = daemon->connections; | 379 | else |
356 | else | 380 | pos = prev->next; |
357 | pos = prev->next; | 381 | continue; |
358 | continue; | 382 | } |
359 | } | 383 | prev = pos; |
360 | prev = pos; | 384 | pos = pos->next; |
361 | pos = pos->next; | 385 | } |
362 | } | 386 | } |
363 | } | ||
364 | 387 | ||
365 | /** | 388 | /** |
366 | * Obtain timeout value for select for this daemon | 389 | * Obtain timeout value for select for this daemon |
@@ -373,35 +396,34 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) | |||
373 | * not used (or no connections exist that would | 396 | * not used (or no connections exist that would |
374 | * necessiate the use of a timeout right now). | 397 | * necessiate the use of a timeout right now). |
375 | */ | 398 | */ |
376 | int | 399 | int MHD_get_timeout(struct MHD_Daemon *daemon, unsigned long long *timeout) |
377 | MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout) | 400 | { |
378 | { | 401 | time_t earliest_deadline; |
379 | time_t earliest_deadline; | 402 | time_t now; |
380 | time_t now; | 403 | struct MHD_Connection *pos; |
381 | struct MHD_Connection *pos; | 404 | unsigned int dto; |
382 | unsigned int dto; | 405 | |
383 | 406 | dto = daemon->connection_timeout; | |
384 | dto = daemon->connection_timeout; | 407 | if (0 == dto) |
385 | if (0 == dto) | 408 | return MHD_NO; |
386 | return MHD_NO; | 409 | pos = daemon->connections; |
387 | pos = daemon->connections; | 410 | if (pos == NULL) |
388 | if (pos == NULL) | 411 | return MHD_NO; /* no connections */ |
389 | return MHD_NO; /* no connections */ | 412 | now = time(NULL); |
390 | now = time (NULL); | 413 | /* start with conservative estimate */ |
391 | /* start with conservative estimate */ | 414 | earliest_deadline = now + dto; |
392 | earliest_deadline = now + dto; | 415 | while (pos != NULL) |
393 | while (pos != NULL) | 416 | { |
394 | { | 417 | if (earliest_deadline > pos->last_activity + dto) |
395 | if (earliest_deadline > pos->last_activity + dto) | 418 | earliest_deadline = pos->last_activity + dto; |
396 | earliest_deadline = pos->last_activity + dto; | 419 | pos = pos->next; |
397 | pos = pos->next; | 420 | } |
398 | } | 421 | if (earliest_deadline < now) |
399 | if (earliest_deadline < now) | 422 | *timeout = 0; |
400 | *timeout = 0; | 423 | else |
401 | else | 424 | *timeout = (earliest_deadline - now); |
402 | *timeout = (earliest_deadline - now); | 425 | return MHD_YES; |
403 | return MHD_YES; | 426 | } |
404 | } | ||
405 | 427 | ||
406 | /** | 428 | /** |
407 | * Main select call. | 429 | * Main select call. |
@@ -409,101 +431,102 @@ MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout) | |||
409 | * @param may_block YES if blocking, NO if non-blocking | 431 | * @param may_block YES if blocking, NO if non-blocking |
410 | * @return MHD_NO on serious errors, MHD_YES on success | 432 | * @return MHD_NO on serious errors, MHD_YES on success |
411 | */ | 433 | */ |
412 | static int | 434 | static int MHD_select(struct MHD_Daemon *daemon, int may_block) |
413 | MHD_select (struct MHD_Daemon *daemon, int may_block) | 435 | { |
414 | { | 436 | struct MHD_Connection *pos; |
415 | struct MHD_Connection *pos; | 437 | int num_ready; |
416 | int num_ready; | 438 | fd_set rs; |
417 | fd_set rs; | 439 | fd_set ws; |
418 | fd_set ws; | 440 | fd_set es; |
419 | fd_set es; | 441 | int max; |
420 | int max; | 442 | struct timeval timeout; |
421 | struct timeval timeout; | 443 | unsigned long long ltimeout; |
422 | unsigned long long ltimeout; | 444 | int ds; |
423 | int ds; | 445 | time_t now; |
424 | time_t now; | 446 | |
425 | 447 | timeout.tv_sec = 0; | |
426 | timeout.tv_sec = 0; | 448 | timeout.tv_usec = 0; |
427 | timeout.tv_usec = 0; | 449 | if (daemon == NULL) |
428 | if (daemon == NULL) | 450 | abort(); |
429 | abort (); | 451 | if (daemon->shutdown == MHD_YES) |
430 | if (daemon->shutdown == MHD_YES) | 452 | return MHD_NO; |
431 | return MHD_NO; | 453 | FD_ZERO (&rs); |
432 | FD_ZERO (&rs); | 454 | FD_ZERO (&ws); |
433 | FD_ZERO (&ws); | 455 | FD_ZERO (&es); |
434 | FD_ZERO (&es); | 456 | max = 0; |
435 | max = 0; | 457 | |
436 | 458 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | |
437 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | 459 | { |
438 | { | 460 | /* single-threaded, go over everything */ |
439 | /* single-threaded, go over everything */ | 461 | if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) |
440 | if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) | 462 | return MHD_NO; |
441 | return MHD_NO; | 463 | } |
442 | } | 464 | else |
443 | else | 465 | { |
444 | { | 466 | /* accept only, have one thread per connection */ |
445 | /* accept only, have one thread per connection */ | 467 | max = daemon->socket_fd; |
446 | max = daemon->socket_fd; | 468 | if (max == -1) |
447 | if (max == -1) | 469 | return MHD_NO; |
448 | return MHD_NO; | 470 | FD_SET (max, &rs); |
449 | FD_SET (max, &rs); | 471 | } |
450 | } | 472 | |
451 | if (may_block == MHD_NO) | 473 | if (may_block == MHD_NO) |
452 | { | 474 | { |
453 | timeout.tv_usec = 0; | 475 | timeout.tv_usec = 0; |
454 | timeout.tv_sec = 0; | 476 | timeout.tv_sec = 0; |
455 | } | 477 | } |
456 | else | 478 | else |
457 | { | 479 | { |
458 | /* ltimeout is in ms */ | 480 | /* ltimeout is in ms */ |
459 | if (MHD_YES == MHD_get_timeout (daemon, <imeout)) | 481 | if (MHD_YES == MHD_get_timeout (daemon, <imeout)) |
460 | { | 482 | { |
461 | timeout.tv_usec = (ltimeout % 1000) * 1000; | 483 | timeout.tv_usec = (ltimeout % 1000) * 1000; |
462 | timeout.tv_sec = ltimeout / 1000; | 484 | timeout.tv_sec = ltimeout / 1000; |
463 | may_block = MHD_NO; | 485 | may_block = MHD_NO; |
464 | } | 486 | } |
465 | } | 487 | } |
466 | num_ready = SELECT (max + 1, | 488 | |
467 | &rs, &ws, &es, may_block == MHD_NO ? &timeout : NULL); | 489 | num_ready = select(max + 1, &rs, &ws, &es, may_block == MHD_NO ? &timeout |
468 | if (daemon->shutdown == MHD_YES) | 490 | : NULL); |
469 | return MHD_NO; | 491 | |
470 | if (num_ready < 0) | 492 | if (daemon->shutdown == MHD_YES) |
471 | { | 493 | return MHD_NO; |
472 | if (errno == EINTR) | 494 | if (num_ready < 0) |
473 | return MHD_YES; | 495 | { |
496 | if (errno == EINTR) | ||
497 | return MHD_YES; | ||
474 | #if HAVE_MESSAGES | 498 | #if HAVE_MESSAGES |
475 | MHD_DLOG (daemon, "Select failed: %s\n", STRERROR (errno)); | 499 | MHD_DLOG(daemon, "Select failed: %s\n", STRERROR (errno)); |
476 | #endif | 500 | #endif |
477 | return MHD_NO; | 501 | return MHD_NO; |
478 | } | 502 | } |
479 | ds = daemon->socket_fd; | 503 | ds = daemon->socket_fd; |
480 | if (ds == -1) | 504 | if (ds == -1) |
505 | return MHD_YES; | ||
506 | if (__FD_ISSET (ds, &rs)) | ||
507 | MHD_accept_connection(daemon); | ||
508 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | ||
509 | { | ||
510 | /* do not have a thread per connection, process all connections now */ | ||
511 | now = time(NULL); | ||
512 | pos = daemon->connections; | ||
513 | while (pos != NULL) | ||
514 | { | ||
515 | ds = pos->socket_fd; | ||
516 | if (ds != -1) | ||
517 | { | ||
518 | if (FD_ISSET (ds, &rs)) | ||
519 | MHD_connection_handle_read(pos); | ||
520 | if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws))) | ||
521 | MHD_connection_handle_write(pos); | ||
522 | if (pos->socket_fd != -1) | ||
523 | MHD_connection_handle_idle(pos); | ||
524 | } | ||
525 | pos = pos->next; | ||
526 | } | ||
527 | } | ||
481 | return MHD_YES; | 528 | return MHD_YES; |
482 | if (FD_ISSET (ds, &rs)) | 529 | } |
483 | MHD_accept_connection (daemon); | ||
484 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) | ||
485 | { | ||
486 | /* do not have a thread per connection, process all connections now */ | ||
487 | now = time (NULL); | ||
488 | pos = daemon->connections; | ||
489 | while (pos != NULL) | ||
490 | { | ||
491 | ds = pos->socket_fd; | ||
492 | if (ds != -1) | ||
493 | { | ||
494 | if (FD_ISSET (ds, &rs)) | ||
495 | MHD_connection_handle_read (pos); | ||
496 | if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws))) | ||
497 | MHD_connection_handle_write (pos); | ||
498 | if (pos->socket_fd != -1) | ||
499 | MHD_connection_handle_idle (pos); | ||
500 | } | ||
501 | pos = pos->next; | ||
502 | } | ||
503 | } | ||
504 | return MHD_YES; | ||
505 | } | ||
506 | |||
507 | 530 | ||
508 | /** | 531 | /** |
509 | * Run webserver operations (without blocking unless | 532 | * Run webserver operations (without blocking unless |
@@ -515,35 +538,31 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) | |||
515 | * daemon was not started with the right | 538 | * daemon was not started with the right |
516 | * options for this call. | 539 | * options for this call. |
517 | */ | 540 | */ |
518 | int | 541 | int MHD_run(struct MHD_Daemon *daemon) |
519 | MHD_run (struct MHD_Daemon *daemon) | 542 | { |
520 | { | 543 | if ((daemon->shutdown != MHD_NO) || (0 != (daemon->options |
521 | if ((daemon->shutdown != MHD_NO) || | 544 | & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (daemon->options |
522 | (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || | 545 | & MHD_USE_SELECT_INTERNALLY))) |
523 | (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) | 546 | return MHD_NO; |
524 | return MHD_NO; | 547 | MHD_select(daemon, MHD_NO); |
525 | MHD_select (daemon, MHD_NO); | 548 | MHD_cleanup_connections(daemon); |
526 | MHD_cleanup_connections (daemon); | 549 | return MHD_YES; |
527 | return MHD_YES; | 550 | } |
528 | } | ||
529 | |||
530 | 551 | ||
531 | /** | 552 | /** |
532 | * Thread that runs the select loop until the daemon | 553 | * Thread that runs the select loop until the daemon |
533 | * is explicitly shut down. | 554 | * is explicitly shut down. |
534 | */ | 555 | */ |
535 | static void * | 556 | static void * MHD_select_thread(void *cls) |
536 | MHD_select_thread (void *cls) | 557 | { |
537 | { | 558 | struct MHD_Daemon *daemon = cls; |
538 | struct MHD_Daemon *daemon = cls; | 559 | while (daemon->shutdown == MHD_NO) |
539 | while (daemon->shutdown == MHD_NO) | 560 | { |
540 | { | 561 | MHD_select(daemon, MHD_YES); |
541 | MHD_select (daemon, MHD_YES); | 562 | MHD_cleanup_connections(daemon); |
542 | MHD_cleanup_connections (daemon); | 563 | } |
543 | } | 564 | return NULL; |
544 | return NULL; | 565 | } |
545 | } | ||
546 | |||
547 | 566 | ||
548 | /** | 567 | /** |
549 | * Start a webserver on the given port. | 568 | * Start a webserver on the given port. |
@@ -556,106 +575,119 @@ MHD_select_thread (void *cls) | |||
556 | * @param dh_cls extra argument to dh | 575 | * @param dh_cls extra argument to dh |
557 | * @return NULL on error, handle to daemon on success | 576 | * @return NULL on error, handle to daemon on success |
558 | */ | 577 | */ |
559 | struct MHD_Daemon * | 578 | struct MHD_Daemon * MHD_start_daemon(unsigned int options, |
560 | MHD_start_daemon (unsigned int options, | 579 | unsigned short port, |
561 | unsigned short port, | 580 | MHD_AcceptPolicyCallback apc, |
562 | MHD_AcceptPolicyCallback apc, | 581 | void *apc_cls, |
563 | void *apc_cls, | 582 | MHD_AccessHandlerCallback dh, |
564 | MHD_AccessHandlerCallback dh, void *dh_cls, ...) | 583 | void *dh_cls, |
565 | { | 584 | ...) |
566 | const int on = 1; | 585 | { |
567 | struct MHD_Daemon *retVal; | 586 | const int on = 1; |
568 | int socket_fd; | 587 | struct MHD_Daemon *retVal; |
569 | struct sockaddr_in servaddr4; | 588 | |
570 | struct sockaddr_in6 servaddr6; | 589 | /* listeningss sockets used by the daemon */ |
571 | const struct sockaddr *servaddr; | 590 | int socket_fd; |
572 | socklen_t addrlen; | 591 | |
573 | va_list ap; | 592 | struct sockaddr_in servaddr4; |
574 | enum MHD_OPTION opt; | 593 | struct sockaddr_in6 servaddr6; |
575 | 594 | const struct sockaddr *servaddr; | |
576 | if ((options & MHD_USE_SSL) != 0) | 595 | socklen_t addrlen; |
577 | return NULL; | 596 | va_list ap; |
578 | if ((port == 0) || (dh == NULL)) | 597 | enum MHD_OPTION opt; |
579 | return NULL; | 598 | |
580 | if ((options & MHD_USE_IPv6) != 0) | 599 | if ((port == 0) || (dh == NULL)) |
581 | socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0); | 600 | return NULL; |
582 | else | 601 | if ((options & MHD_USE_IPv6) != 0) |
583 | socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0); | 602 | socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0); |
584 | if (socket_fd < 0) | 603 | else |
585 | { | 604 | socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0); |
605 | if (socket_fd < 0) | ||
606 | { | ||
586 | #if HAVE_MESSAGES | 607 | #if HAVE_MESSAGES |
587 | if ((options & MHD_USE_DEBUG) != 0) | 608 | if ((options & MHD_USE_DEBUG) != 0) |
588 | fprintf (stderr, "Call to socket failed: %s\n", STRERROR (errno)); | 609 | fprintf(stderr, "Call to socket failed: %s\n", STRERROR (errno)); |
589 | #endif | 610 | #endif |
590 | return NULL; | 611 | return NULL; |
591 | } | 612 | } |
592 | if ((SETSOCKOPT (socket_fd, | 613 | if ((SETSOCKOPT (socket_fd, |
593 | SOL_SOCKET, | 614 | SOL_SOCKET, |
594 | SO_REUSEADDR, | 615 | SO_REUSEADDR, |
595 | &on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0) | 616 | &on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0) |
596 | { | 617 | { |
597 | #if HAVE_MESSAGES | 618 | #if HAVE_MESSAGES |
598 | fprintf (stderr, "setsockopt failed: %s\n", STRERROR (errno)); | 619 | fprintf(stderr, "setsockopt failed: %s\n", STRERROR (errno)); |
599 | #endif | 620 | #endif |
600 | } | 621 | } |
601 | if ((options & MHD_USE_IPv6) != 0) | 622 | if ((options & MHD_USE_IPv6) != 0) |
602 | { | 623 | { |
603 | memset (&servaddr6, 0, sizeof (struct sockaddr_in6)); | 624 | memset(&servaddr6, 0, sizeof(struct sockaddr_in6)); |
604 | servaddr6.sin6_family = AF_INET6; | 625 | servaddr6.sin6_family = AF_INET6; |
605 | servaddr6.sin6_port = htons (port); | 626 | servaddr6.sin6_port = htons(port); |
606 | servaddr = (struct sockaddr *) &servaddr6; | 627 | servaddr = (struct sockaddr *) &servaddr6; |
607 | addrlen = sizeof (struct sockaddr_in6); | 628 | addrlen = sizeof(struct sockaddr_in6); |
608 | } | 629 | } |
609 | else | 630 | else |
610 | { | 631 | { |
611 | memset (&servaddr4, 0, sizeof (struct sockaddr_in)); | 632 | memset(&servaddr4, 0, sizeof(struct sockaddr_in)); |
612 | servaddr4.sin_family = AF_INET; | 633 | servaddr4.sin_family = AF_INET; |
613 | servaddr4.sin_port = htons (port); | 634 | servaddr4.sin_port = htons(port); |
614 | servaddr = (struct sockaddr *) &servaddr4; | 635 | servaddr = (struct sockaddr *) &servaddr4; |
615 | addrlen = sizeof (struct sockaddr_in); | 636 | addrlen = sizeof(struct sockaddr_in); |
616 | } | 637 | } |
617 | if (BIND (socket_fd, servaddr, addrlen) < 0) | 638 | if (BIND (socket_fd, servaddr, addrlen) < 0) |
618 | { | 639 | { |
619 | #if HAVE_MESSAGES | 640 | #if HAVE_MESSAGES |
620 | if ((options & MHD_USE_DEBUG) != 0) | 641 | if ((options & MHD_USE_DEBUG) != 0) |
621 | fprintf (stderr, | 642 | fprintf(stderr, |
622 | "Failed to bind to port %u: %s\n", port, STRERROR (errno)); | 643 | "Failed to bind to port %u: %s\n", port, STRERROR (errno)); |
623 | #endif | 644 | #endif |
624 | CLOSE (socket_fd); | 645 | CLOSE (socket_fd); |
625 | return NULL; | 646 | return NULL; |
626 | } | 647 | } |
627 | if (LISTEN (socket_fd, 20) < 0) | 648 | if (LISTEN (socket_fd, 20) < 0) |
628 | { | 649 | { |
629 | #if HAVE_MESSAGES | 650 | #if HAVE_MESSAGES |
630 | if ((options & MHD_USE_DEBUG) != 0) | 651 | if ((options & MHD_USE_DEBUG) != 0) |
631 | fprintf (stderr, | 652 | fprintf(stderr, |
632 | "Failed to listen for connections: %s\n", STRERROR (errno)); | 653 | "Failed to listen for connections: %s\n", STRERROR (errno)); |
633 | #endif | 654 | #endif |
634 | CLOSE (socket_fd); | 655 | CLOSE (socket_fd); |
635 | return NULL; | 656 | return NULL; |
636 | } | 657 | } |
637 | retVal = malloc (sizeof (struct MHD_Daemon)); | 658 | |
638 | if (retVal == NULL) | 659 | /* allocate the mhd daemon */ |
639 | { | 660 | |
640 | CLOSE (socket_fd); | 661 | retVal = malloc(sizeof(struct MHD_Daemon)); |
641 | return NULL; | 662 | |
642 | } | 663 | if (retVal == NULL) |
643 | memset (retVal, 0, sizeof (struct MHD_Daemon)); | 664 | { |
644 | retVal->options = options; | 665 | CLOSE (socket_fd); |
645 | retVal->port = port; | 666 | return NULL; |
646 | retVal->apc = apc; | 667 | } |
647 | retVal->apc_cls = apc_cls; | 668 | |
648 | retVal->socket_fd = socket_fd; | 669 | memset(retVal, 0, sizeof(struct MHD_Daemon)); |
649 | retVal->default_handler = dh; | 670 | retVal->options = options; |
650 | retVal->default_handler_cls = dh_cls; | 671 | retVal->port = port; |
651 | retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; | 672 | retVal->apc = apc; |
652 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; | 673 | retVal->apc_cls = apc_cls; |
653 | retVal->connection_timeout = 0; /* no timeout */ | 674 | retVal->socket_fd = socket_fd; |
654 | va_start (ap, dh_cls); | 675 | retVal->default_handler = dh; |
655 | while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION))) | 676 | retVal->default_handler_cls = dh_cls; |
656 | { | 677 | retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; |
657 | switch (opt) | 678 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; |
658 | { | 679 | retVal->connection_timeout = 0; /* no timeout */ |
680 | |||
681 | /* initializes the argument pointer variable */ | ||
682 | va_start (ap, dh_cls); | ||
683 | |||
684 | /* | ||
685 | * loop through daemon options | ||
686 | */ | ||
687 | while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION))) | ||
688 | { | ||
689 | switch (opt) | ||
690 | { | ||
659 | case MHD_OPTION_CONNECTION_MEMORY_LIMIT: | 691 | case MHD_OPTION_CONNECTION_MEMORY_LIMIT: |
660 | retVal->pool_size = va_arg (ap, unsigned int); | 692 | retVal->pool_size = va_arg (ap, unsigned int); |
661 | break; | 693 | break; |
@@ -666,8 +698,7 @@ MHD_start_daemon (unsigned int options, | |||
666 | retVal->connection_timeout = va_arg (ap, unsigned int); | 698 | retVal->connection_timeout = va_arg (ap, unsigned int); |
667 | break; | 699 | break; |
668 | case MHD_OPTION_NOTIFY_COMPLETED: | 700 | case MHD_OPTION_NOTIFY_COMPLETED: |
669 | retVal->notify_completed = | 701 | retVal->notify_completed = va_arg (ap, MHD_RequestCompletedCallback); |
670 | va_arg (ap, MHD_RequestCompletedCallback); | ||
671 | retVal->notify_completed_cls = va_arg (ap, void *); | 702 | retVal->notify_completed_cls = va_arg (ap, void *); |
672 | break; | 703 | break; |
673 | case MHD_OPTION_PER_IP_CONNECTION_LIMIT: | 704 | case MHD_OPTION_PER_IP_CONNECTION_LIMIT: |
@@ -675,76 +706,76 @@ MHD_start_daemon (unsigned int options, | |||
675 | break; | 706 | break; |
676 | default: | 707 | default: |
677 | #if HAVE_MESSAGES | 708 | #if HAVE_MESSAGES |
678 | fprintf (stderr, | 709 | fprintf(stderr, |
679 | "Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n"); | 710 | "Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n"); |
680 | #endif | 711 | #endif |
681 | abort (); | 712 | abort(); |
682 | } | 713 | } |
683 | } | 714 | } |
684 | va_end (ap); | 715 | va_end (ap); |
685 | if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || | 716 | if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (options |
686 | (0 != (options & MHD_USE_SELECT_INTERNALLY))) && | 717 | & MHD_USE_SELECT_INTERNALLY))) && (0 != pthread_create(&retVal->pid, |
687 | (0 != pthread_create (&retVal->pid, NULL, &MHD_select_thread, retVal))) | 718 | NULL, &MHD_select_thread, retVal))) |
688 | { | 719 | { |
689 | #if HAVE_MESSAGES | 720 | #if HAVE_MESSAGES |
690 | MHD_DLOG (retVal, | 721 | MHD_DLOG(retVal, "Failed to create listen thread: %s\n", |
691 | "Failed to create listen thread: %s\n", STRERROR (errno)); | 722 | STRERROR (errno)); |
692 | #endif | 723 | #endif |
693 | free (retVal); | 724 | free(retVal); |
694 | CLOSE (socket_fd); | 725 | CLOSE (socket_fd); |
695 | return NULL; | 726 | return NULL; |
696 | } | 727 | } |
697 | return retVal; | 728 | |
698 | } | 729 | return retVal; |
730 | } | ||
699 | 731 | ||
700 | /** | 732 | /** |
701 | * Shutdown an http daemon. | 733 | * Shutdown an http daemon. |
702 | */ | 734 | */ |
703 | void | 735 | void MHD_stop_daemon(struct MHD_Daemon *daemon) |
704 | MHD_stop_daemon (struct MHD_Daemon *daemon) | 736 | { |
705 | { | 737 | void *unused; |
706 | void *unused; | 738 | int fd; |
707 | int fd; | 739 | |
708 | 740 | if (daemon == NULL) | |
709 | if (daemon == NULL) | 741 | return; |
710 | return; | 742 | daemon->shutdown = MHD_YES; |
711 | daemon->shutdown = MHD_YES; | 743 | fd = daemon->socket_fd; |
712 | fd = daemon->socket_fd; | 744 | daemon->socket_fd = -1; |
713 | daemon->socket_fd = -1; | ||
714 | #if DEBUG_CLOSE | 745 | #if DEBUG_CLOSE |
715 | #if HAVE_MESSAGES | 746 | #if HAVE_MESSAGES |
716 | MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n"); | 747 | MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n"); |
717 | #endif | 748 | #endif |
718 | #endif | 749 | #endif |
719 | CLOSE (fd); | 750 | CLOSE (fd); |
720 | if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || | 751 | if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || (0 |
721 | (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) | 752 | != (daemon-> |
722 | { | 753 | options & MHD_USE_SELECT_INTERNALLY))) |
723 | pthread_kill (daemon->pid, SIGALRM); | 754 | { |
724 | pthread_join (daemon->pid, &unused); | 755 | pthread_kill(daemon->pid, SIGALRM); |
725 | } | 756 | pthread_join(daemon->pid, &unused); |
726 | while (daemon->connections != NULL) | 757 | } |
727 | { | 758 | while (daemon->connections != NULL) |
728 | if (-1 != daemon->connections->socket_fd) | 759 | { |
729 | { | 760 | if (-1 != daemon->connections->socket_fd) |
761 | { | ||
730 | #if DEBUG_CLOSE | 762 | #if DEBUG_CLOSE |
731 | #if HAVE_MESSAGES | 763 | #if HAVE_MESSAGES |
732 | MHD_DLOG (daemon, "MHD shutdown, closing active connections\n"); | 764 | MHD_DLOG (daemon, "MHD shutdown, closing active connections\n"); |
733 | #endif | 765 | #endif |
734 | #endif | 766 | #endif |
735 | if (daemon->notify_completed != NULL) | 767 | if (daemon->notify_completed != NULL) |
736 | daemon->notify_completed (daemon->notify_completed_cls, | 768 | daemon->notify_completed(daemon->notify_completed_cls, |
737 | daemon->connections, | 769 | daemon->connections, &daemon->connections->client_context, |
738 | &daemon->connections->client_context, | 770 | MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); |
739 | MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); | 771 | SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR); |
740 | SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR); | 772 | CLOSE (daemon->connections->socket_fd); |
741 | CLOSE (daemon->connections->socket_fd); | 773 | daemon->connections->socket_fd = -1; |
742 | daemon->connections->socket_fd = -1; | 774 | } |
743 | } | 775 | MHD_cleanup_connections(daemon); |
744 | MHD_cleanup_connections (daemon); | 776 | } |
745 | } | 777 | free(daemon); |
746 | free (daemon); | 778 | } |
747 | } | ||
748 | 779 | ||
749 | #ifndef WINDOWS | 780 | #ifndef WINDOWS |
750 | 781 | ||
@@ -752,38 +783,37 @@ static struct sigaction sig; | |||
752 | 783 | ||
753 | static struct sigaction old; | 784 | static struct sigaction old; |
754 | 785 | ||
755 | static void | 786 | static void sigalrmHandler(int sig) |
756 | sigalrmHandler (int sig) | 787 | { |
757 | { | 788 | } |
758 | } | ||
759 | 789 | ||
760 | /** | 790 | /** |
761 | * Initialize the signal handler for SIGALRM. | 791 | * Initialize the signal handler for SIGALRM. |
762 | */ | 792 | */ |
763 | void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init () | 793 | void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init() |
764 | { | 794 | { |
765 | /* make sure SIGALRM does not kill us */ | 795 | /* make sure SIGALRM does not kill us */ |
766 | memset (&sig, 0, sizeof (struct sigaction)); | 796 | memset(&sig, 0, sizeof(struct sigaction)); |
767 | memset (&old, 0, sizeof (struct sigaction)); | 797 | memset(&old, 0, sizeof(struct sigaction)); |
768 | sig.sa_flags = SA_NODEFER; | 798 | sig.sa_flags = SA_NODEFER; |
769 | sig.sa_handler = &sigalrmHandler; | 799 | sig.sa_handler = &sigalrmHandler; |
770 | sigaction (SIGALRM, &sig, &old); | 800 | sigaction(SIGALRM, &sig, &old); |
771 | } | 801 | } |
772 | 802 | ||
773 | void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini () | 803 | void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini() |
774 | { | 804 | { |
775 | sigaction (SIGALRM, &old, &sig); | 805 | sigaction(SIGALRM, &old, &sig); |
776 | } | 806 | } |
777 | #else | 807 | #else |
778 | void __attribute__ ((constructor)) MHD_win_ltdl_init () | 808 | void __attribute__ ((constructor)) MHD_win_ltdl_init () |
779 | { | 809 | { |
780 | plibc_init ("CRISP", "libmicrohttpd"); | 810 | plibc_init ("CRISP", "libmicrohttpd"); |
781 | } | 811 | } |
782 | 812 | ||
783 | void __attribute__ ((destructor)) MHD_win_ltdl_fini () | 813 | void __attribute__ ((destructor)) MHD_win_ltdl_fini () |
784 | { | 814 | { |
785 | plibc_shutdown (); | 815 | plibc_shutdown (); |
786 | } | 816 | } |
787 | #endif | 817 | #endif |
788 | 818 | ||
789 | /* end of daemon.c */ | 819 | /* end of daemon.c */ |
diff --git a/src/daemon/internal.h b/src/daemon/internal.h index e22de32f..a06d7ae2 100644 --- a/src/daemon/internal.h +++ b/src/daemon/internal.h | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of libmicrohttpd | 2 | This file is part of libmicrohttpd |
3 | (C) 2007 Daniel Pittman and Christian Grothoff | 3 | (C) 2007 Daniel Pittman and Christian Grothoff |
4 | 4 | ||
5 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public | 6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | 7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. | 8 | version 2.1 of the License, or (at your option) any later version. |
9 | 9 | ||
10 | This library is distributed in the hope that it will be useful, | 10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. | 13 | Lesser General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU Lesser General Public | 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 | 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 | 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | */ | 18 | */ |
19 | 19 | ||
20 | /** | 20 | /** |
21 | * @file internal.h | 21 | * @file internal.h |
@@ -27,7 +27,6 @@ | |||
27 | #ifndef INTERNAL_H | 27 | #ifndef INTERNAL_H |
28 | #define INTERNAL_H | 28 | #define INTERNAL_H |
29 | 29 | ||
30 | |||
31 | #include <stdio.h> | 30 | #include <stdio.h> |
32 | #include <stdlib.h> | 31 | #include <stdlib.h> |
33 | #include <string.h> | 32 | #include <string.h> |
@@ -64,101 +63,101 @@ | |||
64 | * fprintf-like helper function for logging debug | 63 | * fprintf-like helper function for logging debug |
65 | * messages. | 64 | * messages. |
66 | */ | 65 | */ |
67 | void MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...); | 66 | void MHD_DLOG(const struct MHD_Daemon *daemon, const char *format, ...); |
68 | #endif | 67 | #endif |
69 | 68 | ||
70 | /** | 69 | /** |
71 | * Process escape sequences ('+'=space, %HH). | 70 | * Process escape sequences ('+'=space, %HH). |
72 | * Updates val in place. | 71 | * Updates val in place. |
73 | */ | 72 | */ |
74 | void MHD_http_unescape (char *val); | 73 | void MHD_http_unescape(char *val); |
75 | 74 | ||
76 | /** | 75 | /** |
77 | * Header or cookie in HTTP request or response. | 76 | * Header or cookie in HTTP request or response. |
78 | */ | 77 | */ |
79 | struct MHD_HTTP_Header | 78 | struct MHD_HTTP_Header |
80 | { | 79 | { |
81 | struct MHD_HTTP_Header *next; | 80 | struct MHD_HTTP_Header *next; |
82 | 81 | ||
83 | char *header; | 82 | char *header; |
84 | 83 | ||
85 | char *value; | 84 | char *value; |
86 | 85 | ||
87 | enum MHD_ValueKind kind; | 86 | enum MHD_ValueKind kind; |
88 | 87 | ||
89 | }; | 88 | }; |
90 | 89 | ||
91 | /** | 90 | /** |
92 | * Representation of a response. | 91 | * Representation of a response. |
93 | */ | 92 | */ |
94 | struct MHD_Response | 93 | struct MHD_Response |
95 | { | 94 | { |
96 | 95 | ||
97 | /** | 96 | /** |
98 | * Headers to send for the response. Initially | 97 | * Headers to send for the response. Initially |
99 | * the linked list is created in inverse order; | 98 | * the linked list is created in inverse order; |
100 | * the order should be inverted before sending! | 99 | * the order should be inverted before sending! |
101 | */ | 100 | */ |
102 | struct MHD_HTTP_Header *first_header; | 101 | struct MHD_HTTP_Header *first_header; |
103 | 102 | ||
104 | /** | 103 | /** |
105 | * Buffer pointing to data that we are supposed | 104 | * Buffer pointing to data that we are supposed |
106 | * to send as a response. | 105 | * to send as a response. |
107 | */ | 106 | */ |
108 | char *data; | 107 | char *data; |
109 | 108 | ||
110 | /** | 109 | /** |
111 | * Closure to give to the content reader | 110 | * Closure to give to the content reader |
112 | * free callback. | 111 | * free callback. |
113 | */ | 112 | */ |
114 | void *crc_cls; | 113 | void *crc_cls; |
115 | 114 | ||
116 | /** | 115 | /** |
117 | * How do we get more data? NULL if we are | 116 | * How do we get more data? NULL if we are |
118 | * given all of the data up front. | 117 | * given all of the data up front. |
119 | */ | 118 | */ |
120 | MHD_ContentReaderCallback crc; | 119 | MHD_ContentReaderCallback crc; |
121 | 120 | ||
122 | /** | 121 | /** |
123 | * NULL if data must not be freed, otherwise | 122 | * NULL if data must not be freed, otherwise |
124 | * either user-specified callback or "&free". | 123 | * either user-specified callback or "&free". |
125 | */ | 124 | */ |
126 | MHD_ContentReaderFreeCallback crfc; | 125 | MHD_ContentReaderFreeCallback crfc; |
127 | 126 | ||
128 | /** | 127 | /** |
129 | * Mutex to synchronize access to data/size and | 128 | * Mutex to synchronize access to data/size and |
130 | * reference counts. | 129 | * reference counts. |
131 | */ | 130 | */ |
132 | pthread_mutex_t mutex; | 131 | pthread_mutex_t mutex; |
133 | 132 | ||
134 | /** | 133 | /** |
135 | * Reference count for this response. Free | 134 | * Reference count for this response. Free |
136 | * once the counter hits zero. | 135 | * once the counter hits zero. |
137 | */ | 136 | */ |
138 | unsigned int reference_count; | 137 | unsigned int reference_count; |
139 | 138 | ||
140 | /** | 139 | /** |
141 | * Set to -1 if size is not known. | 140 | * Set to -1 if size is not known. |
142 | */ | 141 | */ |
143 | size_t total_size; | 142 | size_t total_size; |
144 | 143 | ||
145 | /** | 144 | /** |
146 | * Size of data. | 145 | * Size of data. |
147 | */ | 146 | */ |
148 | size_t data_size; | 147 | size_t data_size; |
149 | 148 | ||
150 | /** | 149 | /** |
151 | * Size of the data buffer. | 150 | * Size of the data buffer. |
152 | */ | 151 | */ |
153 | size_t data_buffer_size; | 152 | size_t data_buffer_size; |
154 | 153 | ||
155 | /** | 154 | /** |
156 | * At what offset in the stream is the | 155 | * At what offset in the stream is the |
157 | * beginning of data located? | 156 | * beginning of data located? |
158 | */ | 157 | */ |
159 | size_t data_start; | 158 | size_t data_start; |
160 | 159 | ||
161 | }; | 160 | }; |
162 | 161 | ||
163 | /** | 162 | /** |
164 | * States in a state machine for a connection. | 163 | * States in a state machine for a connection. |
@@ -175,419 +174,438 @@ struct MHD_Response | |||
175 | * requires the write to be complete. | 174 | * requires the write to be complete. |
176 | */ | 175 | */ |
177 | enum MHD_CONNECTION_STATE | 176 | enum MHD_CONNECTION_STATE |
178 | { | 177 | { |
179 | /** | 178 | /** |
180 | * Connection just started (no headers received). | 179 | * Connection just started (no headers received). |
181 | * Waiting for the line with the request type, URL and version. | 180 | * Waiting for the line with the request type, URL and version. |
182 | */ | 181 | */ |
183 | MHD_CONNECTION_INIT = 0, | 182 | MHD_CONNECTION_INIT = 0, |
184 | 183 | ||
185 | /** | 184 | /** |
186 | * 1: We got the URL (and request type and version). Wait for a header line. | 185 | * 1: We got the URL (and request type and version). Wait for a header line. |
187 | */ | 186 | */ |
188 | MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1, | 187 | MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1, |
189 | 188 | ||
190 | /** | 189 | /** |
191 | * 2: We got part of a multi-line request header. Wait for the rest. | 190 | * 2: We got part of a multi-line request header. Wait for the rest. |
192 | */ | 191 | */ |
193 | MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, | 192 | MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, |
194 | 193 | ||
195 | /** | 194 | /** |
196 | * 3: We got the request headers. Process them. | 195 | * 3: We got the request headers. Process them. |
197 | */ | 196 | */ |
198 | MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, | 197 | MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, |
199 | 198 | ||
200 | /** | 199 | /** |
201 | * 4: We have processed the request headers. Send 100 continue. | 200 | * 4: We have processed the request headers. Send 100 continue. |
202 | */ | 201 | */ |
203 | MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, | 202 | MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, |
204 | 203 | ||
205 | /** | 204 | /** |
206 | * 5: We have processed the headers and need to send 100 CONTINUE. | 205 | * 5: We have processed the headers and need to send 100 CONTINUE. |
207 | */ | 206 | */ |
208 | MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, | 207 | MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, |
209 | 208 | ||
210 | /** | 209 | /** |
211 | * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. | 210 | * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. |
212 | */ | 211 | */ |
213 | MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1, | 212 | MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1, |
214 | 213 | ||
215 | /** | 214 | /** |
216 | * 7: We got the request body. Wait for a line of the footer. | 215 | * 7: We got the request body. Wait for a line of the footer. |
217 | */ | 216 | */ |
218 | MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1, | 217 | MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1, |
219 | 218 | ||
220 | /** | 219 | /** |
221 | * 8: We got part of a line of the footer. Wait for the | 220 | * 8: We got part of a line of the footer. Wait for the |
222 | * rest. | 221 | * rest. |
223 | */ | 222 | */ |
224 | MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, | 223 | MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, |
225 | 224 | ||
226 | /** | 225 | /** |
227 | * 9: We received the entire footer. Wait for a response to be queued | 226 | * 9: We received the entire footer. Wait for a response to be queued |
228 | * and prepare the response headers. | 227 | * and prepare the response headers. |
229 | */ | 228 | */ |
230 | MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, | 229 | MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, |
231 | 230 | ||
232 | /** | 231 | /** |
233 | * 10: We have prepared the response headers in the writ buffer. | 232 | * 10: We have prepared the response headers in the writ buffer. |
234 | * Send the response headers. | 233 | * Send the response headers. |
235 | */ | 234 | */ |
236 | MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1, | 235 | MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1, |
237 | 236 | ||
238 | /** | 237 | /** |
239 | * 11: We have sent the response headers. Get ready to send the body. | 238 | * 11: We have sent the response headers. Get ready to send the body. |
240 | */ | 239 | */ |
241 | MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, | 240 | MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, |
242 | 241 | ||
243 | /** | 242 | /** |
244 | * 12: We are ready to send a part of a non-chunked body. Send it. | 243 | * 12: We are ready to send a part of a non-chunked body. Send it. |
245 | */ | 244 | */ |
246 | MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1, | 245 | MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1, |
247 | 246 | ||
248 | /** | 247 | /** |
249 | * 13: We are waiting for the client to provide more | 248 | * 13: We are waiting for the client to provide more |
250 | * data of a non-chunked body. | 249 | * data of a non-chunked body. |
251 | */ | 250 | */ |
252 | MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, | 251 | MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, |
253 | 252 | ||
254 | /** | 253 | /** |
255 | * 14: We are ready to send a chunk. | 254 | * 14: We are ready to send a chunk. |
256 | */ | 255 | */ |
257 | MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, | 256 | MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, |
258 | 257 | ||
259 | /** | 258 | /** |
260 | * 15: We are waiting for the client to provide a chunk of the body. | 259 | * 15: We are waiting for the client to provide a chunk of the body. |
261 | */ | 260 | */ |
262 | MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1, | 261 | MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1, |
263 | 262 | ||
264 | /** | 263 | /** |
265 | * 16: We have sent the response body. Prepare the footers. | 264 | * 16: We have sent the response body. Prepare the footers. |
266 | */ | 265 | */ |
267 | MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, | 266 | MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, |
268 | 267 | ||
269 | /** | 268 | /** |
270 | * 17: We have prepared the response footer. Send it. | 269 | * 17: We have prepared the response footer. Send it. |
271 | */ | 270 | */ |
272 | MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1, | 271 | MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1, |
273 | 272 | ||
274 | /** | 273 | /** |
275 | * 18: We have sent the response footer. Shutdown or restart. | 274 | * 18: We have sent the response footer. Shutdown or restart. |
276 | */ | 275 | */ |
277 | MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, | 276 | MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, |
278 | 277 | ||
279 | /** | 278 | /** |
280 | * 19: This connection is closed (no more activity | 279 | * 19: This connection is closed (no more activity |
281 | * allowed). | 280 | * allowed). |
282 | */ | 281 | */ |
283 | MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1, | 282 | MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1, |
284 | 283 | ||
285 | }; | 284 | }; |
285 | |||
286 | enum MHDS_CONNECTION_STATE | ||
287 | { | ||
288 | MHDS_CONNECTION_INIT = 0, | ||
289 | |||
290 | /** | ||
291 | * 1: We got the URL (and request type and version). Wait for a header line. | ||
292 | */ | ||
293 | MHDS_HANDSHAKE_COMPLETE = MHDS_CONNECTION_INIT + 1, | ||
294 | |||
295 | MHDS_CONNECTION_CONTINUE_SENDING = MHDS_HANDSHAKE_COMPLETE + 1, | ||
296 | |||
297 | MHDS_CONNECTION_CLOSED = MHDS_CONNECTION_CONTINUE_SENDING + 1 | ||
298 | }; | ||
286 | 299 | ||
287 | struct MHD_Connection | 300 | struct MHD_Connection |
288 | { | 301 | { |
289 | 302 | ||
290 | /** | 303 | /** |
291 | * This is a linked list. | 304 | * This is a linked list. |
292 | */ | 305 | */ |
293 | struct MHD_Connection *next; | 306 | struct MHD_Connection *next; |
294 | 307 | ||
295 | /** | 308 | /** |
296 | * Reference to the MHD_Daemon struct. | 309 | * Reference to the MHD_Daemon struct. |
297 | */ | 310 | */ |
298 | struct MHD_Daemon *daemon; | 311 | struct MHD_Daemon *daemon; |
299 | 312 | ||
300 | /** | 313 | /** |
301 | * Linked list of parsed headers. | 314 | * Linked list of parsed headers. |
302 | */ | 315 | */ |
303 | struct MHD_HTTP_Header *headers_received; | 316 | struct MHD_HTTP_Header *headers_received; |
304 | 317 | ||
305 | /** | 318 | /** |
306 | * Response to transmit (initially NULL). | 319 | * Response to transmit (initially NULL). |
307 | */ | 320 | */ |
308 | struct MHD_Response *response; | 321 | struct MHD_Response *response; |
309 | 322 | ||
310 | /** | 323 | /** |
311 | * The memory pool is created whenever we first read | 324 | * The memory pool is created whenever we first read |
312 | * from the TCP stream and destroyed at the end of | 325 | * from the TCP stream and destroyed at the end of |
313 | * each request (and re-created for the next request). | 326 | * each request (and re-created for the next request). |
314 | * In the meantime, this pointer is NULL. The | 327 | * In the meantime, this pointer is NULL. The |
315 | * pool is used for all connection-related data | 328 | * pool is used for all connection-related data |
316 | * except for the response (which maybe shared between | 329 | * except for the response (which maybe shared between |
317 | * connections) and the IP address (which persists | 330 | * connections) and the IP address (which persists |
318 | * across individual requests). | 331 | * across individual requests). |
319 | */ | 332 | */ |
320 | struct MemoryPool *pool; | 333 | struct MemoryPool *pool; |
321 | 334 | ||
322 | /** | 335 | /** |
323 | * We allow the main application to associate some | 336 | * We allow the main application to associate some |
324 | * pointer with the connection. Here is where we | 337 | * pointer with the connection. Here is where we |
325 | * store it. (MHD does not know or care what it | 338 | * store it. (MHD does not know or care what it |
326 | * is). | 339 | * is). |
327 | */ | 340 | */ |
328 | void *client_context; | 341 | void *client_context; |
329 | 342 | ||
330 | /** | 343 | /** |
331 | * Request method. Should be GET/POST/etc. Allocated | 344 | * Request method. Should be GET/POST/etc. Allocated |
332 | * in pool. | 345 | * in pool. |
333 | */ | 346 | */ |
334 | char *method; | 347 | char *method; |
335 | 348 | ||
336 | /** | 349 | /** |
337 | * Requested URL (everything after "GET" only). Allocated | 350 | * Requested URL (everything after "GET" only). Allocated |
338 | * in pool. | 351 | * in pool. |
339 | */ | 352 | */ |
340 | char *url; | 353 | char *url; |
341 | 354 | ||
342 | /** | 355 | /** |
343 | * HTTP version string (i.e. http/1.1). Allocated | 356 | * HTTP version string (i.e. http/1.1). Allocated |
344 | * in pool. | 357 | * in pool. |
345 | */ | 358 | */ |
346 | char *version; | 359 | char *version; |
347 | 360 | ||
348 | /** | 361 | /** |
349 | * Buffer for reading requests. Allocated | 362 | * Buffer for reading requests. Allocated |
350 | * in pool. Actually one byte larger than | 363 | * in pool. Actually one byte larger than |
351 | * read_buffer_size (if non-NULL) to allow for | 364 | * read_buffer_size (if non-NULL) to allow for |
352 | * 0-termination. | 365 | * 0-termination. |
353 | */ | 366 | */ |
354 | char *read_buffer; | 367 | char *read_buffer; |
355 | 368 | ||
356 | /** | 369 | /** |
357 | * Buffer for writing response (headers only). Allocated | 370 | * Buffer for writing response (headers only). Allocated |
358 | * in pool. | 371 | * in pool. |
359 | */ | 372 | */ |
360 | char *write_buffer; | 373 | char *write_buffer; |
361 | 374 | ||
362 | /** | 375 | /** |
363 | * Last incomplete header line during parsing of headers. | 376 | * Last incomplete header line during parsing of headers. |
364 | * Allocated in pool. Only valid if state is | 377 | * Allocated in pool. Only valid if state is |
365 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. | 378 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. |
366 | */ | 379 | */ |
367 | char *last; | 380 | char *last; |
368 | 381 | ||
369 | /** | 382 | /** |
370 | * Position after the colon on the last incomplete header | 383 | * Position after the colon on the last incomplete header |
371 | * line during parsing of headers. | 384 | * line during parsing of headers. |
372 | * Allocated in pool. Only valid if state is | 385 | * Allocated in pool. Only valid if state is |
373 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. | 386 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. |
374 | */ | 387 | */ |
375 | char *colon; | 388 | char *colon; |
376 | 389 | ||
377 | /** | 390 | /** |
378 | * Foreign address (of length addr_len). MALLOCED (not | 391 | * Foreign address (of length addr_len). MALLOCED (not |
379 | * in pool!). | 392 | * in pool!). |
380 | */ | 393 | */ |
381 | struct sockaddr_in *addr; | 394 | struct sockaddr_in *addr; |
382 | 395 | ||
383 | /** | 396 | /** |
384 | * Thread for this connection (if we are using | 397 | * Thread for this connection (if we are using |
385 | * one thread per connection). | 398 | * one thread per connection). |
386 | */ | 399 | */ |
387 | pthread_t pid; | 400 | pthread_t pid; |
388 | 401 | ||
389 | /** | 402 | /** |
390 | * Size of read_buffer (in bytes). This value indicates | 403 | * Size of read_buffer (in bytes). This value indicates |
391 | * how many bytes we're willing to read into the buffer; | 404 | * how many bytes we're willing to read into the buffer; |
392 | * the real buffer is one byte longer to allow for | 405 | * the real buffer is one byte longer to allow for |
393 | * adding zero-termination (when needed). | 406 | * adding zero-termination (when needed). |
394 | */ | 407 | */ |
395 | size_t read_buffer_size; | 408 | size_t read_buffer_size; |
396 | 409 | ||
397 | /** | 410 | /** |
398 | * Position where we currently append data in | 411 | * Position where we currently append data in |
399 | * read_buffer (last valid position). | 412 | * read_buffer (last valid position). |
400 | */ | 413 | */ |
401 | size_t read_buffer_offset; | 414 | size_t read_buffer_offset; |
402 | 415 | ||
403 | /** | 416 | /** |
404 | * Size of write_buffer (in bytes). | 417 | * Size of write_buffer (in bytes). |
405 | */ | 418 | */ |
406 | size_t write_buffer_size; | 419 | size_t write_buffer_size; |
407 | 420 | ||
408 | /** | 421 | /** |
409 | * Offset where we are with sending from write_buffer. | 422 | * Offset where we are with sending from write_buffer. |
410 | */ | 423 | */ |
411 | size_t write_buffer_send_offset; | 424 | size_t write_buffer_send_offset; |
412 | 425 | ||
413 | /** | 426 | /** |
414 | * Last valid location in write_buffer (where do we | 427 | * Last valid location in write_buffer (where do we |
415 | * append and up to where is it safe to send?) | 428 | * append and up to where is it safe to send?) |
416 | */ | 429 | */ |
417 | size_t write_buffer_append_offset; | 430 | size_t write_buffer_append_offset; |
418 | 431 | ||
419 | /** | 432 | /** |
420 | * How many more bytes of the body do we expect | 433 | * How many more bytes of the body do we expect |
421 | * to read? "-1" for unknown. | 434 | * to read? "-1" for unknown. |
422 | */ | 435 | */ |
423 | size_t remaining_upload_size; | 436 | size_t remaining_upload_size; |
424 | 437 | ||
425 | /** | 438 | /** |
426 | * Current write position in the actual response | 439 | * Current write position in the actual response |
427 | * (excluding headers, content only; should be 0 | 440 | * (excluding headers, content only; should be 0 |
428 | * while sending headers). | 441 | * while sending headers). |
429 | */ | 442 | */ |
430 | size_t response_write_position; | 443 | size_t response_write_position; |
431 | 444 | ||
432 | /** | 445 | /** |
433 | * Position in the 100 CONTINUE message that | 446 | * Position in the 100 CONTINUE message that |
434 | * we need to send when receiving http 1.1 requests. | 447 | * we need to send when receiving http 1.1 requests. |
435 | */ | 448 | */ |
436 | size_t continue_message_write_offset; | 449 | size_t continue_message_write_offset; |
437 | 450 | ||
438 | /** | 451 | /** |
439 | * Length of the foreign address. | 452 | * Length of the foreign address. |
440 | */ | 453 | */ |
441 | socklen_t addr_len; | 454 | socklen_t addr_len; |
442 | 455 | ||
443 | /** | 456 | /** |
444 | * Last time this connection had any activity | 457 | * Last time this connection had any activity |
445 | * (reading or writing). | 458 | * (reading or writing). |
446 | */ | 459 | */ |
447 | time_t last_activity; | 460 | time_t last_activity; |
448 | 461 | ||
449 | /** | 462 | /** |
450 | * Socket for this connection. Set to -1 if | 463 | * Socket for this connection. Set to -1 if |
451 | * this connection has died (daemon should clean | 464 | * this connection has died (daemon should clean |
452 | * up in that case). | 465 | * up in that case). |
453 | */ | 466 | */ |
454 | int socket_fd; | 467 | int socket_fd; |
455 | 468 | ||
456 | /** | 469 | /** |
457 | * Has this socket been closed for reading (i.e. | 470 | * Has this socket been closed for reading (i.e. |
458 | * other side closed the connection)? If so, | 471 | * other side closed the connection)? If so, |
459 | * we must completely close the connection once | 472 | * we must completely close the connection once |
460 | * we are done sending our response (and stop | 473 | * we are done sending our response (and stop |
461 | * trying to read from this socket). | 474 | * trying to read from this socket). |
462 | */ | 475 | */ |
463 | int read_closed; | 476 | int read_closed; |
464 | 477 | ||
465 | /** | 478 | /** |
466 | * State in the FSM for this connection. | 479 | * State in the FSM for this connection. |
467 | */ | 480 | */ |
468 | enum MHD_CONNECTION_STATE state; | 481 | enum MHD_CONNECTION_STATE state; |
469 | 482 | ||
470 | /** | 483 | /** |
471 | * HTTP response code. Only valid if response object | 484 | * HTTP response code. Only valid if response object |
472 | * is already set. | 485 | * is already set. |
473 | */ | 486 | */ |
474 | unsigned int responseCode; | 487 | unsigned int responseCode; |
475 | 488 | ||
476 | /** | 489 | /** |
477 | * Set to MHD_YES if the response's content reader | 490 | * Set to MHD_YES if the response's content reader |
478 | * callback failed to provide data the last time | 491 | * callback failed to provide data the last time |
479 | * we tried to read from it. In that case, the | 492 | * we tried to read from it. In that case, the |
480 | * write socket should be marked as unready until | 493 | * write socket should be marked as unready until |
481 | * the CRC call succeeds. | 494 | * the CRC call succeeds. |
482 | */ | 495 | */ |
483 | int response_unready; | 496 | int response_unready; |
484 | 497 | ||
485 | /** | 498 | /** |
486 | * Are we sending with chunked encoding? | 499 | * Are we sending with chunked encoding? |
487 | */ | 500 | */ |
488 | int have_chunked_response; | 501 | int have_chunked_response; |
489 | 502 | ||
490 | /** | 503 | /** |
491 | * Are we receiving with chunked encoding? This will be set to | 504 | * Are we receiving with chunked encoding? This will be set to |
492 | * MHD_YES after we parse the headers and are processing the body | 505 | * MHD_YES after we parse the headers and are processing the body |
493 | * with chunks. After we are done with the body and we are | 506 | * with chunks. After we are done with the body and we are |
494 | * processing the footers; once the footers are also done, this will | 507 | * processing the footers; once the footers are also done, this will |
495 | * be set to MHD_NO again (before the final call to the handler). | 508 | * be set to MHD_NO again (before the final call to the handler). |
496 | */ | 509 | */ |
497 | int have_chunked_upload; | 510 | int have_chunked_upload; |
498 | 511 | ||
499 | /** | 512 | /** |
500 | * If we are receiving with chunked encoding, where are we right | 513 | * If we are receiving with chunked encoding, where are we right |
501 | * now? Set to 0 if we are waiting to receive the chunk size; | 514 | * now? Set to 0 if we are waiting to receive the chunk size; |
502 | * otherwise, this is the size of the current chunk. A value of | 515 | * otherwise, this is the size of the current chunk. A value of |
503 | * zero is also used when we're at the end of the chunks. | 516 | * zero is also used when we're at the end of the chunks. |
504 | */ | 517 | */ |
505 | unsigned int current_chunk_size; | 518 | unsigned int current_chunk_size; |
506 | 519 | ||
507 | /** | 520 | /** |
508 | * If we are receiving with chunked encoding, where are we currently | 521 | * If we are receiving with chunked encoding, where are we currently |
509 | * with respect to the current chunk (at what offset / position)? | 522 | * with respect to the current chunk (at what offset / position)? |
510 | */ | 523 | */ |
511 | unsigned int current_chunk_offset; | 524 | unsigned int current_chunk_offset; |
512 | 525 | ||
513 | }; | 526 | }; |
514 | 527 | ||
515 | 528 | typedef struct MHD_Connection MHD_Connection_t; | |
516 | 529 | ||
517 | struct MHD_Daemon | 530 | struct MHD_Daemon |
518 | { | 531 | { |
519 | 532 | ||
520 | /** | 533 | /** |
521 | * Callback function for all requests. | 534 | * Callback function for all requests. |
522 | */ | 535 | */ |
523 | MHD_AccessHandlerCallback default_handler; | 536 | MHD_AccessHandlerCallback default_handler; |
524 | 537 | ||
525 | /** | 538 | /** |
526 | * Closure argument to default_handler. | 539 | * Closure argument to default_handler. |
527 | */ | 540 | */ |
528 | void *default_handler_cls; | 541 | void *default_handler_cls; |
529 | 542 | ||
530 | /** | 543 | /** |
531 | * Linked list of our current connections. | 544 | * Linked list of our current connections. |
532 | */ | 545 | */ |
533 | struct MHD_Connection *connections; | 546 | struct MHD_Connection *connections; |
534 | 547 | ||
535 | MHD_AcceptPolicyCallback apc; | 548 | /** |
536 | 549 | * Linked list of our current connections. | |
537 | void *apc_cls; | 550 | */ |
538 | 551 | // TODO switch to a dedicated tls connection struct | |
539 | MHD_RequestCompletedCallback notify_completed; | 552 | struct MHD_Connection *tls_connections; |
540 | 553 | ||
541 | void *notify_completed_cls; | 554 | MHD_AcceptPolicyCallback apc; |
542 | 555 | ||
543 | /** | 556 | void *apc_cls; |
544 | * PID of the select thread (if we have internal select) | 557 | |
545 | */ | 558 | MHD_RequestCompletedCallback notify_completed; |
546 | pthread_t pid; | 559 | |
547 | 560 | void *notify_completed_cls; | |
548 | /** | 561 | |
549 | * Listen socket. | 562 | /** |
550 | */ | 563 | * PID of the select thread (if we have internal select) |
551 | int socket_fd; | 564 | */ |
552 | 565 | pthread_t pid; | |
553 | /** | 566 | |
554 | * Are we shutting down? | 567 | /** |
555 | */ | 568 | * Listen socket. |
556 | int shutdown; | 569 | */ |
557 | 570 | int socket_fd; | |
558 | /** | 571 | |
559 | * Size of the per-connection memory pools. | 572 | /** |
560 | */ | 573 | * Are we shutting down? |
561 | unsigned int pool_size; | 574 | */ |
562 | 575 | int shutdown; | |
563 | /** | 576 | |
564 | * Limit on the number of parallel connections. | 577 | /** |
565 | */ | 578 | * Size of the per-connection memory pools. |
566 | unsigned int max_connections; | 579 | */ |
567 | 580 | unsigned int pool_size; | |
568 | /** | 581 | |
569 | * After how many seconds of inactivity should | 582 | /** |
570 | * connections time out? Zero for no timeout. | 583 | * Limit on the number of parallel connections. |
571 | */ | 584 | */ |
572 | unsigned int connection_timeout; | 585 | unsigned int max_connections; |
573 | 586 | ||
574 | /** | 587 | /** |
575 | * Maximum number of connections per IP, or 0 for | 588 | * After how many seconds of inactivity should |
576 | * unlimited. | 589 | * connections time out? Zero for no timeout. |
577 | */ | 590 | */ |
578 | unsigned int per_ip_connection_limit; | 591 | unsigned int connection_timeout; |
579 | 592 | ||
580 | /** | 593 | /** |
581 | * Daemon's options. | 594 | * Maximum number of connections per IP, or 0 for |
582 | */ | 595 | * unlimited. |
583 | enum MHD_OPTION options; | 596 | */ |
584 | 597 | unsigned int per_ip_connection_limit; | |
585 | /** | 598 | |
586 | * Listen port. | 599 | /** |
587 | */ | 600 | * Daemon's options. |
588 | unsigned short port; | 601 | */ |
589 | 602 | enum MHD_OPTION options; | |
590 | }; | 603 | |
591 | 604 | /** | |
605 | * Listen port. | ||
606 | */ | ||
607 | unsigned short port; | ||
608 | |||
609 | }; | ||
592 | 610 | ||
593 | #endif | 611 | #endif |
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index 3d693b23..340f7824 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am | |||
@@ -1,10 +1,16 @@ | |||
1 | SUBDIRS = . | 1 | SUBDIRS = . |
2 | 2 | ||
3 | INCLUDES = -I$(top_srcdir)/src/include | 3 | AM_CPPFLAGS = -I$(top_srcdir)/src/include |
4 | 4 | ||
5 | # example programs | 5 | # example programs |
6 | 6 | ||
7 | noinst_PROGRAMS = minimal_example querystring_example fileserver_example fileserver_example_external_select | 7 | noinst_PROGRAMS = \ |
8 | https_server_example \ | ||
9 | https_echo_client_example \ | ||
10 | minimal_example \ | ||
11 | querystring_example \ | ||
12 | fileserver_example \ | ||
13 | fileserver_example_external_select | ||
8 | 14 | ||
9 | minimal_example_SOURCES = \ | 15 | minimal_example_SOURCES = \ |
10 | minimal_example.c | 16 | minimal_example.c |
@@ -21,9 +27,29 @@ fileserver_example_SOURCES = \ | |||
21 | fileserver_example_LDADD = \ | 27 | fileserver_example_LDADD = \ |
22 | $(top_builddir)/src/daemon/libmicrohttpd.la | 28 | $(top_builddir)/src/daemon/libmicrohttpd.la |
23 | 29 | ||
24 | |||
25 | fileserver_example_external_select_SOURCES = \ | 30 | fileserver_example_external_select_SOURCES = \ |
26 | fileserver_example_external_select.c | 31 | fileserver_example_external_select.c |
27 | fileserver_example_external_select_LDADD = \ | 32 | fileserver_example_external_select_LDADD = \ |
28 | $(top_builddir)/src/daemon/libmicrohttpd.la | 33 | $(top_builddir)/src/daemon/libmicrohttpd.la |
29 | 34 | ||
35 | https_server_example_CPPFLAGS = \ | ||
36 | $(GNUTLS_CPPFLAGS) \ | ||
37 | -I$(top_srcdir)/src/daemon | ||
38 | https_server_example_SOURCES = \ | ||
39 | https_server_example.c | ||
40 | https_server_example_LDADD = \ | ||
41 | $(top_builddir)/src/daemon/libmicrohttpd.la | ||
42 | https_server_example_LDFLAGS = \ | ||
43 | -L$(GNUTLS_LIB_PATH) \ | ||
44 | -lgnutls | ||
45 | |||
46 | https_echo_client_example_CPPFLAGS = \ | ||
47 | $(GNUTLS_CPPFLAGS) \ | ||
48 | -I$(top_srcdir)/src/daemon | ||
49 | https_echo_client_example_SOURCES = \ | ||
50 | https_echo_client_example.c | ||
51 | https_echo_client_example_LDADD = \ | ||
52 | $(top_builddir)/src/daemon/libmicrohttpd.la | ||
53 | https_echo_client_example_LDFLAGS = \ | ||
54 | -L$(GNUTLS_LIB_PATH) \ | ||
55 | -lgnutls | ||
diff --git a/src/examples/https_echo_client_example.c b/src/examples/https_echo_client_example.c new file mode 100644 index 00000000..04e26c74 --- /dev/null +++ b/src/examples/https_echo_client_example.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | (C) 2007 Christian Grothoff | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file https_echo_client.c | ||
23 | * @brief a simple echo client to use in conjuction with the echo TLS server. | ||
24 | * @author LV-426 | ||
25 | */ | ||
26 | |||
27 | #if HAVE_CONFIG_H | ||
28 | # include <config.h> | ||
29 | #endif | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <sys/types.h> | ||
35 | #include <sys/socket.h> | ||
36 | #include <arpa/inet.h> | ||
37 | #include <unistd.h> | ||
38 | #include <gnutls/gnutls.h> | ||
39 | |||
40 | #define MAX_BUF 1024 | ||
41 | #define SA struct sockaddr | ||
42 | #define MSG "GET / HTTP/1.0\r\n\r\n" | ||
43 | |||
44 | extern int tcp_connect (void); | ||
45 | extern void tcp_close (int sd); | ||
46 | |||
47 | int | ||
48 | main (int argc, char **argv) | ||
49 | { | ||
50 | int ret, sd, ii, err; | ||
51 | gnutls_session_t session; | ||
52 | char buffer[MAX_BUF + 1]; | ||
53 | gnutls_anon_client_credentials_t anoncred; | ||
54 | |||
55 | struct sockaddr_in servaddr4; | ||
56 | const struct sockaddr *servaddr; | ||
57 | struct sockaddr_in sa; | ||
58 | socklen_t addrlen; | ||
59 | |||
60 | if (argc < 2) | ||
61 | { | ||
62 | printf ("Usage : %s SERVER-PORT\n", argv[0]); | ||
63 | return 1; | ||
64 | } | ||
65 | |||
66 | gnutls_global_init (); | ||
67 | |||
68 | gnutls_anon_allocate_client_credentials (&anoncred); | ||
69 | |||
70 | /* Initialize TLS session */ | ||
71 | gnutls_init (&session, GNUTLS_CLIENT); | ||
72 | |||
73 | /* Use default priorities */ | ||
74 | gnutls_priority_set_direct (session, "PERFORMANCE:+ANON-DH:!ARCFOUR-128", | ||
75 | NULL); | ||
76 | |||
77 | /* put the anonymous credentials to the current session */ | ||
78 | gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); | ||
79 | |||
80 | sd = socket (AF_INET, SOCK_STREAM, 0); | ||
81 | memset (&sa, '\0', sizeof (sa)); | ||
82 | sa.sin_family = AF_INET; | ||
83 | sa.sin_port = htons (atoi (argv[1])); | ||
84 | inet_pton (AF_INET, "127.0.0.1", &sa.sin_addr); | ||
85 | |||
86 | /* connect to the peer */ | ||
87 | err = connect (sd, (struct sockaddr *) &sa, sizeof (sa)); | ||
88 | if (err < 0) | ||
89 | { | ||
90 | fprintf (stderr, "Connect error\n"); | ||
91 | exit (1); | ||
92 | } | ||
93 | |||
94 | gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd); | ||
95 | |||
96 | /* Perform the TLS handshake */ | ||
97 | ret = gnutls_handshake (session); | ||
98 | |||
99 | if (ret < 0) | ||
100 | { | ||
101 | fprintf (stderr, "*** Handshake failed\n"); | ||
102 | gnutls_perror (ret); | ||
103 | goto end; | ||
104 | } | ||
105 | else | ||
106 | { | ||
107 | printf ("- Handshake was completed\n"); | ||
108 | } | ||
109 | |||
110 | for (;;) | ||
111 | { | ||
112 | /**/ scanf ("%s", buffer); | ||
113 | |||
114 | if (strcmp (buffer, "exit") == 0) | ||
115 | { | ||
116 | gnutls_record_send (session, buffer, strlen (MSG)); | ||
117 | break; | ||
118 | } | ||
119 | gnutls_record_send (session, buffer, strlen (MSG)); | ||
120 | |||
121 | ret = gnutls_record_recv (session, buffer, MAX_BUF); | ||
122 | if (ret == 0) | ||
123 | { | ||
124 | printf ("- Peer has closed the TLS connection\n"); | ||
125 | goto end; | ||
126 | } | ||
127 | else if (ret < 0) | ||
128 | { | ||
129 | fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret)); | ||
130 | break; | ||
131 | } | ||
132 | |||
133 | printf ("- Received %d bytes: ", ret); | ||
134 | for (ii = 0; ii < ret; ii++) | ||
135 | { | ||
136 | fputc (buffer[ii], stdout); | ||
137 | } | ||
138 | fputs ("\n", stdout); | ||
139 | } | ||
140 | |||
141 | end: | ||
142 | |||
143 | shutdown (sd, SHUT_RDWR); | ||
144 | close (sd); | ||
145 | |||
146 | gnutls_deinit (session); | ||
147 | |||
148 | gnutls_anon_free_client_credentials (anoncred); | ||
149 | |||
150 | gnutls_global_deinit (); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
diff --git a/src/examples/https_server_example.c b/src/examples/https_server_example.c new file mode 100644 index 00000000..709f8502 --- /dev/null +++ b/src/examples/https_server_example.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | (C) 2007 Christian Grothoff | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file https_server_example.c | ||
23 | * @brief a simple echo server using TLS. echo input from client until 'exit' message is received. | ||
24 | * @author LV-426 | ||
25 | */ | ||
26 | |||
27 | #include "config.h" | ||
28 | #include <microhttpd.h> | ||
29 | #include "internal.h" | ||
30 | |||
31 | #include <stdlib.h> | ||
32 | #ifndef MINGW | ||
33 | #include <unistd.h> | ||
34 | #endif | ||
35 | #include <string.h> | ||
36 | #include <stdio.h> | ||
37 | #include <gnutls/gnutls.h> | ||
38 | |||
39 | #define DH_BITS 1024 | ||
40 | #define MAX_BUF 1024 | ||
41 | /* server credintials */ | ||
42 | gnutls_anon_server_credentials_t anoncred; | ||
43 | |||
44 | /* server Diffie-Hellman parameters */ | ||
45 | static gnutls_dh_params_t dh_params; | ||
46 | |||
47 | |||
48 | /* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */ | ||
49 | static int | ||
50 | generate_dh_params (void) | ||
51 | { | ||
52 | |||
53 | gnutls_dh_params_init (&dh_params); | ||
54 | gnutls_dh_params_generate2 (dh_params, DH_BITS); | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | gnutls_session_t | ||
59 | initialize_tls_session (void) | ||
60 | { | ||
61 | gnutls_session_t session; | ||
62 | |||
63 | gnutls_init (&session, GNUTLS_SERVER); | ||
64 | |||
65 | gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL); | ||
66 | |||
67 | gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); | ||
68 | |||
69 | gnutls_dh_set_prime_bits (session, DH_BITS); | ||
70 | |||
71 | return session; | ||
72 | } | ||
73 | |||
74 | /* Accept Policy Callback */ | ||
75 | static int | ||
76 | TLS_echo (void *cls, | ||
77 | struct MHD_Connection *connection, | ||
78 | const char *url, | ||
79 | const char *method, | ||
80 | const char *upload_data, | ||
81 | const char *version, unsigned int *upload_data_size, void **ptr) | ||
82 | { | ||
83 | gnutls_session_t session; | ||
84 | static int aptr; | ||
85 | struct MHD_Response *response; | ||
86 | char buffer[MAX_BUF + 1]; | ||
87 | int ret; | ||
88 | |||
89 | printf ("accepted connection from %d\n", connection->addr->sin_addr); | ||
90 | |||
91 | session = initialize_tls_session (); | ||
92 | |||
93 | gnutls_transport_set_ptr (session, connection->socket_fd); | ||
94 | |||
95 | ret = gnutls_handshake (session); | ||
96 | if (ret < 0) | ||
97 | { | ||
98 | /* set connection as closed */ | ||
99 | connection->socket_fd = 1; | ||
100 | gnutls_deinit (session); | ||
101 | fprintf (stderr, "*** Handshake has failed (%s)\n\n", | ||
102 | gnutls_strerror (ret)); | ||
103 | return MHD_NO; | ||
104 | } | ||
105 | |||
106 | printf ("TLS Handshake completed\n"); | ||
107 | connection->state = MHDS_HANDSHAKE_COMPLETE; | ||
108 | |||
109 | /* simple echo loop. message encryption/decryption is acheived through 'gnutls_record_send' | ||
110 | * & gnutls_record_recv calls. */ | ||
111 | for (;;) | ||
112 | { | ||
113 | memset (buffer, 0, MAX_BUF + 1); | ||
114 | ret = gnutls_record_recv (session, buffer, MAX_BUF); | ||
115 | |||
116 | if (ret < 0) | ||
117 | { | ||
118 | fprintf (stderr, "\n*** Received corrupted " | ||
119 | "data(%d). Closing the connection.\n\n", ret); | ||
120 | break; | ||
121 | } | ||
122 | else if (ret >= 0) | ||
123 | { | ||
124 | if (strcmp (buffer, "exit") == 0) | ||
125 | { | ||
126 | printf ("\n- Peer has closed the GNUTLS connection\n"); | ||
127 | break; | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | /* echo data back to the client */ | ||
132 | gnutls_record_send (session, buffer, strlen (buffer)); | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | printf ("\n"); | ||
137 | |||
138 | /* mark connection as closed */ | ||
139 | connection->socket_fd = -1; | ||
140 | |||
141 | gnutls_deinit (session); | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | int | ||
147 | main (int argc, char *const *argv) | ||
148 | { | ||
149 | struct MHD_Daemon *daemon; | ||
150 | struct MHD_Daemon *TLS_daemon; | ||
151 | |||
152 | /* look for HTTPS port argument */ | ||
153 | if (argc < 4) | ||
154 | { | ||
155 | printf ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT\n", argv[0]); | ||
156 | return 1; | ||
157 | } | ||
158 | |||
159 | gnutls_global_init (); | ||
160 | |||
161 | gnutls_anon_allocate_server_credentials (&anoncred); | ||
162 | |||
163 | generate_dh_params (); | ||
164 | |||
165 | gnutls_anon_set_server_dh_params (anoncred, dh_params); | ||
166 | |||
167 | TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | ||
168 | | MHD_USE_DEBUG | MHD_USE_SSL, | ||
169 | atoi (argv[3]), NULL, NULL, &TLS_echo, NULL, | ||
170 | MHD_OPTION_END); | ||
171 | |||
172 | if (TLS_daemon == NULL) | ||
173 | return 1; | ||
174 | sleep (atoi (argv[2])); | ||
175 | |||
176 | MHD_stop_daemon (daemon); | ||
177 | |||
178 | gnutls_anon_free_server_credentials (anoncred); | ||
179 | |||
180 | gnutls_global_deinit (); | ||
181 | return 0; | ||
182 | } | ||