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.

http_client.c 10 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #include <assert.h>
  2. #include <ctype.h>
  3. #include <errno.h>
  4. #include <stdint.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include "iobuf.h"
  10. #include "subr.h"
  11. #include "http_client.h"
  12. struct http_url *
  13. http_url_parse(const char *url)
  14. {
  15. size_t ulen = strlen(url);
  16. if (strncmp(url, "http://", 7) != 0)
  17. return NULL;
  18. const char *cur, *at, *uri = NULL, *uri_e = NULL;
  19. const char *host = NULL, *host_e = NULL;
  20. const char *port = NULL, *port_e = NULL;
  21. at = strchr(url + 7, '@');
  22. uri = strchr(url + 7, '/');
  23. cur = strchr(url + 7, '?');
  24. if (cur != NULL && (uri == NULL || cur < uri))
  25. uri = cur;
  26. if (uri == NULL)
  27. uri = url + ulen;
  28. if (at != NULL && at < uri)
  29. host = at + 1;
  30. else
  31. host = url + 7;
  32. cur = host;
  33. while (cur < uri && *cur != ':')
  34. cur++;
  35. host_e = cur;
  36. if (host_e == host)
  37. return NULL;
  38. if (*cur == ':') {
  39. cur++;
  40. port = cur;
  41. while (cur < uri && *cur >= '0' && *cur <= '9')
  42. cur++;
  43. if (cur == port || cur != uri)
  44. return NULL;
  45. port_e = cur;
  46. }
  47. while (*cur != '\0')
  48. cur++;
  49. uri_e = cur;
  50. struct http_url *res =
  51. malloc(sizeof(*res) + host_e - host + 1 + uri_e - uri + 2);
  52. if (res == NULL)
  53. return NULL;
  54. if (port != NULL)
  55. sscanf(port, "%hu", &res->port);
  56. else
  57. res->port = 80;
  58. res->host = (char *)(res + 1);
  59. res->uri = res->host + (host_e - host + 1);
  60. bcopy(host, res->host, host_e - host);
  61. res->host[host_e - host] = '\0';
  62. if (*uri != '/') {
  63. res->uri[0] = '/';
  64. bcopy(uri, res->uri + 1, uri_e - uri);
  65. res->uri[uri_e - uri + 1] = '\0';
  66. } else {
  67. bcopy(uri, res->uri, uri_e - uri);
  68. res->uri[uri_e - uri] = '\0';
  69. }
  70. return res;
  71. }
  72. void
  73. http_url_free(struct http_url *url)
  74. {
  75. free(url);
  76. }
  77. struct http_req {
  78. enum {
  79. PS_HEAD, PS_CHUNK_SIZE, PS_CHUNK_DATA, PS_CHUNK_CRLF, PS_ID_DATA
  80. } pstate;
  81. int parsing;
  82. int cancel;
  83. int chunked;
  84. long length;
  85. http_cb_t cb;
  86. void *arg;
  87. struct http_url *url;
  88. struct iobuf rbuf;
  89. struct iobuf wbuf;
  90. };
  91. static void
  92. http_free(struct http_req *req)
  93. {
  94. if (req->url != NULL)
  95. http_url_free(req->url);
  96. iobuf_free(&req->rbuf);
  97. iobuf_free(&req->wbuf);
  98. free(req);
  99. }
  100. static void
  101. http_error(struct http_req *req)
  102. {
  103. struct http_response res;
  104. res.type = HTTP_T_ERR;
  105. res.v.error = 1;
  106. req->cb(req, &res, req->arg);
  107. http_free(req);
  108. }
  109. static char *
  110. strnl(char *str, int *nlsize)
  111. {
  112. char *nl = strchr(str, '\n');
  113. if (nl != NULL && nl > str && *(nl - 1) == '\r') {
  114. *nlsize = 2;
  115. return nl - 1;
  116. } else {
  117. *nlsize = 1;
  118. return nl;
  119. }
  120. }
  121. static int
  122. headers_parse(struct http_req *req, char *buf, char *end)
  123. {
  124. int code, majv, minv, nlsize;
  125. char *cur, *nl;
  126. char name[128], value[872];
  127. struct http_response res;
  128. req->chunked = 0;
  129. req->length = -1;
  130. if (sscanf(buf, "HTTP/%d.%d %d", &majv, &minv, &code) != 3)
  131. return 0;
  132. res.type = HTTP_T_CODE;
  133. res.v.code = code;
  134. req->cb(req, &res, req->arg);
  135. if (req->cancel)
  136. return 1;
  137. cur = strchr(buf, '\n') + 1;
  138. nl = strnl(cur, &nlsize);
  139. while (cur < end) {
  140. int i;
  141. char *colon = strchr(cur, ':');
  142. if (colon == NULL || colon > nl)
  143. return 0;
  144. snprintf(name, sizeof(name), "%.*s", (int)(colon - cur), cur);
  145. cur = colon + 1;
  146. i = 0;
  147. val_loop:
  148. while (isblank(*cur))
  149. cur++;
  150. while (cur < nl) {
  151. if (i < sizeof(value) - 1) {
  152. value[i] = *cur;
  153. i++;
  154. }
  155. cur++;
  156. }
  157. cur += nlsize;
  158. nl = strnl(cur, &nlsize);
  159. if (isblank(*cur)) {
  160. if (i < sizeof(value) - 1) {
  161. value[i] = ' ';
  162. i++;
  163. }
  164. cur++;
  165. goto val_loop;
  166. }
  167. value[i] = '\0';
  168. for (i--; i >= 0 && isblank(value[i]); i--)
  169. value[i] = '\0';
  170. res.type = HTTP_T_HEADER;
  171. res.v.header.n = name;
  172. res.v.header.v = value;
  173. req->cb(req, &res, req->arg);
  174. if (req->cancel)
  175. return 1;
  176. if ((!req->chunked
  177. && strcasecmp("Transfer-Encoding", name) == 0
  178. && strcasecmp("chunked", value) == 0))
  179. req->chunked = 1;
  180. if ((!req->chunked && req->length == -1
  181. && strcasecmp("Content-Length", name) == 0)) {
  182. errno = 0;
  183. req->length = strtol(value, NULL, 10);
  184. if (errno)
  185. req->length = -1;
  186. }
  187. }
  188. if (req->chunked)
  189. req->pstate = PS_CHUNK_SIZE;
  190. else
  191. req->pstate = PS_ID_DATA;
  192. return 1;
  193. }
  194. static int
  195. http_parse(struct http_req *req, int len)
  196. {
  197. char *end, *numend;
  198. size_t dlen;
  199. struct http_response res;
  200. again:
  201. switch (req->pstate) {
  202. case PS_HEAD:
  203. if (len == 0)
  204. goto error;
  205. if ((end = iobuf_find(&req->rbuf, "\r\n\r\n", 4)) != NULL)
  206. dlen = 4;
  207. else if ((end = iobuf_find(&req->rbuf, "\n\n", 2)) != NULL)
  208. dlen = 2;
  209. else {
  210. if (req->rbuf.off < (1 << 15))
  211. return 1;
  212. else
  213. goto error;
  214. }
  215. if (!iobuf_write(&req->rbuf, "", 1))
  216. goto error;
  217. req->rbuf.off--;
  218. if (!headers_parse(req, req->rbuf.buf, end))
  219. goto error;
  220. if (req->cancel)
  221. goto cancel;
  222. iobuf_consumed(&req->rbuf, end - (char *)req->rbuf.buf + dlen);
  223. goto again;
  224. case PS_CHUNK_SIZE:
  225. assert(req->chunked);
  226. if (len == 0)
  227. goto error;
  228. if ((end = iobuf_find(&req->rbuf, "\n", 1)) == NULL) {
  229. if (req->rbuf.off < 20)
  230. return 1;
  231. else
  232. goto error;
  233. }
  234. errno = 0;
  235. req->length = strtol(req->rbuf.buf, &numend, 16);
  236. if (req->length < 0 || numend == (char *)req->rbuf.buf || errno)
  237. goto error;
  238. if (req->length == 0)
  239. goto done;
  240. iobuf_consumed(&req->rbuf, end - (char *)req->rbuf.buf + 1);
  241. req->pstate = PS_CHUNK_DATA;
  242. goto again;
  243. case PS_CHUNK_DATA:
  244. if (len == 0)
  245. goto error;
  246. assert(req->length > 0);
  247. dlen = min(req->rbuf.off, req->length);
  248. if (dlen > 0) {
  249. res.type = HTTP_T_DATA;
  250. res.v.data.l = dlen;
  251. res.v.data.p = req->rbuf.buf;
  252. req->cb(req, &res, req->arg);
  253. if (req->cancel)
  254. goto cancel;
  255. iobuf_consumed(&req->rbuf, dlen);
  256. req->length -= dlen;
  257. if (req->length == 0) {
  258. req->pstate = PS_CHUNK_CRLF;
  259. goto again;
  260. }
  261. }
  262. return 1;
  263. case PS_CHUNK_CRLF:
  264. if (len == 0)
  265. goto error;
  266. assert(req->length == 0);
  267. if (req->rbuf.off < 2)
  268. return 1;
  269. if (req->rbuf.buf[0] == '\r' && req->rbuf.buf[1] == '\n')
  270. dlen = 2;
  271. else if (req->rbuf.buf[0] == '\n')
  272. dlen = 1;
  273. else
  274. goto error;
  275. iobuf_consumed(&req->rbuf, dlen);
  276. req->pstate = PS_CHUNK_SIZE;
  277. goto again;
  278. case PS_ID_DATA:
  279. if (len == 0 && req->length < 0)
  280. goto done;
  281. else if (len == 0)
  282. goto error;
  283. if (req->length < 0)
  284. dlen = req->rbuf.off;
  285. else
  286. dlen = min(req->rbuf.off, req->length);
  287. if (dlen > 0) {
  288. res.type = HTTP_T_DATA;
  289. res.v.data.p = req->rbuf.buf;
  290. res.v.data.l = dlen;
  291. req->cb(req, &res, req->arg);
  292. if (req->cancel)
  293. goto cancel;
  294. iobuf_consumed(&req->rbuf, dlen);
  295. if (req->length > 0) {
  296. req->length -= dlen;
  297. if (req->length == 0)
  298. goto done;
  299. }
  300. }
  301. return 1;
  302. default:
  303. abort();
  304. }
  305. error:
  306. http_error(req);
  307. return 0;
  308. done:
  309. res.type = HTTP_T_DONE;
  310. req->cb(req, &res, req->arg);
  311. cancel:
  312. http_free(req);
  313. return 0;
  314. }
  315. struct http_url *
  316. http_url_get(struct http_req *req)
  317. {
  318. return req->url;
  319. }
  320. int
  321. http_want_read(struct http_req *req)
  322. {
  323. return 1;
  324. }
  325. int
  326. http_want_write(struct http_req *req)
  327. {
  328. return req->wbuf.off > 0;
  329. }
  330. int
  331. http_read(struct http_req *req, int sd)
  332. {
  333. if (!iobuf_accommodate(&req->rbuf, 4096)) {
  334. http_error(req);
  335. return 0;
  336. }
  337. ssize_t nr = read(sd, req->rbuf.buf + req->rbuf.off, 4096);
  338. if (nr < 0 && errno == EAGAIN)
  339. return 1;
  340. else if (nr < 0) {
  341. http_error(req);
  342. return 0;
  343. } else {
  344. req->rbuf.off += nr;
  345. req->parsing = 1;
  346. if (http_parse(req, nr)) {
  347. req->parsing = 0;
  348. return 1;
  349. } else
  350. return 0;
  351. }
  352. }
  353. int
  354. http_write(struct http_req *req, int sd)
  355. {
  356. assert(req->wbuf.off > 0);
  357. ssize_t nw =
  358. write(sd, req->wbuf.buf, req->wbuf.off);
  359. if (nw < 0 && errno == EAGAIN)
  360. return 1;
  361. else if (nw < 0) {
  362. http_error(req);
  363. return 0;
  364. } else {
  365. iobuf_consumed(&req->wbuf, nw);
  366. return 1;
  367. }
  368. }
  369. int
  370. http_get(struct http_req **out, const char *url, const char *hdrs,
  371. http_cb_t cb, void *arg)
  372. {
  373. struct http_req *req = calloc(1, sizeof(*req));
  374. if (req == NULL)
  375. return 0;
  376. req->cb = cb;
  377. req->arg = arg;
  378. req->url = http_url_parse(url);
  379. if (req->url == NULL)
  380. goto error;
  381. req->rbuf = iobuf_init(4096);
  382. req->wbuf = iobuf_init(1024);
  383. if (!iobuf_print(&req->wbuf, "GET %s HTTP/1.1\r\n"
  384. "Host: %s:%hu\r\n"
  385. "Accept-Encoding:\r\n"
  386. "Connection: close\r\n"
  387. "%s"
  388. "\r\n", req->url->uri, req->url->host, req->url->port, hdrs))
  389. goto error;
  390. if (out != NULL)
  391. *out = req;
  392. return 1;
  393. error:
  394. http_free(req);
  395. return 0;
  396. }
  397. void
  398. http_cancel(struct http_req *req)
  399. {
  400. if (req->parsing)
  401. req->cancel = 1;
  402. else
  403. http_free(req);
  404. }