/* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_echo_filter.h" #include "ngx_http_echo_handler.h" #include "ngx_http_echo_echo.h" #include "ngx_http_echo_util.h" #include "ngx_http_echo_sleep.h" #include "ngx_http_echo_var.h" #include "ngx_http_echo_timer.h" #include "ngx_http_echo_location.h" #include "ngx_http_echo_subrequest.h" #include "ngx_http_echo_request_info.h" #include "ngx_http_echo_foreach.h" #include #include void ngx_http_echo_wev_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_echo_ctx_t *ctx; dd("wev handler"); ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { ngx_http_finalize_request(r, NGX_ERROR); return; } dd("waiting: %d, done: %d", (int) ctx->waiting, (int) ctx->done); if (ctx->waiting && ! ctx->done) { if (r == r->connection->data && r->postponed) { if (r->postponed->request) { r->connection->data = r->postponed->request; #if defined(nginx_version) && nginx_version >= 8012 ngx_http_post_request(r->postponed->request, NULL); #else ngx_http_post_request(r->postponed->request); #endif } else { ngx_http_echo_flush_postponed_outputs(r); } } return; } ctx->done = 0; ctx->next_handler_cmd++; rc = ngx_http_echo_run_cmds(r); dd("rc: %d", (int) rc); if (rc == NGX_ERROR || rc == NGX_DONE) { ngx_http_finalize_request(r, rc); return; } if (rc == NGX_AGAIN) { dd("mark busy %d for %.*s", (int) ctx->next_handler_cmd, (int) r->uri.len, r->uri.data); ctx->waiting = 1; ctx->done = 0; } else { dd("mark ready %d", (int) ctx->next_handler_cmd); ctx->waiting = 0; ctx->done = 1; dd("finalizing with rc %d", (int) rc); dd("finalize request %.*s with %d", (int) r->uri.len, r->uri.data, (int) rc); ngx_http_finalize_request(r, rc); } } ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_echo_ctx_t *ctx; dd("subrequest in memory: %d", (int) r->subrequest_in_memory); rc = ngx_http_echo_run_cmds(r); dd("run cmds returned %d", (int) rc); if (rc == NGX_ERROR || rc == NGX_OK || rc == NGX_DONE || rc == NGX_DECLINED) { return rc; } ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { if (ctx && r->header_sent) { return NGX_ERROR; } return rc; } /* rc == NGX_AGAIN */ #if defined(nginx_version) && nginx_version >= 8011 r->main->count++; #endif dd("%d", r->connection->destroyed); dd("%d", r->done); if (ctx) { dd("mark busy %d for %.*s", (int) ctx->next_handler_cmd, (int) r->uri.len, r->uri.data); ctx->waiting = 1; ctx->done = 0; } return NGX_DONE; } ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r) { ngx_http_echo_loc_conf_t *elcf; ngx_http_echo_ctx_t *ctx; ngx_int_t rc; ngx_array_t *cmds; ngx_array_t *computed_args = NULL; ngx_http_echo_cmd_t *cmd; ngx_http_echo_cmd_t *cmd_elts; ngx_array_t *opts = NULL; elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); cmds = elcf->handler_cmds; if (cmds == NULL) { return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); if (ctx == NULL) { ctx = ngx_http_echo_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_echo_module); } dd("exec handler: %.*s: %i", (int) r->uri.len, r->uri.data, (int) ctx->next_handler_cmd); cmd_elts = cmds->elts; for (; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) { cmd = &cmd_elts[ctx->next_handler_cmd]; /* evaluate arguments for the current cmd (if any) */ if (cmd->args) { computed_args = ngx_array_create(r->pool, cmd->args->nelts, sizeof(ngx_str_t)); if (computed_args == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); if (opts == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to evaluate arguments for " "the directive."); return rc; } } /* do command dispatch based on the opcode */ switch (cmd->opcode) { case echo_opcode_echo_sync: rc = ngx_http_echo_exec_echo_sync(r, ctx); break; case echo_opcode_echo: /* XXX moved the following code to a separate * function */ dd("found echo opcode"); rc = ngx_http_echo_exec_echo(r, ctx, computed_args, 0 /* in filter */, opts); break; case echo_opcode_echo_request_body: rc = ngx_http_echo_exec_echo_request_body(r, ctx); break; case echo_opcode_echo_location_async: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } dd("found opcode echo location async..."); rc = ngx_http_echo_exec_echo_location_async(r, ctx, computed_args); break; case echo_opcode_echo_location: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } return ngx_http_echo_exec_echo_location(r, ctx, computed_args); case echo_opcode_echo_subrequest_async: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } dd("found opcode echo subrequest async..."); rc = ngx_http_echo_exec_echo_subrequest_async(r, ctx, computed_args); break; case echo_opcode_echo_subrequest: if (!r->request_body) { /* we require reading the request body before doing * subrequests */ ctx->next_handler_cmd--; /* re-run the current cmd */ goto read_request_body; } return ngx_http_echo_exec_echo_subrequest(r, ctx, computed_args); case echo_opcode_echo_sleep: return ngx_http_echo_exec_echo_sleep(r, ctx, computed_args); case echo_opcode_echo_flush: rc = ngx_http_echo_exec_echo_flush(r, ctx); break; case echo_opcode_echo_blocking_sleep: rc = ngx_http_echo_exec_echo_blocking_sleep(r, ctx, computed_args); break; case echo_opcode_echo_reset_timer: rc = ngx_http_echo_exec_echo_reset_timer(r, ctx); break; case echo_opcode_echo_duplicate: rc = ngx_http_echo_exec_echo_duplicate(r, ctx, computed_args); break; case echo_opcode_echo_read_request_body: read_request_body: ctx->wait_read_request_body = 0; rc = ngx_http_echo_exec_echo_read_request_body(r, ctx); if (rc == NGX_ERROR) { return NGX_ERROR; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version >= 8011 && nginx_version < 1002006) \ || (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } #if nginx_version >= 8011 r->main->count--; #endif dd("read request body: %d", (int) rc); if (rc == NGX_OK) { continue; } /* rc == NGX_AGAIN */ ctx->wait_read_request_body = 1; return NGX_AGAIN; case echo_opcode_echo_foreach_split: rc = ngx_http_echo_exec_echo_foreach_split(r, ctx, computed_args); break; case echo_opcode_echo_end: rc = ngx_http_echo_exec_echo_end(r, ctx); break; case echo_opcode_echo_exec: dd("echo_exec"); return ngx_http_echo_exec_exec(r, ctx, computed_args); default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unknown opcode: %d", cmd->opcode); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } rc = ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (!r->request_body) { if (ngx_http_discard_request_body(r) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_http_echo_ctx_t *ctx = data; ngx_http_request_t *pr; ngx_http_echo_ctx_t *pr_ctx; dd("echo post_subrequest: %.*s", (int) r->uri.len, r->uri.data); if (ctx->run_post_subrequest) { dd("already run post_subrequest: %p: %.*s", ctx, (int) r->uri.len, r->uri.data); return rc; } dd("setting run_post_subrequest to 1 for %p for %.*s", ctx, (int) r->uri.len, r->uri.data); ctx->run_post_subrequest = 1; pr = r->parent; pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_echo_module); if (pr_ctx == NULL) { return NGX_ERROR; } dd("mark ready %d", (int) pr_ctx->next_handler_cmd); pr_ctx->waiting = 0; pr_ctx->done = 1; pr->write_event_handler = ngx_http_echo_wev_handler; /* work-around issues in nginx's event module */ if (r != r->connection->data && r->postponed && (r->main->posted_requests == NULL || r->main->posted_requests->request != pr)) { #if defined(nginx_version) && nginx_version >= 8012 ngx_http_post_request(pr, NULL); #else ngx_http_post_request(pr); #endif } return rc; }