xref: /unit/src/nxt_http_variables.c (revision 2654:639dde26c5dd)
1 
2 /*
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 #include <nxt_router.h>
7 #include <nxt_http.h>
8 #include <nxt_h1proto.h>
9 
10 
11 static nxt_int_t nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str,
12     void *ctx, void *data);
13 static nxt_int_t nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str,
14     void *ctx, void *data);
15 static nxt_int_t nxt_http_var_method(nxt_task_t *task, nxt_str_t *str,
16     void *ctx, void *data);
17 static nxt_int_t nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str,
18     void *ctx, void *data);
19 static nxt_int_t nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx,
20     void *data);
21 static nxt_int_t nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx,
22     void *data);
23 static nxt_int_t nxt_http_var_remote_addr(nxt_task_t *task, nxt_str_t *str,
24     void *ctx, void *data);
25 static nxt_int_t nxt_http_var_time_local(nxt_task_t *task, nxt_str_t *str,
26     void *ctx, void *data);
27 static u_char *nxt_http_log_date(u_char *buf, nxt_realtime_t *now,
28     struct tm *tm, size_t size, const char *format);
29 static nxt_int_t nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str,
30     void *ctx, void *data);
31 static nxt_int_t nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str,
32     void *ctx, void *data);
33 static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str,
34     void *ctx, void *data);
35 static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str,
36     void *ctx, void *data);
37 static nxt_int_t nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str,
38     void *ctx, void *data);
39 static nxt_int_t nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str,
40     void *ctx, void *data);
41 static nxt_int_t nxt_http_var_response_connection(nxt_task_t *task,
42     nxt_str_t *str, void *ctx, void *data);
43 static nxt_int_t nxt_http_var_response_content_length(nxt_task_t *task,
44     nxt_str_t *str, void *ctx, void *data);
45 static nxt_int_t nxt_http_var_response_transfer_encoding(nxt_task_t *task,
46     nxt_str_t *str, void *ctx, void *data);
47 static nxt_int_t nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx,
48     void *data);
49 static nxt_int_t nxt_http_var_header(nxt_task_t *task, nxt_str_t *str,
50     void *ctx, void *data);
51 static nxt_int_t nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str,
52     void *ctx, void *data);
53 static nxt_int_t nxt_http_var_response_header(nxt_task_t *task, nxt_str_t *str,
54     void *ctx, void *data);
55 
56 
57 static nxt_var_decl_t  nxt_http_vars[] = {
58     {
59         .name = nxt_string("dollar"),
60         .handler = nxt_http_var_dollar,
61         .cacheable = 1,
62     }, {
63         .name = nxt_string("request_time"),
64         .handler = nxt_http_var_request_time,
65         .cacheable = 1,
66     }, {
67         .name = nxt_string("method"),
68         .handler = nxt_http_var_method,
69         .cacheable = 1,
70     }, {
71         .name = nxt_string("request_uri"),
72         .handler = nxt_http_var_request_uri,
73         .cacheable = 1,
74     }, {
75         .name = nxt_string("uri"),
76         .handler = nxt_http_var_uri,
77         .cacheable = 0,
78     }, {
79         .name = nxt_string("host"),
80         .handler = nxt_http_var_host,
81         .cacheable = 1,
82     }, {
83         .name = nxt_string("remote_addr"),
84         .handler = nxt_http_var_remote_addr,
85         .cacheable = 1,
86     }, {
87         .name = nxt_string("time_local"),
88         .handler = nxt_http_var_time_local,
89         .cacheable = 1,
90     }, {
91         .name = nxt_string("request_line"),
92         .handler = nxt_http_var_request_line,
93         .cacheable = 1,
94     }, {
95         .name = nxt_string("request_id"),
96         .handler = nxt_http_var_request_id,
97         .cacheable = 1,
98     }, {
99         .name = nxt_string("status"),
100         .handler = nxt_http_var_status,
101         .cacheable = 1,
102     }, {
103         .name = nxt_string("body_bytes_sent"),
104         .handler = nxt_http_var_body_bytes_sent,
105         .cacheable = 1,
106     }, {
107         .name = nxt_string("header_referer"),
108         .handler = nxt_http_var_referer,
109         .cacheable = 1,
110     }, {
111         .name = nxt_string("response_header_connection"),
112         .handler = nxt_http_var_response_connection,
113         .cacheable = 1,
114     }, {
115         .name = nxt_string("response_header_content_length"),
116         .handler = nxt_http_var_response_content_length,
117         .cacheable = 1,
118     }, {
119         .name = nxt_string("response_header_transfer_encoding"),
120         .handler = nxt_http_var_response_transfer_encoding,
121         .cacheable = 1,
122     }, {
123         .name = nxt_string("header_user_agent"),
124         .handler = nxt_http_var_user_agent,
125         .cacheable = 1,
126     },
127 };
128 
129 
130 nxt_int_t
nxt_http_register_variables(void)131 nxt_http_register_variables(void)
132 {
133     return nxt_var_register(nxt_http_vars, nxt_nitems(nxt_http_vars));
134 }
135 
136 
137 nxt_int_t
nxt_http_unknown_var_ref(nxt_mp_t * mp,nxt_var_ref_t * ref,nxt_str_t * name)138 nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name)
139 {
140     int64_t    hash;
141     nxt_str_t  str, *lower;
142 
143     if (nxt_str_start(name, "response_header_", 16)) {
144         ref->handler = nxt_http_var_response_header;
145         ref->cacheable = 0;
146 
147         str.start = name->start + 16;
148         str.length = name->length - 16;
149 
150         if (str.length == 0) {
151             return NXT_ERROR;
152         }
153 
154         lower = nxt_str_alloc(mp, str.length);
155         if (nxt_slow_path(lower == NULL)) {
156             return NXT_ERROR;
157         }
158 
159         nxt_memcpy_lowcase(lower->start, str.start, str.length);
160 
161         ref->data = lower;
162 
163         return NXT_OK;
164     }
165 
166     if (nxt_str_start(name, "header_", 7)) {
167         ref->handler = nxt_http_var_header;
168         ref->cacheable = 1;
169 
170         str.start = name->start + 7;
171         str.length = name->length - 7;
172 
173         if (str.length == 0) {
174             return NXT_ERROR;
175         }
176 
177         hash = nxt_http_header_hash(mp, &str);
178         if (nxt_slow_path(hash == -1)) {
179             return NXT_ERROR;
180         }
181 
182     } else if (nxt_str_start(name, "arg_", 4)) {
183         ref->handler = nxt_http_var_arg;
184         ref->cacheable = 1;
185 
186         str.start = name->start + 4;
187         str.length = name->length - 4;
188 
189         if (str.length == 0) {
190             return NXT_ERROR;
191         }
192 
193         hash = nxt_http_argument_hash(mp, &str);
194         if (nxt_slow_path(hash == -1)) {
195             return NXT_ERROR;
196         }
197 
198     } else if (nxt_str_start(name, "cookie_", 7)) {
199         ref->handler = nxt_http_var_cookie;
200         ref->cacheable = 1;
201 
202         str.start = name->start + 7;
203         str.length = name->length - 7;
204 
205         if (str.length == 0) {
206             return NXT_ERROR;
207         }
208 
209         hash = nxt_http_cookie_hash(mp, &str);
210         if (nxt_slow_path(hash == -1)) {
211             return NXT_ERROR;
212         }
213 
214     } else {
215         return NXT_ERROR;
216     }
217 
218     ref->data = nxt_var_field_new(mp, &str, (uint32_t) hash);
219     if (nxt_slow_path(ref->data == NULL)) {
220         return NXT_ERROR;
221     }
222 
223     return NXT_OK;
224 }
225 
226 
227 static nxt_int_t
nxt_http_var_dollar(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)228 nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
229 {
230     nxt_str_set(str, "$");
231 
232     return NXT_OK;
233 }
234 
235 
236 static nxt_int_t
nxt_http_var_request_time(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)237 nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, void *ctx,
238     void *data)
239 {
240     u_char              *p;
241     nxt_msec_t          ms;
242     nxt_nsec_t          now;
243     nxt_http_request_t  *r;
244 
245     r = ctx;
246 
247     now = nxt_thread_monotonic_time(task->thread);
248     ms = (now - r->start_time) / 1000000;
249 
250     str->start = nxt_mp_nget(r->mem_pool, NXT_TIME_T_LEN + 4);
251     if (nxt_slow_path(str->start == NULL)) {
252         return NXT_ERROR;
253     }
254 
255     p = nxt_sprintf(str->start, str->start + NXT_TIME_T_LEN, "%T.%03M",
256                     (nxt_time_t) ms / 1000, ms % 1000);
257 
258     str->length = p - str->start;
259 
260     return NXT_OK;
261 }
262 
263 
264 static nxt_int_t
nxt_http_var_method(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)265 nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
266 {
267     nxt_http_request_t  *r;
268 
269     r = ctx;
270 
271     *str = *r->method;
272 
273     return NXT_OK;
274 }
275 
276 
277 static nxt_int_t
nxt_http_var_request_uri(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)278 nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str, void *ctx,
279     void *data)
280 {
281     nxt_http_request_t  *r;
282 
283     r = ctx;
284 
285     *str = r->target;
286 
287     return NXT_OK;
288 }
289 
290 
291 static nxt_int_t
nxt_http_var_uri(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)292 nxt_http_var_uri(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
293 {
294     nxt_http_request_t  *r;
295 
296     r = ctx;
297 
298     *str = *r->path;
299 
300     return NXT_OK;
301 }
302 
303 
304 static nxt_int_t
nxt_http_var_host(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)305 nxt_http_var_host(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
306 {
307     nxt_http_request_t  *r;
308 
309     r = ctx;
310 
311     *str = r->host;
312 
313     return NXT_OK;
314 }
315 
316 
317 static nxt_int_t
nxt_http_var_remote_addr(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)318 nxt_http_var_remote_addr(nxt_task_t *task, nxt_str_t *str, void *ctx,
319     void *data)
320 {
321     nxt_http_request_t  *r;
322 
323     r = ctx;
324 
325     str->length = r->remote->address_length;
326     str->start = nxt_sockaddr_address(r->remote);
327 
328     return NXT_OK;
329 }
330 
331 
332 static nxt_int_t
nxt_http_var_time_local(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)333 nxt_http_var_time_local(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
334 {
335     nxt_http_request_t  *r;
336 
337     static nxt_time_string_t  date_cache = {
338         (nxt_atomic_uint_t) -1,
339         nxt_http_log_date,
340         "%02d/%s/%4d:%02d:%02d:%02d %c%02d%02d",
341         nxt_length("31/Dec/1986:19:40:00 +0300"),
342         NXT_THREAD_TIME_LOCAL,
343         NXT_THREAD_TIME_SEC,
344     };
345 
346     r = ctx;
347 
348     str->length = date_cache.size;
349 
350     str->start = nxt_mp_nget(r->mem_pool, str->length);
351     if (nxt_slow_path(str->start == NULL)) {
352         return NXT_ERROR;
353     }
354 
355     str->length = nxt_thread_time_string(task->thread, &date_cache, str->start)
356                   - str->start;
357 
358     return NXT_OK;
359 }
360 
361 
362 static u_char *
nxt_http_log_date(u_char * buf,nxt_realtime_t * now,struct tm * tm,size_t size,const char * format)363 nxt_http_log_date(u_char *buf, nxt_realtime_t *now, struct tm *tm,
364     size_t size, const char *format)
365 {
366     u_char  sign;
367     time_t  gmtoff;
368 
369     static const char  *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
370                                     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
371 
372     gmtoff = nxt_timezone(tm) / 60;
373 
374     if (gmtoff < 0) {
375         gmtoff = -gmtoff;
376         sign = '-';
377 
378     } else {
379         sign = '+';
380     }
381 
382     return nxt_sprintf(buf, buf + size, format,
383                        tm->tm_mday, month[tm->tm_mon], tm->tm_year + 1900,
384                        tm->tm_hour, tm->tm_min, tm->tm_sec,
385                        sign, gmtoff / 60, gmtoff % 60);
386 }
387 
388 
389 static nxt_int_t
nxt_http_var_request_line(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)390 nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx,
391     void *data)
392 {
393     nxt_http_request_t  *r;
394 
395     r = ctx;
396 
397     *str = r->request_line;
398 
399     return NXT_OK;
400 }
401 
402 
403 static nxt_int_t
nxt_http_var_request_id(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)404 nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str, void *ctx,
405     void *data)
406 {
407     nxt_random_t        *rand;
408     nxt_http_request_t  *r;
409 
410     r = ctx;
411 
412     str->start = nxt_mp_nget(r->mem_pool, 32);
413     if (nxt_slow_path(str->start == NULL)) {
414         return NXT_ERROR;
415     }
416 
417     str->length = 32;
418 
419     rand = &task->thread->random;
420 
421     (void) nxt_sprintf(str->start, str->start + 32, "%08xD%08xD%08xD%08xD",
422                        nxt_random(rand), nxt_random(rand),
423                        nxt_random(rand), nxt_random(rand));
424 
425     return NXT_OK;
426 }
427 
428 
429 static nxt_int_t
nxt_http_var_body_bytes_sent(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)430 nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx,
431     void *data)
432 {
433     u_char              *p;
434     nxt_off_t           bytes;
435     nxt_http_request_t  *r;
436 
437     r = ctx;
438 
439     str->start = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
440     if (nxt_slow_path(str->start == NULL)) {
441         return NXT_ERROR;
442     }
443 
444     bytes = nxt_http_proto[r->protocol].body_bytes_sent(task, r->proto);
445 
446     p = nxt_sprintf(str->start, str->start + NXT_OFF_T_LEN, "%O", bytes);
447 
448     str->length = p - str->start;
449 
450     return NXT_OK;
451 }
452 
453 
454 static nxt_int_t
nxt_http_var_status(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)455 nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
456 {
457     nxt_http_request_t  *r;
458 
459     r = ctx;
460 
461     str->start = nxt_mp_nget(r->mem_pool, 3);
462     if (nxt_slow_path(str->start == NULL)) {
463         return NXT_ERROR;
464     }
465 
466     (void) nxt_sprintf(str->start, str->start + 3, "%03d", r->status);
467 
468     str->length = 3;
469 
470     return NXT_OK;
471 }
472 
473 
474 static nxt_int_t
nxt_http_var_referer(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)475 nxt_http_var_referer(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
476 {
477     nxt_http_request_t  *r;
478 
479     r = ctx;
480 
481     if (r->referer != NULL) {
482         str->start = r->referer->value;
483         str->length = r->referer->value_length;
484 
485     } else {
486         nxt_str_null(str);
487     }
488 
489     return NXT_OK;
490 }
491 
492 
493 static nxt_int_t
nxt_http_var_user_agent(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)494 nxt_http_var_user_agent(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
495 {
496     nxt_http_request_t  *r;
497 
498     r = ctx;
499 
500     if (r->user_agent != NULL) {
501         str->start = r->user_agent->value;
502         str->length = r->user_agent->value_length;
503 
504     } else {
505         nxt_str_null(str);
506     }
507 
508     return NXT_OK;
509 }
510 
511 
512 static nxt_int_t
nxt_http_var_response_connection(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)513 nxt_http_var_response_connection(nxt_task_t *task, nxt_str_t *str, void *ctx,
514     void *data)
515 {
516     nxt_int_t           conn;
517     nxt_bool_t          http11;
518     nxt_h1proto_t       *h1p;
519     nxt_http_request_t  *r;
520 
521     static const nxt_str_t  connection[3] = {
522         nxt_string("close"),
523         nxt_string("keep-alive"),
524         nxt_string("Upgrade"),
525     };
526 
527     r = ctx;
528     h1p = r->proto.h1;
529 
530     conn = -1;
531 
532     if (r->websocket_handshake && r->status == NXT_HTTP_SWITCHING_PROTOCOLS) {
533         conn = 2;
534 
535     } else {
536         http11 = nxt_h1p_is_http11(h1p);
537 
538         if (http11 ^ h1p->keepalive) {
539             conn = h1p->keepalive;
540         }
541     }
542 
543     if (conn >= 0) {
544         *str = connection[conn];
545 
546     } else {
547         nxt_str_null(str);
548     }
549 
550     return NXT_OK;
551 }
552 
553 
554 static nxt_int_t
nxt_http_var_response_content_length(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)555 nxt_http_var_response_content_length(nxt_task_t *task, nxt_str_t *str,
556     void *ctx, void *data)
557 {
558     u_char              *p;
559     nxt_http_request_t  *r;
560 
561     r = ctx;
562 
563     if (r->resp.content_length != NULL) {
564         str->length = r->resp.content_length->value_length;
565         str->start = r->resp.content_length->value;
566 
567         return NXT_OK;
568     }
569 
570     if (r->resp.content_length_n >= 0) {
571         str->start = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
572         if (str->start == NULL) {
573             return NXT_ERROR;
574         }
575 
576         p = nxt_sprintf(str->start, str->start + NXT_OFF_T_LEN,
577                         "%O", r->resp.content_length_n);
578 
579         str->length = p - str->start;
580 
581         return NXT_OK;
582     }
583 
584     nxt_str_null(str);
585 
586     return NXT_OK;
587 }
588 
589 
590 static nxt_int_t
nxt_http_var_response_transfer_encoding(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)591 nxt_http_var_response_transfer_encoding(nxt_task_t *task, nxt_str_t *str,
592     void *ctx, void *data)
593 {
594     nxt_http_request_t  *r;
595 
596     r = ctx;
597 
598     if (r->proto.h1->chunked) {
599         nxt_str_set(str, "chunked");
600 
601     } else {
602         nxt_str_null(str);
603     }
604 
605     return NXT_OK;
606 }
607 
608 
609 static nxt_int_t
nxt_http_var_arg(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)610 nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
611 {
612     nxt_array_t            *args;
613     nxt_var_field_t        *vf;
614     nxt_http_request_t     *r;
615     nxt_http_name_value_t  *nv, *start;
616 
617     r = ctx;
618     vf = data;
619 
620     args = nxt_http_arguments_parse(r);
621     if (nxt_slow_path(args == NULL)) {
622         return NXT_ERROR;
623     }
624 
625     start = args->elts;
626     nv = start + args->nelts - 1;
627 
628     while (nv >= start) {
629 
630         if (vf->hash == nv->hash
631             && vf->name.length == nv->name_length
632             && memcmp(vf->name.start, nv->name, nv->name_length) == 0)
633         {
634             str->start = nv->value;
635             str->length = nv->value_length;
636 
637             return NXT_OK;
638         }
639 
640         nv--;
641     }
642 
643     nxt_str_null(str);
644 
645     return NXT_OK;
646 }
647 
648 
649 static nxt_int_t
nxt_http_var_header(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)650 nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
651 {
652     nxt_var_field_t     *vf;
653     nxt_http_field_t    *f;
654     nxt_http_request_t  *r;
655 
656     r = ctx;
657     vf = data;
658 
659     nxt_list_each(f, r->fields) {
660 
661         if (vf->hash == f->hash
662             && vf->name.length == f->name_length
663             && nxt_strncasecmp(vf->name.start, f->name, f->name_length) == 0)
664         {
665             str->start = f->value;
666             str->length = f->value_length;
667 
668             return NXT_OK;
669         }
670 
671     } nxt_list_loop;
672 
673     nxt_str_null(str);
674 
675     return NXT_OK;
676 }
677 
678 
679 static nxt_int_t
nxt_http_var_cookie(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)680 nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data)
681 {
682     nxt_array_t            *cookies;
683     nxt_var_field_t        *vf;
684     nxt_http_request_t     *r;
685     nxt_http_name_value_t  *nv, *end;
686 
687     r = ctx;
688     vf = data;
689 
690     cookies = nxt_http_cookies_parse(r);
691     if (nxt_slow_path(cookies == NULL)) {
692         return NXT_ERROR;
693     }
694 
695     nv = cookies->elts;
696     end = nv + cookies->nelts;
697 
698     while (nv < end) {
699 
700         if (vf->hash == nv->hash
701             && vf->name.length == nv->name_length
702             && memcmp(vf->name.start, nv->name, nv->name_length) == 0)
703         {
704             str->start = nv->value;
705             str->length = nv->value_length;
706 
707             return NXT_OK;
708         }
709 
710         nv++;
711     }
712 
713     nxt_str_null(str);
714 
715     return NXT_OK;
716 }
717 
718 
719 static int
nxt_http_field_name_cmp(nxt_str_t * name,nxt_http_field_t * field)720 nxt_http_field_name_cmp(nxt_str_t *name, nxt_http_field_t *field)
721 {
722     size_t  i;
723     u_char  c1, c2;
724 
725     if (name->length != field->name_length) {
726         return 1;
727     }
728 
729     for (i = 0; i < name->length; i++) {
730         c1 = name->start[i];
731         c2 = field->name[i];
732 
733         if (c2 >= 'A' && c2 <= 'Z') {
734             c2 |= 0x20;
735 
736         } else if (c2 == '-') {
737             c2 = '_';
738         }
739 
740         if (c1 != c2) {
741             return 1;
742         }
743     }
744 
745     return 0;
746 }
747 
748 
749 static nxt_int_t
nxt_http_var_response_header(nxt_task_t * task,nxt_str_t * str,void * ctx,void * data)750 nxt_http_var_response_header(nxt_task_t *task, nxt_str_t *str, void *ctx,
751     void *data)
752 {
753     nxt_str_t           *name;
754     nxt_http_field_t    *f;
755     nxt_http_request_t  *r;
756 
757     r = ctx;
758     name = data;
759 
760     nxt_list_each(f, r->resp.fields) {
761 
762         if (f->skip) {
763             continue;
764         }
765 
766         if (nxt_http_field_name_cmp(name, f) == 0) {
767             str->start = f->value;
768             str->length = f->value_length;
769 
770             return NXT_OK;
771         }
772 
773     } nxt_list_loop;
774 
775     nxt_str_null(str);
776 
777     return NXT_OK;
778 }
779