Skip to content

Commit 19bbe01

Browse files
author
Alex
committed
update 003-ngx_lua_rewrite_by_lua.md
1 parent 256a8af commit 19bbe01

File tree

1 file changed

+160
-2
lines changed

1 file changed

+160
-2
lines changed

chapters/003-ngx_lua_rewrite_by_lua.md

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
> 以下代码均出自 lua-nginx-module v0.10.7 版本
22
33
> 这篇文章主要要介绍 `rewrite_by_lua` 这个指令 <br>
4-
> rewrite 是 Nginx HTTP 框架划分的 11 个阶段的其中之一,通常在这个阶段我们可以实现 uri 的修改(进行内部重定向)或者 301、302 的 HTTP 重定向
4+
> rewrite 是 Nginx HTTP 框架划分的 11 个阶段的其中之一,通常在这个阶段我们可以实现 uri 的修改(进行内部重定向)或者 301、302 的 HTTP 重定向;另外,下面的讲解以 `rewrite_by_lua` 这个指令来展开,`rewrite_by_lua_block``rewrite_by_lua _file` 类似,不再赘述
55
>
66
77

@@ -130,14 +130,172 @@ ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
130130

131131
<img src="../images/003-ngx_http_lua_rewrite_by_lua-002.png" alt="rewrite_src_key">
132132

133-
后面就是用这个 key 来检索缓存 Lua code,当然前提是 `lua_code_cache` `on`
133+
后面就是用这个 key 来检索缓存 Lua code,当然前提是 `lua_code_cache` 这条指令打开,生产环境中一般都需要对 Lua code 进行缓存以提高效率;开发环境中为了调试方便则可以关闭缓存。
134134

135135
恩,指令解析到这里就结束了,那么,`rewrite_by_lua` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟
136136

137137
### rewrite_by_lua_handler
138138

139139
这是真正被插入到 rewrite 阶段的回调方法(见 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 关于`ngx_http_lua_init` 的介绍)
140140

