Changeset 1896

Show
Ignore:
Timestamp:
08/12/2007 10:02:07 PM (9 months ago)
Author:
jan
Message:

- added parsing of X-Progress-ID=... from the query-string
- fixed memory-leak
- cleaned up the connection-map

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/NEWS

    r1866 r1896  
    55 
    66- 1.5.0 - ... 
    7   * -F option added for spawm-fcgi 
     7  * -F option added for spawn-fcgi 
    88  * replaced mod_fastcgi, mod_scgi, mod_proxy with mod_proxy_core + backends 
    9   * enabled mod_uploadprogress again 
     9  * added query-string parsing for mod_uploadprogress 
    1010  * added threaded stat()  
    1111  * added threaded disk-read() support 
  • trunk/src/mod_uploadprogress.c

    r1653 r1896  
    1919 * uploadprogress for lighttpd 
    2020 * 
    21  * no author and contact infos yet? Shamelessly adding... 
    22  * 
    2321 * Initial: Jan Kneschke <jan@kneschke.de> 
    2422 * Timeout+Status addon: Bjoern Kalkbrenner <terminar@cyberphoria.org> [20070112] 
    2523 * 
     24 * the timeout is used to keep in the status information intact even if the parent  
     25 * connection is gone already 
    2626 */ 
    2727 
    2828typedef struct { 
    29         buffer     *con_id; 
     29        buffer     *tracking_id; 
    3030        connection *con; 
    31         int timeout; 
     31 
     32        time_t timeout; 
    3233        int status; 
    3334} connection_map_entry; 
     
    5354        connection_map *con_map; 
    5455 
     56        buffer *tmp_buf; /** used as temporary buffer for extracting the tracking id */ 
     57 
    5558        plugin_config **config_storage; 
    5659 
     
    6568 
    6669/* init the plugin data */ 
    67 connection_map *connection_map_init() { 
     70static connection_map *connection_map_init() { 
    6871        connection_map *cm; 
    6972 
     
    7376} 
    7477 
    75 void connection_map_free(connection_map *cm) { 
     78static void connection_map_free(connection_map *cm) { 
    7679        size_t i; 
    7780        for (i = 0; i < cm->size; i++) { 
     
    8083                if (!cme) break; 
    8184 
    82                 if (cme->con_id) { 
    83                         buffer_free(cme->con_id); 
     85                if (cme->tracking_id) { 
     86                        buffer_free(cme->tracking_id); 
    8487                } 
    8588                free(cme); 
     
    8992} 
    9093 
    91 connection_map_entry *connection_map_insert(connection_map *cm, connection *con, buffer *con_id) { 
     94static connection_map_entry *connection_map_insert(connection_map *cm, buffer *tracking_id, connection *con) { 
    9295        connection_map_entry *cme; 
    9396        size_t i; 
     
    112115        } else { 
    113116                cme = malloc(sizeof(*cme)); 
     117                cme->tracking_id = buffer_init(); 
    114118        } 
    115119        cme->timeout = 0; 
    116120        cme->status = 0; 
    117         cme->con_id = buffer_init(); 
    118         buffer_copy_string_buffer(cme->con_id, con_id); 
     121        buffer_copy_string_buffer(cme->tracking_id, tracking_id); 
    119122        cme->con = con; 
    120123 
     
    124127} 
    125128 
    126 connection_map_entry *connection_map_get_connection_entry(connection_map *cm, buffer *con_id) { 
     129static connection_map_entry *connection_map_get_connection_entry(connection_map *cm, buffer *tracking_id) { 
    127130        size_t i; 
    128131 
     
    130133                connection_map_entry *cme = cm->ptr[i]; 
    131134 
    132                 if (buffer_is_equal(cme->con_id, con_id)) { 
     135                if (buffer_is_equal(cme->tracking_id, tracking_id)) { 
    133136                        /* found connection */ 
    134137                        return cme; 
     
    138141} 
    139142 
    140 int connection_map_remove_connection(connection_map *cm, connection_map_entry *entry) { 
     143static int connection_map_remove_connection(connection_map *cm, connection_map_entry *entry) { 
    141144        size_t i; 
    142145 
     
    146149                if (cme == entry) { 
    147150                        /* found connection */ 
    148                         buffer_reset(cme->con_id); 
     151                        buffer_reset(cme->tracking_id); 
    149152                        cme->timeout=0; 
    150153                        cme->status=0; 
    151                         cme->con = NULL; 
    152154 
    153155                        cm->used--; 
     
    166168} 
    167169 
    168 int connection_map_set_timeout(plugin_data *p, connection *con) { 
     170/** 
     171 * remove dead tracking IDs  
     172 * 
     173 * uploadprogress.remove-timeout sets a grace-period in which the  
     174 * connection status is still known even of the connection is already  
     175 * being removed 
     176 * 
     177 */ 
     178static void connection_map_clear_timeout_connections(connection_map *cm) { 
    169179        size_t i; 
    170  
    171         if(p->conf.debug) TRACE("set_timeout for connection=%p",con); 
    172         for (i = 0; i < p->con_map->used; i++) { 
    173                 connection_map_entry *cme = p->con_map->ptr[i]; 
    174  
    175                 if (cme->con == con) { 
    176                         cme->con = NULL; 
    177                          
    178                         /* found connection */ 
    179                         cme->timeout = time(NULL) + p->conf.remove_timeout; 
    180                         if(p->conf.debug) TRACE("set_timeout for connection=%p, timeout=%d",con, cme->timeout); 
    181  
    182                         return 1; 
    183                 } 
    184         } 
    185  
    186         return 0; 
    187 
    188  
    189  
    190 void connection_map_clear_timeout_connections(connection_map *cm) { 
    191         size_t i; 
    192         int now_t = time(NULL); 
     180        time_t now_t = time(NULL); 
    193181 
    194182        for (i = 0; i < cm->used; i++) { 
     
    197185                if (cme->timeout != 0 && cme->timeout < now_t) { 
    198186                        /* found connection */ 
    199                         connection_map_remove_connection(cm,cme); 
    200                 } 
    201         } 
    202 
    203  
    204 buffer *get_tracking_id(plugin_data *p, connection *con) { 
     187                        connection_map_remove_connection(cm, cme); 
     188                } 
     189        } 
     190
     191 
     192/**  
     193 * extract the tracking-id from the parameters  
     194 * 
     195 * for POST requests it is part of the request headers 
     196 * for GET requests ... too 
     197 */ 
     198static buffer *get_tracking_id(plugin_data *p, connection *con) { 
    205199        data_string *ds; 
    206200        buffer *b = NULL; 
     
    210204        /* the request has to contain a 32byte ID */ 
    211205        if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("X-Progress-ID")))) { 
     206                char *amp = NULL; 
     207 
    212208                /* perhaps the POST request is using the querystring to pass the X-Progress-ID */ 
    213209                if (buffer_is_empty(con->uri.query)) { 
     
    218214                                /** extract query string from request.uri */ 
    219215                                buffer_copy_string(con->uri.query, qstr + 1); 
    220                                 b = con->uri.query; 
    221216                        } else { 
    222217                                return NULL; 
    223218                        } 
    224                 } else { 
    225                         b = con->uri.query; 
    226                 } 
     219                } 
     220 
     221                /** split the query-string and extract the X-Progress-ID */ 
     222                do { 
     223                        char *eq = NULL; 
     224                        char *start = amp ? amp + 1 : con->uri.query->ptr; 
     225 
     226                        amp = strchr(start, '&'); 
     227 
     228                        /* check the string between start and amp for = */ 
     229 
     230                        if (amp) { 
     231                                buffer_copy_string_len(p->tmp_buf, start, amp - start); 
     232                        } else { 
     233                                buffer_copy_string(p->tmp_buf, start); 
     234                        } 
     235 
     236                        eq = strchr(p->tmp_buf->ptr, '='); 
     237                         
     238                        if (eq) { 
     239                                *eq = '\0'; 
     240 
     241                                if (0 == strcmp(p->tmp_buf->ptr, "X-Progress-ID")) { 
     242                                        size_t key_len = sizeof("X-Progress-ID") - 1; 
     243                                        size_t var_len = p->tmp_buf->used - 1; 
     244                                        /* found */ 
     245 
     246                                        buffer_copy_string_len(p->tmp_buf, start + key_len + 1, var_len - key_len - 1); 
     247                 
     248                                        b = p->tmp_buf; 
     249 
     250                                        break; 
     251                                }  
     252                        } 
     253                } while (amp); 
     254 
     255                if (!b) return NULL; 
    227256        } else { 
     257                /* request header was found, use it */ 
    228258                b = ds->value; 
    229259        } 
     
    255285 
    256286        p->con_map = connection_map_init(); 
     287        p->tmp_buf = buffer_init(); 
    257288 
    258289        return p; 
     
    262293FREE_FUNC(mod_uploadprogress_free) { 
    263294        plugin_data *p = p_d; 
    264  
    265         UNUSED(srv); 
    266295 
    267296        if (!p) return HANDLER_GO_ON; 
     
    281310 
    282311        connection_map_free(p->con_map); 
     312        buffer_free(p->tmp_buf); 
    283313 
    284314        free(p); 
     
    308338 
    309339                s = calloc(1, sizeof(plugin_config)); 
    310                 s->progress_url    = buffer_init_string("/progress"); 
     340                s->progress_url    = buffer_init(); 
    311341                s->remove_timeout  = 60; 
    312342                s->debug  = 0; 
     
    384414        connection_map_entry *map_con_entry = NULL; 
    385415 
    386         UNUSED(srv); 
    387  
    388         if (con->uri.path->used == 0) return HANDLER_GO_ON; 
     416        if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; 
    389417 
    390418        mod_uploadprogress_patch_connection(srv, con, p); 
     419 
     420        /* no progress URL set, ignore request */        
     421        if (buffer_is_empty(p->conf.progress_url)) return HANDLER_GO_ON; 
    391422 
    392423        switch(con->request.http_method) { 
    393424        case HTTP_METHOD_POST: 
    394                 /* get the tracker id */ 
     425                /** 
     426                 * a POST request is the UPLOAD itself 
     427                 * 
     428                 * get the unique tracker id  
     429                 */ 
    395430                if (NULL == (tracking_id = get_tracking_id(p, con))) { 
    396431                        return HANDLER_GO_ON; 
    397432                } 
    398433 
    399                 if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map,tracking_id))) { 
    400                         connection_map_insert(p->con_map, con, tracking_id); 
     434                if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { 
     435                        connection_map_insert(p->con_map, tracking_id, con); 
     436                 
     437                        if (p->conf.debug) TRACE("POST: connection is new, registered: %s", BUF_STR(tracking_id)); 
    401438                } else { 
    402439                        map_con_entry->timeout = 0; 
    403440                        map_con_entry->status = 0; 
    404                         map_con_entry->con = con; 
    405                         buffer_copy_string_buffer(map_con_entry->con_id,tracking_id); 
     441                         
     442                        if (p->conf.debug) TRACE("POST: connection is known, id: %s", BUF_STR(tracking_id)); 
    406443                } 
    407444 
    408445                return HANDLER_GO_ON; 
    409446        case HTTP_METHOD_GET: 
     447                /** 
     448                 * the status request for the current connection 
     449                 */ 
     450                if (p->conf.debug) TRACE("(uploadprogress) urls %s == %s", BUF_STR(con->uri.path), BUF_STR(p->conf.progress_url)); 
     451 
    410452                if (!buffer_is_equal(con->uri.path, p->conf.progress_url)) { 
    411453                        return HANDLER_GO_ON; 
     
    437479                /* get the connection */ 
    438480                if (NULL == (post_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { 
     481                        /** 
     482                         * looks like we don't know the tracking id yet, GET and POST out of sync ? */ 
    439483                        BUFFER_APPEND_STRING_CONST(b, "new Object({ 'state' : 'starting' })\r\n"); 
    440484                         
     485                        if (p->conf.debug) TRACE("connection unknown: %s, sending: %s", BUF_STR(tracking_id), BUF_STR(b)); 
     486 
    441487                        return HANDLER_FINISHED; 
    442                 } else { 
    443                         if(p->conf.debug) TRACE("connection found: con=%p id=%s",post_con_entry->con,tracking_id->ptr); 
    444                 } 
    445  
    446                 /* prepare XML */ 
     488                } 
     489 
    447490                BUFFER_COPY_STRING_CONST(b, "new Object({ 'state' : "); 
    448                 
     491         
    449492                if (post_con_entry->status == 413) { 
     493                        /* the upload was too large */   
    450494                        BUFFER_APPEND_STRING_CONST(b, "'error', 'status' : 413"); 
    451                 } else if (post_con_entry->timeout > 0) { 
     495                } else if (post_con_entry->con == NULL) { 
     496                        /* the connection is already gone */ 
    452497                        buffer_append_string(b, "'done'"); 
    453498                } else { 
     499                        /* the upload is already done, but the connection might be still open */ 
    454500                        buffer_append_string(b, post_con_entry->con->recv->is_closed ? "'done'" : "'uploading'"); 
     501                        BUFFER_APPEND_STRING_CONST(b, ", 'received' : "); 
     502                        buffer_append_off_t(b, post_con_entry->con->recv->bytes_in); 
    455503                        BUFFER_APPEND_STRING_CONST(b, ", 'size' : "); 
    456504                        buffer_append_off_t(b, post_con_entry->con->request.content_length == -1 ? 0 : post_con_entry->con->request.content_length); 
    457                         BUFFER_APPEND_STRING_CONST(b, ", 'received' : "); 
    458                         buffer_append_off_t(b, post_con_entry->con->recv->bytes_in); 
    459505                } 
    460506                BUFFER_APPEND_STRING_CONST(b, "})\r\n"); 
     507 
     508                if (p->conf.debug) TRACE("connection is known: %s, sending: %s", BUF_STR(tracking_id), BUF_STR(b)); 
    461509 
    462510                return HANDLER_FINISHED; 
     
    468516} 
    469517 
     518/** 
     519 * check if request parser sent 413 for our POST request 
     520 */ 
     521URIHANDLER_FUNC(mod_uploadprogress_response_header) { 
     522        plugin_data *p = p_d; 
     523 
     524        buffer *tracking_id; 
     525        connection_map_entry *map_con_entry = NULL; 
     526 
     527        UNUSED(srv); 
     528 
     529        /* 
     530         * we only want to process an 413 (Bad length) error for the upload (POST request) 
     531         */ 
     532        if (con->request.http_method != HTTP_METHOD_POST || con->http_status != 413) { 
     533                return HANDLER_GO_ON; 
     534        } 
     535 
     536        if (p->conf.debug) { 
     537                TRACE("response_header: con=%p, http_method=%d, http_status=%d",con, 
     538                        con->request.http_method, con->http_status); 
     539        } 
     540 
     541        /* get the tracker id */ 
     542        if (NULL == (tracking_id = get_tracking_id(p, con))) { 
     543                return HANDLER_GO_ON; 
     544        } 
     545 
     546        if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { 
     547                /** 
     548                 * in case the request parser meant the request was too large the URI handler won't  
     549                 * get called. Insert the connection mapping here 
     550                 */ 
     551                if (NULL == (map_con_entry = connection_map_insert(p->con_map, tracking_id, con))) { 
     552                        return HANDLER_GO_ON; 
     553                } 
     554        } 
     555 
     556        /* ok, found our entries, setting 413 here for status */ 
     557        map_con_entry->status = 413; 
     558         
     559        return HANDLER_GO_ON; 
     560} 
     561 
     562/** 
     563 * remove the parent connection from the connection mapping 
     564 * when it got closed 
     565 * 
     566 * keep the mapping active for a while to send a valid final status 
     567 */ 
    470568REQUESTDONE_FUNC(mod_uploadprogress_request_done) { 
    471569        plugin_data *p = p_d; 
     570        buffer *tracking_id; 
     571        connection_map_entry *cm = NULL; 
    472572 
    473573        UNUSED(srv); 
    474574 
    475         if (con->uri.path->used == 0) return HANDLER_GO_ON; 
     575        if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; 
    476576         
    477577        /* 
     
    482582        } 
    483583 
    484         if(p->conf.debug) TRACE("request_done: con=%p, http_method=%d, http_status=%d",con, 
    485                         con->request.http_method, con->http_status); 
     584        if (NULL == (tracking_id = get_tracking_id(p, con))) { 
     585                return HANDLER_GO_ON; 
     586        } 
     587 
     588        if (p->conf.debug) { 
     589                TRACE("upload is done, moving tracking-id to backlog: tracking-id=%s, http_status=%d", 
     590                                BUF_STR(tracking_id), 
     591                                con->http_status); 
     592        } 
     593 
    486594        /* 
    487595         * set timeout on the upload's connection_map_entry. 
    488596         */ 
    489         if (!connection_map_set_timeout(p, con)) { 
    490                 if(p->conf.debug) TRACE("connection not found??? %p",con); 
    491         } 
     597        if (NULL == (cm = connection_map_get_connection_entry(p->con_map, tracking_id))) { 
     598                if (p->conf.debug) { 
     599                        TRACE("tracking ID %s not found, can't set timeout", BUF_STR(tracking_id)); 
     600                } 
     601                return HANDLER_GO_ON; 
     602        } 
     603 
     604        cm->timeout = time(NULL) + p->conf.remove_timeout; 
     605        cm->con     = NULL; /* con becomes invalid very soon */ 
    492606 
    493607        return HANDLER_GO_ON; 
    494608} 
    495609 
    496 URIHANDLER_FUNC(mod_uploadprogress_response_header) 
    497 
     610/** 
     611 * remove dead connections once in while  
     612 */ 
     613TRIGGER_FUNC(mod_uploadprogress_trigger) { 
    498614        plugin_data *p = p_d; 
    499615 
    500         buffer *tracking_id; 
    501         connection_map_entry *map_con_entry = NULL; 
    502  
    503         UNUSED(srv); 
    504  
    505         /* 
    506          * we only want to process an 413 (Bad length) error for the upload (POST request) 
    507          */ 
    508         if (con->request.http_method != HTTP_METHOD_POST || con->http_status != 413) { 
    509                 return HANDLER_GO_ON; 
    510         } 
    511  
    512         if(p->conf.debug) TRACE("response_header: con=%p, http_method=%d, http_status=%d",con, 
    513                         con->request.http_method, con->http_status); 
    514  
    515         /* get the tracker id */ 
    516         if (NULL == (tracking_id = get_tracking_id(p, con))) { 
    517                 return HANDLER_GO_ON; 
    518         } 
    519  
    520         if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map,tracking_id))) { 
    521                 /* add entry if it doesn't exists */ 
    522                 if (NULL == (map_con_entry = connection_map_insert(p->con_map, con, tracking_id))) { 
    523                         return HANDLER_GO_ON; 
    524                 } 
    525         } else { 
    526                 map_con_entry->con = con; 
    527                 buffer_copy_string_buffer(map_con_entry->con_id,tracking_id); 
    528         } 
    529  
    530         //ok, found our entries, setting 413 here for status 
    531         map_con_entry->timeout = time(NULL) + p->conf.remove_timeout; 
    532         map_con_entry->status = 413; 
    533          
     616        if ((srv->cur_ts % 10) != 0) return HANDLER_GO_ON; 
     617 
     618        connection_map_clear_timeout_connections(p->con_map); 
     619 
    534620        return HANDLER_GO_ON; 
    535621} 
    536622 
    537 TRIGGER_FUNC(mod_uploadprogress_trigger) 
    538 { 
    539         plugin_data *p = p_d; 
    540  
    541         UNUSED(srv); 
    542  
    543         if ((srv->cur_ts % 60) != 0) return HANDLER_GO_ON; 
    544  
    545         connection_map_clear_timeout_connections(p->con_map); 
    546  
    547         return HANDLER_GO_ON; 
    548 } 
    549623 
    550624/* this function is called at dlopen() time and inits the callbacks */