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