aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/transport/Makefile.am39
-rw-r--r--src/transport/plugin_transport_http.c2131
-rw-r--r--src/transport/test_plugin_transport_http.c28
-rw-r--r--src/transport/test_transport_api_http_peer1.conf4
-rw-r--r--src/transport/test_transport_api_http_peer2.conf2
5 files changed, 198 insertions, 2006 deletions
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
index c7482f4a8..1cb61c0a9 100644
--- a/src/transport/Makefile.am
+++ b/src/transport/Makefile.am
@@ -70,7 +70,7 @@ plugin_LTLIBRARIES = \
70 libgnunet_plugin_transport_udp.la \ 70 libgnunet_plugin_transport_udp.la \
71 libgnunet_plugin_transport_udp_nat.la \ 71 libgnunet_plugin_transport_udp_nat.la \
72 libgnunet_plugin_transport_template.la 72 libgnunet_plugin_transport_template.la
73# libgnunet_plugin_transport_http.la 73# libgnunet_plugin_transport_http.la
74# TODO: add http, nat, etc. 74# TODO: add http, nat, etc.
75 75
76libgnunet_plugin_transport_tcp_la_SOURCES = \ 76libgnunet_plugin_transport_tcp_la_SOURCES = \
@@ -110,22 +110,23 @@ libgnunet_plugin_transport_udp_nat_la_LIBADD = \
110libgnunet_plugin_transport_udp_nat_la_LDFLAGS = \ 110libgnunet_plugin_transport_udp_nat_la_LDFLAGS = \
111 $(GN_PLUGIN_LDFLAGS) 111 $(GN_PLUGIN_LDFLAGS)
112 112
113#libgnunet_plugin_transport_http_la_SOURCES = \ 113libgnunet_plugin_transport_http_la_SOURCES = \
114# plugin_transport_http.c 114 plugin_transport_http.c
115#libgnunet_plugin_transport_http_la_LIBADD = \ 115libgnunet_plugin_transport_http_la_LIBADD = \
116# $(top_builddir)/src/hello/libgnunethello.la \ 116 $(top_builddir)/src/hello/libgnunethello.la \
117# $(top_builddir)/src/statistics/libgnunetstatistics.la \ 117 $(top_builddir)/src/statistics/libgnunetstatistics.la \
118# $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ 118 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
119# $(top_builddir)/src/util/libgnunetutil.la 119 $(top_builddir)/src/util/libgnunetutil.la
120#libgnunet_plugin_transport_http_la_LDFLAGS = \ 120libgnunet_plugin_transport_http_la_LDFLAGS = \
121# $(GN_PLUGIN_LDFLAGS) 121 $(GN_PLUGIN_LDFLAGS)
122 122
123 123
124check_PROGRAMS = \ 124check_PROGRAMS = \
125 test_transport_api_tcp \ 125 test_transport_api_tcp \
126 test_transport_api_udp \ 126 test_transport_api_udp \
127 test_transport_api_udp_nat \ 127 test_transport_api_udp_nat \
128 test_transport_api_http 128 test_plugin_transport_http
129# test_transport_api_http \
129# TODO: add tests for http, nat, etc. 130# TODO: add tests for http, nat, etc.
130 131
131TESTS = $(check_PROGRAMS) 132TESTS = $(check_PROGRAMS)
@@ -148,12 +149,18 @@ test_transport_api_udp_nat_LDADD = \
148 $(top_builddir)/src/transport/libgnunettransport.la \ 149 $(top_builddir)/src/transport/libgnunettransport.la \
149 $(top_builddir)/src/util/libgnunetutil.la 150 $(top_builddir)/src/util/libgnunetutil.la
150 151
151test_transport_api_http_SOURCES = \ 152#test_transport_api_http_SOURCES = \
152 test_transport_api.c 153# test_transport_api.c
153test_transport_api_http_LDADD = \ 154#test_transport_api_http_LDADD = \
155# $(top_builddir)/src/transport/libgnunettransport.la \
156# $(top_builddir)/src/util/libgnunetutil.la
157
158test_plugin_transport_http_SOURCES = \
159 test_plugin_transport_http.c
160test_plugin_transport_http_LDADD = \
154 $(top_builddir)/src/transport/libgnunettransport.la \ 161 $(top_builddir)/src/transport/libgnunettransport.la \
155 $(top_builddir)/src/util/libgnunetutil.la 162 $(top_builddir)/src/util/libgnunetutil.la
156 163
157 164
158EXTRA_DIST = \ 165EXTRA_DIST = \
159 test_transport_api_data.conf \ 166 test_transport_api_data.conf \
@@ -163,5 +170,5 @@ EXTRA_DIST = \
163 test_transport_api_udp_peer2.conf \ 170 test_transport_api_udp_peer2.conf \
164 test_transport_api_udp_nat_peer1.conf \ 171 test_transport_api_udp_nat_peer1.conf \
165 test_transport_api_udp_nat_peer2.conf \ 172 test_transport_api_udp_nat_peer2.conf \
166 test_plugin_transport_data.conf \ 173 test_plugin_transport_data.conf \
167 test_plugin_transport_data_http.conf 174 test_plugin_transport_data_http.conf
diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c
index 6d4413bba..a0711660b 100644
--- a/src/transport/plugin_transport_http.c
+++ b/src/transport/plugin_transport_http.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors) 3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -21,2081 +21,268 @@
21/** 21/**
22 * @file transport/plugin_transport_http.c 22 * @file transport/plugin_transport_http.c
23 * @brief Implementation of the HTTP transport service 23 * @brief Implementation of the HTTP transport service
24 * @author Christian Grothoff 24 * @author Matthias Wachs
25 */ 25 */
26 26
27#include "platform.h" 27#include "platform.h"
28#include "gnunet_util.h"
29#include "gnunet_protocols.h" 28#include "gnunet_protocols.h"
30#include "gnunet_transport.h" 29#include "gnunet_connection_lib.h"
31#include "gnunet_stats_service.h" 30#include "gnunet_server_lib.h"
32#include "gnunet_upnp_service.h" 31#include "gnunet_service_lib.h"
33#include <stdint.h> 32#include "gnunet_statistics_service.h"
34#include <microhttpd.h> 33#include "gnunet_transport_service.h"
35#include <curl/curl.h> 34#include "plugin_transport.h"
36#include "ip.h"
37 35
38#define DEBUG_HTTP GNUNET_NO 36#define DEBUG_HTTP GNUNET_YES
39 37
40/** 38/**
41 * Disable GET (for debugging only!). Must be GNUNET_YES 39 * After how long do we expire an address that we
42 * in production use! 40 * learned from another peer if it is not reconfirmed
41 * by anyone?
43 */ 42 */
44#define DO_GET GNUNET_YES 43#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
45 44
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 45
60/** 46/**
61 * Default maximum size of the HTTP read and write buffer. 47 * Encapsulation of all of the state of the plugin.
62 */ 48 */
63#define HTTP_BUF_SIZE (64 * 1024) 49struct Plugin;
64
65/**
66 * Text of the response sent back after the last bytes of a PUT
67 * request have been received (just to formally obey the HTTP
68 * protocol).
69 */
70#define HTTP_PUT_RESPONSE "Thank you!"
71
72#define MY_TRANSPORT_NAME "HTTP"
73#include "common.c"
74
75/**
76 * Client-side data per PUT request.
77 */
78struct HTTPPutData
79{
80 /**
81 * This is a linked list.
82 */
83 struct HTTPPutData *next;
84
85 /**
86 * Handle to our CURL request.
87 */
88 CURL *curl_put;
89 50
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 51
120/** 52/**
121 * Server-side data per PUT request. 53 * Session handle for connections.
122 */ 54 */
123struct MHDPutData 55struct Session
124{ 56{
125 /**
126 * This is a linked list.
127 */
128 struct MHDPutData *next;
129 57
130 /** 58 /**
131 * MHD connection handle for this request. 59 * Stored in a linked list.
132 */ 60 */
133 struct MHD_Connection *session; 61 struct Session *next;
134 62
135 /** 63 /**
136 * Last time we received data on this PUT 64 * Pointer to the global plugin struct.
137 * connection.
138 */ 65 */
139 GNUNET_CronTime last_activity; 66 struct Plugin *plugin;
140 67
141 /** 68 /**
142 * Read buffer for the header (from PUT) 69 * The client (used to identify this connection)
143 */ 70 */
144 char rbuff1[sizeof (GNUNET_MessageHeader)]; 71 /* void *client; */
145 72
146 /** 73 /**
147 * The read buffer (used only receiving PUT data). 74 * Continuation function to call once the transmission buffer
75 * has again space available. NULL if there is no
76 * continuation to call.
148 */ 77 */
149 char *rbuff2; 78 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
150 79
151 /** 80 /**
152 * Number of valid bytes in rbuff1 81 * Closure for transmit_cont.
153 */ 82 */
154 unsigned int rpos1; 83 void *transmit_cont_cls;
155 84
156 /** 85 /**
157 * Number of valid bytes in rbuff2 86 * To whom are we talking to (set to our identity
87 * if we are still waiting for the welcome message)
158 */ 88 */
159 unsigned int rpos2; 89 struct GNUNET_PeerIdentity sender;
160
161 90
162 /** 91 /**
163 * Size of the rbuff2 buffer. 92 * At what time did we reset last_received last?
164 */ 93 */
165 unsigned int rsize2; 94 struct GNUNET_TIME_Absolute last_quota_update;
166 95
167 /** 96 /**
168 * Should we sent a response for this PUT yet? 97 * How many bytes have we received since the "last_quota_update"
98 * timestamp?
169 */ 99 */
170 int ready; 100 uint64_t last_received;
171 101
172 /** 102 /**
173 * Have we sent a response for this PUT yet? 103 * Number of bytes per ms that this peer is allowed
104 * to send to us.
174 */ 105 */
175 int done; 106 uint32_t quota;
176 107
177}; 108};
178 109
179/** 110/**
180 * Server-side data for a GET request. 111 * Encapsulation of all of the state of the plugin.
181 */ 112 */
182struct MHDGetData 113struct Plugin
183{ 114{
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 /** 115 /**
217 * Current write position in wbuff 116 * Our environment.
218 */ 117 */
219 unsigned int woff; 118 struct GNUNET_TRANSPORT_PluginEnvironment *env;
220 119
221 /** 120 /**
222 * Number of valid bytes in wbuff (starting at woff) 121 * List of open sessions.
223 */ 122 */
224 unsigned int wpos; 123 struct Session *sessions;
225 124
226 /** 125 /**
227 * Size of the write buffer. 126 * Handle for the statistics service.
228 */ 127 */
229 unsigned int wsize; 128 struct GNUNET_STATISTICS_Handle *statistics;
230 129
231}; 130};
232 131
233/** 132/**
234 * Transport Session handle. 133 * Function that can be used by the transport service to transmit
235 */ 134 * a message using the plugin.
236typedef struct HTTPSession
237{
238
239 /**
240 * GNUNET_TSession for this session.
241 */
242 GNUNET_TSession *tsession;
243
244 /**
245 * To whom are we talking to.
246 */
247 GNUNET_PeerIdentity sender;
248
249 /**
250 * number of users of this session
251 */
252 unsigned int users;
253
254 /**
255 * Has this session been destroyed?
256 */
257 int destroyed;
258
259 /**
260 * Are we client or server? Determines which of the
261 * structs in the union below is being used for this
262 * connection!
263 */
264 int is_client;
265
266 /**
267 * Is MHD still using this session handle?
268 */
269 int is_mhd_active;
270
271 /**
272 * Data maintained for the http client-server connection
273 * (depends on if we are client or server).
274 */
275 union
276 {
277
278 struct
279 {
280 /**
281 * Active PUT requests (linked list).
282 */
283 struct MHDPutData *puts;
284
285#if DO_GET
286 /**
287 * Active GET requests (linked list; most
288 * recent received GET is the head of the list).
289 */
290 struct MHDGetData *gets;
291#endif
292
293 } server;
294
295 struct
296 {
297
298 /**
299 * Address of the other peer.
300 */
301 HostAddress address;
302
303#if DO_GET
304 /**
305 * Last time the GET was active.
306 */
307 GNUNET_CronTime last_get_activity;
308
309 /**
310 * What was the last time we were able to
311 * transmit data using the current get handle?
312 */
313 GNUNET_CronTime last_get_initiated;
314
315 /**
316 * GET operation
317 */
318 CURL *get;
319
320 /**
321 * Read buffer for the header (from GET).
322 */
323 char rbuff1[sizeof (GNUNET_MessageHeader)];
324
325 /**
326 * The read buffer (used only receiving GET data).
327 */
328 char *rbuff2;
329
330 /**
331 * Number of valid bytes in rbuff1
332 */
333 unsigned int rpos1;
334
335 /**
336 * Number of valid bytes in rbuff2
337 */
338 unsigned int rpos2;
339
340 /**
341 * Current size of the read buffer rbuff2.
342 */
343 unsigned int rsize2;
344#endif
345
346 /**
347 * URL of the get and put operations.
348 */
349 char *url;
350
351 /**
352 * Linked list of PUT operations.
353 */
354 struct HTTPPutData *puts;
355
356 } client;
357
358 } cs;
359
360} HTTPSession;
361
362/* *********** globals ************* */
363
364static int stat_bytesReceived;
365
366static int stat_bytesSent;
367
368static int stat_bytesDropped;
369
370static int stat_get_issued;
371
372static int stat_get_received;
373
374static int stat_put_issued;
375
376static int stat_put_received;
377
378static int stat_select_calls;
379
380static int stat_send_calls;
381
382static int stat_connect_calls;
383
384static int stat_curl_send_callbacks;
385
386static int stat_curl_receive_callbacks;
387
388static int stat_mhd_access_callbacks;
389
390static int stat_mhd_read_callbacks;
391
392static int stat_mhd_close_callbacks;
393
394static int stat_connect_calls;
395
396/**
397 * How many requests do we have currently pending
398 * (with libcurl)?
399 */
400static unsigned int http_requests_pending;
401
402static struct GNUNET_DISK_FileHandle signal_pipe[2];
403
404static char *proxy;
405
406/**
407 * Daemon for listening for new connections.
408 */
409static struct MHD_Daemon *mhd_daemon;
410
411/**
412 * Curl multi for managing client operations.
413 */
414static CURLM *curl_multi;
415
416/**
417 * Set to GNUNET_YES while the transport is running.
418 */
419static int http_running;
420
421/**
422 * Thread running libcurl activities.
423 */
424static struct GNUNET_ThreadHandle *curl_thread;
425
426/**
427 * Array of currently active HTTP sessions.
428 */
429static GNUNET_TSession **tsessions;
430
431/**
432 * Number of valid entries in tsessions.
433 */
434static unsigned int tsessionCount;
435
436/**
437 * Sie of the tsessions array.
438 */
439static unsigned int tsessionArrayLength;
440
441/**
442 * Lock for concurrent access to all structures used
443 * by http, including CURL.
444 */
445static struct GNUNET_Mutex *lock;
446
447
448/**
449 * Signal select thread that its selector
450 * set may have changed.
451 */
452static void
453signal_select ()
454{
455 static char c;
456 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 */
462static int
463acceptPolicyCallback (void *cls,
464 const struct sockaddr *addr, socklen_t addr_len)
465{
466 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
467 return MHD_NO;
468 return MHD_YES;
469}
470
471/**
472 * Disconnect from a remote node. May only be called
473 * on sessions that were acquired by the caller first.
474 * For the core, aquiration means to call associate or
475 * connect. The number of disconnects must match the
476 * number of calls to connect+associate.
477 * 135 *
478 * Sessions are actually discarded in cleanup_connections. 136 * @param cls closure
479 * 137 * @param target who should receive this message
480 * 138 * @param priority how important is the message
481 * @param tsession the session that is closed 139 * @param msgbuf the message to transmit
482 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed 140 * @param msgbuf_size number of bytes in 'msgbuf'
483 */ 141 * @param timeout when should we time out
484static int 142 * @param session which session must be used (or NULL for "any")
485httpDisconnect (GNUNET_TSession * tsession) 143 * @param addr the address to use (can be NULL if the plugin
486{ 144 * is "on its own" (i.e. re-use existing TCP connection))
487 HTTPSession *httpsession = tsession->internal; 145 * @param addrlen length of the address in bytes
488 if (httpsession == NULL) 146 * @param force_address GNUNET_YES if the plugin MUST use the given address,
489 { 147 * otherwise the plugin may use other addresses or
490 GNUNET_free (tsession); 148 * existing connections (if available)
491 return GNUNET_OK; 149 * @param cont continuation to call once the message has
492 } 150 * been transmitted (or if the transport is ready
493 GNUNET_mutex_lock (lock); 151 * for the next transmission call; or if the
494 httpsession->users--; 152 * peer disconnected...)
495 GNUNET_mutex_unlock (lock); 153 * @param cont_cls closure for cont
496 return GNUNET_OK; 154 * @return number of bytes used (on the physical network, with overheads);
497} 155 * -1 on hard errors (i.e. address invalid); 0 is a legal value
498 156 * and does NOT mean that the message was not transmitted (DV)
499static void 157 */
500destroy_tsession (GNUNET_TSession * tsession) 158static ssize_t
159http_plugin_send (void *cls,
160 const struct GNUNET_PeerIdentity *
161 target,
162 const char *msgbuf,
163 size_t msgbuf_size,
164 unsigned int priority,
165 struct GNUNET_TIME_Relative timeout,
166 struct Session *session,
167 const void *addr,
168 size_t addrlen,
169 int force_address,
170 GNUNET_TRANSPORT_TransmitContinuation
171 cont, void *cont_cls)
501{ 172{
502 HTTPSession *httpsession = tsession->internal; 173 int bytes_sent = 0;
503 struct HTTPPutData *pos; 174 /* struct Plugin *plugin = cls; */
504 struct HTTPPutData *next; 175 return bytes_sent;
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} 176}
569 177
570/**
571 * MHD is done handling a request. Cleanup
572 * the respective transport state.
573 */
574static void
575requestCompletedCallback (void *unused,
576 struct MHD_Connection *session,
577 void **httpSessionCache)
578{
579 HTTPSession *httpsession = *httpSessionCache;
580 struct MHDPutData *pprev;
581 struct MHDPutData *ppos;
582#if DO_GET
583 struct MHDGetData *gprev;
584 struct MHDGetData *gpos;
585#endif
586 178
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 179
623/** 180/**
624 * A (core) Session is to be associated with a transport session. The 181 * Function that can be used to force the plugin to disconnect
625 * transport service may want to know in order to call back on the 182 * from the given peer and cancel all previous transmissions
626 * core if the connection is being closed. Associate can also be 183 * (and their continuationc).
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 * 184 *
636 * @param tsession the session handle passed along 185 * @param cls closure
637 * from the call to receive that was made by the transport 186 * @param target peer from which to disconnect
638 * layer
639 * @return GNUNET_OK if the session could be associated,
640 * GNUNET_SYSERR if not.
641 */ 187 */
642static int 188void
643httpAssociate (GNUNET_TSession * tsession) 189http_plugin_disconnect (void *cls,
190 const struct GNUNET_PeerIdentity *target)
644{ 191{
645 HTTPSession *httpSession; 192 // struct Plugin *plugin = cls;
646 193 // FIXME
647 if (tsession == NULL) 194 return;
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} 195}
663 196
664/**
665 * Add a new session to the array watched by the select thread. Grows
666 * the array if needed. If the caller wants to do anything useful
667 * with the return value, it must have the lock before
668 * calling. It is ok to call this function without holding lock if
669 * the return value is ignored.
670 */
671static unsigned int
672addTSession (GNUNET_TSession * tsession)
673{
674 unsigned int i;
675
676 GNUNET_mutex_lock (lock);
677 if (tsessionCount == tsessionArrayLength)
678 GNUNET_array_grow (tsessions, tsessionArrayLength,
679 tsessionArrayLength * 2);
680 i = tsessionCount;
681 tsessions[tsessionCount++] = tsession;
682 GNUNET_mutex_unlock (lock);
683 return i;
684}
685 197
686#if DO_GET
687/** 198/**
688 * Callback for processing GET requests if our side is the 199 * Convert the transports address to a nice, human-readable
689 * MHD HTTP server. 200 * format.
690 * 201 *
691 * @param cls the HTTP session 202 * @param cls closure
692 * @param pos read-offset in the stream 203 * @param type name of the transport that generated the address
693 * @param buf where to write the data 204 * @param addr one of the addresses of the host, NULL for the last address
694 * @param max how much data to write (at most) 205 * the specific address format depends on the transport
695 * @return number of bytes written, 0 is allowed! 206 * @param addrlen length of the address
696 */ 207 * @param numeric should (IP) addresses be displayed in numeric form?
697static int 208 * @param timeout after how long should we give up?
698contentReaderCallback (void *cls, uint64_t pos, char *buf, int max) 209 * @param asc function to call on each string
699{ 210 * @param asc_cls closure for asc
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 */ 211 */
733static void 212static void
734contentReaderFreeCallback (void *cls) 213http_plugin_address_pretty_printer (void *cls,
214 const char *type,
215 const void *addr,
216 size_t addrlen,
217 int numeric,
218 struct GNUNET_TIME_Relative timeout,
219 GNUNET_TRANSPORT_AddressStringCallback
220 asc, void *asc_cls)
735{ 221{
736 struct MHDGetData *mgd = cls; 222 asc (asc_cls, NULL);
737
738 GNUNET_GE_ASSERT (NULL, mgd->get == NULL);
739 GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0);
740 GNUNET_free (mgd);
741} 223}
742#endif
743
744/**
745 * Process GET or PUT request received via MHD. For
746 * GET, queue response that will send back our pending
747 * messages. For PUT, process incoming data and send
748 * to GNUnet core. In either case, check if a session
749 * already exists and create a new one if not.
750 */
751static int
752accessHandlerCallback (void *cls,
753 struct MHD_Connection *session,
754 const char *url,
755 const char *method,
756 const char *version,
757 const char *upload_data,
758 size_t * upload_data_size, void **httpSessionCache)
759{
760 GNUNET_TSession *tsession;
761 struct MHDPutData *put;
762 struct MHDGetData *get;
763 HTTPSession *httpSession;
764 struct MHD_Response *response;
765 GNUNET_HashCode client;
766 int i;
767 unsigned int have;
768 GNUNET_MessageHeader *hdr;
769 GNUNET_TransportPacket *mp;
770 unsigned int cpy;
771 unsigned int poff;
772
773 if (stats != NULL)
774 stats->change (stat_mhd_access_callbacks, 1);
775#if DEBUG_HTTP
776 GNUNET_GE_LOG (coreAPI->ectx,
777 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
778 "HTTP/MHD receives `%s' request.\n", method);
779#endif
780 /* convert URL to sender peer id */
781 if ((strlen (url) < 2)
782 || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client)))
783 {
784 /* invalid request */
785 /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely
786 somebody scanning for MyDoom.X-opened backdoors */
787 return MHD_NO;
788 }
789 224
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 225
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 226
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/** 227/**
980 * Process downloaded bits (from GET via CURL). 228 * Another peer has suggested an address for this
981 */ 229 * peer and transport plugin. Check that this could be a valid
982static size_t 230 * address. If so, consider adding it to the list
983receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx) 231 * of addresses.
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.
1010 client.rbuff1[httpSession->cs.client.rpos1], &inbuf[poff],
1011 cpy);
1012 httpSession->cs.client.rpos1 += cpy;
1013 have -= cpy;
1014 poff += cpy;
1015 httpSession->cs.client.rpos2 = 0;
1016 }
1017 if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader))
1018 break;
1019 hdr = (GNUNET_MessageHeader *) httpSession->cs.client.rbuff1;
1020 GNUNET_array_grow (httpSession->cs.client.rbuff2,
1021 httpSession->cs.client.rsize2,
1022 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader));
1023 if (httpSession->cs.client.rpos2 <
1024 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1025 {
1026 cpy =
1027 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) -
1028 httpSession->cs.client.rpos2;
1029 if (cpy > have)
1030 cpy = have;
1031 memcpy (&httpSession->cs.
1032 client.rbuff2[httpSession->cs.client.rpos2], &inbuf[poff],
1033 cpy);
1034 have -= cpy;
1035 poff += cpy;
1036 httpSession->cs.client.rpos2 += cpy;
1037 }
1038 if (httpSession->cs.client.rpos2 <
1039 ntohs (hdr->size) - sizeof (GNUNET_MessageHeader))
1040 break;
1041 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
1042 mp->msg = httpSession->cs.client.rbuff2;
1043 mp->sender = httpSession->sender;
1044 mp->tsession = httpSession->tsession;
1045 mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader);
1046 coreAPI->receive (mp);
1047 httpSession->cs.client.rbuff2 = NULL;
1048 httpSession->cs.client.rpos2 = 0;
1049 httpSession->cs.client.rsize2 = 0;
1050 httpSession->cs.client.rpos1 = 0;
1051 }
1052 if (stats != NULL)
1053 stats->change (stat_bytesReceived, size * nmemb);
1054 return size * nmemb;
1055}
1056#endif
1057
1058/**
1059 * Provide bits for upload: we're using CURL for a PUT request
1060 * and now need to provide data from the message we are transmitting.
1061 */
1062static size_t
1063sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx)
1064{
1065 struct HTTPPutData *put = ctx;
1066 size_t max = size * nmemb;
1067
1068 if (stats != NULL)
1069 stats->change (stat_curl_send_callbacks, 1);
1070 put->last_activity = GNUNET_get_time ();
1071 if (max > put->size - put->pos)
1072 max = put->size - put->pos;
1073 memcpy (ptr, &put->msg[put->pos], max);
1074 put->pos += max;
1075#if DEBUG_HTTP
1076 GNUNET_GE_LOG (coreAPI->ectx,
1077 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1078 "HTTP/CURL sends %u bytes in PUT request.\n", max);
1079#endif
1080 if (stats != NULL)
1081 stats->change (stat_bytesSent, max);
1082 return max;
1083}
1084
1085#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);
1086#define IP_BUF_LEN 128
1087
1088static void
1089create_session_url (HTTPSession * httpSession)
1090{
1091 char buf[IP_BUF_LEN];
1092 char *url;
1093 GNUNET_EncName enc;
1094 unsigned short available;
1095 const char *obr;
1096 const char *cbr;
1097 const HostAddress *haddr =
1098 (const HostAddress *) &httpSession->cs.client.address;
1099
1100 url = httpSession->cs.client.url;
1101 if (url == NULL)
1102 {
1103 GNUNET_hash_to_enc (&coreAPI->my_identity->hashPubKey, &enc);
1104 available = ntohs (haddr->availability) & available_protocols;
1105 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
1106 {
1107 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
1108 available = VERSION_AVAILABLE_IPV4;
1109 else
1110 available = VERSION_AVAILABLE_IPV6;
1111 }
1112 if ((available & VERSION_AVAILABLE_IPV4) > 0)
1113 {
1114 if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN))
1115 {
1116 /* log? */
1117 return;
1118 }
1119 obr = "";
1120 cbr = "";
1121 }
1122 else if ((available & VERSION_AVAILABLE_IPV6) > 0)
1123 {
1124 if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN))
1125 {
1126 /* log? */
1127 return;
1128 }
1129 obr = "[";
1130 cbr = "]";
1131 }
1132 else
1133 return; /* error */
1134 url = GNUNET_malloc (64 + sizeof (GNUNET_EncName) + strlen (buf));
1135 GNUNET_snprintf (url,
1136 64 + sizeof (GNUNET_EncName),
1137 "http://%s%s%s:%u/%s", obr, buf, cbr,
1138 ntohs (haddr->port), &enc);
1139 httpSession->cs.client.url = url;
1140 }
1141}
1142
1143#if DO_GET
1144/**
1145 * Try to do a GET on the other peer of the given
1146 * http session.
1147 * 232 *
1148 * @return GNUNET_OK on success, GNUNET_SYSERR on error 233 * @param cls closure
234 * @param addr pointer to the address
235 * @param addrlen length of addr
236 * @return GNUNET_OK if this is a plausible address for this peer
237 * and transport
1149 */ 238 */
1150static int 239static int
1151create_curl_get (HTTPSession * httpSession) 240http_plugin_address_suggested (void *cls,
241 void *addr, size_t addrlen)
1152{ 242{
1153 CURL *curl_get; 243 /* struct Plugin *plugin = cls; */
1154 CURLcode ret;
1155 CURLMcode mret;
1156 GNUNET_CronTime now;
1157 244
1158 if (httpSession->cs.client.url == NULL) 245 /* check if the address is plausible; if so,
1159 return GNUNET_SYSERR; 246 add it to our list! */
1160 curl_get = httpSession->cs.client.get;
1161 if (curl_get != NULL)
1162 {
1163 GNUNET_mutex_lock (lock);
1164 curl_multi_remove_handle (curl_multi, curl_get);
1165 http_requests_pending--;
1166 signal_select ();
1167 curl_easy_cleanup (curl_get);
1168 GNUNET_mutex_unlock (lock);
1169 httpSession->cs.client.get = NULL;
1170 }
1171 curl_get = curl_easy_init ();
1172 if (curl_get == NULL)
1173 return GNUNET_SYSERR;
1174 /* create GET */
1175 CURL_EASY_SETOPT (curl_get, CURLOPT_FAILONERROR, 1);
1176 CURL_EASY_SETOPT (curl_get, CURLOPT_URL, httpSession->cs.client.url);
1177 if (strlen (proxy) > 0)
1178 CURL_EASY_SETOPT (curl_get, CURLOPT_PROXY, proxy);
1179 CURL_EASY_SETOPT (curl_get, CURLOPT_BUFFERSIZE, 32 * 1024);
1180 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1181 CURL_EASY_SETOPT (curl_get, CURLOPT_USERAGENT, "GNUnet-http");
1182#if 0
1183 CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1);
1184#endif
1185 CURL_EASY_SETOPT (curl_get, CURLOPT_CONNECTTIMEOUT, 150L);
1186 /* NOTE: use of CONNECTTIMEOUT without also
1187 setting NOSIGNAL results in really weird
1188 crashes on my system! */
1189 CURL_EASY_SETOPT (curl_get, CURLOPT_NOSIGNAL, 1);
1190 CURL_EASY_SETOPT (curl_get, CURLOPT_TIMEOUT, 150L);
1191 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEFUNCTION, &receiveContentCallback);
1192 CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEDATA, httpSession);
1193 CURL_EASY_SETOPT (curl_get, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1194 if (ret != CURLE_OK)
1195 {
1196 curl_easy_cleanup (curl_get);
1197 return GNUNET_SYSERR;
1198 }
1199 GNUNET_mutex_lock (lock);
1200 mret = curl_multi_add_handle (curl_multi, curl_get);
1201 http_requests_pending++;
1202 GNUNET_mutex_unlock (lock);
1203 if (stats != NULL)
1204 stats->change (stat_get_issued, 1);
1205 if (mret != CURLM_OK)
1206 {
1207 GNUNET_GE_LOG (coreAPI->ectx,
1208 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1209 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1210 "curl_multi_add_handle", __FILE__, __LINE__,
1211 curl_multi_strerror (mret));
1212 curl_easy_cleanup (curl_get);
1213 return GNUNET_SYSERR;
1214 }
1215 signal_select ();
1216 now = GNUNET_get_time ();
1217 httpSession->cs.client.last_get_activity = now;
1218 httpSession->cs.client.get = curl_get;
1219 httpSession->cs.client.last_get_initiated = now;
1220#if DEBUG_HTTP
1221 GNUNET_GE_LOG (coreAPI->ectx,
1222 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1223 "HTTP/CURL initiated GET request.\n");
1224#endif
1225 return GNUNET_OK; 247 return GNUNET_OK;
1226} 248}
1227#endif
1228 249
1229/**
1230 * Establish a connection to a remote node.
1231 *
1232 * @param hello the hello-Message for the target node
1233 * @param tsessionPtr the session handle that is set
1234 * @param may_reuse are we allowed to re-use an existing connection?
1235 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1236 */
1237static int
1238httpConnect (const GNUNET_MessageHello * hello,
1239 GNUNET_TSession ** tsessionPtr, int may_reuse)
1240{
1241 const HostAddress *haddr = (const HostAddress *) &hello[1];
1242 GNUNET_TSession *tsession;
1243 HTTPSession *httpSession;
1244 int i;
1245
1246 if (stats != NULL)
1247 stats->change (stat_connect_calls, 1);
1248 /* check if we have a session pending for this peer */
1249 tsession = NULL;
1250 if (may_reuse)
1251 {
1252 GNUNET_mutex_lock (lock);
1253 for (i = 0; i < tsessionCount; i++)
1254 {
1255 if (0 == memcmp (&hello->senderIdentity,
1256 &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity)))
1257 {
1258 tsession = tsessions[i];
1259 break;
1260 }
1261 }
1262 if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession)))
1263 {
1264 *tsessionPtr = tsession;
1265 GNUNET_mutex_unlock (lock);
1266 return GNUNET_OK;
1267 }
1268 GNUNET_mutex_unlock (lock);
1269 }
1270 /* no session pending, initiate a new one! */
1271 httpSession = GNUNET_malloc (sizeof (HTTPSession));
1272 memset (httpSession, 0, sizeof (HTTPSession));
1273 httpSession->sender = hello->senderIdentity;
1274 httpSession->users = 1; /* us only, core has not seen this tsession! */
1275 httpSession->is_client = GNUNET_YES;
1276 httpSession->cs.client.address = *haddr;
1277 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
1278 memset (tsession, 0, sizeof (GNUNET_TSession));
1279 httpSession->tsession = tsession;
1280 tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
1281 tsession->internal = httpSession;
1282 tsession->peer = hello->senderIdentity;
1283 create_session_url (httpSession);
1284#if DO_GET
1285 if (GNUNET_OK != create_curl_get (httpSession))
1286 {
1287 GNUNET_free (tsession);
1288 GNUNET_free (httpSession);
1289 return GNUNET_SYSERR;
1290 }
1291#endif
1292 /* PUTs will be created as needed */
1293 addTSession (tsession);
1294 *tsessionPtr = tsession;
1295#if DEBUG_HTTP
1296 GNUNET_GE_LOG (coreAPI->ectx,
1297 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1298 "HTTP/CURL initiated connection to `%s'.\n",
1299 httpSession->cs.client.url);
1300#endif
1301 return GNUNET_OK;
1302}
1303
1304/**
1305 * We received the "Thank you!" response to a PUT.
1306 * Discard the data (not useful) and mark the PUT
1307 * operation as completed.
1308 */
1309static size_t
1310discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls)
1311{
1312 struct HTTPPutData *put = put_cls;
1313 /* this condition should pretty much always be
1314 true; just checking here in case the PUT
1315 response comes early somehow */
1316 if (put->pos == put->size)
1317 put->done = GNUNET_YES;
1318 return size * nmemb;
1319}
1320 250
1321/** 251/**
1322 * Create a new PUT request for the given PUT data. 252 * Entry point for the plugin.
1323 */ 253 */
1324static int 254void *
1325create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put) 255libgnunet_plugin_transport_http_init (void *cls)
1326{ 256{
1327 CURL *curl_put; 257 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1328 CURLcode ret; 258 struct GNUNET_TRANSPORT_PluginFunctions *api;
1329 CURLMcode mret; 259 struct Plugin *plugin;
1330 long size; 260
1331 261 plugin = GNUNET_malloc (sizeof (struct Plugin));
1332 /* we should have initiated a GET earlier, 262 plugin->env = env;
1333 so URL must not be NULL here */ 263 plugin->statistics = NULL;
1334 if (httpSession->cs.client.url == NULL) 264 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1335 return GNUNET_SYSERR; 265 api->cls = plugin;
1336 curl_put = curl_easy_init (); 266 api->send = &http_plugin_send;
1337 if (curl_put == NULL) 267 api->disconnect = &http_plugin_disconnect;
1338 return GNUNET_SYSERR; 268 api->address_pretty_printer = &http_plugin_address_pretty_printer;
1339 CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1); 269 api->check_address = &http_plugin_address_suggested;
1340 CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url); 270 return api;
1341 if (strlen (proxy) > 0)
1342 CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy);
1343 CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size);
1344 if (0 == strncmp (httpSession->cs.client.url, "http", 4))
1345 CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http");
1346 CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1);
1347#if 0
1348 CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1);
1349#endif
1350 CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L);
1351 /* NOTE: use of CONNECTTIMEOUT without also
1352 setting NOSIGNAL results in really weird
1353 crashes on my system! */
1354 CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1);
1355 CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L);
1356 size = put->size;
1357 CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size);
1358 CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback);
1359 CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put);
1360 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback);
1361 CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put);
1362 CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1363 if (ret != CURLE_OK)
1364 {
1365 curl_easy_cleanup (curl_put);
1366 return GNUNET_SYSERR;
1367 }
1368 GNUNET_mutex_lock (lock);
1369 mret = curl_multi_add_handle (curl_multi, curl_put);
1370 http_requests_pending++;
1371 GNUNET_mutex_unlock (lock);
1372 if (stats != NULL)
1373 stats->change (stat_put_issued, 1);
1374 if (mret != CURLM_OK)
1375 {
1376 GNUNET_GE_LOG (coreAPI->ectx,
1377 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1378 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1379 "curl_multi_add_handle", __FILE__, __LINE__,
1380 curl_multi_strerror (mret));
1381 return GNUNET_SYSERR;
1382 }
1383 signal_select ();
1384 put->curl_put = curl_put;
1385#if DEBUG_HTTP
1386 GNUNET_GE_LOG (coreAPI->ectx,
1387 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1388 "HTTP/CURL initiated PUT request to `%s'.\n",
1389 httpSession->cs.client.url);
1390#endif
1391 return GNUNET_OK;
1392} 271}
1393 272
1394 273
1395/** 274/**
1396 * Test if the transport would even try to send 275 * Exit point from the plugin.
1397 * a message of the given size and importance
1398 * for the given session.<br>
1399 * This function is used to check if the core should
1400 * even bother to construct (and encrypt) this kind
1401 * of message.
1402 *
1403 * @return GNUNET_YES if the transport would try (i.e. queue
1404 * the message or call the OS to send),
1405 * GNUNET_NO if the transport would just drop the message,
1406 * GNUNET_SYSERR if the size/session is invalid
1407 */
1408static int
1409httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size,
1410 int important)
1411{
1412 HTTPSession *httpSession = tsession->internal;
1413 struct MHDGetData *get;
1414 int ret;
1415
1416 if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader))
1417 {
1418 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1419 return GNUNET_SYSERR;
1420 }
1421 if (size == 0)
1422 {
1423 GNUNET_GE_BREAK (coreAPI->ectx, 0);
1424 return GNUNET_SYSERR;
1425 }
1426 if (httpSession->is_client)
1427 {
1428 /* client */
1429 if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL))
1430 return GNUNET_NO;
1431 return GNUNET_YES;
1432 }
1433 else
1434 {
1435 /* server */
1436 GNUNET_mutex_lock (lock);
1437 get = httpSession->cs.server.gets;
1438 if (get == NULL)
1439 ret = GNUNET_NO;
1440 else
1441 {
1442 if (get->wsize == 0)
1443 ret = GNUNET_YES;
1444 else if ((get->wpos + size > get->wsize)
1445 && (important != GNUNET_YES))
1446 ret = GNUNET_NO;
1447 else
1448 ret = GNUNET_YES;
1449 }
1450 GNUNET_mutex_unlock (lock);
1451 return ret;
1452 }
1453}
1454
1455
1456/**
1457 * Send a message to the specified remote node.
1458 *
1459 * @param tsession the GNUNET_MessageHello identifying the remote node
1460 * @param msg the message
1461 * @param size the size of the message
1462 * @param important is this message so important that usual restrictions do not apply?
1463 * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full
1464 */
1465static int
1466httpSend (GNUNET_TSession * tsession,
1467 const void *msg, unsigned int size, int important)
1468{
1469 HTTPSession *httpSession = tsession->internal;
1470 struct HTTPPutData *putData;
1471 GNUNET_MessageHeader *hdr;
1472#if DO_GET
1473 struct MHDGetData *getData;
1474 char *tmp;
1475#endif
1476
1477 if (stats != NULL)
1478 stats->change (stat_send_calls, 1);
1479 if (httpSession->is_client)
1480 {
1481 /* we need to do a PUT (we are the client) */
1482 if (size >= GNUNET_MAX_BUFFER_SIZE)
1483 return GNUNET_SYSERR;
1484 if (size == 0)
1485 {
1486 GNUNET_GE_BREAK (NULL, 0);
1487 return GNUNET_SYSERR;
1488 }
1489 if (important != GNUNET_YES)
1490 {
1491 GNUNET_mutex_lock (lock);
1492 if (httpSession->cs.client.puts != NULL)
1493 {
1494 /* do not queue more than one unimportant PUT at a time */
1495 signal_select (); /* do clean up now! */
1496 GNUNET_mutex_unlock (lock);
1497 if (stats != NULL)
1498 stats->change (stat_bytesDropped, size);
1499
1500 return GNUNET_NO;
1501 }
1502 GNUNET_mutex_unlock (lock);
1503 }
1504 putData = GNUNET_malloc (sizeof (struct HTTPPutData));
1505 memset (putData, 0, sizeof (struct HTTPPutData));
1506 putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader));
1507 hdr = (GNUNET_MessageHeader *) putData->msg;
1508 hdr->size = htons (size + sizeof (GNUNET_MessageHeader));
1509 hdr->type = htons (0);
1510 memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size);
1511 putData->size = size + sizeof (GNUNET_MessageHeader);
1512 putData->last_activity = GNUNET_get_time ();
1513 if (GNUNET_OK != create_curl_put (httpSession, putData))
1514 {
1515 GNUNET_free (putData->msg);
1516 GNUNET_free (putData);
1517 return GNUNET_SYSERR;
1518 }
1519 GNUNET_mutex_lock (lock);
1520 putData->next = httpSession->cs.client.puts;
1521 httpSession->cs.client.puts = putData;
1522 GNUNET_mutex_unlock (lock);
1523 return GNUNET_OK;
1524 }
1525
1526 /* httpSession->isClient == false, respond to a GET (we
1527 hopefully have one or will have one soon) */
1528#if DEBUG_HTTP
1529 GNUNET_GE_LOG (coreAPI->ectx,
1530 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1531 "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n",
1532 size);
1533#endif
1534#if DO_GET
1535 GNUNET_mutex_lock (lock);
1536 getData = httpSession->cs.server.gets;
1537 if (getData == NULL)
1538 {
1539 GNUNET_mutex_unlock (lock);
1540 return GNUNET_SYSERR;
1541 }
1542 if (getData->wsize == 0)
1543 GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE);
1544 size += sizeof (GNUNET_MessageHeader);
1545 if (getData->wpos + size > getData->wsize)
1546 {
1547 /* need to grow or discard */
1548 if (!important)
1549 {
1550 GNUNET_mutex_unlock (lock);
1551 return GNUNET_NO;
1552 }
1553 tmp = GNUNET_malloc (getData->wpos + size);
1554 memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos);
1555 hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos];
1556 hdr->type = htons (0);
1557 hdr->size = htons (size);
1558 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1559 GNUNET_free (getData->wbuff);
1560 getData->wbuff = tmp;
1561 getData->wsize = getData->wpos + size;
1562 getData->woff = 0;
1563 getData->wpos = getData->wpos + size;
1564 }
1565 else
1566 {
1567 /* fits without growing */
1568 if (getData->wpos + getData->woff + size > getData->wsize)
1569 {
1570 /* need to compact first */
1571 memmove (getData->wbuff,
1572 &getData->wbuff[getData->woff], getData->wpos);
1573 getData->woff = 0;
1574 }
1575 /* append */
1576 hdr =
1577 (GNUNET_MessageHeader *) & getData->wbuff[getData->woff +
1578 getData->wpos];
1579 hdr->size = htons (size);
1580 hdr->type = htons (0);
1581 memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader));
1582 getData->wpos += size;
1583 }
1584 signal_select ();
1585 GNUNET_mutex_unlock (lock);
1586#endif
1587 return GNUNET_OK;
1588}
1589
1590/**
1591 * Function called to cleanup dead connections
1592 * (completed PUTs, GETs that have timed out,
1593 * etc.). Also re-vives GETs that have timed out
1594 * if we are still interested in the connection.
1595 */
1596static void
1597cleanup_connections ()
1598{
1599 int i;
1600 HTTPSession *s;
1601 struct HTTPPutData *prev;
1602 struct HTTPPutData *pos;
1603 struct MHDPutData *mpos;
1604 struct MHDPutData *mprev;
1605#if DO_GET
1606 struct MHD_Response *r;
1607 struct MHDGetData *gpos;
1608 struct MHDGetData *gnext;
1609#endif
1610 GNUNET_CronTime now;
1611
1612 GNUNET_mutex_lock (lock);
1613 now = GNUNET_get_time ();
1614 for (i = 0; i < tsessionCount; i++)
1615 {
1616 s = tsessions[i]->internal;
1617 if (s->is_client)
1618 {
1619 if ((s->cs.client.puts == NULL) && (s->users == 0)
1620#if DO_GET
1621 && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now)
1622#endif
1623 )
1624 {
1625#if DO_GET
1626#if DEBUG_HTTP
1627 GNUNET_GE_LOG (coreAPI->ectx,
1628 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1629 GNUNET_GE_USER,
1630 "HTTP transport destroys old (%llu ms) unused client session\n",
1631 now - s->cs.client.last_get_activity);
1632#endif
1633#endif
1634 destroy_tsession (tsessions[i]);
1635 i--;
1636 continue;
1637 }
1638
1639 prev = NULL;
1640 pos = s->cs.client.puts;
1641 while (pos != NULL)
1642 {
1643 if (pos->last_activity + HTTP_TIMEOUT < now)
1644 pos->done = GNUNET_YES;
1645 if (pos->done)
1646 {
1647 if (prev == NULL)
1648 s->cs.client.puts = pos->next;
1649 else
1650 prev->next = pos->next;
1651 GNUNET_free (pos->msg);
1652 curl_multi_remove_handle (curl_multi, pos->curl_put);
1653 http_requests_pending--;
1654 signal_select ();
1655 curl_easy_cleanup (pos->curl_put);
1656 GNUNET_free (pos);
1657 if (prev == NULL)
1658 pos = s->cs.client.puts;
1659 else
1660 pos = prev->next;
1661 continue;
1662 }
1663 prev = pos;
1664 pos = pos->next;
1665 }
1666#if DO_GET
1667 if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) &&
1668 ((s->users > 0) || (s->cs.client.puts != NULL)) &&
1669 ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) ||
1670 (s->cs.client.get == NULL)) &&
1671 ((s->cs.client.get == NULL) ||
1672 (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now)))
1673 create_curl_get (s);
1674#endif
1675 }
1676 else
1677 {
1678 mpos = s->cs.server.puts;
1679 mprev = NULL;
1680 while (mpos != NULL)
1681 {
1682 if (mpos->last_activity == 0)
1683 {
1684 if (mprev == NULL)
1685 s->cs.server.puts = mpos->next;
1686 else
1687 mprev->next = mpos->next;
1688 GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0);
1689 GNUNET_free (mpos);
1690 if (mprev == NULL)
1691 mpos = s->cs.server.puts;
1692 else
1693 mpos = mprev->next;
1694 continue;
1695 }
1696 mprev = mpos;
1697 mpos = mpos->next;
1698 }
1699
1700 /* ! s->is_client */
1701#if DO_GET
1702 gpos = s->cs.server.gets;
1703 while (gpos != NULL)
1704 {
1705 gnext = gpos->next;
1706 gpos->next = NULL;
1707 if ((gpos->last_get_activity + HTTP_TIMEOUT < now) ||
1708 (gpos != s->cs.server.gets))
1709 {
1710 if (gpos == s->cs.server.gets)
1711 s->cs.server.gets = NULL;
1712 r = gpos->get;
1713 gpos->get = NULL;
1714 MHD_destroy_response (r);
1715 }
1716 gpos = gnext;
1717 }
1718#endif
1719 if (
1720#if DO_GET
1721 (s->cs.server.gets == NULL) &&
1722#endif
1723 (s->is_mhd_active == 0) && (s->users == 0))
1724 {
1725#if DO_GET
1726#if DEBUG_HTTP
1727 GNUNET_GE_LOG (coreAPI->ectx,
1728 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
1729 GNUNET_GE_USER,
1730 "HTTP transport destroys unused server session\n");
1731#endif
1732#endif
1733 destroy_tsession (tsessions[i]);
1734 i--;
1735 continue;
1736 }
1737 }
1738 }
1739 GNUNET_mutex_unlock (lock);
1740}
1741
1742/**
1743 * Thread that runs the CURL and MHD requests.
1744 */ 276 */
1745static void * 277void *
1746curl_runner (void *unused) 278libgnunet_plugin_transport_http_done (void *cls)
1747{ 279{
1748 CURLMcode mret; 280 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1749 fd_set rs; 281 struct Plugin *plugin = api->cls;
1750 fd_set ws;
1751 fd_set es;
1752 struct GNUNET_NETWORK_FDSet *hrs;
1753 struct GNUNET_NETWORK_FDSet *hws;
1754 struct GNUNET_NETWORK_FDSet *hes;
1755 int max;
1756 int running;
1757 unsigned long long timeout;
1758 long ms;
1759 int have_tv;
1760 char buf[128]; /* for reading from pipe */
1761 int ret;
1762
1763#if DEBUG_HTTP
1764 GNUNET_GE_LOG (coreAPI->ectx,
1765 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1766 "HTTP transport select thread started\n");
1767#endif
1768
1769 hrs = GNUNET_net_fdset_create ();
1770 hws = GNUNET_net_fdset_create ();
1771 hes = GNUNET_net_fdset_create ();
1772
1773 while (GNUNET_YES == http_running)
1774 {
1775 max = 0;
1776 FD_ZERO (&rs);
1777 FD_ZERO (&ws);
1778 FD_ZERO (&es);
1779 GNUNET_mutex_lock (lock);
1780 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1781 GNUNET_mutex_unlock (lock);
1782 if (mret != CURLM_OK)
1783 {
1784 GNUNET_GE_LOG (coreAPI->ectx,
1785 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1786 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1787 "curl_multi_fdset", __FILE__, __LINE__,
1788 curl_multi_strerror (mret));
1789 break;
1790 }
1791 if (mhd_daemon != NULL)
1792 MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max);
1793 timeout = 0;
1794 have_tv = MHD_NO;
1795 if (mhd_daemon != NULL)
1796 have_tv = MHD_get_timeout (mhd_daemon, &timeout);
1797 GNUNET_mutex_lock (lock);
1798 if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) &&
1799 (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO)))
1800 {
1801 timeout = ms;
1802 have_tv = MHD_YES;
1803 }
1804 GNUNET_mutex_unlock (lock);
1805
1806 GNUNET_net_fdset_zero (hws);
1807 GNUNET_net_fdset_zero (hrs);
1808 GNUNET_net_fdset_zero (hes);
1809 GNUNET_net_fdset_copy_native (hws, ws);
1810 GNUNET_net_fdset_copy_native (hrs, rs);
1811 GNUNET_net_fdset_copy_native (hes, es);
1812 282
1813 GNUNET_net_fdset_handle_set (signal_pipe[0], hrs); 283 GNUNET_free (plugin);
1814 if (stats != NULL) 284 GNUNET_free (api);
1815 stats->change (stat_select_calls, 1);
1816 ret =
1817 GNUNET_net_select (hrs, hws, hes,
1818 (have_tv ==
1819 MHD_YES) ? timeout :
1820 GNUNET_TIME_UNIT_FOREVER_REL);
1821 if (ret == GNUNET_SYSERR)
1822 {
1823 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
1824 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1825 GNUNET_GE_DEVELOPER, "select");
1826 }
1827 if (GNUNET_YES != http_running)
1828 break;
1829 running = 0;
1830 do
1831 {
1832 GNUNET_mutex_lock (lock);
1833 mret = curl_multi_perform (curl_multi, &running);
1834 GNUNET_mutex_unlock (lock);
1835 }
1836 while ((mret == CURLM_CALL_MULTI_PERFORM)
1837 && (http_running == GNUNET_YES));
1838 if (GNUNET_net_fdset_handle_isset (signal_pipe[0], hrs))
1839 GNUNET_DISK_file_read (signal_pipe[0], buf, sizeof (buf));
1840 if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM))
1841 GNUNET_GE_LOG (coreAPI->ectx,
1842 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
1843 GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"),
1844 "curl_multi_perform", __FILE__, __LINE__,
1845 curl_multi_strerror (mret));
1846 if (mhd_daemon != NULL)
1847 MHD_run (mhd_daemon);
1848 cleanup_connections ();
1849 }
1850#if DEBUG_HTTP
1851 GNUNET_GE_LOG (coreAPI->ectx,
1852 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
1853 "HTTP transport select thread exits.\n");
1854#endif
1855 return NULL; 285 return NULL;
1856} 286}
1857 287
1858 288/* end of plugin_transport_http.c */
1859/**
1860 * Start the server process to receive inbound traffic.
1861 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
1862 */
1863static int
1864startTransportServer ()
1865{
1866 unsigned short port;
1867
1868 if ((curl_multi != NULL) || (http_running == GNUNET_YES))
1869 return GNUNET_SYSERR;
1870 curl_multi = curl_multi_init ();
1871 if (curl_multi == NULL)
1872 return GNUNET_SYSERR;
1873 port = get_port ();
1874 if ((mhd_daemon == NULL) && (port != 0))
1875 {
1876 if (GNUNET_YES !=
1877 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1878 "DISABLE-IPV6",
1879 GNUNET_YES))
1880 {
1881 mhd_daemon = MHD_start_daemon (MHD_USE_IPv6,
1882 port,
1883 &acceptPolicyCallback,
1884 NULL, &accessHandlerCallback, NULL,
1885 MHD_OPTION_CONNECTION_TIMEOUT,
1886 (unsigned int) HTTP_TIMEOUT,
1887 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1888 (unsigned int) 1024 * 128,
1889 MHD_OPTION_CONNECTION_LIMIT,
1890 (unsigned int) 128,
1891 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1892 (unsigned int) 8,
1893 MHD_OPTION_NOTIFY_COMPLETED,
1894 &requestCompletedCallback, NULL,
1895 MHD_OPTION_END);
1896 }
1897 if (mhd_daemon == NULL)
1898 {
1899 /* try without IPv6 */
1900 mhd_daemon = MHD_start_daemon (MHD_NO_FLAG,
1901 port,
1902 &acceptPolicyCallback,
1903 NULL, &accessHandlerCallback, NULL,
1904 MHD_OPTION_CONNECTION_TIMEOUT,
1905 (unsigned int) HTTP_TIMEOUT,
1906 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1907 (unsigned int) 1024 * 128,
1908 MHD_OPTION_CONNECTION_LIMIT,
1909 (unsigned int) 128,
1910 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
1911 (unsigned int) 8,
1912 MHD_OPTION_NOTIFY_COMPLETED,
1913 &requestCompletedCallback, NULL,
1914 MHD_OPTION_END);
1915 }
1916 else
1917 {
1918 available_protocols |= VERSION_AVAILABLE_IPV6;
1919 }
1920 if (mhd_daemon != NULL)
1921 available_protocols |= VERSION_AVAILABLE_IPV4;
1922 }
1923 if (port == 0)
1924 {
1925 /* NAT */
1926 available_protocols |= VERSION_AVAILABLE_IPV4;
1927 if (GNUNET_YES !=
1928 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD",
1929 "DISABLE-IPV6",
1930 GNUNET_YES))
1931 available_protocols |= VERSION_AVAILABLE_IPV6;
1932 }
1933 if (GNUNET_OK != GNUNET_DISK_pipe (signal_pipe, GNUNET_NO))
1934 {
1935 MHD_stop_daemon (mhd_daemon);
1936 curl_multi_cleanup (curl_multi);
1937 curl_multi = NULL;
1938 mhd_daemon = NULL;
1939 return GNUNET_SYSERR;
1940 }
1941 http_running = GNUNET_YES;
1942 curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024);
1943 if (curl_thread == NULL)
1944 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
1945 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
1946 GNUNET_GE_IMMEDIATE, "pthread_create");
1947 return GNUNET_OK;
1948}
1949
1950/**
1951 * Shutdown the server process (stop receiving inbound
1952 * traffic). May be restarted later!
1953 */
1954static int
1955stopTransportServer ()
1956{
1957 void *unused;
1958 int i;
1959 HTTPSession *s;
1960
1961 if ((http_running == GNUNET_NO) || (curl_multi == NULL))
1962 return GNUNET_SYSERR;
1963 http_running = GNUNET_NO;
1964 signal_select ();
1965 GNUNET_thread_stop_sleep (curl_thread);
1966 GNUNET_thread_join (curl_thread, &unused);
1967 GNUNET_DISK_close (signal_pipe[0]);
1968 GNUNET_DISK_close (signal_pipe[1]);
1969 if (mhd_daemon != NULL)
1970 {
1971 MHD_stop_daemon (mhd_daemon);
1972 mhd_daemon = NULL;
1973 }
1974 cleanup_connections ();
1975 for (i = 0; i < tsessionCount; i++)
1976 {
1977 s = tsessions[i]->internal;
1978 if (s->users == 0)
1979 {
1980 destroy_tsession (tsessions[i]);
1981 i--;
1982 }
1983 }
1984 curl_multi_cleanup (curl_multi);
1985 curl_multi = NULL;
1986 return GNUNET_OK;
1987}
1988
1989/* ******************** public API ******************** */
1990
1991/**
1992 * The exported method. Makes the core api available
1993 * via a global and returns the udp transport API.
1994 */
1995GNUNET_TransportAPI *
1996inittransport_http (GNUNET_CoreAPIForTransport * core)
1997{
1998 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
1999 coreAPI = core;
2000 cfg = coreAPI->cfg;
2001 lock = GNUNET_mutex_create (GNUNET_YES);
2002 if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg,
2003 &reload_configuration, NULL))
2004 {
2005 GNUNET_mutex_destroy (lock);
2006 lock = NULL;
2007 return NULL;
2008 }
2009 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2010 {
2011 GNUNET_GE_BREAK (NULL, 0);
2012 GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration,
2013 NULL);
2014 GNUNET_mutex_destroy (lock);
2015 lock = NULL;
2016 return NULL;
2017 }
2018 tsessionCount = 0;
2019 tsessionArrayLength = 0;
2020 GNUNET_array_grow (tsessions, tsessionArrayLength, 32);
2021 if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg,
2022 "HTTP", "UPNP",
2023 GNUNET_YES) == GNUNET_YES)
2024 {
2025 upnp = coreAPI->service_request ("upnp");
2026
2027 if (upnp == NULL)
2028 {
2029 GNUNET_GE_LOG (coreAPI->ectx,
2030 GNUNET_GE_ERROR | GNUNET_GE_USER |
2031 GNUNET_GE_IMMEDIATE,
2032 _
2033 ("The UPnP service could not be loaded. To disable UPnP, set the "
2034 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"),
2035 "HTTP");
2036 }
2037 }
2038 stats = coreAPI->service_request ("stats");
2039 if (stats != NULL)
2040 {
2041 stat_bytesReceived
2042 = stats->create (gettext_noop ("# bytes received via HTTP"));
2043 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP"));
2044 stat_bytesDropped
2045 = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)"));
2046 stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued"));
2047 stat_get_received
2048 = stats->create (gettext_noop ("# HTTP GET received"));
2049 stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued"));
2050 stat_put_received
2051 = stats->create (gettext_noop ("# HTTP PUT received"));
2052 stat_select_calls
2053 = stats->create (gettext_noop ("# HTTP select calls"));
2054
2055 stat_send_calls = stats->create (gettext_noop ("# HTTP send calls"));
2056
2057 stat_curl_send_callbacks
2058 = stats->create (gettext_noop ("# HTTP curl send callbacks"));
2059 stat_curl_receive_callbacks
2060 = stats->create (gettext_noop ("# HTTP curl receive callbacks"));
2061 stat_mhd_access_callbacks
2062 = stats->create (gettext_noop ("# HTTP mhd access callbacks"));
2063 stat_mhd_read_callbacks
2064 = stats->create (gettext_noop ("# HTTP mhd read callbacks"));
2065 stat_mhd_close_callbacks
2066 = stats->create (gettext_noop ("# HTTP mhd close callbacks"));
2067 stat_connect_calls
2068 = stats->create (gettext_noop ("# HTTP connect calls"));
2069 }
2070 GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
2071 "GNUNETD", "HTTP-PROXY", "",
2072 &proxy);
2073
2074 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP;
2075 myAPI.mtu = 0;
2076 myAPI.cost = 20000; /* about equal to udp */
2077 myAPI.hello_verify = &verify_hello;
2078 myAPI.hello_create = &create_hello;
2079 myAPI.connect = &httpConnect;
2080 myAPI.associate = &httpAssociate;
2081 myAPI.send = &httpSend;
2082 myAPI.disconnect = &httpDisconnect;
2083 myAPI.server_start = &startTransportServer;
2084 myAPI.server_stop = &stopTransportServer;
2085 myAPI.hello_to_address = &hello_to_address;
2086 myAPI.send_now_test = &httpTestWouldTry;
2087
2088 return &myAPI;
2089}
2090
2091void
2092donetransport_http ()
2093{
2094 curl_global_cleanup ();
2095 GNUNET_free_non_null (proxy);
2096 proxy = NULL;
2097 GNUNET_array_grow (tsessions, tsessionArrayLength, 0);
2098 do_shutdown ();
2099}
2100
2101/* end of http.c */
diff --git a/src/transport/test_plugin_transport_http.c b/src/transport/test_plugin_transport_http.c
index 9095e14c5..1e9f36286 100644
--- a/src/transport/test_plugin_transport_http.c
+++ b/src/transport/test_plugin_transport_http.c
@@ -18,11 +18,9 @@
18 Boston, MA 02111-1307, USA. 18 Boston, MA 02111-1307, USA.
19*/ 19*/
20/** 20/**
21 * @file transport/test_transport_api.c 21 * @file transport/test_plugin_transport_http.c
22 * @brief testcase for transport_api.c 22 * @brief testcase for plugin_transport_http.c
23 * @author Sailor Siraj 23 * @author Matthias Wachs
24 * @author Christian Grothoff
25 * @author Nathan Evans
26 */ 24 */
27 25
28#include "platform.h" 26#include "platform.h"
@@ -38,7 +36,7 @@
38#include "plugin_transport.h" 36#include "plugin_transport.h"
39#include "transport.h" 37#include "transport.h"
40 38
41#define VERBOSE GNUNET_NO 39#define VERBOSE GNUNET_YES
42 40
43/** 41/**
44 * How long until we give up on transmitting the message? 42 * How long until we give up on transmitting the message?
@@ -223,15 +221,15 @@ run (void *cls,
223 221
224 /* load plugins... */ 222 /* load plugins... */
225 setup_plugin_environment (); 223 setup_plugin_environment ();
226 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading udp transport plugin\n")); 224 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading http transport plugin\n"));
227 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_udp"); 225 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_http");
228 226
229 api = GNUNET_PLUGIN_load (libname, &env); 227 api = GNUNET_PLUGIN_load (libname, &env);
230 GNUNET_free (libname); 228 GNUNET_free (libname);
231 if (api == NULL) 229 if (api == NULL)
232 { 230 {
233 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234 _("Failed to load transport plugin for udp\n")); 232 _("Failed to load transport plugin for http\n"));
235 /* FIXME: set some error code for main */ 233 /* FIXME: set some error code for main */
236 return; 234 return;
237 } 235 }
@@ -254,9 +252,9 @@ main (int argc, char *const *argv)
254 }; 252 };
255 int ret; 253 int ret;
256 char *const argv_prog[] = { 254 char *const argv_prog[] = {
257 "test_plugin_transport", 255 "test-gnunetd-plugin-transport_http",
258 "-c", 256 "-c",
259 "test_plugin_transport_data_udp.conf", 257 "test_plugin_transport_data_http.conf",
260 "-L", 258 "-L",
261#if VERBOSE 259#if VERBOSE
262 "DEBUG", 260 "DEBUG",
@@ -265,7 +263,7 @@ main (int argc, char *const *argv)
265#endif 263#endif
266 NULL 264 NULL
267 }; 265 };
268 GNUNET_log_setup ("test-plugin-transport", 266 GNUNET_log_setup ("test-gnunetd-plugin-transport_http",
269#if VERBOSE 267#if VERBOSE
270 "DEBUG", 268 "DEBUG",
271#else 269#else
@@ -276,12 +274,12 @@ main (int argc, char *const *argv)
276 ret = (GNUNET_OK == 274 ret = (GNUNET_OK ==
277 GNUNET_PROGRAM_run (5, 275 GNUNET_PROGRAM_run (5,
278 argv_prog, 276 argv_prog,
279 "test-plugin-transport", 277 "test-plugin-transport_http",
280 "testcase", options, &run, NULL)) ? ok : 1; 278 "testcase", options, &run, NULL)) ? ok : 1;
281 GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport"); 279 GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport_http");
282 /* FIXME: return correct value */ 280 /* FIXME: return correct value */
283 /* return ret; */ 281 /* return ret; */
284 return GNUNET_NO; 282 return GNUNET_NO;
285} 283}
286 284
287/* end of test_plugin_transport_udp.c */ 285/* end of test_plugin_transport_http.c */
diff --git a/src/transport/test_transport_api_http_peer1.conf b/src/transport/test_transport_api_http_peer1.conf
index b0f491187..5fe8864e2 100644
--- a/src/transport/test_transport_api_http_peer1.conf
+++ b/src/transport/test_transport_api_http_peer1.conf
@@ -49,7 +49,7 @@ MINIMUM-FRIENDS = 0
49 49
50[transport] 50[transport]
51PLUGINS = http 51PLUGINS = http
52#DEBUG = YES 52DEBUG = YES
53#PREFIX = xterm -T transport2 -e gdb --command=cmd --args 53#PREFIX = xterm -T transport2 -e gdb --command=cmd --args
54#PREFIX = valgrind --leak-check=full 54#PREFIX = valgrind --leak-check=full
55ALLOW_SHUTDOWN = YES 55ALLOW_SHUTDOWN = YES
@@ -117,6 +117,6 @@ WEAKRANDOM = YES
117HOSTKEY = $SERVICEHOME/.hostkey 117HOSTKEY = $SERVICEHOME/.hostkey
118 118
119[PATHS] 119[PATHS]
120DEFAULTCONFIG = test_transport_api_tcp_peer1.conf 120DEFAULTCONFIG = test_transport_api_http_peer1.conf
121SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/ 121SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/
122 122
diff --git a/src/transport/test_transport_api_http_peer2.conf b/src/transport/test_transport_api_http_peer2.conf
index 72348f529..2ca26fddd 100644
--- a/src/transport/test_transport_api_http_peer2.conf
+++ b/src/transport/test_transport_api_http_peer2.conf
@@ -118,6 +118,6 @@ WEAKRANDOM = YES
118HOSTKEY = $SERVICEHOME/.hostkey 118HOSTKEY = $SERVICEHOME/.hostkey
119 119
120[PATHS] 120[PATHS]
121DEFAULTCONFIG = test_transport_api_tcp_peer2.conf 121DEFAULTCONFIG = test_transport_api_http_peer2.conf
122SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/ 122SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/
123 123