plugin_transport_http_client.c 72 KB

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