2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file transport/plugin_transport_http.c
23 * @brief Implementation of the HTTP transport service
24 * @author Christian Grothoff
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"
34 #include <microhttpd.h>
35 #include <curl/curl.h>
38 #define DEBUG_HTTP GNUNET_NO
41 * Disable GET (for debugging only!). Must be GNUNET_YES
44 #define DO_GET GNUNET_YES
47 * After how much time of the core not being associated with a http
48 * connection anymore do we close it?
50 * Needs to be larger than SECONDS_INACTIVE_DROP in
53 #define HTTP_TIMEOUT (600 * GNUNET_CRON_SECONDS)
56 * How often do we re-issue GET requests?
58 #define HTTP_GET_REFRESH (5 * GNUNET_CRON_SECONDS)
61 * Default maximum size of the HTTP read and write buffer.
63 #define HTTP_BUF_SIZE (64 * 1024)
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
70 #define HTTP_PUT_RESPONSE "Thank you!"
72 #define MY_TRANSPORT_NAME "HTTP"
76 * Client-side data per PUT request.
81 * This is a linked list.
83 struct HTTPPutData *next;
86 * Handle to our CURL request.
91 * Last time we made progress with the PUT.
93 GNUNET_CronTime last_activity;
96 * The message we are sending.
106 * Current position in msg.
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.
121 * Server-side data per PUT request.
126 * This is a linked list.
128 struct MHDPutData *next;
131 * MHD connection handle for this request.
133 struct MHD_Connection *session;
136 * Last time we received data on this PUT
139 GNUNET_CronTime last_activity;
142 * Read buffer for the header (from PUT)
144 char rbuff1[sizeof (GNUNET_MessageHeader)];
147 * The read buffer (used only receiving PUT data).
152 * Number of valid bytes in rbuff1
157 * Number of valid bytes in rbuff2
163 * Size of the rbuff2 buffer.
168 * Should we sent a response for this PUT yet?
173 * Have we sent a response for this PUT yet?
180 * Server-side data for a GET request.
186 * This is a linked list.
188 struct MHDGetData *next;
191 * MHD connection handle for this request.
193 struct MHD_Connection *session;
196 * GET session response handle
198 struct MHD_Response *get;
203 struct HTTPSession *httpsession;
206 * The write buffer (for sending GET response)
211 * What was the last time we were able to
212 * transmit data using the current get handle?
214 GNUNET_CronTime last_get_activity;
217 * Current write position in wbuff
222 * Number of valid bytes in wbuff (starting at woff)
227 * Size of the write buffer.
234 * Transport Session handle.
236 typedef struct HTTPSession
240 * GNUNET_TSession for this session.
242 GNUNET_TSession *tsession;
245 * To whom are we talking to.
247 GNUNET_PeerIdentity sender;
250 * number of users of this session
255 * Has this session been destroyed?
260 * Are we client or server? Determines which of the
261 * structs in the union below is being used for this
267 * Is MHD still using this session handle?
272 * Data maintained for the http client-server connection
273 * (depends on if we are client or server).
281 * Active PUT requests (linked list).
283 struct MHDPutData *puts;
287 * Active GET requests (linked list; most
288 * recent received GET is the head of the list).
290 struct MHDGetData *gets;
299 * Address of the other peer.
305 * Last time the GET was active.
307 GNUNET_CronTime last_get_activity;
310 * What was the last time we were able to
311 * transmit data using the current get handle?
313 GNUNET_CronTime last_get_initiated;
321 * Read buffer for the header (from GET).
323 char rbuff1[sizeof (GNUNET_MessageHeader)];
326 * The read buffer (used only receiving GET data).
331 * Number of valid bytes in rbuff1
336 * Number of valid bytes in rbuff2
341 * Current size of the read buffer rbuff2.
347 * URL of the get and put operations.
352 * Linked list of PUT operations.
354 struct HTTPPutData *puts;
362 /* *********** globals ************* */
364 static int stat_bytesReceived;
366 static int stat_bytesSent;
368 static int stat_bytesDropped;
370 static int stat_get_issued;
372 static int stat_get_received;
374 static int stat_put_issued;
376 static int stat_put_received;
378 static int stat_select_calls;
380 static int stat_send_calls;
382 static int stat_connect_calls;
384 static int stat_curl_send_callbacks;
386 static int stat_curl_receive_callbacks;
388 static int stat_mhd_access_callbacks;
390 static int stat_mhd_read_callbacks;
392 static int stat_mhd_close_callbacks;
394 static int stat_connect_calls;
397 * How many requests do we have currently pending
400 static unsigned int http_requests_pending;
402 static struct GNUNET_DISK_FileHandle signal_pipe[2];
407 * Daemon for listening for new connections.
409 static struct MHD_Daemon *mhd_daemon;
412 * Curl multi for managing client operations.
414 static CURLM *curl_multi;
417 * Set to GNUNET_YES while the transport is running.
419 static int http_running;
422 * Thread running libcurl activities.
424 static struct GNUNET_ThreadHandle *curl_thread;
427 * Array of currently active HTTP sessions.
429 static GNUNET_TSession **tsessions;
432 * Number of valid entries in tsessions.
434 static unsigned int tsessionCount;
437 * Sie of the tsessions array.
439 static unsigned int tsessionArrayLength;
442 * Lock for concurrent access to all structures used
443 * by http, including CURL.
445 static struct GNUNET_Mutex *lock;
449 * Signal select thread that its selector
450 * set may have changed.
456 GNUNET_DISK_file_write (signal_pipe[1], &c, sizeof (c));
460 * Check if we are allowed to connect to the given IP.
463 acceptPolicyCallback (void *cls,
464 const struct sockaddr *addr, socklen_t addr_len)
466 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
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.
478 * Sessions are actually discarded in cleanup_connections.
481 * @param tsession the session that is closed
482 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
485 httpDisconnect (GNUNET_TSession * tsession)
487 HTTPSession *httpsession = tsession->internal;
488 if (httpsession == NULL)
490 GNUNET_free (tsession);
493 GNUNET_mutex_lock (lock);
494 httpsession->users--;
495 GNUNET_mutex_unlock (lock);
500 destroy_tsession (GNUNET_TSession * tsession)
502 HTTPSession *httpsession = tsession->internal;
503 struct HTTPPutData *pos;
504 struct HTTPPutData *next;
506 struct MHDGetData *gpos;
507 struct MHDGetData *gnext;
509 struct MHD_Response *r;
512 GNUNET_mutex_lock (lock);
513 for (i = 0; i < tsessionCount; i++)
515 if (tsessions[i] == tsession)
517 tsessions[i] = tsessions[--tsessionCount];
521 if (httpsession->is_client)
524 curl_multi_remove_handle (curl_multi, httpsession->cs.client.get);
525 http_requests_pending--;
527 curl_easy_cleanup (httpsession->cs.client.get);
528 GNUNET_array_grow (httpsession->cs.client.rbuff2,
529 httpsession->cs.client.rsize2, 0);
531 GNUNET_free_non_null (httpsession->cs.client.url);
532 pos = httpsession->cs.client.puts;
536 curl_multi_remove_handle (curl_multi, pos->curl_put);
537 http_requests_pending--;
539 curl_easy_cleanup (pos->curl_put);
540 GNUNET_free (pos->msg);
544 GNUNET_free (httpsession);
545 GNUNET_free (tsession);
549 httpsession->destroyed = GNUNET_YES;
550 GNUNET_GE_BREAK (NULL, httpsession->cs.server.puts == NULL);
552 gpos = httpsession->cs.server.gets;
555 GNUNET_array_grow (gpos->wbuff, gpos->wsize, 0);
559 MHD_destroy_response (r);
562 httpsession->cs.server.gets = NULL;
564 GNUNET_free (httpsession->tsession);
565 GNUNET_free (httpsession);
567 GNUNET_mutex_unlock (lock);
571 * MHD is done handling a request. Cleanup
572 * the respective transport state.
575 requestCompletedCallback (void *unused,
576 struct MHD_Connection *session,
577 void **httpSessionCache)
579 HTTPSession *httpsession = *httpSessionCache;
580 struct MHDPutData *pprev;
581 struct MHDPutData *ppos;
583 struct MHDGetData *gprev;
584 struct MHDGetData *gpos;
588 stats->change (stat_mhd_close_callbacks, 1);
589 if (httpsession == NULL)
591 GNUNET_GE_ASSERT (NULL, !httpsession->is_client);
593 ppos = httpsession->cs.server.puts;
596 if (ppos->session == session)
598 ppos->last_activity = 0;
607 gpos = httpsession->cs.server.gets;
610 if (gpos->session == session)
612 gpos->last_get_activity = 0;
620 httpsession->is_mhd_active--;
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.
636 * @param tsession the session handle passed along
637 * from the call to receive that was made by the transport
639 * @return GNUNET_OK if the session could be associated,
640 * GNUNET_SYSERR if not.
643 httpAssociate (GNUNET_TSession * tsession)
645 HTTPSession *httpSession;
647 if (tsession == NULL)
649 GNUNET_GE_BREAK (NULL, 0);
650 return GNUNET_SYSERR;
652 httpSession = tsession->internal;
653 GNUNET_mutex_lock (lock);
654 if (httpSession->destroyed == GNUNET_YES)
656 GNUNET_mutex_unlock (lock);
657 return GNUNET_SYSERR;
659 httpSession->users++;
660 GNUNET_mutex_unlock (lock);
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.
672 addTSession (GNUNET_TSession * tsession)
676 GNUNET_mutex_lock (lock);
677 if (tsessionCount == tsessionArrayLength)
678 GNUNET_array_grow (tsessions, tsessionArrayLength,
679 tsessionArrayLength * 2);
681 tsessions[tsessionCount++] = tsession;
682 GNUNET_mutex_unlock (lock);
688 * Callback for processing GET requests if our side is the
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!
698 contentReaderCallback (void *cls, uint64_t pos, char *buf, int max)
700 struct MHDGetData *mgd = cls;
703 stats->change (stat_mhd_read_callbacks, 1);
704 GNUNET_mutex_lock (lock);
707 memcpy (buf, &mgd->wbuff[mgd->woff], max);
711 mgd->last_get_activity = GNUNET_get_time ();
714 GNUNET_mutex_unlock (lock);
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);
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) */
730 * Notification that libmicrohttpd no longer needs the
734 contentReaderFreeCallback (void *cls)
736 struct MHDGetData *mgd = cls;
738 GNUNET_GE_ASSERT (NULL, mgd->get == NULL);
739 GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0);
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.
752 accessHandlerCallback (void *cls,
753 struct MHD_Connection *session,
757 const char *upload_data,
758 size_t * upload_data_size, void **httpSessionCache)
760 GNUNET_TSession *tsession;
761 struct MHDPutData *put;
762 struct MHDGetData *get;
763 HTTPSession *httpSession;
764 struct MHD_Response *response;
765 GNUNET_HashCode client;
768 GNUNET_MessageHeader *hdr;
769 GNUNET_TransportPacket *mp;
774 stats->change (stat_mhd_access_callbacks, 1);
776 GNUNET_GE_LOG (coreAPI->ectx,
777 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
778 "HTTP/MHD receives `%s' request.\n", method);
780 /* convert URL to sender peer id */
781 if ((strlen (url) < 2)
782 || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client)))
784 /* invalid request */
785 /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely
786 somebody scanning for MyDoom.X-opened backdoors */
790 /* check if we already have a session for this */
791 httpSession = *httpSessionCache;
792 if (httpSession == NULL)
794 /* new http connection */
797 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
798 stats->change (stat_put_received, 1);
800 stats->change (stat_get_received, 1);
802 GNUNET_mutex_lock (lock);
803 for (i = 0; i < tsessionCount; i++)
805 tsession = tsessions[i];
806 httpSession = tsession->internal;
808 memcmp (&httpSession->sender, &client,
809 sizeof (GNUNET_HashCode)))
810 && (httpSession->is_client == GNUNET_NO))
815 GNUNET_mutex_unlock (lock);
817 /* create new session if necessary */
818 if (httpSession == NULL)
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",
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);
838 if (*httpSessionCache == NULL)
840 httpSession->is_mhd_active++;
841 *httpSessionCache = httpSession;
843 GNUNET_mutex_lock (lock);
845 if (0 == strcasecmp (MHD_HTTP_METHOD_GET, method))
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]);
853 /* handle get; create response object if we do not
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,
864 contentReaderCallback,
866 contentReaderFreeCallback);
867 MHD_queue_response (session, MHD_HTTP_OK, get->get);
868 GNUNET_mutex_unlock (lock);
872 if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method))
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);
880 put = httpSession->cs.server.puts;
881 while ((put != NULL) && (put->session != session))
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;
891 put->last_activity = GNUNET_get_time ();
893 /* handle put (upload_data!) */
895 have = *upload_data_size;
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))
902 put->done = GNUNET_YES;
903 /* end of upload, send response! */
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");
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);
919 put->ready = GNUNET_NO;
920 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
922 cpy = sizeof (GNUNET_MessageHeader) - put->rpos1;
925 memcpy (&put->rbuff1[put->rpos1], &upload_data[poff], cpy);
931 if (put->rpos1 < sizeof (GNUNET_MessageHeader))
933 hdr = (GNUNET_MessageHeader *) put->rbuff1;
934 GNUNET_array_grow (put->rbuff2,
937 sizeof (GNUNET_MessageHeader));
938 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
941 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
945 memcpy (&put->rbuff2[put->rpos2], &upload_data[poff], cpy);
950 if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
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);
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",
963 coreAPI->receive (mp);
968 put->ready = GNUNET_YES;
970 GNUNET_mutex_unlock (lock);
973 GNUNET_mutex_unlock (lock);
974 GNUNET_GE_BREAK_OP (NULL, 0); /* invalid request */
980 * Process downloaded bits (from GET via CURL).
983 receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
985 HTTPSession *httpSession = ctx;
986 const char *inbuf = ptr;
987 size_t have = size * nmemb;
990 GNUNET_MessageHeader *hdr;
991 GNUNET_TransportPacket *mp;
994 stats->change (stat_curl_receive_callbacks, 1);
995 httpSession->cs.client.last_get_activity = GNUNET_get_time ();
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",
1004 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1006 cpy = sizeof (GNUNET_MessageHeader) - httpSession->cs.client.rpos1;
1009 memcpy (&httpSession->cs.client.
1010 rbuff1[httpSession->cs.client.rpos1], &inbuf[poff], cpy);
1011 httpSession->cs.client.rpos1 += cpy;
1014 httpSession->cs.client.rpos2 = 0;
1016 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
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))
1026 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
1027 httpSession->cs.client.rpos2;
1030 memcpy (&httpSession->cs.client.
1031 rbuff2[httpSession->cs.client.rpos2], &inbuf[poff], cpy);
1034 httpSession->cs.client.rpos2 += cpy;
1036 if (httpSession->cs.client.rpos2 <
1037 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
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;
1051 stats->change (stat_bytesReceived, size * nmemb);
1052 return size * nmemb;
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.
1061 sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
1063 struct HTTPPutData *put = ctx;
1064 size_t max = size * nmemb;
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);
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);
1079 stats->change (stat_bytesSent, max);
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
1087 create_session_url (HTTPSession * httpSession)
1089 char buf[IP_BUF_LEN];
1092 unsigned short available;
1095 const HostAddress *haddr =
1096 (const HostAddress *) &httpSession->cs.client.address;
1098 url = httpSession->cs.client.url;
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))
1105 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
1106 available = VERSION_AVAILABLE_IPV4;
1108 available = VERSION_AVAILABLE_IPV6;
1110 if ((available & VERSION_AVAILABLE_IPV4) > 0)
1112 if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN))
1120 else if ((available & VERSION_AVAILABLE_IPV6) > 0)
1122 if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN))
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;
1143 * Try to do a GET on the other peer of the given
1146 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1149 create_curl_get (HTTPSession * httpSession)
1154 GNUNET_CronTime now;
1156 if (httpSession->cs.client.url == NULL)
1157 return GNUNET_SYSERR;
1158 curl_get = httpSession->cs.client.get;
1159 if (curl_get != NULL)
1161 GNUNET_mutex_lock (lock);
1162 curl_multi_remove_handle (curl_multi, curl_get);
1163 http_requests_pending--;
1165 curl_easy_cleanup (curl_get);
1166 GNUNET_mutex_unlock (lock);
1167 httpSession->cs.client.get = NULL;
1169 curl_get = curl_easy_init ();
1170 if (curl_get == NULL)
1171 return GNUNET_SYSERR;
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");
1181 CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1);
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)
1194 curl_easy_cleanup (curl_get);
1195 return GNUNET_SYSERR;
1197 GNUNET_mutex_lock (lock);
1198 mret = curl_multi_add_handle (curl_multi, curl_get);
1199 http_requests_pending++;
1200 GNUNET_mutex_unlock (lock);
1202 stats->change (stat_get_issued, 1);
1203 if (mret != CURLM_OK)
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;
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;
1219 GNUNET_GE_LOG (coreAPI->ectx,
1220 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1221 "HTTP/CURL initiated GET request.\n");
1228 * Establish a connection to a remote node.
1230 * @param hello the hello-Message for the target node
1231 * @param tsessionPtr the session handle that is set
1232 * @param may_reuse are we allowed to re-use an existing connection?
1233 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1236 httpConnect (const GNUNET_MessageHello * hello,
1237 GNUNET_TSession ** tsessionPtr, int may_reuse)
1239 const HostAddress *haddr = (const HostAddress *) &hello[1];
1240 GNUNET_TSession *tsession;
1241 HTTPSession *httpSession;
1245 stats->change (stat_connect_calls, 1);
1246 /* check if we have a session pending for this peer */
1250 GNUNET_mutex_lock (lock);
1251 for (i = 0; i < tsessionCount; i++)
1253 if (0 == memcmp (&hello->senderIdentity,
1254 &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity)))
1256 tsession = tsessions[i];
1260 if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession)))
1262 *tsessionPtr = tsession;
1263 GNUNET_mutex_unlock (lock);
1266 GNUNET_mutex_unlock (lock);
1268 /* no session pending, initiate a new one! */
1269 httpSession = GNUNET_malloc (sizeof (HTTPSession));
1270 memset (httpSession, 0, sizeof (HTTPSession));
1271 httpSession->sender = hello->senderIdentity;
1272 httpSession->users = 1; /* us only, core has not seen this tsession! */
1273 httpSession->is_client = GNUNET_YES;
1274 httpSession->cs.client.address = *haddr;
1275 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
1276 memset (tsession, 0, sizeof (GNUNET_TSession));
1277 httpSession->tsession = tsession;
1278 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
1279 tsession->internal = httpSession;
1280 tsession->peer = hello->senderIdentity;
1281 create_session_url (httpSession);
1283 if (GNUNET_OK != create_curl_get (httpSession))
1285 GNUNET_free (tsession);
1286 GNUNET_free (httpSession);
1287 return GNUNET_SYSERR;
1290 /* PUTs will be created as needed */
1291 addTSession (tsession);
1292 *tsessionPtr = tsession;
1294 GNUNET_GE_LOG (coreAPI->ectx,
1295 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1296 "HTTP/CURL initiated connection to `%s'.\n",
1297 httpSession->cs.client.url);
1303 * We received the "Thank you!" response to a PUT.
1304 * Discard the data (not useful) and mark the PUT
1305 * operation as completed.
1308 discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls)
1310 struct HTTPPutData *put = put_cls;
1311 /* this condition should pretty much always be
1312 true; just checking here in case the PUT
1313 response comes early somehow */
1314 if (put->pos == put->size)
1315 put->done = GNUNET_YES;
1316 return size * nmemb;
1320 * Create a new PUT request for the given PUT data.
1323 create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put)
1330 /* we should have initiated a GET earlier,
1331 so URL must not be NULL here */
1332 if (httpSession->cs.client.url == NULL)
1333 return GNUNET_SYSERR;
1334 curl_put = curl_easy_init ();
1335 if (curl_put == NULL)
1336 return GNUNET_SYSERR;
1337 CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1);
1338 CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url);
1339 if (strlen (proxy) > 0)
1340 CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy);
1341 CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size);
1342 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1343 CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http");
1344 CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1);
1346 CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1);
1348 CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L);
1349 /* NOTE: use of CONNECTTIMEOUT without also
1350 setting NOSIGNAL results in really weird
1351 crashes on my system! */
1352 CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1);
1353 CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L);
1355 CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size);
1356 CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback);
1357 CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put);
1358 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback);
1359 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put);
1360 CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1361 if (ret != CURLE_OK)
1363 curl_easy_cleanup (curl_put);
1364 return GNUNET_SYSERR;
1366 GNUNET_mutex_lock (lock);
1367 mret = curl_multi_add_handle (curl_multi, curl_put);
1368 http_requests_pending++;
1369 GNUNET_mutex_unlock (lock);
1371 stats->change (stat_put_issued, 1);
1372 if (mret != CURLM_OK)
1374 GNUNET_GE_LOG (coreAPI->ectx,
1375 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1376 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1377 "curl_multi_add_handle", __FILE__, __LINE__,
1378 curl_multi_strerror (mret));
1379 return GNUNET_SYSERR;
1382 put->curl_put = curl_put;
1384 GNUNET_GE_LOG (coreAPI->ectx,
1385 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1386 "HTTP/CURL initiated PUT request to `%s'.\n",
1387 httpSession->cs.client.url);
1394 * Test if the transport would even try to send
1395 * a message of the given size and importance
1396 * for the given session.<br>
1397 * This function is used to check if the core should
1398 * even bother to construct (and encrypt) this kind
1401 * @return GNUNET_YES if the transport would try (i.e. queue
1402 * the message or call the OS to send),
1403 * GNUNET_NO if the transport would just drop the message,
1404 * GNUNET_SYSERR if the size/session is invalid
1407 httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size,
1410 HTTPSession *httpSession = tsession->internal;
1411 struct MHDGetData *get;
1414 if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader))
1416 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1417 return GNUNET_SYSERR;
1421 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1422 return GNUNET_SYSERR;
1424 if (httpSession->is_client)
1427 if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL))
1434 GNUNET_mutex_lock (lock);
1435 get = httpSession->cs.server.gets;
1440 if (get->wsize == 0)
1442 else if ((get->wpos + size > get->wsize)
1443 && (important != GNUNET_YES))
1448 GNUNET_mutex_unlock (lock);
1455 * Send a message to the specified remote node.
1457 * @param tsession the GNUNET_MessageHello identifying the remote node
1458 * @param msg the message
1459 * @param size the size of the message
1460 * @param important is this message so important that usual restrictions do not apply?
1461 * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full
1464 httpSend (GNUNET_TSession * tsession,
1465 const void *msg, unsigned int size, int important)
1467 HTTPSession *httpSession = tsession->internal;
1468 struct HTTPPutData *putData;
1469 GNUNET_MessageHeader *hdr;
1471 struct MHDGetData *getData;
1476 stats->change (stat_send_calls, 1);
1477 if (httpSession->is_client)
1479 /* we need to do a PUT (we are the client) */
1480 if (size >= GNUNET_MAX_BUFFER_SIZE)
1481 return GNUNET_SYSERR;
1484 GNUNET_GE_BREAK (NULL, 0);
1485 return GNUNET_SYSERR;
1487 if (important != GNUNET_YES)
1489 GNUNET_mutex_lock (lock);
1490 if (httpSession->cs.client.puts != NULL)
1492 /* do not queue more than one unimportant PUT at a time */
1493 signal_select (); /* do clean up now! */
1494 GNUNET_mutex_unlock (lock);
1496 stats->change (stat_bytesDropped, size);
1500 GNUNET_mutex_unlock (lock);
1502 putData = GNUNET_malloc (sizeof (struct HTTPPutData));
1503 memset (putData, 0, sizeof (struct HTTPPutData));
1504 putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader));
1505 hdr = (GNUNET_MessageHeader *) putData->msg;
1506 hdr->size = htons (size + sizeof (GNUNET_MessageHeader));
1507 hdr->type = htons (0);
1508 memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size);
1509 putData->size = size + sizeof (GNUNET_MessageHeader);
1510 putData->last_activity = GNUNET_get_time ();
1511 if (GNUNET_OK != create_curl_put (httpSession, putData))
1513 GNUNET_free (putData->msg);
1514 GNUNET_free (putData);
1515 return GNUNET_SYSERR;
1517 GNUNET_mutex_lock (lock);
1518 putData->next = httpSession->cs.client.puts;
1519 httpSession->cs.client.puts = putData;
1520 GNUNET_mutex_unlock (lock);
1524 /* httpSession->isClient == false, respond to a GET (we
1525 hopefully have one or will have one soon) */
1527 GNUNET_GE_LOG (coreAPI->ectx,
1528 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1529 "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n",
1533 GNUNET_mutex_lock (lock);
1534 getData = httpSession->cs.server.gets;
1535 if (getData == NULL)
1537 GNUNET_mutex_unlock (lock);
1538 return GNUNET_SYSERR;
1540 if (getData->wsize == 0)
1541 GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE);
1542 size += sizeof (GNUNET_MessageHeader);
1543 if (getData->wpos + size > getData->wsize)
1545 /* need to grow or discard */
1548 GNUNET_mutex_unlock (lock);
1551 tmp = GNUNET_malloc (getData->wpos + size);
1552 memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos);
1553 hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos];
1554 hdr->type = htons (0);
1555 hdr->size = htons (size);
1556 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1557 GNUNET_free (getData->wbuff);
1558 getData->wbuff = tmp;
1559 getData->wsize = getData->wpos + size;
1561 getData->wpos = getData->wpos + size;
1565 /* fits without growing */
1566 if (getData->wpos + getData->woff + size > getData->wsize)
1568 /* need to compact first */
1569 memmove (getData->wbuff,
1570 &getData->wbuff[getData->woff], getData->wpos);
1575 (GNUNET_MessageHeader *) & getData->wbuff[getData->woff +
1577 hdr->size = htons (size);
1578 hdr->type = htons (0);
1579 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1580 getData->wpos += size;
1583 GNUNET_mutex_unlock (lock);
1589 * Function called to cleanup dead connections
1590 * (completed PUTs, GETs that have timed out,
1591 * etc.). Also re-vives GETs that have timed out
1592 * if we are still interested in the connection.
1595 cleanup_connections ()
1599 struct HTTPPutData *prev;
1600 struct HTTPPutData *pos;
1601 struct MHDPutData *mpos;
1602 struct MHDPutData *mprev;
1604 struct MHD_Response *r;
1605 struct MHDGetData *gpos;
1606 struct MHDGetData *gnext;
1608 GNUNET_CronTime now;
1610 GNUNET_mutex_lock (lock);
1611 now = GNUNET_get_time ();
1612 for (i = 0; i < tsessionCount; i++)
1614 s = tsessions[i]->internal;
1617 if ((s->cs.client.puts == NULL) && (s->users == 0)
1619 && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now)
1625 GNUNET_GE_LOG (coreAPI->ectx,
1626 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1628 "HTTP transport destroys old (%llu ms) unused client session\n",
1629 now - s->cs.client.last_get_activity);
1632 destroy_tsession (tsessions[i]);
1638 pos = s->cs.client.puts;
1641 if (pos->last_activity + HTTP_TIMEOUT < now)
1642 pos->done = GNUNET_YES;
1646 s->cs.client.puts = pos->next;
1648 prev->next = pos->next;
1649 GNUNET_free (pos->msg);
1650 curl_multi_remove_handle (curl_multi, pos->curl_put);
1651 http_requests_pending--;
1653 curl_easy_cleanup (pos->curl_put);
1656 pos = s->cs.client.puts;
1665 if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) &&
1666 ((s->users > 0) || (s->cs.client.puts != NULL)) &&
1667 ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) ||
1668 (s->cs.client.get == NULL)) &&
1669 ((s->cs.client.get == NULL) ||
1670 (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now)))
1671 create_curl_get (s);
1676 mpos = s->cs.server.puts;
1678 while (mpos != NULL)
1680 if (mpos->last_activity == 0)
1683 s->cs.server.puts = mpos->next;
1685 mprev->next = mpos->next;
1686 GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0);
1689 mpos = s->cs.server.puts;
1698 /* ! s->is_client */
1700 gpos = s->cs.server.gets;
1701 while (gpos != NULL)
1705 if ((gpos->last_get_activity + HTTP_TIMEOUT < now) ||
1706 (gpos != s->cs.server.gets))
1708 if (gpos == s->cs.server.gets)
1709 s->cs.server.gets = NULL;
1712 MHD_destroy_response (r);
1719 (s->cs.server.gets == NULL) &&
1721 (s->is_mhd_active == 0) && (s->users == 0))
1725 GNUNET_GE_LOG (coreAPI->ectx,
1726 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1728 "HTTP transport destroys unused server session\n");
1731 destroy_tsession (tsessions[i]);
1737 GNUNET_mutex_unlock (lock);
1741 * Thread that runs the CURL and MHD requests.
1744 curl_runner (void *unused)
1750 struct GNUNET_NETWORK_FDSet *hrs;
1751 struct GNUNET_NETWORK_FDSet *hws;
1752 struct GNUNET_NETWORK_FDSet *hes;
1755 unsigned long long timeout;
1758 char buf[128]; /* for reading from pipe */
1762 GNUNET_GE_LOG (coreAPI->ectx,
1763 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1764 "HTTP transport select thread started\n");
1767 hrs = GNUNET_net_fdset_create ();
1768 hws = GNUNET_net_fdset_create ();
1769 hes = GNUNET_net_fdset_create ();
1771 while (GNUNET_YES == http_running)
1777 GNUNET_mutex_lock (lock);
1778 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1779 GNUNET_mutex_unlock (lock);
1780 if (mret != CURLM_OK)
1782 GNUNET_GE_LOG (coreAPI->ectx,
1783 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1784 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1785 "curl_multi_fdset", __FILE__, __LINE__,
1786 curl_multi_strerror (mret));
1789 if (mhd_daemon != NULL)
1790 MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max);
1793 if (mhd_daemon != NULL)
1794 have_tv = MHD_get_timeout (mhd_daemon, &timeout);
1795 GNUNET_mutex_lock (lock);
1796 if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) &&
1797 (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO)))
1802 GNUNET_mutex_unlock (lock);
1804 GNUNET_net_fdset_zero (hws);
1805 GNUNET_net_fdset_zero (hrs);
1806 GNUNET_net_fdset_zero (hes);
1807 GNUNET_net_fdset_copy_native (hws, ws);
1808 GNUNET_net_fdset_copy_native (hrs, rs);
1809 GNUNET_net_fdset_copy_native (hes, es);
1811 GNUNET_net_fdset_handle_set (signal_pipe[0], hrs);
1813 stats->change (stat_select_calls, 1);
1815 GNUNET_net_select (hrs, hws, hes,
1817 MHD_YES) ? timeout :
1818 GNUNET_TIME_UNIT_FOREVER_REL);
1819 if (ret == GNUNET_SYSERR)
1821 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
1822 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1823 GNUNET_GE_DEVELOPER, "select");
1825 if (GNUNET_YES != http_running)
1830 GNUNET_mutex_lock (lock);
1831 mret = curl_multi_perform (curl_multi, &running);
1832 GNUNET_mutex_unlock (lock);
1834 while ((mret == CURLM_CALL_MULTI_PERFORM)
1835 && (http_running == GNUNET_YES));
1836 if (GNUNET_net_fdset_handle_isset (signal_pipe[0], hrs))
1837 GNUNET_DISK_file_read (signal_pipe[0], buf, sizeof (buf));
1838 if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM))
1839 GNUNET_GE_LOG (coreAPI->ectx,
1840 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1841 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1842 "curl_multi_perform", __FILE__, __LINE__,
1843 curl_multi_strerror (mret));
1844 if (mhd_daemon != NULL)
1845 MHD_run (mhd_daemon);
1846 cleanup_connections ();
1849 GNUNET_GE_LOG (coreAPI->ectx,
1850 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1851 "HTTP transport select thread exits.\n");
1858 * Start the server process to receive inbound traffic.
1859 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1862 startTransportServer ()
1864 unsigned short port;
1866 if ((curl_multi != NULL) || (http_running == GNUNET_YES))
1867 return GNUNET_SYSERR;
1868 curl_multi = curl_multi_init ();
1869 if (curl_multi == NULL)
1870 return GNUNET_SYSERR;
1872 if ((mhd_daemon == NULL) && (port != 0))
1875 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1879 mhd_daemon = MHD_start_daemon (MHD_USE_IPv6,
1881 &acceptPolicyCallback,
1882 NULL, &accessHandlerCallback, NULL,
1883 MHD_OPTION_CONNECTION_TIMEOUT,
1884 (unsigned int) HTTP_TIMEOUT,
1885 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1886 (unsigned int) 1024 * 128,
1887 MHD_OPTION_CONNECTION_LIMIT,
1889 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1891 MHD_OPTION_NOTIFY_COMPLETED,
1892 &requestCompletedCallback, NULL,
1895 if (mhd_daemon == NULL)
1897 /* try without IPv6 */
1898 mhd_daemon = MHD_start_daemon (MHD_NO_FLAG,
1900 &acceptPolicyCallback,
1901 NULL, &accessHandlerCallback, NULL,
1902 MHD_OPTION_CONNECTION_TIMEOUT,
1903 (unsigned int) HTTP_TIMEOUT,
1904 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1905 (unsigned int) 1024 * 128,
1906 MHD_OPTION_CONNECTION_LIMIT,
1908 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1910 MHD_OPTION_NOTIFY_COMPLETED,
1911 &requestCompletedCallback, NULL,
1916 available_protocols |= VERSION_AVAILABLE_IPV6;
1918 if (mhd_daemon != NULL)
1919 available_protocols |= VERSION_AVAILABLE_IPV4;
1924 available_protocols |= VERSION_AVAILABLE_IPV4;
1926 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1929 available_protocols |= VERSION_AVAILABLE_IPV6;
1931 if (GNUNET_OK != GNUNET_DISK_pipe (signal_pipe, GNUNET_NO))
1933 MHD_stop_daemon (mhd_daemon);
1934 curl_multi_cleanup (curl_multi);
1937 return GNUNET_SYSERR;
1939 http_running = GNUNET_YES;
1940 curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024);
1941 if (curl_thread == NULL)
1942 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
1943 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
1944 GNUNET_GE_IMMEDIATE, "pthread_create");
1949 * Shutdown the server process (stop receiving inbound
1950 * traffic). May be restarted later!
1953 stopTransportServer ()
1959 if ((http_running == GNUNET_NO) || (curl_multi == NULL))
1960 return GNUNET_SYSERR;
1961 http_running = GNUNET_NO;
1963 GNUNET_thread_stop_sleep (curl_thread);
1964 GNUNET_thread_join (curl_thread, &unused);
1965 GNUNET_DISK_close (signal_pipe[0]);
1966 GNUNET_DISK_close (signal_pipe[1]);
1967 if (mhd_daemon != NULL)
1969 MHD_stop_daemon (mhd_daemon);
1972 cleanup_connections ();
1973 for (i = 0; i < tsessionCount; i++)
1975 s = tsessions[i]->internal;
1978 destroy_tsession (tsessions[i]);
1982 curl_multi_cleanup (curl_multi);
1987 /* ******************** public API ******************** */
1990 * The exported method. Makes the core api available
1991 * via a global and returns the udp transport API.
1993 GNUNET_TransportAPI *
1994 inittransport_http (GNUNET_CoreAPIForTransport * core)
1996 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
1999 lock = GNUNET_mutex_create (GNUNET_YES);
2000 if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg,
2001 &reload_configuration, NULL))
2003 GNUNET_mutex_destroy (lock);
2007 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2009 GNUNET_GE_BREAK (NULL, 0);
2010 GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration,
2012 GNUNET_mutex_destroy (lock);
2017 tsessionArrayLength = 0;
2018 GNUNET_array_grow (tsessions, tsessionArrayLength, 32);
2019 if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
2021 GNUNET_YES) == GNUNET_YES)
2023 upnp = coreAPI->service_request ("upnp");
2027 GNUNET_GE_LOG (coreAPI->ectx,
2028 GNUNET_GE_ERROR | GNUNET_GE_USER |
2029 GNUNET_GE_IMMEDIATE,
2031 ("The UPnP service could not be loaded. To disable UPnP, set the "
2032 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"),
2036 stats = coreAPI->service_request ("stats");
2040 = stats->create (gettext_noop ("# bytes received via HTTP"));
2041 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP"));
2043 = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)"));
2044 stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued"));
2046 = stats->create (gettext_noop ("# HTTP GET received"));
2047 stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued"));
2049 = stats->create (gettext_noop ("# HTTP PUT received"));
2051 = stats->create (gettext_noop ("# HTTP select calls"));
2053 stat_send_calls = stats->create (gettext_noop ("# HTTP send calls"));
2055 stat_curl_send_callbacks
2056 = stats->create (gettext_noop ("# HTTP curl send callbacks"));
2057 stat_curl_receive_callbacks
2058 = stats->create (gettext_noop ("# HTTP curl receive callbacks"));
2059 stat_mhd_access_callbacks
2060 = stats->create (gettext_noop ("# HTTP mhd access callbacks"));
2061 stat_mhd_read_callbacks
2062 = stats->create (gettext_noop ("# HTTP mhd read callbacks"));
2063 stat_mhd_close_callbacks
2064 = stats->create (gettext_noop ("# HTTP mhd close callbacks"));
2066 = stats->create (gettext_noop ("# HTTP connect calls"));
2068 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
2069 "GNUNETD", "HTTP-PROXY", "",
2072 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
2074 myAPI.cost = 20000; /* about equal to udp */
2075 myAPI.hello_verify = &verify_hello;
2076 myAPI.hello_create = &create_hello;
2077 myAPI.connect = &httpConnect;
2078 myAPI.associate = &httpAssociate;
2079 myAPI.send = &httpSend;
2080 myAPI.disconnect = &httpDisconnect;
2081 myAPI.server_start = &startTransportServer;
2082 myAPI.server_stop = &stopTransportServer;
2083 myAPI.hello_to_address = &hello_to_address;
2084 myAPI.send_now_test = &httpTestWouldTry;
2090 donetransport_http ()
2092 curl_global_cleanup ();
2093 GNUNET_free_non_null (proxy);
2095 GNUNET_array_grow (tsessions, tsessionArrayLength, 0);