前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >openresty是如何通过lua代码获取nginx内请求数据的

openresty是如何通过lua代码获取nginx内请求数据的

原创
作者头像
stan1ey
发布2022-06-05 00:17:54
2.3K0
发布2022-06-05 00:17:54
举报
文章被收录于专栏:安全开发记录安全开发记录

nginx中处理请求是围绕ngx_http_request_t结构体进行的。

ngx_http_request_t结构体包含了当前http请求的所有数据。

ngx_http_lua_module与nginx进行交互,主要围绕这个结构体实现的,lua代码获取nginx内部http请求数据,然后进行处理。

lua_module为此在建立新的协程,将ngx_http_request_t的指针保存在了lua_State的全局变量中。

经过 ngx_http_lua_set_req 将请求与协程关联。

static ngx_inline void
ngx_http_lua_set_req(lua_State *L, ngx_http_request_t *r)
{
#ifdef OPENRESTY_LUAJIT
    lua_setexdata(L, (void *) r);
#else
    lua_pushlightuserdata(L, r);
    lua_setglobal(L, ngx_http_lua_req_key);
#endif
}

经过ngx_http_lua_get_req获取请求的ngx_http_request_t结构体,从结构体中把想要获取的http数据返回。

// ngx_http_lua_util.h文件
// 经过 ngx_http_lua_get_req 从lua_State中获取协程关联的请求
 
static ngx_inline ngx_http_request_t *
ngx_http_lua_get_req(lua_State *L)
{
    ngx_http_request_t    *r;
 
    lua_getglobal(L, ngx_http_lua_req_key);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);
 
    return r;
}

lua获取nginx请求方法

function ngx.req.get_method()
    local r = get_request()
    if not r then
        error("no request found")
    end
 
    do
        local id = C.ngx_http_lua_ffi_req_get_method(r)
        if id == FFI_BAD_CONTEXT then
            error("API disabled in the current context", 2)
        end
 
        local method = methods[id]
        if method then
            return method
        end
    end
 
    local sizep = get_size_ptr()
    local rc = C.ngx_http_lua_ffi_req_get_method_name(r, namep, sizep)
    if rc ~= 0 then
        return nil
    end
 
    return ffi_str(namep[0], sizep[0])
end
ngx_http_lua_req_method.c

int
ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r)
{
    if (r->connection->fd == (ngx_socket_t) -1) {
        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
    }
 
    return r->method;
}
 
int
ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r, u_char **name,
    size_t *len)
{
    if (r->connection->fd == (ngx_socket_t) -1) {
        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
    }
 
    *name = r->method_name.data;
    *len = r->method_name.len;
 
    return NGX_OK;
}

lua获取nginx请求参数

function ngx.req.get_uri_args(max_args)
    -- ...
    -- 获取请求参数个数
    local n = C.ngx_http_lua_ffi_req_get_uri_args_count(r, max_args, truncated)
 
    -- 获取请求参数长度
    local args_len = C.ngx_http_lua_ffi_req_get_querystring_len(r)
 
    -- 用于存放请求参数的数据结构
    local strbuf = get_string_buf(args_len + n * table_elt_size)
    local kvbuf = ffi_cast(table_elt_type, strbuf + args_len)
 
    -- nargs为请求参数的个数
    -- kvbuf为table类型kv结构 用于保存请求参数/index.html?aa=11&bb=22&cc=33
    local nargs = C.ngx_http_lua_ffi_req_get_uri_args(r, strbuf, kvbuf, n)
 
    -- 最终请求参数保存到 args 表中返回
    local args = new_tab(0, nargs)
    for i = 0, nargs - 1 do
        local arg = kvbuf[i]
 
        local key = arg.key
        key = ffi_str(key.data, key.len)
 
        local value = arg.value
 
        -- value 为arg.value.data
        -- 最终保存到args table中
        args[key] = value
        -- 如果 参数keys值有重复
        args[key] = {existvalue, value}
    end
 
    if truncated[0] ~= 0 then
        return args, "truncated"
    end
 
    return args
