| |
| /* |
| * Copyright (C) Xiaozhe Wang (chaoslawful) |
| * Copyright (C) Yichun Zhang (agentzh) |
| */ |
| |
| |
| #ifndef DDEBUG |
| #define DDEBUG 0 |
| #endif |
| #include "ddebug.h" |
| |
| |
| #include "ngx_http_lua_subrequest.h" |
| #include "ngx_http_lua_util.h" |
| #include "ngx_http_lua_ctx.h" |
| #include "ngx_http_lua_contentby.h" |
| #include "ngx_http_lua_headers_in.h" |
| #if defined(NGX_DTRACE) && NGX_DTRACE |
| #include "ngx_http_probe.h" |
| #endif |
| |
| |
| #define NGX_HTTP_LUA_SHARE_ALL_VARS 0x01 |
| #define NGX_HTTP_LUA_COPY_ALL_VARS 0x02 |
| |
| |
| #define ngx_http_lua_method_name(m) { sizeof(m) - 1, (u_char *) m " " } |
| |
| ngx_str_t ngx_http_lua_get_method = ngx_http_lua_method_name("GET"); |
| ngx_str_t ngx_http_lua_put_method = ngx_http_lua_method_name("PUT"); |
| ngx_str_t ngx_http_lua_post_method = ngx_http_lua_method_name("POST"); |
| ngx_str_t ngx_http_lua_head_method = ngx_http_lua_method_name("HEAD"); |
| ngx_str_t ngx_http_lua_delete_method = |
| ngx_http_lua_method_name("DELETE"); |
| ngx_str_t ngx_http_lua_options_method = |
| ngx_http_lua_method_name("OPTIONS"); |
| ngx_str_t ngx_http_lua_copy_method = ngx_http_lua_method_name("COPY"); |
| ngx_str_t ngx_http_lua_move_method = ngx_http_lua_method_name("MOVE"); |
| ngx_str_t ngx_http_lua_lock_method = ngx_http_lua_method_name("LOCK"); |
| ngx_str_t ngx_http_lua_mkcol_method = |
| ngx_http_lua_method_name("MKCOL"); |
| ngx_str_t ngx_http_lua_propfind_method = |
| ngx_http_lua_method_name("PROPFIND"); |
| ngx_str_t ngx_http_lua_proppatch_method = |
| ngx_http_lua_method_name("PROPPATCH"); |
| ngx_str_t ngx_http_lua_unlock_method = |
| ngx_http_lua_method_name("UNLOCK"); |
| ngx_str_t ngx_http_lua_patch_method = |
| ngx_http_lua_method_name("PATCH"); |
| ngx_str_t ngx_http_lua_trace_method = |
| ngx_http_lua_method_name("TRACE"); |
| |
| |
| static ngx_str_t ngx_http_lua_content_length_header_key = |
| ngx_string("Content-Length"); |
| |
| |
| static ngx_int_t ngx_http_lua_set_content_length_header(ngx_http_request_t *r, |
| off_t len); |
| static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, |
| ngx_uint_t method, int forward_body, |
| ngx_http_request_body_t *body, unsigned vars_action, |
| ngx_array_t *extra_vars); |
| static int ngx_http_lua_ngx_location_capture(lua_State *L); |
| static int ngx_http_lua_ngx_location_capture_multi(lua_State *L); |
| static void ngx_http_lua_process_vars_option(ngx_http_request_t *r, |
| lua_State *L, int table, ngx_array_t **varsp); |
| static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r, |
| ngx_array_t *extra_vars); |
| static ngx_int_t ngx_http_lua_subrequest(ngx_http_request_t *r, |
| ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, |
| ngx_http_post_subrequest_t *ps, ngx_uint_t flags); |
| static ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r); |
| static void ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, |
| ngx_http_lua_ctx_t *ctx); |
| static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r); |
| static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r); |
| static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r); |
| static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, |
| ngx_http_request_t *r); |
| |
| |
| /* ngx.location.capture is just a thin wrapper around |
| * ngx.location.capture_multi */ |
| static int |
| ngx_http_lua_ngx_location_capture(lua_State *L) |
| { |
| int n; |
| |
| n = lua_gettop(L); |
| |
| if (n != 1 && n != 2) { |
| return luaL_error(L, "expecting one or two arguments"); |
| } |
| |
| lua_createtable(L, n, 0); /* uri opts? table */ |
| lua_insert(L, 1); /* table uri opts? */ |
| if (n == 1) { /* table uri */ |
| lua_rawseti(L, 1, 1); /* table */ |
| |
| } else { /* table uri opts */ |
| lua_rawseti(L, 1, 2); /* table uri */ |
| lua_rawseti(L, 1, 1); /* table */ |
| } |
| |
| lua_createtable(L, 1, 0); /* table table' */ |
| lua_insert(L, 1); /* table' table */ |
| lua_rawseti(L, 1, 1); /* table' */ |
| |
| return ngx_http_lua_ngx_location_capture_multi(L); |
| } |
| |
| |
| static int |
| ngx_http_lua_ngx_location_capture_multi(lua_State *L) |
| { |
| ngx_http_request_t *r; |
| ngx_http_request_t *sr; /* subrequest object */ |
| ngx_http_post_subrequest_t *psr; |
| ngx_http_lua_ctx_t *sr_ctx; |
| ngx_http_lua_ctx_t *ctx; |
| ngx_array_t *extra_vars; |
| ngx_str_t uri; |
| ngx_str_t args; |
| ngx_str_t extra_args; |
| ngx_uint_t flags; |
| u_char *p; |
| u_char *q; |
| size_t len; |
| size_t nargs; |
| int rc; |
| int n; |
| int always_forward_body = 0; |
| ngx_uint_t method; |
| ngx_http_request_body_t *body; |
| int type; |
| ngx_buf_t *b; |
| unsigned vars_action; |
| ngx_uint_t nsubreqs; |
| ngx_uint_t index; |
| size_t sr_statuses_len; |
| size_t sr_headers_len; |
| size_t sr_bodies_len; |
| size_t sr_flags_len; |
| unsigned custom_ctx; |
| ngx_http_lua_co_ctx_t *coctx; |
| |
| ngx_http_lua_post_subrequest_data_t *psr_data; |
| |
| n = lua_gettop(L); |
| if (n != 1) { |
| return luaL_error(L, "only one argument is expected, but got %d", n); |
| } |
| |
| luaL_checktype(L, 1, LUA_TTABLE); |
| |
| nsubreqs = lua_objlen(L, 1); |
| if (nsubreqs == 0) { |
| return luaL_error(L, "at least one subrequest should be specified"); |
| } |
| |
| r = ngx_http_lua_get_req(L); |
| if (r == NULL) { |
| return luaL_error(L, "no request object found"); |
| } |
| |
| #if (NGX_HTTP_SPDY) |
| if (r->spdy_stream) { |
| return luaL_error(L, "spdy not supported yet"); |
| } |
| #endif |
| |
| ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); |
| if (ctx == NULL) { |
| return luaL_error(L, "no ctx found"); |
| } |
| |
| ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE |
| | NGX_HTTP_LUA_CONTEXT_ACCESS |
| | NGX_HTTP_LUA_CONTEXT_CONTENT); |
| |
| coctx = ctx->cur_co_ctx; |
| if (coctx == NULL) { |
| return luaL_error(L, "no co ctx found"); |
| } |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua location capture, uri:\"%V\" c:%ud", &r->uri, |
| r->main->count); |
| |
| sr_statuses_len = nsubreqs * sizeof(ngx_int_t); |
| sr_headers_len = nsubreqs * sizeof(ngx_http_headers_out_t *); |
| sr_bodies_len = nsubreqs * sizeof(ngx_str_t); |
| sr_flags_len = nsubreqs * sizeof(uint8_t); |
| |
| p = ngx_pcalloc(r->pool, sr_statuses_len + sr_headers_len + |
| sr_bodies_len + sr_flags_len); |
| |
| if (p == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| coctx->sr_statuses = (void *) p; |
| p += sr_statuses_len; |
| |
| coctx->sr_headers = (void *) p; |
| p += sr_headers_len; |
| |
| coctx->sr_bodies = (void *) p; |
| p += sr_bodies_len; |
| |
| coctx->sr_flags = (void *) p; |
| |
| coctx->nsubreqs = nsubreqs; |
| |
| coctx->pending_subreqs = 0; |
| |
| extra_vars = NULL; |
| |
| for (index = 0; index < nsubreqs; index++) { |
| coctx->pending_subreqs++; |
| |
| lua_rawgeti(L, 1, index + 1); |
| if (lua_isnil(L, -1)) { |
| return luaL_error(L, "only array-like tables are allowed"); |
| } |
| |
| dd("queries query: top %d", lua_gettop(L)); |
| |
| if (lua_type(L, -1) != LUA_TTABLE) { |
| return luaL_error(L, "the query argument %d is not a table, " |
| "but a %s", |
| index, lua_typename(L, lua_type(L, -1))); |
| } |
| |
| nargs = lua_objlen(L, -1); |
| |
| if (nargs != 1 && nargs != 2) { |
| return luaL_error(L, "query argument %d expecting one or " |
| "two arguments", index); |
| } |
| |
| lua_rawgeti(L, 2, 1); /* queries query uri */ |
| |
| dd("queries query uri: %d", lua_gettop(L)); |
| |
| dd("first arg in first query: %s", lua_typename(L, lua_type(L, -1))); |
| |
| body = NULL; |
| |
| extra_args.data = NULL; |
| extra_args.len = 0; |
| |
| if (extra_vars != NULL) { |
| /* flush out existing elements in the array */ |
| extra_vars->nelts = 0; |
| } |
| |
| vars_action = 0; |
| |
| custom_ctx = 0; |
| |
| if (nargs == 2) { |
| /* check out the options table */ |
| |
| lua_rawgeti(L, 2, 2); /* queries query uri opts */ |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| if (lua_type(L, 4) != LUA_TTABLE) { |
| return luaL_error(L, "expecting table as the 2nd argument for " |
| "subrequest %d, but got %s", index, |
| luaL_typename(L, 4)); |
| } |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the args option */ |
| |
| lua_getfield(L, 4, "args"); |
| |
| type = lua_type(L, -1); |
| |
| switch (type) { |
| case LUA_TTABLE: |
| ngx_http_lua_process_args_option(r, L, -1, &extra_args); |
| break; |
| |
| case LUA_TNIL: |
| /* do nothing */ |
| break; |
| |
| case LUA_TNUMBER: |
| case LUA_TSTRING: |
| extra_args.data = (u_char *) lua_tolstring(L, -1, &len); |
| extra_args.len = len; |
| |
| break; |
| |
| default: |
| return luaL_error(L, "Bad args option value"); |
| } |
| |
| lua_pop(L, 1); |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the vars option */ |
| |
| lua_getfield(L, 4, "vars"); |
| |
| switch (lua_type(L, -1)) { |
| case LUA_TTABLE: |
| ngx_http_lua_process_vars_option(r, L, -1, &extra_vars); |
| |
| dd("post process vars top: %d", lua_gettop(L)); |
| break; |
| |
| case LUA_TNIL: |
| /* do nothing */ |
| break; |
| |
| default: |
| return luaL_error(L, "Bad vars option value"); |
| } |
| |
| lua_pop(L, 1); |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the share_all_vars option */ |
| |
| lua_getfield(L, 4, "share_all_vars"); |
| |
| switch (lua_type(L, -1)) { |
| case LUA_TNIL: |
| /* do nothing */ |
| break; |
| |
| case LUA_TBOOLEAN: |
| if (lua_toboolean(L, -1)) { |
| vars_action |= NGX_HTTP_LUA_SHARE_ALL_VARS; |
| } |
| break; |
| |
| default: |
| return luaL_error(L, "Bad share_all_vars option value"); |
| } |
| |
| lua_pop(L, 1); |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the copy_all_vars option */ |
| |
| lua_getfield(L, 4, "copy_all_vars"); |
| |
| switch (lua_type(L, -1)) { |
| case LUA_TNIL: |
| /* do nothing */ |
| break; |
| |
| case LUA_TBOOLEAN: |
| if (lua_toboolean(L, -1)) { |
| vars_action |= NGX_HTTP_LUA_COPY_ALL_VARS; |
| } |
| break; |
| |
| default: |
| return luaL_error(L, "Bad copy_all_vars option value"); |
| } |
| |
| lua_pop(L, 1); |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the "forward_body" option */ |
| |
| lua_getfield(L, 4, "always_forward_body"); |
| always_forward_body = lua_toboolean(L, -1); |
| lua_pop(L, 1); |
| |
| dd("always foward body: %d", always_forward_body); |
| |
| /* check the "method" option */ |
| |
| lua_getfield(L, 4, "method"); |
| |
| type = lua_type(L, -1); |
| |
| if (type == LUA_TNIL) { |
| method = NGX_HTTP_GET; |
| |
| } else { |
| if (type != LUA_TNUMBER) { |
| return luaL_error(L, "Bad http request method"); |
| } |
| |
| method = (ngx_uint_t) lua_tonumber(L, -1); |
| } |
| |
| lua_pop(L, 1); |
| |
| dd("queries query uri opts: %d", lua_gettop(L)); |
| |
| /* check the "ctx" option */ |
| |
| lua_getfield(L, 4, "ctx"); |
| |
| type = lua_type(L, -1); |
| |
| if (type != LUA_TNIL) { |
| if (type != LUA_TTABLE) { |
| return luaL_error(L, "Bad ctx option value type %s, " |
| "expected a Lua table", |
| lua_typename(L, type)); |
| } |
| |
| custom_ctx = 1; |
| |
| } else { |
| lua_pop(L, 1); |
| } |
| |
| dd("queries query uri opts ctx?: %d", lua_gettop(L)); |
| |
| /* check the "body" option */ |
| |
| lua_getfield(L, 4, "body"); |
| |
| type = lua_type(L, -1); |
| |
| if (type != LUA_TNIL) { |
| if (type != LUA_TSTRING && type != LUA_TNUMBER) { |
| return luaL_error(L, "Bad http request body"); |
| } |
| |
| body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
| |
| if (body == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| q = (u_char *) lua_tolstring(L, -1, &len); |
| |
| dd("request body: [%.*s]", (int) len, q); |
| |
| if (len) { |
| b = ngx_create_temp_buf(r->pool, len); |
| if (b == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| b->last = ngx_copy(b->last, q, len); |
| |
| body->bufs = ngx_alloc_chain_link(r->pool); |
| if (body->bufs == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| body->bufs->buf = b; |
| body->bufs->next = NULL; |
| |
| body->buf = b; |
| } |
| } |
| |
| lua_pop(L, 1); /* pop the body */ |
| |
| /* stack: queries query uri opts ctx? */ |
| |
| lua_remove(L, 4); |
| |
| /* stack: queries query uri ctx? */ |
| |
| dd("queries query uri ctx?: %d", lua_gettop(L)); |
| |
| } else { |
| method = NGX_HTTP_GET; |
| } |
| |
| /* stack: queries query uri ctx? */ |
| |
| p = (u_char *) luaL_checklstring(L, 3, &len); |
| |
| uri.data = ngx_palloc(r->pool, len); |
| if (uri.data == NULL) { |
| return luaL_error(L, "memory allocation error"); |
| } |
| |
| ngx_memcpy(uri.data, p, len); |
| |
| uri.len = len; |
| |
| args.data = NULL; |
| args.len = 0; |
| |
| flags = 0; |
| |
| rc = ngx_http_parse_unsafe_uri(r, &uri, &args, &flags); |
| if (rc != NGX_OK) { |
| dd("rc = %d", (int) rc); |
| |
| return luaL_error(L, "unsafe uri in argument #1: %s", p); |
| } |
| |
| if (args.len == 0) { |
| if (extra_args.len) { |
| p = ngx_palloc(r->pool, extra_args.len); |
| if (p == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| ngx_memcpy(p, extra_args.data, extra_args.len); |
| |
| args.data = p; |
| args.len = extra_args.len; |
| } |
| |
| } else if (extra_args.len) { |
| /* concatenate the two parts of args together */ |
| len = args.len + (sizeof("&") - 1) + extra_args.len; |
| |
| p = ngx_palloc(r->pool, len); |
| if (p == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| q = ngx_copy(p, args.data, args.len); |
| *q++ = '&'; |
| ngx_memcpy(q, extra_args.data, extra_args.len); |
| |
| args.data = p; |
| args.len = len; |
| } |
| |
| p = ngx_pnalloc(r->pool, sizeof(ngx_http_post_subrequest_t) |
| + sizeof(ngx_http_lua_ctx_t) |
| + sizeof(ngx_http_lua_post_subrequest_data_t)); |
| if (p == NULL) { |
| return luaL_error(L, "out of memory"); |
| } |
| |
| psr = (ngx_http_post_subrequest_t *) p; |
| |
| p += sizeof(ngx_http_post_subrequest_t); |
| |
| sr_ctx = (ngx_http_lua_ctx_t *) p; |
| |
| p += sizeof(ngx_http_lua_ctx_t); |
| |
| psr_data = (ngx_http_lua_post_subrequest_data_t *) p; |
| |
| ngx_memzero(sr_ctx, sizeof(ngx_http_lua_ctx_t)); |
| |
| /* set by ngx_memzero: |
| * sr_ctx->run_post_subrequest = 0 |
| * sr_ctx->free = NULL |
| * sr_ctx->body = NULL |
| */ |
| |
| psr_data->ctx = sr_ctx; |
| psr_data->pr_co_ctx = coctx; |
| |
| psr->handler = ngx_http_lua_post_subrequest; |
| psr->data = psr_data; |
| |
| rc = ngx_http_lua_subrequest(r, &uri, &args, &sr, psr, 0); |
| |
| if (rc != NGX_OK) { |
| return luaL_error(L, "failed to issue subrequest: %d", (int) rc); |
| } |
| |
| ngx_http_lua_init_ctx(sr, sr_ctx); |
| |
| sr_ctx->capture = 1; |
| sr_ctx->index = index; |
| sr_ctx->last_body = &sr_ctx->body; |
| sr_ctx->vm_state = ctx->vm_state; |
| |
| ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module); |
| |
| rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body, |
| body, vars_action, extra_vars); |
| |
| if (rc != NGX_OK) { |
| ngx_http_lua_cancel_subreq(sr); |
| return luaL_error(L, "failed to adjust the subrequest: %d", |
| (int) rc); |
| } |
| |
| dd("queries query uri opts ctx? %d", lua_gettop(L)); |
| |
| /* stack: queries query uri ctx? */ |
| |
| if (custom_ctx) { |
| ngx_http_lua_ngx_set_ctx_helper(L, sr, sr_ctx, -1); |
| lua_pop(L, 3); |
| |
| } else { |
| lua_pop(L, 2); |
| } |
| |
| /* stack: queries */ |
| } |
| |
| if (extra_vars) { |
| ngx_array_destroy(extra_vars); |
| } |
| |
| ctx->no_abort = 1; |
| |
| return lua_yield(L, 0); |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, |
| int always_forward_body, ngx_http_request_body_t *body, |
| unsigned vars_action, ngx_array_t *extra_vars) |
| { |
| ngx_http_request_t *r; |
| ngx_int_t rc; |
| ngx_http_core_main_conf_t *cmcf; |
| size_t size; |
| |
| r = sr->parent; |
| |
| sr->header_in = r->header_in; |
| |
| if (body) { |
| sr->request_body = body; |
| |
| rc = ngx_http_lua_set_content_length_header(sr, |
| body->buf |
| ? ngx_buf_size(body->buf) |
| : 0); |
| |
| if (rc != NGX_OK) { |
| return NGX_ERROR; |
| } |
| |
| } else if (!always_forward_body |
| && method != NGX_HTTP_PUT |
| && method != NGX_HTTP_POST |
| && r->headers_in.content_length_n > 0) |
| { |
| rc = ngx_http_lua_set_content_length_header(sr, 0); |
| if (rc != NGX_OK) { |
| return NGX_ERROR; |
| } |
| |
| #if 1 |
| sr->request_body = NULL; |
| #endif |
| |
| } else { |
| if (ngx_http_lua_copy_request_headers(sr, r) != NGX_OK) { |
| return NGX_ERROR; |
| } |
| |
| if (sr->request_body) { |
| |
| /* deep-copy the request body */ |
| |
| if (sr->request_body->temp_file) { |
| if (ngx_http_lua_copy_in_file_request_body(sr) != NGX_OK) { |
| return NGX_ERROR; |
| } |
| } |
| } |
| } |
| |
| sr->method = method; |
| |
| switch (method) { |
| case NGX_HTTP_GET: |
| sr->method_name = ngx_http_lua_get_method; |
| break; |
| |
| case NGX_HTTP_POST: |
| sr->method_name = ngx_http_lua_post_method; |
| break; |
| |
| case NGX_HTTP_PUT: |
| sr->method_name = ngx_http_lua_put_method; |
| break; |
| |
| case NGX_HTTP_HEAD: |
| sr->method_name = ngx_http_lua_head_method; |
| break; |
| |
| case NGX_HTTP_DELETE: |
| sr->method_name = ngx_http_lua_delete_method; |
| break; |
| |
| case NGX_HTTP_OPTIONS: |
| sr->method_name = ngx_http_lua_options_method; |
| break; |
| |
| case NGX_HTTP_MKCOL: |
| sr->method_name = ngx_http_lua_mkcol_method; |
| break; |
| |
| case NGX_HTTP_COPY: |
| sr->method_name = ngx_http_lua_copy_method; |
| break; |
| |
| case NGX_HTTP_MOVE: |
| sr->method_name = ngx_http_lua_move_method; |
| break; |
| |
| case NGX_HTTP_PROPFIND: |
| sr->method_name = ngx_http_lua_propfind_method; |
| break; |
| |
| case NGX_HTTP_PROPPATCH: |
| sr->method_name = ngx_http_lua_proppatch_method; |
| break; |
| |
| case NGX_HTTP_LOCK: |
| sr->method_name = ngx_http_lua_lock_method; |
| break; |
| |
| case NGX_HTTP_UNLOCK: |
| sr->method_name = ngx_http_lua_unlock_method; |
| break; |
| |
| case NGX_HTTP_PATCH: |
| sr->method_name = ngx_http_lua_patch_method; |
| break; |
| |
| case NGX_HTTP_TRACE: |
| sr->method_name = ngx_http_lua_trace_method; |
| break; |
| |
| default: |
| ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
| "unsupported HTTP method: %u", (unsigned) method); |
| |
| return NGX_ERROR; |
| } |
| |
| if (!(vars_action & NGX_HTTP_LUA_SHARE_ALL_VARS)) { |
| /* we do not inherit the parent request's variables */ |
| cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); |
| |
| size = cmcf->variables.nelts * sizeof(ngx_http_variable_value_t); |
| |
| if (vars_action & NGX_HTTP_LUA_COPY_ALL_VARS) { |
| |
| sr->variables = ngx_palloc(sr->pool, size); |
| if (sr->variables == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ngx_memcpy(sr->variables, r->variables, size); |
| |
| } else { |
| |
| /* we do not inherit the parent request's variables */ |
| |
| sr->variables = ngx_pcalloc(sr->pool, size); |
| if (sr->variables == NULL) { |
| return NGX_ERROR; |
| } |
| } |
| } |
| |
| return ngx_http_lua_subrequest_add_extra_vars(sr, extra_vars); |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *sr, |
| ngx_array_t *extra_vars) |
| { |
| ngx_http_core_main_conf_t *cmcf; |
| ngx_http_variable_t *v; |
| ngx_http_variable_value_t *vv; |
| u_char *val; |
| u_char *p; |
| ngx_uint_t i, hash; |
| ngx_str_t name; |
| size_t len; |
| ngx_hash_t *variables_hash; |
| ngx_keyval_t *var; |
| |
| /* set any extra variables that were passed to the subrequest */ |
| |
| if (extra_vars == NULL || extra_vars->nelts == 0) { |
| return NGX_OK; |
| } |
| |
| cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); |
| |
| variables_hash = &cmcf->variables_hash; |
| |
| var = extra_vars->elts; |
| |
| for (i = 0; i < extra_vars->nelts; i++, var++) { |
| /* copy the variable's name and value because they are allocated |
| * by the lua VM */ |
| |
| len = var->key.len + var->value.len; |
| |
| p = ngx_pnalloc(sr->pool, len); |
| if (p == NULL) { |
| return NGX_ERROR; |
| } |
| |
| name.data = p; |
| name.len = var->key.len; |
| |
| p = ngx_copy(p, var->key.data, var->key.len); |
| |
| hash = ngx_hash_strlow(name.data, name.data, name.len); |
| |
| val = p; |
| len = var->value.len; |
| |
| ngx_memcpy(p, var->value.data, len); |
| |
| v = ngx_hash_find(variables_hash, hash, name.data, name.len); |
| |
| if (v) { |
| if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { |
| ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0, |
| "variable \"%V\" not changeable", &name); |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| if (v->set_handler) { |
| vv = ngx_palloc(sr->pool, sizeof(ngx_http_variable_value_t)); |
| if (vv == NULL) { |
| return NGX_ERROR; |
| } |
| |
| vv->valid = 1; |
| vv->not_found = 0; |
| vv->no_cacheable = 0; |
| |
| vv->data = val; |
| vv->len = len; |
| |
| v->set_handler(sr, vv, v->data); |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0, |
| "variable \"%V\" set to value \"%v\"", &name, |
| vv); |
| |
| continue; |
| } |
| |
| if (v->flags & NGX_HTTP_VAR_INDEXED) { |
| vv = &sr->variables[v->index]; |
| |
| vv->valid = 1; |
| vv->not_found = 0; |
| vv->no_cacheable = 0; |
| |
| vv->data = val; |
| vv->len = len; |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0, |
| "variable \"%V\" set to value \"%v\"", |
| &name, vv); |
| |
| continue; |
| } |
| } |
| |
| ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0, |
| "variable \"%V\" cannot be assigned a value (maybe you " |
| "forgot to define it first?) ", &name); |
| |
| return NGX_ERROR; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| static void |
| ngx_http_lua_process_vars_option(ngx_http_request_t *r, lua_State *L, |
| int table, ngx_array_t **varsp) |
| { |
| ngx_array_t *vars; |
| ngx_keyval_t *var; |
| |
| if (table < 0) { |
| table = lua_gettop(L) + table + 1; |
| } |
| |
| vars = *varsp; |
| |
| if (vars == NULL) { |
| |
| vars = ngx_array_create(r->pool, 4, sizeof(ngx_keyval_t)); |
| if (vars == NULL) { |
| dd("here"); |
| luaL_error(L, "out of memory"); |
| return; |
| } |
| |
| *varsp = vars; |
| } |
| |
| lua_pushnil(L); |
| while (lua_next(L, table) != 0) { |
| |
| if (lua_type(L, -2) != LUA_TSTRING) { |
| luaL_error(L, "attempt to use a non-string key in the " |
| "\"vars\" option table"); |
| return; |
| } |
| |
| if (!lua_isstring(L, -1)) { |
| luaL_error(L, "attempt to use bad variable value type %s", |
| luaL_typename(L, -1)); |
| } |
| |
| var = ngx_array_push(vars); |
| if (var == NULL) { |
| dd("here"); |
| luaL_error(L, "out of memory"); |
| return; |
| } |
| |
| var->key.data = (u_char *) lua_tolstring(L, -2, &var->key.len); |
| var->value.data = (u_char *) lua_tolstring(L, -1, &var->value.len); |
| |
| lua_pop(L, 1); |
| } |
| } |
| |
| |
| ngx_int_t |
| ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) |
| { |
| ngx_http_request_t *pr; |
| ngx_http_lua_ctx_t *pr_ctx; |
| ngx_http_lua_ctx_t *ctx; /* subrequest ctx */ |
| ngx_http_lua_co_ctx_t *pr_coctx; |
| size_t len; |
| ngx_str_t *body_str; |
| u_char *p; |
| ngx_chain_t *cl; |
| |
| ngx_http_lua_post_subrequest_data_t *psr_data = data; |
| |
| ctx = psr_data->ctx; |
| |
| if (ctx->run_post_subrequest) { |
| if (r != r->connection->data) { |
| r->connection->data = r; |
| } |
| |
| return NGX_OK; |
| } |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua run post subrequest handler, rc:%i c:%ud", rc, |
| r->main->count); |
| |
| ctx->run_post_subrequest = 1; |
| |
| pr = r->parent; |
| |
| pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_lua_module); |
| if (pr_ctx == NULL) { |
| return NGX_ERROR; |
| } |
| |
| pr_coctx = psr_data->pr_co_ctx; |
| pr_coctx->pending_subreqs--; |
| |
| if (pr_coctx->pending_subreqs == 0) { |
| dd("all subrequests are done"); |
| |
| pr_ctx->no_abort = 0; |
| pr_ctx->resume_handler = ngx_http_lua_subrequest_resume; |
| pr_ctx->cur_co_ctx = pr_coctx; |
| } |
| |
| if (pr_ctx->entered_content_phase) { |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua restoring write event handler"); |
| |
| pr->write_event_handler = ngx_http_lua_content_wev_handler; |
| |
| } else { |
| pr->write_event_handler = ngx_http_core_run_phases; |
| } |
| |
| dd("status rc = %d", (int) rc); |
| dd("status headers_out.status = %d", (int) r->headers_out.status); |
| dd("uri: %.*s", (int) r->uri.len, r->uri.data); |
| |
| /* capture subrequest response status */ |
| |
| pr_coctx->sr_statuses[ctx->index] = r->headers_out.status; |
| |
| if (pr_coctx->sr_statuses[ctx->index] == 0) { |
| if (rc == NGX_OK) { |
| rc = NGX_HTTP_OK; |
| } |
| |
| if (rc == NGX_ERROR) { |
| rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
| pr_coctx->sr_statuses[ctx->index] = rc; |
| } |
| } |
| |
| if (!ctx->seen_last_for_subreq) { |
| pr_coctx->sr_flags[ctx->index] |= NGX_HTTP_LUA_SUBREQ_TRUNCATED; |
| } |
| |
| dd("pr_coctx status: %d", (int) pr_coctx->sr_statuses[ctx->index]); |
| |
| /* copy subrequest response headers */ |
| |
| pr_coctx->sr_headers[ctx->index] = &r->headers_out; |
| |
| /* copy subrequest response body */ |
| |
| body_str = &pr_coctx->sr_bodies[ctx->index]; |
| |
| len = 0; |
| for (cl = ctx->body; cl; cl = cl->next) { |
| /* ignore all non-memory buffers */ |
| len += cl->buf->last - cl->buf->pos; |
| } |
| |
| body_str->len = len; |
| |
| if (len == 0) { |
| body_str->data = NULL; |
| |
| } else { |
| p = ngx_palloc(r->pool, len); |
| if (p == NULL) { |
| return NGX_ERROR; |
| } |
| |
| body_str->data = p; |
| |
| /* copy from and then free the data buffers */ |
| |
| for (cl = ctx->body; cl; cl = cl->next) { |
| p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); |
| |
| cl->buf->last = cl->buf->pos; |
| |
| #if 0 |
| dd("free body chain link buf ASAP"); |
| ngx_pfree(r->pool, cl->buf->start); |
| #endif |
| } |
| } |
| |
| if (ctx->body) { |
| |
| #if defined(nginx_version) && nginx_version >= 1001004 |
| ngx_chain_update_chains(r->pool, |
| #else |
| ngx_chain_update_chains( |
| #endif |
| &pr_ctx->free_bufs, &pr_ctx->busy_bufs, |
| &ctx->body, |
| (ngx_buf_tag_t) &ngx_http_lua_module); |
| |
| dd("free bufs: %p", pr_ctx->free_bufs); |
| } |
| |
| ngx_http_post_request_to_head(pr); |
| |
| if (r != r->connection->data) { |
| r->connection->data = r; |
| } |
| |
| if (rc == NGX_ERROR |
| || rc == NGX_HTTP_CREATED |
| || rc == NGX_HTTP_NO_CONTENT |
| || (rc >= NGX_HTTP_SPECIAL_RESPONSE |
| && rc != NGX_HTTP_CLOSE |
| && rc != NGX_HTTP_REQUEST_TIME_OUT |
| && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST)) |
| { |
| /* emulate ngx_http_special_response_handler */ |
| |
| if (rc > NGX_OK) { |
| r->err_status = rc; |
| |
| r->expect_tested = 1; |
| r->headers_out.content_type.len = 0; |
| r->headers_out.content_length_n = 0; |
| |
| ngx_http_clear_accept_ranges(r); |
| ngx_http_clear_last_modified(r); |
| |
| rc = ngx_http_lua_send_header_if_needed(r, ctx); |
| if (rc == NGX_ERROR) { |
| return NGX_ERROR; |
| } |
| } |
| |
| return NGX_OK; |
| } |
| |
| return rc; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_set_content_length_header(ngx_http_request_t *r, off_t len) |
| { |
| ngx_table_elt_t *h, *header; |
| u_char *p; |
| ngx_list_part_t *part; |
| ngx_http_request_t *pr; |
| ngx_uint_t i; |
| |
| r->headers_in.content_length_n = len; |
| |
| if (ngx_list_init(&r->headers_in.headers, r->pool, 20, |
| sizeof(ngx_table_elt_t)) != NGX_OK) |
| { |
| return NGX_ERROR; |
| } |
| |
| h = ngx_list_push(&r->headers_in.headers); |
| if (h == NULL) { |
| return NGX_ERROR; |
| } |
| |
| h->key = ngx_http_lua_content_length_header_key; |
| h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); |
| if (h->lowcase_key == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ngx_strlow(h->lowcase_key, h->key.data, h->key.len); |
| |
| r->headers_in.content_length = h; |
| |
| p = ngx_palloc(r->pool, NGX_OFF_T_LEN); |
| if (p == NULL) { |
| return NGX_ERROR; |
| } |
| |
| h->value.data = p; |
| |
| h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; |
| |
| h->hash = ngx_http_lua_content_length_hash; |
| |
| #if 0 |
| dd("content length hash: %lu == %lu", (unsigned long) h->hash, |
| ngx_hash_key_lc((u_char *) "Content-Length", |
| sizeof("Content-Length") - 1)); |
| #endif |
| |
| dd("r content length: %.*s", |
| (int)r->headers_in.content_length->value.len, |
| r->headers_in.content_length->value.data); |
| |
| pr = r->parent; |
| |
| if (pr == NULL) { |
| return NGX_OK; |
| } |
| |
| /* forward the parent request's all other request headers */ |
| |
| part = &pr->headers_in.headers.part; |
| header = part->elts; |
| |
| for (i = 0; /* void */; i++) { |
| |
| if (i >= part->nelts) { |
| if (part->next == NULL) { |
| break; |
| } |
| |
| part = part->next; |
| header = part->elts; |
| i = 0; |
| } |
| |
| if (header[i].key.len == sizeof("Content-Length") - 1 |
| && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length", |
| sizeof("Content-Length") - 1) == 0) |
| { |
| continue; |
| } |
| |
| if (ngx_http_lua_set_input_header(r, header[i].key, |
| header[i].value, 0) == NGX_ERROR) |
| { |
| return NGX_ERROR; |
| } |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| static void |
| ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, |
| ngx_http_lua_ctx_t *ctx) |
| { |
| ngx_uint_t i, count; |
| ngx_uint_t index; |
| lua_State *co; |
| ngx_str_t *body_str; |
| ngx_table_elt_t *header; |
| ngx_list_part_t *part; |
| ngx_http_headers_out_t *sr_headers; |
| ngx_http_lua_co_ctx_t *coctx; |
| |
| u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; |
| |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua handle subrequest responses"); |
| |
| coctx = ctx->cur_co_ctx; |
| co = coctx->co; |
| |
| for (index = 0; index < coctx->nsubreqs; index++) { |
| dd("summary: reqs %d, subquery %d, pending %d, req %.*s", |
| (int) coctx->nsubreqs, |
| (int) index, |
| (int) coctx->pending_subreqs, |
| (int) r->uri.len, r->uri.data); |
| |
| /* {{{ construct ret value */ |
| lua_createtable(co, 0 /* narr */, 4 /* nrec */); |
| |
| /* copy captured status */ |
| lua_pushinteger(co, coctx->sr_statuses[index]); |
| lua_setfield(co, -2, "status"); |
| |
| dd("captured subrequest flags: %d", (int) coctx->sr_flags[index]); |
| |
| /* set truncated flag if truncation happens */ |
| if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) { |
| lua_pushboolean(co, 1); |
| lua_setfield(co, -2, "truncated"); |
| |
| } else { |
| lua_pushboolean(co, 0); |
| lua_setfield(co, -2, "truncated"); |
| } |
| |
| /* copy captured body */ |
| |
| body_str = &coctx->sr_bodies[index]; |
| |
| lua_pushlstring(co, (char *) body_str->data, body_str->len); |
| lua_setfield(co, -2, "body"); |
| |
| if (body_str->data) { |
| dd("free body buffer ASAP"); |
| ngx_pfree(r->pool, body_str->data); |
| } |
| |
| /* copy captured headers */ |
| |
| sr_headers = coctx->sr_headers[index]; |
| |
| part = &sr_headers->headers.part; |
| count = part->nelts; |
| while (part->next) { |
| part = part->next; |
| count += part->nelts; |
| } |
| |
| lua_createtable(co, 0, count + 5); /* res.header */ |
| |
| dd("saving subrequest response headers"); |
| |
| part = &sr_headers->headers.part; |
| header = part->elts; |
| |
| for (i = 0; /* void */; i++) { |
| |
| if (i >= part->nelts) { |
| if (part->next == NULL) { |
| break; |
| } |
| |
| part = part->next; |
| header = part->elts; |
| i = 0; |
| } |
| |
| dd("checking sr header %.*s", (int) header[i].key.len, |
| header[i].key.data); |
| |
| #if 1 |
| if (header[i].hash == 0) { |
| continue; |
| } |
| #endif |
| |
| header[i].hash = 0; |
| |
| dd("pushing sr header %.*s", (int) header[i].key.len, |
| header[i].key.data); |
| |
| lua_pushlstring(co, (char *) header[i].key.data, |
| header[i].key.len); /* header key */ |
| lua_pushvalue(co, -1); /* stack: table key key */ |
| |
| /* check if header already exists */ |
| lua_rawget(co, -3); /* stack: table key value */ |
| |
| if (lua_isnil(co, -1)) { |
| lua_pop(co, 1); /* stack: table key */ |
| |
| lua_pushlstring(co, (char *) header[i].value.data, |
| header[i].value.len); |
| /* stack: table key value */ |
| |
| lua_rawset(co, -3); /* stack: table */ |
| |
| } else { |
| |
| if (!lua_istable(co, -1)) { /* already inserted one value */ |
| lua_createtable(co, 4, 0); |
| /* stack: table key value table */ |
| |
| lua_insert(co, -2); /* stack: table key table value */ |
| lua_rawseti(co, -2, 1); /* stack: table key table */ |
| |
| lua_pushlstring(co, (char *) header[i].value.data, |
| header[i].value.len); |
| /* stack: table key table value */ |
| |
| lua_rawseti(co, -2, lua_objlen(co, -2) + 1); |
| /* stack: table key table */ |
| |
| lua_rawset(co, -3); /* stack: table */ |
| |
| } else { |
| lua_pushlstring(co, (char *) header[i].value.data, |
| header[i].value.len); |
| /* stack: table key table value */ |
| |
| lua_rawseti(co, -2, lua_objlen(co, -2) + 1); |
| /* stack: table key table */ |
| |
| lua_pop(co, 2); /* stack: table */ |
| } |
| } |
| } |
| |
| if (sr_headers->content_type.len) { |
| lua_pushliteral(co, "Content-Type"); /* header key */ |
| lua_pushlstring(co, (char *) sr_headers->content_type.data, |
| sr_headers->content_type.len); /* head key value */ |
| lua_rawset(co, -3); /* head */ |
| } |
| |
| if (sr_headers->content_length == NULL |
| && sr_headers->content_length_n >= 0) |
| { |
| lua_pushliteral(co, "Content-Length"); /* header key */ |
| |
| lua_pushnumber(co, (lua_Number) sr_headers->content_length_n); |
| /* head key value */ |
| |
| lua_rawset(co, -3); /* head */ |
| } |
| |
| /* to work-around an issue in ngx_http_static_module |
| * (github issue #41) */ |
| if (sr_headers->location && sr_headers->location->value.len) { |
| lua_pushliteral(co, "Location"); /* header key */ |
| lua_pushlstring(co, (char *) sr_headers->location->value.data, |
| sr_headers->location->value.len); |
| /* head key value */ |
| lua_rawset(co, -3); /* head */ |
| } |
| |
| if (sr_headers->last_modified_time != -1) { |
| if (sr_headers->status != NGX_HTTP_OK |
| && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT |
| && sr_headers->status != NGX_HTTP_NOT_MODIFIED |
| && sr_headers->status != NGX_HTTP_NO_CONTENT) |
| { |
| sr_headers->last_modified_time = -1; |
| sr_headers->last_modified = NULL; |
| } |
| } |
| |
| if (sr_headers->last_modified == NULL |
| && sr_headers->last_modified_time != -1) |
| { |
| (void) ngx_http_time(buf, sr_headers->last_modified_time); |
| |
| lua_pushliteral(co, "Last-Modified"); /* header key */ |
| lua_pushlstring(co, (char *) buf, sizeof(buf)); /* head key value */ |
| lua_rawset(co, -3); /* head */ |
| } |
| |
| lua_setfield(co, -2, "header"); |
| |
| /* }}} */ |
| } |
| } |
| |
| |
| void |
| ngx_http_lua_inject_subrequest_api(lua_State *L) |
| { |
| lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* .location */ |
| |
| lua_pushcfunction(L, ngx_http_lua_ngx_location_capture); |
| lua_setfield(L, -2, "capture"); |
| |
| lua_pushcfunction(L, ngx_http_lua_ngx_location_capture_multi); |
| lua_setfield(L, -2, "capture_multi"); |
| |
| lua_setfield(L, -2, "location"); |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_subrequest(ngx_http_request_t *r, |
| ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, |
| ngx_http_post_subrequest_t *ps, ngx_uint_t flags) |
| { |
| ngx_time_t *tp; |
| ngx_connection_t *c; |
| ngx_http_request_t *sr; |
| ngx_http_core_srv_conf_t *cscf; |
| |
| r->main->subrequests--; |
| |
| if (r->main->subrequests == 0) { |
| #if defined(NGX_DTRACE) && NGX_DTRACE |
| ngx_http_probe_subrequest_cycle(r, uri, args); |
| #endif |
| |
| ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
| "subrequests cycle while processing \"%V\"", uri); |
| r->main->subrequests = 1; |
| return NGX_ERROR; |
| } |
| |
| sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t)); |
| if (sr == NULL) { |
| return NGX_ERROR; |
| } |
| |
| sr->signature = NGX_HTTP_MODULE; |
| |
| c = r->connection; |
| sr->connection = c; |
| |
| sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); |
| if (sr->ctx == NULL) { |
| return NGX_ERROR; |
| } |
| |
| if (ngx_list_init(&sr->headers_out.headers, r->pool, 20, |
| sizeof(ngx_table_elt_t)) |
| != NGX_OK) |
| { |
| return NGX_ERROR; |
| } |
| |
| cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
| sr->main_conf = cscf->ctx->main_conf; |
| sr->srv_conf = cscf->ctx->srv_conf; |
| sr->loc_conf = cscf->ctx->loc_conf; |
| |
| sr->pool = r->pool; |
| |
| sr->headers_in.content_length_n = -1; |
| sr->headers_in.keep_alive_n = -1; |
| |
| ngx_http_clear_content_length(sr); |
| ngx_http_clear_accept_ranges(sr); |
| ngx_http_clear_last_modified(sr); |
| |
| sr->request_body = r->request_body; |
| |
| #ifdef HAVE_ALLOW_REQUEST_BODY_UPDATING_PATCH |
| sr->content_length_n = -1; |
| #endif |
| |
| sr->method = NGX_HTTP_GET; |
| sr->http_version = r->http_version; |
| |
| sr->request_line = r->request_line; |
| sr->uri = *uri; |
| |
| if (args) { |
| sr->args = *args; |
| } |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http subrequest \"%V?%V\"", uri, &sr->args); |
| |
| sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0; |
| sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0; |
| |
| sr->unparsed_uri = r->unparsed_uri; |
| sr->method_name = ngx_http_core_get_method; |
| sr->http_protocol = r->http_protocol; |
| |
| ngx_http_set_exten(sr); |
| |
| sr->main = r->main; |
| sr->parent = r; |
| sr->post_subrequest = ps; |
| sr->read_event_handler = ngx_http_request_empty_handler; |
| sr->write_event_handler = ngx_http_handler; |
| |
| sr->variables = r->variables; |
| |
| sr->log_handler = r->log_handler; |
| |
| sr->internal = 1; |
| |
| sr->discard_body = r->discard_body; |
| sr->expect_tested = 1; |
| sr->main_filter_need_in_memory = r->main_filter_need_in_memory; |
| |
| sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; |
| |
| tp = ngx_timeofday(); |
| sr->start_sec = tp->sec; |
| sr->start_msec = tp->msec; |
| |
| r->main->count++; |
| |
| *psr = sr; |
| |
| #if defined(NGX_DTRACE) && NGX_DTRACE |
| ngx_http_probe_subrequest_start(sr); |
| #endif |
| |
| return ngx_http_post_request(sr, NULL); |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_subrequest_resume(ngx_http_request_t *r) |
| { |
| lua_State *vm; |
| ngx_int_t rc; |
| ngx_connection_t *c; |
| ngx_http_lua_ctx_t *ctx; |
| ngx_http_lua_co_ctx_t *coctx; |
| |
| ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); |
| if (ctx == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ctx->resume_handler = ngx_http_lua_wev_handler; |
| |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua run subrequests done, resuming lua thread"); |
| |
| coctx = ctx->cur_co_ctx; |
| |
| dd("nsubreqs: %d", (int) coctx->nsubreqs); |
| |
| ngx_http_lua_handle_subreq_responses(r, ctx); |
| |
| dd("free sr_statues/headers/bodies memory ASAP"); |
| |
| #if 1 |
| ngx_pfree(r->pool, coctx->sr_statuses); |
| |
| coctx->sr_statuses = NULL; |
| coctx->sr_headers = NULL; |
| coctx->sr_bodies = NULL; |
| coctx->sr_flags = NULL; |
| #endif |
| |
| c = r->connection; |
| vm = ngx_http_lua_get_lua_vm(r, ctx); |
| |
| rc = ngx_http_lua_run_thread(vm, r, ctx, coctx->nsubreqs); |
| |
| ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
| "lua run thread returned %d", rc); |
| |
| if (rc == NGX_AGAIN) { |
| return ngx_http_lua_run_posted_threads(c, vm, r, ctx); |
| } |
| |
| if (rc == NGX_DONE) { |
| ngx_http_lua_finalize_request(r, NGX_DONE); |
| return ngx_http_lua_run_posted_threads(c, vm, r, ctx); |
| } |
| |
| /* rc == NGX_ERROR || rc >= NGX_OK */ |
| |
| if (ctx->entered_content_phase) { |
| ngx_http_lua_finalize_request(r, rc); |
| return NGX_DONE; |
| } |
| |
| return rc; |
| } |
| |
| |
| static void |
| ngx_http_lua_cancel_subreq(ngx_http_request_t *r) |
| { |
| ngx_http_posted_request_t *pr; |
| ngx_http_posted_request_t **p; |
| |
| #if 1 |
| r->main->count--; |
| r->main->subrequests++; |
| #endif |
| |
| p = &r->main->posted_requests; |
| for (pr = r->main->posted_requests; pr->next; pr = pr->next) { |
| p = &pr->next; |
| } |
| |
| *p = NULL; |
| |
| r->connection->data = r->parent; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_post_request_to_head(ngx_http_request_t *r) |
| { |
| ngx_http_posted_request_t *pr; |
| |
| pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); |
| if (pr == NULL) { |
| return NGX_ERROR; |
| } |
| |
| pr->request = r; |
| pr->next = r->main->posted_requests; |
| r->main->posted_requests = pr; |
| |
| return NGX_OK; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r) |
| { |
| ngx_temp_file_t *tf; |
| |
| ngx_http_request_body_t *body; |
| |
| tf = r->request_body->temp_file; |
| |
| if (!tf->persistent || !tf->clean) { |
| ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
| "the request body was not read by ngx_lua"); |
| |
| return NGX_ERROR; |
| } |
| |
| body = ngx_palloc(r->pool, sizeof(ngx_http_request_body_t)); |
| if (body == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ngx_memcpy(body, r->request_body, sizeof(ngx_http_request_body_t)); |
| |
| body->temp_file = ngx_palloc(r->pool, sizeof(ngx_temp_file_t)); |
| if (body->temp_file == NULL) { |
| return NGX_ERROR; |
| } |
| |
| ngx_memcpy(body->temp_file, tf, sizeof(ngx_temp_file_t)); |
| dd("file fd: %d", body->temp_file->file.fd); |
| |
| r->request_body = body; |
| |
| return NGX_OK; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, ngx_http_request_t *r) |
| { |
| ngx_table_elt_t *header; |
| ngx_list_part_t *part; |
| ngx_uint_t i; |
| |
| if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20, |
| sizeof(ngx_table_elt_t)) != NGX_OK) |
| { |
| return NGX_ERROR; |
| } |
| |
| dd("before: parent req headers count: %d", |
| (int) r->headers_in.headers.part.nelts); |
| |
| part = &r->headers_in.headers.part; |
| header = part->elts; |
| |
| for (i = 0; /* void */; i++) { |
| |
| if (i >= part->nelts) { |
| if (part->next == NULL) { |
| break; |
| } |
| |
| part = part->next; |
| header = part->elts; |
| i = 0; |
| } |
| |
| dd("setting request header %.*s: %.*s", (int) header[i].key.len, |
| header[i].key.data, (int) header[i].value.len, |
| header[i].value.data); |
| |
| if (ngx_http_lua_set_input_header(sr, header[i].key, |
| header[i].value, 0) == NGX_ERROR) |
| { |
| return NGX_ERROR; |
| } |
| } |
| |
| dd("after: parent req headers count: %d", |
| (int) r->headers_in.headers.part.nelts); |
| |
| return NGX_OK; |
| } |
| |
| /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ |