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