diff options
-rw-r--r-- | src/transport/Makefile.am | 39 | ||||
-rw-r--r-- | src/transport/plugin_transport_http.c | 2131 | ||||
-rw-r--r-- | src/transport/test_plugin_transport_http.c | 28 | ||||
-rw-r--r-- | src/transport/test_transport_api_http_peer1.conf | 4 | ||||
-rw-r--r-- | src/transport/test_transport_api_http_peer2.conf | 2 |
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 | ||
76 | libgnunet_plugin_transport_tcp_la_SOURCES = \ | 76 | libgnunet_plugin_transport_tcp_la_SOURCES = \ |
@@ -110,22 +110,23 @@ libgnunet_plugin_transport_udp_nat_la_LIBADD = \ | |||
110 | libgnunet_plugin_transport_udp_nat_la_LDFLAGS = \ | 110 | libgnunet_plugin_transport_udp_nat_la_LDFLAGS = \ |
111 | $(GN_PLUGIN_LDFLAGS) | 111 | $(GN_PLUGIN_LDFLAGS) |
112 | 112 | ||
113 | #libgnunet_plugin_transport_http_la_SOURCES = \ | 113 | libgnunet_plugin_transport_http_la_SOURCES = \ |
114 | # plugin_transport_http.c | 114 | plugin_transport_http.c |
115 | #libgnunet_plugin_transport_http_la_LIBADD = \ | 115 | libgnunet_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 = \ | 120 | libgnunet_plugin_transport_http_la_LDFLAGS = \ |
121 | # $(GN_PLUGIN_LDFLAGS) | 121 | $(GN_PLUGIN_LDFLAGS) |
122 | 122 | ||
123 | 123 | ||
124 | check_PROGRAMS = \ | 124 | check_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 | ||
131 | TESTS = $(check_PROGRAMS) | 132 | TESTS = $(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 | ||
151 | test_transport_api_http_SOURCES = \ | 152 | #test_transport_api_http_SOURCES = \ |
152 | test_transport_api.c | 153 | # test_transport_api.c |
153 | test_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 | |||
158 | test_plugin_transport_http_SOURCES = \ | ||
159 | test_plugin_transport_http.c | ||
160 | test_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 | ||
158 | EXTRA_DIST = \ | 165 | EXTRA_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) | 49 | struct 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 | */ | ||
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 | 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 | */ |
123 | struct MHDPutData | 55 | struct 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 | */ |
182 | struct MHDGetData | 113 | struct 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. |
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 | * | 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 |
484 | static int | 142 | * @param session which session must be used (or NULL for "any") |
485 | httpDisconnect (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) | |
499 | static void | 157 | */ |
500 | destroy_tsession (GNUNET_TSession * tsession) | 158 | static ssize_t |
159 | http_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 | */ | ||
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 | 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 | */ |
642 | static int | 188 | void |
643 | httpAssociate (GNUNET_TSession * tsession) | 189 | http_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 | */ | ||
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 | 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? |
697 | static int | 208 | * @param timeout after how long should we give up? |
698 | contentReaderCallback (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 | */ |
733 | static void | 212 | static void |
734 | contentReaderFreeCallback (void *cls) | 213 | http_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 | */ | ||
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 | 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 |
982 | static size_t | 230 | * address. If so, consider adding it to the list |
983 | receiveContentCallback (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 | */ | ||
1062 | static size_t | ||
1063 | sendContentCallback (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 | |||
1088 | static void | ||
1089 | create_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 | */ |
1150 | static int | 239 | static int |
1151 | create_curl_get (HTTPSession * httpSession) | 240 | http_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 | */ | ||
1237 | static int | ||
1238 | httpConnect (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 | */ | ||
1309 | static size_t | ||
1310 | discardContentCallback (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 | */ |
1324 | static int | 254 | void * |
1325 | create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put) | 255 | libgnunet_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 | */ | ||
1408 | static int | ||
1409 | httpTestWouldTry (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 | */ | ||
1465 | static int | ||
1466 | httpSend (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 | */ | ||
1596 | static void | ||
1597 | cleanup_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 | */ |
1745 | static void * | 277 | void * |
1746 | curl_runner (void *unused) | 278 | libgnunet_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 | */ | ||
1863 | static int | ||
1864 | startTransportServer () | ||
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 | */ | ||
1954 | static int | ||
1955 | stopTransportServer () | ||
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 | */ | ||
1995 | GNUNET_TransportAPI * | ||
1996 | inittransport_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 | |||
2091 | void | ||
2092 | donetransport_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] |
51 | PLUGINS = http | 51 | PLUGINS = http |
52 | #DEBUG = YES | 52 | DEBUG = 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 |
55 | ALLOW_SHUTDOWN = YES | 55 | ALLOW_SHUTDOWN = YES |
@@ -117,6 +117,6 @@ WEAKRANDOM = YES | |||
117 | HOSTKEY = $SERVICEHOME/.hostkey | 117 | HOSTKEY = $SERVICEHOME/.hostkey |
118 | 118 | ||
119 | [PATHS] | 119 | [PATHS] |
120 | DEFAULTCONFIG = test_transport_api_tcp_peer1.conf | 120 | DEFAULTCONFIG = test_transport_api_http_peer1.conf |
121 | SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/ | 121 | SERVICEHOME = /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 | |||
118 | HOSTKEY = $SERVICEHOME/.hostkey | 118 | HOSTKEY = $SERVICEHOME/.hostkey |
119 | 119 | ||
120 | [PATHS] | 120 | [PATHS] |
121 | DEFAULTCONFIG = test_transport_api_tcp_peer2.conf | 121 | DEFAULTCONFIG = test_transport_api_http_peer2.conf |
122 | SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/ | 122 | SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/ |
123 | 123 | ||