This commit is contained in:
Demi Marie Obenour 2026-06-26 00:51:58 +00:00 committed by GitHub
commit 7aefd11e7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 119 additions and 4 deletions

View file

@ -1810,9 +1810,9 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
if (ctx->status.code == NGX_HTTP_EARLY_HINTS) {
if (ctx->status.code < NGX_HTTP_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent HTTP/1.0 response with early hints");
"upstream sent 1xx HTTP/1.0 response");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}

View file

@ -482,7 +482,7 @@ ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
} else {
if (ret->status > 999) {
if (ret->status > 999 || ret->status < NGX_HTTP_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid return code \"%V\"", &value[1]);
return NGX_CONF_ERROR;

View file

@ -1804,6 +1804,15 @@ ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
return rc;
}
/*
* Can't respond 1xx, as that would mean a subsequent response is
* coming or the protocol is switching. Can't respond 2xx to a
* CONNECT request, as that would mean a tunnel has opened.
*/
if (status < (r->method == NGX_HTTP_CONNECT ? 300 : NGX_HTTP_OK)) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->headers_out.status = status;
if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {

View file

@ -27,6 +27,8 @@ static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_process_upgrade(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
#endif
static void ngx_http_upstream_init_request(ngx_http_request_t *r);
@ -330,6 +332,10 @@ static ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, content_encoding), 0 },
{ ngx_string("Upgrade"),
ngx_http_upstream_process_upgrade, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_null_string, NULL, 0, NULL, 0, 0 }
};
@ -2605,6 +2611,96 @@ done:
/* rc == NGX_OK */
/*
* Check that we don't send a 0xx response to the client.
* Also check that we don't send a 1xx response to a client that can't
* handle it, and that the connection was upgraded if it was supposed
* to be.
*/
if (r->method == NGX_HTTP_CONNECT
&& u->headers_in.status_n >= NGX_HTTP_OK
&& u->headers_in.status_n <= 299)
{
if (!u->upgrade) {
/*
* Something (probably an upstream server) successfully handled
* a CONNECT request, but this isn't supported.
*/
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Successful response to CONNECT request, "
"but no upgrade. Check that your upstream "
"servers reject CONNECT requests.");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (r->http_version != NGX_HTTP_VERSION_11) {
/* Rejected in ngx_http_request.c */
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"Attempt to upgrade non-HTTP/1.1 connection");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
} else if (u->upgrade) {
/* Someone upgraded the connection when they should not have. */
if (u->headers_in.status_n != NGX_HTTP_SWITCHING_PROTOCOLS) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"Upgrade flag sent, but status is %ui (not 101)",
u->headers_in.status_n);
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (r->http_version != NGX_HTTP_VERSION_11) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Upstream sent 101 response to non-HTTP/1.1 request");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
if (r->method == NGX_HTTP_HEAD) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Upstream sent 101 response to HEAD request");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
if (!r->headers_in.upgrade) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Upstream sent 101 response, but client didn't "
"send Upgrade header");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
if (!u->headers_in.upgrade) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Upstream sent 101 without setting Upgrade header");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
} else if (u->headers_in.status_n < NGX_HTTP_OK) {
/*
* This means that the upstream protocol (probably FastCGI, uWSGI,
* or SCGI) cannot handle 1xx responses of the type the upstream server
* provided. It could also mean that 0xx responses were not rejected.
* Log an error and return 502 Bad Gateway.
*
* 1xx responses from upstream HTTP servers don't reach here. Their
* protocol handlers return NGX_HTTP_UPSTREAM_EARLY_HINTS from their
* process_headers() function when they get a 1xx response. That
* causes a 1xx response to be sent to the client.
*/
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"Upstream sent response with status %ui that upstream "
"protocol does not support", u->headers_in.status_n);
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
u->state->header_time = ngx_current_msec - u->start_time;
if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {
@ -2644,7 +2740,7 @@ ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints");
if (u->conf->pass_early_hints) {
if (u->conf->pass_early_hints && r->http_version >= NGX_HTTP_VERSION_11) {
u->early_hints_length += u->buffer.pos - u->buffer.start;
@ -5458,6 +5554,15 @@ ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
}
static ngx_int_t
ngx_http_upstream_process_upgrade(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
r->upstream->headers_in.upgrade = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)

View file

@ -311,6 +311,7 @@ typedef struct {
unsigned chunked:1;
unsigned no_cache:1;
unsigned expired:1;
unsigned upgrade:1;
} ngx_http_upstream_headers_in_t;