Line data Source code
1 :
2 : /*
3 : * Copyright (C) Xiaozhe Wang (chaoslawful)
4 : * Copyright (C) Yichun Zhang (agentzh)
5 : */
6 :
7 :
8 : #ifndef DDEBUG
9 : #define DDEBUG 0
10 : #endif
11 : #include "ddebug.h"
12 :
13 :
14 : #include "ngx_http_lua_variable.h"
15 : #include "ngx_http_lua_util.h"
16 :
17 :
18 : static int ngx_http_lua_var_get(lua_State *L);
19 : static int ngx_http_lua_var_set(lua_State *L);
20 :
21 :
22 : void
23 18 : ngx_http_lua_inject_variable_api(lua_State *L)
24 : {
25 : /* {{{ register reference maps */
26 18 : lua_newtable(L); /* ngx.var */
27 :
28 18 : lua_createtable(L, 0, 2 /* nrec */); /* metatable for .var */
29 18 : lua_pushcfunction(L, ngx_http_lua_var_get);
30 18 : lua_setfield(L, -2, "__index");
31 18 : lua_pushcfunction(L, ngx_http_lua_var_set);
32 18 : lua_setfield(L, -2, "__newindex");
33 18 : lua_setmetatable(L, -2);
34 :
35 18 : lua_setfield(L, -2, "var");
36 18 : }
37 :
38 :
39 : /**
40 : * Get nginx internal variables content
41 : *
42 : * @retval Always return a string or nil on Lua stack. Return nil when failed
43 : * to get content, and actual content string when found the specified variable.
44 : * @seealso ngx_http_lua_var_set
45 : * */
46 : static int
47 0 : ngx_http_lua_var_get(lua_State *L)
48 : {
49 : ngx_http_request_t *r;
50 : u_char *p, *lowcase;
51 : size_t len;
52 : ngx_uint_t hash;
53 : ngx_str_t name;
54 : ngx_http_variable_value_t *vv;
55 :
56 : #if (NGX_PCRE)
57 : u_char *val;
58 : ngx_uint_t n;
59 : LUA_NUMBER index;
60 : int *cap;
61 : #endif
62 :
63 0 : r = ngx_http_lua_get_req(L);
64 0 : if (r == NULL) {
65 0 : return luaL_error(L, "no request object found");
66 : }
67 :
68 0 : ngx_http_lua_check_fake_request(L, r);
69 :
70 : #if (NGX_PCRE)
71 0 : if (lua_type(L, -1) == LUA_TNUMBER) {
72 : /* it is a regex capturing variable */
73 :
74 0 : index = lua_tonumber(L, -1);
75 :
76 0 : if (index <= 0) {
77 0 : lua_pushnil(L);
78 0 : return 1;
79 : }
80 :
81 0 : n = (ngx_uint_t) index * 2;
82 :
83 : dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures);
84 :
85 0 : if (r->captures == NULL
86 0 : || r->captures_data == NULL
87 0 : || n >= r->ncaptures)
88 : {
89 0 : lua_pushnil(L);
90 0 : return 1;
91 : }
92 :
93 : /* n >= 0 && n < r->ncaptures */
94 :
95 0 : cap = r->captures;
96 :
97 0 : p = r->captures_data;
98 :
99 0 : val = &p[cap[n]];
100 :
101 0 : lua_pushlstring(L, (const char *) val, (size_t) (cap[n + 1] - cap[n]));
102 :
103 0 : return 1;
104 : }
105 : #endif
106 :
107 0 : if (lua_type(L, -1) != LUA_TSTRING) {
108 0 : return luaL_error(L, "bad variable name");
109 : }
110 :
111 0 : p = (u_char *) lua_tolstring(L, -1, &len);
112 :
113 0 : lowcase = lua_newuserdata(L, len);
114 :
115 0 : hash = ngx_hash_strlow(lowcase, p, len);
116 :
117 0 : name.len = len;
118 0 : name.data = lowcase;
119 :
120 0 : vv = ngx_http_get_variable(r, &name, hash);
121 :
122 0 : if (vv == NULL || vv->not_found) {
123 0 : lua_pushnil(L);
124 0 : return 1;
125 : }
126 :
127 0 : lua_pushlstring(L, (const char *) vv->data, (size_t) vv->len);
128 0 : return 1;
129 : }
130 :
131 :
132 : /**
133 : * Set nginx internal variable content
134 : *
135 : * @retval Always return a boolean on Lua stack. Return true when variable
136 : * content was modified successfully, false otherwise.
137 : * @seealso ngx_http_lua_var_get
138 : * */
139 : static int
140 0 : ngx_http_lua_var_set(lua_State *L)
141 : {
142 : ngx_http_variable_t *v;
143 : ngx_http_variable_value_t *vv;
144 : ngx_http_core_main_conf_t *cmcf;
145 : u_char *p, *lowcase, *val;
146 : size_t len;
147 : ngx_str_t name;
148 : ngx_uint_t hash;
149 : ngx_http_request_t *r;
150 : int value_type;
151 : const char *msg;
152 :
153 0 : r = ngx_http_lua_get_req(L);
154 0 : if (r == NULL) {
155 0 : return luaL_error(L, "no request object found");
156 : }
157 :
158 0 : ngx_http_lua_check_fake_request(L, r);
159 :
160 : /* we skip the first argument that is the table */
161 :
162 : /* we read the variable name */
163 :
164 0 : if (lua_type(L, 2) != LUA_TSTRING) {
165 0 : return luaL_error(L, "bad variable name");
166 : }
167 :
168 0 : p = (u_char *) lua_tolstring(L, 2, &len);
169 :
170 0 : lowcase = lua_newuserdata(L, len + 1);
171 :
172 0 : hash = ngx_hash_strlow(lowcase, p, len);
173 0 : lowcase[len] = '\0';
174 :
175 0 : name.len = len;
176 0 : name.data = lowcase;
177 :
178 : /* we read the variable new value */
179 :
180 0 : value_type = lua_type(L, 3);
181 0 : switch (value_type) {
182 0 : case LUA_TNUMBER:
183 : case LUA_TSTRING:
184 0 : p = (u_char *) luaL_checklstring(L, 3, &len);
185 :
186 0 : val = ngx_palloc(r->pool, len);
187 0 : if (val == NULL) {
188 0 : return luaL_error(L, "memory allocation error");
189 : }
190 :
191 0 : ngx_memcpy(val, p, len);
192 :
193 0 : break;
194 :
195 0 : case LUA_TNIL:
196 : /* undef the variable */
197 :
198 0 : val = NULL;
199 0 : len = 0;
200 :
201 0 : break;
202 :
203 0 : default:
204 0 : msg = lua_pushfstring(L, "string, number, or nil expected, "
205 : "but got %s", lua_typename(L, value_type));
206 0 : return luaL_argerror(L, 1, msg);
207 : }
208 :
209 : /* we fetch the variable itself */
210 :
211 0 : cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
212 :
213 0 : v = ngx_hash_find(&cmcf->variables_hash, hash, name.data, name.len);
214 :
215 0 : if (v) {
216 0 : if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
217 0 : return luaL_error(L, "variable \"%s\" not changeable", lowcase);
218 : }
219 :
220 0 : if (v->set_handler) {
221 :
222 : dd("set variables with set_handler");
223 :
224 0 : vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
225 0 : if (vv == NULL) {
226 0 : return luaL_error(L, "no memory");
227 : }
228 :
229 0 : if (value_type == LUA_TNIL) {
230 0 : vv->valid = 0;
231 0 : vv->not_found = 1;
232 0 : vv->no_cacheable = 0;
233 0 : vv->data = NULL;
234 0 : vv->len = 0;
235 :
236 : } else {
237 0 : vv->valid = 1;
238 0 : vv->not_found = 0;
239 0 : vv->no_cacheable = 0;
240 :
241 0 : vv->data = val;
242 0 : vv->len = len;
243 : }
244 :
245 0 : v->set_handler(r, vv, v->data);
246 :
247 0 : return 0;
248 : }
249 :
250 0 : if (v->flags & NGX_HTTP_VAR_INDEXED) {
251 0 : vv = &r->variables[v->index];
252 :
253 : dd("set indexed variable");
254 :
255 0 : if (value_type == LUA_TNIL) {
256 0 : vv->valid = 0;
257 0 : vv->not_found = 1;
258 0 : vv->no_cacheable = 0;
259 :
260 0 : vv->data = NULL;
261 0 : vv->len = 0;
262 :
263 : } else {
264 0 : vv->valid = 1;
265 0 : vv->not_found = 0;
266 0 : vv->no_cacheable = 0;
267 :
268 0 : vv->data = val;
269 0 : vv->len = len;
270 : }
271 :
272 0 : return 0;
273 : }
274 :
275 0 : return luaL_error(L, "variable \"%s\" cannot be assigned a value",
276 : lowcase);
277 : }
278 :
279 : /* variable not found */
280 :
281 0 : return luaL_error(L, "variable \"%s\" not found for writing; "
282 : "maybe it is a built-in variable that is not changeable "
283 : "or you forgot to use \"set $%s '';\" "
284 : "in the config file to define it first",
285 : lowcase, lowcase);
286 : }
287 :
288 :
289 : #ifndef NGX_LUA_NO_FFI_API
290 : int
291 0 : ngx_http_lua_ffi_var_get(ngx_http_request_t *r, u_char *name_data,
292 : size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value,
293 : size_t *value_len, char **err)
294 : {
295 : ngx_uint_t hash;
296 : ngx_str_t name;
297 : ngx_http_variable_value_t *vv;
298 :
299 : #if (NGX_PCRE)
300 : u_char *p;
301 : ngx_uint_t n;
302 : int *cap;
303 : #endif
304 :
305 0 : if (r == NULL) {
306 0 : *err = "no request object found";
307 0 : return NGX_ERROR;
308 : }
309 :
310 0 : if ((r)->connection->fd == (ngx_socket_t) -1) {
311 0 : *err = "API disabled in the current context";
312 0 : return NGX_ERROR;
313 : }
314 :
315 : #if (NGX_PCRE)
316 0 : if (name_data == 0) {
317 0 : if (capture_id <= 0) {
318 0 : return NGX_DECLINED;
319 : }
320 :
321 : /* it is a regex capturing variable */
322 :
323 0 : n = (ngx_uint_t) capture_id * 2;
324 :
325 : dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures);
326 :
327 0 : if (r->captures == NULL
328 0 : || r->captures_data == NULL
329 0 : || n >= r->ncaptures)
330 : {
331 0 : return NGX_DECLINED;
332 : }
333 :
334 : /* n >= 0 && n < r->ncaptures */
335 :
336 0 : cap = r->captures;
337 0 : p = r->captures_data;
338 :
339 0 : *value = &p[cap[n]];
340 0 : *value_len = (size_t) (cap[n + 1] - cap[n]);
341 :
342 0 : return NGX_OK;
343 : }
344 : #endif
345 :
346 0 : hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);
347 :
348 0 : name.data = lowcase_buf;
349 0 : name.len = name_len;
350 :
351 : dd("variable name: %.*s", (int) name_len, lowcase_buf);
352 :
353 0 : vv = ngx_http_get_variable(r, &name, hash);
354 0 : if (vv == NULL || vv->not_found) {
355 0 : return NGX_DECLINED;
356 : }
357 :
358 0 : *value = vv->data;
359 0 : *value_len = vv->len;
360 0 : return NGX_OK;
361 : }
362 :
363 :
364 : int
365 0 : ngx_http_lua_ffi_var_set(ngx_http_request_t *r, u_char *name_data,
366 : size_t name_len, u_char *lowcase_buf, u_char *value, size_t value_len,
367 : u_char *errbuf, size_t errlen)
368 : {
369 : u_char *p;
370 : ngx_uint_t hash;
371 : ngx_http_variable_t *v;
372 : ngx_http_variable_value_t *vv;
373 : ngx_http_core_main_conf_t *cmcf;
374 :
375 0 : if (r == NULL) {
376 0 : ngx_snprintf(errbuf, errlen, "no request object found");
377 0 : return NGX_ERROR;
378 : }
379 :
380 0 : if ((r)->connection->fd == (ngx_socket_t) -1) {
381 0 : ngx_snprintf(errbuf, errlen, "API disabled in the current context");
382 0 : return NGX_ERROR;
383 : }
384 :
385 0 : hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);
386 :
387 : dd("variable name: %.*s", (int) name_len, lowcase_buf);
388 :
389 : /* we fetch the variable itself */
390 :
391 0 : cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
392 :
393 0 : v = ngx_hash_find(&cmcf->variables_hash, hash, lowcase_buf, name_len);
394 :
395 0 : if (v) {
396 0 : if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
397 : dd("variable not changeable");
398 0 : ngx_snprintf(errbuf, errlen, "variable \"%*s\" not changeable",
399 : name_len, lowcase_buf);
400 0 : return NGX_ERROR;
401 : }
402 :
403 0 : if (v->set_handler) {
404 :
405 : dd("set variables with set_handler");
406 :
407 0 : if (value != NULL && value_len) {
408 0 : vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)
409 : + value_len);
410 0 : if (vv == NULL) {
411 0 : goto nomem;
412 : }
413 :
414 0 : p = (u_char *) vv + sizeof(ngx_http_variable_value_t);
415 0 : ngx_memcpy(p, value, value_len);
416 0 : value = p;
417 :
418 : } else {
419 0 : vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
420 0 : if (vv == NULL) {
421 0 : goto nomem;
422 : }
423 : }
424 :
425 0 : if (value == NULL) {
426 0 : vv->valid = 0;
427 0 : vv->not_found = 1;
428 0 : vv->no_cacheable = 0;
429 0 : vv->data = NULL;
430 0 : vv->len = 0;
431 :
432 : } else {
433 0 : vv->valid = 1;
434 0 : vv->not_found = 0;
435 0 : vv->no_cacheable = 0;
436 :
437 0 : vv->data = value;
438 0 : vv->len = value_len;
439 : }
440 :
441 0 : v->set_handler(r, vv, v->data);
442 0 : return NGX_OK;
443 : }
444 :
445 0 : if (v->flags & NGX_HTTP_VAR_INDEXED) {
446 0 : vv = &r->variables[v->index];
447 :
448 : dd("set indexed variable");
449 :
450 0 : if (value == NULL) {
451 0 : vv->valid = 0;
452 0 : vv->not_found = 1;
453 0 : vv->no_cacheable = 0;
454 :
455 0 : vv->data = NULL;
456 0 : vv->len = 0;
457 :
458 : } else {
459 0 : p = ngx_palloc(r->pool, value_len);
460 0 : if (p == NULL) {
461 0 : goto nomem;
462 : }
463 :
464 0 : ngx_memcpy(p, value, value_len);
465 0 : value = p;
466 :
467 0 : vv->valid = 1;
468 0 : vv->not_found = 0;
469 0 : vv->no_cacheable = 0;
470 :
471 0 : vv->data = value;
472 0 : vv->len = value_len;
473 : }
474 :
475 0 : return NGX_OK;
476 : }
477 :
478 0 : ngx_snprintf(errbuf, errlen, "variable \"%*s\" cannot be assigned "
479 : "a value", name_len, lowcase_buf);
480 0 : return NGX_ERROR;
481 : }
482 :
483 : /* variable not found */
484 :
485 0 : ngx_snprintf(errbuf, errlen, "variable \"%*s\" not found for writing; "
486 : "maybe it is a built-in variable that is not changeable "
487 : "or you forgot to use \"set $%*s '';\" "
488 : "in the config file to define it first",
489 : name_len, lowcase_buf, name_len, lowcase_buf);
490 0 : return NGX_ERROR;
491 :
492 0 : nomem:
493 :
494 0 : ngx_snprintf(errbuf, errlen, "no memory");
495 0 : return NGX_ERROR;
496 : }
497 : #endif /* NGX_LUA_NO_FFI_API */
498 :
499 :
500 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|