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