xref: /unit/src/nxt_http_request.c (revision 2104:2ae3c205fcad)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_router.h>
8 #include <nxt_http.h>
9 
10 
11 static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp);
12 static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data);
13 static nxt_int_t nxt_http_request_client_ip(nxt_task_t *task,
14     nxt_http_request_t *r);
15 static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr(
16     nxt_http_request_t *r, u_char *start, size_t len);
17 static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data);
18 static void nxt_http_request_proto_info(nxt_task_t *task,
19     nxt_http_request_t *r);
20 static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
21     void *data);
22 static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
23 
24 static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now,
25     struct tm *tm, size_t size, const char *format);
26 
27 static nxt_http_name_value_t *nxt_http_argument(nxt_array_t *array,
28     u_char *name, size_t name_length, uint32_t hash, u_char *start,
29     u_char *end);
30 static nxt_int_t nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start,
31     u_char *end);
32 static nxt_http_name_value_t *nxt_http_cookie(nxt_array_t *array, u_char *name,
33     size_t name_length, u_char *start, u_char *end);
34 
35 
36 #define NXT_HTTP_COOKIE_HASH                                                  \
37     (nxt_http_field_hash_end(                                                 \
38      nxt_http_field_hash_char(                                                \
39      nxt_http_field_hash_char(                                                \
40      nxt_http_field_hash_char(                                                \
41      nxt_http_field_hash_char(                                                \
42      nxt_http_field_hash_char(                                                \
43      nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT,                       \
44         'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF)
45 
46 
47 static const nxt_http_request_state_t  nxt_http_request_init_state;
48 static const nxt_http_request_state_t  nxt_http_request_body_state;
49 
50 
51 nxt_time_string_t  nxt_http_date_cache = {
52     (nxt_atomic_uint_t) -1,
53     nxt_http_date_cache_handler,
54     NULL,
55     NXT_HTTP_DATE_LEN,
56     NXT_THREAD_TIME_GMT,
57     NXT_THREAD_TIME_SEC,
58 };
59 
60 
61 nxt_int_t
nxt_http_init(nxt_task_t * task)62 nxt_http_init(nxt_task_t *task)
63 {
64     nxt_int_t  ret;
65 
66     ret = nxt_h1p_init(task);
67 
68     if (ret != NXT_OK) {
69         return ret;
70     }
71 
72     return nxt_http_response_hash_init(task);
73 }
74 
75 
76 nxt_int_t
nxt_http_request_host(void * ctx,nxt_http_field_t * field,uintptr_t data)77 nxt_http_request_host(void *ctx, nxt_http_field_t *field, uintptr_t data)
78 {
79     nxt_int_t           ret;
80     nxt_str_t           host;
81     nxt_http_request_t  *r;
82 
83     r = ctx;
84 
85     if (nxt_slow_path(r->host.start != NULL)) {
86         return NXT_HTTP_BAD_REQUEST;
87     }
88 
89     host.length = field->value_length;
90     host.start = field->value;
91 
92     ret = nxt_http_validate_host(&host, r->mem_pool);
93 
94     if (nxt_fast_path(ret == NXT_OK)) {
95         r->host = host;
96     }
97 
98     return ret;
99 }
100 
101 
102 static nxt_int_t
nxt_http_validate_host(nxt_str_t * host,nxt_mp_t * mp)103 nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp)
104 {
105     u_char      *h, ch;
106     size_t      i, dot_pos, host_length;
107     nxt_bool_t  lowcase;
108 
109     enum {
110         sw_usual,
111         sw_literal,
112         sw_rest
113     } state;
114 
115     dot_pos = host->length;
116     host_length = host->length;
117 
118     h = host->start;
119 
120     lowcase = 0;
121     state = sw_usual;
122 
123     for (i = 0; i < host->length; i++) {
124         ch = h[i];
125 
126         if (ch > ']') {
127             /* Short path. */
128             continue;
129         }
130 
131         switch (ch) {
132 
133         case '.':
134             if (dot_pos == i - 1) {
135                 return NXT_HTTP_BAD_REQUEST;
136             }
137 
138             dot_pos = i;
139             break;
140 
141         case ':':
142             if (state == sw_usual) {
143                 host_length = i;
144                 state = sw_rest;
145             }
146 
147             break;
148 
149         case '[':
150             if (i == 0) {
151                 state = sw_literal;
152             }
153 
154             break;
155 
156         case ']':
157             if (state == sw_literal) {
158                 host_length = i + 1;
159                 state = sw_rest;
160             }
161 
162             break;
163 
164         case '/':
165             return NXT_HTTP_BAD_REQUEST;
166 
167         default:
168             if (ch >= 'A' && ch <= 'Z') {
169                 lowcase = 1;
170             }
171 
172             break;
173         }
174     }
175 
176     if (dot_pos == host_length - 1) {
177         host_length--;
178     }
179 
180     host->length = host_length;
181 
182     if (lowcase) {
183         host->start = nxt_mp_nget(mp, host_length);
184         if (nxt_slow_path(host->start == NULL)) {
185             return NXT_HTTP_INTERNAL_SERVER_ERROR;
186         }
187 
188         nxt_memcpy_lowcase(host->start, h, host_length);
189     }
190 
191     return NXT_OK;
192 }
193 
194 
195 nxt_int_t
nxt_http_request_field(void * ctx,nxt_http_field_t * field,uintptr_t offset)196 nxt_http_request_field(void *ctx, nxt_http_field_t *field, uintptr_t offset)
197 {
198     nxt_http_request_t  *r;
199 
200     r = ctx;
201 
202     nxt_value_at(nxt_http_field_t *, r, offset) = field;
203 
204     return NXT_OK;
205 }
206 
207 
208 nxt_int_t
nxt_http_request_content_length(void * ctx,nxt_http_field_t * field,uintptr_t data)209 nxt_http_request_content_length(void *ctx, nxt_http_field_t *field,
210     uintptr_t data)
211 {
212     nxt_off_t           n, max_body_size;
213     nxt_http_request_t  *r;
214 
215     r = ctx;
216 
217     if (nxt_fast_path(r->content_length == NULL)) {
218         r->content_length = field;
219 
220         n = nxt_off_t_parse(field->value, field->value_length);
221 
222         if (nxt_fast_path(n >= 0)) {
223             r->content_length_n = n;
224 
225             max_body_size = r->conf->socket_conf->max_body_size;
226 
227             if (nxt_slow_path(n > max_body_size)) {
228                 return NXT_HTTP_PAYLOAD_TOO_LARGE;
229             }
230 
231             return NXT_OK;
232         }
233     }
234 
235     return NXT_HTTP_BAD_REQUEST;
236 }
237 
238 
239 nxt_http_request_t *
nxt_http_request_create(nxt_task_t * task)240 nxt_http_request_create(nxt_task_t *task)
241 {
242     nxt_mp_t            *mp;
243     nxt_buf_t           *last;
244     nxt_http_request_t  *r;
245 
246     mp = nxt_mp_create(4096, 128, 512, 32);
247     if (nxt_slow_path(mp == NULL)) {
248         return NULL;
249     }
250 
251     r = nxt_mp_zget(mp, sizeof(nxt_http_request_t));
252     if (nxt_slow_path(r == NULL)) {
253         goto fail;
254     }
255 
256     r->resp.fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
257     if (nxt_slow_path(r->resp.fields == NULL)) {
258         goto fail;
259     }
260 
261     last = nxt_mp_zget(mp, NXT_BUF_SYNC_SIZE);
262     if (nxt_slow_path(last == NULL)) {
263         goto fail;
264     }
265 
266     nxt_buf_set_sync(last);
267     nxt_buf_set_last(last);
268     last->completion_handler = nxt_http_request_done;
269     last->parent = r;
270     r->last = last;
271 
272     r->mem_pool = mp;
273     r->content_length_n = -1;
274     r->resp.content_length_n = -1;
275     r->state = &nxt_http_request_init_state;
276 
277     return r;
278 
279 fail:
280 
281     nxt_mp_release(mp);
282 
283     return NULL;
284 }
285 
286 
287 static const nxt_http_request_state_t  nxt_http_request_init_state
288     nxt_aligned(64) =
289 {
290     .ready_handler = nxt_http_request_start,
291     .error_handler = nxt_http_request_close_handler,
292 };
293 
294 
295 static void
nxt_http_request_start(nxt_task_t * task,void * obj,void * data)296 nxt_http_request_start(nxt_task_t *task, void *obj, void *data)
297 {
298     nxt_int_t           ret;
299     nxt_http_request_t  *r;
300 
301     r = obj;
302 
303     r->state = &nxt_http_request_body_state;
304 
305     ret = nxt_http_request_client_ip(task, r);
306     if (nxt_slow_path(ret != NXT_OK)) {
307         nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
308     }
309 
310     nxt_http_request_read_body(task, r);
311 }
312 
313 
314 static nxt_int_t
nxt_http_request_client_ip(nxt_task_t * task,nxt_http_request_t * r)315 nxt_http_request_client_ip(nxt_task_t *task, nxt_http_request_t *r)
316 {
317     u_char                *start, *p;
318     nxt_int_t             ret, i, len;
319     nxt_str_t             *header;
320     nxt_array_t           *fields_arr;  /* of nxt_http_field_t * */
321     nxt_sockaddr_t        *sa, *prev_sa;
322     nxt_http_field_t      *f, **fields;
323     nxt_http_client_ip_t  *client_ip;
324 
325     client_ip = r->conf->socket_conf->client_ip;
326 
327     if (client_ip == NULL) {
328         return NXT_OK;
329     }
330 
331     ret = nxt_http_route_addr_rule(r, client_ip->source, r->remote);
332     if (ret <= 0) {
333         return NXT_OK;
334     }
335 
336     header = client_ip->header;
337 
338     fields_arr = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_field_t *));
339     if (nxt_slow_path(fields_arr == NULL)) {
340         return NXT_ERROR;
341     }
342 
343     nxt_list_each(f, r->fields) {
344         if (f->hash == client_ip->header_hash
345             && f->name_length == client_ip->header->length
346             && f->value_length > 0
347             && nxt_memcasecmp(f->name, header->start, header->length) == 0)
348         {
349             fields = nxt_array_add(fields_arr);
350             if (nxt_slow_path(fields == NULL)) {
351                 return NXT_ERROR;
352             }
353 
354             *fields = f;
355         }
356     } nxt_list_loop;
357 
358     prev_sa = r->remote;
359     fields = (nxt_http_field_t **) fields_arr->elts;
360 
361     i = fields_arr->nelts;
362 
363     while (i-- > 0) {
364         f = fields[i];
365         start = f->value;
366         len = f->value_length;
367 
368         do {
369             for (p = start + len - 1; p > start; p--, len--) {
370                 if (*p != ' ' && *p != ',') {
371                     break;
372                 }
373             }
374 
375             for (/* void */; p > start; p--) {
376                 if (*p == ' ' || *p == ',') {
377                     p++;
378                     break;
379                 }
380             }
381 
382             sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start));
383             if (nxt_slow_path(sa == NULL)) {
384                 if (prev_sa != NULL) {
385                     r->remote = prev_sa;
386                 }
387 
388                 return NXT_OK;
389             }
390 
391             if (!client_ip->recursive) {
392                 r->remote = sa;
393 
394                 return NXT_OK;
395             }
396 
397             ret = nxt_http_route_addr_rule(r, client_ip->source, sa);
398             if (ret <= 0 || (i == 0 && p == start)) {
399                 r->remote = sa;
400 
401                 return NXT_OK;
402             }
403 
404             prev_sa = sa;
405             len = p - 1 - start;
406 
407         } while (len > 0);
408     }
409 
410     return NXT_OK;
411 }
412 
413 
414 static nxt_sockaddr_t *
nxt_http_request_client_ip_sockaddr(nxt_http_request_t * r,u_char * start,size_t len)415 nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start,
416     size_t len)
417 {
418     nxt_str_t       addr;
419     nxt_sockaddr_t  *sa;
420 
421     addr.start = start;
422     addr.length = len;
423 
424     sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr);
425     if (nxt_slow_path(sa == NULL)) {
426         return NULL;
427     }
428 
429     switch (sa->u.sockaddr.sa_family) {
430         case AF_INET:
431             if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) {
432                 return NULL;
433             }
434 
435             break;
436 
437 #if (NXT_INET6)
438         case AF_INET6:
439             if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) {
440                 return NULL;
441             }
442 
443             break;
444 #endif /* NXT_INET6 */
445 
446         default:
447             return NULL;
448     }
449 
450     return sa;
451 }
452 
453 
454 static const nxt_http_request_state_t  nxt_http_request_body_state
455     nxt_aligned(64) =
456 {
457     .ready_handler = nxt_http_request_ready,
458     .error_handler = nxt_http_request_close_handler,
459 };
460 
461 
462 static void
nxt_http_request_ready(nxt_task_t * task,void * obj,void * data)463 nxt_http_request_ready(nxt_task_t *task, void *obj, void *data)
464 {
465     nxt_http_action_t   *action;
466     nxt_http_request_t  *r;
467 
468     r = obj;
469     action = r->conf->socket_conf->action;
470 
471     nxt_http_request_action(task, r, action);
472 }
473 
474 
475 void
nxt_http_request_action(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)476 nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
477     nxt_http_action_t *action)
478 {
479     if (nxt_fast_path(action != NULL)) {
480 
481         do {
482             action = action->handler(task, r, action);
483 
484             if (action == NULL) {
485                 return;
486             }
487 
488             if (action == NXT_HTTP_ACTION_ERROR) {
489                 break;
490             }
491 
492         } while (r->pass_count++ < 255);
493     }
494 
495     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
496 }
497 
498 
499 nxt_http_action_t *
nxt_http_application_handler(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)500 nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r,
501     nxt_http_action_t *action)
502 {
503     nxt_debug(task, "http application handler");
504 
505     /*
506      * TODO: need an application flag to get local address
507      * required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go.
508      */
509     nxt_http_request_proto_info(task, r);
510 
511     if (r->host.length != 0) {
512         r->server_name = r->host;
513 
514     } else {
515         nxt_str_set(&r->server_name, "localhost");
516     }
517 
518     nxt_router_process_http_request(task, r, action);
519 
520     return NULL;
521 }
522 
523 
524 static void
nxt_http_request_proto_info(nxt_task_t * task,nxt_http_request_t * r)525 nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r)
526 {
527     if (nxt_fast_path(r->proto.any != NULL)) {
528         nxt_http_proto[r->protocol].local_addr(task, r);
529     }
530 }
531 
532 
533 void
nxt_http_request_read_body(nxt_task_t * task,nxt_http_request_t * r)534 nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r)
535 {
536     if (nxt_fast_path(r->proto.any != NULL)) {
537         nxt_http_proto[r->protocol].body_read(task, r);
538     }
539 }
540 
541 
542 void
nxt_http_request_header_send(nxt_task_t * task,nxt_http_request_t * r,nxt_work_handler_t body_handler,void * data)543 nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
544     nxt_work_handler_t body_handler, void *data)
545 {
546     u_char            *p, *end;
547     nxt_http_field_t  *server, *date, *content_length;
548 
549     /*
550      * TODO: "Server", "Date", and "Content-Length" processing should be moved
551      * to the last header filter.
552      */
553 
554     server = nxt_list_zero_add(r->resp.fields);
555     if (nxt_slow_path(server == NULL)) {
556         goto fail;
557     }
558 
559     nxt_http_field_set(server, "Server", NXT_SERVER);
560 
561     if (r->resp.date == NULL) {
562         date = nxt_list_zero_add(r->resp.fields);
563         if (nxt_slow_path(date == NULL)) {
564             goto fail;
565         }
566 
567         nxt_http_field_name_set(date, "Date");
568 
569         p = nxt_mp_nget(r->mem_pool, nxt_http_date_cache.size);
570         if (nxt_slow_path(p == NULL)) {
571             goto fail;
572         }
573 
574         (void) nxt_thread_time_string(task->thread, &nxt_http_date_cache, p);
575 
576         date->value = p;
577         date->value_length = nxt_http_date_cache.size;
578 
579         r->resp.date = date;
580     }
581 
582     if (r->resp.content_length_n != -1
583         && (r->resp.content_length == NULL || r->resp.content_length->skip))
584     {
585         content_length = nxt_list_zero_add(r->resp.fields);
586         if (nxt_slow_path(content_length == NULL)) {
587             goto fail;
588         }
589 
590         nxt_http_field_name_set(content_length, "Content-Length");
591 
592         p = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
593         if (nxt_slow_path(p == NULL)) {
594             goto fail;
595         }
596 
597         content_length->value = p;
598         end = nxt_sprintf(p, p + NXT_OFF_T_LEN, "%O", r->resp.content_length_n);
599         content_length->value_length = end - p;
600 
601         r->resp.content_length = content_length;
602     }
603 
604     if (nxt_fast_path(r->proto.any != NULL)) {
605         nxt_http_proto[r->protocol].header_send(task, r, body_handler, data);
606     }
607 
608     return;
609 
610 fail:
611 
612     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
613 }
614 
615 
616 void
nxt_http_request_ws_frame_start(nxt_task_t * task,nxt_http_request_t * r,nxt_buf_t * ws_frame)617 nxt_http_request_ws_frame_start(nxt_task_t *task, nxt_http_request_t *r,
618     nxt_buf_t *ws_frame)
619 {
620     if (r->proto.any != NULL) {
621         nxt_http_proto[r->protocol].ws_frame_start(task, r, ws_frame);
622     }
623 }
624 
625 
626 void
nxt_http_request_send(nxt_task_t * task,nxt_http_request_t * r,nxt_buf_t * out)627 nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
628 {
629     if (nxt_fast_path(r->proto.any != NULL)) {
630         nxt_http_proto[r->protocol].send(task, r, out);
631     }
632 }
633 
634 
635 nxt_buf_t *
nxt_http_buf_mem(nxt_task_t * task,nxt_http_request_t * r,size_t size)636 nxt_http_buf_mem(nxt_task_t *task, nxt_http_request_t *r, size_t size)
637 {
638     nxt_buf_t  *b;
639 
640     b = nxt_buf_mem_alloc(r->mem_pool, size, 0);
641     if (nxt_fast_path(b != NULL)) {
642         b->completion_handler = nxt_http_request_mem_buf_completion;
643         b->parent = r;
644         nxt_mp_retain(r->mem_pool);
645 
646     } else {
647         nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
648     }
649 
650     return b;
651 }
652 
653 
654 static void
nxt_http_request_mem_buf_completion(nxt_task_t * task,void * obj,void * data)655 nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data)
656 {
657     nxt_buf_t           *b, *next;
658     nxt_http_request_t  *r;
659 
660     b = obj;
661     r = data;
662 
663     do {
664         next = b->next;
665 
666         nxt_mp_free(r->mem_pool, b);
667         nxt_mp_release(r->mem_pool);
668 
669         b = next;
670     } while (b != NULL);
671 }
672 
673 
674 nxt_buf_t *
nxt_http_buf_last(nxt_http_request_t * r)675 nxt_http_buf_last(nxt_http_request_t *r)
676 {
677     nxt_buf_t  *last;
678 
679     last = r->last;
680     r->last = NULL;
681 
682     return last;
683 }
684 
685 
686 static void
nxt_http_request_done(nxt_task_t * task,void * obj,void * data)687 nxt_http_request_done(nxt_task_t *task, void *obj, void *data)
688 {
689     nxt_http_request_t  *r;
690 
691     r = data;
692 
693     nxt_debug(task, "http request done");
694 
695     nxt_http_request_close_handler(task, r, r->proto.any);
696 }
697 
698 
699 void
nxt_http_request_error_handler(nxt_task_t * task,void * obj,void * data)700 nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data)
701 {
702     nxt_http_proto_t    proto;
703     nxt_http_request_t  *r;
704 
705     r = obj;
706     proto.any = data;
707 
708     nxt_debug(task, "http request error handler");
709 
710     r->error = 1;
711 
712     if (nxt_fast_path(proto.any != NULL)) {
713         nxt_http_proto[r->protocol].discard(task, r, nxt_http_buf_last(r));
714     }
715 }
716 
717 
718 void
nxt_http_request_close_handler(nxt_task_t * task,void * obj,void * data)719 nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
720 {
721     nxt_http_proto_t         proto;
722     nxt_http_request_t       *r;
723     nxt_http_protocol_t      protocol;
724     nxt_socket_conf_joint_t  *conf;
725     nxt_router_access_log_t  *access_log;
726 
727     r = obj;
728     proto.any = data;
729 
730     nxt_debug(task, "http request close handler");
731 
732     conf = r->conf;
733 
734     if (!r->logged) {
735         r->logged = 1;
736 
737         access_log = conf->socket_conf->router_conf->access_log;
738 
739         if (access_log != NULL) {
740             access_log->handler(task, r, access_log);
741         }
742     }
743 
744     r->proto.any = NULL;
745 
746     if (r->body != NULL && nxt_buf_is_file(r->body)
747         && r->body->file->fd != -1)
748     {
749         nxt_fd_close(r->body->file->fd);
750 
751         r->body->file->fd = -1;
752     }
753 
754     if (nxt_fast_path(proto.any != NULL)) {
755         protocol = r->protocol;
756 
757         nxt_http_proto[protocol].close(task, proto, conf);
758 
759         nxt_mp_release(r->mem_pool);
760     }
761 }
762 
763 
764 static u_char *
nxt_http_date_cache_handler(u_char * buf,nxt_realtime_t * now,struct tm * tm,size_t size,const char * format)765 nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm,
766     size_t size, const char *format)
767 {
768     return nxt_http_date(buf, tm);
769 }
770 
771 
772 nxt_array_t *
nxt_http_arguments_parse(nxt_http_request_t * r)773 nxt_http_arguments_parse(nxt_http_request_t *r)
774 {
775     size_t                 name_length;
776     u_char                 *p, *dst, *dst_start, *start, *end, *name;
777     uint8_t                d0, d1;
778     uint32_t               hash;
779     nxt_array_t            *args;
780     nxt_http_name_value_t  *nv;
781 
782     if (r->arguments != NULL) {
783         return r->arguments;
784     }
785 
786     args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
787     if (nxt_slow_path(args == NULL)) {
788         return NULL;
789     }
790 
791     hash = NXT_HTTP_FIELD_HASH_INIT;
792     name = NULL;
793     name_length = 0;
794 
795     dst_start = nxt_mp_nget(r->mem_pool, r->args->length);
796     if (nxt_slow_path(dst_start == NULL)) {
797         return NULL;
798     }
799 
800     r->args_decoded.start = dst_start;
801 
802     start = r->args->start;
803     end = start + r->args->length;
804 
805     for (p = start, dst = dst_start; p < end; p++, dst++) {
806         *dst = *p;
807 
808         switch (*p) {
809         case '=':
810             if (name == NULL) {
811                 name_length = dst - dst_start;
812                 name = dst_start;
813                 dst_start = dst + 1;
814             }
815 
816             continue;
817 
818         case '&':
819             if (name_length != 0 || dst != dst_start) {
820                 nv = nxt_http_argument(args, name, name_length, hash, dst_start,
821                                        dst);
822                 if (nxt_slow_path(nv == NULL)) {
823                     return NULL;
824                 }
825             }
826 
827             hash = NXT_HTTP_FIELD_HASH_INIT;
828             name_length = 0;
829             name = NULL;
830             dst_start = dst + 1;
831 
832             continue;
833 
834         case '+':
835             *dst = ' ';
836 
837             break;
838 
839         case '%':
840             if (nxt_slow_path(end - p <= 2)) {
841                 break;
842             }
843 
844             d0 = nxt_hex2int[p[1]];
845             d1 = nxt_hex2int[p[2]];
846 
847             if (nxt_slow_path((d0 | d1) >= 16)) {
848                 break;
849             }
850 
851             p += 2;
852             *dst = (d0 << 4) + d1;
853 
854             break;
855         }
856 
857         if (name == NULL) {
858             hash = nxt_http_field_hash_char(hash, *dst);
859         }
860     }
861 
862     r->args_decoded.length = dst - r->args_decoded.start;
863 
864     if (name_length != 0 || dst != dst_start) {
865         nv = nxt_http_argument(args, name, name_length, hash, dst_start, dst);
866         if (nxt_slow_path(nv == NULL)) {
867             return NULL;
868         }
869     }
870 
871     r->arguments = args;
872 
873     return args;
874 }
875 
876 
877 static nxt_http_name_value_t *
nxt_http_argument(nxt_array_t * array,u_char * name,size_t name_length,uint32_t hash,u_char * start,u_char * end)878 nxt_http_argument(nxt_array_t *array, u_char *name, size_t name_length,
879     uint32_t hash, u_char *start, u_char *end)
880 {
881     size_t                 length;
882     nxt_http_name_value_t  *nv;
883 
884     nv = nxt_array_add(array);
885     if (nxt_slow_path(nv == NULL)) {
886         return NULL;
887     }
888 
889     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
890 
891     length = end - start;
892 
893     if (name == NULL) {
894         name_length = length;
895         name = start;
896         length = 0;
897     }
898 
899     nv->name_length = name_length;
900     nv->value_length = length;
901     nv->name = name;
902     nv->value = start;
903 
904     return nv;
905 }
906 
907 
908 nxt_array_t *
nxt_http_cookies_parse(nxt_http_request_t * r)909 nxt_http_cookies_parse(nxt_http_request_t *r)
910 {
911     nxt_int_t         ret;
912     nxt_array_t       *cookies;
913     nxt_http_field_t  *f;
914 
915     if (r->cookies != NULL) {
916         return r->cookies;
917     }
918 
919     cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
920     if (nxt_slow_path(cookies == NULL)) {
921         return NULL;
922     }
923 
924     nxt_list_each(f, r->fields) {
925 
926         if (f->hash != NXT_HTTP_COOKIE_HASH
927             || f->name_length != 6
928             || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0)
929         {
930             continue;
931         }
932 
933         ret = nxt_http_cookie_parse(cookies, f->value,
934                                     f->value + f->value_length);
935         if (ret != NXT_OK) {
936             return NULL;
937         }
938 
939     } nxt_list_loop;
940 
941     r->cookies = cookies;
942 
943     return cookies;
944 }
945 
946 
947 static nxt_int_t
nxt_http_cookie_parse(nxt_array_t * cookies,u_char * start,u_char * end)948 nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end)
949 {
950     size_t                 name_length;
951     u_char                 c, *p, *name;
952     nxt_http_name_value_t  *nv;
953 
954     name = NULL;
955     name_length = 0;
956 
957     for (p = start; p < end; p++) {
958         c = *p;
959 
960         if (c == '=') {
961             while (start[0] == ' ') { start++; }
962 
963             name_length = p - start;
964 
965             if (name_length != 0) {
966                 name = start;
967             }
968 
969             start = p + 1;
970 
971         } else if (c == ';') {
972             if (name != NULL) {
973                 nv = nxt_http_cookie(cookies, name, name_length, start, p);
974                 if (nxt_slow_path(nv == NULL)) {
975                     return NXT_ERROR;
976                 }
977             }
978 
979             name = NULL;
980             start = p + 1;
981          }
982     }
983 
984     if (name != NULL) {
985         nv = nxt_http_cookie(cookies, name, name_length, start, p);
986         if (nxt_slow_path(nv == NULL)) {
987             return NXT_ERROR;
988         }
989     }
990 
991     return NXT_OK;
992 }
993 
994 
995 static nxt_http_name_value_t *
nxt_http_cookie(nxt_array_t * array,u_char * name,size_t name_length,u_char * start,u_char * end)996 nxt_http_cookie(nxt_array_t *array, u_char *name, size_t name_length,
997     u_char *start, u_char *end)
998 {
999     u_char                 c, *p;
1000     uint32_t               hash;
1001     nxt_http_name_value_t  *nv;
1002 
1003     nv = nxt_array_add(array);
1004     if (nxt_slow_path(nv == NULL)) {
1005         return NULL;
1006     }
1007 
1008     nv->name_length = name_length;
1009     nv->name = name;
1010 
1011     hash = NXT_HTTP_FIELD_HASH_INIT;
1012 
1013     for (p = name; p < name + name_length; p++) {
1014         c = *p;
1015         hash = nxt_http_field_hash_char(hash, c);
1016     }
1017 
1018     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
1019 
1020     while (start < end && end[-1] == ' ') { end--; }
1021 
1022     nv->value_length = end - start;
1023     nv->value = start;
1024 
1025     return nv;
1026 }
1027