1
/* vim:set ft=c ts=4 sw=4 et fdm=marker: */
2
/* Copyright (C) agentzh */
8
6
#include "ngx_http_lua_headers.h"
7
#include "ngx_http_lua_headers_out.h"
8
#include "ngx_http_lua_headers_in.h"
9
9
#include "ngx_http_lua_util.h"
14
static ngx_int_t ngx_http_set_header(ngx_http_request_t *r,
15
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
17
static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,
18
ngx_http_lua_header_val_t *hv, ngx_str_t *value,
19
ngx_table_elt_t **output_header, ngx_flag_t no_create);
21
static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,
22
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
24
static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,
25
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
27
static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r,
28
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
30
static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,
31
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
33
static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,
34
ngx_http_lua_header_val_t *hv, ngx_str_t *value);
37
static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = {
39
{ ngx_string("Server"),
40
offsetof(ngx_http_headers_out_t, server),
41
ngx_http_set_builtin_header },
44
offsetof(ngx_http_headers_out_t, date),
45
ngx_http_set_builtin_header },
47
{ ngx_string("Content-Encoding"),
48
offsetof(ngx_http_headers_out_t, content_encoding),
49
ngx_http_set_builtin_header },
51
{ ngx_string("Location"),
52
offsetof(ngx_http_headers_out_t, location),
53
ngx_http_set_builtin_header },
55
{ ngx_string("Refresh"),
56
offsetof(ngx_http_headers_out_t, refresh),
57
ngx_http_set_builtin_header },
59
{ ngx_string("Last-Modified"),
60
offsetof(ngx_http_headers_out_t, last_modified),
61
ngx_http_set_builtin_header },
63
{ ngx_string("Content-Range"),
64
offsetof(ngx_http_headers_out_t, content_range),
65
ngx_http_set_builtin_header },
67
{ ngx_string("Accept-Ranges"),
68
offsetof(ngx_http_headers_out_t, accept_ranges),
69
ngx_http_set_builtin_header },
71
{ ngx_string("WWW-Authenticate"),
72
offsetof(ngx_http_headers_out_t, www_authenticate),
73
ngx_http_set_builtin_header },
75
{ ngx_string("Expires"),
76
offsetof(ngx_http_headers_out_t, expires),
77
ngx_http_set_builtin_header },
79
{ ngx_string("E-Tag"),
80
offsetof(ngx_http_headers_out_t, etag),
81
ngx_http_set_builtin_header },
83
{ ngx_string("Content-Length"),
84
offsetof(ngx_http_headers_out_t, content_length),
85
ngx_http_set_content_length_header },
87
{ ngx_string("Content-Type"),
89
ngx_http_set_content_type_header },
91
{ ngx_null_string, 0, ngx_http_set_header }
95
/* request time implementation */
98
ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
101
return ngx_http_set_header_helper(r, hv, value, NULL, 0);
106
ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
107
ngx_str_t *value, ngx_table_elt_t **output_header,
108
ngx_flag_t no_create)
111
ngx_list_part_t *part;
113
ngx_flag_t matched = 0;
115
if (hv->no_override) {
119
part = &r->headers_out.headers.part;
12
static int ngx_http_lua_ngx_req_header_set_helper(lua_State *L);
13
static int ngx_http_lua_ngx_header_get(lua_State *L);
14
static int ngx_http_lua_ngx_header_set(lua_State *L);
15
static int ngx_http_lua_ngx_req_get_headers(lua_State *L);
16
static int ngx_http_lua_ngx_req_header_clear(lua_State *L);
17
static int ngx_http_lua_ngx_req_header_set(lua_State *L);
21
ngx_http_lua_ngx_req_get_headers(lua_State *L) {
22
ngx_list_part_t *part;
23
ngx_table_elt_t *header;
24
ngx_http_request_t *r;
27
if (lua_gettop(L) != 0) {
28
return luaL_error(L, "expecting 0 arguments but seen %d",
32
lua_getglobal(L, GLOBALS_SYMBOL_REQUEST);
33
r = lua_touserdata(L, -1);
37
return luaL_error(L, "no request object found");
40
lua_createtable(L, 0, 4);
42
part = &r->headers_in.headers.part;
122
45
for (i = 0; /* void */; i++) {
123
47
if (i >= part->nelts) {
124
48
if (part->next == NULL) {
127
52
part = part->next;
133
(h[i].key.len == hv->key.len
134
&& ngx_strncasecmp(h[i].key.data,
138
(h[i].key.len >= hv->key.len-1
139
&& ngx_strncasecmp(h[i].key.data,
144
if (value->len == 0) {
145
dd("clearing normal header for %.*s", (int) hv->key.len,
154
*output_header = &h[i];
166
if (no_create && value->len == 0) {
171
h = ngx_list_push(&r->headers_out.headers);
174
return NGX_HTTP_INTERNAL_SERVER_ERROR;
177
if (value->len == 0) {
186
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
187
if (h->lowcase_key == NULL) {
188
return NGX_HTTP_INTERNAL_SERVER_ERROR;
191
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
202
ngx_http_set_builtin_header(ngx_http_request_t *r,
203
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
205
ngx_table_elt_t *h, **old;
208
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
214
if (old == NULL || *old == NULL) {
215
return ngx_http_set_header_helper(r, hv, value, old, 0);
220
if (value->len == 0) {
221
dd("clearing the builtin header");
238
ngx_http_set_content_type_header(ngx_http_request_t *r,
239
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
241
r->headers_out.content_type_len = value->len;
242
r->headers_out.content_type = *value;
243
r->headers_out.content_type_hash = hv->hash;
244
r->headers_out.content_type_lowcase = NULL;
248
return ngx_http_set_header_helper(r, hv, value, NULL, 1);
253
ngx_http_set_content_length_header(ngx_http_request_t *r,
254
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
258
if (value->len == 0) {
259
return ngx_http_clear_content_length_header(r, hv, value);
262
len = ngx_atosz(value->data, value->len);
263
if (len == NGX_ERROR) {
267
r->headers_out.content_length_n = len;
269
return ngx_http_set_builtin_header(r, hv, value);
274
ngx_http_clear_content_length_header(ngx_http_request_t *r,
275
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
277
r->headers_out.content_length_n = -1;
279
return ngx_http_clear_builtin_header(r, hv, value);
284
ngx_http_clear_builtin_header(ngx_http_request_t *r,
285
ngx_http_lua_header_val_t *hv, ngx_str_t *value)
289
return ngx_http_set_builtin_header(r, hv, value);
294
ngx_http_lua_set_header(ngx_http_request_t *r, ngx_str_t key, ngx_str_t value,
297
ngx_http_lua_header_val_t hv;
298
ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers;
302
dd("set header value: %.*s", (int) value.len, value.data);
308
hv.no_override = ! override;
310
for (i = 0; handlers[i].name.len; i++) {
311
if (hv.key.len != handlers[i].name.len
312
|| ngx_strncasecmp(hv.key.data, handlers[i].name.data,
313
handlers[i].name.len) != 0)
315
dd("hv key comparison: %s <> %s", handlers[i].name.data,
321
dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data);
323
hv.offset = handlers[i].offset;
324
hv.handler = handlers[i].handler;
329
if (handlers[i].name.len == 0 && handlers[i].handler) {
330
hv.offset = handlers[i].offset;
331
hv.handler = handlers[i].handler;
334
return hv.handler(r, &hv, &value);
57
lua_pushlstring(L, (char *) header[i].key.data, header[i].key.len);
58
/* stack: table key */
60
lua_pushlstring(L, (char *) header[i].value.data,
61
header[i].value.len); /* stack: table key value */
63
ngx_http_lua_set_multi_value_table(L, -3);
65
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
66
"lua request header: \"%V: %V\"",
67
&header[i].key, &header[i].value);
75
ngx_http_lua_ngx_header_get(lua_State *L)
77
ngx_http_request_t *r;
83
lua_getglobal(L, GLOBALS_SYMBOL_REQUEST);
84
r = lua_touserdata(L, -1);
88
return luaL_error(L, "no request object found");
91
/* we skip the first argument that is the table */
92
p = (u_char *) luaL_checklstring(L, 2, &len);
94
dd("key: %.*s, len %d", (int) len, p, (int) len);
96
/* replace "_" with "-" */
97
for (i = 0; i < len; i++) {
103
key.data = ngx_palloc(r->pool, len + 1);
104
if (key.data == NULL) {
105
return luaL_error(L, "out of memory");
108
ngx_memcpy(key.data, p, len);
110
key.data[len] = '\0';
114
return ngx_http_lua_get_output_header(L, r, &key);
119
ngx_http_lua_ngx_header_set(lua_State *L)
121
ngx_http_request_t *r;
127
ngx_http_lua_ctx_t *ctx;
131
lua_getglobal(L, GLOBALS_SYMBOL_REQUEST);
132
r = lua_touserdata(L, -1);
136
return luaL_error(L, "no request object found");
139
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
141
if (ctx->headers_sent) {
142
return luaL_error(L, "attempt to set ngx.header.HEADER after "
143
"sending out response headers");
146
/* we skip the first argument that is the table */
147
p = (u_char *) luaL_checklstring(L, 2, &len);
149
dd("key: %.*s, len %d", (int) len, p, (int) len);
151
/* replace "_" with "-" */
152
for (i = 0; i < len; i++) {
158
key.data = ngx_palloc(r->pool, len + 1);
159
if (key.data == NULL) {
160
return luaL_error(L, "out of memory");
163
ngx_memcpy(key.data, p, len);
165
key.data[len] = '\0';
169
if (! ctx->headers_set) {
170
rc = ngx_http_set_content_type(r);
173
"failed to set default content type: %d",
177
ctx->headers_set = 1;
180
if (lua_type(L, 3) == LUA_TNIL) {
184
} else if (lua_type(L, 3) == LUA_TTABLE) {
191
for (i = 1; i <= n; i++) {
192
dd("header value table index %d", (int) i);
194
lua_rawgeti(L, 3, i);
195
p = (u_char *) luaL_checklstring(L, -1, &len);
197
value.data = ngx_palloc(r->pool, len);
198
if (value.data == NULL) {
199
return luaL_error(L, "out of memory");
202
ngx_memcpy(value.data, p, len);
205
rc = ngx_http_lua_set_output_header(r, key, value,
206
i == 1 /* override */);
210
"failed to set header %s (error: %d)",
219
p = (u_char *) luaL_checklstring(L, 3, &len);
220
value.data = ngx_palloc(r->pool, len);
221
if (value.data == NULL) {
222
return luaL_error(L, "out of memory");
225
ngx_memcpy(value.data, p, len);
229
dd("key: %.*s, value: %.*s",
230
(int) key.len, key.data, (int) value.len, value.data);
232
rc = ngx_http_lua_set_output_header(r, key, value, 1 /* override */);
235
return luaL_error(L, "failed to set header %s (error: %d)",
244
ngx_http_lua_ngx_req_header_clear(lua_State *L)
246
if (lua_gettop(L) != 1) {
247
return luaL_error(L, "expecting one arguments, but seen %d",
253
return ngx_http_lua_ngx_req_header_set_helper(L);
258
ngx_http_lua_ngx_req_header_set(lua_State *L)
260
if (lua_gettop(L) != 2) {
261
return luaL_error(L, "expecting two arguments, but seen %d",
265
return ngx_http_lua_ngx_req_header_set_helper(L);
270
ngx_http_lua_ngx_req_header_set_helper(lua_State *L)
272
ngx_http_request_t *r;
281
lua_getglobal(L, GLOBALS_SYMBOL_REQUEST);
282
r = lua_touserdata(L, -1);
286
return luaL_error(L, "no request object found");
289
p = (u_char *) luaL_checklstring(L, 1, &len);
291
dd("key: %.*s, len %d", (int) len, p, (int) len);
293
/* replace "_" with "-" */
294
for (i = 0; i < len; i++) {
300
key.data = ngx_palloc(r->pool, len + 1);
301
if (key.data == NULL) {
302
return luaL_error(L, "out of memory");
305
ngx_memcpy(key.data, p, len);
307
key.data[len] = '\0';
311
if (lua_type(L, 2) == LUA_TNIL) {
315
} else if (lua_type(L, 2) == LUA_TTABLE) {
322
for (i = 1; i <= n; i++) {
323
dd("header value table index %d", (int) i);
325
lua_rawgeti(L, 2, i);
326
p = (u_char *) luaL_checklstring(L, -1, &len);
328
value.data = ngx_palloc(r->pool, len);
329
if (value.data == NULL) {
330
return luaL_error(L, "out of memory");
333
ngx_memcpy(value.data, p, len);
336
rc = ngx_http_lua_set_input_header(r, key, value,
337
i == 1 /* override */);
341
"failed to set header %s (error: %d)",
350
p = (u_char *) luaL_checklstring(L, 2, &len);
351
value.data = ngx_palloc(r->pool, len);
352
if (value.data == NULL) {
353
return luaL_error(L, "out of memory");
356
ngx_memcpy(value.data, p, len);
360
dd("key: %.*s, value: %.*s",
361
(int) key.len, key.data, (int) value.len, value.data);
363
rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */);
366
return luaL_error(L, "failed to set header %s (error: %d)",
375
ngx_http_lua_inject_resp_header_api(lua_State *L)
377
lua_newtable(L); /* .header */
379
lua_createtable(L, 0, 2); /* metatable for .header */
380
lua_pushcfunction(L, ngx_http_lua_ngx_header_get);
381
lua_setfield(L, -2, "__index");
382
lua_pushcfunction(L, ngx_http_lua_ngx_header_set);
383
lua_setfield(L, -2, "__newindex");
384
lua_setmetatable(L, -2);
386
lua_setfield(L, -2, "header");
391
ngx_http_lua_inject_req_header_api(lua_State *L)
393
lua_pushcfunction(L, ngx_http_lua_ngx_req_header_clear);
394
lua_setfield(L, -2, "clear_header");
396
lua_pushcfunction(L, ngx_http_lua_ngx_req_header_set);
397
lua_setfield(L, -2, "set_header");
399
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_headers);
400
lua_setfield(L, -2, "get_headers");