curlmulti.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*=============================================================================
  2. curlMulti
  3. ===============================================================================
  4. This is an extension to Curl's CURLM object. The extensions are:
  5. 1) It has a lock so multiple threads can use it simultaneously.
  6. 2) Its "select" file descriptor vectors are self-contained. CURLM
  7. requires the user to maintain them separately.
  8. =============================================================================*/
  9. #define _XOPEN_SOURCE 600 /* Make sure strdup() is in <string.h> */
  10. #include "xmlrpc_config.h"
  11. #include <stdlib.h>
  12. #if HAVE_SYS_SELECT_H
  13. #include <sys/select.h>
  14. #endif
  15. #include <curl/curl.h>
  16. #include <curl/types.h>
  17. #include <curl/easy.h>
  18. #include <curl/multi.h>
  19. #include "mallocvar.h"
  20. #include "xmlrpc-c/util.h"
  21. #include "xmlrpc-c/string_int.h"
  22. #include "curlversion.h"
  23. #include "lock.h"
  24. #include "lock_pthread.h"
  25. #include "curlmulti.h"
  26. static void
  27. interpretCurlMultiError(const char ** const descriptionP,
  28. CURLMcode const code) {
  29. #if HAVE_CURL_STRERROR
  30. *descriptionP = strdup(curl_multi_strerror(code));
  31. #else
  32. xmlrpc_asprintf(descriptionP, "Curl error code (CURLMcode) %d", code);
  33. #endif
  34. }
  35. struct curlMulti {
  36. CURLM * curlMultiP;
  37. lock * lockP;
  38. /* Hold this lock while accessing or using *curlMultiP. You're
  39. using the multi manager whenever you're calling a Curl
  40. library multi manager function.
  41. */
  42. /* The following file descriptor sets are an integral part of the
  43. CURLM object; Our curlMulti_fdset() routine binds them to the
  44. CURLM object, and said object expects us to use them in a very
  45. specific way, including doing a select() on them. It is very,
  46. very messy.
  47. */
  48. fd_set readFdSet;
  49. fd_set writeFdSet;
  50. fd_set exceptFdSet;
  51. };
  52. curlMulti *
  53. curlMulti_create(void) {
  54. curlMulti * retval;
  55. curlMulti * curlMultiP;
  56. MALLOCVAR(curlMultiP);
  57. if (curlMultiP == NULL)
  58. retval = NULL;
  59. else {
  60. curlMultiP->lockP = curlLock_create_pthread();
  61. if (curlMultiP->lockP == NULL)
  62. retval = NULL;
  63. else {
  64. curlMultiP->curlMultiP = curl_multi_init();
  65. if (curlMultiP->curlMultiP == NULL)
  66. retval = NULL;
  67. else
  68. retval = curlMultiP;
  69. if (retval == NULL)
  70. curlMultiP->lockP->destroy(curlMultiP->lockP);
  71. }
  72. if (retval == NULL)
  73. free(curlMultiP);
  74. }
  75. return retval;
  76. }
  77. void
  78. curlMulti_destroy(curlMulti * const curlMultiP) {
  79. curl_multi_cleanup(curlMultiP->curlMultiP);
  80. curlMultiP->lockP->destroy(curlMultiP->lockP);
  81. free(curlMultiP);
  82. }
  83. void
  84. curlMulti_perform(xmlrpc_env * const envP,
  85. curlMulti * const curlMultiP,
  86. bool * const immediateWorkToDoP,
  87. int * const runningHandlesP) {
  88. /*----------------------------------------------------------------------------
  89. Do whatever work is ready to be done under the control of multi
  90. manager 'curlMultiP'. E.g. if HTTP response data has recently arrived
  91. from the network, process it as an HTTP response.
  92. Iff this results in some work being finished from our point of view,
  93. return *immediateWorkToDoP. (Caller can query the multi manager for
  94. messages and find out what it is).
  95. Return as *runningHandlesP the number of Curl easy handles under the
  96. multi manager's control that are still running -- yet to finish.
  97. -----------------------------------------------------------------------------*/
  98. CURLMcode rc;
  99. curlMultiP->lockP->acquire(curlMultiP->lockP);
  100. rc = curl_multi_perform(curlMultiP->curlMultiP, runningHandlesP);
  101. curlMultiP->lockP->release(curlMultiP->lockP);
  102. if (rc == CURLM_CALL_MULTI_PERFORM) {
  103. *immediateWorkToDoP = true;
  104. } else {
  105. *immediateWorkToDoP = false;
  106. if (rc != CURLM_OK) {
  107. const char * reason;
  108. interpretCurlMultiError(&reason, rc);
  109. xmlrpc_faultf(envP, "Impossible failure of curl_multi_perform(): "
  110. "%s", reason);
  111. xmlrpc_strfree(reason);
  112. }
  113. }
  114. }
  115. void
  116. curlMulti_addHandle(xmlrpc_env * const envP,
  117. curlMulti * const curlMultiP,
  118. CURL * const curlSessionP) {
  119. CURLMcode rc;
  120. curlMultiP->lockP->acquire(curlMultiP->lockP);
  121. rc = curl_multi_add_handle(curlMultiP->curlMultiP, curlSessionP);
  122. curlMultiP->lockP->release(curlMultiP->lockP);
  123. if (rc != CURLM_OK) {
  124. const char * reason;
  125. interpretCurlMultiError(&reason, rc);
  126. xmlrpc_faultf(envP, "Could not add Curl session to the "
  127. "curl multi manager. curl_multi_add_handle() "
  128. "failed: %s", reason);
  129. xmlrpc_strfree(reason);
  130. }
  131. }
  132. void
  133. curlMulti_removeHandle(curlMulti * const curlMultiP,
  134. CURL * const curlSessionP) {
  135. curlMultiP->lockP->acquire(curlMultiP->lockP);
  136. curl_multi_remove_handle(curlMultiP->curlMultiP, curlSessionP);
  137. curlMultiP->lockP->release(curlMultiP->lockP);
  138. }
  139. void
  140. curlMulti_getMessage(curlMulti * const curlMultiP,
  141. bool * const endOfMessagesP,
  142. CURLMsg * const curlMsgP) {
  143. /*----------------------------------------------------------------------------
  144. Get the next message from the queue of things the Curl multi manager
  145. wants to say to us.
  146. Return the message as *curlMsgP.
  147. Iff there are no messages in the queue, return *endOfMessagesP == true.
  148. -----------------------------------------------------------------------------*/
  149. int remainingMsgCount;
  150. CURLMsg * privateCurlMsgP;
  151. /* Note that this is a pointer into the multi manager's memory,
  152. so we have to use it under lock.
  153. */
  154. curlMultiP->lockP->acquire(curlMultiP->lockP);
  155. privateCurlMsgP = curl_multi_info_read(curlMultiP->curlMultiP,
  156. &remainingMsgCount);
  157. if (privateCurlMsgP == NULL)
  158. *endOfMessagesP = true;
  159. else {
  160. *endOfMessagesP = false;
  161. *curlMsgP = *privateCurlMsgP;
  162. }
  163. curlMultiP->lockP->release(curlMultiP->lockP);
  164. }
  165. void
  166. curlMulti_fdset(xmlrpc_env * const envP,
  167. curlMulti * const curlMultiP,
  168. fd_set * const readFdSetP,
  169. fd_set * const writeFdSetP,
  170. fd_set * const exceptFdSetP,
  171. int * const maxFdP) {
  172. /*----------------------------------------------------------------------------
  173. Set the CURLM object's file descriptor sets to those in the
  174. curlMulti object, update those file descriptor sets with the
  175. current needs of the multi manager, and return the resulting values
  176. of the file descriptor sets.
  177. This is a bizarre operation, but is necessary because of the nonmodular
  178. way in which the Curl multi interface works with respect to waiting
  179. for work with select().
  180. -----------------------------------------------------------------------------*/
  181. CURLMcode rc;
  182. curlMultiP->lockP->acquire(curlMultiP->lockP);
  183. /* curl_multi_fdset() doesn't _set_ the fdsets. It adds to existing
  184. ones (so you can easily do a select() on other fds and Curl
  185. fds at the same time). So we have to clear first:
  186. */
  187. FD_ZERO(&curlMultiP->readFdSet);
  188. FD_ZERO(&curlMultiP->writeFdSet);
  189. FD_ZERO(&curlMultiP->exceptFdSet);
  190. /* WARNING: curl_multi_fdset() doesn't just update the fdsets pointed
  191. to by its arguments. It makes the CURLM object remember those
  192. pointers and refer back to them later! In fact, curl_multi_perform
  193. expects its caller to have done a select() on those masks. No,
  194. really. The man page even admits it.
  195. Inspection of the Libcurl code in March 2007 indicates that
  196. this isn't actually true -- curl_multi_fdset() updates your
  197. fdset and doesn't remember the pointer at all. I.e. it's just
  198. what you would expect. The man pages still says it's as
  199. described above. My guess is that Libcurl was fixed at some
  200. time and the man page not updated. In any case, we have to
  201. work with old Libcurl if at all possible, so we still maintain
  202. these fdsets as if they belong to the CURLM object.
  203. */
  204. rc = curl_multi_fdset(curlMultiP->curlMultiP,
  205. &curlMultiP->readFdSet,
  206. &curlMultiP->writeFdSet,
  207. &curlMultiP->exceptFdSet,
  208. maxFdP);
  209. *readFdSetP = curlMultiP->readFdSet;
  210. *writeFdSetP = curlMultiP->writeFdSet;
  211. *exceptFdSetP = curlMultiP->exceptFdSet;
  212. curlMultiP->lockP->release(curlMultiP->lockP);
  213. if (rc != CURLM_OK) {
  214. const char * reason;
  215. interpretCurlMultiError(&reason, rc);
  216. xmlrpc_faultf(envP, "Impossible failure of curl_multi_fdset(): %s",
  217. reason);
  218. xmlrpc_strfree(reason);
  219. }
  220. }
  221. void
  222. curlMulti_updateFdSet(curlMulti * const curlMultiP,
  223. fd_set const readFdSet,
  224. fd_set const writeFdSet,
  225. fd_set const exceptFdSet) {
  226. /*----------------------------------------------------------------------------
  227. curl_multi_perform() expects the file descriptor sets, which were bound
  228. to the CURLM object via a prior curlMulti_fdset(), to contain the results
  229. of a recent select(). This subroutine provides you a way to supply those.
  230. -----------------------------------------------------------------------------*/
  231. curlMultiP->readFdSet = readFdSet;
  232. curlMultiP->writeFdSet = writeFdSet;
  233. curlMultiP->exceptFdSet = exceptFdSet;
  234. }