xref: /unit/src/nxt_http_parse.c (revision 16)
1*16Svbart@nginx.com 
2*16Svbart@nginx.com /*
3*16Svbart@nginx.com  * Copyright (C) NGINX, Inc.
4*16Svbart@nginx.com  * Copyright (C) Valentin V. Bartenev
5*16Svbart@nginx.com  */
6*16Svbart@nginx.com 
7*16Svbart@nginx.com #include <nxt_main.h>
8*16Svbart@nginx.com 
9*16Svbart@nginx.com #ifdef __SSE4_2__
10*16Svbart@nginx.com #include <x86intrin.h>
11*16Svbart@nginx.com #endif
12*16Svbart@nginx.com 
13*16Svbart@nginx.com 
14*16Svbart@nginx.com typedef struct {
15*16Svbart@nginx.com     nxt_http_field_handler_t  handler;
16*16Svbart@nginx.com     uintptr_t                 data;
17*16Svbart@nginx.com     union {
18*16Svbart@nginx.com         uint8_t               str[8];
19*16Svbart@nginx.com         uint64_t              ui64;
20*16Svbart@nginx.com     } key[];
21*16Svbart@nginx.com } nxt_http_fields_hash_entry_t;
22*16Svbart@nginx.com 
23*16Svbart@nginx.com 
24*16Svbart@nginx.com #define nxt_http_fields_hash_next_entry(entry, n)                             \
25*16Svbart@nginx.com     ((nxt_http_fields_hash_entry_t *) ((u_char *) (entry)                     \
26*16Svbart@nginx.com                                        + sizeof(nxt_http_fields_hash_entry_t) \
27*16Svbart@nginx.com                                        + n * 8))
28*16Svbart@nginx.com 
29*16Svbart@nginx.com 
30*16Svbart@nginx.com struct nxt_http_fields_hash_s {
31*16Svbart@nginx.com     size_t                        min_length;
32*16Svbart@nginx.com     size_t                        max_length;
33*16Svbart@nginx.com     void                          *long_fields;
34*16Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entries[];
35*16Svbart@nginx.com };
36*16Svbart@nginx.com 
37*16Svbart@nginx.com 
38*16Svbart@nginx.com static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp,
39*16Svbart@nginx.com     u_char **pos, u_char *end);
40*16Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp,
41*16Svbart@nginx.com     u_char **pos, u_char *end);
42*16Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp,
43*16Svbart@nginx.com     u_char **pos, u_char *end);
44*16Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp,
45*16Svbart@nginx.com     u_char **pos, u_char *end);
46*16Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end);
47*16Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
48*16Svbart@nginx.com     u_char **pos, u_char *end);
49*16Svbart@nginx.com 
50*16Svbart@nginx.com static nxt_http_fields_hash_entry_t *nxt_http_fields_hash_lookup(
51*16Svbart@nginx.com     nxt_http_fields_hash_t *hash, uint64_t *key, nxt_str_t *value);
52*16Svbart@nginx.com static nxt_http_fields_hash_entry_t *nxt_http_header_fields_hash_lookup_long(
53*16Svbart@nginx.com     nxt_http_fields_hash_t *hash, nxt_str_t *value);
54*16Svbart@nginx.com 
55*16Svbart@nginx.com 
56*16Svbart@nginx.com typedef enum {
57*16Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
58*16Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
59*16Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
60*16Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
61*16Svbart@nginx.com 
62*16Svbart@nginx.com     /* traps below are used for extended check only */
63*16Svbart@nginx.com 
64*16Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
65*16Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
66*16Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
67*16Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
68*16Svbart@nginx.com     NXT_HTTP_TARGET_PLUS,        /*  +  */
69*16Svbart@nginx.com } nxt_http_target_traps_e;
70*16Svbart@nginx.com 
71*16Svbart@nginx.com 
72*16Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
73*16Svbart@nginx.com     /* \0                               \n        \r       */
74*16Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
75*16Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
76*16Svbart@nginx.com     /*
77*16Svbart@nginx.com      * \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  /
78*16Svbart@nginx.com      *  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ?
79*16Svbart@nginx.com      */
80*16Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 9,  0, 0, 6, 5,
81*16Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
82*16Svbart@nginx.com };
83*16Svbart@nginx.com 
84*16Svbart@nginx.com 
85*16Svbart@nginx.com nxt_inline nxt_http_target_traps_e
86*16Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
87*16Svbart@nginx.com {
88*16Svbart@nginx.com     u_char      *p;
89*16Svbart@nginx.com     nxt_uint_t  trap;
90*16Svbart@nginx.com 
91*16Svbart@nginx.com     p = *pos;
92*16Svbart@nginx.com 
93*16Svbart@nginx.com     for ( ;; ) {
94*16Svbart@nginx.com         if (nxt_slow_path(end - p < 10)) {
95*16Svbart@nginx.com             return NXT_HTTP_TARGET_AGAIN;
96*16Svbart@nginx.com         }
97*16Svbart@nginx.com 
98*16Svbart@nginx.com #define nxt_http_parse_target_step                                            \
99*16Svbart@nginx.com         {                                                                     \
100*16Svbart@nginx.com             trap = nxt_http_target_chars[*p];                                 \
101*16Svbart@nginx.com                                                                               \
102*16Svbart@nginx.com             if (nxt_slow_path(trap != 0)) {                                   \
103*16Svbart@nginx.com                 break;                                                        \
104*16Svbart@nginx.com             }                                                                 \
105*16Svbart@nginx.com                                                                               \
106*16Svbart@nginx.com             p++;                                                              \
107*16Svbart@nginx.com         }
108*16Svbart@nginx.com 
109*16Svbart@nginx.com         nxt_http_parse_target_step
110*16Svbart@nginx.com         nxt_http_parse_target_step
111*16Svbart@nginx.com         nxt_http_parse_target_step
112*16Svbart@nginx.com         nxt_http_parse_target_step
113*16Svbart@nginx.com 
114*16Svbart@nginx.com         nxt_http_parse_target_step
115*16Svbart@nginx.com         nxt_http_parse_target_step
116*16Svbart@nginx.com         nxt_http_parse_target_step
117*16Svbart@nginx.com         nxt_http_parse_target_step
118*16Svbart@nginx.com 
119*16Svbart@nginx.com         nxt_http_parse_target_step
120*16Svbart@nginx.com         nxt_http_parse_target_step
121*16Svbart@nginx.com 
122*16Svbart@nginx.com #undef nxt_http_parse_target_step
123*16Svbart@nginx.com     }
124*16Svbart@nginx.com 
125*16Svbart@nginx.com     *pos = p;
126*16Svbart@nginx.com 
127*16Svbart@nginx.com     return trap;
128*16Svbart@nginx.com }
129*16Svbart@nginx.com 
130*16Svbart@nginx.com 
131*16Svbart@nginx.com #ifdef __SSE4_2__
132*16Svbart@nginx.com 
133*16Svbart@nginx.com nxt_inline nxt_http_target_traps_e
134*16Svbart@nginx.com nxt_http_parse_target_rest(u_char **pos, u_char *end)
135*16Svbart@nginx.com {
136*16Svbart@nginx.com     int         n;
137*16Svbart@nginx.com     u_char      *p;
138*16Svbart@nginx.com     nxt_uint_t  i;
139*16Svbart@nginx.com 
140*16Svbart@nginx.com     static const u_char  stop_chars[16] nxt_aligned(16) = " #\r\n";
141*16Svbart@nginx.com 
142*16Svbart@nginx.com     __m128i pattern = _mm_load_si128((__m128i *) stop_chars);
143*16Svbart@nginx.com 
144*16Svbart@nginx.com     p = *pos;
145*16Svbart@nginx.com 
146*16Svbart@nginx.com     for (n = (end - p) / 16; nxt_fast_path(n != 0); n--) {
147*16Svbart@nginx.com 
148*16Svbart@nginx.com         __m128i test = _mm_loadu_si128((__m128i *) p);
149*16Svbart@nginx.com 
150*16Svbart@nginx.com         i = _mm_cmpistri(pattern, test, _SIDD_LEAST_SIGNIFICANT
151*16Svbart@nginx.com                                         | _SIDD_CMP_EQUAL_ANY
152*16Svbart@nginx.com                                         | _SIDD_UBYTE_OPS);
153*16Svbart@nginx.com 
154*16Svbart@nginx.com         p += i;
155*16Svbart@nginx.com 
156*16Svbart@nginx.com         if (i != 16) {
157*16Svbart@nginx.com             *pos = p;
158*16Svbart@nginx.com             return nxt_http_target_chars[*p];
159*16Svbart@nginx.com         }
160*16Svbart@nginx.com     }
161*16Svbart@nginx.com 
162*16Svbart@nginx.com     *pos = p;
163*16Svbart@nginx.com 
164*16Svbart@nginx.com     return nxt_http_parse_target(pos, end);
165*16Svbart@nginx.com }
166*16Svbart@nginx.com 
167*16Svbart@nginx.com #else
168*16Svbart@nginx.com #define nxt_http_parse_target_rest  nxt_http_parse_target
169*16Svbart@nginx.com #endif
170*16Svbart@nginx.com 
171*16Svbart@nginx.com 
172*16Svbart@nginx.com nxt_int_t
173*16Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
174*16Svbart@nginx.com {
175*16Svbart@nginx.com     nxt_int_t  rc;
176*16Svbart@nginx.com 
177*16Svbart@nginx.com     if (rp->handler == NULL) {
178*16Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
179*16Svbart@nginx.com     }
180*16Svbart@nginx.com 
181*16Svbart@nginx.com     do {
182*16Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
183*16Svbart@nginx.com     } while (rc == NXT_OK);
184*16Svbart@nginx.com 
185*16Svbart@nginx.com     return rc;
186*16Svbart@nginx.com }
187*16Svbart@nginx.com 
188*16Svbart@nginx.com 
189*16Svbart@nginx.com static nxt_int_t
190*16Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
191*16Svbart@nginx.com     u_char *end)
192*16Svbart@nginx.com {
193*16Svbart@nginx.com     u_char                   *p, ch, *after_slash;
194*16Svbart@nginx.com     nxt_int_t                rc;
195*16Svbart@nginx.com     nxt_http_ver_t           version;
196*16Svbart@nginx.com     nxt_http_target_traps_e  trap;
197*16Svbart@nginx.com 
198*16Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
199*16Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
200*16Svbart@nginx.com 
201*16Svbart@nginx.com     p = *pos;
202*16Svbart@nginx.com 
203*16Svbart@nginx.com     rp->method.start = p;
204*16Svbart@nginx.com 
205*16Svbart@nginx.com     for ( ;; p++) {
206*16Svbart@nginx.com 
207*16Svbart@nginx.com         for ( ;; ) {
208*16Svbart@nginx.com             if (nxt_slow_path(end - p < 12)) {
209*16Svbart@nginx.com                 return NXT_AGAIN;
210*16Svbart@nginx.com             }
211*16Svbart@nginx.com 
212*16Svbart@nginx.com #define nxt_http_parse_request_line_step                                      \
213*16Svbart@nginx.com             {                                                                 \
214*16Svbart@nginx.com                 ch = *p;                                                      \
215*16Svbart@nginx.com                                                                               \
216*16Svbart@nginx.com                 if (nxt_slow_path(ch < 'A' || ch > 'Z')) {                    \
217*16Svbart@nginx.com                     break;                                                    \
218*16Svbart@nginx.com                 }                                                             \
219*16Svbart@nginx.com                                                                               \
220*16Svbart@nginx.com                 p++;                                                          \
221*16Svbart@nginx.com             }
222*16Svbart@nginx.com 
223*16Svbart@nginx.com             nxt_http_parse_request_line_step
224*16Svbart@nginx.com             nxt_http_parse_request_line_step
225*16Svbart@nginx.com             nxt_http_parse_request_line_step
226*16Svbart@nginx.com             nxt_http_parse_request_line_step
227*16Svbart@nginx.com 
228*16Svbart@nginx.com             nxt_http_parse_request_line_step
229*16Svbart@nginx.com             nxt_http_parse_request_line_step
230*16Svbart@nginx.com             nxt_http_parse_request_line_step
231*16Svbart@nginx.com             nxt_http_parse_request_line_step
232*16Svbart@nginx.com 
233*16Svbart@nginx.com #undef nxt_http_parse_request_line_step
234*16Svbart@nginx.com         }
235*16Svbart@nginx.com 
236*16Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
237*16Svbart@nginx.com             rp->method.length = p - rp->method.start;
238*16Svbart@nginx.com             break;
239*16Svbart@nginx.com         }
240*16Svbart@nginx.com 
241*16Svbart@nginx.com         if (ch == '_' || ch == '-') {
242*16Svbart@nginx.com             continue;
243*16Svbart@nginx.com         }
244*16Svbart@nginx.com 
245*16Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
246*16Svbart@nginx.com             rp->method.start++;
247*16Svbart@nginx.com             continue;
248*16Svbart@nginx.com         }
249*16Svbart@nginx.com 
250*16Svbart@nginx.com         return NXT_ERROR;
251*16Svbart@nginx.com     }
252*16Svbart@nginx.com 
253*16Svbart@nginx.com     p++;
254*16Svbart@nginx.com 
255*16Svbart@nginx.com     if (nxt_slow_path(p == end)) {
256*16Svbart@nginx.com         return NXT_AGAIN;
257*16Svbart@nginx.com     }
258*16Svbart@nginx.com 
259*16Svbart@nginx.com     /* target */
260*16Svbart@nginx.com 
261*16Svbart@nginx.com     nxt_prefetch(&nxt_http_target_chars[' ']);
262*16Svbart@nginx.com     nxt_prefetch(&nxt_http_target_chars['@']);
263*16Svbart@nginx.com     nxt_prefetch(&nxt_http_target_chars['`']);
264*16Svbart@nginx.com 
265*16Svbart@nginx.com     ch = *p;
266*16Svbart@nginx.com 
267*16Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
268*16Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
269*16Svbart@nginx.com 
270*16Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
271*16Svbart@nginx.com             return rc;
272*16Svbart@nginx.com         }
273*16Svbart@nginx.com     }
274*16Svbart@nginx.com 
275*16Svbart@nginx.com     rp->target_start = p;
276*16Svbart@nginx.com 
277*16Svbart@nginx.com     after_slash = p + 1;
278*16Svbart@nginx.com 
279*16Svbart@nginx.com     for ( ;; ) {
280*16Svbart@nginx.com         p++;
281*16Svbart@nginx.com 
282*16Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
283*16Svbart@nginx.com 
284*16Svbart@nginx.com         switch (trap) {
285*16Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
286*16Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
287*16Svbart@nginx.com                 rp->complex_target = 1;
288*16Svbart@nginx.com                 goto rest_of_target;
289*16Svbart@nginx.com             }
290*16Svbart@nginx.com 
291*16Svbart@nginx.com             after_slash = p + 1;
292*16Svbart@nginx.com 
293*16Svbart@nginx.com             rp->exten_start = NULL;
294*16Svbart@nginx.com             continue;
295*16Svbart@nginx.com 
296*16Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
297*16Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
298*16Svbart@nginx.com                 rp->complex_target = 1;
299*16Svbart@nginx.com                 goto rest_of_target;
300*16Svbart@nginx.com             }
301*16Svbart@nginx.com 
302*16Svbart@nginx.com             rp->exten_start = p + 1;
303*16Svbart@nginx.com             continue;
304*16Svbart@nginx.com 
305*16Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
306*16Svbart@nginx.com             rp->args_start = p + 1;
307*16Svbart@nginx.com             goto rest_of_target;
308*16Svbart@nginx.com 
309*16Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
310*16Svbart@nginx.com             rp->target_end = p;
311*16Svbart@nginx.com             goto space_after_target;
312*16Svbart@nginx.com 
313*16Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
314*16Svbart@nginx.com             rp->quoted_target = 1;
315*16Svbart@nginx.com             goto rest_of_target;
316*16Svbart@nginx.com 
317*16Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
318*16Svbart@nginx.com             rp->plus_in_target = 1;
319*16Svbart@nginx.com             continue;
320*16Svbart@nginx.com 
321*16Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
322*16Svbart@nginx.com             rp->complex_target = 1;
323*16Svbart@nginx.com             goto rest_of_target;
324*16Svbart@nginx.com 
325*16Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
326*16Svbart@nginx.com             return NXT_AGAIN;
327*16Svbart@nginx.com 
328*16Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
329*16Svbart@nginx.com             return NXT_ERROR;
330*16Svbart@nginx.com         }
331*16Svbart@nginx.com 
332*16Svbart@nginx.com         nxt_unreachable();
333*16Svbart@nginx.com     }
334*16Svbart@nginx.com 
335*16Svbart@nginx.com rest_of_target:
336*16Svbart@nginx.com 
337*16Svbart@nginx.com     for ( ;; ) {
338*16Svbart@nginx.com         p++;
339*16Svbart@nginx.com 
340*16Svbart@nginx.com         trap = nxt_http_parse_target_rest(&p, end);
341*16Svbart@nginx.com 
342*16Svbart@nginx.com         switch (trap) {
343*16Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
344*16Svbart@nginx.com             rp->target_end = p;
345*16Svbart@nginx.com             goto space_after_target;
346*16Svbart@nginx.com 
347*16Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
348*16Svbart@nginx.com             rp->complex_target = 1;
349*16Svbart@nginx.com             continue;
350*16Svbart@nginx.com 
351*16Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
352*16Svbart@nginx.com             return NXT_AGAIN;
353*16Svbart@nginx.com 
354*16Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
355*16Svbart@nginx.com             return NXT_ERROR;
356*16Svbart@nginx.com 
357*16Svbart@nginx.com         default:
358*16Svbart@nginx.com             continue;
359*16Svbart@nginx.com         }
360*16Svbart@nginx.com 
361*16Svbart@nginx.com         nxt_unreachable();
362*16Svbart@nginx.com     }
363*16Svbart@nginx.com 
364*16Svbart@nginx.com space_after_target:
365*16Svbart@nginx.com 
366*16Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
367*16Svbart@nginx.com         return NXT_AGAIN;
368*16Svbart@nginx.com     }
369*16Svbart@nginx.com 
370*16Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
371*16Svbart@nginx.com 
372*16Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
373*16Svbart@nginx.com 
374*16Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
375*16Svbart@nginx.com                        || version.ui64 == http10.ui64
376*16Svbart@nginx.com                        || (p[1] == 'H'
377*16Svbart@nginx.com                            && p[2] == 'T'
378*16Svbart@nginx.com                            && p[3] == 'T'
379*16Svbart@nginx.com                            && p[4] == 'P'
380*16Svbart@nginx.com                            && p[5] == '/'
381*16Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
382*16Svbart@nginx.com                            && p[7] == '.'
383*16Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
384*16Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
385*16Svbart@nginx.com     {
386*16Svbart@nginx.com         rp->version.ui64 = version.ui64;
387*16Svbart@nginx.com 
388*16Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
389*16Svbart@nginx.com             p += 10;
390*16Svbart@nginx.com 
391*16Svbart@nginx.com             if (nxt_slow_path(p == end)) {
392*16Svbart@nginx.com                 return NXT_AGAIN;
393*16Svbart@nginx.com             }
394*16Svbart@nginx.com 
395*16Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
396*16Svbart@nginx.com                 return NXT_ERROR;
397*16Svbart@nginx.com             }
398*16Svbart@nginx.com 
399*16Svbart@nginx.com             *pos = p + 1;
400*16Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
401*16Svbart@nginx.com         }
402*16Svbart@nginx.com 
403*16Svbart@nginx.com         *pos = p + 10;
404*16Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
405*16Svbart@nginx.com     }
406*16Svbart@nginx.com 
407*16Svbart@nginx.com     if (p[1] == ' ') {
408*16Svbart@nginx.com         /* surplus space after tartet */
409*16Svbart@nginx.com         p++;
410*16Svbart@nginx.com         goto space_after_target;
411*16Svbart@nginx.com     }
412*16Svbart@nginx.com 
413*16Svbart@nginx.com     rp->space_in_target = 1;
414*16Svbart@nginx.com     goto rest_of_target;
415*16Svbart@nginx.com }
416*16Svbart@nginx.com 
417*16Svbart@nginx.com 
418*16Svbart@nginx.com static nxt_int_t
419*16Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
420*16Svbart@nginx.com     u_char *end)
421*16Svbart@nginx.com {
422*16Svbart@nginx.com     u_char  *p, ch;
423*16Svbart@nginx.com 
424*16Svbart@nginx.com     p = *pos;
425*16Svbart@nginx.com 
426*16Svbart@nginx.com     ch = *p;
427*16Svbart@nginx.com 
428*16Svbart@nginx.com     if (ch == ' ') {
429*16Svbart@nginx.com         /* skip surplus spaces before target */
430*16Svbart@nginx.com 
431*16Svbart@nginx.com         do {
432*16Svbart@nginx.com             p++;
433*16Svbart@nginx.com 
434*16Svbart@nginx.com             if (nxt_slow_path(p == end)) {
435*16Svbart@nginx.com                 return NXT_AGAIN;
436*16Svbart@nginx.com             }
437*16Svbart@nginx.com 
438*16Svbart@nginx.com             ch = *p;
439*16Svbart@nginx.com 
440*16Svbart@nginx.com         } while (ch == ' ');
441*16Svbart@nginx.com 
442*16Svbart@nginx.com         if (ch == '/') {
443*16Svbart@nginx.com             *pos = p;
444*16Svbart@nginx.com             return NXT_OK;
445*16Svbart@nginx.com         }
446*16Svbart@nginx.com     }
447*16Svbart@nginx.com 
448*16Svbart@nginx.com     /* absolute path or '*' */
449*16Svbart@nginx.com 
450*16Svbart@nginx.com     /* TODO */
451*16Svbart@nginx.com 
452*16Svbart@nginx.com     return NXT_ERROR;
453*16Svbart@nginx.com }
454*16Svbart@nginx.com 
455*16Svbart@nginx.com 
456*16Svbart@nginx.com static nxt_int_t
457*16Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
458*16Svbart@nginx.com     u_char *end)
459*16Svbart@nginx.com {
460*16Svbart@nginx.com     u_char  *p, ch, c;
461*16Svbart@nginx.com     size_t  i, size;
462*16Svbart@nginx.com 
463*16Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
464*16Svbart@nginx.com         "\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"
465*16Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
466*16Svbart@nginx.com 
467*16Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
468*16Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
469*16Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
470*16Svbart@nginx.com 
471*16Svbart@nginx.com         "\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"
472*16Svbart@nginx.com         "\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"
473*16Svbart@nginx.com         "\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"
474*16Svbart@nginx.com         "\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";
475*16Svbart@nginx.com 
476*16Svbart@nginx.com     p = *pos;
477*16Svbart@nginx.com 
478*16Svbart@nginx.com     size = end - p;
479*16Svbart@nginx.com 
480*16Svbart@nginx.com     for (i = rp->offset; i != size; i++) {
481*16Svbart@nginx.com 
482*16Svbart@nginx.com         ch = p[i];
483*16Svbart@nginx.com 
484*16Svbart@nginx.com         c = normal[ch];
485*16Svbart@nginx.com 
486*16Svbart@nginx.com         if (nxt_fast_path(c != '\0')) {
487*16Svbart@nginx.com             rp->field_name_key.str[i % 32] = c;
488*16Svbart@nginx.com             continue;
489*16Svbart@nginx.com         }
490*16Svbart@nginx.com 
491*16Svbart@nginx.com         if (nxt_fast_path(ch == ':')) {
492*16Svbart@nginx.com             if (nxt_slow_path(i == 0)) {
493*16Svbart@nginx.com                 return NXT_ERROR;
494*16Svbart@nginx.com             }
495*16Svbart@nginx.com 
496*16Svbart@nginx.com             *pos = &p[i] + 1;
497*16Svbart@nginx.com 
498*16Svbart@nginx.com             rp->field_name.start = p;
499*16Svbart@nginx.com             rp->field_name.length = i;
500*16Svbart@nginx.com 
501*16Svbart@nginx.com             rp->offset = 0;
502*16Svbart@nginx.com 
503*16Svbart@nginx.com             return nxt_http_parse_field_value(rp, pos, end);
504*16Svbart@nginx.com         }
505*16Svbart@nginx.com 
506*16Svbart@nginx.com         *pos = &p[i];
507*16Svbart@nginx.com 
508*16Svbart@nginx.com         rp->field_name.length = 0;
509*16Svbart@nginx.com 
510*16Svbart@nginx.com         return nxt_http_parse_field_end(rp, pos, end);
511*16Svbart@nginx.com     }
512*16Svbart@nginx.com 
513*16Svbart@nginx.com     rp->offset = i;
514*16Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
515*16Svbart@nginx.com 
516*16Svbart@nginx.com     return NXT_AGAIN;
517*16Svbart@nginx.com }
518*16Svbart@nginx.com 
519*16Svbart@nginx.com 
520*16Svbart@nginx.com static nxt_int_t
521*16Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
522*16Svbart@nginx.com     u_char *end)
523*16Svbart@nginx.com {
524*16Svbart@nginx.com     u_char  *p, ch;
525*16Svbart@nginx.com 
526*16Svbart@nginx.com     p = *pos;
527*16Svbart@nginx.com 
528*16Svbart@nginx.com     for ( ;; ) {
529*16Svbart@nginx.com         if (nxt_slow_path(p == end)) {
530*16Svbart@nginx.com             *pos = p;
531*16Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
532*16Svbart@nginx.com             return NXT_AGAIN;
533*16Svbart@nginx.com         }
534*16Svbart@nginx.com 
535*16Svbart@nginx.com         if (*p != ' ') {
536*16Svbart@nginx.com             break;
537*16Svbart@nginx.com         }
538*16Svbart@nginx.com 
539*16Svbart@nginx.com         p++;
540*16Svbart@nginx.com     }
541*16Svbart@nginx.com 
542*16Svbart@nginx.com     *pos = p;
543*16Svbart@nginx.com 
544*16Svbart@nginx.com     p += rp->offset;
545*16Svbart@nginx.com 
546*16Svbart@nginx.com     for ( ;; ) {
547*16Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
548*16Svbart@nginx.com 
549*16Svbart@nginx.com         if (nxt_slow_path(p == end)) {
550*16Svbart@nginx.com             rp->offset = p - *pos;
551*16Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
552*16Svbart@nginx.com             return NXT_AGAIN;
553*16Svbart@nginx.com         }
554*16Svbart@nginx.com 
555*16Svbart@nginx.com         ch = *p;
556*16Svbart@nginx.com 
557*16Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
558*16Svbart@nginx.com             break;
559*16Svbart@nginx.com         }
560*16Svbart@nginx.com 
561*16Svbart@nginx.com         if (ch == '\0') {
562*16Svbart@nginx.com             return NXT_ERROR;
563*16Svbart@nginx.com         }
564*16Svbart@nginx.com     }
565*16Svbart@nginx.com 
566*16Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
567*16Svbart@nginx.com         while (p[-1] == ' ') {
568*16Svbart@nginx.com             p--;
569*16Svbart@nginx.com         }
570*16Svbart@nginx.com     }
571*16Svbart@nginx.com 
572*16Svbart@nginx.com     rp->offset = 0;
573*16Svbart@nginx.com 
574*16Svbart@nginx.com     rp->field_value.start = *pos;
575*16Svbart@nginx.com     rp->field_value.length = p - *pos;
576*16Svbart@nginx.com 
577*16Svbart@nginx.com     *pos = p;
578*16Svbart@nginx.com 
579*16Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
580*16Svbart@nginx.com }
581*16Svbart@nginx.com 
582*16Svbart@nginx.com 
583*16Svbart@nginx.com static u_char *
584*16Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
585*16Svbart@nginx.com {
586*16Svbart@nginx.com     nxt_uint_t  n;
587*16Svbart@nginx.com #ifdef __SSE4_2__
588*16Svbart@nginx.com     nxt_uint_t  i;
589*16Svbart@nginx.com 
590*16Svbart@nginx.com     static const u_char  end_chars[16] nxt_aligned(16) = "\r\n";
591*16Svbart@nginx.com 
592*16Svbart@nginx.com     __m128i pattern = _mm_load_si128((__m128i *) end_chars);
593*16Svbart@nginx.com 
594*16Svbart@nginx.com     for (n = (end - p) / 16; nxt_fast_path(n != 0); n--) {
595*16Svbart@nginx.com 
596*16Svbart@nginx.com         __m128i test = _mm_loadu_si128((__m128i *) p);
597*16Svbart@nginx.com 
598*16Svbart@nginx.com         i = _mm_cmpistri(pattern, test, _SIDD_LEAST_SIGNIFICANT
599*16Svbart@nginx.com                                         | _SIDD_CMP_EQUAL_ANY
600*16Svbart@nginx.com                                         | _SIDD_UBYTE_OPS);
601*16Svbart@nginx.com 
602*16Svbart@nginx.com         p += i;
603*16Svbart@nginx.com 
604*16Svbart@nginx.com         if (i != 16) {
605*16Svbart@nginx.com             return p;
606*16Svbart@nginx.com         }
607*16Svbart@nginx.com     }
608*16Svbart@nginx.com #endif
609*16Svbart@nginx.com 
610*16Svbart@nginx.com #define nxt_http_lookup_field_end_step                                        \
611*16Svbart@nginx.com     {                                                                         \
612*16Svbart@nginx.com         if (nxt_slow_path(*p <= '\r')) {                                      \
613*16Svbart@nginx.com             return p;                                                         \
614*16Svbart@nginx.com         }                                                                     \
615*16Svbart@nginx.com                                                                               \
616*16Svbart@nginx.com         p++;                                                                  \
617*16Svbart@nginx.com     }
618*16Svbart@nginx.com 
619*16Svbart@nginx.com     for (n = (end - p) / 8; nxt_fast_path(n != 0); n--) {
620*16Svbart@nginx.com         nxt_http_lookup_field_end_step
621*16Svbart@nginx.com         nxt_http_lookup_field_end_step
622*16Svbart@nginx.com         nxt_http_lookup_field_end_step
623*16Svbart@nginx.com         nxt_http_lookup_field_end_step
624*16Svbart@nginx.com 
625*16Svbart@nginx.com         nxt_http_lookup_field_end_step
626*16Svbart@nginx.com         nxt_http_lookup_field_end_step
627*16Svbart@nginx.com         nxt_http_lookup_field_end_step
628*16Svbart@nginx.com         nxt_http_lookup_field_end_step
629*16Svbart@nginx.com     }
630*16Svbart@nginx.com 
631*16Svbart@nginx.com     switch (end - p) {
632*16Svbart@nginx.com     case 7:
633*16Svbart@nginx.com         nxt_http_lookup_field_end_step
634*16Svbart@nginx.com     case 6:
635*16Svbart@nginx.com         nxt_http_lookup_field_end_step
636*16Svbart@nginx.com     case 5:
637*16Svbart@nginx.com         nxt_http_lookup_field_end_step
638*16Svbart@nginx.com     case 4:
639*16Svbart@nginx.com         nxt_http_lookup_field_end_step
640*16Svbart@nginx.com     case 3:
641*16Svbart@nginx.com         nxt_http_lookup_field_end_step
642*16Svbart@nginx.com     case 2:
643*16Svbart@nginx.com         nxt_http_lookup_field_end_step
644*16Svbart@nginx.com     case 1:
645*16Svbart@nginx.com         nxt_http_lookup_field_end_step
646*16Svbart@nginx.com     case 0:
647*16Svbart@nginx.com         break;
648*16Svbart@nginx.com     default:
649*16Svbart@nginx.com         nxt_unreachable();
650*16Svbart@nginx.com     }
651*16Svbart@nginx.com 
652*16Svbart@nginx.com #undef nxt_http_lookup_field_end_step
653*16Svbart@nginx.com 
654*16Svbart@nginx.com     return p;
655*16Svbart@nginx.com }
656*16Svbart@nginx.com 
657*16Svbart@nginx.com 
658*16Svbart@nginx.com static nxt_int_t
659*16Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
660*16Svbart@nginx.com     u_char *end)
661*16Svbart@nginx.com {
662*16Svbart@nginx.com     u_char                        *p;
663*16Svbart@nginx.com     nxt_int_t                     rc;
664*16Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entry;
665*16Svbart@nginx.com 
666*16Svbart@nginx.com     p = *pos;
667*16Svbart@nginx.com 
668*16Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
669*16Svbart@nginx.com         p++;
670*16Svbart@nginx.com 
671*16Svbart@nginx.com         if (nxt_slow_path(p == end)) {
672*16Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
673*16Svbart@nginx.com             return NXT_AGAIN;
674*16Svbart@nginx.com         }
675*16Svbart@nginx.com     }
676*16Svbart@nginx.com 
677*16Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
678*16Svbart@nginx.com         *pos = p + 1;
679*16Svbart@nginx.com 
680*16Svbart@nginx.com         if (rp->field_name.length != 0) {
681*16Svbart@nginx.com             entry = nxt_http_fields_hash_lookup(rp->hash,
682*16Svbart@nginx.com                                                   rp->field_name_key.ui64,
683*16Svbart@nginx.com                                                   &rp->field_name);
684*16Svbart@nginx.com 
685*16Svbart@nginx.com             if (entry != NULL) {
686*16Svbart@nginx.com                 rc = entry->handler(rp->ctx, &rp->field_name, &rp->field_value,
687*16Svbart@nginx.com                                     entry->data);
688*16Svbart@nginx.com 
689*16Svbart@nginx.com                 if (nxt_slow_path(rc != NXT_OK)) {
690*16Svbart@nginx.com                     return NXT_ERROR;
691*16Svbart@nginx.com                 }
692*16Svbart@nginx.com             }
693*16Svbart@nginx.com 
694*16Svbart@nginx.com             nxt_memzero(rp->field_name_key.str, 32);
695*16Svbart@nginx.com 
696*16Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
697*16Svbart@nginx.com             return NXT_OK;
698*16Svbart@nginx.com         }
699*16Svbart@nginx.com 
700*16Svbart@nginx.com         return NXT_DONE;
701*16Svbart@nginx.com     }
702*16Svbart@nginx.com 
703*16Svbart@nginx.com     return NXT_ERROR;
704*16Svbart@nginx.com }
705*16Svbart@nginx.com 
706*16Svbart@nginx.com 
707*16Svbart@nginx.com static nxt_http_fields_hash_entry_t *
708*16Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, uint64_t *key,
709*16Svbart@nginx.com     nxt_str_t *value)
710*16Svbart@nginx.com {
711*16Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entry;
712*16Svbart@nginx.com 
713*16Svbart@nginx.com     if (hash == NULL || value->length < hash->min_length) {
714*16Svbart@nginx.com         return NULL;
715*16Svbart@nginx.com     }
716*16Svbart@nginx.com 
717*16Svbart@nginx.com     if (value->length > hash->max_length) {
718*16Svbart@nginx.com         if (value->length > 32 && hash->long_fields != NULL) {
719*16Svbart@nginx.com             return nxt_http_header_fields_hash_lookup_long(hash, value);
720*16Svbart@nginx.com         }
721*16Svbart@nginx.com 
722*16Svbart@nginx.com         return NULL;
723*16Svbart@nginx.com     }
724*16Svbart@nginx.com 
725*16Svbart@nginx.com     entry = hash->entries[value->length - hash->min_length];
726*16Svbart@nginx.com 
727*16Svbart@nginx.com     if (entry == NULL) {
728*16Svbart@nginx.com         return NULL;
729*16Svbart@nginx.com     }
730*16Svbart@nginx.com 
731*16Svbart@nginx.com     switch ((value->length + 7) / 8) {
732*16Svbart@nginx.com     case 1:
733*16Svbart@nginx.com         do {
734*16Svbart@nginx.com             if (entry->key[0].ui64 == key[0]) {
735*16Svbart@nginx.com                 return entry;
736*16Svbart@nginx.com             }
737*16Svbart@nginx.com 
738*16Svbart@nginx.com             entry = nxt_http_fields_hash_next_entry(entry, 1);
739*16Svbart@nginx.com 
740*16Svbart@nginx.com         } while (entry->handler != NULL);
741*16Svbart@nginx.com 
742*16Svbart@nginx.com         break;
743*16Svbart@nginx.com 
744*16Svbart@nginx.com     case 2:
745*16Svbart@nginx.com         do {
746*16Svbart@nginx.com             if (entry->key[0].ui64 == key[0]
747*16Svbart@nginx.com                 && entry->key[1].ui64 == key[1])
748*16Svbart@nginx.com             {
749*16Svbart@nginx.com                 return entry;
750*16Svbart@nginx.com             }
751*16Svbart@nginx.com 
752*16Svbart@nginx.com             entry = nxt_http_fields_hash_next_entry(entry, 2);
753*16Svbart@nginx.com 
754*16Svbart@nginx.com         } while (entry->handler != NULL);
755*16Svbart@nginx.com 
756*16Svbart@nginx.com         break;
757*16Svbart@nginx.com 
758*16Svbart@nginx.com     case 3:
759*16Svbart@nginx.com         do {
760*16Svbart@nginx.com             if (entry->key[0].ui64 == key[0]
761*16Svbart@nginx.com                 && entry->key[1].ui64 == key[1]
762*16Svbart@nginx.com                 && entry->key[2].ui64 == key[2])
763*16Svbart@nginx.com             {
764*16Svbart@nginx.com                 return entry;
765*16Svbart@nginx.com             }
766*16Svbart@nginx.com 
767*16Svbart@nginx.com             entry = nxt_http_fields_hash_next_entry(entry, 3);
768*16Svbart@nginx.com 
769*16Svbart@nginx.com         } while (entry->handler != NULL);
770*16Svbart@nginx.com 
771*16Svbart@nginx.com         break;
772*16Svbart@nginx.com 
773*16Svbart@nginx.com     case 4:
774*16Svbart@nginx.com         do {
775*16Svbart@nginx.com             if (entry->key[0].ui64 == key[0]
776*16Svbart@nginx.com                 && entry->key[1].ui64 == key[1]
777*16Svbart@nginx.com                 && entry->key[2].ui64 == key[2]
778*16Svbart@nginx.com                 && entry->key[3].ui64 == key[3])
779*16Svbart@nginx.com             {
780*16Svbart@nginx.com                 return entry;
781*16Svbart@nginx.com             }
782*16Svbart@nginx.com 
783*16Svbart@nginx.com             entry = nxt_http_fields_hash_next_entry(entry, 4);
784*16Svbart@nginx.com 
785*16Svbart@nginx.com         } while (entry->handler != NULL);
786*16Svbart@nginx.com 
787*16Svbart@nginx.com         break;
788*16Svbart@nginx.com 
789*16Svbart@nginx.com     default:
790*16Svbart@nginx.com         nxt_unreachable();
791*16Svbart@nginx.com     }
792*16Svbart@nginx.com 
793*16Svbart@nginx.com     return NULL;
794*16Svbart@nginx.com }
795*16Svbart@nginx.com 
796*16Svbart@nginx.com 
797*16Svbart@nginx.com static nxt_http_fields_hash_entry_t *
798*16Svbart@nginx.com nxt_http_header_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
799*16Svbart@nginx.com     nxt_str_t *value)
800*16Svbart@nginx.com {
801*16Svbart@nginx.com     /* TODO */
802*16Svbart@nginx.com     return NULL;
803*16Svbart@nginx.com }
804*16Svbart@nginx.com 
805*16Svbart@nginx.com 
806*16Svbart@nginx.com nxt_http_fields_hash_t *
807*16Svbart@nginx.com nxt_http_fields_hash(nxt_http_fields_t *fields, nxt_mem_pool_t *mp)
808*16Svbart@nginx.com {
809*16Svbart@nginx.com     size_t                        min_length, max_length, length, size;
810*16Svbart@nginx.com     nxt_uint_t                    i, j, n;
811*16Svbart@nginx.com     nxt_http_fields_hash_t        *hash;
812*16Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entry;
813*16Svbart@nginx.com 
814*16Svbart@nginx.com     min_length = 0;
815*16Svbart@nginx.com     max_length = 0;
816*16Svbart@nginx.com 
817*16Svbart@nginx.com     for (i = 0; fields[i].handler != NULL; i++) {
818*16Svbart@nginx.com         length = fields[i].name.length;
819*16Svbart@nginx.com 
820*16Svbart@nginx.com         if (length > 32) {
821*16Svbart@nginx.com             /* TODO */
822*16Svbart@nginx.com             return NULL;
823*16Svbart@nginx.com         }
824*16Svbart@nginx.com 
825*16Svbart@nginx.com         min_length = nxt_min(length, min_length);
826*16Svbart@nginx.com         max_length = nxt_max(length, max_length);
827*16Svbart@nginx.com     }
828*16Svbart@nginx.com 
829*16Svbart@nginx.com     size = (max_length - min_length + 1)
830*16Svbart@nginx.com            * sizeof(nxt_http_fields_hash_entry_t *);
831*16Svbart@nginx.com 
832*16Svbart@nginx.com     hash = nxt_mem_zalloc(mp, sizeof(nxt_http_fields_hash_t) + size);
833*16Svbart@nginx.com 
834*16Svbart@nginx.com     if (nxt_slow_path(hash == NULL)) {
835*16Svbart@nginx.com         return NULL;
836*16Svbart@nginx.com     }
837*16Svbart@nginx.com 
838*16Svbart@nginx.com     hash->min_length = min_length;
839*16Svbart@nginx.com     hash->max_length = max_length;
840*16Svbart@nginx.com 
841*16Svbart@nginx.com     for (i = 0; fields[i].handler != NULL; i++) {
842*16Svbart@nginx.com         length = fields[i].name.length;
843*16Svbart@nginx.com         entry = hash->entries[length - min_length];
844*16Svbart@nginx.com 
845*16Svbart@nginx.com         if (entry != NULL) {
846*16Svbart@nginx.com             continue;
847*16Svbart@nginx.com         }
848*16Svbart@nginx.com 
849*16Svbart@nginx.com         n = 1;
850*16Svbart@nginx.com 
851*16Svbart@nginx.com         for (j = i + 1; fields[j].handler != NULL; j++) {
852*16Svbart@nginx.com             if (length == fields[j].name.length) {
853*16Svbart@nginx.com                 n++;
854*16Svbart@nginx.com             }
855*16Svbart@nginx.com         }
856*16Svbart@nginx.com 
857*16Svbart@nginx.com         size = sizeof(nxt_http_fields_hash_entry_t) + nxt_align_size(length, 8);
858*16Svbart@nginx.com 
859*16Svbart@nginx.com         entry = nxt_mem_zalloc(mp, n * size
860*16Svbart@nginx.com                                    + sizeof(nxt_http_fields_hash_entry_t));
861*16Svbart@nginx.com 
862*16Svbart@nginx.com         if (nxt_slow_path(entry == NULL)) {
863*16Svbart@nginx.com             return NULL;
864*16Svbart@nginx.com         }
865*16Svbart@nginx.com 
866*16Svbart@nginx.com         hash->entries[length - min_length] = entry;
867*16Svbart@nginx.com 
868*16Svbart@nginx.com         for (j = i; fields[j].handler != NULL; j++) {
869*16Svbart@nginx.com             if (length != fields[j].name.length) {
870*16Svbart@nginx.com                 continue;
871*16Svbart@nginx.com             }
872*16Svbart@nginx.com 
873*16Svbart@nginx.com             entry->handler = fields[j].handler;
874*16Svbart@nginx.com             entry->data = fields[j].data;
875*16Svbart@nginx.com 
876*16Svbart@nginx.com             nxt_memcpy_lowcase(entry->key->str, fields[j].name.start, length);
877*16Svbart@nginx.com 
878*16Svbart@nginx.com             n--;
879*16Svbart@nginx.com 
880*16Svbart@nginx.com             if (n == 0) {
881*16Svbart@nginx.com                 break;
882*16Svbart@nginx.com             }
883*16Svbart@nginx.com 
884*16Svbart@nginx.com             entry = (nxt_http_fields_hash_entry_t *) ((u_char *) entry + size);
885*16Svbart@nginx.com         }
886*16Svbart@nginx.com     }
887*16Svbart@nginx.com 
888*16Svbart@nginx.com     return hash;
889*16Svbart@nginx.com }
890