Annotation of embedaddon/lighttpd/src/mod_proxy.c, revision 1.1

1.1     ! misho       1: #include "buffer.h"
        !             2: #include "server.h"
        !             3: #include "keyvalue.h"
        !             4: #include "log.h"
        !             5: 
        !             6: #include "http_chunk.h"
        !             7: #include "fdevent.h"
        !             8: #include "connections.h"
        !             9: #include "response.h"
        !            10: #include "joblist.h"
        !            11: 
        !            12: #include "plugin.h"
        !            13: 
        !            14: #include "inet_ntop_cache.h"
        !            15: #include "crc32.h"
        !            16: 
        !            17: #include <sys/types.h>
        !            18: 
        !            19: #include <unistd.h>
        !            20: #include <errno.h>
        !            21: #include <fcntl.h>
        !            22: #include <string.h>
        !            23: #include <stdlib.h>
        !            24: #include <ctype.h>
        !            25: #include <assert.h>
        !            26: 
        !            27: #include <stdio.h>
        !            28: 
        !            29: #ifdef HAVE_SYS_FILIO_H
        !            30: # include <sys/filio.h>
        !            31: #endif
        !            32: 
        !            33: #include "sys-socket.h"
        !            34: 
        !            35: #define data_proxy data_fastcgi
        !            36: #define data_proxy_init data_fastcgi_init
        !            37: 
        !            38: #define PROXY_RETRY_TIMEOUT 60
        !            39: 
        !            40: /**
        !            41:  *
        !            42:  * the proxy module is based on the fastcgi module
        !            43:  *
        !            44:  * 28.06.2004 Jan Kneschke     The first release
        !            45:  * 01.07.2004 Evgeny Rodichev  Several bugfixes and cleanups
        !            46:  *            - co-ordinate up- and downstream flows correctly (proxy_demux_response
        !            47:  *              and proxy_handle_fdevent)
        !            48:  *            - correctly transfer upstream http_response_status;
        !            49:  *            - some unused structures removed.
        !            50:  *
        !            51:  * TODO:      - delay upstream read if write_queue is too large
        !            52:  *              (to prevent memory eating, like in apache). Shoud be
        !            53:  *              configurable).
        !            54:  *            - persistent connection with upstream servers
        !            55:  *            - HTTP/1.1
        !            56:  */
        !            57: typedef enum {
        !            58:        PROXY_BALANCE_UNSET,
        !            59:        PROXY_BALANCE_FAIR,
        !            60:        PROXY_BALANCE_HASH,
        !            61:        PROXY_BALANCE_RR
        !            62: } proxy_balance_t;
        !            63: 
        !            64: typedef struct {
        !            65:        array *extensions;
        !            66:        unsigned short debug;
        !            67: 
        !            68:        proxy_balance_t balance;
        !            69: } plugin_config;
        !            70: 
        !            71: typedef struct {
        !            72:        PLUGIN_DATA;
        !            73: 
        !            74:        buffer *parse_response;
        !            75:        buffer *balance_buf;
        !            76: 
        !            77:        plugin_config **config_storage;
        !            78: 
        !            79:        plugin_config conf;
        !            80: } plugin_data;
        !            81: 
        !            82: typedef enum {
        !            83:        PROXY_STATE_INIT,
        !            84:        PROXY_STATE_CONNECT,
        !            85:        PROXY_STATE_PREPARE_WRITE,
        !            86:        PROXY_STATE_WRITE,
        !            87:        PROXY_STATE_READ,
        !            88:        PROXY_STATE_ERROR
        !            89: } proxy_connection_state_t;
        !            90: 
        !            91: enum { PROXY_STDOUT, PROXY_END_REQUEST };
        !            92: 
        !            93: typedef struct {
        !            94:        proxy_connection_state_t state;
        !            95:        time_t state_timestamp;
        !            96: 
        !            97:        data_proxy *host;
        !            98: 
        !            99:        buffer *response;
        !           100:        buffer *response_header;
        !           101: 
        !           102:        chunkqueue *wb;
        !           103: 
        !           104:        int fd; /* fd to the proxy process */
        !           105:        int fde_ndx; /* index into the fd-event buffer */
        !           106: 
        !           107:        size_t path_info_offset; /* start of path_info in uri.path */
        !           108: 
        !           109:        connection *remote_conn;  /* dump pointer */
        !           110:        plugin_data *plugin_data; /* dump pointer */
        !           111: } handler_ctx;
        !           112: 
        !           113: 
        !           114: /* ok, we need a prototype */
        !           115: static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents);
        !           116: 
        !           117: static handler_ctx * handler_ctx_init(void) {
        !           118:        handler_ctx * hctx;
        !           119: 
        !           120: 
        !           121:        hctx = calloc(1, sizeof(*hctx));
        !           122: 
        !           123:        hctx->state = PROXY_STATE_INIT;
        !           124:        hctx->host = NULL;
        !           125: 
        !           126:        hctx->response = buffer_init();
        !           127:        hctx->response_header = buffer_init();
        !           128: 
        !           129:        hctx->wb = chunkqueue_init();
        !           130: 
        !           131:        hctx->fd = -1;
        !           132:        hctx->fde_ndx = -1;
        !           133: 
        !           134:        return hctx;
        !           135: }
        !           136: 
        !           137: static void handler_ctx_free(handler_ctx *hctx) {
        !           138:        buffer_free(hctx->response);
        !           139:        buffer_free(hctx->response_header);
        !           140:        chunkqueue_free(hctx->wb);
        !           141: 
        !           142:        free(hctx);
        !           143: }
        !           144: 
        !           145: INIT_FUNC(mod_proxy_init) {
        !           146:        plugin_data *p;
        !           147: 
        !           148:        p = calloc(1, sizeof(*p));
        !           149: 
        !           150:        p->parse_response = buffer_init();
        !           151:        p->balance_buf = buffer_init();
        !           152: 
        !           153:        return p;
        !           154: }
        !           155: 
        !           156: 
        !           157: FREE_FUNC(mod_proxy_free) {
        !           158:        plugin_data *p = p_d;
        !           159: 
        !           160:        UNUSED(srv);
        !           161: 
        !           162:        buffer_free(p->parse_response);
        !           163:        buffer_free(p->balance_buf);
        !           164: 
        !           165:        if (p->config_storage) {
        !           166:                size_t i;
        !           167:                for (i = 0; i < srv->config_context->used; i++) {
        !           168:                        plugin_config *s = p->config_storage[i];
        !           169: 
        !           170:                        if (s) {
        !           171: 
        !           172:                                array_free(s->extensions);
        !           173: 
        !           174:                                free(s);
        !           175:                        }
        !           176:                }
        !           177:                free(p->config_storage);
        !           178:        }
        !           179: 
        !           180:        free(p);
        !           181: 
        !           182:        return HANDLER_GO_ON;
        !           183: }
        !           184: 
        !           185: SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
        !           186:        plugin_data *p = p_d;
        !           187:        data_unset *du;
        !           188:        size_t i = 0;
        !           189: 
        !           190:        config_values_t cv[] = {
        !           191:                { "proxy.server",              NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
        !           192:                { "proxy.debug",               NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
        !           193:                { "proxy.balance",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 2 */
        !           194:                { NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
        !           195:        };
        !           196: 
        !           197:        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
        !           198: 
        !           199:        for (i = 0; i < srv->config_context->used; i++) {
        !           200:                plugin_config *s;
        !           201:                array *ca;
        !           202: 
        !           203:                s = malloc(sizeof(plugin_config));
        !           204:                s->extensions    = array_init();
        !           205:                s->debug         = 0;
        !           206: 
        !           207:                cv[0].destination = s->extensions;
        !           208:                cv[1].destination = &(s->debug);
        !           209:                cv[2].destination = p->balance_buf;
        !           210: 
        !           211:                buffer_reset(p->balance_buf);
        !           212: 
        !           213:                p->config_storage[i] = s;
        !           214:                ca = ((data_config *)srv->config_context->data[i])->value;
        !           215: 
        !           216:                if (0 != config_insert_values_global(srv, ca, cv)) {
        !           217:                        return HANDLER_ERROR;
        !           218:                }
        !           219: 
        !           220:                if (buffer_is_empty(p->balance_buf)) {
        !           221:                        s->balance = PROXY_BALANCE_FAIR;
        !           222:                } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) {
        !           223:                        s->balance = PROXY_BALANCE_FAIR;
        !           224:                } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("round-robin"))) {
        !           225:                        s->balance = PROXY_BALANCE_RR;
        !           226:                } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("hash"))) {
        !           227:                        s->balance = PROXY_BALANCE_HASH;
        !           228:                } else {
        !           229:                        log_error_write(srv, __FILE__, __LINE__, "sb",
        !           230:                                        "proxy.balance has to be one of: fair, round-robin, hash, but not:", p->balance_buf);
        !           231:                        return HANDLER_ERROR;
        !           232:                }
        !           233: 
        !           234:                if (NULL != (du = array_get_element(ca, "proxy.server"))) {
        !           235:                        size_t j;
        !           236:                        data_array *da = (data_array *)du;
        !           237: 
        !           238:                        if (du->type != TYPE_ARRAY) {
        !           239:                                log_error_write(srv, __FILE__, __LINE__, "sss",
        !           240:                                                "unexpected type for key: ", "proxy.server", "array of strings");
        !           241: 
        !           242:                                return HANDLER_ERROR;
        !           243:                        }
        !           244: 
        !           245:                        /*
        !           246:                         * proxy.server = ( "<ext>" => ...,
        !           247:                         *                  "<ext>" => ... )
        !           248:                         */
        !           249: 
        !           250:                        for (j = 0; j < da->value->used; j++) {
        !           251:                                data_array *da_ext = (data_array *)da->value->data[j];
        !           252:                                size_t n;
        !           253: 
        !           254:                                if (da_ext->type != TYPE_ARRAY) {
        !           255:                                        log_error_write(srv, __FILE__, __LINE__, "sssbs",
        !           256:                                                        "unexpected type for key: ", "proxy.server",
        !           257:                                                        "[", da->value->data[j]->key, "](string)");
        !           258: 
        !           259:                                        return HANDLER_ERROR;
        !           260:                                }
        !           261: 
        !           262:                                /*
        !           263:                                 * proxy.server = ( "<ext>" =>
        !           264:                                 *                     ( "<host>" => ( ... ),
        !           265:                                 *                       "<host>" => ( ... )
        !           266:                                 *                     ),
        !           267:                                 *                    "<ext>" => ... )
        !           268:                                 */
        !           269: 
        !           270:                                for (n = 0; n < da_ext->value->used; n++) {
        !           271:                                        data_array *da_host = (data_array *)da_ext->value->data[n];
        !           272: 
        !           273:                                        data_proxy *df;
        !           274:                                        data_array *dfa;
        !           275: 
        !           276:                                        config_values_t pcv[] = {
        !           277:                                                { "host",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 0 */
        !           278:                                                { "port",              NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
        !           279:                                                { NULL,                NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
        !           280:                                        };
        !           281: 
        !           282:                                        if (da_host->type != TYPE_ARRAY) {
        !           283:                                                log_error_write(srv, __FILE__, __LINE__, "ssSBS",
        !           284:                                                                "unexpected type for key:",
        !           285:                                                                "proxy.server",
        !           286:                                                                "[", da_ext->value->data[n]->key, "](string)");
        !           287: 
        !           288:                                                return HANDLER_ERROR;
        !           289:                                        }
        !           290: 
        !           291:                                        df = data_proxy_init();
        !           292: 
        !           293:                                        df->port = 80;
        !           294: 
        !           295:                                        buffer_copy_string_buffer(df->key, da_host->key);
        !           296: 
        !           297:                                        pcv[0].destination = df->host;
        !           298:                                        pcv[1].destination = &(df->port);
        !           299: 
        !           300:                                        if (0 != config_insert_values_internal(srv, da_host->value, pcv)) {
        !           301:                                                return HANDLER_ERROR;
        !           302:                                        }
        !           303: 
        !           304:                                        if (buffer_is_empty(df->host)) {
        !           305:                                                log_error_write(srv, __FILE__, __LINE__, "sbbbs",
        !           306:                                                                "missing key (string):",
        !           307:                                                                da->key,
        !           308:                                                                da_ext->key,
        !           309:                                                                da_host->key,
        !           310:                                                                "host");
        !           311: 
        !           312:                                                return HANDLER_ERROR;
        !           313:                                        }
        !           314: 
        !           315:                                        /* if extension already exists, take it */
        !           316: 
        !           317:                                        if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) {
        !           318:                                                dfa = data_array_init();
        !           319: 
        !           320:                                                buffer_copy_string_buffer(dfa->key, da_ext->key);
        !           321: 
        !           322:                                                array_insert_unique(dfa->value, (data_unset *)df);
        !           323:                                                array_insert_unique(s->extensions, (data_unset *)dfa);
        !           324:                                        } else {
        !           325:                                                array_insert_unique(dfa->value, (data_unset *)df);
        !           326:                                        }
        !           327:                                }
        !           328:                        }
        !           329:                }
        !           330:        }
        !           331: 
        !           332:        return HANDLER_GO_ON;
        !           333: }
        !           334: 
        !           335: static void proxy_connection_close(server *srv, handler_ctx *hctx) {
        !           336:        plugin_data *p;
        !           337:        connection *con;
        !           338: 
        !           339:        if (NULL == hctx) return;
        !           340: 
        !           341:        p    = hctx->plugin_data;
        !           342:        con  = hctx->remote_conn;
        !           343: 
        !           344:        if (hctx->fd != -1) {
        !           345:                fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
        !           346:                fdevent_unregister(srv->ev, hctx->fd);
        !           347: 
        !           348:                close(hctx->fd);
        !           349:                srv->cur_fds--;
        !           350:        }
        !           351: 
        !           352:        if (hctx->host) {
        !           353:                hctx->host->usage--;
        !           354:        }
        !           355: 
        !           356:        handler_ctx_free(hctx);
        !           357:        con->plugin_ctx[p->id] = NULL;
        !           358: }
        !           359: 
        !           360: static int proxy_establish_connection(server *srv, handler_ctx *hctx) {
        !           361:        struct sockaddr *proxy_addr;
        !           362:        struct sockaddr_in proxy_addr_in;
        !           363: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
        !           364:        struct sockaddr_in6 proxy_addr_in6;
        !           365: #endif
        !           366:        socklen_t servlen;
        !           367: 
        !           368:        plugin_data *p    = hctx->plugin_data;
        !           369:        data_proxy *host= hctx->host;
        !           370:        int proxy_fd       = hctx->fd;
        !           371: 
        !           372: 
        !           373: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
        !           374:        if (strstr(host->host->ptr, ":")) {
        !           375:                memset(&proxy_addr_in6, 0, sizeof(proxy_addr_in6));
        !           376:                proxy_addr_in6.sin6_family = AF_INET6;
        !           377:                inet_pton(AF_INET6, host->host->ptr, (char *) &proxy_addr_in6.sin6_addr);
        !           378:                proxy_addr_in6.sin6_port = htons(host->port);
        !           379:                servlen = sizeof(proxy_addr_in6);
        !           380:                proxy_addr = (struct sockaddr *) &proxy_addr_in6;
        !           381:        } else
        !           382: #endif
        !           383:        {
        !           384:                memset(&proxy_addr_in, 0, sizeof(proxy_addr_in));
        !           385:                proxy_addr_in.sin_family = AF_INET;
        !           386:                proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr);
        !           387:                proxy_addr_in.sin_port = htons(host->port);
        !           388:                servlen = sizeof(proxy_addr_in);
        !           389:                proxy_addr = (struct sockaddr *) &proxy_addr_in;
        !           390:        }
        !           391: 
        !           392: 
        !           393:        if (-1 == connect(proxy_fd, proxy_addr, servlen)) {
        !           394:                if (errno == EINPROGRESS || errno == EALREADY) {
        !           395:                        if (p->conf.debug) {
        !           396:                                log_error_write(srv, __FILE__, __LINE__, "sd",
        !           397:                                                "connect delayed:", proxy_fd);
        !           398:                        }
        !           399: 
        !           400:                        return 1;
        !           401:                } else {
        !           402: 
        !           403:                        log_error_write(srv, __FILE__, __LINE__, "sdsd",
        !           404:                                        "connect failed:", proxy_fd, strerror(errno), errno);
        !           405: 
        !           406:                        return -1;
        !           407:                }
        !           408:        }
        !           409:        if (p->conf.debug) {
        !           410:                log_error_write(srv, __FILE__, __LINE__, "sd",
        !           411:                                "connect succeeded: ", proxy_fd);
        !           412:        }
        !           413: 
        !           414:        return 0;
        !           415: }
        !           416: 
        !           417: static void proxy_set_header(connection *con, const char *key, const char *value) {
        !           418:     data_string *ds_dst;
        !           419: 
        !           420:     if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
        !           421:           ds_dst = data_string_init();
        !           422:     }
        !           423: 
        !           424:     buffer_copy_string(ds_dst->key, key);
        !           425:     buffer_copy_string(ds_dst->value, value);
        !           426:     array_insert_unique(con->request.headers, (data_unset *)ds_dst);
        !           427: }
        !           428: 
        !           429: static void proxy_append_header(connection *con, const char *key, const char *value) {
        !           430:     data_string *ds_dst;
        !           431: 
        !           432:     if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
        !           433:           ds_dst = data_string_init();
        !           434:     }
        !           435: 
        !           436:     buffer_copy_string(ds_dst->key, key);
        !           437:     buffer_append_string(ds_dst->value, value);
        !           438:     array_insert_unique(con->request.headers, (data_unset *)ds_dst);
        !           439: }
        !           440: 
        !           441: 
        !           442: static int proxy_create_env(server *srv, handler_ctx *hctx) {
        !           443:        size_t i;
        !           444: 
        !           445:        connection *con   = hctx->remote_conn;
        !           446:        buffer *b;
        !           447: 
        !           448:        /* build header */
        !           449: 
        !           450:        b = chunkqueue_get_append_buffer(hctx->wb);
        !           451: 
        !           452:        /* request line */
        !           453:        buffer_copy_string(b, get_http_method_name(con->request.http_method));
        !           454:        buffer_append_string_len(b, CONST_STR_LEN(" "));
        !           455: 
        !           456:        buffer_append_string_buffer(b, con->request.uri);
        !           457:        buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n"));
        !           458: 
        !           459:        proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
        !           460:        /* http_host is NOT is just a pointer to a buffer
        !           461:         * which is NULL if it is not set */
        !           462:        if (con->request.http_host &&
        !           463:            !buffer_is_empty(con->request.http_host)) {
        !           464:                proxy_set_header(con, "X-Host", con->request.http_host->ptr);
        !           465:        }
        !           466:        proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr);
        !           467: 
        !           468:        /* request header */
        !           469:        for (i = 0; i < con->request.headers->used; i++) {
        !           470:                data_string *ds;
        !           471: 
        !           472:                ds = (data_string *)con->request.headers->data[i];
        !           473: 
        !           474:                if (ds->value->used && ds->key->used) {
        !           475:                        if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue;
        !           476:                        if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue;
        !           477: 
        !           478:                        buffer_append_string_buffer(b, ds->key);
        !           479:                        buffer_append_string_len(b, CONST_STR_LEN(": "));
        !           480:                        buffer_append_string_buffer(b, ds->value);
        !           481:                        buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
        !           482:                }
        !           483:        }
        !           484: 
        !           485:        buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
        !           486: 
        !           487:        hctx->wb->bytes_in += b->used - 1;
        !           488:        /* body */
        !           489: 
        !           490:        if (con->request.content_length) {
        !           491:                chunkqueue *req_cq = con->request_content_queue;
        !           492:                chunk *req_c;
        !           493:                off_t offset;
        !           494: 
        !           495:                /* something to send ? */
        !           496:                for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; req_c = req_c->next) {
        !           497:                        off_t weWant = req_cq->bytes_in - offset;
        !           498:                        off_t weHave = 0;
        !           499: 
        !           500:                        /* we announce toWrite octects
        !           501:                         * now take all the request_content chunk that we need to fill this request
        !           502:                         * */
        !           503: 
        !           504:                        switch (req_c->type) {
        !           505:                        case FILE_CHUNK:
        !           506:                                weHave = req_c->file.length - req_c->offset;
        !           507: 
        !           508:                                if (weHave > weWant) weHave = weWant;
        !           509: 
        !           510:                                chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave);
        !           511: 
        !           512:                                req_c->offset += weHave;
        !           513:                                req_cq->bytes_out += weHave;
        !           514: 
        !           515:                                hctx->wb->bytes_in += weHave;
        !           516: 
        !           517:                                break;
        !           518:                        case MEM_CHUNK:
        !           519:                                /* append to the buffer */
        !           520:                                weHave = req_c->mem->used - 1 - req_c->offset;
        !           521: 
        !           522:                                if (weHave > weWant) weHave = weWant;
        !           523: 
        !           524:                                b = chunkqueue_get_append_buffer(hctx->wb);
        !           525:                                buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave);
        !           526:                                b->used++; /* add virtual \0 */
        !           527: 
        !           528:                                req_c->offset += weHave;
        !           529:                                req_cq->bytes_out += weHave;
        !           530: 
        !           531:                                hctx->wb->bytes_in += weHave;
        !           532: 
        !           533:                                break;
        !           534:                        default:
        !           535:                                break;
        !           536:                        }
        !           537: 
        !           538:                        offset += weHave;
        !           539:                }
        !           540: 
        !           541:        }
        !           542: 
        !           543:        return 0;
        !           544: }
        !           545: 
        !           546: static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_state_t state) {
        !           547:        hctx->state = state;
        !           548:        hctx->state_timestamp = srv->cur_ts;
        !           549: 
        !           550:        return 0;
        !           551: }
        !           552: 
        !           553: 
        !           554: static int proxy_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
        !           555:        char *s, *ns;
        !           556:        int http_response_status = -1;
        !           557: 
        !           558:        UNUSED(srv);
        !           559: 
        !           560:        /* \r\n -> \0\0 */
        !           561: 
        !           562:        buffer_copy_string_buffer(p->parse_response, in);
        !           563: 
        !           564:        for (s = p->parse_response->ptr; NULL != (ns = strstr(s, "\r\n")); s = ns + 2) {
        !           565:                char *key, *value;
        !           566:                int key_len;
        !           567:                data_string *ds;
        !           568:                int copy_header;
        !           569: 
        !           570:                ns[0] = '\0';
        !           571:                ns[1] = '\0';
        !           572: 
        !           573:                if (-1 == http_response_status) {
        !           574:                        /* The first line of a Response message is the Status-Line */
        !           575: 
        !           576:                        for (key=s; *key && *key != ' '; key++);
        !           577: 
        !           578:                        if (*key) {
        !           579:                                http_response_status = (int) strtol(key, NULL, 10);
        !           580:                                if (http_response_status <= 0) http_response_status = 502;
        !           581:                        } else {
        !           582:                                http_response_status = 502;
        !           583:                        }
        !           584: 
        !           585:                        con->http_status = http_response_status;
        !           586:                        con->parsed_response |= HTTP_STATUS;
        !           587:                        continue;
        !           588:                }
        !           589: 
        !           590:                if (NULL == (value = strchr(s, ':'))) {
        !           591:                        /* now we expect: "<key>: <value>\n" */
        !           592: 
        !           593:                        continue;
        !           594:                }
        !           595: 
        !           596:                key = s;
        !           597:                key_len = value - key;
        !           598: 
        !           599:                value++;
        !           600:                /* strip WS */
        !           601:                while (*value == ' ' || *value == '\t') value++;
        !           602: 
        !           603:                copy_header = 1;
        !           604: 
        !           605:                switch(key_len) {
        !           606:                case 4:
        !           607:                        if (0 == strncasecmp(key, "Date", key_len)) {
        !           608:                                con->parsed_response |= HTTP_DATE;
        !           609:                        }
        !           610:                        break;
        !           611:                case 8:
        !           612:                        if (0 == strncasecmp(key, "Location", key_len)) {
        !           613:                                con->parsed_response |= HTTP_LOCATION;
        !           614:                        }
        !           615:                        break;
        !           616:                case 10:
        !           617:                        if (0 == strncasecmp(key, "Connection", key_len)) {
        !           618:                                copy_header = 0;
        !           619:                        }
        !           620:                        break;
        !           621:                case 14:
        !           622:                        if (0 == strncasecmp(key, "Content-Length", key_len)) {
        !           623:                                con->response.content_length = strtol(value, NULL, 10);
        !           624:                                con->parsed_response |= HTTP_CONTENT_LENGTH;
        !           625:                        }
        !           626:                        break;
        !           627:                default:
        !           628:                        break;
        !           629:                }
        !           630: 
        !           631:                if (copy_header) {
        !           632:                        if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
        !           633:                                ds = data_response_init();
        !           634:                        }
        !           635:                        buffer_copy_string_len(ds->key, key, key_len);
        !           636:                        buffer_copy_string(ds->value, value);
        !           637: 
        !           638:                        array_insert_unique(con->response.headers, (data_unset *)ds);
        !           639:                }
        !           640:        }
        !           641: 
        !           642:        return 0;
        !           643: }
        !           644: 
        !           645: 
        !           646: static int proxy_demux_response(server *srv, handler_ctx *hctx) {
        !           647:        int fin = 0;
        !           648:        int b;
        !           649:        ssize_t r;
        !           650: 
        !           651:        plugin_data *p    = hctx->plugin_data;
        !           652:        connection *con   = hctx->remote_conn;
        !           653:        int proxy_fd       = hctx->fd;
        !           654: 
        !           655:        /* check how much we have to read */
        !           656:        if (ioctl(hctx->fd, FIONREAD, &b)) {
        !           657:                log_error_write(srv, __FILE__, __LINE__, "sd",
        !           658:                                "ioctl failed: ",
        !           659:                                proxy_fd);
        !           660:                return -1;
        !           661:        }
        !           662: 
        !           663: 
        !           664:        if (p->conf.debug) {
        !           665:                log_error_write(srv, __FILE__, __LINE__, "sd",
        !           666:                               "proxy - have to read:", b);
        !           667:        }
        !           668: 
        !           669:        if (b > 0) {
        !           670:                if (hctx->response->used == 0) {
        !           671:                        /* avoid too small buffer */
        !           672:                        buffer_prepare_append(hctx->response, b + 1);
        !           673:                        hctx->response->used = 1;
        !           674:                } else {
        !           675:                        buffer_prepare_append(hctx->response, b);
        !           676:                }
        !           677: 
        !           678:                if (-1 == (r = read(hctx->fd, hctx->response->ptr + hctx->response->used - 1, b))) {
        !           679:                        if (errno == EAGAIN) return 0;
        !           680:                        log_error_write(srv, __FILE__, __LINE__, "sds",
        !           681:                                        "unexpected end-of-file (perhaps the proxy process died):",
        !           682:                                        proxy_fd, strerror(errno));
        !           683:                        return -1;
        !           684:                }
        !           685: 
        !           686:                /* this should be catched by the b > 0 above */
        !           687:                assert(r);
        !           688: 
        !           689:                hctx->response->used += r;
        !           690:                hctx->response->ptr[hctx->response->used - 1] = '\0';
        !           691: 
        !           692: #if 0
        !           693:                log_error_write(srv, __FILE__, __LINE__, "sdsbs",
        !           694:                                "demux: Response buffer len", hctx->response->used, ":", hctx->response, ":");
        !           695: #endif
        !           696: 
        !           697:                if (0 == con->got_response) {
        !           698:                        con->got_response = 1;
        !           699:                        buffer_prepare_copy(hctx->response_header, 128);
        !           700:                }
        !           701: 
        !           702:                if (0 == con->file_started) {
        !           703:                        char *c;
        !           704: 
        !           705:                        /* search for the \r\n\r\n in the string */
        !           706:                        if (NULL != (c = buffer_search_string_len(hctx->response, "\r\n\r\n", 4))) {
        !           707:                                size_t hlen = c - hctx->response->ptr + 4;
        !           708:                                size_t blen = hctx->response->used - hlen - 1;
        !           709:                                /* found */
        !           710: 
        !           711:                                buffer_append_string_len(hctx->response_header, hctx->response->ptr, c - hctx->response->ptr + 4);
        !           712: #if 0
        !           713:                                log_error_write(srv, __FILE__, __LINE__, "sb", "Header:", hctx->response_header);
        !           714: #endif
        !           715:                                /* parse the response header */
        !           716:                                proxy_response_parse(srv, con, p, hctx->response_header);
        !           717: 
        !           718:                                /* enable chunked-transfer-encoding */
        !           719:                                if (con->request.http_version == HTTP_VERSION_1_1 &&
        !           720:                                    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
        !           721:                                        con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
        !           722:                                }
        !           723: 
        !           724:                                con->file_started = 1;
        !           725:                                if (blen) {
        !           726:                                        http_chunk_append_mem(srv, con, c + 4, blen + 1);
        !           727:                                }
        !           728:                                hctx->response->used = 0;
        !           729:                                joblist_append(srv, con);
        !           730:                        }
        !           731:                } else {
        !           732:                        http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
        !           733:                        joblist_append(srv, con);
        !           734:                        hctx->response->used = 0;
        !           735:                }
        !           736: 
        !           737:        } else {
        !           738:                /* reading from upstream done */
        !           739:                con->file_finished = 1;
        !           740: 
        !           741:                http_chunk_append_mem(srv, con, NULL, 0);
        !           742:                joblist_append(srv, con);
        !           743: 
        !           744:                fin = 1;
        !           745:        }
        !           746: 
        !           747:        return fin;
        !           748: }
        !           749: 
        !           750: 
        !           751: static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
        !           752:        data_proxy *host= hctx->host;
        !           753:        connection *con   = hctx->remote_conn;
        !           754: 
        !           755:        int ret;
        !           756: 
        !           757:        if (!host ||
        !           758:            (!host->host->used || !host->port)) return -1;
        !           759: 
        !           760:        switch(hctx->state) {
        !           761:        case PROXY_STATE_CONNECT:
        !           762:                /* wait for the connect() to finish */
        !           763: 
        !           764:                /* connect failed ? */
        !           765:                if (-1 == hctx->fde_ndx) return HANDLER_ERROR;
        !           766: 
        !           767:                /* wait */
        !           768:                return HANDLER_WAIT_FOR_EVENT;
        !           769: 
        !           770:                break;
        !           771: 
        !           772:        case PROXY_STATE_INIT:
        !           773: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
        !           774:                if (strstr(host->host->ptr,":")) {
        !           775:                    if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) {
        !           776:                        log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
        !           777:                        return HANDLER_ERROR;
        !           778:                    }
        !           779:                } else
        !           780: #endif
        !           781:                {
        !           782:                    if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) {
        !           783:                        log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
        !           784:                        return HANDLER_ERROR;
        !           785:                    }
        !           786:                }
        !           787:                hctx->fde_ndx = -1;
        !           788: 
        !           789:                srv->cur_fds++;
        !           790: 
        !           791:                fdevent_register(srv->ev, hctx->fd, proxy_handle_fdevent, hctx);
        !           792: 
        !           793:                if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
        !           794:                        log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
        !           795: 
        !           796:                        return HANDLER_ERROR;
        !           797:                }
        !           798: 
        !           799:                switch (proxy_establish_connection(srv, hctx)) {
        !           800:                case 1:
        !           801:                        proxy_set_state(srv, hctx, PROXY_STATE_CONNECT);
        !           802: 
        !           803:                        /* connection is in progress, wait for an event and call getsockopt() below */
        !           804: 
        !           805:                        fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
        !           806: 
        !           807:                        return HANDLER_WAIT_FOR_EVENT;
        !           808:                case -1:
        !           809:                        /* if ECONNREFUSED choose another connection -> FIXME */
        !           810:                        hctx->fde_ndx = -1;
        !           811: 
        !           812:                        return HANDLER_ERROR;
        !           813:                default:
        !           814:                        /* everything is ok, go on */
        !           815:                        proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
        !           816:                        break;
        !           817:                }
        !           818: 
        !           819:                /* fall through */
        !           820: 
        !           821:        case PROXY_STATE_PREPARE_WRITE:
        !           822:                proxy_create_env(srv, hctx);
        !           823: 
        !           824:                proxy_set_state(srv, hctx, PROXY_STATE_WRITE);
        !           825: 
        !           826:                /* fall through */
        !           827:        case PROXY_STATE_WRITE:;
        !           828:                ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
        !           829: 
        !           830:                chunkqueue_remove_finished_chunks(hctx->wb);
        !           831: 
        !           832:                if (-1 == ret) { /* error on our side */
        !           833:                        log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno);
        !           834: 
        !           835:                        return HANDLER_ERROR;
        !           836:                } else if (-2 == ret) { /* remote close */
        !           837:                        log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno);
        !           838: 
        !           839:                        return HANDLER_ERROR;
        !           840:                }
        !           841: 
        !           842:                if (hctx->wb->bytes_out == hctx->wb->bytes_in) {
        !           843:                        proxy_set_state(srv, hctx, PROXY_STATE_READ);
        !           844: 
        !           845:                        fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
        !           846:                        fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
        !           847:                } else {
        !           848:                        fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
        !           849: 
        !           850:                        return HANDLER_WAIT_FOR_EVENT;
        !           851:                }
        !           852: 
        !           853:                return HANDLER_WAIT_FOR_EVENT;
        !           854:        case PROXY_STATE_READ:
        !           855:                /* waiting for a response */
        !           856:                return HANDLER_WAIT_FOR_EVENT;
        !           857:        default:
        !           858:                log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
        !           859:                return HANDLER_ERROR;
        !           860:        }
        !           861: 
        !           862:        return HANDLER_GO_ON;
        !           863: }
        !           864: 
        !           865: #define PATCH(x) \
        !           866:        p->conf.x = s->x;
        !           867: static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data *p) {
        !           868:        size_t i, j;
        !           869:        plugin_config *s = p->config_storage[0];
        !           870: 
        !           871:        PATCH(extensions);
        !           872:        PATCH(debug);
        !           873:        PATCH(balance);
        !           874: 
        !           875:        /* skip the first, the global context */
        !           876:        for (i = 1; i < srv->config_context->used; i++) {
        !           877:                data_config *dc = (data_config *)srv->config_context->data[i];
        !           878:                s = p->config_storage[i];
        !           879: 
        !           880:                /* condition didn't match */
        !           881:                if (!config_check_cond(srv, con, dc)) continue;
        !           882: 
        !           883:                /* merge config */
        !           884:                for (j = 0; j < dc->value->used; j++) {
        !           885:                        data_unset *du = dc->value->data[j];
        !           886: 
        !           887:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.server"))) {
        !           888:                                PATCH(extensions);
        !           889:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.debug"))) {
        !           890:                                PATCH(debug);
        !           891:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.balance"))) {
        !           892:                                PATCH(balance);
        !           893:                        }
        !           894:                }
        !           895:        }
        !           896: 
        !           897:        return 0;
        !           898: }
        !           899: #undef PATCH
        !           900: 
        !           901: SUBREQUEST_FUNC(mod_proxy_handle_subrequest) {
        !           902:        plugin_data *p = p_d;
        !           903: 
        !           904:        handler_ctx *hctx = con->plugin_ctx[p->id];
        !           905:        data_proxy *host;
        !           906: 
        !           907:        if (NULL == hctx) return HANDLER_GO_ON;
        !           908: 
        !           909:        mod_proxy_patch_connection(srv, con, p);
        !           910: 
        !           911:        host = hctx->host;
        !           912: 
        !           913:        /* not my job */
        !           914:        if (con->mode != p->id) return HANDLER_GO_ON;
        !           915: 
        !           916:        /* ok, create the request */
        !           917:        switch(proxy_write_request(srv, hctx)) {
        !           918:        case HANDLER_ERROR:
        !           919:                log_error_write(srv, __FILE__, __LINE__,  "sbdd", "proxy-server disabled:",
        !           920:                                host->host,
        !           921:                                host->port,
        !           922:                                hctx->fd);
        !           923: 
        !           924:                /* disable this server */
        !           925:                host->is_disabled = 1;
        !           926:                host->disable_ts = srv->cur_ts;
        !           927: 
        !           928:                proxy_connection_close(srv, hctx);
        !           929: 
        !           930:                /* reset the enviroment and restart the sub-request */
        !           931:                buffer_reset(con->physical.path);
        !           932:                con->mode = DIRECT;
        !           933: 
        !           934:                joblist_append(srv, con);
        !           935: 
        !           936:                /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop
        !           937:                 * and hope that the childs will be restarted
        !           938:                 *
        !           939:                 */
        !           940: 
        !           941:                return HANDLER_WAIT_FOR_FD;
        !           942:        case HANDLER_WAIT_FOR_EVENT:
        !           943:                break;
        !           944:        case HANDLER_WAIT_FOR_FD:
        !           945:                return HANDLER_WAIT_FOR_FD;
        !           946:        default:
        !           947:                break;
        !           948:        }
        !           949: 
        !           950:        if (con->file_started == 1) {
        !           951:                return HANDLER_FINISHED;
        !           952:        } else {
        !           953:                return HANDLER_WAIT_FOR_EVENT;
        !           954:        }
        !           955: }
        !           956: 
        !           957: static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
        !           958:        handler_ctx *hctx = ctx;
        !           959:        connection  *con  = hctx->remote_conn;
        !           960:        plugin_data *p    = hctx->plugin_data;
        !           961: 
        !           962: 
        !           963:        if ((revents & FDEVENT_IN) &&
        !           964:            hctx->state == PROXY_STATE_READ) {
        !           965: 
        !           966:                if (p->conf.debug) {
        !           967:                        log_error_write(srv, __FILE__, __LINE__, "sd",
        !           968:                                        "proxy: fdevent-in", hctx->state);
        !           969:                }
        !           970: 
        !           971:                switch (proxy_demux_response(srv, hctx)) {
        !           972:                case 0:
        !           973:                        break;
        !           974:                case 1:
        !           975:                        /* we are done */
        !           976:                        proxy_connection_close(srv, hctx);
        !           977: 
        !           978:                        joblist_append(srv, con);
        !           979:                        return HANDLER_FINISHED;
        !           980:                case -1:
        !           981:                        if (con->file_started == 0) {
        !           982:                                /* nothing has been send out yet, send a 500 */
        !           983:                                connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
        !           984:                                con->http_status = 500;
        !           985:                                con->mode = DIRECT;
        !           986:                        } else {
        !           987:                                /* response might have been already started, kill the connection */
        !           988:                                connection_set_state(srv, con, CON_STATE_ERROR);
        !           989:                        }
        !           990: 
        !           991:                        joblist_append(srv, con);
        !           992:                        return HANDLER_FINISHED;
        !           993:                }
        !           994:        }
        !           995: 
        !           996:        if (revents & FDEVENT_OUT) {
        !           997:                if (p->conf.debug) {
        !           998:                        log_error_write(srv, __FILE__, __LINE__, "sd",
        !           999:                                        "proxy: fdevent-out", hctx->state);
        !          1000:                }
        !          1001: 
        !          1002:                if (hctx->state == PROXY_STATE_CONNECT) {
        !          1003:                        int socket_error;
        !          1004:                        socklen_t socket_error_len = sizeof(socket_error);
        !          1005: 
        !          1006:                        /* we don't need it anymore */
        !          1007:                        fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
        !          1008:                        hctx->fde_ndx = -1;
        !          1009: 
        !          1010:                        /* try to finish the connect() */
        !          1011:                        if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
        !          1012:                                log_error_write(srv, __FILE__, __LINE__, "ss",
        !          1013:                                        "getsockopt failed:", strerror(errno));
        !          1014: 
        !          1015:                                joblist_append(srv, con);
        !          1016:                                return HANDLER_FINISHED;
        !          1017:                        }
        !          1018:                        if (socket_error != 0) {
        !          1019:                                log_error_write(srv, __FILE__, __LINE__, "ss",
        !          1020:                                        "establishing connection failed:", strerror(socket_error),
        !          1021:                                        "port:", hctx->host->port);
        !          1022: 
        !          1023:                                joblist_append(srv, con);
        !          1024:                                return HANDLER_FINISHED;
        !          1025:                        }
        !          1026:                        if (p->conf.debug) {
        !          1027:                                log_error_write(srv, __FILE__, __LINE__,  "s", "proxy - connect - delayed success");
        !          1028:                        }
        !          1029: 
        !          1030:                        proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
        !          1031:                }
        !          1032: 
        !          1033:                if (hctx->state == PROXY_STATE_PREPARE_WRITE ||
        !          1034:                    hctx->state == PROXY_STATE_WRITE) {
        !          1035:                        /* we are allowed to send something out
        !          1036:                         *
        !          1037:                         * 1. after a just finished connect() call
        !          1038:                         * 2. in a unfinished write() call (long POST request)
        !          1039:                         */
        !          1040:                        return mod_proxy_handle_subrequest(srv, con, p);
        !          1041:                } else {
        !          1042:                        log_error_write(srv, __FILE__, __LINE__, "sd",
        !          1043:                                        "proxy: out", hctx->state);
        !          1044:                }
        !          1045:        }
        !          1046: 
        !          1047:        /* perhaps this issue is already handled */
        !          1048:        if (revents & FDEVENT_HUP) {
        !          1049:                if (p->conf.debug) {
        !          1050:                        log_error_write(srv, __FILE__, __LINE__, "sd",
        !          1051:                                        "proxy: fdevent-hup", hctx->state);
        !          1052:                }
        !          1053: 
        !          1054:                if (hctx->state == PROXY_STATE_CONNECT) {
        !          1055:                        /* connect() -> EINPROGRESS -> HUP */
        !          1056: 
        !          1057:                        /**
        !          1058:                         * what is proxy is doing if it can't reach the next hop ?
        !          1059:                         *
        !          1060:                         */
        !          1061: 
        !          1062:                        if (hctx->host) {
        !          1063:                                hctx->host->is_disabled = 1;
        !          1064:                                hctx->host->disable_ts = srv->cur_ts;
        !          1065:                                log_error_write(srv, __FILE__, __LINE__,  "sbdd", "proxy-server disabled:",
        !          1066:                                                hctx->host->host,
        !          1067:                                                hctx->host->port,
        !          1068:                                                hctx->fd);
        !          1069: 
        !          1070:                                /* disable this server */
        !          1071:                                hctx->host->is_disabled = 1;
        !          1072:                                hctx->host->disable_ts = srv->cur_ts;
        !          1073: 
        !          1074:                                proxy_connection_close(srv, hctx);
        !          1075: 
        !          1076:                                /* reset the enviroment and restart the sub-request */
        !          1077:                                buffer_reset(con->physical.path);
        !          1078:                                con->mode = DIRECT;
        !          1079: 
        !          1080:                                joblist_append(srv, con);
        !          1081:                        } else {
        !          1082:                                proxy_connection_close(srv, hctx);
        !          1083:                                joblist_append(srv, con);
        !          1084: 
        !          1085:                                con->mode = DIRECT;
        !          1086:                                con->http_status = 503;
        !          1087:                        }
        !          1088: 
        !          1089:                        return HANDLER_FINISHED;
        !          1090:                }
        !          1091: 
        !          1092:                if (!con->file_finished) {
        !          1093:                        http_chunk_append_mem(srv, con, NULL, 0);
        !          1094:                }
        !          1095: 
        !          1096:                con->file_finished = 1;
        !          1097:                proxy_connection_close(srv, hctx);
        !          1098:                joblist_append(srv, con);
        !          1099:        } else if (revents & FDEVENT_ERR) {
        !          1100:                /* kill all connections to the proxy process */
        !          1101: 
        !          1102:                log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents);
        !          1103: 
        !          1104:                con->file_finished = 1;
        !          1105:                joblist_append(srv, con);
        !          1106:                proxy_connection_close(srv, hctx);
        !          1107:        }
        !          1108: 
        !          1109:        return HANDLER_FINISHED;
        !          1110: }
        !          1111: 
        !          1112: static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p_d) {
        !          1113:        plugin_data *p = p_d;
        !          1114:        size_t s_len;
        !          1115:        unsigned long last_max = ULONG_MAX;
        !          1116:        int max_usage = INT_MAX;
        !          1117:        int ndx = -1;
        !          1118:        size_t k;
        !          1119:        buffer *fn;
        !          1120:        data_array *extension = NULL;
        !          1121:        size_t path_info_offset;
        !          1122: 
        !          1123:        if (con->mode != DIRECT) return HANDLER_GO_ON;
        !          1124: 
        !          1125:        /* Possibly, we processed already this request */
        !          1126:        if (con->file_started == 1) return HANDLER_GO_ON;
        !          1127: 
        !          1128:        mod_proxy_patch_connection(srv, con, p);
        !          1129: 
        !          1130:        fn = con->uri.path;
        !          1131: 
        !          1132:        if (fn->used == 0) {
        !          1133:                return HANDLER_ERROR;
        !          1134:        }
        !          1135: 
        !          1136:        s_len = fn->used - 1;
        !          1137: 
        !          1138: 
        !          1139:        path_info_offset = 0;
        !          1140: 
        !          1141:        if (p->conf.debug) {
        !          1142:                log_error_write(srv, __FILE__, __LINE__,  "s", "proxy - start");
        !          1143:        }
        !          1144: 
        !          1145:        /* check if extension matches */
        !          1146:        for (k = 0; k < p->conf.extensions->used; k++) {
        !          1147:                data_array *ext = NULL;
        !          1148:                size_t ct_len;
        !          1149: 
        !          1150:                ext = (data_array *)p->conf.extensions->data[k];
        !          1151: 
        !          1152:                if (ext->key->used == 0) continue;
        !          1153: 
        !          1154:                ct_len = ext->key->used - 1;
        !          1155: 
        !          1156:                if (s_len < ct_len) continue;
        !          1157: 
        !          1158:                /* check extension in the form "/proxy_pattern" */
        !          1159:                if (*(ext->key->ptr) == '/') {
        !          1160:                        if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) {
        !          1161:                                if (s_len > ct_len + 1) {
        !          1162:                                        char *pi_offset;
        !          1163: 
        !          1164:                                        if (NULL != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) {
        !          1165:                                                path_info_offset = pi_offset - fn->ptr;
        !          1166:                                        }
        !          1167:                                }
        !          1168:                                extension = ext;
        !          1169:                                break;
        !          1170:                        }
        !          1171:                } else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) {
        !          1172:                        /* check extension in the form ".fcg" */
        !          1173:                        extension = ext;
        !          1174:                        break;
        !          1175:                }
        !          1176:        }
        !          1177: 
        !          1178:        if (NULL == extension) {
        !          1179:                return HANDLER_GO_ON;
        !          1180:        }
        !          1181: 
        !          1182:        if (p->conf.debug) {
        !          1183:                log_error_write(srv, __FILE__, __LINE__,  "s", "proxy - ext found");
        !          1184:        }
        !          1185: 
        !          1186:        if (extension->value->used == 1) {
        !          1187:                if ( ((data_proxy *)extension->value->data[0])->is_disabled ) {
        !          1188:                        ndx = -1;
        !          1189:                } else {
        !          1190:                        ndx = 0;
        !          1191:                }
        !          1192:        } else if (extension->value->used != 0) switch(p->conf.balance) {
        !          1193:        case PROXY_BALANCE_HASH:
        !          1194:                /* hash balancing */
        !          1195: 
        !          1196:                if (p->conf.debug) {
        !          1197:                        log_error_write(srv, __FILE__, __LINE__,  "sd",
        !          1198:                                        "proxy - used hash balancing, hosts:", extension->value->used);
        !          1199:                }
        !          1200: 
        !          1201:                for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->value->used; k++) {
        !          1202:                        data_proxy *host = (data_proxy *)extension->value->data[k];
        !          1203:                        unsigned long cur_max;
        !          1204: 
        !          1205:                        if (host->is_disabled) continue;
        !          1206: 
        !          1207:                        cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) +
        !          1208:                                generate_crc32c(CONST_BUF_LEN(host->host)) + /* we can cache this */
        !          1209:                                generate_crc32c(CONST_BUF_LEN(con->uri.authority));
        !          1210: 
        !          1211:                        if (p->conf.debug) {
        !          1212:                                log_error_write(srv, __FILE__, __LINE__,  "sbbbd",
        !          1213:                                                "proxy - election:",
        !          1214:                                                con->uri.path,
        !          1215:                                                host->host,
        !          1216:                                                con->uri.authority,
        !          1217:                                                cur_max);
        !          1218:                        }
        !          1219: 
        !          1220:                        if ((last_max == ULONG_MAX) || /* first round */
        !          1221:                            (cur_max > last_max)) {
        !          1222:                                last_max = cur_max;
        !          1223: 
        !          1224:                                ndx = k;
        !          1225:                        }
        !          1226:                }
        !          1227: 
        !          1228:                break;
        !          1229:        case PROXY_BALANCE_FAIR:
        !          1230:                /* fair balancing */
        !          1231:                if (p->conf.debug) {
        !          1232:                        log_error_write(srv, __FILE__, __LINE__,  "s",
        !          1233:                                        "proxy - used fair balancing");
        !          1234:                }
        !          1235: 
        !          1236:                for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) {
        !          1237:                        data_proxy *host = (data_proxy *)extension->value->data[k];
        !          1238: 
        !          1239:                        if (host->is_disabled) continue;
        !          1240: 
        !          1241:                        if (host->usage < max_usage) {
        !          1242:                                max_usage = host->usage;
        !          1243: 
        !          1244:                                ndx = k;
        !          1245:                        }
        !          1246:                }
        !          1247: 
        !          1248:                break;
        !          1249:        case PROXY_BALANCE_RR: {
        !          1250:                data_proxy *host;
        !          1251: 
        !          1252:                /* round robin */
        !          1253:                if (p->conf.debug) {
        !          1254:                        log_error_write(srv, __FILE__, __LINE__,  "s",
        !          1255:                                        "proxy - used round-robin balancing");
        !          1256:                }
        !          1257: 
        !          1258:                /* just to be sure */
        !          1259:                assert(extension->value->used < INT_MAX);
        !          1260: 
        !          1261:                host = (data_proxy *)extension->value->data[0];
        !          1262: 
        !          1263:                /* Use last_used_ndx from first host in list */
        !          1264:                k = host->last_used_ndx;
        !          1265:                ndx = k + 1; /* use next host after the last one */
        !          1266:                if (ndx < 0) ndx = 0;
        !          1267: 
        !          1268:                /* Search first active host after last_used_ndx */
        !          1269:                while ( ndx < (int) extension->value->used
        !          1270:                                && (host = (data_proxy *)extension->value->data[ndx])->is_disabled ) ndx++;
        !          1271: 
        !          1272:                if (ndx >= (int) extension->value->used) {
        !          1273:                        /* didn't found a higher id, wrap to the start */
        !          1274:                        for (ndx = 0; ndx <= (int) k; ndx++) {
        !          1275:                                host = (data_proxy *)extension->value->data[ndx];
        !          1276:                                if (!host->is_disabled) break;
        !          1277:                        }
        !          1278: 
        !          1279:                        /* No active host found */
        !          1280:                        if (host->is_disabled) ndx = -1;
        !          1281:                }
        !          1282: 
        !          1283:                /* Save new index for next round */
        !          1284:                ((data_proxy *)extension->value->data[0])->last_used_ndx = ndx;
        !          1285: 
        !          1286:                break;
        !          1287:        }
        !          1288:        default:
        !          1289:                break;
        !          1290:        }
        !          1291: 
        !          1292:        /* found a server */
        !          1293:        if (ndx != -1) {
        !          1294:                data_proxy *host = (data_proxy *)extension->value->data[ndx];
        !          1295: 
        !          1296:                /*
        !          1297:                 * if check-local is disabled, use the uri.path handler
        !          1298:                 *
        !          1299:                 */
        !          1300: 
        !          1301:                /* init handler-context */
        !          1302:                handler_ctx *hctx;
        !          1303:                hctx = handler_ctx_init();
        !          1304: 
        !          1305:                hctx->path_info_offset = path_info_offset;
        !          1306:                hctx->remote_conn      = con;
        !          1307:                hctx->plugin_data      = p;
        !          1308:                hctx->host             = host;
        !          1309: 
        !          1310:                con->plugin_ctx[p->id] = hctx;
        !          1311: 
        !          1312:                host->usage++;
        !          1313: 
        !          1314:                con->mode = p->id;
        !          1315: 
        !          1316:                if (p->conf.debug) {
        !          1317:                        log_error_write(srv, __FILE__, __LINE__,  "sbd",
        !          1318:                                        "proxy - found a host",
        !          1319:                                        host->host, host->port);
        !          1320:                }
        !          1321: 
        !          1322:                return HANDLER_GO_ON;
        !          1323:        } else {
        !          1324:                /* no handler found */
        !          1325:                con->http_status = 500;
        !          1326: 
        !          1327:                log_error_write(srv, __FILE__, __LINE__,  "sb",
        !          1328:                                "no proxy-handler found for:",
        !          1329:                                fn);
        !          1330: 
        !          1331:                return HANDLER_FINISHED;
        !          1332:        }
        !          1333:        return HANDLER_GO_ON;
        !          1334: }
        !          1335: 
        !          1336: static handler_t mod_proxy_connection_close_callback(server *srv, connection *con, void *p_d) {
        !          1337:        plugin_data *p = p_d;
        !          1338: 
        !          1339:        proxy_connection_close(srv, con->plugin_ctx[p->id]);
        !          1340: 
        !          1341:        return HANDLER_GO_ON;
        !          1342: }
        !          1343: 
        !          1344: /**
        !          1345:  *
        !          1346:  * the trigger re-enables the disabled connections after the timeout is over
        !          1347:  *
        !          1348:  * */
        !          1349: 
        !          1350: TRIGGER_FUNC(mod_proxy_trigger) {
        !          1351:        plugin_data *p = p_d;
        !          1352: 
        !          1353:        if (p->config_storage) {
        !          1354:                size_t i, n, k;
        !          1355:                for (i = 0; i < srv->config_context->used; i++) {
        !          1356:                        plugin_config *s = p->config_storage[i];
        !          1357: 
        !          1358:                        if (!s) continue;
        !          1359: 
        !          1360:                        /* get the extensions for all configs */
        !          1361: 
        !          1362:                        for (k = 0; k < s->extensions->used; k++) {
        !          1363:                                data_array *extension = (data_array *)s->extensions->data[k];
        !          1364: 
        !          1365:                                /* get all hosts */
        !          1366:                                for (n = 0; n < extension->value->used; n++) {
        !          1367:                                        data_proxy *host = (data_proxy *)extension->value->data[n];
        !          1368: 
        !          1369:                                        if (!host->is_disabled ||
        !          1370:                                            srv->cur_ts - host->disable_ts < 5) continue;
        !          1371: 
        !          1372:                                        log_error_write(srv, __FILE__, __LINE__,  "sbd",
        !          1373:                                                        "proxy - re-enabled:",
        !          1374:                                                        host->host, host->port);
        !          1375: 
        !          1376:                                        host->is_disabled = 0;
        !          1377:                                }
        !          1378:                        }
        !          1379:                }
        !          1380:        }
        !          1381: 
        !          1382:        return HANDLER_GO_ON;
        !          1383: }
        !          1384: 
        !          1385: 
        !          1386: int mod_proxy_plugin_init(plugin *p);
        !          1387: int mod_proxy_plugin_init(plugin *p) {
        !          1388:        p->version      = LIGHTTPD_VERSION_ID;
        !          1389:        p->name         = buffer_init_string("proxy");
        !          1390: 
        !          1391:        p->init         = mod_proxy_init;
        !          1392:        p->cleanup      = mod_proxy_free;
        !          1393:        p->set_defaults = mod_proxy_set_defaults;
        !          1394:        p->connection_reset        = mod_proxy_connection_close_callback; /* end of req-resp cycle */
        !          1395:        p->handle_connection_close = mod_proxy_connection_close_callback; /* end of client connection */
        !          1396:        p->handle_uri_clean        = mod_proxy_check_extension;
        !          1397:        p->handle_subrequest       = mod_proxy_handle_subrequest;
        !          1398:        p->handle_trigger          = mod_proxy_trigger;
        !          1399: 
        !          1400:        p->data         = NULL;
        !          1401: 
        !          1402:        return 0;
        !          1403: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>