1
/* Copyright (C) agentzh */
7
#include "ngx_http_headers_more_headers_out.h"
8
#include "ngx_http_headers_more_util.h"
14
ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd,
15
void *conf, ngx_http_headers_more_opcode_t opcode);
19
static ngx_flag_t ngx_http_headers_more_check_type(ngx_http_request_t *r,
22
static ngx_flag_t ngx_http_headers_more_check_status(ngx_http_request_t *r,
23
ngx_array_t *statuses);
25
static ngx_int_t ngx_http_set_header(ngx_http_request_t *r,
26
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
28
static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r,
29
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value,
30
ngx_table_elt_t **output_header, ngx_flag_t no_create);
32
static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r,
33
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
35
static ngx_int_t ngx_http_set_accept_ranges_header(ngx_http_request_t *r,
36
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
38
static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r,
39
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
41
static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r,
42
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
44
static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r,
45
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
47
static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r,
48
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value);
51
static ngx_http_headers_more_set_header_t ngx_http_headers_more_set_handlers[]
54
{ ngx_string("Server"),
55
offsetof(ngx_http_headers_out_t, server),
56
ngx_http_set_builtin_header },
59
offsetof(ngx_http_headers_out_t, date),
60
ngx_http_set_builtin_header },
62
{ ngx_string("Content-Encoding"),
63
offsetof(ngx_http_headers_out_t, content_encoding),
64
ngx_http_set_builtin_header },
66
{ ngx_string("Location"),
67
offsetof(ngx_http_headers_out_t, location),
68
ngx_http_set_builtin_header },
70
{ ngx_string("Refresh"),
71
offsetof(ngx_http_headers_out_t, refresh),
72
ngx_http_set_builtin_header },
74
{ ngx_string("Last-Modified"),
75
offsetof(ngx_http_headers_out_t, last_modified),
76
ngx_http_set_builtin_header },
78
{ ngx_string("Content-Range"),
79
offsetof(ngx_http_headers_out_t, content_range),
80
ngx_http_set_builtin_header },
82
{ ngx_string("Accept-Ranges"),
83
offsetof(ngx_http_headers_out_t, accept_ranges),
84
ngx_http_set_accept_ranges_header },
86
{ ngx_string("WWW-Authenticate"),
87
offsetof(ngx_http_headers_out_t, www_authenticate),
88
ngx_http_set_builtin_header },
90
{ ngx_string("Expires"),
91
offsetof(ngx_http_headers_out_t, expires),
92
ngx_http_set_builtin_header },
94
{ ngx_string("E-Tag"),
95
offsetof(ngx_http_headers_out_t, etag),
96
ngx_http_set_builtin_header },
98
{ ngx_string("Content-Length"),
99
offsetof(ngx_http_headers_out_t, content_length),
100
ngx_http_set_content_length_header },
102
{ ngx_string("Content-Type"),
104
ngx_http_set_content_type_header },
106
{ ngx_null_string, 0, ngx_http_set_header }
110
/* request time implementation */
113
ngx_http_headers_more_exec_cmd(ngx_http_request_t *r,
114
ngx_http_headers_more_cmd_t *cmd)
117
ngx_http_headers_more_header_val_t *h;
125
if ( ! ngx_http_headers_more_check_type(r, cmd->types) ) {
131
if ( ! ngx_http_headers_more_check_status(r, cmd->statuses) ) {
134
dd("status check is passed");
137
h = cmd->headers->elts;
138
for (i = 0; i < cmd->headers->nelts; i++) {
140
if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
144
if (h[i].handler(r, &h[i], &value) != NGX_OK) {
154
ngx_http_set_header(ngx_http_request_t *r,
155
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
157
return ngx_http_set_header_helper(r, hv, value, NULL, 0);
162
ngx_http_set_header_helper(ngx_http_request_t *r,
163
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value,
164
ngx_table_elt_t **output_header, ngx_flag_t no_create)
167
ngx_list_part_t *part;
169
ngx_flag_t matched = 0;
173
part = &r->headers_out.headers.part;
176
for (i = 0; /* void */; i++) {
177
if (i >= part->nelts) {
178
if (part->next == NULL) {
187
(!hv->wildcard && (h[i].key.len == hv->key.len
188
&& ngx_strncasecmp(h[i].key.data,
192
(hv->wildcard && (h[i].key.len >= hv->key.len-1
193
&& ngx_strncasecmp(h[i].key.data,
195
hv->key.len-1) == 0))
198
if (value->len == 0 || matched) {
199
dd("clearing normal header for %.*s", (int) hv->key.len,
211
*output_header = &h[i];
222
if ((hv->wildcard || no_create) && value->len == 0) {
226
/* XXX we still need to create header slot even if the value
227
* is empty because some builtin headers like Last-Modified
228
* relies on this to get cleared */
230
h = ngx_list_push(&r->headers_out.headers);
233
return NGX_HTTP_INTERNAL_SERVER_ERROR;
236
if (value->len == 0) {
246
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
247
if (h->lowcase_key == NULL) {
248
return NGX_HTTP_INTERNAL_SERVER_ERROR;
251
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
262
ngx_http_set_builtin_header(ngx_http_request_t *r,
263
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
265
ngx_table_elt_t *h, **old;
270
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
276
if (old == NULL || *old == NULL) {
277
return ngx_http_set_header_helper(r, hv, value, old, 0);
282
if (value->len == 0) {
283
dd("clearing the builtin header");
300
ngx_http_set_content_type_header(ngx_http_request_t *r,
301
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
303
u_char *p, *last, *end;
305
r->headers_out.content_type_len = value->len;
306
r->headers_out.content_type = *value;
307
r->headers_out.content_type_hash = hv->hash;
308
r->headers_out.content_type_lowcase = NULL;
311
end = p + value->len;
313
for (; p != end; p++) {
321
while (*++p == ' ') { /* void */ }
327
if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
333
r->headers_out.content_type_len = last - value->data;
341
if (*(last - 1) == '"') {
345
r->headers_out.charset.len = last - p;
346
r->headers_out.charset.data = p;
353
return ngx_http_set_header_helper(r, hv, value, NULL, 1);
358
ngx_http_set_content_length_header(ngx_http_request_t *r,
359
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
363
if (value->len == 0) {
364
return ngx_http_clear_content_length_header(r, hv, value);
367
len = ngx_atosz(value->data, value->len);
368
if (len == NGX_ERROR) {
372
r->headers_out.content_length_n = len;
374
return ngx_http_set_builtin_header(r, hv, value);
379
ngx_http_set_accept_ranges_header(ngx_http_request_t *r,
380
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
382
if (value->len == 0) {
386
return ngx_http_set_builtin_header(r, hv, value);
391
ngx_http_clear_content_length_header(ngx_http_request_t *r,
392
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
394
r->headers_out.content_length_n = -1;
396
return ngx_http_clear_builtin_header(r, hv, value);
401
ngx_http_clear_builtin_header(ngx_http_request_t *r,
402
ngx_http_headers_more_header_val_t *hv, ngx_str_t *value)
408
return ngx_http_set_builtin_header(r, hv, value);
413
ngx_http_headers_more_set_headers(ngx_conf_t *cf,
414
ngx_command_t *cmd, void *conf)
416
return ngx_http_headers_more_parse_directive(cf, cmd, conf,
417
ngx_http_headers_more_opcode_set);
422
ngx_http_headers_more_clear_headers(ngx_conf_t *cf,
423
ngx_command_t *cmd, void *conf)
425
return ngx_http_headers_more_parse_directive(cf, cmd, conf,
426
ngx_http_headers_more_opcode_clear);
431
ngx_http_headers_more_check_type(ngx_http_request_t *r, ngx_array_t *types)
436
dd("headers_out->content_type: %.*s (len %d)",
437
(int) r->headers_out.content_type.len,
438
r->headers_out.content_type.data,
439
(int) r->headers_out.content_type.len);
443
for (i = 0; i < types->nelts; i++) {
444
dd("...comparing with type [%.*s]", (int) t[i].len, t[i].data);
446
if (r->headers_out.content_type.len == t[i].len
447
&& ngx_strncmp(r->headers_out.content_type.data,
448
t[i].data, t[i].len) == 0)
459
ngx_http_headers_more_check_status(ngx_http_request_t *r, ngx_array_t *statuses)
464
dd("headers_out.status = %d", (int) r->headers_out.status);
466
status = statuses->elts;
467
for (i = 0; i < statuses->nelts; i++) {
468
dd("...comparing with specified status %d", (int) status[i]);
470
if (r->headers_out.status == status[i]) {
479
/* config time implementation */
482
ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd,
483
void *conf, ngx_http_headers_more_opcode_t opcode)
485
ngx_http_headers_more_loc_conf_t *hcf = conf;
488
ngx_http_headers_more_cmd_t *cmd;
490
ngx_flag_t ignore_next_arg;
494
if (hcf->cmds == NULL) {
495
hcf->cmds = ngx_array_create(cf->pool, 1,
496
sizeof(ngx_http_headers_more_cmd_t));
498
if (hcf->cmds == NULL) {
499
return NGX_CONF_ERROR;
503
cmd = ngx_array_push(hcf->cmds);
506
return NGX_CONF_ERROR;
509
cmd->headers = ngx_array_create(cf->pool, 1,
510
sizeof(ngx_http_headers_more_header_val_t));
511
if (cmd->headers == NULL) {
512
return NGX_CONF_ERROR;
515
cmd->types = ngx_array_create(cf->pool, 1,
517
if (cmd->types == NULL) {
518
return NGX_CONF_ERROR;
521
cmd->statuses = ngx_array_create(cf->pool, 1,
523
if (cmd->statuses == NULL) {
524
return NGX_CONF_ERROR;
527
arg = cf->args->elts;
533
for (i = 1; i < cf->args->nelts; i++) {
534
if (ignore_next_arg) {
539
if (arg[i].len == 0) {
543
if (arg[i].data[0] != '-') {
544
rc = ngx_http_headers_more_parse_header(cf, cmd_name,
545
&arg[i], cmd->headers, opcode,
546
ngx_http_headers_more_set_handlers);
549
return NGX_CONF_ERROR;
555
if (arg[i].len == 2) {
556
if (arg[i].data[1] == 't') {
557
if (i == cf->args->nelts - 1) {
558
ngx_log_error(NGX_LOG_ERR, cf->log, 0,
559
"%V: option -t takes an argument.",
562
return NGX_CONF_ERROR;
565
rc = ngx_http_headers_more_parse_types(cf->log, cmd_name,
566
&arg[i + 1], cmd->types);
569
return NGX_CONF_ERROR;
576
} else if (arg[i].data[1] == 's') {
577
if (i == cf->args->nelts - 1) {
578
ngx_log_error(NGX_LOG_ERR, cf->log, 0,
579
"%V: option -s takes an argument.",
583
return NGX_CONF_ERROR;
586
rc = ngx_http_headers_more_parse_statuses(cf->log, cmd_name,
587
&arg[i + 1], cmd->statuses);
590
return NGX_CONF_ERROR;
599
ngx_log_error(NGX_LOG_ERR, cf->log, 0,
600
"%V: invalid option name: \"%V\"",
603
return NGX_CONF_ERROR;
606
dd("Found %d statuses, %d types, and %d headers",
607
(int) cmd->statuses->nelts, (int) cmd->types->nelts,
608
(int) cmd->headers->nelts);
610
if (cmd->headers->nelts == 0) {
614
if (cmd->types->nelts == 0) {
618
if (cmd->statuses->nelts == 0) {
619
cmd->statuses = NULL;
624
ngx_http_headers_more_filter_used = 1;