diff options
author | Matthias Wachs <wachs@net.in.tum.de> | 2012-08-21 14:28:55 +0000 |
---|---|---|
committer | Matthias Wachs <wachs@net.in.tum.de> | 2012-08-21 14:28:55 +0000 |
commit | b109b888a1a99ce9c9396e606cea57204762c1d4 (patch) | |
tree | 500df275a7916d8fe61e4ba536b8cb03b26fe8e3 /src/transport/plugin_transport_http_client.c | |
parent | 77463bc3a995cf19a1bfb7512530c29aa1bc80cc (diff) | |
download | gnunet-b109b888a1a99ce9c9396e606cea57204762c1d4.tar.gz gnunet-b109b888a1a99ce9c9396e606cea57204762c1d4.zip |
- preprations for http plugin split
Diffstat (limited to 'src/transport/plugin_transport_http_client.c')
-rw-r--r-- | src/transport/plugin_transport_http_client.c | 784 |
1 files changed, 223 insertions, 561 deletions
diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c index ff23da974..f4790b0f6 100644 --- a/src/transport/plugin_transport_http_client.c +++ b/src/transport/plugin_transport_http_client.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of GNUnet | 2 | This file is part of GNUnet |
3 | (C) 2002--2012 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 |
@@ -20,626 +20,288 @@ | |||
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file transport/plugin_transport_http_client.c | 22 | * @file transport/plugin_transport_http_client.c |
23 | * @brief http transport service plugin | 23 | * @brief HTTP/S client transport plugin |
24 | * @author Matthias Wachs | 24 | * @author Matthias Wachs |
25 | */ | 25 | */ |
26 | 26 | ||
27 | #include "plugin_transport_http.h" | 27 | #if BUILD_HTTPS |
28 | #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init | ||
29 | #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done | ||
30 | #else | ||
31 | #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init | ||
32 | #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done | ||
33 | #endif | ||
34 | |||
28 | 35 | ||
29 | static struct Plugin * p; | 36 | #include "platform.h" |
37 | #include "gnunet_protocols.h" | ||
38 | #include "gnunet_connection_lib.h" | ||
39 | #include "gnunet_server_lib.h" | ||
40 | #include "gnunet_service_lib.h" | ||
41 | #include "gnunet_statistics_service.h" | ||
42 | #include "gnunet_transport_service.h" | ||
43 | #include "gnunet_transport_plugin.h" | ||
44 | |||
45 | #define DEBUG_TEMPLATE GNUNET_EXTRA_LOGGING | ||
30 | 46 | ||
31 | #if VERBOSE_CURL | ||
32 | /** | 47 | /** |
33 | * Function to log curl debug messages with GNUNET_log | 48 | * After how long do we expire an address that we |
34 | * @param curl handle | 49 | * learned from another peer if it is not reconfirmed |
35 | * @param type curl_infotype | 50 | * by anyone? |
36 | * @param data data | ||
37 | * @param size size | ||
38 | * @param cls closure | ||
39 | * @return 0 | ||
40 | */ | 51 | */ |
41 | static int | 52 | #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) |
42 | client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls) | 53 | |
43 | { | ||
44 | if (type == CURLINFO_TEXT) | ||
45 | { | ||
46 | char text[size + 2]; | ||
47 | |||
48 | memcpy (text, data, size); | ||
49 | if (text[size - 1] == '\n') | ||
50 | text[size] = '\0'; | ||
51 | else | ||
52 | { | ||
53 | text[size] = '\n'; | ||
54 | text[size + 1] = '\0'; | ||
55 | } | ||
56 | #if BUILD_HTTPS | ||
57 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https", | ||
58 | "Client: %p - %s", cls, text); | ||
59 | #else | ||
60 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http", | ||
61 | "Client: %p - %s", cls, text); | ||
62 | #endif | ||
63 | } | ||
64 | return 0; | ||
65 | } | ||
66 | #endif | ||
67 | 54 | ||
68 | /** | 55 | /** |
69 | * Task performing curl operations | 56 | * Encapsulation of all of the state of the plugin. |
70 | * @param cls plugin as closure | ||
71 | * @param tc gnunet scheduler task context | ||
72 | */ | 57 | */ |
73 | static void | 58 | struct Plugin; |
74 | client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
75 | 59 | ||
76 | 60 | ||
77 | /** | 61 | /** |
78 | * Function setting up file descriptors and scheduling task to run | 62 | * Session handle for connections. |
79 | * | ||
80 | * @param plugin plugin as closure | ||
81 | * @param now schedule task in 1ms, regardless of what curl may say | ||
82 | * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok | ||
83 | */ | 63 | */ |
84 | static int | 64 | struct Session |
85 | client_schedule (struct Plugin *plugin, int now) | ||
86 | { | 65 | { |
87 | fd_set rs; | 66 | /** |
88 | fd_set ws; | 67 | * To whom are we talking to (set to our identity |
89 | fd_set es; | 68 | * if we are still waiting for the welcome message) |
90 | int max; | 69 | */ |
91 | struct GNUNET_NETWORK_FDSet *grs; | 70 | struct GNUNET_PeerIdentity sender; |
92 | struct GNUNET_NETWORK_FDSet *gws; | 71 | |
93 | long to; | 72 | /** |
94 | CURLMcode mret; | 73 | * Stored in a linked list. |
95 | struct GNUNET_TIME_Relative timeout; | 74 | */ |
96 | 75 | struct Session *next; | |
97 | /* Cancel previous scheduled task */ | 76 | |
98 | if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) | 77 | /** |
99 | { | 78 | * Pointer to the global plugin struct. |
100 | GNUNET_SCHEDULER_cancel (plugin->client_perform_task); | 79 | */ |
101 | plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; | 80 | struct Plugin *plugin; |
102 | } | 81 | |
103 | max = -1; | 82 | /** |
104 | FD_ZERO (&rs); | 83 | * The client (used to identify this connection) |
105 | FD_ZERO (&ws); | 84 | */ |
106 | FD_ZERO (&es); | 85 | /* void *client; */ |
107 | mret = curl_multi_fdset (plugin->client_mh, &rs, &ws, &es, &max); | 86 | |
108 | if (mret != CURLM_OK) | 87 | /** |
109 | { | 88 | * Continuation function to call once the transmission buffer |
110 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"), | 89 | * has again space available. NULL if there is no |
111 | "curl_multi_fdset", __FILE__, __LINE__, | 90 | * continuation to call. |
112 | curl_multi_strerror (mret)); | 91 | */ |
113 | return GNUNET_SYSERR; | 92 | GNUNET_TRANSPORT_TransmitContinuation transmit_cont; |
114 | } | 93 | |
115 | mret = curl_multi_timeout (plugin->client_mh, &to); | 94 | /** |
116 | if (to == -1) | 95 | * Closure for transmit_cont. |
117 | timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1); | 96 | */ |
118 | else | 97 | void *transmit_cont_cls; |
119 | timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to); | 98 | |
120 | if (now == GNUNET_YES) | 99 | /** |
121 | timeout = GNUNET_TIME_UNIT_MILLISECONDS; | 100 | * At what time did we reset last_received last? |
122 | 101 | */ | |
123 | if (mret != CURLM_OK) | 102 | struct GNUNET_TIME_Absolute last_quota_update; |
124 | { | 103 | |
125 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"), | 104 | /** |
126 | "curl_multi_timeout", __FILE__, __LINE__, | 105 | * How many bytes have we received since the "last_quota_update" |
127 | curl_multi_strerror (mret)); | 106 | * timestamp? |
128 | return GNUNET_SYSERR; | 107 | */ |
129 | } | 108 | uint64_t last_received; |
130 | 109 | ||
131 | grs = GNUNET_NETWORK_fdset_create (); | 110 | /** |
132 | gws = GNUNET_NETWORK_fdset_create (); | 111 | * Number of bytes per ms that this peer is allowed |
133 | GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); | 112 | * to send to us. |
134 | GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); | 113 | */ |
135 | 114 | uint32_t quota; | |
136 | plugin->client_perform_task = | 115 | |
137 | GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | 116 | }; |
138 | timeout, grs, gws, | ||
139 | &client_run, plugin); | ||
140 | GNUNET_NETWORK_fdset_destroy (gws); | ||
141 | GNUNET_NETWORK_fdset_destroy (grs); | ||
142 | return GNUNET_OK; | ||
143 | } | ||
144 | |||
145 | 117 | ||
146 | int | 118 | /** |
147 | client_send (struct Session *s, struct HTTP_Message *msg) | 119 | * Encapsulation of all of the state of the plugin. |
120 | */ | ||
121 | struct Plugin | ||
148 | { | 122 | { |
149 | GNUNET_assert (s != NULL); | 123 | /** |
150 | GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); | 124 | * Our environment. |
151 | 125 | */ | |
152 | if (GNUNET_YES != exist_session(p, s)) | 126 | struct GNUNET_TRANSPORT_PluginEnvironment *env; |
153 | { | ||
154 | GNUNET_break (0); | ||
155 | return GNUNET_SYSERR; | ||
156 | } | ||
157 | if (s->client_put_paused == GNUNET_YES) | ||
158 | { | ||
159 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, | ||
160 | "Client: %p was suspended, unpausing\n", s->client_put); | ||
161 | s->client_put_paused = GNUNET_NO; | ||
162 | curl_easy_pause (s->client_put, CURLPAUSE_CONT); | ||
163 | } | ||
164 | client_schedule (s->plugin, GNUNET_YES); | ||
165 | 127 | ||
166 | return GNUNET_OK; | 128 | /** |
167 | } | 129 | * List of open sessions. |
130 | */ | ||
131 | struct Session *sessions; | ||
132 | |||
133 | }; | ||
168 | 134 | ||
169 | 135 | ||
170 | /** | 136 | /** |
171 | * Task performing curl operations | 137 | * Function that can be used by the transport service to transmit |
138 | * a message using the plugin. Note that in the case of a | ||
139 | * peer disconnecting, the continuation MUST be called | ||
140 | * prior to the disconnect notification itself. This function | ||
141 | * will be called with this peer's HELLO message to initiate | ||
142 | * a fresh connection to another peer. | ||
172 | * | 143 | * |
173 | * @param cls plugin as closure | 144 | * @param cls closure |
174 | * @param tc gnunet scheduler task context | 145 | * @param session which session must be used |
146 | * @param msgbuf the message to transmit | ||
147 | * @param msgbuf_size number of bytes in 'msgbuf' | ||
148 | * @param priority how important is the message (most plugins will | ||
149 | * ignore message priority and just FIFO) | ||
150 | * @param to how long to wait at most for the transmission (does not | ||
151 | * require plugins to discard the message after the timeout, | ||
152 | * just advisory for the desired delay; most plugins will ignore | ||
153 | * this as well) | ||
154 | * @param cont continuation to call once the message has | ||
155 | * been transmitted (or if the transport is ready | ||
156 | * for the next transmission call; or if the | ||
157 | * peer disconnected...); can be NULL | ||
158 | * @param cont_cls closure for cont | ||
159 | * @return number of bytes used (on the physical network, with overheads); | ||
160 | * -1 on hard errors (i.e. address invalid); 0 is a legal value | ||
161 | * and does NOT mean that the message was not transmitted (DV) | ||
175 | */ | 162 | */ |
176 | static void | 163 | static ssize_t |
177 | client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 164 | http_client_plugin_send (void *cls, |
165 | struct Session *session, | ||
166 | const char *msgbuf, size_t msgbuf_size, | ||
167 | unsigned int priority, | ||
168 | struct GNUNET_TIME_Relative to, | ||
169 | GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) | ||
178 | { | 170 | { |
179 | struct Plugin *plugin = cls; | 171 | struct Plugin *plugin = cls; |
180 | int running; | 172 | int bytes_sent = 0; |
181 | CURLMcode mret; | ||
182 | |||
183 | GNUNET_assert (cls != NULL); | ||
184 | |||
185 | plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; | ||
186 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
187 | return; | ||
188 | |||
189 | do | ||
190 | { | ||
191 | running = 0; | ||
192 | mret = curl_multi_perform (plugin->client_mh, &running); | ||
193 | |||
194 | CURLMsg *msg; | ||
195 | int msgs_left; | ||
196 | |||
197 | while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left))) | ||
198 | { | ||
199 | CURL *easy_h = msg->easy_handle; | ||
200 | struct Session *s = NULL; | ||
201 | char *d = (char *) s; | ||
202 | |||
203 | if (easy_h == NULL) | ||
204 | { | ||
205 | GNUNET_break (0); | ||
206 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
207 | "Client: connection to ended with reason %i: `%s', %i handles running\n", | ||
208 | msg->data.result, | ||
209 | curl_easy_strerror (msg->data.result), running); | ||
210 | continue; | ||
211 | } | ||
212 | |||
213 | GNUNET_assert (CURLE_OK == | ||
214 | curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d)); | ||
215 | s = (struct Session *) d; | ||
216 | |||
217 | if (GNUNET_YES != exist_session(plugin, s)) | ||
218 | { | ||
219 | GNUNET_break (0); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | GNUNET_assert (s != NULL); | ||
224 | if (msg->msg == CURLMSG_DONE) | ||
225 | { | ||
226 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
227 | "Client: %p connection to '%s' %s ended with reason %i: `%s'\n", | ||
228 | msg->easy_handle, GNUNET_i2s (&s->target), | ||
229 | http_plugin_address_to_string (NULL, s->addr, | ||
230 | s->addrlen), | ||
231 | msg->data.result, | ||
232 | curl_easy_strerror (msg->data.result)); | ||
233 | |||
234 | /* Disconnect other transmission direction and tell transport */ | ||
235 | client_disconnect (s); | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | while (mret == CURLM_CALL_MULTI_PERFORM); | ||
240 | client_schedule (plugin, GNUNET_NO); | ||
241 | } | ||
242 | 173 | ||
174 | GNUNET_assert (plugin != NULL); | ||
175 | GNUNET_assert (session != NULL); | ||
243 | 176 | ||
244 | int | 177 | /* struct Plugin *plugin = cls; */ |
245 | client_disconnect (struct Session *s) | 178 | return bytes_sent; |
246 | { | ||
247 | int res = GNUNET_OK; | ||
248 | CURLMcode mret; | ||
249 | struct Plugin *plugin = s->plugin; | ||
250 | struct HTTP_Message *msg; | ||
251 | struct HTTP_Message *t; | ||
252 | |||
253 | if (GNUNET_YES != exist_session(plugin, s)) | ||
254 | { | ||
255 | GNUNET_break (0); | ||
256 | return GNUNET_SYSERR; | ||
257 | } | ||
258 | |||
259 | if (s->client_put != NULL) | ||
260 | { | ||
261 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
262 | "Client: %p / %p Deleting outbound PUT session to peer `%s'\n", | ||
263 | s, s->client_put, GNUNET_i2s (&s->target)); | ||
264 | |||
265 | /* remove curl handle from multi handle */ | ||
266 | mret = curl_multi_remove_handle (plugin->client_mh, s->client_put); | ||
267 | if (mret != CURLM_OK) | ||
268 | { | ||
269 | /* clean up easy handle, handle is now invalid and free'd */ | ||
270 | res = GNUNET_SYSERR; | ||
271 | GNUNET_break (0); | ||
272 | } | ||
273 | curl_easy_cleanup (s->client_put); | ||
274 | s->client_put = NULL; | ||
275 | } | ||
276 | |||
277 | |||
278 | if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) | ||
279 | { | ||
280 | GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); | ||
281 | s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; | ||
282 | } | ||
283 | |||
284 | if (s->client_get != NULL) | ||
285 | { | ||
286 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
287 | "Client: %p / %p Deleting outbound GET session to peer `%s'\n", | ||
288 | s, | ||
289 | s->client_get, GNUNET_i2s (&s->target)); | ||
290 | |||
291 | /* remove curl handle from multi handle */ | ||
292 | mret = curl_multi_remove_handle (plugin->client_mh, s->client_get); | ||
293 | if (mret != CURLM_OK) | ||
294 | { | ||
295 | /* clean up easy handle, handle is now invalid and free'd */ | ||
296 | res = GNUNET_SYSERR; | ||
297 | GNUNET_break (0); | ||
298 | } | ||
299 | curl_easy_cleanup (s->client_get); | ||
300 | s->client_get = NULL; | ||
301 | } | ||
302 | |||
303 | msg = s->msg_head; | ||
304 | while (msg != NULL) | ||
305 | { | ||
306 | t = msg->next; | ||
307 | if (NULL != msg->transmit_cont) | ||
308 | msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); | ||
309 | GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); | ||
310 | GNUNET_free (msg); | ||
311 | msg = t; | ||
312 | } | ||
313 | |||
314 | plugin->cur_connections -= 2; | ||
315 | |||
316 | notify_session_end (plugin, &s->target, s); | ||
317 | |||
318 | GNUNET_assert (plugin->outbound_sessions > 0); | ||
319 | plugin->outbound_sessions --; | ||
320 | GNUNET_STATISTICS_set (plugin->env->stats, | ||
321 | "# HTTP outbound sessions", | ||
322 | plugin->outbound_sessions, | ||
323 | GNUNET_NO); | ||
324 | |||
325 | /* Re-schedule since handles have changed */ | ||
326 | if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) | ||
327 | { | ||
328 | GNUNET_SCHEDULER_cancel (plugin->client_perform_task); | ||
329 | plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; | ||
330 | } | ||
331 | client_schedule (plugin, GNUNET_YES); | ||
332 | |||
333 | return res; | ||
334 | } | 179 | } |
335 | 180 | ||
336 | static int | ||
337 | client_receive_mst_cb (void *cls, void *client, | ||
338 | const struct GNUNET_MessageHeader *message) | ||
339 | { | ||
340 | struct Session *s = cls; | ||
341 | struct GNUNET_TIME_Relative delay; | ||
342 | |||
343 | if (GNUNET_YES != exist_session(p, s)) | ||
344 | { | ||
345 | GNUNET_break (0); | ||
346 | return GNUNET_OK; | ||
347 | } | ||
348 | |||
349 | delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); | ||
350 | s->next_receive = | ||
351 | GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); | ||
352 | |||
353 | if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value) | ||
354 | { | ||
355 | struct Plugin *plugin = s->plugin; | ||
356 | |||
357 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
358 | "Client: peer `%s' address `%s' next read delayed for %llu ms\n", | ||
359 | GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), | ||
360 | delay); | ||
361 | } | ||
362 | return GNUNET_OK; | ||
363 | } | ||
364 | 181 | ||
365 | 182 | ||
183 | /** | ||
184 | * Function that can be used to force the plugin to disconnect | ||
185 | * from the given peer and cancel all previous transmissions | ||
186 | * (and their continuationc). | ||
187 | * | ||
188 | * @param cls closure | ||
189 | * @param target peer from which to disconnect | ||
190 | */ | ||
366 | static void | 191 | static void |
367 | client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 192 | http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) |
368 | { | 193 | { |
369 | struct Session *s = cls; | 194 | // struct Plugin *plugin = cls; |
370 | 195 | // FIXME | |
371 | if (GNUNET_YES != exist_session(p, s)) | ||
372 | { | ||
373 | GNUNET_break (0); | ||
374 | return; | ||
375 | } | ||
376 | s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; | ||
377 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
378 | return; | ||
379 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, | ||
380 | "Client: %p Waking up receive handle\n", s->client_get); | ||
381 | if (s->client_get != NULL) | ||
382 | curl_easy_pause (s->client_get, CURLPAUSE_CONT); | ||
383 | } | 196 | } |
384 | 197 | ||
385 | 198 | ||
386 | /** | 199 | /** |
387 | * Callback method used with libcurl | 200 | * Convert the transports address to a nice, human-readable |
388 | * Method is called when libcurl needs to write data during sending | 201 | * format. |
389 | * | 202 | * |
390 | * @param stream pointer where to write data | 203 | * @param cls closure |
391 | * @param size size of an individual element | 204 | * @param type name of the transport that generated the address |
392 | * @param nmemb count of elements that can be written to the buffer | 205 | * @param addr one of the addresses of the host, NULL for the last address |
393 | * @param cls destination pointer, passed to the libcurl handle | 206 | * the specific address format depends on the transport |
394 | * @return bytes read from stream | 207 | * @param addrlen length of the address |
208 | * @param numeric should (IP) addresses be displayed in numeric form? | ||
209 | * @param timeout after how long should we give up? | ||
210 | * @param asc function to call on each string | ||
211 | * @param asc_cls closure for asc | ||
395 | */ | 212 | */ |
396 | static size_t | 213 | static void |
397 | client_receive (void *stream, size_t size, size_t nmemb, void *cls) | 214 | http_client_plugin_address_pretty_printer (void *cls, const char *type, |
215 | const void *addr, size_t addrlen, | ||
216 | int numeric, | ||
217 | struct GNUNET_TIME_Relative timeout, | ||
218 | GNUNET_TRANSPORT_AddressStringCallback | ||
219 | asc, void *asc_cls) | ||
398 | { | 220 | { |
399 | struct Session *s = cls; | 221 | asc (asc_cls, NULL); |
400 | struct GNUNET_TIME_Absolute now; | ||
401 | size_t len = size * nmemb; | ||
402 | struct Plugin *plugin = s->plugin; | ||
403 | |||
404 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
405 | "Client: Received %u bytes from peer `%s'\n", len, | ||
406 | GNUNET_i2s (&s->target)); | ||
407 | now = GNUNET_TIME_absolute_get (); | ||
408 | if (now.abs_value < s->next_receive.abs_value) | ||
409 | { | ||
410 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
411 | struct GNUNET_TIME_Relative delta = | ||
412 | GNUNET_TIME_absolute_get_difference (now, s->next_receive); | ||
413 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
414 | "Client: %p No inbound bandwidth available! Next read was delayed for %llu ms\n", | ||
415 | s->client_get, delta.rel_value); | ||
416 | if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) | ||
417 | { | ||
418 | GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); | ||
419 | s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; | ||
420 | } | ||
421 | s->recv_wakeup_task = | ||
422 | GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s); | ||
423 | return CURLPAUSE_ALL; | ||
424 | } | ||
425 | if (NULL == s->msg_tk) | ||
426 | s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s); | ||
427 | GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO); | ||
428 | return len; | ||
429 | } | 222 | } |
430 | 223 | ||
431 | 224 | ||
225 | |||
432 | /** | 226 | /** |
433 | * Callback method used with libcurl | 227 | * Another peer has suggested an address for this |
434 | * Method is called when libcurl needs to read data during sending | 228 | * peer and transport plugin. Check that this could be a valid |
229 | * address. If so, consider adding it to the list | ||
230 | * of addresses. | ||
435 | * | 231 | * |
436 | * @param stream pointer where to write data | 232 | * @param cls closure |
437 | * @param size size of an individual element | 233 | * @param addr pointer to the address |
438 | * @param nmemb count of elements that can be written to the buffer | 234 | * @param addrlen length of addr |
439 | * @param cls source pointer, passed to the libcurl handle | 235 | * @return GNUNET_OK if this is a plausible address for this peer |
440 | * @return bytes written to stream, returning 0 will terminate connection! | 236 | * and transport |
441 | */ | 237 | */ |
442 | static size_t | 238 | static int |
443 | client_send_cb (void *stream, size_t size, size_t nmemb, void *cls) | 239 | http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) |
444 | { | 240 | { |
445 | struct Session *s = cls; | 241 | /* struct Plugin *plugin = cls; */ |
446 | struct Plugin *plugin = s->plugin; | 242 | |
447 | struct HTTP_Message *msg = s->msg_head; | 243 | /* check if the address is plausible; if so, |
448 | size_t len; | 244 | * add it to our list! */ |
449 | 245 | return GNUNET_OK; | |
450 | if (GNUNET_YES != exist_session(plugin, s)) | ||
451 | { | ||
452 | GNUNET_break (0); | ||
453 | return 0; | ||
454 | } | ||
455 | if (NULL == msg) | ||
456 | { | ||
457 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
458 | "Client: %p Nothing to send! Suspending PUT handle!\n", | ||
459 | s->client_put); | ||
460 | s->client_put_paused = GNUNET_YES; | ||
461 | return CURL_READFUNC_PAUSE; | ||
462 | } | ||
463 | /* data to send */ | ||
464 | GNUNET_assert (msg->pos < msg->size); | ||
465 | /* calculate how much fits in buffer */ | ||
466 | len = GNUNET_MIN (msg->size - msg->pos, | ||
467 | size * nmemb); | ||
468 | memcpy (stream, &msg->buf[msg->pos], len); | ||
469 | msg->pos += len; | ||
470 | if (msg->pos == msg->size) | ||
471 | { | ||
472 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
473 | "Client: %p Message with %u bytes sent, removing message from queue\n", | ||
474 | s->client_put, msg->size, msg->pos); | ||
475 | /* Calling transmit continuation */ | ||
476 | GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); | ||
477 | if (NULL != msg->transmit_cont) | ||
478 | msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); | ||
479 | GNUNET_free (msg); | ||
480 | } | ||
481 | return len; | ||
482 | } | 246 | } |
483 | 247 | ||
484 | 248 | ||
485 | int | 249 | /** |
486 | client_connect (struct Session *s) | 250 | * Function called for a quick conversion of the binary address to |
251 | * a numeric address. Note that the caller must not free the | ||
252 | * address and that the next call to this function is allowed | ||
253 | * to override the address again. | ||
254 | * | ||
255 | * @param cls closure | ||
256 | * @param addr binary address | ||
257 | * @param addrlen length of the address | ||
258 | * @return string representing the same address | ||
259 | */ | ||
260 | static const char * | ||
261 | http_client_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) | ||
487 | { | 262 | { |
488 | struct Plugin *plugin = s->plugin; | 263 | GNUNET_break (0); |
489 | int res = GNUNET_OK; | 264 | return NULL; |
490 | char *url; | 265 | } |
491 | CURLMcode mret; | ||
492 | |||
493 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, | ||
494 | "Initiating outbound session peer `%s'\n", | ||
495 | GNUNET_i2s (&s->target)); | ||
496 | s->inbound = GNUNET_NO; | ||
497 | plugin->last_tag++; | ||
498 | /* create url */ | ||
499 | GNUNET_asprintf (&url, "%s%s;%u", | ||
500 | http_plugin_address_to_string (plugin, s->addr, s->addrlen), | ||
501 | GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey), | ||
502 | plugin->last_tag); | ||
503 | #if 0 | ||
504 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url); | ||
505 | #endif | ||
506 | /* create get connection */ | ||
507 | s->client_get = curl_easy_init (); | ||
508 | #if VERBOSE_CURL | ||
509 | curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L); | ||
510 | curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log); | ||
511 | curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get); | ||
512 | #endif | ||
513 | #if BUILD_HTTPS | ||
514 | curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); | ||
515 | curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0); | ||
516 | curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0); | ||
517 | #endif | ||
518 | curl_easy_setopt (s->client_get, CURLOPT_URL, url); | ||
519 | //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb); | ||
520 | //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps); | ||
521 | curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb); | ||
522 | curl_easy_setopt (s->client_get, CURLOPT_READDATA, s); | ||
523 | curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive); | ||
524 | curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s); | ||
525 | curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS, | ||
526 | (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); | ||
527 | curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s); | ||
528 | curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS, | ||
529 | (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); | ||
530 | curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE, | ||
531 | 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); | ||
532 | #if CURL_TCP_NODELAY | ||
533 | curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1); | ||
534 | #endif | ||
535 | 266 | ||
536 | /* create put connection */ | ||
537 | s->client_put = curl_easy_init (); | ||
538 | #if VERBOSE_CURL | ||
539 | curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L); | ||
540 | curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log); | ||
541 | curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put); | ||
542 | #endif | ||
543 | #if BUILD_HTTPS | ||
544 | curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); | ||
545 | curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0); | ||
546 | curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0); | ||
547 | #endif | ||
548 | curl_easy_setopt (s->client_put, CURLOPT_URL, url); | ||
549 | curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L); | ||
550 | //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb); | ||
551 | //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps); | ||
552 | curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb); | ||
553 | curl_easy_setopt (s->client_put, CURLOPT_READDATA, s); | ||
554 | curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive); | ||
555 | curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s); | ||
556 | curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS, | ||
557 | (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); | ||
558 | curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s); | ||
559 | curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS, | ||
560 | (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); | ||
561 | curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE, | ||
562 | 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); | ||
563 | #if CURL_TCP_NODELAY | ||
564 | curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1); | ||
565 | #endif | ||
566 | 267 | ||
567 | GNUNET_free (url); | ||
568 | |||
569 | mret = curl_multi_add_handle (plugin->client_mh, s->client_get); | ||
570 | if (mret != CURLM_OK) | ||
571 | { | ||
572 | curl_easy_cleanup (s->client_get); | ||
573 | res = GNUNET_SYSERR; | ||
574 | GNUNET_break (0); | ||
575 | } | ||
576 | |||
577 | mret = curl_multi_add_handle (plugin->client_mh, s->client_put); | ||
578 | if (mret != CURLM_OK) | ||
579 | { | ||
580 | curl_multi_remove_handle (plugin->client_mh, s->client_get); | ||
581 | curl_easy_cleanup (s->client_get); | ||
582 | curl_easy_cleanup (s->client_put); | ||
583 | res = GNUNET_SYSERR; | ||
584 | GNUNET_break (0); | ||
585 | } | ||
586 | |||
587 | /* Perform connect */ | ||
588 | plugin->cur_connections += 2; | ||
589 | |||
590 | plugin->outbound_sessions ++; | ||
591 | GNUNET_STATISTICS_set (plugin->env->stats, | ||
592 | "# HTTP outbound sessions", | ||
593 | plugin->outbound_sessions, | ||
594 | GNUNET_NO); | ||
595 | |||
596 | /* Re-schedule since handles have changed */ | ||
597 | if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) | ||
598 | { | ||
599 | GNUNET_SCHEDULER_cancel (plugin->client_perform_task); | ||
600 | plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; | ||
601 | } | ||
602 | plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin); | ||
603 | |||
604 | return res; | ||
605 | } | ||
606 | 268 | ||
607 | 269 | ||
608 | int | 270 | /** |
609 | client_start (struct Plugin *plugin) | 271 | * Entry point for the plugin. |
272 | */ | ||
273 | void * | ||
274 | LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) | ||
610 | { | 275 | { |
611 | int res = GNUNET_OK; | 276 | struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; |
612 | p = plugin; | 277 | struct GNUNET_TRANSPORT_PluginFunctions *api; |
613 | 278 | struct Plugin *plugin; | |
614 | curl_global_init (CURL_GLOBAL_ALL); | 279 | |
615 | plugin->client_mh = curl_multi_init (); | 280 | plugin = GNUNET_malloc (sizeof (struct Plugin)); |
616 | 281 | plugin->env = env; | |
617 | if (NULL == plugin->client_mh) | 282 | api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); |
618 | { | 283 | api->cls = plugin; |
619 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, | 284 | api->send = &http_client_plugin_send; |
620 | _ | 285 | api->disconnect = &http_client_plugin_disconnect; |
621 | ("Could not initialize curl multi handle, failed to start %s plugin!\n"), | 286 | api->address_pretty_printer = &http_client_plugin_address_pretty_printer; |
622 | plugin->name); | 287 | api->check_address = &http_client_plugin_address_suggested; |
623 | res = GNUNET_SYSERR; | 288 | api->address_to_string = &http_client_plugin_address_to_string; |
624 | } | 289 | return api; |
625 | return res; | ||
626 | } | 290 | } |
627 | 291 | ||
628 | 292 | ||
629 | void | 293 | /** |
630 | client_stop (struct Plugin *plugin) | 294 | * Exit point from the plugin. |
295 | */ | ||
296 | void * | ||
297 | LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) | ||
631 | { | 298 | { |
632 | p = NULL; | 299 | struct GNUNET_TRANSPORT_PluginFunctions *api = cls; |
633 | if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) | 300 | struct Plugin *plugin = api->cls; |
634 | { | ||
635 | GNUNET_SCHEDULER_cancel (plugin->client_perform_task); | ||
636 | plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; | ||
637 | } | ||
638 | |||
639 | curl_multi_cleanup (plugin->client_mh); | ||
640 | curl_global_cleanup (); | ||
641 | } | ||
642 | |||
643 | 301 | ||
302 | GNUNET_free (plugin); | ||
303 | GNUNET_free (api); | ||
304 | return NULL; | ||
305 | } | ||
644 | 306 | ||
645 | /* end of plugin_transport_http_client.c */ | 307 | /* end of plugin_transport_http_client.c */ |