xmlrpc_curl_transport.c 22 KB

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