156 строки
5.7 KiB
Diff
156 строки
5.7 KiB
Diff
|
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
|
||
|
index 7c05ff1e7..13dcd0e0f 100644
|
||
|
--- a/src/http/v2/ngx_http_v2.c
|
||
|
+++ b/src/http/v2/ngx_http_v2.c
|
||
|
@@ -194,6 +194,9 @@ static void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node);
|
||
|
|
||
|
static void ngx_http_v2_pool_cleanup(void *data);
|
||
|
|
||
|
+static void ngx_http_v2_update_stream_rate(ngx_http_v2_connection_t *h2c);
|
||
|
+
|
||
|
+
|
||
|
|
||
|
static ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = {
|
||
|
ngx_http_v2_state_data, /* NGX_HTTP_V2_DATA_FRAME */
|
||
|
@@ -1190,6 +1193,23 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
return ngx_http_v2_state_complete(h2c, pos, end);
|
||
|
}
|
||
|
|
||
|
+static void
|
||
|
+ngx_http_v2_update_stream_rate(ngx_http_v2_connection_t *h2c)
|
||
|
+{
|
||
|
+ ngx_http_v2_srv_conf_t *h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
|
||
|
+ ngx_http_v2_module);
|
||
|
+
|
||
|
+ /* dividing time by configured interval gives us the fixed window id */
|
||
|
+ if (h2c->last_stream_record_time / h2scf->stream_rate_interval_ms
|
||
|
+ != ngx_current_msec / h2scf->stream_rate_interval_ms)
|
||
|
+ {
|
||
|
+ h2c->stream_rate = 0;
|
||
|
+ h2c->last_stream_record_time = ngx_current_msec;
|
||
|
+ }
|
||
|
+
|
||
|
+ h2c->stream_rate++;
|
||
|
+}
|
||
|
+
|
||
|
|
||
|
static u_char *
|
||
|
ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
@@ -1321,6 +1341,27 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
goto rst_stream;
|
||
|
}
|
||
|
|
||
|
+ /*
|
||
|
+ * track the number of streams per connection
|
||
|
+ * if stream rate exceeds the limit, send a GOAWAY frame
|
||
|
+ */
|
||
|
+ ngx_http_v2_update_stream_rate(h2c);
|
||
|
+
|
||
|
+ if (h2scf->max_stream_rate != 0
|
||
|
+ && h2c->stream_rate >= (double) h2scf->max_stream_rate) {
|
||
|
+ ngx_log_error(NGX_LOG_ERR, h2c->connection->log, 0,
|
||
|
+ "frameshift detected, conn: %d, mitigation enabled: %d, stream rate: %ui",
|
||
|
+ h2c->connection->fd, h2scf->enable_frameshift_mitigation, h2c->stream_rate);
|
||
|
+
|
||
|
+ if (h2scf->enable_frameshift_mitigation) {
|
||
|
+ /*
|
||
|
+ * finalize connection sends goaway and closes the connection
|
||
|
+ * when there are no more active streams
|
||
|
+ */
|
||
|
+ return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
if (!h2c->settings_ack
|
||
|
&& !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
|
||
|
&& h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
|
||
|
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
|
||
|
index cb9014ccf..cf5510165 100644
|
||
|
--- a/src/http/v2/ngx_http_v2_module.h
|
||
|
+++ b/src/http/v2/ngx_http_v2_module.h
|
||
|
@@ -26,6 +26,9 @@ typedef struct {
|
||
|
ngx_uint_t concurrent_pushes;
|
||
|
size_t preread_size;
|
||
|
ngx_uint_t streams_index_mask;
|
||
|
+ ngx_uint_t max_stream_rate;
|
||
|
+ ngx_msec_t stream_rate_interval_ms;
|
||
|
+ ngx_flag_t enable_frameshift_mitigation;
|
||
|
} ngx_http_v2_srv_conf_t;
|
||
|
|
||
|
|
||
|
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
|
||
|
index cb9014ccf..cf5510165 100644
|
||
|
--- a/src/http/v2/ngx_http_v2.h
|
||
|
+++ b/src/http/v2/ngx_http_v2.h
|
||
|
@@ -126,6 +126,9 @@ struct ngx_http_v2_connection_s {
|
||
|
ngx_uint_t idle;
|
||
|
ngx_uint_t priority_limit;
|
||
|
|
||
|
+ ngx_uint_t stream_rate;
|
||
|
+ ngx_msec_t last_stream_record_time;
|
||
|
+
|
||
|
ngx_uint_t pushing;
|
||
|
ngx_uint_t concurrent_pushes;
|
||
|
|
||
|
diff --git a/src/http/v2/ngx_http_v2_module.c b/src/http/v2/ngx_http_v2_module.c
|
||
|
index 62af9a543..90266404c 100644
|
||
|
--- a/src/http/v2/ngx_http_v2_module.c
|
||
|
+++ b/src/http/v2/ngx_http_v2_module.c
|
||
|
@@ -103,6 +103,27 @@ static ngx_command_t ngx_http_v2_commands[] = {
|
||
|
offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),
|
||
|
NULL },
|
||
|
|
||
|
+ { ngx_string("http2_max_stream_rate"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||
|
+ ngx_conf_set_num_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_v2_srv_conf_t, max_stream_rate),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("http2_stream_rate_interval_ms"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||
|
+ ngx_conf_set_msec_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_v2_srv_conf_t, stream_rate_interval_ms),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("http2_frameshift_mitigation"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_flag_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_v2_srv_conf_t, enable_frameshift_mitigation),
|
||
|
+ NULL },
|
||
|
+
|
||
|
{ ngx_string("http2_max_requests"),
|
||
|
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||
|
ngx_http_v2_obsolete,
|
||
|
@@ -323,6 +344,12 @@ ngx_http_v2_create_srv_conf(ngx_conf_t *cf)
|
||
|
|
||
|
h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;
|
||
|
|
||
|
+ h2scf->max_stream_rate = NGX_CONF_UNSET_UINT;
|
||
|
+
|
||
|
+ h2scf->stream_rate_interval_ms = NGX_CONF_UNSET_MSEC;
|
||
|
+
|
||
|
+ h2scf->enable_frameshift_mitigation = NGX_CONF_UNSET;
|
||
|
+
|
||
|
return h2scf;
|
||
|
}
|
||
|
|
||
|
@@ -345,6 +372,15 @@ ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||
|
ngx_conf_merge_uint_value(conf->streams_index_mask,
|
||
|
prev->streams_index_mask, 32 - 1);
|
||
|
|
||
|
+ ngx_conf_merge_uint_value(conf->max_stream_rate,
|
||
|
+ prev->max_stream_rate, 0);
|
||
|
+
|
||
|
+ ngx_conf_merge_uint_value(conf->stream_rate_interval_ms,
|
||
|
+ prev->stream_rate_interval_ms, 100.0);
|
||
|
+
|
||
|
+ ngx_conf_merge_value(conf->enable_frameshift_mitigation,
|
||
|
+ prev->enable_frameshift_mitigation, 0);
|
||
|
+
|
||
|
return NGX_CONF_OK;
|
||
|
}
|
||
|
|