diff options
Diffstat (limited to 'src/transport/plugin_transport_http.c')
-rw-r--r-- | src/transport/plugin_transport_http.c | 2085 |
1 files changed, 2085 insertions, 0 deletions
diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c new file mode 100644 index 000000000..eb69f4386 --- /dev/null +++ b/src/transport/plugin_transport_http.c | |||
@@ -0,0 +1,2085 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file transports/http.c | ||
23 | * @brief Implementation of the HTTP transport service | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "platform.h" | ||
28 | #include "gnunet_util.h" | ||
29 | #include "gnunet_protocols.h" | ||
30 | #include "gnunet_transport.h" | ||
31 | #include "gnunet_stats_service.h" | ||
32 | #include "gnunet_upnp_service.h" | ||
33 | #include <stdint.h> | ||
34 | #include <microhttpd.h> | ||
35 | #include <curl/curl.h> | ||
36 | #include "ip.h" | ||
37 | |||
38 | #define DEBUG_HTTP GNUNET_NO | ||
39 | |||
40 | /** | ||
41 | * Disable GET (for debugging only!). Must be GNUNET_YES | ||
42 | * in production use! | ||
43 | */ | ||
44 | #define DO_GET GNUNET_YES | ||
45 | |||
46 | /** | ||
47 | * After how much time of the core not being associated with a http | ||
48 | * connection anymore do we close it? | ||
49 | * | ||
50 | * Needs to be larger than SECONDS_INACTIVE_DROP in | ||
51 | * core's connection.s | ||
52 | */ | ||
53 | #define HTTP_TIMEOUT (600 * GNUNET_CRON_SECONDS) | ||
54 | |||
55 | /** | ||
56 | * How often do we re-issue GET requests? | ||
57 | */ | ||
58 | #define HTTP_GET_REFRESH (5 * GNUNET_CRON_SECONDS) | ||
59 | |||
60 | /** | ||
61 | * Default maximum size of the HTTP read and write buffer. | ||
62 | */ | ||
63 | #define HTTP_BUF_SIZE (64 * 1024) | ||
64 | |||
65 | /** | ||
66 | * Text of the response sent back after the last bytes of a PUT | ||
67 | * request have been received (just to formally obey the HTTP | ||
68 | * protocol). | ||
69 | */ | ||
70 | #define HTTP_PUT_RESPONSE "Thank you!" | ||
71 | |||
72 | #define MY_TRANSPORT_NAME "HTTP" | ||
73 | #include "common.c" | ||
74 | |||
75 | /** | ||
76 | * Client-side data per PUT request. | ||
77 | */ | ||
78 | struct HTTPPutData | ||
79 | { | ||
80 | /** | ||
81 | * This is a linked list. | ||
82 | */ | ||
83 | struct HTTPPutData *next; | ||
84 | |||
85 | /** | ||
86 | * Handle to our CURL request. | ||
87 | */ | ||
88 | CURL *curl_put; | ||
89 | |||
90 | /** | ||
91 | * Last time we made progress with the PUT. | ||
92 | */ | ||
93 | GNUNET_CronTime last_activity; | ||
94 | |||
95 | /** | ||
96 | * The message we are sending. | ||
97 | */ | ||
98 | char *msg; | ||
99 | |||
100 | /** | ||
101 | * Size of msg. | ||
102 | */ | ||
103 | unsigned int size; | ||
104 | |||
105 | /** | ||
106 | * Current position in msg. | ||
107 | */ | ||
108 | unsigned int pos; | ||
109 | |||
110 | /** | ||
111 | * Are we done sending? Set to 1 after we | ||
112 | * completed sending and started to receive | ||
113 | * a response ("Thank you!") or once the | ||
114 | * timeout has been reached. | ||
115 | */ | ||
116 | int done; | ||
117 | |||
118 | }; | ||
119 | |||
120 | /** | ||
121 | * Server-side data per PUT request. | ||
122 | */ | ||
123 | struct MHDPutData | ||
124 | { | ||
125 | /** | ||
126 | * This is a linked list. | ||
127 | */ | ||
128 | struct MHDPutData *next; | ||
129 | |||
130 | /** | ||
131 | * MHD connection handle for this request. | ||
132 | */ | ||
133 | struct MHD_Connection *session; | ||
134 | |||
135 | /** | ||
136 | * Last time we received data on this PUT | ||
137 | * connection. | ||
138 | */ | ||
139 | GNUNET_CronTime last_activity; | ||
140 | |||
141 | /** | ||
142 | * Read buffer for the header (from PUT) | ||
143 | */ | ||
144 | char rbuff1[sizeof (GNUNET_MessageHeader)]; | ||
145 | |||
146 | /** | ||
147 | * The read buffer (used only receiving PUT data). | ||
148 | */ | ||
149 | char *rbuff2; | ||
150 | |||
151 | /** | ||
152 | * Number of valid bytes in rbuff1 | ||
153 | */ | ||
154 | unsigned int rpos1; | ||
155 | |||
156 | /** | ||
157 | * Number of valid bytes in rbuff2 | ||
158 | */ | ||
159 | unsigned int rpos2; | ||
160 | |||
161 | |||
162 | /** | ||
163 | * Size of the rbuff2 buffer. | ||
164 | */ | ||
165 | unsigned int rsize2; | ||
166 | |||
167 | /** | ||
168 | * Should we sent a response for this PUT yet? | ||
169 | */ | ||
170 | int ready; | ||
171 | |||
172 | /** | ||
173 | * Have we sent a response for this PUT yet? | ||
174 | */ | ||
175 | int done; | ||
176 | |||
177 | }; | ||
178 | |||
179 | /** | ||
180 | * Server-side data for a GET request. | ||
181 | */ | ||
182 | struct MHDGetData | ||
183 | { | ||
184 | |||
185 | /** | ||
186 | * This is a linked list. | ||
187 | */ | ||
188 | struct MHDGetData *next; | ||
189 | |||
190 | /** | ||
191 | * MHD connection handle for this request. | ||
192 | */ | ||
193 | struct MHD_Connection *session; | ||
194 | |||
195 | /** | ||
196 | * GET session response handle | ||
197 | */ | ||
198 | struct MHD_Response *get; | ||
199 | |||
200 | /** | ||
201 | * My HTTP session. | ||
202 | */ | ||
203 | struct HTTPSession *httpsession; | ||
204 | |||
205 | /** | ||
206 | * The write buffer (for sending GET response) | ||
207 | */ | ||
208 | char *wbuff; | ||
209 | |||
210 | /** | ||
211 | * What was the last time we were able to | ||
212 | * transmit data using the current get handle? | ||
213 | */ | ||
214 | GNUNET_CronTime last_get_activity; | ||
215 | |||
216 | /** | ||
217 | * Current write position in wbuff | ||
218 | */ | ||
219 | unsigned int woff; | ||
220 | |||
221 | /** | ||
222 | * Number of valid bytes in wbuff (starting at woff) | ||
223 | */ | ||
224 | unsigned int wpos; | ||
225 | |||
226 | /** | ||
227 | * Size of the write buffer. | ||
228 | */ | ||
229 | unsigned int wsize; | ||
230 | |||
231 | }; | ||
232 | |||
233 | /** | ||
234 | * Transport Session handle. | ||
235 | */ | ||
236 | typedef struct HTTPSession | ||
237 | { | ||
238 | |||
239 | /** | ||
240 | * GNUNET_TSession for this session. | ||
241 | */ | ||
242 | GNUNET_TSession *tsession; | ||
243 | |||
244 | /** | ||
245 | * To whom are we talking to. | ||
246 | */ | ||
247 | GNUNET_PeerIdentity sender; | ||
248 | |||
249 | /** | ||
250 | * number of users of this session | ||
251 | */ | ||
252 | unsigned int users; | ||
253 | |||
254 | /** | ||
255 | * Has this session been destroyed? | ||
256 | */ | ||
257 | int destroyed; | ||
258 | |||
259 | /** | ||
260 | * Are we client or server? Determines which of the | ||
261 | * structs in the union below is being used for this | ||
262 | * connection! | ||
263 | */ | ||
264 | int is_client; | ||
265 | |||
266 | /** | ||
267 | * Is MHD still using this session handle? | ||
268 | */ | ||
269 | int is_mhd_active; | ||
270 | |||
271 | /** | ||
272 | * Data maintained for the http client-server connection | ||
273 | * (depends on if we are client or server). | ||
274 | */ | ||
275 | union | ||
276 | { | ||
277 | |||
278 | struct | ||
279 | { | ||
280 | /** | ||
281 | * Active PUT requests (linked list). | ||
282 | */ | ||
283 | struct MHDPutData *puts; | ||
284 | |||
285 | #if DO_GET | ||
286 | /** | ||
287 | * Active GET requests (linked list; most | ||
288 | * recent received GET is the head of the list). | ||
289 | */ | ||
290 | struct MHDGetData *gets; | ||
291 | #endif | ||
292 | |||
293 | } server; | ||
294 | |||
295 | struct | ||
296 | { | ||
297 | |||
298 | /** | ||
299 | * Address of the other peer. | ||
300 | */ | ||
301 | HostAddress address; | ||
302 | |||
303 | #if DO_GET | ||
304 | /** | ||
305 | * Last time the GET was active. | ||
306 | */ | ||
307 | GNUNET_CronTime last_get_activity; | ||
308 | |||
309 | /** | ||
310 | * What was the last time we were able to | ||
311 | * transmit data using the current get handle? | ||
312 | */ | ||
313 | GNUNET_CronTime last_get_initiated; | ||
314 | |||
315 | /** | ||
316 | * GET operation | ||
317 | */ | ||
318 | CURL *get; | ||
319 | |||
320 | /** | ||
321 | * Read buffer for the header (from GET). | ||
322 | */ | ||
323 | char rbuff1[sizeof (GNUNET_MessageHeader)]; | ||
324 | |||
325 | /** | ||
326 | * The read buffer (used only receiving GET data). | ||
327 | */ | ||
328 | char *rbuff2; | ||
329 | |||
330 | /** | ||
331 | * Number of valid bytes in rbuff1 | ||
332 | */ | ||
333 | unsigned int rpos1; | ||
334 | |||
335 | /** | ||
336 | * Number of valid bytes in rbuff2 | ||
337 | */ | ||
338 | unsigned int rpos2; | ||
339 | |||
340 | /** | ||
341 | * Current size of the read buffer rbuff2. | ||
342 | */ | ||
343 | unsigned int rsize2; | ||
344 | #endif | ||
345 | |||
346 | /** | ||
347 | * URL of the get and put operations. | ||
348 | */ | ||
349 | char *url; | ||
350 | |||
351 | /** | ||
352 | * Linked list of PUT operations. | ||
353 | */ | ||
354 | struct HTTPPutData *puts; | ||
355 | |||
356 | } client; | ||
357 | |||
358 | } cs; | ||
359 | |||
360 | } HTTPSession; | ||
361 | |||
362 | /* *********** globals ************* */ | ||
363 | |||
364 | static int stat_bytesReceived; | ||
365 | |||
366 | static int stat_bytesSent; | ||
367 | |||
368 | static int stat_bytesDropped; | ||
369 | |||
370 | static int stat_get_issued; | ||
371 | |||
372 | static int stat_get_received; | ||
373 | |||
374 | static int stat_put_issued; | ||
375 | |||
376 | static int stat_put_received; | ||
377 | |||
378 | static int stat_select_calls; | ||
379 | |||
380 | static int stat_send_calls; | ||
381 | |||
382 | static int stat_connect_calls; | ||
383 | |||
384 | static int stat_curl_send_callbacks; | ||
385 | |||
386 | static int stat_curl_receive_callbacks; | ||
387 | |||
388 | static int stat_mhd_access_callbacks; | ||
389 | |||
390 | static int stat_mhd_read_callbacks; | ||
391 | |||
392 | static int stat_mhd_close_callbacks; | ||
393 | |||
394 | static int stat_connect_calls; | ||
395 | |||
396 | /** | ||
397 | * How many requests do we have currently pending | ||
398 | * (with libcurl)? | ||
399 | */ | ||
400 | static unsigned int http_requests_pending; | ||
401 | |||
402 | static int signal_pipe[2]; | ||
403 | |||
404 | static char *proxy; | ||
405 | |||
406 | /** | ||
407 | * Daemon for listening for new connections. | ||
408 | */ | ||
409 | static struct MHD_Daemon *mhd_daemon; | ||
410 | |||
411 | /** | ||
412 | * Curl multi for managing client operations. | ||
413 | */ | ||
414 | static CURLM *curl_multi; | ||
415 | |||
416 | /** | ||
417 | * Set to GNUNET_YES while the transport is running. | ||
418 | */ | ||
419 | static int http_running; | ||
420 | |||
421 | /** | ||
422 | * Thread running libcurl activities. | ||
423 | */ | ||
424 | static struct GNUNET_ThreadHandle *curl_thread; | ||
425 | |||
426 | /** | ||
427 | * Array of currently active HTTP sessions. | ||
428 | */ | ||
429 | static GNUNET_TSession **tsessions; | ||
430 | |||
431 | /** | ||
432 | * Number of valid entries in tsessions. | ||
433 | */ | ||
434 | static unsigned int tsessionCount; | ||
435 | |||
436 | /** | ||
437 | * Sie of the tsessions array. | ||
438 | */ | ||
439 | static unsigned int tsessionArrayLength; | ||
440 | |||
441 | /** | ||
442 | * Lock for concurrent access to all structures used | ||
443 | * by http, including CURL. | ||
444 | */ | ||
445 | static struct GNUNET_Mutex *lock; | ||
446 | |||
447 | |||
448 | /** | ||
449 | * Signal select thread that its selector | ||
450 | * set may have changed. | ||
451 | */ | ||
452 | static void | ||
453 | signal_select () | ||
454 | { | ||
455 | static char c; | ||
456 | WRITE (signal_pipe[1], &c, sizeof (c)); | ||
457 | } | ||
458 | |||
459 | /** | ||
460 | * Check if we are allowed to connect to the given IP. | ||
461 | */ | ||
462 | static int | ||
463 | acceptPolicyCallback (void *cls, | ||
464 | const struct sockaddr *addr, socklen_t addr_len) | ||
465 | { | ||
466 | if (GNUNET_NO != is_rejected_tester (addr, addr_len)) | ||
467 | return MHD_NO; | ||
468 | return MHD_YES; | ||
469 | } | ||
470 | |||
471 | /** | ||
472 | * Disconnect from a remote node. May only be called | ||
473 | * on sessions that were acquired by the caller first. | ||
474 | * For the core, aquiration means to call associate or | ||
475 | * connect. The number of disconnects must match the | ||
476 | * number of calls to connect+associate. | ||
477 | * | ||
478 | * Sessions are actually discarded in cleanup_connections. | ||
479 | * | ||
480 | * | ||
481 | * @param tsession the session that is closed | ||
482 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
483 | */ | ||
484 | static int | ||
485 | httpDisconnect (GNUNET_TSession * tsession) | ||
486 | { | ||
487 | HTTPSession *httpsession = tsession->internal; | ||
488 | if (httpsession == NULL) | ||
489 | { | ||
490 | GNUNET_free (tsession); | ||
491 | return GNUNET_OK; | ||
492 | } | ||
493 | GNUNET_mutex_lock (lock); | ||
494 | httpsession->users--; | ||
495 | GNUNET_mutex_unlock (lock); | ||
496 | return GNUNET_OK; | ||
497 | } | ||
498 | |||
499 | static void | ||
500 | destroy_tsession (GNUNET_TSession * tsession) | ||
501 | { | ||
502 | HTTPSession *httpsession = tsession->internal; | ||
503 | struct HTTPPutData *pos; | ||
504 | struct HTTPPutData *next; | ||
505 | #if DO_GET | ||
506 | struct MHDGetData *gpos; | ||
507 | struct MHDGetData *gnext; | ||
508 | #endif | ||
509 | struct MHD_Response *r; | ||
510 | int i; | ||
511 | |||
512 | GNUNET_mutex_lock (lock); | ||
513 | for (i = 0; i < tsessionCount; i++) | ||
514 | { | ||
515 | if (tsessions[i] == tsession) | ||
516 | { | ||
517 | tsessions[i] = tsessions[--tsessionCount]; | ||
518 | break; | ||
519 | } | ||
520 | } | ||
521 | if (httpsession->is_client) | ||
522 | { | ||
523 | #if DO_GET | ||
524 | curl_multi_remove_handle (curl_multi, httpsession->cs.client.get); | ||
525 | http_requests_pending--; | ||
526 | signal_select (); | ||
527 | curl_easy_cleanup (httpsession->cs.client.get); | ||
528 | GNUNET_array_grow (httpsession->cs.client.rbuff2, | ||
529 | httpsession->cs.client.rsize2, 0); | ||
530 | #endif | ||
531 | GNUNET_free_non_null (httpsession->cs.client.url); | ||
532 | pos = httpsession->cs.client.puts; | ||
533 | while (pos != NULL) | ||
534 | { | ||
535 | next = pos->next; | ||
536 | curl_multi_remove_handle (curl_multi, pos->curl_put); | ||
537 | http_requests_pending--; | ||
538 | signal_select (); | ||
539 | curl_easy_cleanup (pos->curl_put); | ||
540 | GNUNET_free (pos->msg); | ||
541 | GNUNET_free (pos); | ||
542 | pos = next; | ||
543 | } | ||
544 | GNUNET_free (httpsession); | ||
545 | GNUNET_free (tsession); | ||
546 | } | ||
547 | else | ||
548 | { | ||
549 | httpsession->destroyed = GNUNET_YES; | ||
550 | GNUNET_GE_BREAK (NULL, httpsession->cs.server.puts == NULL); | ||
551 | #if DO_GET | ||
552 | gpos = httpsession->cs.server.gets; | ||
553 | while (gpos != NULL) | ||
554 | { | ||
555 | GNUNET_array_grow (gpos->wbuff, gpos->wsize, 0); | ||
556 | r = gpos->get; | ||
557 | gpos->get = NULL; | ||
558 | gnext = gpos->next; | ||
559 | MHD_destroy_response (r); | ||
560 | gpos = gnext; | ||
561 | } | ||
562 | httpsession->cs.server.gets = NULL; | ||
563 | #endif | ||
564 | GNUNET_free (httpsession->tsession); | ||
565 | GNUNET_free (httpsession); | ||
566 | } | ||
567 | GNUNET_mutex_unlock (lock); | ||
568 | } | ||
569 | |||
570 | /** | ||
571 | * MHD is done handling a request. Cleanup | ||
572 | * the respective transport state. | ||
573 | */ | ||
574 | static void | ||
575 | requestCompletedCallback (void *unused, | ||
576 | struct MHD_Connection *session, | ||
577 | void **httpSessionCache) | ||
578 | { | ||
579 | HTTPSession *httpsession = *httpSessionCache; | ||
580 | struct MHDPutData *pprev; | ||
581 | struct MHDPutData *ppos; | ||
582 | #if DO_GET | ||
583 | struct MHDGetData *gprev; | ||
584 | struct MHDGetData *gpos; | ||
585 | #endif | ||
586 | |||
587 | if (stats != NULL) | ||
588 | stats->change (stat_mhd_close_callbacks, 1); | ||
589 | if (httpsession == NULL) | ||
590 | return; /* oops */ | ||
591 | GNUNET_GE_ASSERT (NULL, !httpsession->is_client); | ||
592 | pprev = NULL; | ||
593 | ppos = httpsession->cs.server.puts; | ||
594 | while (ppos != NULL) | ||
595 | { | ||
596 | if (ppos->session == session) | ||
597 | { | ||
598 | ppos->last_activity = 0; | ||
599 | signal_select (); | ||
600 | return; | ||
601 | } | ||
602 | pprev = ppos; | ||
603 | ppos = ppos->next; | ||
604 | } | ||
605 | #if DO_GET | ||
606 | gprev = NULL; | ||
607 | gpos = httpsession->cs.server.gets; | ||
608 | while (gpos != NULL) | ||
609 | { | ||
610 | if (gpos->session == session) | ||
611 | { | ||
612 | gpos->last_get_activity = 0; | ||
613 | signal_select (); | ||
614 | return; | ||
615 | } | ||
616 | gprev = gpos; | ||
617 | gpos = gpos->next; | ||
618 | } | ||
619 | #endif | ||
620 | httpsession->is_mhd_active--; | ||
621 | } | ||
622 | |||
623 | /** | ||
624 | * A (core) Session is to be associated with a transport session. The | ||
625 | * transport service may want to know in order to call back on the | ||
626 | * core if the connection is being closed. Associate can also be | ||
627 | * called to test if it would be possible to associate the session | ||
628 | * later, in this case the argument session is NULL. This can be used | ||
629 | * to test if the connection must be closed by the core or if the core | ||
630 | * can assume that it is going to be self-managed (if associate | ||
631 | * returns GNUNET_OK and session was NULL, the transport layer is responsible | ||
632 | * for eventually freeing resources associated with the tesession). If | ||
633 | * session is not NULL, the core takes responsbility for eventually | ||
634 | * calling disconnect. | ||
635 | * | ||
636 | * @param tsession the session handle passed along | ||
637 | * from the call to receive that was made by the transport | ||
638 | * layer | ||
639 | * @return GNUNET_OK if the session could be associated, | ||
640 | * GNUNET_SYSERR if not. | ||
641 | */ | ||
642 | static int | ||
643 | httpAssociate (GNUNET_TSession * tsession) | ||
644 | { | ||
645 | HTTPSession *httpSession; | ||
646 | |||
647 | if (tsession == NULL) | ||
648 | { | ||
649 | GNUNET_GE_BREAK (NULL, 0); | ||
650 | return GNUNET_SYSERR; | ||
651 | } | ||
652 | httpSession = tsession->internal; | ||
653 | GNUNET_mutex_lock (lock); | ||
654 | if (httpSession->destroyed == GNUNET_YES) | ||
655 | { | ||
656 | GNUNET_mutex_unlock (lock); | ||
657 | return GNUNET_SYSERR; | ||
658 | } | ||
659 | httpSession->users++; | ||
660 | GNUNET_mutex_unlock (lock); | ||
661 | return GNUNET_OK; | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * Add a new session to the array watched by the select thread. Grows | ||
666 | * the array if needed. If the caller wants to do anything useful | ||
667 | * with the return value, it must have the lock before | ||
668 | * calling. It is ok to call this function without holding lock if | ||
669 | * the return value is ignored. | ||
670 | */ | ||
671 | static unsigned int | ||
672 | addTSession (GNUNET_TSession * tsession) | ||
673 | { | ||
674 | unsigned int i; | ||
675 | |||
676 | GNUNET_mutex_lock (lock); | ||
677 | if (tsessionCount == tsessionArrayLength) | ||
678 | GNUNET_array_grow (tsessions, tsessionArrayLength, | ||
679 | tsessionArrayLength * 2); | ||
680 | i = tsessionCount; | ||
681 | tsessions[tsessionCount++] = tsession; | ||
682 | GNUNET_mutex_unlock (lock); | ||
683 | return i; | ||
684 | } | ||
685 | |||
686 | #if DO_GET | ||
687 | /** | ||
688 | * Callback for processing GET requests if our side is the | ||
689 | * MHD HTTP server. | ||
690 | * | ||
691 | * @param cls the HTTP session | ||
692 | * @param pos read-offset in the stream | ||
693 | * @param buf where to write the data | ||
694 | * @param max how much data to write (at most) | ||
695 | * @return number of bytes written, 0 is allowed! | ||
696 | */ | ||
697 | static int | ||
698 | contentReaderCallback (void *cls, uint64_t pos, char *buf, int max) | ||
699 | { | ||
700 | struct MHDGetData *mgd = cls; | ||
701 | |||
702 | if (stats != NULL) | ||
703 | stats->change (stat_mhd_read_callbacks, 1); | ||
704 | GNUNET_mutex_lock (lock); | ||
705 | if (mgd->wpos < max) | ||
706 | max = mgd->wpos; | ||
707 | memcpy (buf, &mgd->wbuff[mgd->woff], max); | ||
708 | mgd->wpos -= max; | ||
709 | mgd->woff += max; | ||
710 | if (max > 0) | ||
711 | mgd->last_get_activity = GNUNET_get_time (); | ||
712 | if (mgd->wpos == 0) | ||
713 | mgd->woff = 0; | ||
714 | GNUNET_mutex_unlock (lock); | ||
715 | #if DEBUG_HTTP | ||
716 | GNUNET_GE_LOG (coreAPI->ectx, | ||
717 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
718 | "HTTP returns %u bytes in MHD's GET handler.\n", max); | ||
719 | #endif | ||
720 | if (stats != NULL) | ||
721 | stats->change (stat_bytesSent, max); | ||
722 | if ((max == 0) && (mgd->httpsession->cs.server.gets != mgd)) | ||
723 | return -1; /* end of response (another GET replaces this one) */ | ||
724 | return max; | ||
725 | } | ||
726 | #endif | ||
727 | |||
728 | #if DO_GET | ||
729 | /** | ||
730 | * Notification that libmicrohttpd no longer needs the | ||
731 | * response object. | ||
732 | */ | ||
733 | static void | ||
734 | contentReaderFreeCallback (void *cls) | ||
735 | { | ||
736 | struct MHDGetData *mgd = cls; | ||
737 | |||
738 | GNUNET_GE_ASSERT (NULL, mgd->get == NULL); | ||
739 | GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0); | ||
740 | GNUNET_free (mgd); | ||
741 | } | ||
742 | #endif | ||
743 | |||
744 | /** | ||
745 | * Process GET or PUT request received via MHD. For | ||
746 | * GET, queue response that will send back our pending | ||
747 | * messages. For PUT, process incoming data and send | ||
748 | * to GNUnet core. In either case, check if a session | ||
749 | * already exists and create a new one if not. | ||
750 | */ | ||
751 | static int | ||
752 | accessHandlerCallback (void *cls, | ||
753 | struct MHD_Connection *session, | ||
754 | const char *url, | ||
755 | const char *method, | ||
756 | const char *version, | ||
757 | const char *upload_data, | ||
758 | size_t * upload_data_size, void **httpSessionCache) | ||
759 | { | ||
760 | GNUNET_TSession *tsession; | ||
761 | struct MHDPutData *put; | ||
762 | struct MHDGetData *get; | ||
763 | HTTPSession *httpSession; | ||
764 | struct MHD_Response *response; | ||
765 | GNUNET_HashCode client; | ||
766 | int i; | ||
767 | unsigned int have; | ||
768 | GNUNET_MessageHeader *hdr; | ||
769 | GNUNET_TransportPacket *mp; | ||
770 | unsigned int cpy; | ||
771 | unsigned int poff; | ||
772 | |||
773 | if (stats != NULL) | ||
774 | stats->change (stat_mhd_access_callbacks, 1); | ||
775 | #if DEBUG_HTTP | ||
776 | GNUNET_GE_LOG (coreAPI->ectx, | ||
777 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
778 | "HTTP/MHD receives `%s' request.\n", method); | ||
779 | #endif | ||
780 | /* convert URL to sender peer id */ | ||
781 | if ((strlen (url) < 2) | ||
782 | || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client))) | ||
783 | { | ||
784 | /* invalid request */ | ||
785 | /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely | ||
786 | somebody scanning for MyDoom.X-opened backdoors */ | ||
787 | return MHD_NO; | ||
788 | } | ||
789 | |||
790 | /* check if we already have a session for this */ | ||
791 | httpSession = *httpSessionCache; | ||
792 | if (httpSession == NULL) | ||
793 | { | ||
794 | /* new http connection */ | ||
795 | if (stats != NULL) | ||
796 | { | ||
797 | if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method)) | ||
798 | stats->change (stat_put_received, 1); | ||
799 | else | ||
800 | stats->change (stat_get_received, 1); | ||
801 | } | ||
802 | GNUNET_mutex_lock (lock); | ||
803 | for (i = 0; i < tsessionCount; i++) | ||
804 | { | ||
805 | tsession = tsessions[i]; | ||
806 | httpSession = tsession->internal; | ||
807 | if ((0 == | ||
808 | memcmp (&httpSession->sender, &client, | ||
809 | sizeof (GNUNET_HashCode))) | ||
810 | && (httpSession->is_client == GNUNET_NO)) | ||
811 | break; | ||
812 | tsession = NULL; | ||
813 | httpSession = NULL; | ||
814 | } | ||
815 | GNUNET_mutex_unlock (lock); | ||
816 | } | ||
817 | /* create new session if necessary */ | ||
818 | if (httpSession == NULL) | ||
819 | { | ||
820 | #if DEBUG_HTTP | ||
821 | GNUNET_GE_LOG (coreAPI->ectx, | ||
822 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
823 | "HTTP/MHD creates new session for request from `%s'.\n", | ||
824 | &url[1]); | ||
825 | #endif | ||
826 | httpSession = GNUNET_malloc (sizeof (HTTPSession)); | ||
827 | memset (httpSession, 0, sizeof (HTTPSession)); | ||
828 | httpSession->sender.hashPubKey = client; | ||
829 | httpSession->users = 0; /* MHD */ | ||
830 | tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); | ||
831 | memset (tsession, 0, sizeof (GNUNET_TSession)); | ||
832 | tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; | ||
833 | tsession->internal = httpSession; | ||
834 | tsession->peer.hashPubKey = client; | ||
835 | httpSession->tsession = tsession; | ||
836 | addTSession (tsession); | ||
837 | } | ||
838 | if (*httpSessionCache == NULL) | ||
839 | { | ||
840 | httpSession->is_mhd_active++; | ||
841 | *httpSessionCache = httpSession; | ||
842 | } | ||
843 | GNUNET_mutex_lock (lock); | ||
844 | #if DO_GET | ||
845 | if (0 == strcasecmp (MHD_HTTP_METHOD_GET, method)) | ||
846 | { | ||
847 | #if DEBUG_HTTP | ||
848 | GNUNET_GE_LOG (coreAPI->ectx, | ||
849 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
850 | "HTTP/MHD receives GET request from `%s'.\n", &url[1]); | ||
851 | #endif | ||
852 | |||
853 | /* handle get; create response object if we do not | ||
854 | have one already */ | ||
855 | get = GNUNET_malloc (sizeof (struct MHDGetData)); | ||
856 | memset (get, 0, sizeof (struct MHDGetData)); | ||
857 | get->next = httpSession->cs.server.gets; | ||
858 | httpSession->cs.server.gets = get; | ||
859 | get->session = session; | ||
860 | get->httpsession = httpSession; | ||
861 | get->last_get_activity = GNUNET_get_time (); | ||
862 | get->get = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, | ||
863 | 64 * 1024, | ||
864 | contentReaderCallback, | ||
865 | get, | ||
866 | contentReaderFreeCallback); | ||
867 | MHD_queue_response (session, MHD_HTTP_OK, get->get); | ||
868 | GNUNET_mutex_unlock (lock); | ||
869 | return MHD_YES; | ||
870 | } | ||
871 | #endif | ||
872 | if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method)) | ||
873 | { | ||
874 | #if DEBUG_HTTP | ||
875 | GNUNET_GE_LOG (coreAPI->ectx, | ||
876 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
877 | "HTTP/MHD receives PUT request from `%s' with %u bytes.\n", | ||
878 | &url[1], *upload_data_size); | ||
879 | #endif | ||
880 | put = httpSession->cs.server.puts; | ||
881 | while ((put != NULL) && (put->session != session)) | ||
882 | put = put->next; | ||
883 | if (put == NULL) | ||
884 | { | ||
885 | put = GNUNET_malloc (sizeof (struct MHDPutData)); | ||
886 | memset (put, 0, sizeof (struct MHDPutData)); | ||
887 | put->next = httpSession->cs.server.puts; | ||
888 | httpSession->cs.server.puts = put; | ||
889 | put->session = session; | ||
890 | } | ||
891 | put->last_activity = GNUNET_get_time (); | ||
892 | |||
893 | /* handle put (upload_data!) */ | ||
894 | poff = 0; | ||
895 | have = *upload_data_size; | ||
896 | if (stats != NULL) | ||
897 | stats->change (stat_bytesReceived, have); | ||
898 | *upload_data_size = 0; /* we will always process everything */ | ||
899 | if ((have == 0) && (put->done == GNUNET_NO) | ||
900 | && (put->ready == GNUNET_YES)) | ||
901 | { | ||
902 | put->done = GNUNET_YES; | ||
903 | /* end of upload, send response! */ | ||
904 | #if DEBUG_HTTP | ||
905 | GNUNET_GE_LOG (coreAPI->ectx, | ||
906 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
907 | "HTTP/MHD queues dummy response to completed PUT request.\n"); | ||
908 | #endif | ||
909 | response = | ||
910 | MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE), | ||
911 | HTTP_PUT_RESPONSE, MHD_NO, MHD_NO); | ||
912 | MHD_queue_response (session, MHD_HTTP_OK, response); | ||
913 | MHD_destroy_response (response); | ||
914 | GNUNET_mutex_unlock (lock); | ||
915 | return MHD_YES; | ||
916 | } | ||
917 | while (have > 0) | ||
918 | { | ||
919 | put->ready = GNUNET_NO; | ||
920 | if (put->rpos1 < sizeof (GNUNET_MessageHeader)) | ||
921 | { | ||
922 | cpy = sizeof (GNUNET_MessageHeader) - put->rpos1; | ||
923 | if (cpy > have) | ||
924 | cpy = have; | ||
925 | memcpy (&put->rbuff1[put->rpos1], &upload_data[poff], cpy); | ||
926 | put->rpos1 += cpy; | ||
927 | have -= cpy; | ||
928 | poff += cpy; | ||
929 | put->rpos2 = 0; | ||
930 | } | ||
931 | if (put->rpos1 < sizeof (GNUNET_MessageHeader)) | ||
932 | break; | ||
933 | hdr = (GNUNET_MessageHeader *) put->rbuff1; | ||
934 | GNUNET_array_grow (put->rbuff2, | ||
935 | put->rsize2, | ||
936 | ntohs (hdr->size) - | ||
937 | sizeof (GNUNET_MessageHeader)); | ||
938 | if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) | ||
939 | { | ||
940 | cpy = | ||
941 | ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) - | ||
942 | put->rpos2; | ||
943 | if (cpy > have) | ||
944 | cpy = have; | ||
945 | memcpy (&put->rbuff2[put->rpos2], &upload_data[poff], cpy); | ||
946 | have -= cpy; | ||
947 | poff += cpy; | ||
948 | put->rpos2 += cpy; | ||
949 | } | ||
950 | if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) | ||
951 | break; | ||
952 | mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); | ||
953 | mp->msg = put->rbuff2; | ||
954 | mp->sender = httpSession->sender; | ||
955 | mp->tsession = httpSession->tsession; | ||
956 | mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader); | ||
957 | #if DEBUG_HTTP | ||
958 | GNUNET_GE_LOG (coreAPI->ectx, | ||
959 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
960 | "HTTP/MHD passes %u bytes to core (received via PUT request).\n", | ||
961 | mp->size); | ||
962 | #endif | ||
963 | coreAPI->receive (mp); | ||
964 | put->rbuff2 = NULL; | ||
965 | put->rpos2 = 0; | ||
966 | put->rsize2 = 0; | ||
967 | put->rpos1 = 0; | ||
968 | put->ready = GNUNET_YES; | ||
969 | } | ||
970 | GNUNET_mutex_unlock (lock); | ||
971 | return MHD_YES; | ||
972 | } | ||
973 | GNUNET_mutex_unlock (lock); | ||
974 | GNUNET_GE_BREAK_OP (NULL, 0); /* invalid request */ | ||
975 | return MHD_NO; | ||
976 | } | ||
977 | |||
978 | #if DO_GET | ||
979 | /** | ||
980 | * Process downloaded bits (from GET via CURL). | ||
981 | */ | ||
982 | static size_t | ||
983 | receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx) | ||
984 | { | ||
985 | HTTPSession *httpSession = ctx; | ||
986 | const char *inbuf = ptr; | ||
987 | size_t have = size * nmemb; | ||
988 | size_t poff = 0; | ||
989 | size_t cpy; | ||
990 | GNUNET_MessageHeader *hdr; | ||
991 | GNUNET_TransportPacket *mp; | ||
992 | |||
993 | if (stats != NULL) | ||
994 | stats->change (stat_curl_receive_callbacks, 1); | ||
995 | httpSession->cs.client.last_get_activity = GNUNET_get_time (); | ||
996 | #if DEBUG_HTTP | ||
997 | GNUNET_GE_LOG (coreAPI->ectx, | ||
998 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
999 | "HTTP/CURL receives %u bytes as response to GET.\n", | ||
1000 | size * nmemb); | ||
1001 | #endif | ||
1002 | while (have > 0) | ||
1003 | { | ||
1004 | if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader)) | ||
1005 | { | ||
1006 | cpy = sizeof (GNUNET_MessageHeader) - httpSession->cs.client.rpos1; | ||
1007 | if (cpy > have) | ||
1008 | cpy = have; | ||
1009 | memcpy (&httpSession->cs.client. | ||
1010 | rbuff1[httpSession->cs.client.rpos1], &inbuf[poff], cpy); | ||
1011 | httpSession->cs.client.rpos1 += cpy; | ||
1012 | have -= cpy; | ||
1013 | poff += cpy; | ||
1014 | httpSession->cs.client.rpos2 = 0; | ||
1015 | } | ||
1016 | if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader)) | ||
1017 | break; | ||
1018 | hdr = (GNUNET_MessageHeader *) httpSession->cs.client.rbuff1; | ||
1019 | GNUNET_array_grow (httpSession->cs.client.rbuff2, | ||
1020 | httpSession->cs.client.rsize2, | ||
1021 | ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)); | ||
1022 | if (httpSession->cs.client.rpos2 < | ||
1023 | ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) | ||
1024 | { | ||
1025 | cpy = | ||
1026 | ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) - | ||
1027 | httpSession->cs.client.rpos2; | ||
1028 | if (cpy > have) | ||
1029 | cpy = have; | ||
1030 | memcpy (&httpSession->cs.client. | ||
1031 | rbuff2[httpSession->cs.client.rpos2], &inbuf[poff], cpy); | ||
1032 | have -= cpy; | ||
1033 | poff += cpy; | ||
1034 | httpSession->cs.client.rpos2 += cpy; | ||
1035 | } | ||
1036 | if (httpSession->cs.client.rpos2 < | ||
1037 | ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) | ||
1038 | break; | ||
1039 | mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); | ||
1040 | mp->msg = httpSession->cs.client.rbuff2; | ||
1041 | mp->sender = httpSession->sender; | ||
1042 | mp->tsession = httpSession->tsession; | ||
1043 | mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader); | ||
1044 | coreAPI->receive (mp); | ||
1045 | httpSession->cs.client.rbuff2 = NULL; | ||
1046 | httpSession->cs.client.rpos2 = 0; | ||
1047 | httpSession->cs.client.rsize2 = 0; | ||
1048 | httpSession->cs.client.rpos1 = 0; | ||
1049 | } | ||
1050 | if (stats != NULL) | ||
1051 | stats->change (stat_bytesReceived, size * nmemb); | ||
1052 | return size * nmemb; | ||
1053 | } | ||
1054 | #endif | ||
1055 | |||
1056 | /** | ||
1057 | * Provide bits for upload: we're using CURL for a PUT request | ||
1058 | * and now need to provide data from the message we are transmitting. | ||
1059 | */ | ||
1060 | static size_t | ||
1061 | sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx) | ||
1062 | { | ||
1063 | struct HTTPPutData *put = ctx; | ||
1064 | size_t max = size * nmemb; | ||
1065 | |||
1066 | if (stats != NULL) | ||
1067 | stats->change (stat_curl_send_callbacks, 1); | ||
1068 | put->last_activity = GNUNET_get_time (); | ||
1069 | if (max > put->size - put->pos) | ||
1070 | max = put->size - put->pos; | ||
1071 | memcpy (ptr, &put->msg[put->pos], max); | ||
1072 | put->pos += max; | ||
1073 | #if DEBUG_HTTP | ||
1074 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1075 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1076 | "HTTP/CURL sends %u bytes in PUT request.\n", max); | ||
1077 | #endif | ||
1078 | if (stats != NULL) | ||
1079 | stats->change (stat_bytesSent, max); | ||
1080 | return max; | ||
1081 | } | ||
1082 | |||
1083 | #define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0); | ||
1084 | #define IP_BUF_LEN 128 | ||
1085 | |||
1086 | static void | ||
1087 | create_session_url (HTTPSession * httpSession) | ||
1088 | { | ||
1089 | char buf[IP_BUF_LEN]; | ||
1090 | char *url; | ||
1091 | GNUNET_EncName enc; | ||
1092 | unsigned short available; | ||
1093 | const char *obr; | ||
1094 | const char *cbr; | ||
1095 | const HostAddress *haddr = | ||
1096 | (const HostAddress *) &httpSession->cs.client.address; | ||
1097 | |||
1098 | url = httpSession->cs.client.url; | ||
1099 | if (url == NULL) | ||
1100 | { | ||
1101 | GNUNET_hash_to_enc (&coreAPI->my_identity->hashPubKey, &enc); | ||
1102 | available = ntohs (haddr->availability) & available_protocols; | ||
1103 | if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6)) | ||
1104 | { | ||
1105 | if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0) | ||
1106 | available = VERSION_AVAILABLE_IPV4; | ||
1107 | else | ||
1108 | available = VERSION_AVAILABLE_IPV6; | ||
1109 | } | ||
1110 | if ((available & VERSION_AVAILABLE_IPV4) > 0) | ||
1111 | { | ||
1112 | if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN)) | ||
1113 | { | ||
1114 | /* log? */ | ||
1115 | return; | ||
1116 | } | ||
1117 | obr = ""; | ||
1118 | cbr = ""; | ||
1119 | } | ||
1120 | else if ((available & VERSION_AVAILABLE_IPV6) > 0) | ||
1121 | { | ||
1122 | if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN)) | ||
1123 | { | ||
1124 | /* log? */ | ||
1125 | return; | ||
1126 | } | ||
1127 | obr = "["; | ||
1128 | cbr = "]"; | ||
1129 | } | ||
1130 | else | ||
1131 | return; /* error */ | ||
1132 | url = GNUNET_malloc (64 + sizeof (GNUNET_EncName) + strlen (buf)); | ||
1133 | GNUNET_snprintf (url, | ||
1134 | 64 + sizeof (GNUNET_EncName), | ||
1135 | "http://%s%s%s:%u/%s", obr, buf, cbr, | ||
1136 | ntohs (haddr->port), &enc); | ||
1137 | httpSession->cs.client.url = url; | ||
1138 | } | ||
1139 | } | ||
1140 | |||
1141 | #if DO_GET | ||
1142 | /** | ||
1143 | * Try to do a GET on the other peer of the given | ||
1144 | * http session. | ||
1145 | * | ||
1146 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
1147 | */ | ||
1148 | static int | ||
1149 | create_curl_get (HTTPSession * httpSession) | ||
1150 | { | ||
1151 | CURL *curl_get; | ||
1152 | CURLcode ret; | ||
1153 | CURLMcode mret; | ||
1154 | GNUNET_CronTime now; | ||
1155 | |||
1156 | if (httpSession->cs.client.url == NULL) | ||
1157 | return GNUNET_SYSERR; | ||
1158 | curl_get = httpSession->cs.client.get; | ||
1159 | if (curl_get != NULL) | ||
1160 | { | ||
1161 | GNUNET_mutex_lock (lock); | ||
1162 | curl_multi_remove_handle (curl_multi, curl_get); | ||
1163 | http_requests_pending--; | ||
1164 | signal_select (); | ||
1165 | curl_easy_cleanup (curl_get); | ||
1166 | GNUNET_mutex_unlock (lock); | ||
1167 | httpSession->cs.client.get = NULL; | ||
1168 | } | ||
1169 | curl_get = curl_easy_init (); | ||
1170 | if (curl_get == NULL) | ||
1171 | return GNUNET_SYSERR; | ||
1172 | /* create GET */ | ||
1173 | CURL_EASY_SETOPT (curl_get, CURLOPT_FAILONERROR, 1); | ||
1174 | CURL_EASY_SETOPT (curl_get, CURLOPT_URL, httpSession->cs.client.url); | ||
1175 | if (strlen (proxy) > 0) | ||
1176 | CURL_EASY_SETOPT (curl_get, CURLOPT_PROXY, proxy); | ||
1177 | CURL_EASY_SETOPT (curl_get, CURLOPT_BUFFERSIZE, 32 * 1024); | ||
1178 | if (0 == strncmp (httpSession->cs.client.url, "http", 4)) | ||
1179 | CURL_EASY_SETOPT (curl_get, CURLOPT_USERAGENT, "GNUnet-http"); | ||
1180 | #if 0 | ||
1181 | CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1); | ||
1182 | #endif | ||
1183 | CURL_EASY_SETOPT (curl_get, CURLOPT_CONNECTTIMEOUT, 150L); | ||
1184 | /* NOTE: use of CONNECTTIMEOUT without also | ||
1185 | setting NOSIGNAL results in really weird | ||
1186 | crashes on my system! */ | ||
1187 | CURL_EASY_SETOPT (curl_get, CURLOPT_NOSIGNAL, 1); | ||
1188 | CURL_EASY_SETOPT (curl_get, CURLOPT_TIMEOUT, 150L); | ||
1189 | CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEFUNCTION, &receiveContentCallback); | ||
1190 | CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEDATA, httpSession); | ||
1191 | CURL_EASY_SETOPT (curl_get, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | ||
1192 | if (ret != CURLE_OK) | ||
1193 | { | ||
1194 | curl_easy_cleanup (curl_get); | ||
1195 | return GNUNET_SYSERR; | ||
1196 | } | ||
1197 | GNUNET_mutex_lock (lock); | ||
1198 | mret = curl_multi_add_handle (curl_multi, curl_get); | ||
1199 | http_requests_pending++; | ||
1200 | GNUNET_mutex_unlock (lock); | ||
1201 | if (stats != NULL) | ||
1202 | stats->change (stat_get_issued, 1); | ||
1203 | if (mret != CURLM_OK) | ||
1204 | { | ||
1205 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1206 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
1207 | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), | ||
1208 | "curl_multi_add_handle", __FILE__, __LINE__, | ||
1209 | curl_multi_strerror (mret)); | ||
1210 | curl_easy_cleanup (curl_get); | ||
1211 | return GNUNET_SYSERR; | ||
1212 | } | ||
1213 | signal_select (); | ||
1214 | now = GNUNET_get_time (); | ||
1215 | httpSession->cs.client.last_get_activity = now; | ||
1216 | httpSession->cs.client.get = curl_get; | ||
1217 | httpSession->cs.client.last_get_initiated = now; | ||
1218 | #if DEBUG_HTTP | ||
1219 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1220 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1221 | "HTTP/CURL initiated GET request.\n"); | ||
1222 | #endif | ||
1223 | return GNUNET_OK; | ||
1224 | } | ||
1225 | #endif | ||
1226 | |||
1227 | /** | ||
1228 | * Establish a connection to a remote node. | ||
1229 | * | ||
1230 | * @param hello the hello-Message for the target node | ||
1231 | * @param tsessionPtr the session handle that is set | ||
1232 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
1233 | */ | ||
1234 | static int | ||
1235 | httpConnect (const GNUNET_MessageHello * hello, | ||
1236 | GNUNET_TSession ** tsessionPtr, int may_reuse) | ||
1237 | { | ||
1238 | const HostAddress *haddr = (const HostAddress *) &hello[1]; | ||
1239 | GNUNET_TSession *tsession; | ||
1240 | HTTPSession *httpSession; | ||
1241 | int i; | ||
1242 | |||
1243 | if (stats != NULL) | ||
1244 | stats->change (stat_connect_calls, 1); | ||
1245 | /* check if we have a session pending for this peer */ | ||
1246 | tsession = NULL; | ||
1247 | if (may_reuse) | ||
1248 | { | ||
1249 | GNUNET_mutex_lock (lock); | ||
1250 | for (i = 0; i < tsessionCount; i++) | ||
1251 | { | ||
1252 | if (0 == memcmp (&hello->senderIdentity, | ||
1253 | &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity))) | ||
1254 | { | ||
1255 | tsession = tsessions[i]; | ||
1256 | break; | ||
1257 | } | ||
1258 | } | ||
1259 | if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession))) | ||
1260 | { | ||
1261 | *tsessionPtr = tsession; | ||
1262 | GNUNET_mutex_unlock (lock); | ||
1263 | return GNUNET_OK; | ||
1264 | } | ||
1265 | GNUNET_mutex_unlock (lock); | ||
1266 | } | ||
1267 | /* no session pending, initiate a new one! */ | ||
1268 | httpSession = GNUNET_malloc (sizeof (HTTPSession)); | ||
1269 | memset (httpSession, 0, sizeof (HTTPSession)); | ||
1270 | httpSession->sender = hello->senderIdentity; | ||
1271 | httpSession->users = 1; /* us only, core has not seen this tsession! */ | ||
1272 | httpSession->is_client = GNUNET_YES; | ||
1273 | httpSession->cs.client.address = *haddr; | ||
1274 | tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); | ||
1275 | memset (tsession, 0, sizeof (GNUNET_TSession)); | ||
1276 | httpSession->tsession = tsession; | ||
1277 | tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; | ||
1278 | tsession->internal = httpSession; | ||
1279 | tsession->peer = hello->senderIdentity; | ||
1280 | create_session_url (httpSession); | ||
1281 | #if DO_GET | ||
1282 | if (GNUNET_OK != create_curl_get (httpSession)) | ||
1283 | { | ||
1284 | GNUNET_free (tsession); | ||
1285 | GNUNET_free (httpSession); | ||
1286 | return GNUNET_SYSERR; | ||
1287 | } | ||
1288 | #endif | ||
1289 | /* PUTs will be created as needed */ | ||
1290 | addTSession (tsession); | ||
1291 | *tsessionPtr = tsession; | ||
1292 | #if DEBUG_HTTP | ||
1293 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1294 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1295 | "HTTP/CURL initiated connection to `%s'.\n", | ||
1296 | httpSession->cs.client.url); | ||
1297 | #endif | ||
1298 | return GNUNET_OK; | ||
1299 | } | ||
1300 | |||
1301 | /** | ||
1302 | * We received the "Thank you!" response to a PUT. | ||
1303 | * Discard the data (not useful) and mark the PUT | ||
1304 | * operation as completed. | ||
1305 | */ | ||
1306 | static size_t | ||
1307 | discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls) | ||
1308 | { | ||
1309 | struct HTTPPutData *put = put_cls; | ||
1310 | /* this condition should pretty much always be | ||
1311 | true; just checking here in case the PUT | ||
1312 | response comes early somehow */ | ||
1313 | if (put->pos == put->size) | ||
1314 | put->done = GNUNET_YES; | ||
1315 | return size * nmemb; | ||
1316 | } | ||
1317 | |||
1318 | /** | ||
1319 | * Create a new PUT request for the given PUT data. | ||
1320 | */ | ||
1321 | static int | ||
1322 | create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put) | ||
1323 | { | ||
1324 | CURL *curl_put; | ||
1325 | CURLcode ret; | ||
1326 | CURLMcode mret; | ||
1327 | long size; | ||
1328 | |||
1329 | /* we should have initiated a GET earlier, | ||
1330 | so URL must not be NULL here */ | ||
1331 | if (httpSession->cs.client.url == NULL) | ||
1332 | return GNUNET_SYSERR; | ||
1333 | curl_put = curl_easy_init (); | ||
1334 | if (curl_put == NULL) | ||
1335 | return GNUNET_SYSERR; | ||
1336 | CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1); | ||
1337 | CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url); | ||
1338 | if (strlen (proxy) > 0) | ||
1339 | CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy); | ||
1340 | CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size); | ||
1341 | if (0 == strncmp (httpSession->cs.client.url, "http", 4)) | ||
1342 | CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http"); | ||
1343 | CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1); | ||
1344 | #if 0 | ||
1345 | CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1); | ||
1346 | #endif | ||
1347 | CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L); | ||
1348 | /* NOTE: use of CONNECTTIMEOUT without also | ||
1349 | setting NOSIGNAL results in really weird | ||
1350 | crashes on my system! */ | ||
1351 | CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1); | ||
1352 | CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L); | ||
1353 | size = put->size; | ||
1354 | CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size); | ||
1355 | CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback); | ||
1356 | CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put); | ||
1357 | CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback); | ||
1358 | CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put); | ||
1359 | CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | ||
1360 | if (ret != CURLE_OK) | ||
1361 | { | ||
1362 | curl_easy_cleanup (curl_put); | ||
1363 | return GNUNET_SYSERR; | ||
1364 | } | ||
1365 | GNUNET_mutex_lock (lock); | ||
1366 | mret = curl_multi_add_handle (curl_multi, curl_put); | ||
1367 | http_requests_pending++; | ||
1368 | GNUNET_mutex_unlock (lock); | ||
1369 | if (stats != NULL) | ||
1370 | stats->change (stat_put_issued, 1); | ||
1371 | if (mret != CURLM_OK) | ||
1372 | { | ||
1373 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1374 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
1375 | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), | ||
1376 | "curl_multi_add_handle", __FILE__, __LINE__, | ||
1377 | curl_multi_strerror (mret)); | ||
1378 | return GNUNET_SYSERR; | ||
1379 | } | ||
1380 | signal_select (); | ||
1381 | put->curl_put = curl_put; | ||
1382 | #if DEBUG_HTTP | ||
1383 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1384 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1385 | "HTTP/CURL initiated PUT request to `%s'.\n", | ||
1386 | httpSession->cs.client.url); | ||
1387 | #endif | ||
1388 | return GNUNET_OK; | ||
1389 | } | ||
1390 | |||
1391 | |||
1392 | /** | ||
1393 | * Test if the transport would even try to send | ||
1394 | * a message of the given size and importance | ||
1395 | * for the given session.<br> | ||
1396 | * This function is used to check if the core should | ||
1397 | * even bother to construct (and encrypt) this kind | ||
1398 | * of message. | ||
1399 | * | ||
1400 | * @return GNUNET_YES if the transport would try (i.e. queue | ||
1401 | * the message or call the OS to send), | ||
1402 | * GNUNET_NO if the transport would just drop the message, | ||
1403 | * GNUNET_SYSERR if the size/session is invalid | ||
1404 | */ | ||
1405 | static int | ||
1406 | httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size, | ||
1407 | int important) | ||
1408 | { | ||
1409 | HTTPSession *httpSession = tsession->internal; | ||
1410 | struct MHDGetData *get; | ||
1411 | int ret; | ||
1412 | |||
1413 | if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader)) | ||
1414 | { | ||
1415 | GNUNET_GE_BREAK (coreAPI->ectx, 0); | ||
1416 | return GNUNET_SYSERR; | ||
1417 | } | ||
1418 | if (size == 0) | ||
1419 | { | ||
1420 | GNUNET_GE_BREAK (coreAPI->ectx, 0); | ||
1421 | return GNUNET_SYSERR; | ||
1422 | } | ||
1423 | if (httpSession->is_client) | ||
1424 | { | ||
1425 | /* client */ | ||
1426 | if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL)) | ||
1427 | return GNUNET_NO; | ||
1428 | return GNUNET_YES; | ||
1429 | } | ||
1430 | else | ||
1431 | { | ||
1432 | /* server */ | ||
1433 | GNUNET_mutex_lock (lock); | ||
1434 | get = httpSession->cs.server.gets; | ||
1435 | if (get == NULL) | ||
1436 | ret = GNUNET_NO; | ||
1437 | else | ||
1438 | { | ||
1439 | if (get->wsize == 0) | ||
1440 | ret = GNUNET_YES; | ||
1441 | else if ((get->wpos + size > get->wsize) | ||
1442 | && (important != GNUNET_YES)) | ||
1443 | ret = GNUNET_NO; | ||
1444 | else | ||
1445 | ret = GNUNET_YES; | ||
1446 | } | ||
1447 | GNUNET_mutex_unlock (lock); | ||
1448 | return ret; | ||
1449 | } | ||
1450 | } | ||
1451 | |||
1452 | |||
1453 | /** | ||
1454 | * Send a message to the specified remote node. | ||
1455 | * | ||
1456 | * @param tsession the GNUNET_MessageHello identifying the remote node | ||
1457 | * @param msg the message | ||
1458 | * @param size the size of the message | ||
1459 | * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full | ||
1460 | */ | ||
1461 | static int | ||
1462 | httpSend (GNUNET_TSession * tsession, | ||
1463 | const void *msg, unsigned int size, int important) | ||
1464 | { | ||
1465 | HTTPSession *httpSession = tsession->internal; | ||
1466 | struct HTTPPutData *putData; | ||
1467 | GNUNET_MessageHeader *hdr; | ||
1468 | #if DO_GET | ||
1469 | struct MHDGetData *getData; | ||
1470 | char *tmp; | ||
1471 | #endif | ||
1472 | |||
1473 | if (stats != NULL) | ||
1474 | stats->change (stat_send_calls, 1); | ||
1475 | if (httpSession->is_client) | ||
1476 | { | ||
1477 | /* we need to do a PUT (we are the client) */ | ||
1478 | if (size >= GNUNET_MAX_BUFFER_SIZE) | ||
1479 | return GNUNET_SYSERR; | ||
1480 | if (size == 0) | ||
1481 | { | ||
1482 | GNUNET_GE_BREAK (NULL, 0); | ||
1483 | return GNUNET_SYSERR; | ||
1484 | } | ||
1485 | if (important != GNUNET_YES) | ||
1486 | { | ||
1487 | GNUNET_mutex_lock (lock); | ||
1488 | if (httpSession->cs.client.puts != NULL) | ||
1489 | { | ||
1490 | /* do not queue more than one unimportant PUT at a time */ | ||
1491 | signal_select (); /* do clean up now! */ | ||
1492 | GNUNET_mutex_unlock (lock); | ||
1493 | if (stats != NULL) | ||
1494 | stats->change (stat_bytesDropped, size); | ||
1495 | |||
1496 | return GNUNET_NO; | ||
1497 | } | ||
1498 | GNUNET_mutex_unlock (lock); | ||
1499 | } | ||
1500 | putData = GNUNET_malloc (sizeof (struct HTTPPutData)); | ||
1501 | memset (putData, 0, sizeof (struct HTTPPutData)); | ||
1502 | putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader)); | ||
1503 | hdr = (GNUNET_MessageHeader *) putData->msg; | ||
1504 | hdr->size = htons (size + sizeof (GNUNET_MessageHeader)); | ||
1505 | hdr->type = htons (0); | ||
1506 | memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size); | ||
1507 | putData->size = size + sizeof (GNUNET_MessageHeader); | ||
1508 | putData->last_activity = GNUNET_get_time (); | ||
1509 | if (GNUNET_OK != create_curl_put (httpSession, putData)) | ||
1510 | { | ||
1511 | GNUNET_free (putData->msg); | ||
1512 | GNUNET_free (putData); | ||
1513 | return GNUNET_SYSERR; | ||
1514 | } | ||
1515 | GNUNET_mutex_lock (lock); | ||
1516 | putData->next = httpSession->cs.client.puts; | ||
1517 | httpSession->cs.client.puts = putData; | ||
1518 | GNUNET_mutex_unlock (lock); | ||
1519 | return GNUNET_OK; | ||
1520 | } | ||
1521 | |||
1522 | /* httpSession->isClient == false, respond to a GET (we | ||
1523 | hopefully have one or will have one soon) */ | ||
1524 | #if DEBUG_HTTP | ||
1525 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1526 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1527 | "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n", | ||
1528 | size); | ||
1529 | #endif | ||
1530 | #if DO_GET | ||
1531 | GNUNET_mutex_lock (lock); | ||
1532 | getData = httpSession->cs.server.gets; | ||
1533 | if (getData == NULL) | ||
1534 | { | ||
1535 | GNUNET_mutex_unlock (lock); | ||
1536 | return GNUNET_SYSERR; | ||
1537 | } | ||
1538 | if (getData->wsize == 0) | ||
1539 | GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE); | ||
1540 | size += sizeof (GNUNET_MessageHeader); | ||
1541 | if (getData->wpos + size > getData->wsize) | ||
1542 | { | ||
1543 | /* need to grow or discard */ | ||
1544 | if (!important) | ||
1545 | { | ||
1546 | GNUNET_mutex_unlock (lock); | ||
1547 | return GNUNET_NO; | ||
1548 | } | ||
1549 | tmp = GNUNET_malloc (getData->wpos + size); | ||
1550 | memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos); | ||
1551 | hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos]; | ||
1552 | hdr->type = htons (0); | ||
1553 | hdr->size = htons (size); | ||
1554 | memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader)); | ||
1555 | GNUNET_free (getData->wbuff); | ||
1556 | getData->wbuff = tmp; | ||
1557 | getData->wsize = getData->wpos + size; | ||
1558 | getData->woff = 0; | ||
1559 | getData->wpos = getData->wpos + size; | ||
1560 | } | ||
1561 | else | ||
1562 | { | ||
1563 | /* fits without growing */ | ||
1564 | if (getData->wpos + getData->woff + size > getData->wsize) | ||
1565 | { | ||
1566 | /* need to compact first */ | ||
1567 | memmove (getData->wbuff, | ||
1568 | &getData->wbuff[getData->woff], getData->wpos); | ||
1569 | getData->woff = 0; | ||
1570 | } | ||
1571 | /* append */ | ||
1572 | hdr = | ||
1573 | (GNUNET_MessageHeader *) & getData->wbuff[getData->woff + | ||
1574 | getData->wpos]; | ||
1575 | hdr->size = htons (size); | ||
1576 | hdr->type = htons (0); | ||
1577 | memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader)); | ||
1578 | getData->wpos += size; | ||
1579 | } | ||
1580 | signal_select (); | ||
1581 | GNUNET_mutex_unlock (lock); | ||
1582 | #endif | ||
1583 | return GNUNET_OK; | ||
1584 | } | ||
1585 | |||
1586 | /** | ||
1587 | * Function called to cleanup dead connections | ||
1588 | * (completed PUTs, GETs that have timed out, | ||
1589 | * etc.). Also re-vives GETs that have timed out | ||
1590 | * if we are still interested in the connection. | ||
1591 | */ | ||
1592 | static void | ||
1593 | cleanup_connections () | ||
1594 | { | ||
1595 | int i; | ||
1596 | HTTPSession *s; | ||
1597 | struct HTTPPutData *prev; | ||
1598 | struct HTTPPutData *pos; | ||
1599 | struct MHDPutData *mpos; | ||
1600 | struct MHDPutData *mprev; | ||
1601 | #if DO_GET | ||
1602 | struct MHD_Response *r; | ||
1603 | struct MHDGetData *gpos; | ||
1604 | struct MHDGetData *gnext; | ||
1605 | #endif | ||
1606 | GNUNET_CronTime now; | ||
1607 | |||
1608 | GNUNET_mutex_lock (lock); | ||
1609 | now = GNUNET_get_time (); | ||
1610 | for (i = 0; i < tsessionCount; i++) | ||
1611 | { | ||
1612 | s = tsessions[i]->internal; | ||
1613 | if (s->is_client) | ||
1614 | { | ||
1615 | if ((s->cs.client.puts == NULL) && (s->users == 0) | ||
1616 | #if DO_GET | ||
1617 | && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now) | ||
1618 | #endif | ||
1619 | ) | ||
1620 | { | ||
1621 | #if DO_GET | ||
1622 | #if DEBUG_HTTP | ||
1623 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1624 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | | ||
1625 | GNUNET_GE_USER, | ||
1626 | "HTTP transport destroys old (%llu ms) unused client session\n", | ||
1627 | now - s->cs.client.last_get_activity); | ||
1628 | #endif | ||
1629 | #endif | ||
1630 | destroy_tsession (tsessions[i]); | ||
1631 | i--; | ||
1632 | continue; | ||
1633 | } | ||
1634 | |||
1635 | prev = NULL; | ||
1636 | pos = s->cs.client.puts; | ||
1637 | while (pos != NULL) | ||
1638 | { | ||
1639 | if (pos->last_activity + HTTP_TIMEOUT < now) | ||
1640 | pos->done = GNUNET_YES; | ||
1641 | if (pos->done) | ||
1642 | { | ||
1643 | if (prev == NULL) | ||
1644 | s->cs.client.puts = pos->next; | ||
1645 | else | ||
1646 | prev->next = pos->next; | ||
1647 | GNUNET_free (pos->msg); | ||
1648 | curl_multi_remove_handle (curl_multi, pos->curl_put); | ||
1649 | http_requests_pending--; | ||
1650 | signal_select (); | ||
1651 | curl_easy_cleanup (pos->curl_put); | ||
1652 | GNUNET_free (pos); | ||
1653 | if (prev == NULL) | ||
1654 | pos = s->cs.client.puts; | ||
1655 | else | ||
1656 | pos = prev->next; | ||
1657 | continue; | ||
1658 | } | ||
1659 | prev = pos; | ||
1660 | pos = pos->next; | ||
1661 | } | ||
1662 | #if DO_GET | ||
1663 | if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) && | ||
1664 | ((s->users > 0) || (s->cs.client.puts != NULL)) && | ||
1665 | ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) || | ||
1666 | (s->cs.client.get == NULL)) && | ||
1667 | ((s->cs.client.get == NULL) || | ||
1668 | (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now))) | ||
1669 | create_curl_get (s); | ||
1670 | #endif | ||
1671 | } | ||
1672 | else | ||
1673 | { | ||
1674 | mpos = s->cs.server.puts; | ||
1675 | mprev = NULL; | ||
1676 | while (mpos != NULL) | ||
1677 | { | ||
1678 | if (mpos->last_activity == 0) | ||
1679 | { | ||
1680 | if (mprev == NULL) | ||
1681 | s->cs.server.puts = mpos->next; | ||
1682 | else | ||
1683 | mprev->next = mpos->next; | ||
1684 | GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0); | ||
1685 | GNUNET_free (mpos); | ||
1686 | if (mprev == NULL) | ||
1687 | mpos = s->cs.server.puts; | ||
1688 | else | ||
1689 | mpos = mprev->next; | ||
1690 | continue; | ||
1691 | } | ||
1692 | mprev = mpos; | ||
1693 | mpos = mpos->next; | ||
1694 | } | ||
1695 | |||
1696 | /* ! s->is_client */ | ||
1697 | #if DO_GET | ||
1698 | gpos = s->cs.server.gets; | ||
1699 | while (gpos != NULL) | ||
1700 | { | ||
1701 | gnext = gpos->next; | ||
1702 | gpos->next = NULL; | ||
1703 | if ((gpos->last_get_activity + HTTP_TIMEOUT < now) || | ||
1704 | (gpos != s->cs.server.gets)) | ||
1705 | { | ||
1706 | if (gpos == s->cs.server.gets) | ||
1707 | s->cs.server.gets = NULL; | ||
1708 | r = gpos->get; | ||
1709 | gpos->get = NULL; | ||
1710 | MHD_destroy_response (r); | ||
1711 | } | ||
1712 | gpos = gnext; | ||
1713 | } | ||
1714 | #endif | ||
1715 | if ( | ||
1716 | #if DO_GET | ||
1717 | (s->cs.server.gets == NULL) && | ||
1718 | #endif | ||
1719 | (s->is_mhd_active == 0) && (s->users == 0)) | ||
1720 | { | ||
1721 | #if DO_GET | ||
1722 | #if DEBUG_HTTP | ||
1723 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1724 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | | ||
1725 | GNUNET_GE_USER, | ||
1726 | "HTTP transport destroys unused server session\n"); | ||
1727 | #endif | ||
1728 | #endif | ||
1729 | destroy_tsession (tsessions[i]); | ||
1730 | i--; | ||
1731 | continue; | ||
1732 | } | ||
1733 | } | ||
1734 | } | ||
1735 | GNUNET_mutex_unlock (lock); | ||
1736 | } | ||
1737 | |||
1738 | /** | ||
1739 | * Thread that runs the CURL and MHD requests. | ||
1740 | */ | ||
1741 | static void * | ||
1742 | curl_runner (void *unused) | ||
1743 | { | ||
1744 | CURLMcode mret; | ||
1745 | fd_set rs; | ||
1746 | fd_set ws; | ||
1747 | fd_set es; | ||
1748 | int max; | ||
1749 | struct timeval tv; | ||
1750 | int running; | ||
1751 | unsigned long long timeout; | ||
1752 | long ms; | ||
1753 | int have_tv; | ||
1754 | char buf[128]; /* for reading from pipe */ | ||
1755 | int ret; | ||
1756 | |||
1757 | #if DEBUG_HTTP | ||
1758 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1759 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1760 | "HTTP transport select thread started\n"); | ||
1761 | #endif | ||
1762 | while (GNUNET_YES == http_running) | ||
1763 | { | ||
1764 | max = 0; | ||
1765 | FD_ZERO (&rs); | ||
1766 | FD_ZERO (&ws); | ||
1767 | FD_ZERO (&es); | ||
1768 | GNUNET_mutex_lock (lock); | ||
1769 | mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max); | ||
1770 | GNUNET_mutex_unlock (lock); | ||
1771 | if (mret != CURLM_OK) | ||
1772 | { | ||
1773 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1774 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
1775 | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), | ||
1776 | "curl_multi_fdset", __FILE__, __LINE__, | ||
1777 | curl_multi_strerror (mret)); | ||
1778 | break; | ||
1779 | } | ||
1780 | if (mhd_daemon != NULL) | ||
1781 | MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max); | ||
1782 | timeout = 0; | ||
1783 | have_tv = MHD_NO; | ||
1784 | if (mhd_daemon != NULL) | ||
1785 | have_tv = MHD_get_timeout (mhd_daemon, &timeout); | ||
1786 | GNUNET_mutex_lock (lock); | ||
1787 | if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) && | ||
1788 | (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO))) | ||
1789 | { | ||
1790 | timeout = ms; | ||
1791 | have_tv = MHD_YES; | ||
1792 | } | ||
1793 | GNUNET_mutex_unlock (lock); | ||
1794 | FD_SET (signal_pipe[0], &rs); | ||
1795 | if (max < signal_pipe[0]) | ||
1796 | max = signal_pipe[0]; | ||
1797 | tv.tv_sec = timeout / 1000; | ||
1798 | tv.tv_usec = (timeout % 1000) * 1000; | ||
1799 | if (stats != NULL) | ||
1800 | stats->change (stat_select_calls, 1); | ||
1801 | ret = | ||
1802 | SELECT (max + 1, &rs, &ws, &es, (have_tv == MHD_YES) ? &tv : NULL); | ||
1803 | if (ret == -1) | ||
1804 | { | ||
1805 | GNUNET_GE_LOG_STRERROR (coreAPI->ectx, | ||
1806 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | | ||
1807 | GNUNET_GE_DEVELOPER, "select"); | ||
1808 | } | ||
1809 | if (GNUNET_YES != http_running) | ||
1810 | break; | ||
1811 | running = 0; | ||
1812 | do | ||
1813 | { | ||
1814 | GNUNET_mutex_lock (lock); | ||
1815 | mret = curl_multi_perform (curl_multi, &running); | ||
1816 | GNUNET_mutex_unlock (lock); | ||
1817 | } | ||
1818 | while ((mret == CURLM_CALL_MULTI_PERFORM) | ||
1819 | && (http_running == GNUNET_YES)); | ||
1820 | if (FD_ISSET (signal_pipe[0], &rs)) | ||
1821 | read (signal_pipe[0], buf, sizeof (buf)); | ||
1822 | if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM)) | ||
1823 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1824 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
1825 | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), | ||
1826 | "curl_multi_perform", __FILE__, __LINE__, | ||
1827 | curl_multi_strerror (mret)); | ||
1828 | if (mhd_daemon != NULL) | ||
1829 | MHD_run (mhd_daemon); | ||
1830 | cleanup_connections (); | ||
1831 | } | ||
1832 | #if DEBUG_HTTP | ||
1833 | GNUNET_GE_LOG (coreAPI->ectx, | ||
1834 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
1835 | "HTTP transport select thread exits.\n"); | ||
1836 | #endif | ||
1837 | return NULL; | ||
1838 | } | ||
1839 | |||
1840 | |||
1841 | /** | ||
1842 | * Start the server process to receive inbound traffic. | ||
1843 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
1844 | */ | ||
1845 | static int | ||
1846 | startTransportServer () | ||
1847 | { | ||
1848 | unsigned short port; | ||
1849 | |||
1850 | if ((curl_multi != NULL) || (http_running == GNUNET_YES)) | ||
1851 | return GNUNET_SYSERR; | ||
1852 | curl_multi = curl_multi_init (); | ||
1853 | if (curl_multi == NULL) | ||
1854 | return GNUNET_SYSERR; | ||
1855 | port = get_port (); | ||
1856 | if ((mhd_daemon == NULL) && (port != 0)) | ||
1857 | { | ||
1858 | if (GNUNET_YES != | ||
1859 | GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", | ||
1860 | "DISABLE-IPV6", | ||
1861 | GNUNET_YES)) | ||
1862 | { | ||
1863 | mhd_daemon = MHD_start_daemon (MHD_USE_IPv6, | ||
1864 | port, | ||
1865 | &acceptPolicyCallback, | ||
1866 | NULL, &accessHandlerCallback, NULL, | ||
1867 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1868 | (unsigned int) HTTP_TIMEOUT, | ||
1869 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, | ||
1870 | (unsigned int) 1024 * 128, | ||
1871 | MHD_OPTION_CONNECTION_LIMIT, | ||
1872 | (unsigned int) 128, | ||
1873 | MHD_OPTION_PER_IP_CONNECTION_LIMIT, | ||
1874 | (unsigned int) 8, | ||
1875 | MHD_OPTION_NOTIFY_COMPLETED, | ||
1876 | &requestCompletedCallback, NULL, | ||
1877 | MHD_OPTION_END); | ||
1878 | } | ||
1879 | if (mhd_daemon == NULL) | ||
1880 | { | ||
1881 | /* try without IPv6 */ | ||
1882 | mhd_daemon = MHD_start_daemon (MHD_NO_FLAG, | ||
1883 | port, | ||
1884 | &acceptPolicyCallback, | ||
1885 | NULL, &accessHandlerCallback, NULL, | ||
1886 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1887 | (unsigned int) HTTP_TIMEOUT, | ||
1888 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, | ||
1889 | (unsigned int) 1024 * 128, | ||
1890 | MHD_OPTION_CONNECTION_LIMIT, | ||
1891 | (unsigned int) 128, | ||
1892 | MHD_OPTION_PER_IP_CONNECTION_LIMIT, | ||
1893 | (unsigned int) 8, | ||
1894 | MHD_OPTION_NOTIFY_COMPLETED, | ||
1895 | &requestCompletedCallback, NULL, | ||
1896 | MHD_OPTION_END); | ||
1897 | } | ||
1898 | else | ||
1899 | { | ||
1900 | available_protocols |= VERSION_AVAILABLE_IPV6; | ||
1901 | } | ||
1902 | if (mhd_daemon != NULL) | ||
1903 | available_protocols |= VERSION_AVAILABLE_IPV4; | ||
1904 | } | ||
1905 | if (port == 0) | ||
1906 | { | ||
1907 | /* NAT */ | ||
1908 | available_protocols |= VERSION_AVAILABLE_IPV4; | ||
1909 | if (GNUNET_YES != | ||
1910 | GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", | ||
1911 | "DISABLE-IPV6", | ||
1912 | GNUNET_YES)) | ||
1913 | available_protocols |= VERSION_AVAILABLE_IPV6; | ||
1914 | } | ||
1915 | if (0 != PIPE (signal_pipe)) | ||
1916 | { | ||
1917 | MHD_stop_daemon (mhd_daemon); | ||
1918 | curl_multi_cleanup (curl_multi); | ||
1919 | curl_multi = NULL; | ||
1920 | mhd_daemon = NULL; | ||
1921 | return GNUNET_SYSERR; | ||
1922 | } | ||
1923 | GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[0]); | ||
1924 | GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[1]); | ||
1925 | http_running = GNUNET_YES; | ||
1926 | curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024); | ||
1927 | if (curl_thread == NULL) | ||
1928 | GNUNET_GE_DIE_STRERROR (coreAPI->ectx, | ||
1929 | GNUNET_GE_FATAL | GNUNET_GE_ADMIN | | ||
1930 | GNUNET_GE_IMMEDIATE, "pthread_create"); | ||
1931 | return GNUNET_OK; | ||
1932 | } | ||
1933 | |||
1934 | /** | ||
1935 | * Shutdown the server process (stop receiving inbound | ||
1936 | * traffic). May be restarted later! | ||
1937 | */ | ||
1938 | static int | ||
1939 | stopTransportServer () | ||
1940 | { | ||
1941 | void *unused; | ||
1942 | int i; | ||
1943 | HTTPSession *s; | ||
1944 | |||
1945 | if ((http_running == GNUNET_NO) || (curl_multi == NULL)) | ||
1946 | return GNUNET_SYSERR; | ||
1947 | http_running = GNUNET_NO; | ||
1948 | signal_select (); | ||
1949 | GNUNET_thread_stop_sleep (curl_thread); | ||
1950 | GNUNET_thread_join (curl_thread, &unused); | ||
1951 | CLOSE (signal_pipe[0]); | ||
1952 | CLOSE (signal_pipe[1]); | ||
1953 | if (mhd_daemon != NULL) | ||
1954 | { | ||
1955 | MHD_stop_daemon (mhd_daemon); | ||
1956 | mhd_daemon = NULL; | ||
1957 | } | ||
1958 | cleanup_connections (); | ||
1959 | for (i = 0; i < tsessionCount; i++) | ||
1960 | { | ||
1961 | s = tsessions[i]->internal; | ||
1962 | if (s->users == 0) | ||
1963 | { | ||
1964 | destroy_tsession (tsessions[i]); | ||
1965 | i--; | ||
1966 | } | ||
1967 | } | ||
1968 | curl_multi_cleanup (curl_multi); | ||
1969 | curl_multi = NULL; | ||
1970 | return GNUNET_OK; | ||
1971 | } | ||
1972 | |||
1973 | /* ******************** public API ******************** */ | ||
1974 | |||
1975 | /** | ||
1976 | * The exported method. Makes the core api available | ||
1977 | * via a global and returns the udp transport API. | ||
1978 | */ | ||
1979 | GNUNET_TransportAPI * | ||
1980 | inittransport_http (GNUNET_CoreAPIForTransport * core) | ||
1981 | { | ||
1982 | GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24); | ||
1983 | coreAPI = core; | ||
1984 | cfg = coreAPI->cfg; | ||
1985 | lock = GNUNET_mutex_create (GNUNET_YES); | ||
1986 | if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg, | ||
1987 | &reload_configuration, NULL)) | ||
1988 | { | ||
1989 | GNUNET_mutex_destroy (lock); | ||
1990 | lock = NULL; | ||
1991 | return NULL; | ||
1992 | } | ||
1993 | if (0 != curl_global_init (CURL_GLOBAL_WIN32)) | ||
1994 | { | ||
1995 | GNUNET_GE_BREAK (NULL, 0); | ||
1996 | GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration, | ||
1997 | NULL); | ||
1998 | GNUNET_mutex_destroy (lock); | ||
1999 | lock = NULL; | ||
2000 | return NULL; | ||
2001 | } | ||
2002 | tsessionCount = 0; | ||
2003 | tsessionArrayLength = 0; | ||
2004 | GNUNET_array_grow (tsessions, tsessionArrayLength, 32); | ||
2005 | if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg, | ||
2006 | "HTTP", "UPNP", | ||
2007 | GNUNET_YES) == GNUNET_YES) | ||
2008 | { | ||
2009 | upnp = coreAPI->service_request ("upnp"); | ||
2010 | |||
2011 | if (upnp == NULL) | ||
2012 | { | ||
2013 | GNUNET_GE_LOG (coreAPI->ectx, | ||
2014 | GNUNET_GE_ERROR | GNUNET_GE_USER | | ||
2015 | GNUNET_GE_IMMEDIATE, | ||
2016 | _ | ||
2017 | ("The UPnP service could not be loaded. To disable UPnP, set the " | ||
2018 | "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"), | ||
2019 | "HTTP"); | ||
2020 | } | ||
2021 | } | ||
2022 | stats = coreAPI->service_request ("stats"); | ||
2023 | if (stats != NULL) | ||
2024 | { | ||
2025 | stat_bytesReceived | ||
2026 | = stats->create (gettext_noop ("# bytes received via HTTP")); | ||
2027 | stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP")); | ||
2028 | stat_bytesDropped | ||
2029 | = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)")); | ||
2030 | stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued")); | ||
2031 | stat_get_received | ||
2032 | = stats->create (gettext_noop ("# HTTP GET received")); | ||
2033 | stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued")); | ||
2034 | stat_put_received | ||
2035 | = stats->create (gettext_noop ("# HTTP PUT received")); | ||
2036 | stat_select_calls | ||
2037 | = stats->create (gettext_noop ("# HTTP select calls")); | ||
2038 | |||
2039 | stat_send_calls = stats->create (gettext_noop ("# HTTP send calls")); | ||
2040 | |||
2041 | stat_curl_send_callbacks | ||
2042 | = stats->create (gettext_noop ("# HTTP curl send callbacks")); | ||
2043 | stat_curl_receive_callbacks | ||
2044 | = stats->create (gettext_noop ("# HTTP curl receive callbacks")); | ||
2045 | stat_mhd_access_callbacks | ||
2046 | = stats->create (gettext_noop ("# HTTP mhd access callbacks")); | ||
2047 | stat_mhd_read_callbacks | ||
2048 | = stats->create (gettext_noop ("# HTTP mhd read callbacks")); | ||
2049 | stat_mhd_close_callbacks | ||
2050 | = stats->create (gettext_noop ("# HTTP mhd close callbacks")); | ||
2051 | stat_connect_calls | ||
2052 | = stats->create (gettext_noop ("# HTTP connect calls")); | ||
2053 | } | ||
2054 | GNUNET_GC_get_configuration_value_string (coreAPI->cfg, | ||
2055 | "GNUNETD", "HTTP-PROXY", "", | ||
2056 | &proxy); | ||
2057 | |||
2058 | myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; | ||
2059 | myAPI.mtu = 0; | ||
2060 | myAPI.cost = 20000; /* about equal to udp */ | ||
2061 | myAPI.hello_verify = &verify_hello; | ||
2062 | myAPI.hello_create = &create_hello; | ||
2063 | myAPI.connect = &httpConnect; | ||
2064 | myAPI.associate = &httpAssociate; | ||
2065 | myAPI.send = &httpSend; | ||
2066 | myAPI.disconnect = &httpDisconnect; | ||
2067 | myAPI.server_start = &startTransportServer; | ||
2068 | myAPI.server_stop = &stopTransportServer; | ||
2069 | myAPI.hello_to_address = &hello_to_address; | ||
2070 | myAPI.send_now_test = &httpTestWouldTry; | ||
2071 | |||
2072 | return &myAPI; | ||
2073 | } | ||
2074 | |||
2075 | void | ||
2076 | donetransport_http () | ||
2077 | { | ||
2078 | curl_global_cleanup (); | ||
2079 | GNUNET_free_non_null (proxy); | ||
2080 | proxy = NULL; | ||
2081 | GNUNET_array_grow (tsessions, tsessionArrayLength, 0); | ||
2082 | do_shutdown (); | ||
2083 | } | ||
2084 | |||
2085 | /* end of http.c */ | ||