Changeset 696

Show
Ignore:
Timestamp:
09/08/2005 10:00:32 AM (3 years ago)
Author:
jan
Message:

- don't keep the full fastcgi response in memory
- don't cache more than 4 chunks in a queue for reuse
- on chunkqueue_reset, clean the chunks

Location:
branches/lighttpd-merge-1.4.x/src
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • branches/lighttpd-merge-1.4.x/src/chunk.c

    r1 r696  
    5555        free(c); 
    5656} 
     57 
     58static void chunk_reset(chunk *c) { 
     59        if (!c) return; 
     60         
     61        /* c->data.mem overlaps with c->data.file.name */ 
     62        switch (c->type) { 
     63        case MEM_CHUNK: buffer_reset(c->data.mem); break; 
     64        case FILE_CHUNK: buffer_reset(c->data.file.name); break; 
     65        default: break;  
     66        } 
     67} 
     68 
    5769 
    5870void chunkqueue_free(chunkqueue *cq) { 
     
    8799                cq->unused = c->next; 
    88100                c->next = NULL; 
     101                cq->unused_chunks--; 
    89102        } 
    90103         
     
    117130 
    118131void chunkqueue_reset(chunkqueue *cq) { 
     132        chunk *c; 
    119133        /* move everything to the unused queue */ 
    120134         
    121         if (cq->last == NULL) return; 
    122          
    123         cq->last->next = cq->unused; 
    124         cq->unused = cq->first; 
    125          
    126         /* disconnect active chain */ 
    127         cq->first = cq->last = NULL; 
     135        /* mark all read written */  
     136        for (c = cq->first; c; c = c->next) { 
     137                switch(c->type) { 
     138                case MEM_CHUNK: 
     139                        c->offset = c->data.mem->used - 1; 
     140                        break; 
     141                case FILE_CHUNK: 
     142                        c->offset = c->data.file.length; 
     143                        break; 
     144                default:  
     145                        break; 
     146                } 
     147        } 
     148 
     149        chunkqueue_remove_finished_chunks(cq); 
    128150} 
    129151 
     
    262284} 
    263285 
    264 #ifdef DEBUG_CHUNK 
    265  
    266 static int write_chunkqueue(int fd, chunkqueue *c) { 
    267         UNUSED(fd); 
    268         UNUSED(c); 
    269  
    270         return 0; 
    271 } 
    272  
    273 int main(int argc, char **argv) { 
    274         chunkqueue *c; 
    275         buffer *b, *fn; 
    276          
    277         UNUSED(argc); 
    278         UNUSED(argv); 
    279  
    280         c = chunkqueue_init(); 
    281          
    282         fn = buffer_init_string("server.c"); 
    283          
    284         chunkqueue_append_file(c, fn, 0, 10); 
    285         chunkqueue_append_file(c, fn, 10, 10); 
    286         chunkqueue_append_file(c, fn, 20, 10); 
    287          
    288         write_chunkqueue(STDERR_FILENO, c); 
    289         chunkqueue_reset(c); 
    290          
    291         b = buffer_init(); 
    292         buffer_copy_string(b, "\ntest string mit vielen Zeichen\n"); 
    293         chunkqueue_append_buffer(c, b); 
    294          
    295         write_chunkqueue(STDERR_FILENO, c); 
    296         chunkqueue_reset(c); 
    297          
    298         chunkqueue_append_file(c, fn, 0, 10); 
    299         buffer_copy_string(b, "\ntest string mit vielen Zeichen\n"); 
    300         chunkqueue_append_buffer(c, b); 
    301         chunkqueue_append_file(c, fn, 10, 10); 
    302         chunkqueue_append_file(c, fn, 20, 10); 
    303         chunkqueue_append_file(c, fn, 50, 40); 
    304          
    305         write_chunkqueue(STDERR_FILENO, c); 
    306         chunkqueue_reset(c); 
    307          
    308         chunkqueue_free(c); 
    309          
    310         return 0; 
    311 } 
    312 #endif 
     286int chunkqueue_remove_finished_chunks(chunkqueue *cq) { 
     287        chunk *c; 
     288 
     289        for (c = cq->first; c; c = cq->first) { 
     290                int is_finished = 0; 
     291 
     292                switch (c->type) { 
     293                case MEM_CHUNK: 
     294                        if (c->offset == (off_t)c->data.mem->used - 1) is_finished = 1; 
     295                        break; 
     296                case FILE_CHUNK: 
     297                        if (c->offset == c->data.file.length) is_finished = 1;  
     298                        break; 
     299                default:  
     300                        break; 
     301                } 
     302 
     303                if (!is_finished) break; 
     304 
     305                chunk_reset(c); 
     306 
     307                cq->first = c->next; 
     308                if (c == cq->last) cq->last = NULL; 
     309 
     310                /* keep at max 4 chunks in the 'unused'-cache */ 
     311                if (cq->unused_chunks > 4) { 
     312                        chunk_free(c); 
     313                } else { 
     314                        c->next = cq->unused; 
     315                        cq->unused = c; 
     316                        cq->unused_chunks++; 
     317                } 
     318        } 
     319 
     320        return 0; 
     321} 
  • branches/lighttpd-merge-1.4.x/src/chunk.h

    r1 r696  
    3636         
    3737        chunk *unused; 
     38        size_t unused_chunks; 
    3839} chunkqueue; 
    3940 
     
    4748buffer * chunkqueue_get_prepend_buffer(chunkqueue *c); 
    4849 
     50int chunkqueue_remove_finished_chunks(chunkqueue *cq); 
     51 
    4952off_t chunkqueue_length(chunkqueue *c); 
    5053off_t chunkqueue_written(chunkqueue *c); 
  • branches/lighttpd-merge-1.4.x/src/mod_fastcgi.c

    r690 r696  
    303303 
    304304typedef struct { 
    305         buffer  *response;  
    306         size_t   response_len; 
    307         int      response_type; 
    308         int      response_padding; 
    309         size_t   response_request_id; 
    310          
    311305        fcgi_proc *proc; 
    312306        fcgi_extension_host *host; 
     
    320314        size_t    write_offset; 
    321315         
    322         read_buffer *rb; 
     316        chunkqueue *rb; 
    323317         
    324318        buffer   *response_header; 
     
    355349        hctx->fde_ndx = -1; 
    356350         
    357         hctx->response = buffer_init(); 
    358351        hctx->response_header = buffer_init(); 
    359352        hctx->write_buffer = buffer_init(); 
     
    363356        hctx->proc = NULL; 
    364357         
    365         hctx->response_len = 0; 
    366         hctx->response_type = 0; 
    367         hctx->response_padding = 0; 
    368         hctx->response_request_id = 0; 
    369358        hctx->fd = -1; 
    370359         
    371360        hctx->reconnects = 0; 
     361 
     362        hctx->rb = chunkqueue_init(); 
    372363         
    373364        return hctx; 
     
    375366 
    376367static void handler_ctx_free(handler_ctx *hctx) { 
    377         buffer_free(hctx->response); 
    378368        buffer_free(hctx->response_header); 
    379369        buffer_free(hctx->write_buffer); 
    380          
    381         if (hctx->rb) { 
    382                 if (hctx->rb->ptr) free(hctx->rb->ptr); 
    383                 free(hctx->rb); 
    384         } 
    385          
     370 
     371        chunkqueue_free(hctx->rb); 
     372 
    386373        free(hctx); 
    387374} 
     
    19401927} 
    19411928 
     1929typedef struct { 
     1930        buffer  *b;  
     1931        size_t   len; 
     1932        int      type; 
     1933        int      padding; 
     1934        size_t   request_id; 
     1935} fastcgi_response_packet; 
     1936 
     1937static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) { 
     1938        chunk * c; 
     1939        size_t offset = 0; 
     1940        size_t toread = 0; 
     1941        FCGI_Header *header; 
     1942 
     1943        if (!hctx->rb->first) return -1; 
     1944 
     1945        packet->b = buffer_init(); 
     1946        packet->len = 0; 
     1947        packet->type = 0; 
     1948        packet->padding = 0; 
     1949        packet->request_id = 0; 
     1950 
     1951        /* get at least the FastCGI header */ 
     1952        for (c = hctx->rb->first; c; c = c->next) { 
     1953                if (packet->b->used == 0) { 
     1954                        buffer_copy_string_len(packet->b, c->data.mem->ptr + c->offset, c->data.mem->used - c->offset - 1); 
     1955                } else { 
     1956                        buffer_append_string_len(packet->b, c->data.mem->ptr + c->offset, c->data.mem->used - c->offset - 1); 
     1957                } 
     1958 
     1959                if (packet->b->used >= sizeof(*header) + 1) break; 
     1960        } 
     1961 
     1962        if ((packet->b->used == 0) || 
     1963            (packet->b->used - 1 < sizeof(FCGI_Header))) { 
     1964                /* no header */ 
     1965                buffer_free(packet->b); 
     1966 
     1967                log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header to small"); 
     1968                return -1; 
     1969        } 
     1970 
     1971        /* we have at least a header, now check how much me have to fetch */  
     1972        header = (FCGI_Header *)(packet->b->ptr); 
     1973                         
     1974        packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength; 
     1975        packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); 
     1976        packet->type = header->type; 
     1977        packet->padding = header->paddingLength; 
     1978 
     1979        /* the first bytes in packet->b are the header */ 
     1980        offset = sizeof(*header); 
     1981 
     1982        log_error_write(srv, __FILE__, __LINE__, "sddd", "FastCGI: got header:", packet->len, packet->request_id, packet->type); 
     1983 
     1984        /* ->b should only be the content */ 
     1985        buffer_reset(packet->b); 
     1986 
     1987        if (packet->len) { 
     1988                /* copy the content */ 
     1989                for (; c && (packet->b->used < packet->len + 1); c = c->next) { 
     1990                        toread = c->data.mem->used - c->offset - offset - 1 > packet->len ? packet->len : c->data.mem->used - c->offset - offset - 1; 
     1991                         
     1992                        log_error_write(srv, __FILE__, __LINE__, "sdd", "FastCGI: reading content:", packet->b->used, toread); 
     1993 
     1994                        buffer_append_string_len(packet->b, c->data.mem->ptr + c->offset + offset, toread); 
     1995                        offset = 0; 
     1996                } 
     1997 
     1998                if (packet->b->used < packet->len + 1) { 
     1999                        /* we didn't got the full packet */ 
     2000                        log_error_write(srv, __FILE__, __LINE__, "sdd", "FastCGI: not the full packet", packet->b->used, packet->len); 
     2001 
     2002                        buffer_free(packet->b); 
     2003                        return -1; 
     2004                } 
     2005 
     2006                packet->b->used -= packet->padding; 
     2007                packet->b->ptr[packet->b->used - 1] = '\0'; 
     2008        } 
     2009 
     2010        /* tag the chunks as read */ 
     2011        toread = packet->len + sizeof(FCGI_Header); 
     2012        for (c = hctx->rb->first; c && toread; c = c->next) { 
     2013                if (c->data.mem->used - c->offset - 1 <= toread) { 
     2014                        /* we read this whole buffer, move it to unused */ 
     2015                        toread -= c->data.mem->used - c->offset - 1; 
     2016                        c->offset = c->data.mem->used - 1; /* everthing has been written */ 
     2017                } else { 
     2018                        c->offset += toread; 
     2019                        toread = 0; 
     2020                } 
     2021        } 
     2022 
     2023        chunkqueue_remove_finished_chunks(hctx->rb); 
     2024         
     2025        return 0; 
     2026} 
    19422027 
    19432028static int fcgi_demux_response(server *srv, handler_ctx *hctx) { 
    1944         ssize_t len; 
    19452029        int fin = 0; 
    1946         int b; 
     2030        int toread; 
    19472031        ssize_t r; 
    19482032         
     
    19562040         * check how much we have to read  
    19572041         */ 
    1958         if (ioctl(hctx->fd, FIONREAD, &b)) { 
     2042        if (ioctl(hctx->fd, FIONREAD, &toread)) { 
    19592043                log_error_write(srv, __FILE__, __LINE__, "sd",  
    19602044                                "unexpected end-of-file (perhaps the fastcgi process died):", 
     
    19642048         
    19652049        /* init read-buffer */ 
    1966         if (hctx->rb == NULL) { 
    1967                 hctx->rb = calloc(1, sizeof(*hctx->rb)); 
    1968         } 
    1969          
    1970         if (b > 0) { 
    1971                 if (hctx->rb->size == 0) { 
    1972                         hctx->rb->size = b; 
    1973                         hctx->rb->ptr = malloc(hctx->rb->size * sizeof(*hctx->rb->ptr)); 
    1974                 } else if (hctx->rb->size < hctx->rb->used + b) { 
    1975                         hctx->rb->size += b; 
    1976                         hctx->rb->ptr = realloc(hctx->rb->ptr, hctx->rb->size * sizeof(*hctx->rb->ptr)); 
    1977                 } 
    1978                  
     2050         
     2051        if (toread > 0) { 
     2052                buffer *b; 
     2053 
     2054                b = chunkqueue_get_append_buffer(hctx->rb); 
     2055                buffer_prepare_copy(b, toread + 1); 
     2056 
    19792057                /* append to read-buffer */ 
    1980                 if (-1 == (r = read(hctx->fd, hctx->rb->ptr + hctx->rb->used, b))) { 
     2058                if (-1 == (r = read(hctx->fd, b->ptr, toread))) { 
    19812059                        log_error_write(srv, __FILE__, __LINE__, "sds",  
    19822060                                        "unexpected end-of-file (perhaps the fastcgi process died):", 
     
    19872065                /* this should be catched by the b > 0 above */ 
    19882066                assert(r); 
    1989                  
    1990                 hctx->rb->used += r; 
     2067 
     2068                b->used = r + 1; /* one extra for the fake \0 */ 
     2069                b->ptr[b->used - 1] = '\0'; 
    19912070        } else { 
    19922071                log_error_write(srv, __FILE__, __LINE__, "ssdsdsd",  
     
    19982077                return -1; 
    19992078        } 
    2000          
    2001         /* parse all fcgi packets  
    2002          *  
    2003          *   start: hctx->rb->ptr  
    2004          *   end  : hctx->rb->ptr + hctx->rb->used 
    2005          *  
    2006          */ 
     2079 
     2080        /* 
     2081         * parse the fastcgi packets and forward the content to the write-queue 
     2082         * 
     2083         */      
    20072084        while (fin == 0) { 
    2008                 size_t request_id; 
    2009                  
    2010                 if (hctx->response_len == 0) { 
    2011                         FCGI_Header *header; 
    2012                          
    2013                         if (hctx->rb->used - hctx->rb->offset < sizeof(*header)) { 
    2014                                 /* didn't get the full header packet (most often 0), 
    2015                                  * but didn't recieved the final packet either 
     2085                fastcgi_response_packet packet; 
     2086 
     2087                /* check if we have at least one packet */ 
     2088                if (0 != fastcgi_get_packet(srv, hctx, &packet)) { 
     2089                        /* no full packet */ 
     2090 
     2091                        hctx->delayed = 1; 
     2092 
     2093                        break; 
     2094                } 
     2095 
     2096                switch(packet.type) { 
     2097                case FCGI_STDOUT: 
     2098                        if (packet.len == 0) break; 
     2099 
     2100                        /* is the header already finished */ 
     2101                        if (0 == con->file_started) { 
     2102                                char *c; 
     2103                                size_t blen; 
     2104                                         
     2105                                /* search for header terminator  
    20162106                                 *  
    2017                                  * we will come back later and finish everything 
    2018                                  *  
     2107                                 * if we start with \r\n check if last packet terminated with \r\n 
     2108                                 * if we start with \n check if last packet terminated with \n 
     2109                                 * search for \r\n\r\n 
     2110                                 * search for \n\n 
    20192111                                 */ 
    2020                                  
    2021                                 hctx->delayed = 1; 
    2022 #if 0 
    2023                                 log_error_write(srv, __FILE__, __LINE__, "sddd", "didn't get the full header: ", 
    2024                                                 hctx->rb->used - hctx->rb->offset, sizeof(*header), 
    2025                                                 fcgi_fd 
    2026                                                 ); 
    2027 #endif 
    2028                                 break; 
    2029                         } 
    2030 #if 0 
    2031                         fprintf(stderr, "fcgi-version: %02x\n", hctx->rb->ptr[hctx->rb->offset]); 
    2032 #endif 
    2033                          
    2034                         header = (FCGI_Header *)(hctx->rb->ptr + hctx->rb->offset); 
    2035                         hctx->rb->offset += sizeof(*header); 
    2036                          
    2037                         len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength; 
    2038                         request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); 
    2039  
    2040                         hctx->response_len = len; 
    2041                         hctx->response_request_id = request_id; 
    2042                         hctx->response_type = header->type; 
    2043                         hctx->response_padding = header->paddingLength; 
    2044                          
    2045 #if 0 
    2046                         log_error_write(srv, __FILE__, __LINE__, "sddd", "offset: ", 
    2047                                         fcgi_fd, hctx->rb->offset, header->type 
    2048                                         ); 
    2049 #endif 
    2050                          
    2051                 } else { 
    2052                         len = hctx->response_len; 
    2053                 } 
    2054                  
    2055                 if (hctx->rb->used - hctx->rb->offset < hctx->response_len) { 
    2056                         /* we are not finished yet */ 
    2057                         break; 
    2058                 } 
    2059                  
    2060                 hctx->response->ptr = hctx->rb->ptr + hctx->rb->offset; 
    2061                 hctx->rb->offset += hctx->response_len; 
    2062 #if 0 
    2063                 log_error_write(srv, __FILE__, __LINE__, "sdd", "offset: ", 
    2064                                 fcgi_fd, hctx->rb->offset 
    2065                                 ); 
    2066 #endif 
    2067                  
    2068                 /* remove padding */ 
    2069 #if 0 
    2070                 hctx->response->ptr[hctx->response_len - hctx->response_padding] = '\0'; 
    2071 #endif 
    2072                 hctx->response->used = hctx->response_len - hctx->response_padding + 1; 
    2073                  
    2074                 /* mark the fast-cgi packet as finished */ 
    2075                 hctx->response_len = 0; 
    2076                  
    2077                 switch(hctx->response_type) { 
    2078                 case FCGI_STDOUT: 
    2079                         if (len) { 
    2080 #if 0 
    2081                                 log_error_write(srv, __FILE__, __LINE__, "sdb", "len", len, hctx->response); 
    2082 #endif 
    2083                                  
    2084                                 if (0 == con->got_response) { 
    2085                                         con->got_response = 1; 
    2086                                         buffer_prepare_copy(hctx->response_header, 128); 
     2112 
     2113                                if (hctx->response_header->used == 0) { 
     2114                                        buffer_copy_string_buffer(hctx->response_header, packet.b); 
     2115                                } else { 
     2116                                        buffer_append_string_buffer(hctx->response_header, packet.b); 
    20872117                                } 
    2088                                  
    2089                                 if (0 == con->file_started) { 
    2090                                         char *c; 
    2091                                         size_t hlen; 
    2092                                          
    2093                                         /* search for header terminator  
    2094                                          *  
    2095                                          * if we start with \r\n check if last packet terminated with \r\n 
    2096                                          * if we start with \n check if last packet terminated with \n 
    2097                                          * search for \r\n\r\n 
    2098                                          * search for \n\n 
    2099                                          */ 
    2100                                          
    2101                                         if (hctx->response->used > 2 && 
    2102                                             hctx->response->ptr[0] == '\r' && 
    2103                                             hctx->response->ptr[1] == '\n' && 
    2104                                             hctx->response_header->used > 3 && 
    2105                                             hctx->response_header->ptr[hctx->response_header->used - 3] == '\r' && 
    2106                                             hctx->response_header->ptr[hctx->response_header->used - 2] == '\n') { 
    2107                                                 hlen = 2; 
    2108                                                 c = hctx->response->ptr + 2; 
    2109                                         } else if (hctx->response->used > 2 && 
    2110                                                    hctx->response->ptr[0] == '\n' && 
    2111                                                    hctx->response_header->used > 2 && 
    2112                                                    hctx->response_header->ptr[hctx->response_header->used - 2] == '\n') { 
    2113                                                 hlen = 1; 
    2114                                                 c = hctx->response->ptr + 1; 
    2115                                         } else if (NULL != (c = buffer_search_string_len(hctx->response, "\r\n\r\n", 4))) { 
    2116                                                 c += 4; 
    2117                                                 hlen = c - hctx->response->ptr; 
     2118 
     2119                                if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\r\n\r\n")))) { 
     2120                                        blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 4; 
     2121                                        hctx->response_header->used = c - hctx->response_header->ptr; 
     2122                                        c += 4; /* point the the start of the response */ 
     2123                                } else if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\n\n")))) { 
     2124                                        blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 2; 
     2125                                        hctx->response_header->used = c - hctx->response_header->ptr; 
     2126                                        c += 2; /* point the the start of the response */ 
     2127                                } else { 
     2128                                        /* no luck, no header found */ 
     2129                                        break; 
     2130                                } 
     2131 
     2132                                /* parse the response header */ 
     2133                                fcgi_response_parse(srv, con, p, hctx->response_header); 
    21182134                                                 
    2119                                         } else if (NULL != (c = buffer_search_string_len(hctx->response, "\n\n", 2))) { 
    2120                                                 c += 2; 
    2121                                                 hlen = c - hctx->response->ptr; 
    2122                                         } 
    2123                                          
    2124                                         if (c != NULL) { 
    2125                                                 size_t blen = hctx->response->used - hlen - 1; 
    2126                                                 /* found */ 
     2135                                if (host->mode != FCGI_AUTHORIZER || 
     2136                                    !(con->http_status == 0 || 
     2137                                      con->http_status == 200)) { 
     2138                                                         
     2139                                        con->file_started = 1; 
    21272140                                                 
    2128                                                 buffer_append_string_len(hctx->response_header, hctx->response->ptr, hlen); 
    2129 #if 0 
    2130                                                 log_error_write(srv, __FILE__, __LINE__, "ss", "Header:", hctx->response_header->ptr); 
    2131 #endif 
    2132                                                 /* parse the response header */ 
    2133                                                 fcgi_response_parse(srv, con, p, hctx->response_header); 
    2134                                                  
    2135                                                 if (host->mode != FCGI_AUTHORIZER || 
    2136                                                     !(con->http_status == 0 || 
    2137                                                       con->http_status == 200)) { 
    2138                                                          
    2139                                                         con->file_started = 1; 
    2140                                                  
    2141                                                         if (blen) { 
    2142                                                                 /* enable chunked-transfer-encoding */ 
    2143                                                                 if (con->request.http_version == HTTP_VERSION_1_1 && 
    2144                                                                     !(con->parsed_response & HTTP_CONTENT_LENGTH)) { 
    2145                                                                         con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; 
    2146                                                                 } 
    2147  
    2148