|
1 | 1 | > 以下代码均出自 lua-nginx-module v0.10.7 版本
|
2 | 2 |
|
3 | 3 | > 这篇文章主要要介绍 `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` 类似,不再赘述 |
5 | 5 | >
|
6 | 6 |
|
7 | 7 |
|
@@ -130,14 +130,172 @@ ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
130 | 130 |
|
131 | 131 | <img src="../images/003-ngx_http_lua_rewrite_by_lua-002.png" alt="rewrite_src_key">
|
132 | 132 |
|
133 |
| -后面就是用这个 key 来检索缓存 Lua code,当然前提是 `lua_code_cache` 是 `on` |
| 133 | +后面就是用这个 key 来检索缓存 Lua code,当然前提是 `lua_code_cache` 这条指令打开,生产环境中一般都需要对 Lua code 进行缓存以提高效率;开发环境中为了调试方便则可以关闭缓存。 |
134 | 134 |
|
135 | 135 | 恩,指令解析到这里就结束了,那么,`rewrite_by_lua` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟
|
136 | 136 |
|
137 | 137 | ### rewrite_by_lua_handler
|
138 | 138 |
|
139 | 139 | 这是真正被插入到 rewrite 阶段的回调方法(见 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 关于`ngx_http_lua_init` 的介绍)
|
140 | 140 |
|
| 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 | +
|
141 | 299 | ```c
|
142 | 300 | ngx_int_t
|
143 | 301 | ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)
|
|
0 commit comments