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.

435 lignes
10 KiB

  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, consumed;
  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. /* req->rbuf.buf may be reallocated inside iobuf_write()
  217. * so calculate the offset before that is called */
  218. consumed = end - (char *)req->rbuf.buf + dlen;
  219. if (!iobuf_write(&req->rbuf, "", 1))
  220. goto error;
  221. req->rbuf.off--;
  222. if (!headers_parse(req, req->rbuf.buf, end))
  223. goto error;
  224. if (req->cancel)
  225. goto cancel;
  226. iobuf_consumed(&req->rbuf, consumed);
  227. goto again;
  228. case PS_CHUNK_SIZE:
  229. assert(req->chunked);
  230. if (len == 0)
  231. goto error;
  232. if ((end = iobuf_find(&req->rbuf, "\n", 1)) == NULL) {
  233. if (req->rbuf.off < 20)
  234. return 1;
  235. else
  236. goto error;
  237. }
  238. errno = 0;
  239. req->length = strtol(req->rbuf.buf, &numend, 16);
  240. if (req->length < 0 || numend == (char *)req->rbuf.buf || errno)
  241. goto error;
  242. if (req->length == 0)
  243. goto done;
  244. iobuf_consumed(&req->rbuf, end - (char *)req->rbuf.buf + 1);
  245. req->pstate = PS_CHUNK_DATA;
  246. goto again;
  247. case PS_CHUNK_DATA:
  248. if (len == 0)
  249. goto error;
  250. assert(req->length > 0);
  251. dlen = min(req->rbuf.off, req->length);
  252. if (dlen > 0) {
  253. res.type = HTTP_T_DATA;
  254. res.v.data.l = dlen;
  255. res.v.data.p = req->rbuf.buf;
  256. req->cb(req, &res, req->arg);
  257. if (req->cancel)
  258. goto cancel;
  259. iobuf_consumed(&req->rbuf, dlen);
  260. req->length -= dlen;
  261. if (req->length == 0) {
  262. req->pstate = PS_CHUNK_CRLF;
  263. goto again;
  264. }
  265. }
  266. return 1;
  267. case PS_CHUNK_CRLF:
  268. if (len == 0)
  269. goto error;
  270. assert(req->length == 0);
  271. if (req->rbuf.off < 2)
  272. return 1;
  273. if (req->rbuf.buf[0] == '\r' && req->rbuf.buf[1] == '\n')
  274. dlen = 2;
  275. else if (req->rbuf.buf[0] == '\n')
  276. dlen = 1;
  277. else
  278. goto error;
  279. iobuf_consumed(&req->rbuf, dlen);
  280. req->pstate = PS_CHUNK_SIZE;
  281. goto again;
  282. case PS_ID_DATA:
  283. if (len == 0 && req->length < 0)
  284. goto done;
  285. else if (len == 0)
  286. goto error;
  287. if (req->length < 0)
  288. dlen = req->rbuf.off;
  289. else
  290. dlen = min(req->rbuf.off, req->length);
  291. if (dlen > 0) {
  292. res.type = HTTP_T_DATA;
  293. res.v.data.p = req->rbuf.buf;
  294. res.v.data.l = dlen;
  295. req->cb(req, &res, req->arg);
  296. if (req->cancel)
  297. goto cancel;
  298. iobuf_consumed(&req->rbuf, dlen);
  299. if (req->length > 0) {
  300. req->length -= dlen;
  301. if (req->length == 0)
  302. goto done;
  303. }
  304. }
  305. return 1;
  306. default:
  307. abort();
  308. }
  309. error:
  310. http_error(req);
  311. return 0;
  312. done:
  313. res.type = HTTP_T_DONE;
  314. req->cb(req, &res, req->arg);
  315. cancel:
  316. http_free(req);
  317. return 0;
  318. }
  319. struct http_url *
  320. http_url_get(struct http_req *req)
  321. {
  322. return req->url;
  323. }
  324. int
  325. http_want_read(struct http_req *req)
  326. {
  327. return 1;
  328. }
  329. int
  330. http_want_write(struct http_req *req)
  331. {
  332. return req->wbuf.off > 0;
  333. }
  334. int
  335. http_read(struct http_req *req, int sd)
  336. {
  337. if (!iobuf_accommodate(&req->rbuf, 4096)) {
  338. http_error(req);
  339. return 0;
  340. }
  341. ssize_t nr = read(sd, req->rbuf.buf + req->rbuf.off, 4096);
  342. if (nr < 0 && errno == EAGAIN)
  343. return 1;
  344. else if (nr < 0) {
  345. http_error(req);
  346. return 0;
  347. } else {
  348. req->rbuf.off += nr;
  349. req->parsing = 1;
  350. if (http_parse(req, nr)) {
  351. req->parsing = 0;
  352. return 1;
  353. } else
  354. return 0;
  355. }
  356. }
  357. int
  358. http_write(struct http_req *req, int sd)
  359. {
  360. assert(req->wbuf.off > 0);
  361. ssize_t nw =
  362. write(sd, req->wbuf.buf, req->wbuf.off);
  363. if (nw < 0 && errno == EAGAIN)
  364. return 1;
  365. else if (nw < 0) {
  366. http_error(req);
  367. return 0;
  368. } else {
  369. iobuf_consumed(&req->wbuf, nw);
  370. return 1;
  371. }
  372. }
  373. int
  374. http_get(struct http_req **out, const char *url, const char *hdrs,
  375. http_cb_t cb, void *arg)
  376. {
  377. struct http_req *req = calloc(1, sizeof(*req));
  378. if (req == NULL)
  379. return 0;
  380. req->cb = cb;
  381. req->arg = arg;
  382. req->url = http_url_parse(url);
  383. if (req->url == NULL)
  384. goto error;
  385. req->rbuf = iobuf_init(4096);
  386. req->wbuf = iobuf_init(1024);
  387. if (!iobuf_print(&req->wbuf, "GET %s HTTP/1.1\r\n"
  388. "Host: %s:%hu\r\n"
  389. "Accept-Encoding:\r\n"
  390. "Connection: close\r\n"
  391. "%s"
  392. "\r\n", req->url->uri, req->url->host, req->url->port, hdrs))
  393. goto error;
  394. if (out != NULL)
  395. *out = req;
  396. return 1;
  397. error:
  398. http_free(req);
  399. return 0;
  400. }
  401. void
  402. http_cancel(struct http_req *req)
  403. {
  404. if (req->parsing)
  405. req->cancel = 1;
  406. else
  407. http_free(req);
  408. }