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

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