Changeset 1896
- Timestamp:
- 08/12/2007 10:02:07 PM (9 months ago)
- Files:
-
- trunk/NEWS (modified) (1 diff)
- trunk/src/mod_uploadprogress.c (modified) (23 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/NEWS
r1866 r1896 5 5 6 6 - 1.5.0 - ... 7 * -F option added for spaw m-fcgi7 * -F option added for spawn-fcgi 8 8 * replaced mod_fastcgi, mod_scgi, mod_proxy with mod_proxy_core + backends 9 * enabled mod_uploadprogress again9 * added query-string parsing for mod_uploadprogress 10 10 * added threaded stat() 11 11 * added threaded disk-read() support trunk/src/mod_uploadprogress.c
r1653 r1896 19 19 * uploadprogress for lighttpd 20 20 * 21 * no author and contact infos yet? Shamelessly adding...22 *23 21 * Initial: Jan Kneschke <jan@kneschke.de> 24 22 * Timeout+Status addon: Bjoern Kalkbrenner <terminar@cyberphoria.org> [20070112] 25 23 * 24 * the timeout is used to keep in the status information intact even if the parent 25 * connection is gone already 26 26 */ 27 27 28 28 typedef struct { 29 buffer * con_id;29 buffer *tracking_id; 30 30 connection *con; 31 int timeout; 31 32 time_t timeout; 32 33 int status; 33 34 } connection_map_entry; … … 53 54 connection_map *con_map; 54 55 56 buffer *tmp_buf; /** used as temporary buffer for extracting the tracking id */ 57 55 58 plugin_config **config_storage; 56 59 … … 65 68 66 69 /* init the plugin data */ 67 connection_map *connection_map_init() {70 static connection_map *connection_map_init() { 68 71 connection_map *cm; 69 72 … … 73 76 } 74 77 75 void connection_map_free(connection_map *cm) {78 static void connection_map_free(connection_map *cm) { 76 79 size_t i; 77 80 for (i = 0; i < cm->size; i++) { … … 80 83 if (!cme) break; 81 84 82 if (cme-> con_id) {83 buffer_free(cme-> con_id);85 if (cme->tracking_id) { 86 buffer_free(cme->tracking_id); 84 87 } 85 88 free(cme); … … 89 92 } 90 93 91 connection_map_entry *connection_map_insert(connection_map *cm, connection *con, buffer *con_id) {94 static connection_map_entry *connection_map_insert(connection_map *cm, buffer *tracking_id, connection *con) { 92 95 connection_map_entry *cme; 93 96 size_t i; … … 112 115 } else { 113 116 cme = malloc(sizeof(*cme)); 117 cme->tracking_id = buffer_init(); 114 118 } 115 119 cme->timeout = 0; 116 120 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); 119 122 cme->con = con; 120 123 … … 124 127 } 125 128 126 connection_map_entry *connection_map_get_connection_entry(connection_map *cm, buffer *con_id) {129 static connection_map_entry *connection_map_get_connection_entry(connection_map *cm, buffer *tracking_id) { 127 130 size_t i; 128 131 … … 130 133 connection_map_entry *cme = cm->ptr[i]; 131 134 132 if (buffer_is_equal(cme-> con_id, con_id)) {135 if (buffer_is_equal(cme->tracking_id, tracking_id)) { 133 136 /* found connection */ 134 137 return cme; … … 138 141 } 139 142 140 int connection_map_remove_connection(connection_map *cm, connection_map_entry *entry) {143 static int connection_map_remove_connection(connection_map *cm, connection_map_entry *entry) { 141 144 size_t i; 142 145 … … 146 149 if (cme == entry) { 147 150 /* found connection */ 148 buffer_reset(cme-> con_id);151 buffer_reset(cme->tracking_id); 149 152 cme->timeout=0; 150 153 cme->status=0; 151 cme->con = NULL;152 154 153 155 cm->used--; … … 166 168 } 167 169 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 */ 178 static void connection_map_clear_timeout_connections(connection_map *cm) { 169 179 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); 193 181 194 182 for (i = 0; i < cm->used; i++) { … … 197 185 if (cme->timeout != 0 && cme->timeout < now_t) { 198 186 /* 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 */ 198 static buffer *get_tracking_id(plugin_data *p, connection *con) { 205 199 data_string *ds; 206 200 buffer *b = NULL; … … 210 204 /* the request has to contain a 32byte ID */ 211 205 if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("X-Progress-ID")))) { 206 char *amp = NULL; 207 212 208 /* perhaps the POST request is using the querystring to pass the X-Progress-ID */ 213 209 if (buffer_is_empty(con->uri.query)) { … … 218 214 /** extract query string from request.uri */ 219 215 buffer_copy_string(con->uri.query, qstr + 1); 220 b = con->uri.query;221 216 } else { 222 217 return NULL; 223 218 } 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; 227 256 } else { 257 /* request header was found, use it */ 228 258 b = ds->value; 229 259 } … … 255 285 256 286 p->con_map = connection_map_init(); 287 p->tmp_buf = buffer_init(); 257 288 258 289 return p; … … 262 293 FREE_FUNC(mod_uploadprogress_free) { 263 294 plugin_data *p = p_d; 264 265 UNUSED(srv);266 295 267 296 if (!p) return HANDLER_GO_ON; … … 281 310 282 311 connection_map_free(p->con_map); 312 buffer_free(p->tmp_buf); 283 313 284 314 free(p); … … 308 338 309 339 s = calloc(1, sizeof(plugin_config)); 310 s->progress_url = buffer_init _string("/progress");340 s->progress_url = buffer_init(); 311 341 s->remove_timeout = 60; 312 342 s->debug = 0; … … 384 414 connection_map_entry *map_con_entry = NULL; 385 415 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; 389 417 390 418 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; 391 422 392 423 switch(con->request.http_method) { 393 424 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 */ 395 430 if (NULL == (tracking_id = get_tracking_id(p, con))) { 396 431 return HANDLER_GO_ON; 397 432 } 398 433 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)); 401 438 } else { 402 439 map_con_entry->timeout = 0; 403 440 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)); 406 443 } 407 444 408 445 return HANDLER_GO_ON; 409 446 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 410 452 if (!buffer_is_equal(con->uri.path, p->conf.progress_url)) { 411 453 return HANDLER_GO_ON; … … 437 479 /* get the connection */ 438 480 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 ? */ 439 483 BUFFER_APPEND_STRING_CONST(b, "new Object({ 'state' : 'starting' })\r\n"); 440 484 485 if (p->conf.debug) TRACE("connection unknown: %s, sending: %s", BUF_STR(tracking_id), BUF_STR(b)); 486 441 487 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 447 490 BUFFER_COPY_STRING_CONST(b, "new Object({ 'state' : "); 448 491 449 492 if (post_con_entry->status == 413) { 493 /* the upload was too large */ 450 494 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 */ 452 497 buffer_append_string(b, "'done'"); 453 498 } else { 499 /* the upload is already done, but the connection might be still open */ 454 500 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); 455 503 BUFFER_APPEND_STRING_CONST(b, ", 'size' : "); 456 504 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);459 505 } 460 506 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)); 461 509 462 510 return HANDLER_FINISHED; … … 468 516 } 469 517 518 /** 519 * check if request parser sent 413 for our POST request 520 */ 521 URIHANDLER_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 */ 470 568 REQUESTDONE_FUNC(mod_uploadprogress_request_done) { 471 569 plugin_data *p = p_d; 570 buffer *tracking_id; 571 connection_map_entry *cm = NULL; 472 572 473 573 UNUSED(srv); 474 574 475 if ( con->uri.path->used == 0) return HANDLER_GO_ON;575 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; 476 576 477 577 /* … … 482 582 } 483 583 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 486 594 /* 487 595 * set timeout on the upload's connection_map_entry. 488 596 */ 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 */ 492 606 493 607 return HANDLER_GO_ON; 494 608 } 495 609 496 URIHANDLER_FUNC(mod_uploadprogress_response_header) 497 { 610 /** 611 * remove dead connections once in while 612 */ 613 TRIGGER_FUNC(mod_uploadprogress_trigger) { 498 614 plugin_data *p = p_d; 499 615 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 534 620 return HANDLER_GO_ON; 535 621 } 536 622 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 }549 623 550 624 /* this function is called at dlopen() time and inits the callbacks */

