Mercurial > hg > nginx
annotate src/http/v3/ngx_http_v3_request.c @ 8226:268f4389130d quic
Refactored HTTP/3 parser.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Wed, 18 Mar 2020 13:46:35 +0300 |
parents | 1307308c3cf1 |
children | 31f7c697b6d9 |
rev | line source |
---|---|
8215 | 1 |
2 /* | |
3 * Copyright (C) Roman Arutyunyan | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, | |
14 ngx_str_t *name, ngx_str_t *value); | |
15 | |
16 | |
17 struct { | |
18 ngx_str_t name; | |
19 ngx_uint_t method; | |
20 } ngx_http_v3_methods[] = { | |
21 | |
22 { ngx_string("GET"), NGX_HTTP_GET }, | |
23 { ngx_string("POST"), NGX_HTTP_POST }, | |
24 { ngx_string("HEAD"), NGX_HTTP_HEAD }, | |
25 { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, | |
26 { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, | |
27 { ngx_string("PUT"), NGX_HTTP_PUT }, | |
28 { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, | |
29 { ngx_string("DELETE"), NGX_HTTP_DELETE }, | |
30 { ngx_string("COPY"), NGX_HTTP_COPY }, | |
31 { ngx_string("MOVE"), NGX_HTTP_MOVE }, | |
32 { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, | |
33 { ngx_string("LOCK"), NGX_HTTP_LOCK }, | |
34 { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, | |
35 { ngx_string("PATCH"), NGX_HTTP_PATCH }, | |
36 { ngx_string("TRACE"), NGX_HTTP_TRACE } | |
37 }; | |
38 | |
39 | |
40 ngx_int_t | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
41 ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b) |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
42 { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
43 ngx_int_t rc; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
44 ngx_str_t *name, *value; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
45 ngx_connection_t *c; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
46 ngx_http_v3_parse_headers_t *st; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
47 enum { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
48 sw_start = 0, |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
49 sw_prev, |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
50 sw_headers, |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
51 sw_last, |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
52 sw_done |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
53 }; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
54 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
55 c = r->connection; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
56 st = r->h3_parse; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
57 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
58 if (st == NULL) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
59 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header"); |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
60 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
61 st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_headers_t)); |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
62 if (st == NULL) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
63 goto failed; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
64 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
65 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
66 r->h3_parse = st; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
67 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
68 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
69 switch (r->state) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
70 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
71 case sw_prev: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
72 r->state = sw_headers; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
73 return NGX_OK; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
74 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
75 case sw_done: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
76 goto done; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
77 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
78 case sw_last: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
79 r->state = sw_done; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
80 return NGX_OK; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
81 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
82 default: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
83 break; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
84 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
85 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
86 for ( /* void */ ; b->pos < b->last; b->pos++) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
87 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
88 rc = ngx_http_v3_parse_headers(c, st, *b->pos); |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
89 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
90 if (rc == NGX_ERROR) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
91 goto failed; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
92 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
93 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
94 if (rc == NGX_AGAIN) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
95 continue; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
96 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
97 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
98 name = &st->header_rep.header.name; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
99 value = &st->header_rep.header.value; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
100 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
101 if (r->state == sw_start |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
102 && ngx_http_v3_process_pseudo_header(r, name, value) != NGX_OK) |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
103 { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
104 if (rc == NGX_DONE) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
105 r->state = sw_last; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
106 } else { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
107 r->state = sw_prev; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
108 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
109 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
110 } else if (rc == NGX_DONE) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
111 r->state = sw_done; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
112 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
113 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
114 if (r->state == sw_start) { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
115 continue; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
116 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
117 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
118 r->header_name_start = name->data; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
119 r->header_name_end = name->data + name->len; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
120 r->header_start = value->data; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
121 r->header_end = value->data + value->len; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
122 r->header_hash = ngx_hash_key(name->data, name->len); |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
123 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
124 /* XXX r->lowcase_index = i; */ |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
125 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
126 return NGX_OK; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
127 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
128 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
129 return NGX_AGAIN; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
130 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
131 failed: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
132 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
133 return r->state == sw_start ? NGX_HTTP_PARSE_INVALID_REQUEST |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
134 : NGX_HTTP_PARSE_INVALID_HEADER; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
135 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
136 done: |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
137 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
138 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header done"); |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
139 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
140 return NGX_HTTP_PARSE_HEADER_DONE; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
141 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
142 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
143 #if 0 |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
144 ngx_int_t |
8215 | 145 ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t pseudo) |
146 { | |
147 u_char *p, ch; | |
148 ngx_str_t name, value; | |
149 ngx_int_t rc; | |
150 ngx_uint_t length, index, insert_count, sign, base, delta_base, | |
151 huffman, dynamic, offset; | |
152 ngx_connection_t *c; | |
153 ngx_http_v3_header_t *h; | |
154 enum { | |
155 sw_start = 0, | |
156 sw_length, | |
157 sw_length_1, | |
158 sw_length_2, | |
159 sw_length_3, | |
160 sw_header_block, | |
161 sw_req_insert_count, | |
162 sw_delta_base, | |
163 sw_read_delta_base, | |
164 sw_header, | |
165 sw_old_header, | |
166 sw_header_ri, | |
167 sw_header_pbi, | |
168 sw_header_lri, | |
169 sw_header_lpbi, | |
170 sw_header_l_name_len, | |
171 sw_header_l_name, | |
172 sw_header_value_len, | |
173 sw_header_read_value_len, | |
174 sw_header_value | |
175 } state; | |
176 | |
177 c = r->connection; | |
178 | |
179 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
180 "http3 parse header, pseudo:%ui", pseudo); | |
181 | |
182 if (r->state == sw_old_header) { | |
183 r->state = sw_header; | |
184 return NGX_OK; | |
185 } | |
186 | |
187 length = r->h3_length; | |
188 index = r->h3_index; | |
189 insert_count = r->h3_insert_count; | |
190 sign = r->h3_sign; | |
191 delta_base = r->h3_delta_base; | |
192 huffman = r->h3_huffman; | |
193 dynamic = r->h3_dynamic; | |
194 offset = r->h3_offset; | |
195 | |
196 name.data = r->header_name_start; | |
197 name.len = r->header_name_end - r->header_name_start; | |
198 value.data = r->header_start; | |
199 value.len = r->header_end - r->header_start; | |
200 | |
201 if (r->state == sw_start) { | |
202 length = 1; | |
203 } | |
204 | |
205 again: | |
206 | |
207 state = r->state; | |
208 | |
209 if (state == sw_header && length == 0) { | |
210 r->state = sw_start; | |
211 return NGX_HTTP_PARSE_HEADER_DONE; | |
212 } | |
213 | |
214 for (p = b->pos; p < b->last; p++) { | |
215 | |
216 if (state >= sw_header_block && length-- == 0) { | |
217 goto failed; | |
218 } | |
219 | |
220 ch = *p; | |
221 | |
222 switch (state) { | |
223 | |
224 case sw_start: | |
225 | |
226 if (ch != NGX_HTTP_V3_FRAME_HEADERS) { | |
227 goto failed; | |
228 } | |
229 | |
230 r->request_start = p; | |
231 state = sw_length; | |
232 break; | |
233 | |
234 case sw_length: | |
235 | |
236 length = ch; | |
237 if (length & 0xc0) { | |
238 state = sw_length_1; | |
239 break; | |
240 } | |
241 | |
242 state = sw_header_block; | |
243 break; | |
244 | |
245 case sw_length_1: | |
246 | |
247 length = (length << 8) + ch; | |
248 if ((length & 0xc000) != 0x4000) { | |
249 state = sw_length_2; | |
250 break; | |
251 } | |
252 | |
253 length &= 0x3fff; | |
254 state = sw_header_block; | |
255 break; | |
256 | |
257 case sw_length_2: | |
258 | |
259 length = (length << 8) + ch; | |
260 if ((length & 0xc00000) != 0x800000) { | |
261 state = sw_length_3; | |
262 break; | |
263 } | |
264 | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
265 length &= 0x3fffff; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
266 state = sw_header_block; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
267 break; |
8215 | 268 |
269 case sw_length_3: | |
270 | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
271 length = (length << 8) + ch; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
272 length &= 0x3fffffff; |
8215 | 273 state = sw_header_block; |
274 break; | |
275 | |
276 case sw_header_block: | |
277 | |
278 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
279 "http3 header block length:%ui", length); | |
280 | |
281 if (ch != 0xff) { | |
282 insert_count = ch; | |
283 state = sw_delta_base; | |
284 break; | |
285 } | |
286 | |
287 insert_count = 0; | |
288 state = sw_req_insert_count; | |
289 break; | |
290 | |
291 case sw_req_insert_count: | |
292 | |
293 insert_count = (insert_count << 7) + (ch & 0x7f); | |
294 if (ch & 0x80) { | |
295 break; | |
296 } | |
297 | |
298 insert_count += 0xff; | |
299 state = sw_delta_base; | |
300 break; | |
301 | |
302 case sw_delta_base: | |
303 | |
304 sign = (ch & 0x80) ? 1 : 0; | |
305 delta_base = ch & 0x7f; | |
306 | |
307 if (delta_base != 0x7f) { | |
308 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
309 "http3 header block " | |
310 "insert_count:%ui, sign:%ui, delta_base:%ui", | |
311 insert_count, sign, delta_base); | |
312 goto done; | |
313 } | |
314 | |
315 delta_base = 0; | |
316 state = sw_read_delta_base; | |
317 break; | |
318 | |
319 case sw_read_delta_base: | |
320 | |
321 delta_base = (delta_base << 7) + (ch & 0x7f); | |
322 if (ch & 0x80) { | |
323 break; | |
324 } | |
325 | |
326 delta_base += 0x7f; | |
327 | |
328 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
329 "http3 header block " | |
330 "insert_count:%ui, sign:%ui, delta_base:%ui", | |
331 insert_count, sign, delta_base); | |
332 goto done; | |
333 | |
334 case sw_header: | |
335 | |
336 index = 0; | |
337 huffman = 0; | |
338 ngx_str_null(&name); | |
339 ngx_str_null(&value); | |
340 | |
341 if (ch & 0x80) { | |
342 /* Indexed Header Field */ | |
343 | |
344 dynamic = (ch & 0x40) ? 0 : 1; | |
345 index = ch & 0x3f; | |
346 | |
347 if (index != 0x3f) { | |
348 goto done; | |
349 } | |
350 | |
351 index = 0; | |
352 state = sw_header_ri; | |
353 break; | |
354 } | |
355 | |
356 if (ch & 0x40) { | |
357 /* Literal Header Field With Name Reference */ | |
358 | |
359 dynamic = (ch & 0x10) ? 0 : 1; | |
360 index = ch & 0x0f; | |
361 | |
362 if (index != 0x0f) { | |
363 state = sw_header_value_len; | |
364 break; | |
365 } | |
366 | |
367 index = 0; | |
368 state = sw_header_lri; | |
369 break; | |
370 } | |
371 | |
372 if (ch & 0x20) { | |
373 /* Literal Header Field Without Name Reference */ | |
374 | |
375 huffman = (ch & 0x08) ? 1 : 0; | |
376 name.len = ch & 0x07; | |
377 | |
378 if (name.len == 0) { | |
379 goto failed; | |
380 } | |
381 | |
382 if (name.len != 0x07) { | |
383 offset = 0; | |
384 state = sw_header_l_name; | |
385 break; | |
386 } | |
387 | |
388 name.len = 0; | |
389 state = sw_header_l_name_len; | |
390 break; | |
391 } | |
392 | |
393 if (ch & 10) { | |
394 /* Indexed Header Field With Post-Base Index */ | |
395 | |
396 dynamic = 2; | |
397 index = ch & 0x0f; | |
398 | |
399 if (index != 0x0f) { | |
400 goto done; | |
401 } | |
402 | |
403 index = 0; | |
404 state = sw_header_pbi; | |
405 break; | |
406 } | |
407 | |
408 /* Literal Header Field With Post-Base Name Reference */ | |
409 | |
410 dynamic = 2; | |
411 index = ch & 0x07; | |
412 | |
413 if (index != 0x07) { | |
414 state = sw_header_value_len; | |
415 break; | |
416 } | |
417 | |
418 index = 0; | |
419 state = sw_header_lpbi; | |
420 break; | |
421 | |
422 case sw_header_ri: | |
423 | |
424 index = (index << 7) + (ch & 0x7f); | |
425 if (ch & 0x80) { | |
426 break; | |
427 } | |
428 | |
429 index += 0x3f; | |
430 goto done; | |
431 | |
432 case sw_header_pbi: | |
433 | |
434 index = (index << 7) + (ch & 0x7f); | |
435 if (ch & 0x80) { | |
436 break; | |
437 } | |
438 | |
439 index += 0x0f; | |
440 goto done; | |
441 | |
442 case sw_header_lri: | |
443 | |
444 index = (index << 7) + (ch & 0x7f); | |
445 if (ch & 0x80) { | |
446 break; | |
447 } | |
448 | |
449 index += 0x0f; | |
450 state = sw_header_value_len; | |
451 break; | |
452 | |
453 case sw_header_lpbi: | |
454 | |
455 index = (index << 7) + (ch & 0x7f); | |
456 if (ch & 0x80) { | |
457 break; | |
458 } | |
459 | |
460 index += 0x07; | |
461 state = sw_header_value_len; | |
462 break; | |
463 | |
464 | |
465 case sw_header_l_name_len: | |
466 | |
467 name.len = (name.len << 7) + (ch & 0x7f); | |
468 if (ch & 0x80) { | |
469 break; | |
470 } | |
471 | |
472 name.len += 0x07; | |
473 offset = 0; | |
474 state = sw_header_l_name; | |
475 break; | |
476 | |
477 case sw_header_l_name: | |
478 if (offset++ == 0) { | |
479 name.data = p; | |
480 } | |
481 | |
482 if (offset != name.len) { | |
483 break; | |
484 } | |
485 | |
486 if (huffman) { | |
487 if (ngx_http_v3_decode_huffman(c, &name) != NGX_OK) { | |
488 goto failed; | |
489 } | |
490 } | |
491 | |
492 state = sw_header_value_len; | |
493 break; | |
494 | |
495 case sw_header_value_len: | |
496 | |
497 huffman = (ch & 0x80) ? 1 : 0; | |
498 value.len = ch & 0x7f; | |
499 | |
500 if (value.len == 0) { | |
501 value.data = p; | |
502 goto done; | |
503 } | |
504 | |
505 if (value.len != 0x7f) { | |
506 offset = 0; | |
507 state = sw_header_value; | |
508 break; | |
509 } | |
510 | |
511 value.len = 0; | |
512 state = sw_header_read_value_len; | |
513 break; | |
514 | |
515 case sw_header_read_value_len: | |
516 | |
517 value.len = (value.len << 7) + (ch & 0x7f); | |
518 if (ch & 0x80) { | |
519 break; | |
520 } | |
521 | |
522 value.len += 0x7f; | |
523 offset = 0; | |
524 state = sw_header_value; | |
525 break; | |
526 | |
527 case sw_header_value: | |
528 | |
529 if (offset++ == 0) { | |
530 value.data = p; | |
531 } | |
532 | |
533 if (offset != value.len) { | |
534 break; | |
535 } | |
536 | |
537 if (huffman) { | |
538 if (ngx_http_v3_decode_huffman(c, &value) != NGX_OK) { | |
539 goto failed; | |
540 } | |
541 } | |
542 | |
543 goto done; | |
544 | |
545 case sw_old_header: | |
546 | |
547 break; | |
548 } | |
549 } | |
550 | |
551 b->pos = p; | |
552 r->state = state; | |
553 r->h3_length = length; | |
554 r->h3_index = index; | |
555 r->h3_insert_count = insert_count; | |
556 r->h3_sign = sign; | |
557 r->h3_delta_base = delta_base; | |
558 r->h3_huffman = huffman; | |
559 r->h3_dynamic = dynamic; | |
560 r->h3_offset = offset; | |
561 | |
562 /* XXX fix large reallocations */ | |
563 r->header_name_start = name.data; | |
564 r->header_name_end = name.data + name.len; | |
565 r->header_start = value.data; | |
566 r->header_end = value.data + value.len; | |
567 | |
568 /* XXX r->lowcase_index = i; */ | |
569 | |
570 return NGX_AGAIN; | |
571 | |
572 done: | |
573 | |
574 b->pos = p + 1; | |
575 r->state = sw_header; | |
576 r->h3_length = length; | |
577 r->h3_insert_count = insert_count; | |
578 r->h3_sign = sign; | |
579 r->h3_delta_base = delta_base; | |
580 | |
581 if (state < sw_header) { | |
582 if (ngx_http_v3_check_insert_count(c, insert_count) != NGX_OK) { | |
583 return NGX_DONE; | |
584 } | |
585 | |
586 goto again; | |
587 } | |
588 | |
589 if (sign == 0) { | |
590 base = insert_count + delta_base; | |
591 } else { | |
592 base = insert_count - delta_base - 1; | |
593 } | |
594 | |
595 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
596 "http3 header %s[%ui], base:%ui, \"%V\":\"%V\"", | |
597 dynamic ? "dynamic" : "static", index, base, &name, &value); | |
598 | |
599 if (name.data == NULL) { | |
600 | |
601 if (dynamic == 2) { | |
602 index = base - index - 1; | |
603 } else if (dynamic == 1) { | |
604 index += base; | |
605 } | |
606 | |
607 h = ngx_http_v3_lookup_table(c, dynamic, index); | |
608 if (h == NULL) { | |
609 goto failed; | |
610 } | |
611 | |
612 name = h->name; | |
613 | |
614 if (value.data == NULL) { | |
615 value = h->value; | |
616 } | |
617 } | |
618 | |
8219
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
619 /* XXX ugly reallocation for the trailing '\0' */ |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
620 |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
621 p = ngx_pnalloc(c->pool, name.len + value.len + 2); |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
622 if (p == NULL) { |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
623 return NGX_ERROR; |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
624 } |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
625 |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
626 ngx_memcpy(p, name.data, name.len); |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
627 name.data = p; |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
628 ngx_memcpy(p + name.len + 1, value.data, value.len); |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
629 value.data = p + name.len + 1; |
1307308c3cf1
Temporary fix for header null-termination in HTTP/3.
Roman Arutyunyan <arut@nginx.com>
parents:
8215
diff
changeset
|
630 |
8215 | 631 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
632 "http3 header \"%V\":\"%V\"", &name, &value); | |
633 | |
634 if (pseudo) { | |
635 rc = ngx_http_v3_process_pseudo_header(r, &name, &value); | |
636 | |
637 if (rc == NGX_ERROR) { | |
638 goto failed; | |
639 } | |
640 | |
641 if (rc == NGX_OK) { | |
642 r->request_end = p + 1; | |
643 goto again; | |
644 } | |
645 | |
646 /* rc == NGX_DONE */ | |
647 | |
648 r->state = sw_old_header; | |
649 } | |
650 | |
651 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
652 "http3 header left:%ui", length); | |
653 | |
654 r->header_name_start = name.data; | |
655 r->header_name_end = name.data + name.len; | |
656 r->header_start = value.data; | |
657 r->header_end = value.data + value.len; | |
658 r->header_hash = ngx_hash_key(name.data, name.len); /* XXX */ | |
659 | |
660 /* XXX r->lowcase_index = i; */ | |
661 | |
662 return NGX_OK; | |
663 | |
664 failed: | |
665 | |
666 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
667 } | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
668 #endif |
8215 | 669 |
670 | |
671 static ngx_int_t | |
672 ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, | |
673 ngx_str_t *value) | |
674 { | |
675 ngx_uint_t i; | |
676 ngx_connection_t *c; | |
677 | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
678 if (name->len == 0 || name->data[0] != ':') { |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
679 return NGX_DECLINED; |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
680 } |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
681 |
8215 | 682 c = r->connection; |
683 | |
684 if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { | |
685 r->method_start = value->data; | |
686 r->method_end = value->data + value->len; | |
687 | |
688 for (i = 0; i < sizeof(ngx_http_v3_methods) | |
689 / sizeof(ngx_http_v3_methods[0]); i++) | |
690 { | |
691 if (value->len == ngx_http_v3_methods[i].name.len | |
692 && ngx_strncmp(value->data, ngx_http_v3_methods[i].name.data, | |
693 value->len) == 0) | |
694 { | |
695 r->method = ngx_http_v3_methods[i].method; | |
696 break; | |
697 } | |
698 } | |
699 | |
700 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
701 "http3 method \"%V\" %ui", value, r->method); | |
702 return NGX_OK; | |
703 } | |
704 | |
705 if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { | |
706 r->uri_start = value->data; | |
707 r->uri_end = value->data + value->len; | |
708 | |
709 if (ngx_http_parse_uri(r) != NGX_OK) { | |
710 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
711 "client sent invalid :path header: \"%V\"", value); | |
712 return NGX_ERROR; | |
713 } | |
714 | |
715 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
716 "http3 path \"%V\"", value); | |
717 | |
718 return NGX_OK; | |
719 } | |
720 | |
721 if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { | |
722 r->schema_start = value->data; | |
723 r->schema_end = value->data + value->len; | |
724 | |
725 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
726 "http3 schema \"%V\"", value); | |
727 | |
728 return NGX_OK; | |
729 } | |
730 | |
731 if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { | |
732 r->host_start = value->data; | |
733 r->host_end = value->data + value->len; | |
734 | |
735 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
736 "http3 authority \"%V\"", value); | |
737 | |
738 return NGX_OK; | |
739 } | |
740 | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
741 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
742 "http3 unknown pseudo header \"%V\" \"%V\"", name, value); |
8215 | 743 |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
744 return NGX_OK; |
8215 | 745 } |
746 | |
747 | |
748 ngx_chain_t * | |
749 ngx_http_v3_create_header(ngx_http_request_t *r) | |
750 { | |
751 u_char *p; | |
752 size_t len, hlen, n; | |
753 ngx_buf_t *b; | |
754 ngx_uint_t i, j; | |
755 ngx_chain_t *hl, *cl, *bl; | |
756 ngx_list_part_t *part; | |
757 ngx_table_elt_t *header; | |
758 ngx_connection_t *c; | |
759 ngx_http_core_loc_conf_t *clcf; | |
760 | |
761 c = r->connection; | |
762 | |
763 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); | |
764 | |
765 /* XXX support chunked body in the chunked filter */ | |
766 if (r->headers_out.content_length_n == -1) { | |
767 return NULL; | |
768 } | |
769 | |
770 len = 0; | |
771 | |
772 if (r->headers_out.status == NGX_HTTP_OK) { | |
773 len += ngx_http_v3_encode_prefix_int(NULL, 25, 6); | |
774 | |
775 } else { | |
776 len += 3 + ngx_http_v3_encode_prefix_int(NULL, 25, 4) | |
777 + ngx_http_v3_encode_prefix_int(NULL, 3, 7); | |
778 } | |
779 | |
780 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
781 | |
782 if (r->headers_out.server == NULL) { | |
783 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
784 n = sizeof(NGINX_VER) - 1; | |
785 | |
786 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
787 n = sizeof(NGINX_VER_BUILD) - 1; | |
788 | |
789 } else { | |
790 n = sizeof("nginx") - 1; | |
791 } | |
792 | |
793 len += ngx_http_v3_encode_prefix_int(NULL, 92, 4) | |
794 + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; | |
795 } | |
796 | |
797 if (r->headers_out.date == NULL) { | |
798 len += ngx_http_v3_encode_prefix_int(NULL, 6, 4) | |
799 + ngx_http_v3_encode_prefix_int(NULL, ngx_cached_http_time.len, | |
800 7) | |
801 + ngx_cached_http_time.len; | |
802 } | |
803 | |
804 if (r->headers_out.content_type.len) { | |
805 n = r->headers_out.content_type.len; | |
806 | |
807 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
808 && r->headers_out.charset.len) | |
809 { | |
810 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
811 } | |
812 | |
813 len += ngx_http_v3_encode_prefix_int(NULL, 53, 4) | |
814 + ngx_http_v3_encode_prefix_int(NULL, n, 7) + n; | |
815 } | |
816 | |
817 if (r->headers_out.content_length_n == 0) { | |
818 len += ngx_http_v3_encode_prefix_int(NULL, 4, 6); | |
819 | |
820 } else { | |
821 len += ngx_http_v3_encode_prefix_int(NULL, 4, 4) + 1 + NGX_OFF_T_LEN; | |
822 } | |
823 | |
824 if (r->headers_out.last_modified == NULL | |
825 && r->headers_out.last_modified_time != -1) | |
826 { | |
827 len += ngx_http_v3_encode_prefix_int(NULL, 10, 4) + 1 | |
828 + sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT"); | |
829 } | |
830 | |
831 /* XXX location */ | |
832 | |
833 #if (NGX_HTTP_GZIP) | |
834 if (r->gzip_vary) { | |
835 if (clcf->gzip_vary) { | |
836 /* Vary: Accept-Encoding */ | |
837 len += ngx_http_v3_encode_prefix_int(NULL, 59, 6); | |
838 | |
839 } else { | |
840 r->gzip_vary = 0; | |
841 } | |
842 } | |
843 #endif | |
844 | |
845 part = &r->headers_out.headers.part; | |
846 header = part->elts; | |
847 | |
848 for (i = 0; /* void */; i++) { | |
849 | |
850 if (i >= part->nelts) { | |
851 if (part->next == NULL) { | |
852 break; | |
853 } | |
854 | |
855 part = part->next; | |
856 header = part->elts; | |
857 i = 0; | |
858 } | |
859 | |
860 if (header[i].hash == 0) { | |
861 continue; | |
862 } | |
863 | |
864 len += ngx_http_v3_encode_prefix_int(NULL, header[i].key.len, 3) | |
865 + header[i].key.len | |
866 + ngx_http_v3_encode_prefix_int(NULL, header[i].value.len, 7 ) | |
867 + header[i].value.len; | |
868 } | |
869 | |
870 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); | |
871 | |
872 b = ngx_create_temp_buf(r->pool, len); | |
873 if (b == NULL) { | |
874 return NULL; | |
875 } | |
876 | |
877 *b->last++ = 0; | |
878 *b->last++ = 0; | |
879 | |
880 if (r->headers_out.status == NGX_HTTP_OK) { | |
881 /* :status: 200 */ | |
882 *b->last = 0xc0; | |
883 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 6); | |
884 | |
885 } else { | |
886 /* :status: 200 */ | |
887 *b->last = 0x70; | |
888 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 25, 4); | |
889 *b->last = 0; | |
890 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 3, 7); | |
8226
268f4389130d
Refactored HTTP/3 parser.
Roman Arutyunyan <arut@nginx.com>
parents:
8219
diff
changeset
|
891 b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); |
8215 | 892 } |
893 | |
894 if (r->headers_out.server == NULL) { | |
895 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
896 p = (u_char *) NGINX_VER; | |
897 n = sizeof(NGINX_VER) - 1; | |
898 | |
899 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
900 p = (u_char *) NGINX_VER_BUILD; | |
901 n = sizeof(NGINX_VER_BUILD) - 1; | |
902 | |
903 } else { | |
904 p = (u_char *) "nginx"; | |
905 n = sizeof("nginx") - 1; | |
906 } | |
907 | |
908 /* server */ | |
909 *b->last = 0x70; | |
910 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 92, 4); | |
911 *b->last = 0; | |
912 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); | |
913 b->last = ngx_cpymem(b->last, p, n); | |
914 } | |
915 | |
916 if (r->headers_out.date == NULL) { | |
917 /* date */ | |
918 *b->last = 0x70; | |
919 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 6, 4); | |
920 *b->last = 0; | |
921 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, | |
922 ngx_cached_http_time.len, 7); | |
923 b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, | |
924 ngx_cached_http_time.len); | |
925 } | |
926 | |
927 if (r->headers_out.content_type.len) { | |
928 n = r->headers_out.content_type.len; | |
929 | |
930 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
931 && r->headers_out.charset.len) | |
932 { | |
933 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
934 } | |
935 | |
936 /* content-type: text/plain */ | |
937 *b->last = 0x70; | |
938 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 53, 4); | |
939 *b->last = 0; | |
940 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, n, 7); | |
941 | |
942 p = b->last; | |
943 b->last = ngx_copy(b->last, r->headers_out.content_type.data, | |
944 r->headers_out.content_type.len); | |
945 | |
946 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
947 && r->headers_out.charset.len) | |
948 { | |
949 b->last = ngx_cpymem(b->last, "; charset=", | |
950 sizeof("; charset=") - 1); | |
951 b->last = ngx_copy(b->last, r->headers_out.charset.data, | |
952 r->headers_out.charset.len); | |
953 | |
954 /* update r->headers_out.content_type for possible logging */ | |
955 | |
956 r->headers_out.content_type.len = b->last - p; | |
957 r->headers_out.content_type.data = p; | |
958 } | |
959 } | |
960 | |
961 if (r->headers_out.content_length_n == 0) { | |
962 /* content-length: 0 */ | |
963 *b->last = 0xc0; | |
964 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 6); | |
965 | |
966 } else if (r->headers_out.content_length_n > 0) { | |
967 /* content-length: 0 */ | |
968 *b->last = 0x70; | |
969 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 4, 4); | |
970 p = b->last++; | |
971 b->last = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); | |
972 *p = b->last - p - 1; | |
973 } | |
974 | |
975 if (r->headers_out.last_modified == NULL | |
976 && r->headers_out.last_modified_time != -1) | |
977 { | |
978 /* last-modified */ | |
979 *b->last = 0x70; | |
980 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 10, 4); | |
981 p = b->last++; | |
982 b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); | |
983 *p = b->last - p - 1; | |
984 } | |
985 | |
986 #if (NGX_HTTP_GZIP) | |
987 if (r->gzip_vary) { | |
988 /* vary: accept-encoding */ | |
989 *b->last = 0xc0; | |
990 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, 59, 6); | |
991 } | |
992 #endif | |
993 | |
994 part = &r->headers_out.headers.part; | |
995 header = part->elts; | |
996 | |
997 for (i = 0; /* void */; i++) { | |
998 | |
999 if (i >= part->nelts) { | |
1000 if (part->next == NULL) { | |
1001 break; | |
1002 } | |
1003 | |
1004 part = part->next; | |
1005 header = part->elts; | |
1006 i = 0; | |
1007 } | |
1008 | |
1009 if (header[i].hash == 0) { | |
1010 continue; | |
1011 } | |
1012 | |
1013 *b->last = 0x30; | |
1014 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, | |
1015 header[i].key.len, | |
1016 3); | |
1017 for (j = 0; j < header[i].key.len; j++) { | |
1018 *b->last++ = ngx_tolower(header[i].key.data[j]); | |
1019 } | |
1020 | |
1021 *b->last = 0; | |
1022 b->last = (u_char *) ngx_http_v3_encode_prefix_int(b->last, | |
1023 header[i].value.len, | |
1024 7); | |
1025 b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); | |
1026 } | |
1027 | |
1028 cl = ngx_alloc_chain_link(c->pool); | |
1029 if (cl == NULL) { | |
1030 return NULL; | |
1031 } | |
1032 | |
1033 cl->buf = b; | |
1034 cl->next = NULL; | |
1035 | |
1036 n = b->last - b->pos; | |
1037 | |
1038 len = 1 + ngx_http_v3_encode_varlen_int(NULL, n); | |
1039 | |
1040 b = ngx_create_temp_buf(c->pool, len); | |
1041 if (b == NULL) { | |
1042 return NULL; | |
1043 } | |
1044 | |
1045 *b->last++ = NGX_HTTP_V3_FRAME_HEADERS; | |
1046 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); | |
1047 | |
1048 hl = ngx_alloc_chain_link(c->pool); | |
1049 if (hl == NULL) { | |
1050 return NULL; | |
1051 } | |
1052 | |
1053 hl->buf = b; | |
1054 hl->next = cl; | |
1055 | |
1056 hlen = 1 + ngx_http_v3_encode_varlen_int(NULL, len); | |
1057 | |
1058 if (r->headers_out.content_length_n >= 0) { | |
1059 len = 1 + ngx_http_v3_encode_varlen_int(NULL, | |
1060 r->headers_out.content_length_n); | |
1061 | |
1062 b = ngx_create_temp_buf(c->pool, len); | |
1063 if (b == NULL) { | |
1064 NULL; | |
1065 } | |
1066 | |
1067 *b->last++ = NGX_HTTP_V3_FRAME_DATA; | |
1068 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
1069 r->headers_out.content_length_n); | |
1070 | |
1071 bl = ngx_alloc_chain_link(c->pool); | |
1072 if (bl == NULL) { | |
1073 return NULL; | |
1074 } | |
1075 | |
1076 bl->buf = b; | |
1077 bl->next = NULL; | |
1078 cl->next = bl; | |
1079 } | |
1080 | |
1081 return hl; | |
1082 } |