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