Mercurial > hg > nginx
annotate src/http/modules/ngx_http_grpc_module.c @ 7281:bd6563e81cea
Limit req: improved handling of negative times.
Negative times can appear since workers only update time on an event
loop iteration start. If a worker was blocked for a long time during
an event loop iteration, it is possible that another worker already
updated the time stored in the node. As such, time since last update
of the node (ms) will be negative.
Previous code used ngx_abs(ms) in the calculations. That is, negative
times were effectively treated as positive ones. As a result, it was
not possible to maintain high request rates, where the same node can be
updated multiple times from during an event loop iteration.
In particular, this affected setups with many SSL handshakes, see
http://mailman.nginx.org/pipermail/nginx/2018-May/056291.html.
Fix is to only update the last update time stored in the node if the
new time is larger than previously stored one. If a future time is
stored in the node, we preserve this time as is.
To prevent breaking things on platforms without monotonic time available
if system time is updated backwards, a safety limit of 60 seconds is
used. If the time stored in the node is more than 60 seconds in the future,
we assume that the time was changed backwards and update lr->last
to the current time.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 30 May 2018 15:40:34 +0300 |
parents | 9e25a5380a21 |
children | 6cfd45d4c754 |
rev | line source |
---|---|
7233 | 1 |
2 /* | |
3 * Copyright (C) Maxim Dounin | |
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 typedef struct { | |
14 ngx_array_t *flushes; | |
15 ngx_array_t *lengths; | |
16 ngx_array_t *values; | |
17 ngx_hash_t hash; | |
18 } ngx_http_grpc_headers_t; | |
19 | |
20 | |
21 typedef struct { | |
22 ngx_http_upstream_conf_t upstream; | |
23 | |
24 ngx_http_grpc_headers_t headers; | |
25 ngx_array_t *headers_source; | |
26 | |
27 ngx_str_t host; | |
28 ngx_uint_t host_set; | |
29 | |
30 #if (NGX_HTTP_SSL) | |
31 ngx_uint_t ssl; | |
32 ngx_uint_t ssl_protocols; | |
33 ngx_str_t ssl_ciphers; | |
34 ngx_uint_t ssl_verify_depth; | |
35 ngx_str_t ssl_trusted_certificate; | |
36 ngx_str_t ssl_crl; | |
37 ngx_str_t ssl_certificate; | |
38 ngx_str_t ssl_certificate_key; | |
39 ngx_array_t *ssl_passwords; | |
40 #endif | |
41 } ngx_http_grpc_loc_conf_t; | |
42 | |
43 | |
44 typedef enum { | |
45 ngx_http_grpc_st_start = 0, | |
46 ngx_http_grpc_st_length_2, | |
47 ngx_http_grpc_st_length_3, | |
48 ngx_http_grpc_st_type, | |
49 ngx_http_grpc_st_flags, | |
50 ngx_http_grpc_st_stream_id, | |
51 ngx_http_grpc_st_stream_id_2, | |
52 ngx_http_grpc_st_stream_id_3, | |
53 ngx_http_grpc_st_stream_id_4, | |
54 ngx_http_grpc_st_payload, | |
55 ngx_http_grpc_st_padding | |
56 } ngx_http_grpc_state_e; | |
57 | |
58 | |
59 typedef struct { | |
60 size_t init_window; | |
61 size_t send_window; | |
62 size_t recv_window; | |
63 ngx_uint_t last_stream_id; | |
64 } ngx_http_grpc_conn_t; | |
65 | |
66 | |
67 typedef struct { | |
68 ngx_http_grpc_state_e state; | |
69 ngx_uint_t frame_state; | |
70 ngx_uint_t fragment_state; | |
71 | |
72 ngx_chain_t *in; | |
73 ngx_chain_t *out; | |
74 ngx_chain_t *free; | |
75 ngx_chain_t *busy; | |
76 | |
77 ngx_http_grpc_conn_t *connection; | |
78 | |
79 ngx_uint_t id; | |
80 | |
81 ssize_t send_window; | |
82 size_t recv_window; | |
83 | |
84 size_t rest; | |
85 ngx_uint_t stream_id; | |
86 u_char type; | |
87 u_char flags; | |
88 u_char padding; | |
89 | |
90 ngx_uint_t error; | |
91 ngx_uint_t window_update; | |
92 | |
93 ngx_uint_t setting_id; | |
94 ngx_uint_t setting_value; | |
95 | |
96 u_char ping_data[8]; | |
97 | |
98 ngx_uint_t index; | |
99 ngx_str_t name; | |
100 ngx_str_t value; | |
101 | |
102 u_char *field_end; | |
103 size_t field_length; | |
104 size_t field_rest; | |
105 u_char field_state; | |
106 | |
107 unsigned literal:1; | |
108 unsigned field_huffman:1; | |
109 | |
110 unsigned header_sent:1; | |
111 unsigned output_closed:1; | |
112 unsigned parsing_headers:1; | |
113 unsigned end_stream:1; | |
114 unsigned status:1; | |
115 | |
116 ngx_http_request_t *request; | |
117 } ngx_http_grpc_ctx_t; | |
118 | |
119 | |
120 typedef struct { | |
121 u_char length_0; | |
122 u_char length_1; | |
123 u_char length_2; | |
124 u_char type; | |
125 u_char flags; | |
126 u_char stream_id_0; | |
127 u_char stream_id_1; | |
128 u_char stream_id_2; | |
129 u_char stream_id_3; | |
130 } ngx_http_grpc_frame_t; | |
131 | |
132 | |
133 static ngx_int_t ngx_http_grpc_create_request(ngx_http_request_t *r); | |
134 static ngx_int_t ngx_http_grpc_reinit_request(ngx_http_request_t *r); | |
135 static ngx_int_t ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in); | |
136 static ngx_int_t ngx_http_grpc_process_header(ngx_http_request_t *r); | |
137 static ngx_int_t ngx_http_grpc_filter_init(void *data); | |
138 static ngx_int_t ngx_http_grpc_filter(void *data, ssize_t bytes); | |
139 | |
140 static ngx_int_t ngx_http_grpc_parse_frame(ngx_http_request_t *r, | |
141 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
142 static ngx_int_t ngx_http_grpc_parse_header(ngx_http_request_t *r, | |
143 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
144 static ngx_int_t ngx_http_grpc_parse_fragment(ngx_http_request_t *r, | |
145 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
146 static ngx_int_t ngx_http_grpc_validate_header_name(ngx_http_request_t *r, | |
147 ngx_str_t *s); | |
148 static ngx_int_t ngx_http_grpc_validate_header_value(ngx_http_request_t *r, | |
149 ngx_str_t *s); | |
150 static ngx_int_t ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, | |
151 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
152 static ngx_int_t ngx_http_grpc_parse_goaway(ngx_http_request_t *r, | |
153 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
154 static ngx_int_t ngx_http_grpc_parse_window_update(ngx_http_request_t *r, | |
155 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
156 static ngx_int_t ngx_http_grpc_parse_settings(ngx_http_request_t *r, | |
157 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
158 static ngx_int_t ngx_http_grpc_parse_ping(ngx_http_request_t *r, | |
159 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); | |
160 | |
161 static ngx_int_t ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, | |
162 ngx_http_grpc_ctx_t *ctx); | |
163 static ngx_int_t ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, | |
164 ngx_http_grpc_ctx_t *ctx); | |
165 static ngx_int_t ngx_http_grpc_send_window_update(ngx_http_request_t *r, | |
166 ngx_http_grpc_ctx_t *ctx); | |
167 | |
168 static ngx_chain_t *ngx_http_grpc_get_buf(ngx_http_request_t *r, | |
169 ngx_http_grpc_ctx_t *ctx); | |
170 static ngx_http_grpc_ctx_t *ngx_http_grpc_get_ctx(ngx_http_request_t *r); | |
171 static ngx_int_t ngx_http_grpc_get_connection_data(ngx_http_request_t *r, | |
172 ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc); | |
173 static void ngx_http_grpc_cleanup(void *data); | |
174 | |
175 static void ngx_http_grpc_abort_request(ngx_http_request_t *r); | |
176 static void ngx_http_grpc_finalize_request(ngx_http_request_t *r, | |
177 ngx_int_t rc); | |
178 | |
7234
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
179 static ngx_int_t ngx_http_grpc_internal_trailers_variable( |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
180 ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
181 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
182 static ngx_int_t ngx_http_grpc_add_variables(ngx_conf_t *cf); |
7233 | 183 static void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf); |
184 static char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, | |
185 void *parent, void *child); | |
186 static ngx_int_t ngx_http_grpc_init_headers(ngx_conf_t *cf, | |
187 ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_headers_t *headers, | |
188 ngx_keyval_t *default_headers); | |
189 | |
190 static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, | |
191 void *conf); | |
192 | |
193 #if (NGX_HTTP_SSL) | |
194 static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, | |
195 ngx_command_t *cmd, void *conf); | |
196 static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf, | |
197 ngx_http_grpc_loc_conf_t *glcf); | |
198 #endif | |
199 | |
200 | |
201 static ngx_conf_bitmask_t ngx_http_grpc_next_upstream_masks[] = { | |
202 { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, | |
203 { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, | |
204 { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, | |
205 { ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT }, | |
206 { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, | |
207 { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 }, | |
208 { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, | |
209 { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, | |
210 { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, | |
211 { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, | |
212 { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, | |
213 { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, | |
214 { ngx_null_string, 0 } | |
215 }; | |
216 | |
217 | |
218 #if (NGX_HTTP_SSL) | |
219 | |
220 static ngx_conf_bitmask_t ngx_http_grpc_ssl_protocols[] = { | |
221 { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, | |
222 { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, | |
223 { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, | |
224 { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, | |
225 { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, | |
226 { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, | |
227 { ngx_null_string, 0 } | |
228 }; | |
229 | |
230 #endif | |
231 | |
232 | |
233 static ngx_command_t ngx_http_grpc_commands[] = { | |
234 | |
235 { ngx_string("grpc_pass"), | |
236 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, | |
237 ngx_http_grpc_pass, | |
238 NGX_HTTP_LOC_CONF_OFFSET, | |
239 0, | |
240 NULL }, | |
241 | |
242 { ngx_string("grpc_bind"), | |
243 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, | |
244 ngx_http_upstream_bind_set_slot, | |
245 NGX_HTTP_LOC_CONF_OFFSET, | |
246 offsetof(ngx_http_grpc_loc_conf_t, upstream.local), | |
247 NULL }, | |
248 | |
249 { ngx_string("grpc_connect_timeout"), | |
250 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
251 ngx_conf_set_msec_slot, | |
252 NGX_HTTP_LOC_CONF_OFFSET, | |
253 offsetof(ngx_http_grpc_loc_conf_t, upstream.connect_timeout), | |
254 NULL }, | |
255 | |
256 { ngx_string("grpc_send_timeout"), | |
257 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
258 ngx_conf_set_msec_slot, | |
259 NGX_HTTP_LOC_CONF_OFFSET, | |
260 offsetof(ngx_http_grpc_loc_conf_t, upstream.send_timeout), | |
261 NULL }, | |
262 | |
263 { ngx_string("grpc_intercept_errors"), | |
264 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
265 ngx_conf_set_flag_slot, | |
266 NGX_HTTP_LOC_CONF_OFFSET, | |
267 offsetof(ngx_http_grpc_loc_conf_t, upstream.intercept_errors), | |
268 NULL }, | |
269 | |
270 { ngx_string("grpc_buffer_size"), | |
271 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
272 ngx_conf_set_size_slot, | |
273 NGX_HTTP_LOC_CONF_OFFSET, | |
274 offsetof(ngx_http_grpc_loc_conf_t, upstream.buffer_size), | |
275 NULL }, | |
276 | |
277 { ngx_string("grpc_read_timeout"), | |
278 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
279 ngx_conf_set_msec_slot, | |
280 NGX_HTTP_LOC_CONF_OFFSET, | |
281 offsetof(ngx_http_grpc_loc_conf_t, upstream.read_timeout), | |
282 NULL }, | |
283 | |
284 { ngx_string("grpc_next_upstream"), | |
285 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
286 ngx_conf_set_bitmask_slot, | |
287 NGX_HTTP_LOC_CONF_OFFSET, | |
288 offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream), | |
289 &ngx_http_grpc_next_upstream_masks }, | |
290 | |
291 { ngx_string("grpc_next_upstream_tries"), | |
292 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
293 ngx_conf_set_num_slot, | |
294 NGX_HTTP_LOC_CONF_OFFSET, | |
295 offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_tries), | |
296 NULL }, | |
297 | |
298 { ngx_string("grpc_next_upstream_timeout"), | |
299 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
300 ngx_conf_set_msec_slot, | |
301 NGX_HTTP_LOC_CONF_OFFSET, | |
302 offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_timeout), | |
303 NULL }, | |
304 | |
305 { ngx_string("grpc_set_header"), | |
306 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, | |
307 ngx_conf_set_keyval_slot, | |
308 NGX_HTTP_LOC_CONF_OFFSET, | |
309 offsetof(ngx_http_grpc_loc_conf_t, headers_source), | |
310 NULL }, | |
311 | |
312 { ngx_string("grpc_pass_header"), | |
313 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
314 ngx_conf_set_str_array_slot, | |
315 NGX_HTTP_LOC_CONF_OFFSET, | |
316 offsetof(ngx_http_grpc_loc_conf_t, upstream.pass_headers), | |
317 NULL }, | |
318 | |
319 { ngx_string("grpc_hide_header"), | |
320 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
321 ngx_conf_set_str_array_slot, | |
322 NGX_HTTP_LOC_CONF_OFFSET, | |
323 offsetof(ngx_http_grpc_loc_conf_t, upstream.hide_headers), | |
324 NULL }, | |
325 | |
326 { ngx_string("grpc_ignore_headers"), | |
327 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
328 ngx_conf_set_bitmask_slot, | |
329 NGX_HTTP_LOC_CONF_OFFSET, | |
330 offsetof(ngx_http_grpc_loc_conf_t, upstream.ignore_headers), | |
331 &ngx_http_upstream_ignore_headers_masks }, | |
332 | |
333 #if (NGX_HTTP_SSL) | |
334 | |
335 { ngx_string("grpc_ssl_session_reuse"), | |
336 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
337 ngx_conf_set_flag_slot, | |
338 NGX_HTTP_LOC_CONF_OFFSET, | |
339 offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_session_reuse), | |
340 NULL }, | |
341 | |
342 { ngx_string("grpc_ssl_protocols"), | |
343 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
344 ngx_conf_set_bitmask_slot, | |
345 NGX_HTTP_LOC_CONF_OFFSET, | |
346 offsetof(ngx_http_grpc_loc_conf_t, ssl_protocols), | |
347 &ngx_http_grpc_ssl_protocols }, | |
348 | |
349 { ngx_string("grpc_ssl_ciphers"), | |
350 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
351 ngx_conf_set_str_slot, | |
352 NGX_HTTP_LOC_CONF_OFFSET, | |
353 offsetof(ngx_http_grpc_loc_conf_t, ssl_ciphers), | |
354 NULL }, | |
355 | |
356 { ngx_string("grpc_ssl_name"), | |
357 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
358 ngx_http_set_complex_value_slot, | |
359 NGX_HTTP_LOC_CONF_OFFSET, | |
360 offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_name), | |
361 NULL }, | |
362 | |
363 { ngx_string("grpc_ssl_server_name"), | |
364 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
365 ngx_conf_set_flag_slot, | |
366 NGX_HTTP_LOC_CONF_OFFSET, | |
367 offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_server_name), | |
368 NULL }, | |
369 | |
370 { ngx_string("grpc_ssl_verify"), | |
371 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
372 ngx_conf_set_flag_slot, | |
373 NGX_HTTP_LOC_CONF_OFFSET, | |
374 offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_verify), | |
375 NULL }, | |
376 | |
377 { ngx_string("grpc_ssl_verify_depth"), | |
378 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
379 ngx_conf_set_num_slot, | |
380 NGX_HTTP_LOC_CONF_OFFSET, | |
381 offsetof(ngx_http_grpc_loc_conf_t, ssl_verify_depth), | |
382 NULL }, | |
383 | |
384 { ngx_string("grpc_ssl_trusted_certificate"), | |
385 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
386 ngx_conf_set_str_slot, | |
387 NGX_HTTP_LOC_CONF_OFFSET, | |
388 offsetof(ngx_http_grpc_loc_conf_t, ssl_trusted_certificate), | |
389 NULL }, | |
390 | |
391 { ngx_string("grpc_ssl_crl"), | |
392 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
393 ngx_conf_set_str_slot, | |
394 NGX_HTTP_LOC_CONF_OFFSET, | |
395 offsetof(ngx_http_grpc_loc_conf_t, ssl_crl), | |
396 NULL }, | |
397 | |
398 { ngx_string("grpc_ssl_certificate"), | |
399 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
400 ngx_conf_set_str_slot, | |
401 NGX_HTTP_LOC_CONF_OFFSET, | |
402 offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate), | |
403 NULL }, | |
404 | |
405 { ngx_string("grpc_ssl_certificate_key"), | |
406 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
407 ngx_conf_set_str_slot, | |
408 NGX_HTTP_LOC_CONF_OFFSET, | |
409 offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate_key), | |
410 NULL }, | |
411 | |
412 { ngx_string("grpc_ssl_password_file"), | |
413 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
414 ngx_http_grpc_ssl_password_file, | |
415 NGX_HTTP_LOC_CONF_OFFSET, | |
416 0, | |
417 NULL }, | |
418 | |
419 #endif | |
420 | |
421 ngx_null_command | |
422 }; | |
423 | |
424 | |
425 static ngx_http_module_t ngx_http_grpc_module_ctx = { | |
7234
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
426 ngx_http_grpc_add_variables, /* preconfiguration */ |
7233 | 427 NULL, /* postconfiguration */ |
428 | |
429 NULL, /* create main configuration */ | |
430 NULL, /* init main configuration */ | |
431 | |
432 NULL, /* create server configuration */ | |
433 NULL, /* merge server configuration */ | |
434 | |
435 ngx_http_grpc_create_loc_conf, /* create location configuration */ | |
436 ngx_http_grpc_merge_loc_conf /* merge location configuration */ | |
437 }; | |
438 | |
439 | |
440 ngx_module_t ngx_http_grpc_module = { | |
441 NGX_MODULE_V1, | |
442 &ngx_http_grpc_module_ctx, /* module context */ | |
443 ngx_http_grpc_commands, /* module directives */ | |
444 NGX_HTTP_MODULE, /* module type */ | |
445 NULL, /* init master */ | |
446 NULL, /* init module */ | |
447 NULL, /* init process */ | |
448 NULL, /* init thread */ | |
449 NULL, /* exit thread */ | |
450 NULL, /* exit process */ | |
451 NULL, /* exit master */ | |
452 NGX_MODULE_V1_PADDING | |
453 }; | |
454 | |
455 | |
456 static u_char ngx_http_grpc_connection_start[] = | |
457 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */ | |
458 | |
459 "\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */ | |
460 "\x00\x01\x00\x00\x00\x00" /* header table size */ | |
461 "\x00\x02\x00\x00\x00\x00" /* disable push */ | |
462 "\x00\x04\x7f\xff\xff\xff" /* initial window */ | |
463 | |
464 "\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */ | |
465 "\x7f\xff\x00\x00"; | |
466 | |
467 | |
468 static ngx_keyval_t ngx_http_grpc_headers[] = { | |
469 { ngx_string("Content-Length"), ngx_string("$content_length") }, | |
7234
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
470 { ngx_string("TE"), ngx_string("$grpc_internal_trailers") }, |
7233 | 471 { ngx_string("Host"), ngx_string("") }, |
472 { ngx_string("Connection"), ngx_string("") }, | |
473 { ngx_string("Transfer-Encoding"), ngx_string("") }, | |
474 { ngx_string("Keep-Alive"), ngx_string("") }, | |
475 { ngx_string("Expect"), ngx_string("") }, | |
476 { ngx_string("Upgrade"), ngx_string("") }, | |
477 { ngx_null_string, ngx_null_string } | |
478 }; | |
479 | |
480 | |
481 static ngx_str_t ngx_http_grpc_hide_headers[] = { | |
482 ngx_string("Date"), | |
483 ngx_string("Server"), | |
484 ngx_string("X-Accel-Expires"), | |
485 ngx_string("X-Accel-Redirect"), | |
486 ngx_string("X-Accel-Limit-Rate"), | |
487 ngx_string("X-Accel-Buffering"), | |
488 ngx_string("X-Accel-Charset"), | |
489 ngx_null_string | |
490 }; | |
491 | |
492 | |
7234
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
493 static ngx_http_variable_t ngx_http_grpc_vars[] = { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
494 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
495 { ngx_string("grpc_internal_trailers"), NULL, |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
496 ngx_http_grpc_internal_trailers_variable, 0, |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
497 NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
498 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
499 ngx_http_null_variable |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
500 }; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
501 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
502 |
7233 | 503 static ngx_int_t |
504 ngx_http_grpc_handler(ngx_http_request_t *r) | |
505 { | |
506 ngx_int_t rc; | |
507 ngx_http_upstream_t *u; | |
508 ngx_http_grpc_ctx_t *ctx; | |
509 ngx_http_grpc_loc_conf_t *glcf; | |
510 | |
511 if (ngx_http_upstream_create(r) != NGX_OK) { | |
512 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
513 } | |
514 | |
515 glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); | |
516 | |
517 u = r->upstream; | |
518 | |
519 #if (NGX_HTTP_SSL) | |
520 u->ssl = (glcf->upstream.ssl != NULL); | |
521 | |
522 if (u->ssl) { | |
523 ngx_str_set(&u->schema, "grpcs://"); | |
524 | |
525 } else { | |
526 ngx_str_set(&u->schema, "grpc://"); | |
527 } | |
528 #else | |
529 ngx_str_set(&u->schema, "grpc://"); | |
530 #endif | |
531 | |
532 u->output.tag = (ngx_buf_tag_t) &ngx_http_grpc_module; | |
533 | |
534 u->conf = &glcf->upstream; | |
535 | |
536 u->create_request = ngx_http_grpc_create_request; | |
537 u->reinit_request = ngx_http_grpc_reinit_request; | |
538 u->process_header = ngx_http_grpc_process_header; | |
539 u->abort_request = ngx_http_grpc_abort_request; | |
540 u->finalize_request = ngx_http_grpc_finalize_request; | |
541 | |
542 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_grpc_ctx_t)); | |
543 if (ctx == NULL) { | |
544 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
545 } | |
546 | |
547 ctx->request = r; | |
548 | |
549 ngx_http_set_ctx(r, ctx, ngx_http_grpc_module); | |
550 | |
551 u->input_filter_init = ngx_http_grpc_filter_init; | |
552 u->input_filter = ngx_http_grpc_filter; | |
553 u->input_filter_ctx = ctx; | |
554 | |
555 r->request_body_no_buffering = 1; | |
556 | |
557 rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); | |
558 | |
559 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { | |
560 return rc; | |
561 } | |
562 | |
563 return NGX_DONE; | |
564 } | |
565 | |
566 | |
567 static ngx_int_t | |
568 ngx_http_grpc_create_request(ngx_http_request_t *r) | |
569 { | |
570 u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame; | |
571 size_t len, tmp_len, key_len, val_len, uri_len; | |
572 uintptr_t escape; | |
573 ngx_buf_t *b; | |
574 ngx_uint_t i, next; | |
575 ngx_chain_t *cl, *body; | |
576 ngx_list_part_t *part; | |
577 ngx_table_elt_t *header; | |
578 ngx_http_upstream_t *u; | |
579 ngx_http_grpc_frame_t *f; | |
580 ngx_http_script_code_pt code; | |
581 ngx_http_grpc_loc_conf_t *glcf; | |
582 ngx_http_script_engine_t e, le; | |
583 ngx_http_script_len_code_pt lcode; | |
584 | |
585 u = r->upstream; | |
586 | |
587 glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); | |
588 | |
589 len = sizeof(ngx_http_grpc_connection_start) - 1 | |
590 + sizeof(ngx_http_grpc_frame_t); /* headers frame */ | |
591 | |
592 /* :method header */ | |
593 | |
594 if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) { | |
595 len += 1; | |
596 tmp_len = 0; | |
597 | |
598 } else { | |
599 len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len; | |
600 tmp_len = r->method_name.len; | |
601 } | |
602 | |
603 /* :scheme header */ | |
604 | |
605 len += 1; | |
606 | |
607 /* :path header */ | |
608 | |
609 if (r->valid_unparsed_uri) { | |
610 escape = 0; | |
611 uri_len = r->unparsed_uri.len; | |
612 | |
613 } else { | |
614 escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, | |
615 NGX_ESCAPE_URI); | |
616 uri_len = r->uri.len + escape + sizeof("?") - 1 + r->args.len; | |
617 } | |
618 | |
619 len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; | |
620 | |
621 if (tmp_len < uri_len) { | |
622 tmp_len = uri_len; | |
623 } | |
624 | |
625 /* :authority header */ | |
626 | |
627 if (!glcf->host_set) { | |
628 len += 1 + NGX_HTTP_V2_INT_OCTETS + glcf->host.len; | |
629 | |
630 if (tmp_len < glcf->host.len) { | |
631 tmp_len = glcf->host.len; | |
632 } | |
633 } | |
634 | |
635 /* other headers */ | |
636 | |
637 ngx_http_script_flush_no_cacheable_variables(r, glcf->headers.flushes); | |
638 ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); | |
639 | |
640 le.ip = glcf->headers.lengths->elts; | |
641 le.request = r; | |
642 le.flushed = 1; | |
643 | |
644 while (*(uintptr_t *) le.ip) { | |
645 | |
646 lcode = *(ngx_http_script_len_code_pt *) le.ip; | |
647 key_len = lcode(&le); | |
648 | |
649 for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { | |
650 lcode = *(ngx_http_script_len_code_pt *) le.ip; | |
651 } | |
652 le.ip += sizeof(uintptr_t); | |
653 | |
654 if (val_len == 0) { | |
655 continue; | |
656 } | |
657 | |
658 len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len | |
659 + NGX_HTTP_V2_INT_OCTETS + val_len; | |
660 | |
661 if (tmp_len < key_len) { | |
662 tmp_len = key_len; | |
663 } | |
664 | |
665 if (tmp_len < val_len) { | |
666 tmp_len = val_len; | |
667 } | |
668 } | |
669 | |
670 if (glcf->upstream.pass_request_headers) { | |
671 part = &r->headers_in.headers.part; | |
672 header = part->elts; | |
673 | |
674 for (i = 0; /* void */; i++) { | |
675 | |
676 if (i >= part->nelts) { | |
677 if (part->next == NULL) { | |
678 break; | |
679 } | |
680 | |
681 part = part->next; | |
682 header = part->elts; | |
683 i = 0; | |
684 } | |
685 | |
686 if (ngx_hash_find(&glcf->headers.hash, header[i].hash, | |
687 header[i].lowcase_key, header[i].key.len)) | |
688 { | |
689 continue; | |
690 } | |
691 | |
692 len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len | |
693 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; | |
694 | |
695 if (tmp_len < header[i].key.len) { | |
696 tmp_len = header[i].key.len; | |
697 } | |
698 | |
699 if (tmp_len < header[i].value.len) { | |
700 tmp_len = header[i].value.len; | |
701 } | |
702 } | |
703 } | |
704 | |
705 /* continuation frames */ | |
706 | |
707 len += sizeof(ngx_http_grpc_frame_t) | |
708 * (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE); | |
709 | |
710 | |
711 b = ngx_create_temp_buf(r->pool, len); | |
712 if (b == NULL) { | |
713 return NGX_ERROR; | |
714 } | |
715 | |
716 cl = ngx_alloc_chain_link(r->pool); | |
717 if (cl == NULL) { | |
718 return NGX_ERROR; | |
719 } | |
720 | |
721 cl->buf = b; | |
722 cl->next = NULL; | |
723 | |
724 tmp = ngx_palloc(r->pool, tmp_len * 3); | |
725 if (tmp == NULL) { | |
726 return NGX_ERROR; | |
727 } | |
728 | |
729 key_tmp = tmp + tmp_len; | |
730 val_tmp = tmp + 2 * tmp_len; | |
731 | |
732 /* connection preface */ | |
733 | |
734 b->last = ngx_copy(b->last, ngx_http_grpc_connection_start, | |
735 sizeof(ngx_http_grpc_connection_start) - 1); | |
736 | |
737 /* headers frame */ | |
738 | |
739 headers_frame = b->last; | |
740 | |
741 f = (ngx_http_grpc_frame_t *) b->last; | |
742 b->last += sizeof(ngx_http_grpc_frame_t); | |
743 | |
744 f->length_0 = 0; | |
745 f->length_1 = 0; | |
746 f->length_2 = 0; | |
747 f->type = NGX_HTTP_V2_HEADERS_FRAME; | |
748 f->flags = 0; | |
749 f->stream_id_0 = 0; | |
750 f->stream_id_1 = 0; | |
751 f->stream_id_2 = 0; | |
752 f->stream_id_3 = 1; | |
753 | |
754 if (r->method == NGX_HTTP_GET) { | |
755 *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX); | |
756 | |
757 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
758 "grpc header: \":method: GET\""); | |
759 | |
760 } else if (r->method == NGX_HTTP_POST) { | |
761 *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX); | |
762 | |
763 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
764 "grpc header: \":method: POST\""); | |
765 | |
766 } else { | |
767 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX); | |
768 b->last = ngx_http_v2_write_value(b->last, r->method_name.data, | |
769 r->method_name.len, tmp); | |
770 | |
771 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
772 "grpc header: \":method: %V\"", &r->method_name); | |
773 } | |
774 | |
775 #if (NGX_HTTP_SSL) | |
776 if (glcf->ssl) { | |
777 *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX); | |
778 | |
779 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
780 "grpc header: \":scheme: https\""); | |
781 } else | |
782 #endif | |
783 { | |
784 *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); | |
785 | |
786 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
787 "grpc header: \":scheme: http\""); | |
788 } | |
789 | |
790 if (r->valid_unparsed_uri) { | |
791 | |
792 if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') { | |
793 *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX); | |
794 | |
795 } else { | |
796 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); | |
797 b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data, | |
798 r->unparsed_uri.len, tmp); | |
799 } | |
800 | |
801 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
802 "grpc header: \":path: %V\"", &r->unparsed_uri); | |
803 | |
804 } else if (escape || r->args.len > 0) { | |
805 p = val_tmp; | |
806 | |
807 if (escape) { | |
808 p = (u_char *) ngx_escape_uri(p, r->uri.data, r->uri.len, | |
809 NGX_ESCAPE_URI); | |
810 | |
811 } else { | |
812 p = ngx_copy(p, r->uri.data, r->uri.len); | |
813 } | |
814 | |
815 if (r->args.len > 0) { | |
816 *p++ = '?'; | |
817 p = ngx_copy(p, r->args.data, r->args.len); | |
818 } | |
819 | |
820 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); | |
821 b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp); | |
822 | |
823 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
824 "grpc header: \":path: %*s\"", p - val_tmp, val_tmp); | |
825 | |
826 } else { | |
827 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); | |
828 b->last = ngx_http_v2_write_value(b->last, r->uri.data, | |
829 r->uri.len, tmp); | |
830 | |
831 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
832 "grpc header: \":path: %V\"", &r->uri); | |
833 } | |
834 | |
835 if (!glcf->host_set) { | |
836 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); | |
837 b->last = ngx_http_v2_write_value(b->last, glcf->host.data, | |
838 glcf->host.len, tmp); | |
839 | |
840 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
841 "grpc header: \":authority: %V\"", &glcf->host); | |
842 } | |
843 | |
844 ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); | |
845 | |
846 e.ip = glcf->headers.values->elts; | |
847 e.request = r; | |
848 e.flushed = 1; | |
849 | |
850 le.ip = glcf->headers.lengths->elts; | |
851 | |
852 while (*(uintptr_t *) le.ip) { | |
853 | |
854 lcode = *(ngx_http_script_len_code_pt *) le.ip; | |
855 key_len = lcode(&le); | |
856 | |
857 for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { | |
858 lcode = *(ngx_http_script_len_code_pt *) le.ip; | |
859 } | |
860 le.ip += sizeof(uintptr_t); | |
861 | |
862 if (val_len == 0) { | |
863 e.skip = 1; | |
864 | |
865 while (*(uintptr_t *) e.ip) { | |
866 code = *(ngx_http_script_code_pt *) e.ip; | |
867 code((ngx_http_script_engine_t *) &e); | |
868 } | |
869 e.ip += sizeof(uintptr_t); | |
870 | |
871 e.skip = 0; | |
872 | |
873 continue; | |
874 } | |
875 | |
876 *b->last++ = 0; | |
877 | |
878 e.pos = key_tmp; | |
879 | |
880 code = *(ngx_http_script_code_pt *) e.ip; | |
881 code((ngx_http_script_engine_t *) &e); | |
882 | |
883 b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp); | |
884 | |
885 e.pos = val_tmp; | |
886 | |
887 while (*(uintptr_t *) e.ip) { | |
888 code = *(ngx_http_script_code_pt *) e.ip; | |
889 code((ngx_http_script_engine_t *) &e); | |
890 } | |
891 e.ip += sizeof(uintptr_t); | |
892 | |
893 b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp); | |
894 | |
895 #if (NGX_DEBUG) | |
896 if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
897 ngx_strlow(key_tmp, key_tmp, key_len); | |
898 | |
899 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
900 "grpc header: \"%*s: %*s\"", | |
901 key_len, key_tmp, val_len, val_tmp); | |
902 } | |
903 #endif | |
904 } | |
905 | |
906 if (glcf->upstream.pass_request_headers) { | |
907 part = &r->headers_in.headers.part; | |
908 header = part->elts; | |
909 | |
910 for (i = 0; /* void */; i++) { | |
911 | |
912 if (i >= part->nelts) { | |
913 if (part->next == NULL) { | |
914 break; | |
915 } | |
916 | |
917 part = part->next; | |
918 header = part->elts; | |
919 i = 0; | |
920 } | |
921 | |
922 if (ngx_hash_find(&glcf->headers.hash, header[i].hash, | |
923 header[i].lowcase_key, header[i].key.len)) | |
924 { | |
925 continue; | |
926 } | |
927 | |
928 *b->last++ = 0; | |
929 | |
930 b->last = ngx_http_v2_write_name(b->last, header[i].key.data, | |
931 header[i].key.len, tmp); | |
932 | |
933 b->last = ngx_http_v2_write_value(b->last, header[i].value.data, | |
934 header[i].value.len, tmp); | |
935 | |
936 #if (NGX_DEBUG) | |
937 if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
938 ngx_strlow(tmp, header[i].key.data, header[i].key.len); | |
939 | |
940 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
941 "grpc header: \"%*s: %V\"", | |
942 header[i].key.len, tmp, &header[i].value); | |
943 } | |
944 #endif | |
945 } | |
946 } | |
947 | |
948 /* update headers frame length */ | |
949 | |
950 len = b->last - headers_frame - sizeof(ngx_http_grpc_frame_t); | |
951 | |
952 if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { | |
953 len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; | |
954 next = 1; | |
955 | |
956 } else { | |
957 next = 0; | |
958 } | |
959 | |
960 f = (ngx_http_grpc_frame_t *) headers_frame; | |
961 | |
962 f->length_0 = (u_char) ((len >> 16) & 0xff); | |
963 f->length_1 = (u_char) ((len >> 8) & 0xff); | |
964 f->length_2 = (u_char) (len & 0xff); | |
965 | |
966 /* create additional continuation frames */ | |
967 | |
968 p = headers_frame; | |
969 | |
970 while (next) { | |
971 p += sizeof(ngx_http_grpc_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE; | |
972 len = b->last - p; | |
973 | |
974 ngx_memmove(p + sizeof(ngx_http_grpc_frame_t), p, len); | |
975 b->last += sizeof(ngx_http_grpc_frame_t); | |
976 | |
977 if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { | |
978 len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; | |
979 next = 1; | |
980 | |
981 } else { | |
982 next = 0; | |
983 } | |
984 | |
985 f = (ngx_http_grpc_frame_t *) p; | |
986 | |
987 f->length_0 = (u_char) ((len >> 16) & 0xff); | |
988 f->length_1 = (u_char) ((len >> 8) & 0xff); | |
989 f->length_2 = (u_char) (len & 0xff); | |
990 f->type = NGX_HTTP_V2_CONTINUATION_FRAME; | |
991 f->flags = 0; | |
992 f->stream_id_0 = 0; | |
993 f->stream_id_1 = 0; | |
994 f->stream_id_2 = 0; | |
995 f->stream_id_3 = 1; | |
996 } | |
997 | |
998 f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG; | |
999 | |
1000 #if (NGX_DEBUG) | |
1001 if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
1002 u_char buf[512]; | |
1003 size_t n, m; | |
1004 | |
1005 n = ngx_min(b->last - b->pos, 256); | |
1006 m = ngx_hex_dump(buf, b->pos, n) - buf; | |
1007 | |
1008 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1009 "grpc header: %*s%s, len: %uz", | |
1010 m, buf, b->last - b->pos > 256 ? "..." : "", | |
1011 b->last - b->pos); | |
1012 } | |
1013 #endif | |
1014 | |
1015 if (r->request_body_no_buffering) { | |
1016 | |
1017 u->request_bufs = cl; | |
1018 | |
1019 } else { | |
1020 | |
1021 body = u->request_bufs; | |
1022 u->request_bufs = cl; | |
1023 | |
1024 if (body == NULL) { | |
1025 f = (ngx_http_grpc_frame_t *) headers_frame; | |
1026 f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; | |
1027 } | |
1028 | |
1029 while (body) { | |
1030 b = ngx_alloc_buf(r->pool); | |
1031 if (b == NULL) { | |
1032 return NGX_ERROR; | |
1033 } | |
1034 | |
1035 ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); | |
1036 | |
1037 cl->next = ngx_alloc_chain_link(r->pool); | |
1038 if (cl->next == NULL) { | |
1039 return NGX_ERROR; | |
1040 } | |
1041 | |
1042 cl = cl->next; | |
1043 cl->buf = b; | |
1044 | |
1045 body = body->next; | |
1046 } | |
1047 | |
1048 b->last_buf = 1; | |
1049 } | |
1050 | |
1051 u->output.output_filter = ngx_http_grpc_body_output_filter; | |
1052 u->output.filter_ctx = r; | |
1053 | |
1054 b->flush = 1; | |
1055 cl->next = NULL; | |
1056 | |
1057 return NGX_OK; | |
1058 } | |
1059 | |
1060 | |
1061 static ngx_int_t | |
1062 ngx_http_grpc_reinit_request(ngx_http_request_t *r) | |
1063 { | |
1064 ngx_http_grpc_ctx_t *ctx; | |
1065 | |
1066 ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module); | |
1067 | |
1068 if (ctx == NULL) { | |
1069 return NGX_OK; | |
1070 } | |
1071 | |
1072 ctx->state = 0; | |
1073 ctx->header_sent = 0; | |
1074 ctx->output_closed = 0; | |
1075 ctx->parsing_headers = 0; | |
1076 ctx->end_stream = 0; | |
1077 ctx->status = 0; | |
1078 ctx->connection = NULL; | |
1079 | |
1080 return NGX_OK; | |
1081 } | |
1082 | |
1083 | |
1084 static ngx_int_t | |
1085 ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in) | |
1086 { | |
1087 ngx_http_request_t *r = data; | |
1088 | |
1089 off_t file_pos; | |
1090 u_char *p, *pos, *start; | |
1091 size_t len, limit; | |
1092 ngx_buf_t *b; | |
1093 ngx_int_t rc; | |
1094 ngx_uint_t next, last; | |
1095 ngx_chain_t *cl, *out, **ll; | |
1096 ngx_http_grpc_ctx_t *ctx; | |
1097 ngx_http_grpc_frame_t *f; | |
1098 | |
1099 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1100 "grpc output filter"); | |
1101 | |
1102 ctx = ngx_http_grpc_get_ctx(r); | |
1103 | |
1104 if (ctx == NULL) { | |
1105 return NGX_ERROR; | |
1106 } | |
1107 | |
1108 if (in) { | |
1109 if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { | |
1110 return NGX_ERROR; | |
1111 } | |
1112 } | |
1113 | |
1114 out = NULL; | |
1115 ll = &out; | |
1116 | |
1117 if (!ctx->header_sent) { | |
1118 /* first buffer contains headers */ | |
1119 | |
1120 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1121 "grpc output header"); | |
1122 | |
1123 ctx->header_sent = 1; | |
1124 | |
1125 if (ctx->id != 1) { | |
1126 /* | |
1127 * keepalive connection: skip connection preface, | |
1128 * update stream identifiers | |
1129 */ | |
1130 | |
1131 b = ctx->in->buf; | |
1132 b->pos += sizeof(ngx_http_grpc_connection_start) - 1; | |
1133 | |
1134 p = b->pos; | |
1135 | |
1136 while (p < b->last) { | |
1137 f = (ngx_http_grpc_frame_t *) p; | |
1138 p += sizeof(ngx_http_grpc_frame_t); | |
1139 | |
1140 f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); | |
1141 f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); | |
1142 f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); | |
1143 f->stream_id_3 = (u_char) (ctx->id & 0xff); | |
1144 | |
1145 p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2; | |
1146 } | |
1147 } | |
1148 | |
1149 if (ctx->in->buf->last_buf) { | |
1150 ctx->output_closed = 1; | |
1151 } | |
1152 | |
1153 *ll = ctx->in; | |
1154 ll = &ctx->in->next; | |
1155 | |
1156 ctx->in = ctx->in->next; | |
1157 } | |
1158 | |
1159 if (ctx->out) { | |
1160 /* queued control frames */ | |
1161 | |
1162 *ll = ctx->out; | |
1163 | |
1164 for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) { | |
1165 ll = &cl->next; | |
1166 } | |
1167 | |
1168 ctx->out = NULL; | |
1169 } | |
1170 | |
1171 f = NULL; | |
1172 last = 0; | |
1173 | |
1174 limit = ngx_max(0, ctx->send_window); | |
1175 | |
1176 if (limit > ctx->connection->send_window) { | |
1177 limit = ctx->connection->send_window; | |
1178 } | |
1179 | |
1180 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1181 "grpc output limit: %uz w:%z:%uz", | |
1182 limit, ctx->send_window, ctx->connection->send_window); | |
1183 | |
1184 #if (NGX_SUPPRESS_WARN) | |
1185 file_pos = 0; | |
1186 pos = NULL; | |
1187 cl = NULL; | |
1188 #endif | |
1189 | |
1190 in = ctx->in; | |
1191 | |
1192 while (in && limit > 0) { | |
1193 | |
1194 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
1195 "grpc output in l:%d f:%d %p, pos %p, size: %z " | |
1196 "file: %O, size: %O", | |
1197 in->buf->last_buf, | |
1198 in->buf->in_file, | |
1199 in->buf->start, in->buf->pos, | |
1200 in->buf->last - in->buf->pos, | |
1201 in->buf->file_pos, | |
1202 in->buf->file_last - in->buf->file_pos); | |
1203 | |
1204 if (ngx_buf_special(in->buf)) { | |
1205 goto next; | |
1206 } | |
1207 | |
1208 if (in->buf->in_file) { | |
1209 file_pos = in->buf->file_pos; | |
1210 | |
1211 } else { | |
1212 pos = in->buf->pos; | |
1213 } | |
1214 | |
1215 next = 0; | |
1216 | |
1217 do { | |
1218 | |
1219 cl = ngx_http_grpc_get_buf(r, ctx); | |
1220 if (cl == NULL) { | |
1221 return NGX_ERROR; | |
1222 } | |
1223 | |
1224 b = cl->buf; | |
1225 | |
1226 f = (ngx_http_grpc_frame_t *) b->last; | |
1227 b->last += sizeof(ngx_http_grpc_frame_t); | |
1228 | |
1229 *ll = cl; | |
1230 ll = &cl->next; | |
1231 | |
1232 cl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
1233 if (cl == NULL) { | |
1234 return NGX_ERROR; | |
1235 } | |
1236 | |
1237 b = cl->buf; | |
1238 start = b->start; | |
1239 | |
1240 ngx_memcpy(b, in->buf, sizeof(ngx_buf_t)); | |
1241 | |
1242 /* | |
1243 * restore b->start to preserve memory allocated in the buffer, | |
1244 * to reuse it later for headers and control frames | |
1245 */ | |
1246 | |
1247 b->start = start; | |
1248 | |
1249 if (in->buf->in_file) { | |
1250 b->file_pos = file_pos; | |
1251 file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit); | |
1252 | |
1253 if (file_pos >= in->buf->file_last) { | |
1254 file_pos = in->buf->file_last; | |
1255 next = 1; | |
1256 } | |
1257 | |
1258 b->file_last = file_pos; | |
1259 len = (ngx_uint_t) (file_pos - b->file_pos); | |
1260 | |
1261 } else { | |
1262 b->pos = pos; | |
1263 pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit); | |
1264 | |
1265 if (pos >= in->buf->last) { | |
1266 pos = in->buf->last; | |
1267 next = 1; | |
1268 } | |
1269 | |
1270 b->last = pos; | |
1271 len = (ngx_uint_t) (pos - b->pos); | |
1272 } | |
1273 | |
1274 b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter; | |
1275 b->shadow = in->buf; | |
1276 b->last_shadow = next; | |
1277 | |
1278 b->last_buf = 0; | |
1279 b->last_in_chain = 0; | |
1280 | |
1281 *ll = cl; | |
1282 ll = &cl->next; | |
1283 | |
1284 f->length_0 = (u_char) ((len >> 16) & 0xff); | |
1285 f->length_1 = (u_char) ((len >> 8) & 0xff); | |
1286 f->length_2 = (u_char) (len & 0xff); | |
1287 f->type = NGX_HTTP_V2_DATA_FRAME; | |
1288 f->flags = 0; | |
1289 f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); | |
1290 f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); | |
1291 f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); | |
1292 f->stream_id_3 = (u_char) (ctx->id & 0xff); | |
1293 | |
1294 limit -= len; | |
1295 ctx->send_window -= len; | |
1296 ctx->connection->send_window -= len; | |
1297 | |
1298 } while (!next && limit > 0); | |
1299 | |
1300 if (!next) { | |
1301 /* | |
1302 * if the buffer wasn't fully sent due to flow control limits, | |
1303 * preserve position for future use | |
1304 */ | |
1305 | |
1306 if (in->buf->in_file) { | |
1307 in->buf->file_pos = file_pos; | |
1308 | |
1309 } else { | |
1310 in->buf->pos = pos; | |
1311 } | |
1312 | |
1313 break; | |
1314 } | |
1315 | |
1316 next: | |
1317 | |
1318 if (in->buf->last_buf) { | |
1319 last = 1; | |
1320 } | |
1321 | |
1322 in = in->next; | |
1323 } | |
1324 | |
1325 ctx->in = in; | |
1326 | |
1327 if (last) { | |
1328 | |
1329 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1330 "grpc output last"); | |
1331 | |
1332 ctx->output_closed = 1; | |
1333 | |
1334 if (f) { | |
1335 f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; | |
1336 | |
1337 } else { | |
1338 cl = ngx_http_grpc_get_buf(r, ctx); | |
1339 if (cl == NULL) { | |
1340 return NGX_ERROR; | |
1341 } | |
1342 | |
1343 b = cl->buf; | |
1344 | |
1345 f = (ngx_http_grpc_frame_t *) b->last; | |
1346 b->last += sizeof(ngx_http_grpc_frame_t); | |
1347 | |
1348 f->length_0 = 0; | |
1349 f->length_1 = 0; | |
1350 f->length_2 = 0; | |
1351 f->type = NGX_HTTP_V2_DATA_FRAME; | |
1352 f->flags = NGX_HTTP_V2_END_STREAM_FLAG; | |
1353 f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); | |
1354 f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); | |
1355 f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); | |
1356 f->stream_id_3 = (u_char) (ctx->id & 0xff); | |
1357 | |
1358 *ll = cl; | |
1359 ll = &cl->next; | |
1360 } | |
1361 | |
1362 cl->buf->last_buf = 1; | |
1363 } | |
1364 | |
1365 *ll = NULL; | |
1366 | |
1367 #if (NGX_DEBUG) | |
1368 | |
1369 for (cl = out; cl; cl = cl->next) { | |
1370 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
1371 "grpc output out l:%d f:%d %p, pos %p, size: %z " | |
1372 "file: %O, size: %O", | |
1373 cl->buf->last_buf, | |
1374 cl->buf->in_file, | |
1375 cl->buf->start, cl->buf->pos, | |
1376 cl->buf->last - cl->buf->pos, | |
1377 cl->buf->file_pos, | |
1378 cl->buf->file_last - cl->buf->file_pos); | |
1379 } | |
1380 | |
1381 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1382 "grpc output limit: %uz w:%z:%uz", | |
1383 limit, ctx->send_window, ctx->connection->send_window); | |
1384 | |
1385 #endif | |
1386 | |
1387 rc = ngx_chain_writer(&r->upstream->writer, out); | |
1388 | |
1389 ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, | |
1390 (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter); | |
1391 | |
1392 for (cl = ctx->free; cl; cl = cl->next) { | |
1393 | |
1394 /* mark original buffers as sent */ | |
1395 | |
1396 if (cl->buf->shadow) { | |
1397 if (cl->buf->last_shadow) { | |
1398 b = cl->buf->shadow; | |
1399 b->pos = b->last; | |
1400 } | |
1401 | |
1402 cl->buf->shadow = NULL; | |
1403 } | |
1404 } | |
1405 | |
1406 if (rc == NGX_OK && ctx->in) { | |
1407 rc = NGX_AGAIN; | |
1408 } | |
1409 | |
1410 return rc; | |
1411 } | |
1412 | |
1413 | |
1414 static ngx_int_t | |
1415 ngx_http_grpc_process_header(ngx_http_request_t *r) | |
1416 { | |
1417 ngx_str_t *status_line; | |
1418 ngx_int_t rc, status; | |
1419 ngx_buf_t *b; | |
1420 ngx_table_elt_t *h; | |
1421 ngx_http_upstream_t *u; | |
1422 ngx_http_grpc_ctx_t *ctx; | |
1423 ngx_http_upstream_header_t *hh; | |
1424 ngx_http_upstream_main_conf_t *umcf; | |
1425 | |
1426 u = r->upstream; | |
1427 b = &u->buffer; | |
1428 | |
1429 #if (NGX_DEBUG) | |
1430 if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { | |
1431 u_char buf[512]; | |
1432 size_t n, m; | |
1433 | |
1434 n = ngx_min(b->last - b->pos, 256); | |
1435 m = ngx_hex_dump(buf, b->pos, n) - buf; | |
1436 | |
1437 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1438 "grpc response: %*s%s, len: %uz", | |
1439 m, buf, b->last - b->pos > 256 ? "..." : "", | |
1440 b->last - b->pos); | |
1441 } | |
1442 #endif | |
1443 | |
1444 ctx = ngx_http_grpc_get_ctx(r); | |
1445 | |
1446 if (ctx == NULL) { | |
1447 return NGX_ERROR; | |
1448 } | |
1449 | |
1450 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); | |
1451 | |
1452 for ( ;; ) { | |
1453 | |
1454 if (ctx->state < ngx_http_grpc_st_payload) { | |
1455 | |
1456 rc = ngx_http_grpc_parse_frame(r, ctx, b); | |
1457 | |
1458 if (rc == NGX_AGAIN) { | |
1459 | |
1460 /* | |
1461 * there can be a lot of window update frames, | |
1462 * so we reset buffer if it is empty and we haven't | |
1463 * started parsing headers yet | |
1464 */ | |
1465 | |
1466 if (!ctx->parsing_headers) { | |
1467 b->pos = b->start; | |
1468 b->last = b->pos; | |
1469 } | |
1470 | |
1471 return NGX_AGAIN; | |
1472 } | |
1473 | |
1474 if (rc == NGX_ERROR) { | |
1475 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1476 } | |
1477 | |
1478 /* | |
1479 * RFC 7540 says that implementations MUST discard frames | |
1480 * that have unknown or unsupported types. However, extension | |
1481 * frames that appear in the middle of a header block are | |
1482 * not permitted. Also, for obvious reasons CONTINUATION frames | |
1483 * cannot appear before headers, and DATA frames are not expected | |
1484 * to appear before all headers are parsed. | |
1485 */ | |
1486 | |
1487 if (ctx->type == NGX_HTTP_V2_DATA_FRAME | |
1488 || (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME | |
1489 && !ctx->parsing_headers) | |
1490 || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME | |
1491 && ctx->parsing_headers)) | |
1492 { | |
1493 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1494 "upstream sent unexpected http2 frame: %d", | |
1495 ctx->type); | |
1496 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1497 } | |
1498 | |
1499 if (ctx->stream_id && ctx->stream_id != ctx->id) { | |
1500 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1501 "upstream sent frame for unknown stream %ui", | |
1502 ctx->stream_id); | |
1503 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1504 } | |
1505 } | |
1506 | |
1507 /* frame payload */ | |
1508 | |
1509 if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) { | |
1510 | |
1511 rc = ngx_http_grpc_parse_rst_stream(r, ctx, b); | |
1512 | |
1513 if (rc == NGX_AGAIN) { | |
1514 return NGX_AGAIN; | |
1515 } | |
1516 | |
1517 if (rc == NGX_ERROR) { | |
1518 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1519 } | |
1520 | |
1521 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1522 "upstream rejected request with error %ui", | |
1523 ctx->error); | |
1524 | |
1525 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1526 } | |
1527 | |
1528 if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { | |
1529 | |
1530 rc = ngx_http_grpc_parse_goaway(r, ctx, b); | |
1531 | |
1532 if (rc == NGX_AGAIN) { | |
1533 return NGX_AGAIN; | |
1534 } | |
1535 | |
1536 if (rc == NGX_ERROR) { | |
1537 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1538 } | |
1539 | |
1540 /* | |
1541 * If stream_id is lower than one we use, our | |
1542 * request won't be processed and needs to be retried. | |
1543 * If stream_id is greater or equal to the one we use, | |
1544 * we can continue normally (except we can't use this | |
1545 * connection for additional requests). If there is | |
1546 * a real error, the connection will be closed. | |
1547 */ | |
1548 | |
1549 if (ctx->stream_id < ctx->id) { | |
1550 | |
1551 /* TODO: we can retry non-idempotent requests */ | |
1552 | |
1553 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1554 "upstream sent goaway with error %ui", | |
1555 ctx->error); | |
1556 | |
1557 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1558 } | |
1559 | |
1560 continue; | |
1561 } | |
1562 | |
1563 if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { | |
1564 | |
1565 rc = ngx_http_grpc_parse_window_update(r, ctx, b); | |
1566 | |
1567 if (rc == NGX_AGAIN) { | |
1568 return NGX_AGAIN; | |
1569 } | |
1570 | |
1571 if (rc == NGX_ERROR) { | |
1572 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1573 } | |
1574 | |
1575 if (ctx->in) { | |
1576 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
1577 } | |
1578 | |
1579 continue; | |
1580 } | |
1581 | |
1582 if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) { | |
1583 | |
1584 rc = ngx_http_grpc_parse_settings(r, ctx, b); | |
1585 | |
1586 if (rc == NGX_AGAIN) { | |
1587 return NGX_AGAIN; | |
1588 } | |
1589 | |
1590 if (rc == NGX_ERROR) { | |
1591 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1592 } | |
1593 | |
1594 if (ctx->in) { | |
1595 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
1596 } | |
1597 | |
1598 continue; | |
1599 } | |
1600 | |
1601 if (ctx->type == NGX_HTTP_V2_PING_FRAME) { | |
1602 | |
1603 rc = ngx_http_grpc_parse_ping(r, ctx, b); | |
1604 | |
1605 if (rc == NGX_AGAIN) { | |
1606 return NGX_AGAIN; | |
1607 } | |
1608 | |
1609 if (rc == NGX_ERROR) { | |
1610 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1611 } | |
1612 | |
1613 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
1614 continue; | |
1615 } | |
1616 | |
1617 if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) { | |
1618 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1619 "upstream sent unexpected push promise frame"); | |
1620 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1621 } | |
1622 | |
1623 if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME | |
1624 && ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME) | |
1625 { | |
1626 /* priority, unknown frames */ | |
1627 | |
1628 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
1629 ctx->rest -= b->last - b->pos; | |
1630 b->pos = b->last; | |
1631 return NGX_AGAIN; | |
1632 } | |
1633 | |
1634 b->pos += ctx->rest; | |
1635 ctx->rest = 0; | |
1636 ctx->state = ngx_http_grpc_st_start; | |
1637 | |
1638 continue; | |
1639 } | |
1640 | |
1641 /* headers */ | |
1642 | |
1643 for ( ;; ) { | |
1644 | |
1645 rc = ngx_http_grpc_parse_header(r, ctx, b); | |
1646 | |
1647 if (rc == NGX_AGAIN) { | |
1648 break; | |
1649 } | |
1650 | |
1651 if (rc == NGX_OK) { | |
1652 | |
1653 /* a header line has been parsed successfully */ | |
1654 | |
1655 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1656 "grpc header: \"%V: %V\"", | |
1657 &ctx->name, &ctx->value); | |
1658 | |
1659 if (ctx->name.len && ctx->name.data[0] == ':') { | |
1660 | |
1661 if (ctx->name.len != sizeof(":status") - 1 | |
1662 || ngx_strncmp(ctx->name.data, ":status", | |
1663 sizeof(":status") - 1) | |
1664 != 0) | |
1665 { | |
1666 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1667 "upstream sent invalid header \"%V: %V\"", | |
1668 &ctx->name, &ctx->value); | |
1669 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1670 } | |
1671 | |
1672 if (ctx->status) { | |
1673 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1674 "upstream sent duplicate :status header"); | |
1675 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1676 } | |
1677 | |
1678 status_line = &ctx->value; | |
1679 | |
1680 if (status_line->len != 3) { | |
1681 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1682 "upstream sent invalid :status \"%V\"", | |
1683 status_line); | |
1684 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1685 } | |
1686 | |
1687 status = ngx_atoi(status_line->data, 3); | |
1688 | |
1689 if (status == NGX_ERROR) { | |
1690 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1691 "upstream sent invalid :status \"%V\"", | |
1692 status_line); | |
1693 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1694 } | |
1695 | |
1696 if (status < NGX_HTTP_OK) { | |
1697 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1698 "upstream sent unexpected :status \"%V\"", | |
1699 status_line); | |
1700 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1701 } | |
1702 | |
1703 u->headers_in.status_n = status; | |
1704 | |
1705 if (u->state && u->state->status == 0) { | |
1706 u->state->status = status; | |
1707 } | |
1708 | |
1709 ctx->status = 1; | |
1710 | |
1711 continue; | |
1712 | |
1713 } else if (!ctx->status) { | |
1714 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1715 "upstream sent no :status header"); | |
1716 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1717 } | |
1718 | |
1719 h = ngx_list_push(&u->headers_in.headers); | |
1720 if (h == NULL) { | |
1721 return NGX_ERROR; | |
1722 } | |
1723 | |
1724 h->key = ctx->name; | |
1725 h->value = ctx->value; | |
1726 h->lowcase_key = h->key.data; | |
1727 h->hash = ngx_hash_key(h->key.data, h->key.len); | |
1728 | |
1729 hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, | |
1730 h->lowcase_key, h->key.len); | |
1731 | |
1732 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { | |
1733 return NGX_ERROR; | |
1734 } | |
1735 | |
1736 continue; | |
1737 } | |
1738 | |
1739 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { | |
1740 | |
1741 /* a whole header has been parsed successfully */ | |
1742 | |
1743 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1744 "grpc header done"); | |
1745 | |
7235
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1746 if (ctx->end_stream) { |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1747 u->headers_in.content_length_n = 0; |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1748 |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1749 if (ctx->in == NULL |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1750 && ctx->out == NULL |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1751 && ctx->output_closed |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1752 && b->last == b->pos) |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1753 { |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1754 u->keepalive = 1; |
c2a0a838c40f
gRPC: special handling of "trailer only" responses.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7234
diff
changeset
|
1755 } |
7233 | 1756 } |
1757 | |
1758 return NGX_OK; | |
1759 } | |
1760 | |
1761 /* there was error while a header line parsing */ | |
1762 | |
1763 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1764 "upstream sent invalid header"); | |
1765 | |
1766 return NGX_HTTP_UPSTREAM_INVALID_HEADER; | |
1767 } | |
1768 | |
1769 /* rc == NGX_AGAIN */ | |
1770 | |
1771 if (ctx->rest == 0) { | |
1772 ctx->state = ngx_http_grpc_st_start; | |
1773 continue; | |
1774 } | |
1775 | |
1776 return NGX_AGAIN; | |
1777 } | |
1778 } | |
1779 | |
1780 | |
1781 static ngx_int_t | |
1782 ngx_http_grpc_filter_init(void *data) | |
1783 { | |
1784 ngx_http_grpc_ctx_t *ctx = data; | |
1785 | |
1786 ngx_http_request_t *r; | |
1787 ngx_http_upstream_t *u; | |
1788 | |
1789 r = ctx->request; | |
1790 u = r->upstream; | |
1791 | |
1792 u->length = 1; | |
1793 | |
1794 if (ctx->end_stream) { | |
1795 u->length = 0; | |
1796 } | |
1797 | |
1798 return NGX_OK; | |
1799 } | |
1800 | |
1801 | |
1802 static ngx_int_t | |
1803 ngx_http_grpc_filter(void *data, ssize_t bytes) | |
1804 { | |
1805 ngx_http_grpc_ctx_t *ctx = data; | |
1806 | |
1807 ngx_int_t rc; | |
1808 ngx_buf_t *b, *buf; | |
1809 ngx_chain_t *cl, **ll; | |
1810 ngx_table_elt_t *h; | |
1811 ngx_http_request_t *r; | |
1812 ngx_http_upstream_t *u; | |
1813 | |
1814 r = ctx->request; | |
1815 u = r->upstream; | |
1816 b = &u->buffer; | |
1817 | |
1818 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1819 "grpc filter bytes:%z", bytes); | |
1820 | |
1821 b->pos = b->last; | |
1822 b->last += bytes; | |
1823 | |
1824 for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { | |
1825 ll = &cl->next; | |
1826 } | |
1827 | |
1828 for ( ;; ) { | |
1829 | |
1830 if (ctx->state < ngx_http_grpc_st_payload) { | |
1831 | |
1832 rc = ngx_http_grpc_parse_frame(r, ctx, b); | |
1833 | |
1834 if (rc == NGX_AGAIN) { | |
1835 return NGX_AGAIN; | |
1836 } | |
1837 | |
1838 if (rc == NGX_ERROR) { | |
1839 return NGX_ERROR; | |
1840 } | |
1841 | |
1842 if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME | |
1843 && !ctx->parsing_headers) | |
1844 || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME | |
1845 && ctx->parsing_headers)) | |
1846 { | |
1847 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1848 "upstream sent unexpected http2 frame: %d", | |
1849 ctx->type); | |
1850 return NGX_ERROR; | |
1851 } | |
1852 | |
1853 if (ctx->type == NGX_HTTP_V2_DATA_FRAME) { | |
1854 | |
1855 if (ctx->stream_id != ctx->id) { | |
1856 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1857 "upstream sent data frame " | |
1858 "for unknown stream %ui", | |
1859 ctx->stream_id); | |
1860 return NGX_ERROR; | |
1861 } | |
1862 | |
1863 if (ctx->rest > ctx->recv_window) { | |
1864 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1865 "upstream violated stream flow control, " | |
1866 "received %uz data frame with window %uz", | |
1867 ctx->rest, ctx->recv_window); | |
1868 return NGX_ERROR; | |
1869 } | |
1870 | |
1871 if (ctx->rest > ctx->connection->recv_window) { | |
1872 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1873 "upstream violated connection flow control, " | |
1874 "received %uz data frame with window %uz", | |
1875 ctx->rest, ctx->connection->recv_window); | |
1876 return NGX_ERROR; | |
1877 } | |
1878 | |
1879 ctx->recv_window -= ctx->rest; | |
1880 ctx->connection->recv_window -= ctx->rest; | |
1881 | |
1882 if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4 | |
1883 || ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) | |
1884 { | |
1885 if (ngx_http_grpc_send_window_update(r, ctx) != NGX_OK) { | |
1886 return NGX_ERROR; | |
1887 } | |
1888 | |
1889 ngx_post_event(u->peer.connection->write, | |
1890 &ngx_posted_events); | |
1891 } | |
1892 } | |
1893 | |
1894 if (ctx->stream_id && ctx->stream_id != ctx->id) { | |
1895 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1896 "upstream sent frame for unknown stream %ui", | |
1897 ctx->stream_id); | |
1898 return NGX_ERROR; | |
1899 } | |
1900 | |
1901 ctx->padding = 0; | |
1902 } | |
1903 | |
1904 if (ctx->state == ngx_http_grpc_st_padding) { | |
1905 | |
1906 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
1907 ctx->rest -= b->last - b->pos; | |
1908 b->pos = b->last; | |
1909 return NGX_AGAIN; | |
1910 } | |
1911 | |
1912 b->pos += ctx->rest; | |
1913 ctx->rest = 0; | |
1914 ctx->state = ngx_http_grpc_st_start; | |
1915 | |
1916 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { | |
1917 u->length = 0; | |
1918 | |
1919 if (ctx->in == NULL | |
1920 && ctx->out == NULL | |
1921 && ctx->output_closed | |
1922 && b->last == b->pos) | |
1923 { | |
1924 u->keepalive = 1; | |
1925 } | |
1926 | |
1927 break; | |
1928 } | |
1929 | |
1930 continue; | |
1931 } | |
1932 | |
1933 /* frame payload */ | |
1934 | |
1935 if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) { | |
1936 | |
1937 rc = ngx_http_grpc_parse_rst_stream(r, ctx, b); | |
1938 | |
1939 if (rc == NGX_AGAIN) { | |
1940 return NGX_AGAIN; | |
1941 } | |
1942 | |
1943 if (rc == NGX_ERROR) { | |
1944 return NGX_ERROR; | |
1945 } | |
1946 | |
1947 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1948 "upstream rejected request with error %ui", | |
1949 ctx->error); | |
1950 | |
1951 return NGX_ERROR; | |
1952 } | |
1953 | |
1954 if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { | |
1955 | |
1956 rc = ngx_http_grpc_parse_goaway(r, ctx, b); | |
1957 | |
1958 if (rc == NGX_AGAIN) { | |
1959 return NGX_AGAIN; | |
1960 } | |
1961 | |
1962 if (rc == NGX_ERROR) { | |
1963 return NGX_ERROR; | |
1964 } | |
1965 | |
1966 /* | |
1967 * If stream_id is lower than one we use, our | |
1968 * request won't be processed and needs to be retried. | |
1969 * If stream_id is greater or equal to the one we use, | |
1970 * we can continue normally (except we can't use this | |
1971 * connection for additional requests). If there is | |
1972 * a real error, the connection will be closed. | |
1973 */ | |
1974 | |
1975 if (ctx->stream_id < ctx->id) { | |
1976 | |
1977 /* TODO: we can retry non-idempotent requests */ | |
1978 | |
1979 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1980 "upstream sent goaway with error %ui", | |
1981 ctx->error); | |
1982 | |
1983 return NGX_ERROR; | |
1984 } | |
1985 | |
1986 continue; | |
1987 } | |
1988 | |
1989 if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { | |
1990 | |
1991 rc = ngx_http_grpc_parse_window_update(r, ctx, b); | |
1992 | |
1993 if (rc == NGX_AGAIN) { | |
1994 return NGX_AGAIN; | |
1995 } | |
1996 | |
1997 if (rc == NGX_ERROR) { | |
1998 return NGX_ERROR; | |
1999 } | |
2000 | |
2001 if (ctx->in) { | |
2002 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
2003 } | |
2004 | |
2005 continue; | |
2006 } | |
2007 | |
2008 if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) { | |
2009 | |
2010 rc = ngx_http_grpc_parse_settings(r, ctx, b); | |
2011 | |
2012 if (rc == NGX_AGAIN) { | |
2013 return NGX_AGAIN; | |
2014 } | |
2015 | |
2016 if (rc == NGX_ERROR) { | |
2017 return NGX_ERROR; | |
2018 } | |
2019 | |
2020 if (ctx->in) { | |
2021 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
2022 } | |
2023 | |
2024 continue; | |
2025 } | |
2026 | |
2027 if (ctx->type == NGX_HTTP_V2_PING_FRAME) { | |
2028 | |
2029 rc = ngx_http_grpc_parse_ping(r, ctx, b); | |
2030 | |
2031 if (rc == NGX_AGAIN) { | |
2032 return NGX_AGAIN; | |
2033 } | |
2034 | |
2035 if (rc == NGX_ERROR) { | |
2036 return NGX_ERROR; | |
2037 } | |
2038 | |
2039 ngx_post_event(u->peer.connection->write, &ngx_posted_events); | |
2040 continue; | |
2041 } | |
2042 | |
2043 if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) { | |
2044 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2045 "upstream sent unexpected push promise frame"); | |
2046 return NGX_ERROR; | |
2047 } | |
2048 | |
2049 if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME | |
2050 || ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) | |
2051 { | |
2052 for ( ;; ) { | |
2053 | |
2054 rc = ngx_http_grpc_parse_header(r, ctx, b); | |
2055 | |
2056 if (rc == NGX_AGAIN) { | |
2057 break; | |
2058 } | |
2059 | |
2060 if (rc == NGX_OK) { | |
2061 | |
2062 /* a header line has been parsed successfully */ | |
2063 | |
2064 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2065 "grpc trailer: \"%V: %V\"", | |
2066 &ctx->name, &ctx->value); | |
2067 | |
2068 if (ctx->name.len && ctx->name.data[0] == ':') { | |
2069 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2070 "upstream sent invalid " | |
2071 "trailer \"%V: %V\"", | |
2072 &ctx->name, &ctx->value); | |
2073 return NGX_ERROR; | |
2074 } | |
2075 | |
2076 h = ngx_list_push(&u->headers_in.trailers); | |
2077 if (h == NULL) { | |
2078 return NGX_ERROR; | |
2079 } | |
2080 | |
2081 h->key = ctx->name; | |
2082 h->value = ctx->value; | |
2083 h->lowcase_key = h->key.data; | |
2084 h->hash = ngx_hash_key(h->key.data, h->key.len); | |
2085 | |
2086 continue; | |
2087 } | |
2088 | |
2089 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { | |
2090 | |
2091 /* a whole header has been parsed successfully */ | |
2092 | |
2093 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2094 "grpc trailer done"); | |
2095 | |
2096 if (ctx->end_stream) { | |
2097 u->length = 0; | |
2098 | |
2099 if (ctx->in == NULL | |
2100 && ctx->out == NULL | |
2101 && ctx->output_closed | |
2102 && b->last == b->pos) | |
2103 { | |
2104 u->keepalive = 1; | |
2105 } | |
2106 | |
2107 return NGX_OK; | |
2108 } | |
2109 | |
2110 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2111 "upstream sent trailer without " | |
2112 "end stream flag"); | |
2113 return NGX_ERROR; | |
2114 } | |
2115 | |
2116 /* there was error while a header line parsing */ | |
2117 | |
2118 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2119 "upstream sent invalid trailer"); | |
2120 | |
2121 return NGX_ERROR; | |
2122 } | |
2123 | |
2124 /* rc == NGX_AGAIN */ | |
2125 | |
2126 if (ctx->rest == 0) { | |
2127 ctx->state = ngx_http_grpc_st_start; | |
2128 continue; | |
2129 } | |
2130 | |
2131 return NGX_AGAIN; | |
2132 } | |
2133 | |
2134 if (ctx->type != NGX_HTTP_V2_DATA_FRAME) { | |
2135 | |
2136 /* priority, unknown frames */ | |
2137 | |
2138 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
2139 ctx->rest -= b->last - b->pos; | |
2140 b->pos = b->last; | |
2141 return NGX_AGAIN; | |
2142 } | |
2143 | |
2144 b->pos += ctx->rest; | |
2145 ctx->rest = 0; | |
2146 ctx->state = ngx_http_grpc_st_start; | |
2147 | |
2148 continue; | |
2149 } | |
2150 | |
2151 /* | |
2152 * data frame: | |
2153 * | |
2154 * +---------------+ | |
2155 * |Pad Length? (8)| | |
2156 * +---------------+-----------------------------------------------+ | |
2157 * | Data (*) ... | |
2158 * +---------------------------------------------------------------+ | |
2159 * | Padding (*) ... | |
2160 * +---------------------------------------------------------------+ | |
2161 */ | |
2162 | |
2163 if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) { | |
2164 | |
2165 if (ctx->rest == 0) { | |
2166 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2167 "upstream sent too short http2 frame"); | |
2168 return NGX_ERROR; | |
2169 } | |
2170 | |
2171 if (b->pos == b->last) { | |
2172 return NGX_AGAIN; | |
2173 } | |
2174 | |
2175 ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG; | |
2176 ctx->padding = *b->pos++; | |
2177 ctx->rest -= 1; | |
2178 | |
2179 if (ctx->padding > ctx->rest) { | |
2180 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2181 "upstream sent http2 frame with too long " | |
2182 "padding: %d in frame %uz", | |
2183 ctx->padding, ctx->rest); | |
2184 return NGX_ERROR; | |
2185 } | |
2186 | |
2187 continue; | |
2188 } | |
2189 | |
2190 if (ctx->rest == ctx->padding) { | |
2191 goto done; | |
2192 } | |
2193 | |
2194 if (b->pos == b->last) { | |
2195 return NGX_AGAIN; | |
2196 } | |
2197 | |
2198 cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); | |
2199 if (cl == NULL) { | |
2200 return NGX_ERROR; | |
2201 } | |
2202 | |
2203 *ll = cl; | |
2204 ll = &cl->next; | |
2205 | |
2206 buf = cl->buf; | |
2207 | |
2208 buf->flush = 1; | |
2209 buf->memory = 1; | |
2210 | |
2211 buf->pos = b->pos; | |
2212 buf->tag = u->output.tag; | |
2213 | |
2214 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2215 "grpc output buf %p", buf->pos); | |
2216 | |
2217 if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) { | |
2218 | |
2219 ctx->rest -= b->last - b->pos; | |
2220 b->pos = b->last; | |
2221 buf->last = b->pos; | |
2222 | |
2223 return NGX_AGAIN; | |
2224 } | |
2225 | |
2226 b->pos += ctx->rest - ctx->padding; | |
2227 buf->last = b->pos; | |
2228 ctx->rest = ctx->padding; | |
2229 | |
2230 done: | |
2231 | |
2232 if (ctx->padding) { | |
2233 ctx->state = ngx_http_grpc_st_padding; | |
2234 continue; | |
2235 } | |
2236 | |
2237 ctx->state = ngx_http_grpc_st_start; | |
2238 | |
2239 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { | |
2240 u->length = 0; | |
2241 | |
2242 if (ctx->in == NULL | |
2243 && ctx->out == NULL | |
2244 && ctx->output_closed | |
2245 && b->last == b->pos) | |
2246 { | |
2247 u->keepalive = 1; | |
2248 } | |
2249 | |
2250 break; | |
2251 } | |
2252 } | |
2253 | |
2254 return NGX_OK; | |
2255 } | |
2256 | |
2257 | |
2258 static ngx_int_t | |
2259 ngx_http_grpc_parse_frame(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
2260 ngx_buf_t *b) | |
2261 { | |
2262 u_char ch, *p; | |
2263 ngx_http_grpc_state_e state; | |
2264 | |
2265 state = ctx->state; | |
2266 | |
2267 for (p = b->pos; p < b->last; p++) { | |
2268 ch = *p; | |
2269 | |
2270 #if 0 | |
2271 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2272 "grpc frame byte: %02Xd, s:%d", ch, state); | |
2273 #endif | |
2274 | |
2275 switch (state) { | |
2276 | |
2277 case ngx_http_grpc_st_start: | |
2278 ctx->rest = ch << 16; | |
2279 state = ngx_http_grpc_st_length_2; | |
2280 break; | |
2281 | |
2282 case ngx_http_grpc_st_length_2: | |
2283 ctx->rest |= ch << 8; | |
2284 state = ngx_http_grpc_st_length_3; | |
2285 break; | |
2286 | |
2287 case ngx_http_grpc_st_length_3: | |
2288 ctx->rest |= ch; | |
2289 | |
2290 if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { | |
2291 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2292 "upstream sent too large http2 frame: %uz", | |
2293 ctx->rest); | |
2294 return NGX_ERROR; | |
2295 } | |
2296 | |
2297 state = ngx_http_grpc_st_type; | |
2298 break; | |
2299 | |
2300 case ngx_http_grpc_st_type: | |
2301 ctx->type = ch; | |
2302 state = ngx_http_grpc_st_flags; | |
2303 break; | |
2304 | |
2305 case ngx_http_grpc_st_flags: | |
2306 ctx->flags = ch; | |
2307 state = ngx_http_grpc_st_stream_id; | |
2308 break; | |
2309 | |
2310 case ngx_http_grpc_st_stream_id: | |
2311 ctx->stream_id = (ch & 0x7f) << 24; | |
2312 state = ngx_http_grpc_st_stream_id_2; | |
2313 break; | |
2314 | |
2315 case ngx_http_grpc_st_stream_id_2: | |
2316 ctx->stream_id |= ch << 16; | |
2317 state = ngx_http_grpc_st_stream_id_3; | |
2318 break; | |
2319 | |
2320 case ngx_http_grpc_st_stream_id_3: | |
2321 ctx->stream_id |= ch << 8; | |
2322 state = ngx_http_grpc_st_stream_id_4; | |
2323 break; | |
2324 | |
2325 case ngx_http_grpc_st_stream_id_4: | |
2326 ctx->stream_id |= ch; | |
2327 | |
2328 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2329 "grpc frame: %d, len: %uz, f:%d, i:%ui", | |
2330 ctx->type, ctx->rest, ctx->flags, ctx->stream_id); | |
2331 | |
2332 b->pos = p + 1; | |
2333 | |
2334 ctx->state = ngx_http_grpc_st_payload; | |
2335 ctx->frame_state = 0; | |
2336 | |
2337 return NGX_OK; | |
2338 | |
2339 /* suppress warning */ | |
2340 case ngx_http_grpc_st_payload: | |
2341 case ngx_http_grpc_st_padding: | |
2342 break; | |
2343 } | |
2344 } | |
2345 | |
2346 b->pos = p; | |
2347 ctx->state = state; | |
2348 | |
2349 return NGX_AGAIN; | |
2350 } | |
2351 | |
2352 | |
2353 static ngx_int_t | |
2354 ngx_http_grpc_parse_header(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
2355 ngx_buf_t *b) | |
2356 { | |
2357 u_char ch, *p, *last; | |
2358 size_t min; | |
2359 ngx_int_t rc; | |
2360 enum { | |
2361 sw_start = 0, | |
2362 sw_padding_length, | |
2363 sw_dependency, | |
2364 sw_dependency_2, | |
2365 sw_dependency_3, | |
2366 sw_dependency_4, | |
2367 sw_weight, | |
2368 sw_fragment, | |
2369 sw_padding | |
2370 } state; | |
2371 | |
2372 state = ctx->frame_state; | |
2373 | |
2374 if (state == sw_start) { | |
2375 | |
2376 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2377 "grpc parse header: start"); | |
2378 | |
2379 if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) { | |
2380 ctx->parsing_headers = 1; | |
2381 ctx->fragment_state = 0; | |
2382 | |
2383 min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0) | |
2384 + (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0); | |
2385 | |
2386 if (ctx->rest < min) { | |
2387 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2388 "upstream sent headers frame " | |
2389 "with invalid length: %uz", | |
2390 ctx->rest); | |
2391 return NGX_ERROR; | |
2392 } | |
2393 | |
2394 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { | |
2395 ctx->end_stream = 1; | |
2396 } | |
2397 | |
2398 if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) { | |
2399 state = sw_padding_length; | |
2400 | |
2401 } else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) { | |
2402 state = sw_dependency; | |
2403 | |
2404 } else { | |
2405 state = sw_fragment; | |
2406 } | |
2407 | |
2408 } else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) { | |
2409 state = sw_fragment; | |
2410 } | |
2411 | |
2412 ctx->padding = 0; | |
7242
25a4353633a0
gRPC: fixed missing state save in frame header parsing.
Sergey Kandaurov <pluknet@nginx.com>
parents:
7240
diff
changeset
|
2413 ctx->frame_state = state; |
7233 | 2414 } |
2415 | |
2416 if (state < sw_fragment) { | |
2417 | |
2418 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
2419 last = b->last; | |
2420 | |
2421 } else { | |
2422 last = b->pos + ctx->rest; | |
2423 } | |
2424 | |
2425 for (p = b->pos; p < last; p++) { | |
2426 ch = *p; | |
2427 | |
2428 #if 0 | |
2429 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2430 "grpc header byte: %02Xd s:%d", ch, state); | |
2431 #endif | |
2432 | |
2433 /* | |
2434 * headers frame: | |
2435 * | |
2436 * +---------------+ | |
2437 * |Pad Length? (8)| | |
2438 * +-+-------------+----------------------------------------------+ | |
2439 * |E| Stream Dependency? (31) | | |
2440 * +-+-------------+----------------------------------------------+ | |
2441 * | Weight? (8) | | |
2442 * +-+-------------+----------------------------------------------+ | |
2443 * | Header Block Fragment (*) ... | |
2444 * +--------------------------------------------------------------+ | |
2445 * | Padding (*) ... | |
2446 * +--------------------------------------------------------------+ | |
2447 */ | |
2448 | |
2449 switch (state) { | |
2450 | |
2451 case sw_padding_length: | |
2452 | |
2453 ctx->padding = ch; | |
2454 | |
2455 if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) { | |
2456 state = sw_dependency; | |
2457 break; | |
2458 } | |
2459 | |
2460 goto fragment; | |
2461 | |
2462 case sw_dependency: | |
2463 state = sw_dependency_2; | |
2464 break; | |
2465 | |
2466 case sw_dependency_2: | |
2467 state = sw_dependency_3; | |
2468 break; | |
2469 | |
2470 case sw_dependency_3: | |
2471 state = sw_dependency_4; | |
2472 break; | |
2473 | |
2474 case sw_dependency_4: | |
2475 state = sw_weight; | |
2476 break; | |
2477 | |
2478 case sw_weight: | |
2479 goto fragment; | |
2480 | |
2481 /* suppress warning */ | |
2482 case sw_start: | |
2483 case sw_fragment: | |
2484 case sw_padding: | |
2485 break; | |
2486 } | |
2487 } | |
2488 | |
2489 ctx->rest -= p - b->pos; | |
2490 b->pos = p; | |
2491 | |
2492 ctx->frame_state = state; | |
2493 return NGX_AGAIN; | |
2494 | |
2495 fragment: | |
2496 | |
2497 p++; | |
2498 ctx->rest -= p - b->pos; | |
2499 b->pos = p; | |
2500 | |
2501 if (ctx->padding > ctx->rest) { | |
2502 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2503 "upstream sent http2 frame with too long " | |
2504 "padding: %d in frame %uz", | |
2505 ctx->padding, ctx->rest); | |
2506 return NGX_ERROR; | |
2507 } | |
2508 | |
2509 state = sw_fragment; | |
2510 ctx->frame_state = state; | |
2511 } | |
2512 | |
2513 if (state == sw_fragment) { | |
2514 | |
2515 rc = ngx_http_grpc_parse_fragment(r, ctx, b); | |
2516 | |
2517 if (rc == NGX_AGAIN) { | |
2518 return NGX_AGAIN; | |
2519 } | |
2520 | |
2521 if (rc == NGX_ERROR) { | |
2522 return NGX_ERROR; | |
2523 } | |
2524 | |
2525 if (rc == NGX_OK) { | |
2526 return NGX_OK; | |
2527 } | |
2528 | |
2529 /* rc == NGX_DONE */ | |
2530 | |
2531 state = sw_padding; | |
2532 ctx->frame_state = state; | |
2533 } | |
2534 | |
2535 if (state == sw_padding) { | |
2536 | |
2537 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
2538 | |
2539 ctx->rest -= b->last - b->pos; | |
2540 b->pos = b->last; | |
2541 | |
2542 return NGX_AGAIN; | |
2543 } | |
2544 | |
2545 b->pos += ctx->rest; | |
2546 ctx->rest = 0; | |
2547 | |
2548 ctx->state = ngx_http_grpc_st_start; | |
2549 | |
2550 if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) { | |
2551 | |
2552 if (ctx->fragment_state) { | |
2553 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2554 "upstream sent truncated http2 header"); | |
2555 return NGX_ERROR; | |
2556 } | |
2557 | |
2558 ctx->parsing_headers = 0; | |
2559 | |
2560 return NGX_HTTP_PARSE_HEADER_DONE; | |
2561 } | |
2562 | |
2563 return NGX_AGAIN; | |
2564 } | |
2565 | |
2566 /* unreachable */ | |
2567 | |
2568 return NGX_ERROR; | |
2569 } | |
2570 | |
2571 | |
2572 static ngx_int_t | |
2573 ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
2574 ngx_buf_t *b) | |
2575 { | |
2576 u_char ch, *p, *last; | |
2577 size_t size; | |
2578 ngx_uint_t index, size_update; | |
2579 enum { | |
2580 sw_start = 0, | |
2581 sw_index, | |
2582 sw_name_length, | |
2583 sw_name_length_2, | |
2584 sw_name_length_3, | |
2585 sw_name_length_4, | |
2586 sw_name, | |
2587 sw_name_bytes, | |
2588 sw_value_length, | |
2589 sw_value_length_2, | |
2590 sw_value_length_3, | |
2591 sw_value_length_4, | |
2592 sw_value, | |
2593 sw_value_bytes | |
2594 } state; | |
2595 | |
2596 /* header block fragment */ | |
2597 | |
2598 #if 0 | |
2599 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2600 "grpc header fragment %p:%p rest:%uz", | |
2601 b->pos, b->last, ctx->rest); | |
2602 #endif | |
2603 | |
2604 if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) { | |
2605 last = b->last; | |
2606 | |
2607 } else { | |
2608 last = b->pos + ctx->rest - ctx->padding; | |
2609 } | |
2610 | |
2611 state = ctx->fragment_state; | |
2612 | |
2613 for (p = b->pos; p < last; p++) { | |
2614 ch = *p; | |
2615 | |
2616 #if 0 | |
2617 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2618 "grpc header byte: %02Xd s:%d", ch, state); | |
2619 #endif | |
2620 | |
2621 switch (state) { | |
2622 | |
2623 case sw_start: | |
2624 ctx->index = 0; | |
2625 | |
2626 if ((ch & 0x80) == 0x80) { | |
2627 /* | |
2628 * indexed header: | |
2629 * | |
2630 * 0 1 2 3 4 5 6 7 | |
2631 * +---+---+---+---+---+---+---+---+ | |
2632 * | 1 | Index (7+) | | |
2633 * +---+---------------------------+ | |
2634 */ | |
2635 | |
2636 index = ch & ~0x80; | |
2637 | |
2638 if (index == 0 || index > 61) { | |
2639 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2640 "upstream sent invalid http2 " | |
2641 "table index: %ui", index); | |
2642 return NGX_ERROR; | |
2643 } | |
2644 | |
2645 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2646 "grpc indexed header: %ui", index); | |
2647 | |
2648 ctx->index = index; | |
2649 ctx->literal = 0; | |
2650 | |
2651 goto done; | |
2652 | |
2653 } else if ((ch & 0xc0) == 0x40) { | |
2654 /* | |
2655 * literal header with incremental indexing: | |
2656 * | |
2657 * 0 1 2 3 4 5 6 7 | |
2658 * +---+---+---+---+---+---+---+---+ | |
2659 * | 0 | 1 | Index (6+) | | |
2660 * +---+---+-----------------------+ | |
2661 * | H | Value Length (7+) | | |
2662 * +---+---------------------------+ | |
2663 * | Value String (Length octets) | | |
2664 * +-------------------------------+ | |
2665 * | |
2666 * 0 1 2 3 4 5 6 7 | |
2667 * +---+---+---+---+---+---+---+---+ | |
2668 * | 0 | 1 | 0 | | |
2669 * +---+---+-----------------------+ | |
2670 * | H | Name Length (7+) | | |
2671 * +---+---------------------------+ | |
2672 * | Name String (Length octets) | | |
2673 * +---+---------------------------+ | |
2674 * | H | Value Length (7+) | | |
2675 * +---+---------------------------+ | |
2676 * | Value String (Length octets) | | |
2677 * +-------------------------------+ | |
2678 */ | |
2679 | |
2680 index = ch & ~0xc0; | |
2681 | |
2682 if (index > 61) { | |
2683 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2684 "upstream sent invalid http2 " | |
2685 "table index: %ui", index); | |
2686 return NGX_ERROR; | |
2687 } | |
2688 | |
2689 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2690 "grpc literal header: %ui", index); | |
2691 | |
2692 if (index == 0) { | |
2693 state = sw_name_length; | |
2694 break; | |
2695 } | |
2696 | |
2697 ctx->index = index; | |
2698 ctx->literal = 1; | |
2699 | |
2700 state = sw_value_length; | |
2701 break; | |
2702 | |
2703 } else if ((ch & 0xe0) == 0x20) { | |
2704 /* | |
2705 * dynamic table size update: | |
2706 * | |
2707 * 0 1 2 3 4 5 6 7 | |
2708 * +---+---+---+---+---+---+---+---+ | |
2709 * | 0 | 0 | 1 | Max size (5+) | | |
2710 * +---+---------------------------+ | |
2711 */ | |
2712 | |
2713 size_update = ch & ~0xe0; | |
2714 | |
2715 if (size_update > 0) { | |
2716 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2717 "upstream sent invalid http2 " | |
2718 "dynamic table size update: %ui", | |
2719 size_update); | |
2720 return NGX_ERROR; | |
2721 } | |
2722 | |
2723 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2724 "grpc table size update: %ui", size_update); | |
2725 | |
2726 break; | |
2727 | |
2728 } else if ((ch & 0xf0) == 0x10) { | |
2729 /* | |
2730 * literal header field never indexed: | |
2731 * | |
2732 * 0 1 2 3 4 5 6 7 | |
2733 * +---+---+---+---+---+---+---+---+ | |
2734 * | 0 | 0 | 0 | 1 | Index (4+) | | |
2735 * +---+---+-----------------------+ | |
2736 * | H | Value Length (7+) | | |
2737 * +---+---------------------------+ | |
2738 * | Value String (Length octets) | | |
2739 * +-------------------------------+ | |
2740 * | |
2741 * 0 1 2 3 4 5 6 7 | |
2742 * +---+---+---+---+---+---+---+---+ | |
2743 * | 0 | 0 | 0 | 1 | 0 | | |
2744 * +---+---+-----------------------+ | |
2745 * | H | Name Length (7+) | | |
2746 * +---+---------------------------+ | |
2747 * | Name String (Length octets) | | |
2748 * +---+---------------------------+ | |
2749 * | H | Value Length (7+) | | |
2750 * +---+---------------------------+ | |
2751 * | Value String (Length octets) | | |
2752 * +-------------------------------+ | |
2753 */ | |
2754 | |
2755 index = ch & ~0xf0; | |
2756 | |
2757 if (index == 0x0f) { | |
2758 ctx->index = index; | |
2759 ctx->literal = 1; | |
2760 state = sw_index; | |
2761 break; | |
2762 } | |
2763 | |
2764 if (index == 0) { | |
2765 state = sw_name_length; | |
2766 break; | |
2767 } | |
2768 | |
2769 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2770 "grpc literal header never indexed: %ui", | |
2771 index); | |
2772 | |
2773 ctx->index = index; | |
2774 ctx->literal = 1; | |
2775 | |
2776 state = sw_value_length; | |
2777 break; | |
2778 | |
2779 } else if ((ch & 0xf0) == 0x00) { | |
2780 /* | |
2781 * literal header field without indexing: | |
2782 * | |
2783 * 0 1 2 3 4 5 6 7 | |
2784 * +---+---+---+---+---+---+---+---+ | |
2785 * | 0 | 0 | 0 | 0 | Index (4+) | | |
2786 * +---+---+-----------------------+ | |
2787 * | H | Value Length (7+) | | |
2788 * +---+---------------------------+ | |
2789 * | Value String (Length octets) | | |
2790 * +-------------------------------+ | |
2791 * | |
2792 * 0 1 2 3 4 5 6 7 | |
2793 * +---+---+---+---+---+---+---+---+ | |
2794 * | 0 | 0 | 0 | 0 | 0 | | |
2795 * +---+---+-----------------------+ | |
2796 * | H | Name Length (7+) | | |
2797 * +---+---------------------------+ | |
2798 * | Name String (Length octets) | | |
2799 * +---+---------------------------+ | |
2800 * | H | Value Length (7+) | | |
2801 * +---+---------------------------+ | |
2802 * | Value String (Length octets) | | |
2803 * +-------------------------------+ | |
2804 */ | |
2805 | |
2806 index = ch & ~0xf0; | |
2807 | |
2808 if (index == 0x0f) { | |
2809 ctx->index = index; | |
2810 ctx->literal = 1; | |
2811 state = sw_index; | |
2812 break; | |
2813 } | |
2814 | |
2815 if (index == 0) { | |
2816 state = sw_name_length; | |
2817 break; | |
2818 } | |
2819 | |
2820 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2821 "grpc literal header without indexing: %ui", | |
2822 index); | |
2823 | |
2824 ctx->index = index; | |
2825 ctx->literal = 1; | |
2826 | |
2827 state = sw_value_length; | |
2828 break; | |
2829 } | |
2830 | |
2831 /* not reached */ | |
2832 | |
2833 return NGX_ERROR; | |
2834 | |
2835 case sw_index: | |
2836 ctx->index = ctx->index + (ch & ~0x80); | |
2837 | |
2838 if (ch & 0x80) { | |
2839 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2840 "upstream sent http2 table index " | |
2841 "with continuation flag"); | |
2842 return NGX_ERROR; | |
2843 } | |
2844 | |
2845 if (ctx->index > 61) { | |
2846 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2847 "upstream sent invalid http2 " | |
2848 "table index: %ui", ctx->index); | |
2849 return NGX_ERROR; | |
2850 } | |
2851 | |
2852 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2853 "grpc header index: %ui", ctx->index); | |
2854 | |
2855 state = sw_value_length; | |
2856 break; | |
2857 | |
2858 case sw_name_length: | |
2859 ctx->field_huffman = ch & 0x80 ? 1 : 0; | |
2860 ctx->field_length = ch & ~0x80; | |
2861 | |
2862 if (ctx->field_length == 0x7f) { | |
2863 state = sw_name_length_2; | |
2864 break; | |
2865 } | |
2866 | |
2867 if (ctx->field_length == 0) { | |
2868 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2869 "upstream sent zero http2 " | |
2870 "header name length"); | |
2871 return NGX_ERROR; | |
2872 } | |
2873 | |
2874 state = sw_name; | |
2875 break; | |
2876 | |
2877 case sw_name_length_2: | |
2878 ctx->field_length += ch & ~0x80; | |
2879 | |
2880 if (ch & 0x80) { | |
2881 state = sw_name_length_3; | |
2882 break; | |
2883 } | |
2884 | |
2885 state = sw_name; | |
2886 break; | |
2887 | |
2888 case sw_name_length_3: | |
2889 ctx->field_length += (ch & ~0x80) << 7; | |
2890 | |
2891 if (ch & 0x80) { | |
2892 state = sw_name_length_4; | |
2893 break; | |
2894 } | |
2895 | |
2896 state = sw_name; | |
2897 break; | |
2898 | |
2899 case sw_name_length_4: | |
2900 ctx->field_length += (ch & ~0x80) << 14; | |
2901 | |
2902 if (ch & 0x80) { | |
2903 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2904 "upstream sent too large http2 " | |
2905 "header name length"); | |
2906 return NGX_ERROR; | |
2907 } | |
2908 | |
2909 state = sw_name; | |
2910 break; | |
2911 | |
2912 case sw_name: | |
2913 ctx->name.len = ctx->field_huffman ? | |
2914 ctx->field_length * 8 / 5 : ctx->field_length; | |
2915 | |
2916 ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1); | |
2917 if (ctx->name.data == NULL) { | |
2918 return NGX_ERROR; | |
2919 } | |
2920 | |
2921 ctx->field_end = ctx->name.data; | |
2922 ctx->field_rest = ctx->field_length; | |
2923 ctx->field_state = 0; | |
2924 | |
2925 state = sw_name_bytes; | |
2926 | |
2927 /* fall through */ | |
2928 | |
2929 case sw_name_bytes: | |
2930 | |
2931 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2932 "grpc name: len:%uz h:%d last:%uz, rest:%uz", | |
2933 ctx->field_length, | |
2934 ctx->field_huffman, | |
2935 last - p, | |
2936 ctx->rest - (p - b->pos)); | |
2937 | |
2938 size = ngx_min(last - p, (ssize_t) ctx->field_rest); | |
2939 ctx->field_rest -= size; | |
2940 | |
2941 if (ctx->field_huffman) { | |
2942 if (ngx_http_v2_huff_decode(&ctx->field_state, p, size, | |
2943 &ctx->field_end, | |
2944 ctx->field_rest == 0, | |
2945 r->connection->log) | |
2946 != NGX_OK) | |
2947 { | |
2948 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
2949 "upstream sent invalid encoded header"); | |
2950 return NGX_ERROR; | |
2951 } | |
2952 | |
2953 ctx->name.len = ctx->field_end - ctx->name.data; | |
2954 ctx->name.data[ctx->name.len] = '\0'; | |
2955 | |
2956 } else { | |
7240
413189f03c8d
gRPC: fixed parsing response headers split on CONTINUATION frames.
Sergey Kandaurov <pluknet@nginx.com>
parents:
7235
diff
changeset
|
2957 ctx->field_end = ngx_cpymem(ctx->field_end, p, size); |
7233 | 2958 ctx->name.data[ctx->name.len] = '\0'; |
2959 } | |
2960 | |
2961 p += size - 1; | |
2962 | |
2963 if (ctx->field_rest == 0) { | |
2964 state = sw_value_length; | |
2965 } | |
2966 | |
2967 break; | |
2968 | |
2969 case sw_value_length: | |
2970 ctx->field_huffman = ch & 0x80 ? 1 : 0; | |
2971 ctx->field_length = ch & ~0x80; | |
2972 | |
2973 if (ctx->field_length == 0x7f) { | |
2974 state = sw_value_length_2; | |
2975 break; | |
2976 } | |
2977 | |
2978 if (ctx->field_length == 0) { | |
2979 ngx_str_set(&ctx->value, ""); | |
2980 goto done; | |
2981 } | |
2982 | |
2983 state = sw_value; | |
2984 break; | |
2985 | |
2986 case sw_value_length_2: | |
2987 ctx->field_length += ch & ~0x80; | |
2988 | |
2989 if (ch & 0x80) { | |
2990 state = sw_value_length_3; | |
2991 break; | |
2992 } | |
2993 | |
2994 state = sw_value; | |
2995 break; | |
2996 | |
2997 case sw_value_length_3: | |
2998 ctx->field_length += (ch & ~0x80) << 7; | |
2999 | |
3000 if (ch & 0x80) { | |
3001 state = sw_value_length_4; | |
3002 break; | |
3003 } | |
3004 | |
3005 state = sw_value; | |
3006 break; | |
3007 | |
3008 case sw_value_length_4: | |
3009 ctx->field_length += (ch & ~0x80) << 14; | |
3010 | |
3011 if (ch & 0x80) { | |
3012 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3013 "upstream sent too large http2 " | |
3014 "header value length"); | |
3015 return NGX_ERROR; | |
3016 } | |
3017 | |
3018 state = sw_value; | |
3019 break; | |
3020 | |
3021 case sw_value: | |
3022 ctx->value.len = ctx->field_huffman ? | |
3023 ctx->field_length * 8 / 5 : ctx->field_length; | |
3024 | |
3025 ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1); | |
3026 if (ctx->value.data == NULL) { | |
3027 return NGX_ERROR; | |
3028 } | |
3029 | |
3030 ctx->field_end = ctx->value.data; | |
3031 ctx->field_rest = ctx->field_length; | |
3032 ctx->field_state = 0; | |
3033 | |
3034 state = sw_value_bytes; | |
3035 | |
3036 /* fall through */ | |
3037 | |
3038 case sw_value_bytes: | |
3039 | |
3040 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3041 "grpc value: len:%uz h:%d last:%uz, rest:%uz", | |
3042 ctx->field_length, | |
3043 ctx->field_huffman, | |
3044 last - p, | |
3045 ctx->rest - (p - b->pos)); | |
3046 | |
3047 size = ngx_min(last - p, (ssize_t) ctx->field_rest); | |
3048 ctx->field_rest -= size; | |
3049 | |
3050 if (ctx->field_huffman) { | |
3051 if (ngx_http_v2_huff_decode(&ctx->field_state, p, size, | |
3052 &ctx->field_end, | |
3053 ctx->field_rest == 0, | |
3054 r->connection->log) | |
3055 != NGX_OK) | |
3056 { | |
3057 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3058 "upstream sent invalid encoded header"); | |
3059 return NGX_ERROR; | |
3060 } | |
3061 | |
3062 ctx->value.len = ctx->field_end - ctx->value.data; | |
3063 ctx->value.data[ctx->value.len] = '\0'; | |
3064 | |
3065 } else { | |
7240
413189f03c8d
gRPC: fixed parsing response headers split on CONTINUATION frames.
Sergey Kandaurov <pluknet@nginx.com>
parents:
7235
diff
changeset
|
3066 ctx->field_end = ngx_cpymem(ctx->field_end, p, size); |
7233 | 3067 ctx->value.data[ctx->value.len] = '\0'; |
3068 } | |
3069 | |
3070 p += size - 1; | |
3071 | |
3072 if (ctx->field_rest == 0) { | |
3073 goto done; | |
3074 } | |
3075 | |
3076 break; | |
3077 } | |
3078 | |
3079 continue; | |
3080 | |
3081 done: | |
3082 | |
3083 p++; | |
3084 ctx->rest -= p - b->pos; | |
3085 ctx->fragment_state = sw_start; | |
3086 b->pos = p; | |
3087 | |
3088 if (ctx->index) { | |
3089 ctx->name = *ngx_http_v2_get_static_name(ctx->index); | |
3090 } | |
3091 | |
3092 if (ctx->index && !ctx->literal) { | |
3093 ctx->value = *ngx_http_v2_get_static_value(ctx->index); | |
3094 } | |
3095 | |
3096 if (!ctx->index) { | |
3097 if (ngx_http_grpc_validate_header_name(r, &ctx->name) != NGX_OK) { | |
3098 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3099 "upstream sent invalid header: \"%V: %V\"", | |
3100 &ctx->name, &ctx->value); | |
3101 return NGX_ERROR; | |
3102 } | |
3103 } | |
3104 | |
3105 if (!ctx->index || ctx->literal) { | |
3106 if (ngx_http_grpc_validate_header_value(r, &ctx->value) != NGX_OK) { | |
3107 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3108 "upstream sent invalid header: \"%V: %V\"", | |
3109 &ctx->name, &ctx->value); | |
3110 return NGX_ERROR; | |
3111 } | |
3112 } | |
3113 | |
3114 return NGX_OK; | |
3115 } | |
3116 | |
3117 ctx->rest -= p - b->pos; | |
3118 ctx->fragment_state = state; | |
3119 b->pos = p; | |
3120 | |
3121 if (ctx->rest > ctx->padding) { | |
3122 return NGX_AGAIN; | |
3123 } | |
3124 | |
3125 return NGX_DONE; | |
3126 } | |
3127 | |
3128 | |
3129 static ngx_int_t | |
3130 ngx_http_grpc_validate_header_name(ngx_http_request_t *r, ngx_str_t *s) | |
3131 { | |
3132 u_char ch; | |
3133 ngx_uint_t i; | |
3134 | |
3135 for (i = 0; i < s->len; i++) { | |
3136 ch = s->data[i]; | |
3137 | |
3138 if (ch == ':' && i > 0) { | |
3139 return NGX_ERROR; | |
3140 } | |
3141 | |
3142 if (ch >= 'A' && ch <= 'Z') { | |
3143 return NGX_ERROR; | |
3144 } | |
3145 | |
3146 if (ch == '\0' || ch == CR || ch == LF) { | |
3147 return NGX_ERROR; | |
3148 } | |
3149 } | |
3150 | |
3151 return NGX_OK; | |
3152 } | |
3153 | |
3154 | |
3155 static ngx_int_t | |
3156 ngx_http_grpc_validate_header_value(ngx_http_request_t *r, ngx_str_t *s) | |
3157 { | |
3158 u_char ch; | |
3159 ngx_uint_t i; | |
3160 | |
3161 for (i = 0; i < s->len; i++) { | |
3162 ch = s->data[i]; | |
3163 | |
3164 if (ch == '\0' || ch == CR || ch == LF) { | |
3165 return NGX_ERROR; | |
3166 } | |
3167 } | |
3168 | |
3169 return NGX_OK; | |
3170 } | |
3171 | |
3172 | |
3173 static ngx_int_t | |
3174 ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
3175 ngx_buf_t *b) | |
3176 { | |
3177 u_char ch, *p, *last; | |
3178 enum { | |
3179 sw_start = 0, | |
3180 sw_error_2, | |
3181 sw_error_3, | |
3182 sw_error_4 | |
3183 } state; | |
3184 | |
3185 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
3186 last = b->last; | |
3187 | |
3188 } else { | |
3189 last = b->pos + ctx->rest; | |
3190 } | |
3191 | |
3192 state = ctx->frame_state; | |
3193 | |
3194 if (state == sw_start) { | |
3195 if (ctx->rest != 4) { | |
3196 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3197 "upstream sent rst stream frame " | |
3198 "with invalid length: %uz", | |
3199 ctx->rest); | |
3200 return NGX_ERROR; | |
3201 } | |
3202 } | |
3203 | |
3204 for (p = b->pos; p < last; p++) { | |
3205 ch = *p; | |
3206 | |
3207 #if 0 | |
3208 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3209 "grpc rst byte: %02Xd s:%d", ch, state); | |
3210 #endif | |
3211 | |
3212 switch (state) { | |
3213 | |
3214 case sw_start: | |
7249
070c972336c4
gRPC: fixed possible sign extension of error and setting_value.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7242
diff
changeset
|
3215 ctx->error = (ngx_uint_t) ch << 24; |
7233 | 3216 state = sw_error_2; |
3217 break; | |
3218 | |
3219 case sw_error_2: | |
3220 ctx->error |= ch << 16; | |
3221 state = sw_error_3; | |
3222 break; | |
3223 | |
3224 case sw_error_3: | |
3225 ctx->error |= ch << 8; | |
3226 state = sw_error_4; | |
3227 break; | |
3228 | |
3229 case sw_error_4: | |
3230 ctx->error |= ch; | |
3231 state = sw_start; | |
3232 | |
3233 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3234 "grpc error: %ui", ctx->error); | |
3235 | |
3236 break; | |
3237 } | |
3238 } | |
3239 | |
3240 ctx->rest -= p - b->pos; | |
3241 ctx->frame_state = state; | |
3242 b->pos = p; | |
3243 | |
3244 if (ctx->rest > 0) { | |
3245 return NGX_AGAIN; | |
3246 } | |
3247 | |
3248 return NGX_OK; | |
3249 } | |
3250 | |
3251 | |
3252 static ngx_int_t | |
3253 ngx_http_grpc_parse_goaway(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
3254 ngx_buf_t *b) | |
3255 { | |
3256 u_char ch, *p, *last; | |
3257 enum { | |
3258 sw_start = 0, | |
3259 sw_last_stream_id_2, | |
3260 sw_last_stream_id_3, | |
3261 sw_last_stream_id_4, | |
3262 sw_error, | |
3263 sw_error_2, | |
3264 sw_error_3, | |
3265 sw_error_4, | |
3266 sw_debug | |
3267 } state; | |
3268 | |
3269 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
3270 last = b->last; | |
3271 | |
3272 } else { | |
3273 last = b->pos + ctx->rest; | |
3274 } | |
3275 | |
3276 state = ctx->frame_state; | |
3277 | |
3278 if (state == sw_start) { | |
3279 | |
3280 if (ctx->stream_id) { | |
3281 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3282 "upstream sent goaway frame " | |
3283 "with non-zero stream id: %ui", | |
3284 ctx->stream_id); | |
3285 return NGX_ERROR; | |
3286 } | |
3287 | |
3288 if (ctx->rest < 8) { | |
3289 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3290 "upstream sent goaway frame " | |
3291 "with invalid length: %uz", | |
3292 ctx->rest); | |
3293 return NGX_ERROR; | |
3294 } | |
3295 } | |
3296 | |
3297 for (p = b->pos; p < last; p++) { | |
3298 ch = *p; | |
3299 | |
3300 #if 0 | |
3301 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3302 "grpc goaway byte: %02Xd s:%d", ch, state); | |
3303 #endif | |
3304 | |
3305 switch (state) { | |
3306 | |
3307 case sw_start: | |
3308 ctx->stream_id = (ch & 0x7f) << 24; | |
3309 state = sw_last_stream_id_2; | |
3310 break; | |
3311 | |
3312 case sw_last_stream_id_2: | |
3313 ctx->stream_id |= ch << 16; | |
3314 state = sw_last_stream_id_3; | |
3315 break; | |
3316 | |
3317 case sw_last_stream_id_3: | |
3318 ctx->stream_id |= ch << 8; | |
3319 state = sw_last_stream_id_4; | |
3320 break; | |
3321 | |
3322 case sw_last_stream_id_4: | |
3323 ctx->stream_id |= ch; | |
3324 state = sw_error; | |
3325 break; | |
3326 | |
3327 case sw_error: | |
7249
070c972336c4
gRPC: fixed possible sign extension of error and setting_value.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7242
diff
changeset
|
3328 ctx->error = (ngx_uint_t) ch << 24; |
7233 | 3329 state = sw_error_2; |
3330 break; | |
3331 | |
3332 case sw_error_2: | |
3333 ctx->error |= ch << 16; | |
3334 state = sw_error_3; | |
3335 break; | |
3336 | |
3337 case sw_error_3: | |
3338 ctx->error |= ch << 8; | |
3339 state = sw_error_4; | |
3340 break; | |
3341 | |
3342 case sw_error_4: | |
3343 ctx->error |= ch; | |
3344 state = sw_debug; | |
3345 break; | |
3346 | |
3347 case sw_debug: | |
3348 break; | |
3349 } | |
3350 } | |
3351 | |
3352 ctx->rest -= p - b->pos; | |
3353 ctx->frame_state = state; | |
3354 b->pos = p; | |
3355 | |
3356 if (ctx->rest > 0) { | |
3357 return NGX_AGAIN; | |
3358 } | |
3359 | |
3360 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3361 "grpc goaway: %ui, stream %ui", | |
3362 ctx->error, ctx->stream_id); | |
3363 | |
3364 ctx->state = ngx_http_grpc_st_start; | |
3365 | |
3366 return NGX_OK; | |
3367 } | |
3368 | |
3369 | |
3370 static ngx_int_t | |
3371 ngx_http_grpc_parse_window_update(ngx_http_request_t *r, | |
3372 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b) | |
3373 { | |
3374 u_char ch, *p, *last; | |
3375 enum { | |
3376 sw_start = 0, | |
3377 sw_size_2, | |
3378 sw_size_3, | |
3379 sw_size_4 | |
3380 } state; | |
3381 | |
3382 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
3383 last = b->last; | |
3384 | |
3385 } else { | |
3386 last = b->pos + ctx->rest; | |
3387 } | |
3388 | |
3389 state = ctx->frame_state; | |
3390 | |
3391 if (state == sw_start) { | |
3392 if (ctx->rest != 4) { | |
3393 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3394 "upstream sent window update frame " | |
3395 "with invalid length: %uz", | |
3396 ctx->rest); | |
3397 return NGX_ERROR; | |
3398 } | |
3399 } | |
3400 | |
3401 for (p = b->pos; p < last; p++) { | |
3402 ch = *p; | |
3403 | |
3404 #if 0 | |
3405 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3406 "grpc window update byte: %02Xd s:%d", ch, state); | |
3407 #endif | |
3408 | |
3409 switch (state) { | |
3410 | |
3411 case sw_start: | |
3412 ctx->window_update = (ch & 0x7f) << 24; | |
3413 state = sw_size_2; | |
3414 break; | |
3415 | |
3416 case sw_size_2: | |
3417 ctx->window_update |= ch << 16; | |
3418 state = sw_size_3; | |
3419 break; | |
3420 | |
3421 case sw_size_3: | |
3422 ctx->window_update |= ch << 8; | |
3423 state = sw_size_4; | |
3424 break; | |
3425 | |
3426 case sw_size_4: | |
3427 ctx->window_update |= ch; | |
3428 state = sw_start; | |
3429 break; | |
3430 } | |
3431 } | |
3432 | |
3433 ctx->rest -= p - b->pos; | |
3434 ctx->frame_state = state; | |
3435 b->pos = p; | |
3436 | |
3437 if (ctx->rest > 0) { | |
3438 return NGX_AGAIN; | |
3439 } | |
3440 | |
3441 ctx->state = ngx_http_grpc_st_start; | |
3442 | |
3443 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3444 "grpc window update: %ui", ctx->window_update); | |
3445 | |
3446 if (ctx->stream_id) { | |
3447 | |
3448 if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW | |
3449 - ctx->send_window) | |
3450 { | |
3451 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3452 "upstream sent too large window update"); | |
3453 return NGX_ERROR; | |
3454 } | |
3455 | |
3456 ctx->send_window += ctx->window_update; | |
3457 | |
3458 } else { | |
3459 | |
3460 if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW | |
3461 - ctx->connection->send_window) | |
3462 { | |
3463 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3464 "upstream sent too large window update"); | |
3465 return NGX_ERROR; | |
3466 } | |
3467 | |
3468 ctx->connection->send_window += ctx->window_update; | |
3469 } | |
3470 | |
3471 return NGX_OK; | |
3472 } | |
3473 | |
3474 | |
3475 static ngx_int_t | |
3476 ngx_http_grpc_parse_settings(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, | |
3477 ngx_buf_t *b) | |
3478 { | |
3479 u_char ch, *p, *last; | |
3480 ssize_t window_update; | |
3481 enum { | |
3482 sw_start = 0, | |
3483 sw_id, | |
3484 sw_id_2, | |
3485 sw_value, | |
3486 sw_value_2, | |
3487 sw_value_3, | |
3488 sw_value_4 | |
3489 } state; | |
3490 | |
3491 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
3492 last = b->last; | |
3493 | |
3494 } else { | |
3495 last = b->pos + ctx->rest; | |
3496 } | |
3497 | |
3498 state = ctx->frame_state; | |
3499 | |
3500 if (state == sw_start) { | |
3501 | |
3502 if (ctx->stream_id) { | |
3503 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3504 "upstream sent settings frame " | |
3505 "with non-zero stream id: %ui", | |
3506 ctx->stream_id); | |
3507 return NGX_ERROR; | |
3508 } | |
3509 | |
3510 if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) { | |
3511 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3512 "grpc settings ack"); | |
3513 | |
3514 if (ctx->rest != 0) { | |
3515 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3516 "upstream sent settings frame " | |
3517 "with ack flag and non-zero length: %uz", | |
3518 ctx->rest); | |
3519 return NGX_ERROR; | |
3520 } | |
3521 | |
3522 ctx->state = ngx_http_grpc_st_start; | |
3523 | |
3524 return NGX_OK; | |
3525 } | |
3526 | |
3527 if (ctx->rest % 6 != 0) { | |
3528 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3529 "upstream sent settings frame " | |
3530 "with invalid length: %uz", | |
3531 ctx->rest); | |
3532 return NGX_ERROR; | |
3533 } | |
3534 } | |
3535 | |
3536 for (p = b->pos; p < last; p++) { | |
3537 ch = *p; | |
3538 | |
3539 #if 0 | |
3540 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3541 "grpc settings byte: %02Xd s:%d", ch, state); | |
3542 #endif | |
3543 | |
3544 switch (state) { | |
3545 | |
3546 case sw_start: | |
3547 case sw_id: | |
3548 ctx->setting_id = ch << 8; | |
3549 state = sw_id_2; | |
3550 break; | |
3551 | |
3552 case sw_id_2: | |
3553 ctx->setting_id |= ch; | |
3554 state = sw_value; | |
3555 break; | |
3556 | |
3557 case sw_value: | |
7249
070c972336c4
gRPC: fixed possible sign extension of error and setting_value.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7242
diff
changeset
|
3558 ctx->setting_value = (ngx_uint_t) ch << 24; |
7233 | 3559 state = sw_value_2; |
3560 break; | |
3561 | |
3562 case sw_value_2: | |
3563 ctx->setting_value |= ch << 16; | |
3564 state = sw_value_3; | |
3565 break; | |
3566 | |
3567 case sw_value_3: | |
3568 ctx->setting_value |= ch << 8; | |
3569 state = sw_value_4; | |
3570 break; | |
3571 | |
3572 case sw_value_4: | |
3573 ctx->setting_value |= ch; | |
3574 state = sw_id; | |
3575 | |
3576 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3577 "grpc setting: %ui %ui", | |
3578 ctx->setting_id, ctx->setting_value); | |
3579 | |
3580 /* | |
3581 * The following settings are defined by the protocol: | |
3582 * | |
3583 * SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH, | |
3584 * SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE, | |
3585 * SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE | |
3586 * | |
3587 * Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in | |
3588 * a simple client. | |
3589 */ | |
3590 | |
3591 if (ctx->setting_id == 0x04) { | |
3592 /* SETTINGS_INITIAL_WINDOW_SIZE */ | |
3593 | |
3594 if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) { | |
3595 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3596 "upstream sent settings frame " | |
3597 "with too large initial window size: %ui", | |
3598 ctx->setting_value); | |
3599 return NGX_ERROR; | |
3600 } | |
3601 | |
3602 window_update = ctx->setting_value | |
3603 - ctx->connection->init_window; | |
3604 ctx->connection->init_window = ctx->setting_value; | |
3605 | |
3606 if (ctx->send_window > 0 | |
3607 && window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW | |
3608 - ctx->send_window) | |
3609 { | |
3610 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3611 "upstream sent settings frame " | |
3612 "with too large initial window size: %ui", | |
3613 ctx->setting_value); | |
3614 return NGX_ERROR; | |
3615 } | |
3616 | |
3617 ctx->send_window += window_update; | |
3618 } | |
3619 | |
3620 break; | |
3621 } | |
3622 } | |
3623 | |
3624 ctx->rest -= p - b->pos; | |
3625 ctx->frame_state = state; | |
3626 b->pos = p; | |
3627 | |
3628 if (ctx->rest > 0) { | |
3629 return NGX_AGAIN; | |
3630 } | |
3631 | |
3632 ctx->state = ngx_http_grpc_st_start; | |
3633 | |
3634 return ngx_http_grpc_send_settings_ack(r, ctx); | |
3635 } | |
3636 | |
3637 | |
3638 static ngx_int_t | |
3639 ngx_http_grpc_parse_ping(ngx_http_request_t *r, | |
3640 ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b) | |
3641 { | |
3642 u_char ch, *p, *last; | |
3643 enum { | |
3644 sw_start = 0, | |
3645 sw_data_2, | |
3646 sw_data_3, | |
3647 sw_data_4, | |
3648 sw_data_5, | |
3649 sw_data_6, | |
3650 sw_data_7, | |
3651 sw_data_8 | |
3652 } state; | |
3653 | |
3654 if (b->last - b->pos < (ssize_t) ctx->rest) { | |
3655 last = b->last; | |
3656 | |
3657 } else { | |
3658 last = b->pos + ctx->rest; | |
3659 } | |
3660 | |
3661 state = ctx->frame_state; | |
3662 | |
3663 if (state == sw_start) { | |
3664 | |
3665 if (ctx->stream_id) { | |
3666 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3667 "upstream sent ping frame " | |
3668 "with non-zero stream id: %ui", | |
3669 ctx->stream_id); | |
3670 return NGX_ERROR; | |
3671 } | |
3672 | |
3673 if (ctx->rest != 8) { | |
3674 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3675 "upstream sent ping frame " | |
3676 "with invalid length: %uz", | |
3677 ctx->rest); | |
3678 return NGX_ERROR; | |
3679 } | |
3680 | |
3681 if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) { | |
3682 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
3683 "upstream sent ping frame with ack flag"); | |
3684 return NGX_ERROR; | |
3685 } | |
3686 } | |
3687 | |
3688 for (p = b->pos; p < last; p++) { | |
3689 ch = *p; | |
3690 | |
3691 #if 0 | |
3692 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3693 "grpc ping byte: %02Xd s:%d", ch, state); | |
3694 #endif | |
3695 | |
3696 if (state < sw_data_8) { | |
3697 ctx->ping_data[state] = ch; | |
3698 state++; | |
3699 | |
3700 } else { | |
3701 ctx->ping_data[7] = ch; | |
3702 state = sw_start; | |
3703 | |
3704 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3705 "grpc ping"); | |
3706 } | |
3707 } | |
3708 | |
3709 ctx->rest -= p - b->pos; | |
3710 ctx->frame_state = state; | |
3711 b->pos = p; | |
3712 | |
3713 if (ctx->rest > 0) { | |
3714 return NGX_AGAIN; | |
3715 } | |
3716 | |
3717 ctx->state = ngx_http_grpc_st_start; | |
3718 | |
3719 return ngx_http_grpc_send_ping_ack(r, ctx); | |
3720 } | |
3721 | |
3722 | |
3723 static ngx_int_t | |
3724 ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) | |
3725 { | |
3726 ngx_chain_t *cl, **ll; | |
3727 ngx_http_grpc_frame_t *f; | |
3728 | |
3729 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3730 "grpc send settings ack"); | |
3731 | |
3732 for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { | |
3733 ll = &cl->next; | |
3734 } | |
3735 | |
3736 cl = ngx_http_grpc_get_buf(r, ctx); | |
3737 if (cl == NULL) { | |
3738 return NGX_ERROR; | |
3739 } | |
3740 | |
3741 f = (ngx_http_grpc_frame_t *) cl->buf->last; | |
3742 cl->buf->last += sizeof(ngx_http_grpc_frame_t); | |
3743 | |
3744 f->length_0 = 0; | |
3745 f->length_1 = 0; | |
3746 f->length_2 = 0; | |
3747 f->type = NGX_HTTP_V2_SETTINGS_FRAME; | |
3748 f->flags = NGX_HTTP_V2_ACK_FLAG; | |
3749 f->stream_id_0 = 0; | |
3750 f->stream_id_1 = 0; | |
3751 f->stream_id_2 = 0; | |
3752 f->stream_id_3 = 0; | |
3753 | |
3754 *ll = cl; | |
3755 | |
3756 return NGX_OK; | |
3757 } | |
3758 | |
3759 | |
3760 static ngx_int_t | |
3761 ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) | |
3762 { | |
3763 ngx_chain_t *cl, **ll; | |
3764 ngx_http_grpc_frame_t *f; | |
3765 | |
3766 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3767 "grpc send ping ack"); | |
3768 | |
3769 for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { | |
3770 ll = &cl->next; | |
3771 } | |
3772 | |
3773 cl = ngx_http_grpc_get_buf(r, ctx); | |
3774 if (cl == NULL) { | |
3775 return NGX_ERROR; | |
3776 } | |
3777 | |
3778 f = (ngx_http_grpc_frame_t *) cl->buf->last; | |
3779 cl->buf->last += sizeof(ngx_http_grpc_frame_t); | |
3780 | |
3781 f->length_0 = 0; | |
3782 f->length_1 = 0; | |
3783 f->length_2 = 8; | |
3784 f->type = NGX_HTTP_V2_PING_FRAME; | |
3785 f->flags = NGX_HTTP_V2_ACK_FLAG; | |
3786 f->stream_id_0 = 0; | |
3787 f->stream_id_1 = 0; | |
3788 f->stream_id_2 = 0; | |
3789 f->stream_id_3 = 0; | |
3790 | |
3791 cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8); | |
3792 | |
3793 *ll = cl; | |
3794 | |
3795 return NGX_OK; | |
3796 } | |
3797 | |
3798 | |
3799 static ngx_int_t | |
3800 ngx_http_grpc_send_window_update(ngx_http_request_t *r, | |
3801 ngx_http_grpc_ctx_t *ctx) | |
3802 { | |
3803 size_t n; | |
3804 ngx_chain_t *cl, **ll; | |
3805 ngx_http_grpc_frame_t *f; | |
3806 | |
3807 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3808 "grpc send window update: %uz %uz", | |
3809 ctx->connection->recv_window, ctx->recv_window); | |
3810 | |
3811 for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { | |
3812 ll = &cl->next; | |
3813 } | |
3814 | |
3815 cl = ngx_http_grpc_get_buf(r, ctx); | |
3816 if (cl == NULL) { | |
3817 return NGX_ERROR; | |
3818 } | |
3819 | |
3820 f = (ngx_http_grpc_frame_t *) cl->buf->last; | |
3821 cl->buf->last += sizeof(ngx_http_grpc_frame_t); | |
3822 | |
3823 f->length_0 = 0; | |
3824 f->length_1 = 0; | |
3825 f->length_2 = 4; | |
3826 f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME; | |
3827 f->flags = 0; | |
3828 f->stream_id_0 = 0; | |
3829 f->stream_id_1 = 0; | |
3830 f->stream_id_2 = 0; | |
3831 f->stream_id_3 = 0; | |
3832 | |
3833 n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window; | |
3834 ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW; | |
3835 | |
3836 *cl->buf->last++ = (u_char) ((n >> 24) & 0xff); | |
3837 *cl->buf->last++ = (u_char) ((n >> 16) & 0xff); | |
3838 *cl->buf->last++ = (u_char) ((n >> 8) & 0xff); | |
3839 *cl->buf->last++ = (u_char) (n & 0xff); | |
3840 | |
3841 f = (ngx_http_grpc_frame_t *) cl->buf->last; | |
3842 cl->buf->last += sizeof(ngx_http_grpc_frame_t); | |
3843 | |
3844 f->length_0 = 0; | |
3845 f->length_1 = 0; | |
3846 f->length_2 = 4; | |
3847 f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME; | |
3848 f->flags = 0; | |
3849 f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff); | |
3850 f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff); | |
3851 f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff); | |
3852 f->stream_id_3 = (u_char) (ctx->id & 0xff); | |
3853 | |
3854 n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window; | |
3855 ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; | |
3856 | |
3857 *cl->buf->last++ = (u_char) ((n >> 24) & 0xff); | |
3858 *cl->buf->last++ = (u_char) ((n >> 16) & 0xff); | |
3859 *cl->buf->last++ = (u_char) ((n >> 8) & 0xff); | |
3860 *cl->buf->last++ = (u_char) (n & 0xff); | |
3861 | |
3862 *ll = cl; | |
3863 | |
3864 return NGX_OK; | |
3865 } | |
3866 | |
3867 | |
3868 static ngx_chain_t * | |
3869 ngx_http_grpc_get_buf(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx) | |
3870 { | |
3871 ngx_buf_t *b; | |
3872 ngx_chain_t *cl; | |
3873 | |
3874 cl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
3875 if (cl == NULL) { | |
3876 return NULL; | |
3877 } | |
3878 | |
3879 b = cl->buf; | |
3880 | |
3881 b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter; | |
3882 b->temporary = 1; | |
3883 b->flush = 1; | |
3884 | |
3885 if (b->start == NULL) { | |
3886 | |
3887 /* | |
3888 * each buffer is large enough to hold two window update | |
3889 * frames in a row | |
3890 */ | |
3891 | |
3892 b->start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8); | |
3893 if (b->start == NULL) { | |
3894 return NULL; | |
3895 } | |
3896 | |
3897 b->pos = b->start; | |
3898 b->last = b->start; | |
3899 | |
3900 b->end = b->start + 2 * sizeof(ngx_http_grpc_frame_t) + 8; | |
3901 } | |
3902 | |
3903 return cl; | |
3904 } | |
3905 | |
3906 | |
3907 static ngx_http_grpc_ctx_t * | |
3908 ngx_http_grpc_get_ctx(ngx_http_request_t *r) | |
3909 { | |
3910 ngx_http_grpc_ctx_t *ctx; | |
3911 ngx_http_upstream_t *u; | |
3912 | |
3913 ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module); | |
3914 | |
3915 if (ctx->connection == NULL) { | |
3916 u = r->upstream; | |
3917 | |
3918 if (ngx_http_grpc_get_connection_data(r, ctx, &u->peer) != NGX_OK) { | |
3919 return NULL; | |
3920 } | |
3921 } | |
3922 | |
3923 return ctx; | |
3924 } | |
3925 | |
3926 | |
3927 static ngx_int_t | |
3928 ngx_http_grpc_get_connection_data(ngx_http_request_t *r, | |
3929 ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc) | |
3930 { | |
3931 ngx_connection_t *c; | |
3932 ngx_pool_cleanup_t *cln; | |
3933 | |
3934 c = pc->connection; | |
3935 | |
3936 if (pc->cached) { | |
3937 | |
3938 /* | |
3939 * for cached connections, connection data can be found | |
3940 * in the cleanup handler | |
3941 */ | |
3942 | |
3943 for (cln = c->pool->cleanup; cln; cln = cln->next) { | |
3944 if (cln->handler == ngx_http_grpc_cleanup) { | |
3945 ctx->connection = cln->data; | |
3946 break; | |
3947 } | |
3948 } | |
3949 | |
3950 if (ctx->connection == NULL) { | |
3951 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
3952 "no connection data found for " | |
3953 "keepalive http2 connection"); | |
3954 return NGX_ERROR; | |
3955 } | |
3956 | |
3957 ctx->send_window = ctx->connection->init_window; | |
3958 ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; | |
3959 | |
3960 ctx->connection->last_stream_id += 2; | |
3961 ctx->id = ctx->connection->last_stream_id; | |
3962 | |
3963 return NGX_OK; | |
3964 } | |
3965 | |
3966 cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_grpc_conn_t)); | |
3967 if (cln == NULL) { | |
3968 return NGX_ERROR; | |
3969 } | |
3970 | |
3971 cln->handler = ngx_http_grpc_cleanup; | |
3972 ctx->connection = cln->data; | |
3973 | |
3974 ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW; | |
3975 ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; | |
3976 ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW; | |
3977 | |
3978 ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; | |
3979 ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW; | |
3980 | |
3981 ctx->id = 1; | |
3982 ctx->connection->last_stream_id = 1; | |
3983 | |
3984 return NGX_OK; | |
3985 } | |
3986 | |
3987 | |
3988 static void | |
3989 ngx_http_grpc_cleanup(void *data) | |
3990 { | |
3991 #if 0 | |
3992 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
3993 "grpc cleanup"); | |
3994 #endif | |
3995 return; | |
3996 } | |
3997 | |
3998 | |
3999 static void | |
4000 ngx_http_grpc_abort_request(ngx_http_request_t *r) | |
4001 { | |
4002 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
4003 "abort grpc request"); | |
4004 return; | |
4005 } | |
4006 | |
4007 | |
4008 static void | |
4009 ngx_http_grpc_finalize_request(ngx_http_request_t *r, ngx_int_t rc) | |
4010 { | |
4011 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
4012 "finalize grpc request"); | |
4013 return; | |
4014 } | |
4015 | |
4016 | |
7234
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4017 static ngx_int_t |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4018 ngx_http_grpc_internal_trailers_variable(ngx_http_request_t *r, |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4019 ngx_http_variable_value_t *v, uintptr_t data) |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4020 { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4021 ngx_table_elt_t *te; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4022 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4023 te = r->headers_in.te; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4024 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4025 if (te == NULL) { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4026 v->not_found = 1; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4027 return NGX_OK; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4028 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4029 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4030 if (ngx_strlcasestrn(te->value.data, te->value.data + te->value.len, |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4031 (u_char *) "trailers", 8 - 1) |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4032 == NULL) |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4033 { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4034 v->not_found = 1; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4035 return NGX_OK; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4036 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4037 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4038 v->valid = 1; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4039 v->no_cacheable = 0; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4040 v->not_found = 0; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4041 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4042 v->data = (u_char *) "trailers"; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4043 v->len = sizeof("trailers") - 1; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4044 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4045 return NGX_OK; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4046 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4047 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4048 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4049 static ngx_int_t |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4050 ngx_http_grpc_add_variables(ngx_conf_t *cf) |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4051 { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4052 ngx_http_variable_t *var, *v; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4053 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4054 for (v = ngx_http_grpc_vars; v->name.len; v++) { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4055 var = ngx_http_add_variable(cf, &v->name, v->flags); |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4056 if (var == NULL) { |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4057 return NGX_ERROR; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4058 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4059 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4060 var->get_handler = v->get_handler; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4061 var->data = v->data; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4062 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4063 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4064 return NGX_OK; |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4065 } |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4066 |
c693daca57f7
gRPC: special handling of the TE request header.
Maxim Dounin <mdounin@mdounin.ru>
parents:
7233
diff
changeset
|
4067 |
7233 | 4068 static void * |
4069 ngx_http_grpc_create_loc_conf(ngx_conf_t *cf) | |
4070 { | |
4071 ngx_http_grpc_loc_conf_t *conf; | |
4072 | |
4073 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_grpc_loc_conf_t)); | |
4074 if (conf == NULL) { | |
4075 return NULL; | |
4076 } | |
4077 | |
4078 /* | |
4079 * set by ngx_pcalloc(): | |
4080 * | |
4081 * conf->upstream.ignore_headers = 0; | |
4082 * conf->upstream.next_upstream = 0; | |
4083 * conf->upstream.hide_headers_hash = { NULL, 0 }; | |
4084 * conf->upstream.ssl_name = NULL; | |
4085 * | |
4086 * conf->headers_source = NULL; | |
4087 * conf->headers.lengths = NULL; | |
4088 * conf->headers.values = NULL; | |
4089 * conf->headers.hash = { NULL, 0 }; | |
4090 * conf->host = { 0, NULL }; | |
4091 * conf->host_set = 0; | |
4092 * conf->ssl = 0; | |
4093 * conf->ssl_protocols = 0; | |
4094 * conf->ssl_ciphers = { 0, NULL }; | |
4095 * conf->ssl_trusted_certificate = { 0, NULL }; | |
4096 * conf->ssl_crl = { 0, NULL }; | |
4097 * conf->ssl_certificate = { 0, NULL }; | |
4098 * conf->ssl_certificate_key = { 0, NULL }; | |
4099 */ | |
4100 | |
4101 conf->upstream.local = NGX_CONF_UNSET_PTR; | |
4102 conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; | |
4103 conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; | |
4104 conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; | |
4105 conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; | |
4106 conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC; | |
4107 | |
4108 conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; | |
4109 | |
4110 conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; | |
4111 conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; | |
4112 | |
4113 conf->upstream.intercept_errors = NGX_CONF_UNSET; | |
4114 | |
4115 #if (NGX_HTTP_SSL) | |
4116 conf->upstream.ssl_session_reuse = NGX_CONF_UNSET; | |
4117 conf->upstream.ssl_server_name = NGX_CONF_UNSET; | |
4118 conf->upstream.ssl_verify = NGX_CONF_UNSET; | |
4119 conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; | |
4120 conf->ssl_passwords = NGX_CONF_UNSET_PTR; | |
4121 #endif | |
4122 | |
4123 /* the hardcoded values */ | |
4124 conf->upstream.cyclic_temp_file = 0; | |
4125 conf->upstream.buffering = 0; | |
4126 conf->upstream.ignore_client_abort = 0; | |
4127 conf->upstream.send_lowat = 0; | |
4128 conf->upstream.bufs.num = 0; | |
4129 conf->upstream.busy_buffers_size = 0; | |
4130 conf->upstream.max_temp_file_size = 0; | |
4131 conf->upstream.temp_file_write_size = 0; | |
4132 conf->upstream.pass_request_headers = 1; | |
4133 conf->upstream.pass_request_body = 1; | |
4134 conf->upstream.force_ranges = 0; | |
4135 conf->upstream.pass_trailers = 1; | |
4136 conf->upstream.preserve_output = 1; | |
4137 | |
4138 ngx_str_set(&conf->upstream.module, "grpc"); | |
4139 | |
4140 return conf; | |
4141 } | |
4142 | |
4143 | |
4144 static char * | |
4145 ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
4146 { | |
4147 ngx_http_grpc_loc_conf_t *prev = parent; | |
4148 ngx_http_grpc_loc_conf_t *conf = child; | |
4149 | |
4150 ngx_int_t rc; | |
4151 ngx_hash_init_t hash; | |
4152 ngx_http_core_loc_conf_t *clcf; | |
4153 | |
4154 ngx_conf_merge_ptr_value(conf->upstream.local, | |
4155 prev->upstream.local, NULL); | |
4156 | |
4157 ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries, | |
4158 prev->upstream.next_upstream_tries, 0); | |
4159 | |
4160 ngx_conf_merge_msec_value(conf->upstream.connect_timeout, | |
4161 prev->upstream.connect_timeout, 60000); | |
4162 | |
4163 ngx_conf_merge_msec_value(conf->upstream.send_timeout, | |
4164 prev->upstream.send_timeout, 60000); | |
4165 | |
4166 ngx_conf_merge_msec_value(conf->upstream.read_timeout, | |
4167 prev->upstream.read_timeout, 60000); | |
4168 | |
4169 ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout, | |
4170 prev->upstream.next_upstream_timeout, 0); | |
4171 | |
4172 ngx_conf_merge_size_value(conf->upstream.buffer_size, | |
4173 prev->upstream.buffer_size, | |
4174 (size_t) ngx_pagesize); | |
4175 | |
4176 ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, | |
4177 prev->upstream.ignore_headers, | |
4178 NGX_CONF_BITMASK_SET); | |
4179 | |
4180 ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, | |
4181 prev->upstream.next_upstream, | |
4182 (NGX_CONF_BITMASK_SET | |
4183 |NGX_HTTP_UPSTREAM_FT_ERROR | |
4184 |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); | |
4185 | |
4186 if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { | |
4187 conf->upstream.next_upstream = NGX_CONF_BITMASK_SET | |
4188 |NGX_HTTP_UPSTREAM_FT_OFF; | |
4189 } | |
4190 | |
4191 ngx_conf_merge_value(conf->upstream.intercept_errors, | |
4192 prev->upstream.intercept_errors, 0); | |
4193 | |
4194 #if (NGX_HTTP_SSL) | |
4195 | |
4196 ngx_conf_merge_value(conf->upstream.ssl_session_reuse, | |
4197 prev->upstream.ssl_session_reuse, 1); | |
4198 | |
4199 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, | |
4200 (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 | |
4201 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); | |
4202 | |
4203 ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, | |
4204 "DEFAULT"); | |
4205 | |
4206 if (conf->upstream.ssl_name == NULL) { | |
4207 conf->upstream.ssl_name = prev->upstream.ssl_name; | |
4208 } | |
4209 | |
4210 ngx_conf_merge_value(conf->upstream.ssl_server_name, | |
4211 prev->upstream.ssl_server_name, 0); | |
4212 ngx_conf_merge_value(conf->upstream.ssl_verify, | |
4213 prev->upstream.ssl_verify, 0); | |
4214 ngx_conf_merge_uint_value(conf->ssl_verify_depth, | |
4215 prev->ssl_verify_depth, 1); | |
4216 ngx_conf_merge_str_value(conf->ssl_trusted_certificate, | |
4217 prev->ssl_trusted_certificate, ""); | |
4218 ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); | |
4219 | |
4220 ngx_conf_merge_str_value(conf->ssl_certificate, | |
4221 prev->ssl_certificate, ""); | |
4222 ngx_conf_merge_str_value(conf->ssl_certificate_key, | |
4223 prev->ssl_certificate_key, ""); | |
4224 ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); | |
4225 | |
4226 if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) { | |
4227 return NGX_CONF_ERROR; | |
4228 } | |
4229 | |
4230 #endif | |
4231 | |
4232 hash.max_size = 512; | |
4233 hash.bucket_size = ngx_align(64, ngx_cacheline_size); | |
4234 hash.name = "grpc_headers_hash"; | |
4235 | |
4236 if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, | |
4237 &prev->upstream, ngx_http_grpc_hide_headers, &hash) | |
4238 != NGX_OK) | |
4239 { | |
4240 return NGX_CONF_ERROR; | |
4241 } | |
4242 | |
4243 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); | |
4244 | |
4245 if (clcf->noname && conf->upstream.upstream == NULL) { | |
4246 conf->upstream.upstream = prev->upstream.upstream; | |
4247 conf->host = prev->host; | |
4248 #if (NGX_HTTP_SSL) | |
4249 conf->upstream.ssl = prev->upstream.ssl; | |
4250 #endif | |
4251 } | |
4252 | |
4253 if (clcf->lmt_excpt && clcf->handler == NULL && conf->upstream.upstream) { | |
4254 clcf->handler = ngx_http_grpc_handler; | |
4255 } | |
4256 | |
4257 if (conf->headers_source == NULL) { | |
4258 conf->headers = prev->headers; | |
4259 conf->headers_source = prev->headers_source; | |
4260 conf->host_set = prev->host_set; | |
4261 } | |
4262 | |
4263 rc = ngx_http_grpc_init_headers(cf, conf, &conf->headers, | |
4264 ngx_http_grpc_headers); | |
4265 if (rc != NGX_OK) { | |
4266 return NGX_CONF_ERROR; | |
4267 } | |
4268 | |
4269 /* | |
4270 * special handling to preserve conf->headers in the "http" section | |
4271 * to inherit it to all servers | |
4272 */ | |
4273 | |
4274 if (prev->headers.hash.buckets == NULL | |
4275 && conf->headers_source == prev->headers_source) | |
4276 { | |
4277 prev->headers = conf->headers; | |
4278 prev->host_set = conf->host_set; | |
4279 } | |
4280 | |
4281 return NGX_CONF_OK; | |
4282 } | |
4283 | |
4284 | |
4285 static ngx_int_t | |
4286 ngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf, | |
4287 ngx_http_grpc_headers_t *headers, ngx_keyval_t *default_headers) | |
4288 { | |
4289 u_char *p; | |
4290 size_t size; | |
4291 uintptr_t *code; | |
4292 ngx_uint_t i; | |
4293 ngx_array_t headers_names, headers_merged; | |
4294 ngx_keyval_t *src, *s, *h; | |
4295 ngx_hash_key_t *hk; | |
4296 ngx_hash_init_t hash; | |
4297 ngx_http_script_compile_t sc; | |
4298 ngx_http_script_copy_code_t *copy; | |
4299 | |
4300 if (headers->hash.buckets) { | |
4301 return NGX_OK; | |
4302 } | |
4303 | |
4304 if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) | |
4305 != NGX_OK) | |
4306 { | |
4307 return NGX_ERROR; | |
4308 } | |
4309 | |
4310 if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) | |
4311 != NGX_OK) | |
4312 { | |
4313 return NGX_ERROR; | |
4314 } | |
4315 | |
4316 headers->lengths = ngx_array_create(cf->pool, 64, 1); | |
4317 if (headers->lengths == NULL) { | |
4318 return NGX_ERROR; | |
4319 } | |
4320 | |
4321 headers->values = ngx_array_create(cf->pool, 512, 1); | |
4322 if (headers->values == NULL) { | |
4323 return NGX_ERROR; | |
4324 } | |
4325 | |
4326 if (conf->headers_source) { | |
4327 | |
4328 src = conf->headers_source->elts; | |
4329 for (i = 0; i < conf->headers_source->nelts; i++) { | |
4330 | |
4331 if (src[i].key.len == 4 | |
4332 && ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0) | |
4333 { | |
4334 conf->host_set = 1; | |
4335 } | |
4336 | |
4337 s = ngx_array_push(&headers_merged); | |
4338 if (s == NULL) { | |
4339 return NGX_ERROR; | |
4340 } | |
4341 | |
4342 *s = src[i]; | |
4343 } | |
4344 } | |
4345 | |
4346 h = default_headers; | |
4347 | |
4348 while (h->key.len) { | |
4349 | |
4350 src = headers_merged.elts; | |
4351 for (i = 0; i < headers_merged.nelts; i++) { | |
4352 if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { | |
4353 goto next; | |
4354 } | |
4355 } | |
4356 | |
4357 s = ngx_array_push(&headers_merged); | |
4358 if (s == NULL) { | |
4359 return NGX_ERROR; | |
4360 } | |
4361 | |
4362 *s = *h; | |
4363 | |
4364 next: | |
4365 | |
4366 h++; | |
4367 } | |
4368 | |
4369 | |
4370 src = headers_merged.elts; | |
4371 for (i = 0; i < headers_merged.nelts; i++) { | |
4372 | |
4373 hk = ngx_array_push(&headers_names); | |
4374 if (hk == NULL) { | |
4375 return NGX_ERROR; | |
4376 } | |
4377 | |
4378 hk->key = src[i].key; | |
4379 hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len); | |
4380 hk->value = (void *) 1; | |
4381 | |
4382 if (src[i].value.len == 0) { | |
4383 continue; | |
4384 } | |
4385 | |
4386 copy = ngx_array_push_n(headers->lengths, | |
4387 sizeof(ngx_http_script_copy_code_t)); | |
4388 if (copy == NULL) { | |
4389 return NGX_ERROR; | |
4390 } | |
4391 | |
7271
9e25a5380a21
Silenced -Wcast-function-type warnings (closes #1546).
Sergey Kandaurov <pluknet@nginx.com>
parents:
7249
diff
changeset
|
4392 copy->code = (ngx_http_script_code_pt) (void *) |
9e25a5380a21
Silenced -Wcast-function-type warnings (closes #1546).
Sergey Kandaurov <pluknet@nginx.com>
parents:
7249
diff
changeset
|
4393 ngx_http_script_copy_len_code; |
7233 | 4394 copy->len = src[i].key.len; |
4395 | |
4396 size = (sizeof(ngx_http_script_copy_code_t) | |
4397 + src[i].key.len + sizeof(uintptr_t) - 1) | |
4398 & ~(sizeof(uintptr_t) - 1); | |
4399 | |
4400 copy = ngx_array_push_n(headers->values, size); | |
4401 if (copy == NULL) { | |
4402 return NGX_ERROR; | |
4403 } | |
4404 | |
4405 copy->code = ngx_http_script_copy_code; | |
4406 copy->len = src[i].key.len; | |
4407 | |
4408 p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); | |
4409 ngx_memcpy(p, src[i].key.data, src[i].key.len); | |
4410 | |
4411 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); | |
4412 | |
4413 sc.cf = cf; | |
4414 sc.source = &src[i].value; | |
4415 sc.flushes = &headers->flushes; | |
4416 sc.lengths = &headers->lengths; | |
4417 sc.values = &headers->values; | |
4418 | |
4419 if (ngx_http_script_compile(&sc) != NGX_OK) { | |
4420 return NGX_ERROR; | |
4421 } | |
4422 | |
4423 code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); | |
4424 if (code == NULL) { | |
4425 return NGX_ERROR; | |
4426 } | |
4427 | |
4428 *code = (uintptr_t) NULL; | |
4429 | |
4430 code = ngx_array_push_n(headers->values, sizeof(uintptr_t)); | |
4431 if (code == NULL) { | |
4432 return NGX_ERROR; | |
4433 } | |
4434 | |
4435 *code = (uintptr_t) NULL; | |
4436 } | |
4437 | |
4438 code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); | |
4439 if (code == NULL) { | |
4440 return NGX_ERROR; | |
4441 } | |
4442 | |
4443 *code = (uintptr_t) NULL; | |
4444 | |
4445 | |
4446 hash.hash = &headers->hash; | |
4447 hash.key = ngx_hash_key_lc; | |
4448 hash.max_size = 512; | |
4449 hash.bucket_size = 64; | |
4450 hash.name = "grpc_headers_hash"; | |
4451 hash.pool = cf->pool; | |
4452 hash.temp_pool = NULL; | |
4453 | |
4454 return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts); | |
4455 } | |
4456 | |
4457 | |
4458 static char * | |
4459 ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
4460 { | |
4461 ngx_http_grpc_loc_conf_t *glcf = conf; | |
4462 | |
4463 size_t add; | |
4464 ngx_str_t *value, *url; | |
4465 ngx_url_t u; | |
4466 ngx_http_core_loc_conf_t *clcf; | |
4467 | |
4468 if (glcf->upstream.upstream) { | |
4469 return "is duplicate"; | |
4470 } | |
4471 | |
4472 value = cf->args->elts; | |
4473 url = &value[1]; | |
4474 | |
4475 if (ngx_strncasecmp(url->data, (u_char *) "grpc://", 7) == 0) { | |
4476 add = 7; | |
4477 | |
4478 } else if (ngx_strncasecmp(url->data, (u_char *) "grpcs://", 8) == 0) { | |
4479 | |
4480 #if (NGX_HTTP_SSL) | |
4481 glcf->ssl = 1; | |
4482 | |
4483 add = 8; | |
4484 #else | |
4485 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
4486 "grpcs protocol requires SSL support"); | |
4487 return NGX_CONF_ERROR; | |
4488 #endif | |
4489 | |
4490 } else { | |
4491 add = 0; | |
4492 } | |
4493 | |
4494 ngx_memzero(&u, sizeof(ngx_url_t)); | |
4495 | |
4496 u.url.len = url->len - add; | |
4497 u.url.data = url->data + add; | |
4498 u.no_resolve = 1; | |
4499 | |
4500 glcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); | |
4501 if (glcf->upstream.upstream == NULL) { | |
4502 return NGX_CONF_ERROR; | |
4503 } | |
4504 | |
4505 if (u.family != AF_UNIX) { | |
4506 | |
4507 if (u.no_port) { | |
4508 glcf->host = u.host; | |
4509 | |
4510 } else { | |
4511 glcf->host.len = u.host.len + 1 + u.port_text.len; | |
4512 glcf->host.data = u.host.data; | |
4513 } | |
4514 | |
4515 } else { | |
4516 ngx_str_set(&glcf->host, "localhost"); | |
4517 } | |
4518 | |
4519 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); | |
4520 | |
4521 clcf->handler = ngx_http_grpc_handler; | |
4522 | |
4523 if (clcf->name.data[clcf->name.len - 1] == '/') { | |
4524 clcf->auto_redirect = 1; | |
4525 } | |
4526 | |
4527 return NGX_CONF_OK; | |
4528 } | |
4529 | |
4530 | |
4531 #if (NGX_HTTP_SSL) | |
4532 | |
4533 static char * | |
4534 ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
4535 { | |
4536 ngx_http_grpc_loc_conf_t *glcf = conf; | |
4537 | |
4538 ngx_str_t *value; | |
4539 | |
4540 if (glcf->ssl_passwords != NGX_CONF_UNSET_PTR) { | |
4541 return "is duplicate"; | |
4542 } | |
4543 | |
4544 value = cf->args->elts; | |
4545 | |
4546 glcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]); | |
4547 | |
4548 if (glcf->ssl_passwords == NULL) { | |
4549 return NGX_CONF_ERROR; | |
4550 } | |
4551 | |
4552 return NGX_CONF_OK; | |
4553 } | |
4554 | |
4555 | |
4556 static ngx_int_t | |
4557 ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf) | |
4558 { | |
4559 ngx_pool_cleanup_t *cln; | |
4560 | |
4561 glcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); | |
4562 if (glcf->upstream.ssl == NULL) { | |
4563 return NGX_ERROR; | |
4564 } | |
4565 | |
4566 glcf->upstream.ssl->log = cf->log; | |
4567 | |
4568 if (ngx_ssl_create(glcf->upstream.ssl, glcf->ssl_protocols, NULL) | |
4569 != NGX_OK) | |
4570 { | |
4571 return NGX_ERROR; | |
4572 } | |
4573 | |
4574 cln = ngx_pool_cleanup_add(cf->pool, 0); | |
4575 if (cln == NULL) { | |
4576 return NGX_ERROR; | |
4577 } | |
4578 | |
4579 cln->handler = ngx_ssl_cleanup_ctx; | |
4580 cln->data = glcf->upstream.ssl; | |
4581 | |
4582 if (glcf->ssl_certificate.len) { | |
4583 | |
4584 if (glcf->ssl_certificate_key.len == 0) { | |
4585 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
4586 "no \"grpc_ssl_certificate_key\" is defined " | |
4587 "for certificate \"%V\"", &glcf->ssl_certificate); | |
4588 return NGX_ERROR; | |
4589 } | |
4590 | |
4591 if (ngx_ssl_certificate(cf, glcf->upstream.ssl, &glcf->ssl_certificate, | |
4592 &glcf->ssl_certificate_key, glcf->ssl_passwords) | |
4593 != NGX_OK) | |
4594 { | |
4595 return NGX_ERROR; | |
4596 } | |
4597 } | |
4598 | |
4599 if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0) | |
4600 != NGX_OK) | |
4601 { | |
4602 return NGX_ERROR; | |
4603 } | |
4604 | |
4605 if (glcf->upstream.ssl_verify) { | |
4606 if (glcf->ssl_trusted_certificate.len == 0) { | |
4607 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
4608 "no grpc_ssl_trusted_certificate for grpc_ssl_verify"); | |
4609 return NGX_ERROR; | |
4610 } | |
4611 | |
4612 if (ngx_ssl_trusted_certificate(cf, glcf->upstream.ssl, | |
4613 &glcf->ssl_trusted_certificate, | |
4614 glcf->ssl_verify_depth) | |
4615 != NGX_OK) | |
4616 { | |
4617 return NGX_ERROR; | |
4618 } | |
4619 | |
4620 if (ngx_ssl_crl(cf, glcf->upstream.ssl, &glcf->ssl_crl) != NGX_OK) { | |
4621 return NGX_ERROR; | |
4622 } | |
4623 } | |
4624 | |
4625 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation | |
4626 | |
4627 if (SSL_CTX_set_alpn_protos(glcf->upstream.ssl->ctx, | |
4628 (u_char *) "\x02h2", 3) | |
4629 != 0) | |
4630 { | |
4631 ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, | |
4632 "SSL_CTX_set_alpn_protos() failed"); | |
4633 return NGX_ERROR; | |
4634 } | |
4635 | |
4636 #endif | |
4637 | |
4638 return NGX_OK; | |
4639 } | |
4640 | |
4641 #endif |