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