Proxy: extracted control frame and skip functions for HTTP/2.

This commit is contained in:
Zhidao HONG 2025-11-30 16:27:26 +00:00 committed by Roman Arutyunyan
parent 2a0342a17d
commit fdd8e97558
1 changed files with 154 additions and 196 deletions

View File

@ -117,6 +117,10 @@ static ngx_int_t ngx_http_proxy_v2_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_v2_filter_init(void *data); static ngx_int_t ngx_http_proxy_v2_filter_init(void *data);
static ngx_int_t ngx_http_proxy_v2_non_buffered_filter(void *data, static ngx_int_t ngx_http_proxy_v2_non_buffered_filter(void *data,
ssize_t bytes); ssize_t bytes);
static ngx_int_t ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx,
ngx_buf_t *b);
static ngx_int_t ngx_http_proxy_v2_process_frames(ngx_http_request_t *r, static ngx_int_t ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b); ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
@ -1391,118 +1395,31 @@ ngx_http_proxy_v2_process_header(ngx_http_request_t *r)
return NGX_HTTP_UPSTREAM_INVALID_HEADER; return NGX_HTTP_UPSTREAM_INVALID_HEADER;
} }
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
rc = ngx_http_proxy_v2_parse_goaway(r, ctx, b); if (rc == NGX_AGAIN) {
return NGX_AGAIN;
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ctx->goaway = 1;
continue;
} }
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { if (rc == NGX_ERROR) {
rc = ngx_http_proxy_v2_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_proxy_v2_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_proxy_v2_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
continue;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_HTTP_UPSTREAM_INVALID_HEADER; return NGX_HTTP_UPSTREAM_INVALID_HEADER;
} }
if (rc == NGX_OK) {
continue;
}
if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME
&& ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME) && ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME)
{ {
/* priority, unknown frames */ /* priority, unknown frames */
if (b->last - b->pos < (ssize_t) ctx->rest) { rc = ngx_http_proxy_v2_skip_frame(ctx, b);
ctx->rest -= b->last - b->pos;
b->pos = b->last; if (rc == NGX_AGAIN) {
return NGX_AGAIN; return NGX_AGAIN;
} }
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
continue; continue;
} }
@ -1807,6 +1724,134 @@ ngx_http_proxy_v2_non_buffered_filter(void *data, ssize_t bytes)
} }
static ngx_int_t
ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
u = r->upstream;
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
rc = ngx_http_proxy_v2_parse_goaway(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_ERROR;
}
ctx->goaway = 1;
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {
rc = ngx_http_proxy_v2_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_proxy_v2_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_proxy_v2_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
return NGX_OK;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_ERROR;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
{
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
return NGX_OK;
}
static ngx_int_t static ngx_int_t
ngx_http_proxy_v2_process_frames(ngx_http_request_t *r, ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b) ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
@ -1990,103 +2035,20 @@ ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
continue; continue;
} }
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) { rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
rc = ngx_http_proxy_v2_parse_goaway(r, ctx, b); if (rc == NGX_AGAIN) {
return NGX_AGAIN;
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_ERROR;
}
ctx->goaway = 1;
continue;
} }
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) { if (rc == NGX_ERROR) {
rc = ngx_http_proxy_v2_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_proxy_v2_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_proxy_v2_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
continue;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_ERROR; return NGX_ERROR;
} }
if (rc == NGX_OK) {
continue;
}
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME
|| ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) || ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME)
{ {
@ -2171,16 +2133,12 @@ ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
/* priority, unknown frames */ /* priority, unknown frames */
if (b->last - b->pos < (ssize_t) ctx->rest) { rc = ngx_http_proxy_v2_skip_frame(ctx, b);
ctx->rest -= b->last - b->pos;
b->pos = b->last; if (rc == NGX_AGAIN) {
return NGX_AGAIN; return NGX_AGAIN;
} }
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_proxy_v2_st_start;
continue; continue;
} }