A clone of btpd with my configuration changes.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

230 lignes
5.8 KiB

  1. #include <pthread.h>
  2. #include <stdarg.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <curl/curl.h>
  6. #include "btpd.h"
  7. #include "http.h"
  8. #define MAX_DOWNLOAD (1 << 18) // 256kB
  9. #define CURL_SELECT_TIME (& (struct timeval) { 1, 0 })
  10. enum http_state {
  11. HS_ADD,
  12. HS_ACTIVE,
  13. HS_DONE,
  14. HS_NOADD,
  15. HS_CANCEL
  16. };
  17. struct http {
  18. enum http_state state;
  19. char *url;
  20. CURL *curlh;
  21. struct http_res res;
  22. BTPDQ_ENTRY(http) entry;
  23. void (*cb)(struct http *, struct http_res *, void *);
  24. void *cb_arg;
  25. };
  26. BTPDQ_HEAD(http_tq, http);
  27. static struct http_tq m_httpq = BTPDQ_HEAD_INITIALIZER(m_httpq);
  28. static pthread_mutex_t m_httpq_lock;
  29. static pthread_cond_t m_httpq_cond;
  30. static CURLM *m_curlh;
  31. static size_t
  32. http_write_cb(void *ptr, size_t size, size_t nmemb, void *arg)
  33. {
  34. char *mem;
  35. struct http_res *res = arg;
  36. size_t nbytes = size * nmemb;
  37. size_t nlength = res->length + nbytes;
  38. if (nlength > MAX_DOWNLOAD)
  39. return 0;
  40. if ((mem = realloc(res->content, nlength)) == NULL)
  41. return 0;
  42. res->content = mem;
  43. bcopy(ptr, res->content + res->length, nbytes);
  44. res->length = nlength;
  45. return nbytes;
  46. }
  47. int
  48. http_get(struct http **ret,
  49. void (*cb)(struct http *, struct http_res *, void *), void *arg,
  50. const char *fmt, ...)
  51. {
  52. struct http *h = btpd_calloc(1, sizeof(*h));
  53. h->state = HS_ADD;
  54. h->cb = cb;
  55. h->cb_arg = arg;
  56. if ((h->curlh = curl_easy_init()) == NULL)
  57. btpd_err("Fatal error in curl.\n");
  58. va_list ap;
  59. va_start(ap, fmt);
  60. if (vasprintf(&h->url, fmt, ap) == -1)
  61. btpd_err("Out of memory.\n");
  62. va_end(ap);
  63. curl_easy_setopt(h->curlh, CURLOPT_URL, h->url);
  64. curl_easy_setopt(h->curlh, CURLOPT_USERAGENT, BTPD_VERSION);
  65. curl_easy_setopt(h->curlh, CURLOPT_WRITEFUNCTION, http_write_cb);
  66. curl_easy_setopt(h->curlh, CURLOPT_WRITEDATA, &h->res);
  67. curl_easy_setopt(h->curlh, CURLOPT_FOLLOWLOCATION, 1);
  68. pthread_mutex_lock(&m_httpq_lock);
  69. BTPDQ_INSERT_TAIL(&m_httpq, h, entry);
  70. pthread_mutex_unlock(&m_httpq_lock);
  71. pthread_cond_signal(&m_httpq_cond);
  72. if (ret != NULL)
  73. *ret = h;
  74. return 0;
  75. }
  76. void
  77. http_cancel(struct http *http)
  78. {
  79. pthread_mutex_lock(&m_httpq_lock);
  80. if (http->state == HS_ADD)
  81. http->state = HS_NOADD;
  82. else
  83. http->state = HS_CANCEL;
  84. pthread_mutex_unlock(&m_httpq_lock);
  85. }
  86. int
  87. http_succeeded(struct http_res *res)
  88. {
  89. return res->res == HRES_OK && res->code >= 200 && res->code < 300;
  90. }
  91. static void
  92. http_td_cb(void *arg)
  93. {
  94. struct http *h = arg;
  95. if (h->res.res == HRES_OK)
  96. curl_easy_getinfo(h->curlh, CURLINFO_RESPONSE_CODE, &h->res.code);
  97. if (h->res.res == HRES_FAIL)
  98. h->res.errmsg = curl_easy_strerror(h->res.code);
  99. if (h->state != HS_CANCEL)
  100. h->cb(h, &h->res, h->cb_arg);
  101. curl_easy_cleanup(h->curlh);
  102. if (h->res.content != NULL)
  103. free(h->res.content);
  104. free(h->url);
  105. free(h);
  106. }
  107. static void
  108. http_td_actions(void)
  109. {
  110. int nmsgs;
  111. struct http *http, *next;
  112. struct http_tq postq;
  113. CURLMsg *cmsg;
  114. pthread_mutex_lock(&m_httpq_lock);
  115. do {
  116. while (BTPDQ_EMPTY(&m_httpq))
  117. pthread_cond_wait(&m_httpq_cond, &m_httpq_lock);
  118. BTPDQ_INIT(&postq);
  119. BTPDQ_FOREACH_MUTABLE(http, &m_httpq, entry, next) {
  120. switch (http->state) {
  121. case HS_ADD:
  122. curl_multi_add_handle(m_curlh, http->curlh);
  123. http->state = HS_ACTIVE;
  124. break;
  125. case HS_CANCEL:
  126. curl_multi_remove_handle(m_curlh, http->curlh);
  127. case HS_NOADD:
  128. BTPDQ_REMOVE(&m_httpq, http, entry);
  129. BTPDQ_INSERT_TAIL(&postq, http, entry);
  130. http->state = HS_CANCEL;
  131. http->res.res = HRES_CANCEL;
  132. break;
  133. case HS_DONE:
  134. abort();
  135. default:
  136. break;
  137. }
  138. }
  139. while ((cmsg = curl_multi_info_read(m_curlh, &nmsgs)) != NULL) {
  140. BTPDQ_FOREACH(http, &m_httpq, entry) {
  141. if (http->curlh == cmsg->easy_handle)
  142. break;
  143. }
  144. assert(http != NULL);
  145. BTPDQ_REMOVE(&m_httpq, http, entry);
  146. BTPDQ_INSERT_TAIL(&postq, http, entry);
  147. http->state = HS_DONE;
  148. if (cmsg->data.result == 0)
  149. http->res.res = HRES_OK;
  150. else {
  151. http->res.res = HRES_FAIL;
  152. http->res.code = cmsg->data.result;
  153. }
  154. curl_multi_remove_handle(m_curlh, http->curlh);
  155. }
  156. if (!BTPDQ_EMPTY(&postq)) {
  157. pthread_mutex_unlock(&m_httpq_lock);
  158. td_post_begin();
  159. BTPDQ_FOREACH(http, &postq, entry)
  160. td_post(http_td_cb, http);
  161. td_post_end();
  162. pthread_mutex_lock(&m_httpq_lock);
  163. }
  164. } while (BTPDQ_EMPTY(&m_httpq));
  165. pthread_mutex_unlock(&m_httpq_lock);
  166. }
  167. static void
  168. http_td(void *arg)
  169. {
  170. fd_set rset, wset, eset;
  171. int maxfd, nbusy;
  172. for (;;) {
  173. http_td_actions();
  174. while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(m_curlh, &nbusy))
  175. ;
  176. if (nbusy > 0) {
  177. FD_ZERO(&rset);
  178. FD_ZERO(&wset);
  179. FD_ZERO(&eset);
  180. curl_multi_fdset(m_curlh, &rset, &wset, &eset, &maxfd);
  181. select(maxfd + 1, &rset, &wset, &eset, CURL_SELECT_TIME);
  182. }
  183. }
  184. }
  185. static void
  186. errdie(int err)
  187. {
  188. if (err != 0)
  189. btpd_err("Fatal error in http_init.\n");
  190. }
  191. void
  192. http_init(void)
  193. {
  194. pthread_t ret;
  195. errdie(curl_global_init(0));
  196. errdie((m_curlh = curl_multi_init()) == NULL);
  197. errdie(pthread_mutex_init(&m_httpq_lock, NULL));
  198. errdie(pthread_cond_init(&m_httpq_cond, NULL));
  199. errdie(pthread_create(&ret, NULL, (void *(*)(void *))http_td, NULL));
  200. }