1 /* $OpenBSD: http.c,v 1.32 2021/04/20 14:32:49 claudio Exp $ */ 2 /* 3 * Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com> 4 * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /*- 20 * Copyright (c) 1997 The NetBSD Foundation, Inc. 21 * All rights reserved. 22 * 23 * This code is derived from software contributed to The NetBSD Foundation 24 * by Jason Thorpe and Luke Mewburn. 25 * 26 * Redistribution and use in source and binary forms, with or without 27 * modification, are permitted provided that the following conditions 28 * are met: 29 * 1. Redistributions of source code must retain the above copyright 30 * notice, this list of conditions and the following disclaimer. 31 * 2. Redistributions in binary form must reproduce the above copyright 32 * notice, this list of conditions and the following disclaimer in the 33 * documentation and/or other materials provided with the distribution. 34 * 35 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 36 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 37 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 39 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 * POSSIBILITY OF SUCH DAMAGE. 46 */ 47 #include <sys/types.h> 48 #include <sys/queue.h> 49 #include <sys/socket.h> 50 51 #include <assert.h> 52 #include <ctype.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <limits.h> 56 #include <netdb.h> 57 #include <poll.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <vis.h> 64 #include <imsg.h> 65 66 #include <tls.h> 67 68 #include "extern.h" 69 70 #define HTTP_USER_AGENT "OpenBSD rpki-client" 71 #define HTTP_BUF_SIZE (32 * 1024) 72 #define HTTP_IDLE_TIMEOUT 10 73 #define MAX_CONNECTIONS 64 74 #define NPFDS (MAX_CONNECTIONS + 1) 75 76 enum res { 77 DONE, 78 WANT_POLLIN, 79 WANT_POLLOUT, 80 }; 81 82 enum http_state { 83 STATE_FREE, 84 STATE_CONNECT, 85 STATE_TLSCONNECT, 86 STATE_REQUEST, 87 STATE_RESPONSE_STATUS, 88 STATE_RESPONSE_HEADER, 89 STATE_RESPONSE_DATA, 90 STATE_RESPONSE_CHUNKED_HEADER, 91 STATE_RESPONSE_CHUNKED_TRAILER, 92 STATE_WRITE_DATA, 93 STATE_IDLE, 94 STATE_CLOSE, 95 }; 96 97 struct http_proxy { 98 char *proxyhost; 99 char *proxyuser; 100 char *proxypw; 101 }; 102 103 struct http_connection { 104 LIST_ENTRY(http_connection) entry; 105 char *host; 106 char *port; 107 char *last_modified; 108 char *redir_uri; 109 struct http_request *req; 110 struct pollfd *pfd; 111 struct addrinfo *res0; 112 struct addrinfo *res; 113 struct tls *tls; 114 char *buf; 115 size_t bufsz; 116 size_t bufpos; 117 off_t iosz; 118 time_t idle_time; 119 int status; 120 int fd; 121 int chunked; 122 int keep_alive; 123 short events; 124 enum http_state state; 125 }; 126 127 LIST_HEAD(http_conn_list, http_connection); 128 129 struct http_request { 130 TAILQ_ENTRY(http_request) entry; 131 char *uri; 132 char *modified_since; 133 char *host; 134 char *port; 135 const char *path; /* points into uri */ 136 size_t id; 137 int outfd; 138 int redirect_loop; 139 }; 140 141 TAILQ_HEAD(http_req_queue, http_request); 142 143 static struct http_conn_list active = LIST_HEAD_INITIALIZER(active); 144 static struct http_conn_list idle = LIST_HEAD_INITIALIZER(idle); 145 static struct http_req_queue queue = TAILQ_HEAD_INITIALIZER(queue); 146 static size_t http_conn_count; 147 148 static struct msgbuf msgq; 149 static struct sockaddr_storage http_bindaddr; 150 static struct tls_config *tls_config; 151 static uint8_t *tls_ca_mem; 152 static size_t tls_ca_size; 153 154 /* HTTP request API */ 155 static void http_req_new(size_t, char *, char *, int); 156 static void http_req_free(struct http_request *); 157 static void http_req_done(size_t, enum http_result, const char *); 158 static void http_req_fail(size_t); 159 static int http_req_schedule(struct http_request *); 160 161 /* HTTP connection API */ 162 static void http_new(struct http_request *); 163 static void http_free(struct http_connection *); 164 165 static enum res http_done(struct http_connection *, enum http_result); 166 static enum res http_failed(struct http_connection *); 167 168 /* HTTP connection FSM functions */ 169 static void http_do(struct http_connection *, 170 enum res (*)(struct http_connection *)); 171 172 /* These functions can be used with http_do() */ 173 static enum res http_connect(struct http_connection *); 174 static enum res http_request(struct http_connection *); 175 static enum res http_close(struct http_connection *); 176 static enum res http_handle(struct http_connection *); 177 178 /* Internal state functions used by the above functions */ 179 static enum res http_finish_connect(struct http_connection *); 180 static enum res http_tls_connect(struct http_connection *); 181 static enum res http_tls_handshake(struct http_connection *); 182 static enum res http_read(struct http_connection *); 183 static enum res http_write(struct http_connection *); 184 static enum res data_write(struct http_connection *); 185 186 static time_t 187 getmonotime(void) 188 { 189 struct timespec ts; 190 191 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 192 err(1, "clock_gettime"); 193 return (ts.tv_sec); 194 } 195 196 /* 197 * Return a string that can be used in error message to identify the 198 * connection. 199 */ 200 static const char * 201 http_info(const char *uri) 202 { 203 static char buf[80]; 204 205 if (strnvis(buf, uri, sizeof buf, VIS_SAFE) >= (int)sizeof buf) { 206 /* overflow, add indicator */ 207 memcpy(buf + sizeof buf - 4, "...", 4); 208 } 209 210 return buf; 211 } 212 213 /* 214 * Determine whether the character needs encoding, per RFC1738: 215 * - No corresponding graphic US-ASCII. 216 * - Unsafe characters. 217 */ 218 static int 219 unsafe_char(const char *c0) 220 { 221 const char *unsafe_chars = " <>\"#{}|\\^~[]`"; 222 const unsigned char *c = (const unsigned char *)c0; 223 224 /* 225 * No corresponding graphic US-ASCII. 226 * Control characters and octets not used in US-ASCII. 227 */ 228 return (iscntrl(*c) || !isascii(*c) || 229 230 /* 231 * Unsafe characters. 232 * '%' is also unsafe, if is not followed by two 233 * hexadecimal digits. 234 */ 235 strchr(unsafe_chars, *c) != NULL || 236 (*c == '%' && (!isxdigit(c[1]) || !isxdigit(c[2])))); 237 } 238 239 /* 240 * Encode given URL, per RFC1738. 241 * Allocate and return string to the caller. 242 */ 243 static char * 244 url_encode(const char *path) 245 { 246 size_t i, length, new_length; 247 char *epath, *epathp; 248 249 length = new_length = strlen(path); 250 251 /* 252 * First pass: 253 * Count unsafe characters, and determine length of the 254 * final URL. 255 */ 256 for (i = 0; i < length; i++) 257 if (unsafe_char(path + i)) 258 new_length += 2; 259 260 epath = epathp = malloc(new_length + 1); /* One more for '\0'. */ 261 if (epath == NULL) 262 err(1, NULL); 263 264 /* 265 * Second pass: 266 * Encode, and copy final URL. 267 */ 268 for (i = 0; i < length; i++) 269 if (unsafe_char(path + i)) { 270 snprintf(epathp, 4, "%%" "%02x", 271 (unsigned char)path[i]); 272 epathp += 3; 273 } else 274 *(epathp++) = path[i]; 275 276 *epathp = '\0'; 277 return (epath); 278 } 279 280 /* 281 * Parse a URI and split it up into host, port and path. 282 * Does some basic URI validation. Both host and port need to be freed 283 * by the caller whereas path points into the uri. 284 */ 285 static int 286 http_parse_uri(char *uri, char **ohost, char **oport, char **opath) 287 { 288 char *host, *port = NULL, *path; 289 char *hosttail; 290 291 if (strncasecmp(uri, "https://", 8) != 0) { 292 warnx("%s: not using https schema", http_info(uri)); 293 return -1; 294 } 295 host = uri + 8; 296 if ((path = strchr(host, '/')) == NULL) { 297 warnx("%s: missing https path", http_info(uri)); 298 return -1; 299 } 300 if (path - uri > INT_MAX - 1) { 301 warnx("%s: preposterous host length", http_info(uri)); 302 return -1; 303 } 304 if (*host == '[') { 305 char *scope; 306 if ((hosttail = memrchr(host, ']', path - host)) == NULL) { 307 warnx("%s: unmatched opening bracket", http_info(uri)); 308 return -1; 309 } 310 if (hosttail[1] == '/' || hosttail[1] == ':') 311 host++; 312 if (hosttail[1] == ':') 313 port = hosttail + 2; 314 if ((scope = memchr(host, '%', hosttail - host)) != NULL) 315 hosttail = scope; 316 } else { 317 if ((hosttail = memrchr(host, ':', path - host)) != NULL) 318 port = hosttail + 1; 319 else 320 hosttail = path; 321 } 322 323 if ((host = strndup(host, hosttail - host)) == NULL) 324 err(1, NULL); 325 if (port != NULL) { 326 if ((port = strndup(port, path - port)) == NULL) 327 err(1, NULL); 328 } else { 329 if ((port = strdup("443")) == NULL) 330 err(1, NULL); 331 } 332 /* do not include the initial / in path */ 333 path++; 334 335 *ohost = host; 336 *oport = port; 337 *opath = path; 338 339 return 0; 340 } 341 342 /* 343 * Lookup the IP addresses for host:port. 344 * Returns 0 on success and -1 on failure. 345 */ 346 static int 347 http_resolv(struct addrinfo **res, const char *host, const char *port) 348 { 349 struct addrinfo hints; 350 int error; 351 352 memset(&hints, 0, sizeof(hints)); 353 hints.ai_family = PF_UNSPEC; 354 hints.ai_socktype = SOCK_STREAM; 355 error = getaddrinfo(host, port, &hints, res); 356 /* 357 * If the services file is corrupt/missing, fall back 358 * on our hard-coded defines. 359 */ 360 if (error == EAI_SERVICE) 361 error = getaddrinfo(host, "443", &hints, res); 362 if (error != 0) { 363 warnx("%s: %s", host, gai_strerror(error)); 364 return -1; 365 } 366 367 return 0; 368 } 369 370 /* 371 * Create and queue a new request. 372 */ 373 static void 374 http_req_new(size_t id, char *uri, char *modified_since, int outfd) 375 { 376 struct http_request *req; 377 char *host, *port, *path; 378 379 if (http_parse_uri(uri, &host, &port, &path) == -1) { 380 free(uri); 381 free(modified_since); 382 close(outfd); 383 http_req_fail(id); 384 return; 385 } 386 387 if ((req = calloc(1, sizeof(*req))) == NULL) 388 err(1, NULL); 389 390 req->id = id; 391 req->outfd = outfd; 392 req->host = host; 393 req->port = port; 394 req->path = path; 395 req->uri = uri; 396 req->modified_since = modified_since; 397 398 TAILQ_INSERT_TAIL(&queue, req, entry); 399 } 400 401 /* 402 * Free a request, request is not allowed to be on the req queue. 403 */ 404 static void 405 http_req_free(struct http_request *req) 406 { 407 if (req == NULL) 408 return; 409 410 free(req->host); 411 free(req->port); 412 /* no need to free req->path it points into req->uri */ 413 free(req->uri); 414 free(req->modified_since); 415 416 if (req->outfd != -1) 417 close(req->outfd); 418 } 419 420 /* 421 * Enqueue request response 422 */ 423 static void 424 http_req_done(size_t id, enum http_result res, const char *last_modified) 425 { 426 struct ibuf *b; 427 428 if ((b = ibuf_dynamic(64, UINT_MAX)) == NULL) 429 err(1, NULL); 430 io_simple_buffer(b, &id, sizeof(id)); 431 io_simple_buffer(b, &res, sizeof(res)); 432 io_str_buffer(b, last_modified); 433 ibuf_close(&msgq, b); 434 } 435 436 /* 437 * Enqueue request failure response 438 */ 439 static void 440 http_req_fail(size_t id) 441 { 442 struct ibuf *b; 443 enum http_result res = HTTP_FAILED; 444 445 if ((b = ibuf_dynamic(8, UINT_MAX)) == NULL) 446 err(1, NULL); 447 io_simple_buffer(b, &id, sizeof(id)); 448 io_simple_buffer(b, &res, sizeof(res)); 449 io_str_buffer(b, NULL); 450 ibuf_close(&msgq, b); 451 } 452 453 /* 454 * Schedule new requests until maximum number of connections is reached. 455 * Try to reuse an idle connection if one exists that matches host and port. 456 */ 457 static int 458 http_req_schedule(struct http_request *req) 459 { 460 struct http_connection *conn; 461 462 TAILQ_REMOVE(&queue, req, entry); 463 464 /* check list of idle connections first */ 465 LIST_FOREACH(conn, &idle, entry) { 466 if (strcmp(conn->host, req->host) != 0) 467 continue; 468 if (strcmp(conn->port, req->port) != 0) 469 continue; 470 471 LIST_REMOVE(conn, entry); 472 LIST_INSERT_HEAD(&active, conn, entry); 473 474 /* use established connection */ 475 conn->req = req; 476 conn->idle_time = 0; 477 478 /* start request */ 479 http_do(conn, http_request); 480 if (conn->state == STATE_FREE) 481 http_free(conn); 482 return 1; 483 } 484 485 if (http_conn_count < MAX_CONNECTIONS) { 486 http_new(req); 487 return 1; 488 } 489 490 /* no more slots free, requeue */ 491 TAILQ_INSERT_HEAD(&queue, req, entry); 492 return 0; 493 } 494 495 /* 496 * Create a new HTTP connection which will be used for the HTTP request req. 497 * On errors a req faulure is issued and both connection and request are freed. 498 */ 499 static void 500 http_new(struct http_request *req) 501 { 502 struct http_connection *conn; 503 504 if ((conn = calloc(1, sizeof(*conn))) == NULL) 505 err(1, NULL); 506 507 conn->fd = -1; 508 conn->req = req; 509 if ((conn->host = strdup(req->host)) == NULL) 510 err(1, NULL); 511 if ((conn->port = strdup(req->port)) == NULL) 512 err(1, NULL); 513 514 LIST_INSERT_HEAD(&active, conn, entry); 515 http_conn_count++; 516 517 /* TODO proxy support (overload of host and port) */ 518 519 if (http_resolv(&conn->res0, conn->host, conn->port) == -1) { 520 http_req_fail(req->id); 521 http_free(conn); 522 return; 523 } 524 525 /* connect and start request */ 526 http_do(conn, http_connect); 527 if (conn->state == STATE_FREE) 528 http_free(conn); 529 } 530 531 /* 532 * Free a no longer active connection, releasing all memory and closing 533 * any open file descriptor. 534 */ 535 static void 536 http_free(struct http_connection *conn) 537 { 538 assert(conn->state == STATE_FREE); 539 540 LIST_REMOVE(conn, entry); 541 http_conn_count--; 542 543 http_req_free(conn->req); 544 free(conn->host); 545 free(conn->port); 546 free(conn->last_modified); 547 free(conn->redir_uri); 548 free(conn->buf); 549 550 if (conn->res0 != NULL) 551 freeaddrinfo(conn->res0); 552 553 tls_free(conn->tls); 554 555 if (conn->fd != -1) 556 close(conn->fd); 557 free(conn); 558 } 559 560 /* 561 * Called when a request on this connection is finished. 562 * Move connection into idle state and onto idle queue. 563 * If there is a request connected to it send back a response 564 * with http_result res, else ignore the res. 565 */ 566 static enum res 567 http_done(struct http_connection *conn, enum http_result res) 568 { 569 assert(conn->bufpos == 0); 570 assert(conn->iosz == 0); 571 assert(conn->chunked == 0); 572 assert(conn->redir_uri == NULL); 573 574 conn->state = STATE_IDLE; 575 conn->idle_time = getmonotime() + HTTP_IDLE_TIMEOUT; 576 577 if (conn->req) { 578 http_req_done(conn->req->id, res, conn->last_modified); 579 http_req_free(conn->req); 580 conn->req = NULL; 581 } 582 583 if (!conn->keep_alive) 584 return http_close(conn); 585 586 LIST_REMOVE(conn, entry); 587 LIST_INSERT_HEAD(&idle, conn, entry); 588 589 /* reset status and keep-alive for good measures */ 590 conn->status = 0; 591 conn->keep_alive = 0; 592 593 return WANT_POLLIN; 594 } 595 596 /* 597 * Called in case of error, moves connection into free state. 598 * This will skip proper shutdown of the TLS session. 599 * If a request is pending fail and free the request. 600 */ 601 static enum res 602 http_failed(struct http_connection *conn) 603 { 604 conn->state = STATE_FREE; 605 606 if (conn->req) { 607 http_req_fail(conn->req->id); 608 http_req_free(conn->req); 609 conn->req = NULL; 610 } 611 612 return DONE; 613 } 614 615 /* 616 * Call the function f and update the connection events based 617 * on the return value. 618 */ 619 static void 620 http_do(struct http_connection *conn, enum res (*f)(struct http_connection *)) 621 { 622 switch (f(conn)) { 623 case DONE: 624 conn->events = 0; 625 break; 626 case WANT_POLLIN: 627 conn->events = POLLIN; 628 break; 629 case WANT_POLLOUT: 630 conn->events = POLLOUT; 631 break; 632 default: 633 errx(1, "%s: unexpected function return", 634 http_info(conn->host)); 635 } 636 } 637 638 /* 639 * Connection successfully establish, initiate TLS handshake. 640 */ 641 static enum res 642 http_connect_done(struct http_connection *conn) 643 { 644 freeaddrinfo(conn->res0); 645 conn->res0 = NULL; 646 conn->res = NULL; 647 648 #if 0 649 /* TODO proxy connect */ 650 if (proxyenv) 651 proxy_connect(conn->fd, sslhost, proxy_credentials); */ 652 #endif 653 654 return http_tls_connect(conn); 655 } 656 657 /* 658 * Start an asynchronous connect. 659 */ 660 static enum res 661 http_connect(struct http_connection *conn) 662 { 663 const char *cause = NULL; 664 665 assert(conn->fd == -1); 666 conn->state = STATE_CONNECT; 667 668 /* start the loop below with first or next address */ 669 if (conn->res == NULL) 670 conn->res = conn->res0; 671 else 672 conn->res = conn->res->ai_next; 673 for (; conn->res != NULL; conn->res = conn->res->ai_next) { 674 struct addrinfo *res = conn->res; 675 int fd, save_errno; 676 677 fd = socket(res->ai_family, 678 res->ai_socktype | SOCK_NONBLOCK, res->ai_protocol); 679 if (fd == -1) { 680 cause = "socket"; 681 continue; 682 } 683 conn->fd = fd; 684 685 if (http_bindaddr.ss_family == res->ai_family) { 686 if (bind(conn->fd, (struct sockaddr *)&http_bindaddr, 687 res->ai_addrlen) == -1) { 688 save_errno = errno; 689 close(conn->fd); 690 conn->fd = -1; 691 errno = save_errno; 692 cause = "bind"; 693 continue; 694 } 695 } 696 697 if (connect(conn->fd, res->ai_addr, res->ai_addrlen) == -1) { 698 if (errno == EINPROGRESS) { 699 /* wait for async connect to finish. */ 700 return WANT_POLLOUT; 701 } else { 702 save_errno = errno; 703 close(conn->fd); 704 conn->fd = -1; 705 errno = save_errno; 706 cause = "connect"; 707 continue; 708 } 709 } 710 711 break; /* okay we got one */ 712 } 713 714 if (conn->fd == -1) { 715 if (cause != NULL) 716 warn("%s: %s", http_info(conn->req->uri), cause); 717 return http_failed(conn); 718 } 719 720 return http_connect_done(conn); 721 } 722 723 /* 724 * Called once an asynchronus connect request finished. 725 */ 726 static enum res 727 http_finish_connect(struct http_connection *conn) 728 { 729 int error = 0; 730 socklen_t len; 731 732 len = sizeof(error); 733 if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { 734 warn("%s: getsockopt SO_ERROR", http_info(conn->req->uri)); 735 goto fail; 736 } 737 if (error != 0) { 738 errno = error; 739 warn("%s: connect", http_info(conn->req->uri)); 740 goto fail; 741 } 742 743 return http_connect_done(conn); 744 745 fail: 746 close(conn->fd); 747 conn->fd = -1; 748 749 return http_connect(conn); 750 } 751 752 /* 753 * Initiate TLS session on a new connection. 754 */ 755 static enum res 756 http_tls_connect(struct http_connection *conn) 757 { 758 assert(conn->state == STATE_CONNECT); 759 conn->state = STATE_TLSCONNECT; 760 761 if ((conn->tls = tls_client()) == NULL) { 762 warn("tls_client"); 763 return http_failed(conn); 764 } 765 if (tls_configure(conn->tls, tls_config) == -1) { 766 warnx("%s: TLS configuration: %s\n", http_info(conn->req->uri), 767 tls_error(conn->tls)); 768 return http_failed(conn); 769 } 770 if (tls_connect_socket(conn->tls, conn->fd, conn->host) == -1) { 771 warnx("%s: TLS connect: %s\n", http_info(conn->req->uri), 772 tls_error(conn->tls)); 773 return http_failed(conn); 774 } 775 776 return http_tls_handshake(conn); 777 } 778 779 /* 780 * Do the tls_handshake and then send out the HTTP request. 781 */ 782 static enum res 783 http_tls_handshake(struct http_connection *conn) 784 { 785 switch (tls_handshake(conn->tls)) { 786 case -1: 787 warnx("%s: TLS handshake: %s", http_info(conn->req->uri), 788 tls_error(conn->tls)); 789 return http_failed(conn); 790 case TLS_WANT_POLLIN: 791 return WANT_POLLIN; 792 case TLS_WANT_POLLOUT: 793 return WANT_POLLOUT; 794 } 795 796 /* ready to send request */ 797 return http_request(conn); 798 } 799 800 /* 801 * Build the HTTP request and send it out. 802 */ 803 static enum res 804 http_request(struct http_connection *conn) 805 { 806 char *host, *epath, *modified_since; 807 int r, with_port = 0; 808 809 assert(conn->state == STATE_IDLE || conn->state == STATE_TLSCONNECT); 810 conn->state = STATE_REQUEST; 811 812 /* TODO adjust request for HTTP proxy setups */ 813 814 /* 815 * Send port number only if it's specified and does not equal 816 * the default. Some broken HTTP servers get confused if you explicitly 817 * send them the port number. 818 */ 819 if (strcmp(conn->port, "443") != 0) 820 with_port = 1; 821 822 /* Construct the Host header from host and port info */ 823 if (strchr(conn->host, ':')) { 824 if (asprintf(&host, "[%s]%s%s", conn->host, 825 with_port ? ":" : "", with_port ? conn->port : "") == -1) 826 err(1, NULL); 827 828 } else { 829 if (asprintf(&host, "%s%s%s", conn->host, 830 with_port ? ":" : "", with_port ? conn->port : "") == -1) 831 err(1, NULL); 832 } 833 834 /* 835 * Construct and send the request. Proxy requests don't want leading /. 836 */ 837 epath = url_encode(conn->req->path); 838 839 modified_since = NULL; 840 if (conn->req->modified_since != NULL) { 841 if (asprintf(&modified_since, "If-Modified-Since: %s\r\n", 842 conn->req->modified_since) == -1) 843 err(1, NULL); 844 } 845 846 free(conn->buf); 847 conn->bufpos = 0; 848 if ((r = asprintf(&conn->buf, 849 "GET /%s HTTP/1.1\r\n" 850 "Connection: keep-alive\r\n" 851 "User-Agent: " HTTP_USER_AGENT "\r\n" 852 "Host: %s\r\n%s\r\n", 853 epath, host, 854 modified_since ? modified_since : "")) == -1) 855 err(1, NULL); 856 conn->bufsz = r; 857 858 free(epath); 859 free(host); 860 free(modified_since); 861 862 return http_write(conn); 863 } 864 865 /* 866 * Parse the HTTP status line. 867 * Return 0 for status codes 200, 301-304, 307-308. 868 * Failure codes and other errors return -1. 869 * The redirect loop limit is enforced here. 870 */ 871 static int 872 http_parse_status(struct http_connection *conn, char *buf) 873 { 874 const char *errstr; 875 char *cp, ststr[4]; 876 char gerror[200]; 877 int status; 878 879 cp = strchr(buf, ' '); 880 if (cp == NULL) { 881 warnx("Improper response from %s", http_info(conn->host)); 882 return -1; 883 } else 884 cp++; 885 886 strlcpy(ststr, cp, sizeof(ststr)); 887 status = strtonum(ststr, 200, 599, &errstr); 888 if (errstr != NULL) { 889 strnvis(gerror, cp, sizeof gerror, VIS_SAFE); 890 warnx("Error retrieving %s: %s", http_info(conn->host), 891 gerror); 892 return -1; 893 } 894 895 switch (status) { 896 case 301: 897 case 302: 898 case 303: 899 case 307: 900 case 308: 901 if (conn->req->redirect_loop++ > 10) { 902 warnx("%s: Too many redirections requested", 903 http_info(conn->host)); 904 return -1; 905 } 906 /* FALLTHROUGH */ 907 case 200: 908 case 304: 909 conn->status = status; 910 break; 911 default: 912 strnvis(gerror, cp, sizeof gerror, VIS_SAFE); 913 warnx("Error retrieving %s: %s", http_info(conn->host), 914 gerror); 915 return -1; 916 } 917 918 return 0; 919 } 920 921 /* 922 * Returns true if the connection status is any of the redirect codes. 923 */ 924 static inline int 925 http_isredirect(struct http_connection *conn) 926 { 927 if ((conn->status >= 301 && conn->status <= 303) || 928 conn->status == 307 || conn->status == 308) 929 return 1; 930 return 0; 931 } 932 933 static void 934 http_redirect(struct http_connection *conn) 935 { 936 char *uri, *mod_since = NULL; 937 int outfd; 938 939 /* move uri and fd out for new request */ 940 outfd = conn->req->outfd; 941 conn->req->outfd = -1; 942 943 uri = conn->redir_uri; 944 conn->redir_uri = NULL; 945 946 if (conn->req->modified_since) 947 if ((mod_since = strdup(conn->req->modified_since)) == NULL) 948 err(1, NULL); 949 950 logx("redirect to %s", http_info(uri)); 951 http_req_new(conn->req->id, uri, mod_since, outfd); 952 953 /* clear request before moving connection to idle */ 954 http_req_free(conn->req); 955 conn->req = NULL; 956 } 957 958 static int 959 http_parse_header(struct http_connection *conn, char *buf) 960 { 961 #define CONTENTLEN "Content-Length: " 962 #define LOCATION "Location: " 963 #define CONNECTION "Connection: " 964 #define TRANSFER_ENCODING "Transfer-Encoding: " 965 #define LAST_MODIFIED "Last-Modified: " 966 const char *errstr; 967 char *cp, *redirurl; 968 char *locbase, *loctail; 969 970 cp = buf; 971 /* empty line, end of header */ 972 if (*cp == '\0') 973 return 0; 974 else if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) { 975 size_t s; 976 cp += sizeof(CONTENTLEN) - 1; 977 if ((s = strcspn(cp, " \t")) != 0) 978 *(cp+s) = 0; 979 conn->iosz = strtonum(cp, 0, LLONG_MAX, &errstr); 980 if (errstr != NULL) { 981 warnx("Content-Length of %s is %s", 982 http_info(conn->req->uri), errstr); 983 return -1; 984 } 985 } else if (http_isredirect(conn) && 986 strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) { 987 cp += sizeof(LOCATION) - 1; 988 /* 989 * If there is a colon before the first slash, this URI 990 * is not relative. RFC 3986 4.2 991 */ 992 if (cp[strcspn(cp, ":/")] != ':') { 993 /* XXX doesn't handle protocol-relative URIs */ 994 if (*cp == '/') { 995 locbase = NULL; 996 cp++; 997 } else { 998 locbase = strdup(conn->req->path); 999 if (locbase == NULL) 1000 err(1, NULL); 1001 loctail = strchr(locbase, '#'); 1002 if (loctail != NULL) 1003 *loctail = '\0'; 1004 loctail = strchr(locbase, '?'); 1005 if (loctail != NULL) 1006 *loctail = '\0'; 1007 loctail = strrchr(locbase, '/'); 1008 if (loctail == NULL) { 1009 free(locbase); 1010 locbase = NULL; 1011 } else 1012 loctail[1] = '\0'; 1013 } 1014 /* Construct URL from relative redirect */ 1015 if (asprintf(&redirurl, "%.*s/%s%s", 1016 (int)(conn->req->path - conn->req->uri), 1017 conn->req->uri, locbase ? locbase : "", cp) == -1) 1018 err(1, "Cannot build redirect URL"); 1019 free(locbase); 1020 } else if ((redirurl = strdup(cp)) == NULL) 1021 err(1, "Cannot build redirect URL"); 1022 loctail = strchr(redirurl, '#'); 1023 if (loctail != NULL) 1024 *loctail = '\0'; 1025 conn->redir_uri = redirurl; 1026 } else if (strncasecmp(cp, TRANSFER_ENCODING, 1027 sizeof(TRANSFER_ENCODING) - 1) == 0) { 1028 cp += sizeof(TRANSFER_ENCODING) - 1; 1029 cp[strcspn(cp, " \t")] = '\0'; 1030 if (strcasecmp(cp, "chunked") == 0) 1031 conn->chunked = 1; 1032 } else if (strncasecmp(cp, CONNECTION, sizeof(CONNECTION) - 1) == 0) { 1033 cp += sizeof(CONNECTION) - 1; 1034 cp[strcspn(cp, " \t")] = '\0'; 1035 if (strcasecmp(cp, "keep-alive") == 0) 1036 conn->keep_alive = 1; 1037 } else if (strncasecmp(cp, LAST_MODIFIED, 1038 sizeof(LAST_MODIFIED) - 1) == 0) { 1039 cp += sizeof(LAST_MODIFIED) - 1; 1040 if ((conn->last_modified = strdup(cp)) == NULL) 1041 err(1, NULL); 1042 } 1043 1044 return 1; 1045 } 1046 1047 /* 1048 * Return one line from the HTTP response. 1049 * The line returned has any possible '\r' and '\n' at the end stripped. 1050 * The buffer is advanced to the start of the next line. 1051 * If there is currently no full line in the buffer NULL is returned. 1052 */ 1053 static char * 1054 http_get_line(struct http_connection *conn) 1055 { 1056 char *end, *line; 1057 size_t len; 1058 1059 end = memchr(conn->buf, '\n', conn->bufpos); 1060 if (end == NULL) 1061 return NULL; 1062 1063 len = end - conn->buf; 1064 while (len > 0 && conn->buf[len - 1] == '\r') 1065 --len; 1066 if ((line = strndup(conn->buf, len)) == NULL) 1067 err(1, NULL); 1068 1069 /* consume line including \n */ 1070 end++; 1071 conn->bufpos -= end - conn->buf; 1072 memmove(conn->buf, end, conn->bufpos); 1073 1074 return line; 1075 } 1076 1077 /* 1078 * Parse the header between data chunks during chunked transfers. 1079 * Returns 0 if a new chunk size could be correctly read. 1080 * Returns 1 for the empty trailer lines. 1081 * If the chuck size could not be converted properly -1 is returned. 1082 */ 1083 static int 1084 http_parse_chunked(struct http_connection *conn, char *buf) 1085 { 1086 char *header = buf; 1087 char *end; 1088 unsigned long chunksize; 1089 1090 /* empty lines are used as trailer */ 1091 if (*header == '\0') 1092 return 1; 1093 1094 /* strip CRLF and any optional chunk extension */ 1095 header[strcspn(header, ";\r\n")] = '\0'; 1096 errno = 0; 1097 chunksize = strtoul(header, &end, 16); 1098 if (header[0] == '\0' || *end != '\0' || (errno == ERANGE && 1099 chunksize == ULONG_MAX) || chunksize > INT_MAX) 1100 return -1; 1101 1102 conn->iosz = chunksize; 1103 return 0; 1104 } 1105 1106 static enum res 1107 http_read(struct http_connection *conn) 1108 { 1109 ssize_t s; 1110 char *buf; 1111 int done; 1112 1113 read_more: 1114 s = tls_read(conn->tls, conn->buf + conn->bufpos, 1115 conn->bufsz - conn->bufpos); 1116 if (s == -1) { 1117 warn("%s: TLS read: %s", http_info(conn->host), 1118 tls_error(conn->tls)); 1119 return http_failed(conn); 1120 } else if (s == TLS_WANT_POLLIN) { 1121 return WANT_POLLIN; 1122 } else if (s == TLS_WANT_POLLOUT) { 1123 return WANT_POLLOUT; 1124 } 1125 1126 if (s == 0 && conn->bufpos == 0) { 1127 if (conn->req) 1128 warnx("%s: short read, connection closed", 1129 http_info(conn->req->uri)); 1130 return http_failed(conn); 1131 } 1132 1133 conn->bufpos += s; 1134 1135 again: 1136 switch (conn->state) { 1137 case STATE_RESPONSE_STATUS: 1138 buf = http_get_line(conn); 1139 if (buf == NULL) 1140 goto read_more; 1141 if (http_parse_status(conn, buf) == -1) { 1142 free(buf); 1143 return http_failed(conn); 1144 } 1145 free(buf); 1146 conn->state = STATE_RESPONSE_HEADER; 1147 goto again; 1148 case STATE_RESPONSE_HEADER: 1149 done = 0; 1150 while (!done) { 1151 int rv; 1152 1153 buf = http_get_line(conn); 1154 if (buf == NULL) 1155 goto read_more; 1156 1157 rv = http_parse_header(conn, buf); 1158 free(buf); 1159 1160 if (rv == -1) 1161 return http_failed(conn); 1162 if (rv == 0) 1163 done = 1; 1164 } 1165 1166 /* Check status header and decide what to do next */ 1167 if (conn->status == 200 || http_isredirect(conn)) { 1168 if (http_isredirect(conn)) 1169 http_redirect(conn); 1170 1171 if (conn->chunked) 1172 conn->state = STATE_RESPONSE_CHUNKED_HEADER; 1173 else 1174 conn->state = STATE_RESPONSE_DATA; 1175 goto again; 1176 } else if (conn->status == 304) { 1177 return http_done(conn, HTTP_NOT_MOD); 1178 } 1179 1180 return http_failed(conn); 1181 case STATE_RESPONSE_DATA: 1182 if (conn->bufpos != conn->bufsz && 1183 conn->iosz > (off_t)conn->bufpos) 1184 goto read_more; 1185 1186 /* got a full buffer full of data */ 1187 if (conn->req == NULL) { 1188 /* 1189 * After redirects all data needs to be discarded. 1190 */ 1191 if (conn->iosz < (off_t)conn->bufpos) { 1192 conn->bufpos -= conn->iosz; 1193 conn->iosz = 0; 1194 } else { 1195 conn->iosz -= conn->bufpos; 1196 conn->bufpos = 0; 1197 } 1198 if (conn->chunked) 1199 conn->state = STATE_RESPONSE_CHUNKED_TRAILER; 1200 else 1201 conn->state = STATE_RESPONSE_DATA; 1202 goto read_more; 1203 } 1204 1205 conn->state = STATE_WRITE_DATA; 1206 return WANT_POLLOUT; 1207 case STATE_RESPONSE_CHUNKED_HEADER: 1208 assert(conn->iosz == 0); 1209 1210 buf = http_get_line(conn); 1211 if (buf == NULL) 1212 goto read_more; 1213 if (http_parse_chunked(conn, buf) != 0) { 1214 warnx("%s: bad chunk encoding", http_info(conn->host)); 1215 free(buf); 1216 return http_failed(conn); 1217 } 1218 1219 /* 1220 * check if transfer is done, in which case the last trailer 1221 * still needs to be processed. 1222 */ 1223 if (conn->iosz == 0) { 1224 conn->chunked = 0; 1225 conn->state = STATE_RESPONSE_CHUNKED_TRAILER; 1226 goto again; 1227 } 1228 1229 conn->state = STATE_RESPONSE_DATA; 1230 goto again; 1231 case STATE_RESPONSE_CHUNKED_TRAILER: 1232 buf = http_get_line(conn); 1233 if (buf == NULL) 1234 goto read_more; 1235 if (http_parse_chunked(conn, buf) != 1) { 1236 warnx("%s: bad chunk encoding", http_info(conn->host)); 1237 free(buf); 1238 return http_failed(conn); 1239 } 1240 free(buf); 1241 1242 /* if chunked got cleared then the transfer is over */ 1243 if (conn->chunked == 0) 1244 return http_done(conn, HTTP_OK); 1245 1246 conn->state = STATE_RESPONSE_CHUNKED_HEADER; 1247 goto again; 1248 default: 1249 errx(1, "unexpected http state"); 1250 } 1251 } 1252 1253 /* 1254 * Send out the HTTP request. When done, replace buffer with the read buffer. 1255 */ 1256 static enum res 1257 http_write(struct http_connection *conn) 1258 { 1259 ssize_t s; 1260 1261 assert(conn->state == STATE_REQUEST); 1262 1263 while (conn->bufpos < conn->bufsz) { 1264 s = tls_write(conn->tls, conn->buf + conn->bufpos, 1265 conn->bufsz - conn->bufpos); 1266 if (s == -1) { 1267 warnx("%s: TLS write: %s", http_info(conn->host), 1268 tls_error(conn->tls)); 1269 return http_failed(conn); 1270 } else if (s == TLS_WANT_POLLIN) { 1271 return WANT_POLLIN; 1272 } else if (s == TLS_WANT_POLLOUT) { 1273 return WANT_POLLOUT; 1274 } 1275 1276 conn->bufpos += s; 1277 } 1278 1279 /* done writing, first thing we need the status */ 1280 conn->state = STATE_RESPONSE_STATUS; 1281 1282 /* free write buffer and allocate the read buffer */ 1283 free(conn->buf); 1284 conn->bufpos = 0; 1285 conn->bufsz = HTTP_BUF_SIZE; 1286 if ((conn->buf = malloc(conn->bufsz)) == NULL) 1287 err(1, NULL); 1288 1289 return http_read(conn); 1290 } 1291 1292 /* 1293 * Properly shutdown the TLS session else move connection into free state. 1294 */ 1295 static enum res 1296 http_close(struct http_connection *conn) 1297 { 1298 assert(conn->state == STATE_IDLE || conn->state == STATE_CLOSE); 1299 1300 conn->state = STATE_CLOSE; 1301 1302 if (conn->tls != NULL) { 1303 switch (tls_close(conn->tls)) { 1304 case TLS_WANT_POLLIN: 1305 return WANT_POLLIN; 1306 case TLS_WANT_POLLOUT: 1307 return WANT_POLLOUT; 1308 case 0: 1309 case -1: 1310 break; 1311 } 1312 } 1313 1314 conn->state = STATE_FREE; 1315 return DONE; 1316 } 1317 1318 /* 1319 * Write data into provided file descriptor. If all data got written 1320 * the connection may change into idle state. 1321 */ 1322 static enum res 1323 data_write(struct http_connection *conn) 1324 { 1325 ssize_t s; 1326 size_t bsz = conn->bufpos; 1327 1328 assert(conn->state == STATE_WRITE_DATA); 1329 1330 if (conn->iosz < (off_t)bsz) 1331 bsz = conn->iosz; 1332 1333 s = write(conn->req->outfd, conn->buf, bsz); 1334 1335 if (s == -1) { 1336 warn("%s: data write", http_info(conn->req->uri)); 1337 return http_failed(conn); 1338 } 1339 1340 conn->bufpos -= s; 1341 conn->iosz -= s; 1342 memmove(conn->buf, conn->buf + s, conn->bufpos); 1343 1344 /* check if regular file transfer is finished */ 1345 if (!conn->chunked && conn->iosz == 0) 1346 return http_done(conn, HTTP_OK); 1347 1348 /* all data written, switch back to read */ 1349 if (conn->bufpos == 0 || conn->iosz == 0) { 1350 if (conn->chunked) 1351 conn->state = STATE_RESPONSE_CHUNKED_TRAILER; 1352 else 1353 conn->state = STATE_RESPONSE_DATA; 1354 return http_read(conn); 1355 } 1356 1357 /* still more data to write in buffer */ 1358 return WANT_POLLOUT; 1359 } 1360 1361 /* 1362 * Do one IO call depending on the connection state. 1363 * Return WANT_POLLIN or WANT_POLLOUT to poll for more data. 1364 * If 0 is returned this stage is finished and the protocol should move 1365 * to the next stage by calling http_nextstep(). On error return -1. 1366 */ 1367 static enum res 1368 http_handle(struct http_connection *conn) 1369 { 1370 assert (conn->pfd != NULL && conn->pfd->revents != 0); 1371 1372 switch (conn->state) { 1373 case STATE_CONNECT: 1374 return http_finish_connect(conn); 1375 case STATE_TLSCONNECT: 1376 return http_tls_handshake(conn); 1377 case STATE_REQUEST: 1378 return http_write(conn); 1379 case STATE_RESPONSE_STATUS: 1380 case STATE_RESPONSE_HEADER: 1381 case STATE_RESPONSE_DATA: 1382 case STATE_RESPONSE_CHUNKED_HEADER: 1383 case STATE_RESPONSE_CHUNKED_TRAILER: 1384 return http_read(conn); 1385 case STATE_WRITE_DATA: 1386 return data_write(conn); 1387 case STATE_CLOSE: 1388 return http_close(conn); 1389 case STATE_IDLE: 1390 conn->state = STATE_RESPONSE_HEADER; 1391 return http_read(conn); 1392 case STATE_FREE: 1393 errx(1, "bad http state"); 1394 } 1395 errx(1, "unknown http state"); 1396 } 1397 1398 /* 1399 * Initialisation done before pledge() call to load certificates. 1400 */ 1401 static void 1402 http_setup(void) 1403 { 1404 tls_config = tls_config_new(); 1405 if (tls_config == NULL) 1406 errx(1, "tls config failed"); 1407 1408 #if 0 1409 /* TODO Should we allow extra protos and ciphers? */ 1410 if (tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL) == -1) 1411 errx(1, "tls set protocols failed: %s", 1412 tls_config_error(tls_config)); 1413 if (tls_config_set_ciphers(tls_config, "legacy") == -1) 1414 errx(1, "tls set ciphers failed: %s", 1415 tls_config_error(tls_config)); 1416 #endif 1417 1418 /* load cert file from disk now */ 1419 tls_ca_mem = tls_load_file(tls_default_ca_cert_file(), 1420 &tls_ca_size, NULL); 1421 if (tls_ca_mem == NULL) 1422 err(1, "tls_load_file: %s", tls_default_ca_cert_file()); 1423 tls_config_set_ca_mem(tls_config, tls_ca_mem, tls_ca_size); 1424 1425 /* TODO initalize proxy settings */ 1426 } 1427 1428 void 1429 proc_http(char *bind_addr, int fd) 1430 { 1431 struct pollfd pfds[NPFDS]; 1432 struct http_connection *conn, *nc; 1433 struct http_request *req, *nr; 1434 1435 if (bind_addr != NULL) { 1436 struct addrinfo hints, *res; 1437 1438 bzero(&hints, sizeof(hints)); 1439 hints.ai_family = AF_UNSPEC; 1440 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 1441 hints.ai_flags = AI_NUMERICHOST; 1442 if (getaddrinfo(bind_addr, NULL, &hints, &res) == 0) { 1443 memcpy(&http_bindaddr, res->ai_addr, res->ai_addrlen); 1444 freeaddrinfo(res); 1445 } 1446 } 1447 http_setup(); 1448 1449 if (pledge("stdio inet dns recvfd", NULL) == -1) 1450 err(1, "pledge"); 1451 1452 memset(&pfds, 0, sizeof(pfds)); 1453 1454 msgbuf_init(&msgq); 1455 msgq.fd = fd; 1456 1457 for (;;) { 1458 time_t now; 1459 int timeout; 1460 size_t i; 1461 1462 pfds[0].fd = fd; 1463 pfds[0].events = POLLIN; 1464 if (msgq.queued) 1465 pfds[0].events |= POLLOUT; 1466 1467 i = 1; 1468 timeout = INFTIM; 1469 now = getmonotime(); 1470 LIST_FOREACH(conn, &active, entry) { 1471 if (conn->state == STATE_WRITE_DATA) 1472 pfds[i].fd = conn->req->outfd; 1473 else 1474 pfds[i].fd = conn->fd; 1475 1476 pfds[i].events = conn->events; 1477 conn->pfd = &pfds[i]; 1478 i++; 1479 if (i > NPFDS) 1480 errx(1, "too many connections"); 1481 } 1482 LIST_FOREACH(conn, &idle, entry) { 1483 if (conn->idle_time <= now) 1484 timeout = 0; 1485 else { 1486 int diff = conn->idle_time - now; 1487 diff *= 1000; 1488 if (timeout == INFTIM || diff < timeout) 1489 timeout = diff; 1490 } 1491 pfds[i].fd = conn->fd; 1492 pfds[i].events = POLLIN; 1493 conn->pfd = &pfds[i]; 1494 i++; 1495 if (i > NPFDS) 1496 errx(1, "too many connections"); 1497 } 1498 1499 if (poll(pfds, i, timeout) == -1) 1500 err(1, "poll"); 1501 1502 if (pfds[0].revents & POLLHUP) 1503 break; 1504 if (pfds[0].revents & POLLOUT) { 1505 switch (msgbuf_write(&msgq)) { 1506 case 0: 1507 errx(1, "write: connection closed"); 1508 case -1: 1509 err(1, "write"); 1510 } 1511 } 1512 if (pfds[0].revents & POLLIN) { 1513 size_t id; 1514 int outfd; 1515 char *uri; 1516 char *mod; 1517 1518 outfd = io_recvfd(fd, &id, sizeof(id)); 1519 io_str_read(fd, &uri); 1520 io_str_read(fd, &mod); 1521 1522 /* queue up new requests */ 1523 http_req_new(id, uri, mod, outfd); 1524 } 1525 1526 now = getmonotime(); 1527 /* process idle connections */ 1528 LIST_FOREACH_SAFE(conn, &idle, entry, nc) { 1529 if (conn->pfd != NULL && conn->pfd->revents != 0) 1530 http_do(conn, http_handle); 1531 else if (conn->idle_time <= now) 1532 http_do(conn, http_close); 1533 1534 if (conn->state == STATE_FREE) 1535 http_free(conn); 1536 } 1537 1538 /* then active http requests */ 1539 LIST_FOREACH_SAFE(conn, &active, entry, nc) { 1540 /* check if event is ready */ 1541 if (conn->pfd != NULL && conn->pfd->revents != 0) 1542 http_do(conn, http_handle); 1543 1544 if (conn->state == STATE_FREE) 1545 http_free(conn); 1546 } 1547 1548 1549 TAILQ_FOREACH_SAFE(req, &queue, entry, nr) 1550 if (!http_req_schedule(req)) 1551 break; 1552 } 1553 1554 exit(0); 1555 } 1556