xmlrpc_curl_transport.c 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. /*=============================================================================
  2. xmlrpc_curl_transport
  3. ===============================================================================
  4. Curl-based client transport for Xmlrpc-c
  5. By Bryan Henderson 04.12.10.
  6. Contributed to the public domain by its author.
  7. =============================================================================*/
  8. /*----------------------------------------------------------------------------
  9. Curl global variables:
  10. Curl maintains some minor information in process-global variables.
  11. One must call curl_global_init() to initialize them before calling
  12. any other Curl library function. This is not state information --
  13. it is constants. They just aren't the kind of constants that the
  14. library loader knows how to set, so there has to be this explicit
  15. call to set them up. The matching function curl_global_cleanup()
  16. returns resources these use (to wit, the constants live in
  17. malloc'ed storage and curl_global_cleanup() frees the storage).
  18. So our setup_global_const transport operation calls
  19. curl_global_init() and our teardown_global_const calls
  20. curl_global_cleanup().
  21. The Curl library is supposed to maintain a reference count for the
  22. global constants so that multiple modules using the library and
  23. independently calling curl_global_init() and curl_global_cleanup()
  24. are not a problem. But today, it just keeps a flag "I have been
  25. initialized" and the first call to curl_global_cleanup() destroys
  26. the constants for everybody. Therefore, the user of the Xmlrpc-c
  27. Curl client XML transport must make sure not to call
  28. teardownGlobalConstants until everything else in his program is
  29. done using the Curl library.
  30. Note that curl_global_init() is not threadsafe (with or without the
  31. reference count), therefore our setup_global_const is not, and must
  32. be called when no other thread in the process is running.
  33. Typically, one calls it right at the beginning of the program.
  34. There are actually two other classes of global variables in the
  35. Curl library, which we are ignoring: debug options and custom
  36. memory allocator function identities. Our code never changes these
  37. global variables from default. If something else in the user's
  38. program does, User is responsible for making sure it doesn't
  39. interfere with our use of the library.
  40. Note that when we say what the Curl library does, we're also
  41. talking about various other libraries Curl uses internally, and in
  42. fact much of what we're saying about global variables springs from
  43. such subordinate libraries as OpenSSL and Winsock.
  44. -----------------------------------------------------------------------------*/
  45. #define _XOPEN_SOURCE 600 /* Make sure strdup() is in <string.h> */
  46. #include "xmlrpc_config.h"
  47. #include <string.h>
  48. #include <stdlib.h>
  49. #include <errno.h>
  50. #include <assert.h>
  51. #include <limits.h>
  52. #if HAVE_SYS_SELECT_H
  53. #include <sys/select.h>
  54. #endif
  55. #include <signal.h>
  56. #ifdef WIN32
  57. #include "curllink.h"
  58. #endif
  59. #include "bool.h"
  60. #include "girmath.h"
  61. #include "mallocvar.h"
  62. #include "linklist.h"
  63. #include "girstring.h"
  64. #include "pthreadx.h"
  65. #include "xmlrpc-c/util.h"
  66. #include "xmlrpc-c/string_int.h"
  67. #include "xmlrpc-c/select_int.h"
  68. #include "xmlrpc-c/client_int.h"
  69. #include "xmlrpc-c/transport.h"
  70. #include "xmlrpc-c/time_int.h"
  71. #include <curl/curl.h>
  72. #include <curl/types.h>
  73. #include <curl/easy.h>
  74. #include <curl/multi.h>
  75. #include "lock.h"
  76. #include "lock_pthread.h"
  77. #include "curltransaction.h"
  78. #include "curlmulti.h"
  79. #include "curlversion.h"
  80. #if MSVCRT
  81. #if defined(_DEBUG)
  82. # include <crtdbg.h>
  83. # define new DEBUG_NEW
  84. # define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
  85. # undef THIS_FILE
  86. static char THIS_FILE[] = __FILE__;
  87. #endif
  88. #endif
  89. typedef struct rpc rpc;
  90. static int
  91. timeDiffMillisec(xmlrpc_timespec const minuend,
  92. xmlrpc_timespec const subtractor) {
  93. unsigned int const million = 1000000;
  94. return (minuend.tv_sec - subtractor.tv_sec) * 1000 +
  95. (minuend.tv_nsec - subtractor.tv_nsec + million/2) / million;
  96. }
  97. static bool
  98. timeIsAfter(xmlrpc_timespec const comparator,
  99. xmlrpc_timespec const comparand) {
  100. if (comparator.tv_sec > comparand.tv_sec)
  101. return true;
  102. else if (comparator.tv_sec < comparand.tv_sec)
  103. return false;
  104. else {
  105. /* Seconds are equal */
  106. if (comparator.tv_nsec > comparand.tv_nsec)
  107. return true;
  108. else
  109. return false;
  110. }
  111. }
  112. static void
  113. addMilliseconds(xmlrpc_timespec const addend,
  114. unsigned int const adder,
  115. xmlrpc_timespec * const sumP) {
  116. unsigned int const million = 1000000;
  117. unsigned int const billion = 1000000000;
  118. xmlrpc_timespec sum;
  119. sum.tv_sec = addend.tv_sec + adder / 1000;
  120. sum.tv_nsec = addend.tv_nsec + (adder % 1000) * million;
  121. if ((uint32_t)sum.tv_nsec >= billion) {
  122. sum.tv_sec += 1;
  123. sum.tv_nsec -= billion;
  124. }
  125. *sumP = sum;
  126. }
  127. struct xmlrpc_client_transport {
  128. CURL * syncCurlSessionP;
  129. /* Handle for a Curl library session object that we use for
  130. all synchronous RPCs. An async RPC has one of its own,
  131. and consequently does not share things such as persistent
  132. connections and cookies with any other RPC.
  133. */
  134. lock * syncCurlSessionLockP;
  135. /* Hold this lock while accessing or using *syncCurlSessionP.
  136. You're using the session from the time you set any
  137. attributes in it or start a transaction with it until any
  138. transaction has finished and you've lost interest in any
  139. attributes of the session.
  140. */
  141. curlMulti * syncCurlMultiP;
  142. /* The Curl multi manager that this transport uses to execute
  143. Curl transactions for RPCs requested via the synchronous
  144. interface. The fact that there is never more than one such
  145. transaction going at a time might make you wonder why a
  146. "multi" manager is needed. The reason is that it is the only
  147. interface in libcurl that gives us the flexibility to execute
  148. the transaction with proper interruptibility. The only Curl
  149. transaction ever attached to this multi manager is
  150. 'syncCurlSessionP'.
  151. This is constant (the handle, not the object).
  152. */
  153. curlMulti * asyncCurlMultiP;
  154. /* The Curl multi manager that this transport uses to execute
  155. Curl transactions for RPCs requested via the asynchronous
  156. interface. Note that there may be multiple such Curl transactions
  157. simultaneously and one can't wait for a particular one to finish;
  158. the collection of asynchronous RPCs are an indivisible mass.
  159. This is constant (the handle, not the object).
  160. */
  161. bool dontAdvertise;
  162. /* Don't identify to the server the XML-RPC engine we are using. If
  163. false, include a User-Agent HTTP header in all requests that
  164. identifies the Xmlrpc-c and Curl libraries.
  165. See also 'userAgent'.
  166. This is constant.
  167. */
  168. const char * userAgent;
  169. /* Information to include in a User-Agent HTTP header, reflecting
  170. facilities outside of Xmlrpc-c.
  171. Null means none.
  172. The full User-Agent header value is this information (if
  173. 'userAgent' is non-null) followed by identification of Xmlrpc-c
  174. and Curl (if 'dontAdvertise' is false). If 'userAgent' is null
  175. and 'dontAdvertise' is true, we put no User-Agent header at all
  176. in the request.
  177. This is constant.
  178. */
  179. struct curlSetup curlSetupStuff;
  180. /* This is constant */
  181. int * interruptP;
  182. /* Pointer to a value that user sets to nonzero to indicate he wants
  183. the transport to give up on whatever it is doing and return ASAP.
  184. NULL means none -- transport never gives up.
  185. This is constant.
  186. */
  187. };
  188. struct rpc {
  189. struct xmlrpc_client_transport * transportP;
  190. /* The client XML transport that transports this RPC */
  191. curlTransaction * curlTransactionP;
  192. /* The object which does the HTTP transaction, with no knowledge
  193. of XML-RPC or Xmlrpc-c.
  194. */
  195. CURL * curlSessionP;
  196. /* The Curl session to use for the Curl transaction to perform
  197. the RPC.
  198. */
  199. xmlrpc_mem_block * responseXmlP;
  200. /* Where the response XML for this RPC should go or has gone. */
  201. xmlrpc_transport_asynch_complete complete;
  202. /* Routine to call to complete the RPC after it is complete HTTP-wise.
  203. NULL if none.
  204. */
  205. xmlrpc_transport_progress progress;
  206. /* Routine to call periodically to report the progress of transporting
  207. the call and response. NULL if none.
  208. */
  209. struct xmlrpc_call_info * callInfoP;
  210. /* User's identifier for this RPC */
  211. };
  212. static void
  213. lockSyncCurlSession(struct xmlrpc_client_transport * const transportP) {
  214. transportP->syncCurlSessionLockP->acquire(
  215. transportP->syncCurlSessionLockP);
  216. }
  217. static void
  218. unlockSyncCurlSession(struct xmlrpc_client_transport * const transportP) {
  219. transportP->syncCurlSessionLockP->release(
  220. transportP->syncCurlSessionLockP);
  221. }
  222. static void
  223. initWindowsStuff(xmlrpc_env * const envP ATTR_UNUSED) {
  224. #if defined (WIN32)
  225. /* This is CRITICAL so that cURL-Win32 works properly! */
  226. /* So this commenter says, but I wonder why. libcurl should do the
  227. required WSAStartup() itself, and it looks to me like it does.
  228. -Bryan 06.01.01
  229. */
  230. WORD wVersionRequested;
  231. WSADATA wsaData;
  232. int err;
  233. wVersionRequested = MAKEWORD(1, 1);
  234. err = WSAStartup(wVersionRequested, &wsaData);
  235. if (err)
  236. xmlrpc_env_set_fault_formatted(
  237. envP, XMLRPC_INTERNAL_ERROR,
  238. "Winsock startup failed. WSAStartup returned rc %d", err);
  239. else {
  240. if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
  241. /* Tell the user that we couldn't find a useable */
  242. /* winsock.dll. */
  243. xmlrpc_env_set_fault_formatted(
  244. envP, XMLRPC_INTERNAL_ERROR, "Winsock reported that "
  245. "it does not implement the requested version 1.1.");
  246. }
  247. if (envP->fault_occurred)
  248. WSACleanup();
  249. }
  250. #endif
  251. }
  252. static void
  253. termWindowsStuff(void) {
  254. #if defined (WIN32)
  255. WSACleanup();
  256. #endif
  257. }
  258. static bool
  259. curlHasNosignal(void) {
  260. bool retval;
  261. #if HAVE_CURL_NOSIGNAL
  262. curl_version_info_data * const curlInfoP =
  263. curl_version_info(CURLVERSION_NOW);
  264. retval = (curlInfoP->version_num >= 0x070A00); /* 7.10.0 */
  265. #else
  266. retval = false;
  267. #endif
  268. return retval;
  269. }
  270. static xmlrpc_timespec
  271. pselectTimeout(xmlrpc_timeoutType const timeoutType,
  272. xmlrpc_timespec const timeoutDt) {
  273. /*----------------------------------------------------------------------------
  274. Return the value that should be used in the select() call to wait for
  275. there to be work for the Curl multi manager to do, given that the user
  276. wants to timeout according to 'timeoutType' and 'timeoutDt'.
  277. -----------------------------------------------------------------------------*/
  278. unsigned int const million = 1000000;
  279. unsigned int selectTimeoutMillisec;
  280. xmlrpc_timespec retval;
  281. /* We assume there is work to do at least every 3 seconds, because
  282. the Curl multi manager often has retries and other scheduled work
  283. that doesn't involve file handles on which we can select().
  284. */
  285. switch (timeoutType) {
  286. case timeout_no:
  287. selectTimeoutMillisec = 3000;
  288. break;
  289. case timeout_yes: {
  290. xmlrpc_timespec nowTime;
  291. int timeLeft;
  292. xmlrpc_gettimeofday(&nowTime);
  293. timeLeft = timeDiffMillisec(timeoutDt, nowTime);
  294. selectTimeoutMillisec = MIN(3000, MAX(0, timeLeft));
  295. } break;
  296. }
  297. retval.tv_sec = selectTimeoutMillisec / 1000;
  298. retval.tv_nsec = (uint32_t)((selectTimeoutMillisec % 1000) * million);
  299. return retval;
  300. }
  301. static void
  302. processCurlMessages(xmlrpc_env * const envP,
  303. curlMulti * const curlMultiP) {
  304. bool endOfMessages;
  305. endOfMessages = false; /* initial assumption */
  306. while (!endOfMessages && !envP->fault_occurred) {
  307. CURLMsg curlMsg;
  308. curlMulti_getMessage(curlMultiP, &endOfMessages, &curlMsg);
  309. if (!endOfMessages) {
  310. if (curlMsg.msg == CURLMSG_DONE) {
  311. curlTransaction * curlTransactionP;
  312. curl_easy_getinfo(curlMsg.easy_handle, CURLINFO_PRIVATE,
  313. (void *)&curlTransactionP);
  314. curlTransaction_finish(envP,
  315. curlTransactionP, curlMsg.data.result);
  316. }
  317. }
  318. }
  319. }
  320. static void
  321. waitForWork(xmlrpc_env * const envP,
  322. curlMulti * const curlMultiP,
  323. xmlrpc_timeoutType const timeoutType,
  324. xmlrpc_timespec const deadline,
  325. sigset_t * const sigmaskP) {
  326. /*----------------------------------------------------------------------------
  327. Wait for the Curl multi manager to have work to do, time to run out,
  328. or a signal to be received (and caught), whichever comes first.
  329. Update the Curl multi manager's file descriptor sets to indicate what
  330. work we found for it to do.
  331. Wait under signal mask *sigmaskP. The point of this is that Caller
  332. can make sure that arrival of a signal of a certain class
  333. interrupts our wait, even if the signal arrives shortly before we
  334. begin waiting. Caller blocks that signal class, then checks
  335. whether a signal of that class has already been received. If not,
  336. he calls us with *sigmaskP indicating that class NOT blocked.
  337. Thus, if a signal of that class arrived any time after Caller
  338. checked, we will return immediately or when the signal arrives,
  339. whichever is sooner. Note that we can provide this service only
  340. because pselect() has the same atomic unblock/wait feature.
  341. If sigmaskP is NULL, wait under whatever the current signal mask
  342. is.
  343. -----------------------------------------------------------------------------*/
  344. fd_set readFdSet;
  345. fd_set writeFdSet;
  346. fd_set exceptFdSet;
  347. int maxFd;
  348. curlMulti_fdset(envP, curlMultiP,
  349. &readFdSet, &writeFdSet, &exceptFdSet, &maxFd);
  350. if (!envP->fault_occurred) {
  351. if (maxFd == -1) {
  352. /* There are no Curl file descriptors on which to wait.
  353. So either there's work to do right now or all transactions
  354. are already complete.
  355. */
  356. } else {
  357. xmlrpc_timespec const pselectTimeoutArg =
  358. pselectTimeout(timeoutType, deadline);
  359. int rc;
  360. rc = xmlrpc_pselect(maxFd+1, &readFdSet, &writeFdSet, &exceptFdSet,
  361. &pselectTimeoutArg, sigmaskP);
  362. if (rc < 0 && errno != EINTR)
  363. xmlrpc_faultf(envP, "Impossible failure of pselect() "
  364. "with errno %d (%s)",
  365. errno, strerror(errno));
  366. else {
  367. /* Believe it or not, the Curl multi manager needs the
  368. results of our pselect(). So hand them over:
  369. */
  370. curlMulti_updateFdSet(curlMultiP,
  371. readFdSet, writeFdSet, exceptFdSet);
  372. }
  373. }
  374. }
  375. }
  376. static void
  377. waitForWorkInt(xmlrpc_env * const envP,
  378. curlMulti * const curlMultiP,
  379. xmlrpc_timeoutType const timeoutType,
  380. xmlrpc_timespec const deadline,
  381. int * const interruptP) {
  382. /*----------------------------------------------------------------------------
  383. Same as waitForWork(), except we guarantee to return if a signal handler
  384. sets or has set *interruptP, whereas waitForWork() can miss a signal
  385. that happens before or just after it starts.
  386. We mess with global state -- the signal mask -- so we might mess up
  387. a multithreaded program. Therefore, don't call this if
  388. waitForWork() will suffice.
  389. -----------------------------------------------------------------------------*/
  390. sigset_t callerBlockSet;
  391. #ifdef WIN32
  392. waitForWork(envP, curlMultiP, timeoutType, deadline, &callerBlockSet);
  393. #else
  394. sigset_t allSignals;
  395. assert(interruptP != NULL);
  396. sigfillset(&allSignals);
  397. sigprocmask(SIG_BLOCK, &allSignals, &callerBlockSet);
  398. if (*interruptP == 0)
  399. waitForWork(envP, curlMultiP, timeoutType, deadline, &callerBlockSet);
  400. sigprocmask(SIG_SETMASK, &callerBlockSet, NULL);
  401. #endif
  402. }
  403. static void
  404. doCurlWork(xmlrpc_env * const envP,
  405. curlMulti * const curlMultiP,
  406. bool * const transStillRunningP) {
  407. /*----------------------------------------------------------------------------
  408. Do whatever work is ready to be done by the Curl multi manager
  409. identified by 'curlMultiP'. This typically is transferring data on
  410. an HTTP connection because the server is ready.
  411. For each transaction for which the multi manager finishes all the
  412. required work, complete the transaction by calling its
  413. "finish" routine.
  414. Return *transStillRunningP false if this work completes all of the
  415. manager's transactions so that there is no reason to call us ever
  416. again.
  417. -----------------------------------------------------------------------------*/
  418. bool immediateWorkToDo;
  419. int runningHandles;
  420. immediateWorkToDo = true; /* initial assumption */
  421. while (immediateWorkToDo && !envP->fault_occurred) {
  422. curlMulti_perform(envP, curlMultiP,
  423. &immediateWorkToDo, &runningHandles);
  424. }
  425. /* We either did all the work that's ready to do or hit an error. */
  426. if (!envP->fault_occurred) {
  427. /* The work we did may have resulted in asynchronous messages
  428. (asynchronous to the thing they refer to, not to us, of course).
  429. In particular the message "Curl transaction has completed".
  430. So we process those now.
  431. */
  432. processCurlMessages(envP, curlMultiP);
  433. *transStillRunningP = runningHandles > 0;
  434. }
  435. }
  436. static void
  437. finishCurlMulti(xmlrpc_env * const envP,
  438. curlMulti * const curlMultiP,
  439. xmlrpc_timeoutType const timeoutType,
  440. xmlrpc_timespec const deadline,
  441. int * const interruptP) {
  442. /*----------------------------------------------------------------------------
  443. Prosecute all the Curl transactions under the control of
  444. *curlMultiP. E.g. send data if server is ready to take it, get
  445. data if server has sent some, wind up the transaction if it is
  446. done.
  447. Don't return until all the Curl transactions are done or we time out.
  448. The *interruptP flag alone will not interrupt us. We will wait in
  449. spite of it for all Curl transactions to complete. *interruptP
  450. just gives us a hint that the Curl transactions are being
  451. interrupted, so we know there is work to do for them. (The way it
  452. works is Caller sets up a "progress" function that checks the same
  453. interrupt flag and reports "kill me." When we see the interrupt
  454. flag, we call that progress function and get the message).
  455. -----------------------------------------------------------------------------*/
  456. bool rpcStillRunning;
  457. bool timedOut;
  458. rpcStillRunning = true; /* initial assumption */
  459. timedOut = false;
  460. while (rpcStillRunning && !timedOut && !envP->fault_occurred) {
  461. if (interruptP) {
  462. waitForWorkInt(envP, curlMultiP, timeoutType, deadline,
  463. interruptP);
  464. } else
  465. waitForWork(envP, curlMultiP, timeoutType, deadline, NULL);
  466. if (!envP->fault_occurred) {
  467. xmlrpc_timespec nowTime;
  468. /* doCurlWork() (among other things) finds Curl
  469. transactions that user wants to abort and finishes
  470. them.
  471. */
  472. doCurlWork(envP, curlMultiP, &rpcStillRunning);
  473. xmlrpc_gettimeofday(&nowTime);
  474. timedOut = (timeoutType == timeout_yes &&
  475. timeIsAfter(nowTime, deadline));
  476. }
  477. }
  478. }
  479. static void
  480. getTimeoutParm(xmlrpc_env * const envP,
  481. const struct xmlrpc_curl_xportparms * const curlXportParmsP,
  482. size_t const parmSize,
  483. unsigned int * const timeoutP) {
  484. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(timeout))
  485. *timeoutP = 0;
  486. else {
  487. if (curlHasNosignal()) {
  488. /* libcurl takes a 'long' in milliseconds for the timeout value */
  489. if ((unsigned)(long)(curlXportParmsP->timeout) !=
  490. curlXportParmsP->timeout)
  491. xmlrpc_faultf(envP, "Timeout value %u is too large.",
  492. curlXportParmsP->timeout);
  493. else
  494. *timeoutP = curlXportParmsP->timeout;
  495. } else
  496. xmlrpc_faultf(envP, "You cannot specify a 'timeout' parameter "
  497. "because the Curl library is too old and is not "
  498. "capable of doing timeouts except by using "
  499. "signals. You need at least Curl 7.10");
  500. }
  501. }
  502. static void
  503. setVerbose(bool * const verboseP) {
  504. const char * const xmlrpcTraceCurl = getenv("XMLRPC_TRACE_CURL");
  505. if (xmlrpcTraceCurl)
  506. *verboseP = true;
  507. else
  508. *verboseP = false;
  509. }
  510. static void
  511. getXportParms(xmlrpc_env * const envP,
  512. const struct xmlrpc_curl_xportparms * const curlXportParmsP,
  513. size_t const parmSize,
  514. struct xmlrpc_client_transport * const transportP) {
  515. /*----------------------------------------------------------------------------
  516. Get the parameters out of *curlXportParmsP and update *transportP
  517. to reflect them.
  518. *curlXportParmsP is a 'parmSize' bytes long prefix of
  519. struct xmlrpc_curl_xportparms.
  520. curlXportParmsP is something the user created. It's designed to be
  521. friendly to the user, not to this program, and is encumbered by
  522. lots of backward compatibility constraints. In particular, the
  523. user may have coded and/or compiled it at a time that struct
  524. xmlrpc_curl_xportparms was smaller than it is now!
  525. Also, the user might have specified something invalid.
  526. So that's why we don't simply attach a copy of *curlXportParmsP to
  527. *transportP.
  528. To the extent that *curlXportParmsP is too small to contain a parameter,
  529. we return the default value for that parameter.
  530. Special case: curlXportParmsP == NULL means there is no input at all.
  531. In that case, we return default values for everything.
  532. -----------------------------------------------------------------------------*/
  533. struct curlSetup * const curlSetupP = &transportP->curlSetupStuff;
  534. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(user_agent))
  535. transportP->userAgent = NULL;
  536. else if (curlXportParmsP->user_agent == NULL)
  537. transportP->userAgent = NULL;
  538. else
  539. transportP->userAgent = strdup(curlXportParmsP->user_agent);
  540. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(dont_advertise))
  541. transportP->dontAdvertise = false;
  542. else
  543. transportP->dontAdvertise = curlXportParmsP->dont_advertise;
  544. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(network_interface))
  545. curlSetupP->networkInterface = NULL;
  546. else if (curlXportParmsP->network_interface == NULL)
  547. curlSetupP->networkInterface = NULL;
  548. else
  549. curlSetupP->networkInterface =
  550. strdup(curlXportParmsP->network_interface);
  551. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(no_ssl_verifypeer))
  552. curlSetupP->sslVerifyPeer = true;
  553. else
  554. curlSetupP->sslVerifyPeer = !curlXportParmsP->no_ssl_verifypeer;
  555. if (!curlXportParmsP ||
  556. parmSize < XMLRPC_CXPSIZE(no_ssl_verifyhost))
  557. curlSetupP->sslVerifyHost = true;
  558. else
  559. curlSetupP->sslVerifyHost = !curlXportParmsP->no_ssl_verifyhost;
  560. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(ssl_cert))
  561. curlSetupP->sslCert = NULL;
  562. else if (curlXportParmsP->ssl_cert == NULL)
  563. curlSetupP->sslCert = NULL;
  564. else
  565. curlSetupP->sslCert = strdup(curlXportParmsP->ssl_cert);
  566. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslcerttype))
  567. curlSetupP->sslCertType = NULL;
  568. else if (curlXportParmsP->sslcerttype == NULL)
  569. curlSetupP->sslCertType = NULL;
  570. else
  571. curlSetupP->sslCertType = strdup(curlXportParmsP->sslcerttype);
  572. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslcertpasswd))
  573. curlSetupP->sslCertPasswd = NULL;
  574. else if (curlXportParmsP->sslcertpasswd == NULL)
  575. curlSetupP->sslCertPasswd = NULL;
  576. else
  577. curlSetupP->sslCertPasswd = strdup(curlXportParmsP->sslcertpasswd);
  578. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslkey))
  579. curlSetupP->sslKey = NULL;
  580. else if (curlXportParmsP->sslkey == NULL)
  581. curlSetupP->sslKey = NULL;
  582. else
  583. curlSetupP->sslKey = strdup(curlXportParmsP->sslkey);
  584. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslkeytype))
  585. curlSetupP->sslKeyType = NULL;
  586. else if (curlXportParmsP->sslkeytype == NULL)
  587. curlSetupP->sslKeyType = NULL;
  588. else
  589. curlSetupP->sslKeyType = strdup(curlXportParmsP->sslkeytype);
  590. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslkeypasswd))
  591. curlSetupP->sslKeyPasswd = NULL;
  592. else if (curlXportParmsP->sslkeypasswd == NULL)
  593. curlSetupP->sslKeyPasswd = NULL;
  594. else
  595. curlSetupP->sslKeyPasswd = strdup(curlXportParmsP->sslkeypasswd);
  596. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslengine))
  597. curlSetupP->sslEngine = NULL;
  598. else if (curlXportParmsP->sslengine == NULL)
  599. curlSetupP->sslEngine = NULL;
  600. else
  601. curlSetupP->sslEngine = strdup(curlXportParmsP->sslengine);
  602. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslengine_default))
  603. curlSetupP->sslEngineDefault = false;
  604. else
  605. curlSetupP->sslEngineDefault = !!curlXportParmsP->sslengine_default;
  606. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(sslversion))
  607. curlSetupP->sslVersion = XMLRPC_SSLVERSION_DEFAULT;
  608. else
  609. curlSetupP->sslVersion = curlXportParmsP->sslversion;
  610. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(cainfo))
  611. curlSetupP->caInfo = NULL;
  612. else if (curlXportParmsP->cainfo == NULL)
  613. curlSetupP->caInfo = NULL;
  614. else
  615. curlSetupP->caInfo = strdup(curlXportParmsP->cainfo);
  616. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(capath))
  617. curlSetupP->caPath = NULL;
  618. else if (curlXportParmsP->capath == NULL)
  619. curlSetupP->caPath = NULL;
  620. else
  621. curlSetupP->caPath = strdup(curlXportParmsP->capath);
  622. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(randomfile))
  623. curlSetupP->randomFile = NULL;
  624. else if (curlXportParmsP->randomfile == NULL)
  625. curlSetupP->randomFile = NULL;
  626. else
  627. curlSetupP->randomFile = strdup(curlXportParmsP->randomfile);
  628. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(egdsocket))
  629. curlSetupP->egdSocket = NULL;
  630. else if (curlXportParmsP->egdsocket == NULL)
  631. curlSetupP->egdSocket = NULL;
  632. else
  633. curlSetupP->egdSocket = strdup(curlXportParmsP->egdsocket);
  634. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(ssl_cipher_list))
  635. curlSetupP->sslCipherList = NULL;
  636. else if (curlXportParmsP->ssl_cipher_list == NULL)
  637. curlSetupP->sslCipherList = NULL;
  638. else
  639. curlSetupP->sslCipherList = strdup(curlXportParmsP->ssl_cipher_list);
  640. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(proxy))
  641. curlSetupP->proxy = NULL;
  642. else if (curlXportParmsP->proxy == NULL)
  643. curlSetupP->proxy = NULL;
  644. else
  645. curlSetupP->proxy = strdup(curlXportParmsP->proxy);
  646. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(proxy_port))
  647. curlSetupP->proxyPort = 8080;
  648. else
  649. curlSetupP->proxyPort = curlXportParmsP->proxy_port;
  650. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(proxy_auth))
  651. curlSetupP->proxyAuth = CURLAUTH_BASIC;
  652. else
  653. curlSetupP->proxyAuth = curlXportParmsP->proxy_auth;
  654. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(proxy_userpwd))
  655. curlSetupP->proxyUserPwd = NULL;
  656. else if (curlXportParmsP->proxy_userpwd == NULL)
  657. curlSetupP->proxyUserPwd = NULL;
  658. else
  659. curlSetupP->proxyUserPwd = strdup(curlXportParmsP->proxy_userpwd);
  660. if (!curlXportParmsP || parmSize < XMLRPC_CXPSIZE(proxy_type))
  661. curlSetupP->proxyType = CURLPROXY_HTTP;
  662. else
  663. curlSetupP->proxyType = curlXportParmsP->proxy_type;
  664. getTimeoutParm(envP, curlXportParmsP, parmSize, &curlSetupP->timeout);
  665. }
  666. static void
  667. freeXportParms(const struct xmlrpc_client_transport * const transportP) {
  668. const struct curlSetup * const curlSetupP = &transportP->curlSetupStuff;
  669. if (curlSetupP->sslCipherList)
  670. xmlrpc_strfree(curlSetupP->sslCipherList);
  671. if (curlSetupP->egdSocket)
  672. xmlrpc_strfree(curlSetupP->egdSocket);
  673. if (curlSetupP->randomFile)
  674. xmlrpc_strfree(curlSetupP->randomFile);
  675. if (curlSetupP->caPath)
  676. xmlrpc_strfree(curlSetupP->caPath);
  677. if (curlSetupP->caInfo)
  678. xmlrpc_strfree(curlSetupP->caInfo);
  679. if (curlSetupP->sslEngine)
  680. xmlrpc_strfree(curlSetupP->sslEngine);
  681. if (curlSetupP->sslKeyPasswd)
  682. xmlrpc_strfree(curlSetupP->sslKeyPasswd);
  683. if (curlSetupP->sslKeyType)
  684. xmlrpc_strfree(curlSetupP->sslKeyType);
  685. if (curlSetupP->sslKey)
  686. xmlrpc_strfree(curlSetupP->sslKey);
  687. if (curlSetupP->sslCertPasswd)
  688. xmlrpc_strfree(curlSetupP->sslCertPasswd);
  689. if (curlSetupP->sslCertType)
  690. xmlrpc_strfree(curlSetupP->sslCertType);
  691. if (curlSetupP->sslCert)
  692. xmlrpc_strfree(curlSetupP->sslCert);
  693. if (curlSetupP->networkInterface)
  694. xmlrpc_strfree(curlSetupP->networkInterface);
  695. if (transportP->userAgent)
  696. xmlrpc_strfree(transportP->userAgent);
  697. if (curlSetupP->proxy)
  698. xmlrpc_strfree(curlSetupP->proxy);
  699. if (curlSetupP->proxyUserPwd)
  700. xmlrpc_strfree(curlSetupP->proxyUserPwd);
  701. }
  702. static void
  703. createSyncCurlSession(xmlrpc_env * const envP,
  704. CURL ** const curlSessionPP) {
  705. /*----------------------------------------------------------------------------
  706. Create a Curl session to be used for multiple serial transactions.
  707. The Curl session we create is not complete -- it still has to be
  708. further set up for each particular transaction.
  709. We can't set up anything here that changes from one transaction to the
  710. next.
  711. We don't bother setting up anything that has to be set up for an
  712. asynchronous transaction because code that is common between synchronous
  713. and asynchronous transactions takes care of that anyway.
  714. That leaves things, such as cookies, that don't exist for
  715. asynchronous transactions, and are common to multiple serial
  716. synchronous transactions.
  717. -----------------------------------------------------------------------------*/
  718. CURL * const curlSessionP = curl_easy_init();
  719. if (curlSessionP == NULL)
  720. xmlrpc_faultf(envP, "Could not create Curl session. "
  721. "curl_easy_init() failed.");
  722. else {
  723. /* The following is a trick. CURLOPT_COOKIEFILE is the name
  724. of the file containing the initial cookies for the Curl
  725. session. But setting it is also what turns on the cookie
  726. function itself, whereby the Curl library accepts and
  727. stores cookies from the server and sends them back on
  728. future requests. We don't have a file of initial cookies, but
  729. we want to turn on cookie function, so we set the option to
  730. something we know does not validly name a file. Curl will
  731. ignore the error and just start up cookie function with no
  732. initial cookies.
  733. */
  734. curl_easy_setopt(curlSessionP, CURLOPT_COOKIEFILE, "");
  735. *curlSessionPP = curlSessionP;
  736. }
  737. }
  738. static void
  739. destroySyncCurlSession(CURL * const curlSessionP) {
  740. curl_easy_cleanup(curlSessionP);
  741. }
  742. static void
  743. makeSyncCurlSession(xmlrpc_env * const envP,
  744. struct xmlrpc_client_transport * const transportP) {
  745. transportP->syncCurlSessionLockP = curlLock_create_pthread();
  746. if (transportP->syncCurlSessionLockP == NULL)
  747. xmlrpc_faultf(envP, "Unable to create lock for "
  748. "synchronous Curl session.");
  749. else {
  750. createSyncCurlSession(envP, &transportP->syncCurlSessionP);
  751. if (!envP->fault_occurred) {
  752. /* We'll need a multi manager to actually execute this session: */
  753. transportP->syncCurlMultiP = curlMulti_create();
  754. if (transportP->syncCurlMultiP == NULL)
  755. xmlrpc_faultf(envP, "Unable to create Curl multi manager for "
  756. "synchronous RPCs");
  757. if (envP->fault_occurred)
  758. destroySyncCurlSession(transportP->syncCurlSessionP);
  759. }
  760. if (envP->fault_occurred)
  761. transportP->syncCurlSessionLockP->destroy(
  762. transportP->syncCurlSessionLockP);
  763. }
  764. }
  765. static void
  766. unmakeSyncCurlSession(struct xmlrpc_client_transport * const transportP) {
  767. curlMulti_destroy(transportP->syncCurlMultiP);
  768. destroySyncCurlSession(transportP->syncCurlSessionP);
  769. transportP->syncCurlSessionLockP->destroy(
  770. transportP->syncCurlSessionLockP);
  771. }
  772. static void
  773. create(xmlrpc_env * const envP,
  774. int const flags ATTR_UNUSED,
  775. const char * const appname ATTR_UNUSED,
  776. const char * const appversion ATTR_UNUSED,
  777. const void * const transportparmsP,
  778. size_t const parm_size,
  779. struct xmlrpc_client_transport ** const handlePP) {
  780. /*----------------------------------------------------------------------------
  781. This does the 'create' operation for a Curl client transport.
  782. -----------------------------------------------------------------------------*/
  783. const struct xmlrpc_curl_xportparms * const curlXportParmsP =
  784. transportparmsP;
  785. struct xmlrpc_client_transport * transportP;
  786. MALLOCVAR(transportP);
  787. if (transportP == NULL)
  788. xmlrpc_faultf(envP, "Unable to allocate transport descriptor.");
  789. else {
  790. setVerbose(&transportP->curlSetupStuff.verbose);
  791. transportP->interruptP = NULL;
  792. transportP->asyncCurlMultiP = curlMulti_create();
  793. if (transportP->asyncCurlMultiP == NULL)
  794. xmlrpc_faultf(envP, "Unable to create Curl multi manager for "
  795. "asynchronous RPCs");
  796. else {
  797. getXportParms(envP, curlXportParmsP, parm_size, transportP);
  798. if (!envP->fault_occurred) {
  799. makeSyncCurlSession(envP, transportP);
  800. if (envP->fault_occurred)
  801. freeXportParms(transportP);
  802. }
  803. if (envP->fault_occurred)
  804. curlMulti_destroy(transportP->asyncCurlMultiP);
  805. }
  806. if (envP->fault_occurred)
  807. free(transportP);
  808. }
  809. *handlePP = transportP;
  810. }
  811. static void
  812. setInterrupt(struct xmlrpc_client_transport * const clientTransportP,
  813. int * const interruptP) {
  814. clientTransportP->interruptP = interruptP;
  815. }
  816. static void
  817. assertNoOutstandingCurlWork(curlMulti * const curlMultiP) {
  818. xmlrpc_env env;
  819. bool immediateWorkToDo;
  820. int runningHandles;
  821. xmlrpc_env_init(&env);
  822. curlMulti_perform(&env, curlMultiP, &immediateWorkToDo, &runningHandles);
  823. /* We know the above was a no-op, since we're asserting that there
  824. is no outstanding work.
  825. */
  826. XMLRPC_ASSERT(!env.fault_occurred);
  827. XMLRPC_ASSERT(!immediateWorkToDo);
  828. XMLRPC_ASSERT(runningHandles == 0);
  829. xmlrpc_env_clean(&env);
  830. }
  831. static void
  832. destroy(struct xmlrpc_client_transport * const clientTransportP) {
  833. /*----------------------------------------------------------------------------
  834. This does the 'destroy' operation for a Curl client transport.
  835. An RPC is a reference to a client XML transport, so you may not
  836. destroy a transport while RPCs are running. To ensure no
  837. asynchronous RPCs are running, you must successfully execute the
  838. transport 'finishAsync' method, with no interruptions or timeouts
  839. allowed. To speed that up, you can set the transport's interrupt
  840. flag to 1 first, which will make all outstanding RPCs fail
  841. immediately.
  842. -----------------------------------------------------------------------------*/
  843. XMLRPC_ASSERT(clientTransportP != NULL);
  844. assertNoOutstandingCurlWork(clientTransportP->asyncCurlMultiP);
  845. /* We know this is true because a condition of destroying the
  846. transport is that there be no outstanding asynchronous RPCs.
  847. */
  848. assertNoOutstandingCurlWork(clientTransportP->syncCurlMultiP);
  849. /* This is because a condition of destroying the transport is
  850. that no transport method be running. The only way a
  851. synchronous RPC can be in progress is for the 'perform' method
  852. to be running.
  853. */
  854. unmakeSyncCurlSession(clientTransportP);
  855. curlMulti_destroy(clientTransportP->asyncCurlMultiP);
  856. freeXportParms(clientTransportP);
  857. free(clientTransportP);
  858. }
  859. static void
  860. performCurlTransaction(xmlrpc_env * const envP,
  861. curlTransaction * const curlTransactionP,
  862. curlMulti * const curlMultiP,
  863. int * const interruptP) {
  864. curlMulti_addHandle(envP, curlMultiP,
  865. curlTransaction_curlSession(curlTransactionP));
  866. /* Failure here just means something screwy in the multi manager;
  867. Above does not even begin to perform the HTTP transaction
  868. */
  869. if (!envP->fault_occurred) {
  870. xmlrpc_timespec const dummy = {0,0};
  871. finishCurlMulti(envP, curlMultiP, timeout_no, dummy, interruptP);
  872. /* Failure here just means something screwy in the multi
  873. manager; any failure of the HTTP transaction would have been
  874. recorded in *curlTransactionP.
  875. */
  876. if (!envP->fault_occurred) {
  877. /* Curl session completed OK. But did HTTP transaction
  878. work?
  879. */
  880. curlTransaction_getError(curlTransactionP, envP);
  881. }
  882. /* If the CURL transaction is still going, removing the handle
  883. here aborts it. At least it's supposed to. From what I've
  884. seen in the Curl code in 2007, I don't think it does. I
  885. couldn't get Curl maintainers interested in the problem,
  886. except to say, "If you're right, there's a bug."
  887. */
  888. curlMulti_removeHandle(curlMultiP,
  889. curlTransaction_curlSession(curlTransactionP));
  890. }
  891. }
  892. static void
  893. startRpc(xmlrpc_env * const envP,
  894. rpc * const rpcP) {
  895. curlMulti_addHandle(envP,
  896. rpcP->transportP->asyncCurlMultiP,
  897. curlTransaction_curlSession(rpcP->curlTransactionP));
  898. }
  899. static curlt_finishFn finishRpcCurlTransaction;
  900. static curlt_progressFn curlTransactionProgress;
  901. static void
  902. createRpc(xmlrpc_env * const envP,
  903. struct xmlrpc_client_transport * const clientTransportP,
  904. CURL * const curlSessionP,
  905. const xmlrpc_server_info * const serverP,
  906. xmlrpc_mem_block * const callXmlP,
  907. xmlrpc_mem_block * const responseXmlP,
  908. xmlrpc_transport_asynch_complete complete,
  909. xmlrpc_transport_progress progress,
  910. struct xmlrpc_call_info * const callInfoP,
  911. rpc ** const rpcPP) {
  912. rpc * rpcP;
  913. MALLOCVAR(rpcP);
  914. if (rpcP == NULL)
  915. xmlrpc_faultf(envP, "Couldn't allocate memory for rpc object");
  916. else {
  917. rpcP->transportP = clientTransportP;
  918. rpcP->curlSessionP = curlSessionP;
  919. rpcP->callInfoP = callInfoP;
  920. rpcP->complete = complete;
  921. rpcP->progress = progress;
  922. rpcP->responseXmlP = responseXmlP;
  923. curlTransaction_create(envP,
  924. curlSessionP,
  925. serverP,
  926. callXmlP, responseXmlP,
  927. clientTransportP->dontAdvertise,
  928. clientTransportP->userAgent,
  929. &clientTransportP->curlSetupStuff,
  930. rpcP,
  931. complete ? &finishRpcCurlTransaction : NULL,
  932. progress ? &curlTransactionProgress : NULL,
  933. &rpcP->curlTransactionP);
  934. if (!envP->fault_occurred) {
  935. if (envP->fault_occurred)
  936. curlTransaction_destroy(rpcP->curlTransactionP);
  937. }
  938. if (envP->fault_occurred)
  939. free(rpcP);
  940. }
  941. *rpcPP = rpcP;
  942. }
  943. static void
  944. destroyRpc(rpc * const rpcP) {
  945. XMLRPC_ASSERT_PTR_OK(rpcP);
  946. curlTransaction_destroy(rpcP->curlTransactionP);
  947. free(rpcP);
  948. }
  949. static void
  950. performRpc(xmlrpc_env * const envP,
  951. rpc * const rpcP,
  952. curlMulti * const curlMultiP,
  953. int * const interruptP) {
  954. performCurlTransaction(envP, rpcP->curlTransactionP, curlMultiP,
  955. interruptP);
  956. }
  957. static curlt_finishFn finishRpcCurlTransaction;
  958. static void
  959. finishRpcCurlTransaction(xmlrpc_env * const envP ATTR_UNUSED,
  960. void * const userContextP) {
  961. /*----------------------------------------------------------------------------
  962. Handle the event that a Curl transaction for an asynchronous RPC has
  963. completed on the Curl session identified by 'curlSessionP'.
  964. Tell the requester of the RPC the results.
  965. Remove the Curl session from its Curl multi manager and destroy the
  966. Curl session, the XML response buffer, the Curl transaction, and the RPC.
  967. -----------------------------------------------------------------------------*/
  968. rpc * const rpcP = userContextP;
  969. curlTransaction * const curlTransactionP = rpcP->curlTransactionP;
  970. struct xmlrpc_client_transport * const transportP = rpcP->transportP;
  971. curlMulti_removeHandle(transportP->asyncCurlMultiP,
  972. curlTransaction_curlSession(curlTransactionP));
  973. {
  974. xmlrpc_env env;
  975. xmlrpc_env_init(&env);
  976. curlTransaction_getError(curlTransactionP, &env);
  977. rpcP->complete(rpcP->callInfoP, rpcP->responseXmlP, env);
  978. xmlrpc_env_clean(&env);
  979. }
  980. curl_easy_cleanup(rpcP->curlSessionP);
  981. XMLRPC_MEMBLOCK_FREE(char, rpcP->responseXmlP);
  982. destroyRpc(rpcP);
  983. }
  984. static curlt_progressFn curlTransactionProgress;
  985. static void
  986. curlTransactionProgress(void * const context,
  987. double const dlTotal,
  988. double const dlNow,
  989. double const ulTotal,
  990. double const ulNow,
  991. bool * const abortP) {
  992. /*----------------------------------------------------------------------------
  993. This is equivalent to a Curl "progress function" (the curlTransaction
  994. object just passes through the call from libcurl).
  995. The curlTransaction calls this once a second telling us how much
  996. data has transferred. If the transport user has set up a progress
  997. function, we call that with this progress information. That
  998. function might e.g. display a progress bar.
  999. Additionally, the curlTransaction gives us the opportunity to tell it
  1000. to abort the transaction, which we do if the user has set his
  1001. "interrupt" flag (which he registered with the transport when he
  1002. created it).
  1003. -----------------------------------------------------------------------------*/
  1004. rpc * const rpcP = context;
  1005. struct xmlrpc_client_transport * const transportP = rpcP->transportP;
  1006. struct xmlrpc_progress_data progressData;
  1007. assert(rpcP);
  1008. assert(transportP);
  1009. assert(rpcP->progress);
  1010. progressData.response.total = dlTotal;
  1011. progressData.response.now = dlNow;
  1012. progressData.call.total = ulTotal;
  1013. progressData.call.now = ulNow;
  1014. rpcP->progress(rpcP->callInfoP, progressData);
  1015. if (transportP->interruptP)
  1016. *abortP = *transportP->interruptP;
  1017. else
  1018. *abortP = false;
  1019. }
  1020. static void
  1021. sendRequest(xmlrpc_env * const envP,
  1022. struct xmlrpc_client_transport * const clientTransportP,
  1023. const xmlrpc_server_info * const serverP,
  1024. xmlrpc_mem_block * const callXmlP,
  1025. xmlrpc_transport_asynch_complete complete,
  1026. xmlrpc_transport_progress progress,
  1027. struct xmlrpc_call_info * const callInfoP) {
  1028. /*----------------------------------------------------------------------------
  1029. Initiate an XML-RPC rpc asynchronously. Don't wait for it to go to
  1030. the server.
  1031. Unless we return failure, we arrange to have complete() called when
  1032. the rpc completes.
  1033. This does the 'send_request' operation for a Curl client transport.
  1034. -----------------------------------------------------------------------------*/
  1035. rpc * rpcP;
  1036. xmlrpc_mem_block * responseXmlP;
  1037. responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
  1038. if (!envP->fault_occurred) {
  1039. CURL * const curlSessionP = curl_easy_init();
  1040. if (curlSessionP == NULL)
  1041. xmlrpc_faultf(envP, "Could not create Curl session. "
  1042. "curl_easy_init() failed.");
  1043. else {
  1044. createRpc(envP, clientTransportP, curlSessionP, serverP,
  1045. callXmlP, responseXmlP, complete, progress, callInfoP,
  1046. &rpcP);
  1047. if (!envP->fault_occurred) {
  1048. startRpc(envP, rpcP);
  1049. if (envP->fault_occurred)
  1050. destroyRpc(rpcP);
  1051. }
  1052. if (envP->fault_occurred)
  1053. curl_easy_cleanup(curlSessionP);
  1054. }
  1055. if (envP->fault_occurred)
  1056. XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
  1057. }
  1058. /* If we're returning success, the user's eventual finish_asynch
  1059. call will destroy this RPC, Curl session, and response buffer
  1060. and remove the Curl session from the Curl multi manager.
  1061. (If we're returning failure, we didn't create any of those).
  1062. */
  1063. }
  1064. static void
  1065. finishAsynch(
  1066. struct xmlrpc_client_transport * const clientTransportP,
  1067. xmlrpc_timeoutType const timeoutType,
  1068. xmlrpc_timeout const timeout) {
  1069. /*----------------------------------------------------------------------------
  1070. Wait for the Curl multi manager to finish the Curl transactions for
  1071. all outstanding RPCs and destroy those RPCs.
  1072. But give up if a) too much time passes as defined by 'timeoutType'
  1073. and 'timeout'; or b) the transport client requests interruption
  1074. (i.e. the transport's interrupt flag becomes nonzero). Normally, a
  1075. signal must get our attention for us to notice the interrupt flag.
  1076. This does the 'finish_asynch' operation for a Curl client transport.
  1077. It would be cool to replace this with something analogous to the
  1078. Curl asynchronous interface: Have something like curl_multi_fdset()
  1079. that returns a bunch of file descriptors on which the user can wait
  1080. (along with possibly other file descriptors of his own) and
  1081. something like curl_multi_perform() to finish whatever RPCs are
  1082. ready to finish at that moment. The implementation would be little
  1083. more than wrapping curl_multi_fdset() and curl_multi_perform().
  1084. Note that the user can call this multiple times, due to timeouts,
  1085. but must eventually call it once with no timeout so he
  1086. knows that all the RPCs are finished. Either that or terminate the
  1087. process so it doesn't matter if RPCs are still going.
  1088. -----------------------------------------------------------------------------*/
  1089. xmlrpc_env env;
  1090. xmlrpc_timespec waitTimeoutTime;
  1091. /* The datetime after which we should quit waiting */
  1092. xmlrpc_env_init(&env);
  1093. if (timeoutType == timeout_yes) {
  1094. xmlrpc_timespec waitStartTime;
  1095. xmlrpc_gettimeofday(&waitStartTime);
  1096. addMilliseconds(waitStartTime, timeout, &waitTimeoutTime);
  1097. }
  1098. finishCurlMulti(&env, clientTransportP->asyncCurlMultiP,
  1099. timeoutType, waitTimeoutTime,
  1100. clientTransportP->interruptP);
  1101. /* If the above fails, it is catastrophic, because it means there is
  1102. no way to complete outstanding Curl transactions and RPCs, and
  1103. no way to release their resources.
  1104. We should at least expand this interface some day to push the
  1105. problem back up to the user, but for now we just do this Hail Mary
  1106. response.
  1107. Note that a failure of finish_curlMulti() does not mean that
  1108. a session completed with an error or an RPC completed with an
  1109. error. Those things are reported up through the user's
  1110. xmlrpc_transport_asynch_complete routine. A failure here is
  1111. something that stopped us from calling that.
  1112. Note that a timeout causes a successful completion,
  1113. but without finishing all the RPCs!
  1114. */
  1115. if (env.fault_occurred)
  1116. fprintf(stderr, "finishAsync() failed. Xmlrpc-c Curl transport "
  1117. "is now in an unknown state and may not be able to "
  1118. "continue functioning. Specifics of the failure: %s\n",
  1119. env.fault_string);
  1120. xmlrpc_env_clean(&env);
  1121. }
  1122. static void
  1123. call(xmlrpc_env * const envP,
  1124. struct xmlrpc_client_transport * const clientTransportP,
  1125. const xmlrpc_server_info * const serverP,
  1126. xmlrpc_mem_block * const callXmlP,
  1127. xmlrpc_mem_block ** const responseXmlPP) {
  1128. xmlrpc_mem_block * responseXmlP;
  1129. rpc * rpcP;
  1130. XMLRPC_ASSERT_ENV_OK(envP);
  1131. XMLRPC_ASSERT_PTR_OK(serverP);
  1132. XMLRPC_ASSERT_PTR_OK(callXmlP);
  1133. XMLRPC_ASSERT_PTR_OK(responseXmlPP);
  1134. responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
  1135. if (!envP->fault_occurred) {
  1136. /* Only one RPC at a time can use a Curl session, so we have to
  1137. hold the lock as long as our RPC exists.
  1138. */
  1139. lockSyncCurlSession(clientTransportP);
  1140. createRpc(envP, clientTransportP, clientTransportP->syncCurlSessionP,
  1141. serverP,
  1142. callXmlP, responseXmlP,
  1143. NULL, NULL, NULL,
  1144. &rpcP);
  1145. if (!envP->fault_occurred) {
  1146. performRpc(envP, rpcP, clientTransportP->syncCurlMultiP,
  1147. clientTransportP->interruptP);
  1148. *responseXmlPP = responseXmlP;
  1149. destroyRpc(rpcP);
  1150. }
  1151. unlockSyncCurlSession(clientTransportP);
  1152. if (envP->fault_occurred)
  1153. XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
  1154. }
  1155. }
  1156. static void
  1157. setupGlobalConstants(xmlrpc_env * const envP) {
  1158. /*----------------------------------------------------------------------------
  1159. See longwinded discussion of the global constant issue at the top of
  1160. this file.
  1161. -----------------------------------------------------------------------------*/
  1162. initWindowsStuff(envP);
  1163. if (!envP->fault_occurred) {
  1164. CURLcode rc;
  1165. rc = curl_global_init(CURL_GLOBAL_ALL);
  1166. if (rc != CURLE_OK)
  1167. xmlrpc_faultf(envP, "curl_global_init() failed with code %d", rc);
  1168. }
  1169. }
  1170. static void
  1171. teardownGlobalConstants(void) {
  1172. /*----------------------------------------------------------------------------
  1173. See longwinded discussionof the global constant issue at the top of
  1174. this file.
  1175. -----------------------------------------------------------------------------*/
  1176. curl_global_cleanup();
  1177. termWindowsStuff();
  1178. }
  1179. struct xmlrpc_client_transport_ops xmlrpc_curl_transport_ops = {
  1180. &setupGlobalConstants,
  1181. &teardownGlobalConstants,
  1182. &create,
  1183. &destroy,
  1184. &sendRequest,
  1185. &call,
  1186. &finishAsynch,
  1187. &setInterrupt,
  1188. };