3293918edb8ad94e51a8b8e0884c857bcf982181
[oweals/gnunet.git] / src / transport / plugin_transport_http.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 3, 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 transport/plugin_transport_http.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_connection_lib.h"
31 #include "gnunet_service_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_resolver_service.h"
35 #include "gnunet_server_lib.h"
36 #include "gnunet_container_lib.h"
37 #include "plugin_transport.h"
38 #include "gnunet_os_lib.h"
39 #include "microhttpd.h"
40 #include <curl/curl.h>
41
42
43 #define DEBUG_CURL GNUNET_YES
44 #define DEBUG_HTTP GNUNET_NO
45
46 #define INBOUND GNUNET_NO
47 #define OUTBOUND GNUNET_YES
48
49 /**
50  * Text of the response sent back after the last bytes of a PUT
51  * request have been received (just to formally obey the HTTP
52  * protocol).
53  */
54 #define HTTP_PUT_RESPONSE "Thank you!"
55
56 /**
57  * After how long do we expire an address that we
58  * learned from another peer if it is not reconfirmed
59  * by anyone?
60  */
61 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
62
63 /**
64  * Page returned if request invalid
65  */
66 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
67
68 /**
69  * Timeout for a http connect
70  */
71 #define HTTP_CONNECT_TIMEOUT 30
72
73 /**
74  * Network format for IPv4 addresses.
75  */
76 struct IPv4HttpAddress
77 {
78   /**
79    * IPv4 address, in network byte order.
80    */
81   uint32_t ipv4_addr GNUNET_PACKED;
82
83   /**
84    * Port number, in network byte order.
85    */
86   uint16_t u_port GNUNET_PACKED;
87
88 };
89
90
91 /**
92  * Network format for IPv6 addresses.
93  */
94 struct IPv6HttpAddress
95 {
96   /**
97    * IPv6 address.
98    */
99   struct in6_addr ipv6_addr GNUNET_PACKED;
100
101   /**
102    * Port number, in network byte order.
103    */
104   uint16_t u6_port GNUNET_PACKED;
105
106 };
107
108
109 /**
110  *  Message to send using http
111  */
112 struct HTTP_Message
113 {
114   /**
115    * next pointer for double linked list
116    */
117   struct HTTP_Message * next;
118
119   /**
120    * previous pointer for double linked list
121    */
122   struct HTTP_Message * prev;
123
124   /**
125    * buffer containing data to send
126    */
127   char *buf;
128
129   /**
130    * amount of data already sent
131    */
132   size_t pos;
133
134   /**
135    * buffer length
136    */
137   size_t size;
138
139   /**
140    * Continuation function to call once the transmission buffer
141    * has again space available.  NULL if there is no
142    * continuation to call.
143    */
144   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
145
146   /**
147    * Closure for transmit_cont.
148    */
149   void *transmit_cont_cls;
150 };
151
152
153 struct HTTP_PeerContext
154 {
155   struct GNUNET_PeerIdentity identity;
156
157   /**
158    * Pointer to the global plugin struct.
159    */
160   struct Plugin *plugin;
161
162   struct HTTP_Session * head;
163   struct HTTP_Session * tail;
164 };
165
166
167 struct HTTP_Session
168 {
169   struct HTTP_Session * next;
170   struct HTTP_Session * prev;
171
172   void * addr;
173   size_t addrlen;
174
175   /**
176    * target url
177    */
178   char * url;
179
180   /**
181    * Message queue for outbound messages
182    * head of queue
183    */
184   struct HTTP_Message * pending_msgs_head;
185
186   /**
187    * Message queue for outbound messages
188    * tail of queue
189    */
190   struct HTTP_Message * pending_msgs_tail;
191
192   /**
193    * partner peer this connection belongs to
194    */
195   struct HTTP_PeerContext * peercontext;
196
197   /**
198    * message stream tokenizer for incoming data
199    */
200   struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
201
202   /**
203    * session direction
204    * outbound: GNUNET_YES
205    * inbound : GNUNET_NO
206    */
207   unsigned int direction;
208
209   unsigned int send_connected;
210   unsigned int send_active;
211   unsigned int recv_connected;
212   unsigned int recv_active;
213
214   /**
215    * entity managing sending data
216    */
217   void * send_endpoint;
218
219   /**
220    * entity managing recieving data
221    */
222   void * recv_endpoint;
223 };
224
225
226 /**
227  * Session handle for connections.
228  */
229 struct Session
230 {
231
232   /**
233    * API requirement.
234    */
235   struct SessionHeader header;
236
237   /**
238    * Stored in a linked list.
239    */
240   struct Session *next;
241
242   /**
243    * Pointer to the global plugin struct.
244    */
245   struct Plugin *plugin;
246
247   /**
248    * To whom are we talking to (set to our identity
249    * if we are still waiting for the welcome message)
250    */
251   struct GNUNET_PeerIdentity identity;
252
253   /**
254    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
255    */
256   int is_client;
257
258   /**
259    * At what time did we reset last_received last?
260    */
261   struct GNUNET_TIME_Absolute last_quota_update;
262
263   /**
264    * How many bytes have we received since the "last_quota_update"
265    * timestamp?
266    */
267   uint64_t last_received;
268
269   /**
270    * Number of bytes per ms that this peer is allowed
271    * to send to us.
272    */
273   uint32_t quota;
274
275   /**
276    * Encoded hash
277    */
278   struct GNUNET_CRYPTO_HashAsciiEncoded hash;
279 };
280
281 /**
282  * Encapsulation of all of the state of the plugin.
283  */
284 struct Plugin
285 {
286   /**
287    * Our environment.
288    */
289   struct GNUNET_TRANSPORT_PluginEnvironment *env;
290
291   unsigned int port_inbound;
292
293   struct GNUNET_CONTAINER_MultiHashMap *peers;
294
295   /**
296    * Daemon for listening for new IPv4 connections.
297    */
298   struct MHD_Daemon *http_server_daemon_v4;
299
300   /**
301    * Daemon for listening for new IPv6connections.
302    */
303   struct MHD_Daemon *http_server_daemon_v6;
304
305   /**
306    * Our primary task for http daemon handling IPv4 connections
307    */
308   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
309
310   /**
311    * Our primary task for http daemon handling IPv6 connections
312    */
313   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
314
315   /**
316    * The task sending data
317    */
318   GNUNET_SCHEDULER_TaskIdentifier http_server_task_send;
319
320   /**
321    * cURL Multihandle
322    */
323   CURLM * multi_handle;
324
325   /**
326    * Our ASCII encoded, hashed peer identity
327    * This string is used to distinguish between connections and is added to the urls
328    */
329   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
330 };
331
332
333 /**
334  * Function called for a quick conversion of the binary address to
335  * a numeric address.  Note that the caller must not free the
336  * address and that the next call to this function is allowed
337  * to override the address again.
338  *
339  * @param cls closure
340  * @param addr binary address
341  * @param addrlen length of the address
342  * @return string representing the same address
343  */
344 static const char*
345 http_plugin_address_to_string (void *cls,
346                                    const void *addr,
347                                    size_t addrlen);
348
349 static char * create_url(void * cls, const void * addr, size_t addrlen)
350 {
351   struct Plugin *plugin = cls;
352   char *url = NULL;
353
354   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
355   GNUNET_asprintf(&url,
356                   "http://%s/%s",
357                   http_plugin_address_to_string(NULL, addr, addrlen),
358                   (char *) (&plugin->my_ascii_hash_ident));
359
360   return url;
361 }
362
363 /**
364  * Removes a message from the linked list of messages
365  * @param con connection to remove message from
366  * @param msg message to remove
367  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
368  */
369
370 static int remove_http_message(struct HTTP_Session * ps, struct HTTP_Message * msg)
371 {
372   GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
373   GNUNET_free(msg);
374   return GNUNET_OK;
375 }
376
377 static struct HTTP_Session * get_HTTP_Session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addr_len)
378 {
379   struct HTTP_Session * cc = pc->head;
380   struct HTTP_Session * con = NULL;
381   unsigned int count = 0;
382
383   GNUNET_assert((addr_len == sizeof (struct IPv4HttpAddress)) || (addr_len == sizeof (struct IPv6HttpAddress)));
384   while (cc!=NULL)
385   {
386     if (addr_len == cc->addrlen)
387     {
388       if (0 == memcmp(cc->addr, addr, addr_len))
389       {
390         con = cc;
391         break;
392       }
393     }
394     count++;
395     cc=cc->next;
396   }
397   return con;
398 }
399
400
401 /**
402  * Callback called by MHD when a connection is terminated
403  */
404 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
405 {
406   struct HTTP_Session * ps = *httpSessionCache;
407   if (ps == NULL)
408     return;
409   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&ps->peercontext->identity));
410   /* session set to inactive */
411   //ps-> = GNUNET_NO;
412   //con->is_bad_request = GNUNET_NO;
413 }
414
415 static void mhd_write_mst_cb (void *cls,
416                               void *client,
417                               const struct GNUNET_MessageHeader *message)
418 {
419
420   struct HTTP_Session *ps  = cls;
421   struct HTTP_PeerContext *pc = ps->peercontext;
422   GNUNET_assert(ps != NULL);
423   GNUNET_assert(pc != NULL);
424
425   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
426               "Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
427               ntohs(message->type),
428               ntohs(message->size),
429               GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
430
431   pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
432                             &pc->identity,
433                             message, 1, ps,
434                             ps->addr,
435                             ps->addrlen);
436 }
437
438 static void curl_write_mst_cb  (void *cls,
439                                 void *client,
440                                 const struct GNUNET_MessageHeader *message)
441 {
442   struct HTTP_Session *ps  = cls;
443   struct HTTP_PeerContext *pc = ps->peercontext;
444   GNUNET_assert(ps != NULL);
445   GNUNET_assert(pc != NULL);
446
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448               "Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
449               ntohs(message->type),
450               ntohs(message->size),
451               GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
452
453   pc->plugin->env->receive (pc->plugin->env->cls,
454                             &pc->identity,
455                             message, 1, ps,
456                             ps->addr,
457                             ps->addrlen);
458 }
459
460
461 /**
462  * Check if ip is allowed to connect.
463  */
464 static int
465 acceptPolicyCallback (void *cls,
466                       const struct sockaddr *addr, socklen_t addr_len)
467 {
468 #if 0
469   struct Plugin *plugin = cls;
470 #endif
471   /* Every connection is accepted, nothing more to do here */
472   return MHD_YES;
473 }
474
475 int server_read_callback (void *cls, uint64_t pos, char *buf, int max)
476 {
477   int bytes_read = 0;
478
479   struct HTTP_Session * ps = cls;
480   struct HTTP_PeerContext * pc;
481   struct HTTP_Message * msg;
482   int res;res=5;
483
484   GNUNET_assert (ps!=NULL);
485   pc = ps->peercontext;
486   msg = ps->pending_msgs_tail;
487
488   if (msg!=NULL)
489   {
490     if ((msg->size-msg->pos) <= max)
491       {
492         memcpy(buf,&msg->buf[pos],(msg->size-msg->pos));
493         pos+=(msg->size-msg->pos);
494       }
495       else
496       {
497         memcpy(buf,&msg->buf[pos],max);
498         pos+=max;
499       }
500
501       if (msg->pos==msg->size)
502       {
503         if (NULL!=msg->transmit_cont)
504           msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
505         res = remove_http_message(ps,msg);
506       }
507   }
508   return bytes_read;
509 }
510
511 /**
512  * Process GET or PUT request received via MHD.  For
513  * GET, queue response that will send back our pending
514  * messages.  For PUT, process incoming data and send
515  * to GNUnet core.  In either case, check if a session
516  * already exists and create a new one if not.
517  */
518 static int
519 accessHandlerCallback (void *cls,
520                        struct MHD_Connection *mhd_connection,
521                        const char *url,
522                        const char *method,
523                        const char *version,
524                        const char *upload_data,
525                        size_t * upload_data_size, void **httpSessionCache)
526 {
527   struct Plugin *plugin = cls;
528   struct MHD_Response *response;
529   const union MHD_ConnectionInfo * conn_info;
530
531   struct sockaddr_in  *addrin;
532   struct sockaddr_in6 *addrin6;
533
534   char address[INET6_ADDRSTRLEN+14];
535   struct GNUNET_PeerIdentity pi_in;
536
537   struct IPv4HttpAddress ipv4addr;
538   struct IPv6HttpAddress ipv6addr;
539
540   struct HTTP_PeerContext *pc;
541   struct HTTP_Session *ps;
542
543   int res = GNUNET_NO;
544   int send_error_to_client;
545   void * addr;
546   size_t addr_len;
547
548   GNUNET_assert(cls !=NULL);
549   send_error_to_client = GNUNET_NO;
550
551   if (NULL == *httpSessionCache)
552   {
553     /* check url for peer identity , if invalid send HTTP 404*/
554     res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
555     if ( GNUNET_SYSERR == res )
556     {
557       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
558       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
559       MHD_destroy_response (response);
560       if (res == MHD_YES)
561         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
562       else
563         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
564       return res;
565     }
566   }
567   else
568   {
569     ps = *httpSessionCache;
570     pc = ps->peercontext;
571   }
572
573   if (NULL == *httpSessionCache)
574   {
575     /* get peer context */
576     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
577     /* Peer unknown */
578     if (pc==NULL)
579     {
580       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"RECV: CREATING NEW PEER CONTEXT\n");
581       pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
582       pc->plugin = plugin;
583       memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity));
584       GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
585     }
586     else
587     {
588       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"RECV: PEER CONTEXT FOUND\n");
589     }
590
591     conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
592     /* Incoming IPv4 connection */
593     if ( AF_INET == conn_info->client_addr->sin_family)
594     {
595       addrin = conn_info->client_addr;
596       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
597       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
598       ipv4addr.u_port = addrin->sin_port;
599       addr = &ipv4addr;
600       addr_len = sizeof(struct IPv4HttpAddress);
601       //con = session_check_inbound_address (plugin, cs, (const void *) &ipv4addr, sizeof (struct IPv4HttpAddress));
602     }
603     /* Incoming IPv6 connection */
604     if ( AF_INET6 == conn_info->client_addr->sin_family)
605     {
606       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
607       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
608       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr));
609       ipv6addr.u6_port = addrin6->sin6_port;
610       addr = &ipv6addr;
611       addr_len = sizeof(struct IPv6HttpAddress);
612     }
613     /* Set closure and update current session*/
614     ps = get_HTTP_Session(plugin, pc, addr, addr_len);
615     if (ps==NULL)
616     {
617       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"RECV: CREATING NEW SESSION %s\n",http_plugin_address_to_string(NULL, addr, addr_len));
618       ps = GNUNET_malloc(sizeof (struct HTTP_Session));
619       ps->addr = GNUNET_malloc(addr_len);
620       memcpy(ps->addr,addr,addr_len);
621       ps->addrlen = addr_len;
622       ps->direction=INBOUND;
623       ps->pending_msgs_head = NULL;
624       ps->pending_msgs_tail = NULL;
625       ps->send_connected=GNUNET_NO;
626       ps->send_active=GNUNET_NO;
627       ps->recv_connected=GNUNET_NO;
628       ps->recv_active=GNUNET_NO;
629       ps->peercontext=pc;
630       ps->url = create_url (plugin, ps->addr, ps->addrlen);
631       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
632     }
633     else
634     {
635       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"RECV: SESSION CONTEXT FOUND\n");
636     }
637
638     *httpSessionCache = ps;
639     if (ps->msgtok==NULL)
640       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
641
642     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
643                 method,
644                 GNUNET_i2s(&pc->identity),
645                 http_plugin_address_to_string(NULL, ps->addr, ps->addrlen));
646   }
647
648   /* Is it a PUT or a GET request */
649   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
650   {
651     if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO))
652     {
653       ps->recv_active = GNUNET_YES;
654       return MHD_YES;
655     }
656
657     /* Transmission of all data complete */
658     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
659     {
660       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
661       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
662       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
663       MHD_destroy_response (response);
664       ps->recv_active=GNUNET_NO;
665       return MHD_YES;
666     }
667
668     /* Recieving data */
669     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
670     {
671       res = GNUNET_SERVER_mst_receive(ps->msgtok, ps, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO);
672       (*upload_data_size) = 0;
673       return MHD_YES;
674     }
675     else
676       return MHD_NO;
677   }
678   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
679   {
680     response = MHD_create_response_from_callback(-1,32 * 1024, &server_read_callback, ps, NULL);
681     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
682     MHD_destroy_response (response);
683
684     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
685                 method,
686                 GNUNET_i2s(&pc->identity),
687                 http_plugin_address_to_string(NULL, ps->addr, ps->addrlen));
688     return res;
689   }
690   return MHD_NO;
691 }
692
693
694 /**
695  * Call MHD to process pending ipv4 requests and then go back
696  * and schedule the next run.
697  */
698 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
699 /**
700  * Call MHD to process pending ipv6 requests and then go back
701  * and schedule the next run.
702  */
703 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
704
705 /**
706  * Function that queries MHD's select sets and
707  * starts the task waiting for them.
708  */
709 static GNUNET_SCHEDULER_TaskIdentifier
710 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
711 {
712   struct Plugin *plugin = cls;
713   GNUNET_SCHEDULER_TaskIdentifier ret;
714   fd_set rs;
715   fd_set ws;
716   fd_set es;
717   struct GNUNET_NETWORK_FDSet *wrs;
718   struct GNUNET_NETWORK_FDSet *wws;
719   struct GNUNET_NETWORK_FDSet *wes;
720   int max;
721   unsigned long long timeout;
722   int haveto;
723   struct GNUNET_TIME_Relative tv;
724
725   GNUNET_assert(cls !=NULL);
726   ret = GNUNET_SCHEDULER_NO_TASK;
727   FD_ZERO(&rs);
728   FD_ZERO(&ws);
729   FD_ZERO(&es);
730   wrs = GNUNET_NETWORK_fdset_create ();
731   wes = GNUNET_NETWORK_fdset_create ();
732   wws = GNUNET_NETWORK_fdset_create ();
733   max = -1;
734   GNUNET_assert (MHD_YES ==
735                  MHD_get_fdset (daemon_handle,
736                                 &rs,
737                                 &ws,
738                                 &es,
739                                 &max));
740   haveto = MHD_get_timeout (daemon_handle, &timeout);
741   if (haveto == MHD_YES)
742     tv.value = (uint64_t) timeout;
743   else
744     tv = GNUNET_TIME_UNIT_FOREVER_REL;
745   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
746   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
747   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
748   if (daemon_handle == plugin->http_server_daemon_v4)
749   {
750     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
751                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
752                                        GNUNET_SCHEDULER_NO_TASK,
753                                        tv,
754                                        wrs,
755                                        wws,
756                                        &http_server_daemon_v4_run,
757                                        plugin);
758   }
759   if (daemon_handle == plugin->http_server_daemon_v6)
760   {
761     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
762                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
763                                        GNUNET_SCHEDULER_NO_TASK,
764                                        tv,
765                                        wrs,
766                                        wws,
767                                        &http_server_daemon_v6_run,
768                                        plugin);
769   }
770   GNUNET_NETWORK_fdset_destroy (wrs);
771   GNUNET_NETWORK_fdset_destroy (wws);
772   GNUNET_NETWORK_fdset_destroy (wes);
773   return ret;
774 }
775
776 /**
777  * Call MHD to process pending requests and then go back
778  * and schedule the next run.
779  */
780 static void http_server_daemon_v4_run (void *cls,
781                              const struct GNUNET_SCHEDULER_TaskContext *tc)
782 {
783   struct Plugin *plugin = cls;
784
785   GNUNET_assert(cls !=NULL);
786   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
787     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
788
789   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
790     return;
791
792   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
793   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
794   return;
795 }
796
797
798 /**
799  * Call MHD to process pending requests and then go back
800  * and schedule the next run.
801  */
802 static void http_server_daemon_v6_run (void *cls,
803                              const struct GNUNET_SCHEDULER_TaskContext *tc)
804 {
805   struct Plugin *plugin = cls;
806
807   GNUNET_assert(cls !=NULL);
808   if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
809     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
810
811   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
812     return;
813
814   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
815   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
816   return;
817 }
818
819
820 static size_t curl_header_function( void *ptr, size_t size, size_t nmemb, void *stream)
821 {
822   struct HTTP_Session * ps = stream;
823
824   char * tmp;
825   size_t len = size * nmemb;
826   long http_result = 0;
827   int res;
828
829   /* Getting last http result code */
830   if (ps->recv_connected==GNUNET_NO)
831   {
832     GNUNET_assert(NULL!=ps);
833     res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
834     if (CURLE_OK == res)
835     {
836       if (http_result == 200)
837       {
838         ps->recv_connected = GNUNET_YES;
839         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connected\n",ps);
840       }
841     }
842   }
843
844   tmp = NULL;
845   if ((size * nmemb) < SIZE_MAX)
846     tmp = GNUNET_malloc (len+1);
847
848   if ((tmp != NULL) && (len > 0))
849   {
850     memcpy(tmp,ptr,len);
851     if (len>=2)
852     {
853       if (tmp[len-2] == 13)
854         tmp[len-2]= '\0';
855     }
856 #if DEBUG_HTTP
857     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result);
858 #endif
859   }
860   if (NULL != tmp)
861     GNUNET_free (tmp);
862
863   return size * nmemb;
864 }
865
866 /**
867  * Callback method used with libcurl
868  * Method is called when libcurl needs to read data during sending
869  * @param stream pointer where to write data
870  * @param size size of an individual element
871  * @param nmemb count of elements that can be written to the buffer
872  * @param ptr source pointer, passed to the libcurl handle
873  * @return bytes written to stream
874  */
875 static size_t send_curl_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
876 {
877   struct HTTP_Session * ps = ptr;
878   struct HTTP_Message * msg = ps->pending_msgs_tail;
879   size_t bytes_sent;
880   size_t len;
881
882   if (ps->pending_msgs_tail == NULL)
883   {
884     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps);
885     ps->send_active = GNUNET_NO;
886     return CURL_READFUNC_PAUSE;
887   }
888
889   msg = ps->pending_msgs_tail;
890   /* data to send */
891   if (msg->pos < msg->size)
892   {
893     /* data fit in buffer */
894     if ((msg->size - msg->pos) <= (size * nmemb))
895     {
896       len = (msg->size - msg->pos);
897       memcpy(stream, &msg->buf[msg->pos], len);
898       msg->pos += len;
899       bytes_sent = len;
900     }
901     else
902     {
903       len = size*nmemb;
904       memcpy(stream, &msg->buf[msg->pos], len);
905       msg->pos += len;
906       bytes_sent = len;
907     }
908   }
909   /* no data to send */
910   else
911   {
912     bytes_sent = 0;
913   }
914
915   if ( msg->pos == msg->size)
916   {
917     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos);
918     /* Calling transmit continuation  */
919     if (( NULL != ps->pending_msgs_tail) && (NULL != ps->pending_msgs_tail->transmit_cont))
920       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK);
921     remove_http_message(ps, msg);
922   }
923   return bytes_sent;
924 }
925
926 /**
927 * Callback method used with libcurl
928 * Method is called when libcurl needs to write data during sending
929 * @param stream pointer where to write data
930 * @param size size of an individual element
931 * @param nmemb count of elements that can be written to the buffer
932 * @param ptr destination pointer, passed to the libcurl handle
933 * @return bytes read from stream
934 */
935 static size_t send_curl_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
936 {
937   struct HTTP_Session * ps = ptr;
938
939   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes recieved\n",ps, size*nmemb);
940   GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO);
941
942   return (size * nmemb);
943
944 }
945
946 /**
947  * Function setting up file descriptors and scheduling task to run
948  * @param cls closure
949  * @param ses session to send data to
950  * @return bytes sent to peer
951  */
952 static size_t send_schedule(void *cls, struct Session* ses );
953
954
955
956 /**
957  * Function setting up curl handle and selecting message to send
958  * @param cls plugin
959  * @param ses session to send data to
960  * @param con connection
961  * @return bytes sent to peer
962  */
963 static ssize_t send_check_connections (void *cls, struct Session* ses , struct HTTP_Session *ps)
964 {
965   struct Plugin *plugin = cls;
966   int bytes_sent = 0;
967   CURLMcode mret;
968   struct HTTP_Message * msg;
969   struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
970
971   GNUNET_assert(cls !=NULL);
972
973   if (ps->direction == OUTBOUND)
974   {
975     /* Check if session is connected to receive data, otherwise connect to peer */
976     if (ps->recv_connected == GNUNET_NO)
977     {
978         if (ps->recv_endpoint == NULL)
979         {
980           ps->recv_endpoint = curl_easy_init();
981 #if DEBUG_CURL
982         curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
983 #endif
984         curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url);
985         curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_header_function);
986         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
987         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, send_curl_read_callback);
988         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps);
989         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, send_curl_write_callback);
990         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
991         curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
992         curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps);
993         curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
994         curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
995
996         mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint);
997         if (mret != CURLM_OK)
998         {
999           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1000                       _("%s failed at %s:%d: `%s'\n"),
1001                       "curl_multi_add_handle", __FILE__, __LINE__,
1002                       curl_multi_strerror (mret));
1003           return -1;
1004         }
1005
1006         if (ps->msgtok != NULL)
1007           ps->msgtok = GNUNET_SERVER_mst_create (&curl_write_mst_cb, ps);
1008         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps);
1009       }
1010     }
1011
1012     /* Check if session is connected to send data, otherwise connect to peer */
1013     if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL))
1014     {
1015       if (ps->send_connected == GNUNET_NO)
1016       {
1017         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps);
1018         return bytes_sent;
1019       }
1020       if (ps->send_active == GNUNET_NO)
1021       {
1022         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps);
1023         curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT);
1024         ps->send_active=GNUNET_YES;
1025         return bytes_sent;
1026       }
1027     }
1028
1029     /* not connected, initiate connection */
1030     if ( NULL == ps->send_endpoint)
1031       ps->send_endpoint = curl_easy_init();
1032     GNUNET_assert (ps->send_endpoint != NULL);
1033     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps);
1034
1035     GNUNET_assert (NULL != ps->pending_msgs_tail);
1036     msg = ps->pending_msgs_tail;
1037
1038   #if DEBUG_CURL
1039     curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L);
1040   #endif
1041     curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url);
1042     curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L);
1043     curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, send_curl_read_callback);
1044     curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1045     curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, send_curl_write_callback);
1046     curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1047     curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1048     curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps);
1049     curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1050     curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1051
1052     mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint);
1053     if (mret != CURLM_OK)
1054     {
1055       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1056                   _("%s failed at %s:%d: `%s'\n"),
1057                   "curl_multi_add_handle", __FILE__, __LINE__,
1058                   curl_multi_strerror (mret));
1059       return -1;
1060     }
1061     ps->send_connected = GNUNET_YES;
1062     bytes_sent = send_schedule (plugin, NULL);
1063     return bytes_sent;
1064   }
1065   if (ps->direction == INBOUND)
1066   {
1067     GNUNET_assert (NULL != ps->pending_msgs_tail);
1068     msg = ps->pending_msgs_tail;
1069     if ((ps->recv_connected==GNUNET_YES) && (ps->recv_connected==GNUNET_YES))
1070         bytes_sent = msg->size;
1071     return bytes_sent;
1072   }
1073 }
1074
1075 static void send_execute (void *cls,
1076              const struct GNUNET_SCHEDULER_TaskContext *tc)
1077 {
1078   struct Plugin *plugin = cls;
1079   static unsigned int handles_last_run;
1080   int running;
1081   struct CURLMsg *msg;
1082   CURLMcode mret;
1083   struct HTTP_Session *ps = NULL;
1084   struct HTTP_PeerContext *pc = NULL;
1085   struct HTTP_Message * cur_msg = NULL;
1086   long http_result;
1087
1088   GNUNET_assert(cls !=NULL);
1089   plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
1090   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1091     return;
1092
1093   do
1094     {
1095       running = 0;
1096       mret = curl_multi_perform (plugin->multi_handle, &running);
1097       if (running < handles_last_run)
1098         {
1099           do
1100             {
1101
1102               msg = curl_multi_info_read (plugin->multi_handle, &running);
1103               if (msg == NULL)
1104                 break;
1105               /* get session for affected curl handle */
1106               GNUNET_assert ( msg->easy_handle != NULL );
1107               curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char *) &ps);
1108               GNUNET_assert ( ps != NULL );
1109               pc = ps->peercontext;
1110               GNUNET_assert ( pc != NULL );
1111               switch (msg->msg)
1112                 {
1113
1114                 case CURLMSG_DONE:
1115                   if ( (msg->data.result != CURLE_OK) &&
1116                        (msg->data.result != CURLE_GOT_NOTHING) )
1117                   {
1118                     /* sending msg failed*/
1119                     if (msg->easy_handle == ps->send_endpoint)
1120                     {
1121                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1122                                  _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1123                                  ps,
1124                                  GNUNET_i2s(&pc->identity),
1125                                  http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1126                                  "curl_multi_perform",
1127                                  curl_easy_strerror (msg->data.result));
1128
1129                       ps->send_connected = GNUNET_NO;
1130                       curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle);
1131                       curl_easy_cleanup(ps->send_endpoint);
1132                       ps->send_endpoint=NULL;
1133                       cur_msg = ps->pending_msgs_tail;
1134                       if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont))
1135                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1136                     }
1137                     /* GET connection failed */
1138                     if (msg->easy_handle == ps->recv_endpoint)
1139                     {
1140                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1141                            _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1142                            ps,
1143                            GNUNET_i2s(&pc->identity),
1144                            http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1145                            "curl_multi_perform",
1146                            curl_easy_strerror (msg->data.result));
1147                       ps->recv_connected = GNUNET_NO;
1148                       curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle);
1149                       curl_easy_cleanup(ps->recv_endpoint);
1150                       ps->recv_endpoint=NULL;
1151                     }
1152                   }
1153                   else
1154                   {
1155                     if (msg->easy_handle == ps->send_endpoint)
1156                     {
1157                       GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
1158                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1159                                   "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1160                                    ps,
1161                                    GNUNET_i2s(&pc->identity),
1162                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1163                                    http_result);
1164
1165                       /* Calling transmit continuation  */
1166                       cur_msg = ps->pending_msgs_tail;
1167                       if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont))
1168                       {
1169                         /* HTTP 1xx : Last message before here was informational */
1170                         if ((http_result >=100) && (http_result < 200))
1171                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1172                         /* HTTP 2xx: successful operations */
1173                         if ((http_result >=200) && (http_result < 300))
1174                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1175                         /* HTTP 3xx..5xx: error */
1176                         if ((http_result >=300) && (http_result < 600))
1177                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1178                       }
1179                       ps->send_connected = GNUNET_NO;
1180                       curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle);
1181                       curl_easy_cleanup(ps->send_endpoint);
1182                       ps->send_endpoint =NULL;
1183                     }
1184                     if (msg->easy_handle == ps->recv_endpoint)
1185                     {
1186                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1187                                   "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1188                                    ps,
1189                                    GNUNET_i2s(&pc->identity),
1190                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1191                                    http_result);
1192
1193                       ps->recv_connected = GNUNET_NO;
1194                       curl_multi_remove_handle(plugin->multi_handle,msg->easy_handle);
1195                       curl_easy_cleanup(ps->recv_endpoint);
1196                       ps->recv_endpoint=NULL;
1197                     }
1198                   }
1199                   if (ps->pending_msgs_tail != NULL)
1200                   {
1201                     if (ps->pending_msgs_tail->pos>0)
1202                       remove_http_message(ps, ps->pending_msgs_tail);
1203                   }
1204                   return;
1205                 default:
1206                   break;
1207                 }
1208
1209             }
1210           while ( (running > 0) );
1211         }
1212       handles_last_run = running;
1213     }
1214   while (mret == CURLM_CALL_MULTI_PERFORM);
1215   send_schedule(plugin, cls);
1216 }
1217
1218
1219 /**
1220  * Function setting up file descriptors and scheduling task to run
1221  * @param ses session to send data to
1222  * @return bytes sent to peer
1223  */
1224 static size_t send_schedule(void *cls, struct Session* ses )
1225 {
1226   struct Plugin *plugin = cls;
1227   fd_set rs;
1228   fd_set ws;
1229   fd_set es;
1230   int max;
1231   struct GNUNET_NETWORK_FDSet *grs;
1232   struct GNUNET_NETWORK_FDSet *gws;
1233   long to;
1234   CURLMcode mret;
1235
1236   GNUNET_assert(cls !=NULL);
1237   max = -1;
1238   FD_ZERO (&rs);
1239   FD_ZERO (&ws);
1240   FD_ZERO (&es);
1241   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1242   if (mret != CURLM_OK)
1243     {
1244       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1245                   _("%s failed at %s:%d: `%s'\n"),
1246                   "curl_multi_fdset", __FILE__, __LINE__,
1247                   curl_multi_strerror (mret));
1248       return -1;
1249     }
1250   mret = curl_multi_timeout (plugin->multi_handle, &to);
1251   if (mret != CURLM_OK)
1252     {
1253       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1254                   _("%s failed at %s:%d: `%s'\n"),
1255                   "curl_multi_timeout", __FILE__, __LINE__,
1256                   curl_multi_strerror (mret));
1257       return -1;
1258     }
1259
1260   grs = GNUNET_NETWORK_fdset_create ();
1261   gws = GNUNET_NETWORK_fdset_create ();
1262   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1263   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1264   plugin->http_server_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1265                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1266                                    GNUNET_SCHEDULER_NO_TASK,
1267                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1268                                    grs,
1269                                    gws,
1270                                    &send_execute,
1271                                    plugin);
1272   GNUNET_NETWORK_fdset_destroy (gws);
1273   GNUNET_NETWORK_fdset_destroy (grs);
1274
1275   /* FIXME: return bytes REALLY sent */
1276   return 0;
1277 }
1278
1279
1280 /**
1281  * Function that can be used by the transport service to transmit
1282  * a message using the plugin.   Note that in the case of a
1283  * peer disconnecting, the continuation MUST be called
1284  * prior to the disconnect notification itself.  This function
1285  * will be called with this peer's HELLO message to initiate
1286  * a fresh connection to another peer.
1287  *
1288  * @param cls closure
1289  * @param target who should receive this message
1290  * @param msgbuf the message to transmit
1291  * @param msgbuf_size number of bytes in 'msgbuf'
1292  * @param priority how important is the message (most plugins will
1293  *                 ignore message priority and just FIFO)
1294  * @param timeout how long to wait at most for the transmission (does not
1295  *                require plugins to discard the message after the timeout,
1296  *                just advisory for the desired delay; most plugins will ignore
1297  *                this as well)
1298  * @param session which session must be used (or NULL for "any")
1299  * @param addr the address to use (can be NULL if the plugin
1300  *                is "on its own" (i.e. re-use existing TCP connection))
1301  * @param addrlen length of the address in bytes
1302  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1303  *                GNUNET_NO means the plugin may use any other address and
1304  *                GNUNET_SYSERR means that only reliable existing
1305  *                bi-directional connections should be used (regardless
1306  *                of address)
1307  * @param cont continuation to call once the message has
1308  *        been transmitted (or if the transport is ready
1309  *        for the next transmission call; or if the
1310  *        peer disconnected...); can be NULL
1311  * @param cont_cls closure for cont
1312  * @return number of bytes used (on the physical network, with overheads);
1313  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1314  *         and does NOT mean that the message was not transmitted (DV)
1315  */
1316 static ssize_t
1317 http_plugin_send (void *cls,
1318                   const struct GNUNET_PeerIdentity *target,
1319                   const char *msgbuf,
1320                   size_t msgbuf_size,
1321                   unsigned int priority,
1322                   struct GNUNET_TIME_Relative to,
1323                   struct Session *session,
1324                   const void *addr,
1325                   size_t addrlen,
1326                   int force_address,
1327                   GNUNET_TRANSPORT_TransmitContinuation cont,
1328                   void *cont_cls)
1329 {
1330   struct Plugin *plugin = cls;
1331   struct HTTP_Message *msg;
1332
1333   struct HTTP_PeerContext * pc;
1334   struct HTTP_Session * ps;
1335
1336   GNUNET_assert(cls !=NULL);
1337   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
1338
1339   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1340   /* Peer unknown */
1341   if (pc==NULL)
1342   {
1343     pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
1344     pc->plugin = plugin;
1345     memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity));
1346     GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1347   }
1348   ps = get_HTTP_Session(plugin, pc, addr, addrlen);
1349   /* session not existing, but address forced -> creating new session */
1350   if ((ps==NULL) && (force_address == GNUNET_YES))
1351   {
1352     ps = GNUNET_malloc(sizeof (struct HTTP_Session));
1353     ps->addr = GNUNET_malloc(addrlen);
1354     memcpy(ps->addr,addr,addrlen);
1355     ps->addrlen = addrlen;
1356     ps->direction=OUTBOUND;
1357     ps->recv_connected = GNUNET_NO;
1358     ps->send_connected = GNUNET_NO;
1359     ps->pending_msgs_head = NULL;
1360     ps->pending_msgs_tail = NULL;
1361     ps->peercontext=pc;
1362     ps->url = create_url (plugin, ps->addr, ps->addrlen);
1363     GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
1364   }
1365   /* session not existing, address not forced -> looking for other session */
1366   if ((ps==NULL) && (force_address == GNUNET_NO))
1367   {
1368     /* FIXME: CREATING SESSION, SHOULD CHOOSE EXISTING */
1369     ps = GNUNET_malloc(sizeof (struct HTTP_Session));
1370     ps->addr = GNUNET_malloc(addrlen);
1371     memcpy(ps->addr,addr,addrlen);
1372     ps->addrlen = addrlen;
1373     ps->direction=OUTBOUND;
1374     ps->recv_connected = GNUNET_NO;
1375     ps->send_connected = GNUNET_NO;
1376     ps->pending_msgs_head = NULL;
1377     ps->pending_msgs_tail = NULL;
1378     ps->peercontext=pc;
1379     ps->url = create_url (plugin, ps->addr, ps->addrlen);
1380     GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
1381   }
1382   if ((ps==NULL) && (force_address == GNUNET_SYSERR))
1383   {
1384     /* FIXME: CREATING SESSION, SHOULD CHOOSE EXISTING */
1385     ps = GNUNET_malloc(sizeof (struct HTTP_Session));
1386     ps->addr = GNUNET_malloc(addrlen);
1387     memcpy(ps->addr,addr,addrlen);
1388     ps->addrlen = addrlen;
1389     ps->direction=OUTBOUND;
1390     ps->recv_connected = GNUNET_NO;
1391     ps->send_connected = GNUNET_NO;
1392     ps->pending_msgs_head = NULL;
1393     ps->pending_msgs_tail = NULL;
1394     ps->peercontext=pc;
1395     ps->url = create_url (plugin, ps->addr, ps->addrlen);
1396     GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
1397   }
1398
1399   char * force = GNUNET_malloc(30);
1400   if (force_address == GNUNET_YES)
1401     strcpy(force,"forced addr.");
1402   if (force_address == GNUNET_NO)
1403     strcpy(force,"any addr.");
1404   if (force_address == GNUNET_SYSERR)
1405     strcpy(force,"reliable bi-direc. address addr.");
1406   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' %s (%s), session: %X\n",
1407                                       msgbuf_size,
1408                                       GNUNET_i2s(&pc->identity),
1409                                       force,
1410                                       http_plugin_address_to_string(NULL, addr, addrlen),
1411                                       ps);
1412
1413   GNUNET_free(force);
1414   /* create msg */
1415   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
1416   msg->next = NULL;
1417   msg->size = msgbuf_size;
1418   msg->pos = 0;
1419   msg->buf = (char *) &msg[1];
1420   msg->transmit_cont = cont;
1421   msg->transmit_cont_cls = cont_cls;
1422   memcpy (msg->buf,msgbuf, msgbuf_size);
1423   GNUNET_CONTAINER_DLL_insert(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
1424
1425   return send_check_connections (plugin, session, ps);
1426 }
1427
1428
1429
1430 /**
1431  * Function that can be used to force the plugin to disconnect
1432  * from the given peer and cancel all previous transmissions
1433  * (and their continuationc).
1434  *
1435  * @param cls closure
1436  * @param target peer from which to disconnect
1437  */
1438 static void
1439 http_plugin_disconnect (void *cls,
1440                             const struct GNUNET_PeerIdentity *target)
1441 {
1442
1443   struct Plugin *plugin = cls;
1444   struct HTTP_PeerContext *pc = NULL;
1445   struct HTTP_Session *ps = NULL;
1446
1447   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1448   if (pc==NULL)
1449     return;
1450
1451   ps = pc->head;
1452
1453   while (ps!=NULL)
1454   {
1455     if (ps->direction==OUTBOUND)
1456     {
1457       if (ps->send_endpoint!=NULL)
1458       {
1459         curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1460         curl_easy_cleanup(ps->send_endpoint);
1461         ps->send_endpoint=NULL;
1462       }
1463       if (ps->recv_endpoint!=NULL)
1464       {
1465        curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1466        curl_easy_cleanup(ps->recv_endpoint);
1467        ps->recv_endpoint=NULL;
1468       }
1469     }
1470     if (ps->direction==INBOUND)
1471     {
1472
1473     }
1474     ps=ps->next;
1475   }
1476
1477 #if 0
1478   /* get peercontext from hashmap */
1479   cs = session_get(plugin, target);
1480   con = cs->outbound_connections_head;
1481
1482   while (con!=NULL)
1483   {
1484     if (con->put_curl_handle!=NULL)
1485       curl_easy_cleanup(con->put_curl_handle);
1486     con->put_curl_handle=NULL;
1487     con->put_connected = GNUNET_NO;
1488     while (con->pending_msgs_head!=NULL)
1489     {
1490       //remove_http_message(con, con->pending_msgs_head);
1491     }
1492     con=con->next;
1493   }
1494 #endif
1495 }
1496
1497
1498 /**
1499  * Convert the transports address to a nice, human-readable
1500  * format.
1501  *
1502  * @param cls closure
1503  * @param type name of the transport that generated the address
1504  * @param addr one of the addresses of the host, NULL for the last address
1505  *        the specific address format depends on the transport
1506  * @param addrlen length of the address
1507  * @param numeric should (IP) addresses be displayed in numeric form?
1508  * @param timeout after how long should we give up?
1509  * @param asc function to call on each string
1510  * @param asc_cls closure for asc
1511  */
1512 static void
1513 http_plugin_address_pretty_printer (void *cls,
1514                                         const char *type,
1515                                         const void *addr,
1516                                         size_t addrlen,
1517                                         int numeric,
1518                                         struct GNUNET_TIME_Relative timeout,
1519                                         GNUNET_TRANSPORT_AddressStringCallback
1520                                         asc, void *asc_cls)
1521 {
1522   const struct IPv4HttpAddress *t4;
1523   const struct IPv6HttpAddress *t6;
1524   struct sockaddr_in a4;
1525   struct sockaddr_in6 a6;
1526   char * address;
1527   char * ret;
1528   unsigned int port;
1529   unsigned int res;
1530
1531   GNUNET_assert(cls !=NULL);
1532   if (addrlen == sizeof (struct IPv6HttpAddress))
1533   {
1534     address = GNUNET_malloc (INET6_ADDRSTRLEN);
1535     t6 = addr;
1536     a6.sin6_addr = t6->ipv6_addr;
1537     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1538     port = ntohs(t6->u6_port);
1539   }
1540   else if (addrlen == sizeof (struct IPv4HttpAddress))
1541   {
1542     address = GNUNET_malloc (INET_ADDRSTRLEN);
1543     t4 = addr;
1544     a4.sin_addr.s_addr =  t4->ipv4_addr;
1545     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1546     port = ntohs(t4->u_port);
1547   }
1548   else
1549   {
1550     /* invalid address */
1551     GNUNET_break_op (0);
1552     asc (asc_cls, NULL);
1553     return;
1554   }
1555   res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1556   GNUNET_free (address);
1557   GNUNET_assert(res != 0);
1558
1559   asc (asc_cls, ret);
1560 }
1561
1562
1563
1564 /**
1565  * Another peer has suggested an address for this
1566  * peer and transport plugin.  Check that this could be a valid
1567  * address.  If so, consider adding it to the list
1568  * of addresses.
1569  *
1570  * @param cls closure
1571  * @param addr pointer to the address
1572  * @param addrlen length of addr
1573  * @return GNUNET_OK if this is a plausible address for this peer
1574  *         and transport
1575  */
1576 static int
1577 http_plugin_address_suggested (void *cls,
1578                                const void *addr, size_t addrlen)
1579 {
1580   struct Plugin *plugin = cls;
1581   struct IPv4HttpAddress *v4;
1582   struct IPv6HttpAddress *v6;
1583   unsigned int port;
1584
1585   GNUNET_assert(cls !=NULL);
1586   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1587       (addrlen != sizeof (struct IPv6HttpAddress)))
1588     {
1589       return GNUNET_SYSERR;
1590     }
1591   if (addrlen == sizeof (struct IPv4HttpAddress))
1592     {
1593       v4 = (struct IPv4HttpAddress *) addr;
1594       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
1595       {
1596         return GNUNET_SYSERR;
1597       }
1598       port = ntohs (v4->u_port);
1599       if (port != plugin->port_inbound)
1600       {
1601         return GNUNET_SYSERR;
1602       }
1603     }
1604   else
1605     {
1606       v6 = (struct IPv6HttpAddress *) addr;
1607       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1608         {
1609           return GNUNET_SYSERR;
1610         }
1611       port = ntohs (v6->u6_port);
1612       if (port != plugin->port_inbound)
1613       {
1614         return GNUNET_SYSERR;
1615       }
1616     }
1617   return GNUNET_OK;
1618 }
1619
1620
1621 /**
1622  * Function called for a quick conversion of the binary address to
1623  * a numeric address.  Note that the caller must not free the
1624  * address and that the next call to this function is allowed
1625  * to override the address again.
1626  *
1627  * @param cls closure
1628  * @param addr binary address
1629  * @param addrlen length of the address
1630  * @return string representing the same address
1631  */
1632 static const char*
1633 http_plugin_address_to_string (void *cls,
1634                                    const void *addr,
1635                                    size_t addrlen)
1636 {
1637   const struct IPv4HttpAddress *t4;
1638   const struct IPv6HttpAddress *t6;
1639   struct sockaddr_in a4;
1640   struct sockaddr_in6 a6;
1641   char * address;
1642   char * ret;
1643   uint16_t port;
1644   unsigned int res;
1645
1646   if (addrlen == sizeof (struct IPv6HttpAddress))
1647     {
1648       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1649       t6 = addr;
1650       a6.sin6_addr = t6->ipv6_addr;
1651       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1652       port = ntohs(t6->u6_port);
1653     }
1654   else if (addrlen == sizeof (struct IPv4HttpAddress))
1655     {
1656       address = GNUNET_malloc (INET_ADDRSTRLEN);
1657       t4 = addr;
1658       a4.sin_addr.s_addr =  t4->ipv4_addr;
1659       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1660       port = ntohs(t4->u_port);
1661     }
1662   else
1663     {
1664       /* invalid address */
1665       return NULL;
1666     }
1667   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
1668   GNUNET_free (address);
1669   GNUNET_assert(res != 0);
1670   return ret;
1671 }
1672
1673 /**
1674  * Add the IP of our network interface to the list of
1675  * our external IP addresses.
1676  *
1677  * @param cls the 'struct Plugin*'
1678  * @param name name of the interface
1679  * @param isDefault do we think this may be our default interface
1680  * @param addr address of the interface
1681  * @param addrlen number of bytes in addr
1682  * @return GNUNET_OK to continue iterating
1683  */
1684 static int
1685 process_interfaces (void *cls,
1686                     const char *name,
1687                     int isDefault,
1688                     const struct sockaddr *addr, socklen_t addrlen)
1689 {
1690   struct Plugin *plugin = cls;
1691   struct IPv4HttpAddress * t4;
1692   struct IPv6HttpAddress * t6;
1693   int af;
1694
1695   GNUNET_assert(cls !=NULL);
1696   af = addr->sa_family;
1697   if (af == AF_INET)
1698     {
1699       t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress));
1700       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
1701       {
1702         /* skip loopback addresses */
1703         return GNUNET_OK;
1704       }
1705       t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1706       t4->u_port = htons (plugin->port_inbound);
1707       plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
1708
1709     }
1710   else if (af == AF_INET6)
1711     {
1712       t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress));
1713       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
1714         {
1715           /* skip link local addresses */
1716           return GNUNET_OK;
1717         }
1718       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
1719         {
1720           /* skip loopback addresses */
1721           return GNUNET_OK;
1722         }
1723       memcpy (&t6->ipv6_addr,
1724               &((struct sockaddr_in6 *) addr)->sin6_addr,
1725               sizeof (struct in6_addr));
1726       t6->u6_port = htons (plugin->port_inbound);
1727       plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
1728     }
1729   return GNUNET_OK;
1730 }
1731
1732 int peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value)
1733 {
1734   struct HTTP_PeerContext * pc = value;
1735   struct HTTP_Session * ps = pc->head;
1736   struct HTTP_Session * tmp = NULL;
1737   struct HTTP_Message * msg = NULL;
1738   struct HTTP_Message * msg_tmp = NULL;
1739
1740   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity));
1741
1742   while (ps!=NULL)
1743   {
1744     tmp = ps->next;
1745
1746     GNUNET_free(ps->addr);
1747     GNUNET_free(ps->url);
1748     if (ps->msgtok != NULL)
1749       GNUNET_SERVER_mst_destroy (ps->msgtok);
1750
1751     msg = ps->pending_msgs_head;
1752     while (msg!=NULL)
1753     {
1754       msg_tmp = msg->next;
1755       GNUNET_free(msg);
1756       msg = msg_tmp;
1757     }
1758     if (ps->direction==OUTBOUND)
1759     {
1760       if (ps->send_endpoint!=NULL)
1761         curl_easy_cleanup(ps->send_endpoint);
1762       if (ps->recv_endpoint!=NULL)
1763         curl_easy_cleanup(ps->recv_endpoint);
1764     }
1765
1766     GNUNET_free(ps);
1767     ps=tmp;
1768   }
1769   GNUNET_free(pc);
1770   return GNUNET_YES;
1771 }
1772
1773
1774 /**
1775  * Exit point from the plugin.
1776  */
1777 void *
1778 libgnunet_plugin_transport_http_done (void *cls)
1779 {
1780   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1781   struct Plugin *plugin = api->cls;
1782   CURLMcode mret;
1783
1784   GNUNET_assert(cls !=NULL);
1785
1786   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1787   {
1788     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
1789     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1790   }
1791
1792   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1793   {
1794     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
1795     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1796   }
1797
1798   if ( plugin->http_server_task_send != GNUNET_SCHEDULER_NO_TASK)
1799   {
1800     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_send);
1801     plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
1802   }
1803
1804   if (plugin->http_server_daemon_v4 != NULL)
1805   {
1806     MHD_stop_daemon (plugin->http_server_daemon_v4);
1807     plugin->http_server_daemon_v4 = NULL;
1808   }
1809   if (plugin->http_server_daemon_v6 != NULL)
1810   {
1811     MHD_stop_daemon (plugin->http_server_daemon_v6);
1812     plugin->http_server_daemon_v6 = NULL;
1813   }
1814
1815   /* free all peer information */
1816   GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
1817                                          &peer_context_Iterator,
1818                                          NULL);
1819   GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
1820
1821   mret = curl_multi_cleanup(plugin->multi_handle);
1822   if ( CURLM_OK != mret)
1823     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
1824
1825   GNUNET_free (plugin);
1826   GNUNET_free (api);
1827   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n");
1828   return NULL;
1829 }
1830
1831
1832 /**
1833  * Entry point for the plugin.
1834  */
1835 void *
1836 libgnunet_plugin_transport_http_init (void *cls)
1837 {
1838   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1839   struct Plugin *plugin;
1840   struct GNUNET_TRANSPORT_PluginFunctions *api;
1841   struct GNUNET_TIME_Relative gn_timeout;
1842   long long unsigned int port;
1843
1844   GNUNET_assert(cls !=NULL);
1845   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
1846
1847   plugin = GNUNET_malloc (sizeof (struct Plugin));
1848   plugin->env = env;
1849   plugin->peers = NULL;
1850
1851   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1852   api->cls = plugin;
1853   api->send = &http_plugin_send;
1854   api->disconnect = &http_plugin_disconnect;
1855   api->address_pretty_printer = &http_plugin_address_pretty_printer;
1856   api->check_address = &http_plugin_address_suggested;
1857   api->address_to_string = &http_plugin_address_to_string;
1858
1859   /* Hashing our identity to use it in URLs */
1860   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
1861
1862   /* Reading port number from config file */
1863   if ((GNUNET_OK !=
1864        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1865                                               "transport-http",
1866                                               "PORT",
1867                                               &port)) ||
1868       (port > 65535) )
1869     {
1870       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1871                        "http",
1872                        _
1873                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
1874                        "transport-http");
1875       libgnunet_plugin_transport_http_done (api);
1876       return NULL;
1877     }
1878   GNUNET_assert ((port > 0) && (port <= 65535));
1879   plugin->port_inbound = port;
1880   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1881   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->http_server_daemon_v6 == NULL) && (port != 0))
1882     {
1883     plugin->http_server_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
1884                                        port,
1885                                        &acceptPolicyCallback,
1886                                        plugin , &accessHandlerCallback, plugin,
1887                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1888                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1889                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1890                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1891                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1892                                        MHD_OPTION_END);
1893     plugin->http_server_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
1894                                        port,
1895                                        &acceptPolicyCallback,
1896                                        plugin , &accessHandlerCallback, plugin,
1897                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1898                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1899                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1900                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1901                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1902                                        MHD_OPTION_END);
1903     }
1904   if (plugin->http_server_daemon_v4 != NULL)
1905     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1906   if (plugin->http_server_daemon_v6 != NULL)
1907     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1908
1909   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1910     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
1911   else if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1912     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
1913   else
1914   {
1915     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n");
1916     libgnunet_plugin_transport_http_done (api);
1917     return NULL;
1918   }
1919
1920   /* Initializing cURL */
1921   curl_global_init(CURL_GLOBAL_ALL);
1922   plugin->multi_handle = curl_multi_init();
1923
1924   if ( NULL == plugin->multi_handle )
1925   {
1926     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1927                      "http",
1928                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
1929                      "transport-http");
1930     libgnunet_plugin_transport_http_done (api);
1931     return NULL;
1932   }
1933
1934   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
1935   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1936
1937   return api;
1938 }
1939
1940 /* end of plugin_transport_http.c */