A clone of btpd with my configuration changes.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

243 líneas
6.0 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. int
  77. http_redo(struct http **http)
  78. {
  79. int err;
  80. struct http *ret;
  81. err = http_get(&ret, (*http)->cb, "%s", (*http)->url);
  82. http_cancel(*http);
  83. *http = ret;
  84. return err;
  85. }
  86. void
  87. http_cancel(struct http *http)
  88. {
  89. pthread_mutex_lock(&m_httpq_lock);
  90. if (http->state == HS_ADD)
  91. http->state = HS_NOADD;
  92. else
  93. http->state = HS_CANCEL;
  94. pthread_mutex_unlock(&m_httpq_lock);
  95. }
  96. int
  97. http_succeeded(struct http_res *res)
  98. {
  99. return res->res == HRES_OK && res->code >= 200 && res->code < 300;
  100. }
  101. static void
  102. http_td_cb(void *arg)
  103. {
  104. struct http *h = arg;
  105. if (h->res.res == HRES_OK)
  106. curl_easy_getinfo(h->curlh, CURLINFO_RESPONSE_CODE, &h->res.code);
  107. if (h->res.res == HRES_FAIL) {
  108. btpd_log(BTPD_L_ERROR, "Http error for url '%s' (%s).\n", h->url,
  109. curl_easy_strerror(h->res.code));
  110. }
  111. if (h->state != HS_CANCEL)
  112. h->cb(h, &h->res, h->cb_arg);
  113. curl_easy_cleanup(h->curlh);
  114. if (h->res.content != NULL)
  115. free(h->res.content);
  116. free(h->url);
  117. free(h);
  118. }
  119. static void
  120. http_td_actions(void)
  121. {
  122. int nmsgs;
  123. struct http *http, *next;
  124. struct http_tq postq;
  125. CURLMsg *cmsg;
  126. pthread_mutex_lock(&m_httpq_lock);
  127. do {
  128. while (BTPDQ_EMPTY(&m_httpq))
  129. pthread_cond_wait(&m_httpq_cond, &m_httpq_lock);
  130. BTPDQ_INIT(&postq);
  131. BTPDQ_FOREACH_MUTABLE(http, &m_httpq, entry, next) {
  132. switch (http->state) {
  133. case HS_ADD:
  134. curl_multi_add_handle(m_curlh, http->curlh);
  135. http->state = HS_ACTIVE;
  136. break;
  137. case HS_CANCEL:
  138. curl_multi_remove_handle(m_curlh, http->curlh);
  139. case HS_NOADD:
  140. BTPDQ_REMOVE(&m_httpq, http, entry);
  141. BTPDQ_INSERT_TAIL(&postq, http, entry);
  142. http->state = HS_CANCEL;
  143. http->res.res = HRES_CANCEL;
  144. break;
  145. case HS_DONE:
  146. abort();
  147. default:
  148. break;
  149. }
  150. }
  151. while ((cmsg = curl_multi_info_read(m_curlh, &nmsgs)) != NULL) {
  152. BTPDQ_FOREACH(http, &m_httpq, entry) {
  153. if (http->curlh == cmsg->easy_handle)
  154. break;
  155. }
  156. assert(http != NULL);
  157. BTPDQ_REMOVE(&m_httpq, http, entry);
  158. BTPDQ_INSERT_TAIL(&postq, http, entry);
  159. http->state = HS_DONE;
  160. if (cmsg->data.result == 0)
  161. http->res.res = HRES_OK;
  162. else {
  163. http->res.res = HRES_FAIL;
  164. http->res.code = cmsg->data.result;
  165. }
  166. curl_multi_remove_handle(m_curlh, http->curlh);
  167. }
  168. if (!BTPDQ_EMPTY(&postq)) {
  169. pthread_mutex_unlock(&m_httpq_lock);
  170. td_post_begin();
  171. BTPDQ_FOREACH(http, &postq, entry)
  172. td_post(http_td_cb, http);
  173. td_post_end();
  174. pthread_mutex_lock(&m_httpq_lock);
  175. }
  176. } while (BTPDQ_EMPTY(&m_httpq));
  177. pthread_mutex_unlock(&m_httpq_lock);
  178. }
  179. static void
  180. http_td(void *arg)
  181. {
  182. fd_set rset, wset, eset;
  183. int maxfd, nbusy;
  184. for (;;) {
  185. http_td_actions();
  186. while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(m_curlh, &nbusy))
  187. ;
  188. if (nbusy > 0) {
  189. FD_ZERO(&rset);
  190. FD_ZERO(&wset);
  191. FD_ZERO(&eset);
  192. curl_multi_fdset(m_curlh, &rset, &wset, &eset, &maxfd);
  193. select(maxfd + 1, &rset, &wset, &eset, CURL_SELECT_TIME);
  194. }
  195. }
  196. }
  197. static void
  198. errdie(int err)
  199. {
  200. if (err != 0)
  201. btpd_err("Fatal error in http_init.\n");
  202. }
  203. void
  204. http_init(void)
  205. {
  206. pthread_t ret;
  207. errdie(curl_global_init(0));
  208. errdie((m_curlh = curl_multi_init()) == NULL);
  209. errdie(pthread_mutex_init(&m_httpq_lock, NULL));
  210. errdie(pthread_cond_init(&m_httpq_cond, NULL));
  211. errdie(pthread_create(&ret, NULL, (void *(*)(void *))http_td, NULL));
  212. }