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.c2535
1 files changed, 0 insertions, 2535 deletions
diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c
deleted file mode 100644
index 18bfcc054..000000000
--- a/src/transport/plugin_transport_http_client.c
+++ /dev/null
@@ -1,2535 +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 * @param tc gnunet scheduler task context
560 */
561static void
562client_run (void *cls);
563
564
565/**
566 * Function setting up file descriptors and scheduling task to run
567 *
568 * @param plugin the plugin as closure
569 * @param now schedule task in 1ms, regardless of what curl may say
570 * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
571 */
572static int
573client_schedule (struct HTTP_Client_Plugin *plugin,
574 int now)
575{
576 fd_set rs;
577 fd_set ws;
578 fd_set es;
579 int max;
580 struct GNUNET_NETWORK_FDSet *grs;
581 struct GNUNET_NETWORK_FDSet *gws;
582 long to;
583 CURLMcode mret;
584 struct GNUNET_TIME_Relative timeout;
585
586 /* Cancel previous scheduled task */
587 if (plugin->client_perform_task != NULL)
588 {
589 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
590 plugin->client_perform_task = NULL;
591 }
592 max = -1;
593 FD_ZERO (&rs);
594 FD_ZERO (&ws);
595 FD_ZERO (&es);
596 mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
597 if (mret != CURLM_OK)
598 {
599 LOG (GNUNET_ERROR_TYPE_ERROR,
600 _ ("%s failed at %s:%d: `%s'\n"),
601 "curl_multi_fdset",
602 __FILE__,
603 __LINE__,
604 curl_multi_strerror (mret));
605 return GNUNET_SYSERR;
606 }
607 mret = curl_multi_timeout (plugin->curl_multi_handle, &to);
608 if (-1 == to)
609 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
610 else
611 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
612 if (now == GNUNET_YES)
613 timeout = GNUNET_TIME_UNIT_MILLISECONDS;
614
615 if (CURLM_OK != mret)
616 {
617 LOG (GNUNET_ERROR_TYPE_ERROR,
618 _ ("%s failed at %s:%d: `%s'\n"),
619 "curl_multi_timeout", __FILE__, __LINE__,
620 curl_multi_strerror (mret));
621 return GNUNET_SYSERR;
622 }
623
624 grs = GNUNET_NETWORK_fdset_create ();
625 gws = GNUNET_NETWORK_fdset_create ();
626 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
627 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
628
629 /* Schedule task to run when select is ready to read or write */
630 plugin->client_perform_task =
631 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
632 timeout, grs, gws,
633 &client_run, plugin);
634 GNUNET_NETWORK_fdset_destroy (gws);
635 GNUNET_NETWORK_fdset_destroy (grs);
636 return GNUNET_OK;
637}
638
639
640#if VERBOSE_CURL
641/**
642 * Logging function
643 *
644 * @param curl the curl easy handle
645 * @param type message type
646 * @param data data to log, NOT a 0-terminated string
647 * @param size data length
648 * @param cls the closure
649 * @return always 0
650 */
651static int
652client_log (CURL *curl,
653 curl_infotype type,
654 const char *data,
655 size_t size,
656 void *cls)
657{
658 struct RequestHandle *ch = cls;
659 const char *ttype = "UNSPECIFIED";
660 char text[size + 2];
661
662 if (! ((CURLINFO_TEXT == type) ||
663 (CURLINFO_HEADER_IN == type) ||
664 (CURLINFO_HEADER_OUT == type)))
665 return 0;
666 switch (type)
667 {
668 case CURLINFO_TEXT:
669 ttype = "TEXT";
670 break;
671
672 case CURLINFO_HEADER_IN:
673 ttype = "HEADER_IN";
674 break;
675
676 case CURLINFO_HEADER_OUT:
677 ttype = "HEADER_OUT";
678 /* Overhead*/
679 GNUNET_assert (NULL != ch);
680 GNUNET_assert (NULL != ch->easyhandle);
681 GNUNET_assert (NULL != ch->s);
682 ch->s->overhead += size;
683 break;
684
685 default:
686 ttype = "UNSPECIFIED";
687 break;
688 }
689 GNUNET_memcpy (text, data, size);
690 if (text[size - 1] == '\n')
691 {
692 text[size] = '\0';
693 }
694 else
695 {
696 text[size] = '\n';
697 text[size + 1] = '\0';
698 }
699 LOG (GNUNET_ERROR_TYPE_DEBUG,
700 "Request %p %s: %s",
701 ch->easyhandle,
702 ttype,
703 text);
704 return 0;
705}
706
707
708#endif
709
710/**
711 * Connect GET request
712 *
713 * @param s the session to connect
714 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
715 */
716static int
717client_connect_get (struct GNUNET_ATS_Session *s);
718
719
720/**
721 * Connect a HTTP put request
722 *
723 * @param s the session to connect
724 * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for success
725 */
726static int
727client_connect_put (struct GNUNET_ATS_Session *s);
728
729
730/**
731 * Function that can be used by the transport service to transmit
732 * a message using the plugin. Note that in the case of a
733 * peer disconnecting, the continuation MUST be called
734 * prior to the disconnect notification itself. This function
735 * will be called with this peer's HELLO message to initiate
736 * a fresh connection to another peer.
737 *
738 * @param cls closure
739 * @param s which session must be used
740 * @param msgbuf the message to transmit
741 * @param msgbuf_size number of bytes in @a msgbuf
742 * @param priority how important is the message (most plugins will
743 * ignore message priority and just FIFO)
744 * @param to how long to wait at most for the transmission (does not
745 * require plugins to discard the message after the timeout,
746 * just advisory for the desired delay; most plugins will ignore
747 * this as well)
748 * @param cont continuation to call once the message has
749 * been transmitted (or if the transport is ready
750 * for the next transmission call; or if the
751 * peer disconnected...); can be NULL
752 * @param cont_cls closure for @a cont
753 * @return number of bytes used (on the physical network, with overheads);
754 * -1 on hard errors (i.e. address invalid); 0 is a legal value
755 * and does NOT mean that the message was not transmitted (DV)
756 */
757static ssize_t
758http_client_plugin_send (void *cls,
759 struct GNUNET_ATS_Session *s,
760 const char *msgbuf,
761 size_t msgbuf_size,
762 unsigned int priority,
763 struct GNUNET_TIME_Relative to,
764 GNUNET_TRANSPORT_TransmitContinuation cont,
765 void *cont_cls)
766{
767 struct HTTP_Client_Plugin *plugin = cls;
768 struct HTTP_Message *msg;
769 char *stat_txt;
770
771 LOG (GNUNET_ERROR_TYPE_DEBUG,
772 "Session %p/request %p: Sending message with %lu to peer `%s' \n",
773 s,
774 s->put.easyhandle,
775 (unsigned long) msgbuf_size,
776 GNUNET_i2s (&s->address->peer));
777
778 /* create new message and schedule */
779 msg = GNUNET_malloc (sizeof(struct HTTP_Message) + msgbuf_size);
780 msg->size = msgbuf_size;
781 msg->buf = (char *) &msg[1];
782 msg->transmit_cont = cont;
783 msg->transmit_cont_cls = cont_cls;
784 GNUNET_memcpy (msg->buf,
785 msgbuf,
786 msgbuf_size);
787 GNUNET_CONTAINER_DLL_insert_tail (s->msg_head,
788 s->msg_tail,
789 msg);
790 s->msgs_in_queue++;
791 s->bytes_in_queue += msg->size;
792
793 GNUNET_asprintf (&stat_txt,
794 "# bytes currently in %s_client buffers",
795 plugin->protocol);
796 GNUNET_STATISTICS_update (plugin->env->stats,
797 stat_txt, msgbuf_size, GNUNET_NO);
798 GNUNET_free (stat_txt);
799 notify_session_monitor (plugin,
800 s,
801 GNUNET_TRANSPORT_SS_UPDATE);
802 if (H_TMP_DISCONNECTING == s->put.state)
803 {
804 /* PUT request is currently getting disconnected */
805 s->put.state = H_TMP_RECONNECT_REQUIRED;
806 LOG (GNUNET_ERROR_TYPE_DEBUG,
807 "Session %p/request %p: currently disconnecting, reconnecting immediately\n",
808 s,
809 s->put.easyhandle);
810 return msgbuf_size;
811 }
812 if (H_PAUSED == s->put.state)
813 {
814 /* PUT request was paused, unpause */
815 GNUNET_assert (s->put_disconnect_task != NULL);
816 GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
817 s->put_disconnect_task = NULL;
818 LOG (GNUNET_ERROR_TYPE_DEBUG,
819 "Session %p/request %p: unpausing request\n",
820 s, s->put.easyhandle);
821 s->put.state = H_CONNECTED;
822 if (NULL != s->put.easyhandle)
823 curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
824 }
825 else if (H_TMP_DISCONNECTED == s->put.state)
826 {
827 /* PUT request was disconnected, reconnect */
828 LOG (GNUNET_ERROR_TYPE_DEBUG, "Session %p: Reconnecting PUT request\n", s);
829 GNUNET_break (NULL == s->put.easyhandle);
830 if (GNUNET_SYSERR == client_connect_put (s))
831 {
832 /* Could not reconnect */
833 http_client_plugin_session_disconnect (plugin, s);
834 return GNUNET_SYSERR;
835 }
836 }
837 client_schedule (s->plugin, GNUNET_YES);
838 return msgbuf_size;
839}
840
841
842/**
843 * Disconnect a session
844 *
845 * @param cls the `struct HTTP_Client_Plugin *`
846 * @param s session
847 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
848 */
849static int
850http_client_plugin_session_disconnect (void *cls,
851 struct GNUNET_ATS_Session *s)
852{
853 struct HTTP_Client_Plugin *plugin = cls;
854
855 LOG (GNUNET_ERROR_TYPE_DEBUG,
856 "Session %p: notifying transport about ending session\n",
857 s);
858 plugin->env->session_end (plugin->env->cls,
859 s->address,
860 s);
861 client_delete_session (s);
862
863 /* Re-schedule since handles have changed */
864 if (NULL != plugin->client_perform_task)
865 {
866 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
867 plugin->client_perform_task = NULL;
868 }
869 client_schedule (plugin, GNUNET_YES);
870
871 return GNUNET_OK;
872}
873
874
875/**
876 * Function that is called to get the keepalive factor.
877 * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
878 * calculate the interval between keepalive packets.
879 *
880 * @param cls closure with the `struct Plugin`
881 * @return keepalive factor
882 */
883static unsigned int
884http_client_query_keepalive_factor (void *cls)
885{
886 return 3;
887}
888
889
890/**
891 * Callback to destroys all sessions on exit.
892 *
893 * @param cls the `struct HTTP_Client_Plugin *`
894 * @param peer identity of the peer
895 * @param value the `struct GNUNET_ATS_Session *`
896 * @return #GNUNET_OK (continue iterating)
897 */
898static int
899destroy_session_cb (void *cls,
900 const struct GNUNET_PeerIdentity *peer,
901 void *value)
902{
903 struct HTTP_Client_Plugin *plugin = cls;
904 struct GNUNET_ATS_Session *session = value;
905
906 http_client_plugin_session_disconnect (plugin, session);
907 return GNUNET_OK;
908}
909
910
911/**
912 * Function that can be used to force the plugin to disconnect
913 * from the given peer and cancel all previous transmissions
914 * (and their continuationc).
915 *
916 * @param cls closure
917 * @param target peer from which to disconnect
918 */
919static void
920http_client_plugin_peer_disconnect (void *cls,
921 const struct GNUNET_PeerIdentity *target)
922{
923 struct HTTP_Client_Plugin *plugin = cls;
924
925 LOG (GNUNET_ERROR_TYPE_DEBUG,
926 "Transport tells me to disconnect `%s'\n",
927 GNUNET_i2s (target));
928 GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
929 target,
930 &destroy_session_cb,
931 plugin);
932}
933
934
935/**
936 * Closure for #session_lookup_client_by_address().
937 */
938struct GNUNET_ATS_SessionClientCtx
939{
940 /**
941 * Address we are looking for.
942 */
943 const struct GNUNET_HELLO_Address *address;
944
945 /**
946 * Session that was found.
947 */
948 struct GNUNET_ATS_Session *ret;
949};
950
951
952/**
953 * Locate the seession object for a given address.
954 *
955 * @param cls the `struct GNUNET_ATS_SessionClientCtx *`
956 * @param key peer identity
957 * @param value the `struct GNUNET_ATS_Session` to check
958 * @return #GNUNET_NO if found, #GNUNET_OK if not
959 */
960static int
961session_lookup_client_by_address (void *cls,
962 const struct GNUNET_PeerIdentity *key,
963 void *value)
964{
965 struct GNUNET_ATS_SessionClientCtx *sc_ctx = cls;
966 struct GNUNET_ATS_Session *s = value;
967
968 if (0 == GNUNET_HELLO_address_cmp (sc_ctx->address,
969 s->address))
970 {
971 sc_ctx->ret = s;
972 return GNUNET_NO;
973 }
974 return GNUNET_YES;
975}
976
977
978/**
979 * Check if a sessions exists for an specific address
980 *
981 * @param plugin the plugin
982 * @param address the address
983 * @return the session or NULL
984 */
985static struct GNUNET_ATS_Session *
986client_lookup_session (struct HTTP_Client_Plugin *plugin,
987 const struct GNUNET_HELLO_Address *address)
988{
989 struct GNUNET_ATS_SessionClientCtx sc_ctx;
990
991 sc_ctx.address = address;
992 sc_ctx.ret = NULL;
993 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
994 &session_lookup_client_by_address,
995 &sc_ctx);
996 return sc_ctx.ret;
997}
998
999
1000/**
1001 * When we have nothing to transmit, we pause the HTTP PUT
1002 * after a while (so that gnurl stops asking). This task
1003 * is the delayed task that actually disconnects the PUT.
1004 *
1005 * @param cls the `struct GNUNET_ATS_Session *` with the put
1006 */
1007static void
1008client_put_disconnect (void *cls)
1009{
1010 struct GNUNET_ATS_Session *s = cls;
1011
1012 s->put_disconnect_task = NULL;
1013 LOG (GNUNET_ERROR_TYPE_DEBUG,
1014 "Session %p/request %p: will be disconnected due to no activity\n",
1015 s, s->put.easyhandle);
1016 s->put.state = H_TMP_DISCONNECTING;
1017 if (NULL != s->put.easyhandle)
1018 curl_easy_pause (s->put.easyhandle,
1019 CURLPAUSE_CONT);
1020 client_schedule (s->plugin, GNUNET_YES);
1021}
1022
1023
1024/**
1025 * Callback method used with libcurl
1026 * Method is called when libcurl needs to read data during sending
1027 *
1028 * @param stream pointer where to write data
1029 * @param size size of an individual element
1030 * @param nmemb count of elements that can be written to the buffer
1031 * @param cls our `struct GNUNET_ATS_Session`
1032 * @return bytes written to stream, returning 0 will terminate request!
1033 */
1034static size_t
1035client_send_cb (void *stream,
1036 size_t size,
1037 size_t nmemb,
1038 void *cls)
1039{
1040 struct GNUNET_ATS_Session *s = cls;
1041 struct HTTP_Client_Plugin *plugin = s->plugin;
1042 struct HTTP_Message *msg = s->msg_head;
1043 size_t len;
1044 char *stat_txt;
1045
1046 if (H_TMP_DISCONNECTING == s->put.state)
1047 {
1048 LOG (GNUNET_ERROR_TYPE_DEBUG,
1049 "Session %p/request %p: disconnect due to inactivity\n",
1050 s, s->put.easyhandle);
1051 return 0;
1052 }
1053
1054 if (NULL == msg)
1055 {
1056 if (GNUNET_YES == plugin->emulate_xhr)
1057 {
1058 LOG (GNUNET_ERROR_TYPE_DEBUG,
1059 "Session %p/request %p: PUT request finished\n",
1060 s,
1061 s->put.easyhandle);
1062 s->put.state = H_TMP_DISCONNECTING;
1063 return 0;
1064 }
1065
1066 /* We have nothing to send, so pause PUT request */
1067 LOG (GNUNET_ERROR_TYPE_DEBUG,
1068 "Session %p/request %p: nothing to send, suspending\n",
1069 s,
1070 s->put.easyhandle);
1071 s->put_disconnect_task
1072 = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT,
1073 &client_put_disconnect,
1074 s);
1075 s->put.state = H_PAUSED;
1076 return CURL_READFUNC_PAUSE;
1077 }
1078 /* data to send */
1079 GNUNET_assert (msg->pos < msg->size);
1080 /* calculate how much fits in buffer */
1081 len = GNUNET_MIN (msg->size - msg->pos,
1082 size * nmemb);
1083 GNUNET_memcpy (stream,
1084 &msg->buf[msg->pos],
1085 len);
1086 msg->pos += len;
1087 if (msg->pos == msg->size)
1088 {
1089 LOG (GNUNET_ERROR_TYPE_DEBUG,
1090 "Session %p/request %p: sent message with %lu bytes sent, removing message from queue\n",
1091 s,
1092 s->put.easyhandle,
1093 (unsigned long) msg->size);
1094 /* Calling transmit continuation */
1095 GNUNET_CONTAINER_DLL_remove (s->msg_head,
1096 s->msg_tail,
1097 msg);
1098 GNUNET_assert (0 < s->msgs_in_queue);
1099 s->msgs_in_queue--;
1100 GNUNET_assert (msg->size <= s->bytes_in_queue);
1101 s->bytes_in_queue -= msg->size;
1102 if (NULL != msg->transmit_cont)
1103 msg->transmit_cont (msg->transmit_cont_cls,
1104 &s->address->peer,
1105 GNUNET_OK,
1106 msg->size,
1107 msg->size + s->overhead);
1108 s->overhead = 0;
1109 GNUNET_free (msg);
1110 }
1111 notify_session_monitor (plugin,
1112 s,
1113 GNUNET_TRANSPORT_SS_UPDATE);
1114 GNUNET_asprintf (&stat_txt,
1115 "# bytes currently in %s_client buffers",
1116 plugin->protocol);
1117 GNUNET_STATISTICS_update (plugin->env->stats,
1118 stat_txt,
1119 -len,
1120 GNUNET_NO);
1121 GNUNET_free (stat_txt);
1122 GNUNET_asprintf (&stat_txt,
1123 "# bytes transmitted via %s_client",
1124 plugin->protocol);
1125 GNUNET_STATISTICS_update (plugin->env->stats,
1126 stat_txt,
1127 len,
1128 GNUNET_NO);
1129 GNUNET_free (stat_txt);
1130 return len;
1131}
1132
1133
1134/**
1135 * Wake up a curl handle which was suspended
1136 *
1137 * @param cls the session
1138 */
1139static void
1140client_wake_up (void *cls)
1141{
1142 struct GNUNET_ATS_Session *s = cls;
1143
1144 s->recv_wakeup_task = NULL;
1145 LOG (GNUNET_ERROR_TYPE_DEBUG,
1146 "Session %p/request %p: Waking up GET handle\n",
1147 s, s->get.easyhandle);
1148 if (H_PAUSED == s->put.state)
1149 {
1150 /* PUT request was paused, unpause */
1151 GNUNET_assert (s->put_disconnect_task != NULL);
1152 GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
1153 s->put_disconnect_task = NULL;
1154 s->put.state = H_CONNECTED;
1155 if (NULL != s->put.easyhandle)
1156 curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
1157 }
1158 if (NULL != s->get.easyhandle)
1159 curl_easy_pause (s->get.easyhandle, CURLPAUSE_CONT);
1160}
1161
1162
1163/**
1164 * Callback for message stream tokenizer
1165 *
1166 * @param cls the session
1167 * @param message the message received
1168 * @return always #GNUNET_OK
1169 */
1170static int
1171client_receive_mst_cb (void *cls,
1172 const struct GNUNET_MessageHeader *message)
1173{
1174 struct GNUNET_ATS_Session *s = cls;
1175 struct HTTP_Client_Plugin *plugin;
1176 struct GNUNET_TIME_Relative delay;
1177 char *stat_txt;
1178
1179 plugin = s->plugin;
1180 delay = s->plugin->env->receive (plugin->env->cls,
1181 s->address,
1182 s,
1183 message);
1184 GNUNET_asprintf (&stat_txt,
1185 "# bytes received via %s_client",
1186 plugin->protocol);
1187 GNUNET_STATISTICS_update (plugin->env->stats,
1188 stat_txt,
1189 ntohs (message->size),
1190 GNUNET_NO);
1191 GNUNET_free (stat_txt);
1192
1193 s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
1194 if (GNUNET_TIME_absolute_get ().abs_value_us < s->next_receive.abs_value_us)
1195 {
1196 LOG (GNUNET_ERROR_TYPE_DEBUG,
1197 "Client: peer `%s' address `%s' next read delayed for %s\n",
1198 GNUNET_i2s (&s->address->peer),
1199 http_common_plugin_address_to_string (s->plugin->protocol,
1200 s->address->address,
1201 s->address->address_length),
1202 GNUNET_STRINGS_relative_time_to_string (delay,
1203 GNUNET_YES));
1204 }
1205 client_reschedule_session_timeout (s);
1206 return GNUNET_OK;
1207}
1208
1209
1210/**
1211 * Callback method used with libcurl when data for a PUT request are
1212 * received. We do not expect data here, so we just discard it.
1213 *
1214 * @param stream pointer where to write data
1215 * @param size size of an individual element
1216 * @param nmemb count of elements that can be written to the buffer
1217 * @param cls destination pointer, passed to the libcurl handle
1218 * @return bytes read from stream
1219 */
1220static size_t
1221client_receive_put (void *stream,
1222 size_t size,
1223 size_t nmemb,
1224 void *cls)
1225{
1226 return size * nmemb;
1227}
1228
1229
1230/**
1231 * Callback method used with libcurl when data for a GET request are
1232 * received. Forward to MST
1233 *
1234 * @param stream pointer where to write data
1235 * @param size size of an individual element
1236 * @param nmemb count of elements that can be written to the buffer
1237 * @param cls destination pointer, passed to the libcurl handle
1238 * @return bytes read from stream
1239 */
1240static size_t
1241client_receive (void *stream,
1242 size_t size,
1243 size_t nmemb,
1244 void *cls)
1245{
1246 struct GNUNET_ATS_Session *s = cls;
1247 struct GNUNET_TIME_Absolute now;
1248 size_t len = size * nmemb;
1249
1250 LOG (GNUNET_ERROR_TYPE_DEBUG,
1251 "Session %p / request %p: Received %lu bytes from peer `%s'\n",
1252 s,
1253 s->get.easyhandle,
1254 (unsigned long) len,
1255 GNUNET_i2s (&s->address->peer));
1256 now = GNUNET_TIME_absolute_get ();
1257 if (now.abs_value_us < s->next_receive.abs_value_us)
1258 {
1259 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1260 struct GNUNET_TIME_Relative delta
1261 = GNUNET_TIME_absolute_get_difference (now, s->next_receive);
1262
1263 LOG (GNUNET_ERROR_TYPE_DEBUG,
1264 "Session %p / request %p: No inbound bandwidth available! Next read was delayed for %s\n",
1265 s,
1266 s->get.easyhandle,
1267 GNUNET_STRINGS_relative_time_to_string (delta,
1268 GNUNET_YES));
1269 if (s->recv_wakeup_task != NULL)
1270 {
1271 GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
1272 s->recv_wakeup_task = NULL;
1273 }
1274 s->recv_wakeup_task
1275 = GNUNET_SCHEDULER_add_delayed (delta,
1276 &client_wake_up,
1277 s);
1278 return CURL_WRITEFUNC_PAUSE;
1279 }
1280 if (NULL == s->msg_tk)
1281 s->msg_tk = GNUNET_MST_create (&client_receive_mst_cb,
1282 s);
1283 GNUNET_MST_from_buffer (s->msg_tk,
1284 stream,
1285 len,
1286 GNUNET_NO,
1287 GNUNET_NO);
1288 return len;
1289}
1290
1291
1292/**
1293 * Task performing curl operations
1294 *
1295 * @param cls plugin as closure
1296 */
1297static void
1298client_run (void *cls)
1299{
1300 struct HTTP_Client_Plugin *plugin = cls;
1301 int running;
1302 long http_statuscode;
1303 CURLMcode mret;
1304 CURLMsg *msg;
1305 int put_request; /* GNUNET_YES if easy handle is put, GNUNET_NO for get */
1306 int msgs_left;
1307
1308 plugin->client_perform_task = NULL;
1309 /* While data are available or timeouts occurred */
1310 do
1311 {
1312 running = 0;
1313 /* Perform operations for all handles */
1314 mret = curl_multi_perform (plugin->curl_multi_handle, &running);
1315
1316 /* Get additional information for all handles */
1317 while (NULL != (msg = curl_multi_info_read (plugin->curl_multi_handle,
1318 &msgs_left)))
1319 {
1320 CURL *easy_h = msg->easy_handle;
1321 struct GNUNET_ATS_Session *s = NULL;
1322 char *d = NULL; /* curl requires 'd' to be a 'char *' */
1323
1324 GNUNET_assert (NULL != easy_h);
1325
1326 /* Obtain session from easy handle */
1327 GNUNET_assert (CURLE_OK == curl_easy_getinfo (easy_h, CURLINFO_PRIVATE,
1328 &d));
1329 s = (struct GNUNET_ATS_Session *) d;
1330 GNUNET_assert (NULL != s);
1331
1332 if (msg->msg != CURLMSG_DONE)
1333 continue; /* This should not happen */
1334
1335 /* Get HTTP response code */
1336 GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
1337 CURLINFO_RESPONSE_CODE,
1338 &http_statuscode));
1339
1340 if (easy_h == s->put.easyhandle)
1341 put_request = GNUNET_YES;
1342 else
1343 put_request = GNUNET_NO;
1344
1345 /* Log status of terminated request */
1346 if ((0 != msg->data.result) || (http_statuscode != 200))
1347 LOG (GNUNET_ERROR_TYPE_DEBUG,
1348 "Session %p/request %p: %s request to `%s' ended with status %li reason %i: `%s'\n",
1349 s, msg->easy_handle,
1350 (GNUNET_YES == put_request) ? "PUT" : "GET",
1351 GNUNET_i2s (&s->address->peer),
1352 http_statuscode,
1353 msg->data.result,
1354 curl_easy_strerror (msg->data.result));
1355 else
1356 LOG (GNUNET_ERROR_TYPE_DEBUG,
1357 "Session %p/request %p: %s request to `%s' ended normal\n",
1358 s, msg->easy_handle,
1359 (GNUNET_YES == put_request) ? "PUT" : "GET",
1360 GNUNET_i2s (&s->address->peer));
1361
1362 /* Remove easy handle from multi handle */
1363 curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
1364
1365 /* Clean up easy handle */
1366 curl_easy_cleanup (easy_h);
1367
1368 /* Remove information */
1369 GNUNET_assert (plugin->cur_requests > 0);
1370 plugin->cur_requests--;
1371 LOG (GNUNET_ERROR_TYPE_INFO,
1372 "%s request to %s done, number of requests decreased to %u\n",
1373 (GNUNET_YES == put_request) ? "PUT" : "GET",
1374 s->url,
1375 plugin->cur_requests);
1376
1377 if (GNUNET_YES == put_request)
1378 {
1379 /* Clean up a PUT request */
1380 s->put.easyhandle = NULL;
1381 s->put.s = NULL;
1382
1383 switch (s->put.state)
1384 {
1385 case H_NOT_CONNECTED:
1386 case H_DISCONNECTED:
1387 case H_TMP_DISCONNECTED:
1388 /* This must not happen */
1389 GNUNET_break (0);
1390 break;
1391
1392 case H_TMP_RECONNECT_REQUIRED:
1393 /* Transport called send while disconnect in progress, reconnect */
1394 if (GNUNET_SYSERR == client_connect_put (s))
1395 {
1396 /* Reconnect failed, disconnect session */
1397 http_client_plugin_session_disconnect (plugin, s);
1398 }
1399 break;
1400
1401 case H_TMP_DISCONNECTING:
1402 /* PUT gets temporarily disconnected */
1403 s->put.state = H_TMP_DISCONNECTED;
1404 break;
1405
1406 case H_PAUSED:
1407 case H_CONNECTED:
1408 /* PUT gets permanently disconnected */
1409 s->put.state = H_DISCONNECTED;
1410 http_client_plugin_session_disconnect (plugin, s);
1411 break;
1412
1413 default:
1414 GNUNET_break (0);
1415 break;
1416 }
1417 }
1418 else if (GNUNET_NO == put_request)
1419 {
1420 /* Clean up a GET request */
1421 s->get.easyhandle = NULL;
1422 s->get.s = NULL;
1423
1424 /* If we are emulating an XHR client we need to make another GET
1425 * request.
1426 */
1427 if (GNUNET_YES == plugin->emulate_xhr)
1428 {
1429 if (GNUNET_SYSERR == client_connect_get (s))
1430 http_client_plugin_session_disconnect (plugin, s);
1431 }
1432 else
1433 {
1434 /* GET request was terminated, so disconnect session */
1435 http_client_plugin_session_disconnect (plugin, s);
1436 }
1437 }
1438 else
1439 GNUNET_break (0); /* Must not happen */
1440
1441 GNUNET_STATISTICS_set (plugin->env->stats,
1442 HTTP_STAT_STR_CONNECTIONS,
1443 plugin->cur_requests,
1444 GNUNET_NO);
1445 }
1446 }
1447 while (mret == CURLM_CALL_MULTI_PERFORM);
1448 client_schedule (plugin, GNUNET_NO);
1449}
1450
1451
1452#ifdef TCP_STEALTH
1453/**
1454 * Open TCP socket with TCP STEALTH enabled.
1455 *
1456 * @param clientp our `struct GNUNET_ATS_Session *`
1457 * @param purpose why does curl want to open a socket
1458 * @param address what kind of socket does curl want to have opened?
1459 * @return opened socket
1460 */
1461static curl_socket_t
1462open_tcp_stealth_socket_cb (void *clientp,
1463 curlsocktype purpose,
1464 struct curl_sockaddr *address)
1465{
1466 struct GNUNET_ATS_Session *s = clientp;
1467 int ret;
1468
1469 switch (purpose)
1470 {
1471 case CURLSOCKTYPE_IPCXN:
1472 ret = socket (address->family,
1473 address->socktype,
1474 address->protocol);
1475 if (-1 == ret)
1476 return CURL_SOCKET_BAD;
1477 if (((SOCK_STREAM != address->socktype) ||
1478 ((0 != address->protocol) &&
1479 (IPPROTO_TCP != address->protocol))))
1480 return (curl_socket_t) ret;
1481 if ((0 != setsockopt (ret,
1482 IPPROTO_TCP,
1483 TCP_STEALTH,
1484 &s->address->peer,
1485 sizeof(struct GNUNET_PeerIdentity))))
1486 {
1487 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1488 _ ("TCP_STEALTH not supported on this platform.\n"));
1489 (void) close (ret);
1490 return CURL_SOCKET_BAD;
1491 }
1492 return (curl_socket_t) ret;
1493
1494 case CURLSOCKTYPE_ACCEPT:
1495 GNUNET_break (0);
1496 return CURL_SOCKET_BAD;
1497 break;
1498
1499 case CURLSOCKTYPE_LAST:
1500 GNUNET_break (0);
1501 return CURL_SOCKET_BAD;
1502
1503 default:
1504 GNUNET_break (0);
1505 return CURL_SOCKET_BAD;
1506 }
1507}
1508
1509
1510#endif
1511
1512
1513/**
1514 * Connect GET request for a session
1515 *
1516 * @param s the session to connect
1517 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1518 */
1519static int
1520client_connect_get (struct GNUNET_ATS_Session *s)
1521{
1522 CURLMcode mret;
1523 struct HttpAddress *ha;
1524 uint32_t options;
1525
1526 ha = (struct HttpAddress *) s->address->address;
1527 options = ntohl (ha->options);
1528 /* create get request */
1529 s->get.easyhandle = curl_easy_init ();
1530 s->get.s = s;
1531 if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1532 {
1533#ifdef TCP_STEALTH
1534 curl_easy_setopt (s->get.easyhandle,
1535 CURLOPT_OPENSOCKETFUNCTION,
1536 &open_tcp_stealth_socket_cb);
1537 curl_easy_setopt (s->get.easyhandle,
1538 CURLOPT_OPENSOCKETDATA,
1539 s);
1540#else
1541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1542 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1543 curl_easy_cleanup (s->get.easyhandle);
1544 s->get.easyhandle = NULL;
1545 s->get.s = NULL;
1546 return GNUNET_SYSERR;
1547#endif
1548 }
1549
1550#if VERBOSE_CURL
1551 curl_easy_setopt (s->get.easyhandle,
1552 CURLOPT_VERBOSE,
1553 1L);
1554 curl_easy_setopt (s->get.easyhandle,
1555 CURLOPT_DEBUGFUNCTION,
1556 &client_log);
1557 curl_easy_setopt (s->get.easyhandle,
1558 CURLOPT_DEBUGDATA,
1559 &s->get);
1560#endif
1561#if BUILD_HTTPS
1562 curl_easy_setopt (s->get.easyhandle, CURLOPT_SSLVERSION,
1563 CURL_SSLVERSION_TLSv1);
1564 {
1565 if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1566 (options & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1567 {
1568 curl_easy_setopt (s->get.easyhandle,
1569 CURLOPT_SSL_VERIFYPEER, 1L);
1570 curl_easy_setopt (s->get.easyhandle,
1571 CURLOPT_SSL_VERIFYHOST,
1572 2L);
1573 }
1574 else
1575 {
1576 curl_easy_setopt (s->get.easyhandle,
1577 CURLOPT_SSL_VERIFYPEER,
1578 0L);
1579 curl_easy_setopt (s->get.easyhandle,
1580 CURLOPT_SSL_VERIFYHOST,
1581 0L);
1582 }
1583 }
1584 curl_easy_setopt (s->get.easyhandle,
1585 CURLOPT_PROTOCOLS,
1586 CURLPROTO_HTTPS);
1587 curl_easy_setopt (s->get.easyhandle,
1588 CURLOPT_REDIR_PROTOCOLS,
1589 CURLPROTO_HTTPS);
1590#else
1591 curl_easy_setopt (s->get.easyhandle,
1592 CURLOPT_PROTOCOLS,
1593 CURLPROTO_HTTP);
1594 curl_easy_setopt (s->get.easyhandle,
1595 CURLOPT_REDIR_PROTOCOLS,
1596 CURLPROTO_HTTP);
1597#endif
1598
1599 if (NULL != s->plugin->proxy_hostname)
1600 {
1601 curl_easy_setopt (s->get.easyhandle,
1602 CURLOPT_PROXY,
1603 s->plugin->proxy_hostname);
1604 curl_easy_setopt (s->get.easyhandle,
1605 CURLOPT_PROXYTYPE,
1606 s->plugin->proxytype);
1607 if (NULL != s->plugin->proxy_username)
1608 curl_easy_setopt (s->get.easyhandle,
1609 CURLOPT_PROXYUSERNAME,
1610 s->plugin->proxy_username);
1611 if (NULL != s->plugin->proxy_password)
1612 curl_easy_setopt (s->get.easyhandle,
1613 CURLOPT_PROXYPASSWORD,
1614 s->plugin->proxy_password);
1615 if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1616 curl_easy_setopt (s->get.easyhandle,
1617 CURLOPT_HTTPPROXYTUNNEL,
1618 s->plugin->proxy_use_httpproxytunnel);
1619 }
1620
1621 if (GNUNET_YES == s->plugin->emulate_xhr)
1622 {
1623 char *url;
1624
1625 GNUNET_asprintf (&url,
1626 "%s,1",
1627 s->url);
1628 curl_easy_setopt (s->get.easyhandle,
1629 CURLOPT_URL,
1630 url);
1631 GNUNET_free (url);
1632 }
1633 else
1634 {
1635 curl_easy_setopt (s->get.easyhandle,
1636 CURLOPT_URL,
1637 s->url);
1638 }
1639 curl_easy_setopt (s->get.easyhandle,
1640 CURLOPT_READFUNCTION,
1641 &client_send_cb);
1642 curl_easy_setopt (s->get.easyhandle,
1643 CURLOPT_READDATA,
1644 s);
1645 curl_easy_setopt (s->get.easyhandle,
1646 CURLOPT_WRITEFUNCTION,
1647 &client_receive);
1648 curl_easy_setopt (s->get.easyhandle,
1649 CURLOPT_WRITEDATA,
1650 s);
1651 /* No timeout by default, timeout done with session timeout */
1652 curl_easy_setopt (s->get.easyhandle,
1653 CURLOPT_TIMEOUT,
1654 0L);
1655 curl_easy_setopt (s->get.easyhandle,
1656 CURLOPT_PRIVATE, s);
1657 curl_easy_setopt (s->get.easyhandle,
1658 CURLOPT_CONNECTTIMEOUT_MS,
1659 (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us
1660 / 1000LL));
1661 curl_easy_setopt (s->get.easyhandle, CURLOPT_BUFFERSIZE,
1662 2 * GNUNET_MAX_MESSAGE_SIZE);
1663#if CURL_TCP_NODELAY
1664 curl_easy_setopt (ps->recv_endpoint,
1665 CURLOPT_TCP_NODELAY,
1666 1L);
1667#endif
1668 curl_easy_setopt (s->get.easyhandle,
1669 CURLOPT_FOLLOWLOCATION,
1670 0L);
1671
1672 mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1673 s->get.easyhandle);
1674 if (CURLM_OK != mret)
1675 {
1676 LOG (GNUNET_ERROR_TYPE_ERROR,
1677 "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1678 s,
1679 curl_multi_strerror (mret));
1680 curl_easy_cleanup (s->get.easyhandle);
1681 s->get.easyhandle = NULL;
1682 s->get.s = NULL;
1683 GNUNET_break (0);
1684 return GNUNET_SYSERR;
1685 }
1686 s->plugin->cur_requests++;
1687 LOG (GNUNET_ERROR_TYPE_INFO,
1688 "GET request `%s' established, number of requests increased to %u\n",
1689 s->url,
1690 s->plugin->cur_requests);
1691 return GNUNET_OK;
1692}
1693
1694
1695/**
1696 * Connect a HTTP put request
1697 *
1698 * @param s the session to connect
1699 * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
1700 */
1701static int
1702client_connect_put (struct GNUNET_ATS_Session *s)
1703{
1704 CURLMcode mret;
1705 struct HttpAddress *ha;
1706 uint32_t options;
1707
1708 ha = (struct HttpAddress *) s->address->address;
1709 options = ntohl (ha->options);
1710 /* create put request */
1711 LOG (GNUNET_ERROR_TYPE_DEBUG,
1712 "Session %p: Init PUT handle\n",
1713 s);
1714 s->put.easyhandle = curl_easy_init ();
1715 s->put.s = s;
1716#if VERBOSE_CURL
1717 curl_easy_setopt (s->put.easyhandle,
1718 CURLOPT_VERBOSE,
1719 1L);
1720 curl_easy_setopt (s->put.easyhandle,
1721 CURLOPT_DEBUGFUNCTION,
1722 &client_log);
1723 curl_easy_setopt (s->put.easyhandle,
1724 CURLOPT_DEBUGDATA,
1725 &s->put);
1726#endif
1727 if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1728 {
1729#ifdef TCP_STEALTH
1730 curl_easy_setopt (s->put.easyhandle,
1731 CURLOPT_OPENSOCKETFUNCTION,
1732 &open_tcp_stealth_socket_cb);
1733 curl_easy_setopt (s->put.easyhandle,
1734 CURLOPT_OPENSOCKETDATA,
1735 s);
1736#else
1737 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1738 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1739 curl_easy_cleanup (s->put.easyhandle);
1740 s->put.easyhandle = NULL;
1741 s->put.s = NULL;
1742 s->put.state = H_DISCONNECTED;
1743 return GNUNET_SYSERR;
1744#endif
1745 }
1746#if BUILD_HTTPS
1747 curl_easy_setopt (s->put.easyhandle,
1748 CURLOPT_SSLVERSION,
1749 CURL_SSLVERSION_TLSv1);
1750 {
1751 struct HttpAddress *ha;
1752 ha = (struct HttpAddress *) s->address->address;
1753
1754 if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1755 (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1756 {
1757 curl_easy_setopt (s->put.easyhandle,
1758 CURLOPT_SSL_VERIFYPEER,
1759 1L);
1760 curl_easy_setopt (s->put.easyhandle,
1761 CURLOPT_SSL_VERIFYHOST,
1762 2L);
1763 }
1764 else
1765 {
1766 curl_easy_setopt (s->put.easyhandle,
1767 CURLOPT_SSL_VERIFYPEER,
1768 0L);
1769 curl_easy_setopt (s->put.easyhandle,
1770 CURLOPT_SSL_VERIFYHOST,
1771 0L);
1772 }
1773 }
1774 curl_easy_setopt (s->put.easyhandle,
1775 CURLOPT_PROTOCOLS,
1776 CURLPROTO_HTTPS);
1777 curl_easy_setopt (s->put.easyhandle,
1778 CURLOPT_REDIR_PROTOCOLS,
1779 CURLPROTO_HTTPS);
1780#else
1781 curl_easy_setopt (s->put.easyhandle,
1782 CURLOPT_PROTOCOLS,
1783 CURLPROTO_HTTP);
1784 curl_easy_setopt (s->put.easyhandle,
1785 CURLOPT_REDIR_PROTOCOLS,
1786 CURLPROTO_HTTP);
1787#endif
1788 if (NULL != s->plugin->proxy_hostname)
1789 {
1790 curl_easy_setopt (s->put.easyhandle,
1791 CURLOPT_PROXY,
1792 s->plugin->proxy_hostname);
1793 curl_easy_setopt (s->put.easyhandle,
1794 CURLOPT_PROXYTYPE,
1795 s->plugin->proxytype);
1796 if (NULL != s->plugin->proxy_username)
1797 curl_easy_setopt (s->put.easyhandle,
1798 CURLOPT_PROXYUSERNAME,
1799 s->plugin->proxy_username);
1800 if (NULL != s->plugin->proxy_password)
1801 curl_easy_setopt (s->put.easyhandle,
1802 CURLOPT_PROXYPASSWORD,
1803 s->plugin->proxy_password);
1804 if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1805 curl_easy_setopt (s->put.easyhandle,
1806 CURLOPT_HTTPPROXYTUNNEL,
1807 s->plugin->proxy_use_httpproxytunnel);
1808 }
1809
1810 curl_easy_setopt (s->put.easyhandle,
1811 CURLOPT_URL,
1812 s->url);
1813 curl_easy_setopt (s->put.easyhandle,
1814 CURLOPT_UPLOAD,
1815 1L);
1816 curl_easy_setopt (s->put.easyhandle,
1817 CURLOPT_READFUNCTION,
1818 &client_send_cb);
1819 curl_easy_setopt (s->put.easyhandle,
1820 CURLOPT_READDATA,
1821 s);
1822 curl_easy_setopt (s->put.easyhandle,
1823 CURLOPT_WRITEFUNCTION,
1824 &client_receive_put);
1825 curl_easy_setopt (s->put.easyhandle,
1826 CURLOPT_WRITEDATA,
1827 s);
1828 /* No timeout by default, timeout done with session timeout */
1829 curl_easy_setopt (s->put.easyhandle,
1830 CURLOPT_TIMEOUT,
1831 0L);
1832 curl_easy_setopt (s->put.easyhandle,
1833 CURLOPT_PRIVATE,
1834 s);
1835 curl_easy_setopt (s->put.easyhandle,
1836 CURLOPT_CONNECTTIMEOUT_MS,
1837 (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us
1838 / 1000LL));
1839 curl_easy_setopt (s->put.easyhandle, CURLOPT_BUFFERSIZE,
1840 2 * GNUNET_MAX_MESSAGE_SIZE);
1841#if CURL_TCP_NODELAY
1842 curl_easy_setopt (s->put.easyhandle, CURLOPT_TCP_NODELAY, 1);
1843#endif
1844 mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1845 s->put.easyhandle);
1846 if (CURLM_OK != mret)
1847 {
1848 LOG (GNUNET_ERROR_TYPE_ERROR,
1849 "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1850 s, curl_multi_strerror (mret));
1851 curl_easy_cleanup (s->put.easyhandle);
1852 s->put.easyhandle = NULL;
1853 s->put.s = NULL;
1854 s->put.state = H_DISCONNECTED;
1855 return GNUNET_SYSERR;
1856 }
1857 s->put.state = H_CONNECTED;
1858 s->plugin->cur_requests++;
1859
1860 LOG (GNUNET_ERROR_TYPE_INFO,
1861 "PUT request `%s' established, number of requests increased to %u\n",
1862 s->url, s->plugin->cur_requests);
1863
1864 return GNUNET_OK;
1865}
1866
1867
1868/**
1869 * Connect both PUT and GET request for a session
1870 *
1871 * @param s the session to connect
1872 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1873 */
1874static int
1875client_connect (struct GNUNET_ATS_Session *s)
1876{
1877 struct HTTP_Client_Plugin *plugin = s->plugin;
1878 int res = GNUNET_OK;
1879
1880 /* create url */
1881 if (NULL ==
1882 http_common_plugin_address_to_string (plugin->protocol,
1883 s->address->address,
1884 s->address->address_length))
1885 {
1886 LOG (GNUNET_ERROR_TYPE_DEBUG,
1887 "Invalid address peer `%s'\n",
1888 GNUNET_i2s (&s->address->peer));
1889 return GNUNET_SYSERR;
1890 }
1891
1892 GNUNET_asprintf (&s->url,
1893 "%s/%s;%u",
1894 http_common_plugin_address_to_url (NULL,
1895 s->address->address,
1896 s->address->address_length),
1897 GNUNET_i2s_full (plugin->env->my_identity),
1898 plugin->last_tag);
1899
1900 plugin->last_tag++;
1901 LOG (GNUNET_ERROR_TYPE_DEBUG,
1902 "Initiating outbound session peer `%s' using address `%s'\n",
1903 GNUNET_i2s (&s->address->peer), s->url);
1904
1905 if (GNUNET_SYSERR == client_connect_get (s))
1906 return GNUNET_SYSERR;
1907 /* If we are emulating an XHR client then delay sending a PUT request until
1908 * there is something to send.
1909 */
1910 if (GNUNET_YES == plugin->emulate_xhr)
1911 {
1912 s->put.state = H_TMP_DISCONNECTED;
1913 }
1914 else if (GNUNET_SYSERR == client_connect_put (s))
1915 return GNUNET_SYSERR;
1916
1917 LOG (GNUNET_ERROR_TYPE_DEBUG,
1918 "Session %p: connected with GET %p and PUT %p\n",
1919 s, s->get.easyhandle,
1920 s->put.easyhandle);
1921 /* Perform connect */
1922 GNUNET_STATISTICS_set (plugin->env->stats,
1923 HTTP_STAT_STR_CONNECTIONS,
1924 plugin->cur_requests,
1925 GNUNET_NO);
1926 /* Re-schedule since handles have changed */
1927 if (NULL != plugin->client_perform_task)
1928 {
1929 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1930 plugin->client_perform_task = NULL;
1931 }
1932
1933 /* Schedule task to run immediately */
1934 plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run,
1935 plugin);
1936 return res;
1937}
1938
1939
1940/**
1941 * Function obtain the network type for a session
1942 *
1943 * @param cls closure (`struct Plugin*`)
1944 * @param session the session
1945 * @return the network type
1946 */
1947static enum GNUNET_NetworkType
1948http_client_plugin_get_network (void *cls,
1949 struct GNUNET_ATS_Session *session)
1950{
1951 return session->scope;
1952}
1953
1954
1955/**
1956 * Function obtain the network type for an address.
1957 *
1958 * @param cls closure (`struct Plugin *`)
1959 * @param address the address
1960 * @return the network type
1961 */
1962static enum GNUNET_NetworkType
1963http_client_plugin_get_network_for_address (void *cls,
1964 const struct
1965 GNUNET_HELLO_Address *address)
1966{
1967 struct HTTP_Client_Plugin *plugin = cls;
1968
1969 return http_common_get_network_for_address (plugin->env,
1970 address);
1971}
1972
1973
1974/**
1975 * Session was idle, so disconnect it
1976 *
1977 * @param cls the `struct GNUNET_ATS_Session` of the idle session
1978 */
1979static void
1980client_session_timeout (void *cls)
1981{
1982 struct GNUNET_ATS_Session *s = cls;
1983 struct GNUNET_TIME_Relative left;
1984
1985 s->timeout_task = NULL;
1986 left = GNUNET_TIME_absolute_get_remaining (s->timeout);
1987 if (0 != left.rel_value_us)
1988 {
1989 /* not actually our turn yet, but let's at least update
1990 the monitor, it may think we're about to die ... */
1991 notify_session_monitor (s->plugin,
1992 s,
1993 GNUNET_TRANSPORT_SS_UPDATE);
1994 s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
1995 &client_session_timeout,
1996 s);
1997 return;
1998 }
1999 LOG (TIMEOUT_LOG,
2000 "Session %p was idle for %s, disconnecting\n",
2001 s,
2002 GNUNET_STRINGS_relative_time_to_string (HTTP_CLIENT_SESSION_TIMEOUT,
2003 GNUNET_YES));
2004 GNUNET_assert (GNUNET_OK ==
2005 http_client_plugin_session_disconnect (s->plugin,
2006 s));
2007}
2008
2009
2010/**
2011 * Creates a new outbound session the transport service will use to
2012 * send data to the peer
2013 *
2014 * @param cls the plugin
2015 * @param address the address
2016 * @return the session or NULL of max connections exceeded
2017 */
2018static struct GNUNET_ATS_Session *
2019http_client_plugin_get_session (void *cls,
2020 const struct GNUNET_HELLO_Address *address)
2021{
2022 struct HTTP_Client_Plugin *plugin = cls;
2023 struct GNUNET_ATS_Session *s;
2024 struct sockaddr *sa;
2025 enum GNUNET_NetworkType net_type;
2026 size_t salen = 0;
2027 int res;
2028
2029 GNUNET_assert (NULL != address->address);
2030
2031 /* find existing session */
2032 s = client_lookup_session (plugin, address);
2033 if (NULL != s)
2034 return s;
2035
2036 /* create a new session */
2037 if (plugin->max_requests <= plugin->cur_requests)
2038 {
2039 LOG (GNUNET_ERROR_TYPE_WARNING,
2040 "Maximum number of requests (%u) reached: "
2041 "cannot connect to peer `%s'\n",
2042 plugin->max_requests,
2043 GNUNET_i2s (&address->peer));
2044 return NULL;
2045 }
2046
2047 /* Determine network location */
2048 net_type = GNUNET_NT_UNSPECIFIED;
2049 sa = http_common_socket_from_address (address->address,
2050 address->address_length,
2051 &res);
2052 if (GNUNET_SYSERR == res)
2053 return NULL;
2054 if (GNUNET_YES == res)
2055 {
2056 GNUNET_assert (NULL != sa);
2057 if (AF_INET == sa->sa_family)
2058 {
2059 salen = sizeof(struct sockaddr_in);
2060 }
2061 else if (AF_INET6 == sa->sa_family)
2062 {
2063 salen = sizeof(struct sockaddr_in6);
2064 }
2065 net_type = plugin->env->get_address_type (plugin->env->cls, sa, salen);
2066 GNUNET_free (sa);
2067 }
2068 else if (GNUNET_NO == res)
2069 {
2070 /* Cannot convert to sockaddr -> is external hostname */
2071 net_type = GNUNET_NT_WAN;
2072 }
2073 if (GNUNET_NT_UNSPECIFIED == net_type)
2074 {
2075 GNUNET_break (0);
2076 return NULL;
2077 }
2078
2079 s = GNUNET_new (struct GNUNET_ATS_Session);
2080 s->plugin = plugin;
2081 s->address = GNUNET_HELLO_address_copy (address);
2082 s->scope = net_type;
2083
2084 s->put.state = H_NOT_CONNECTED;
2085 s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
2086 s->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
2087 &client_session_timeout,
2088 s);
2089 LOG (GNUNET_ERROR_TYPE_DEBUG,
2090 "Created new session %p for `%s' address `%s''\n",
2091 s,
2092 http_common_plugin_address_to_string (plugin->protocol,
2093 s->address->address,
2094 s->address->address_length),
2095 GNUNET_i2s (&s->address->peer));
2096
2097 /* add new session */
2098 (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
2099 &s->address->peer,
2100 s,
2101 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
2102 /* initiate new connection */
2103 if (GNUNET_SYSERR == client_connect (s))
2104 {
2105 LOG (GNUNET_ERROR_TYPE_ERROR,
2106 "Cannot connect to peer `%s' address `%s''\n",
2107 http_common_plugin_address_to_string (plugin->protocol,
2108 s->address->address,
2109 s->address->address_length),
2110 GNUNET_i2s (&s->address->peer));
2111 client_delete_session (s);
2112 return NULL;
2113 }
2114 notify_session_monitor (plugin,
2115 s,
2116 GNUNET_TRANSPORT_SS_INIT);
2117 notify_session_monitor (plugin,
2118 s,
2119 GNUNET_TRANSPORT_SS_UP); /* or handshake? */
2120 return s;
2121}
2122
2123
2124/**
2125 * Setup http_client plugin
2126 *
2127 * @param plugin the plugin handle
2128 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
2129 */
2130static int
2131client_start (struct HTTP_Client_Plugin *plugin)
2132{
2133 curl_global_init (CURL_GLOBAL_ALL);
2134 plugin->curl_multi_handle = curl_multi_init ();
2135
2136 if (NULL == plugin->curl_multi_handle)
2137 {
2138 LOG (GNUNET_ERROR_TYPE_ERROR,
2139 _ (
2140 "Could not initialize curl multi handle, failed to start %s plugin!\n"),
2141 plugin->name);
2142 return GNUNET_SYSERR;
2143 }
2144 return GNUNET_OK;
2145}
2146
2147
2148/**
2149 * Another peer has suggested an address for this
2150 * peer and transport plugin. Check that this could be a valid
2151 * address. If so, consider adding it to the list
2152 * of addresses.
2153 *
2154 * @param cls closure with the `struct Plugin`
2155 * @param addr pointer to the address
2156 * @param addrlen length of @a addr
2157 * @return #GNUNET_OK if this is a plausible address for this peer
2158 * and transport; always returns #GNUNET_NO (this is the client!)
2159 */
2160static int
2161http_client_plugin_address_suggested (void *cls,
2162 const void *addr,
2163 size_t addrlen)
2164{
2165 /* A HTTP/S client does not have any valid address so:*/
2166 return GNUNET_NO;
2167}
2168
2169
2170/**
2171 * Exit point from the plugin.
2172 *
2173 * @param cls api as closure
2174 * @return NULL
2175 */
2176void *
2177LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2178{
2179 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2180 struct HTTP_Client_Plugin *plugin = api->cls;
2181
2182 if (NULL == api->cls)
2183 {
2184 /* Stub shutdown */
2185 GNUNET_free (api);
2186 return NULL;
2187 }
2188 LOG (GNUNET_ERROR_TYPE_DEBUG,
2189 _ ("Shutting down plugin `%s'\n"),
2190 plugin->name);
2191 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2192 &destroy_session_cb,
2193 plugin);
2194 if (NULL != plugin->client_perform_task)
2195 {
2196 GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
2197 plugin->client_perform_task = NULL;
2198 }
2199 if (NULL != plugin->curl_multi_handle)
2200 {
2201 curl_multi_cleanup (plugin->curl_multi_handle);
2202 plugin->curl_multi_handle = NULL;
2203 }
2204 curl_global_cleanup ();
2205 LOG (GNUNET_ERROR_TYPE_DEBUG,
2206 _ ("Shutdown for plugin `%s' complete\n"),
2207 plugin->name);
2208 GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
2209 GNUNET_free (plugin->proxy_hostname);
2210 GNUNET_free (plugin->proxy_username);
2211 GNUNET_free (plugin->proxy_password);
2212 GNUNET_free (plugin);
2213 GNUNET_free (api);
2214 return NULL;
2215}
2216
2217
2218/**
2219 * Configure plugin
2220 *
2221 * @param plugin the plugin handle
2222 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2223 */
2224static int
2225client_configure_plugin (struct HTTP_Client_Plugin *plugin)
2226{
2227 unsigned long long max_requests;
2228 char *proxy_type;
2229
2230 /* Optional parameters */
2231 if (GNUNET_OK !=
2232 GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2233 plugin->name,
2234 "MAX_CONNECTIONS",
2235 &max_requests))
2236 max_requests = 128;
2237 plugin->max_requests = max_requests;
2238
2239 LOG (GNUNET_ERROR_TYPE_DEBUG,
2240 _ ("Maximum number of requests is %u\n"),
2241 plugin->max_requests);
2242
2243 /* Read proxy configuration */
2244 if (GNUNET_OK ==
2245 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2246 plugin->name,
2247 "PROXY",
2248 &plugin->proxy_hostname))
2249 {
2250 LOG (GNUNET_ERROR_TYPE_DEBUG,
2251 "Found proxy host: `%s'\n",
2252 plugin->proxy_hostname);
2253 /* proxy username */
2254 if (GNUNET_OK ==
2255 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2256 plugin->name,
2257 "PROXY_USERNAME",
2258 &plugin->proxy_username))
2259 {
2260 LOG (GNUNET_ERROR_TYPE_DEBUG,
2261 "Found proxy username name: `%s'\n",
2262 plugin->proxy_username);
2263 }
2264
2265 /* proxy password */
2266 if (GNUNET_OK ==
2267 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2268 plugin->name,
2269 "PROXY_PASSWORD",
2270 &plugin->proxy_password))
2271 {
2272 LOG (GNUNET_ERROR_TYPE_DEBUG,
2273 "Found proxy password name: `%s'\n",
2274 plugin->proxy_password);
2275 }
2276
2277 /* proxy type */
2278 if (GNUNET_OK ==
2279 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2280 plugin->name,
2281 "PROXY_TYPE",
2282 &proxy_type))
2283 {
2284 GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
2285
2286 if (0 == strcmp (proxy_type, "HTTP"))
2287 plugin->proxytype = CURLPROXY_HTTP;
2288 else if (0 == strcmp (proxy_type, "SOCKS4"))
2289 plugin->proxytype = CURLPROXY_SOCKS4;
2290 else if (0 == strcmp (proxy_type, "SOCKS5"))
2291 plugin->proxytype = CURLPROXY_SOCKS5;
2292 else if (0 == strcmp (proxy_type, "SOCKS4A"))
2293 plugin->proxytype = CURLPROXY_SOCKS4A;
2294 else if (0 == strcmp (proxy_type, "SOCKS5_HOSTNAME "))
2295 plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME;
2296 else
2297 {
2298 LOG (GNUNET_ERROR_TYPE_ERROR,
2299 _ (
2300 "Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2301 proxy_type);
2302
2303 GNUNET_free (proxy_type);
2304 GNUNET_free (plugin->proxy_hostname);
2305 plugin->proxy_hostname = NULL;
2306 GNUNET_free (plugin->proxy_username);
2307 plugin->proxy_username = NULL;
2308 GNUNET_free (plugin->proxy_password);
2309 plugin->proxy_password = NULL;
2310
2311 return GNUNET_SYSERR;
2312 }
2313
2314 LOG (GNUNET_ERROR_TYPE_DEBUG,
2315 "Found proxy type: `%s'\n",
2316 proxy_type);
2317 }
2318
2319 /* proxy http tunneling */
2320 plugin->proxy_use_httpproxytunnel
2321 = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2322 plugin->name,
2323 "PROXY_HTTP_TUNNELING");
2324 if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2325 plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2326
2327 GNUNET_free (proxy_type);
2328 }
2329
2330 /* Should we emulate an XHR client for testing? */
2331 plugin->emulate_xhr
2332 = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2333 plugin->name,
2334 "EMULATE_XHR");
2335 return GNUNET_OK;
2336}
2337
2338
2339/**
2340 * Function to convert an address to a human-readable string.
2341 *
2342 * @param cls closure
2343 * @param addr address to convert
2344 * @param addrlen address length
2345 * @return res string if conversion was successful, NULL otherwise
2346 */
2347static const char *
2348http_client_plugin_address_to_string (void *cls,
2349 const void *addr,
2350 size_t addrlen)
2351{
2352 return http_common_plugin_address_to_string (PLUGIN_NAME,
2353 addr,
2354 addrlen);
2355}
2356
2357
2358/**
2359 * Function that will be called whenever the transport service wants to
2360 * notify the plugin that a session is still active and in use and
2361 * therefore the session timeout for this session has to be updated
2362 *
2363 * @param cls closure
2364 * @param peer which peer was the session for
2365 * @param session which session is being updated
2366 */
2367static void
2368http_client_plugin_update_session_timeout (void *cls,
2369 const struct
2370 GNUNET_PeerIdentity *peer,
2371 struct GNUNET_ATS_Session *session)
2372{
2373 client_reschedule_session_timeout (session);
2374}
2375
2376
2377/**
2378 * Function that will be called whenever the transport service wants to
2379 * notify the plugin that the inbound quota changed and that the plugin
2380 * should update it's delay for the next receive value
2381 *
2382 * @param cls closure
2383 * @param peer which peer was the session for
2384 * @param s which session is being updated
2385 * @param delay new delay to use for receiving
2386 */
2387static void
2388http_client_plugin_update_inbound_delay (void *cls,
2389 const struct GNUNET_PeerIdentity *peer,
2390 struct GNUNET_ATS_Session *s,
2391 struct GNUNET_TIME_Relative delay)
2392{
2393 s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2394 LOG (GNUNET_ERROR_TYPE_DEBUG,
2395 "New inbound delay %s\n",
2396 GNUNET_STRINGS_relative_time_to_string (delay,
2397 GNUNET_NO));
2398 if (s->recv_wakeup_task != NULL)
2399 {
2400 GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2401 s->recv_wakeup_task
2402 = GNUNET_SCHEDULER_add_delayed (delay,
2403 &client_wake_up,
2404 s);
2405 }
2406}
2407
2408
2409/**
2410 * Return information about the given session to the
2411 * monitor callback.
2412 *
2413 * @param cls the `struct Plugin` with the monitor callback (`sic`)
2414 * @param peer peer we send information about
2415 * @param value our `struct GNUNET_ATS_Session` to send information about
2416 * @return #GNUNET_OK (continue to iterate)
2417 */
2418static int
2419send_session_info_iter (void *cls,
2420 const struct GNUNET_PeerIdentity *peer,
2421 void *value)
2422{
2423 struct HTTP_Client_Plugin *plugin = cls;
2424 struct GNUNET_ATS_Session *session = value;
2425
2426 notify_session_monitor (plugin,
2427 session,
2428 GNUNET_TRANSPORT_SS_INIT);
2429 notify_session_monitor (plugin,
2430 session,
2431 GNUNET_TRANSPORT_SS_UP); /* FIXME: or handshake? */
2432 return GNUNET_OK;
2433}
2434
2435
2436/**
2437 * Begin monitoring sessions of a plugin. There can only
2438 * be one active monitor per plugin (i.e. if there are
2439 * multiple monitors, the transport service needs to
2440 * multiplex the generated events over all of them).
2441 *
2442 * @param cls closure of the plugin
2443 * @param sic callback to invoke, NULL to disable monitor;
2444 * plugin will being by iterating over all active
2445 * sessions immediately and then enter monitor mode
2446 * @param sic_cls closure for @a sic
2447 */
2448static void
2449http_client_plugin_setup_monitor (void *cls,
2450 GNUNET_TRANSPORT_SessionInfoCallback sic,
2451 void *sic_cls)
2452{
2453 struct HTTP_Client_Plugin *plugin = cls;
2454
2455 plugin->sic = sic;
2456 plugin->sic_cls = sic_cls;
2457 if (NULL != sic)
2458 {
2459 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2460 &send_session_info_iter,
2461 plugin);
2462 /* signal end of first iteration */
2463 sic (sic_cls, NULL, NULL);
2464 }
2465}
2466
2467
2468/**
2469 * Entry point for the plugin.
2470 */
2471void *
2472LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2473{
2474 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2475 struct GNUNET_TRANSPORT_PluginFunctions *api;
2476 struct HTTP_Client_Plugin *plugin;
2477
2478 if (NULL == env->receive)
2479 {
2480 /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2481 initialize the plugin or the API */
2482 api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2483 api->cls = NULL;
2484 api->address_to_string = &http_client_plugin_address_to_string;
2485 api->string_to_address = &http_common_plugin_string_to_address;
2486 api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2487 return api;
2488 }
2489
2490 plugin = GNUNET_new (struct HTTP_Client_Plugin);
2491 plugin->env = env;
2492 plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128,
2493 GNUNET_YES);
2494 api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2495 api->cls = plugin;
2496 api->send = &http_client_plugin_send;
2497 api->disconnect_session = &http_client_plugin_session_disconnect;
2498 api->query_keepalive_factor = &http_client_query_keepalive_factor;
2499 api->disconnect_peer = &http_client_plugin_peer_disconnect;
2500 api->check_address = &http_client_plugin_address_suggested;
2501 api->get_session = &http_client_plugin_get_session;
2502 api->address_to_string = &http_client_plugin_address_to_string;
2503 api->string_to_address = &http_common_plugin_string_to_address;
2504 api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2505 api->get_network = &http_client_plugin_get_network;
2506 api->get_network_for_address = &http_client_plugin_get_network_for_address;
2507 api->update_session_timeout = &http_client_plugin_update_session_timeout;
2508 api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2509 api->setup_monitor = &http_client_plugin_setup_monitor;
2510#if BUILD_HTTPS
2511 plugin->name = "transport-https_client";
2512 plugin->protocol = "https";
2513#else
2514 plugin->name = "transport-http_client";
2515 plugin->protocol = "http";
2516#endif
2517 plugin->last_tag = 1;
2518
2519 if (GNUNET_SYSERR == client_configure_plugin (plugin))
2520 {
2521 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2522 return NULL;
2523 }
2524
2525 /* Start client */
2526 if (GNUNET_SYSERR == client_start (plugin))
2527 {
2528 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2529 return NULL;
2530 }
2531 return api;
2532}
2533
2534
2535/* end of plugin_transport_http_client.c */