diff options
Diffstat (limited to 'src/daemon/daemon.c')
-rw-r--r-- | src/daemon/daemon.c | 1817 |
1 files changed, 396 insertions, 1421 deletions
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index dd22ce6d..8a602df4 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c | |||
@@ -20,544 +20,123 @@ | |||
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file daemon.c | 22 | * @file daemon.c |
23 | * @brief This is my implementation of the libmicrohttpd interface. Many features incomplete at this time. | 23 | * @brief A minimal-HTTP server library |
24 | * @author Daniel Pittman | 24 | * @author Daniel Pittman |
25 | * @author Christian Grothoff | ||
25 | * @version 0.1.0 | 26 | * @version 0.1.0 |
26 | */ | 27 | */ |
27 | 28 | ||
28 | #include "config.h" | ||
29 | #include "microhttpd.h" | ||
30 | |||
31 | #include <netinet/in.h> | ||
32 | #include <stdio.h> | 29 | #include <stdio.h> |
33 | #include <stdlib.h> | 30 | #include <stdlib.h> |
34 | #include <netdb.h> | 31 | #include <netdb.h> |
35 | #include <string.h> | 32 | #include <string.h> |
36 | #include <unistd.h> | 33 | #include <unistd.h> |
34 | #include <stdarg> | ||
37 | #include <fcntl.h> | 35 | #include <fcntl.h> |
38 | #include <pthread.h> | 36 | #include <pthread.h> |
37 | #include <netinet/in.h> | ||
39 | 38 | ||
39 | #include "microhttpd.h" | ||
40 | #include "internal.h" | ||
41 | #include "response.h" | ||
42 | #include "session.h" | ||
43 | #include "config.h" | ||
40 | 44 | ||
41 | #define MHD_MAX_CONNECTIONS FD_SETSIZE -4 | 45 | #define MHD_MAX_CONNECTIONS FD_SETSIZE -4 |
42 | #define MHD_MAX_BUF_SIZE 2048 | ||
43 | #define MHD_MAX_HEADERS 1024 | ||
44 | #define MHD_MAX_HANDLERS 1024 | ||
45 | #define MHD_MAX_RESPONSE 1024 | ||
46 | |||
47 | |||
48 | int MHD_handle_read(int, struct MHD_Daemon *); | ||
49 | int MHD_handle_write(int, struct MHD_Daemon *); | ||
50 | void * MHD_spawn_connections(void * data); | ||
51 | void * MHD_select(void * data); | ||
52 | int MHD_parse_message(struct MHD_Session * session); | ||
53 | void MHD_parse_URL(struct MHD_Session * session); | ||
54 | |||
55 | struct MHD_Daemon { | ||
56 | unsigned int options; | ||
57 | |||
58 | unsigned short port; | ||
59 | int socket_fd; | ||
60 | int max_fd; | ||
61 | |||
62 | MHD_AcceptPolicyCallback apc; | ||
63 | void * apc_cls; | ||
64 | |||
65 | fd_set read_fd_set; | ||
66 | fd_set write_fd_set; | ||
67 | fd_set except_fd_set; | ||
68 | |||
69 | int shutdown; | ||
70 | pthread_t pid; | ||
71 | |||
72 | struct MHD_Session * connections[MHD_MAX_CONNECTIONS]; | ||
73 | |||
74 | int firstFreeHandler; | ||
75 | struct MHD_Access_Handler * handlers[MHD_MAX_HANDLERS]; | ||
76 | MHD_AccessHandlerCallback dh; | ||
77 | void * dh_cls; | ||
78 | }; | ||
79 | |||
80 | struct MHD_Session { | ||
81 | struct sockaddr_in addr; | ||
82 | |||
83 | int readLoc; | ||
84 | int writeLoc; | ||
85 | |||
86 | int id; | ||
87 | int socket_fd; | ||
88 | pthread_t pid; | ||
89 | |||
90 | struct MHD_Daemon * daemon; | ||
91 | |||
92 | int bufPos; | ||
93 | int messagePos; | ||
94 | char inbuf[MHD_MAX_BUF_SIZE]; | ||
95 | |||
96 | int firstFreeHeader; | ||
97 | char * requestType; | ||
98 | char * documentName; | ||
99 | struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS]; | ||
100 | 46 | ||
101 | unsigned short responsePending; | 47 | #define MHD_MAX_BUF_SIZE 2048 |
102 | int currentResponse; | ||
103 | struct MHD_Response * currentResponses[MHD_MAX_RESPONSE]; | ||
104 | }; | ||
105 | |||
106 | |||
107 | struct MHD_Response { | ||
108 | pthread_mutex_t mutex; | ||
109 | |||
110 | unsigned int responseCode; | ||
111 | |||
112 | int freeWhenFinished; | ||
113 | |||
114 | unsigned int headersSent; | ||
115 | |||
116 | size_t size; | ||
117 | void * data; | ||
118 | int bytesSentSoFar; | ||
119 | |||
120 | int must_free; | ||
121 | MHD_ContentReaderCallback crc; | ||
122 | |||
123 | void * crc_cls; | ||
124 | MHD_ContentReaderFreeCallback crfc; | ||
125 | |||
126 | int firstFreeHeader; | ||
127 | struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS]; | ||
128 | |||
129 | struct MHD_Session * currentSession; | ||
130 | }; | ||
131 | |||
132 | struct MHD_Access_Handler { | ||
133 | char * uri_prefix; | ||
134 | MHD_AccessHandlerCallback dh; | ||
135 | void * dh_cls; | ||
136 | }; | ||
137 | 48 | ||
138 | struct MHD_HTTP_Header { | ||
139 | char * header; | ||
140 | char * headerContent; | ||
141 | enum MHD_ValueKind kind; | ||
142 | }; | ||
143 | 49 | ||
144 | /** | 50 | /** |
145 | * Add a header line to the response. | 51 | * fprintf-like helper function for logging debug |
146 | * | 52 | * messages. |
147 | * @return MHD_NO on error (i.e. invalid header or content format). | ||
148 | */ | 53 | */ |
149 | int | 54 | static void DLOG(const struct MHD_Daemon * daemon, |
150 | MHD_add_response_header(struct MHD_Response * response, | 55 | const char * format, |
151 | const char * header, | 56 | ...) { |
152 | const char * content) { | 57 | va_list va; |
153 | //Note that as of this time this function will also return | 58 | |
154 | //an error if the maximum number of headers allowed is exceeded. | 59 | if ( (daemon->options & MHD_USE_DEBUG) == 0) |
155 | 60 | return; | |
156 | char * saveptr; | 61 | va_start(va, format); |
157 | char * newHeader; | 62 | vfprintf(stderr, format, va); |
158 | char * newContent; | 63 | va_end(va); |
159 | int i; | ||
160 | |||
161 | if(response == NULL || header == NULL || content == NULL || strlen(header) == 0 || strlen(content) == 0) { | ||
162 | return MHD_NO; | ||
163 | } | ||
164 | /* CG: use linked list to avoid limitation and over-allocation! */ | ||
165 | if(response->firstFreeHeader >= MHD_MAX_HEADERS) { | ||
166 | return MHD_NO; | ||
167 | } | ||
168 | |||
169 | newHeader = (char *)malloc(strlen(header)+1); | ||
170 | newContent = (char *)malloc(strlen(content)+1); | ||
171 | /* CG: useless check! */ | ||
172 | if(newHeader == NULL || newContent == NULL) { | ||
173 | /* CG: printf! */ | ||
174 | fprintf(stderr, "Error allocating memory!\n"); | ||
175 | return MHD_NO; | ||
176 | } | ||
177 | |||
178 | /* CG: do you mean strcpy/strdup? defer allocation | ||
179 | until you need to (after malformed checks!) */ | ||
180 | sprintf(newHeader, "%s", header); | ||
181 | sprintf(newContent, "%s", content); | ||
182 | |||
183 | /* CG: why not use strstr? */ | ||
184 | if(strtok_r(newHeader, " \t\r\n", &saveptr) != NULL) { | ||
185 | fprintf(stderr, "Malformed header!\n"); | ||
186 | free(newContent); | ||
187 | free(newHeader); | ||
188 | return MHD_NO; | ||
189 | } | ||
190 | |||
191 | /* CG: why not use strstr? */ | ||
192 | if(strtok_r(newContent, "\n", &saveptr) != NULL) { | ||
193 | fprintf(stderr, "Malformed content!\n"); | ||
194 | free(newContent); | ||
195 | free(newHeader); | ||
196 | return MHD_NO; | ||
197 | } | ||
198 | |||
199 | /* CG: this is not C++ -- no need to cast after malloc! */ | ||
200 | struct MHD_HTTP_Header * newHTTPHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); | ||
201 | |||
202 | if(newHTTPHeader == NULL) { | ||
203 | /* CG: useless check, printf */ | ||
204 | fprintf(stderr, "Error allocating memory!\n"); | ||
205 | free(newContent); | ||
206 | free(newHeader); | ||
207 | return MHD_NO; | ||
208 | } | ||
209 | |||
210 | /* CG: strdup here, avoids free's above! */ | ||
211 | response->headers[response->firstFreeHeader]->header = newHeader; | ||
212 | response->headers[response->firstFreeHeader]->headerContent = newContent; | ||
213 | |||
214 | //For now, everything is a HTTP Header... this needs to be improved! | ||
215 | /* CG: what else are you thinking about? Cookies? | ||
216 | sounds like you are proposing an API change!!! */ | ||
217 | |||
218 | response->headers[response->firstFreeHeader]->kind = MHD_HEADER_KIND; | ||
219 | |||
220 | /* CG: YUCK! Yet another reason for linked lists... | ||
221 | Why bother with the firstFreeHandler field if | ||
222 | you're O(n) anyway!? */ | ||
223 | response->firstFreeHeader=MHD_MAX_HEADERS; | ||
224 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
225 | if(response->headers[i] == NULL) { | ||
226 | response->firstFreeHeader = i; | ||
227 | break; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | return MHD_YES; | ||
232 | } | 64 | } |
233 | 65 | ||
234 | /** | ||
235 | * This function accepts an incoming connection | ||
236 | * and creates the MHD_Session object for it. | ||
237 | * It also enforces policy by way of calling the accept policy callback | ||
238 | */ | ||
239 | int | ||
240 | MHD_create_connection(struct MHD_Daemon * daemon) { | ||
241 | int i, first_free, size; | ||
242 | |||
243 | if(daemon == NULL) | ||
244 | return -1; | ||
245 | |||
246 | first_free = -1; | ||
247 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | ||
248 | if(daemon->connections[i] == NULL) { | ||
249 | first_free = i; | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if(first_free == -1) | ||
255 | return -1; | ||
256 | |||
257 | /* CG: delay allocation until at accept has succeeded! */ | ||
258 | daemon->connections[first_free] = (struct MHD_Session *)malloc(sizeof(struct MHD_Session)); | ||
259 | |||
260 | if(daemon->connections[first_free] == NULL) { | ||
261 | /* CG: use MACRO or (static) helper function | ||
262 | instead of writing this option check everywhere! */ | ||
263 | if((daemon->options & MHD_USE_DEBUG) != 0) | ||
264 | fprintf(stderr, "Error allocating memory!\n"); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | size = sizeof(struct sockaddr); | ||
269 | daemon->connections[first_free]->socket_fd = | ||
270 | |||
271 | accept(daemon->socket_fd, (struct sockaddr *)&daemon->connections[first_free]->addr, | ||
272 | (socklen_t *)&size); | ||
273 | |||
274 | if(daemon->connections[first_free]->socket_fd == -1) { | ||
275 | free(daemon->connections[first_free]); | ||
276 | daemon->connections[first_free] = NULL; | ||
277 | if((daemon->options & MHD_USE_DEBUG) != 0) { | ||
278 | fprintf(stderr, "Error accepting incoming connections!\n"); | ||
279 | } | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | if(daemon->apc != NULL && daemon->apc(daemon->apc_cls, (const struct sockaddr *)&daemon->connections[first_free]->addr, (socklen_t)sizeof(struct sockaddr_in))==MHD_NO) { | ||
284 | close(daemon->connections[first_free]->socket_fd); | ||
285 | free(daemon->connections[first_free]); | ||
286 | daemon->connections[first_free] = NULL; | ||
287 | if((daemon->options & MHD_USE_DEBUG) != 0) { | ||
288 | fprintf(stderr, "Connection denied based on accept policy callback!\n"); | ||
289 | } | ||
290 | return -1; | ||
291 | } | ||
292 | |||
293 | daemon->connections[first_free]->id = first_free; | ||
294 | daemon->connections[first_free]->daemon = daemon; | ||
295 | daemon->connections[first_free]->pid = (pthread_t)-1; | ||
296 | daemon->connections[first_free]->bufPos = 0; | ||
297 | daemon->connections[first_free]->messagePos= 0; | ||
298 | daemon->connections[first_free]->responsePending = 0; | ||
299 | memset(daemon->connections[first_free]->inbuf, '\0', MHD_MAX_BUF_SIZE); | ||
300 | daemon->connections[first_free]->currentResponse = 0; | ||
301 | daemon->connections[first_free]->firstFreeHeader = 0; | ||
302 | |||
303 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
304 | daemon->connections[first_free]->headers[i] = NULL; | ||
305 | } | ||
306 | |||
307 | for(i = 0; i < MHD_MAX_RESPONSE; i++) { | ||
308 | daemon->connections[first_free]->currentResponses[i] = NULL; | ||
309 | } | ||
310 | /* CG: maybe better to re-compute max_fd closer to select; | ||
311 | also handles deletion mo re graceful, need to iterate over | ||
312 | all connections anyway for FD_SET... */ | ||
313 | if(daemon->max_fd < daemon->connections[first_free]->socket_fd) { | ||
314 | daemon->max_fd = daemon->connections[first_free]->socket_fd; | ||
315 | } | ||
316 | |||
317 | return first_free; | ||
318 | } | ||
319 | 66 | ||
320 | /** | 67 | /** |
321 | * Create a response object. The response object can be extended with | 68 | * Register an access handler for all URIs beginning with uri_prefix. |
322 | * header information and then be used any number of times. | ||
323 | * | 69 | * |
324 | * @param size size of the data portion of the response, -1 for unknown | 70 | * @param uri_prefix |
325 | * @param crc callback to use to obtain response data | 71 | * @return MRI_NO if a handler for this exact prefix |
326 | * @param crc_cls extra argument to crc | 72 | * already exists |
327 | * @param crfc callback to call to free crc_cls resources | ||
328 | * @return NULL on error (i.e. invalid arguments, out of memory) | ||
329 | */ | 73 | */ |
330 | struct MHD_Response * | 74 | int |
331 | MHD_create_response_from_callback(size_t size, | 75 | MHD_register_handler(struct MHD_Daemon * daemon, |
332 | MHD_ContentReaderCallback crc, | 76 | const char * uri_prefix, |
333 | void * crc_cls, | 77 | MHD_AccessHandlerCallback dh, |
334 | MHD_ContentReaderFreeCallback crfc) { | 78 | void * dh_cls) { |
335 | 79 | struct MHD_Access_Handler * ah; | |
336 | struct MHD_Response * retVal; | 80 | |
337 | int i; | 81 | if ( (daemon == NULL) || |
338 | 82 | (uri_prefix == NULL) || | |
339 | 83 | (dh == NULL) ) | |
340 | if(crc == NULL) { | 84 | return MHD_NO; |
341 | /* CG: printf! */ | 85 | ah = daemon->handlers; |
342 | fprintf(stderr, "A ContentReaderCallback must be provided to MHD_create_response_from_callback!\n"); | 86 | while (ah != NULL) { |
343 | return NULL; | 87 | if (0 == strcmp(uri_prefix, |
344 | } | 88 | ah->uri_prefix)) |
345 | 89 | return MHD_NO; | |
346 | retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response)); | 90 | ah = ah->next; |
347 | if(retVal == NULL) { | 91 | } |
348 | /* CG: printf, useless check, useless cast */ | 92 | ah = malloc(sizeof(MHD_AccessHandlerCallback)); |
349 | fprintf(stderr, "Error allocating memory!\n"); | 93 | ah->next = daemon->handlers; |
350 | return NULL; | 94 | ah->uri_prefix = strdup(uri_prefix); |
351 | } | 95 | ah->dh = dh; |
352 | 96 | ah->dh_cls = dh_cls; | |
353 | retVal->size = size; | 97 | daemon->handlers = ah; |
354 | 98 | return MHD_YES; | |
355 | retVal->crc = crc; | ||
356 | retVal->crc_cls = crc_cls; | ||
357 | |||
358 | retVal->crfc = crfc; | ||
359 | |||
360 | retVal->firstFreeHeader = 0; | ||
361 | |||
362 | retVal->responseCode = 0; | ||
363 | |||
364 | retVal->headersSent = 0; | ||
365 | |||
366 | retVal->bytesSentSoFar = 0; | ||
367 | |||
368 | retVal->freeWhenFinished = 0; | ||
369 | retVal->currentSession = NULL; | ||
370 | |||
371 | if(pthread_mutex_init(&retVal->mutex, NULL) != 0) { | ||
372 | fprintf(stderr, "Error initializing mutex!\n"); | ||
373 | free(retVal); | ||
374 | return NULL; | ||
375 | } | ||
376 | /* CG: use memset? linked list!!? */ | ||
377 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
378 | retVal->headers[i] = NULL; | ||
379 | } | ||
380 | |||
381 | retVal->data = NULL; | ||
382 | retVal->must_free = 0; | ||
383 | |||
384 | return retVal; | ||
385 | } | 99 | } |
386 | 100 | ||
387 | /** | ||
388 | * Create a response object. The response object can be extended with | ||
389 | * header information and then be used any number of times. | ||
390 | * | ||
391 | * @param size size of the data portion of the response | ||
392 | * @param data the data itself | ||
393 | * @param must_free libmicrohttpd should free data when done | ||
394 | * @param must_copy libmicrohttpd must make a copy of data | ||
395 | * right away, the data maybe released anytime after | ||
396 | * this call returns | ||
397 | * @return NULL on error (i.e. invalid arguments, out of memory) | ||
398 | */ | ||
399 | struct MHD_Response * | ||
400 | MHD_create_response_from_data(size_t size, | ||
401 | void * data, | ||
402 | int must_free, | ||
403 | int must_copy) { | ||
404 | |||
405 | struct MHD_Response * retVal; | ||
406 | int i; | ||
407 | |||
408 | |||
409 | if(data == NULL) { | ||
410 | fprintf(stderr, "data must be provided to MHD_Create_response_from_data!\n"); | ||
411 | return NULL; | ||
412 | } | ||
413 | |||
414 | retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response)); | ||
415 | if(retVal == NULL) { | ||
416 | fprintf(stderr, "Error allocating memory!\n"); | ||
417 | return NULL; | ||
418 | } | ||
419 | |||
420 | retVal->size = size; | ||
421 | |||
422 | retVal->crc = NULL; | ||
423 | retVal->crc_cls = NULL; | ||
424 | retVal->crfc = NULL; | ||
425 | |||
426 | retVal->responseCode = 0; | ||
427 | |||
428 | retVal->firstFreeHeader = 0; | ||
429 | retVal->freeWhenFinished = 0; | ||
430 | retVal->currentSession = NULL; | ||
431 | |||
432 | retVal->headersSent = 0; | ||
433 | |||
434 | retVal->bytesSentSoFar = 0; | ||
435 | |||
436 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
437 | retVal->headers[i] = NULL; | ||
438 | } | ||
439 | |||
440 | if(pthread_mutex_init(&retVal->mutex, NULL) != 0) { | ||
441 | fprintf(stderr, "Error initializing mutex!\n"); | ||
442 | free(retVal); | ||
443 | return NULL; | ||
444 | } | ||
445 | |||
446 | if(must_copy) { | ||
447 | retVal->data = malloc(size); | ||
448 | if(retVal->data == NULL) { | ||
449 | fprintf(stderr, "Error allocating memory!\n"); | ||
450 | free(retVal); | ||
451 | return NULL; | ||
452 | } | ||
453 | memcpy(retVal->data, data, size); | ||
454 | retVal->must_free = 1; | ||
455 | } else { | ||
456 | retVal->data = data; | ||
457 | retVal->must_free = must_free; | ||
458 | } | ||
459 | |||
460 | return retVal; | ||
461 | } | ||
462 | 101 | ||
463 | /** | 102 | /** |
464 | * Delete a header line from the response. | 103 | * Unregister an access handler for the URIs beginning with |
104 | * uri_prefix. | ||
465 | * | 105 | * |
466 | * @return MHD_NO on error (no such header known) | 106 | * @param uri_prefix |
467 | */ | 107 | * @return MHD_NO if a handler for this exact prefix |
468 | int | 108 | * is not known for this daemon |
469 | MHD_del_response_header(struct MHD_Response * response, | ||
470 | const char * header, | ||
471 | const char * content) { | ||
472 | int i; | ||
473 | |||
474 | if(header == NULL || content == NULL) { | ||
475 | return MHD_NO; | ||
476 | } | ||
477 | |||
478 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
479 | if(response->headers[i] != NULL && | ||
480 | strncmp(header, response->headers[i]->header, strlen(header)) == 0 && | ||
481 | strncmp(content, response->headers[i]->headerContent, strlen(header)) == 0) { | ||
482 | free(response->headers[i]->header); | ||
483 | free(response->headers[i]->headerContent); | ||
484 | free(response->headers[i]); | ||
485 | response->headers[i] = NULL; | ||
486 | return MHD_YES; | ||
487 | } | ||
488 | } | ||
489 | return MHD_NO; | ||
490 | } | ||
491 | |||
492 | /** | ||
493 | * Destroy a response object and associated resources. Note that | ||
494 | * libmicrohttpd may keep some of the resources around if the response | ||
495 | * is still in the queue for some clients, so the memory may not | ||
496 | * necessarily be freed immediatley. | ||
497 | */ | ||
498 | void | ||
499 | MHD_destroy_response(struct MHD_Response * response) { | ||
500 | int i; | ||
501 | |||
502 | if(response == NULL) { | ||
503 | return; | ||
504 | } | ||
505 | |||
506 | pthread_mutex_lock(&response->mutex); | ||
507 | |||
508 | if(response->currentSession != NULL) { | ||
509 | response->freeWhenFinished = 1; | ||
510 | pthread_mutex_unlock(&response->mutex); | ||
511 | return; | ||
512 | } | ||
513 | |||
514 | if(response->must_free && response->data != NULL) { | ||
515 | free(response->data); | ||
516 | } | ||
517 | |||
518 | if(response->crfc != NULL && response->crc_cls != NULL) { | ||
519 | response->crfc(response->crc_cls); | ||
520 | } | ||
521 | |||
522 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
523 | if(response->headers[i] == NULL) | ||
524 | continue; | ||
525 | |||
526 | free(response->headers[i]->header); | ||
527 | free(response->headers[i]->headerContent); | ||
528 | free(response->headers[i]); | ||
529 | } | ||
530 | |||
531 | pthread_mutex_unlock(&response->mutex); | ||
532 | pthread_mutex_destroy(&response->mutex); | ||
533 | |||
534 | free(response); | ||
535 | } | ||
536 | |||
537 | /** | ||
538 | * Thi function is similar to destroy_response except | ||
539 | * that it was created to destroy the session object. | ||
540 | */ | 109 | */ |
541 | void | 110 | int |
542 | MHD_destroy_session(struct MHD_Session * session) { | 111 | MHD_unregister_handler(struct MHD_Daemon * daemon, |
543 | int i; | 112 | const char * uri_prefix, |
544 | 113 | MHD_AccessHandlerCallback dh, | |
545 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | 114 | void * dh_cls) { |
546 | if(session->headers[i] != NULL) { | 115 | struct MHD_Access_Handler * prev; |
547 | free(session->headers[i]); | 116 | struct MHD_Access_Handler * pos; |
548 | } | 117 | |
549 | } | 118 | if ( (daemon == NULL) || |
550 | 119 | (uri_prefix == NULL) || | |
551 | for(i = 0; i < MHD_MAX_RESPONSE; i++) { | 120 | (dh == NULL) ) |
552 | if(session->currentResponses[i] != NULL) { | 121 | return MHD_NO; |
553 | pthread_mutex_lock(&session->currentResponses[i]->mutex); | 122 | pos = daemon->handlers; |
554 | session->currentResponses[i]->currentSession = NULL; | 123 | prev = NULL; |
555 | pthread_mutex_unlock(&session->currentResponses[i]->mutex); | 124 | while (pos != NULL) { |
556 | } | 125 | if ( (dh == ah->dh) && |
557 | } | 126 | (dh_cls == ah->dh_cls) && |
558 | 127 | (0 == strcmp(uri_prefix, | |
559 | close(session->socket_fd); | 128 | ah->uri_prefix)) ) { |
560 | free(session); | 129 | if (prev == NULL) |
130 | daemon->handlers = pos->next; | ||
131 | else | ||
132 | prev->next = pos->next; | ||
133 | free(pos); | ||
134 | return MHD_YES; | ||
135 | } | ||
136 | prev = pos; | ||
137 | pos = pos->next; | ||
138 | } | ||
139 | return MHD_NO; | ||
561 | } | 140 | } |
562 | 141 | ||
563 | /** | 142 | /** |
@@ -573,576 +152,233 @@ MHD_get_fdset(struct MHD_Daemon * daemon, | |||
573 | fd_set * write_fd_set, | 152 | fd_set * write_fd_set, |
574 | fd_set * except_fd_set, | 153 | fd_set * except_fd_set, |
575 | int * max_fd) { | 154 | int * max_fd) { |
576 | 155 | struct MHD_Session * pos; | |
577 | int i; | 156 | |
578 | 157 | if ( (daemon == NULL) || | |
579 | if(daemon == NULL || read_fd_set == NULL || write_fd_set == NULL || except_fd_set == NULL || max_fd == NULL) { | 158 | (read_fd_set == NULL) || |
580 | return MHD_NO; | 159 | (write_fd_set == NULL) || |
581 | } | 160 | (except_fd_set == NULL) || |
582 | 161 | (max_fd == NULL) || | |
583 | if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { | 162 | ( (daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) ) |
584 | return MHD_NO; | 163 | return MHD_NO; |
585 | } | 164 | FD_SET(daemon->socket_fd, |
586 | 165 | &daemon->read_fd_set); | |
587 | FD_ZERO(read_fd_set); | 166 | if ( (*max_fd) < daemon->socket_fd) |
588 | FD_ZERO(write_fd_set); | 167 | *max_fd = daemon->socket_fd; |
589 | FD_ZERO(except_fd_set); | 168 | pos = daemon->session; |
590 | 169 | while (pos != NULL) { | |
591 | FD_SET(daemon->socket_fd, &daemon->read_fd_set); | 170 | if (MHD_YES != MHD_session_get_fdset(pos, |
592 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | 171 | read_fd_set, |
593 | if(daemon->connections[i] != NULL) { | 172 | write_fd_set, |
594 | FD_SET(daemon->connections[i]->socket_fd, read_fd_set); | 173 | except_fd_set, |
595 | FD_SET(daemon->connections[i]->socket_fd, write_fd_set); | 174 | max_fd)) |
596 | } | 175 | return MHD_NO; |
597 | } | 176 | pos = pos->next; |
598 | 177 | } | |
599 | *max_fd = daemon->max_fd; | 178 | return MHD_YES; |
600 | |||
601 | return MHD_YES; | ||
602 | } | ||
603 | |||
604 | /** | ||
605 | * Get all of the headers added to a response. | ||
606 | * | ||
607 | * @param iterator callback to call on each header; | ||
608 | * maybe NULL (then just count headers) | ||
609 | * @param iterator_cls extra argument to iterator | ||
610 | * @return number of entries iterated over | ||
611 | */ | ||
612 | int | ||
613 | MHD_get_response_headers(struct MHD_Response * response, | ||
614 | MHD_KeyValueIterator * iterator, | ||
615 | void * iterator_cls) { | ||
616 | int i, numHeaders; | ||
617 | |||
618 | if(response == NULL) { | ||
619 | return -1; | ||
620 | } | ||
621 | |||
622 | numHeaders = 0; | ||
623 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
624 | if(response->headers[i] != NULL) { | ||
625 | if(iterator != NULL) { | ||
626 | (*iterator)(iterator_cls, response->headers[i]->kind, response->headers[i]->header, response->headers[i]->headerContent); | ||
627 | } | ||
628 | numHeaders++; | ||
629 | } | ||
630 | } | ||
631 | return numHeaders; | ||
632 | } | 179 | } |
633 | 180 | ||
634 | /** | ||
635 | * Get all of the headers from the request. | ||
636 | * | ||
637 | * @param iterator callback to call on each header; | ||
638 | * maybe NULL (then just count headers) | ||
639 | * @param iterator_cls extra argument to iterator | ||
640 | * @return number of entries iterated over | ||
641 | */ | ||
642 | int | ||
643 | MHD_get_session_values(struct MHD_Session * session, | ||
644 | enum MHD_ValueKind kind, | ||
645 | MHD_KeyValueIterator * iterator, | ||
646 | void * iterator_cls) { | ||
647 | int i, numHeaders; | ||
648 | |||
649 | if(session == NULL) { | ||
650 | return -1; | ||
651 | } | ||
652 | numHeaders = 0; | ||
653 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
654 | if(session->headers[i] != NULL && session->headers[i]->kind == kind) { | ||
655 | if(iterator != NULL) { | ||
656 | (*iterator)(iterator_cls, session->headers[i]->kind, session->headers[i]->header, session->headers[i]->headerContent); | ||
657 | } | ||
658 | numHeaders++; | ||
659 | } | ||
660 | } | ||
661 | return numHeaders; | ||
662 | } | ||
663 | 181 | ||
664 | /** | 182 | /** |
665 | * This function is intented to be called in the case of | 183 | * Main function of the thread that handles an individual |
666 | * multithreaded connections. A thread will be spawned calling this | 184 | * connection. |
667 | * function with a particular connection, and the thread will poll the connection | ||
668 | * (this should be improved) until there is something to do | ||
669 | */ | 185 | */ |
670 | void * | 186 | static void * |
671 | MHD_handle_connection(void * data) { | 187 | MHD_handle_connection(void * data) { |
672 | struct MHD_Session * con; | 188 | struct MHD_Session * con = data; |
673 | int num_ready; | 189 | int num_ready; |
674 | struct timeval timeout; | 190 | fd_set rs; |
675 | fd_set read; | 191 | fd_set ws; |
676 | fd_set write; | 192 | fd_set es; |
677 | 193 | int max; | |
678 | con = data; | 194 | |
679 | 195 | if (con == NULL) | |
680 | if(con == NULL) | 196 | abort(); |
681 | return NULL; | 197 | while (! con->daemon->shutdown) { |
682 | 198 | FD_ZERO(&rs); | |
683 | do { | 199 | FD_ZERO(&ws); |
684 | 200 | FD_ZERO(&es); | |
685 | FD_ZERO(&read); | 201 | max = 0; |
686 | FD_ZERO(&write); | 202 | MHD_session_get_fdset(con, |
687 | 203 | &rs, | |
688 | FD_SET(con->socket_fd, &read); | 204 | &ws, |
689 | FD_SET(con->socket_fd, &write); | 205 | &es, |
690 | 206 | &max); | |
691 | timeout.tv_sec = 0; | 207 | num_ready = select(max + 1, |
692 | timeout.tv_usec = 0; | 208 | &rs, |
693 | 209 | &ws, | |
694 | num_ready = select(con->socket_fd + 1, | 210 | &es, |
695 | &read, &write, NULL, &timeout); | 211 | NULL); |
696 | 212 | if (num_ready <= 0) { | |
697 | if(num_ready > 0) { | 213 | if (errno == EINTR) |
698 | if(FD_ISSET(con->socket_fd, &read)) { | 214 | continue; |
699 | if(MHD_handle_read(con->id, con->daemon) == MHD_NO) { | 215 | break; |
700 | pthread_detach(pthread_self()); | 216 | } |
701 | return NULL; | 217 | if ( ( (FD_ISSET(con->socket_fd, &rs)) && |
702 | } | 218 | (MHD_YES != MHD_session_handle_read(con)) ) || |
703 | } | 219 | ( (FD_ISSET(con->socket_fd, &ws)) && |
704 | if (FD_ISSET(con->socket_fd, &write)) { | 220 | (MHD_YES != MHD_session_handle_write(con)) ) ) |
705 | if(MHD_handle_write(con->id, con->daemon) == MHD_NO) { | 221 | break; |
706 | pthread_detach(pthread_self()); | 222 | } |
707 | return NULL; | 223 | close(con->socket_fd); |
708 | } | 224 | con->socket_fd = -1; |
709 | } | 225 | return NULL; |
710 | } | ||
711 | } while (!con->daemon->shutdown); | ||
712 | |||
713 | return NULL; | ||
714 | } | ||
715 | |||
716 | /** | ||
717 | * This function is created to handle the except file descriptor | ||
718 | * set, but it is doubtfull that it will ever be used. | ||
719 | */ | ||
720 | void | ||
721 | MHD_handle_except(int connection_id, struct MHD_Daemon * daemon) { | ||
722 | //It is unlikely that this function will ever need to be implemented. | ||
723 | } | 226 | } |
724 | 227 | ||
725 | /** | ||
726 | * This function handles a particular connection when it has been | ||
727 | * determined that there is data to be read off a socket. All implementations | ||
728 | * (multithreaded, external select, internal select) call this function | ||
729 | * to handle reads. | ||
730 | */ | ||
731 | int | ||
732 | MHD_handle_read(int connection_id, struct MHD_Daemon * daemon) { | ||
733 | int bytes_read,i; | ||
734 | |||
735 | if((daemon->options & MHD_USE_DEBUG) != 0) { | ||
736 | fprintf(stderr, "Enter MHD_handle_read\n"); | ||
737 | } | ||
738 | |||
739 | if(daemon == NULL || daemon->connections[connection_id]==NULL) { | ||
740 | return MHD_NO; | ||
741 | } | ||
742 | |||
743 | if(daemon->connections[connection_id]->responsePending) { | ||
744 | return MHD_YES; | ||
745 | } | ||
746 | |||
747 | daemon->connections[connection_id]->firstFreeHeader = 0; | ||
748 | daemon->connections[connection_id]->requestType = NULL; | ||
749 | |||
750 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
751 | daemon->connections[connection_id]->headers[i] = NULL; | ||
752 | } | ||
753 | |||
754 | |||
755 | |||
756 | memmove(daemon->connections[connection_id]->inbuf, daemon->connections[connection_id]->inbuf+daemon->connections[connection_id]->messagePos, daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos); | ||
757 | |||
758 | memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, | ||
759 | 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos)); | ||
760 | |||
761 | bytes_read = recv(daemon->connections[connection_id]->socket_fd, | ||
762 | daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, | ||
763 | MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0); | ||
764 | |||
765 | daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos; | ||
766 | |||
767 | if(bytes_read == 0) { | ||
768 | MHD_destroy_session(daemon->connections[connection_id]); | ||
769 | daemon->connections[connection_id] = NULL; | ||
770 | return MHD_NO; | ||
771 | } else { | ||
772 | fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf); | ||
773 | i = MHD_parse_message(daemon->connections[connection_id]); | ||
774 | if(i == -1) { | ||
775 | daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos; | ||
776 | return MHD_YES; | ||
777 | } else { | ||
778 | daemon->connections[connection_id]->messagePos = i; | ||
779 | fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos); | ||
780 | } | ||
781 | |||
782 | daemon->connections[connection_id]->responsePending = 1; | ||
783 | |||
784 | MHD_parse_URL(daemon->connections[connection_id]); | ||
785 | |||
786 | for(i = 0; i < MHD_MAX_HANDLERS; i++) { | ||
787 | if(daemon->handlers[i] == NULL) | ||
788 | continue; | ||
789 | |||
790 | //header 0 will hold the url of the request | ||
791 | if(strstr(daemon->connections[connection_id]->headers[0]->headerContent, daemon->handlers[i]->uri_prefix) != NULL){ | ||
792 | return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id], | ||
793 | daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); | ||
794 | } | ||
795 | } | ||
796 | return daemon->dh(daemon->dh_cls, daemon->connections[connection_id], | ||
797 | daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); | ||
798 | } | ||
799 | |||
800 | return MHD_YES; | ||
801 | } | ||
802 | 228 | ||
803 | /** | 229 | /** |
804 | * This function was created to handle writes to sockets when it has been | 230 | * Accept an incoming connection and create the MHD_Session object for |
805 | * determined that the socket can be written to. If there is no data | 231 | * it. This function also enforces policy by way of checking with the |
806 | * to be written, however, the function call does nothing. All implementations | 232 | * accept policy callback. |
807 | * (multithreaded, external select, internal select) call this function | ||
808 | */ | 233 | */ |
809 | int | 234 | static int |
810 | MHD_handle_write(int connection_id, struct MHD_Daemon * daemon) { | 235 | MHD_accept_connection(struct MHD_Daemon * daemon) { |
811 | struct MHD_Session * session; | 236 | struct MHD_Session * session; |
812 | 237 | struct sockaddr addr; | |
813 | struct MHD_Response * response; | 238 | socklen_t addrlen; |
814 | 239 | int s; | |
815 | int i; | 240 | |
816 | 241 | addrlen = sizeof(struct sockaddr); | |
817 | char * buffer[2048]; | 242 | memset(&addr, |
818 | 243 | 0, | |
819 | char * responseMessage; | 244 | sizeof(struct sockaddr)); |
820 | int numBytesInMessage; | 245 | if ( (0 != (s = accept(daemon->socket_fd, |
821 | 246 | &addr, | |
822 | if((daemon->options & MHD_USE_DEBUG) != 0) { | 247 | &addrlen))) || |
823 | fprintf(stderr, "Enter MHD_handle_write\n"); | 248 | (addrlen <= 0) ) { |
824 | } | 249 | DLOG(daemon, |
825 | 250 | "Error accepting connection: %s\n", | |
826 | 251 | strerror(errno)); | |
827 | session = daemon->connections[connection_id]; | 252 | return MHD_NO; |
828 | 253 | } | |
829 | response = session->currentResponses[session->currentResponse]; | 254 | if (MHD_NO == daemon->apc(mhd->apc_cls, |
830 | 255 | &addr, | |
831 | numBytesInMessage = 25; | 256 | addrlen)) { |
832 | 257 | close(s); | |
833 | responseMessage = malloc(25); | 258 | return MHD_YES; |
834 | if(responseMessage == NULL) { | 259 | } |
835 | if(daemon->options & MHD_USE_DEBUG) | 260 | session = malloc(sizeof(struct MHD_Session)); |
836 | fprintf(stderr, "Error allocating memory!\n"); | 261 | memset(session, |
837 | return MHD_NO; | 262 | 0, |
838 | } | 263 | sizeof(struct MHD_Session)); |
839 | 264 | session->addr = malloc(addrlen); | |
840 | if(response == NULL) | 265 | memcpy(session->addr, |
841 | return MHD_NO; | 266 | &addr, |
842 | 267 | addrlen); | |
843 | pthread_mutex_lock(&response->mutex); | 268 | session->addr_len = addrlen; |
844 | 269 | session->socket_fd = s; | |
845 | if(!response->headersSent) { | 270 | session->daemon = daemon; |
846 | sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode); | 271 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) && |
847 | fprintf(stderr, "%s\n", responseMessage); | 272 | (0 != pthread_create(&session->pid, |
848 | if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { | 273 | NULL, |
849 | fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); | 274 | &MHD_handle_connection, |
850 | pthread_mutex_unlock(&response->mutex); | 275 | session)) ) { |
851 | return MHD_NO; | 276 | DLOG(daemon, |
852 | } | 277 | "Failed to create a thread: %s\n", |
853 | 278 | strerror(errno)); | |
854 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | 279 | free(session->addr); |
855 | if(response->headers[i] == NULL) | 280 | close(s); |
856 | continue; | 281 | free(session); |
857 | 282 | return MHD_NO; | |
858 | if(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5 > numBytesInMessage) { | 283 | } |
859 | free(responseMessage); | 284 | session->next = daemon->connections; |
860 | responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5); | 285 | daemon->connections = session; |
861 | if(responseMessage == NULL) { | 286 | return MHD_YES; |
862 | if(daemon->options & MHD_USE_DEBUG) | ||
863 | fprintf(stderr, "Error allocating memory!\n"); | ||
864 | pthread_mutex_unlock(&response->mutex); | ||
865 | return MHD_NO; | ||
866 | } | ||
867 | numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5; | ||
868 | } | ||
869 | sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->headerContent); | ||
870 | fprintf(stderr, "%s\n", responseMessage); | ||
871 | if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { | ||
872 | fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); | ||
873 | pthread_mutex_unlock(&response->mutex); | ||
874 | return MHD_NO; | ||
875 | } | ||
876 | } | ||
877 | |||
878 | response->headersSent = 1; | ||
879 | } | ||
880 | |||
881 | if(response->data != NULL) { | ||
882 | if(response->bytesSentSoFar == 0) { | ||
883 | if(numBytesInMessage < 32) { | ||
884 | free(responseMessage); | ||
885 | responseMessage = malloc(32); | ||
886 | if(responseMessage == NULL) { | ||
887 | if(daemon->options & MHD_USE_DEBUG) | ||
888 | fprintf(stderr, "Error allocating memory!\n"); | ||
889 | pthread_mutex_unlock(&response->mutex); | ||
890 | return MHD_NO; | ||
891 | } | ||
892 | } | ||
893 | sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size); | ||
894 | fprintf(stderr, "%s\n", responseMessage); | ||
895 | if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) { | ||
896 | fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); | ||
897 | pthread_mutex_unlock(&response->mutex); | ||
898 | return MHD_NO; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0); | ||
903 | response->bytesSentSoFar += i; | ||
904 | |||
905 | fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size); | ||
906 | |||
907 | if(response->bytesSentSoFar == response->size) { | ||
908 | session->currentResponses[session->currentResponse] = NULL; | ||
909 | session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; | ||
910 | response->currentSession = NULL; | ||
911 | |||
912 | if(response->freeWhenFinished) { | ||
913 | pthread_mutex_unlock(&response->mutex); | ||
914 | MHD_destroy_response(response); | ||
915 | } | ||
916 | /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work | ||
917 | * since client never disconnects on their own! | ||
918 | */ | ||
919 | if(session->currentResponses[session->currentResponse] == NULL) { | ||
920 | MHD_destroy_session(session); | ||
921 | daemon->connections[connection_id] = NULL; | ||
922 | return MHD_NO; | ||
923 | } | ||
924 | } | ||
925 | } else { | ||
926 | if(response->crc == NULL) { | ||
927 | pthread_mutex_unlock(&response->mutex); | ||
928 | return MHD_NO; | ||
929 | } | ||
930 | |||
931 | if(response->bytesSentSoFar == 0) { | ||
932 | if(send(session->socket_fd, "\r\n", response->size,0) != 2) { | ||
933 | fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); | ||
934 | pthread_mutex_unlock(&response->mutex); | ||
935 | return MHD_NO; | ||
936 | } | ||
937 | } | ||
938 | memset(buffer, 0, 2048); | ||
939 | |||
940 | i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048); | ||
941 | |||
942 | if(i == -1) { | ||
943 | pthread_mutex_unlock(&response->mutex); | ||
944 | |||
945 | session->currentResponses[session->currentResponse] = NULL; | ||
946 | session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; | ||
947 | response->currentSession = NULL; | ||
948 | |||
949 | if(response->freeWhenFinished) { | ||
950 | pthread_mutex_unlock(&response->mutex); | ||
951 | MHD_destroy_response(response); | ||
952 | } | ||
953 | /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work | ||
954 | * since client never disconnects on their own! | ||
955 | */ | ||
956 | if(session->currentResponses[session->currentResponse] == NULL) { | ||
957 | MHD_destroy_session(session); | ||
958 | daemon->connections[connection_id] = NULL; | ||
959 | return MHD_NO; | ||
960 | } | ||
961 | |||
962 | } else { | ||
963 | i = send(session->socket_fd, buffer, i,0); | ||
964 | response->bytesSentSoFar += i; | ||
965 | } | ||
966 | } | ||
967 | pthread_mutex_unlock(&response->mutex); | ||
968 | return MHD_YES; | ||
969 | } | ||
970 | |||
971 | |||
972 | /** | ||
973 | * Get a particular header value. If multiple | ||
974 | * values match the kind, return any one of them. | ||
975 | * | ||
976 | * @param key the header to look for | ||
977 | * @return NULL if no such item was found | ||
978 | */ | ||
979 | const char * | ||
980 | MHD_lookup_session_value(struct MHD_Session * session, | ||
981 | enum MHD_ValueKind kind, | ||
982 | const char * key) { | ||
983 | int i; | ||
984 | |||
985 | for(i = 0; i < MHD_MAX_HEADERS; i++) { | ||
986 | if(session->headers[i] != NULL && | ||
987 | session->headers[i]->kind == kind && | ||
988 | strncmp(session->headers[i]->header, key, strlen(session->headers[i]->header)) == 0) { | ||
989 | return (const char *)session->headers[i]->headerContent; | ||
990 | } | ||
991 | } | ||
992 | |||
993 | return NULL; | ||
994 | } | 287 | } |
995 | 288 | ||
996 | 289 | ||
997 | /** | 290 | /** |
998 | * This function is designed to parse the input buffer of a given session. | 291 | * Free resources associated with all closed sessions. |
999 | * It is assumed that the data being parsed originates at buffer location | 292 | * (destroy responses, free buffers, etc.). A session |
1000 | * 0 (a valid assumption since the buffer is shifted after each message) | 293 | * is known to be closed if the socket_fd is -1. |
1001 | */ | 294 | */ |
1002 | int | 295 | static void |
1003 | MHD_parse_message(struct MHD_Session * session) { | 296 | MHD_cleanup_sessions(struct MHD_Daemon * daemon) { |
1004 | const char * crlfcrlf = "\r\n\r\n"; | 297 | struct MHD_Session * pos; |
1005 | const char * crlf = "\r\n"; | 298 | struct MHD_Session * prev; |
1006 | 299 | ||
1007 | char * saveptr; | 300 | pos = daemon->connections; |
1008 | char * saveptr1; | 301 | prev = NULL; |
1009 | 302 | while (pos != NULL) { | |
1010 | struct MHD_HTTP_Header * newHeader; | 303 | if (pos->socket_fd == -1) { |
1011 | char * curTok; | 304 | if (prev == NULL) |
1012 | char * curTok1; | 305 | daemon->connections = pos->next; |
1013 | 306 | else | |
1014 | int numBytes; | 307 | prev->next = pos->next; |
1015 | 308 | free(pos->addr); | |
1016 | curTok = strstr(session->inbuf, crlfcrlf); | 309 | /* FIXME: more to free here! */ |
1017 | 310 | free(pos); | |
1018 | if(curTok == NULL) { | 311 | } |
1019 | return -1; | 312 | prev = pos; |
1020 | } | 313 | pos = pos->next; |
1021 | 314 | } | |
1022 | memset(curTok+2, 0, 2); | ||
1023 | |||
1024 | numBytes = strlen(session->inbuf) + 2; | ||
1025 | |||
1026 | curTok = strtok_r(session->inbuf, crlf, &saveptr); | ||
1027 | |||
1028 | session->requestType = strtok_r(curTok, " ", &saveptr1); | ||
1029 | |||
1030 | newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); | ||
1031 | if(newHeader == NULL) { | ||
1032 | if(session->daemon->options & MHD_USE_DEBUG) | ||
1033 | fprintf(stderr, "Error allocating memory!\n"); | ||
1034 | return -1; | ||
1035 | } | ||
1036 | newHeader->kind = MHD_GET_ARGUMENT_KIND; | ||
1037 | newHeader->header = session->requestType; | ||
1038 | newHeader->headerContent = strtok_r(NULL, " ", &saveptr1); | ||
1039 | |||
1040 | session->headers[session->firstFreeHeader++] = newHeader; | ||
1041 | |||
1042 | curTok = strtok_r(NULL, crlf, &saveptr); | ||
1043 | while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) { | ||
1044 | curTok1 = strtok_r(curTok, ":", &saveptr1); | ||
1045 | newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); | ||
1046 | if(newHeader == NULL) { | ||
1047 | if(session->daemon->options & MHD_USE_DEBUG) | ||
1048 | fprintf(stderr, "Error allocating memory!\n"); | ||
1049 | return -1; | ||
1050 | } | ||
1051 | newHeader->header = curTok1; | ||
1052 | newHeader->headerContent = curTok + strlen(curTok1) + 2; | ||
1053 | //For now, everything is a get! | ||
1054 | newHeader->kind = MHD_GET_ARGUMENT_KIND; | ||
1055 | session->headers[session->firstFreeHeader++] = newHeader; | ||
1056 | curTok = strtok_r(NULL, crlf, &saveptr); | ||
1057 | } | ||
1058 | |||
1059 | return numBytes; | ||
1060 | } | 315 | } |
1061 | 316 | ||
1062 | /** | ||
1063 | * This function needs to do a lot more (i.e. break up get arguments) | ||
1064 | * but for now just seperates the prefix of the url from the document | ||
1065 | * portion. | ||
1066 | */ | ||
1067 | void | ||
1068 | MHD_parse_URL(struct MHD_Session * session) { | ||
1069 | char * working; | ||
1070 | int pos,i; | ||
1071 | |||
1072 | working = session->headers[0]->headerContent; | ||
1073 | |||
1074 | pos = 0; | ||
1075 | for(i = 0; i < strlen(working); i++) { | ||
1076 | if(working[i] == '/') | ||
1077 | pos = i+1; | ||
1078 | } | ||
1079 | if(pos >= strlen(working)) | ||
1080 | pos = 0; | ||
1081 | |||
1082 | session->documentName = session->headers[0]->headerContent+pos; | ||
1083 | } | ||
1084 | 317 | ||
1085 | /** | 318 | /** |
1086 | * Queue a response to be transmitted to the client (as soon as | 319 | * Main select call. |
1087 | * possible). | 320 | * |
1088 | * | 321 | * @return MHD_NO on serious errors, MHD_YES on success |
1089 | * @param session the session identifying the client | ||
1090 | * @param status_code HTTP status code (i.e. 200 for OK) | ||
1091 | * @param response response to transmit | ||
1092 | * @return MHD_NO on error (i.e. reply already sent), | ||
1093 | * MHD_YES on success or if message has been queued | ||
1094 | */ | 322 | */ |
1095 | int | 323 | static int |
1096 | MHD_queue_response(struct MHD_Session * session, | 324 | MHD_select(struct MHD_Daemon * daemon) { |
1097 | unsigned int status_code, | 325 | struct MHD_Session * pos; |
1098 | struct MHD_Response * response) { | 326 | int num_ready; |
1099 | 327 | fd_set rs; | |
1100 | //As of now this function can only support a fixed amount of queued responses, and will | 328 | fd_set ws; |
1101 | //return MHD_NO if that queue is full | 329 | fd_set es; |
1102 | 330 | int max; | |
1103 | int index; | 331 | |
1104 | 332 | if(daemon == NULL) | |
1105 | if(session == NULL || response == NULL) { | 333 | abort(); |
1106 | return MHD_NO; | 334 | FD_ZERO(&rs); |
1107 | } | 335 | FD_ZERO(&ws); |
1108 | 336 | FD_ZERO(&es); | |
1109 | pthread_mutex_lock(&response->mutex); | 337 | max = 0; |
1110 | 338 | ||
1111 | if(response->currentSession != NULL) { | 339 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { |
1112 | return MHD_NO; | 340 | /* single-threaded, go over everything */ |
1113 | } | 341 | if (MHD_NO == MHD_get_fdset(daemon, |
1114 | 342 | &rs, | |
1115 | if(session->currentResponses[session->currentResponse] == NULL) { | 343 | &ws, |
1116 | index = session->currentResponse; | 344 | &es, |
1117 | } else if(session->currentResponses[session->currentResponse + 1 % MHD_MAX_RESPONSE] == NULL) { | 345 | &max)) |
1118 | index = session->currentResponse + 1 % MHD_MAX_RESPONSE; | 346 | return MHD_NO; |
1119 | } else { | 347 | } else { |
1120 | pthread_mutex_unlock(&response->mutex); | 348 | /* accept only, have one thread per connection */ |
1121 | return MHD_NO; | 349 | max = daemon->socket_fd; |
1122 | } | 350 | FD_SET(daemon->socket_fd, &rs); |
1123 | 351 | } | |
1124 | response->responseCode = status_code; | 352 | num_ready = select(max + 1, |
1125 | session->currentResponses[index] = response; | 353 | &rs, |
1126 | response->currentSession = session; | 354 | &ws, |
1127 | session->responsePending = 0; | 355 | &es, |
1128 | 356 | NULL); | |
1129 | pthread_mutex_unlock(&response->mutex); | 357 | if (num_ready < 0) { |
1130 | 358 | if (errno == EINTR) | |
1131 | return MHD_YES; | 359 | return MHD_YES; |
360 | DLOG(daemon, | ||
361 | "Select failed: %s\n", | ||
362 | strerror(errno)); | ||
363 | return MHD_NO; | ||
364 | } | ||
365 | if (FD_ISSET(daemon->socket_fd, | ||
366 | &rs)) | ||
367 | MHD_accept_connection(daemon); | ||
368 | if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { | ||
369 | /* do not have a thread per connection, process all connections now */ | ||
370 | pos = daemon->connections; | ||
371 | while (pos != NULL) { | ||
372 | if (FD_ISSET(pos->socket_fd, &rs)) | ||
373 | MHD_session_handle_read(pos); | ||
374 | if (FD_ISSET(pos->socket_fd, &ws)) | ||
375 | MHD_session_handle_write(pos); | ||
376 | pos = pos->next; | ||
377 | } | ||
378 | } | ||
379 | return MHD_YES; | ||
1132 | } | 380 | } |
1133 | 381 | ||
1134 | /** | ||
1135 | * @return -1 if no data uploaded; otherwise number of bytes | ||
1136 | * read into buf; 0 for end of transmission | ||
1137 | * Specification not complete at this time. | ||
1138 | */ | ||
1139 | int | ||
1140 | MHD_read_file_upload(struct MHD_Session * session, | ||
1141 | void * buf, | ||
1142 | size_t len) { | ||
1143 | //This function will not be implemented until the specification is completed. | ||
1144 | return -1; | ||
1145 | } | ||
1146 | 382 | ||
1147 | /** | 383 | /** |
1148 | * Run webserver operations (without blocking unless | 384 | * Run webserver operations (without blocking unless |
@@ -1156,190 +392,34 @@ MHD_read_file_upload(struct MHD_Session * session, | |||
1156 | */ | 392 | */ |
1157 | int | 393 | int |
1158 | MHD_run(struct MHD_Daemon * daemon) { | 394 | MHD_run(struct MHD_Daemon * daemon) { |
1159 | 395 | if ( (daemon->shutdown != 0) || | |
1160 | if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { | 396 | (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || |
1161 | daemon->shutdown = 0; | 397 | (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) ) |
1162 | if(pthread_create(&daemon->pid, NULL, (void *) &MHD_spawn_connections, (void *)daemon) == 0) { | 398 | return MHD_NO; |
1163 | return MHD_YES; | 399 | MHD_select(daemon); |
1164 | } else { | 400 | MHD_cleanup_sessions(daemon); |
1165 | return MHD_NO; | 401 | return MHD_YES; |
1166 | } | ||
1167 | } else if((daemon->options & MHD_USE_SELECT_INTERNALLY) != 0) { | ||
1168 | daemon->shutdown = 0; | ||
1169 | if(pthread_create(&daemon->pid, NULL, (void *) &MHD_select, (void *)daemon) == 0) { | ||
1170 | return MHD_YES; | ||
1171 | } else { | ||
1172 | return MHD_NO; | ||
1173 | } | ||
1174 | } else { | ||
1175 | daemon->shutdown = 1; | ||
1176 | return (MHD_select((void *)daemon) == NULL); | ||
1177 | } | ||
1178 | } | 402 | } |
1179 | 403 | ||
1180 | 404 | ||
1181 | /** | 405 | /** |
1182 | * Register an access handler for all URIs beginning with uri_prefix. | 406 | * Thread that runs the select loop until the daemon |
1183 | * | 407 | * is explicitly shut down. |
1184 | * @param uri_prefix | ||
1185 | * @return MRI_NO if a handler for this exact prefix | ||
1186 | * already exists | ||
1187 | */ | 408 | */ |
1188 | int | 409 | static void * |
1189 | MHD_register_handler(struct MHD_Daemon * daemon, | 410 | MHD_select_thread(void * cls) { |
1190 | const char * uri_prefix, | 411 | struct MHD_Daemon * daemon = cls; |
1191 | MHD_AccessHandlerCallback dh, | 412 | while (daemon->shutdown == 0) { |
1192 | void * dh_cls) { | 413 | MHD_select(daemon); |
1193 | int i; | 414 | MHD_cleanup_sessions(daemon); |
1194 | 415 | } | |
1195 | //This function will also return MHD_NO if the maximum number of supported handlers is exceeded | 416 | return NULL; |
1196 | |||
1197 | if(daemon == NULL || uri_prefix == NULL || dh == NULL) { | ||
1198 | return MHD_NO; | ||
1199 | } | ||
1200 | |||
1201 | if(daemon->firstFreeHandler >= MHD_MAX_HANDLERS) { | ||
1202 | return MHD_NO; | ||
1203 | } | ||
1204 | |||
1205 | daemon->handlers[daemon->firstFreeHandler] = malloc(sizeof(struct MHD_Access_Handler)); | ||
1206 | |||
1207 | if(daemon->handlers[daemon->firstFreeHandler] == NULL) { | ||
1208 | if((daemon->options & MHD_USE_DEBUG) != 0) | ||
1209 | fprintf(stderr, "Error allocating memory!\n"); | ||
1210 | return MHD_NO; | ||
1211 | } | ||
1212 | |||
1213 | daemon->handlers[daemon->firstFreeHandler]->uri_prefix = malloc(strlen(uri_prefix)+1); | ||
1214 | if(daemon->handlers[daemon->firstFreeHandler]->uri_prefix == NULL) { | ||
1215 | if((daemon->options & MHD_USE_DEBUG) != 0) { | ||
1216 | free(daemon->handlers[daemon->firstFreeHandler]); | ||
1217 | fprintf(stderr, "Error allocating memory!\n"); | ||
1218 | } | ||
1219 | return MHD_NO; | ||
1220 | } | ||
1221 | sprintf(daemon->handlers[daemon->firstFreeHandler]->uri_prefix, "%s", uri_prefix); | ||
1222 | |||
1223 | daemon->handlers[daemon->firstFreeHandler]->dh = dh; | ||
1224 | daemon->handlers[daemon->firstFreeHandler]->dh_cls = dh_cls; | ||
1225 | |||
1226 | daemon->firstFreeHandler = MHD_MAX_HANDLERS; | ||
1227 | for(i = 0; i < MHD_MAX_HANDLERS; i++) { | ||
1228 | if(daemon->handlers[i] == NULL) { | ||
1229 | daemon->firstFreeHandler = i; | ||
1230 | break; | ||
1231 | } | ||
1232 | } | ||
1233 | |||
1234 | return MHD_YES; | ||
1235 | |||
1236 | } | 417 | } |
1237 | 418 | ||
1238 | /** | ||
1239 | * This function is the entry point for either internal or external select. | ||
1240 | * The only differences between the two forms of running is whether the call is | ||
1241 | * made from a new thread or the main thread, and whether the initial value | ||
1242 | * of shutdown is 0 or 1 (1 for loop, 0 for one time pass) | ||
1243 | */ | ||
1244 | void * | ||
1245 | MHD_select(void * data) { | ||
1246 | struct MHD_Daemon * daemon; | ||
1247 | int i, num_ready; | ||
1248 | struct timeval timeout; | ||
1249 | |||
1250 | daemon = data; | ||
1251 | if(daemon == NULL) { | ||
1252 | return NULL; | ||
1253 | } | ||
1254 | do { | ||
1255 | FD_ZERO(&daemon->read_fd_set); | ||
1256 | FD_ZERO(&daemon->write_fd_set); | ||
1257 | FD_ZERO(&daemon->except_fd_set); | ||
1258 | |||
1259 | FD_SET(daemon->socket_fd, &daemon->read_fd_set); | ||
1260 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | ||
1261 | if(daemon->connections[i] != NULL) { | ||
1262 | FD_SET(daemon->connections[i]->socket_fd, &daemon->read_fd_set); | ||
1263 | FD_SET(daemon->connections[i]->socket_fd, &daemon->write_fd_set); | ||
1264 | } | ||
1265 | } | ||
1266 | |||
1267 | timeout.tv_sec = 0; | ||
1268 | timeout.tv_usec = 0; | ||
1269 | |||
1270 | num_ready = select(daemon->max_fd + 1, | ||
1271 | &(daemon->read_fd_set), &(daemon->write_fd_set), &(daemon->except_fd_set), &timeout); | ||
1272 | |||
1273 | if(num_ready > 0) { | ||
1274 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | ||
1275 | if(daemon->connections[i] != NULL) { | ||
1276 | if(FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->read_fd_set))) { | ||
1277 | if(MHD_handle_read(i, daemon) == MHD_NO) | ||
1278 | continue; | ||
1279 | } | ||
1280 | if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->write_fd_set))) { | ||
1281 | if(MHD_handle_write(i, daemon) == MHD_NO) | ||
1282 | continue; | ||
1283 | } | ||
1284 | if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->except_fd_set))) { | ||
1285 | MHD_handle_except(i, daemon); | ||
1286 | } | ||
1287 | } | ||
1288 | } | ||
1289 | if(FD_ISSET(daemon->socket_fd, &(daemon->read_fd_set))) { | ||
1290 | if(MHD_create_connection(daemon) == -1) { | ||
1291 | continue; | ||
1292 | } | ||
1293 | } | ||
1294 | } | ||
1295 | } while (!daemon->shutdown); | ||
1296 | |||
1297 | return NULL; | ||
1298 | } | ||
1299 | |||
1300 | /** | ||
1301 | * This function was created for the case of multithreaded connections. | ||
1302 | * A thread will spawned to sit on this function, and in turn spawns more | ||
1303 | * threads, one per connection. | ||
1304 | */ | ||
1305 | void * | ||
1306 | MHD_spawn_connections(void * data) { | ||
1307 | struct MHD_Daemon * daemon; | ||
1308 | int con, num_ready; | ||
1309 | struct timeval timeout; | ||
1310 | fd_set read; | ||
1311 | |||
1312 | daemon = data; | ||
1313 | if(daemon == NULL) { | ||
1314 | return NULL; | ||
1315 | } | ||
1316 | |||
1317 | do { | ||
1318 | FD_ZERO(&read); | ||
1319 | FD_SET(daemon->socket_fd, &read); | ||
1320 | |||
1321 | timeout.tv_sec = 0; | ||
1322 | timeout.tv_usec = 0; | ||
1323 | |||
1324 | num_ready = select(daemon->socket_fd + 1,&read, NULL, NULL, &timeout); | ||
1325 | |||
1326 | if(num_ready > 0) { | ||
1327 | con = MHD_create_connection(daemon); | ||
1328 | if(con == -1) | ||
1329 | continue; | ||
1330 | |||
1331 | if(pthread_create(&daemon->connections[con]->pid, NULL, (void *) &MHD_handle_connection, (void *)daemon->connections[con]) != 0) { | ||
1332 | if((daemon->options & MHD_USE_DEBUG) != 0) | ||
1333 | fprintf(stderr, "Error creating connection handler!.\n"); | ||
1334 | } | ||
1335 | } | ||
1336 | } while (!daemon->shutdown); | ||
1337 | |||
1338 | return NULL; | ||
1339 | } | ||
1340 | 419 | ||
1341 | /** | 420 | /** |
1342 | * Start a webserver on the given port. | 421 | * Start a webserver on the given port. |
422 | * | ||
1343 | * @param port port to bind to | 423 | * @param port port to bind to |
1344 | * @param apc callback to call to check which clients | 424 | * @param apc callback to call to check which clients |
1345 | * will be allowed to connect | 425 | * will be allowed to connect |
@@ -1355,128 +435,73 @@ MHD_start_daemon(unsigned int options, | |||
1355 | void * apc_cls, | 435 | void * apc_cls, |
1356 | MHD_AccessHandlerCallback dh, | 436 | MHD_AccessHandlerCallback dh, |
1357 | void * dh_cls) { | 437 | void * dh_cls) { |
1358 | 438 | struct MHD_Daemon * retVal; | |
1359 | 439 | int socket_fd; | |
1360 | struct MHD_Daemon * retVal = NULL; | 440 | int opt; |
1361 | int socket_fd, opt, res, i; | 441 | int res; |
1362 | struct sockaddr_in servaddr; | 442 | struct sockaddr_in servaddr; |
1363 | struct hostent *hostptr; | 443 | |
1364 | char hostid[32]; | 444 | if ((options & MHD_USE_SSL) != 0) |
1365 | 445 | return NULL; | |
1366 | if((options & MHD_USE_SSL) != 0) { | 446 | if ((options & MHD_USE_IPv6) != 0) |
1367 | if((options & MHD_USE_DEBUG) != 0) | 447 | return NULL; |
1368 | fprintf(stderr, "SSL at this time is unsupported.\n"); | 448 | if ((options & MHD_USE_IPv4) == 0) |
1369 | return NULL; | 449 | return NULL; |
1370 | } | 450 | if ( (port == 0) || |
1371 | if((options & MHD_USE_IPv6) != 0) { | 451 | (dh == NULL) ) |
1372 | if((options & MHD_USE_DEBUG) != 0) | 452 | return NULL; |
1373 | fprintf(stderr, "IP version 6 is not supported at this time.\n"); | 453 | socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
1374 | return NULL; | 454 | if (socket_fd < 0) { |
1375 | } | 455 | /* FIXME: log error */ |
1376 | 456 | return NULL; | |
1377 | if((options & MHD_USE_IPv4) != 0) { | 457 | } |
1378 | if((options & MHD_USE_DEBUG) != 0) | 458 | memset(&servaddr, |
1379 | fprintf(stderr, "Enter MHD_start_daemon. Starting Daemon on port %i\n", port); | 459 | 0, |
1380 | 460 | sizeof(struct sockaddr_in)); | |
1381 | if(port < 1) { | 461 | servaddr.sin_family = AF_INET; |
1382 | if((options & MHD_USE_DEBUG) != 0) | 462 | servaddr.sin_port = htons(port); |
1383 | fprintf(stderr, "Invalid port: %i!\n", port); | 463 | if (bind(socket_fd, |
1384 | return NULL; | 464 | (struct sockaddr *)&servaddr, |
1385 | } | 465 | sizeof(struct sockaddr_in)) < 0) { |
1386 | 466 | /* FIXME: log error */ | |
1387 | if(dh == NULL) { | 467 | close(socket_fd); |
1388 | if((options & MHD_USE_DEBUG) != 0) | 468 | return NULL; |
1389 | fprintf(stderr, "A default access handler must be provided\n"); | 469 | } |
1390 | return NULL; | 470 | if (listen(socket_fd, 20) < 0) { |
1391 | } | 471 | /* FIXME: log error */ |
1392 | 472 | return NULL; | |
1393 | retVal = (struct MHD_Daemon *)malloc(sizeof(struct MHD_Daemon)); | 473 | } |
1394 | if(retVal == NULL) { | 474 | opt = fcntl(socket_fd, F_GETFL, 0); |
1395 | if((options & MHD_USE_DEBUG) != 0) | 475 | res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK); |
1396 | fprintf(stderr, "Error allocating memory!\n"); | 476 | if (res < 0) { |
1397 | return NULL; | 477 | /* FIXME: log error */ |
1398 | } | 478 | close(socket_fd); |
1399 | 479 | return NULL; | |
1400 | retVal->options = options; | 480 | } |
1401 | retVal->port = port; | 481 | |
1402 | retVal->apc = apc; | 482 | retVal = malloc(sizeof(struct MHD_Daemon)); |
1403 | retVal->apc_cls = apc_cls; | 483 | memset(retVal, |
1404 | retVal->dh = dh; | 484 | 0, |
1405 | retVal->dh_cls = dh_cls; | 485 | sizeof(struct MHD_Daemon)); |
1406 | retVal->shutdown = 0; | 486 | retVal->options = options; |
1407 | retVal->pid = (pthread_t)-1; | 487 | retVal->port = port; |
1408 | 488 | retVal->apc = apc; | |
1409 | retVal->firstFreeHandler = 0; | 489 | retVal->apc_cls = apc_cls; |
1410 | for(i = 0; i < MHD_MAX_HANDLERS; i++) { | 490 | retVal->dh = dh; |
1411 | retVal->handlers[i] = NULL; | 491 | retVal->dh_cls = dh_cls; |
1412 | } | 492 | retVal->socket_fd = socket_fd; |
1413 | 493 | if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || | |
1414 | FD_ZERO(&retVal->read_fd_set); | 494 | (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) && |
1415 | FD_ZERO(&retVal->write_fd_set); | 495 | (0 != pthread_create(&daemon->pid, |
1416 | FD_ZERO(&retVal->except_fd_set); | 496 | NULL, |
1417 | 497 | &MHD_select_thread, | |
1418 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | 498 | daemon)) ) { |
1419 | retVal->connections[i] = NULL; | 499 | /* FIXME: log error */ |
1420 | } | 500 | free(retVal); |
1421 | 501 | close(socket_fd); | |
1422 | socket_fd = socket(AF_INET, SOCK_STREAM, 0); | 502 | return NULL; |
1423 | if(socket_fd < 0) { | 503 | } |
1424 | if((options & MHD_USE_DEBUG) != 0) | 504 | return retVal; |
1425 | perror("Error creating socket!"); | ||
1426 | return NULL; | ||
1427 | } | ||
1428 | |||
1429 | memset((void *)&servaddr, 0, (size_t)sizeof(servaddr)); | ||
1430 | |||
1431 | if (gethostname(hostid,32) < 0){ | ||
1432 | if((options & MHD_USE_DEBUG) != 0) | ||
1433 | perror("server_tcp:gethostname"); | ||
1434 | return NULL; | ||
1435 | } | ||
1436 | |||
1437 | if ((hostptr = gethostbyname(hostid)) == NULL){ | ||
1438 | if((options & MHD_USE_DEBUG) != 0) | ||
1439 | fprintf(stderr, "invalid host name, %s\n",hostid); | ||
1440 | return NULL; | ||
1441 | } | ||
1442 | |||
1443 | servaddr.sin_family = AF_INET; | ||
1444 | memcpy((void *)&(servaddr.sin_addr), (void *)(hostptr->h_addr), hostptr->h_length); | ||
1445 | servaddr.sin_port = htons(port); | ||
1446 | |||
1447 | |||
1448 | if (bind(socket_fd, (struct sockaddr *)&servaddr, (socklen_t)sizeof(servaddr)) < 0) { | ||
1449 | if((options & MHD_USE_DEBUG) != 0) | ||
1450 | perror("server:bind"); | ||
1451 | return NULL; | ||
1452 | } | ||
1453 | |||
1454 | if(listen(socket_fd, 20) < 0) { | ||
1455 | if((options & MHD_USE_DEBUG) != 0) | ||
1456 | perror("server:bind"); | ||
1457 | return NULL; | ||
1458 | } | ||
1459 | |||
1460 | retVal->socket_fd = socket_fd; | ||
1461 | retVal->max_fd = socket_fd; | ||
1462 | FD_SET(socket_fd, &retVal->read_fd_set); | ||
1463 | |||
1464 | opt = fcntl(socket_fd, F_GETFL, 0); | ||
1465 | res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK); | ||
1466 | if(res < 0) { | ||
1467 | if((options & MHD_USE_DEBUG) != 0) | ||
1468 | perror("Error disabling block on socket!"); | ||
1469 | return NULL; | ||
1470 | } | ||
1471 | |||
1472 | return retVal; | ||
1473 | } | ||
1474 | |||
1475 | if((options & MHD_USE_DEBUG) != 0) | ||
1476 | fprintf(stderr, "No options given to start_daemon!\n"); | ||
1477 | |||
1478 | return NULL; | ||
1479 | |||
1480 | } | 505 | } |
1481 | 506 | ||
1482 | /** | 507 | /** |
@@ -1484,76 +509,26 @@ MHD_start_daemon(unsigned int options, | |||
1484 | */ | 509 | */ |
1485 | void | 510 | void |
1486 | MHD_stop_daemon(struct MHD_Daemon * daemon) { | 511 | MHD_stop_daemon(struct MHD_Daemon * daemon) { |
1487 | int i, j; | 512 | void * unused; |
1488 | 513 | ||
1489 | if(daemon == NULL) { | 514 | if (daemon == NULL) |
1490 | return; | 515 | return; |
1491 | } | 516 | daemon->shutdown = 1; |
1492 | 517 | close(daemon->socket_fd); | |
1493 | if((daemon->options & MHD_USE_DEBUG) != 0) | 518 | daemon->socket_fd = -1; |
1494 | fprintf(stderr, "Enter MHD_stop_daemon. Stopping daemon on port %i\n", daemon->port); | 519 | if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || |
1495 | 520 | (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) ) | |
1496 | daemon->shutdown = 1; | 521 | pthread_join(daemon->pid, &unused); |
1497 | 522 | ||
1498 | if(daemon->pid != (pthread_t)-1) { | 523 | while (daemon->connections != NULL) { |
1499 | pthread_join(daemon->pid, NULL); | 524 | if (-1 != daemon->connections->socket_fd) { |
1500 | } | 525 | close(daemon->connections->socket_fd); |
1501 | 526 | daemon->connections->socket_fd = -1; | |
1502 | for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { | 527 | } |
1503 | if(daemon->connections[i] != NULL) { | 528 | pthread_join(daemon->connections->pid, &unused); |
1504 | if(daemon->connections[i]->pid != (pthread_t)-1) { | 529 | |
1505 | pthread_join(daemon->connections[i]->pid, NULL); | 530 | MHD_cleanup_sessions(daemon); |
1506 | } | 531 | } |
1507 | 532 | free(daemon); | |
1508 | for(j = 0; j < MHD_MAX_RESPONSE; j++) { | ||
1509 | if(daemon->connections[i]->currentResponses[j] != NULL) { | ||
1510 | MHD_destroy_response(daemon->connections[i]->currentResponses[j]); | ||
1511 | } | ||
1512 | } | ||
1513 | MHD_destroy_session(daemon->connections[i]); | ||
1514 | } | ||
1515 | } | ||
1516 | |||
1517 | for(i = 0; i < MHD_MAX_HANDLERS; i++) { | ||
1518 | if(daemon->handlers[i] != NULL) { | ||
1519 | free(daemon->handlers[i]->uri_prefix); | ||
1520 | free(daemon->handlers[i]); | ||
1521 | } | ||
1522 | } | ||
1523 | |||
1524 | close(daemon->socket_fd); | ||
1525 | |||
1526 | free(daemon); | ||
1527 | } | ||
1528 | |||
1529 | |||
1530 | /** | ||
1531 | * Unregister an access handler for the URIs beginning with | ||
1532 | * uri_prefix. | ||
1533 | * | ||
1534 | * @param uri_prefix | ||
1535 | * @return MHD_NO if a handler for this exact prefix | ||
1536 | * is not known for this daemon | ||
1537 | */ | ||
1538 | int | ||
1539 | MHD_unregister_handler(struct MHD_Daemon * daemon, | ||
1540 | const char * uri_prefix, | ||
1541 | MHD_AccessHandlerCallback dh, | ||
1542 | void * dh_cls) { | ||
1543 | int i; | ||
1544 | |||
1545 | for(i = 0; i < MHD_MAX_HANDLERS; i++) { | ||
1546 | if(daemon->handlers[i] != NULL) { | ||
1547 | if(strncmp(daemon->handlers[i]->uri_prefix, uri_prefix, strlen(daemon->handlers[i]->uri_prefix)) == 0) { | ||
1548 | if(daemon->handlers[i]->dh == dh && daemon->handlers[i]->dh_cls == dh_cls) { | ||
1549 | free(daemon->handlers[i]->uri_prefix); | ||
1550 | free(daemon->handlers[i]); | ||
1551 | return MHD_YES; | ||
1552 | } | ||
1553 | } | ||
1554 | } | ||
1555 | } | ||
1556 | |||
1557 | return MHD_NO; | ||
1558 | } | 533 | } |
1559 | 534 | ||