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