plugin_transport_http_client.c 71 KB

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