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