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