/src/unit/src/nxt_http_parse.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) NGINX, Inc. |
4 | | * Copyright (C) Valentin V. Bartenev |
5 | | */ |
6 | | |
7 | | #include <nxt_main.h> |
8 | | |
9 | | |
10 | | static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, |
11 | | u_char **pos, const u_char *end); |
12 | | static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp, |
13 | | u_char **pos, const u_char *end); |
14 | | static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp, |
15 | | u_char **pos, const u_char *end); |
16 | | static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp, |
17 | | u_char **pos, const u_char *end); |
18 | | static u_char *nxt_http_lookup_field_end(u_char *p, const u_char *end); |
19 | | static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp, |
20 | | u_char **pos, const u_char *end); |
21 | | |
22 | | static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data); |
23 | | |
24 | | static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, |
25 | | void *data); |
26 | | |
27 | | |
28 | | #define NXT_HTTP_MAX_FIELD_NAME 0xFF |
29 | | #define NXT_HTTP_MAX_FIELD_VALUE NXT_INT32_T_MAX |
30 | | |
31 | 0 | #define NXT_HTTP_FIELD_LVLHSH_SHIFT 5 |
32 | | |
33 | | |
34 | | typedef enum { |
35 | | NXT_HTTP_TARGET_SPACE = 1, /* \s */ |
36 | | NXT_HTTP_TARGET_HASH, /* # */ |
37 | | NXT_HTTP_TARGET_AGAIN, |
38 | | NXT_HTTP_TARGET_BAD, /* \0\r\n */ |
39 | | |
40 | | /* traps below are used for extended check only */ |
41 | | |
42 | | NXT_HTTP_TARGET_SLASH = 5, /* / */ |
43 | | NXT_HTTP_TARGET_DOT, /* . */ |
44 | | NXT_HTTP_TARGET_ARGS_MARK, /* ? */ |
45 | | NXT_HTTP_TARGET_QUOTE_MARK, /* % */ |
46 | | } nxt_http_target_traps_e; |
47 | | |
48 | | |
49 | | static const uint8_t nxt_http_target_chars[256] nxt_aligned(64) = { |
50 | | /* \0 \n \r */ |
51 | | 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, |
52 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
53 | | |
54 | | /* \s ! " # $ % & ' ( ) * + , - . / */ |
55 | | 1, 0, 0, 2, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, |
56 | | |
57 | | /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ |
58 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, |
59 | | }; |
60 | | |
61 | | |
62 | | nxt_inline nxt_http_target_traps_e |
63 | | nxt_http_parse_target(u_char **pos, const u_char *end) |
64 | 0 | { |
65 | 0 | u_char *p; |
66 | 0 | nxt_uint_t trap; |
67 | |
|
68 | 0 | p = *pos; |
69 | |
|
70 | 0 | while (nxt_fast_path(end - p >= 10)) { |
71 | |
|
72 | 0 | #define nxt_target_test_char(ch) \ |
73 | 0 | \ |
74 | 0 | trap = nxt_http_target_chars[ch]; \ |
75 | 0 | \ |
76 | 0 | if (nxt_slow_path(trap != 0)) { \ |
77 | 0 | *pos = &(ch); \ |
78 | 0 | return trap; \ |
79 | 0 | } |
80 | | |
81 | | /* enddef */ |
82 | |
|
83 | 0 | nxt_target_test_char(p[0]); |
84 | 0 | nxt_target_test_char(p[1]); |
85 | 0 | nxt_target_test_char(p[2]); |
86 | 0 | nxt_target_test_char(p[3]); |
87 | |
|
88 | 0 | nxt_target_test_char(p[4]); |
89 | 0 | nxt_target_test_char(p[5]); |
90 | 0 | nxt_target_test_char(p[6]); |
91 | 0 | nxt_target_test_char(p[7]); |
92 | |
|
93 | 0 | nxt_target_test_char(p[8]); |
94 | 0 | nxt_target_test_char(p[9]); |
95 | |
|
96 | 0 | p += 10; |
97 | 0 | } |
98 | | |
99 | 0 | while (p != end) { |
100 | 0 | nxt_target_test_char(*p); p++; |
101 | 0 | } |
102 | | |
103 | 0 | return NXT_HTTP_TARGET_AGAIN; |
104 | 0 | } |
105 | | |
106 | | |
107 | | nxt_int_t |
108 | | nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp) |
109 | 0 | { |
110 | 0 | rp->mem_pool = mp; |
111 | |
|
112 | 0 | rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t)); |
113 | 0 | if (nxt_slow_path(rp->fields == NULL)) { |
114 | 0 | return NXT_ERROR; |
115 | 0 | } |
116 | | |
117 | 0 | rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; |
118 | |
|
119 | 0 | return NXT_OK; |
120 | 0 | } |
121 | | |
122 | | |
123 | | nxt_int_t |
124 | | nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) |
125 | 0 | { |
126 | 0 | nxt_int_t rc; |
127 | |
|
128 | 0 | if (rp->handler == NULL) { |
129 | 0 | rp->handler = &nxt_http_parse_request_line; |
130 | 0 | } |
131 | |
|
132 | 0 | do { |
133 | 0 | rc = rp->handler(rp, &b->pos, b->free); |
134 | 0 | } while (rc == NXT_OK); |
135 | |
|
136 | 0 | return rc; |
137 | 0 | } |
138 | | |
139 | | |
140 | | nxt_int_t |
141 | | nxt_http_parse_fields(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) |
142 | 0 | { |
143 | 0 | nxt_int_t rc; |
144 | |
|
145 | 0 | if (rp->handler == NULL) { |
146 | 0 | rp->handler = &nxt_http_parse_field_name; |
147 | 0 | } |
148 | |
|
149 | 0 | do { |
150 | 0 | rc = rp->handler(rp, &b->pos, b->free); |
151 | 0 | } while (rc == NXT_OK); |
152 | |
|
153 | 0 | return rc; |
154 | 0 | } |
155 | | |
156 | | |
157 | | static nxt_int_t |
158 | | nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos, |
159 | | const u_char *end) |
160 | 0 | { |
161 | 0 | u_char *p, ch, *after_slash, *args; |
162 | 0 | nxt_int_t rc; |
163 | 0 | nxt_bool_t rest; |
164 | 0 | nxt_http_ver_t ver; |
165 | 0 | nxt_http_target_traps_e trap; |
166 | |
|
167 | 0 | static const nxt_http_ver_t http11 = { "HTTP/1.1" }; |
168 | 0 | static const nxt_http_ver_t http10 = { "HTTP/1.0" }; |
169 | |
|
170 | 0 | p = *pos; |
171 | |
|
172 | 0 | rp->method.start = p; |
173 | |
|
174 | 0 | for ( ;; ) { |
175 | |
|
176 | 0 | while (nxt_fast_path(end - p >= 8)) { |
177 | |
|
178 | 0 | #define nxt_method_test_char(ch) \ |
179 | 0 | \ |
180 | 0 | if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) { \ |
181 | 0 | p = &(ch); \ |
182 | 0 | goto method_unusual_char; \ |
183 | 0 | } |
184 | | |
185 | | /* enddef */ |
186 | |
|
187 | 0 | nxt_method_test_char(p[0]); |
188 | 0 | nxt_method_test_char(p[1]); |
189 | 0 | nxt_method_test_char(p[2]); |
190 | 0 | nxt_method_test_char(p[3]); |
191 | |
|
192 | 0 | nxt_method_test_char(p[4]); |
193 | 0 | nxt_method_test_char(p[5]); |
194 | 0 | nxt_method_test_char(p[6]); |
195 | 0 | nxt_method_test_char(p[7]); |
196 | |
|
197 | 0 | p += 8; |
198 | 0 | } |
199 | | |
200 | 0 | while (p != end) { |
201 | 0 | nxt_method_test_char(*p); p++; |
202 | 0 | } |
203 | | |
204 | 0 | rp->method.length = p - rp->method.start; |
205 | |
|
206 | 0 | return NXT_AGAIN; |
207 | | |
208 | 0 | method_unusual_char: |
209 | |
|
210 | 0 | ch = *p; |
211 | |
|
212 | 0 | if (nxt_fast_path(ch == ' ')) { |
213 | 0 | rp->method.length = p - rp->method.start; |
214 | 0 | break; |
215 | 0 | } |
216 | | |
217 | 0 | if (ch == '_' || ch == '-') { |
218 | 0 | p++; |
219 | 0 | continue; |
220 | 0 | } |
221 | | |
222 | 0 | if (rp->method.start == p && (ch == '\r' || ch == '\n')) { |
223 | 0 | rp->method.start++; |
224 | 0 | p++; |
225 | 0 | continue; |
226 | 0 | } |
227 | | |
228 | 0 | rp->method.length = p - rp->method.start; |
229 | |
|
230 | 0 | return NXT_HTTP_PARSE_INVALID; |
231 | 0 | } |
232 | | |
233 | 0 | p++; |
234 | |
|
235 | 0 | if (nxt_slow_path(p == end)) { |
236 | 0 | return NXT_AGAIN; |
237 | 0 | } |
238 | | |
239 | | /* target */ |
240 | | |
241 | 0 | ch = *p; |
242 | |
|
243 | 0 | if (nxt_slow_path(ch != '/')) { |
244 | 0 | rc = nxt_http_parse_unusual_target(rp, &p, end); |
245 | |
|
246 | 0 | if (nxt_slow_path(rc != NXT_OK)) { |
247 | 0 | return rc; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | 0 | rp->target_start = p; |
252 | |
|
253 | 0 | after_slash = p + 1; |
254 | 0 | args = NULL; |
255 | 0 | rest = 0; |
256 | |
|
257 | 0 | continue_target: |
258 | |
|
259 | 0 | for ( ;; ) { |
260 | 0 | p++; |
261 | |
|
262 | 0 | trap = nxt_http_parse_target(&p, end); |
263 | |
|
264 | 0 | switch (trap) { |
265 | 0 | case NXT_HTTP_TARGET_SLASH: |
266 | 0 | if (nxt_slow_path(after_slash == p)) { |
267 | 0 | rp->complex_target = 1; |
268 | 0 | goto rest_of_target; |
269 | 0 | } |
270 | | |
271 | 0 | after_slash = p + 1; |
272 | 0 | continue; |
273 | | |
274 | 0 | case NXT_HTTP_TARGET_DOT: |
275 | 0 | if (nxt_slow_path(after_slash == p)) { |
276 | 0 | rp->complex_target = 1; |
277 | 0 | goto rest_of_target; |
278 | 0 | } |
279 | | |
280 | 0 | continue; |
281 | | |
282 | 0 | case NXT_HTTP_TARGET_ARGS_MARK: |
283 | 0 | args = p + 1; |
284 | 0 | goto rest_of_target; |
285 | | |
286 | 0 | case NXT_HTTP_TARGET_SPACE: |
287 | 0 | rp->target_end = p; |
288 | 0 | goto space_after_target; |
289 | | |
290 | 0 | case NXT_HTTP_TARGET_QUOTE_MARK: |
291 | 0 | rp->quoted_target = 1; |
292 | 0 | goto rest_of_target; |
293 | | |
294 | 0 | case NXT_HTTP_TARGET_HASH: |
295 | 0 | rp->complex_target = 1; |
296 | 0 | goto rest_of_target; |
297 | | |
298 | 0 | case NXT_HTTP_TARGET_AGAIN: |
299 | 0 | rp->target_end = p; |
300 | 0 | return NXT_AGAIN; |
301 | | |
302 | 0 | case NXT_HTTP_TARGET_BAD: |
303 | 0 | rp->target_end = p; |
304 | 0 | return NXT_HTTP_PARSE_INVALID; |
305 | 0 | } |
306 | | |
307 | 0 | nxt_unreachable(); |
308 | 0 | } |
309 | | |
310 | 0 | rest_of_target: |
311 | |
|
312 | 0 | rest = 1; |
313 | |
|
314 | 0 | for ( ;; ) { |
315 | 0 | p++; |
316 | |
|
317 | 0 | trap = nxt_http_parse_target(&p, end); |
318 | |
|
319 | 0 | switch (trap) { |
320 | 0 | case NXT_HTTP_TARGET_SPACE: |
321 | 0 | rp->target_end = p; |
322 | 0 | goto space_after_target; |
323 | | |
324 | 0 | case NXT_HTTP_TARGET_HASH: |
325 | 0 | rp->complex_target = 1; |
326 | 0 | continue; |
327 | | |
328 | 0 | case NXT_HTTP_TARGET_AGAIN: |
329 | 0 | rp->target_end = p; |
330 | 0 | return NXT_AGAIN; |
331 | | |
332 | 0 | case NXT_HTTP_TARGET_BAD: |
333 | 0 | rp->target_end = p; |
334 | 0 | return NXT_HTTP_PARSE_INVALID; |
335 | | |
336 | 0 | default: |
337 | 0 | continue; |
338 | 0 | } |
339 | | |
340 | 0 | nxt_unreachable(); |
341 | 0 | } |
342 | | |
343 | 0 | space_after_target: |
344 | |
|
345 | 0 | if (nxt_slow_path(end - p < 10)) { |
346 | |
|
347 | 0 | do { |
348 | 0 | p++; |
349 | |
|
350 | 0 | if (p == end) { |
351 | 0 | return NXT_AGAIN; |
352 | 0 | } |
353 | |
|
354 | 0 | } while (*p == ' '); |
355 | | |
356 | 0 | if (memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) { |
357 | |
|
358 | 0 | switch (end - p) { |
359 | 0 | case 8: |
360 | 0 | if (p[7] < '0' || p[7] > '9') { |
361 | 0 | break; |
362 | 0 | } |
363 | | /* Fall through. */ |
364 | 0 | case 7: |
365 | 0 | if (p[6] != '.') { |
366 | 0 | break; |
367 | 0 | } |
368 | | /* Fall through. */ |
369 | 0 | case 6: |
370 | 0 | if (p[5] < '0' || p[5] > '9') { |
371 | 0 | break; |
372 | 0 | } |
373 | | /* Fall through. */ |
374 | 0 | default: |
375 | 0 | return NXT_AGAIN; |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | //rp->space_in_target = 1; |
380 | | |
381 | 0 | if (rest) { |
382 | 0 | goto rest_of_target; |
383 | 0 | } |
384 | | |
385 | 0 | goto continue_target; |
386 | 0 | } |
387 | | |
388 | | /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */ |
389 | | |
390 | 0 | if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) { |
391 | |
|
392 | 0 | if (p[1] == ' ') { |
393 | | /* surplus space after tartet */ |
394 | 0 | p++; |
395 | 0 | goto space_after_target; |
396 | 0 | } |
397 | | |
398 | | //rp->space_in_target = 1; |
399 | | |
400 | 0 | if (rest) { |
401 | 0 | goto rest_of_target; |
402 | 0 | } |
403 | | |
404 | 0 | goto continue_target; |
405 | 0 | } |
406 | | |
407 | 0 | nxt_memcpy(ver.str, &p[1], 8); |
408 | |
|
409 | 0 | if (nxt_fast_path(ver.ui64 == http11.ui64 |
410 | 0 | || ver.ui64 == http10.ui64 |
411 | 0 | || (memcmp(ver.str, "HTTP/1.", 7) == 0 |
412 | 0 | && ver.s.minor >= '0' && ver.s.minor <= '9'))) |
413 | 0 | { |
414 | 0 | rp->version.ui64 = ver.ui64; |
415 | |
|
416 | 0 | p += 9; |
417 | 0 | if (nxt_fast_path(*p == '\r')) { |
418 | |
|
419 | 0 | if (nxt_slow_path(p + 1 == end)) { |
420 | 0 | return NXT_AGAIN; |
421 | 0 | } |
422 | | |
423 | 0 | if (nxt_slow_path(p[1] != '\n')) { |
424 | 0 | return NXT_HTTP_PARSE_INVALID; |
425 | 0 | } |
426 | | |
427 | 0 | *pos = p + 2; |
428 | |
|
429 | 0 | } else { |
430 | 0 | *pos = p + 1; |
431 | 0 | } |
432 | | |
433 | 0 | rp->request_line_end = p; |
434 | |
|
435 | 0 | if (rp->complex_target || rp->quoted_target) { |
436 | 0 | rc = nxt_http_parse_complex_target(rp); |
437 | |
|
438 | 0 | if (nxt_slow_path(rc != NXT_OK)) { |
439 | 0 | return rc; |
440 | 0 | } |
441 | | |
442 | 0 | return nxt_http_parse_field_name(rp, pos, end); |
443 | 0 | } |
444 | | |
445 | 0 | rp->path.start = rp->target_start; |
446 | |
|
447 | 0 | if (args != NULL) { |
448 | 0 | rp->path.length = args - rp->target_start - 1; |
449 | |
|
450 | 0 | rp->args.length = rp->target_end - args; |
451 | 0 | rp->args.start = args; |
452 | |
|
453 | 0 | } else { |
454 | 0 | rp->path.length = rp->target_end - rp->target_start; |
455 | 0 | } |
456 | |
|
457 | 0 | return nxt_http_parse_field_name(rp, pos, end); |
458 | 0 | } |
459 | | |
460 | 0 | if (memcmp(ver.s.prefix, "HTTP/", 5) == 0 |
461 | 0 | && ver.s.major >= '0' && ver.s.major <= '9' |
462 | 0 | && ver.s.point == '.' |
463 | 0 | && ver.s.minor >= '0' && ver.s.minor <= '9') |
464 | 0 | { |
465 | 0 | rp->version.ui64 = ver.ui64; |
466 | 0 | return NXT_HTTP_PARSE_UNSUPPORTED_VERSION; |
467 | 0 | } |
468 | | |
469 | 0 | return NXT_HTTP_PARSE_INVALID; |
470 | 0 | } |
471 | | |
472 | | |
473 | | static nxt_int_t |
474 | | nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos, |
475 | | const u_char *end) |
476 | 0 | { |
477 | 0 | u_char *p, ch; |
478 | |
|
479 | 0 | p = *pos; |
480 | |
|
481 | 0 | ch = *p; |
482 | |
|
483 | 0 | if (ch == ' ') { |
484 | | /* skip surplus spaces before target */ |
485 | |
|
486 | 0 | do { |
487 | 0 | p++; |
488 | |
|
489 | 0 | if (nxt_slow_path(p == end)) { |
490 | 0 | return NXT_AGAIN; |
491 | 0 | } |
492 | | |
493 | 0 | ch = *p; |
494 | |
|
495 | 0 | } while (ch == ' '); |
496 | | |
497 | 0 | if (ch == '/') { |
498 | 0 | *pos = p; |
499 | 0 | return NXT_OK; |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | | /* absolute path or '*' */ |
504 | | |
505 | | /* TODO */ |
506 | | |
507 | 0 | return NXT_HTTP_PARSE_INVALID; |
508 | 0 | } |
509 | | |
510 | | |
511 | | static nxt_int_t |
512 | | nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, |
513 | | const u_char *end) |
514 | 0 | { |
515 | 0 | u_char *p, c; |
516 | 0 | size_t len; |
517 | 0 | uint32_t hash; |
518 | |
|
519 | 0 | static const u_char normal[256] NXT_NONSTRING nxt_aligned(64) = |
520 | 0 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
521 | | /* \s ! " # $ % & ' ( ) * + , . / : ; < = > ? */ |
522 | 0 | "\0\1\0\1\1\1\1\1\0\0\1\1\0" "-" "\1\0" "0123456789" "\0\0\0\0\0\0" |
523 | | |
524 | | /* @ [ \ ] ^ _ */ |
525 | 0 | "\0" "abcdefghijklmnopqrstuvwxyz" "\0\0\0\1\1" |
526 | | /* ` { | } ~ */ |
527 | 0 | "\1" "abcdefghijklmnopqrstuvwxyz" "\0\1\0\1\0" |
528 | |
|
529 | 0 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
530 | 0 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
531 | 0 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
532 | 0 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; |
533 | |
|
534 | 0 | p = *pos + rp->field_name.length; |
535 | 0 | hash = rp->field_hash; |
536 | |
|
537 | 0 | while (nxt_fast_path(end - p >= 8)) { |
538 | |
|
539 | 0 | #define nxt_field_name_test_char(ch) \ |
540 | 0 | \ |
541 | 0 | c = normal[ch]; \ |
542 | 0 | \ |
543 | 0 | if (nxt_slow_path(c <= '\1')) { \ |
544 | 0 | if (c == '\0') { \ |
545 | 0 | p = &(ch); \ |
546 | 0 | goto name_end; \ |
547 | 0 | } \ |
548 | 0 | \ |
549 | 0 | rp->skip_field = rp->discard_unsafe_fields; \ |
550 | 0 | c = ch; \ |
551 | 0 | } \ |
552 | 0 | \ |
553 | 0 | hash = nxt_http_field_hash_char(hash, c); |
554 | | |
555 | | /* enddef */ |
556 | |
|
557 | 0 | nxt_field_name_test_char(p[0]); |
558 | 0 | nxt_field_name_test_char(p[1]); |
559 | 0 | nxt_field_name_test_char(p[2]); |
560 | 0 | nxt_field_name_test_char(p[3]); |
561 | |
|
562 | 0 | nxt_field_name_test_char(p[4]); |
563 | 0 | nxt_field_name_test_char(p[5]); |
564 | 0 | nxt_field_name_test_char(p[6]); |
565 | 0 | nxt_field_name_test_char(p[7]); |
566 | |
|
567 | 0 | p += 8; |
568 | 0 | } |
569 | | |
570 | 0 | while (nxt_fast_path(p != end)) { |
571 | 0 | nxt_field_name_test_char(*p); p++; |
572 | 0 | } |
573 | | |
574 | 0 | len = p - *pos; |
575 | |
|
576 | 0 | if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { |
577 | 0 | return NXT_HTTP_PARSE_TOO_LARGE_FIELD; |
578 | 0 | } |
579 | | |
580 | 0 | rp->field_hash = hash; |
581 | 0 | rp->field_name.length = len; |
582 | |
|
583 | 0 | rp->handler = &nxt_http_parse_field_name; |
584 | |
|
585 | 0 | return NXT_AGAIN; |
586 | | |
587 | 0 | name_end: |
588 | |
|
589 | 0 | if (nxt_fast_path(*p == ':')) { |
590 | 0 | if (nxt_slow_path(p == *pos)) { |
591 | 0 | return NXT_HTTP_PARSE_INVALID; |
592 | 0 | } |
593 | | |
594 | 0 | len = p - *pos; |
595 | |
|
596 | 0 | if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { |
597 | 0 | return NXT_HTTP_PARSE_TOO_LARGE_FIELD; |
598 | 0 | } |
599 | | |
600 | 0 | rp->field_hash = hash; |
601 | |
|
602 | 0 | rp->field_name.length = len; |
603 | 0 | rp->field_name.start = *pos; |
604 | |
|
605 | 0 | *pos = p + 1; |
606 | |
|
607 | 0 | return nxt_http_parse_field_value(rp, pos, end); |
608 | 0 | } |
609 | | |
610 | 0 | if (nxt_slow_path(p != *pos)) { |
611 | 0 | return NXT_HTTP_PARSE_INVALID; |
612 | 0 | } |
613 | | |
614 | 0 | return nxt_http_parse_field_end(rp, pos, end); |
615 | 0 | } |
616 | | |
617 | | |
618 | | static nxt_int_t |
619 | | nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos, |
620 | | const u_char *end) |
621 | 0 | { |
622 | 0 | u_char *p, *start, ch; |
623 | 0 | size_t len; |
624 | |
|
625 | 0 | p = *pos; |
626 | |
|
627 | 0 | for ( ;; ) { |
628 | 0 | if (nxt_slow_path(p == end)) { |
629 | 0 | *pos = p; |
630 | 0 | rp->handler = &nxt_http_parse_field_value; |
631 | 0 | return NXT_AGAIN; |
632 | 0 | } |
633 | | |
634 | 0 | ch = *p; |
635 | |
|
636 | 0 | if (ch != ' ' && ch != '\t') { |
637 | 0 | break; |
638 | 0 | } |
639 | | |
640 | 0 | p++; |
641 | 0 | } |
642 | | |
643 | 0 | start = p; |
644 | |
|
645 | 0 | p += rp->field_value.length; |
646 | |
|
647 | 0 | for ( ;; ) { |
648 | 0 | p = nxt_http_lookup_field_end(p, end); |
649 | |
|
650 | 0 | if (nxt_slow_path(p == end)) { |
651 | 0 | *pos = start; |
652 | |
|
653 | 0 | len = p - start; |
654 | |
|
655 | 0 | if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { |
656 | 0 | return NXT_HTTP_PARSE_TOO_LARGE_FIELD; |
657 | 0 | } |
658 | | |
659 | 0 | rp->field_value.length = len; |
660 | 0 | rp->handler = &nxt_http_parse_field_value; |
661 | 0 | return NXT_AGAIN; |
662 | 0 | } |
663 | | |
664 | 0 | ch = *p; |
665 | |
|
666 | 0 | if (nxt_fast_path(ch == '\r' || ch == '\n')) { |
667 | 0 | break; |
668 | 0 | } |
669 | | |
670 | 0 | if (ch != '\t') { |
671 | 0 | return NXT_HTTP_PARSE_INVALID; |
672 | 0 | } |
673 | | |
674 | 0 | p++; |
675 | 0 | } |
676 | | |
677 | 0 | *pos = p; |
678 | |
|
679 | 0 | if (nxt_fast_path(p != start)) { |
680 | |
|
681 | 0 | while (p[-1] == ' ' || p[-1] == '\t') { |
682 | 0 | p--; |
683 | 0 | } |
684 | 0 | } |
685 | |
|
686 | 0 | len = p - start; |
687 | |
|
688 | 0 | if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { |
689 | 0 | return NXT_HTTP_PARSE_TOO_LARGE_FIELD; |
690 | 0 | } |
691 | | |
692 | 0 | rp->field_value.length = len; |
693 | 0 | rp->field_value.start = start; |
694 | |
|
695 | 0 | return nxt_http_parse_field_end(rp, pos, end); |
696 | 0 | } |
697 | | |
698 | | |
699 | | static u_char * |
700 | | nxt_http_lookup_field_end(u_char *p, const u_char *end) |
701 | 0 | { |
702 | 0 | while (nxt_fast_path(end - p >= 16)) { |
703 | |
|
704 | 0 | #define nxt_field_end_test_char(ch) \ |
705 | 0 | \ |
706 | 0 | if (nxt_slow_path((ch) < 0x20)) { \ |
707 | 0 | return &(ch); \ |
708 | 0 | } |
709 | | |
710 | | /* enddef */ |
711 | |
|
712 | 0 | nxt_field_end_test_char(p[0]); |
713 | 0 | nxt_field_end_test_char(p[1]); |
714 | 0 | nxt_field_end_test_char(p[2]); |
715 | 0 | nxt_field_end_test_char(p[3]); |
716 | |
|
717 | 0 | nxt_field_end_test_char(p[4]); |
718 | 0 | nxt_field_end_test_char(p[5]); |
719 | 0 | nxt_field_end_test_char(p[6]); |
720 | 0 | nxt_field_end_test_char(p[7]); |
721 | |
|
722 | 0 | nxt_field_end_test_char(p[8]); |
723 | 0 | nxt_field_end_test_char(p[9]); |
724 | 0 | nxt_field_end_test_char(p[10]); |
725 | 0 | nxt_field_end_test_char(p[11]); |
726 | |
|
727 | 0 | nxt_field_end_test_char(p[12]); |
728 | 0 | nxt_field_end_test_char(p[13]); |
729 | 0 | nxt_field_end_test_char(p[14]); |
730 | 0 | nxt_field_end_test_char(p[15]); |
731 | |
|
732 | 0 | p += 16; |
733 | 0 | } |
734 | | |
735 | 0 | while (nxt_fast_path(end - p >= 4)) { |
736 | |
|
737 | 0 | nxt_field_end_test_char(p[0]); |
738 | 0 | nxt_field_end_test_char(p[1]); |
739 | 0 | nxt_field_end_test_char(p[2]); |
740 | 0 | nxt_field_end_test_char(p[3]); |
741 | |
|
742 | 0 | p += 4; |
743 | 0 | } |
744 | | |
745 | 0 | switch (end - p) { |
746 | 0 | case 3: |
747 | 0 | nxt_field_end_test_char(*p); p++; |
748 | | /* Fall through. */ |
749 | 0 | case 2: |
750 | 0 | nxt_field_end_test_char(*p); p++; |
751 | | /* Fall through. */ |
752 | 0 | case 1: |
753 | 0 | nxt_field_end_test_char(*p); p++; |
754 | | /* Fall through. */ |
755 | 0 | case 0: |
756 | 0 | break; |
757 | 0 | default: |
758 | 0 | nxt_unreachable(); |
759 | 0 | } |
760 | | |
761 | 0 | return p; |
762 | 0 | } |
763 | | |
764 | | |
765 | | static nxt_int_t |
766 | | nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos, |
767 | | const u_char *end) |
768 | 0 | { |
769 | 0 | u_char *p; |
770 | 0 | nxt_http_field_t *field; |
771 | |
|
772 | 0 | p = *pos; |
773 | |
|
774 | 0 | if (nxt_fast_path(*p == '\r')) { |
775 | 0 | p++; |
776 | |
|
777 | 0 | if (nxt_slow_path(p == end)) { |
778 | 0 | rp->handler = &nxt_http_parse_field_end; |
779 | 0 | return NXT_AGAIN; |
780 | 0 | } |
781 | 0 | } |
782 | | |
783 | 0 | if (nxt_fast_path(*p == '\n')) { |
784 | 0 | *pos = p + 1; |
785 | |
|
786 | 0 | if (rp->field_name.length != 0) { |
787 | 0 | if (rp->skip_field) { |
788 | 0 | rp->skip_field = 0; |
789 | |
|
790 | 0 | } else { |
791 | 0 | field = nxt_list_add(rp->fields); |
792 | |
|
793 | 0 | if (nxt_slow_path(field == NULL)) { |
794 | 0 | return NXT_ERROR; |
795 | 0 | } |
796 | | |
797 | 0 | field->hash = nxt_http_field_hash_end(rp->field_hash); |
798 | 0 | field->skip = 0; |
799 | 0 | field->hopbyhop = 0; |
800 | |
|
801 | 0 | field->name_length = rp->field_name.length; |
802 | 0 | field->value_length = rp->field_value.length; |
803 | 0 | field->name = rp->field_name.start; |
804 | 0 | field->value = rp->field_value.start; |
805 | 0 | } |
806 | | |
807 | 0 | rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; |
808 | |
|
809 | 0 | rp->field_name.length = 0; |
810 | 0 | rp->field_value.length = 0; |
811 | |
|
812 | 0 | rp->handler = &nxt_http_parse_field_name; |
813 | 0 | return NXT_OK; |
814 | 0 | } |
815 | | |
816 | 0 | return NXT_DONE; |
817 | 0 | } |
818 | | |
819 | 0 | return NXT_HTTP_PARSE_INVALID; |
820 | 0 | } |
821 | | |
822 | | |
823 | | #define nxt_http_is_normal(c) \ |
824 | 0 | (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0)) |
825 | | |
826 | | |
827 | | static const uint8_t nxt_http_normal[32] nxt_aligned(32) = { |
828 | | |
829 | | /* \0 \r \n */ |
830 | | 0xFE, 0xDB, 0xFF, 0xFF, /* 1111 1110 1101 1011 1111 1111 1111 1111 */ |
831 | | |
832 | | /* '&%$ #"! /.-, |*)( 7654 3210 ?>=< ;:98 */ |
833 | | 0xD6, 0x37, 0xFF, 0x7F, /* 1101 0110 0011 0111 1111 1111 0111 1111 */ |
834 | | |
835 | | /* GFED CBA@ ONML KJIH WVUT SRQP _^]\ [ZYX */ |
836 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
837 | | |
838 | | /* gfed cba` onml kjih wvut srqp ~}| {zyx */ |
839 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
840 | | |
841 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
842 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
843 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
844 | | 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ |
845 | | }; |
846 | | |
847 | | |
848 | | nxt_int_t |
849 | | nxt_http_parse_complex_target(nxt_http_request_parse_t *rp) |
850 | 0 | { |
851 | 0 | u_char *p, *u, c, ch, high, *args; |
852 | |
|
853 | 0 | enum { |
854 | 0 | sw_normal = 0, |
855 | 0 | sw_slash, |
856 | 0 | sw_dot, |
857 | 0 | sw_dot_dot, |
858 | 0 | sw_quoted, |
859 | 0 | sw_quoted_second, |
860 | 0 | } state, saved_state; |
861 | |
|
862 | 0 | nxt_prefetch(nxt_http_normal); |
863 | |
|
864 | 0 | state = sw_normal; |
865 | 0 | saved_state = sw_normal; |
866 | 0 | p = rp->target_start; |
867 | |
|
868 | 0 | u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1); |
869 | 0 | if (nxt_slow_path(u == NULL)) { |
870 | 0 | return NXT_ERROR; |
871 | 0 | } |
872 | | |
873 | 0 | rp->path.length = 0; |
874 | 0 | rp->path.start = u; |
875 | |
|
876 | 0 | high = '\0'; |
877 | 0 | args = NULL; |
878 | |
|
879 | 0 | while (p < rp->target_end) { |
880 | |
|
881 | 0 | ch = *p++; |
882 | |
|
883 | 0 | again: |
884 | |
|
885 | 0 | switch (state) { |
886 | | |
887 | 0 | case sw_normal: |
888 | |
|
889 | 0 | if (nxt_http_is_normal(ch)) { |
890 | 0 | *u++ = ch; |
891 | 0 | continue; |
892 | 0 | } |
893 | | |
894 | 0 | switch (ch) { |
895 | 0 | case '/': |
896 | 0 | state = sw_slash; |
897 | 0 | *u++ = ch; |
898 | 0 | continue; |
899 | 0 | case '%': |
900 | 0 | saved_state = state; |
901 | 0 | state = sw_quoted; |
902 | 0 | continue; |
903 | 0 | case '?': |
904 | 0 | args = p; |
905 | 0 | goto args; |
906 | 0 | case '#': |
907 | 0 | goto done; |
908 | 0 | default: |
909 | 0 | *u++ = ch; |
910 | 0 | continue; |
911 | 0 | } |
912 | | |
913 | 0 | break; |
914 | | |
915 | 0 | case sw_slash: |
916 | |
|
917 | 0 | if (nxt_http_is_normal(ch)) { |
918 | 0 | state = sw_normal; |
919 | 0 | *u++ = ch; |
920 | 0 | continue; |
921 | 0 | } |
922 | | |
923 | 0 | switch (ch) { |
924 | 0 | case '/': |
925 | 0 | continue; |
926 | 0 | case '.': |
927 | 0 | state = sw_dot; |
928 | 0 | *u++ = ch; |
929 | 0 | continue; |
930 | 0 | case '%': |
931 | 0 | saved_state = state; |
932 | 0 | state = sw_quoted; |
933 | 0 | continue; |
934 | 0 | case '?': |
935 | 0 | args = p; |
936 | 0 | goto args; |
937 | 0 | case '#': |
938 | 0 | goto done; |
939 | 0 | default: |
940 | 0 | state = sw_normal; |
941 | 0 | *u++ = ch; |
942 | 0 | continue; |
943 | 0 | } |
944 | | |
945 | 0 | break; |
946 | | |
947 | 0 | case sw_dot: |
948 | |
|
949 | 0 | if (nxt_http_is_normal(ch)) { |
950 | 0 | state = sw_normal; |
951 | 0 | *u++ = ch; |
952 | 0 | continue; |
953 | 0 | } |
954 | | |
955 | 0 | switch (ch) { |
956 | 0 | case '/': |
957 | 0 | state = sw_slash; |
958 | 0 | u--; |
959 | 0 | continue; |
960 | 0 | case '.': |
961 | 0 | state = sw_dot_dot; |
962 | 0 | *u++ = ch; |
963 | 0 | continue; |
964 | 0 | case '%': |
965 | 0 | saved_state = state; |
966 | 0 | state = sw_quoted; |
967 | 0 | continue; |
968 | 0 | case '?': |
969 | 0 | u--; |
970 | 0 | args = p; |
971 | 0 | goto args; |
972 | 0 | case '#': |
973 | 0 | u--; |
974 | 0 | goto done; |
975 | 0 | default: |
976 | 0 | state = sw_normal; |
977 | 0 | *u++ = ch; |
978 | 0 | continue; |
979 | 0 | } |
980 | | |
981 | 0 | break; |
982 | | |
983 | 0 | case sw_dot_dot: |
984 | |
|
985 | 0 | if (nxt_http_is_normal(ch)) { |
986 | 0 | state = sw_normal; |
987 | 0 | *u++ = ch; |
988 | 0 | continue; |
989 | 0 | } |
990 | | |
991 | 0 | switch (ch) { |
992 | | |
993 | 0 | case '/': |
994 | 0 | case '?': |
995 | 0 | case '#': |
996 | 0 | u -= 5; |
997 | |
|
998 | 0 | for ( ;; ) { |
999 | 0 | if (u < rp->path.start) { |
1000 | 0 | return NXT_HTTP_PARSE_INVALID; |
1001 | 0 | } |
1002 | | |
1003 | 0 | if (*u == '/') { |
1004 | 0 | u++; |
1005 | 0 | break; |
1006 | 0 | } |
1007 | | |
1008 | 0 | u--; |
1009 | 0 | } |
1010 | | |
1011 | 0 | if (ch == '?') { |
1012 | 0 | args = p; |
1013 | 0 | goto args; |
1014 | 0 | } |
1015 | | |
1016 | 0 | if (ch == '#') { |
1017 | 0 | goto done; |
1018 | 0 | } |
1019 | | |
1020 | 0 | state = sw_slash; |
1021 | 0 | break; |
1022 | | |
1023 | 0 | case '%': |
1024 | 0 | saved_state = state; |
1025 | 0 | state = sw_quoted; |
1026 | 0 | continue; |
1027 | | |
1028 | 0 | default: |
1029 | 0 | state = sw_normal; |
1030 | 0 | *u++ = ch; |
1031 | 0 | continue; |
1032 | 0 | } |
1033 | | |
1034 | 0 | break; |
1035 | | |
1036 | 0 | case sw_quoted: |
1037 | 0 | rp->quoted_target = 1; |
1038 | |
|
1039 | 0 | if (ch >= '0' && ch <= '9') { |
1040 | 0 | high = (u_char) (ch - '0'); |
1041 | 0 | state = sw_quoted_second; |
1042 | 0 | continue; |
1043 | 0 | } |
1044 | | |
1045 | 0 | c = (u_char) (ch | 0x20); |
1046 | 0 | if (c >= 'a' && c <= 'f') { |
1047 | 0 | high = (u_char) (c - 'a' + 10); |
1048 | 0 | state = sw_quoted_second; |
1049 | 0 | continue; |
1050 | 0 | } |
1051 | | |
1052 | 0 | return NXT_HTTP_PARSE_INVALID; |
1053 | | |
1054 | 0 | case sw_quoted_second: |
1055 | 0 | if (ch >= '0' && ch <= '9') { |
1056 | 0 | ch = (u_char) ((high << 4) + ch - '0'); |
1057 | |
|
1058 | 0 | if (ch == '%') { |
1059 | 0 | state = sw_normal; |
1060 | 0 | *u++ = '%'; |
1061 | |
|
1062 | 0 | if (rp->encoded_slashes) { |
1063 | 0 | *u++ = '2'; |
1064 | 0 | *u++ = '5'; |
1065 | 0 | } |
1066 | |
|
1067 | 0 | continue; |
1068 | 0 | } |
1069 | | |
1070 | 0 | if (ch == '#') { |
1071 | 0 | state = sw_normal; |
1072 | 0 | *u++ = '#'; |
1073 | 0 | continue; |
1074 | 0 | } |
1075 | | |
1076 | 0 | if (ch == '\0') { |
1077 | 0 | return NXT_HTTP_PARSE_INVALID; |
1078 | 0 | } |
1079 | | |
1080 | 0 | state = saved_state; |
1081 | 0 | goto again; |
1082 | 0 | } |
1083 | | |
1084 | 0 | c = (u_char) (ch | 0x20); |
1085 | 0 | if (c >= 'a' && c <= 'f') { |
1086 | 0 | ch = (u_char) ((high << 4) + c - 'a' + 10); |
1087 | |
|
1088 | 0 | if (ch == '?') { |
1089 | 0 | state = sw_normal; |
1090 | 0 | *u++ = ch; |
1091 | 0 | continue; |
1092 | 0 | } |
1093 | | |
1094 | 0 | if (ch == '/' && rp->encoded_slashes) { |
1095 | 0 | state = sw_normal; |
1096 | 0 | *u++ = '%'; |
1097 | 0 | *u++ = '2'; |
1098 | 0 | *u++ = p[-1]; /* 'f' or 'F' */ |
1099 | 0 | continue; |
1100 | 0 | } |
1101 | | |
1102 | 0 | state = saved_state; |
1103 | 0 | goto again; |
1104 | 0 | } |
1105 | | |
1106 | 0 | return NXT_HTTP_PARSE_INVALID; |
1107 | 0 | } |
1108 | 0 | } |
1109 | | |
1110 | 0 | if (state >= sw_dot) { |
1111 | 0 | if (state >= sw_quoted) { |
1112 | 0 | return NXT_HTTP_PARSE_INVALID; |
1113 | 0 | } |
1114 | | |
1115 | | /* "/." and "/.." must be normalized similar to "/./" and "/../". */ |
1116 | 0 | ch = '/'; |
1117 | 0 | goto again; |
1118 | 0 | } |
1119 | | |
1120 | 0 | args: |
1121 | |
|
1122 | 0 | for (/* void */; p < rp->target_end; p++) { |
1123 | 0 | if (*p == '#') { |
1124 | 0 | break; |
1125 | 0 | } |
1126 | 0 | } |
1127 | |
|
1128 | 0 | if (args != NULL) { |
1129 | 0 | rp->args.length = p - args; |
1130 | 0 | rp->args.start = args; |
1131 | 0 | } |
1132 | |
|
1133 | 0 | done: |
1134 | |
|
1135 | 0 | rp->path.length = u - rp->path.start; |
1136 | |
|
1137 | 0 | return NXT_OK; |
1138 | 0 | } |
1139 | | |
1140 | | |
1141 | | const nxt_lvlhsh_proto_t nxt_http_fields_hash_proto nxt_aligned(64) = { |
1142 | | NXT_LVLHSH_BUCKET_SIZE(64), |
1143 | | { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 }, |
1144 | | nxt_http_field_hash_test, |
1145 | | nxt_lvlhsh_alloc, |
1146 | | nxt_lvlhsh_free, |
1147 | | }; |
1148 | | |
1149 | | |
1150 | | static nxt_int_t |
1151 | | nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data) |
1152 | 0 | { |
1153 | 0 | nxt_http_field_proc_t *field; |
1154 | |
|
1155 | 0 | field = data; |
1156 | |
|
1157 | 0 | if (nxt_strcasestr_eq(&lhq->key, &field->name)) { |
1158 | 0 | return NXT_OK; |
1159 | 0 | } |
1160 | | |
1161 | 0 | return NXT_DECLINED; |
1162 | 0 | } |
1163 | | |
1164 | | |
1165 | | static nxt_int_t |
1166 | | nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data) |
1167 | 0 | { |
1168 | 0 | return NXT_OK; |
1169 | 0 | } |
1170 | | |
1171 | | |
1172 | | nxt_int_t |
1173 | | nxt_http_fields_hash(nxt_lvlhsh_t *hash, |
1174 | | nxt_http_field_proc_t items[], nxt_uint_t count) |
1175 | 0 | { |
1176 | 0 | u_char ch; |
1177 | 0 | uint32_t key; |
1178 | 0 | nxt_str_t *name; |
1179 | 0 | nxt_int_t ret; |
1180 | 0 | nxt_uint_t i, j; |
1181 | 0 | nxt_lvlhsh_query_t lhq; |
1182 | |
|
1183 | 0 | lhq.replace = 0; |
1184 | 0 | lhq.proto = &nxt_http_fields_hash_proto; |
1185 | 0 | lhq.pool = NULL; |
1186 | |
|
1187 | 0 | for (i = 0; i < count; i++) { |
1188 | 0 | key = NXT_HTTP_FIELD_HASH_INIT; |
1189 | 0 | name = &items[i].name; |
1190 | |
|
1191 | 0 | for (j = 0; j < name->length; j++) { |
1192 | 0 | ch = nxt_lowcase(name->start[j]); |
1193 | 0 | key = nxt_http_field_hash_char(key, ch); |
1194 | 0 | } |
1195 | |
|
1196 | 0 | lhq.key_hash = nxt_http_field_hash_end(key) & 0xFFFF; |
1197 | 0 | lhq.key = *name; |
1198 | 0 | lhq.value = &items[i]; |
1199 | |
|
1200 | 0 | ret = nxt_lvlhsh_insert(hash, &lhq); |
1201 | |
|
1202 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
1203 | 0 | return NXT_ERROR; |
1204 | 0 | } |
1205 | 0 | } |
1206 | | |
1207 | 0 | return NXT_OK; |
1208 | 0 | } |
1209 | | |
1210 | | |
1211 | | nxt_uint_t |
1212 | | nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, |
1213 | | nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level) |
1214 | 0 | { |
1215 | 0 | u_char ch; |
1216 | 0 | uint32_t key, mask; |
1217 | 0 | nxt_str_t *name; |
1218 | 0 | nxt_uint_t colls, i, j; |
1219 | 0 | nxt_lvlhsh_proto_t proto; |
1220 | 0 | nxt_lvlhsh_query_t lhq; |
1221 | |
|
1222 | 0 | proto = nxt_http_fields_hash_proto; |
1223 | 0 | proto.test = nxt_http_field_hash_collision; |
1224 | |
|
1225 | 0 | lhq.replace = 0; |
1226 | 0 | lhq.proto = &proto; |
1227 | |
|
1228 | 0 | mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xFFFF; |
1229 | |
|
1230 | 0 | colls = 0; |
1231 | |
|
1232 | 0 | for (i = 0; i < count; i++) { |
1233 | 0 | key = NXT_HTTP_FIELD_HASH_INIT; |
1234 | 0 | name = &items[i].name; |
1235 | |
|
1236 | 0 | for (j = 0; j < name->length; j++) { |
1237 | 0 | ch = nxt_lowcase(name->start[j]); |
1238 | 0 | key = nxt_http_field_hash_char(key, ch); |
1239 | 0 | } |
1240 | |
|
1241 | 0 | lhq.key_hash = nxt_http_field_hash_end(key) & mask; |
1242 | 0 | lhq.value = &items[i]; |
1243 | |
|
1244 | 0 | if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) { |
1245 | 0 | colls++; |
1246 | 0 | } |
1247 | 0 | } |
1248 | |
|
1249 | 0 | return colls; |
1250 | 0 | } |
1251 | | |
1252 | | |
1253 | | nxt_int_t |
1254 | | nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx) |
1255 | 0 | { |
1256 | 0 | nxt_int_t ret; |
1257 | 0 | nxt_http_field_t *field; |
1258 | |
|
1259 | 0 | nxt_list_each(field, fields) { |
1260 | |
|
1261 | 0 | ret = nxt_http_field_process(field, hash, ctx); |
1262 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
1263 | 0 | return ret; |
1264 | 0 | } |
1265 | |
|
1266 | 0 | } nxt_list_loop; |
1267 | | |
1268 | 0 | return NXT_OK; |
1269 | 0 | } |