aboutsummaryrefslogtreecommitdiff
path: root/src/transport/plugin_transport_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/plugin_transport_http.c')
-rw-r--r--src/transport/plugin_transport_http.c2085
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 */
78struct 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 */
123struct 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 */
182struct 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 */
236typedef 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
364static int stat_bytesReceived;
365
366static int stat_bytesSent;
367
368static int stat_bytesDropped;
369
370static int stat_get_issued;
371
372static int stat_get_received;
373
374static int stat_put_issued;
375
376static int stat_put_received;
377
378static int stat_select_calls;
379
380static int stat_send_calls;
381
382static int stat_connect_calls;
383
384static int stat_curl_send_callbacks;
385
386static int stat_curl_receive_callbacks;
387
388static int stat_mhd_access_callbacks;
389
390static int stat_mhd_read_callbacks;
391
392static int stat_mhd_close_callbacks;
393
394static int stat_connect_calls;
395
396/**
397 * How many requests do we have currently pending
398 * (with libcurl)?
399 */
400static unsigned int http_requests_pending;
401
402static int signal_pipe[2];
403
404static char *proxy;
405
406/**
407 * Daemon for listening for new connections.
408 */
409static struct MHD_Daemon *mhd_daemon;
410
411/**
412 * Curl multi for managing client operations.
413 */
414static CURLM *curl_multi;
415
416/**
417 * Set to GNUNET_YES while the transport is running.
418 */
419static int http_running;
420
421/**
422 * Thread running libcurl activities.
423 */
424static struct GNUNET_ThreadHandle *curl_thread;
425
426/**
427 * Array of currently active HTTP sessions.
428 */
429static GNUNET_TSession **tsessions;
430
431/**
432 * Number of valid entries in tsessions.
433 */
434static unsigned int tsessionCount;
435
436/**
437 * Sie of the tsessions array.
438 */
439static unsigned int tsessionArrayLength;
440
441/**
442 * Lock for concurrent access to all structures used
443 * by http, including CURL.
444 */
445static struct GNUNET_Mutex *lock;
446
447
448/**
449 * Signal select thread that its selector
450 * set may have changed.
451 */
452static void
453signal_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 */
462static int
463acceptPolicyCallback (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 */
484static int
485httpDisconnect (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
499static void
500destroy_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 */
574static void
575requestCompletedCallback (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 */
642static int
643httpAssociate (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 */
671static unsigned int
672addTSession (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 */
697static int
698contentReaderCallback (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 */
733static void
734contentReaderFreeCallback (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 */
751static int
752accessHandlerCallback (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 */
982static size_t
983receiveContentCallback (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 */
1060static size_t
1061sendContentCallback (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
1086static void
1087create_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 */
1148static int
1149create_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 */
1234static int
1235httpConnect (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 */
1306static size_t
1307discardContentCallback (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 */
1321static int
1322create_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 */
1405static int
1406httpTestWouldTry (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 */
1461static int
1462httpSend (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 */
1592static void
1593cleanup_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 */
1741static void *
1742curl_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 */
1845static int
1846startTransportServer ()
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 */
1938static int
1939stopTransportServer ()
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 */
1979GNUNET_TransportAPI *
1980inittransport_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
2075void
2076donetransport_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 */