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