Annotation of embedaddon/mpd/src/contrib/libpdel/http/http_server.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include <sys/types.h>
! 42: #include <sys/param.h>
! 43: #include <sys/socket.h>
! 44: #include <sys/syslog.h>
! 45: #include <sys/queue.h>
! 46:
! 47: #include <stdlib.h>
! 48: #include <stdarg.h>
! 49: #include <unistd.h>
! 50: #include <errno.h>
! 51: #include <assert.h>
! 52: #include <unistd.h>
! 53: #include <string.h>
! 54: #include <fcntl.h>
! 55: #include <ctype.h>
! 56: #include <limits.h>
! 57: #include <regex.h>
! 58: #include <pthread.h>
! 59: #include <netdb.h>
! 60:
! 61: #include <netinet/in_systm.h>
! 62: #include <netinet/in.h>
! 63: #include <arpa/inet.h>
! 64:
! 65: #include <openssl/ssl.h>
! 66: #include <openssl/err.h>
! 67:
! 68: #include "structs/structs.h"
! 69: #include "structs/type/array.h"
! 70:
! 71: #include "io/ssl_fp.h"
! 72: #include "http/http_defs.h"
! 73: #include "http/http_server.h"
! 74: #include "http/http_internal.h"
! 75: #include "http/http_servlet.h"
! 76: #include "util/pevent.h"
! 77: #include "util/ghash.h"
! 78: #include "util/typed_mem.h"
! 79:
! 80: /*
! 81: * Embedded HTTP[S] web server.
! 82: */
! 83:
! 84: #define MAX_CONNECTIONS 1024
! 85: #define HTTP_SERVER_TIMEOUT 90
! 86:
! 87: /* HTTP server */
! 88: struct http_server {
! 89: struct pevent_ctx *ctx; /* event context */
! 90: struct pevent *conn_event; /* new connection event */
! 91: int sock; /* accept(2) socket */
! 92: SSL_CTX *ssl; /* ssl context, if doing ssl */
! 93: char *pkey_pw; /* ssl private key password */
! 94: char *server_name; /* server name */
! 95: struct ghash *vhosts; /* virtual hosts */
! 96: http_proxy_t *proxy_handler; /* proxy handler */
! 97: void *proxy_arg; /* proxy handler cookie */
! 98: LIST_HEAD(,http_connection)
! 99: conn_list; /* active connections */
! 100: int max_conn; /* max number of connections */
! 101: int num_conn; /* number of connections */
! 102: http_logger_t *logger; /* error logging routine */
! 103: pthread_mutex_t mutex; /* mutex */
! 104: u_char stopping; /* server being stopped */
! 105: #if PDEL_DEBUG
! 106: int mutex_count; /* mutex count */
! 107: #endif
! 108: };
! 109:
! 110: /* Virtual host */
! 111: struct http_virthost {
! 112: struct http_server *server; /* back pointer to server */
! 113: char *host; /* virtual hostname */
! 114: LIST_HEAD(,http_servlet_hook)
! 115: servlets; /* registered servlets */
! 116: };
! 117:
! 118: /* What a registered servlet looks like */
! 119: struct http_servlet_hook {
! 120: struct http_servlet *servlet; /* servlet */
! 121: struct http_virthost *vhost; /* back pointer to vhost */
! 122: #if PDEL_DEBUG
! 123: char *spat; /* string pattern */
! 124: #endif
! 125: regex_t pat; /* regex pattern */
! 126: int order; /* processing order */
! 127: pthread_rwlock_t rwlock; /* servlet lock */
! 128: LIST_ENTRY(http_servlet_hook)
! 129: next; /* next in vhost list */
! 130: };
! 131:
! 132: /*
! 133: * Internal functions
! 134: */
! 135: static void *http_server_connection_main(void *arg);
! 136: static void http_server_connection_cleanup(void *arg);
! 137: static void http_server_dispatch(struct http_request *req,
! 138: struct http_response *resp);
! 139: static int http_server_ssl_pem_password_cb(char *buf, int size,
! 140: int rwflag, void *udata);
! 141:
! 142: static pevent_handler_t http_server_accept;
! 143:
! 144: static ghash_equal_t http_server_virthost_equal;
! 145: static ghash_hash_t http_server_virthost_hash;
! 146:
! 147: static ssl_logger_t http_server_ssl_logger;
! 148:
! 149: /*********************************************************************
! 150: SERVER START/STOP ROUTINES
! 151: *********************************************************************/
! 152:
! 153: /*
! 154: * Create and start a new server. The server runs in a separate thread.
! 155: */
! 156: struct http_server *
! 157: http_server_start(struct pevent_ctx *ctx, struct in_addr ip,
! 158: u_int16_t port, const struct http_server_ssl *ssl,
! 159: const char *server_name, http_logger_t *logger)
! 160: {
! 161: static const int one = 1;
! 162: struct http_server *serv;
! 163: struct sockaddr_in sin;
! 164: int got_mutex = 0;
! 165:
! 166: /* Initialize new server structure */
! 167: if ((serv = MALLOC("http_server", sizeof(*serv))) == NULL) {
! 168: (*logger)(LOG_ERR, "%s: %s", "realloc", strerror(errno));
! 169: return (NULL);
! 170: }
! 171: memset(serv, 0, sizeof(*serv));
! 172: LIST_INIT(&serv->conn_list);
! 173: serv->ctx = ctx;
! 174: serv->logger = logger;
! 175: serv->sock = -1;
! 176: serv->max_conn = MAX_CONNECTIONS; /* XXX make configurable */
! 177:
! 178: /* Copy server name */
! 179: if ((serv->server_name
! 180: = STRDUP("http_server.server_name", server_name)) == NULL) {
! 181: (*logger)(LOG_ERR, "%s: %s", "strdup", strerror(errno));
! 182: goto fail;
! 183: }
! 184:
! 185: /* Create virtual host hash table */
! 186: if ((serv->vhosts = ghash_create(serv, 0, 0, "http_server.vhosts",
! 187: http_server_virthost_hash, http_server_virthost_equal, NULL,
! 188: NULL)) == NULL) {
! 189: (*logger)(LOG_ERR, "%s: %s", "strdup", strerror(errno));
! 190: goto fail;
! 191: }
! 192:
! 193: /* Initialize ssl */
! 194: if (ssl != NULL) {
! 195:
! 196: /* Initialize SSL stuff */
! 197: _http_ssl_init();
! 198:
! 199: /* Initialize SSL context for this server */
! 200: if ((serv->ssl = SSL_CTX_new(SSLv2_server_method())) == NULL) {
! 201: ssl_log(http_server_ssl_logger, serv);
! 202: goto fail;
! 203: }
! 204:
! 205: /* Set callback for getting private key file password */
! 206: SSL_CTX_set_default_passwd_cb(serv->ssl,
! 207: http_server_ssl_pem_password_cb);
! 208: SSL_CTX_set_default_passwd_cb_userdata(serv->ssl, serv);
! 209: if (serv->pkey_pw != NULL
! 210: && (serv->pkey_pw = STRDUP("http_server.pkey_pw",
! 211: ssl->pkey_password)) == NULL) {
! 212: (*serv->logger)(LOG_ERR, "%s: %s",
! 213: "strdup", strerror(errno));
! 214: goto fail;
! 215: }
! 216:
! 217: /* Read in certificate and private key files */
! 218: if (SSL_CTX_use_certificate_file(serv->ssl,
! 219: ssl->cert_path, SSL_FILETYPE_PEM) <= 0) {
! 220: ssl_log(http_server_ssl_logger, serv);
! 221: goto fail;
! 222: }
! 223: if (SSL_CTX_use_PrivateKey_file(serv->ssl,
! 224: ssl->pkey_path, SSL_FILETYPE_PEM) <= 0) {
! 225: ssl_log(http_server_ssl_logger, serv);
! 226: goto fail;
! 227: }
! 228:
! 229: /* Verify that certificate actually signs the public key */
! 230: if (!SSL_CTX_check_private_key(serv->ssl)) {
! 231: (*serv->logger)(LOG_ERR,
! 232: "certificate %s does not match private key %s",
! 233: ssl->cert_path, ssl->pkey_path);
! 234: goto fail;
! 235: }
! 236: }
! 237:
! 238: /* Create socket */
! 239: if ((serv->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
! 240: (*serv->logger)(LOG_ERR, "%s: %s", "socket", strerror(errno));
! 241: goto fail;
! 242: }
! 243: (void)fcntl(serv->sock, F_SETFD, 1);
! 244: if (setsockopt(serv->sock, SOL_SOCKET,
! 245: SO_REUSEADDR, (char *)&one, sizeof(one)) == -1) {
! 246: (*serv->logger)(LOG_ERR,
! 247: "%s: %s", "setsockopt", strerror(errno));
! 248: goto fail;
! 249: }
! 250: memset(&sin, 0, sizeof(sin));
! 251: #if !defined(__linux__) && !defined(__sun__)
! 252: sin.sin_len = sizeof(sin);
! 253: #endif
! 254: sin.sin_family = AF_INET;
! 255: sin.sin_port = htons(port);
! 256: sin.sin_addr = ip;
! 257: if (bind(serv->sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
! 258: (*serv->logger)(LOG_ERR, "%s: %s", "bind", strerror(errno));
! 259: goto fail;
! 260: }
! 261: if (listen(serv->sock, 1024) == -1) {
! 262: (*serv->logger)(LOG_ERR, "%s: %s", "listen", strerror(errno));
! 263: goto fail;
! 264: }
! 265:
! 266: /* Initialize mutex */
! 267: if ((errno = pthread_mutex_init(&serv->mutex, NULL)) != 0) {
! 268: (*serv->logger)(LOG_ERR, "%s: %s",
! 269: "pthread_mutex_init", strerror(errno));
! 270: goto fail;
! 271: }
! 272: got_mutex = 1;
! 273:
! 274: /* Start accepting connections */
! 275: if (pevent_register(serv->ctx, &serv->conn_event, PEVENT_RECURRING,
! 276: &serv->mutex, http_server_accept, serv, PEVENT_READ, serv->sock)
! 277: == -1) {
! 278: (*serv->logger)(LOG_ERR, "%s: %s",
! 279: "pevent_register", strerror(errno));
! 280: goto fail;
! 281: }
! 282: DBG(HTTP, "server %p started", serv);
! 283:
! 284: /* Done */
! 285: return (serv);
! 286:
! 287: fail:
! 288: /* Cleanup after failure */
! 289: if (got_mutex)
! 290: pthread_mutex_destroy(&serv->mutex);
! 291: if (serv->pkey_pw != NULL) {
! 292: memset(serv->pkey_pw, 0, strlen(serv->pkey_pw));
! 293: FREE("http_server.pkey_pw", serv->pkey_pw);
! 294: }
! 295: if (serv->sock != -1)
! 296: (void)close(serv->sock);
! 297: if (serv->ssl != NULL)
! 298: SSL_CTX_free(serv->ssl);
! 299: ghash_destroy(&serv->vhosts);
! 300: FREE("http_server.server_name", serv->server_name);
! 301: FREE("http_server", serv);
! 302: return (NULL);
! 303: }
! 304:
! 305: /*
! 306: * Stop HTTP server.
! 307: */
! 308: void
! 309: http_server_stop(struct http_server **sp)
! 310: {
! 311: struct http_server *const serv = *sp;
! 312: struct http_connection *conn;
! 313:
! 314: /* Already stopped? */
! 315: if (serv == NULL)
! 316: return;
! 317: *sp = NULL;
! 318:
! 319: /* Set 'stopping' flag to prevent new servlet registrations */
! 320: if (serv->stopping)
! 321: return;
! 322: serv->stopping = 1;
! 323: DBG(HTTP, "stopping server %p", serv);
! 324:
! 325: /* Acquire mutex */
! 326: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 327:
! 328: /* Stop accepting new connections */
! 329: pevent_unregister(&serv->conn_event);
! 330:
! 331: /*
! 332: * Destroy all registered servlets. Because we must unlock the
! 333: * server to do this, don't rely on iteration. Instead, start at
! 334: * the beginning each time.
! 335: */
! 336: while (ghash_size(serv->vhosts) > 0) {
! 337: struct http_servlet_hook *hook;
! 338: struct http_servlet *servlet;
! 339: struct http_virthost *vhost;
! 340: struct ghash_walk walk;
! 341:
! 342: /* Get the first virtual host */
! 343: ghash_walk_init(serv->vhosts, &walk);
! 344: vhost = ghash_walk_next(serv->vhosts, &walk);
! 345: assert(vhost != NULL);
! 346:
! 347: /* Get the first servlet in this virtual host */
! 348: assert(!LIST_EMPTY(&vhost->servlets));
! 349: hook = LIST_FIRST(&vhost->servlets);
! 350: servlet = hook->servlet;
! 351:
! 352: /* Unlock server and nuke the servlet */
! 353: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 354: http_server_destroy_servlet(&servlet);
! 355: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 356: }
! 357:
! 358: /* Kill any remaining connections */
! 359: while (!LIST_EMPTY(&serv->conn_list)) {
! 360:
! 361: /* Kill active connections; they will clean up themselves */
! 362: LIST_FOREACH(conn, &serv->conn_list, next) {
! 363: if (conn->started && conn->tid != 0) {
! 364: DBG(HTTP, "canceling conn %p (thread %p)",
! 365: conn, conn->tid);
! 366: assert(conn->tid != pthread_self());
! 367: pthread_cancel(conn->tid);
! 368: conn->tid = 0; /* don't cancel twice */
! 369: }
! 370: }
! 371:
! 372: /* Wait for outstanding connections to complete */
! 373: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 374: usleep(100000);
! 375: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 376: }
! 377:
! 378: /* Free SSL context */
! 379: if (serv->ssl != NULL)
! 380: SSL_CTX_free(serv->ssl);
! 381:
! 382: /* Close listen socket */
! 383: (void)close(serv->sock);
! 384:
! 385: /* Free strings */
! 386: if (serv->pkey_pw != NULL) {
! 387: memset(serv->pkey_pw, 0, strlen(serv->pkey_pw));
! 388: FREE("http_server.pkey_pw", serv->pkey_pw);
! 389: }
! 390: FREE("http_server.server_name", serv->server_name);
! 391:
! 392: /* Free virtual hosts hash table */
! 393: assert(ghash_size(serv->vhosts) == 0);
! 394: ghash_destroy(&serv->vhosts);
! 395:
! 396: /* Free server structure itself */
! 397: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 398: pthread_mutex_destroy(&serv->mutex);
! 399: DBG(HTTP, "freeing server %p", serv);
! 400: FREE("http_server", serv);
! 401: }
! 402:
! 403: /*********************************************************************
! 404: SERVLET REGISTRATION
! 405: *********************************************************************/
! 406:
! 407: /*
! 408: * Register a servlet
! 409: */
! 410: int
! 411: http_server_register_servlet(struct http_server *serv,
! 412: struct http_servlet *servlet, const char *vhost,
! 413: const char *urlpat, int order)
! 414: {
! 415: struct http_servlet_hook *hook;
! 416: struct http_virthost vhost_key;
! 417: int got_rwlock = 0;
! 418: int got_regex = 0;
! 419: char ebuf[128];
! 420: int r;
! 421:
! 422: /* Sanity */
! 423: if (servlet == NULL) {
! 424: errno = EINVAL;
! 425: return (-1);
! 426: }
! 427: if (servlet->hook != NULL) {
! 428: errno = EALREADY;
! 429: return (-1);
! 430: }
! 431: if (serv->stopping) {
! 432: errno = ESRCH;
! 433: return (-1);
! 434: }
! 435: if (vhost == NULL)
! 436: vhost = "";
! 437:
! 438: /* Create new servlet hook */
! 439: if ((hook = MALLOC("http_servlet_hook", sizeof(*hook))) == NULL) {
! 440: (*serv->logger)(LOG_ERR, "%s: %s", "malloc", strerror(errno));
! 441: return (-1);
! 442: }
! 443: memset(hook, 0, sizeof(*hook));
! 444: hook->servlet = servlet;
! 445: hook->order = order;
! 446: #if PDEL_DEBUG
! 447: if ((hook->spat = STRDUP("http_servlet_hook.spat", urlpat)) == NULL) {
! 448: (*serv->logger)(LOG_ERR, "%s: %s", "malloc", strerror(errno));
! 449: goto fail;
! 450: }
! 451: #endif
! 452:
! 453: /* Compile URL regular expression */
! 454: if ((r = regcomp(&hook->pat, urlpat, REG_EXTENDED | REG_NOSUB)) != 0) {
! 455: regerror(r, &hook->pat, ebuf, sizeof(ebuf));
! 456: (*serv->logger)(LOG_ERR,
! 457: "invalid URL pattern \"%s\": %s", urlpat, ebuf);
! 458: goto fail;
! 459: }
! 460: got_regex = 1;
! 461:
! 462: /* Initialize read/write lock */
! 463: if ((errno = pthread_rwlock_init(&hook->rwlock, NULL)) != 0) {
! 464: (*serv->logger)(LOG_ERR, "%s: %s",
! 465: "pthread_rwlock_init", strerror(errno));
! 466: goto fail;
! 467: }
! 468: got_rwlock = 1;
! 469:
! 470: /* Lock server */
! 471: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 472:
! 473: /* Find virtual host; create a new one if necessary */
! 474: vhost_key.host = (char *)vhost;
! 475: if ((hook->vhost = ghash_get(serv->vhosts, &vhost_key)) == NULL) {
! 476:
! 477: /* Create a new virtual host */
! 478: if ((hook->vhost = MALLOC("http_virthost",
! 479: sizeof(*hook->vhost))) == NULL) {
! 480: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 481: (*serv->logger)(LOG_ERR, "%s: %s",
! 482: "malloc", strerror(errno));
! 483: goto fail;
! 484: }
! 485: memset(hook->vhost, 0, sizeof(*hook->vhost));
! 486: if ((hook->vhost->host
! 487: = STRDUP("http_virthost.host", vhost)) == NULL) {
! 488: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 489: (*serv->logger)(LOG_ERR, "%s: %s",
! 490: "malloc", strerror(errno));
! 491: goto fail;
! 492: }
! 493: LIST_INIT(&hook->vhost->servlets);
! 494:
! 495: /* Add virtual host to server's list */
! 496: if ((r = ghash_put(serv->vhosts, hook->vhost)) == -1) {
! 497: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 498: goto fail;
! 499: }
! 500: assert(r == 0);
! 501: hook->vhost->server = serv;
! 502: }
! 503:
! 504: /* Register servlet with virtual host */
! 505: LIST_INSERT_HEAD(&hook->vhost->servlets, hook, next);
! 506: servlet->hook = hook;
! 507:
! 508: /* Unlock server */
! 509: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 510:
! 511: /* Done */
! 512: return (0);
! 513:
! 514: fail:
! 515: /* Clean up after failure */
! 516: if (hook->vhost != NULL) {
! 517: FREE("http_virthost.host", hook->vhost->host);
! 518: FREE("http_virthost", hook->vhost);
! 519: }
! 520: if (got_rwlock)
! 521: pthread_rwlock_destroy(&hook->rwlock);
! 522: if (got_regex)
! 523: regfree(&hook->pat);
! 524: #if PDEL_DEBUG
! 525: FREE("http_servlet_hook.spat", hook->spat);
! 526: #endif
! 527: FREE("http_servlet_hook", hook);
! 528: return (-1);
! 529: }
! 530:
! 531: /*
! 532: * Destroy a servlet, unregistering it if necessary.
! 533: *
! 534: * If it's the last servlet in the virtual host, remove the virtual host too.
! 535: */
! 536: void
! 537: http_server_destroy_servlet(struct http_servlet **servletp)
! 538: {
! 539: struct http_servlet *const servlet = *servletp;
! 540: struct http_servlet_hook *hook;
! 541: struct http_virthost *vhost;
! 542: struct http_server *serv;
! 543: int r;
! 544:
! 545: /* Sanity */
! 546: if (servlet == NULL)
! 547: return;
! 548: *servletp = NULL;
! 549: hook = servlet->hook;
! 550:
! 551: /* If servlet is not registered, just destroy it */
! 552: if (hook == NULL)
! 553: goto done;
! 554: vhost = hook->vhost;
! 555: serv = vhost->server;
! 556:
! 557: /* Lock server */
! 558: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 559:
! 560: /* Unregister servlet from its virtual host */
! 561: LIST_REMOVE(hook, next);
! 562: hook->vhost = NULL; /* for good measure */
! 563:
! 564: /* Remove the virtual host if it has no more servlets */
! 565: if (LIST_EMPTY(&vhost->servlets)) {
! 566: r = ghash_remove(serv->vhosts, vhost);
! 567: assert(r == 1);
! 568: vhost->server = NULL; /* for good measure */
! 569: FREE("http_virthost.host", vhost->host);
! 570: FREE("http_virthost", vhost);
! 571: }
! 572:
! 573: /* Unlock server */
! 574: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 575:
! 576: /* Wait for any threads running this servlet to finish */
! 577: r = pthread_rwlock_wrlock(&hook->rwlock);
! 578: assert(r == 0);
! 579: r = pthread_rwlock_unlock(&hook->rwlock);
! 580: assert(r == 0);
! 581:
! 582: /* Destroy servlet hook */
! 583: regfree(&hook->pat);
! 584: pthread_rwlock_destroy(&hook->rwlock);
! 585: #if PDEL_DEBUG
! 586: FREE("http_servlet_hook.spat", hook->spat);
! 587: #endif
! 588: FREE("http_servlet_hook", hook);
! 589: servlet->hook = NULL; /* for good measure */
! 590:
! 591: done:
! 592: /* Destroy servlet itself */
! 593: (*servlet->destroy)(servlet);
! 594: }
! 595:
! 596: /*
! 597: * Set proxy servlet
! 598: */
! 599: extern void
! 600: http_server_set_proxy_handler(struct http_server *serv,
! 601: http_proxy_t *handler, void *arg)
! 602: {
! 603: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 604: serv->proxy_handler = handler;
! 605: serv->proxy_arg = arg;
! 606: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 607: }
! 608:
! 609: /*********************************************************************
! 610: SERVER MAIN THREAD
! 611: *********************************************************************/
! 612:
! 613: /*
! 614: * Accept a new connection.
! 615: *
! 616: * The mutex will be locked when this is called.
! 617: */
! 618: static void
! 619: http_server_accept(void *arg)
! 620: {
! 621: struct http_server *const serv = arg;
! 622: struct http_connection *conn;
! 623: struct sockaddr_in sin;
! 624: socklen_t slen = sizeof(sin);
! 625: int sock;
! 626:
! 627: /* Accept next connection */
! 628: if ((sock = accept(serv->sock, (struct sockaddr *)&sin, &slen)) == -1) {
! 629: if (errno != ECONNABORTED && errno != ENOTCONN) {
! 630: (*serv->logger)(LOG_ERR, "%s: %s",
! 631: "accept", strerror(errno));
! 632: }
! 633: return;
! 634: }
! 635: (void)fcntl(sock, F_SETFD, 1);
! 636:
! 637: /* Get new connection object */
! 638: if ((conn = _http_connection_create(NULL, sock, 1, sin.sin_addr,
! 639: ntohs(sin.sin_port), serv->ssl, serv->logger,
! 640: HTTP_SERVER_TIMEOUT)) == NULL) {
! 641: (*serv->logger)(LOG_ERR, "%s: %s",
! 642: "_http_connection_create", strerror(errno));
! 643: (void)close(sock);
! 644: return;
! 645: }
! 646: conn->owner = serv;
! 647:
! 648: /* Add to server's list of active connections */
! 649: LIST_INSERT_HEAD(&serv->conn_list, conn, next);
! 650: serv->num_conn++;
! 651:
! 652: /* Spawn a new thread to handle request */
! 653: if ((errno = pthread_create(&conn->tid, NULL,
! 654: http_server_connection_main, conn)) != 0) {
! 655: (*serv->logger)(LOG_ERR, "%s: %s",
! 656: "pthread_create", strerror(errno));
! 657: conn->tid = 0;
! 658: LIST_REMOVE(conn, next);
! 659: serv->num_conn--;
! 660: _http_connection_free(&conn);
! 661: }
! 662:
! 663: /* Detach thread */
! 664: pthread_detach(conn->tid);
! 665: DBG(HTTP, "spawning %p for connection %p from %s:%u",
! 666: conn->tid, conn, inet_ntoa(conn->remote_ip), conn->remote_port);
! 667:
! 668: /* If maximum number of connections reached, stop accepting new ones */
! 669: if (serv->num_conn >= serv->max_conn)
! 670: pevent_unregister(&serv->conn_event);
! 671: }
! 672:
! 673: /*********************************************************************
! 674: SERVER CONNECTION MAIN THREAD
! 675: *********************************************************************/
! 676:
! 677: /*
! 678: * HTTP connection thread main routine.
! 679: */
! 680: static void *
! 681: http_server_connection_main(void *arg)
! 682: {
! 683: struct http_connection *const conn = arg;
! 684: struct http_server *const serv = conn->owner;
! 685:
! 686: /* Add shutdown cleanup hook */
! 687: pthread_cleanup_push(http_server_connection_cleanup, conn);
! 688: conn->started = 1; /* avoids pthread_cancel() race */
! 689: DBG(HTTP, "connection %p started", conn);
! 690:
! 691: /* Read requests as long as connection is kept alive */
! 692: conn->keep_alive = 1;
! 693: while (1) {
! 694: struct http_request *const req = conn->req;
! 695: struct http_response *const resp = conn->resp;
! 696: const char *hval;
! 697: char dbuf[64];
! 698: struct tm tm;
! 699: time_t now;
! 700:
! 701: /* Read in request */
! 702: if (_http_request_read(conn) == -1) {
! 703: if (errno == ENOTCONN) /* remote side disconnected */
! 704: break;
! 705: conn->keep_alive = 0;
! 706: goto send_response;
! 707: }
! 708:
! 709: /* Set default response headers */
! 710: now = time(NULL);
! 711: strftime(dbuf, sizeof(dbuf),
! 712: HTTP_TIME_FMT_RFC1123, gmtime_r(&now, &tm));
! 713: if (http_response_set_header(resp, 0,
! 714: HDR_REPLY_VERSION, HTTP_PROTO_1_1) == -1
! 715: || http_response_set_header(resp, 0, HDR_REPLY_STATUS,
! 716: "%d", HTTP_STATUS_OK) == -1
! 717: || http_response_set_header(resp, 0, HDR_REPLY_REASON,
! 718: "%s", http_response_status_msg(HTTP_STATUS_OK)) == -1
! 719: || http_response_set_header(resp, 0, HTTP_HEADER_SERVER,
! 720: "%s", serv->server_name) == -1
! 721: || http_response_set_header(resp, 0, HTTP_HEADER_DATE,
! 722: "%s", dbuf) == -1
! 723: || http_response_set_header(resp, 0,
! 724: HTTP_HEADER_LAST_MODIFIED, "%s", dbuf) == -1) {
! 725: conn->keep_alive = 0;
! 726: goto send_response;
! 727: }
! 728:
! 729: /* Turn off keep-alive if client doesn't want it */
! 730: if (!_http_head_want_keepalive(req->msg->head))
! 731: conn->keep_alive = 0;
! 732:
! 733: /* Handle request */
! 734: http_server_dispatch(req, resp);
! 735:
! 736: /* Set Connection: header if not already set */
! 737: if (((hval = http_request_get_method(req)) == NULL
! 738: || strcmp(hval, HTTP_METHOD_CONNECT) != 0)
! 739: && http_response_get_header(resp,
! 740: _http_message_connection_header(resp->msg)) == NULL) {
! 741: http_response_set_header(resp, 0,
! 742: _http_message_connection_header(resp->msg),
! 743: conn->keep_alive ? "Keep-Alive" : "Close");
! 744: }
! 745:
! 746: /* Slurp up any remaining entity data if keep-alive */
! 747: if (conn->keep_alive && req != NULL && !feof(req->msg->input)) {
! 748: static char buf[1024];
! 749:
! 750: while (fgets(buf, sizeof(buf), req->msg->input) != NULL)
! 751: ;
! 752: }
! 753:
! 754: send_response:
! 755: /* Send back headers (if not already sent) */
! 756: http_response_send_headers(resp, 0);
! 757:
! 758: /* Send back body (if it was buffered) */
! 759: _http_message_send_body(resp->msg);
! 760:
! 761: /* Determine if we can still keep this connection alive */
! 762: if (!resp->msg->no_body
! 763: && (http_response_get_header(resp,
! 764: HTTP_HEADER_CONTENT_LENGTH) == NULL
! 765: || ferror(conn->fp)
! 766: || feof(conn->fp)
! 767: || !_http_head_want_keepalive(resp->msg->head)))
! 768: conn->keep_alive = 0;
! 769:
! 770: /* Close connection unless keeping it alive */
! 771: if (!conn->keep_alive)
! 772: break;
! 773:
! 774: /* Reset request & response for next time */
! 775: _http_request_free(&conn->req);
! 776: _http_response_free(&conn->resp);
! 777: if (_http_request_new(conn) == -1
! 778: || _http_response_new(conn) == -1)
! 779: break;
! 780: }
! 781:
! 782: /* Done with this connection */
! 783: DBG(HTTP, "done with connection %p", conn);
! 784: pthread_cleanup_pop(1);
! 785: return (NULL);
! 786: }
! 787:
! 788: /*
! 789: * Cleanup when shutting down an HTTP server connection.
! 790: */
! 791: static void
! 792: http_server_connection_cleanup(void *arg)
! 793: {
! 794: struct http_connection *conn = arg;
! 795: struct http_server *const serv = conn->owner;
! 796:
! 797: /* Lock server */
! 798: DBG(HTTP, "connection %p cleaning up", conn);
! 799: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 800:
! 801: /* Restart accepting new connections if needed */
! 802: if (serv->conn_event == NULL && !serv->stopping) {
! 803: if (pevent_register(serv->ctx, &serv->conn_event,
! 804: PEVENT_RECURRING, &serv->mutex, http_server_accept,
! 805: serv, PEVENT_READ, serv->sock) == -1) {
! 806: (*serv->logger)(LOG_ERR, "%s: %s",
! 807: "pevent_register", strerror(errno));
! 808: }
! 809: }
! 810:
! 811: /* Remove this connection from the list of connections */
! 812: LIST_REMOVE(conn, next);
! 813: serv->num_conn--;
! 814:
! 815: /* Unlock server */
! 816: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 817:
! 818: /* Free connection */
! 819: _http_connection_free(&conn);
! 820: }
! 821:
! 822: /*
! 823: * Dispatch a request
! 824: */
! 825: static void
! 826: http_server_dispatch(struct http_request *req, struct http_response *resp)
! 827: {
! 828: struct http_connection *conn = req->msg->conn;
! 829: struct http_server *const serv = conn->owner;
! 830: const char *url = http_request_get_path(req);
! 831: struct http_virthost vhost_key;
! 832: char buf[MAXHOSTNAMELEN];
! 833: const char *hval;
! 834: int order;
! 835: int done;
! 836:
! 837: /* Special handling for proxy requests */
! 838: if (conn->proxy) {
! 839: http_proxy_t *proxy_handler;
! 840: void *proxy_arg;
! 841:
! 842: /* Get proxy handler from server */
! 843: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 844: proxy_handler = serv->proxy_handler;
! 845: proxy_arg = serv->proxy_arg;
! 846: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 847:
! 848: /* Handle request */
! 849: if (proxy_handler == NULL) {
! 850: http_response_send_error(resp,
! 851: (strcmp(http_request_get_method(req),
! 852: HTTP_METHOD_CONNECT) == 0) ?
! 853: HTTP_STATUS_METHOD_NOT_ALLOWED :
! 854: HTTP_STATUS_NOT_FOUND, NULL);
! 855: return;
! 856: }
! 857: (*proxy_handler)(proxy_arg, req, resp);
! 858: return;
! 859: }
! 860:
! 861: /* Extract the desired virtual host from the request */
! 862: if ((hval = http_request_get_header(req, HTTP_HEADER_HOST)) != NULL) {
! 863: char *s;
! 864:
! 865: strlcpy(buf, hval, sizeof(buf));
! 866: if ((s = strchr(buf, ':')) != NULL) /* trim port */
! 867: *s = '\0';
! 868: } else
! 869: *buf = '\0'; /* fall back to default vhost */
! 870: vhost_key.host = buf;
! 871:
! 872: /* Lock server */
! 873: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 874:
! 875: /* Choose and fix desired virtual host before running any servlets */
! 876: if (ghash_get(serv->vhosts, &vhost_key) == NULL)
! 877: *buf = '\0'; /* fall back to default vhost */
! 878:
! 879: /* Unlock server */
! 880: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 881:
! 882: /* Execute matching vhost servlets, in order, until there is output */
! 883: for (done = 0, order = INT_MIN; !done; ) {
! 884: struct http_servlet_hook *best = NULL;
! 885: struct http_servlet_hook *hook;
! 886: struct http_virthost *vhost;
! 887: int r;
! 888:
! 889: /* Lock server */
! 890: MUTEX_LOCK(&serv->mutex, serv->mutex_count);
! 891:
! 892: /* Find virtual host (do this each time because we unlock) */
! 893: if ((vhost = ghash_get(serv->vhosts, &vhost_key)) == NULL) {
! 894: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 895: break;
! 896: }
! 897:
! 898: /* Find lowest order virtual host servlet that matches */
! 899: LIST_FOREACH(hook, &vhost->servlets, next) {
! 900:
! 901: /* Skip if we've already tried it */
! 902: if (hook->order <= order)
! 903: continue;
! 904:
! 905: /* Compare URL path, choosing best match as we go */
! 906: if ((r = regexec(&hook->pat, url, 0, NULL, 0)) == 0) {
! 907: if (best == NULL || hook->order < best->order)
! 908: best = hook;
! 909: continue;
! 910: }
! 911:
! 912: /* If it didn't match, try the next servlet */
! 913: if (r == REG_NOMATCH)
! 914: continue;
! 915:
! 916: /* We got some weird error from regexec() */
! 917: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 918: regerror(r, &hook->pat, buf, sizeof(buf));
! 919: (*serv->logger)(LOG_ERR, "%s: %s", "regexec", buf);
! 920: http_response_send_error(resp,
! 921: HTTP_STATUS_INTERNAL_SERVER_ERROR,
! 922: "regexec: %s", buf);
! 923: return;
! 924: }
! 925:
! 926: /* If no matching servlet was found, we're done */
! 927: if ((hook = best) == NULL) {
! 928: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 929: break;
! 930: }
! 931:
! 932: /* Update lowest order for next iteration */
! 933: order = hook->order;
! 934:
! 935: /* Lock servlet to prevent it from going away */
! 936: r = pthread_rwlock_rdlock(&hook->rwlock);
! 937: assert(r == 0);
! 938:
! 939: /* Add cleanup hook that unlocks servlet */
! 940: pthread_cleanup_push((void (*)(void *))pthread_rwlock_unlock,
! 941: &hook->rwlock);
! 942:
! 943: /* Unlock server */
! 944: MUTEX_UNLOCK(&serv->mutex, serv->mutex_count);
! 945:
! 946: /* Execute servlet */
! 947: done = (*hook->servlet->run)(hook->servlet, req, resp);
! 948:
! 949: /* Unlock servlet */
! 950: pthread_cleanup_pop(1);
! 951:
! 952: /* If servlet returned an error, bail out */
! 953: if (done == -1) {
! 954: http_response_send_errno_error(resp);
! 955: break;
! 956: }
! 957: }
! 958:
! 959: /* No matching servlet found? */
! 960: if (!done)
! 961: http_response_send_error(resp, HTTP_STATUS_NOT_FOUND, NULL);
! 962: }
! 963:
! 964: /*********************************************************************
! 965: MISC ROUTINES
! 966: *********************************************************************/
! 967:
! 968: /*
! 969: * SSL callback to get the password for encrypted private key files.
! 970: */
! 971: static int
! 972: http_server_ssl_pem_password_cb(char *buf, int size, int rwflag, void *udata)
! 973: {
! 974: struct http_server *const serv = udata;
! 975:
! 976: if (serv->pkey_pw == NULL) {
! 977: (*serv->logger)(LOG_ERR,
! 978: "SSL private key is encrypted but no key was supplied");
! 979: return (-1);
! 980: }
! 981: strlcpy(buf, serv->pkey_pw, size);
! 982: return (strlen(buf));
! 983: }
! 984:
! 985: /*
! 986: * SSL callback for logging.
! 987: */
! 988: static void
! 989: http_server_ssl_logger(void *arg, int sev, const char *fmt, ...)
! 990: {
! 991: struct http_server *const serv = arg;
! 992: va_list args;
! 993: char *s;
! 994:
! 995: /* Prepend "startup" message to error message */
! 996: va_start(args, fmt);
! 997: VASPRINTF(TYPED_MEM_TEMP, &s, fmt, args);
! 998: va_end(args);
! 999: if (s == NULL)
! 1000: return;
! 1001: (*serv->logger)(sev, "%s: %s", "startup error", s);
! 1002: FREE(TYPED_MEM_TEMP, s);
! 1003: }
! 1004:
! 1005: /*
! 1006: * Virtual host hashing routines
! 1007: */
! 1008: static int
! 1009: http_server_virthost_equal(struct ghash *g,
! 1010: const void *item1, const void *item2)
! 1011: {
! 1012: const struct http_virthost *const vhost1 = item1;
! 1013: const struct http_virthost *const vhost2 = item2;
! 1014:
! 1015: return (strcasecmp(vhost1->host, vhost2->host) == 0);
! 1016: }
! 1017:
! 1018: u_int32_t
! 1019: http_server_virthost_hash(struct ghash *g, const void *item)
! 1020: {
! 1021: const struct http_virthost *const vhost = item;
! 1022: u_int32_t hash;
! 1023: const char *s;
! 1024:
! 1025: for (hash = 0, s = vhost->host; *s != '\0'; s++)
! 1026: hash = (hash * 31) + (u_char)tolower(*s);
! 1027: return (hash);
! 1028: }
! 1029:
! 1030:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>