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