1309 lines
44 KiB
Diff
1309 lines
44 KiB
Diff
|
Add HTTP2 HPACK Encoding Support.
|
||
|
Add Dynamic TLS Record support.
|
||
|
|
||
|
Using: patch -p1 < nginx.patch
|
||
|
|
||
|
diff -uNr a/auto/modules b/auto/modules
|
||
|
--- a/auto/modules 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/auto/modules 2020-03-10 15:47:10.738727477 +0800
|
||
|
@@ -423,6 +423,10 @@
|
||
|
. auto/module
|
||
|
fi
|
||
|
|
||
|
+ if [ $HTTP_V2_HPACK_ENC = YES ]; then
|
||
|
+ have=NGX_HTTP_V2_HPACK_ENC . auto/have
|
||
|
+ fi
|
||
|
+
|
||
|
if :; then
|
||
|
ngx_module_name=ngx_http_static_module
|
||
|
ngx_module_incs=
|
||
|
diff -uNr a/auto/options b/auto/options
|
||
|
--- a/auto/options 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/auto/options 2020-03-10 15:47:10.739727485 +0800
|
||
|
@@ -59,6 +59,7 @@
|
||
|
HTTP_GZIP=YES
|
||
|
HTTP_SSL=NO
|
||
|
HTTP_V2=NO
|
||
|
+HTTP_V2_HPACK_ENC=NO
|
||
|
HTTP_SSI=YES
|
||
|
HTTP_REALIP=NO
|
||
|
HTTP_XSLT=NO
|
||
|
@@ -224,6 +225,7 @@
|
||
|
|
||
|
--with-http_ssl_module) HTTP_SSL=YES ;;
|
||
|
--with-http_v2_module) HTTP_V2=YES ;;
|
||
|
+ --with-http_v2_hpack_enc) HTTP_V2_HPACK_ENC=YES ;;
|
||
|
--with-http_realip_module) HTTP_REALIP=YES ;;
|
||
|
--with-http_addition_module) HTTP_ADDITION=YES ;;
|
||
|
--with-http_xslt_module) HTTP_XSLT=YES ;;
|
||
|
@@ -439,6 +441,7 @@
|
||
|
|
||
|
--with-http_ssl_module enable ngx_http_ssl_module
|
||
|
--with-http_v2_module enable ngx_http_v2_module
|
||
|
+ --with-http_v2_hpack_enc enable ngx_http_v2_hpack_enc
|
||
|
--with-http_realip_module enable ngx_http_realip_module
|
||
|
--with-http_addition_module enable ngx_http_addition_module
|
||
|
--with-http_xslt_module enable ngx_http_xslt_module
|
||
|
diff -uNr a/src/core/ngx_murmurhash.c b/src/core/ngx_murmurhash.c
|
||
|
--- a/src/core/ngx_murmurhash.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/core/ngx_murmurhash.c 2020-03-10 15:47:10.739727485 +0800
|
||
|
@@ -50,3 +50,63 @@
|
||
|
|
||
|
return h;
|
||
|
}
|
||
|
+
|
||
|
+
|
||
|
+uint64_t
|
||
|
+ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed)
|
||
|
+{
|
||
|
+ uint64_t h, k;
|
||
|
+
|
||
|
+ h = seed ^ len;
|
||
|
+
|
||
|
+ while (len >= 8) {
|
||
|
+ k = data[0];
|
||
|
+ k |= data[1] << 8;
|
||
|
+ k |= data[2] << 16;
|
||
|
+ k |= data[3] << 24;
|
||
|
+ k |= (uint64_t)data[4] << 32;
|
||
|
+ k |= (uint64_t)data[5] << 40;
|
||
|
+ k |= (uint64_t)data[6] << 48;
|
||
|
+ k |= (uint64_t)data[7] << 56;
|
||
|
+
|
||
|
+ k *= 0xc6a4a7935bd1e995ull;
|
||
|
+ k ^= k >> 47;
|
||
|
+ k *= 0xc6a4a7935bd1e995ull;
|
||
|
+
|
||
|
+ h ^= k;
|
||
|
+ h *= 0xc6a4a7935bd1e995ull;
|
||
|
+
|
||
|
+ data += 8;
|
||
|
+ len -= 8;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (len) {
|
||
|
+ case 7:
|
||
|
+ h ^= (uint64_t)data[6] << 48;
|
||
|
+ /* fall through */
|
||
|
+ case 6:
|
||
|
+ h ^= (uint64_t)data[5] << 40;
|
||
|
+ /* fall through */
|
||
|
+ case 5:
|
||
|
+ h ^= (uint64_t)data[4] << 32;
|
||
|
+ /* fall through */
|
||
|
+ case 4:
|
||
|
+ h ^= data[3] << 24;
|
||
|
+ /* fall through */
|
||
|
+ case 3:
|
||
|
+ h ^= data[2] << 16;
|
||
|
+ /* fall through */
|
||
|
+ case 2:
|
||
|
+ h ^= data[1] << 8;
|
||
|
+ /* fall through */
|
||
|
+ case 1:
|
||
|
+ h ^= data[0];
|
||
|
+ h *= 0xc6a4a7935bd1e995ull;
|
||
|
+ }
|
||
|
+
|
||
|
+ h ^= h >> 47;
|
||
|
+ h *= 0xc6a4a7935bd1e995ull;
|
||
|
+ h ^= h >> 47;
|
||
|
+
|
||
|
+ return h;
|
||
|
+}
|
||
|
diff -uNr a/src/core/ngx_murmurhash.h b/src/core/ngx_murmurhash.h
|
||
|
--- a/src/core/ngx_murmurhash.h 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/core/ngx_murmurhash.h 2020-03-10 15:47:10.739727485 +0800
|
||
|
@@ -15,5 +15,7 @@
|
||
|
|
||
|
uint32_t ngx_murmur_hash2(u_char *data, size_t len);
|
||
|
|
||
|
+uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed);
|
||
|
+
|
||
|
|
||
|
#endif /* _NGX_MURMURHASH_H_INCLUDED_ */
|
||
|
diff -uNr a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
|
||
|
--- a/src/event/ngx_event_openssl.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/event/ngx_event_openssl.c 2020-03-10 15:47:11.441732971 +0800
|
||
|
@@ -1507,6 +1507,7 @@
|
||
|
|
||
|
sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
|
||
|
sc->buffer_size = ssl->buffer_size;
|
||
|
+ sc->dyn_rec = ssl->dyn_rec;
|
||
|
|
||
|
sc->session_ctx = ssl->ctx;
|
||
|
|
||
|
@@ -2404,6 +2405,41 @@
|
||
|
|
||
|
for ( ;; ) {
|
||
|
|
||
|
+ /* Dynamic record resizing:
|
||
|
+ We want the initial records to fit into one TCP segment
|
||
|
+ so we don't get TCP HoL blocking due to TCP Slow Start.
|
||
|
+ A connection always starts with small records, but after
|
||
|
+ a given amount of records sent, we make the records larger
|
||
|
+ to reduce header overhead.
|
||
|
+ After a connection has idled for a given timeout, begin
|
||
|
+ the process from the start. The actual parameters are
|
||
|
+ configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */
|
||
|
+
|
||
|
+ if (c->ssl->dyn_rec.timeout > 0 ) {
|
||
|
+
|
||
|
+ if (ngx_current_msec - c->ssl->dyn_rec_last_write >
|
||
|
+ c->ssl->dyn_rec.timeout)
|
||
|
+ {
|
||
|
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo;
|
||
|
+ c->ssl->dyn_rec_records_sent = 0;
|
||
|
+
|
||
|
+ } else {
|
||
|
+ if (c->ssl->dyn_rec_records_sent >
|
||
|
+ c->ssl->dyn_rec.threshold * 2)
|
||
|
+ {
|
||
|
+ buf->end = buf->start + c->ssl->buffer_size;
|
||
|
+
|
||
|
+ } else if (c->ssl->dyn_rec_records_sent >
|
||
|
+ c->ssl->dyn_rec.threshold)
|
||
|
+ {
|
||
|
+ buf->end = buf->start + c->ssl->dyn_rec.size_hi;
|
||
|
+
|
||
|
+ } else {
|
||
|
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
while (in && buf->last < buf->end && send < limit) {
|
||
|
if (in->buf->last_buf || in->buf->flush) {
|
||
|
flush = 1;
|
||
|
@@ -2511,6 +2547,9 @@
|
||
|
|
||
|
if (n > 0) {
|
||
|
|
||
|
+ c->ssl->dyn_rec_records_sent++;
|
||
|
+ c->ssl->dyn_rec_last_write = ngx_current_msec;
|
||
|
+
|
||
|
if (c->ssl->saved_read_handler) {
|
||
|
|
||
|
c->read->handler = c->ssl->saved_read_handler;
|
||
|
diff -uNr a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
|
||
|
--- a/src/event/ngx_event_openssl.h 2021-04-20 19:35:47.000000000 +0600
|
||
|
+++ b/src/event/ngx_event_openssl.h 2021-04-21 14:47:37.000000000 +0600
|
||
|
@@ -66,11 +66,19 @@
|
||
|
|
||
|
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
|
||
|
|
||
|
+typedef struct {
|
||
|
+ ngx_msec_t timeout;
|
||
|
+ ngx_uint_t threshold;
|
||
|
+ size_t size_lo;
|
||
|
+ size_t size_hi;
|
||
|
+} ngx_ssl_dyn_rec_t;
|
||
|
+
|
||
|
|
||
|
struct ngx_ssl_s {
|
||
|
SSL_CTX *ctx;
|
||
|
ngx_log_t *log;
|
||
|
size_t buffer_size;
|
||
|
+ ngx_ssl_dyn_rec_t dyn_rec;
|
||
|
};
|
||
|
|
||
|
|
||
|
@@ -106,6 +114,10 @@
|
||
|
unsigned in_ocsp:1;
|
||
|
unsigned early_preread:1;
|
||
|
unsigned write_blocked:1;
|
||
|
+
|
||
|
+ ngx_ssl_dyn_rec_t dyn_rec;
|
||
|
+ ngx_msec_t dyn_rec_last_write;
|
||
|
+ ngx_uint_t dyn_rec_records_sent;
|
||
|
};
|
||
|
|
||
|
|
||
|
@@ -115,7 +127,7 @@
|
||
|
#define NGX_SSL_DFLT_BUILTIN_SCACHE -5
|
||
|
|
||
|
|
||
|
-#define NGX_SSL_MAX_SESSION_SIZE 4096
|
||
|
+#define NGX_SSL_MAX_SESSION_SIZE 16384
|
||
|
|
||
|
typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t;
|
||
|
|
||
|
diff -uNr a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
|
||
|
--- a/src/http/modules/ngx_http_ssl_module.c 2021-04-20 19:35:47.000000000 +0600
|
||
|
+++ b/src/http/modules/ngx_http_ssl_module.c 2021-04-21 14:55:37.000000000 +0600
|
||
|
@@ -301,6 +301,41 @@
|
||
|
offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),
|
||
|
NULL },
|
||
|
|
||
|
+ { ngx_string("ssl_dyn_rec_enable"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_flag_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("ssl_dyn_rec_timeout"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_msec_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("ssl_dyn_rec_size_lo"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_size_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("ssl_dyn_rec_size_hi"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_size_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),
|
||
|
+ NULL },
|
||
|
+
|
||
|
+ { ngx_string("ssl_dyn_rec_threshold"),
|
||
|
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||
|
+ ngx_conf_set_num_slot,
|
||
|
+ NGX_HTTP_SRV_CONF_OFFSET,
|
||
|
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),
|
||
|
+ NULL },
|
||
|
+
|
||
|
ngx_null_command
|
||
|
};
|
||
|
|
||
|
@@ -637,6 +672,11 @@
|
||
|
sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;
|
||
|
sscf->stapling = NGX_CONF_UNSET;
|
||
|
sscf->stapling_verify = NGX_CONF_UNSET;
|
||
|
+ sscf->dyn_rec_enable = NGX_CONF_UNSET;
|
||
|
+ sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;
|
||
|
+ sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;
|
||
|
+ sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;
|
||
|
+ sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;
|
||
|
|
||
|
return sscf;
|
||
|
}
|
||
|
@@ -712,6 +752,20 @@
|
||
|
ngx_conf_merge_str_value(conf->stapling_responder,
|
||
|
prev->stapling_responder, "");
|
||
|
|
||
|
+ ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);
|
||
|
+ ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,
|
||
|
+ 1000);
|
||
|
+ /* Default sizes for the dynamic record sizes are defined to fit maximal
|
||
|
+ TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:
|
||
|
+ 1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */
|
||
|
+ ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,
|
||
|
+ 1369);
|
||
|
+ /* 4229 = (1500 - 40 - 20 - 10) * 3 - 61 */
|
||
|
+ ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,
|
||
|
+ 4229);
|
||
|
+ ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,
|
||
|
+ 40);
|
||
|
+
|
||
|
conf->ssl.log = cf->log;
|
||
|
|
||
|
if (conf->enable) {
|
||
|
@@ -943,6 +997,28 @@
|
||
|
return NGX_CONF_ERROR;
|
||
|
}
|
||
|
|
||
|
+ if (conf->dyn_rec_enable) {
|
||
|
+ conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;
|
||
|
+ conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;
|
||
|
+
|
||
|
+ if (conf->buffer_size > conf->dyn_rec_size_lo) {
|
||
|
+ conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;
|
||
|
+
|
||
|
+ } else {
|
||
|
+ conf->ssl.dyn_rec.size_lo = conf->buffer_size;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (conf->buffer_size > conf->dyn_rec_size_hi) {
|
||
|
+ conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;
|
||
|
+
|
||
|
+ } else {
|
||
|
+ conf->ssl.dyn_rec.size_hi = conf->buffer_size;
|
||
|
+ }
|
||
|
+
|
||
|
+ } else {
|
||
|
+ conf->ssl.dyn_rec.timeout = 0;
|
||
|
+ }
|
||
|
+
|
||
|
return NGX_CONF_OK;
|
||
|
}
|
||
|
|
||
|
diff -uNr a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
|
||
|
--- a/src/http/modules/ngx_http_ssl_module.h 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/modules/ngx_http_ssl_module.h 2020-03-10 15:47:11.442732979 +0800
|
||
|
@@ -61,6 +61,12 @@
|
||
|
|
||
|
u_char *file;
|
||
|
ngx_uint_t line;
|
||
|
+
|
||
|
+ ngx_flag_t dyn_rec_enable;
|
||
|
+ ngx_msec_t dyn_rec_timeout;
|
||
|
+ size_t dyn_rec_size_lo;
|
||
|
+ size_t dyn_rec_size_hi;
|
||
|
+ ngx_uint_t dyn_rec_threshold;
|
||
|
} ngx_http_ssl_srv_conf_t;
|
||
|
|
||
|
|
||
|
diff -uNr a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
|
||
|
--- a/src/http/v2/ngx_http_v2.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/v2/ngx_http_v2.c 2020-03-10 15:47:10.741727500 +0800
|
||
|
@@ -271,6 +271,8 @@
|
||
|
|
||
|
h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
|
||
|
|
||
|
+ h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE;
|
||
|
+
|
||
|
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
|
||
|
|
||
|
h2c->concurrent_pushes = h2scf->concurrent_pushes;
|
||
|
@@ -2098,6 +2100,14 @@
|
||
|
case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:
|
||
|
|
||
|
h2c->table_update = 1;
|
||
|
+
|
||
|
+ if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {
|
||
|
+ h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE;
|
||
|
+ } else {
|
||
|
+ h2c->max_hpack_table_size = value;
|
||
|
+ }
|
||
|
+
|
||
|
+ h2c->indicate_resize = 1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
diff -uNr a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c
|
||
|
--- a/src/http/v2/ngx_http_v2_encode.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/v2/ngx_http_v2_encode.c 2020-03-10 15:47:10.742727508 +0800
|
||
|
@@ -10,7 +10,7 @@
|
||
|
#include <ngx_http.h>
|
||
|
|
||
|
|
||
|
-static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
|
||
|
+u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
|
||
|
ngx_uint_t value);
|
||
|
|
||
|
|
||
|
@@ -40,7 +40,7 @@
|
||
|
}
|
||
|
|
||
|
|
||
|
-static u_char *
|
||
|
+u_char *
|
||
|
ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
|
||
|
{
|
||
|
if (value < prefix) {
|
||
|
diff -uNr a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
|
||
|
--- a/src/http/v2/ngx_http_v2_filter_module.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/v2/ngx_http_v2_filter_module.c 2020-03-10 15:47:10.743727516 +0800
|
||
|
@@ -23,10 +23,53 @@
|
||
|
#define ngx_http_v2_literal_size(h) \
|
||
|
(ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
|
||
|
|
||
|
+#define ngx_http_v2_indexed(i) (128 + (i))
|
||
|
+#define ngx_http_v2_inc_indexed(i) (64 + (i))
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_ENCODE_RAW 0
|
||
|
+#define NGX_HTTP_V2_ENCODE_HUFF 0x80
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_AUTHORITY_INDEX 1
|
||
|
+#define NGX_HTTP_V2_METHOD_GET_INDEX 2
|
||
|
+#define NGX_HTTP_V2_PATH_INDEX 4
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6
|
||
|
+#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_STATUS_INDEX 8
|
||
|
+#define NGX_HTTP_V2_STATUS_200_INDEX 8
|
||
|
+#define NGX_HTTP_V2_STATUS_204_INDEX 9
|
||
|
+#define NGX_HTTP_V2_STATUS_206_INDEX 10
|
||
|
+#define NGX_HTTP_V2_STATUS_304_INDEX 11
|
||
|
+#define NGX_HTTP_V2_STATUS_400_INDEX 12
|
||
|
+#define NGX_HTTP_V2_STATUS_404_INDEX 13
|
||
|
+#define NGX_HTTP_V2_STATUS_500_INDEX 14
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
|
||
|
+#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
|
||
|
+#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
|
||
|
+#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
|
||
|
+#define NGX_HTTP_V2_DATE_INDEX 33
|
||
|
+#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
|
||
|
+#define NGX_HTTP_V2_LOCATION_INDEX 46
|
||
|
+#define NGX_HTTP_V2_SERVER_INDEX 54
|
||
|
+#define NGX_HTTP_V2_USER_AGENT_INDEX 58
|
||
|
+#define NGX_HTTP_V2_VARY_INDEX 59
|
||
|
|
||
|
#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
|
||
|
|
||
|
|
||
|
+static const struct {
|
||
|
+ u_char *name;
|
||
|
+ u_char const len;
|
||
|
+} push_header[] = {
|
||
|
+ { (u_char*)":authority" , 10 },
|
||
|
+ { (u_char*)"accept-encoding" , 15 },
|
||
|
+ { (u_char*)"accept-language" , 15 },
|
||
|
+ { (u_char*)"user-agent" , 10 }
|
||
|
+};
|
||
|
+
|
||
|
+
|
||
|
typedef struct {
|
||
|
ngx_str_t name;
|
||
|
u_char index;
|
||
|
@@ -155,11 +198,9 @@
|
||
|
#endif
|
||
|
|
||
|
static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
|
||
|
- static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
|
||
|
|
||
|
static size_t nginx_ver_build_len =
|
||
|
ngx_http_v2_literal_size(NGINX_VER_BUILD);
|
||
|
- static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
|
||
|
|
||
|
stream = r->stream;
|
||
|
|
||
|
@@ -435,7 +476,7 @@
|
||
|
}
|
||
|
|
||
|
tmp = ngx_palloc(r->pool, tmp_len);
|
||
|
- pos = ngx_pnalloc(r->pool, len);
|
||
|
+ pos = ngx_pnalloc(r->pool, len + 15 + 1);
|
||
|
|
||
|
if (pos == NULL || tmp == NULL) {
|
||
|
return NGX_ERROR;
|
||
|
@@ -443,11 +484,16 @@
|
||
|
|
||
|
start = pos;
|
||
|
|
||
|
- if (h2c->table_update) {
|
||
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 table size update: 0");
|
||
|
- *pos++ = (1 << 5) | 0;
|
||
|
- h2c->table_update = 0;
|
||
|
+ h2c = r->stream->connection;
|
||
|
+
|
||
|
+ if (h2c->indicate_resize) {
|
||
|
+ *pos = 32;
|
||
|
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),
|
||
|
+ h2c->max_hpack_table_size);
|
||
|
+ h2c->indicate_resize = 0;
|
||
|
+#if (NGX_HTTP_V2_HPACK_ENC)
|
||
|
+ ngx_http_v2_table_resize(h2c);
|
||
|
+#endif
|
||
|
}
|
||
|
|
||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
@@ -458,67 +504,28 @@
|
||
|
*pos++ = status;
|
||
|
|
||
|
} else {
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
|
||
|
- *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
|
||
|
- pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
|
||
|
+ ngx_sprintf(pos + 8, "%O3ui", r->headers_out.status);
|
||
|
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)":status",
|
||
|
+ sizeof(":status") - 1, pos + 8, 3, tmp);
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.server == NULL) {
|
||
|
-
|
||
|
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"server: %s\"",
|
||
|
- NGINX_VER);
|
||
|
-
|
||
|
- } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"server: %s\"",
|
||
|
- NGINX_VER_BUILD);
|
||
|
-
|
||
|
- } else {
|
||
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"server: nginx\"");
|
||
|
- }
|
||
|
-
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
|
||
|
-
|
||
|
- if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
|
||
|
- if (nginx_ver[0] == '\0') {
|
||
|
- p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
|
||
|
- sizeof(NGINX_VER) - 1, tmp);
|
||
|
- nginx_ver_len = p - nginx_ver;
|
||
|
- }
|
||
|
-
|
||
|
- pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
|
||
|
+ pos = ngx_http_v2_write_header_str("server", NGINX_VER);
|
||
|
|
||
|
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
|
||
|
- if (nginx_ver_build[0] == '\0') {
|
||
|
- p = ngx_http_v2_write_value(nginx_ver_build,
|
||
|
- (u_char *) NGINX_VER_BUILD,
|
||
|
- sizeof(NGINX_VER_BUILD) - 1, tmp);
|
||
|
- nginx_ver_build_len = p - nginx_ver_build;
|
||
|
- }
|
||
|
-
|
||
|
- pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
|
||
|
+ pos = ngx_http_v2_write_header_str("server", NGINX_VER_BUILD);
|
||
|
|
||
|
} else {
|
||
|
- pos = ngx_cpymem(pos, nginx, sizeof(nginx));
|
||
|
+ pos = ngx_http_v2_write_header_str("server", "nginx");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.date == NULL) {
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"date: %V\"",
|
||
|
- &ngx_cached_http_time);
|
||
|
-
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
|
||
|
- pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
|
||
|
- ngx_cached_http_time.len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header_tbl("date", ngx_cached_http_time);
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.content_type.len) {
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
|
||
|
-
|
||
|
if (r->headers_out.content_type_len == r->headers_out.content_type.len
|
||
|
&& r->headers_out.charset.len)
|
||
|
{
|
||
|
@@ -544,64 +551,36 @@
|
||
|
r->headers_out.content_type.data = p - len;
|
||
|
}
|
||
|
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"content-type: %V\"",
|
||
|
- &r->headers_out.content_type);
|
||
|
-
|
||
|
- pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
|
||
|
- r->headers_out.content_type.len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header_tbl("content-type",
|
||
|
+ r->headers_out.content_type);
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.content_length == NULL
|
||
|
&& r->headers_out.content_length_n >= 0)
|
||
|
{
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"content-length: %O\"",
|
||
|
- r->headers_out.content_length_n);
|
||
|
-
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
|
||
|
-
|
||
|
- p = pos;
|
||
|
- pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
|
||
|
- *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
|
||
|
+ p = ngx_sprintf(pos + 15, "%O", r->headers_out.content_length_n);
|
||
|
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"content-length",
|
||
|
+ sizeof("content-length") - 1, pos + 15,
|
||
|
+ p - (pos + 15), tmp);
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.last_modified == NULL
|
||
|
&& r->headers_out.last_modified_time != -1)
|
||
|
{
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
|
||
|
-
|
||
|
- ngx_http_time(pos, r->headers_out.last_modified_time);
|
||
|
+ ngx_http_time(pos + 14, r->headers_out.last_modified_time);
|
||
|
len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
|
||
|
-
|
||
|
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"last-modified: %*s\"",
|
||
|
- len, pos);
|
||
|
-
|
||
|
- /*
|
||
|
- * Date will always be encoded using huffman in the temporary buffer,
|
||
|
- * so it's safe here to use src and dst pointing to the same address.
|
||
|
- */
|
||
|
- pos = ngx_http_v2_write_value(pos, pos, len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"last-modified",
|
||
|
+ sizeof("last-modified") - 1, pos + 14,
|
||
|
+ len, tmp);
|
||
|
}
|
||
|
|
||
|
if (r->headers_out.location && r->headers_out.location->value.len) {
|
||
|
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"location: %V\"",
|
||
|
- &r->headers_out.location->value);
|
||
|
-
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
|
||
|
- pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
|
||
|
- r->headers_out.location->value.len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header_tbl("location", r->headers_out.location->value);
|
||
|
}
|
||
|
|
||
|
#if (NGX_HTTP_GZIP)
|
||
|
if (r->gzip_vary) {
|
||
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"vary: Accept-Encoding\"");
|
||
|
-
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
|
||
|
- pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
|
||
|
+ pos = ngx_http_v2_write_header_str("vary", "Accept-Encoding");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
@@ -624,23 +603,10 @@
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
-#if (NGX_DEBUG)
|
||
|
- if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
|
||
|
- ngx_strlow(tmp, header[i].key.data, header[i].key.len);
|
||
|
-
|
||
|
- ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 output header: \"%*s: %V\"",
|
||
|
- header[i].key.len, tmp, &header[i].value);
|
||
|
- }
|
||
|
-#endif
|
||
|
-
|
||
|
- *pos++ = 0;
|
||
|
-
|
||
|
- pos = ngx_http_v2_write_name(pos, header[i].key.data,
|
||
|
- header[i].key.len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data,
|
||
|
+ header[i].key.len, header[i].value.data,
|
||
|
+ header[i].value.len, tmp);
|
||
|
|
||
|
- pos = ngx_http_v2_write_value(pos, header[i].value.data,
|
||
|
- header[i].value.len, tmp);
|
||
|
}
|
||
|
|
||
|
fin = r->header_only
|
||
|
@@ -998,6 +964,7 @@
|
||
|
|
||
|
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
|
||
|
len += binary[i].len;
|
||
|
+ len += push_header[i].len + 1;
|
||
|
}
|
||
|
|
||
|
pos = ngx_pnalloc(r->pool, len);
|
||
|
@@ -1007,12 +974,17 @@
|
||
|
|
||
|
start = pos;
|
||
|
|
||
|
- if (h2c->table_update) {
|
||
|
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
- "http2 table size update: 0");
|
||
|
- *pos++ = (1 << 5) | 0;
|
||
|
- h2c->table_update = 0;
|
||
|
- }
|
||
|
+ h2c = r->stream->connection;
|
||
|
+
|
||
|
+ if (h2c->indicate_resize) {
|
||
|
+ *pos = 32;
|
||
|
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5),
|
||
|
+ h2c->max_hpack_table_size);
|
||
|
+ h2c->indicate_resize = 0;
|
||
|
+#if (NGX_HTTP_V2_HPACK_ENC)
|
||
|
+ ngx_http_v2_table_resize(h2c);
|
||
|
+#endif
|
||
|
+ }
|
||
|
|
||
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
"http2 push header: \":method: GET\"");
|
||
|
@@ -1022,8 +994,7 @@
|
||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
"http2 push header: \":path: %V\"", path);
|
||
|
|
||
|
- *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
|
||
|
- pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
|
||
|
+ pos = ngx_http_v2_write_header_pot(":path", path);
|
||
|
|
||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
"http2 push header: \":scheme: %V\"", &r->schema);
|
||
|
@@ -1048,11 +1019,15 @@
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
+ value = &(*h)->value;
|
||
|
+
|
||
|
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
|
||
|
"http2 push header: \"%V: %V\"",
|
||
|
&ph[i].name, &(*h)->value);
|
||
|
|
||
|
- pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
|
||
|
+ pos = ngx_http_v2_write_header(h2c, pos,
|
||
|
+ push_header[i].name, push_header[i].len, value->data, value->len,
|
||
|
+ tmp);
|
||
|
}
|
||
|
|
||
|
frame = ngx_http_v2_create_push_frame(r, start, pos);
|
||
|
diff -uNr a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
|
||
|
--- a/src/http/v2/ngx_http_v2.h 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/v2/ngx_http_v2.h 2020-03-10 15:47:10.741727500 +0800
|
||
|
@@ -52,6 +52,14 @@
|
||
|
#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1)
|
||
|
#define NGX_HTTP_V2_DEFAULT_WINDOW 65535
|
||
|
|
||
|
+#define HPACK_ENC_HTABLE_SZ 128 /* better to keep a PoT < 64k */
|
||
|
+#define HPACK_ENC_HTABLE_ENTRIES ((HPACK_ENC_HTABLE_SZ * 100) / 128)
|
||
|
+#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ 10 /* 10 is sufficient for most */
|
||
|
+#define HPACK_ENC_MAX_ENTRY 512 /* longest header size to match */
|
||
|
+
|
||
|
+#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE 4096
|
||
|
+#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE 16384 /* < 64k */
|
||
|
+
|
||
|
#define NGX_HTTP_V2_DEFAULT_WEIGHT 16
|
||
|
|
||
|
|
||
|
@@ -115,6 +123,46 @@
|
||
|
} ngx_http_v2_hpack_t;
|
||
|
|
||
|
|
||
|
+#if (NGX_HTTP_V2_HPACK_ENC)
|
||
|
+typedef struct {
|
||
|
+ uint64_t hash_val;
|
||
|
+ uint32_t index;
|
||
|
+ uint16_t pos;
|
||
|
+ uint16_t klen, vlen;
|
||
|
+ uint16_t size;
|
||
|
+ uint16_t next;
|
||
|
+} ngx_http_v2_hpack_enc_entry_t;
|
||
|
+
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ uint64_t hash_val;
|
||
|
+ uint32_t index;
|
||
|
+ uint16_t pos;
|
||
|
+ uint16_t klen;
|
||
|
+} ngx_http_v2_hpack_name_entry_t;
|
||
|
+
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ size_t size; /* size as defined in RFC 7541 */
|
||
|
+ uint32_t top; /* the last entry */
|
||
|
+ uint32_t pos;
|
||
|
+ uint16_t n_elems; /* number of elements */
|
||
|
+ uint16_t base; /* index of the oldest entry */
|
||
|
+ uint16_t last; /* index of the newest entry */
|
||
|
+
|
||
|
+ /* hash table for dynamic entries, instead using a generic hash table,
|
||
|
+ which would be too slow to process a significant amount of headers,
|
||
|
+ this table is not determenistic, and might ocasionally fail to insert
|
||
|
+ a value, at the cost of slightly worse compression, but significantly
|
||
|
+ faster performance */
|
||
|
+ ngx_http_v2_hpack_enc_entry_t htable[HPACK_ENC_HTABLE_SZ];
|
||
|
+ ngx_http_v2_hpack_name_entry_t heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ];
|
||
|
+ u_char storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE +
|
||
|
+ HPACK_ENC_MAX_ENTRY];
|
||
|
+} ngx_http_v2_hpack_enc_t;
|
||
|
+#endif
|
||
|
+
|
||
|
+
|
||
|
struct ngx_http_v2_connection_s {
|
||
|
ngx_connection_t *connection;
|
||
|
ngx_http_connection_t *http_connection;
|
||
|
@@ -136,6 +184,8 @@
|
||
|
|
||
|
size_t frame_size;
|
||
|
|
||
|
+ size_t max_hpack_table_size;
|
||
|
+
|
||
|
ngx_queue_t waiting;
|
||
|
|
||
|
ngx_http_v2_state_t state;
|
||
|
@@ -163,6 +213,11 @@
|
||
|
unsigned blocked:1;
|
||
|
unsigned goaway:1;
|
||
|
unsigned push_disabled:1;
|
||
|
+ unsigned indicate_resize:1;
|
||
|
+
|
||
|
+#if (NGX_HTTP_V2_HPACK_ENC)
|
||
|
+ ngx_http_v2_hpack_enc_t hpack_enc;
|
||
|
+#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
@@ -206,6 +261,8 @@
|
||
|
|
||
|
ngx_array_t *cookies;
|
||
|
|
||
|
+ size_t header_limit;
|
||
|
+
|
||
|
ngx_pool_t *pool;
|
||
|
|
||
|
unsigned waiting:1;
|
||
|
@@ -418,4 +475,35 @@
|
||
|
u_char *tmp, ngx_uint_t lower);
|
||
|
|
||
|
|
||
|
+u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
|
||
|
+ u_char *tmp, ngx_uint_t lower);
|
||
|
+
|
||
|
+u_char *
|
||
|
+ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value);
|
||
|
+
|
||
|
+#define ngx_http_v2_write_name(dst, src, len, tmp) \
|
||
|
+ ngx_http_v2_string_encode(dst, src, len, tmp, 1)
|
||
|
+#define ngx_http_v2_write_value(dst, src, len, tmp) \
|
||
|
+ ngx_http_v2_string_encode(dst, src, len, tmp, 0)
|
||
|
+
|
||
|
+u_char *
|
||
|
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
+ u_char *key, size_t key_len, u_char *value, size_t value_len,
|
||
|
+ u_char *tmp);
|
||
|
+
|
||
|
+void
|
||
|
+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c);
|
||
|
+
|
||
|
+#define ngx_http_v2_write_header_str(key, value) \
|
||
|
+ ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
|
||
|
+ (u_char *) value, sizeof(value) - 1, tmp);
|
||
|
+
|
||
|
+#define ngx_http_v2_write_header_tbl(key, val) \
|
||
|
+ ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
|
||
|
+ val.data, val.len, tmp);
|
||
|
+
|
||
|
+#define ngx_http_v2_write_header_pot(key, val) \
|
||
|
+ ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \
|
||
|
+ val->data, val->len, tmp);
|
||
|
+
|
||
|
#endif /* _NGX_HTTP_V2_H_INCLUDED_ */
|
||
|
diff -uNr a/src/http/v2/ngx_http_v2_table.c b/src/http/v2/ngx_http_v2_table.c
|
||
|
--- a/src/http/v2/ngx_http_v2_table.c 2020-03-03 23:04:21.000000000 +0800
|
||
|
+++ b/src/http/v2/ngx_http_v2_table.c 2020-03-10 15:47:10.743727516 +0800
|
||
|
@@ -361,3 +361,434 @@
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
+
|
||
|
+
|
||
|
+#if (NGX_HTTP_V2_HPACK_ENC)
|
||
|
+
|
||
|
+static ngx_int_t
|
||
|
+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len);
|
||
|
+
|
||
|
+static ngx_int_t
|
||
|
+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,
|
||
|
+ uint8_t *key, size_t key_len);
|
||
|
+
|
||
|
+
|
||
|
+void
|
||
|
+ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c)
|
||
|
+{
|
||
|
+ ngx_http_v2_hpack_enc_entry_t *table;
|
||
|
+ uint64_t idx;
|
||
|
+
|
||
|
+ table = h2c->hpack_enc.htable;
|
||
|
+
|
||
|
+ while (h2c->hpack_enc.size > h2c->max_hpack_table_size) {
|
||
|
+ idx = h2c->hpack_enc.base;
|
||
|
+ h2c->hpack_enc.base = table[idx].next;
|
||
|
+ h2c->hpack_enc.size -= table[idx].size;
|
||
|
+ table[idx].hash_val = 0;
|
||
|
+ h2c->hpack_enc.n_elems--;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/* checks if a header is in the hpack table - if so returns the table entry,
|
||
|
+ otherwise encodes and inserts into the table and returns 0,
|
||
|
+ if failed to insert into table, returns -1 */
|
||
|
+static ngx_int_t
|
||
|
+ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c,
|
||
|
+ size_t key_len, size_t val_len, uint8_t *key, uint8_t *val,
|
||
|
+ ngx_int_t *header_idx)
|
||
|
+{
|
||
|
+ uint64_t hash_val, key_hash, idx, lru;
|
||
|
+ int i;
|
||
|
+ size_t size = key_len + val_len + 32;
|
||
|
+ uint8_t *storage = h2c->hpack_enc.storage;
|
||
|
+
|
||
|
+ ngx_http_v2_hpack_enc_entry_t *table;
|
||
|
+ ngx_http_v2_hpack_name_entry_t *name;
|
||
|
+
|
||
|
+ *header_idx = NGX_ERROR;
|
||
|
+ /* step 1: compute the hash value of header */
|
||
|
+ if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) {
|
||
|
+ return NGX_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234);
|
||
|
+ hash_val = ngx_murmur_hash2_64(val, val_len, key_hash);
|
||
|
+
|
||
|
+ if (hash_val == 0) {
|
||
|
+ return NGX_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* step 2: check if full header in the table */
|
||
|
+ idx = hash_val;
|
||
|
+ i = -1;
|
||
|
+ while (idx) {
|
||
|
+ /* at most 8 locations are checked, but most will be done in 1 or 2 */
|
||
|
+ table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ];
|
||
|
+ if (table->hash_val == hash_val
|
||
|
+ && table->klen == key_len
|
||
|
+ && table->vlen == val_len
|
||
|
+ && ngx_memcmp(key, storage + table->pos, key_len) == 0
|
||
|
+ && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0)
|
||
|
+ {
|
||
|
+ return (h2c->hpack_enc.top - table->index) + 61;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (table->hash_val == 0 && i == -1) {
|
||
|
+ i = idx % HPACK_ENC_HTABLE_SZ;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ idx >>= 8;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* step 3: check if key is in one of the tables */
|
||
|
+ *header_idx = hpack_get_static_index(h2c, key, key_len);
|
||
|
+
|
||
|
+ if (i == -1) {
|
||
|
+ return NGX_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (*header_idx == NGX_ERROR) {
|
||
|
+ *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* step 4: store the new entry */
|
||
|
+ table = h2c->hpack_enc.htable;
|
||
|
+
|
||
|
+ if (h2c->hpack_enc.top == 0xffffffff) {
|
||
|
+ /* just to be on the safe side, avoid overflow */
|
||
|
+ ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t));
|
||
|
+ }
|
||
|
+
|
||
|
+ while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size)
|
||
|
+ || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) {
|
||
|
+ /* make space for the new entry first */
|
||
|
+ idx = h2c->hpack_enc.base;
|
||
|
+ h2c->hpack_enc.base = table[idx].next;
|
||
|
+ h2c->hpack_enc.size -= table[idx].size;
|
||
|
+ table[idx].hash_val = 0;
|
||
|
+ h2c->hpack_enc.n_elems--;
|
||
|
+ }
|
||
|
+
|
||
|
+ table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val,
|
||
|
+ .index = h2c->hpack_enc.top,
|
||
|
+ .pos = h2c->hpack_enc.pos,
|
||
|
+ .klen = key_len,
|
||
|
+ .vlen = val_len,
|
||
|
+ .size = size,
|
||
|
+ .next = 0};
|
||
|
+
|
||
|
+ table[h2c->hpack_enc.last].next = i;
|
||
|
+ if (h2c->hpack_enc.n_elems == 0) {
|
||
|
+ h2c->hpack_enc.base = i;
|
||
|
+ }
|
||
|
+
|
||
|
+ h2c->hpack_enc.last = i;
|
||
|
+ h2c->hpack_enc.top++;
|
||
|
+ h2c->hpack_enc.size += size;
|
||
|
+ h2c->hpack_enc.n_elems++;
|
||
|
+
|
||
|
+ /* update header name lookup */
|
||
|
+ if (*header_idx == NGX_ERROR ) {
|
||
|
+ lru = h2c->hpack_enc.top;
|
||
|
+
|
||
|
+ for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {
|
||
|
+
|
||
|
+ name = &h2c->hpack_enc.heads[i];
|
||
|
+
|
||
|
+ if ( name->hash_val == 0 || (name->hash_val == key_hash
|
||
|
+ && ngx_memcmp(storage + name->pos, key, key_len) == 0) )
|
||
|
+ {
|
||
|
+ name->hash_val = key_hash;
|
||
|
+ name->pos = h2c->hpack_enc.pos;
|
||
|
+ name->index = h2c->hpack_enc.top - 1;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (lru > name->index) {
|
||
|
+ lru = name->index;
|
||
|
+ idx = i;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) {
|
||
|
+ name = &h2c->hpack_enc.heads[idx];
|
||
|
+ name->hash_val = hash_val;
|
||
|
+ name->pos = h2c->hpack_enc.pos;
|
||
|
+ name->index = h2c->hpack_enc.top - 1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len);
|
||
|
+ ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len);
|
||
|
+
|
||
|
+ h2c->hpack_enc.pos += size;
|
||
|
+ if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) {
|
||
|
+ h2c->hpack_enc.pos = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NGX_OK;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+u_char *
|
||
|
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
+ u_char *key, size_t key_len,
|
||
|
+ u_char *value, size_t value_len,
|
||
|
+ u_char *tmp)
|
||
|
+{
|
||
|
+ ngx_int_t idx, header_idx;
|
||
|
+
|
||
|
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 output header: %*s: %*s", key_len, key, value_len,
|
||
|
+ value);
|
||
|
+
|
||
|
+ /* attempt to find the value in the dynamic table */
|
||
|
+ idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value,
|
||
|
+ &header_idx);
|
||
|
+
|
||
|
+ if (idx > 0) {
|
||
|
+ /* positive index indicates success */
|
||
|
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 hpack encode: Indexed Header Field: %ud", idx);
|
||
|
+
|
||
|
+ *pos = 128;
|
||
|
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx);
|
||
|
+
|
||
|
+ } else {
|
||
|
+
|
||
|
+ if (header_idx == NGX_ERROR) { /* if key is not present */
|
||
|
+
|
||
|
+ if (idx == NGX_ERROR) { /* if header was not added */
|
||
|
+ *pos++ = 0;
|
||
|
+
|
||
|
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 hpack encode: Literal Header Field without"
|
||
|
+ " Indexing — New Name");
|
||
|
+ } else { /* if header was added */
|
||
|
+ *pos++ = 64;
|
||
|
+
|
||
|
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 hpack encode: Literal Header Field with "
|
||
|
+ "Incremental Indexing — New Name");
|
||
|
+ }
|
||
|
+
|
||
|
+ pos = ngx_http_v2_write_name(pos, key, key_len, tmp);
|
||
|
+
|
||
|
+ } else { /* if key is present */
|
||
|
+
|
||
|
+ if (idx == NGX_ERROR) {
|
||
|
+ *pos = 0;
|
||
|
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx);
|
||
|
+
|
||
|
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 hpack encode: Literal Header Field without"
|
||
|
+ " Indexing — Indexed Name: %ud", header_idx);
|
||
|
+ } else {
|
||
|
+ *pos = 64;
|
||
|
+ pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx);
|
||
|
+
|
||
|
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 hpack encode: Literal Header Field with "
|
||
|
+ "Incremental Indexing — Indexed Name: %ud", header_idx);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pos = ngx_http_v2_write_value(pos, value, value_len, tmp);
|
||
|
+ }
|
||
|
+
|
||
|
+ return pos;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static ngx_int_t
|
||
|
+hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash,
|
||
|
+ uint8_t *key, size_t key_len)
|
||
|
+{
|
||
|
+ ngx_http_v2_hpack_name_entry_t *name;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i=0; i<HPACK_ENC_DYNAMIC_KEY_TBL_SZ; i++) {
|
||
|
+ name = &h2c->hpack_enc.heads[i];
|
||
|
+
|
||
|
+ if (name->hash_val == key_hash
|
||
|
+ && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0)
|
||
|
+ {
|
||
|
+ if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) {
|
||
|
+ return (h2c->hpack_enc.top - name->index) + 61;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return NGX_ERROR;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/* decide if a given header is present in the static dictionary, this could be
|
||
|
+ done in several ways, but it seems the fastest one is "exhaustive" search */
|
||
|
+static ngx_int_t
|
||
|
+hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len)
|
||
|
+{
|
||
|
+ /* the static dictionary of response only headers,
|
||
|
+ although response headers can be put by origin,
|
||
|
+ that would be rare */
|
||
|
+ static const struct {
|
||
|
+ u_char len;
|
||
|
+ const u_char val[28];
|
||
|
+ u_char idx;
|
||
|
+ } server_headers[] = {
|
||
|
+ { 3, "age", 21},//0
|
||
|
+ { 3, "via", 60},
|
||
|
+ { 4, "date", 33},//2
|
||
|
+ { 4, "etag", 34},
|
||
|
+ { 4, "link", 45},
|
||
|
+ { 4, "vary", 59},
|
||
|
+ { 5, "allow", 22},//6
|
||
|
+ { 6, "server", 54},//7
|
||
|
+ { 7, "expires", 36},//8
|
||
|
+ { 7, "refresh", 52},
|
||
|
+ { 8, "location", 46},//10
|
||
|
+ {10, "set-cookie", 55},//11
|
||
|
+ {11, "retry-after", 53},//12
|
||
|
+ {12, "content-type", 31},//13
|
||
|
+ {13, "content-range", 30},//14
|
||
|
+ {13, "accept-ranges", 18},
|
||
|
+ {13, "cache-control", 24},
|
||
|
+ {13, "last-modified", 44},
|
||
|
+ {14, "content-length", 28},//18
|
||
|
+ {16, "content-encoding", 26},//19
|
||
|
+ {16, "content-language", 27},
|
||
|
+ {16, "content-location", 29},
|
||
|
+ {16, "www-authenticate", 61},
|
||
|
+ {17, "transfer-encoding", 57},//23
|
||
|
+ {18, "proxy-authenticate", 48},//24
|
||
|
+ {19, "content-disposition", 25},//25
|
||
|
+ {25, "strict-transport-security", 56},//26
|
||
|
+ {27, "access-control-allow-origin", 20},//27
|
||
|
+ {99, "", 99},
|
||
|
+ }, *header;
|
||
|
+
|
||
|
+ /* for a given length, where to start the search
|
||
|
+ since minimal length is 3, the table has a -3
|
||
|
+ offset */
|
||
|
+ static const int8_t start_at[] = {
|
||
|
+ [3-3] = 0,
|
||
|
+ [4-3] = 2,
|
||
|
+ [5-3] = 6,
|
||
|
+ [6-3] = 7,
|
||
|
+ [7-3] = 8,
|
||
|
+ [8-3] = 10,
|
||
|
+ [9-3] = -1,
|
||
|
+ [10-3] = 11,
|
||
|
+ [11-3] = 12,
|
||
|
+ [12-3] = 13,
|
||
|
+ [13-3] = 14,
|
||
|
+ [14-3] = 18,
|
||
|
+ [15-3] = -1,
|
||
|
+ [16-3] = 19,
|
||
|
+ [17-3] = 23,
|
||
|
+ [18-3] = 24,
|
||
|
+ [19-3] = 25,
|
||
|
+ [20-3] = -1,
|
||
|
+ [21-3] = -1,
|
||
|
+ [22-3] = -1,
|
||
|
+ [23-3] = -1,
|
||
|
+ [24-3] = -1,
|
||
|
+ [25-3] = 26,
|
||
|
+ [26-3] = -1,
|
||
|
+ [27-3] = 27,
|
||
|
+ };
|
||
|
+
|
||
|
+ uint64_t pref;
|
||
|
+ size_t save_len = len, i;
|
||
|
+ int8_t start;
|
||
|
+
|
||
|
+ /* early exit for out of bounds lengths */
|
||
|
+ if (len < 3 || len > 27) {
|
||
|
+ return NGX_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ start = start_at[len - 3];
|
||
|
+ if (start == -1) {
|
||
|
+ /* exit for non existent lengths */
|
||
|
+ return NGX_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ header = &server_headers[start_at[len - 3]];
|
||
|
+
|
||
|
+ /* load first 8 bytes of key, for fast comparison */
|
||
|
+ if (len < 8) {
|
||
|
+ pref = 0;
|
||
|
+ if (len >= 4) {
|
||
|
+ pref = *(uint32_t *)(val + len - 4) | 0x20202020;
|
||
|
+ len -= 4;
|
||
|
+ }
|
||
|
+ while (len > 0) { /* 3 iterations at most */
|
||
|
+ pref = (pref << 8) ^ (val[len - 1] | 0x20);
|
||
|
+ len--;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ pref = *(uint64_t *)val | 0x2020202020202020;
|
||
|
+ len -= 8;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* iterate over headers with the right length */
|
||
|
+ while (header->len == save_len) {
|
||
|
+ /* quickly compare the first 8 bytes, most tests will end here */
|
||
|
+ if (pref != *(uint64_t *) header->val) {
|
||
|
+ header++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (len == 0) {
|
||
|
+ /* len == 0, indicates prefix held the entire key */
|
||
|
+ return header->idx;
|
||
|
+ }
|
||
|
+ /* for longer keys compare the rest */
|
||
|
+ i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */
|
||
|
+
|
||
|
+ while (i + 8 <= save_len) { /* 3 iterations at most */
|
||
|
+ if ( *(uint64_t *)&header->val[i]
|
||
|
+ != (*(uint64_t *) &val[i]| 0x2020202020202020) )
|
||
|
+ {
|
||
|
+ header++;
|
||
|
+ i = 0;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ i += 8;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (i == 0) {
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* found the corresponding entry in the static dictionary */
|
||
|
+ return header->idx;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NGX_ERROR;
|
||
|
+}
|
||
|
+
|
||
|
+#else
|
||
|
+
|
||
|
+u_char *
|
||
|
+ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||
|
+ u_char *key, size_t key_len,
|
||
|
+ u_char *value, size_t value_len,
|
||
|
+ u_char *tmp)
|
||
|
+{
|
||
|
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||
|
+ "http2 output header: %*s: %*s", key_len, key, value_len,
|
||
|
+ value);
|
||
|
+
|
||
|
+ *pos++ = 64;
|
||
|
+ pos = ngx_http_v2_write_name(pos, key, key_len, tmp);
|
||
|
+ pos = ngx_http_v2_write_value(pos, value, value_len, tmp);
|
||
|
+
|
||
|
+ return pos;
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|