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