xref: /unit/src/nxt_http_request.c (revision 2133)
1431Sigor@sysoev.ru 
2431Sigor@sysoev.ru /*
3431Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4431Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5431Sigor@sysoev.ru  */
6431Sigor@sysoev.ru 
7431Sigor@sysoev.ru #include <nxt_router.h>
8431Sigor@sysoev.ru #include <nxt_http.h>
9431Sigor@sysoev.ru 
10431Sigor@sysoev.ru 
11945Svbart@nginx.com static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp);
12431Sigor@sysoev.ru static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data);
13*2133Sz.hong@f5.com static nxt_int_t nxt_http_request_forward(nxt_task_t *task,
14*2133Sz.hong@f5.com     nxt_http_request_t *r, nxt_http_forward_t *forward);
15*2133Sz.hong@f5.com static void nxt_http_request_forward_client_ip(nxt_http_request_t *r,
16*2133Sz.hong@f5.com     nxt_http_forward_t *forward, nxt_array_t *fields);
171936So.canty@f5.com static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr(
181936So.canty@f5.com     nxt_http_request_t *r, u_char *start, size_t len);
19*2133Sz.hong@f5.com static void nxt_http_request_forward_protocol(nxt_http_request_t *r,
20*2133Sz.hong@f5.com     nxt_http_field_t *field);
211563Svbart@nginx.com static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data);
221011Smax.romanov@nginx.com static void nxt_http_request_proto_info(nxt_task_t *task,
231011Smax.romanov@nginx.com     nxt_http_request_t *r);
24608Sigor@sysoev.ru static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
25608Sigor@sysoev.ru     void *data);
26431Sigor@sysoev.ru static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
27431Sigor@sysoev.ru 
281183Svbart@nginx.com static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now,
291183Svbart@nginx.com     struct tm *tm, size_t size, const char *format);
30543Svbart@nginx.com 
312104Sz.hong@f5.com static nxt_http_name_value_t *nxt_http_argument(nxt_array_t *array,
322104Sz.hong@f5.com     u_char *name, size_t name_length, uint32_t hash, u_char *start,
332104Sz.hong@f5.com     u_char *end);
342104Sz.hong@f5.com static nxt_int_t nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start,
352104Sz.hong@f5.com     u_char *end);
362104Sz.hong@f5.com static nxt_http_name_value_t *nxt_http_cookie(nxt_array_t *array, u_char *name,
372104Sz.hong@f5.com     size_t name_length, u_char *start, u_char *end);
382104Sz.hong@f5.com 
392104Sz.hong@f5.com 
402104Sz.hong@f5.com #define NXT_HTTP_COOKIE_HASH                                                  \
412104Sz.hong@f5.com     (nxt_http_field_hash_end(                                                 \
422104Sz.hong@f5.com      nxt_http_field_hash_char(                                                \
432104Sz.hong@f5.com      nxt_http_field_hash_char(                                                \
442104Sz.hong@f5.com      nxt_http_field_hash_char(                                                \
452104Sz.hong@f5.com      nxt_http_field_hash_char(                                                \
462104Sz.hong@f5.com      nxt_http_field_hash_char(                                                \
472104Sz.hong@f5.com      nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT,                       \
482104Sz.hong@f5.com         'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF)
492104Sz.hong@f5.com 
50431Sigor@sysoev.ru 
51431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_init_state;
52431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_body_state;
53431Sigor@sysoev.ru 
54431Sigor@sysoev.ru 
55740Sigor@sysoev.ru nxt_time_string_t  nxt_http_date_cache = {
56740Sigor@sysoev.ru     (nxt_atomic_uint_t) -1,
571183Svbart@nginx.com     nxt_http_date_cache_handler,
581183Svbart@nginx.com     NULL,
591183Svbart@nginx.com     NXT_HTTP_DATE_LEN,
60740Sigor@sysoev.ru     NXT_THREAD_TIME_GMT,
61740Sigor@sysoev.ru     NXT_THREAD_TIME_SEC,
62740Sigor@sysoev.ru };
63740Sigor@sysoev.ru 
64740Sigor@sysoev.ru 
65431Sigor@sysoev.ru nxt_int_t
661459Smax.romanov@nginx.com nxt_http_init(nxt_task_t *task)
67431Sigor@sysoev.ru {
68431Sigor@sysoev.ru     nxt_int_t  ret;
69431Sigor@sysoev.ru 
701459Smax.romanov@nginx.com     ret = nxt_h1p_init(task);
71431Sigor@sysoev.ru 
72431Sigor@sysoev.ru     if (ret != NXT_OK) {
73431Sigor@sysoev.ru         return ret;
74431Sigor@sysoev.ru     }
75431Sigor@sysoev.ru 
761459Smax.romanov@nginx.com     return nxt_http_response_hash_init(task);
77431Sigor@sysoev.ru }
78431Sigor@sysoev.ru 
79431Sigor@sysoev.ru 
80431Sigor@sysoev.ru nxt_int_t
81431Sigor@sysoev.ru nxt_http_request_host(void *ctx, nxt_http_field_t *field, uintptr_t data)
82431Sigor@sysoev.ru {
83945Svbart@nginx.com     nxt_int_t           ret;
84945Svbart@nginx.com     nxt_str_t           host;
85431Sigor@sysoev.ru     nxt_http_request_t  *r;
86431Sigor@sysoev.ru 
87431Sigor@sysoev.ru     r = ctx;
88431Sigor@sysoev.ru 
89945Svbart@nginx.com     if (nxt_slow_path(r->host.start != NULL)) {
90945Svbart@nginx.com         return NXT_HTTP_BAD_REQUEST;
91945Svbart@nginx.com     }
92945Svbart@nginx.com 
93945Svbart@nginx.com     host.length = field->value_length;
94945Svbart@nginx.com     host.start = field->value;
95945Svbart@nginx.com 
96945Svbart@nginx.com     ret = nxt_http_validate_host(&host, r->mem_pool);
97945Svbart@nginx.com 
98945Svbart@nginx.com     if (nxt_fast_path(ret == NXT_OK)) {
99945Svbart@nginx.com         r->host = host;
100945Svbart@nginx.com     }
101945Svbart@nginx.com 
102945Svbart@nginx.com     return ret;
103945Svbart@nginx.com }
104945Svbart@nginx.com 
105945Svbart@nginx.com 
106945Svbart@nginx.com static nxt_int_t
107945Svbart@nginx.com nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp)
108945Svbart@nginx.com {
109945Svbart@nginx.com     u_char      *h, ch;
110945Svbart@nginx.com     size_t      i, dot_pos, host_length;
111945Svbart@nginx.com     nxt_bool_t  lowcase;
112945Svbart@nginx.com 
113945Svbart@nginx.com     enum {
114945Svbart@nginx.com         sw_usual,
115945Svbart@nginx.com         sw_literal,
116945Svbart@nginx.com         sw_rest
117945Svbart@nginx.com     } state;
118945Svbart@nginx.com 
119945Svbart@nginx.com     dot_pos = host->length;
120945Svbart@nginx.com     host_length = host->length;
121945Svbart@nginx.com 
122945Svbart@nginx.com     h = host->start;
123945Svbart@nginx.com 
124945Svbart@nginx.com     lowcase = 0;
125945Svbart@nginx.com     state = sw_usual;
126945Svbart@nginx.com 
127945Svbart@nginx.com     for (i = 0; i < host->length; i++) {
128945Svbart@nginx.com         ch = h[i];
129945Svbart@nginx.com 
130945Svbart@nginx.com         if (ch > ']') {
131945Svbart@nginx.com             /* Short path. */
132945Svbart@nginx.com             continue;
133945Svbart@nginx.com         }
134945Svbart@nginx.com 
135945Svbart@nginx.com         switch (ch) {
136945Svbart@nginx.com 
137945Svbart@nginx.com         case '.':
138945Svbart@nginx.com             if (dot_pos == i - 1) {
139945Svbart@nginx.com                 return NXT_HTTP_BAD_REQUEST;
140945Svbart@nginx.com             }
141431Sigor@sysoev.ru 
142945Svbart@nginx.com             dot_pos = i;
143945Svbart@nginx.com             break;
144945Svbart@nginx.com 
145945Svbart@nginx.com         case ':':
146945Svbart@nginx.com             if (state == sw_usual) {
147945Svbart@nginx.com                 host_length = i;
148945Svbart@nginx.com                 state = sw_rest;
149945Svbart@nginx.com             }
150945Svbart@nginx.com 
151945Svbart@nginx.com             break;
152945Svbart@nginx.com 
153945Svbart@nginx.com         case '[':
154945Svbart@nginx.com             if (i == 0) {
155945Svbart@nginx.com                 state = sw_literal;
156945Svbart@nginx.com             }
157945Svbart@nginx.com 
158945Svbart@nginx.com             break;
159945Svbart@nginx.com 
160945Svbart@nginx.com         case ']':
161945Svbart@nginx.com             if (state == sw_literal) {
162945Svbart@nginx.com                 host_length = i + 1;
163945Svbart@nginx.com                 state = sw_rest;
164945Svbart@nginx.com             }
165945Svbart@nginx.com 
166945Svbart@nginx.com             break;
167945Svbart@nginx.com 
168945Svbart@nginx.com         case '/':
169945Svbart@nginx.com             return NXT_HTTP_BAD_REQUEST;
170945Svbart@nginx.com 
171945Svbart@nginx.com         default:
172945Svbart@nginx.com             if (ch >= 'A' && ch <= 'Z') {
173945Svbart@nginx.com                 lowcase = 1;
174945Svbart@nginx.com             }
175945Svbart@nginx.com 
176945Svbart@nginx.com             break;
177945Svbart@nginx.com         }
178945Svbart@nginx.com     }
179945Svbart@nginx.com 
180945Svbart@nginx.com     if (dot_pos == host_length - 1) {
181945Svbart@nginx.com         host_length--;
182945Svbart@nginx.com     }
183945Svbart@nginx.com 
184945Svbart@nginx.com     host->length = host_length;
185945Svbart@nginx.com 
186945Svbart@nginx.com     if (lowcase) {
187945Svbart@nginx.com         host->start = nxt_mp_nget(mp, host_length);
188945Svbart@nginx.com         if (nxt_slow_path(host->start == NULL)) {
189945Svbart@nginx.com             return NXT_HTTP_INTERNAL_SERVER_ERROR;
190945Svbart@nginx.com         }
191945Svbart@nginx.com 
192945Svbart@nginx.com         nxt_memcpy_lowcase(host->start, h, host_length);
193945Svbart@nginx.com     }
194431Sigor@sysoev.ru 
195431Sigor@sysoev.ru     return NXT_OK;
196431Sigor@sysoev.ru }
197431Sigor@sysoev.ru 
198431Sigor@sysoev.ru 
199431Sigor@sysoev.ru nxt_int_t
200431Sigor@sysoev.ru nxt_http_request_field(void *ctx, nxt_http_field_t *field, uintptr_t offset)
201431Sigor@sysoev.ru {
202431Sigor@sysoev.ru     nxt_http_request_t  *r;
203431Sigor@sysoev.ru 
204431Sigor@sysoev.ru     r = ctx;
205431Sigor@sysoev.ru 
206431Sigor@sysoev.ru     nxt_value_at(nxt_http_field_t *, r, offset) = field;
207431Sigor@sysoev.ru 
208431Sigor@sysoev.ru     return NXT_OK;
209431Sigor@sysoev.ru }
210431Sigor@sysoev.ru 
211431Sigor@sysoev.ru 
212431Sigor@sysoev.ru nxt_int_t
213431Sigor@sysoev.ru nxt_http_request_content_length(void *ctx, nxt_http_field_t *field,
214431Sigor@sysoev.ru     uintptr_t data)
215431Sigor@sysoev.ru {
2161401Smax.romanov@nginx.com     nxt_off_t           n, max_body_size;
217431Sigor@sysoev.ru     nxt_http_request_t  *r;
218431Sigor@sysoev.ru 
219431Sigor@sysoev.ru     r = ctx;
220431Sigor@sysoev.ru 
221942Svbart@nginx.com     if (nxt_fast_path(r->content_length == NULL)) {
222942Svbart@nginx.com         r->content_length = field;
223431Sigor@sysoev.ru 
224942Svbart@nginx.com         n = nxt_off_t_parse(field->value, field->value_length);
225942Svbart@nginx.com 
226942Svbart@nginx.com         if (nxt_fast_path(n >= 0)) {
227942Svbart@nginx.com             r->content_length_n = n;
2281401Smax.romanov@nginx.com 
2291401Smax.romanov@nginx.com             max_body_size = r->conf->socket_conf->max_body_size;
2301401Smax.romanov@nginx.com 
2311401Smax.romanov@nginx.com             if (nxt_slow_path(n > max_body_size)) {
2321401Smax.romanov@nginx.com                 return NXT_HTTP_PAYLOAD_TOO_LARGE;
2331401Smax.romanov@nginx.com             }
2341401Smax.romanov@nginx.com 
235942Svbart@nginx.com             return NXT_OK;
236942Svbart@nginx.com         }
237918Svbart@nginx.com     }
238918Svbart@nginx.com 
239945Svbart@nginx.com     return NXT_HTTP_BAD_REQUEST;
240431Sigor@sysoev.ru }
241431Sigor@sysoev.ru 
242431Sigor@sysoev.ru 
243431Sigor@sysoev.ru nxt_http_request_t *
244431Sigor@sysoev.ru nxt_http_request_create(nxt_task_t *task)
245431Sigor@sysoev.ru {
246431Sigor@sysoev.ru     nxt_mp_t            *mp;
247608Sigor@sysoev.ru     nxt_buf_t           *last;
248431Sigor@sysoev.ru     nxt_http_request_t  *r;
249431Sigor@sysoev.ru 
2501656Svbart@nginx.com     mp = nxt_mp_create(4096, 128, 512, 32);
251431Sigor@sysoev.ru     if (nxt_slow_path(mp == NULL)) {
252431Sigor@sysoev.ru         return NULL;
253431Sigor@sysoev.ru     }
254431Sigor@sysoev.ru 
255431Sigor@sysoev.ru     r = nxt_mp_zget(mp, sizeof(nxt_http_request_t));
256431Sigor@sysoev.ru     if (nxt_slow_path(r == NULL)) {
257431Sigor@sysoev.ru         goto fail;
258431Sigor@sysoev.ru     }
259431Sigor@sysoev.ru 
260431Sigor@sysoev.ru     r->resp.fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
261451Sigor@sysoev.ru     if (nxt_slow_path(r->resp.fields == NULL)) {
262431Sigor@sysoev.ru         goto fail;
263431Sigor@sysoev.ru     }
264431Sigor@sysoev.ru 
265608Sigor@sysoev.ru     last = nxt_mp_zget(mp, NXT_BUF_SYNC_SIZE);
266608Sigor@sysoev.ru     if (nxt_slow_path(last == NULL)) {
267608Sigor@sysoev.ru         goto fail;
268608Sigor@sysoev.ru     }
269608Sigor@sysoev.ru 
270608Sigor@sysoev.ru     nxt_buf_set_sync(last);
271608Sigor@sysoev.ru     nxt_buf_set_last(last);
272608Sigor@sysoev.ru     last->completion_handler = nxt_http_request_done;
273608Sigor@sysoev.ru     last->parent = r;
274608Sigor@sysoev.ru     r->last = last;
275608Sigor@sysoev.ru 
276431Sigor@sysoev.ru     r->mem_pool = mp;
277431Sigor@sysoev.ru     r->content_length_n = -1;
278431Sigor@sysoev.ru     r->resp.content_length_n = -1;
279431Sigor@sysoev.ru     r->state = &nxt_http_request_init_state;
280431Sigor@sysoev.ru 
281431Sigor@sysoev.ru     return r;
282431Sigor@sysoev.ru 
283431Sigor@sysoev.ru fail:
284431Sigor@sysoev.ru 
285431Sigor@sysoev.ru     nxt_mp_release(mp);
286608Sigor@sysoev.ru 
287431Sigor@sysoev.ru     return NULL;
288431Sigor@sysoev.ru }
289431Sigor@sysoev.ru 
290431Sigor@sysoev.ru 
291431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_init_state
292431Sigor@sysoev.ru     nxt_aligned(64) =
293431Sigor@sysoev.ru {
294431Sigor@sysoev.ru     .ready_handler = nxt_http_request_start,
295431Sigor@sysoev.ru     .error_handler = nxt_http_request_close_handler,
296431Sigor@sysoev.ru };
297431Sigor@sysoev.ru 
298431Sigor@sysoev.ru 
299431Sigor@sysoev.ru static void
300431Sigor@sysoev.ru nxt_http_request_start(nxt_task_t *task, void *obj, void *data)
301431Sigor@sysoev.ru {
3021936So.canty@f5.com     nxt_int_t           ret;
303*2133Sz.hong@f5.com     nxt_socket_conf_t   *skcf;
304431Sigor@sysoev.ru     nxt_http_request_t  *r;
305431Sigor@sysoev.ru 
306431Sigor@sysoev.ru     r = obj;
307431Sigor@sysoev.ru 
308431Sigor@sysoev.ru     r->state = &nxt_http_request_body_state;
309431Sigor@sysoev.ru 
310*2133Sz.hong@f5.com     skcf = r->conf->socket_conf;
311*2133Sz.hong@f5.com 
312*2133Sz.hong@f5.com     if (skcf->forwarded != NULL) {
313*2133Sz.hong@f5.com         ret = nxt_http_request_forward(task, r, skcf->forwarded);
314*2133Sz.hong@f5.com         if (nxt_slow_path(ret != NXT_OK)) {
315*2133Sz.hong@f5.com             goto fail;
316*2133Sz.hong@f5.com         }
317*2133Sz.hong@f5.com     }
318*2133Sz.hong@f5.com 
319*2133Sz.hong@f5.com     if (skcf->client_ip != NULL) {
320*2133Sz.hong@f5.com         ret = nxt_http_request_forward(task, r, skcf->client_ip);
321*2133Sz.hong@f5.com         if (nxt_slow_path(ret != NXT_OK)) {
322*2133Sz.hong@f5.com             goto fail;
323*2133Sz.hong@f5.com         }
3241936So.canty@f5.com     }
3251936So.canty@f5.com 
326431Sigor@sysoev.ru     nxt_http_request_read_body(task, r);
327*2133Sz.hong@f5.com 
328*2133Sz.hong@f5.com     return;
329*2133Sz.hong@f5.com 
330*2133Sz.hong@f5.com fail:
331*2133Sz.hong@f5.com     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
332431Sigor@sysoev.ru }
333431Sigor@sysoev.ru 
334431Sigor@sysoev.ru 
3351936So.canty@f5.com static nxt_int_t
336*2133Sz.hong@f5.com nxt_http_request_forward(nxt_task_t *task, nxt_http_request_t *r,
337*2133Sz.hong@f5.com     nxt_http_forward_t *forward)
3381936So.canty@f5.com {
339*2133Sz.hong@f5.com     nxt_int_t                  ret;
340*2133Sz.hong@f5.com     nxt_array_t                *client_ip_fields;
341*2133Sz.hong@f5.com     nxt_http_field_t           *f, **fields, *protocol_field;
342*2133Sz.hong@f5.com     nxt_http_forward_header_t  *client_ip, *protocol;
3431936So.canty@f5.com 
3442132Sz.hong@f5.com     ret = nxt_http_route_addr_rule(r, forward->source, r->remote);
3451936So.canty@f5.com     if (ret <= 0) {
3461936So.canty@f5.com         return NXT_OK;
3471936So.canty@f5.com     }
3481936So.canty@f5.com 
3492132Sz.hong@f5.com     client_ip = &forward->client_ip;
350*2133Sz.hong@f5.com     protocol = &forward->protocol;
3511936So.canty@f5.com 
352*2133Sz.hong@f5.com     if (client_ip->header != NULL) {
353*2133Sz.hong@f5.com         client_ip_fields = nxt_array_create(r->mem_pool, 1,
354*2133Sz.hong@f5.com                                             sizeof(nxt_http_field_t *));
355*2133Sz.hong@f5.com         if (nxt_slow_path(client_ip_fields == NULL)) {
356*2133Sz.hong@f5.com             return NXT_ERROR;
357*2133Sz.hong@f5.com         }
358*2133Sz.hong@f5.com 
359*2133Sz.hong@f5.com     } else {
360*2133Sz.hong@f5.com         client_ip_fields = NULL;
3611936So.canty@f5.com     }
3621936So.canty@f5.com 
363*2133Sz.hong@f5.com     protocol_field = NULL;
364*2133Sz.hong@f5.com 
3651936So.canty@f5.com     nxt_list_each(f, r->fields) {
366*2133Sz.hong@f5.com         if (client_ip_fields != NULL
367*2133Sz.hong@f5.com             && f->hash == client_ip->header_hash
3681936So.canty@f5.com             && f->value_length > 0
369*2133Sz.hong@f5.com             && f->name_length == client_ip->header->length
370*2133Sz.hong@f5.com             && nxt_memcasecmp(f->name, client_ip->header->start,
371*2133Sz.hong@f5.com                               client_ip->header->length) == 0)
3721936So.canty@f5.com         {
373*2133Sz.hong@f5.com             fields = nxt_array_add(client_ip_fields);
3741936So.canty@f5.com             if (nxt_slow_path(fields == NULL)) {
3751936So.canty@f5.com                 return NXT_ERROR;
3761936So.canty@f5.com             }
3771936So.canty@f5.com 
3781936So.canty@f5.com             *fields = f;
3791936So.canty@f5.com         }
380*2133Sz.hong@f5.com 
381*2133Sz.hong@f5.com         if (protocol->header != NULL
382*2133Sz.hong@f5.com             && protocol_field == NULL
383*2133Sz.hong@f5.com             && f->hash == protocol->header_hash
384*2133Sz.hong@f5.com             && f->value_length > 0
385*2133Sz.hong@f5.com             && f->name_length == protocol->header->length
386*2133Sz.hong@f5.com             && nxt_memcasecmp(f->name, protocol->header->start,
387*2133Sz.hong@f5.com                               protocol->header->length) == 0)
388*2133Sz.hong@f5.com         {
389*2133Sz.hong@f5.com             protocol_field = f;
390*2133Sz.hong@f5.com         }
3911936So.canty@f5.com     } nxt_list_loop;
3921936So.canty@f5.com 
393*2133Sz.hong@f5.com     if (client_ip_fields != NULL) {
394*2133Sz.hong@f5.com         nxt_http_request_forward_client_ip(r, forward, client_ip_fields);
395*2133Sz.hong@f5.com     }
396*2133Sz.hong@f5.com 
397*2133Sz.hong@f5.com     if (protocol_field != NULL) {
398*2133Sz.hong@f5.com         nxt_http_request_forward_protocol(r, protocol_field);
399*2133Sz.hong@f5.com     }
400*2133Sz.hong@f5.com 
401*2133Sz.hong@f5.com     return NXT_OK;
402*2133Sz.hong@f5.com }
403*2133Sz.hong@f5.com 
404*2133Sz.hong@f5.com 
405*2133Sz.hong@f5.com static void
406*2133Sz.hong@f5.com nxt_http_request_forward_client_ip(nxt_http_request_t *r,
407*2133Sz.hong@f5.com     nxt_http_forward_t *forward, nxt_array_t *fields)
408*2133Sz.hong@f5.com {
409*2133Sz.hong@f5.com     u_char            *start, *p;
410*2133Sz.hong@f5.com     nxt_int_t         ret, i, len;
411*2133Sz.hong@f5.com     nxt_sockaddr_t    *sa, *prev_sa;
412*2133Sz.hong@f5.com     nxt_http_field_t  **f;
413*2133Sz.hong@f5.com 
4141936So.canty@f5.com     prev_sa = r->remote;
415*2133Sz.hong@f5.com     f = (nxt_http_field_t **) fields->elts;
4161936So.canty@f5.com 
417*2133Sz.hong@f5.com     i = fields->nelts;
4181936So.canty@f5.com 
4191936So.canty@f5.com     while (i-- > 0) {
420*2133Sz.hong@f5.com         start = f[i]->value;
421*2133Sz.hong@f5.com         len = f[i]->value_length;
4221936So.canty@f5.com 
4231936So.canty@f5.com         do {
4241936So.canty@f5.com             for (p = start + len - 1; p > start; p--, len--) {
4251936So.canty@f5.com                 if (*p != ' ' && *p != ',') {
4261936So.canty@f5.com                     break;
4271936So.canty@f5.com                 }
4281936So.canty@f5.com             }
4291936So.canty@f5.com 
4301936So.canty@f5.com             for (/* void */; p > start; p--) {
4311936So.canty@f5.com                 if (*p == ' ' || *p == ',') {
4321936So.canty@f5.com                     p++;
4331936So.canty@f5.com                     break;
4341936So.canty@f5.com                 }
4351936So.canty@f5.com             }
4361936So.canty@f5.com 
4371936So.canty@f5.com             sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start));
4381936So.canty@f5.com             if (nxt_slow_path(sa == NULL)) {
4391936So.canty@f5.com                 if (prev_sa != NULL) {
4401936So.canty@f5.com                     r->remote = prev_sa;
4411936So.canty@f5.com                 }
4421936So.canty@f5.com 
443*2133Sz.hong@f5.com                 return;
4441936So.canty@f5.com             }
4451936So.canty@f5.com 
4462132Sz.hong@f5.com             if (!forward->recursive) {
4471936So.canty@f5.com                 r->remote = sa;
448*2133Sz.hong@f5.com                 return;
4491936So.canty@f5.com             }
4501936So.canty@f5.com 
4512132Sz.hong@f5.com             ret = nxt_http_route_addr_rule(r, forward->source, sa);
4521936So.canty@f5.com             if (ret <= 0 || (i == 0 && p == start)) {
4531936So.canty@f5.com                 r->remote = sa;
454*2133Sz.hong@f5.com                 return;
4551936So.canty@f5.com             }
4561936So.canty@f5.com 
4571936So.canty@f5.com             prev_sa = sa;
4581936So.canty@f5.com             len = p - 1 - start;
4591936So.canty@f5.com 
4601936So.canty@f5.com         } while (len > 0);
4611936So.canty@f5.com     }
4621936So.canty@f5.com }
4631936So.canty@f5.com 
4641936So.canty@f5.com 
4651936So.canty@f5.com static nxt_sockaddr_t *
4661936So.canty@f5.com nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start,
4671936So.canty@f5.com     size_t len)
4681936So.canty@f5.com {
4691936So.canty@f5.com     nxt_str_t       addr;
4701936So.canty@f5.com     nxt_sockaddr_t  *sa;
4711936So.canty@f5.com 
4721936So.canty@f5.com     addr.start = start;
4731936So.canty@f5.com     addr.length = len;
4741936So.canty@f5.com 
4751936So.canty@f5.com     sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr);
4761936So.canty@f5.com     if (nxt_slow_path(sa == NULL)) {
4771936So.canty@f5.com         return NULL;
4781936So.canty@f5.com     }
4791936So.canty@f5.com 
4801936So.canty@f5.com     switch (sa->u.sockaddr.sa_family) {
4811936So.canty@f5.com         case AF_INET:
4821936So.canty@f5.com             if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) {
4831936So.canty@f5.com                 return NULL;
4841936So.canty@f5.com             }
4851936So.canty@f5.com 
4861936So.canty@f5.com             break;
4871936So.canty@f5.com 
4881936So.canty@f5.com #if (NXT_INET6)
4891936So.canty@f5.com         case AF_INET6:
4901936So.canty@f5.com             if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) {
4911936So.canty@f5.com                 return NULL;
4921936So.canty@f5.com             }
4931936So.canty@f5.com 
4941936So.canty@f5.com             break;
4951936So.canty@f5.com #endif /* NXT_INET6 */
4961936So.canty@f5.com 
4971936So.canty@f5.com         default:
4981936So.canty@f5.com             return NULL;
4991936So.canty@f5.com     }
5001936So.canty@f5.com 
5011936So.canty@f5.com     return sa;
5021936So.canty@f5.com }
5031936So.canty@f5.com 
5041936So.canty@f5.com 
505*2133Sz.hong@f5.com static void
506*2133Sz.hong@f5.com nxt_http_request_forward_protocol(nxt_http_request_t *r,
507*2133Sz.hong@f5.com     nxt_http_field_t *field)
508*2133Sz.hong@f5.com {
509*2133Sz.hong@f5.com     if (field->value_length == 4) {
510*2133Sz.hong@f5.com         if (nxt_memcasecmp(field->value, "http", 4) == 0) {
511*2133Sz.hong@f5.com             r->tls = 0;
512*2133Sz.hong@f5.com         }
513*2133Sz.hong@f5.com 
514*2133Sz.hong@f5.com     } else if (field->value_length == 5) {
515*2133Sz.hong@f5.com         if (nxt_memcasecmp(field->value, "https", 5) == 0) {
516*2133Sz.hong@f5.com             r->tls = 1;
517*2133Sz.hong@f5.com         }
518*2133Sz.hong@f5.com 
519*2133Sz.hong@f5.com     } else if (field->value_length == 2) {
520*2133Sz.hong@f5.com         if (nxt_memcasecmp(field->value, "on", 2) == 0) {
521*2133Sz.hong@f5.com             r->tls = 1;
522*2133Sz.hong@f5.com         }
523*2133Sz.hong@f5.com     }
524*2133Sz.hong@f5.com }
525*2133Sz.hong@f5.com 
526*2133Sz.hong@f5.com 
527431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_body_state
528431Sigor@sysoev.ru     nxt_aligned(64) =
529431Sigor@sysoev.ru {
5301563Svbart@nginx.com     .ready_handler = nxt_http_request_ready,
531431Sigor@sysoev.ru     .error_handler = nxt_http_request_close_handler,
532431Sigor@sysoev.ru };
533431Sigor@sysoev.ru 
534431Sigor@sysoev.ru 
535431Sigor@sysoev.ru static void
5361563Svbart@nginx.com nxt_http_request_ready(nxt_task_t *task, void *obj, void *data)
537964Sigor@sysoev.ru {
5381264Sigor@sysoev.ru     nxt_http_action_t   *action;
539964Sigor@sysoev.ru     nxt_http_request_t  *r;
540964Sigor@sysoev.ru 
541964Sigor@sysoev.ru     r = obj;
5421264Sigor@sysoev.ru     action = r->conf->socket_conf->action;
543964Sigor@sysoev.ru 
5441563Svbart@nginx.com     nxt_http_request_action(task, r, action);
5451563Svbart@nginx.com }
5461563Svbart@nginx.com 
5471563Svbart@nginx.com 
5481563Svbart@nginx.com void
5491563Svbart@nginx.com nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
5501563Svbart@nginx.com     nxt_http_action_t *action)
5511563Svbart@nginx.com {
5521264Sigor@sysoev.ru     if (nxt_fast_path(action != NULL)) {
553964Sigor@sysoev.ru 
5541060Sigor@sysoev.ru         do {
5551264Sigor@sysoev.ru             action = action->handler(task, r, action);
556964Sigor@sysoev.ru 
5571264Sigor@sysoev.ru             if (action == NULL) {
5581060Sigor@sysoev.ru                 return;
5591060Sigor@sysoev.ru             }
560964Sigor@sysoev.ru 
5611264Sigor@sysoev.ru             if (action == NXT_HTTP_ACTION_ERROR) {
5621060Sigor@sysoev.ru                 break;
5631060Sigor@sysoev.ru             }
5641060Sigor@sysoev.ru 
5651060Sigor@sysoev.ru         } while (r->pass_count++ < 255);
566964Sigor@sysoev.ru     }
567964Sigor@sysoev.ru 
568964Sigor@sysoev.ru     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
569964Sigor@sysoev.ru }
570964Sigor@sysoev.ru 
571964Sigor@sysoev.ru 
5721264Sigor@sysoev.ru nxt_http_action_t *
5731264Sigor@sysoev.ru nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r,
5741264Sigor@sysoev.ru     nxt_http_action_t *action)
575431Sigor@sysoev.ru {
5761264Sigor@sysoev.ru     nxt_debug(task, "http application handler");
577431Sigor@sysoev.ru 
578431Sigor@sysoev.ru     /*
579431Sigor@sysoev.ru      * TODO: need an application flag to get local address
580431Sigor@sysoev.ru      * required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go.
581431Sigor@sysoev.ru      */
5821011Smax.romanov@nginx.com     nxt_http_request_proto_info(task, r);
583431Sigor@sysoev.ru 
584967Svbart@nginx.com     if (r->host.length != 0) {
5851007Salexander.borisov@nginx.com         r->server_name = r->host;
586967Svbart@nginx.com 
587967Svbart@nginx.com     } else {
5881007Salexander.borisov@nginx.com         nxt_str_set(&r->server_name, "localhost");
589431Sigor@sysoev.ru     }
590431Sigor@sysoev.ru 
5911925Sz.hong@f5.com     nxt_router_process_http_request(task, r, action);
592964Sigor@sysoev.ru 
593964Sigor@sysoev.ru     return NULL;
594431Sigor@sysoev.ru }
595431Sigor@sysoev.ru 
596431Sigor@sysoev.ru 
5971011Smax.romanov@nginx.com static void
5981011Smax.romanov@nginx.com nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r)
5991011Smax.romanov@nginx.com {
6001112Sigor@sysoev.ru     if (nxt_fast_path(r->proto.any != NULL)) {
6011112Sigor@sysoev.ru         nxt_http_proto[r->protocol].local_addr(task, r);
6021011Smax.romanov@nginx.com     }
6031011Smax.romanov@nginx.com }
6041011Smax.romanov@nginx.com 
6051011Smax.romanov@nginx.com 
606431Sigor@sysoev.ru void
607431Sigor@sysoev.ru nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r)
608431Sigor@sysoev.ru {
6091112Sigor@sysoev.ru     if (nxt_fast_path(r->proto.any != NULL)) {
6101112Sigor@sysoev.ru         nxt_http_proto[r->protocol].body_read(task, r);
611431Sigor@sysoev.ru     }
612431Sigor@sysoev.ru }
613431Sigor@sysoev.ru 
614431Sigor@sysoev.ru 
615431Sigor@sysoev.ru void
6161148Sigor@sysoev.ru nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r,
6171270Sigor@sysoev.ru     nxt_work_handler_t body_handler, void *data)
618431Sigor@sysoev.ru {
619431Sigor@sysoev.ru     u_char            *p, *end;
620543Svbart@nginx.com     nxt_http_field_t  *server, *date, *content_length;
621543Svbart@nginx.com 
622431Sigor@sysoev.ru     /*
623543Svbart@nginx.com      * TODO: "Server", "Date", and "Content-Length" processing should be moved
624431Sigor@sysoev.ru      * to the last header filter.
625431Sigor@sysoev.ru      */
626431Sigor@sysoev.ru 
627431Sigor@sysoev.ru     server = nxt_list_zero_add(r->resp.fields);
628431Sigor@sysoev.ru     if (nxt_slow_path(server == NULL)) {
629431Sigor@sysoev.ru         goto fail;
630431Sigor@sysoev.ru     }
631431Sigor@sysoev.ru 
632673Svbart@nginx.com     nxt_http_field_set(server, "Server", NXT_SERVER);
633431Sigor@sysoev.ru 
634543Svbart@nginx.com     if (r->resp.date == NULL) {
635543Svbart@nginx.com         date = nxt_list_zero_add(r->resp.fields);
636543Svbart@nginx.com         if (nxt_slow_path(date == NULL)) {
637543Svbart@nginx.com             goto fail;
638543Svbart@nginx.com         }
639543Svbart@nginx.com 
640543Svbart@nginx.com         nxt_http_field_name_set(date, "Date");
641543Svbart@nginx.com 
642740Sigor@sysoev.ru         p = nxt_mp_nget(r->mem_pool, nxt_http_date_cache.size);
643543Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
644543Svbart@nginx.com             goto fail;
645543Svbart@nginx.com         }
646543Svbart@nginx.com 
647740Sigor@sysoev.ru         (void) nxt_thread_time_string(task->thread, &nxt_http_date_cache, p);
648543Svbart@nginx.com 
649543Svbart@nginx.com         date->value = p;
650740Sigor@sysoev.ru         date->value_length = nxt_http_date_cache.size;
651543Svbart@nginx.com 
652543Svbart@nginx.com         r->resp.date = date;
653543Svbart@nginx.com     }
654543Svbart@nginx.com 
655431Sigor@sysoev.ru     if (r->resp.content_length_n != -1
656431Sigor@sysoev.ru         && (r->resp.content_length == NULL || r->resp.content_length->skip))
657431Sigor@sysoev.ru     {
658431Sigor@sysoev.ru         content_length = nxt_list_zero_add(r->resp.fields);
659431Sigor@sysoev.ru         if (nxt_slow_path(content_length == NULL)) {
660431Sigor@sysoev.ru             goto fail;
661431Sigor@sysoev.ru         }
662431Sigor@sysoev.ru 
663431Sigor@sysoev.ru         nxt_http_field_name_set(content_length, "Content-Length");
664431Sigor@sysoev.ru 
665431Sigor@sysoev.ru         p = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
666431Sigor@sysoev.ru         if (nxt_slow_path(p == NULL)) {
667431Sigor@sysoev.ru             goto fail;
668431Sigor@sysoev.ru         }
669431Sigor@sysoev.ru 
670431Sigor@sysoev.ru         content_length->value = p;
671431Sigor@sysoev.ru         end = nxt_sprintf(p, p + NXT_OFF_T_LEN, "%O", r->resp.content_length_n);
672431Sigor@sysoev.ru         content_length->value_length = end - p;
673431Sigor@sysoev.ru 
674431Sigor@sysoev.ru         r->resp.content_length = content_length;
675431Sigor@sysoev.ru     }
676431Sigor@sysoev.ru 
6771112Sigor@sysoev.ru     if (nxt_fast_path(r->proto.any != NULL)) {
6781270Sigor@sysoev.ru         nxt_http_proto[r->protocol].header_send(task, r, body_handler, data);
679431Sigor@sysoev.ru     }
680431Sigor@sysoev.ru 
681431Sigor@sysoev.ru     return;
682431Sigor@sysoev.ru 
683431Sigor@sysoev.ru fail:
684431Sigor@sysoev.ru 
685431Sigor@sysoev.ru     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
686431Sigor@sysoev.ru }
687431Sigor@sysoev.ru 
688431Sigor@sysoev.ru 
689431Sigor@sysoev.ru void
6901131Smax.romanov@nginx.com nxt_http_request_ws_frame_start(nxt_task_t *task, nxt_http_request_t *r,
6911131Smax.romanov@nginx.com     nxt_buf_t *ws_frame)
6921131Smax.romanov@nginx.com {
6931131Smax.romanov@nginx.com     if (r->proto.any != NULL) {
6941131Smax.romanov@nginx.com         nxt_http_proto[r->protocol].ws_frame_start(task, r, ws_frame);
6951131Smax.romanov@nginx.com     }
6961131Smax.romanov@nginx.com }
6971131Smax.romanov@nginx.com 
6981131Smax.romanov@nginx.com 
6991131Smax.romanov@nginx.com void
700431Sigor@sysoev.ru nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
701431Sigor@sysoev.ru {
7021112Sigor@sysoev.ru     if (nxt_fast_path(r->proto.any != NULL)) {
7031112Sigor@sysoev.ru         nxt_http_proto[r->protocol].send(task, r, out);
704431Sigor@sysoev.ru     }
705431Sigor@sysoev.ru }
706431Sigor@sysoev.ru 
707431Sigor@sysoev.ru 
708431Sigor@sysoev.ru nxt_buf_t *
709608Sigor@sysoev.ru nxt_http_buf_mem(nxt_task_t *task, nxt_http_request_t *r, size_t size)
710431Sigor@sysoev.ru {
711431Sigor@sysoev.ru     nxt_buf_t  *b;
712431Sigor@sysoev.ru 
713608Sigor@sysoev.ru     b = nxt_buf_mem_alloc(r->mem_pool, size, 0);
714431Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
715608Sigor@sysoev.ru         b->completion_handler = nxt_http_request_mem_buf_completion;
716431Sigor@sysoev.ru         b->parent = r;
717608Sigor@sysoev.ru         nxt_mp_retain(r->mem_pool);
718431Sigor@sysoev.ru 
719431Sigor@sysoev.ru     } else {
720608Sigor@sysoev.ru         nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
721431Sigor@sysoev.ru     }
722431Sigor@sysoev.ru 
723431Sigor@sysoev.ru     return b;
724431Sigor@sysoev.ru }
725431Sigor@sysoev.ru 
726431Sigor@sysoev.ru 
727431Sigor@sysoev.ru static void
728608Sigor@sysoev.ru nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data)
729608Sigor@sysoev.ru {
7301269Sigor@sysoev.ru     nxt_buf_t           *b, *next;
731608Sigor@sysoev.ru     nxt_http_request_t  *r;
732608Sigor@sysoev.ru 
733608Sigor@sysoev.ru     b = obj;
734608Sigor@sysoev.ru     r = data;
735608Sigor@sysoev.ru 
7361269Sigor@sysoev.ru     do {
7371269Sigor@sysoev.ru         next = b->next;
738608Sigor@sysoev.ru 
7391269Sigor@sysoev.ru         nxt_mp_free(r->mem_pool, b);
7401269Sigor@sysoev.ru         nxt_mp_release(r->mem_pool);
7411269Sigor@sysoev.ru 
7421269Sigor@sysoev.ru         b = next;
7431269Sigor@sysoev.ru     } while (b != NULL);
744608Sigor@sysoev.ru }
745608Sigor@sysoev.ru 
746608Sigor@sysoev.ru 
747608Sigor@sysoev.ru nxt_buf_t *
748608Sigor@sysoev.ru nxt_http_buf_last(nxt_http_request_t *r)
749608Sigor@sysoev.ru {
750608Sigor@sysoev.ru     nxt_buf_t  *last;
751608Sigor@sysoev.ru 
752608Sigor@sysoev.ru     last = r->last;
753608Sigor@sysoev.ru     r->last = NULL;
754608Sigor@sysoev.ru 
755608Sigor@sysoev.ru     return last;
756608Sigor@sysoev.ru }
757608Sigor@sysoev.ru 
758608Sigor@sysoev.ru 
759608Sigor@sysoev.ru static void
760431Sigor@sysoev.ru nxt_http_request_done(nxt_task_t *task, void *obj, void *data)
761431Sigor@sysoev.ru {
762431Sigor@sysoev.ru     nxt_http_request_t  *r;
763431Sigor@sysoev.ru 
764431Sigor@sysoev.ru     r = data;
765431Sigor@sysoev.ru 
766431Sigor@sysoev.ru     nxt_debug(task, "http request done");
767431Sigor@sysoev.ru 
768431Sigor@sysoev.ru     nxt_http_request_close_handler(task, r, r->proto.any);
769431Sigor@sysoev.ru }
770431Sigor@sysoev.ru 
771431Sigor@sysoev.ru 
772431Sigor@sysoev.ru void
773608Sigor@sysoev.ru nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data)
774431Sigor@sysoev.ru {
775608Sigor@sysoev.ru     nxt_http_proto_t    proto;
776608Sigor@sysoev.ru     nxt_http_request_t  *r;
777608Sigor@sysoev.ru 
778608Sigor@sysoev.ru     r = obj;
779608Sigor@sysoev.ru     proto.any = data;
780431Sigor@sysoev.ru 
781608Sigor@sysoev.ru     nxt_debug(task, "http request error handler");
782608Sigor@sysoev.ru 
7831009Smax.romanov@nginx.com     r->error = 1;
7841009Smax.romanov@nginx.com 
7851112Sigor@sysoev.ru     if (nxt_fast_path(proto.any != NULL)) {
7861112Sigor@sysoev.ru         nxt_http_proto[r->protocol].discard(task, r, nxt_http_buf_last(r));
787608Sigor@sysoev.ru     }
788431Sigor@sysoev.ru }
789431Sigor@sysoev.ru 
790431Sigor@sysoev.ru 
7911131Smax.romanov@nginx.com void
792431Sigor@sysoev.ru nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
793431Sigor@sysoev.ru {
794630Svbart@nginx.com     nxt_http_proto_t         proto;
795630Svbart@nginx.com     nxt_http_request_t       *r;
7961112Sigor@sysoev.ru     nxt_http_protocol_t      protocol;
797683Sigor@sysoev.ru     nxt_socket_conf_joint_t  *conf;
798630Svbart@nginx.com     nxt_router_access_log_t  *access_log;
799431Sigor@sysoev.ru 
800431Sigor@sysoev.ru     r = obj;
801431Sigor@sysoev.ru     proto.any = data;
802431Sigor@sysoev.ru 
803431Sigor@sysoev.ru     nxt_debug(task, "http request close handler");
804431Sigor@sysoev.ru 
805683Sigor@sysoev.ru     conf = r->conf;
806683Sigor@sysoev.ru 
807431Sigor@sysoev.ru     if (!r->logged) {
808431Sigor@sysoev.ru         r->logged = 1;
809630Svbart@nginx.com 
810683Sigor@sysoev.ru         access_log = conf->socket_conf->router_conf->access_log;
811630Svbart@nginx.com 
812630Svbart@nginx.com         if (access_log != NULL) {
813630Svbart@nginx.com             access_log->handler(task, r, access_log);
814630Svbart@nginx.com         }
815431Sigor@sysoev.ru     }
816431Sigor@sysoev.ru 
817431Sigor@sysoev.ru     r->proto.any = NULL;
818431Sigor@sysoev.ru 
8191403Smax.romanov@nginx.com     if (r->body != NULL && nxt_buf_is_file(r->body)
8201403Smax.romanov@nginx.com         && r->body->file->fd != -1)
8211403Smax.romanov@nginx.com     {
8221403Smax.romanov@nginx.com         nxt_fd_close(r->body->file->fd);
8231403Smax.romanov@nginx.com 
8241403Smax.romanov@nginx.com         r->body->file->fd = -1;
8251403Smax.romanov@nginx.com     }
8261403Smax.romanov@nginx.com 
8271112Sigor@sysoev.ru     if (nxt_fast_path(proto.any != NULL)) {
8281131Smax.romanov@nginx.com         protocol = r->protocol;
8291131Smax.romanov@nginx.com 
8301265Sigor@sysoev.ru         nxt_http_proto[protocol].close(task, proto, conf);
8311131Smax.romanov@nginx.com 
8321265Sigor@sysoev.ru         nxt_mp_release(r->mem_pool);
833431Sigor@sysoev.ru     }
834431Sigor@sysoev.ru }
835543Svbart@nginx.com 
836543Svbart@nginx.com 
837543Svbart@nginx.com static u_char *
8381183Svbart@nginx.com nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm,
8391183Svbart@nginx.com     size_t size, const char *format)
840543Svbart@nginx.com {
8411183Svbart@nginx.com     return nxt_http_date(buf, tm);
842543Svbart@nginx.com }
8432104Sz.hong@f5.com 
8442104Sz.hong@f5.com 
8452104Sz.hong@f5.com nxt_array_t *
8462104Sz.hong@f5.com nxt_http_arguments_parse(nxt_http_request_t *r)
8472104Sz.hong@f5.com {
8482104Sz.hong@f5.com     size_t                 name_length;
8492104Sz.hong@f5.com     u_char                 *p, *dst, *dst_start, *start, *end, *name;
8502104Sz.hong@f5.com     uint8_t                d0, d1;
8512104Sz.hong@f5.com     uint32_t               hash;
8522104Sz.hong@f5.com     nxt_array_t            *args;
8532104Sz.hong@f5.com     nxt_http_name_value_t  *nv;
8542104Sz.hong@f5.com 
8552104Sz.hong@f5.com     if (r->arguments != NULL) {
8562104Sz.hong@f5.com         return r->arguments;
8572104Sz.hong@f5.com     }
8582104Sz.hong@f5.com 
8592104Sz.hong@f5.com     args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
8602104Sz.hong@f5.com     if (nxt_slow_path(args == NULL)) {
8612104Sz.hong@f5.com         return NULL;
8622104Sz.hong@f5.com     }
8632104Sz.hong@f5.com 
8642104Sz.hong@f5.com     hash = NXT_HTTP_FIELD_HASH_INIT;
8652104Sz.hong@f5.com     name = NULL;
8662104Sz.hong@f5.com     name_length = 0;
8672104Sz.hong@f5.com 
8682104Sz.hong@f5.com     dst_start = nxt_mp_nget(r->mem_pool, r->args->length);
8692104Sz.hong@f5.com     if (nxt_slow_path(dst_start == NULL)) {
8702104Sz.hong@f5.com         return NULL;
8712104Sz.hong@f5.com     }
8722104Sz.hong@f5.com 
8732104Sz.hong@f5.com     r->args_decoded.start = dst_start;
8742104Sz.hong@f5.com 
8752104Sz.hong@f5.com     start = r->args->start;
8762104Sz.hong@f5.com     end = start + r->args->length;
8772104Sz.hong@f5.com 
8782104Sz.hong@f5.com     for (p = start, dst = dst_start; p < end; p++, dst++) {
8792104Sz.hong@f5.com         *dst = *p;
8802104Sz.hong@f5.com 
8812104Sz.hong@f5.com         switch (*p) {
8822104Sz.hong@f5.com         case '=':
8832104Sz.hong@f5.com             if (name == NULL) {
8842104Sz.hong@f5.com                 name_length = dst - dst_start;
8852104Sz.hong@f5.com                 name = dst_start;
8862104Sz.hong@f5.com                 dst_start = dst + 1;
8872104Sz.hong@f5.com             }
8882104Sz.hong@f5.com 
8892104Sz.hong@f5.com             continue;
8902104Sz.hong@f5.com 
8912104Sz.hong@f5.com         case '&':
8922104Sz.hong@f5.com             if (name_length != 0 || dst != dst_start) {
8932104Sz.hong@f5.com                 nv = nxt_http_argument(args, name, name_length, hash, dst_start,
8942104Sz.hong@f5.com                                        dst);
8952104Sz.hong@f5.com                 if (nxt_slow_path(nv == NULL)) {
8962104Sz.hong@f5.com                     return NULL;
8972104Sz.hong@f5.com                 }
8982104Sz.hong@f5.com             }
8992104Sz.hong@f5.com 
9002104Sz.hong@f5.com             hash = NXT_HTTP_FIELD_HASH_INIT;
9012104Sz.hong@f5.com             name_length = 0;
9022104Sz.hong@f5.com             name = NULL;
9032104Sz.hong@f5.com             dst_start = dst + 1;
9042104Sz.hong@f5.com 
9052104Sz.hong@f5.com             continue;
9062104Sz.hong@f5.com 
9072104Sz.hong@f5.com         case '+':
9082104Sz.hong@f5.com             *dst = ' ';
9092104Sz.hong@f5.com 
9102104Sz.hong@f5.com             break;
9112104Sz.hong@f5.com 
9122104Sz.hong@f5.com         case '%':
9132104Sz.hong@f5.com             if (nxt_slow_path(end - p <= 2)) {
9142104Sz.hong@f5.com                 break;
9152104Sz.hong@f5.com             }
9162104Sz.hong@f5.com 
9172104Sz.hong@f5.com             d0 = nxt_hex2int[p[1]];
9182104Sz.hong@f5.com             d1 = nxt_hex2int[p[2]];
9192104Sz.hong@f5.com 
9202104Sz.hong@f5.com             if (nxt_slow_path((d0 | d1) >= 16)) {
9212104Sz.hong@f5.com                 break;
9222104Sz.hong@f5.com             }
9232104Sz.hong@f5.com 
9242104Sz.hong@f5.com             p += 2;
9252104Sz.hong@f5.com             *dst = (d0 << 4) + d1;
9262104Sz.hong@f5.com 
9272104Sz.hong@f5.com             break;
9282104Sz.hong@f5.com         }
9292104Sz.hong@f5.com 
9302104Sz.hong@f5.com         if (name == NULL) {
9312104Sz.hong@f5.com             hash = nxt_http_field_hash_char(hash, *dst);
9322104Sz.hong@f5.com         }
9332104Sz.hong@f5.com     }
9342104Sz.hong@f5.com 
9352104Sz.hong@f5.com     r->args_decoded.length = dst - r->args_decoded.start;
9362104Sz.hong@f5.com 
9372104Sz.hong@f5.com     if (name_length != 0 || dst != dst_start) {
9382104Sz.hong@f5.com         nv = nxt_http_argument(args, name, name_length, hash, dst_start, dst);
9392104Sz.hong@f5.com         if (nxt_slow_path(nv == NULL)) {
9402104Sz.hong@f5.com             return NULL;
9412104Sz.hong@f5.com         }
9422104Sz.hong@f5.com     }
9432104Sz.hong@f5.com 
9442104Sz.hong@f5.com     r->arguments = args;
9452104Sz.hong@f5.com 
9462104Sz.hong@f5.com     return args;
9472104Sz.hong@f5.com }
9482104Sz.hong@f5.com 
9492104Sz.hong@f5.com 
9502104Sz.hong@f5.com static nxt_http_name_value_t *
9512104Sz.hong@f5.com nxt_http_argument(nxt_array_t *array, u_char *name, size_t name_length,
9522104Sz.hong@f5.com     uint32_t hash, u_char *start, u_char *end)
9532104Sz.hong@f5.com {
9542104Sz.hong@f5.com     size_t                 length;
9552104Sz.hong@f5.com     nxt_http_name_value_t  *nv;
9562104Sz.hong@f5.com 
9572104Sz.hong@f5.com     nv = nxt_array_add(array);
9582104Sz.hong@f5.com     if (nxt_slow_path(nv == NULL)) {
9592104Sz.hong@f5.com         return NULL;
9602104Sz.hong@f5.com     }
9612104Sz.hong@f5.com 
9622104Sz.hong@f5.com     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
9632104Sz.hong@f5.com 
9642104Sz.hong@f5.com     length = end - start;
9652104Sz.hong@f5.com 
9662104Sz.hong@f5.com     if (name == NULL) {
9672104Sz.hong@f5.com         name_length = length;
9682104Sz.hong@f5.com         name = start;
9692104Sz.hong@f5.com         length = 0;
9702104Sz.hong@f5.com     }
9712104Sz.hong@f5.com 
9722104Sz.hong@f5.com     nv->name_length = name_length;
9732104Sz.hong@f5.com     nv->value_length = length;
9742104Sz.hong@f5.com     nv->name = name;
9752104Sz.hong@f5.com     nv->value = start;
9762104Sz.hong@f5.com 
9772104Sz.hong@f5.com     return nv;
9782104Sz.hong@f5.com }
9792104Sz.hong@f5.com 
9802104Sz.hong@f5.com 
9812104Sz.hong@f5.com nxt_array_t *
9822104Sz.hong@f5.com nxt_http_cookies_parse(nxt_http_request_t *r)
9832104Sz.hong@f5.com {
9842104Sz.hong@f5.com     nxt_int_t         ret;
9852104Sz.hong@f5.com     nxt_array_t       *cookies;
9862104Sz.hong@f5.com     nxt_http_field_t  *f;
9872104Sz.hong@f5.com 
9882104Sz.hong@f5.com     if (r->cookies != NULL) {
9892104Sz.hong@f5.com         return r->cookies;
9902104Sz.hong@f5.com     }
9912104Sz.hong@f5.com 
9922104Sz.hong@f5.com     cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
9932104Sz.hong@f5.com     if (nxt_slow_path(cookies == NULL)) {
9942104Sz.hong@f5.com         return NULL;
9952104Sz.hong@f5.com     }
9962104Sz.hong@f5.com 
9972104Sz.hong@f5.com     nxt_list_each(f, r->fields) {
9982104Sz.hong@f5.com 
9992104Sz.hong@f5.com         if (f->hash != NXT_HTTP_COOKIE_HASH
10002104Sz.hong@f5.com             || f->name_length != 6
10012104Sz.hong@f5.com             || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0)
10022104Sz.hong@f5.com         {
10032104Sz.hong@f5.com             continue;
10042104Sz.hong@f5.com         }
10052104Sz.hong@f5.com 
10062104Sz.hong@f5.com         ret = nxt_http_cookie_parse(cookies, f->value,
10072104Sz.hong@f5.com                                     f->value + f->value_length);
10082104Sz.hong@f5.com         if (ret != NXT_OK) {
10092104Sz.hong@f5.com             return NULL;
10102104Sz.hong@f5.com         }
10112104Sz.hong@f5.com 
10122104Sz.hong@f5.com     } nxt_list_loop;
10132104Sz.hong@f5.com 
10142104Sz.hong@f5.com     r->cookies = cookies;
10152104Sz.hong@f5.com 
10162104Sz.hong@f5.com     return cookies;
10172104Sz.hong@f5.com }
10182104Sz.hong@f5.com 
10192104Sz.hong@f5.com 
10202104Sz.hong@f5.com static nxt_int_t
10212104Sz.hong@f5.com nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end)
10222104Sz.hong@f5.com {
10232104Sz.hong@f5.com     size_t                 name_length;
10242104Sz.hong@f5.com     u_char                 c, *p, *name;
10252104Sz.hong@f5.com     nxt_http_name_value_t  *nv;
10262104Sz.hong@f5.com 
10272104Sz.hong@f5.com     name = NULL;
10282104Sz.hong@f5.com     name_length = 0;
10292104Sz.hong@f5.com 
10302104Sz.hong@f5.com     for (p = start; p < end; p++) {
10312104Sz.hong@f5.com         c = *p;
10322104Sz.hong@f5.com 
10332104Sz.hong@f5.com         if (c == '=') {
10342104Sz.hong@f5.com             while (start[0] == ' ') { start++; }
10352104Sz.hong@f5.com 
10362104Sz.hong@f5.com             name_length = p - start;
10372104Sz.hong@f5.com 
10382104Sz.hong@f5.com             if (name_length != 0) {
10392104Sz.hong@f5.com                 name = start;
10402104Sz.hong@f5.com             }
10412104Sz.hong@f5.com 
10422104Sz.hong@f5.com             start = p + 1;
10432104Sz.hong@f5.com 
10442104Sz.hong@f5.com         } else if (c == ';') {
10452104Sz.hong@f5.com             if (name != NULL) {
10462104Sz.hong@f5.com                 nv = nxt_http_cookie(cookies, name, name_length, start, p);
10472104Sz.hong@f5.com                 if (nxt_slow_path(nv == NULL)) {
10482104Sz.hong@f5.com                     return NXT_ERROR;
10492104Sz.hong@f5.com                 }
10502104Sz.hong@f5.com             }
10512104Sz.hong@f5.com 
10522104Sz.hong@f5.com             name = NULL;
10532104Sz.hong@f5.com             start = p + 1;
10542104Sz.hong@f5.com          }
10552104Sz.hong@f5.com     }
10562104Sz.hong@f5.com 
10572104Sz.hong@f5.com     if (name != NULL) {
10582104Sz.hong@f5.com         nv = nxt_http_cookie(cookies, name, name_length, start, p);
10592104Sz.hong@f5.com         if (nxt_slow_path(nv == NULL)) {
10602104Sz.hong@f5.com             return NXT_ERROR;
10612104Sz.hong@f5.com         }
10622104Sz.hong@f5.com     }
10632104Sz.hong@f5.com 
10642104Sz.hong@f5.com     return NXT_OK;
10652104Sz.hong@f5.com }
10662104Sz.hong@f5.com 
10672104Sz.hong@f5.com 
10682104Sz.hong@f5.com static nxt_http_name_value_t *
10692104Sz.hong@f5.com nxt_http_cookie(nxt_array_t *array, u_char *name, size_t name_length,
10702104Sz.hong@f5.com     u_char *start, u_char *end)
10712104Sz.hong@f5.com {
10722104Sz.hong@f5.com     u_char                 c, *p;
10732104Sz.hong@f5.com     uint32_t               hash;
10742104Sz.hong@f5.com     nxt_http_name_value_t  *nv;
10752104Sz.hong@f5.com 
10762104Sz.hong@f5.com     nv = nxt_array_add(array);
10772104Sz.hong@f5.com     if (nxt_slow_path(nv == NULL)) {
10782104Sz.hong@f5.com         return NULL;
10792104Sz.hong@f5.com     }
10802104Sz.hong@f5.com 
10812104Sz.hong@f5.com     nv->name_length = name_length;
10822104Sz.hong@f5.com     nv->name = name;
10832104Sz.hong@f5.com 
10842104Sz.hong@f5.com     hash = NXT_HTTP_FIELD_HASH_INIT;
10852104Sz.hong@f5.com 
10862104Sz.hong@f5.com     for (p = name; p < name + name_length; p++) {
10872104Sz.hong@f5.com         c = *p;
10882104Sz.hong@f5.com         hash = nxt_http_field_hash_char(hash, c);
10892104Sz.hong@f5.com     }
10902104Sz.hong@f5.com 
10912104Sz.hong@f5.com     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
10922104Sz.hong@f5.com 
10932104Sz.hong@f5.com     while (start < end && end[-1] == ' ') { end--; }
10942104Sz.hong@f5.com 
10952104Sz.hong@f5.com     nv->value_length = end - start;
10962104Sz.hong@f5.com     nv->value = start;
10972104Sz.hong@f5.com 
10982104Sz.hong@f5.com     return nv;
10992104Sz.hong@f5.com }
11002123Sz.hong@f5.com 
11012123Sz.hong@f5.com 
11022123Sz.hong@f5.com int64_t
11032123Sz.hong@f5.com nxt_http_field_hash(nxt_mp_t *mp, nxt_str_t *name, nxt_bool_t case_sensitive,
11042123Sz.hong@f5.com     uint8_t encoding)
11052123Sz.hong@f5.com {
11062123Sz.hong@f5.com     u_char      c, *p, *src, *start, *end, plus;
11072123Sz.hong@f5.com     uint8_t     d0, d1;
11082123Sz.hong@f5.com     uint32_t    hash;
11092123Sz.hong@f5.com     nxt_str_t   str;
11102123Sz.hong@f5.com     nxt_uint_t  i;
11112123Sz.hong@f5.com 
11122123Sz.hong@f5.com     str.length = name->length;
11132123Sz.hong@f5.com 
11142123Sz.hong@f5.com     str.start = nxt_mp_nget(mp, str.length);
11152123Sz.hong@f5.com     if (nxt_slow_path(str.start == NULL)) {
11162123Sz.hong@f5.com         return -1;
11172123Sz.hong@f5.com     }
11182123Sz.hong@f5.com 
11192123Sz.hong@f5.com     p = str.start;
11202123Sz.hong@f5.com 
11212123Sz.hong@f5.com     hash = NXT_HTTP_FIELD_HASH_INIT;
11222123Sz.hong@f5.com 
11232123Sz.hong@f5.com     if (encoding == NXT_HTTP_URI_ENCODING_NONE) {
11242123Sz.hong@f5.com         for (i = 0; i < name->length; i++) {
11252123Sz.hong@f5.com             c = name->start[i];
11262123Sz.hong@f5.com             *p++ = c;
11272123Sz.hong@f5.com 
11282123Sz.hong@f5.com             c = case_sensitive ? c : nxt_lowcase(c);
11292123Sz.hong@f5.com             hash = nxt_http_field_hash_char(hash, c);
11302123Sz.hong@f5.com         }
11312123Sz.hong@f5.com 
11322123Sz.hong@f5.com         goto end;
11332123Sz.hong@f5.com     }
11342123Sz.hong@f5.com 
11352123Sz.hong@f5.com     plus = (encoding == NXT_HTTP_URI_ENCODING_PLUS) ? ' ' : '+';
11362123Sz.hong@f5.com 
11372123Sz.hong@f5.com     start = name->start;
11382123Sz.hong@f5.com     end = start + name->length;
11392123Sz.hong@f5.com 
11402123Sz.hong@f5.com     for (src = start; src < end; src++) {
11412123Sz.hong@f5.com         c = *src;
11422123Sz.hong@f5.com 
11432123Sz.hong@f5.com         switch (c) {
11442123Sz.hong@f5.com         case '%':
11452123Sz.hong@f5.com             if (nxt_slow_path(end - src <= 2)) {
11462123Sz.hong@f5.com                 return -1;
11472123Sz.hong@f5.com             }
11482123Sz.hong@f5.com 
11492123Sz.hong@f5.com             d0 = nxt_hex2int[src[1]];
11502123Sz.hong@f5.com             d1 = nxt_hex2int[src[2]];
11512123Sz.hong@f5.com             src += 2;
11522123Sz.hong@f5.com 
11532123Sz.hong@f5.com             if (nxt_slow_path((d0 | d1) >= 16)) {
11542123Sz.hong@f5.com                 return -1;
11552123Sz.hong@f5.com             }
11562123Sz.hong@f5.com 
11572123Sz.hong@f5.com             c = (d0 << 4) + d1;
11582123Sz.hong@f5.com             *p++ = c;
11592123Sz.hong@f5.com             break;
11602123Sz.hong@f5.com 
11612123Sz.hong@f5.com         case '+':
11622123Sz.hong@f5.com             c = plus;
11632123Sz.hong@f5.com             *p++ = c;
11642123Sz.hong@f5.com             break;
11652123Sz.hong@f5.com 
11662123Sz.hong@f5.com         default:
11672123Sz.hong@f5.com             *p++ = c;
11682123Sz.hong@f5.com             break;
11692123Sz.hong@f5.com         }
11702123Sz.hong@f5.com 
11712123Sz.hong@f5.com         c = case_sensitive ? c : nxt_lowcase(c);
11722123Sz.hong@f5.com         hash = nxt_http_field_hash_char(hash, c);
11732123Sz.hong@f5.com     }
11742123Sz.hong@f5.com 
11752123Sz.hong@f5.com     str.length = p - str.start;
11762123Sz.hong@f5.com 
11772123Sz.hong@f5.com end:
11782123Sz.hong@f5.com 
11792123Sz.hong@f5.com     *name = str;
11802123Sz.hong@f5.com 
11812123Sz.hong@f5.com     return nxt_http_field_hash_end(hash) & 0xFFFF;
11822123Sz.hong@f5.com }
1183