aboutsummaryrefslogtreecommitdiff
path: root/src/transport/plugin_transport_http_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/plugin_transport_http_client.c')
-rw-r--r--src/transport/plugin_transport_http_client.c2523
1 files changed, 0 insertions, 2523 deletions
diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c
deleted file mode 100644
index 82468992b..000000000
--- a/src/transport/plugin_transport_http_client.c
+++ /dev/null
@@ -1,2523 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2002-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file transport/plugin_transport_http_client.c
23 * @brief HTTP/S client transport plugin
24 * @author Matthias Wachs
25 * @author Christian Grothoff
26 */
27
28#if BUILD_HTTPS
29#define PLUGIN_NAME "https_client"
30#define HTTP_STAT_STR_CONNECTIONS "# HTTPS client connections"
31#define LIBGNUNET_PLUGIN_TRANSPORT_INIT \
32 libgnunet_plugin_transport_https_client_init
33#define LIBGNUNET_PLUGIN_TRANSPORT_DONE \
34 libgnunet_plugin_transport_https_client_done
35#else
36#define PLUGIN_NAME "http_client"
37#define HTTP_STAT_STR_CONNECTIONS "# HTTP client connections"
38#define LIBGNUNET_PLUGIN_TRANSPORT_INIT \
39 libgnunet_plugin_transport_http_client_init
40#define LIBGNUNET_PLUGIN_TRANSPORT_DONE \
41 libgnunet_plugin_transport_http_client_done
42#endif
43
44#define VERBOSE_CURL GNUNET_NO
45
46#define PUT_DISCONNECT_TIMEOUT GNUNET_TIME_relative_multiply ( \
47 GNUNET_TIME_UNIT_SECONDS, 1)
48
49#define ENABLE_PUT GNUNET_YES
50#define ENABLE_GET GNUNET_YES
51
52#include "platform.h"
53#include "gnunet_util_lib.h"
54#include "gnunet_protocols.h"
55#include "gnunet_transport_plugin.h"
56#include "plugin_transport_http_common.h"
57/* Just included for the right curl.h */
58#include "gnunet_curl_lib.h"
59
60
61#define LOG(kind, ...) GNUNET_log_from (kind, PLUGIN_NAME, __VA_ARGS__)
62
63/**
64 * Encapsulation of all of the state of the plugin.
65 */
66struct HTTP_Client_Plugin;
67
68/**
69 * State of a HTTP PUT request
70 */
71enum HTTP_PUT_REQUEST_STATE
72{
73 /**
74 * Just created, not yet connected
75 */
76 H_NOT_CONNECTED,
77
78 /**
79 * Connected
80 */
81 H_CONNECTED,
82
83 /**
84 * Paused, nothing to send
85 */
86 H_PAUSED,
87
88 /**
89 * Temporary disconnect in progress due to inactivity
90 */
91 H_TMP_DISCONNECTING,
92
93 /**
94 * Send request while temporary disconnect, reconnect
95 */
96 H_TMP_RECONNECT_REQUIRED,
97
98 /**
99 * Temporarily disconnected
100 */
101 H_TMP_DISCONNECTED,
102
103 /**
104 * Disconnected
105 */
106 H_DISCONNECTED
107};
108
109/**
110 * Message to send using http
111 */
112struct HTTP_Message
113{
114 /**
115 * next pointer for double linked list
116 */
117 struct HTTP_Message *next;
118
119 /**
120 * previous pointer for double linked list
121 */
122 struct HTTP_Message *prev;
123
124 /**
125 * buffer containing data to send
126 */
127 char *buf;
128
129 /**
130 * Continuation function to call once the transmission buffer
131 * has again space available. NULL if there is no
132 * continuation to call.
133 */
134 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
135
136 /**
137 * Closure for @e transmit_cont.
138 */
139 void *transmit_cont_cls;
140
141 /**
142 * amount of data already sent
143 */
144 size_t pos;
145
146 /**
147 * buffer length
148 */
149 size_t size;
150};
151
152
153/**
154 * Session handle for HTTP(S) connections.
155 */
156struct GNUNET_ATS_Session;
157
158
159/**
160 * A request handle
161 *
162 */
163struct RequestHandle
164{
165 /**
166 * Current state of this request
167 */
168 enum HTTP_PUT_REQUEST_STATE state;
169
170 /**
171 * The curl easy handle
172 */
173 CURL *easyhandle;
174
175 /**
176 * The related session
177 */
178 struct GNUNET_ATS_Session *s;
179};
180
181
182/**
183 * Session handle for connections.
184 */
185struct GNUNET_ATS_Session
186{
187 /**
188 * The URL to connect to
189 */
190 char *url;
191
192 /**
193 * Address
194 */
195 struct GNUNET_HELLO_Address *address;
196
197 /**
198 * Pointer to the global plugin struct.
199 */
200 struct HTTP_Client_Plugin *plugin;
201
202 /**
203 * Handle for the HTTP PUT request.
204 */
205 struct RequestHandle put;
206
207 /**
208 * Handle for the HTTP GET request.
209 */
210 struct RequestHandle get;
211
212 /**
213 * next pointer for double linked list
214 */
215 struct HTTP_Message *msg_head;
216
217 /**
218 * previous pointer for double linked list
219 */
220 struct HTTP_Message *msg_tail;
221
222 /**
223 * Message stream tokenizer for incoming data
224 */
225 struct GNUNET_MessageStreamTokenizer *msg_tk;
226
227 /**
228 * Session timeout task
229 */
230 struct GNUNET_SCHEDULER_Task *put_disconnect_task;
231
232 /**
233 * Session timeout task
234 */
235 struct GNUNET_SCHEDULER_Task *timeout_task;
236
237 /**
238 * Task to wake up client receive handle when receiving is allowed again
239 */
240 struct GNUNET_SCHEDULER_Task *recv_wakeup_task;
241
242 /**
243 * Absolute time when to receive data again.
244 * Used for receive throttling.
245 */
246 struct GNUNET_TIME_Absolute next_receive;
247
248 /**
249 * When does this session time out.
250 */
251 struct GNUNET_TIME_Absolute timeout;
252
253 /**
254 * Number of bytes waiting for transmission to this peer.
255 */
256 unsigned long long bytes_in_queue;
257
258 /**
259 * Outbound overhead due to HTTP connection
260 * Add to next message of this session when calling callback
261 */
262 size_t overhead;
263
264 /**
265 * Number of messages waiting for transmission to this peer.
266 */
267 unsigned int msgs_in_queue;
268
269 /**
270 * ATS network type.
271 */
272 enum GNUNET_NetworkType scope;
273};
274
275
276/**
277 * Encapsulation of all of the state of the plugin.
278 */
279struct HTTP_Client_Plugin
280{
281 /**
282 * Our environment.
283 */
284 struct GNUNET_TRANSPORT_PluginEnvironment *env;
285
286 /**
287 * Open sessions.
288 */
289 struct GNUNET_CONTAINER_MultiPeerMap *sessions;
290
291 /**
292 * Function to call about session status changes.
293 */
294 GNUNET_TRANSPORT_SessionInfoCallback sic;
295
296 /**
297 * Closure for @e sic.
298 */
299 void *sic_cls;
300
301 /**
302 * Plugin name
303 */
304 char *name;
305
306 /**
307 * Protocol
308 */
309 char *protocol;
310
311 /**
312 * Proxy configuration: hostname or ip of the proxy server
313 */
314 char *proxy_hostname;
315
316 /**
317 * Username for the proxy server
318 */
319 char *proxy_username;
320
321 /**
322 * Password for the proxy server
323 */
324 char *proxy_password;
325
326 /**
327 * cURL Multihandle
328 */
329 CURLM *curl_multi_handle;
330
331 /**
332 * curl perform task
333 */
334 struct GNUNET_SCHEDULER_Task *client_perform_task;
335
336 /**
337 * Type of proxy server:
338 *
339 * Valid values as supported by curl:
340 * CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4, CURLPROXY_SOCKS5,
341 * CURLPROXY_SOCKS4A, CURLPROXY_SOCKS5_HOSTNAME
342 */
343 curl_proxytype proxytype;
344
345 /**
346 * Use proxy tunneling:
347 * Tunnel all operations through a given HTTP instead of have the proxy
348 * evaluate the HTTP request
349 *
350 * Default: #GNUNET_NO, #GNUNET_YES experimental
351 */
352 int proxy_use_httpproxytunnel;
353
354 /**
355 * My options to be included in the address
356 */
357 uint32_t options;
358
359 /**
360 * Maximum number of sockets the plugin can use
361 * Each http connections are two requests
362 */
363 unsigned int max_requests;
364
365 /**
366 * Current number of sockets the plugin can use
367 * Each http connections are two requests
368 */
369 unsigned int cur_requests;
370
371 /**
372 * Last used unique HTTP connection tag
373 */
374 uint32_t last_tag;
375
376 /**
377 * use IPv6
378 */
379 uint16_t use_ipv6;
380
381 /**
382 * use IPv4
383 */
384 uint16_t use_ipv4;
385
386 /**
387 * Should we emulate an XHR client for testing?
388 */
389 int emulate_xhr;
390};
391
392
393/**
394 * Disconnect a session
395 *
396 * @param cls the `struct HTTP_Client_Plugin *`
397 * @param s session
398 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
399 */
400static int
401http_client_plugin_session_disconnect (void *cls, struct GNUNET_ATS_Session *s);
402
403
404/**
405 * If a session monitor is attached, notify it about the new
406 * session state.
407 *
408 * @param plugin our plugin
409 * @param session session that changed state
410 * @param state new state of the session
411 */
412static void
413notify_session_monitor (struct HTTP_Client_Plugin *plugin,
414 struct GNUNET_ATS_Session *session,
415 enum GNUNET_TRANSPORT_SessionState state)
416{
417 struct GNUNET_TRANSPORT_SessionInfo info;
418
419 if (NULL == plugin->sic)
420 return;
421 memset (&info, 0, sizeof(info));
422 info.state = state;
423 info.is_inbound = GNUNET_NO;
424 info.num_msg_pending = session->msgs_in_queue;
425 info.num_bytes_pending = session->bytes_in_queue;
426 info.receive_delay = session->next_receive;
427 info.session_timeout = session->timeout;
428 info.address = session->address;
429 plugin->sic (plugin->sic_cls,
430 session,
431 &info);
432}
433
434
435/**
436 * Delete session @a s.
437 *
438 * @param s the session to delete
439 */
440static void
441client_delete_session (struct GNUNET_ATS_Session *s)
442{
443 struct HTTP_Client_Plugin *plugin = s->plugin;
444 struct HTTP_Message *pos;
445 struct HTTP_Message *next;
446 CURLMcode mret;
447
448 if (NULL != s->timeout_task)
449 {
450 GNUNET_SCHEDULER_cancel (s->timeout_task);
451 s->timeout_task = NULL;
452 s->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
453 }
454 if (NULL != s->put_disconnect_task)
455 {
456 GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
457 s->put_disconnect_task = NULL;
458 }
459 if (NULL != s->recv_wakeup_task)
460 {
461 GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
462 s->recv_wakeup_task = NULL;
463 }
464 GNUNET_assert (GNUNET_OK ==
465 GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
466 &s->address->peer,
467 s));
468 if (NULL != s->put.easyhandle)
469 {
470 LOG (GNUNET_ERROR_TYPE_DEBUG,
471 "Session %p/request %p: disconnecting PUT request to peer `%s'\n",
472 s,
473 s->put.easyhandle,
474 GNUNET_i2s (&s->address->peer));
475
476 /* remove curl handle from multi handle */
477 mret = curl_multi_remove_handle (plugin->curl_multi_handle,
478 s->put.easyhandle);
479 GNUNET_break (CURLM_OK == mret);
480 curl_easy_cleanup (s->put.easyhandle);
481 GNUNET_assert (plugin->cur_requests > 0);
482 plugin->cur_requests--;
483 s->put.easyhandle = NULL;
484 }
485 if (NULL != s->get.easyhandle)
486 {
487 LOG (GNUNET_ERROR_TYPE_DEBUG,
488 "Session %p/request %p: disconnecting GET request to peer `%s'\n",
489 s, s->get.easyhandle,
490 GNUNET_i2s (&s->address->peer));
491 /* remove curl handle from multi handle */
492 mret = curl_multi_remove_handle (plugin->curl_multi_handle,
493 s->get.easyhandle);
494 GNUNET_break (CURLM_OK == mret);
495 curl_easy_cleanup (s->get.easyhandle);
496 GNUNET_assert (plugin->cur_requests > 0);
497 plugin->cur_requests--;
498 s->get.easyhandle = NULL;
499 }
500
501 GNUNET_STATISTICS_set (plugin->env->stats,
502 HTTP_STAT_STR_CONNECTIONS,
503 plugin->cur_requests,
504 GNUNET_NO);
505 next = s->msg_head;
506 while (NULL != (pos = next))
507 {
508 next = pos->next;
509 GNUNET_CONTAINER_DLL_remove (s->msg_head,
510 s->msg_tail,
511 pos);
512 GNUNET_assert (0 < s->msgs_in_queue);
513 s->msgs_in_queue--;
514 GNUNET_assert (pos->size <= s->bytes_in_queue);
515 s->bytes_in_queue -= pos->size;
516 if (NULL != pos->transmit_cont)
517 pos->transmit_cont (pos->transmit_cont_cls,
518 &s->address->peer,
519 GNUNET_SYSERR,
520 pos->size,
521 pos->pos + s->overhead);
522 s->overhead = 0;
523 GNUNET_free (pos);
524 }
525 GNUNET_assert (0 == s->msgs_in_queue);
526 GNUNET_assert (0 == s->bytes_in_queue);
527 notify_session_monitor (plugin,
528 s,
529 GNUNET_TRANSPORT_SS_DONE);
530 if (NULL != s->msg_tk)
531 {
532 GNUNET_MST_destroy (s->msg_tk);
533 s->msg_tk = NULL;
534 }
535 GNUNET_HELLO_address_free (s->address);
536 GNUNET_free (s->url);
537 GNUNET_free (s);
538}
539
540
541/**
542 * Increment session timeout due to activity for session @a s.
543 *
544 * @param s the session
545 */
546static void
547client_reschedule_session_timeout (struct GNUNET_ATS_Session *s)
548{
549 GNUNET_assert (NULL != s->timeout_task);
550 s->timeout = GNUNET_TIME_relative_to_absolute (
551 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
552}
553
554
555/**
556 * Task performing curl operations
557 *
558 * @param cls plugin as closure
559 */
560static void
561client_run (void *cls);
562
563
564/**
565 * Function setting up file descriptors and scheduling task to run
566 *
567 * @param plugin the plugin as closure
568 * @param now schedule task in 1ms, regardless of what curl may say
569 * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
570 */
571static int
572client_schedule (struct HTTP_Client_Plugin *plugin,
573 int now)
574{
575 fd_set rs;
576 fd_set ws;
577 fd_set es;
578 int max;
579 struct GNUNET_NETWORK_FDSet *grs;
580 struct GNUNET_NETWORK_FDSet *gws;
581 long to;
582 CURLMcode mret;
583 struct GNUNET_TIME_Relative timeout;
584
585 /* Cancel previous scheduled task */
586 if (plugin->client_perform_task != NULL)
587 {
588 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
589 plugin->client_perform_task = NULL;
590 }
591 max = -1;
592 FD_ZERO (&rs);
593 FD_ZERO (&ws);
594 FD_ZERO (&es);
595 mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
596 if (mret != CURLM_OK)
597 {
598 LOG (GNUNET_ERROR_TYPE_ERROR,
599 _ ("%s failed at %s:%d: `%s'\n"),
600 "curl_multi_fdset",
601 __FILE__,
602 __LINE__,
603 curl_multi_strerror (mret));
604 return GNUNET_SYSERR;
605 }
606 mret = curl_multi_timeout (plugin->curl_multi_handle, &to);
607 if (-1 == to)
608 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
609 else
610 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
611 if (now == GNUNET_YES)
612 timeout = GNUNET_TIME_UNIT_MILLISECONDS;
613
614 if (CURLM_OK != mret)
615 {
616 LOG (GNUNET_ERROR_TYPE_ERROR,
617 _ ("%s failed at %s:%d: `%s'\n"),
618 "curl_multi_timeout", __FILE__, __LINE__,
619 curl_multi_strerror (mret));
620 return GNUNET_SYSERR;
621 }
622
623 grs = GNUNET_NETWORK_fdset_create ();
624 gws = GNUNET_NETWORK_fdset_create ();
625 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
626 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
627
628 /* Schedule task to run when select is ready to read or write */
629 plugin->client_perform_task =
630 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
631 timeout, grs, gws,
632 &client_run, plugin);
633 GNUNET_NETWORK_fdset_destroy (gws);
634 GNUNET_NETWORK_fdset_destroy (grs);
635 return GNUNET_OK;
636}
637
638
639#if VERBOSE_CURL
640/**
641 * Logging function
642 *
643 * @param curl the curl easy handle
644 * @param type message type
645 * @param data data to log, NOT a 0-terminated string
646 * @param size data length
647 * @param cls the closure
648 * @return always 0
649 */
650static int
651client_log (CURL *curl,
652 curl_infotype type,
653 const char *data,
654 size_t size,
655 void *cls)
656{
657 struct RequestHandle *ch = cls;
658 const char *ttype = "UNSPECIFIED";
659 char text[size + 2];
660
661 if (! ((CURLINFO_TEXT == type) ||
662 (CURLINFO_HEADER_IN == type) ||
663 (CURLINFO_HEADER_OUT == type)))
664 return 0;
665 switch (type)
666 {
667 case CURLINFO_TEXT:
668 ttype = "TEXT";
669 break;
670
671 case CURLINFO_HEADER_IN:
672 ttype = "HEADER_IN";
673 break;
674
675 case CURLINFO_HEADER_OUT:
676 ttype = "HEADER_OUT";
677 /* Overhead*/
678 GNUNET_assert (NULL != ch);
679 GNUNET_assert (NULL != ch->easyhandle);
680 GNUNET_assert (NULL != ch->s);
681 ch->s->overhead += size;
682 break;
683
684 default:
685 ttype = "UNSPECIFIED";
686 break;
687 }
688 GNUNET_memcpy (text, data, size);
689 if (text[size - 1] == '\n')
690 {
691 text[size] = '\0';
692 }
693 else
694 {
695 text[size] = '\n';
696 text[size + 1] = '\0';
697 }
698 LOG (GNUNET_ERROR_TYPE_DEBUG,
699 "Request %p %s: %s",
700 ch->easyhandle,
701 ttype,
702 text);
703 return 0;
704}
705
706
707#endif
708
709/**
710 * Connect GET request
711 *
712 * @param s the session to connect
713 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
714 */
715static int
716client_connect_get (struct GNUNET_ATS_Session *s);
717
718
719/**
720 * Connect a HTTP put request
721 *
722 * @param s the session to connect
723 * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for success
724 */
725static int
726client_connect_put (struct GNUNET_ATS_Session *s);
727
728
729/**
730 * Function that can be used by the transport service to transmit
731 * a message using the plugin. Note that in the case of a
732 * peer disconnecting, the continuation MUST be called
733 * prior to the disconnect notification itself. This function
734 * will be called with this peer's HELLO message to initiate
735 * a fresh connection to another peer.
736 *
737 * @param cls closure
738 * @param s which session must be used
739 * @param msgbuf the message to transmit
740 * @param msgbuf_size number of bytes in @a msgbuf
741 * @param priority how important is the message (most plugins will
742 * ignore message priority and just FIFO)
743 * @param to how long to wait at most for the transmission (does not
744 * require plugins to discard the message after the timeout,
745 * just advisory for the desired delay; most plugins will ignore
746 * this as well)
747 * @param cont continuation to call once the message has
748 * been transmitted (or if the transport is ready
749 * for the next transmission call; or if the
750 * peer disconnected...); can be NULL
751 * @param cont_cls closure for @a cont
752 * @return number of bytes used (on the physical network, with overheads);
753 * -1 on hard errors (i.e. address invalid); 0 is a legal value
754 * and does NOT mean that the message was not transmitted (DV)
755 */
756static ssize_t
757http_client_plugin_send (void *cls,
758 struct GNUNET_ATS_Session *s,
759 const char *msgbuf,
760 size_t msgbuf_size,
761 unsigned int priority,
762 struct GNUNET_TIME_Relative to,
763 GNUNET_TRANSPORT_TransmitContinuation cont,
764 void *cont_cls)
765{
766 struct HTTP_Client_Plugin *plugin = cls;
767 struct HTTP_Message *msg;
768 char *stat_txt;
769
770 LOG (GNUNET_ERROR_TYPE_DEBUG,
771 "Session %p/request %p: Sending message with %lu to peer `%s' \n",
772 s,
773 s->put.easyhandle,
774 (unsigned long) msgbuf_size,
775 GNUNET_i2s (&s->address->peer));
776
777 /* create new message and schedule */
778 msg = GNUNET_malloc (sizeof(struct HTTP_Message) + msgbuf_size);
779 msg->size = msgbuf_size;
780 msg->buf = (char *) &msg[1];
781 msg->transmit_cont = cont;
782 msg->transmit_cont_cls = cont_cls;
783 GNUNET_memcpy (msg->buf,
784 msgbuf,
785 msgbuf_size);
786 GNUNET_CONTAINER_DLL_insert_tail (s->msg_head,
787 s->msg_tail,
788 msg);
789 s->msgs_in_queue++;
790 s->bytes_in_queue += msg->size;
791
792 GNUNET_asprintf (&stat_txt,
793 "# bytes currently in %s_client buffers",
794 plugin->protocol);
795 GNUNET_STATISTICS_update (plugin->env->stats,
796 stat_txt, msgbuf_size, GNUNET_NO);
797 GNUNET_free (stat_txt);
798 notify_session_monitor (plugin,
799 s,
800 GNUNET_TRANSPORT_SS_UPDATE);
801 if (H_TMP_DISCONNECTING == s->put.state)
802 {
803 /* PUT request is currently getting disconnected */
804 s->put.state = H_TMP_RECONNECT_REQUIRED;
805 LOG (GNUNET_ERROR_TYPE_DEBUG,
806 "Session %p/request %p: currently disconnecting, reconnecting immediately\n",
807 s,
808 s->put.easyhandle);
809 return msgbuf_size;
810 }
811 if (H_PAUSED == s->put.state)
812 {
813 /* PUT request was paused, unpause */
814 GNUNET_assert (s->put_disconnect_task != NULL);
815 GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
816 s->put_disconnect_task = NULL;
817 LOG (GNUNET_ERROR_TYPE_DEBUG,
818 "Session %p/request %p: unpausing request\n",
819 s, s->put.easyhandle);
820 s->put.state = H_CONNECTED;
821 if (NULL != s->put.easyhandle)
822 curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
823 }
824 else if (H_TMP_DISCONNECTED == s->put.state)
825 {
826 /* PUT request was disconnected, reconnect */
827 LOG (GNUNET_ERROR_TYPE_DEBUG, "Session %p: Reconnecting PUT request\n", s);
828 GNUNET_break (NULL == s->put.easyhandle);
829 if (GNUNET_SYSERR == client_connect_put (s))
830 {
831 /* Could not reconnect */
832 http_client_plugin_session_disconnect (plugin, s);
833 return GNUNET_SYSERR;
834 }
835 }
836 client_schedule (s->plugin, GNUNET_YES);
837 return msgbuf_size;
838}
839
840
841/**
842 * Disconnect a session
843 *
844 * @param cls the `struct HTTP_Client_Plugin *`
845 * @param s session
846 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
847 */
848static int
849http_client_plugin_session_disconnect (void *cls,
850 struct GNUNET_ATS_Session *s)
851{
852 struct HTTP_Client_Plugin *plugin = cls;
853
854 LOG (GNUNET_ERROR_TYPE_DEBUG,
855 "Session %p: notifying transport about ending session\n",
856 s);
857 plugin->env->session_end (plugin->env->cls,
858 s->address,
859 s);
860 client_delete_session (s);
861
862 /* Re-schedule since handles have changed */
863 if (NULL != plugin->client_perform_task)
864 {
865 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
866 plugin->client_perform_task = NULL;
867 }
868 client_schedule (plugin, GNUNET_YES);
869
870 return GNUNET_OK;
871}
872
873
874/**
875 * Function that is called to get the keepalive factor.
876 * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
877 * calculate the interval between keepalive packets.
878 *
879 * @param cls closure with the `struct Plugin`
880 * @return keepalive factor
881 */
882static unsigned int
883http_client_query_keepalive_factor (void *cls)
884{
885 return 3;
886}
887
888
889/**
890 * Callback to destroys all sessions on exit.
891 *
892 * @param cls the `struct HTTP_Client_Plugin *`
893 * @param peer identity of the peer
894 * @param value the `struct GNUNET_ATS_Session *`
895 * @return #GNUNET_OK (continue iterating)
896 */
897static int
898destroy_session_cb (void *cls,
899 const struct GNUNET_PeerIdentity *peer,
900 void *value)
901{
902 struct HTTP_Client_Plugin *plugin = cls;
903 struct GNUNET_ATS_Session *session = value;
904
905 http_client_plugin_session_disconnect (plugin, session);
906 return GNUNET_OK;
907}
908
909
910/**
911 * Function that can be used to force the plugin to disconnect
912 * from the given peer and cancel all previous transmissions
913 * (and their continuationc).
914 *
915 * @param cls closure
916 * @param target peer from which to disconnect
917 */
918static void
919http_client_plugin_peer_disconnect (void *cls,
920 const struct GNUNET_PeerIdentity *target)
921{
922 struct HTTP_Client_Plugin *plugin = cls;
923
924 LOG (GNUNET_ERROR_TYPE_DEBUG,
925 "Transport tells me to disconnect `%s'\n",
926 GNUNET_i2s (target));
927 GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
928 target,
929 &destroy_session_cb,
930 plugin);
931}
932
933
934/**
935 * Closure for #session_lookup_client_by_address().
936 */
937struct GNUNET_ATS_SessionClientCtx
938{
939 /**
940 * Address we are looking for.
941 */
942 const struct GNUNET_HELLO_Address *address;
943
944 /**
945 * Session that was found.
946 */
947 struct GNUNET_ATS_Session *ret;
948};
949
950
951/**
952 * Locate the seession object for a given address.
953 *
954 * @param cls the `struct GNUNET_ATS_SessionClientCtx *`
955 * @param key peer identity
956 * @param value the `struct GNUNET_ATS_Session` to check
957 * @return #GNUNET_NO if found, #GNUNET_OK if not
958 */
959static int
960session_lookup_client_by_address (void *cls,
961 const struct GNUNET_PeerIdentity *key,
962 void *value)
963{
964 struct GNUNET_ATS_SessionClientCtx *sc_ctx = cls;
965 struct GNUNET_ATS_Session *s = value;
966
967 if (0 == GNUNET_HELLO_address_cmp (sc_ctx->address,
968 s->address))
969 {
970 sc_ctx->ret = s;
971 return GNUNET_NO;
972 }
973 return GNUNET_YES;
974}
975
976
977/**
978 * Check if a sessions exists for an specific address
979 *
980 * @param plugin the plugin
981 * @param address the address
982 * @return the session or NULL
983 */
984static struct GNUNET_ATS_Session *
985client_lookup_session (struct HTTP_Client_Plugin *plugin,
986 const struct GNUNET_HELLO_Address *address)
987{
988 struct GNUNET_ATS_SessionClientCtx sc_ctx;
989
990 sc_ctx.address = address;
991 sc_ctx.ret = NULL;
992 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
993 &session_lookup_client_by_address,
994 &sc_ctx);
995 return sc_ctx.ret;
996}
997
998
999/**
1000 * When we have nothing to transmit, we pause the HTTP PUT
1001 * after a while (so that gnurl stops asking). This task
1002 * is the delayed task that actually disconnects the PUT.
1003 *
1004 * @param cls the `struct GNUNET_ATS_Session *` with the put
1005 */
1006static void
1007client_put_disconnect (void *cls)
1008{
1009 struct GNUNET_ATS_Session *s = cls;
1010
1011 s->put_disconnect_task = NULL;
1012 LOG (GNUNET_ERROR_TYPE_DEBUG,
1013 "Session %p/request %p: will be disconnected due to no activity\n",
1014 s, s->put.easyhandle);
1015 s->put.state = H_TMP_DISCONNECTING;
1016 if (NULL != s->put.easyhandle)
1017 curl_easy_pause (s->put.easyhandle,
1018 CURLPAUSE_CONT);
1019 client_schedule (s->plugin, GNUNET_YES);
1020}
1021
1022
1023/**
1024 * Callback method used with libcurl
1025 * Method is called when libcurl needs to read data during sending
1026 *
1027 * @param stream pointer where to write data
1028 * @param size size of an individual element
1029 * @param nmemb count of elements that can be written to the buffer
1030 * @param cls our `struct GNUNET_ATS_Session`
1031 * @return bytes written to stream, returning 0 will terminate request!
1032 */
1033static size_t
1034client_send_cb (void *stream,
1035 size_t size,
1036 size_t nmemb,
1037 void *cls)
1038{
1039 struct GNUNET_ATS_Session *s = cls;
1040 struct HTTP_Client_Plugin *plugin = s->plugin;
1041 struct HTTP_Message *msg = s->msg_head;
1042 size_t len;
1043 char *stat_txt;
1044
1045 if (H_TMP_DISCONNECTING == s->put.state)
1046 {
1047 LOG (GNUNET_ERROR_TYPE_DEBUG,
1048 "Session %p/request %p: disconnect due to inactivity\n",
1049 s, s->put.easyhandle);
1050 return 0;
1051 }
1052
1053 if (NULL == msg)
1054 {
1055 if (GNUNET_YES == plugin->emulate_xhr)
1056 {
1057 LOG (GNUNET_ERROR_TYPE_DEBUG,
1058 "Session %p/request %p: PUT request finished\n",
1059 s,
1060 s->put.easyhandle);
1061 s->put.state = H_TMP_DISCONNECTING;
1062 return 0;
1063 }
1064
1065 /* We have nothing to send, so pause PUT request */
1066 LOG (GNUNET_ERROR_TYPE_DEBUG,
1067 "Session %p/request %p: nothing to send, suspending\n",
1068 s,
1069 s->put.easyhandle);
1070 s->put_disconnect_task
1071 = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT,
1072 &client_put_disconnect,
1073 s);
1074 s->put.state = H_PAUSED;
1075 return CURL_READFUNC_PAUSE;
1076 }
1077 /* data to send */
1078 GNUNET_assert (msg->pos < msg->size);
1079 /* calculate how much fits in buffer */
1080 len = GNUNET_MIN (msg->size - msg->pos,
1081 size * nmemb);
1082 GNUNET_memcpy (stream,
1083 &msg->buf[msg->pos],
1084 len);
1085 msg->pos += len;
1086 if (msg->pos == msg->size)
1087 {
1088 LOG (GNUNET_ERROR_TYPE_DEBUG,
1089 "Session %p/request %p: sent message with %lu bytes sent, removing message from queue\n",
1090 s,
1091 s->put.easyhandle,
1092 (unsigned long) msg->size);
1093 /* Calling transmit continuation */
1094 GNUNET_CONTAINER_DLL_remove (s->msg_head,
1095 s->msg_tail,
1096 msg);
1097 GNUNET_assert (0 < s->msgs_in_queue);
1098 s->msgs_in_queue--;
1099 GNUNET_assert (msg->size <= s->bytes_in_queue);
1100 s->bytes_in_queue -= msg->size;
1101 if (NULL != msg->transmit_cont)
1102 msg->transmit_cont (msg->transmit_cont_cls,
1103 &s->address->peer,
1104 GNUNET_OK,
1105 msg->size,
1106 msg->size + s->overhead);
1107 s->overhead = 0;
1108 GNUNET_free (msg);
1109 }
1110 notify_session_monitor (plugin,
1111 s,
1112 GNUNET_TRANSPORT_SS_UPDATE);
1113 GNUNET_asprintf (&stat_txt,
1114 "# bytes currently in %s_client buffers",
1115 plugin->protocol);
1116 GNUNET_STATISTICS_update (plugin->env->stats,
1117 stat_txt,
1118 -len,
1119 GNUNET_NO);
1120 GNUNET_free (stat_txt);
1121 GNUNET_asprintf (&stat_txt,
1122 "# bytes transmitted via %s_client",
1123 plugin->protocol);
1124 GNUNET_STATISTICS_update (plugin->env->stats,
1125 stat_txt,
1126 len,
1127 GNUNET_NO);
1128 GNUNET_free (stat_txt);
1129 return len;
1130}
1131
1132
1133/**
1134 * Wake up a curl handle which was suspended
1135 *
1136 * @param cls the session
1137 */
1138static void
1139client_wake_up (void *cls)
1140{
1141 struct GNUNET_ATS_Session *s = cls;
1142
1143 s->recv_wakeup_task = NULL;
1144 LOG (GNUNET_ERROR_TYPE_DEBUG,
1145 "Session %p/request %p: Waking up GET handle\n",
1146 s, s->get.easyhandle);
1147 if (H_PAUSED == s->put.state)
1148 {
1149 /* PUT request was paused, unpause */
1150 GNUNET_assert (s->put_disconnect_task != NULL);
1151 GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
1152 s->put_disconnect_task = NULL;
1153 s->put.state = H_CONNECTED;
1154 if (NULL != s->put.easyhandle)
1155 curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
1156 }
1157 if (NULL != s->get.easyhandle)
1158 curl_easy_pause (s->get.easyhandle, CURLPAUSE_CONT);
1159}
1160
1161
1162/**
1163 * Callback for message stream tokenizer
1164 *
1165 * @param cls the session
1166 * @param message the message received
1167 * @return always #GNUNET_OK
1168 */
1169static int
1170client_receive_mst_cb (void *cls,
1171 const struct GNUNET_MessageHeader *message)
1172{
1173 struct GNUNET_ATS_Session *s = cls;
1174 struct HTTP_Client_Plugin *plugin;
1175 struct GNUNET_TIME_Relative delay;
1176 char *stat_txt;
1177
1178 plugin = s->plugin;
1179 delay = s->plugin->env->receive (plugin->env->cls,
1180 s->address,
1181 s,
1182 message);
1183 GNUNET_asprintf (&stat_txt,
1184 "# bytes received via %s_client",
1185 plugin->protocol);
1186 GNUNET_STATISTICS_update (plugin->env->stats,
1187 stat_txt,
1188 ntohs (message->size),
1189 GNUNET_NO);
1190 GNUNET_free (stat_txt);
1191
1192 s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
1193 if (GNUNET_TIME_absolute_get ().abs_value_us < s->next_receive.abs_value_us)
1194 {
1195 LOG (GNUNET_ERROR_TYPE_DEBUG,
1196 "Client: peer `%s' address `%s' next read delayed for %s\n",
1197 GNUNET_i2s (&s->address->peer),
1198 http_common_plugin_address_to_string (s->plugin->protocol,
1199 s->address->address,
1200 s->address->address_length),
1201 GNUNET_STRINGS_relative_time_to_string (delay,
1202 GNUNET_YES));
1203 }
1204 client_reschedule_session_timeout (s);
1205 return GNUNET_OK;
1206}
1207
1208
1209/**
1210 * Callback method used with libcurl when data for a PUT request are
1211 * received. We do not expect data here, so we just discard it.
1212 *
1213 * @param stream pointer where to write data
1214 * @param size size of an individual element
1215 * @param nmemb count of elements that can be written to the buffer
1216 * @param cls destination pointer, passed to the libcurl handle
1217 * @return bytes read from stream
1218 */
1219static size_t
1220client_receive_put (void *stream,
1221 size_t size,
1222 size_t nmemb,
1223 void *cls)
1224{
1225 return size * nmemb;
1226}
1227
1228
1229/**
1230 * Callback method used with libcurl when data for a GET request are
1231 * received. Forward to MST
1232 *
1233 * @param stream pointer where to write data
1234 * @param size size of an individual element
1235 * @param nmemb count of elements that can be written to the buffer
1236 * @param cls destination pointer, passed to the libcurl handle
1237 * @return bytes read from stream
1238 */
1239static size_t
1240client_receive (void *stream,
1241 size_t size,
1242 size_t nmemb,
1243 void *cls)
1244{
1245 struct GNUNET_ATS_Session *s = cls;
1246 struct GNUNET_TIME_Absolute now;
1247 size_t len = size * nmemb;
1248
1249 LOG (GNUNET_ERROR_TYPE_DEBUG,
1250 "Session %p / request %p: Received %lu bytes from peer `%s'\n",
1251 s,
1252 s->get.easyhandle,
1253 (unsigned long) len,
1254 GNUNET_i2s (&s->address->peer));
1255 now = GNUNET_TIME_absolute_get ();
1256 if (now.abs_value_us < s->next_receive.abs_value_us)
1257 {
1258 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1259 struct GNUNET_TIME_Relative delta
1260 = GNUNET_TIME_absolute_get_difference (now, s->next_receive);
1261
1262 LOG (GNUNET_ERROR_TYPE_DEBUG,
1263 "Session %p / request %p: No inbound bandwidth available! Next read was delayed for %s\n",
1264 s,
1265 s->get.easyhandle,
1266 GNUNET_STRINGS_relative_time_to_string (delta,
1267 GNUNET_YES));
1268 if (s->recv_wakeup_task != NULL)
1269 {
1270 GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
1271 s->recv_wakeup_task = NULL;
1272 }
1273 s->recv_wakeup_task
1274 = GNUNET_SCHEDULER_add_delayed (delta,
1275 &client_wake_up,
1276 s);
1277 return CURL_WRITEFUNC_PAUSE;
1278 }
1279 if (NULL == s->msg_tk)
1280 s->msg_tk = GNUNET_MST_create (&client_receive_mst_cb,
1281 s);
1282 GNUNET_MST_from_buffer (s->msg_tk,
1283 stream,
1284 len,
1285 GNUNET_NO,
1286 GNUNET_NO);
1287 return len;
1288}
1289
1290
1291static void
1292client_run (void *cls)
1293{
1294 struct HTTP_Client_Plugin *plugin = cls;
1295 int running;
1296 long http_statuscode;
1297 CURLMcode mret;
1298 CURLMsg *msg;
1299 int put_request; /* GNUNET_YES if easy handle is put, GNUNET_NO for get */
1300 int msgs_left;
1301
1302 plugin->client_perform_task = NULL;
1303 /* While data are available or timeouts occurred */
1304 do
1305 {
1306 running = 0;
1307 /* Perform operations for all handles */
1308 mret = curl_multi_perform (plugin->curl_multi_handle, &running);
1309
1310 /* Get additional information for all handles */
1311 while (NULL != (msg = curl_multi_info_read (plugin->curl_multi_handle,
1312 &msgs_left)))
1313 {
1314 CURL *easy_h = msg->easy_handle;
1315 struct GNUNET_ATS_Session *s = NULL;
1316 char *d = NULL; /* curl requires 'd' to be a 'char *' */
1317
1318 GNUNET_assert (NULL != easy_h);
1319
1320 /* Obtain session from easy handle */
1321 GNUNET_assert (CURLE_OK == curl_easy_getinfo (easy_h, CURLINFO_PRIVATE,
1322 &d));
1323 s = (struct GNUNET_ATS_Session *) d;
1324 GNUNET_assert (NULL != s);
1325
1326 if (msg->msg != CURLMSG_DONE)
1327 continue; /* This should not happen */
1328
1329 /* Get HTTP response code */
1330 GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
1331 CURLINFO_RESPONSE_CODE,
1332 &http_statuscode));
1333
1334 if (easy_h == s->put.easyhandle)
1335 put_request = GNUNET_YES;
1336 else
1337 put_request = GNUNET_NO;
1338
1339 /* Log status of terminated request */
1340 if ((0 != msg->data.result) || (http_statuscode != 200))
1341 LOG (GNUNET_ERROR_TYPE_DEBUG,
1342 "Session %p/request %p: %s request to `%s' ended with status %li reason %i: `%s'\n",
1343 s, msg->easy_handle,
1344 (GNUNET_YES == put_request) ? "PUT" : "GET",
1345 GNUNET_i2s (&s->address->peer),
1346 http_statuscode,
1347 msg->data.result,
1348 curl_easy_strerror (msg->data.result));
1349 else
1350 LOG (GNUNET_ERROR_TYPE_DEBUG,
1351 "Session %p/request %p: %s request to `%s' ended normal\n",
1352 s, msg->easy_handle,
1353 (GNUNET_YES == put_request) ? "PUT" : "GET",
1354 GNUNET_i2s (&s->address->peer));
1355
1356 /* Remove easy handle from multi handle */
1357 curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
1358
1359 /* Clean up easy handle */
1360 curl_easy_cleanup (easy_h);
1361
1362 /* Remove information */
1363 GNUNET_assert (plugin->cur_requests > 0);
1364 plugin->cur_requests--;
1365 LOG (GNUNET_ERROR_TYPE_INFO,
1366 "%s request to %s done, number of requests decreased to %u\n",
1367 (GNUNET_YES == put_request) ? "PUT" : "GET",
1368 s->url,
1369 plugin->cur_requests);
1370
1371 if (GNUNET_YES == put_request)
1372 {
1373 /* Clean up a PUT request */
1374 s->put.easyhandle = NULL;
1375 s->put.s = NULL;
1376
1377 switch (s->put.state)
1378 {
1379 case H_NOT_CONNECTED:
1380 case H_DISCONNECTED:
1381 case H_TMP_DISCONNECTED:
1382 /* This must not happen */
1383 GNUNET_break (0);
1384 break;
1385
1386 case H_TMP_RECONNECT_REQUIRED:
1387 /* Transport called send while disconnect in progress, reconnect */
1388 if (GNUNET_SYSERR == client_connect_put (s))
1389 {
1390 /* Reconnect failed, disconnect session */
1391 http_client_plugin_session_disconnect (plugin, s);
1392 }
1393 break;
1394
1395 case H_TMP_DISCONNECTING:
1396 /* PUT gets temporarily disconnected */
1397 s->put.state = H_TMP_DISCONNECTED;
1398 break;
1399
1400 case H_PAUSED:
1401 case H_CONNECTED:
1402 /* PUT gets permanently disconnected */
1403 s->put.state = H_DISCONNECTED;
1404 http_client_plugin_session_disconnect (plugin, s);
1405 break;
1406
1407 default:
1408 GNUNET_break (0);
1409 break;
1410 }
1411 }
1412 else if (GNUNET_NO == put_request)
1413 {
1414 /* Clean up a GET request */
1415 s->get.easyhandle = NULL;
1416 s->get.s = NULL;
1417
1418 /* If we are emulating an XHR client we need to make another GET
1419 * request.
1420 */
1421 if (GNUNET_YES == plugin->emulate_xhr)
1422 {
1423 if (GNUNET_SYSERR == client_connect_get (s))
1424 http_client_plugin_session_disconnect (plugin, s);
1425 }
1426 else
1427 {
1428 /* GET request was terminated, so disconnect session */
1429 http_client_plugin_session_disconnect (plugin, s);
1430 }
1431 }
1432 else
1433 GNUNET_break (0); /* Must not happen */
1434
1435 GNUNET_STATISTICS_set (plugin->env->stats,
1436 HTTP_STAT_STR_CONNECTIONS,
1437 plugin->cur_requests,
1438 GNUNET_NO);
1439 }
1440 }
1441 while (mret == CURLM_CALL_MULTI_PERFORM);
1442 client_schedule (plugin, GNUNET_NO);
1443}
1444
1445
1446#ifdef TCP_STEALTH
1447/**
1448 * Open TCP socket with TCP STEALTH enabled.
1449 *
1450 * @param clientp our `struct GNUNET_ATS_Session *`
1451 * @param purpose why does curl want to open a socket
1452 * @param address what kind of socket does curl want to have opened?
1453 * @return opened socket
1454 */
1455static curl_socket_t
1456open_tcp_stealth_socket_cb (void *clientp,
1457 curlsocktype purpose,
1458 struct curl_sockaddr *address)
1459{
1460 struct GNUNET_ATS_Session *s = clientp;
1461 int ret;
1462
1463 switch (purpose)
1464 {
1465 case CURLSOCKTYPE_IPCXN:
1466 ret = socket (address->family,
1467 address->socktype,
1468 address->protocol);
1469 if (-1 == ret)
1470 return CURL_SOCKET_BAD;
1471 if (((SOCK_STREAM != address->socktype) ||
1472 ((0 != address->protocol) &&
1473 (IPPROTO_TCP != address->protocol))))
1474 return (curl_socket_t) ret;
1475 if ((0 != setsockopt (ret,
1476 IPPROTO_TCP,
1477 TCP_STEALTH,
1478 &s->address->peer,
1479 sizeof(struct GNUNET_PeerIdentity))))
1480 {
1481 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1482 _ ("TCP_STEALTH not supported on this platform.\n"));
1483 (void) close (ret);
1484 return CURL_SOCKET_BAD;
1485 }
1486 return (curl_socket_t) ret;
1487
1488 case CURLSOCKTYPE_ACCEPT:
1489 GNUNET_break (0);
1490 return CURL_SOCKET_BAD;
1491 break;
1492
1493 case CURLSOCKTYPE_LAST:
1494 GNUNET_break (0);
1495 return CURL_SOCKET_BAD;
1496
1497 default:
1498 GNUNET_break (0);
1499 return CURL_SOCKET_BAD;
1500 }
1501}
1502
1503
1504#endif
1505
1506
1507/**
1508 * Connect GET request for a session
1509 *
1510 * @param s the session to connect
1511 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1512 */
1513static int
1514client_connect_get (struct GNUNET_ATS_Session *s)
1515{
1516 CURLMcode mret;
1517 struct HttpAddress *ha;
1518 uint32_t options;
1519
1520 ha = (struct HttpAddress *) s->address->address;
1521 options = ntohl (ha->options);
1522 /* create get request */
1523 s->get.easyhandle = curl_easy_init ();
1524 s->get.s = s;
1525 if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1526 {
1527#ifdef TCP_STEALTH
1528 curl_easy_setopt (s->get.easyhandle,
1529 CURLOPT_OPENSOCKETFUNCTION,
1530 &open_tcp_stealth_socket_cb);
1531 curl_easy_setopt (s->get.easyhandle,
1532 CURLOPT_OPENSOCKETDATA,
1533 s);
1534#else
1535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1537 curl_easy_cleanup (s->get.easyhandle);
1538 s->get.easyhandle = NULL;
1539 s->get.s = NULL;
1540 return GNUNET_SYSERR;
1541#endif
1542 }
1543
1544#if VERBOSE_CURL
1545 curl_easy_setopt (s->get.easyhandle,
1546 CURLOPT_VERBOSE,
1547 1L);
1548 curl_easy_setopt (s->get.easyhandle,
1549 CURLOPT_DEBUGFUNCTION,
1550 &client_log);
1551 curl_easy_setopt (s->get.easyhandle,
1552 CURLOPT_DEBUGDATA,
1553 &s->get);
1554#endif
1555#if BUILD_HTTPS
1556 curl_easy_setopt (s->get.easyhandle, CURLOPT_SSLVERSION,
1557 CURL_SSLVERSION_TLSv1);
1558 {
1559 if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1560 (options & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1561 {
1562 curl_easy_setopt (s->get.easyhandle,
1563 CURLOPT_SSL_VERIFYPEER, 1L);
1564 curl_easy_setopt (s->get.easyhandle,
1565 CURLOPT_SSL_VERIFYHOST,
1566 2L);
1567 }
1568 else
1569 {
1570 curl_easy_setopt (s->get.easyhandle,
1571 CURLOPT_SSL_VERIFYPEER,
1572 0L);
1573 curl_easy_setopt (s->get.easyhandle,
1574 CURLOPT_SSL_VERIFYHOST,
1575 0L);
1576 }
1577 }
1578 curl_easy_setopt (s->get.easyhandle,
1579 CURLOPT_PROTOCOLS,
1580 CURLPROTO_HTTPS);
1581 curl_easy_setopt (s->get.easyhandle,
1582 CURLOPT_REDIR_PROTOCOLS,
1583 CURLPROTO_HTTPS);
1584#else
1585 curl_easy_setopt (s->get.easyhandle,
1586 CURLOPT_PROTOCOLS,
1587 CURLPROTO_HTTP);
1588 curl_easy_setopt (s->get.easyhandle,
1589 CURLOPT_REDIR_PROTOCOLS,
1590 CURLPROTO_HTTP);
1591#endif
1592
1593 if (NULL != s->plugin->proxy_hostname)
1594 {
1595 curl_easy_setopt (s->get.easyhandle,
1596 CURLOPT_PROXY,
1597 s->plugin->proxy_hostname);
1598 curl_easy_setopt (s->get.easyhandle,
1599 CURLOPT_PROXYTYPE,
1600 s->plugin->proxytype);
1601 if (NULL != s->plugin->proxy_username)
1602 curl_easy_setopt (s->get.easyhandle,
1603 CURLOPT_PROXYUSERNAME,
1604 s->plugin->proxy_username);
1605 if (NULL != s->plugin->proxy_password)
1606 curl_easy_setopt (s->get.easyhandle,
1607 CURLOPT_PROXYPASSWORD,
1608 s->plugin->proxy_password);
1609 if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1610 curl_easy_setopt (s->get.easyhandle,
1611 CURLOPT_HTTPPROXYTUNNEL,
1612 s->plugin->proxy_use_httpproxytunnel);
1613 }
1614
1615 if (GNUNET_YES == s->plugin->emulate_xhr)
1616 {
1617 char *url;
1618
1619 GNUNET_asprintf (&url,
1620 "%s,1",
1621 s->url);
1622 curl_easy_setopt (s->get.easyhandle,
1623 CURLOPT_URL,
1624 url);
1625 GNUNET_free (url);
1626 }
1627 else
1628 {
1629 curl_easy_setopt (s->get.easyhandle,
1630 CURLOPT_URL,
1631 s->url);
1632 }
1633 curl_easy_setopt (s->get.easyhandle,
1634 CURLOPT_READFUNCTION,
1635 &client_send_cb);
1636 curl_easy_setopt (s->get.easyhandle,
1637 CURLOPT_READDATA,
1638 s);
1639 curl_easy_setopt (s->get.easyhandle,
1640 CURLOPT_WRITEFUNCTION,
1641 &client_receive);
1642 curl_easy_setopt (s->get.easyhandle,
1643 CURLOPT_WRITEDATA,
1644 s);
1645 /* No timeout by default, timeout done with session timeout */
1646 curl_easy_setopt (s->get.easyhandle,
1647 CURLOPT_TIMEOUT,
1648 0L);
1649 curl_easy_setopt (s->get.easyhandle,
1650 CURLOPT_PRIVATE, s);
1651 curl_easy_setopt (s->get.easyhandle,
1652 CURLOPT_CONNECTTIMEOUT_MS,
1653 (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us
1654 / 1000LL));
1655 curl_easy_setopt (s->get.easyhandle, CURLOPT_BUFFERSIZE,
1656 2 * GNUNET_MAX_MESSAGE_SIZE);
1657#if CURL_TCP_NODELAY
1658 curl_easy_setopt (ps->recv_endpoint,
1659 CURLOPT_TCP_NODELAY,
1660 1L);
1661#endif
1662 curl_easy_setopt (s->get.easyhandle,
1663 CURLOPT_FOLLOWLOCATION,
1664 0L);
1665
1666 mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1667 s->get.easyhandle);
1668 if (CURLM_OK != mret)
1669 {
1670 LOG (GNUNET_ERROR_TYPE_ERROR,
1671 "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1672 s,
1673 curl_multi_strerror (mret));
1674 curl_easy_cleanup (s->get.easyhandle);
1675 s->get.easyhandle = NULL;
1676 s->get.s = NULL;
1677 GNUNET_break (0);
1678 return GNUNET_SYSERR;
1679 }
1680 s->plugin->cur_requests++;
1681 LOG (GNUNET_ERROR_TYPE_INFO,
1682 "GET request `%s' established, number of requests increased to %u\n",
1683 s->url,
1684 s->plugin->cur_requests);
1685 return GNUNET_OK;
1686}
1687
1688
1689static int
1690client_connect_put (struct GNUNET_ATS_Session *s)
1691{
1692 CURLMcode mret;
1693 struct HttpAddress *ha;
1694 uint32_t options;
1695
1696 ha = (struct HttpAddress *) s->address->address;
1697 options = ntohl (ha->options);
1698 /* create put request */
1699 LOG (GNUNET_ERROR_TYPE_DEBUG,
1700 "Session %p: Init PUT handle\n",
1701 s);
1702 s->put.easyhandle = curl_easy_init ();
1703 s->put.s = s;
1704#if VERBOSE_CURL
1705 curl_easy_setopt (s->put.easyhandle,
1706 CURLOPT_VERBOSE,
1707 1L);
1708 curl_easy_setopt (s->put.easyhandle,
1709 CURLOPT_DEBUGFUNCTION,
1710 &client_log);
1711 curl_easy_setopt (s->put.easyhandle,
1712 CURLOPT_DEBUGDATA,
1713 &s->put);
1714#endif
1715 if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1716 {
1717#ifdef TCP_STEALTH
1718 curl_easy_setopt (s->put.easyhandle,
1719 CURLOPT_OPENSOCKETFUNCTION,
1720 &open_tcp_stealth_socket_cb);
1721 curl_easy_setopt (s->put.easyhandle,
1722 CURLOPT_OPENSOCKETDATA,
1723 s);
1724#else
1725 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1726 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1727 curl_easy_cleanup (s->put.easyhandle);
1728 s->put.easyhandle = NULL;
1729 s->put.s = NULL;
1730 s->put.state = H_DISCONNECTED;
1731 return GNUNET_SYSERR;
1732#endif
1733 }
1734#if BUILD_HTTPS
1735 curl_easy_setopt (s->put.easyhandle,
1736 CURLOPT_SSLVERSION,
1737 CURL_SSLVERSION_TLSv1);
1738 {
1739 struct HttpAddress *ha;
1740 ha = (struct HttpAddress *) s->address->address;
1741
1742 if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1743 (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1744 {
1745 curl_easy_setopt (s->put.easyhandle,
1746 CURLOPT_SSL_VERIFYPEER,
1747 1L);
1748 curl_easy_setopt (s->put.easyhandle,
1749 CURLOPT_SSL_VERIFYHOST,
1750 2L);
1751 }
1752 else
1753 {
1754 curl_easy_setopt (s->put.easyhandle,
1755 CURLOPT_SSL_VERIFYPEER,
1756 0L);
1757 curl_easy_setopt (s->put.easyhandle,
1758 CURLOPT_SSL_VERIFYHOST,
1759 0L);
1760 }
1761 }
1762 curl_easy_setopt (s->put.easyhandle,
1763 CURLOPT_PROTOCOLS,
1764 CURLPROTO_HTTPS);
1765 curl_easy_setopt (s->put.easyhandle,
1766 CURLOPT_REDIR_PROTOCOLS,
1767 CURLPROTO_HTTPS);
1768#else
1769 curl_easy_setopt (s->put.easyhandle,
1770 CURLOPT_PROTOCOLS,
1771 CURLPROTO_HTTP);
1772 curl_easy_setopt (s->put.easyhandle,
1773 CURLOPT_REDIR_PROTOCOLS,
1774 CURLPROTO_HTTP);
1775#endif
1776 if (NULL != s->plugin->proxy_hostname)
1777 {
1778 curl_easy_setopt (s->put.easyhandle,
1779 CURLOPT_PROXY,
1780 s->plugin->proxy_hostname);
1781 curl_easy_setopt (s->put.easyhandle,
1782 CURLOPT_PROXYTYPE,
1783 s->plugin->proxytype);
1784 if (NULL != s->plugin->proxy_username)
1785 curl_easy_setopt (s->put.easyhandle,
1786 CURLOPT_PROXYUSERNAME,
1787 s->plugin->proxy_username);
1788 if (NULL != s->plugin->proxy_password)
1789 curl_easy_setopt (s->put.easyhandle,
1790 CURLOPT_PROXYPASSWORD,
1791 s->plugin->proxy_password);
1792 if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1793 curl_easy_setopt (s->put.easyhandle,
1794 CURLOPT_HTTPPROXYTUNNEL,
1795 s->plugin->proxy_use_httpproxytunnel);
1796 }
1797
1798 curl_easy_setopt (s->put.easyhandle,
1799 CURLOPT_URL,
1800 s->url);
1801 curl_easy_setopt (s->put.easyhandle,
1802 CURLOPT_UPLOAD,
1803 1L);
1804 curl_easy_setopt (s->put.easyhandle,
1805 CURLOPT_READFUNCTION,
1806 &client_send_cb);
1807 curl_easy_setopt (s->put.easyhandle,
1808 CURLOPT_READDATA,
1809 s);
1810 curl_easy_setopt (s->put.easyhandle,
1811 CURLOPT_WRITEFUNCTION,
1812 &client_receive_put);
1813 curl_easy_setopt (s->put.easyhandle,
1814 CURLOPT_WRITEDATA,
1815 s);
1816 /* No timeout by default, timeout done with session timeout */
1817 curl_easy_setopt (s->put.easyhandle,
1818 CURLOPT_TIMEOUT,
1819 0L);
1820 curl_easy_setopt (s->put.easyhandle,
1821 CURLOPT_PRIVATE,
1822 s);
1823 curl_easy_setopt (s->put.easyhandle,
1824 CURLOPT_CONNECTTIMEOUT_MS,
1825 (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us
1826 / 1000LL));
1827 curl_easy_setopt (s->put.easyhandle, CURLOPT_BUFFERSIZE,
1828 2 * GNUNET_MAX_MESSAGE_SIZE);
1829#if CURL_TCP_NODELAY
1830 curl_easy_setopt (s->put.easyhandle, CURLOPT_TCP_NODELAY, 1);
1831#endif
1832 mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1833 s->put.easyhandle);
1834 if (CURLM_OK != mret)
1835 {
1836 LOG (GNUNET_ERROR_TYPE_ERROR,
1837 "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1838 s, curl_multi_strerror (mret));
1839 curl_easy_cleanup (s->put.easyhandle);
1840 s->put.easyhandle = NULL;
1841 s->put.s = NULL;
1842 s->put.state = H_DISCONNECTED;
1843 return GNUNET_SYSERR;
1844 }
1845 s->put.state = H_CONNECTED;
1846 s->plugin->cur_requests++;
1847
1848 LOG (GNUNET_ERROR_TYPE_INFO,
1849 "PUT request `%s' established, number of requests increased to %u\n",
1850 s->url, s->plugin->cur_requests);
1851
1852 return GNUNET_OK;
1853}
1854
1855
1856/**
1857 * Connect both PUT and GET request for a session
1858 *
1859 * @param s the session to connect
1860 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1861 */
1862static int
1863client_connect (struct GNUNET_ATS_Session *s)
1864{
1865 struct HTTP_Client_Plugin *plugin = s->plugin;
1866 int res = GNUNET_OK;
1867
1868 /* create url */
1869 if (NULL ==
1870 http_common_plugin_address_to_string (plugin->protocol,
1871 s->address->address,
1872 s->address->address_length))
1873 {
1874 LOG (GNUNET_ERROR_TYPE_DEBUG,
1875 "Invalid address peer `%s'\n",
1876 GNUNET_i2s (&s->address->peer));
1877 return GNUNET_SYSERR;
1878 }
1879
1880 GNUNET_asprintf (&s->url,
1881 "%s/%s;%u",
1882 http_common_plugin_address_to_url (NULL,
1883 s->address->address,
1884 s->address->address_length),
1885 GNUNET_i2s_full (plugin->env->my_identity),
1886 plugin->last_tag);
1887
1888 plugin->last_tag++;
1889 LOG (GNUNET_ERROR_TYPE_DEBUG,
1890 "Initiating outbound session peer `%s' using address `%s'\n",
1891 GNUNET_i2s (&s->address->peer), s->url);
1892
1893 if (GNUNET_SYSERR == client_connect_get (s))
1894 return GNUNET_SYSERR;
1895 /* If we are emulating an XHR client then delay sending a PUT request until
1896 * there is something to send.
1897 */
1898 if (GNUNET_YES == plugin->emulate_xhr)
1899 {
1900 s->put.state = H_TMP_DISCONNECTED;
1901 }
1902 else if (GNUNET_SYSERR == client_connect_put (s))
1903 return GNUNET_SYSERR;
1904
1905 LOG (GNUNET_ERROR_TYPE_DEBUG,
1906 "Session %p: connected with GET %p and PUT %p\n",
1907 s, s->get.easyhandle,
1908 s->put.easyhandle);
1909 /* Perform connect */
1910 GNUNET_STATISTICS_set (plugin->env->stats,
1911 HTTP_STAT_STR_CONNECTIONS,
1912 plugin->cur_requests,
1913 GNUNET_NO);
1914 /* Re-schedule since handles have changed */
1915 if (NULL != plugin->client_perform_task)
1916 {
1917 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1918 plugin->client_perform_task = NULL;
1919 }
1920
1921 /* Schedule task to run immediately */
1922 plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run,
1923 plugin);
1924 return res;
1925}
1926
1927
1928/**
1929 * Function obtain the network type for a session
1930 *
1931 * @param cls closure (`struct Plugin*`)
1932 * @param session the session
1933 * @return the network type
1934 */
1935static enum GNUNET_NetworkType
1936http_client_plugin_get_network (void *cls,
1937 struct GNUNET_ATS_Session *session)
1938{
1939 return session->scope;
1940}
1941
1942
1943/**
1944 * Function obtain the network type for an address.
1945 *
1946 * @param cls closure (`struct Plugin *`)
1947 * @param address the address
1948 * @return the network type
1949 */
1950static enum GNUNET_NetworkType
1951http_client_plugin_get_network_for_address (void *cls,
1952 const struct
1953 GNUNET_HELLO_Address *address)
1954{
1955 struct HTTP_Client_Plugin *plugin = cls;
1956
1957 return http_common_get_network_for_address (plugin->env,
1958 address);
1959}
1960
1961
1962/**
1963 * Session was idle, so disconnect it
1964 *
1965 * @param cls the `struct GNUNET_ATS_Session` of the idle session
1966 */
1967static void
1968client_session_timeout (void *cls)
1969{
1970 struct GNUNET_ATS_Session *s = cls;
1971 struct GNUNET_TIME_Relative left;
1972
1973 s->timeout_task = NULL;
1974 left = GNUNET_TIME_absolute_get_remaining (s->timeout);
1975 if (0 != left.rel_value_us)
1976 {
1977 /* not actually our turn yet, but let's at least update
1978 the monitor, it may think we're about to die ... */
1979 notify_session_monitor (s->plugin,
1980 s,
1981 GNUNET_TRANSPORT_SS_UPDATE);
1982 s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
1983 &client_session_timeout,
1984 s);
1985 return;
1986 }
1987 LOG (TIMEOUT_LOG,
1988 "Session %p was idle for %s, disconnecting\n",
1989 s,
1990 GNUNET_STRINGS_relative_time_to_string (HTTP_CLIENT_SESSION_TIMEOUT,
1991 GNUNET_YES));
1992 GNUNET_assert (GNUNET_OK ==
1993 http_client_plugin_session_disconnect (s->plugin,
1994 s));
1995}
1996
1997
1998/**
1999 * Creates a new outbound session the transport service will use to
2000 * send data to the peer
2001 *
2002 * @param cls the plugin
2003 * @param address the address
2004 * @return the session or NULL of max connections exceeded
2005 */
2006static struct GNUNET_ATS_Session *
2007http_client_plugin_get_session (void *cls,
2008 const struct GNUNET_HELLO_Address *address)
2009{
2010 struct HTTP_Client_Plugin *plugin = cls;
2011 struct GNUNET_ATS_Session *s;
2012 struct sockaddr *sa;
2013 enum GNUNET_NetworkType net_type;
2014 size_t salen = 0;
2015 int res;
2016
2017 GNUNET_assert (NULL != address->address);
2018
2019 /* find existing session */
2020 s = client_lookup_session (plugin, address);
2021 if (NULL != s)
2022 return s;
2023
2024 /* create a new session */
2025 if (plugin->max_requests <= plugin->cur_requests)
2026 {
2027 LOG (GNUNET_ERROR_TYPE_WARNING,
2028 "Maximum number of requests (%u) reached: "
2029 "cannot connect to peer `%s'\n",
2030 plugin->max_requests,
2031 GNUNET_i2s (&address->peer));
2032 return NULL;
2033 }
2034
2035 /* Determine network location */
2036 net_type = GNUNET_NT_UNSPECIFIED;
2037 sa = http_common_socket_from_address (address->address,
2038 address->address_length,
2039 &res);
2040 if (GNUNET_SYSERR == res)
2041 return NULL;
2042 if (GNUNET_YES == res)
2043 {
2044 GNUNET_assert (NULL != sa);
2045 if (AF_INET == sa->sa_family)
2046 {
2047 salen = sizeof(struct sockaddr_in);
2048 }
2049 else if (AF_INET6 == sa->sa_family)
2050 {
2051 salen = sizeof(struct sockaddr_in6);
2052 }
2053 net_type = plugin->env->get_address_type (plugin->env->cls, sa, salen);
2054 GNUNET_free (sa);
2055 }
2056 else if (GNUNET_NO == res)
2057 {
2058 /* Cannot convert to sockaddr -> is external hostname */
2059 net_type = GNUNET_NT_WAN;
2060 }
2061 if (GNUNET_NT_UNSPECIFIED == net_type)
2062 {
2063 GNUNET_break (0);
2064 return NULL;
2065 }
2066
2067 s = GNUNET_new (struct GNUNET_ATS_Session);
2068 s->plugin = plugin;
2069 s->address = GNUNET_HELLO_address_copy (address);
2070 s->scope = net_type;
2071
2072 s->put.state = H_NOT_CONNECTED;
2073 s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
2074 s->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
2075 &client_session_timeout,
2076 s);
2077 LOG (GNUNET_ERROR_TYPE_DEBUG,
2078 "Created new session %p for `%s' address `%s''\n",
2079 s,
2080 http_common_plugin_address_to_string (plugin->protocol,
2081 s->address->address,
2082 s->address->address_length),
2083 GNUNET_i2s (&s->address->peer));
2084
2085 /* add new session */
2086 (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
2087 &s->address->peer,
2088 s,
2089 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
2090 /* initiate new connection */
2091 if (GNUNET_SYSERR == client_connect (s))
2092 {
2093 LOG (GNUNET_ERROR_TYPE_ERROR,
2094 "Cannot connect to peer `%s' address `%s''\n",
2095 http_common_plugin_address_to_string (plugin->protocol,
2096 s->address->address,
2097 s->address->address_length),
2098 GNUNET_i2s (&s->address->peer));
2099 client_delete_session (s);
2100 return NULL;
2101 }
2102 notify_session_monitor (plugin,
2103 s,
2104 GNUNET_TRANSPORT_SS_INIT);
2105 notify_session_monitor (plugin,
2106 s,
2107 GNUNET_TRANSPORT_SS_UP); /* or handshake? */
2108 return s;
2109}
2110
2111
2112/**
2113 * Setup http_client plugin
2114 *
2115 * @param plugin the plugin handle
2116 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
2117 */
2118static int
2119client_start (struct HTTP_Client_Plugin *plugin)
2120{
2121 curl_global_init (CURL_GLOBAL_ALL);
2122 plugin->curl_multi_handle = curl_multi_init ();
2123
2124 if (NULL == plugin->curl_multi_handle)
2125 {
2126 LOG (GNUNET_ERROR_TYPE_ERROR,
2127 _ (
2128 "Could not initialize curl multi handle, failed to start %s plugin!\n"),
2129 plugin->name);
2130 return GNUNET_SYSERR;
2131 }
2132 return GNUNET_OK;
2133}
2134
2135
2136/**
2137 * Another peer has suggested an address for this
2138 * peer and transport plugin. Check that this could be a valid
2139 * address. If so, consider adding it to the list
2140 * of addresses.
2141 *
2142 * @param cls closure with the `struct Plugin`
2143 * @param addr pointer to the address
2144 * @param addrlen length of @a addr
2145 * @return #GNUNET_OK if this is a plausible address for this peer
2146 * and transport; always returns #GNUNET_NO (this is the client!)
2147 */
2148static int
2149http_client_plugin_address_suggested (void *cls,
2150 const void *addr,
2151 size_t addrlen)
2152{
2153 /* A HTTP/S client does not have any valid address so:*/
2154 return GNUNET_NO;
2155}
2156
2157
2158/**
2159 * Exit point from the plugin.
2160 *
2161 * @param cls api as closure
2162 * @return NULL
2163 */
2164void *
2165LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2166{
2167 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2168 struct HTTP_Client_Plugin *plugin = api->cls;
2169
2170 if (NULL == api->cls)
2171 {
2172 /* Stub shutdown */
2173 GNUNET_free (api);
2174 return NULL;
2175 }
2176 LOG (GNUNET_ERROR_TYPE_DEBUG,
2177 _ ("Shutting down plugin `%s'\n"),
2178 plugin->name);
2179 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2180 &destroy_session_cb,
2181 plugin);
2182 if (NULL != plugin->client_perform_task)
2183 {
2184 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
2185 plugin->client_perform_task = NULL;
2186 }
2187 if (NULL != plugin->curl_multi_handle)
2188 {
2189 curl_multi_cleanup (plugin->curl_multi_handle);
2190 plugin->curl_multi_handle = NULL;
2191 }
2192 curl_global_cleanup ();
2193 LOG (GNUNET_ERROR_TYPE_DEBUG,
2194 _ ("Shutdown for plugin `%s' complete\n"),
2195 plugin->name);
2196 GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
2197 GNUNET_free (plugin->proxy_hostname);
2198 GNUNET_free (plugin->proxy_username);
2199 GNUNET_free (plugin->proxy_password);
2200 GNUNET_free (plugin);
2201 GNUNET_free (api);
2202 return NULL;
2203}
2204
2205
2206/**
2207 * Configure plugin
2208 *
2209 * @param plugin the plugin handle
2210 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2211 */
2212static int
2213client_configure_plugin (struct HTTP_Client_Plugin *plugin)
2214{
2215 unsigned long long max_requests;
2216 char *proxy_type;
2217
2218 /* Optional parameters */
2219 if (GNUNET_OK !=
2220 GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2221 plugin->name,
2222 "MAX_CONNECTIONS",
2223 &max_requests))
2224 max_requests = 128;
2225 plugin->max_requests = max_requests;
2226
2227 LOG (GNUNET_ERROR_TYPE_DEBUG,
2228 _ ("Maximum number of requests is %u\n"),
2229 plugin->max_requests);
2230
2231 /* Read proxy configuration */
2232 if (GNUNET_OK ==
2233 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2234 plugin->name,
2235 "PROXY",
2236 &plugin->proxy_hostname))
2237 {
2238 LOG (GNUNET_ERROR_TYPE_DEBUG,
2239 "Found proxy host: `%s'\n",
2240 plugin->proxy_hostname);
2241 /* proxy username */
2242 if (GNUNET_OK ==
2243 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2244 plugin->name,
2245 "PROXY_USERNAME",
2246 &plugin->proxy_username))
2247 {
2248 LOG (GNUNET_ERROR_TYPE_DEBUG,
2249 "Found proxy username name: `%s'\n",
2250 plugin->proxy_username);
2251 }
2252
2253 /* proxy password */
2254 if (GNUNET_OK ==
2255 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2256 plugin->name,
2257 "PROXY_PASSWORD",
2258 &plugin->proxy_password))
2259 {
2260 LOG (GNUNET_ERROR_TYPE_DEBUG,
2261 "Found proxy password name: `%s'\n",
2262 plugin->proxy_password);
2263 }
2264
2265 /* proxy type */
2266 if (GNUNET_OK ==
2267 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2268 plugin->name,
2269 "PROXY_TYPE",
2270 &proxy_type))
2271 {
2272 GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
2273
2274 if (0 == strcmp (proxy_type, "HTTP"))
2275 plugin->proxytype = CURLPROXY_HTTP;
2276 else if (0 == strcmp (proxy_type, "SOCKS4"))
2277 plugin->proxytype = CURLPROXY_SOCKS4;
2278 else if (0 == strcmp (proxy_type, "SOCKS5"))
2279 plugin->proxytype = CURLPROXY_SOCKS5;
2280 else if (0 == strcmp (proxy_type, "SOCKS4A"))
2281 plugin->proxytype = CURLPROXY_SOCKS4A;
2282 else if (0 == strcmp (proxy_type, "SOCKS5_HOSTNAME "))
2283 plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME;
2284 else
2285 {
2286 LOG (GNUNET_ERROR_TYPE_ERROR,
2287 _ (
2288 "Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2289 proxy_type);
2290
2291 GNUNET_free (proxy_type);
2292 GNUNET_free (plugin->proxy_hostname);
2293 plugin->proxy_hostname = NULL;
2294 GNUNET_free (plugin->proxy_username);
2295 plugin->proxy_username = NULL;
2296 GNUNET_free (plugin->proxy_password);
2297 plugin->proxy_password = NULL;
2298
2299 return GNUNET_SYSERR;
2300 }
2301
2302 LOG (GNUNET_ERROR_TYPE_DEBUG,
2303 "Found proxy type: `%s'\n",
2304 proxy_type);
2305 }
2306
2307 /* proxy http tunneling */
2308 plugin->proxy_use_httpproxytunnel
2309 = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2310 plugin->name,
2311 "PROXY_HTTP_TUNNELING");
2312 if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2313 plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2314
2315 GNUNET_free (proxy_type);
2316 }
2317
2318 /* Should we emulate an XHR client for testing? */
2319 plugin->emulate_xhr
2320 = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2321 plugin->name,
2322 "EMULATE_XHR");
2323 return GNUNET_OK;
2324}
2325
2326
2327/**
2328 * Function to convert an address to a human-readable string.
2329 *
2330 * @param cls closure
2331 * @param addr address to convert
2332 * @param addrlen address length
2333 * @return res string if conversion was successful, NULL otherwise
2334 */
2335static const char *
2336http_client_plugin_address_to_string (void *cls,
2337 const void *addr,
2338 size_t addrlen)
2339{
2340 return http_common_plugin_address_to_string (PLUGIN_NAME,
2341 addr,
2342 addrlen);
2343}
2344
2345
2346/**
2347 * Function that will be called whenever the transport service wants to
2348 * notify the plugin that a session is still active and in use and
2349 * therefore the session timeout for this session has to be updated
2350 *
2351 * @param cls closure
2352 * @param peer which peer was the session for
2353 * @param session which session is being updated
2354 */
2355static void
2356http_client_plugin_update_session_timeout (void *cls,
2357 const struct
2358 GNUNET_PeerIdentity *peer,
2359 struct GNUNET_ATS_Session *session)
2360{
2361 client_reschedule_session_timeout (session);
2362}
2363
2364
2365/**
2366 * Function that will be called whenever the transport service wants to
2367 * notify the plugin that the inbound quota changed and that the plugin
2368 * should update it's delay for the next receive value
2369 *
2370 * @param cls closure
2371 * @param peer which peer was the session for
2372 * @param s which session is being updated
2373 * @param delay new delay to use for receiving
2374 */
2375static void
2376http_client_plugin_update_inbound_delay (void *cls,
2377 const struct GNUNET_PeerIdentity *peer,
2378 struct GNUNET_ATS_Session *s,
2379 struct GNUNET_TIME_Relative delay)
2380{
2381 s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2382 LOG (GNUNET_ERROR_TYPE_DEBUG,
2383 "New inbound delay %s\n",
2384 GNUNET_STRINGS_relative_time_to_string (delay,
2385 GNUNET_NO));
2386 if (s->recv_wakeup_task != NULL)
2387 {
2388 GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2389 s->recv_wakeup_task
2390 = GNUNET_SCHEDULER_add_delayed (delay,
2391 &client_wake_up,
2392 s);
2393 }
2394}
2395
2396
2397/**
2398 * Return information about the given session to the
2399 * monitor callback.
2400 *
2401 * @param cls the `struct Plugin` with the monitor callback (`sic`)
2402 * @param peer peer we send information about
2403 * @param value our `struct GNUNET_ATS_Session` to send information about
2404 * @return #GNUNET_OK (continue to iterate)
2405 */
2406static int
2407send_session_info_iter (void *cls,
2408 const struct GNUNET_PeerIdentity *peer,
2409 void *value)
2410{
2411 struct HTTP_Client_Plugin *plugin = cls;
2412 struct GNUNET_ATS_Session *session = value;
2413
2414 notify_session_monitor (plugin,
2415 session,
2416 GNUNET_TRANSPORT_SS_INIT);
2417 notify_session_monitor (plugin,
2418 session,
2419 GNUNET_TRANSPORT_SS_UP); /* FIXME: or handshake? */
2420 return GNUNET_OK;
2421}
2422
2423
2424/**
2425 * Begin monitoring sessions of a plugin. There can only
2426 * be one active monitor per plugin (i.e. if there are
2427 * multiple monitors, the transport service needs to
2428 * multiplex the generated events over all of them).
2429 *
2430 * @param cls closure of the plugin
2431 * @param sic callback to invoke, NULL to disable monitor;
2432 * plugin will being by iterating over all active
2433 * sessions immediately and then enter monitor mode
2434 * @param sic_cls closure for @a sic
2435 */
2436static void
2437http_client_plugin_setup_monitor (void *cls,
2438 GNUNET_TRANSPORT_SessionInfoCallback sic,
2439 void *sic_cls)
2440{
2441 struct HTTP_Client_Plugin *plugin = cls;
2442
2443 plugin->sic = sic;
2444 plugin->sic_cls = sic_cls;
2445 if (NULL != sic)
2446 {
2447 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2448 &send_session_info_iter,
2449 plugin);
2450 /* signal end of first iteration */
2451 sic (sic_cls, NULL, NULL);
2452 }
2453}
2454
2455
2456/**
2457 * Entry point for the plugin.
2458 */
2459void *
2460LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2461{
2462 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2463 struct GNUNET_TRANSPORT_PluginFunctions *api;
2464 struct HTTP_Client_Plugin *plugin;
2465
2466 if (NULL == env->receive)
2467 {
2468 /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2469 initialize the plugin or the API */
2470 api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2471 api->cls = NULL;
2472 api->address_to_string = &http_client_plugin_address_to_string;
2473 api->string_to_address = &http_common_plugin_string_to_address;
2474 api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2475 return api;
2476 }
2477
2478 plugin = GNUNET_new (struct HTTP_Client_Plugin);
2479 plugin->env = env;
2480 plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128,
2481 GNUNET_YES);
2482 api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2483 api->cls = plugin;
2484 api->send = &http_client_plugin_send;
2485 api->disconnect_session = &http_client_plugin_session_disconnect;
2486 api->query_keepalive_factor = &http_client_query_keepalive_factor;
2487 api->disconnect_peer = &http_client_plugin_peer_disconnect;
2488 api->check_address = &http_client_plugin_address_suggested;
2489 api->get_session = &http_client_plugin_get_session;
2490 api->address_to_string = &http_client_plugin_address_to_string;
2491 api->string_to_address = &http_common_plugin_string_to_address;
2492 api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2493 api->get_network = &http_client_plugin_get_network;
2494 api->get_network_for_address = &http_client_plugin_get_network_for_address;
2495 api->update_session_timeout = &http_client_plugin_update_session_timeout;
2496 api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2497 api->setup_monitor = &http_client_plugin_setup_monitor;
2498#if BUILD_HTTPS
2499 plugin->name = "transport-https_client";
2500 plugin->protocol = "https";
2501#else
2502 plugin->name = "transport-http_client";
2503 plugin->protocol = "http";
2504#endif
2505 plugin->last_tag = 1;
2506
2507 if (GNUNET_SYSERR == client_configure_plugin (plugin))
2508 {
2509 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2510 return NULL;
2511 }
2512
2513 /* Start client */
2514 if (GNUNET_SYSERR == client_start (plugin))
2515 {
2516 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2517 return NULL;
2518 }
2519 return api;
2520}
2521
2522
2523/* end of plugin_transport_http_client.c */