end

ngx_http_lua_args.c

int
ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r, int max,
    int *truncated)
{
    int                      count;
    u_char                  *p, *last;
 
    if (r->connection->fd == (ngx_socket_t) -1) {
        return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
    }
 
    *truncated = 0;
 
    if (max < 0) {
        max = NGX_HTTP_LUA_MAX_ARGS;
    }
 
    last = r->args.data + r->args.len;
    count = 0;
 
    for (p = r->args.data; p != last; p++) {
        if (*p == '&') {
            if (count == 0) {
                count += 2;
 
            } else {
                count++;
            }
        }
    }
 
    if (count) {
        if (max > 0 && count > max) {
            count = max;
            *truncated = 1;
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "lua hit query args limit %d", max);
        }
 
        return count;
    }
 
    if (r->args.len) {
        return 1;
    }
 
    return 0;
}

ngx_http_lua_ffi_req_get_querystring_len函数

ngx_http_lua_ffi_req_get_uri_args函数

size_t
ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r)
{
    return r->args.len;
}
 
int
ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r, u_char *buf,
    ngx_http_lua_ffi_table_elt_t *out, int count)
{
    int                          i, parsing_value = 0;
    u_char                      *last, *p, *q;
    u_char                      *src, *dst;
 
    if (count <= 0) {
        return NGX_OK;
    }
 
    ngx_memcpy(buf, r->args.data, r->args.len);
 
    i = 0;
    last = buf + r->args.len;
    p = buf;
    q = p;
 
    while (p != last) {
        if (*p == '=' && !parsing_value) {
            /* key data is between p and q */
 
            src = q; dst = q;
 
            ngx_http_lua_unescape_uri(&dst, &src, p - q,
                                      NGX_UNESCAPE_URI_COMPONENT);
 
            dd("saving key %.*s", (int) (dst - q), q);
 
            out[i].key.data = q;
            out[i].key.len = (int) (dst - q);
 
            /* skip the current '=' char */
            p++;
 
            q = p;
            parsing_value = 1;
 
        } else if (*p == '&') {
            /* reached the end of a key or a value, just save it */
            src = q; dst = q;
 
            ngx_http_lua_unescape_uri(&dst, &src, p - q,
                                      NGX_UNESCAPE_URI_COMPONENT);
 
            dd("pushing key or value %.*s", (int) (dst - q), q);
 
            if (parsing_value) {
                /* end of the current pair's value */
                parsing_value = 0;
 
                if (out[i].key.len) {
                    out[i].value.data = q;
                    out[i].value.len = (int) (dst - q);
                    i++;
                }
 
            } else {
                /* the current parsing pair takes no value,
                 * just push the value "true" */
                dd("pushing boolean true");
 
                if (dst - q) {
                    out[i].key.data = q;
                    out[i].key.len = (int) (dst - q);
                    out[i].value.len = -1;
                    i++;
                }
            }
 
            if (i == count) {
                return i;
            }
 
            /* skip the current '&' char */
            p++;
 
            q = p;
 
        } else {
            p++;
        }
    }
 
    if (p != q || parsing_value) {
        src = q; dst = q;
 
        ngx_http_lua_unescape_uri(&dst, &src, p - q,
                                  NGX_UNESCAPE_URI_COMPONENT);
 
        dd("pushing key or value %.*s", (int) (dst - q), q);
 
        if (parsing_value) {
            if (out[i].key.len) {
                out[i].value.data = q;
                out[i].value.len = (int) (dst - q);
                i++;
            }
 
        } else {
            if (dst - q) {
                out[i].key.data = q;
                out[i].key.len = (int) (dst - q);
                out[i].value.len = (int) -1;
                i++;
            }
        }
    }
 
    return i;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档