Mercurial > hg > nginx
comparison src/http/modules/ngx_http_grpc_module.c @ 7349:f6047a579ca1
gRPC: improved keepalive handling.
The code is now able to parse additional control frames after
the response is received, and can send control frames as well.
This fixes keepalive problems as observed with grpc-c, which can
send window update and ping frames after the response, see
http://mailman.nginx.org/pipermail/nginx/2018-August/056620.html.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 03 Sep 2018 19:34:01 +0300 |
parents | 45e513c3540d |
children | 67c6cb7f477c |
comparison
equal
deleted
inserted
replaced
7348:f6e7831a17d4 | 7349:f6047a579ca1 |
---|---|
109 | 109 |
110 unsigned header_sent:1; | 110 unsigned header_sent:1; |
111 unsigned output_closed:1; | 111 unsigned output_closed:1; |
112 unsigned parsing_headers:1; | 112 unsigned parsing_headers:1; |
113 unsigned end_stream:1; | 113 unsigned end_stream:1; |
114 unsigned done:1; | |
114 unsigned status:1; | 115 unsigned status:1; |
115 | 116 |
116 ngx_http_request_t *request; | 117 ngx_http_request_t *request; |
117 } ngx_http_grpc_ctx_t; | 118 } ngx_http_grpc_ctx_t; |
118 | 119 |
1072 ctx->state = 0; | 1073 ctx->state = 0; |
1073 ctx->header_sent = 0; | 1074 ctx->header_sent = 0; |
1074 ctx->output_closed = 0; | 1075 ctx->output_closed = 0; |
1075 ctx->parsing_headers = 0; | 1076 ctx->parsing_headers = 0; |
1076 ctx->end_stream = 0; | 1077 ctx->end_stream = 0; |
1078 ctx->done = 0; | |
1077 ctx->status = 0; | 1079 ctx->status = 0; |
1078 ctx->connection = NULL; | 1080 ctx->connection = NULL; |
1079 | 1081 |
1080 return NGX_OK; | 1082 return NGX_OK; |
1081 } | 1083 } |
1091 size_t len, limit; | 1093 size_t len, limit; |
1092 ngx_buf_t *b; | 1094 ngx_buf_t *b; |
1093 ngx_int_t rc; | 1095 ngx_int_t rc; |
1094 ngx_uint_t next, last; | 1096 ngx_uint_t next, last; |
1095 ngx_chain_t *cl, *out, **ll; | 1097 ngx_chain_t *cl, *out, **ll; |
1098 ngx_http_upstream_t *u; | |
1096 ngx_http_grpc_ctx_t *ctx; | 1099 ngx_http_grpc_ctx_t *ctx; |
1097 ngx_http_grpc_frame_t *f; | 1100 ngx_http_grpc_frame_t *f; |
1098 | 1101 |
1099 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 1102 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
1100 "grpc output filter"); | 1103 "grpc output filter"); |
1403 } | 1406 } |
1404 } | 1407 } |
1405 | 1408 |
1406 if (rc == NGX_OK && ctx->in) { | 1409 if (rc == NGX_OK && ctx->in) { |
1407 rc = NGX_AGAIN; | 1410 rc = NGX_AGAIN; |
1411 } | |
1412 | |
1413 if (ctx->done) { | |
1414 | |
1415 /* | |
1416 * We have already got the response and were sending some additional | |
1417 * control frames. Even if there is still something unsent, stop | |
1418 * here anyway. | |
1419 */ | |
1420 | |
1421 u = r->upstream; | |
1422 u->length = 0; | |
1423 | |
1424 if (ctx->in == NULL | |
1425 && ctx->out == NULL | |
1426 && ctx->output_closed | |
1427 && ctx->state == ngx_http_grpc_st_start) | |
1428 { | |
1429 u->keepalive = 1; | |
1430 } | |
1431 | |
1432 ngx_post_event(u->peer.connection->read, &ngx_posted_events); | |
1408 } | 1433 } |
1409 | 1434 |
1410 return rc; | 1435 return rc; |
1411 } | 1436 } |
1412 | 1437 |
1830 if (ctx->state < ngx_http_grpc_st_payload) { | 1855 if (ctx->state < ngx_http_grpc_st_payload) { |
1831 | 1856 |
1832 rc = ngx_http_grpc_parse_frame(r, ctx, b); | 1857 rc = ngx_http_grpc_parse_frame(r, ctx, b); |
1833 | 1858 |
1834 if (rc == NGX_AGAIN) { | 1859 if (rc == NGX_AGAIN) { |
1860 | |
1861 if (ctx->done) { | |
1862 | |
1863 /* | |
1864 * We have finished parsing the response and the | |
1865 * remaining control frames. If there are unsent | |
1866 * control frames, post a write event to send them. | |
1867 */ | |
1868 | |
1869 if (ctx->out) { | |
1870 ngx_post_event(u->peer.connection->write, | |
1871 &ngx_posted_events); | |
1872 return NGX_AGAIN; | |
1873 } | |
1874 | |
1875 u->length = 0; | |
1876 | |
1877 if (ctx->in == NULL | |
1878 && ctx->output_closed | |
1879 && ctx->state == ngx_http_grpc_st_start) | |
1880 { | |
1881 u->keepalive = 1; | |
1882 } | |
1883 | |
1884 break; | |
1885 } | |
1886 | |
1835 return NGX_AGAIN; | 1887 return NGX_AGAIN; |
1836 } | 1888 } |
1837 | 1889 |
1838 if (rc == NGX_ERROR) { | 1890 if (rc == NGX_ERROR) { |
1839 return NGX_ERROR; | 1891 return NGX_ERROR; |
1896 "upstream sent frame for unknown stream %ui", | 1948 "upstream sent frame for unknown stream %ui", |
1897 ctx->stream_id); | 1949 ctx->stream_id); |
1898 return NGX_ERROR; | 1950 return NGX_ERROR; |
1899 } | 1951 } |
1900 | 1952 |
1953 if (ctx->stream_id && ctx->done) { | |
1954 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1955 "upstream sent frame for closed stream %ui", | |
1956 ctx->stream_id); | |
1957 return NGX_ERROR; | |
1958 } | |
1959 | |
1901 ctx->padding = 0; | 1960 ctx->padding = 0; |
1902 } | 1961 } |
1903 | 1962 |
1904 if (ctx->state == ngx_http_grpc_st_padding) { | 1963 if (ctx->state == ngx_http_grpc_st_padding) { |
1905 | 1964 |
1912 b->pos += ctx->rest; | 1971 b->pos += ctx->rest; |
1913 ctx->rest = 0; | 1972 ctx->rest = 0; |
1914 ctx->state = ngx_http_grpc_st_start; | 1973 ctx->state = ngx_http_grpc_st_start; |
1915 | 1974 |
1916 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { | 1975 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { |
1917 u->length = 0; | 1976 ctx->done = 1; |
1918 | |
1919 if (ctx->in == NULL | |
1920 && ctx->out == NULL | |
1921 && ctx->output_closed | |
1922 && b->last == b->pos) | |
1923 { | |
1924 u->keepalive = 1; | |
1925 } | |
1926 | |
1927 break; | |
1928 } | 1977 } |
1929 | 1978 |
1930 continue; | 1979 continue; |
1931 } | 1980 } |
1932 | 1981 |
2092 | 2141 |
2093 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 2142 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
2094 "grpc trailer done"); | 2143 "grpc trailer done"); |
2095 | 2144 |
2096 if (ctx->end_stream) { | 2145 if (ctx->end_stream) { |
2097 u->length = 0; | 2146 ctx->done = 1; |
2098 | 2147 break; |
2099 if (ctx->in == NULL | |
2100 && ctx->out == NULL | |
2101 && ctx->output_closed | |
2102 && b->last == b->pos) | |
2103 { | |
2104 u->keepalive = 1; | |
2105 } | |
2106 | |
2107 return NGX_OK; | |
2108 } | 2148 } |
2109 | 2149 |
2110 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 2150 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
2111 "upstream sent trailer without " | 2151 "upstream sent trailer without " |
2112 "end stream flag"); | 2152 "end stream flag"); |
2117 | 2157 |
2118 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 2158 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
2119 "upstream sent invalid trailer"); | 2159 "upstream sent invalid trailer"); |
2120 | 2160 |
2121 return NGX_ERROR; | 2161 return NGX_ERROR; |
2162 } | |
2163 | |
2164 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { | |
2165 continue; | |
2122 } | 2166 } |
2123 | 2167 |
2124 /* rc == NGX_AGAIN */ | 2168 /* rc == NGX_AGAIN */ |
2125 | 2169 |
2126 if (ctx->rest == 0) { | 2170 if (ctx->rest == 0) { |
2235 } | 2279 } |
2236 | 2280 |
2237 ctx->state = ngx_http_grpc_st_start; | 2281 ctx->state = ngx_http_grpc_st_start; |
2238 | 2282 |
2239 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { | 2283 if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) { |
2240 u->length = 0; | 2284 ctx->done = 1; |
2241 | |
2242 if (ctx->in == NULL | |
2243 && ctx->out == NULL | |
2244 && ctx->output_closed | |
2245 && b->last == b->pos) | |
2246 { | |
2247 u->keepalive = 1; | |
2248 } | |
2249 | |
2250 break; | |
2251 } | 2285 } |
2252 } | 2286 } |
2253 | 2287 |
2254 return NGX_OK; | 2288 return NGX_OK; |
2255 } | 2289 } |