Annotation of embedaddon/lighttpd/src/mod_proxy.c, revision 1.1.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>