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 <nginx.h>
15 : #include "ngx_http_lua_headers_out.h"
16 : #include "ngx_http_lua_util.h"
17 : #include <ctype.h>
18 :
19 :
20 : static ngx_int_t ngx_http_set_header(ngx_http_request_t *r,
21 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
22 : static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,
23 : ngx_http_lua_header_val_t *hv, ngx_str_t *value,
24 : ngx_table_elt_t **output_header, unsigned no_create);
25 : static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,
26 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
27 : static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
28 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
29 : static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,
30 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
31 : static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,
32 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
33 : static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r,
34 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
35 : static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,
36 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
37 : static ngx_int_t ngx_http_clear_last_modified_header(ngx_http_request_t *r,
38 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
39 : static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,
40 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
41 : static ngx_int_t ngx_http_set_location_header(ngx_http_request_t *r,
42 : ngx_http_lua_header_val_t *hv, ngx_str_t *value);
43 :
44 :
45 : static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = {
46 :
47 : { ngx_string("Server"),
48 : offsetof(ngx_http_headers_out_t, server),
49 : ngx_http_set_builtin_header },
50 :
51 : { ngx_string("Date"),
52 : offsetof(ngx_http_headers_out_t, date),
53 : ngx_http_set_builtin_header },
54 :
55 : #if 1
56 : { ngx_string("Content-Encoding"),
57 : offsetof(ngx_http_headers_out_t, content_encoding),
58 : ngx_http_set_builtin_header },
59 : #endif
60 :
61 : { ngx_string("Location"),
62 : offsetof(ngx_http_headers_out_t, location),
63 : ngx_http_set_location_header },
64 :
65 : { ngx_string("Refresh"),
66 : offsetof(ngx_http_headers_out_t, refresh),
67 : ngx_http_set_builtin_header },
68 :
69 : { ngx_string("Last-Modified"),
70 : offsetof(ngx_http_headers_out_t, last_modified),
71 : ngx_http_set_last_modified_header },
72 :
73 : { ngx_string("Content-Range"),
74 : offsetof(ngx_http_headers_out_t, content_range),
75 : ngx_http_set_builtin_header },
76 :
77 : { ngx_string("Accept-Ranges"),
78 : offsetof(ngx_http_headers_out_t, accept_ranges),
79 : ngx_http_set_builtin_header },
80 :
81 : { ngx_string("WWW-Authenticate"),
82 : offsetof(ngx_http_headers_out_t, www_authenticate),
83 : ngx_http_set_builtin_header },
84 :
85 : { ngx_string("Expires"),
86 : offsetof(ngx_http_headers_out_t, expires),
87 : ngx_http_set_builtin_header },
88 :
89 : { ngx_string("E-Tag"),
90 : offsetof(ngx_http_headers_out_t, etag),
91 : ngx_http_set_builtin_header },
92 :
93 : { ngx_string("ETag"),
94 : offsetof(ngx_http_headers_out_t, etag),
95 : ngx_http_set_builtin_header },
96 :
97 : { ngx_string("Content-Length"),
98 : offsetof(ngx_http_headers_out_t, content_length),
99 : ngx_http_set_content_length_header },
100 :
101 : { ngx_string("Content-Type"),
102 : offsetof(ngx_http_headers_out_t, content_type),
103 : ngx_http_set_content_type_header },
104 :
105 : { ngx_string("Cache-Control"),
106 : offsetof(ngx_http_headers_out_t, cache_control),
107 : ngx_http_set_builtin_multi_header },
108 :
109 : { ngx_null_string, 0, ngx_http_set_header }
110 : };
111 :
112 :
113 : /* request time implementation */
114 :
115 : static ngx_int_t
116 0 : ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
117 : ngx_str_t *value)
118 : {
119 0 : return ngx_http_set_header_helper(r, hv, value, NULL, 0);
120 : }
121 :
122 :
123 : static ngx_int_t
124 0 : ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv,
125 : ngx_str_t *value, ngx_table_elt_t **output_header,
126 : unsigned no_create)
127 : {
128 : ngx_table_elt_t *h;
129 : ngx_list_part_t *part;
130 : ngx_uint_t i;
131 0 : unsigned matched = 0;
132 :
133 0 : if (hv->no_override) {
134 0 : goto new_header;
135 : }
136 :
137 : #if 1
138 0 : if (r->headers_out.location
139 0 : && r->headers_out.location->value.len
140 0 : && r->headers_out.location->value.data[0] == '/')
141 : {
142 : /* XXX ngx_http_core_find_config_phase, for example,
143 : * may not initialize the "key" and "hash" fields
144 : * for a nasty optimization purpose, and
145 : * we have to work-around it here */
146 :
147 0 : r->headers_out.location->hash = ngx_http_lua_location_hash;
148 0 : ngx_str_set(&r->headers_out.location->key, "Location");
149 : }
150 : #endif
151 :
152 0 : part = &r->headers_out.headers.part;
153 0 : h = part->elts;
154 :
155 0 : for (i = 0; /* void */; i++) {
156 :
157 0 : if (i >= part->nelts) {
158 0 : if (part->next == NULL) {
159 0 : break;
160 : }
161 :
162 0 : part = part->next;
163 0 : h = part->elts;
164 0 : i = 0;
165 : }
166 :
167 0 : if (h[i].hash != 0
168 0 : && h[i].key.len == hv->key.len
169 0 : && ngx_strncasecmp(hv->key.data, h[i].key.data, h[i].key.len) == 0)
170 : {
171 : dd("found out header %.*s", (int) h[i].key.len, h[i].key.data);
172 :
173 0 : if (value->len == 0 || matched) {
174 : dd("clearing normal header for %.*s", (int) hv->key.len,
175 : hv->key.data);
176 :
177 0 : h[i].value.len = 0;
178 0 : h[i].hash = 0;
179 :
180 : } else {
181 : dd("setting header to value %.*s", (int) value->len,
182 : value->data);
183 :
184 0 : h[i].value = *value;
185 0 : h[i].hash = hv->hash;
186 : }
187 :
188 0 : if (output_header) {
189 0 : *output_header = &h[i];
190 : }
191 :
192 : /* return NGX_OK; */
193 0 : matched = 1;
194 : }
195 : }
196 :
197 0 : if (matched){
198 0 : return NGX_OK;
199 : }
200 :
201 0 : if (no_create && value->len == 0) {
202 0 : return NGX_OK;
203 : }
204 :
205 0 : new_header:
206 :
207 : /* XXX we still need to create header slot even if the value
208 : * is empty because some builtin headers like Last-Modified
209 : * relies on this to get cleared */
210 :
211 0 : h = ngx_list_push(&r->headers_out.headers);
212 :
213 0 : if (h == NULL) {
214 0 : return NGX_ERROR;
215 : }
216 :
217 0 : if (value->len == 0) {
218 0 : h->hash = 0;
219 :
220 : } else {
221 0 : h->hash = hv->hash;
222 : }
223 :
224 0 : h->key = hv->key;
225 0 : h->value = *value;
226 :
227 0 : h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
228 0 : if (h->lowcase_key == NULL) {
229 0 : return NGX_ERROR;
230 : }
231 :
232 0 : ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
233 :
234 0 : if (output_header) {
235 0 : *output_header = h;
236 : }
237 :
238 0 : return NGX_OK;
239 : }
240 :
241 :
242 : static ngx_int_t
243 0 : ngx_http_set_location_header(ngx_http_request_t *r,
244 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
245 : {
246 : ngx_int_t rc;
247 : ngx_table_elt_t *h;
248 :
249 0 : rc = ngx_http_set_builtin_header(r, hv, value);
250 0 : if (rc != NGX_OK) {
251 0 : return rc;
252 : }
253 :
254 : /*
255 : * we do not set r->headers_out.location here to avoid the handling
256 : * the local redirects without a host name by ngx_http_header_filter()
257 : */
258 :
259 0 : h = r->headers_out.location;
260 0 : if (h && h->value.len && h->value.data[0] == '/') {
261 0 : r->headers_out.location = NULL;
262 : }
263 :
264 0 : return NGX_OK;
265 : }
266 :
267 :
268 : static ngx_int_t
269 0 : ngx_http_set_builtin_header(ngx_http_request_t *r,
270 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
271 : {
272 : ngx_table_elt_t *h, **old;
273 :
274 0 : if (hv->offset) {
275 0 : old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
276 :
277 : } else {
278 0 : old = NULL;
279 : }
280 :
281 0 : if (old == NULL || *old == NULL) {
282 0 : return ngx_http_set_header_helper(r, hv, value, old, 0);
283 : }
284 :
285 0 : h = *old;
286 :
287 0 : if (value->len == 0) {
288 : dd("clearing the builtin header");
289 :
290 0 : h->hash = 0;
291 0 : h->value = *value;
292 :
293 0 : return NGX_OK;
294 : }
295 :
296 0 : h->hash = hv->hash;
297 0 : h->key = hv->key;
298 0 : h->value = *value;
299 :
300 0 : return NGX_OK;
301 : }
302 :
303 :
304 : static ngx_int_t
305 0 : ngx_http_set_builtin_multi_header(ngx_http_request_t *r,
306 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
307 : {
308 : ngx_array_t *pa;
309 : ngx_table_elt_t *ho, **ph;
310 : ngx_uint_t i;
311 :
312 0 : pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);
313 :
314 0 : if (pa->elts == NULL) {
315 0 : if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *))
316 : != NGX_OK)
317 : {
318 0 : return NGX_ERROR;
319 : }
320 : }
321 :
322 0 : if (hv->no_override) {
323 0 : ph = pa->elts;
324 0 : for (i = 0; i < pa->nelts; i++) {
325 0 : if (!ph[i]->hash) {
326 0 : ph[i]->value = *value;
327 0 : ph[i]->hash = hv->hash;
328 0 : return NGX_OK;
329 : }
330 : }
331 :
332 0 : goto create;
333 : }
334 :
335 : /* override old values (if any) */
336 :
337 0 : if (pa->nelts > 0) {
338 0 : ph = pa->elts;
339 0 : for (i = 1; i < pa->nelts; i++) {
340 0 : ph[i]->hash = 0;
341 0 : ph[i]->value.len = 0;
342 : }
343 :
344 0 : ph[0]->value = *value;
345 :
346 0 : if (value->len == 0) {
347 0 : ph[0]->hash = 0;
348 :
349 : } else {
350 0 : ph[0]->hash = hv->hash;
351 : }
352 :
353 0 : return NGX_OK;
354 : }
355 :
356 0 : create:
357 :
358 0 : ph = ngx_array_push(pa);
359 0 : if (ph == NULL) {
360 0 : return NGX_ERROR;
361 : }
362 :
363 0 : ho = ngx_list_push(&r->headers_out.headers);
364 0 : if (ho == NULL) {
365 0 : return NGX_ERROR;
366 : }
367 :
368 0 : ho->value = *value;
369 :
370 0 : if (value->len == 0) {
371 0 : ho->hash = 0;
372 :
373 : } else {
374 0 : ho->hash = hv->hash;
375 : }
376 :
377 0 : ho->key = hv->key;
378 0 : *ph = ho;
379 :
380 0 : return NGX_OK;
381 : }
382 :
383 :
384 : static ngx_int_t
385 0 : ngx_http_set_content_type_header(ngx_http_request_t *r,
386 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
387 : {
388 : ngx_uint_t i;
389 :
390 0 : r->headers_out.content_type_len = value->len;
391 :
392 : #if 1
393 0 : for (i = 0; i < value->len; i++) {
394 0 : if (value->data[i] == ';') {
395 0 : r->headers_out.content_type_len = i;
396 0 : break;
397 : }
398 : }
399 : #endif
400 :
401 0 : r->headers_out.content_type = *value;
402 0 : r->headers_out.content_type_hash = hv->hash;
403 0 : r->headers_out.content_type_lowcase = NULL;
404 :
405 0 : value->len = 0;
406 :
407 0 : return ngx_http_set_header_helper(r, hv, value, NULL, 1);
408 : }
409 :
410 :
411 0 : static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,
412 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
413 : {
414 0 : if (value->len == 0) {
415 0 : return ngx_http_clear_last_modified_header(r, hv, value);
416 : }
417 :
418 0 : r->headers_out.last_modified_time = ngx_http_parse_time(value->data,
419 : value->len);
420 :
421 : dd("last modified time: %d", (int) r->headers_out.last_modified_time);
422 :
423 0 : return ngx_http_set_builtin_header(r, hv, value);
424 : }
425 :
426 :
427 : static ngx_int_t
428 0 : ngx_http_clear_last_modified_header(ngx_http_request_t *r,
429 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
430 : {
431 0 : r->headers_out.last_modified_time = -1;
432 :
433 0 : return ngx_http_clear_builtin_header(r, hv, value);
434 : }
435 :
436 :
437 : static ngx_int_t
438 0 : ngx_http_set_content_length_header(ngx_http_request_t *r,
439 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
440 : {
441 : off_t len;
442 :
443 0 : if (value->len == 0) {
444 0 : return ngx_http_clear_content_length_header(r, hv, value);
445 : }
446 :
447 0 : len = ngx_atosz(value->data, value->len);
448 0 : if (len == NGX_ERROR) {
449 0 : return NGX_ERROR;
450 : }
451 :
452 0 : r->headers_out.content_length_n = len;
453 :
454 0 : return ngx_http_set_builtin_header(r, hv, value);
455 : }
456 :
457 :
458 : static ngx_int_t
459 0 : ngx_http_clear_content_length_header(ngx_http_request_t *r,
460 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
461 : {
462 0 : r->headers_out.content_length_n = -1;
463 :
464 0 : return ngx_http_clear_builtin_header(r, hv, value);
465 : }
466 :
467 :
468 : static ngx_int_t
469 0 : ngx_http_clear_builtin_header(ngx_http_request_t *r,
470 : ngx_http_lua_header_val_t *hv, ngx_str_t *value)
471 : {
472 0 : value->len = 0;
473 :
474 0 : return ngx_http_set_builtin_header(r, hv, value);
475 : }
476 :
477 :
478 : ngx_int_t
479 0 : ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_str_t key,
480 : ngx_str_t value, unsigned override)
481 : {
482 : ngx_http_lua_header_val_t hv;
483 0 : ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers;
484 : ngx_uint_t i;
485 :
486 : dd("set header value: %.*s", (int) value.len, value.data);
487 :
488 0 : hv.hash = ngx_hash_key_lc(key.data, key.len);
489 0 : hv.key = key;
490 :
491 0 : hv.offset = 0;
492 0 : hv.no_override = !override;
493 0 : hv.handler = NULL;
494 :
495 0 : for (i = 0; handlers[i].name.len; i++) {
496 0 : if (hv.key.len != handlers[i].name.len
497 0 : || ngx_strncasecmp(hv.key.data, handlers[i].name.data,
498 0 : handlers[i].name.len) != 0)
499 : {
500 : dd("hv key comparison: %s <> %s", handlers[i].name.data,
501 : hv.key.data);
502 :
503 0 : continue;
504 : }
505 :
506 : dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data);
507 :
508 0 : hv.offset = handlers[i].offset;
509 0 : hv.handler = handlers[i].handler;
510 :
511 0 : break;
512 : }
513 :
514 0 : if (handlers[i].name.len == 0 && handlers[i].handler) {
515 0 : hv.offset = handlers[i].offset;
516 0 : hv.handler = handlers[i].handler;
517 : }
518 :
519 : #if 1
520 0 : if (hv.handler == NULL) {
521 0 : return NGX_ERROR;
522 : }
523 : #endif
524 :
525 0 : return hv.handler(r, &hv, &value);
526 : }
527 :
528 :
529 : int
530 0 : ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r,
531 : ngx_str_t *key)
532 : {
533 : ngx_table_elt_t *h;
534 : ngx_list_part_t *part;
535 : ngx_uint_t i;
536 : unsigned found;
537 :
538 : dd("looking for response header \"%.*s\"", (int) key->len, key->data);
539 :
540 0 : switch (key->len) {
541 0 : case 14:
542 0 : if (r->headers_out.content_length == NULL
543 0 : && r->headers_out.content_length_n >= 0
544 0 : && ngx_strncasecmp(key->data, (u_char *) "Content-Length", 14) == 0)
545 : {
546 0 : lua_pushinteger(L, (lua_Integer) r->headers_out.content_length_n);
547 0 : return 1;
548 : }
549 :
550 0 : break;
551 :
552 0 : case 12:
553 0 : if (r->headers_out.content_type.len
554 0 : && ngx_strncasecmp(key->data, (u_char *) "Content-Type", 12) == 0)
555 : {
556 0 : lua_pushlstring(L, (char *) r->headers_out.content_type.data,
557 : r->headers_out.content_type.len);
558 0 : return 1;
559 : }
560 :
561 0 : break;
562 :
563 0 : default:
564 0 : break;
565 : }
566 :
567 : dd("not a built-in output header");
568 :
569 0 : found = 0;
570 :
571 : #if 1
572 0 : if (r->headers_out.location
573 0 : && r->headers_out.location->value.len
574 0 : && r->headers_out.location->value.data[0] == '/')
575 : {
576 : /* XXX ngx_http_core_find_config_phase, for example,
577 : * may not initialize the "key" and "hash" fields
578 : * for a nasty optimization purpose, and
579 : * we have to work-around it here */
580 :
581 0 : r->headers_out.location->hash = ngx_http_lua_location_hash;
582 0 : ngx_str_set(&r->headers_out.location->key, "Location");
583 : }
584 : #endif
585 :
586 0 : part = &r->headers_out.headers.part;
587 0 : h = part->elts;
588 :
589 0 : for (i = 0; /* void */; i++) {
590 :
591 0 : if (i >= part->nelts) {
592 0 : if (part->next == NULL) {
593 0 : break;
594 : }
595 :
596 0 : part = part->next;
597 0 : h = part->elts;
598 0 : i = 0;
599 : }
600 :
601 0 : if (h[i].hash == 0) {
602 0 : continue;
603 : }
604 :
605 0 : if (h[i].hash != 0
606 0 : && h[i].key.len == key->len
607 0 : && ngx_strncasecmp(key->data, h[i].key.data, h[i].key.len) == 0)
608 : {
609 0 : if (!found) {
610 0 : found = 1;
611 :
612 0 : lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);
613 0 : continue;
614 : }
615 :
616 0 : if (found == 1) {
617 0 : lua_createtable(L, 4 /* narr */, 0);
618 0 : lua_insert(L, -2);
619 0 : lua_rawseti(L, -2, found);
620 : }
621 :
622 0 : found++;
623 :
624 0 : lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len);
625 0 : lua_rawseti(L, -2, found);
626 : }
627 : }
628 :
629 0 : if (found) {
630 0 : return 1;
631 : }
632 :
633 0 : lua_pushnil(L);
634 0 : return 1;
635 : }
636 :
637 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|