/* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_sleep.h" #include "ngx_http_echo_handler.h" #include #include /* event handler for echo_sleep */ static void ngx_http_echo_post_sleep(ngx_http_request_t *r); static void ngx_http_echo_sleep_cleanup(void *data); ngx_int_t ngx_http_echo_exec_echo_sleep(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ngx_int_t delay; /* in msec */ ngx_http_cleanup_t *cln; computed_arg_elts = computed_args->elts; computed_arg = &computed_arg_elts[0]; delay = ngx_atofp(computed_arg->data, computed_arg->len, 3); if (delay == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid sleep duration \"%V\"", &computed_arg_elts[0]); return NGX_HTTP_BAD_REQUEST; } dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); ngx_add_timer(&ctx->sleep, (ngx_msec_t) delay); /* we don't check broken downstream connections * ourselves so even if the client shuts down * the connection prematurely, nginx will still * go on waiting for our timers to get properly * expired. However, we'd still register a * cleanup handler for completeness. */ cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_echo_sleep_cleanup; cln->data = r; return NGX_AGAIN; } static void ngx_http_echo_post_sleep(ngx_http_request_t *r) { ngx_http_echo_ctx_t *ctx; /* ngx_int_t rc; */ dd("post sleep, r:%.*s", (int) r->uri.len, r->uri.data); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { return; } ctx->waiting = 0; ctx->done = 1; dd("sleep: after get module ctx"); dd("timed out? %d", ctx->sleep.timedout); dd("timer set? %d", ctx->sleep.timer_set); if (!ctx->sleep.timedout) { dd("HERE reached!"); return; } ctx->sleep.timedout = 0; if (ctx->sleep.timer_set) { dd("deleting timer for echo_sleep"); ngx_del_timer(&ctx->sleep); } /* r->write_event_handler = ngx_http_request_empty_handler; */ ngx_http_echo_wev_handler(r); } void ngx_http_echo_sleep_event_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; r = ev->data; c = r->connection; if (c->destroyed) { return; } if (c->error) { ngx_http_finalize_request(r, NGX_ERROR); return; } ctx = c->log->data; ctx->current_request = r; /* XXX when r->done == 1 we should do cleaning immediately * and delete our timer and then quit. */ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "echo sleep event handler: \"%V?%V\"", &r->uri, &r->args); /* if (r->done) { return; } */ ngx_http_echo_post_sleep(r); #if defined(nginx_version) dd("before run posted requests"); ngx_http_run_posted_requests(c); dd("after run posted requests"); #endif } ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) { ngx_str_t *computed_arg; ngx_str_t *computed_arg_elts; ngx_int_t delay; /* in msec */ computed_arg_elts = computed_args->elts; computed_arg = &computed_arg_elts[0]; delay = ngx_atofp(computed_arg->data, computed_arg->len, 3); if (delay == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid sleep duration \"%V\"", &computed_arg_elts[0]); return NGX_HTTP_BAD_REQUEST; } dd("blocking delay: %lu ms", (unsigned long) delay); ngx_msleep((ngx_msec_t) delay); return NGX_OK; } static void ngx_http_echo_sleep_cleanup(void *data) { ngx_http_request_t *r = data; ngx_http_echo_ctx_t *ctx; dd("echo sleep cleanup"); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { return; } if (ctx->sleep.timer_set) { dd("cleanup: deleting timer for echo_sleep"); ngx_del_timer(&ctx->sleep); return; } dd("cleanup: timer not set"); }