141+
```c
142+
ngx_int_t
143+
ngx_http_lua_rewrite_handler(ngx_http_request_t *r)
144+
{
145+
ngx_http_lua_loc_conf_t *llcf;
146+
ngx_http_lua_ctx_t *ctx;
147+
ngx_int_t rc;
148+
ngx_http_lua_main_conf_t *lmcf;
149+
150+
/* XXX we need to take into account ngx_rewrite's location dump */
151+
if (r->uri_changed) {
152+
/* to next http module */
153+
return NGX_DECLINED;
154+
}
155+
156+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
157+
"lua rewrite handler, uri:\"%V\" c:%ud", &r->uri,
158+
r->main->count);
159+
160+
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
161+
162+
/*
163+
* this if block will put the rewrite_by_lua to
164+
* the last position of nginx rewirte phase
165+
*/
166+
if (!lmcf->postponed_to_rewrite_phase_end) {
167+
ngx_http_core_main_conf_t *cmcf;
168+
ngx_http_phase_handler_t tmp;
169+
ngx_http_phase_handler_t *ph;
170+
ngx_http_phase_handler_t *cur_ph;
171+
ngx_http_phase_handler_t *last_ph;
172+
173+
lmcf->postponed_to_rewrite_phase_end = 1;
174+
175+
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
176+
177+
ph = cmcf->phase_engine.handlers;
178+
cur_ph = &ph[r->phase_handler];
179+
/* the last module in rewrite phase */
180+
last_ph = &ph[cur_ph->next - 1];
181+
182+
#if 0
183+
if (cur_ph == last_ph) {
184+
dd("XXX our handler is already the last rewrite phase handler");
185+
}
186+
#endif
187+
188+
if (cur_ph < last_ph) {
189+
dd("swaping the contents of cur_ph and last_ph...");
190+
191+
tmp = *cur_ph;
192+
193+
memmove(cur_ph, cur_ph + 1,
194+
(last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t));
195+
196+
*last_ph = tmp;
197+
198+
r->phase_handler--; /* redo the current ph */
199+
200+
return NGX_DECLINED;
201+
}
202+
}
203+
204+
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
205+
206+
if (llcf->rewrite_handler == NULL) {
207+
dd("no rewrite handler found");
208+
return NGX_DECLINED;
209+
}
210+
211+
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
212+
213+
dd("ctx = %p", ctx);
214+
215+
if (ctx == NULL) {
216+
ctx = ngx_http_lua_create_ctx(r);
217+
if (ctx == NULL) {
218+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
219+
}
220+
}
221+
222+
dd("entered? %d", (int) ctx->entered_rewrite_phase);
223+
224+
/* rewrite_by_lua can't be finished in one epoll dispatch */
225+
if (ctx->entered_rewrite_phase) {
226+
dd("rewriteby: calling wev handler");
227+
rc = ctx->resume_handler(r);
228+
dd("rewriteby: wev handler returns %d", (int) rc);
229+
230+
if (rc == NGX_OK) {
231+
rc = NGX_DECLINED;
232+
}
233+
234+
if (rc == NGX_DECLINED) {
235+
if (r->header_sent) {
236+
dd("header already sent");
237+
238+
/* response header was already generated in access_by_lua*,
239+
* so it is no longer safe to proceed to later phases
240+
* which may generate responses again */
241+
242+
if (!ctx->eof) {
243+
dd("eof not yet sent");
244+
245+
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
246+
/* indicate last_buf */);
247+
if (rc == NGX_ERROR || rc > NGX_OK) {
248+
return rc;
249+
}
250+
251+
return NGX_OK;
252+
}
253+
}
254+
255+
return rc;
256+
}
257+
258+
if (ctx->waiting_more_body) {
259+
/* waiting for more request body */
260+
return NGX_DONE;
261+
}
262+
263+
/* lua_need_request_body directive */
264+
if (llcf->force_read_body && !ctx->read_body_done) {
265+
r->request_body_in_single_buf = 1;
266+
r->request_body_in_persistent_file = 1;
267+
r->request_body_in_clean_file = 1;
268+
269+
rc = ngx_http_read_client_request_body(r,
270+
ngx_http_lua_generic_phase_post_read);
271+
272+
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
273+
#if (nginx_version < 1002006) || \
274+
(nginx_version >= 1003000 && nginx_version < 1003009)
275+
r->main->count--;
276+
#endif
277+
278+
return rc;
279+
}
280+
281+
if (rc == NGX_AGAIN) {
282+
ctx->waiting_more_body = 1;
283+
return NGX_DONE;
284+
}
285+
}
286+
287+
dd("calling rewrite handler");
288+
return llcf->rewrite_handler(r);
289+
}
290+
```
291+
292+
让我们来解剖下这个函数 <br>
293+
首先回顾下 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 这一节,里面有介绍 `ngx_http_lua_init` 这个函数,该函数的一小段代码则是把 `lmcf->postponed_to_rewrite_phase_end ` 置 0,这个变量用来标记 `rewrite_by_lua` 的回调方法是否被放置 rewrite 阶段最后,因此这个函数需要检测下这个变量,当它发现 Lua 的回调方法没有放到最后一位时(第一个被 Lua 介入到的请求),需要手动修改回调方法的位置,我们知道 HTTP 框架通过 `phase_engine.handlers` 这个动态数组来连续存放每个 HTTP 模块插入到所有阶段的回调,因此修改顺序实际只需要修改这个数组就行了,值得注意的是,我们要对 `r->phase_handler` 减一,因为交换顺序完毕后,该函数返回 `NGX_DECLINED`,代表希望 HTTP 框架按顺序执行下一个模块的回调,而在 `ngx_http_core_rewrite_phase` 这个 checker 中则会对 `r->phase_handler` 加一,为了某个模块的回调不被漏掉,这里才对这个值减去了 1
294+
295+
继续看这个函数,我们会发现原来 `lua-nginx-module` 的模块上下文也是在某请求里被创建的,当然只会创建一次;另外,对于重入到 `rewrite_by_lua` 的情况,这个函数也做了处理;最后,当它发现需要需要读取请求体的时候,它还会调用 `ngx_http_read_client_request_body ` 来读请求体,如果一次读不完,把 `ctx->waiting_more_body` 设置为 1,然后返回 `NGX_DONE`,这个特殊的返回值会让 `ngx_http_core_rewrite_phase ` 这个 checker 会让 HTTP 框架的控制权返回到事件模块,调度其他的请求,而这个请求则会在未来某个时刻被重新调度到,届时 `rewrite_by_lua ` 的回调方法会被重入,当然,该读的读完,一切就绪之后,就轮到我们的 Lua 代码被执行了,也就是 `ngx_http_lua_rewrite_handler_inline ` 会被调用。
296+
297+
### ngx_http_lua_rewrite_handler_inline
298+
141299
```c
142300
ngx_int_t
143301
ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)

0 commit comments

Comments
 (0)