A clone of btpd with my configuration changes.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

435 wiersze
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. }