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