A clone of btpd with my configuration changes.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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