/src/unit/src/nxt_http_request.c
Line | Count | Source (jump to first uncovered line) |
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_forward(nxt_task_t *task, |
14 | | nxt_http_request_t *r, nxt_http_forward_t *forward); |
15 | | static void nxt_http_request_forward_client_ip(nxt_http_request_t *r, |
16 | | nxt_http_forward_t *forward, nxt_array_t *fields); |
17 | | static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr( |
18 | | nxt_http_request_t *r, u_char *start, size_t len); |
19 | | static void nxt_http_request_forward_protocol(nxt_http_request_t *r, |
20 | | nxt_http_field_t *field); |
21 | | static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data); |
22 | | static void nxt_http_request_proto_info(nxt_task_t *task, |
23 | | nxt_http_request_t *r); |
24 | | static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, |
25 | | void *data); |
26 | | static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); |
27 | | static nxt_int_t nxt_http_request_access_log(nxt_task_t *task, |
28 | | nxt_http_request_t *r, nxt_router_conf_t *rtcf); |
29 | | |
30 | | static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, |
31 | | struct tm *tm, size_t size, const char *format); |
32 | | |
33 | | static nxt_http_name_value_t *nxt_http_argument(nxt_array_t *array, |
34 | | u_char *name, size_t name_length, uint32_t hash, u_char *start, |
35 | | const u_char *end); |
36 | | static nxt_int_t nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, |
37 | | const u_char *end); |
38 | | static nxt_http_name_value_t *nxt_http_cookie(nxt_array_t *array, u_char *name, |
39 | | size_t name_length, u_char *start, const u_char *end); |
40 | | |
41 | | |
42 | | #define NXT_HTTP_COOKIE_HASH \ |
43 | 0 | (nxt_http_field_hash_end( \ |
44 | 0 | nxt_http_field_hash_char( \ |
45 | 0 | nxt_http_field_hash_char( \ |
46 | 0 | nxt_http_field_hash_char( \ |
47 | 0 | nxt_http_field_hash_char( \ |
48 | 0 | nxt_http_field_hash_char( \ |
49 | 0 | nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT, \ |
50 | 0 | 'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF) |
51 | | |
52 | | |
53 | | static const nxt_http_request_state_t nxt_http_request_init_state; |
54 | | static const nxt_http_request_state_t nxt_http_request_body_state; |
55 | | |
56 | | |
57 | | nxt_time_string_t nxt_http_date_cache = { |
58 | | (nxt_atomic_uint_t) -1, |
59 | | nxt_http_date_cache_handler, |
60 | | NULL, |
61 | | NXT_HTTP_DATE_LEN, |
62 | | NXT_THREAD_TIME_GMT, |
63 | | NXT_THREAD_TIME_SEC, |
64 | | }; |
65 | | |
66 | | |
67 | | nxt_int_t |
68 | | nxt_http_init(nxt_task_t *task) |
69 | 0 | { |
70 | 0 | nxt_int_t ret; |
71 | |
|
72 | 0 | ret = nxt_h1p_init(task); |
73 | |
|
74 | 0 | if (ret != NXT_OK) { |
75 | 0 | return ret; |
76 | 0 | } |
77 | | |
78 | 0 | return nxt_http_response_hash_init(task); |
79 | 0 | } |
80 | | |
81 | | |
82 | | nxt_int_t |
83 | | nxt_http_request_host(void *ctx, nxt_http_field_t *field, uintptr_t data) |
84 | 169 | { |
85 | 169 | nxt_int_t ret; |
86 | 169 | nxt_str_t host; |
87 | 169 | nxt_http_request_t *r; |
88 | | |
89 | 169 | r = ctx; |
90 | | |
91 | 169 | if (nxt_slow_path(r->host.start != NULL)) { |
92 | 2 | return NXT_HTTP_BAD_REQUEST; |
93 | 2 | } |
94 | | |
95 | 167 | host.length = field->value_length; |
96 | 167 | host.start = field->value; |
97 | | |
98 | 167 | ret = nxt_http_validate_host(&host, r->mem_pool); |
99 | | |
100 | 167 | if (nxt_fast_path(ret == NXT_OK)) { |
101 | 162 | r->host = host; |
102 | 162 | } |
103 | | |
104 | 167 | return ret; |
105 | 169 | } |
106 | | |
107 | | |
108 | | static nxt_int_t |
109 | | nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp) |
110 | 167 | { |
111 | 167 | u_char *h, ch; |
112 | 167 | size_t i, dot_pos, host_length; |
113 | 167 | nxt_bool_t lowcase; |
114 | | |
115 | 167 | enum { |
116 | 167 | sw_usual, |
117 | 167 | sw_literal, |
118 | 167 | sw_rest |
119 | 167 | } state; |
120 | | |
121 | 167 | dot_pos = host->length; |
122 | 167 | host_length = host->length; |
123 | | |
124 | 167 | h = host->start; |
125 | | |
126 | 167 | lowcase = 0; |
127 | 167 | state = sw_usual; |
128 | | |
129 | 13.7k | for (i = 0; i < host->length; i++) { |
130 | 13.5k | ch = h[i]; |
131 | | |
132 | 13.5k | if (ch > ']') { |
133 | | /* Short path. */ |
134 | 7.22k | continue; |
135 | 7.22k | } |
136 | | |
137 | 6.35k | switch (ch) { |
138 | | |
139 | 243 | case '.': |
140 | 243 | if (dot_pos == i - 1) { |
141 | 2 | return NXT_HTTP_BAD_REQUEST; |
142 | 2 | } |
143 | | |
144 | 241 | dot_pos = i; |
145 | 241 | break; |
146 | | |
147 | 255 | case ':': |
148 | 255 | if (state == sw_usual) { |
149 | 37 | host_length = i; |
150 | 37 | state = sw_rest; |
151 | 37 | } |
152 | | |
153 | 255 | break; |
154 | | |
155 | 274 | case '[': |
156 | 274 | if (i == 0) { |
157 | 6 | state = sw_literal; |
158 | 6 | } |
159 | | |
160 | 274 | break; |
161 | | |
162 | 254 | case ']': |
163 | 254 | if (state == sw_literal) { |
164 | 2 | host_length = i + 1; |
165 | 2 | state = sw_rest; |
166 | 2 | } |
167 | | |
168 | 254 | break; |
169 | | |
170 | 3 | case '/': |
171 | 3 | return NXT_HTTP_BAD_REQUEST; |
172 | | |
173 | 5.32k | default: |
174 | 5.32k | if (ch >= 'A' && ch <= 'Z') { |
175 | 3.45k | lowcase = 1; |
176 | 3.45k | } |
177 | | |
178 | 5.32k | break; |
179 | 6.35k | } |
180 | 6.35k | } |
181 | | |
182 | 162 | if (dot_pos == host_length - 1) { |
183 | 6 | host_length--; |
184 | 6 | } |
185 | | |
186 | 162 | host->length = host_length; |
187 | | |
188 | 162 | if (lowcase) { |
189 | 108 | host->start = nxt_mp_nget(mp, host_length); |
190 | 108 | if (nxt_slow_path(host->start == NULL)) { |
191 | 0 | return NXT_HTTP_INTERNAL_SERVER_ERROR; |
192 | 0 | } |
193 | | |
194 | 108 | nxt_memcpy_lowcase(host->start, h, host_length); |
195 | 108 | } |
196 | | |
197 | 162 | return NXT_OK; |
198 | 162 | } |
199 | | |
200 | | |
201 | | nxt_int_t |
202 | | nxt_http_request_field(void *ctx, nxt_http_field_t *field, uintptr_t offset) |
203 | 87 | { |
204 | 87 | nxt_http_request_t *r; |
205 | | |
206 | 87 | r = ctx; |
207 | | |
208 | 87 | nxt_value_at(nxt_http_field_t *, r, offset) = field; |
209 | | |
210 | 87 | return NXT_OK; |
211 | 87 | } |
212 | | |
213 | | |
214 | | nxt_int_t |
215 | | nxt_http_request_content_length(void *ctx, nxt_http_field_t *field, |
216 | | uintptr_t data) |
217 | 144 | { |
218 | 144 | nxt_off_t n, max_body_size; |
219 | 144 | nxt_http_request_t *r; |
220 | | |
221 | 144 | r = ctx; |
222 | | |
223 | 144 | if (nxt_fast_path(r->content_length == NULL)) { |
224 | 144 | r->content_length = field; |
225 | | |
226 | 144 | n = nxt_off_t_parse(field->value, field->value_length); |
227 | | |
228 | 144 | if (nxt_fast_path(n >= 0)) { |
229 | 0 | r->content_length_n = n; |
230 | |
|
231 | 0 | max_body_size = r->conf->socket_conf->max_body_size; |
232 | |
|
233 | 0 | if (nxt_slow_path(n > max_body_size)) { |
234 | 0 | return NXT_HTTP_PAYLOAD_TOO_LARGE; |
235 | 0 | } |
236 | | |
237 | 0 | return NXT_OK; |
238 | 0 | } |
239 | 144 | } |
240 | | |
241 | 144 | return NXT_HTTP_BAD_REQUEST; |
242 | 144 | } |
243 | | |
244 | | |
245 | | nxt_http_request_t * |
246 | | nxt_http_request_create(nxt_task_t *task) |
247 | 0 | { |
248 | 0 | nxt_mp_t *mp; |
249 | 0 | nxt_buf_t *last; |
250 | 0 | nxt_http_request_t *r; |
251 | |
|
252 | 0 | mp = nxt_mp_create(4096, 128, 512, 32); |
253 | 0 | if (nxt_slow_path(mp == NULL)) { |
254 | 0 | return NULL; |
255 | 0 | } |
256 | | |
257 | 0 | r = nxt_mp_zget(mp, sizeof(nxt_http_request_t)); |
258 | 0 | if (nxt_slow_path(r == NULL)) { |
259 | 0 | goto fail; |
260 | 0 | } |
261 | | |
262 | 0 | r->resp.fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t)); |
263 | 0 | if (nxt_slow_path(r->resp.fields == NULL)) { |
264 | 0 | goto fail; |
265 | 0 | } |
266 | | |
267 | 0 | last = nxt_mp_zget(mp, NXT_BUF_SYNC_SIZE); |
268 | 0 | if (nxt_slow_path(last == NULL)) { |
269 | 0 | goto fail; |
270 | 0 | } |
271 | | |
272 | 0 | nxt_buf_set_sync(last); |
273 | 0 | nxt_buf_set_last(last); |
274 | 0 | last->completion_handler = nxt_http_request_done; |
275 | 0 | last->parent = r; |
276 | 0 | r->last = last; |
277 | |
|
278 | 0 | r->mem_pool = mp; |
279 | 0 | r->content_length_n = -1; |
280 | 0 | r->resp.content_length_n = -1; |
281 | 0 | r->state = &nxt_http_request_init_state; |
282 | |
|
283 | 0 | r->start_time = nxt_thread_monotonic_time(task->thread); |
284 | |
|
285 | 0 | task->thread->engine->requests_cnt++; |
286 | |
|
287 | 0 | r->tstr_cache.var.pool = mp; |
288 | |
|
289 | 0 | return r; |
290 | | |
291 | 0 | fail: |
292 | |
|
293 | 0 | nxt_mp_release(mp); |
294 | |
|
295 | 0 | return NULL; |
296 | 0 | } |
297 | | |
298 | | |
299 | | static const nxt_http_request_state_t nxt_http_request_init_state |
300 | | nxt_aligned(64) = |
301 | | { |
302 | | .ready_handler = nxt_http_request_start, |
303 | | .error_handler = nxt_http_request_close_handler, |
304 | | }; |
305 | | |
306 | | |
307 | | static void |
308 | | nxt_http_request_start(nxt_task_t *task, void *obj, void *data) |
309 | 0 | { |
310 | 0 | nxt_int_t ret; |
311 | 0 | nxt_socket_conf_t *skcf; |
312 | 0 | nxt_http_request_t *r; |
313 | |
|
314 | 0 | r = obj; |
315 | |
|
316 | 0 | r->state = &nxt_http_request_body_state; |
317 | |
|
318 | 0 | skcf = r->conf->socket_conf; |
319 | |
|
320 | 0 | if (skcf->forwarded != NULL) { |
321 | 0 | ret = nxt_http_request_forward(task, r, skcf->forwarded); |
322 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
323 | 0 | goto fail; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | 0 | if (skcf->client_ip != NULL) { |
328 | 0 | ret = nxt_http_request_forward(task, r, skcf->client_ip); |
329 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
330 | 0 | goto fail; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | 0 | nxt_http_request_read_body(task, r); |
335 | |
|
336 | 0 | return; |
337 | | |
338 | 0 | fail: |
339 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
340 | 0 | } |
341 | | |
342 | | |
343 | | static nxt_int_t |
344 | | nxt_http_request_forward(nxt_task_t *task, nxt_http_request_t *r, |
345 | | nxt_http_forward_t *forward) |
346 | 0 | { |
347 | 0 | nxt_int_t ret; |
348 | 0 | nxt_array_t *client_ip_fields; |
349 | 0 | nxt_http_field_t *f, **fields, *protocol_field; |
350 | 0 | nxt_http_forward_header_t *client_ip, *protocol; |
351 | |
|
352 | 0 | ret = nxt_http_route_addr_rule(r, forward->source, r->remote); |
353 | 0 | if (ret <= 0) { |
354 | 0 | return NXT_OK; |
355 | 0 | } |
356 | | |
357 | 0 | client_ip = &forward->client_ip; |
358 | 0 | protocol = &forward->protocol; |
359 | |
|
360 | 0 | if (client_ip->header != NULL) { |
361 | 0 | client_ip_fields = nxt_array_create(r->mem_pool, 1, |
362 | 0 | sizeof(nxt_http_field_t *)); |
363 | 0 | if (nxt_slow_path(client_ip_fields == NULL)) { |
364 | 0 | return NXT_ERROR; |
365 | 0 | } |
366 | |
|
367 | 0 | } else { |
368 | 0 | client_ip_fields = NULL; |
369 | 0 | } |
370 | | |
371 | 0 | protocol_field = NULL; |
372 | |
|
373 | 0 | nxt_list_each(f, r->fields) { |
374 | 0 | if (client_ip_fields != NULL |
375 | 0 | && f->hash == client_ip->header_hash |
376 | 0 | && f->value_length > 0 |
377 | 0 | && f->name_length == client_ip->header->length |
378 | 0 | && nxt_memcasecmp(f->name, client_ip->header->start, |
379 | 0 | client_ip->header->length) == 0) |
380 | 0 | { |
381 | 0 | fields = nxt_array_add(client_ip_fields); |
382 | 0 | if (nxt_slow_path(fields == NULL)) { |
383 | 0 | return NXT_ERROR; |
384 | 0 | } |
385 | | |
386 | 0 | *fields = f; |
387 | 0 | } |
388 | | |
389 | 0 | if (protocol->header != NULL |
390 | 0 | && protocol_field == NULL |
391 | 0 | && f->hash == protocol->header_hash |
392 | 0 | && f->value_length > 0 |
393 | 0 | && f->name_length == protocol->header->length |
394 | 0 | && nxt_memcasecmp(f->name, protocol->header->start, |
395 | 0 | protocol->header->length) == 0) |
396 | 0 | { |
397 | 0 | protocol_field = f; |
398 | 0 | } |
399 | 0 | } nxt_list_loop; |
400 | | |
401 | 0 | if (client_ip_fields != NULL) { |
402 | 0 | nxt_http_request_forward_client_ip(r, forward, client_ip_fields); |
403 | 0 | } |
404 | |
|
405 | 0 | if (protocol_field != NULL) { |
406 | 0 | nxt_http_request_forward_protocol(r, protocol_field); |
407 | 0 | } |
408 | |
|
409 | 0 | return NXT_OK; |
410 | 0 | } |
411 | | |
412 | | |
413 | | static void |
414 | | nxt_http_request_forward_client_ip(nxt_http_request_t *r, |
415 | | nxt_http_forward_t *forward, nxt_array_t *fields) |
416 | 0 | { |
417 | 0 | u_char *start, *p; |
418 | 0 | nxt_int_t ret, i, len; |
419 | 0 | nxt_sockaddr_t *sa, *prev_sa; |
420 | 0 | nxt_http_field_t **f; |
421 | |
|
422 | 0 | prev_sa = r->remote; |
423 | 0 | f = (nxt_http_field_t **) fields->elts; |
424 | |
|
425 | 0 | i = fields->nelts; |
426 | |
|
427 | 0 | while (i-- > 0) { |
428 | 0 | start = f[i]->value; |
429 | 0 | len = f[i]->value_length; |
430 | |
|
431 | 0 | do { |
432 | 0 | for (p = start + len - 1; p > start; p--, len--) { |
433 | 0 | if (*p != ' ' && *p != ',') { |
434 | 0 | break; |
435 | 0 | } |
436 | 0 | } |
437 | |
|
438 | 0 | for (/* void */; p > start; p--) { |
439 | 0 | if (*p == ' ' || *p == ',') { |
440 | 0 | p++; |
441 | 0 | break; |
442 | 0 | } |
443 | 0 | } |
444 | |
|
445 | 0 | sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start)); |
446 | 0 | if (nxt_slow_path(sa == NULL)) { |
447 | 0 | if (prev_sa != NULL) { |
448 | 0 | r->remote = prev_sa; |
449 | 0 | } |
450 | |
|
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | 0 | if (!forward->recursive) { |
455 | 0 | r->remote = sa; |
456 | 0 | return; |
457 | 0 | } |
458 | | |
459 | 0 | ret = nxt_http_route_addr_rule(r, forward->source, sa); |
460 | 0 | if (ret <= 0 || (i == 0 && p == start)) { |
461 | 0 | r->remote = sa; |
462 | 0 | return; |
463 | 0 | } |
464 | | |
465 | 0 | prev_sa = sa; |
466 | 0 | len = p - 1 - start; |
467 | |
|
468 | 0 | } while (len > 0); |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | |
473 | | static nxt_sockaddr_t * |
474 | | nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start, |
475 | | size_t len) |
476 | 0 | { |
477 | 0 | nxt_str_t addr; |
478 | 0 | nxt_sockaddr_t *sa; |
479 | |
|
480 | 0 | addr.start = start; |
481 | 0 | addr.length = len; |
482 | |
|
483 | 0 | sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr); |
484 | 0 | if (nxt_slow_path(sa == NULL)) { |
485 | 0 | return NULL; |
486 | 0 | } |
487 | | |
488 | 0 | switch (sa->u.sockaddr.sa_family) { |
489 | 0 | case AF_INET: |
490 | 0 | if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) { |
491 | 0 | return NULL; |
492 | 0 | } |
493 | | |
494 | 0 | break; |
495 | | |
496 | 0 | #if (NXT_INET6) |
497 | 0 | case AF_INET6: |
498 | 0 | if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) { |
499 | 0 | return NULL; |
500 | 0 | } |
501 | | |
502 | 0 | break; |
503 | 0 | #endif /* NXT_INET6 */ |
504 | | |
505 | 0 | default: |
506 | 0 | return NULL; |
507 | 0 | } |
508 | | |
509 | 0 | return sa; |
510 | 0 | } |
511 | | |
512 | | |
513 | | static void |
514 | | nxt_http_request_forward_protocol(nxt_http_request_t *r, |
515 | | nxt_http_field_t *field) |
516 | 0 | { |
517 | 0 | if (field->value_length == 4) { |
518 | 0 | if (nxt_memcasecmp(field->value, "http", 4) == 0) { |
519 | 0 | r->tls = 0; |
520 | 0 | } |
521 | |
|
522 | 0 | } else if (field->value_length == 5) { |
523 | 0 | if (nxt_memcasecmp(field->value, "https", 5) == 0) { |
524 | 0 | r->tls = 1; |
525 | 0 | } |
526 | |
|
527 | 0 | } else if (field->value_length == 2) { |
528 | 0 | if (nxt_memcasecmp(field->value, "on", 2) == 0) { |
529 | 0 | r->tls = 1; |
530 | 0 | } |
531 | 0 | } |
532 | 0 | } |
533 | | |
534 | | |
535 | | static const nxt_http_request_state_t nxt_http_request_body_state |
536 | | nxt_aligned(64) = |
537 | | { |
538 | | .ready_handler = nxt_http_request_ready, |
539 | | .error_handler = nxt_http_request_close_handler, |
540 | | }; |
541 | | |
542 | | |
543 | | static nxt_int_t |
544 | | nxt_http_request_chunked_transform(nxt_http_request_t *r) |
545 | 0 | { |
546 | 0 | size_t size; |
547 | 0 | u_char *p, *end; |
548 | 0 | nxt_http_field_t *f; |
549 | |
|
550 | 0 | r->chunked_field->skip = 1; |
551 | |
|
552 | 0 | size = r->body->file_end; |
553 | |
|
554 | 0 | f = nxt_list_zero_add(r->fields); |
555 | 0 | if (nxt_slow_path(f == NULL)) { |
556 | 0 | return NXT_ERROR; |
557 | 0 | } |
558 | | |
559 | 0 | nxt_http_field_name_set(f, "Content-Length"); |
560 | |
|
561 | 0 | p = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN); |
562 | 0 | if (nxt_slow_path(p == NULL)) { |
563 | 0 | return NXT_ERROR; |
564 | 0 | } |
565 | | |
566 | 0 | f->value = p; |
567 | 0 | end = nxt_sprintf(p, p + NXT_OFF_T_LEN, "%uz", size); |
568 | 0 | f->value_length = end - p; |
569 | |
|
570 | 0 | r->content_length = f; |
571 | 0 | r->content_length_n = size; |
572 | |
|
573 | 0 | return NXT_OK; |
574 | 0 | } |
575 | | |
576 | | |
577 | | static void |
578 | | nxt_http_request_ready(nxt_task_t *task, void *obj, void *data) |
579 | 0 | { |
580 | 0 | nxt_int_t ret; |
581 | 0 | nxt_http_action_t *action; |
582 | 0 | nxt_http_request_t *r; |
583 | |
|
584 | 0 | r = obj; |
585 | 0 | action = r->conf->socket_conf->action; |
586 | |
|
587 | 0 | if (r->chunked) { |
588 | 0 | ret = nxt_http_request_chunked_transform(r); |
589 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
590 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
591 | 0 | return; |
592 | 0 | } |
593 | 0 | } |
594 | | |
595 | 0 | nxt_http_request_action(task, r, action); |
596 | 0 | } |
597 | | |
598 | | |
599 | | void |
600 | | nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, |
601 | | nxt_http_action_t *action) |
602 | 0 | { |
603 | 0 | nxt_int_t ret; |
604 | |
|
605 | 0 | if (nxt_fast_path(action != NULL)) { |
606 | |
|
607 | 0 | do { |
608 | 0 | ret = nxt_http_rewrite(task, r); |
609 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
610 | 0 | break; |
611 | 0 | } |
612 | | |
613 | 0 | action = action->handler(task, r, action); |
614 | |
|
615 | 0 | if (action == NULL) { |
616 | 0 | return; |
617 | 0 | } |
618 | | |
619 | 0 | if (action == NXT_HTTP_ACTION_ERROR) { |
620 | 0 | break; |
621 | 0 | } |
622 | |
|
623 | 0 | } while (r->pass_count++ < 255); |
624 | 0 | } |
625 | | |
626 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
627 | 0 | } |
628 | | |
629 | | |
630 | | nxt_http_action_t * |
631 | | nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, |
632 | | nxt_http_action_t *action) |
633 | 0 | { |
634 | 0 | nxt_debug(task, "http application handler"); |
635 | | |
636 | | /* |
637 | | * TODO: need an application flag to get local address |
638 | | * required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go. |
639 | | */ |
640 | 0 | nxt_http_request_proto_info(task, r); |
641 | |
|
642 | 0 | if (r->host.length != 0) { |
643 | 0 | r->server_name = r->host; |
644 | |
|
645 | 0 | } else { |
646 | 0 | nxt_str_set(&r->server_name, "localhost"); |
647 | 0 | } |
648 | |
|
649 | 0 | nxt_router_process_http_request(task, r, action); |
650 | |
|
651 | 0 | return NULL; |
652 | 0 | } |
653 | | |
654 | | |
655 | | static void |
656 | | nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r) |
657 | 0 | { |
658 | 0 | if (nxt_fast_path(r->proto.any != NULL)) { |
659 | 0 | nxt_http_proto[r->protocol].local_addr(task, r); |
660 | 0 | } |
661 | 0 | } |
662 | | |
663 | | |
664 | | void |
665 | | nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r) |
666 | 0 | { |
667 | 0 | if (nxt_fast_path(r->proto.any != NULL)) { |
668 | 0 | nxt_http_proto[r->protocol].body_read(task, r); |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | | |
673 | | void |
674 | | nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r, |
675 | | nxt_work_handler_t body_handler, void *data) |
676 | 0 | { |
677 | 0 | u_char *p, *end, *server_string; |
678 | 0 | nxt_int_t ret; |
679 | 0 | nxt_http_field_t *server, *date, *content_length; |
680 | 0 | nxt_socket_conf_t *skcf; |
681 | |
|
682 | 0 | ret = nxt_http_set_headers(r); |
683 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
684 | 0 | goto fail; |
685 | 0 | } |
686 | | |
687 | | /* |
688 | | * TODO: "Server", "Date", and "Content-Length" processing should be moved |
689 | | * to the last header filter. |
690 | | */ |
691 | | |
692 | 0 | server = nxt_list_zero_add(r->resp.fields); |
693 | 0 | if (nxt_slow_path(server == NULL)) { |
694 | 0 | goto fail; |
695 | 0 | } |
696 | | |
697 | 0 | skcf = r->conf->socket_conf; |
698 | 0 | server_string = (u_char *) (skcf->server_version ? NXT_SERVER : NXT_NAME); |
699 | |
|
700 | 0 | nxt_http_field_name_set(server, "Server"); |
701 | 0 | server->value = server_string; |
702 | 0 | server->value_length = nxt_strlen(server_string); |
703 | |
|
704 | 0 | if (r->resp.date == NULL) { |
705 | 0 | date = nxt_list_zero_add(r->resp.fields); |
706 | 0 | if (nxt_slow_path(date == NULL)) { |
707 | 0 | goto fail; |
708 | 0 | } |
709 | | |
710 | 0 | nxt_http_field_name_set(date, "Date"); |
711 | |
|
712 | 0 | p = nxt_mp_nget(r->mem_pool, nxt_http_date_cache.size); |
713 | 0 | if (nxt_slow_path(p == NULL)) { |
714 | 0 | goto fail; |
715 | 0 | } |
716 | | |
717 | 0 | (void) nxt_thread_time_string(task->thread, &nxt_http_date_cache, p); |
718 | |
|
719 | 0 | date->value = p; |
720 | 0 | date->value_length = nxt_http_date_cache.size; |
721 | |
|
722 | 0 | r->resp.date = date; |
723 | 0 | } |
724 | | |
725 | 0 | if (r->resp.content_length_n != -1 |
726 | 0 | && (r->resp.content_length == NULL || r->resp.content_length->skip)) |
727 | 0 | { |
728 | 0 | content_length = nxt_list_zero_add(r->resp.fields); |
729 | 0 | if (nxt_slow_path(content_length == NULL)) { |
730 | 0 | goto fail; |
731 | 0 | } |
732 | | |
733 | 0 | nxt_http_field_name_set(content_length, "Content-Length"); |
734 | |
|
735 | 0 | p = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN); |
736 | 0 | if (nxt_slow_path(p == NULL)) { |
737 | 0 | goto fail; |
738 | 0 | } |
739 | | |
740 | 0 | content_length->value = p; |
741 | 0 | end = nxt_sprintf(p, p + NXT_OFF_T_LEN, "%O", r->resp.content_length_n); |
742 | 0 | content_length->value_length = end - p; |
743 | |
|
744 | 0 | r->resp.content_length = content_length; |
745 | 0 | } |
746 | | |
747 | 0 | if (nxt_fast_path(r->proto.any != NULL)) { |
748 | 0 | nxt_http_proto[r->protocol].header_send(task, r, body_handler, data); |
749 | 0 | } |
750 | |
|
751 | 0 | return; |
752 | | |
753 | 0 | fail: |
754 | |
|
755 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
756 | 0 | } |
757 | | |
758 | | |
759 | | void |
760 | | nxt_http_request_ws_frame_start(nxt_task_t *task, nxt_http_request_t *r, |
761 | | nxt_buf_t *ws_frame) |
762 | 0 | { |
763 | 0 | if (r->proto.any != NULL) { |
764 | 0 | nxt_http_proto[r->protocol].ws_frame_start(task, r, ws_frame); |
765 | 0 | } |
766 | 0 | } |
767 | | |
768 | | |
769 | | void |
770 | | nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) |
771 | 0 | { |
772 | 0 | if (nxt_fast_path(r->proto.any != NULL)) { |
773 | 0 | nxt_http_proto[r->protocol].send(task, r, out); |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | |
778 | | nxt_buf_t * |
779 | | nxt_http_buf_mem(nxt_task_t *task, nxt_http_request_t *r, size_t size) |
780 | 0 | { |
781 | 0 | nxt_buf_t *b; |
782 | |
|
783 | 0 | b = nxt_buf_mem_alloc(r->mem_pool, size, 0); |
784 | 0 | if (nxt_fast_path(b != NULL)) { |
785 | 0 | b->completion_handler = nxt_http_request_mem_buf_completion; |
786 | 0 | b->parent = r; |
787 | 0 | nxt_mp_retain(r->mem_pool); |
788 | |
|
789 | 0 | } else { |
790 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
791 | 0 | } |
792 | |
|
793 | 0 | return b; |
794 | 0 | } |
795 | | |
796 | | |
797 | | static void |
798 | | nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data) |
799 | 0 | { |
800 | 0 | nxt_buf_t *b, *next; |
801 | 0 | nxt_http_request_t *r; |
802 | |
|
803 | 0 | b = obj; |
804 | 0 | r = data; |
805 | |
|
806 | 0 | do { |
807 | 0 | next = b->next; |
808 | |
|
809 | 0 | nxt_mp_free(r->mem_pool, b); |
810 | 0 | nxt_mp_release(r->mem_pool); |
811 | |
|
812 | 0 | b = next; |
813 | 0 | } while (b != NULL); |
814 | 0 | } |
815 | | |
816 | | |
817 | | nxt_buf_t * |
818 | | nxt_http_buf_last(nxt_http_request_t *r) |
819 | 0 | { |
820 | 0 | nxt_buf_t *last; |
821 | |
|
822 | 0 | last = r->last; |
823 | 0 | r->last = NULL; |
824 | |
|
825 | 0 | return last; |
826 | 0 | } |
827 | | |
828 | | |
829 | | static void |
830 | | nxt_http_request_done(nxt_task_t *task, void *obj, void *data) |
831 | 0 | { |
832 | 0 | nxt_http_request_t *r; |
833 | |
|
834 | 0 | r = data; |
835 | |
|
836 | 0 | nxt_debug(task, "http request done"); |
837 | |
|
838 | 0 | nxt_http_request_close_handler(task, r, r->proto.any); |
839 | 0 | } |
840 | | |
841 | | |
842 | | void |
843 | | nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data) |
844 | 0 | { |
845 | 0 | nxt_http_proto_t proto; |
846 | 0 | nxt_http_request_t *r; |
847 | |
|
848 | 0 | r = obj; |
849 | 0 | proto.any = data; |
850 | |
|
851 | 0 | nxt_debug(task, "http request error handler"); |
852 | |
|
853 | 0 | r->error = 1; |
854 | |
|
855 | 0 | if (nxt_fast_path(proto.any != NULL)) { |
856 | 0 | nxt_http_proto[r->protocol].discard(task, r, nxt_http_buf_last(r)); |
857 | 0 | } |
858 | 0 | } |
859 | | |
860 | | |
861 | | void |
862 | | nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) |
863 | 0 | { |
864 | 0 | nxt_int_t ret; |
865 | 0 | nxt_http_proto_t proto; |
866 | 0 | nxt_router_conf_t *rtcf; |
867 | 0 | nxt_http_request_t *r; |
868 | 0 | nxt_http_protocol_t protocol; |
869 | 0 | nxt_socket_conf_joint_t *conf; |
870 | |
|
871 | 0 | r = obj; |
872 | 0 | proto.any = data; |
873 | |
|
874 | 0 | conf = r->conf; |
875 | 0 | rtcf = conf->socket_conf->router_conf; |
876 | |
|
877 | 0 | if (!r->logged) { |
878 | 0 | r->logged = 1; |
879 | |
|
880 | 0 | if (rtcf->access_log != NULL) { |
881 | 0 | ret = nxt_http_request_access_log(task, r, rtcf); |
882 | 0 | if (ret == NXT_OK) { |
883 | 0 | return; |
884 | 0 | } |
885 | 0 | } |
886 | 0 | } |
887 | | |
888 | 0 | nxt_debug(task, "http request close handler"); |
889 | |
|
890 | 0 | r->proto.any = NULL; |
891 | |
|
892 | 0 | if (r->body != NULL && nxt_buf_is_file(r->body) |
893 | 0 | && r->body->file->fd != -1) |
894 | 0 | { |
895 | 0 | nxt_fd_close(r->body->file->fd); |
896 | |
|
897 | 0 | r->body->file->fd = -1; |
898 | 0 | } |
899 | |
|
900 | 0 | if (r->tstr_query != NULL) { |
901 | 0 | nxt_tstr_query_release(r->tstr_query); |
902 | 0 | } |
903 | |
|
904 | 0 | if (nxt_fast_path(proto.any != NULL)) { |
905 | 0 | protocol = r->protocol; |
906 | |
|
907 | 0 | nxt_http_proto[protocol].close(task, proto, conf); |
908 | |
|
909 | 0 | nxt_mp_release(r->mem_pool); |
910 | 0 | } |
911 | 0 | } |
912 | | |
913 | | |
914 | | static nxt_int_t |
915 | | nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, |
916 | | nxt_router_conf_t *rtcf) |
917 | 0 | { |
918 | 0 | nxt_int_t ret; |
919 | 0 | nxt_str_t str; |
920 | 0 | nxt_bool_t expr; |
921 | 0 | nxt_router_access_log_t *access_log; |
922 | |
|
923 | 0 | access_log = rtcf->access_log; |
924 | |
|
925 | 0 | expr = 1; |
926 | |
|
927 | 0 | if (rtcf->log_expr != NULL) { |
928 | |
|
929 | 0 | if (nxt_tstr_is_const(rtcf->log_expr)) { |
930 | 0 | nxt_tstr_str(rtcf->log_expr, &str); |
931 | |
|
932 | 0 | } else { |
933 | 0 | ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, |
934 | 0 | &r->tstr_cache, r, r->mem_pool); |
935 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
936 | 0 | return NXT_DECLINED; |
937 | 0 | } |
938 | | |
939 | 0 | nxt_tstr_query(task, r->tstr_query, rtcf->log_expr, &str); |
940 | |
|
941 | 0 | if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) { |
942 | 0 | return NXT_DECLINED; |
943 | 0 | } |
944 | 0 | } |
945 | | |
946 | 0 | if (str.length == 0 |
947 | 0 | || nxt_str_eq(&str, "0", 1) |
948 | 0 | || nxt_str_eq(&str, "false", 5) |
949 | 0 | || nxt_str_eq(&str, "null", 4) |
950 | 0 | || nxt_str_eq(&str, "undefined", 9)) |
951 | 0 | { |
952 | 0 | expr = 0; |
953 | 0 | } |
954 | 0 | } |
955 | | |
956 | 0 | if (rtcf->log_negate ^ expr) { |
957 | 0 | access_log->handler(task, r, access_log, rtcf->log_format); |
958 | 0 | return NXT_OK; |
959 | 0 | } |
960 | | |
961 | 0 | return NXT_DECLINED; |
962 | 0 | } |
963 | | |
964 | | |
965 | | static u_char * |
966 | | nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, |
967 | | size_t size, const char *format) |
968 | 0 | { |
969 | 0 | return nxt_http_date(buf, tm); |
970 | 0 | } |
971 | | |
972 | | |
973 | | nxt_array_t * |
974 | | nxt_http_arguments_parse(nxt_http_request_t *r) |
975 | 0 | { |
976 | 0 | size_t name_length; |
977 | 0 | u_char *p, *dst, *dst_start, *start, *end, *name; |
978 | 0 | uint8_t d0, d1; |
979 | 0 | uint32_t hash; |
980 | 0 | nxt_array_t *args; |
981 | 0 | nxt_http_name_value_t *nv; |
982 | |
|
983 | 0 | if (r->arguments != NULL) { |
984 | 0 | return r->arguments; |
985 | 0 | } |
986 | | |
987 | 0 | args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); |
988 | 0 | if (nxt_slow_path(args == NULL)) { |
989 | 0 | return NULL; |
990 | 0 | } |
991 | | |
992 | 0 | if (nxt_slow_path(r->args->start == NULL)) { |
993 | 0 | goto end; |
994 | 0 | } |
995 | | |
996 | 0 | hash = NXT_HTTP_FIELD_HASH_INIT; |
997 | 0 | name = NULL; |
998 | 0 | name_length = 0; |
999 | |
|
1000 | 0 | dst_start = nxt_mp_nget(r->mem_pool, r->args->length); |
1001 | 0 | if (nxt_slow_path(dst_start == NULL)) { |
1002 | 0 | return NULL; |
1003 | 0 | } |
1004 | | |
1005 | 0 | r->args_decoded.start = dst_start; |
1006 | |
|
1007 | 0 | start = r->args->start; |
1008 | 0 | end = start + r->args->length; |
1009 | |
|
1010 | 0 | for (p = start, dst = dst_start; p < end; p++, dst++) { |
1011 | 0 | *dst = *p; |
1012 | |
|
1013 | 0 | switch (*p) { |
1014 | 0 | case '=': |
1015 | 0 | if (name == NULL) { |
1016 | 0 | name_length = dst - dst_start; |
1017 | 0 | name = dst_start; |
1018 | 0 | dst_start = dst + 1; |
1019 | 0 | } |
1020 | |
|
1021 | 0 | continue; |
1022 | | |
1023 | 0 | case '&': |
1024 | 0 | if (name_length != 0 || dst != dst_start) { |
1025 | 0 | nv = nxt_http_argument(args, name, name_length, hash, dst_start, |
1026 | 0 | dst); |
1027 | 0 | if (nxt_slow_path(nv == NULL)) { |
1028 | 0 | return NULL; |
1029 | 0 | } |
1030 | 0 | } |
1031 | | |
1032 | 0 | hash = NXT_HTTP_FIELD_HASH_INIT; |
1033 | 0 | name_length = 0; |
1034 | 0 | name = NULL; |
1035 | 0 | dst_start = dst + 1; |
1036 | |
|
1037 | 0 | continue; |
1038 | | |
1039 | 0 | case '+': |
1040 | 0 | *dst = ' '; |
1041 | |
|
1042 | 0 | break; |
1043 | | |
1044 | 0 | case '%': |
1045 | 0 | if (nxt_slow_path(end - p <= 2)) { |
1046 | 0 | break; |
1047 | 0 | } |
1048 | | |
1049 | 0 | d0 = nxt_hex2int[p[1]]; |
1050 | 0 | d1 = nxt_hex2int[p[2]]; |
1051 | |
|
1052 | 0 | if (nxt_slow_path((d0 | d1) >= 16)) { |
1053 | 0 | break; |
1054 | 0 | } |
1055 | | |
1056 | 0 | p += 2; |
1057 | 0 | *dst = (d0 << 4) + d1; |
1058 | |
|
1059 | 0 | break; |
1060 | 0 | } |
1061 | | |
1062 | 0 | if (name == NULL) { |
1063 | 0 | hash = nxt_http_field_hash_char(hash, *dst); |
1064 | 0 | } |
1065 | 0 | } |
1066 | | |
1067 | 0 | r->args_decoded.length = dst - r->args_decoded.start; |
1068 | |
|
1069 | 0 | if (name_length != 0 || dst != dst_start) { |
1070 | 0 | nv = nxt_http_argument(args, name, name_length, hash, dst_start, dst); |
1071 | 0 | if (nxt_slow_path(nv == NULL)) { |
1072 | 0 | return NULL; |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | 0 | end: |
1077 | |
|
1078 | 0 | r->arguments = args; |
1079 | |
|
1080 | 0 | return args; |
1081 | 0 | } |
1082 | | |
1083 | | |
1084 | | static nxt_http_name_value_t * |
1085 | | nxt_http_argument(nxt_array_t *array, u_char *name, size_t name_length, |
1086 | | uint32_t hash, u_char *start, const u_char *end) |
1087 | 0 | { |
1088 | 0 | size_t length; |
1089 | 0 | nxt_http_name_value_t *nv; |
1090 | |
|
1091 | 0 | nv = nxt_array_add(array); |
1092 | 0 | if (nxt_slow_path(nv == NULL)) { |
1093 | 0 | return NULL; |
1094 | 0 | } |
1095 | | |
1096 | 0 | nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; |
1097 | |
|
1098 | 0 | length = end - start; |
1099 | |
|
1100 | 0 | if (name == NULL) { |
1101 | 0 | name_length = length; |
1102 | 0 | name = start; |
1103 | 0 | length = 0; |
1104 | 0 | } |
1105 | |
|
1106 | 0 | nv->name_length = name_length; |
1107 | 0 | nv->value_length = length; |
1108 | 0 | nv->name = name; |
1109 | 0 | nv->value = start; |
1110 | |
|
1111 | 0 | return nv; |
1112 | 0 | } |
1113 | | |
1114 | | |
1115 | | nxt_array_t * |
1116 | | nxt_http_cookies_parse(nxt_http_request_t *r) |
1117 | 0 | { |
1118 | 0 | nxt_int_t ret; |
1119 | 0 | nxt_array_t *cookies; |
1120 | 0 | nxt_http_field_t *f; |
1121 | |
|
1122 | 0 | if (r->cookies != NULL) { |
1123 | 0 | return r->cookies; |
1124 | 0 | } |
1125 | | |
1126 | 0 | cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); |
1127 | 0 | if (nxt_slow_path(cookies == NULL)) { |
1128 | 0 | return NULL; |
1129 | 0 | } |
1130 | | |
1131 | 0 | nxt_list_each(f, r->fields) { |
1132 | |
|
1133 | 0 | if (f->hash != NXT_HTTP_COOKIE_HASH |
1134 | 0 | || f->name_length != 6 |
1135 | 0 | || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0) |
1136 | 0 | { |
1137 | 0 | continue; |
1138 | 0 | } |
1139 | | |
1140 | 0 | ret = nxt_http_cookie_parse(cookies, f->value, |
1141 | 0 | f->value + f->value_length); |
1142 | 0 | if (ret != NXT_OK) { |
1143 | 0 | return NULL; |
1144 | 0 | } |
1145 | |
|
1146 | 0 | } nxt_list_loop; |
1147 | | |
1148 | 0 | r->cookies = cookies; |
1149 | |
|
1150 | 0 | return cookies; |
1151 | 0 | } |
1152 | | |
1153 | | |
1154 | | static nxt_int_t |
1155 | | nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, const u_char *end) |
1156 | 0 | { |
1157 | 0 | size_t name_length; |
1158 | 0 | u_char c, *p, *name; |
1159 | 0 | nxt_http_name_value_t *nv; |
1160 | |
|
1161 | 0 | name = NULL; |
1162 | 0 | name_length = 0; |
1163 | |
|
1164 | 0 | for (p = start; p < end; p++) { |
1165 | 0 | c = *p; |
1166 | |
|
1167 | 0 | if (c == '=' && name == NULL) { |
1168 | 0 | while (start[0] == ' ') { start++; } |
1169 | |
|
1170 | 0 | name_length = p - start; |
1171 | 0 | name = start; |
1172 | |
|
1173 | 0 | start = p + 1; |
1174 | |
|
1175 | 0 | } else if (c == ';') { |
1176 | 0 | if (name != NULL) { |
1177 | 0 | nv = nxt_http_cookie(cookies, name, name_length, start, p); |
1178 | 0 | if (nxt_slow_path(nv == NULL)) { |
1179 | 0 | return NXT_ERROR; |
1180 | 0 | } |
1181 | 0 | } |
1182 | | |
1183 | 0 | name = NULL; |
1184 | 0 | start = p + 1; |
1185 | 0 | } |
1186 | 0 | } |
1187 | | |
1188 | 0 | if (name != NULL) { |
1189 | 0 | nv = nxt_http_cookie(cookies, name, name_length, start, p); |
1190 | 0 | if (nxt_slow_path(nv == NULL)) { |
1191 | 0 | return NXT_ERROR; |
1192 | 0 | } |
1193 | 0 | } |
1194 | | |
1195 | 0 | return NXT_OK; |
1196 | 0 | } |
1197 | | |
1198 | | |
1199 | | static nxt_http_name_value_t * |
1200 | | nxt_http_cookie(nxt_array_t *array, u_char *name, size_t name_length, |
1201 | | u_char *start, const u_char *end) |
1202 | 0 | { |
1203 | 0 | u_char c, *p; |
1204 | 0 | uint32_t hash; |
1205 | 0 | nxt_http_name_value_t *nv; |
1206 | |
|
1207 | 0 | nv = nxt_array_add(array); |
1208 | 0 | if (nxt_slow_path(nv == NULL)) { |
1209 | 0 | return NULL; |
1210 | 0 | } |
1211 | | |
1212 | 0 | nv->name_length = name_length; |
1213 | 0 | nv->name = name; |
1214 | |
|
1215 | 0 | hash = NXT_HTTP_FIELD_HASH_INIT; |
1216 | |
|
1217 | 0 | for (p = name; p < name + name_length; p++) { |
1218 | 0 | c = *p; |
1219 | 0 | hash = nxt_http_field_hash_char(hash, c); |
1220 | 0 | } |
1221 | |
|
1222 | 0 | nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; |
1223 | |
|
1224 | 0 | while (start < end && end[-1] == ' ') { end--; } |
1225 | |
|
1226 | 0 | nv->value_length = end - start; |
1227 | 0 | nv->value = start; |
1228 | |
|
1229 | 0 | return nv; |
1230 | 0 | } |
1231 | | |
1232 | | |
1233 | | int64_t |
1234 | | nxt_http_field_hash(nxt_mp_t *mp, nxt_str_t *name, nxt_bool_t case_sensitive, |
1235 | | uint8_t encoding) |
1236 | 3.17k | { |
1237 | 3.17k | u_char c, *p, *src, *start, *end, plus; |
1238 | 3.17k | uint8_t d0, d1; |
1239 | 3.17k | uint32_t hash; |
1240 | 3.17k | nxt_str_t str; |
1241 | 3.17k | nxt_uint_t i; |
1242 | | |
1243 | 3.17k | str.length = name->length; |
1244 | | |
1245 | 3.17k | str.start = nxt_mp_nget(mp, str.length); |
1246 | 3.17k | if (nxt_slow_path(str.start == NULL)) { |
1247 | 0 | return -1; |
1248 | 0 | } |
1249 | | |
1250 | 3.17k | p = str.start; |
1251 | | |
1252 | 3.17k | hash = NXT_HTTP_FIELD_HASH_INIT; |
1253 | | |
1254 | 3.17k | if (encoding == NXT_HTTP_URI_ENCODING_NONE) { |
1255 | 6.79k | for (i = 0; i < name->length; i++) { |
1256 | 6.62k | c = name->start[i]; |
1257 | 6.62k | *p++ = c; |
1258 | | |
1259 | 6.62k | c = case_sensitive ? c : nxt_lowcase(c); |
1260 | 6.62k | hash = nxt_http_field_hash_char(hash, c); |
1261 | 6.62k | } |
1262 | | |
1263 | 173 | goto end; |
1264 | 173 | } |
1265 | | |
1266 | 3.00k | plus = (encoding == NXT_HTTP_URI_ENCODING_PLUS) ? ' ' : '+'; |
1267 | | |
1268 | 3.00k | start = name->start; |
1269 | 3.00k | end = start + name->length; |
1270 | | |
1271 | 23.5k | for (src = start; src < end; src++) { |
1272 | 20.5k | c = *src; |
1273 | | |
1274 | 20.5k | switch (c) { |
1275 | 0 | case '%': |
1276 | 0 | if (nxt_slow_path(end - src <= 2)) { |
1277 | 0 | return -1; |
1278 | 0 | } |
1279 | | |
1280 | 0 | d0 = nxt_hex2int[src[1]]; |
1281 | 0 | d1 = nxt_hex2int[src[2]]; |
1282 | 0 | src += 2; |
1283 | |
|
1284 | 0 | if (nxt_slow_path((d0 | d1) >= 16)) { |
1285 | 0 | return -1; |
1286 | 0 | } |
1287 | | |
1288 | 0 | c = (d0 << 4) + d1; |
1289 | 0 | *p++ = c; |
1290 | 0 | break; |
1291 | | |
1292 | 0 | case '+': |
1293 | 0 | c = plus; |
1294 | 0 | *p++ = c; |
1295 | 0 | break; |
1296 | | |
1297 | 20.5k | default: |
1298 | 20.5k | *p++ = c; |
1299 | 20.5k | break; |
1300 | 20.5k | } |
1301 | | |
1302 | 20.5k | c = case_sensitive ? c : nxt_lowcase(c); |
1303 | 20.5k | hash = nxt_http_field_hash_char(hash, c); |
1304 | 20.5k | } |
1305 | | |
1306 | 3.00k | str.length = p - str.start; |
1307 | | |
1308 | 3.17k | end: |
1309 | | |
1310 | 3.17k | *name = str; |
1311 | | |
1312 | 3.17k | return nxt_http_field_hash_end(hash) & 0xFFFF; |
1313 | 3.00k | } |
1314 | | |
1315 | | |
1316 | | int64_t |
1317 | | nxt_http_argument_hash(nxt_mp_t *mp, nxt_str_t *name) |
1318 | 3.00k | { |
1319 | 3.00k | return nxt_http_field_hash(mp, name, 1, NXT_HTTP_URI_ENCODING_PLUS); |
1320 | 3.00k | } |
1321 | | |
1322 | | |
1323 | | int64_t |
1324 | | nxt_http_header_hash(nxt_mp_t *mp, nxt_str_t *name) |
1325 | 195 | { |
1326 | 195 | u_char c, *p; |
1327 | 195 | uint32_t i, hash; |
1328 | 195 | nxt_str_t str; |
1329 | | |
1330 | 195 | str.length = name->length; |
1331 | | |
1332 | 195 | str.start = nxt_mp_nget(mp, str.length); |
1333 | 195 | if (nxt_slow_path(str.start == NULL)) { |
1334 | 0 | return -1; |
1335 | 0 | } |
1336 | | |
1337 | 195 | p = str.start; |
1338 | 195 | hash = NXT_HTTP_FIELD_HASH_INIT; |
1339 | | |
1340 | 4.91k | for (i = 0; i < name->length; i++) { |
1341 | 4.72k | c = name->start[i]; |
1342 | | |
1343 | 4.72k | if (c >= 'A' && c <= 'Z') { |
1344 | 2.05k | *p = c | 0x20; |
1345 | | |
1346 | 2.67k | } else if (c == '_') { |
1347 | 501 | *p = '-'; |
1348 | | |
1349 | 2.17k | } else { |
1350 | 2.17k | *p = c; |
1351 | 2.17k | } |
1352 | | |
1353 | 4.72k | hash = nxt_http_field_hash_char(hash, *p); |
1354 | 4.72k | p++; |
1355 | 4.72k | } |
1356 | | |
1357 | 195 | *name = str; |
1358 | | |
1359 | 195 | return nxt_http_field_hash_end(hash) & 0xFFFF; |
1360 | 195 | } |
1361 | | |
1362 | | |
1363 | | int64_t |
1364 | | nxt_http_cookie_hash(nxt_mp_t *mp, nxt_str_t *name) |
1365 | 173 | { |
1366 | 173 | return nxt_http_field_hash(mp, name, 1, NXT_HTTP_URI_ENCODING_NONE); |
1367 | 173 | } |