Annotation of embedaddon/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>