xmlrpc_curl_transport.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  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. #include "xmlrpc_config.h"
  9. #if defined(__BEOS__)
  10. /* Some helpful system header has char==bool, then bool.h does int==bool. */
  11. #define HAVE_BOOL 1
  12. #endif
  13. #include "bool.h"
  14. #include "mallocvar.h"
  15. #include "linklist.h"
  16. #include "casprintf.h"
  17. #include "xmlrpc.h"
  18. #include "xmlrpc_int.h"
  19. #include "xmlrpc_client.h"
  20. #include "xmlrpc_client_int.h"
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <errno.h>
  24. #if defined(HAVE_PTHREADS)
  25. # include "xmlrpc_pthreads.h"
  26. #endif
  27. #include <cmcurl/curl/curl.h>
  28. #include <cmcurl/curl/types.h>
  29. #include <cmcurl/curl/easy.h>
  30. #ifndef WIN32
  31. # include <unistd.h>
  32. #endif
  33. #if defined (WIN32) && defined(_DEBUG)
  34. # include <crtdbg.h>
  35. # define new DEBUG_NEW
  36. # define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
  37. # undef THIS_FILE
  38. static char THIS_FILE[] = __FILE__;
  39. #endif /*WIN32 && _DEBUG*/
  40. struct clientTransport {
  41. #if defined (HAVE_PTHREADS)
  42. pthread_mutex_t listLock;
  43. #endif
  44. struct list_head rpcList;
  45. /* List of all RPCs that exist for this transport. An RPC exists
  46. from the time the user requests it until the time the user
  47. acknowledges it is done.
  48. */
  49. };
  50. typedef struct {
  51. /* This is all stuff that really ought to be in the CURL object,
  52. but the Curl library is a little too simple for that. So we
  53. build a layer on top of it, and call it a "transaction," as
  54. distinct from the Curl "session" represented by the CURL object.
  55. */
  56. CURL * curlSessionP;
  57. /* Handle for Curl library session object */
  58. char curlError[CURL_ERROR_SIZE];
  59. /* Error message from Curl */
  60. struct curl_slist * headerList;
  61. /* The HTTP headers for the transaction */
  62. const char * serverUrl; /* malloc'ed - belongs to this object */
  63. } curlTransaction;
  64. typedef struct {
  65. struct list_head link; /* link in transport's list of RPCs */
  66. curlTransaction * curlTransactionP;
  67. /* The object which does the HTTP transaction, with no knowledge
  68. of XML-RPC or Xmlrpc-c.
  69. */
  70. xmlrpc_mem_block * responseXmlP;
  71. xmlrpc_bool threadExists;
  72. #if defined(HAVE_PTHREADS)
  73. pthread_t thread;
  74. #endif
  75. transport_asynch_complete complete;
  76. /* Routine to call to complete the RPC after it is complete HTTP-wise.
  77. NULL if none.
  78. */
  79. struct call_info * callInfoP;
  80. /* User's identifier for this RPC */
  81. } rpc;
  82. static size_t
  83. collect(void * const ptr,
  84. size_t const size,
  85. size_t const nmemb,
  86. FILE * const stream) {
  87. /*----------------------------------------------------------------------------
  88. This is a Curl output function. Curl calls this to deliver the
  89. HTTP response body. Curl thinks it's writing to a POSIX stream.
  90. -----------------------------------------------------------------------------*/
  91. xmlrpc_mem_block * const responseXmlP = (xmlrpc_mem_block *) stream;
  92. char * const buffer = ptr;
  93. size_t const length = nmemb * size;
  94. size_t retval;
  95. xmlrpc_env env;
  96. xmlrpc_env_init(&env);
  97. xmlrpc_mem_block_append(&env, responseXmlP, buffer, length);
  98. if (env.fault_occurred)
  99. retval = (size_t)-1;
  100. else
  101. /* Really? Shouldn't it be like fread() and return 'nmemb'? */
  102. retval = length;
  103. return retval;
  104. }
  105. static void
  106. initWindowsStuff(xmlrpc_env * const envP) {
  107. #if defined (WIN32)
  108. /* This is CRITICAL so that cURL-Win32 works properly! */
  109. WORD wVersionRequested;
  110. WSADATA wsaData;
  111. int err;
  112. wVersionRequested = MAKEWORD(1, 1);
  113. err = WSAStartup(wVersionRequested, &wsaData);
  114. (void)err;
  115. if (LOBYTE(wsaData.wVersion) != 1 ||
  116. HIBYTE( wsaData.wVersion) != 1) {
  117. /* Tell the user that we couldn't find a useable */
  118. /* winsock.dll. */
  119. WSACleanup();
  120. xmlrpc_env_set_fault_formatted(
  121. envP, XMLRPC_INTERNAL_ERROR, "Winsock reported that "
  122. "it does not implement the requested version 1.1.");
  123. }
  124. #else
  125. if (0)
  126. envP->fault_occurred = TRUE; /* Avoid unused parm warning */
  127. #endif
  128. }
  129. static void
  130. create(xmlrpc_env * const envP,
  131. int const flags ATTR_UNUSED,
  132. const char * const appname ATTR_UNUSED,
  133. const char * const appversion ATTR_UNUSED,
  134. struct clientTransport ** const handlePP) {
  135. /*----------------------------------------------------------------------------
  136. This does the 'create' operation for a Curl client transport.
  137. -----------------------------------------------------------------------------*/
  138. struct clientTransport * transportP;
  139. initWindowsStuff(envP);
  140. MALLOCVAR(transportP);
  141. if (transportP == NULL)
  142. xmlrpc_env_set_fault_formatted(
  143. envP, XMLRPC_INTERNAL_ERROR,
  144. "Unable to allocate transport descriptor.");
  145. else {
  146. #ifdef HAVE_PTHREADS
  147. pthread_mutex_init(&transportP->listLock, NULL);
  148. #endif
  149. list_make_empty(&transportP->rpcList);
  150. /*
  151. * This is the main global constructor for the app. Call this before
  152. * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
  153. * used, or havoc may be the result.
  154. */
  155. curl_global_init(CURL_GLOBAL_ALL);
  156. /* The above makes it look like Curl is not re-entrant. We should
  157. check into that.
  158. */
  159. *handlePP = transportP;
  160. }
  161. }
  162. static void
  163. termWindowStuff(void) {
  164. #if defined (WIN32)
  165. WSACleanup();
  166. #endif
  167. }
  168. static void
  169. destroy(struct clientTransport * const clientTransportP) {
  170. /*----------------------------------------------------------------------------
  171. This does the 'destroy' operation for a Libwww client transport.
  172. -----------------------------------------------------------------------------*/
  173. XMLRPC_ASSERT(clientTransportP != NULL);
  174. XMLRPC_ASSERT(list_is_empty(&clientTransportP->rpcList));
  175. #if defined(HAVE_PTHREADS)
  176. pthread_mutex_destroy(&clientTransportP->listLock);
  177. #endif
  178. curl_global_cleanup();
  179. termWindowStuff();
  180. free(clientTransportP);
  181. }
  182. static void
  183. createCurlHeaderList(xmlrpc_env * const envP,
  184. xmlrpc_server_info * const serverP,
  185. struct curl_slist ** const headerListP) {
  186. struct curl_slist * headerList;
  187. headerList = NULL; /* initial value */
  188. headerList = curl_slist_append(headerList, "Content-Type: text/xml");
  189. if (headerList == NULL)
  190. xmlrpc_env_set_fault_formatted(
  191. envP, XMLRPC_INTERNAL_ERROR,
  192. "Could not add header. curl_slist_append() failed.");
  193. else {
  194. /* Send an authorization header if we need one. */
  195. if (serverP->_http_basic_auth) {
  196. /* Make the authentication header "Authorization: " */
  197. /* we need 15 + length of _http_basic_auth + 1 for null */
  198. char * const authHeader =
  199. malloc(strlen(serverP->_http_basic_auth) + 15 + 1);
  200. if (authHeader == NULL)
  201. xmlrpc_env_set_fault_formatted(
  202. envP, XMLRPC_INTERNAL_ERROR,
  203. "Couldn't allocate memory for authentication header");
  204. else {
  205. memcpy(authHeader,"Authorization: ", 15);
  206. memcpy(authHeader + 15, serverP->_http_basic_auth,
  207. strlen(serverP->_http_basic_auth) + 1);
  208. headerList = curl_slist_append(headerList, authHeader);
  209. if (headerList == NULL)
  210. xmlrpc_env_set_fault_formatted(
  211. envP, XMLRPC_INTERNAL_ERROR,
  212. "Could not add authentication header. "
  213. "curl_slist_append() failed.");
  214. free(authHeader);
  215. }
  216. }
  217. if (envP->fault_occurred)
  218. free(headerList);
  219. }
  220. *headerListP = headerList;
  221. }
  222. static void
  223. setupCurlSession(xmlrpc_env * const envP,
  224. CURL * const curlSessionP,
  225. curlTransaction * const curlTransactionP,
  226. xmlrpc_mem_block * const callXmlP,
  227. xmlrpc_mem_block * const responseXmlP) {
  228. static char proxy[1024];
  229. static char proxyUser[1024];
  230. int proxy_type = 0;
  231. if ( getenv("HTTP_PROXY") )
  232. {
  233. proxy_type = 1;
  234. if (getenv("HTTP_PROXY_PORT") )
  235. {
  236. sprintf(proxy, "%s:%s", getenv("HTTP_PROXY"), getenv("HTTP_PROXY_PORT"));
  237. }
  238. else
  239. {
  240. sprintf(proxy, "%s", getenv("HTTP_PROXY"));
  241. }
  242. if ( getenv("HTTP_PROXY_TYPE") )
  243. {
  244. /* HTTP/SOCKS4/SOCKS5 */
  245. if ( strcmp(getenv("HTTP_PROXY_TYPE"), "HTTP") == 0 )
  246. {
  247. proxy_type = 1;
  248. }
  249. else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS4") == 0 )
  250. {
  251. proxy_type = 2;
  252. }
  253. else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS5") == 0 )
  254. {
  255. proxy_type = 3;
  256. }
  257. }
  258. if ( getenv("HTTP_PROXY_USER") )
  259. {
  260. strcpy(proxyUser, getenv("HTTP_PROXY_USER"));
  261. }
  262. if ( getenv("HTTP_PROXY_PASSWD") )
  263. {
  264. strcat(proxyUser, ":");
  265. strcat(proxyUser, getenv("HTTP_PROXY_PASSWD"));
  266. }
  267. }
  268. /* Using proxy */
  269. if ( proxy_type > 0 )
  270. {
  271. curl_easy_setopt(curlSessionP, CURLOPT_PROXY, proxy);
  272. switch (proxy_type)
  273. {
  274. case 2:
  275. curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  276. break;
  277. case 3:
  278. curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  279. break;
  280. default:
  281. curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  282. if (*proxyUser)
  283. {
  284. curl_easy_setopt(curlSessionP, CURLOPT_PROXYUSERPWD, proxyUser);
  285. }
  286. }
  287. }
  288. curl_easy_setopt(curlSessionP, CURLOPT_POST, 1 );
  289. curl_easy_setopt(curlSessionP, CURLOPT_URL, curlTransactionP->serverUrl);
  290. XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
  291. if (!envP->fault_occurred) {
  292. curl_easy_setopt(curlSessionP, CURLOPT_POSTFIELDS,
  293. XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP));
  294. curl_easy_setopt(curlSessionP, CURLOPT_FILE, responseXmlP);
  295. curl_easy_setopt(curlSessionP, CURLOPT_HEADER, 0 );
  296. curl_easy_setopt(curlSessionP, CURLOPT_WRITEFUNCTION, collect);
  297. curl_easy_setopt(curlSessionP, CURLOPT_ERRORBUFFER,
  298. curlTransactionP->curlError);
  299. curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 1);
  300. curl_easy_setopt(curlSessionP, CURLOPT_HTTPHEADER,
  301. curlTransactionP->headerList);
  302. }
  303. }
  304. static void
  305. createCurlTransaction(xmlrpc_env * const envP,
  306. xmlrpc_server_info * const serverP,
  307. xmlrpc_mem_block * const callXmlP,
  308. xmlrpc_mem_block * const responseXmlP,
  309. curlTransaction ** const curlTransactionPP) {
  310. curlTransaction * curlTransactionP;
  311. MALLOCVAR(curlTransactionP);
  312. if (curlTransactionP == NULL)
  313. xmlrpc_env_set_fault_formatted(
  314. envP, XMLRPC_INTERNAL_ERROR,
  315. "No memory to create Curl transaction.");
  316. else {
  317. CURL * const curlSessionP = curl_easy_init();
  318. if (curlSessionP == NULL)
  319. xmlrpc_env_set_fault_formatted(
  320. envP, XMLRPC_INTERNAL_ERROR,
  321. "Could not create Curl session. curl_easy_init() failed.");
  322. else {
  323. curlTransactionP->curlSessionP = curlSessionP;
  324. curlTransactionP->serverUrl = strdup(serverP->_server_url);
  325. if (curlTransactionP->serverUrl == NULL)
  326. xmlrpc_env_set_fault_formatted(
  327. envP, XMLRPC_INTERNAL_ERROR,
  328. "Out of memory to store server URL.");
  329. else {
  330. createCurlHeaderList(envP, serverP,
  331. &curlTransactionP->headerList);
  332. if (!envP->fault_occurred)
  333. setupCurlSession(envP, curlSessionP, curlTransactionP,
  334. callXmlP, responseXmlP);
  335. if (envP->fault_occurred)
  336. strfree(curlTransactionP->serverUrl);
  337. }
  338. if (envP->fault_occurred)
  339. curl_easy_cleanup(curlSessionP);
  340. }
  341. if (envP->fault_occurred)
  342. free(curlTransactionP);
  343. }
  344. *curlTransactionPP = curlTransactionP;
  345. }
  346. static void
  347. destroyCurlTransaction(curlTransaction * const curlTransactionP) {
  348. curl_slist_free_all(curlTransactionP->headerList);
  349. strfree(curlTransactionP->serverUrl);
  350. curl_easy_cleanup(curlTransactionP->curlSessionP);
  351. free(curlTransactionP);
  352. }
  353. static void
  354. performCurlTransaction(xmlrpc_env * const envP,
  355. curlTransaction * const curlTransactionP) {
  356. CURL * const curlSessionP = curlTransactionP->curlSessionP;
  357. CURLcode res;
  358. res = curl_easy_perform(curlSessionP);
  359. if (res != CURLE_OK)
  360. xmlrpc_env_set_fault_formatted(
  361. envP, XMLRPC_NETWORK_ERROR, "Curl failed to perform "
  362. "HTTP POST request. curl_easy_perform() says: %s (%d)",
  363. curlTransactionP->curlError, res);
  364. else {
  365. CURLcode crRes;
  366. long http_result;
  367. crRes = curl_easy_getinfo(curlSessionP, CURLINFO_HTTP_CODE,
  368. &http_result);
  369. if (crRes != CURLE_OK)
  370. xmlrpc_env_set_fault_formatted(
  371. envP, XMLRPC_INTERNAL_ERROR,
  372. "Curl performed the HTTP POST request, but was "
  373. "unable to say what the HTTP result code was. "
  374. "curl_easy_getinfo(CURLINFO_HTTP_CODE) says: %s",
  375. curlTransactionP->curlError);
  376. else {
  377. if (http_result != 200)
  378. xmlrpc_env_set_fault_formatted(
  379. envP, XMLRPC_NETWORK_ERROR, "HTTP response: %ld",
  380. http_result);
  381. }
  382. }
  383. }
  384. #if defined(HAVE_PTHREADS)
  385. static void
  386. doAsyncRpc2(void * const arg) {
  387. rpc * const rpcP = arg;
  388. xmlrpc_env env;
  389. xmlrpc_env_init(&env);
  390. performCurlTransaction(&env, rpcP->curlTransactionP);
  391. rpcP->complete(rpcP->callInfoP, rpcP->responseXmlP, env);
  392. xmlrpc_env_clean(&env);
  393. }
  394. #ifdef WIN32
  395. static unsigned __stdcall
  396. doAsyncRpc(void * arg) {
  397. doAsyncRpc2(arg);
  398. return 0;
  399. }
  400. #else
  401. static void *
  402. doAsyncRpc(void * arg) {
  403. doAsyncRpc2(arg);
  404. return NULL;
  405. }
  406. #endif
  407. static void
  408. createRpcThread(xmlrpc_env * const envP,
  409. rpc * const rpcP,
  410. pthread_t * const threadP) {
  411. int rc;
  412. rc = pthread_create(threadP, NULL, doAsyncRpc, rpcP);
  413. switch (rc) {
  414. case 0:
  415. break;
  416. case EAGAIN:
  417. xmlrpc_env_set_fault_formatted(
  418. envP, XMLRPC_INTERNAL_ERROR,
  419. "pthread_create() failed: System Resources exceeded.");
  420. break;
  421. case EINVAL:
  422. xmlrpc_env_set_fault_formatted(
  423. envP, XMLRPC_INTERNAL_ERROR,
  424. "pthread_create() failed: Param Error for attr.");
  425. break;
  426. case ENOMEM:
  427. xmlrpc_env_set_fault_formatted(
  428. envP, XMLRPC_INTERNAL_ERROR,
  429. "pthread_create() failed: No memory for new thread.");
  430. break;
  431. default:
  432. xmlrpc_env_set_fault_formatted(
  433. envP, XMLRPC_INTERNAL_ERROR,
  434. "pthread_create() failed: Unrecognized error code %d.", rc);
  435. break;
  436. }
  437. }
  438. #endif
  439. static void
  440. rpcCreate(xmlrpc_env * const envP,
  441. struct clientTransport * const clientTransportP,
  442. xmlrpc_server_info * const serverP,
  443. xmlrpc_mem_block * const callXmlP,
  444. xmlrpc_mem_block * const responseXmlP,
  445. transport_asynch_complete complete,
  446. struct call_info * const callInfoP,
  447. rpc ** const rpcPP) {
  448. rpc * rpcP;
  449. MALLOCVAR(rpcP);
  450. if (rpcP == NULL)
  451. xmlrpc_env_set_fault_formatted(
  452. envP, XMLRPC_INTERNAL_ERROR,
  453. "Couldn't allocate memory for rpc object");
  454. else {
  455. rpcP->callInfoP = callInfoP;
  456. rpcP->complete = complete;
  457. rpcP->responseXmlP = responseXmlP;
  458. rpcP->threadExists = FALSE;
  459. createCurlTransaction(envP, serverP,
  460. callXmlP, responseXmlP,
  461. &rpcP->curlTransactionP);
  462. if (!envP->fault_occurred) {
  463. if (complete) {
  464. #if defined(HAVE_PTHREADS)
  465. createRpcThread(envP, rpcP, &rpcP->thread);
  466. #else
  467. abort();
  468. #endif
  469. if (!envP->fault_occurred)
  470. rpcP->threadExists = TRUE;
  471. }
  472. if (!envP->fault_occurred) {
  473. list_init_header(&rpcP->link, rpcP);
  474. #if defined(HAVE_PTHREADS)
  475. pthread_mutex_lock(&clientTransportP->listLock);
  476. #endif
  477. list_add_head(&clientTransportP->rpcList, &rpcP->link);
  478. #if defined(HAVE_PTHREADS)
  479. pthread_mutex_unlock(&clientTransportP->listLock);
  480. #endif
  481. }
  482. if (envP->fault_occurred)
  483. destroyCurlTransaction(rpcP->curlTransactionP);
  484. }
  485. if (envP->fault_occurred)
  486. {
  487. free(rpcP);
  488. rpcP = 0; /* set this to null as it is used later on */
  489. }
  490. }
  491. *rpcPP = rpcP;
  492. }
  493. static void
  494. rpcDestroy(rpc * const rpcP) {
  495. XMLRPC_ASSERT_PTR_OK(rpcP);
  496. XMLRPC_ASSERT(!rpcP->threadExists);
  497. destroyCurlTransaction(rpcP->curlTransactionP);
  498. list_remove(&rpcP->link);
  499. free(rpcP);
  500. }
  501. static void
  502. sendRequest(xmlrpc_env * const envP,
  503. struct clientTransport * const clientTransportP,
  504. xmlrpc_server_info * const serverP,
  505. xmlrpc_mem_block * const callXmlP,
  506. transport_asynch_complete complete,
  507. struct call_info * const callInfoP) {
  508. /*----------------------------------------------------------------------------
  509. Initiate an XML-RPC rpc asynchronously. Don't wait for it to go to
  510. the server.
  511. Unless we return failure, we arrange to have complete() called when
  512. the rpc completes.
  513. This does the 'send_request' operation for a Curl client transport.
  514. -----------------------------------------------------------------------------*/
  515. rpc * rpcP;
  516. xmlrpc_mem_block * responseXmlP;
  517. responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
  518. if (!envP->fault_occurred) {
  519. rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
  520. complete, callInfoP,
  521. &rpcP);
  522. if (envP->fault_occurred)
  523. XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
  524. }
  525. /* The user's eventual finish_asynch call will destroy this RPC
  526. and response buffer
  527. */
  528. }
  529. static void *
  530. finishRpc(struct list_head * const headerP,
  531. void * const context ATTR_UNUSED) {
  532. rpc * const rpcP = headerP->itemP;
  533. if (rpcP->threadExists) {
  534. #if defined(HAVE_PTHREADS)
  535. void *status;
  536. int result;
  537. result = pthread_join(rpcP->thread, &status);
  538. (void)result;
  539. #else
  540. abort();
  541. #endif
  542. rpcP->threadExists = FALSE;
  543. }
  544. XMLRPC_MEMBLOCK_FREE(char, rpcP->responseXmlP);
  545. rpcDestroy(rpcP);
  546. return NULL;
  547. }
  548. static void
  549. finishAsynch(struct clientTransport * const clientTransportP ATTR_UNUSED,
  550. enum timeoutType const timeoutType ATTR_UNUSED,
  551. timeout_t const timeout ATTR_UNUSED) {
  552. /*----------------------------------------------------------------------------
  553. Wait for the threads of all outstanding RPCs to exit and destroy those
  554. RPCs.
  555. This does the 'finish_asynch' operation for a Curl client transport.
  556. -----------------------------------------------------------------------------*/
  557. /* We ignore any timeout request. Some day, we should figure out how
  558. to set an alarm and interrupt running threads.
  559. */
  560. #if defined(HAVE_PTHREADS)
  561. pthread_mutex_lock(&clientTransportP->listLock);
  562. #else
  563. abort();
  564. #endif
  565. list_foreach(&clientTransportP->rpcList, finishRpc, NULL);
  566. #if defined(HAVE_PTHREADS)
  567. pthread_mutex_unlock(&clientTransportP->listLock);
  568. #else
  569. abort();
  570. #endif
  571. }
  572. static void
  573. call(xmlrpc_env * const envP,
  574. struct clientTransport * const clientTransportP,
  575. xmlrpc_server_info * const serverP,
  576. xmlrpc_mem_block * const callXmlP,
  577. struct call_info * const callInfoP,
  578. xmlrpc_mem_block ** const responsePP) {
  579. xmlrpc_mem_block * responseXmlP;
  580. rpc * rpcP;
  581. XMLRPC_ASSERT_ENV_OK(envP);
  582. XMLRPC_ASSERT_PTR_OK(serverP);
  583. XMLRPC_ASSERT_PTR_OK(callXmlP);
  584. XMLRPC_ASSERT_PTR_OK(callInfoP);
  585. XMLRPC_ASSERT_PTR_OK(responsePP);
  586. responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
  587. if (!envP->fault_occurred) {
  588. rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
  589. NULL, NULL, &rpcP);
  590. if (!envP->fault_occurred) {
  591. performCurlTransaction(envP, rpcP->curlTransactionP);
  592. *responsePP = responseXmlP;
  593. rpcDestroy(rpcP);
  594. }
  595. if (envP->fault_occurred)
  596. XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
  597. }
  598. }
  599. struct clientTransportOps xmlrpc_curl_transport_ops = {
  600. &create,
  601. &destroy,
  602. &sendRequest,
  603. &call,
  604. &finishAsynch,
  605. };