00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <ngx_config.h>
00023 #include <ngx_core.h>
00024 #include <ngx_http.h>
00025 #include <ngx_log.h>
00026
00027
00028 #include <libxml/parser.h>
00029 #include <libxml/tree.h>
00030
00031 #include "ngx_http_download_module.h"
00032
00033
00034 static void *
00035 ngx_http_download_create_conf(ngx_conf_t *cf);
00036 static char *
00037 ngx_http_download_merge_conf(ngx_conf_t *cf, void *parent, void *child);
00038
00040 static ngx_command_t ngx_http_download_commands[] = {
00041 {ngx_string( "download_server_timeout" ),
00042 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
00043 ngx_conf_set_msec_slot,
00044 NGX_HTTP_LOC_CONF_OFFSET,
00045 offsetof(ngx_http_download_conf_t, server_timeout),
00046 NULL},
00047
00048 ngx_null_command
00049 };
00050
00052 static ngx_http_module_t ngx_http_download_module_ctx = {
00053 NULL,NULL,NULL,NULL,NULL,NULL,
00054 ngx_http_download_create_conf,
00055 ngx_http_download_merge_conf
00056 };
00057
00059 ngx_module_t ngx_http_download_module = {
00060 NGX_MODULE_V1,
00061 &ngx_http_download_module_ctx,
00062 ngx_http_download_commands,
00063 NGX_HTTP_MODULE,
00064 NULL,
00065 NULL,
00066 NULL,
00067 NULL,
00068 NULL,
00069 NULL,
00070 NULL,
00071 NGX_MODULE_V1_PADDING
00072 };
00073
00074
00075
00076
00077 static ngx_int_t
00078 ngx_http_download_create_request(ngx_http_download_conn_t *conn);
00079 static ngx_int_t
00080 ngx_http_download_flush_buffer(ngx_http_download_conn_t *conn, ssize_t bytes);
00081 static void
00082 ngx_http_download_write_handler(ngx_event_t *wev);
00083 static void
00084 ngx_http_download_read_handler(ngx_event_t *rev);
00085 static void
00086 ngx_http_download_dummy_handler(ngx_event_t *ev);
00087 static void
00088 ngx_http_download_parse_headers(ngx_http_download_conn_t *conn);
00089 static void
00090 ngx_http_download_finalize_connection(ngx_http_download_conn_t *conn, ngx_event_t *e, ngx_int_t rc);
00091
00098 ngx_http_download_conn_t *
00099 ngx_http_download_create_new(ngx_http_request_t *r)
00100 {
00101 ngx_http_download_conn_t *conn;
00102 ngx_http_download_conf_t *conf;
00103 ngx_pool_t *pool;
00104 ngx_log_t *dl_log;
00105 ngx_log_t *log;
00106
00107 conf = ngx_http_get_module_loc_conf(r, ngx_http_download_module);
00108
00109 log = r->connection->log;
00110
00111 pool = ngx_create_pool(BUFFER_SIZE, log);
00112 if( pool == NULL ) {
00113 return NULL;
00114 }
00115
00116 dl_log = ngx_palloc(pool, sizeof(ngx_log_t));
00117 if(dl_log == NULL) {
00118 return NULL;
00119 }
00120 ngx_memcpy(dl_log, log, sizeof(ngx_log_t));
00121 pool->log = dl_log;
00122
00123 conn = ngx_pcalloc(pool, sizeof(ngx_http_download_conn_t));
00124 if( conn == NULL ) {
00125 return NULL;
00126 }
00127
00128
00129 conn->r = r;
00130 conn->pool = pool;
00131 conn->rpool = r->pool;
00132 conn->connect_timeout = conf->server_timeout;
00133 conn->read_timeout = conf->server_timeout;
00134 conn->write_timeout = conf->server_timeout;
00135
00136 conn->log = dl_log;
00137
00138 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00139 "[download_create_new] | def timeout: %d", conf->server_timeout);
00140
00141 return conn;
00142 }
00143
00152 ngx_int_t
00153 ngx_http_download_enqueue(ngx_http_download_conn_t *conn)
00154 {
00155 ngx_int_t rc;
00156 ngx_http_request_t *r;
00157 ngx_connection_t *c;
00158 ngx_peer_connection_t *peer;
00159 struct hostent *hp;
00160 struct sockaddr_in *sin;
00161
00162 r = conn->r;
00163 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00164 "[download_enqueue] host: %s, port: %d, timeout: %d", conn->host, conn->port, conn->connect_timeout);
00165
00166 conn->done = 0;
00167 conn->response_length = 0;
00168 conn->out_bufs = NULL;
00169 conn->out_bufs_tail = NULL;
00170
00171 peer = ngx_pcalloc(conn->pool, sizeof(ngx_peer_connection_t));
00172 if( peer == NULL ) {
00173 return NGX_ERROR;
00174 }
00175
00176 conn->buffer = ngx_create_temp_buf(conn->rpool, BUFFER_SIZE);
00177
00178 if( conn->buffer == NULL ) {
00179 return NGX_ERROR;
00180 }
00181
00182 peer->sockaddr = ngx_pcalloc(conn->pool, sizeof(struct sockaddr_in));
00183 if( peer->sockaddr == NULL ) {
00184 return NGX_ERROR;
00185 }
00186
00187 if( conn->request == NULL ) {
00188 if( ngx_http_download_create_request(conn) == NGX_ERROR ) {
00189 return NGX_ERROR;
00190 }
00191 }
00192
00193
00194
00195
00196
00197
00198
00199 sin = (struct sockaddr_in *)peer->sockaddr;
00200 hp = gethostbyname((const char*)conn->host);
00201 if( hp == NULL || hp->h_addr_list[0] == NULL ) {
00202 return NGX_ERROR;
00203 }
00204 bcopy(hp->h_addr, &(sin->sin_addr.s_addr), hp->h_length);
00205 sin->sin_family = AF_INET;
00206 sin->sin_port = htons(conn->port);
00207
00208 peer->socklen = sizeof(struct sockaddr_in);
00209 peer->name = ngx_pcalloc(conn->pool, sizeof(ngx_str_t));
00210 peer->name->data = (u_char*)"download_module";
00211 peer->name->len = sizeof(peer->name->data)-1;
00212 peer->get = ngx_event_get_peer;
00213 peer->log = conn->log;
00214 peer->log_error = NGX_ERROR_ERR;
00215
00216
00217 rc = ngx_event_connect_peer(peer);
00218
00219 if( rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED ) {
00220 ngx_http_download_finalize_connection(conn, NULL, rc);
00221 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00222 "[download_enqueue] can't connect: %d", rc);
00223 return rc;
00224 }
00225 else {
00226 conn->p = peer;
00227 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00228 "[download_enqueue] connect_peer rc: %d (OK)", rc);
00229 }
00230
00231 c = peer->connection;
00232 c->read->handler = ngx_http_download_read_handler;
00233 c->write->handler = ngx_http_download_write_handler;
00234 c->read->data = conn;
00235 c->write->data = conn;
00236 c->data = r;
00237
00238 ngx_add_timer(c->read, conn->read_timeout);
00239 ngx_add_timer(c->write, conn->write_timeout);
00240
00241 ngx_http_download_write_handler(c->write);
00242
00243 return rc;
00244 }
00245
00254 ngx_int_t
00255 ngx_http_download_parse_url(ngx_str_t *url, ngx_http_download_conn_t *conn)
00256 {
00257 u_char *host, *port, *last, *uri, *args;
00258 size_t len;
00259 ngx_int_t n;
00260
00261 if( url->len < 7 ) {
00262 conn->err = (u_char*)"bad url";
00263 return NGX_ERROR;
00264 }
00265
00266 host = url->data;
00267 last = host + url->len;
00268
00269 if( strncmp("http://", (char*)host, 7) == 0 ) {
00270 host += 7;
00271 }
00272 else {
00273 conn->err = (u_char*)"bad url";
00274 return NGX_ERROR;
00275 }
00276
00277 port = ngx_strlchr(host, last, ':');
00278 uri = ngx_strlchr(host, last, '/');
00279 args = ngx_strlchr(host, last, '?');
00280
00281 if (args) {
00282 if (uri == NULL) {
00283 uri = args;
00284 } else if (args < uri) {
00285 uri = args;
00286 }
00287 }
00288
00289 if (uri) {
00290 conn->uri = ngx_pcalloc(conn->pool, last-uri+1);
00291 if( conn->uri == NULL ) {
00292 conn->err = (u_char*)"ngx_calloc error";
00293 return NGX_ERROR;
00294 }
00295 ngx_cpystrn(conn->uri, uri, last-uri+1);
00296
00297 last = uri;
00298
00299 if (uri < port) {
00300 port = NULL;
00301 }
00302 }
00303
00304 if (port) {
00305 port++;
00306
00307 len = last - port;
00308
00309 if (len == 0) {
00310 conn->err = (u_char*)"invalid port";
00311 return NGX_ERROR;
00312 }
00313
00314 n = ngx_atoi(port, len);
00315
00316 if (n < 1 || n > 65536) {
00317 conn->err = (u_char*)"invalid port";
00318 return NGX_ERROR;
00319 }
00320
00321 conn->port = n;
00322
00323 last = port - 1;
00324 }
00325
00326 len = last - host;
00327
00328 if (len == 0) {
00329 conn->err = (u_char*)"no host";
00330 return NGX_ERROR;
00331 }
00332
00333 if (len == 1 && *host == '*') {
00334 len = 0;
00335 }
00336
00337 conn->host = ngx_pcalloc(conn->pool, len+1);
00338 if( conn->host == NULL ) {
00339 conn->err = (u_char*)"ngx_calloc error";
00340 return NGX_ERROR;
00341 }
00342 ngx_cpystrn(conn->host, host, len+1);
00343
00344 if (!port) {
00345 conn->port = 80;
00346 }
00347
00348 return NGX_OK;
00349 }
00350
00356 void
00357 ngx_http_download_cleanup(ngx_http_download_conn_t *conn)
00358 {
00359 if( conn->done || conn->p==NULL ) {
00360 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00361 "[download_cleanup] destroying pool");
00362
00363 if( conn->pool ) {
00364 ngx_destroy_pool(conn->pool);
00365 }
00366 }
00367 else {
00368 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00369 "[download_cleanup] terminating download");
00370 conn->terminated = 1;
00371 conn->done_handler = NULL;
00372 }
00373 }
00374
00381 static ngx_int_t
00382 ngx_http_download_create_request(ngx_http_download_conn_t *conn)
00383 {
00384 ngx_buf_t *req;
00385 size_t size;
00386
00387 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00388 "[download_create_request]");
00389
00390 size = sizeof("GET ")-1 + strlen((char*)conn->uri) + sizeof(" HTTP/1.1\r\n")-1 + \
00391 sizeof("Host: ")-1 + strlen((char*)conn->host) + sizeof("\r\n\r\n")-1;
00392
00393 req = conn->request = ngx_create_temp_buf(conn->pool, size + 1);
00394 if( req == NULL ) {
00395 return NGX_ERROR;
00396 }
00397
00398
00399 req->last = req->pos + size;
00400 ngx_sprintf((u_char*)req->pos, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", conn->uri, conn->host);
00401 req->pos[size] = '\0';
00402
00403
00404
00405
00406 return NGX_OK;
00407 }
00408
00416 static ngx_int_t
00417 ngx_http_download_flush_buffer(ngx_http_download_conn_t *conn, ssize_t bytes)
00418 {
00419
00420 ngx_chain_t *cl;
00421 ngx_http_request_t *r;
00422
00423
00424 cl = NULL;
00425 r = conn->r;
00426 if( conn->out_bufs == NULL ) {
00427 cl = conn->out_bufs = conn->out_bufs_tail = ngx_alloc_chain_link(conn->rpool);
00428 if( cl == NULL ) {
00429 return NGX_ERROR;
00430 }
00431 }
00432
00433 if (cl == NULL) {
00434 cl = conn->out_bufs_tail->next = ngx_alloc_chain_link(conn->rpool);
00435 if( cl == NULL ) {
00436 return NGX_ERROR;
00437 }
00438 conn->out_bufs_tail = cl;
00439 }
00440
00441 cl->next = NULL;
00442 cl->buf = conn->buffer;
00443 conn->buffer = NULL;
00444
00445 cl->buf->flush = 1;
00446 cl->buf->memory = 1;
00447
00448 return NGX_OK;
00449 }
00450
00456 static void
00457 ngx_http_download_write_handler(ngx_event_t *wev)
00458 {
00459 ngx_connection_t *c;
00460 ssize_t n, size;
00461 ngx_http_request_t *r;
00462 ngx_http_download_conn_t *conn;
00463 ngx_peer_connection_t *p;
00464
00465 conn = wev->data;
00466 p = conn->p;
00467 c = p->connection;
00468 r = conn->r;
00469
00470
00471
00472
00473 if( conn->terminated ) {
00474 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00475 "[download_write_handler] terminated");
00476
00477 ngx_http_download_finalize_connection(conn, wev, NGX_ERROR);
00478 return ;
00479 }
00480
00481 if( wev->timedout ) {
00482 ngx_log_error(NGX_LOG_ERR, conn->log, 0,
00483 "[download_write_handler] timeout :: %s", conn->host);
00484 ngx_http_download_finalize_connection(conn, wev, NGX_ETIMEDOUT);
00485 return ;
00486 }
00487
00488 size = conn->request->last - conn->request->pos;
00489
00490
00491
00492
00493 n = ngx_send(c, (u_char*)conn->request->pos, size );
00494
00495
00496
00497
00498 if (n == NGX_ERROR) {
00499 ngx_http_download_finalize_connection(conn, wev, NGX_ERROR);
00500 return;
00501 }
00502
00503 if (n > 0) {
00504 conn->request->pos += n;
00505 if( n == size ) {
00506 c->write->handler = ngx_http_download_dummy_handler;
00507
00508 if (wev->timer_set) {
00509 ngx_del_timer(wev);
00510 }
00511
00512 if( ngx_handle_write_event(wev, 0) == NGX_ERROR ) {
00513 ngx_http_download_finalize_connection(conn, wev, NGX_ERROR);
00514 }
00515
00516 return ;
00517 }
00518 }
00519
00520 if( !wev->timer_set ) {
00521 ngx_add_timer(wev, conn->write_timeout);
00522 }
00523 }
00524
00530 static void ngx_http_download_dummy_handler(ngx_event_t *ev)
00531 {
00532 }
00533
00539 static void
00540 ngx_http_download_read_handler(ngx_event_t *rev)
00541 {
00542 size_t size;
00543 ngx_int_t n;
00544 ngx_buf_t *b;
00545 ngx_connection_t *c;
00546 ngx_http_request_t *r;
00547 ngx_peer_connection_t *p;
00548 ngx_http_download_conn_t *conn;
00549
00550 conn = rev->data;
00551 p = conn->p;
00552 c = p->connection;
00553 r = conn->r;
00554
00555
00556 if( conn->terminated ) {
00557 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00558 "[download_read_handler] terminated");
00559
00560 ngx_http_download_finalize_connection(conn, rev, NGX_ERROR);
00561 return ;
00562 }
00563
00564 if (rev->timedout) {
00565 ngx_connection_error(c, NGX_ETIMEDOUT, "download timed out");
00566 ngx_log_error(NGX_LOG_ERR, conn->log, 0,
00567 "[download_read_handler] timeout :: %s", conn->host);
00568 ngx_http_download_finalize_connection(conn, rev, NGX_ETIMEDOUT);
00569 return;
00570 }
00571
00572
00573
00574 for ( ;; ) {
00575
00576 b = conn->buffer;
00577 size = b->end - b->last;
00578 if( size == 0 ) {
00579 if( ngx_http_download_flush_buffer(conn, b->end - b->start) == NGX_ERROR ) {
00580 ngx_http_download_finalize_connection(conn, rev, NGX_ERROR);
00581 return;
00582 }
00583
00584 conn->buffer = b = ngx_create_temp_buf(conn->rpool, BUFFER_SIZE);
00585
00586 if(b == NULL)
00587 {
00588 ngx_log_error(NGX_LOG_ERR, conn->log, 0,
00589 "[download_read_handler] create temp buf failed");
00590
00591 ngx_http_download_finalize_connection(conn, rev, NGX_ERROR);
00592 return;
00593 }
00594 size = b->end - b->last;
00595 }
00596
00597
00598 n = c->recv(c, b->last, size);
00599
00600
00601
00602
00603 if (n == NGX_AGAIN) {
00604 break;
00605 }
00606
00607 if (n == 0 || n == NGX_ERROR) {
00608 size = b->last - b->pos;
00609 if( ngx_http_download_flush_buffer(conn, size) == NGX_ERROR ) {
00610 ngx_http_download_finalize_connection(conn, rev, NGX_ERROR);
00611 return;
00612 }
00613 ngx_http_download_finalize_connection(conn, rev, n);
00614 return;
00615 }
00616
00617 if( n>0 ) {
00618 conn->response_length += n;
00619 b->last += n;
00620 }
00621
00622 if (!rev->ready) {
00623 break;
00624 }
00625 }
00626
00627 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
00628 ngx_http_download_finalize_connection(conn, rev, NGX_ERROR);
00629 return;
00630 }
00631
00632 if (rev->active) {
00633 ngx_add_timer(rev, conn->read_timeout);
00634 } else if (rev->timer_set) {
00635 ngx_del_timer(rev);
00636 }
00637 }
00638
00645 static void
00646 ngx_http_download_parse_headers(ngx_http_download_conn_t *conn)
00647 {
00648 u_char *s;
00649 u_char *end;
00650
00651 u_char http[9];
00652 ngx_int_t status;
00653
00654 s = conn->out_bufs->buf->pos;
00655 end = conn->body_start;
00656
00657 conn->status = -1;
00658
00659 if( end - s < 12 ) {
00660 return ;
00661 }
00662
00663 if( sscanf((char*)s, "%8s %d", http, (int *) &status) != 2 ) {
00664 return ;
00665 }
00666
00667 if( ngx_strlen(http) != 8 || (ngx_strncmp(http, "HTTP/1.1", 8)!=0 && ngx_strncmp(http, "HTTP/1.0", 8)!=0) ) {
00668 return ;
00669 }
00670
00671 conn->status = status;
00672 }
00673
00681 static void
00682 ngx_http_download_finalize_connection(ngx_http_download_conn_t *conn, ngx_event_t *e, ngx_int_t rc)
00683 {
00684 ngx_peer_connection_t *p;
00685 ngx_http_request_t *r;
00686
00687 p = conn->p;
00688 r = conn->r;
00689
00690 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00691 "[download_finalize_connection] rc: %d", rc);
00692
00693 if ( !conn->done ) {
00694
00695 if( p->connection ) {
00696 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, conn->log, 0,
00697 "[download_finalize_connection] close download connection: %d", p->connection->fd);
00698
00699 if (p->connection->read->timer_set) {
00700 ngx_del_timer(p->connection->read);
00701 }
00702 if (p->connection->write->timer_set) {
00703 ngx_del_timer(p->connection->write);
00704 }
00705
00706 ngx_close_connection(p->connection);
00707
00708 p->connection = NULL;
00709 }
00710
00711 conn->done = 1;
00712 conn->error = rc;
00713
00714 if( conn->out_bufs && conn->out_bufs->buf ) {
00715 conn->body_start = (u_char*)strstr((char*)conn->out_bufs->buf->pos, "\r\n\r\n");
00716 if( conn->body_start ) {
00717 conn->body_start += 4;
00718 }
00719
00720 if( conn->body_start ) {
00721 ngx_http_download_parse_headers(conn);
00722 }
00723
00724 if( conn->out_bufs_tail && conn->out_bufs_tail->buf ) {
00725 conn->out_bufs_tail->buf->last_buf = 1;
00726 conn->out_bufs_tail->buf->last_in_chain = 1;
00727 }
00728 }
00729
00730
00731 if( conn->done_handler != NULL ) {
00732 conn->done_handler(conn);
00733 }
00734 else {
00735 ngx_http_download_cleanup(conn);
00736 }
00737 }
00738 }
00739
00746 static void *
00747 ngx_http_download_create_conf(ngx_conf_t *cf)
00748 {
00749 ngx_http_download_conf_t *conf;
00750
00751 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
00752 "ngx_http_download_create_conf");
00753
00754 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_download_conf_t));
00755 if (conf == NULL) {
00756 return NGX_CONF_ERROR;
00757 }
00758
00759 conf->server_timeout = NGX_CONF_UNSET_MSEC;
00760
00761 conf->log = ngx_palloc(cf->pool, sizeof(ngx_log_t));
00762 ngx_memcpy(conf->log, cf->log, sizeof(ngx_log_t));
00763
00764 return conf;
00765 }
00766
00767
00776 static char *
00777 ngx_http_download_merge_conf(ngx_conf_t *cf, void *parent, void *child)
00778 {
00779 ngx_http_download_conf_t *prev = parent;
00780 ngx_http_download_conf_t *conf = child;
00781
00782 ngx_conf_merge_msec_value(conf->server_timeout, prev->server_timeout, 1000);
00783
00784 if (conf->server_timeout <= 0) {
00785 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
00786 "check_server_timeout must be greater then zero");
00787 return NGX_CONF_ERROR;
00788 }
00789
00790 return NGX_CONF_OK;
00791 }
00792