A clone of btpd with my configuration changes.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
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 <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. }