Changeset 1502

Show
Ignore:
Timestamp:
01/05/2007 04:43:40 AM (22 months ago)
Author:
jakabosky
Message:

Moved HTTP/1.1 chunk encoding into plugin mod_chunked
Created filter chain API for mod_chunked and mod_deflate.
Added new handle_filter_response_content hook to plugin API.

Location:
trunk
Files:
3 added
13 modified

Legend:

Unmodified
Added
Removed
  • trunk/configure.in

    r1481 r1502  
    569569 
    570570 
    571 do_build="mod_proxy_core mod_proxy_backend_http mod_proxy_backend_fastcgi mod_proxy_backend_scgi mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir mod_webdav mod_staticfile mod_flv_streaming"  
     571do_build="mod_proxy_core mod_proxy_backend_http mod_proxy_backend_fastcgi mod_proxy_backend_scgi mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir mod_webdav mod_staticfile mod_chunked mod_flv_streaming"  
    572572 
    573573plugins="mod_rewrite mod_redirect mod_ssi mod_trigger_b4_dl" 
  • trunk/src/Makefile.am

    r1477 r1502  
    4949 
    5050common_src=buffer.c log.c \ 
    51       keyvalue.c chunk.c \ 
     51      keyvalue.c chunk.c filter.c \ 
    5252      stream.c fdevent.c \ 
    5353      stat_cache.c plugin.c joblist.c etag.c array.c \ 
     
    142142mod_staticfile_la_LIBADD = $(common_libadd) 
    143143 
     144lib_LTLIBRARIES += mod_chunked.la 
     145mod_chunked_la_SOURCES = mod_chunked.c  
     146mod_chunked_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined 
     147mod_chunked_la_LIBADD = $(common_libadd) 
     148 
    144149lib_LTLIBRARIES += mod_dirlisting.la 
    145150mod_dirlisting_la_SOURCES = mod_dirlisting.c  
     
    273278 
    274279hdr = server.h buffer.h network.h log.h keyvalue.h \ 
    275       response.h request.h fastcgi.h chunk.h \ 
     280      response.h request.h fastcgi.h chunk.h filter.h \ 
    276281      settings.h http_auth_digest.h \ 
    277282      md5.h http_auth.h stream.h \ 
  • trunk/src/base.h

    r1496 r1502  
    2020#include "array.h" 
    2121#include "chunk.h" 
     22#include "filter.h" 
    2223#include "keyvalue.h" 
    2324#include "settings.h" 
     
    331332        int file_started; 
    332333 
    333         chunkqueue *send;            /* the response-content without encoding */ 
     334        chunkqueue *send;            /* the response-content before filters are applied */ 
    334335        chunkqueue *recv;            /* the request-content, without encoding */ 
    335336 
     337        filter_chain *send_filters;  /* the chain of filters to apply to response-content. */ 
    336338        chunkqueue *send_raw;        /* the full response (HTTP-Header + compression + chunking ) */ 
    337339        chunkqueue *recv_raw;        /* the full request (HTTP-Header + chunking ) */ 
  • trunk/src/chunk.c

    r1496 r1502  
    242242 
    243243/* 
     244 * copy/steal all chunks from in chunkqueue.  return total bytes copied/stolen. 
     245 * 
     246 */ 
     247int chunkqueue_steal_all_chunks(chunkqueue *cq, chunkqueue *in) { 
     248        size_t total = 0; 
     249        off_t we_have = 0; 
     250        chunk *c; 
     251 
     252        if (!cq || !in) return 0; 
     253 
     254        for (c = in->first; c; c = c->next) { 
     255                switch (c->type) { 
     256                case MEM_CHUNK: 
     257                        if (c->mem->used == 0) continue; 
     258 
     259                        we_have = c->mem->used - c->offset - 1; 
     260                        if(we_have == 0) continue; 
     261                        if (c->offset == 0) { 
     262                                chunkqueue_steal_chunk(cq, c); 
     263                        } else { 
     264                                chunkqueue_append_buffer(cq, c->mem); 
     265                                c->offset = c->mem->used - 1; 
     266                        } 
     267                        break; 
     268                case FILE_CHUNK: 
     269                        if (c->file.length == 0) continue; 
     270 
     271                        we_have = c->file.length; 
     272                        if(c->file.is_temp) { 
     273                                chunkqueue_steal_tempfile(cq, c); 
     274                        } else { 
     275                                chunkqueue_append_file(cq, c->file.name, c->file.start, c->file.length); 
     276                        } 
     277 
     278                        c->offset = c->file.length; 
     279                        break; 
     280                case UNUSED_CHUNK: 
     281                        break; 
     282                } 
     283                total += we_have; 
     284        } 
     285 
     286        return total; 
     287} 
     288 
     289/* 
    244290 * copy/steal max_len bytes from chunk chain.  return total bytes copied/stolen. 
    245291 * 
  • trunk/src/chunk.h

    r1496 r1502  
    6868int chunkqueue_steal_chunk(chunkqueue *cq, chunk *c); 
    6969int chunkqueue_steal_chunks_len(chunkqueue *cq, chunk *c, size_t max_len); 
     70int chunkqueue_steal_all_chunks(chunkqueue *cq, chunkqueue *in); 
    7071int chunkqueue_skip(chunkqueue *cq, off_t skip); 
    7172void chunkqueue_remove_empty_last_chunk(chunkqueue *cq); 
  • trunk/src/configfile.c

    r1443 r1502  
    10201020                        array_insert_unique(modules->value, (data_unset *)ds); 
    10211021                } 
     1022 
     1023                if (NULL == array_get_element(modules->value, "mod_chunked")) { 
     1024                        ds = data_string_init(); 
     1025                        buffer_copy_string(ds->value, "mod_chunked"); 
     1026                        array_insert_unique(modules->value, (data_unset *)ds); 
     1027                } 
    10221028        } else { 
    10231029                data_string *ds; 
     
    10361042                ds = data_string_init(); 
    10371043                buffer_copy_string(ds->value, "mod_staticfile"); 
     1044                array_insert_unique(modules->value, (data_unset *)ds); 
     1045 
     1046                ds = data_string_init(); 
     1047                buffer_copy_string(ds->value, "mod_chunked"); 
    10381048                array_insert_unique(modules->value, (data_unset *)ds); 
    10391049 
  • trunk/src/connections.c

    r1496 r1502  
    184184 
    185185static int connection_handle_response_header(server *srv, connection *con) { 
    186         data_string *cl; 
     186        int no_response_body = 0; 
    187187 
    188188        if (con->mode == DIRECT) { 
     
    214214 
    215215                                /* trash the content */ 
    216                                 chunkqueue_reset(con->send); 
     216                                no_response_body = 1; 
    217217 
    218218                                con->http_status = 200; 
    219                                 con->send->is_closed = 1; 
    220  
    221219                        } 
    222220                        break; 
     
    331329        case 304: 
    332330        default: 
     331                no_response_body = 1; 
     332                break; 
     333        } 
     334 
     335        if (con->request.http_method == HTTP_METHOD_HEAD) { 
     336                no_response_body = 1; 
     337        } 
     338 
     339        if (no_response_body) { 
    333340                /* disable chunked encoding again as we have no body */ 
    334341                con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; 
     
    336343 
    337344                con->send->is_closed = 1; 
    338  
    339                 break; 
    340         } 
    341  
    342  
    343         if (con->send->is_closed) { 
    344                 /* we have all the content and chunked encoding is not used, set a content-length */ 
    345  
    346                 if ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0 && 
    347                     NULL == (cl = (data_string *)array_get_element(con->response.headers, "Content-Length"))) { 
    348                         buffer_copy_off_t(srv->tmp_buf, chunkqueue_length(con->send)); 
    349  
    350                         response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); 
    351                 } 
    352         } else { 
    353                 if (NULL == (cl = (data_string *)array_get_element(con->response.headers, "Content-Length"))) { 
    354                         /* we don't know the size of the content yet 
    355                          * - either enable chunking 
    356                          * - or disable keep-alive  */ 
    357  
    358                         if (con->request.http_version == HTTP_VERSION_1_1) { 
    359                                 /* enable chunk-encoding */ 
    360                                 con->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED; 
    361                         } else  { 
    362                                 con->keep_alive = 0; 
    363                         } 
    364                 } 
    365         } 
    366  
    367         if (con->request.http_method == HTTP_METHOD_HEAD) { 
    368                 con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; 
    369                 chunkqueue_reset(con->send); 
    370  
    371                 con->send->is_closed = 1; 
    372         } 
    373  
    374         http_response_write_header(srv, con, con->send_raw); 
     345        } 
    375346 
    376347        return 0; 
     
    420391 
    421392#undef CLEAN 
    422         con->send = chunkqueue_init(); 
     393        con->send_filters = filter_chain_init(); 
     394        /* send is the chunkqueue of the first send filter */ 
     395        con->send = con->send_filters->first->cq; 
    423396        con->recv = chunkqueue_init(); 
    424397 
     
    453426                iosocket_free(con->sock); 
    454427 
    455                 chunkqueue_free(con->send); 
     428                filter_chain_free(con->send_filters); 
     429                con->send = NULL; 
    456430                chunkqueue_free(con->recv); 
    457431                chunkqueue_free(con->send_raw); 
     
    566540        array_reset(con->environment); 
    567541 
    568         chunkqueue_reset(con->send); 
     542        filter_chain_reset(con->send_filters); 
     543        con->send = con->send_filters->first->cq; 
    569544        chunkqueue_reset(con->recv); 
    570545        chunkqueue_reset(con->send_raw); 
     
    939914} 
    940915 
    941 static int http_chunk_append_len(chunkqueue *cq, size_t len) { 
    942         size_t i, olen = len, j; 
    943         buffer *b; 
    944  
    945         b = buffer_init(); 
    946  
    947         if (len == 0) { 
    948                 buffer_copy_string(b, "0"); 
    949         } else { 
    950                 for (i = 0; i < 8 && len; i++) { 
    951                         len >>= 4; 
    952                 } 
    953  
    954                 /* i is the number of hex digits we have */ 
    955                 buffer_prepare_copy(b, i + 1); 
    956  
    957                 for (j = i-1, len = olen; j+1 > 0; j--) { 
    958                         b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); 
    959                         len >>= 4; 
    960                 } 
    961                 b->used = i; 
    962                 b->ptr[b->used++] = '\0'; 
    963         } 
    964  
    965         buffer_append_string(b, "\r\n"); 
    966         chunkqueue_append_buffer(cq, b); 
    967         len = b->used - 1; 
    968  
    969         buffer_free(b); 
    970  
    971         return len; 
    972 } 
    973  
    974  
    975 /** 
    976  * apply chunk encoding if necessary 
    977  */ 
    978 int http_stream_encoder(server *srv, connection *con, chunkqueue *in, chunkqueue *out) { 
    979         chunk *c; 
    980         int is_chunked; 
    981         int we_have = 0; 
    982  
    983         UNUSED(srv); 
    984  
    985         /* no more data to encode. */ 
    986         if (out->is_closed) return 0; 
    987         /**/ 
    988  
    989         is_chunked = (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED); 
    990  
    991         if (is_chunked) { 
    992                 for (c = in->first; c; c = c->next) { 
    993                         switch (c->type) { 
    994                         case MEM_CHUNK: 
    995                                 if (c->mem->used == 0) continue; 
    996  
    997                                 we_have = c->mem->used - c->offset - 1; 
    998                                 in->bytes_out += we_have; 
    999                                 if(we_have == 0) continue; 
    1000                                 we_have += http_chunk_append_len(out, we_have); 
    1001                                 chunkqueue_append_buffer(out, c->mem); 
    1002                                 c->offset = c->mem->used - 1; 
    1003                                 break; 
    1004                         case FILE_CHUNK: 
    1005                                 if (c->file.length == 0) continue; 
    1006  
    1007                                 we_have = c->file.length; 
    1008                                 in->bytes_out += we_have; 
    1009                                 we_have += http_chunk_append_len(out, c->file.length); 
    1010                                 if(c->file.is_temp) { 
    1011                                         chunkqueue_steal_tempfile(out, c); 
    1012                                 } else { 
    1013                                         chunkqueue_append_file(out, c->file.name, c->file.start, c->file.length); 
    1014                                 } 
    1015  
    1016                                 c->offset = c->file.length; 
    1017                                 break; 
    1018                         case UNUSED_CHUNK: 
    1019                                 break; 
    1020                         } 
    1021                         chunkqueue_append_mem(out, "\r\n", 2 + 1); 
    1022                         we_have += 2; 
    1023                         out->bytes_in += we_have; 
    1024                 } 
    1025                 if (in->is_closed) { 
    1026                         chunkqueue_append_mem(out, "0\r\n\r\n", 5 + 1); 
    1027                         out->bytes_in += 5; 
    1028                 } 
    1029         } else { 
    1030                 for (c = in->first; c; c = c->next) { 
    1031                         switch (c->type) { 
    1032                         case MEM_CHUNK: 
    1033                                 if (c->mem->used == 0) continue; 
    1034  
    1035                                 we_have = c->mem->used - c->offset - 1; 
    1036                                 in->bytes_out += we_have; 
    1037                                 if(we_have == 0) continue; 
    1038                                 if (c->offset == 0) { 
    1039                                         chunkqueue_steal_chunk(out, c); 
    1040                                 } else { 
    1041                                         chunkqueue_append_buffer(out, c->mem); 
    1042                                         c->offset = c->mem->used - 1; 
    1043                                 } 
    1044                                 break; 
    1045                         case FILE_CHUNK: 
    1046                                 if (c->file.length == 0) continue; 
    1047  
    1048                                 we_have = c->file.length; 
    1049                                 in->bytes_out += we_have; 
    1050                                 if(c->file.is_temp) { 
    1051                                         chunkqueue_steal_tempfile(out, c); 
    1052                                 } else { 
    1053                                         chunkqueue_append_file(out, c->file.name, c->file.start, c->file.length); 
    1054                                 } 
    1055  
    1056                                 c->offset = c->file.length; 
    1057                                 break; 
    1058                         case UNUSED_CHUNK: 
    1059                                 break; 
    1060                         } 
    1061                         in->bytes_out += we_have; 
    1062                         out->bytes_in += we_have; 
    1063                 } 
    1064         } 
    1065  
    1066         chunkqueue_remove_finished_chunks(in); 
    1067  
    1068         if (in->is_closed) { 
    1069                 /* mark the output queue as finished. */ 
    1070                 out->is_closed = 1; 
    1071         } 
    1072         return 0; 
    1073 } 
    1074  
    1075916int connection_state_machine(server *srv, connection *con) { 
    1076917        int done = 0, r; 
     
    12961137                            con->request.content_length == 0) { 
    12971138                                con->recv->is_closed = 1; 
    1298                         } 
     1139                        } 
    12991140 
    13001141                        if (!con->recv->is_closed && 
     
    13561197                        break; 
    13571198                case CON_STATE_HANDLE_RESPONSE_HEADER: 
     1199                        /* handle the HTTP response headers, or generate error-page */ 
     1200                        connection_handle_response_header(srv, con); 
     1201 
    13581202                        /* we got a response header from the backend 
    13591203                         * call all plugins who want to modify the response header 
     
    13621206                         * 
    13631207                         */ 
     1208                        switch (plugins_call_handle_response_header(srv, con)) { 
     1209                        case HANDLER_GO_ON: 
     1210                        default: 
     1211                                break; 
     1212                        } 
    13641213 
    13651214                        connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); 
     
    13671216                        break; 
    13681217                case CON_STATE_WRITE_RESPONSE_HEADER: 
    1369                         /* create the HTTP response header */ 
    1370                         connection_handle_response_header(srv, con); 
     1218                        /* write response headers */ 
     1219                        http_response_write_header(srv, con, con->send_raw); 
    13711220 
    13721221                        connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_CONTENT); 
     
    13751224                case CON_STATE_WRITE_RESPONSE_CONTENT: 
    13761225                        fdevent_event_del(srv->ev, con->sock); 
     1226 
     1227                        /* looks like we shall read some content from the backend */ 
     1228                        switch (plugins_call_handle_read_response_content(srv, con)) { 
     1229                        case HANDLER_WAIT_FOR_EVENT: 
     1230                                if (!con->send->is_closed && con->send->bytes_in == con->send->bytes_out) { 
     1231                                        /* need to wait for more data */ 
     1232                                        return HANDLER_WAIT_FOR_EVENT; 
     1233                                } 
     1234                                break; 
     1235                        case HANDLER_GO_ON: 
     1236                        default: 
     1237                                break; 
     1238                        } 
    13771239 
    13781240                        /* we might have new content in the con->send buffer 
     
    13811243                         * - compression 
    13821244                         */ 
    1383  
    1384                         /* looks like we shall read some content from the backend */ 
    1385  
    1386                         switch (plugins_call_handle_read_response_content(srv, con)) { 
     1245                        switch (plugins_call_handle_filter_response_content(srv, con)) { 
    13871246                        case HANDLER_GO_ON: 
    13881247                        default: 
     
    13901249                        } 
    13911250 
    1392                         http_stream_encoder(srv, con, con->send, con->send_raw); 
    1393  
     1251                        /* copy output from filters into send_raw. */ 
     1252                        r = filter_chain_copy_output(con->send_filters, con->send_raw); 
     1253 
     1254                        if (!con->send_raw->is_closed && con->send_raw->bytes_in == con->send_raw->bytes_out) { 
     1255                                return HANDLER_WAIT_FOR_EVENT; 
     1256                        } 
    13941257                        switch(network_write_chunkqueue(srv, con, con->send_raw)) { 
    13951258                        case NETWORK_STATUS_SUCCESS: 
  • trunk/src/mod_compress.c

    r1499 r1502  
    424424                /* file exists */ 
    425425 
    426                 chunkqueue_append_file(con->send_raw, p->ofn, 0, compressed_sce->st.st_size); 
     426                chunkqueue_append_file(con->send, p->ofn, 0, compressed_sce->st.st_size); 
    427427                con->send->is_closed = 1; 
    428428 
     
    500500        if (ret != 0) return -1; 
    501501 
    502         chunkqueue_append_file(con->send_raw, p->ofn, 0, r); 
     502        chunkqueue_append_file(con->send, p->ofn, 0, r); 
    503503        con->send->is_closed = 1; 
    504504 
  • trunk/src/mod_proxy_core.c

    r1496 r1502  
    698698        } 
    699699 
    700         /* we are finished decoding the response content. */ 
    701         if(out->is_closed) { 
    702                 proxy_copy_response(srv, con, sess); 
    703         } else { 
     700        /* we are finished decoding the response headers. */ 
     701        if(!out->is_closed) { 
    704702                /* We don't have all the response content try to enable chunked encoding. */ 
    705703                /* does the client allow us to send chunked encoding ? */ 
     
    709707                } 
    710708        } 
     709 
     710        /* we might have part of the response content too */ 
     711        proxy_copy_response(srv, con, sess); 
    711712 
    712713        return PARSE_SUCCESS; /* we have a full header */ 
  • trunk/src/mod_proxy_core.h

    r1496 r1502  
    6767} proxy_state_t; 
    6868 
    69 typedef struct { 
     69typedef struct proxy_session { 
    7070        proxy_connection *proxy_con; 
    7171        proxy_backend *proxy_backend; 
  • trunk/src/mod_proxy_core_protocol.h

    r1465 r1502  
    1212                static int x(server *srv, proxy_session *sess, chunkqueue *in, chunkqueue *out) 
    1313 
    14 typedef struct { 
     14typedef struct proxy_protocol { 
    1515        buffer *name; 
    1616 
  • trunk/src/plugin.c

    r1496 r1502  
    4141                PLUGIN_FUNC_HANDLE_START_BACKEND, 
    4242                PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, 
     43                PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, 
    4344                PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, 
     45                PLUGIN_FUNC_HANDLE_FILTER_RESPONSE_CONTENT, 
    4446                PLUGIN_FUNC_HANDLE_JOBLIST, 
    4547                PLUGIN_FUNC_HANDLE_DOCROOT, 
     
    122124PLUGIN_STATIC(mod_evhost); 
    123125PLUGIN_STATIC(mod_expire); 
     126PLUGIN_STATIC(mod_chunked); 
    124127PLUGIN_STATIC(mod_indexfile); 
    125128PLUGIN_STATIC(mod_mysql_vhost); 
     
    157160PLUGIN_STATIC(mod_evhost), 
    158161PLUGIN_STATIC(mod_expire), 
     162PLUGIN_STATIC(mod_chunked), 
    159163PLUGIN_STATIC(mod_indexfile), 
    160164PLUGIN_STATIC(mod_mysql_vhost), 
     
    366370PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_START_BACKEND, handle_start_backend) 
    367371PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, handle_send_request_content) 
     372PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, handle_response_header) 
    368373PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, handle_read_response_content) 
     374PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_FILTER_RESPONSE_CONTENT, handle_filter_response_content) 
    369375PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close) 
    370376PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_DONE, handle_response_done) 
     
    494500                PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_START_BACKEND, handle_start_backend); 
    495501                PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, handle_send_request_content); 
     502                PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, handle_response_header); 
    496503                PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, handle_read_res