kore

Kore is a web application platform for writing scalable, concurrent web based processes in C or Python.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

curl.c (17328B)



      1 /*
      2  * Copyright (c) 2019-2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 
     19 #include <inttypes.h>
     20 
     21 #include "kore.h"
     22 #include "http.h"
     23 #include "curl.h"
     24 
     25 #if defined(__linux__)
     26 #include "seccomp.h"
     27 
     28 static struct sock_filter filter_curl[] = {
     29 	/* Allow sockets and libcurl to call connect. */
     30 	KORE_SYSCALL_ALLOW(bind),
     31 	KORE_SYSCALL_ALLOW(ioctl),
     32 	KORE_SYSCALL_ALLOW(connect),
     33 	KORE_SYSCALL_ALLOW(socketpair),
     34 	KORE_SYSCALL_ALLOW(getsockopt),
     35 	KORE_SYSCALL_ALLOW(getsockname),
     36 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
     37 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
     38 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
     39 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_NETLINK),
     40 
     41 	/* Threading related. */
     42 #if defined(SYS_clone3)
     43 	KORE_SYSCALL_ALLOW(clone3),
     44 #endif
     45 #if defined(SYS_rseq)
     46 	KORE_SYSCALL_ALLOW(rseq),
     47 #endif
     48 	KORE_SYSCALL_ALLOW(clone),
     49 	KORE_SYSCALL_ALLOW(set_robust_list),
     50 
     51 	/* Other */
     52 	KORE_SYSCALL_ALLOW(uname),
     53 	KORE_SYSCALL_ALLOW(ioctl),
     54 	KORE_SYSCALL_ALLOW(madvise),
     55 	KORE_SYSCALL_ALLOW(recvmsg),
     56 	KORE_SYSCALL_ALLOW(sendmmsg),
     57 	KORE_SYSCALL_ALLOW(faccessat),
     58 	KORE_SYSCALL_ALLOW(newfstatat),
     59 	KORE_SYSCALL_ALLOW(getpeername),
     60 
     61 #if defined(SYS_getdents64)
     62 	KORE_SYSCALL_ALLOW(getdents64),
     63 #endif
     64 
     65 };
     66 #endif
     67 
     68 #define FD_CACHE_BUCKETS	2048
     69 
     70 struct fd_cache {
     71 	struct kore_event	evt;
     72 	int			fd;
     73 	int			scheduled;
     74 	LIST_ENTRY(fd_cache)	list;
     75 };
     76 
     77 struct curl_run {
     78 	int			eof;
     79 	struct fd_cache		*fdc;
     80 	TAILQ_ENTRY(curl_run)	list;
     81 };
     82 
     83 static void	curl_process(void);
     84 static void	curl_event_handle(void *, int);
     85 static void	curl_timeout(void *, u_int64_t);
     86 static int	curl_timer(CURLM *, long, void *);
     87 static void	curl_run_handle(struct curl_run *);
     88 static void	curl_run_schedule(struct fd_cache *, int);
     89 static int	curl_socket(CURL *, curl_socket_t, int, void *, void *);
     90 
     91 static struct fd_cache	*fd_cache_get(int);
     92 
     93 static TAILQ_HEAD(, curl_run)	runlist;
     94 static struct kore_pool		run_pool;
     95 static int			running = 0;
     96 static CURLM			*multi = NULL;
     97 static struct kore_timer	*timer = NULL;
     98 static struct kore_pool		fd_cache_pool;
     99 static char			user_agent[64];
    100 static int			timeout_immediate = 0;
    101 static LIST_HEAD(, fd_cache)	cache[FD_CACHE_BUCKETS];
    102 
    103 u_int16_t	kore_curl_timeout = KORE_CURL_TIMEOUT;
    104 u_int64_t	kore_curl_recv_max = KORE_CURL_RECV_MAX;
    105 
    106 void
    107 kore_curl_sysinit(void)
    108 {
    109 	CURLMcode	res;
    110 	int		i, len;
    111 
    112 	if (curl_global_init(CURL_GLOBAL_ALL))
    113 		fatal("failed to initialize libcurl");
    114 
    115 	if ((multi = curl_multi_init()) == NULL)
    116 		fatal("curl_multi_init(): failed");
    117 
    118 	/* XXX - make configurable? */
    119 	curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, 500);
    120 
    121 	if ((res = curl_multi_setopt(multi,
    122 	    CURLMOPT_SOCKETFUNCTION, curl_socket)) != CURLM_OK)
    123 		fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
    124 
    125 	if ((res = curl_multi_setopt(multi,
    126 	    CURLMOPT_TIMERFUNCTION, curl_timer)) != CURLM_OK)
    127 		fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
    128 
    129 	for (i = 0; i < FD_CACHE_BUCKETS; i++)
    130 		LIST_INIT(&cache[i]);
    131 
    132 	TAILQ_INIT(&runlist);
    133 
    134 	kore_pool_init(&fd_cache_pool, "fd_cache_pool", 100,
    135 	    sizeof(struct fd_cache));
    136 	kore_pool_init(&run_pool, "run_pool", 100, sizeof(struct curl_run));
    137 
    138 	len = snprintf(user_agent, sizeof(user_agent), "kore/%s", kore_version);
    139 	if (len == -1 || (size_t)len >= sizeof(user_agent))
    140 		fatal("user-agent string too long");
    141 
    142 #if defined(__linux__)
    143 	kore_seccomp_filter("curl", filter_curl, KORE_FILTER_LEN(filter_curl));
    144 #endif
    145 #if defined(KORE_USE_PLATFORM_PLEDGE)
    146 	kore_platform_add_pledge("dns");
    147 #endif
    148 }
    149 
    150 int
    151 kore_curl_init(struct kore_curl *client, const char *url, int flags)
    152 {
    153 	CURL		*handle;
    154 
    155 	if ((flags & KORE_CURL_ASYNC) && (flags & KORE_CURL_SYNC)) {
    156 		(void)kore_strlcpy(client->errbuf, "invalid flags",
    157 		    sizeof(client->errbuf));
    158 		return (KORE_RESULT_ERROR);
    159 	}
    160 
    161 	memset(client, 0, sizeof(*client));
    162 
    163 	TAILQ_INIT(&client->http.resp_hdrs);
    164 
    165 	if ((handle = curl_easy_init()) == NULL) {
    166 		(void)kore_strlcpy(client->errbuf, "failed to setup curl",
    167 		    sizeof(client->errbuf));
    168 		return (KORE_RESULT_ERROR);
    169 	}
    170 
    171 	curl_easy_setopt(handle, CURLOPT_WRITEDATA, &client->response);
    172 	curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, kore_curl_tobuf);
    173 
    174 	curl_easy_setopt(handle, CURLOPT_URL, url);
    175 	curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
    176 	curl_easy_setopt(handle, CURLOPT_PRIVATE, client);
    177 	curl_easy_setopt(handle, CURLOPT_USERAGENT, user_agent);
    178 	curl_easy_setopt(handle, CURLOPT_TIMEOUT, kore_curl_timeout);
    179 	curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, client->errbuf);
    180 
    181 	client->flags = flags;
    182 	client->handle = handle;
    183 	client->url = kore_strdup(url);
    184 	client->type = KORE_CURL_TYPE_CUSTOM;
    185 
    186 	return (KORE_RESULT_OK);
    187 }
    188 
    189 void
    190 kore_curl_cleanup(struct kore_curl *client)
    191 {
    192 	struct http_header	*hdr, *next;
    193 
    194 	kore_free(client->url);
    195 
    196 	if (client->flags & KORE_CURL_FLAG_BOUND)
    197 		LIST_REMOVE(client, list);
    198 
    199 	if (client->handle != NULL) {
    200 		curl_multi_remove_handle(multi, client->handle);
    201 		curl_easy_cleanup(client->handle);
    202 	}
    203 
    204 	if (client->http.hdrlist != NULL)
    205 		curl_slist_free_all(client->http.hdrlist);
    206 
    207 	if (client->response != NULL)
    208 		kore_buf_free(client->response);
    209 
    210 	if (client->http.headers != NULL)
    211 		kore_buf_free(client->http.headers);
    212 
    213 	if (client->http.tosend != NULL)
    214 		kore_buf_free(client->http.tosend);
    215 
    216 	for (hdr = TAILQ_FIRST(&client->http.resp_hdrs);
    217 	    hdr != NULL; hdr = next) {
    218 		next = TAILQ_NEXT(hdr, list);
    219 		TAILQ_REMOVE(&client->http.resp_hdrs, hdr, list);
    220 		kore_pool_put(&http_header_pool, hdr);
    221 	}
    222 }
    223 
    224 void
    225 kore_curl_do_timeout(void)
    226 {
    227 	while (timeout_immediate) {
    228 		curl_timeout(NULL, kore_time_ms());
    229 		if (running == 0)
    230 			curl_timer(multi, -1, NULL);
    231 	}
    232 }
    233 
    234 void
    235 kore_curl_run_scheduled(void)
    236 {
    237 	struct curl_run		*run;
    238 
    239 	while ((run = TAILQ_FIRST(&runlist))) {
    240 		TAILQ_REMOVE(&runlist, run, list);
    241 		curl_run_handle(run);
    242 		kore_pool_put(&run_pool, run);
    243 	}
    244 
    245 	curl_process();
    246 }
    247 
    248 size_t
    249 kore_curl_tobuf(char *ptr, size_t size, size_t nmemb, void *udata)
    250 {
    251 	size_t			len;
    252 	struct kore_buf		**buf, *b;
    253 
    254 	if (SIZE_MAX / nmemb < size)
    255 		fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
    256 
    257 	buf = udata;
    258 	len = size * nmemb;
    259 
    260 	if (*buf == NULL)
    261 		*buf = kore_buf_alloc(len);
    262 
    263 	b = *buf;
    264 
    265 	if (b->offset + len < b->offset)
    266 		fatal("%s: %zu+%zu overflows", __func__, b->offset, len);
    267 
    268 	if ((b->offset + len) > kore_curl_recv_max) {
    269 		kore_log(LOG_ERR,
    270 		    "received too large transfer (%zu > %" PRIu64 ")",
    271 		    b->offset + len, kore_curl_recv_max);
    272 		return (0);
    273 	}
    274 
    275 	kore_buf_append(b, ptr, len);
    276 
    277 	return (len);
    278 }
    279 
    280 size_t
    281 kore_curl_frombuf(char *ptr, size_t size, size_t nmemb, void *udata)
    282 {
    283 	size_t			len;
    284 	struct kore_buf		*buf;
    285 
    286 	if (SIZE_MAX / nmemb < size)
    287 		fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
    288 
    289 	buf = udata;
    290 	len = size * nmemb;
    291 
    292 	if (buf->offset == buf->length)
    293 		return (0);
    294 
    295 	if (buf->offset + len < buf->offset)
    296 		fatal("%s: %zu+%zu overflows", __func__, buf->offset, len);
    297 
    298 	if ((buf->offset + len) < buf->length) {
    299 		memcpy(ptr, buf->data + buf->offset, len);
    300 	} else {
    301 		len = buf->length - buf->offset;
    302 		memcpy(ptr, buf->data + buf->offset, len);
    303 	}
    304 
    305 	buf->offset += len;
    306 
    307 	return (len);
    308 }
    309 
    310 void
    311 kore_curl_bind_request(struct kore_curl *client, struct http_request *req)
    312 {
    313 	if (client->cb != NULL)
    314 		fatal("%s: already bound to callback", __func__);
    315 
    316 	client->req = req;
    317 	http_request_sleep(req);
    318 
    319 	client->flags |= KORE_CURL_FLAG_BOUND;
    320 	LIST_INSERT_HEAD(&req->chandles, client, list);
    321 }
    322 
    323 void
    324 kore_curl_bind_callback(struct kore_curl *client,
    325     void (*cb)(struct kore_curl *, void *), void *arg)
    326 {
    327 	if (client->req != NULL)
    328 		fatal("%s: already bound to request", __func__);
    329 
    330 	client->cb = cb;
    331 	client->arg = arg;
    332 }
    333 
    334 void
    335 kore_curl_run(struct kore_curl *client)
    336 {
    337 	if (client->flags & KORE_CURL_ASYNC) {
    338 		curl_multi_add_handle(multi, client->handle);
    339 		return;
    340 	}
    341 
    342 	client->result = curl_easy_perform(client->handle);
    343 
    344 	curl_easy_getinfo(client->handle,
    345 	    CURLINFO_RESPONSE_CODE, &client->http.status);
    346 
    347 	curl_easy_cleanup(client->handle);
    348 	client->handle = NULL;
    349 }
    350 
    351 int
    352 kore_curl_success(struct kore_curl *client)
    353 {
    354 	return (client->result == CURLE_OK);
    355 }
    356 
    357 const char *
    358 kore_curl_strerror(struct kore_curl *client)
    359 {
    360 	const char	*err;
    361 
    362 	if (client->errbuf[0] != '\0')
    363 		err = &client->errbuf[0];
    364 	else
    365 		err = curl_easy_strerror(client->result);
    366 
    367 	return (err);
    368 }
    369 
    370 void
    371 kore_curl_logerror(struct kore_curl *client)
    372 {
    373 	kore_log(LOG_NOTICE, "curl error: %s -> %s", client->url,
    374 	    kore_curl_strerror(client));
    375 }
    376 
    377 void
    378 kore_curl_response_as_bytes(struct kore_curl *client, const u_int8_t **body,
    379     size_t *len)
    380 {
    381 	if (client->response == NULL) {
    382 		*len = 0;
    383 		*body = NULL;
    384 	} else {
    385 		*len = client->response->offset;
    386 		*body = client->response->data;
    387 	}
    388 }
    389 
    390 char *
    391 kore_curl_response_as_string(struct kore_curl *client)
    392 {
    393 	kore_buf_stringify(client->response, NULL);
    394 
    395 	return ((char *)client->response->data);
    396 }
    397 
    398 void
    399 kore_curl_http_setup(struct kore_curl *client, int method, const void *data,
    400     size_t len)
    401 {
    402 	const char	*mname;
    403 	int		has_body;
    404 
    405 	if (client->handle == NULL)
    406 		fatal("%s: called without setup", __func__);
    407 
    408 	mname = NULL;
    409 	has_body = 1;
    410 
    411 	client->type = KORE_CURL_TYPE_HTTP_CLIENT;
    412 
    413 	curl_easy_setopt(client->handle, CURLOPT_HEADERDATA,
    414 	    &client->http.headers);
    415 	curl_easy_setopt(client->handle, CURLOPT_HEADERFUNCTION,
    416 	    kore_curl_tobuf);
    417 
    418 	kore_curl_http_set_header(client, "expect", "");
    419 
    420 	switch (method) {
    421 	case HTTP_METHOD_GET:
    422 	case HTTP_METHOD_OPTIONS:
    423 		break;
    424 	case HTTP_METHOD_HEAD:
    425 		curl_easy_setopt(client->handle, CURLOPT_NOBODY, 1);
    426 		break;
    427 	case HTTP_METHOD_PUT:
    428 		has_body = 1;
    429 		curl_easy_setopt(client->handle, CURLOPT_UPLOAD, 1);
    430 		break;
    431 	case HTTP_METHOD_PATCH:
    432 	case HTTP_METHOD_DELETE:
    433 		mname = http_method_text(method);
    434 		/* fallthrough */
    435 	case HTTP_METHOD_POST:
    436 		has_body = 1;
    437 		curl_easy_setopt(client->handle, CURLOPT_POST, 1);
    438 		break;
    439 	default:
    440 		fatal("%s: unknown method %d", __func__, method);
    441 	}
    442 
    443 	if (has_body && data != NULL && len > 0) {
    444 		client->http.tosend = kore_buf_alloc(len);
    445 		kore_buf_append(client->http.tosend, data, len);
    446 		kore_buf_reset(client->http.tosend);
    447 
    448 		curl_easy_setopt(client->handle, CURLOPT_READDATA,
    449 		    client->http.tosend);
    450 		curl_easy_setopt(client->handle, CURLOPT_READFUNCTION,
    451 		    kore_curl_frombuf);
    452 	}
    453 
    454 	if (has_body) {
    455 		if (method == HTTP_METHOD_PUT) {
    456 			curl_easy_setopt(client->handle,
    457 			    CURLOPT_INFILESIZE_LARGE, (curl_off_t)len);
    458 		} else {
    459 			curl_easy_setopt(client->handle,
    460 			    CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
    461 		}
    462 	} else {
    463 		if (data != NULL || len != 0) {
    464 			fatal("%s: %d should not have a body",
    465 			    __func__, method);
    466 		}
    467 	}
    468 
    469 	if (mname != NULL)
    470 		curl_easy_setopt(client->handle, CURLOPT_CUSTOMREQUEST, mname);
    471 }
    472 
    473 void
    474 kore_curl_http_set_header(struct kore_curl *client, const char *header,
    475     const char *value)
    476 {
    477 	struct kore_buf		buf;
    478 	const char		*hdr;
    479 
    480 	kore_buf_init(&buf, 512);
    481 
    482 	if (value != NULL || *value != '\0') {
    483 		kore_buf_appendf(&buf, "%s: %s", header, value);
    484 	} else {
    485 		kore_buf_appendf(&buf, "%s:", header);
    486 	}
    487 
    488 	hdr = kore_buf_stringify(&buf, NULL);
    489 
    490 	client->http.hdrlist = curl_slist_append(client->http.hdrlist, hdr);
    491 	kore_buf_cleanup(&buf);
    492 
    493 	curl_easy_setopt(client->handle,
    494 	    CURLOPT_HTTPHEADER, client->http.hdrlist);
    495 }
    496 
    497 int
    498 kore_curl_http_get_header(struct kore_curl *client, const char *header,
    499     const char **out)
    500 {
    501 	struct http_header	*hdr;
    502 
    503 	if (!(client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS))
    504 		kore_curl_http_parse_headers(client);
    505 
    506 	TAILQ_FOREACH(hdr, &(client->http.resp_hdrs), list) {
    507 		if (!strcasecmp(hdr->header, header)) {
    508 			*out = hdr->value;
    509 			return (KORE_RESULT_OK);
    510 		}
    511 	}
    512 
    513 	return (KORE_RESULT_ERROR);
    514 }
    515 
    516 void
    517 kore_curl_http_parse_headers(struct kore_curl *client)
    518 {
    519 	struct http_header	*hdr;
    520 	int			i, cnt;
    521 	char			*value, *hbuf, *headers[HTTP_REQ_HEADER_MAX];
    522 
    523 	if (client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS)
    524 		fatal("%s: headers already parsed", __func__);
    525 
    526 	client->flags |= KORE_CURL_FLAG_HTTP_PARSED_HEADERS;
    527 
    528 	if (client->http.headers == NULL)
    529 		return;
    530 
    531 	hbuf = kore_buf_stringify(client->http.headers, NULL);
    532 	cnt = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
    533 
    534 	for (i = 0; i < cnt; i++) {
    535 		if ((value = http_validate_header(headers[i])) == NULL)
    536 			continue;
    537 
    538 		if (*value == '\0')
    539 			continue;
    540 
    541 		hdr = kore_pool_get(&http_header_pool);
    542 		hdr->header = headers[i];
    543 		hdr->value = value;
    544 		TAILQ_INSERT_TAIL(&(client->http.resp_hdrs), hdr, list);
    545 	}
    546 }
    547 
    548 static int
    549 curl_socket(CURL *easy, curl_socket_t fd, int action, void *arg, void *sock)
    550 {
    551 	CURLcode		res;
    552 	struct fd_cache		*fdc;
    553 	struct kore_curl	*client;
    554 
    555 	client = NULL;
    556 
    557 	res = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&client);
    558 	if (res != CURLE_OK)
    559 		fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
    560 
    561 	if (client == NULL)
    562 		fatal("%s: failed to get client context", __func__);
    563 
    564 	fdc = fd_cache_get(fd);
    565 
    566 	switch (action) {
    567 	case CURL_POLL_NONE:
    568 		break;
    569 	case CURL_POLL_IN:
    570 		if (fdc->scheduled) {
    571 			kore_platform_disable_read(fd);
    572 #if !defined(__linux__)
    573 			kore_platform_disable_write(fd);
    574 #endif
    575 		}
    576 		fdc->scheduled = 1;
    577 		kore_platform_event_level_read(fd, fdc);
    578 		break;
    579 	case CURL_POLL_OUT:
    580 	case CURL_POLL_INOUT:
    581 		if (fdc->scheduled) {
    582 			kore_platform_disable_read(fd);
    583 #if !defined(__linux__)
    584 			kore_platform_disable_write(fd);
    585 #endif
    586 		}
    587 		fdc->scheduled = 1;
    588 		kore_platform_event_level_all(fd, fdc);
    589 		break;
    590 	case CURL_POLL_REMOVE:
    591 		if (fdc->scheduled) {
    592 			fdc->evt.flags = 0;
    593 			fdc->scheduled = 0;
    594 			kore_platform_disable_read(fd);
    595 #if !defined(__linux__)
    596 			kore_platform_disable_write(fd);
    597 #endif
    598 		}
    599 		break;
    600 	default:
    601 		fatal("unknown action value: %d", action);
    602 	}
    603 
    604 	if (action != CURL_POLL_NONE && action != CURL_POLL_REMOVE)
    605 		curl_run_schedule(fdc, 0);
    606 
    607 	return (CURLM_OK);
    608 }
    609 
    610 static void
    611 curl_process(void)
    612 {
    613 	CURLcode		res;
    614 	CURLMsg			*msg;
    615 	CURL			*handle;
    616 	struct kore_curl	*client;
    617 	int			pending;
    618 
    619 	pending = 0;
    620 
    621 	while ((msg = curl_multi_info_read(multi, &pending)) != NULL) {
    622 		if (msg->msg != CURLMSG_DONE)
    623 			continue;
    624 
    625 		handle = msg->easy_handle;
    626 
    627 		res = curl_easy_getinfo(handle, CURLINFO_PRIVATE,
    628 		    (char **)&client);
    629 		if (res != CURLE_OK)
    630 			fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
    631 
    632 		if (client == NULL)
    633 			fatal("%s: failed to get client context", __func__);
    634 
    635 		client->result = msg->data.result;
    636 
    637 		if (client->type == KORE_CURL_TYPE_HTTP_CLIENT) {
    638 			curl_easy_getinfo(client->handle,
    639 			    CURLINFO_RESPONSE_CODE, &client->http.status);
    640 		}
    641 
    642 		curl_multi_remove_handle(multi, client->handle);
    643 		curl_easy_cleanup(client->handle);
    644 
    645 		client->handle = NULL;
    646 
    647 		if (client->req != NULL)
    648 			http_request_wakeup(client->req);
    649 		else if (client->cb != NULL)
    650 			client->cb(client, client->arg);
    651 	}
    652 }
    653 
    654 static void
    655 curl_timeout(void *uarg, u_int64_t now)
    656 {
    657 	CURLMcode	res;
    658 
    659 	timer = NULL;
    660 
    661 	res = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running);
    662 	if (res != CURLM_OK)
    663 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
    664 
    665 	curl_process();
    666 }
    667 
    668 static int
    669 curl_timer(CURLM *mctx, long timeout, void *arg)
    670 {
    671 	timeout_immediate = 0;
    672 
    673 	if (timeout < 0) {
    674 		if (timer != NULL) {
    675 			kore_timer_remove(timer);
    676 			timer = NULL;
    677 		}
    678 		return (CURLM_OK);
    679 	}
    680 
    681 	if (timer != NULL) {
    682 		kore_timer_remove(timer);
    683 		timer = NULL;
    684 	}
    685 
    686 	if (timeout == 0) {
    687 		timeout_immediate = 1;
    688 		return (CURLM_OK);
    689 	}
    690 
    691 	timer = kore_timer_add(curl_timeout, timeout, mctx, KORE_TIMER_ONESHOT);
    692 
    693 	return (CURLM_OK);
    694 }
    695 
    696 static void
    697 curl_run_schedule(struct fd_cache *fdc, int eof)
    698 {
    699 	struct curl_run		*run;
    700 
    701 	run = kore_pool_get(&run_pool);
    702 	run->fdc = fdc;
    703 	run->eof = eof;
    704 
    705 	TAILQ_INSERT_TAIL(&runlist, run, list);
    706 }
    707 
    708 static void
    709 curl_event_handle(void *arg, int eof)
    710 {
    711 	curl_run_schedule(arg, eof);
    712 }
    713 
    714 static void
    715 curl_run_handle(struct curl_run *run)
    716 {
    717 	CURLMcode		res;
    718 	int			flags;
    719 	struct fd_cache		*fdc = run->fdc;
    720 
    721 	flags = 0;
    722 
    723 	if (fdc->evt.flags & KORE_EVENT_READ)
    724 		flags |= CURL_CSELECT_IN;
    725 
    726 	if (fdc->evt.flags & KORE_EVENT_WRITE)
    727 		flags |= CURL_CSELECT_OUT;
    728 
    729 	if (run->eof)
    730 		flags |= CURL_CSELECT_ERR;
    731 
    732 	res = curl_multi_socket_action(multi, fdc->fd, flags, &running);
    733 	if (res != CURLM_OK)
    734 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
    735 }
    736 
    737 static struct fd_cache *
    738 fd_cache_get(int fd)
    739 {
    740 	struct fd_cache		*fdc;
    741 	int			bucket;
    742 
    743 	bucket = fd % FD_CACHE_BUCKETS;
    744 
    745 	LIST_FOREACH(fdc, &cache[bucket], list) {
    746 		if (fdc->fd == fd)
    747 			return (fdc);
    748 	}
    749 
    750 	fdc = kore_pool_get(&fd_cache_pool);
    751 
    752 	fdc->fd = fd;
    753 	fdc->scheduled = 0;
    754 
    755 	fdc->evt.flags = 0;
    756 	fdc->evt.handle = curl_event_handle;
    757 	fdc->evt.type = KORE_TYPE_CURL_HANDLE;
    758 
    759 	LIST_INSERT_HEAD(&cache[bucket], fdc, list);
    760 
    761 	return (fdc);
    762 }