diff --git a/Dockerfile b/Dockerfile index 6415fc5..7131e70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.18 -ARG NGINX_VERSION=1.25.0 +ARG NGINX_VERSION=1.25.3 COPY patches /tmp/patches @@ -18,17 +18,18 @@ RUN addgroup -S nginx \ cmake \ curl \ git \ - perl \ - pcre2-dev \ liburing-dev \ linux-headers \ make \ mimalloc2-dev \ + pcre2-dev \ + perl \ + postgresql15-dev \ tar \ zlib-dev \ zstd-dev \ && mkdir -p /usr/src/nginx /etc/ssl /etc/letsencrypt /etc/nginx/sites-enabled \ - && git clone --depth=1 --branch=openssl-3.0.10+quic \ + && git clone --depth=1 --branch=openssl-3.1.4+quic \ https://github.com/quictls/openssl /usr/src/openssl \ && git clone --depth=1 --shallow-submodules --recursive \ https://github.com/google/ngx_brotli /usr/src/ngx_brotli \ @@ -37,6 +38,8 @@ RUN addgroup -S nginx \ && git clone --depth=1 https://github.com/vozlt/nginx-module-vts /usr/src/ngx_vts \ && git clone --depth=1 https://github.com/openresty/memc-nginx-module /usr/src/ngx_memc \ && git clone --depth=1 https://github.com/openresty/redis2-nginx-module /usr/src/ngx_redis2 \ + && git clone --depth=1 https://github.com/RekGRpth/ngx_pq_module /usr/src/ngx_pq \ + && sed -i /usr/src/ngx_pq/config -e 's|`pg_config --includedir`|/usr/include/postgresql|' \ && curl -Ssf https://hg.nginx.org/nginx/archive/release-${NGINX_VERSION}.tar.gz \ | tar xzf - -C /usr/src/nginx --strip-components=1 \ && curl -Ssfo /etc/ssl/dhparam.pem https://2ton.com.au/dhparam/4096 \ @@ -54,8 +57,8 @@ RUN addgroup -S nginx \ --http-client-body-temp-path=/var/cache/nginx/client_temp \ --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ - --user=nginx \ - --group=nginx \ + --user=http \ + --group=http \ --with-compat \ --with-file-aio \ --with-threads \ @@ -71,7 +74,6 @@ RUN addgroup -S nginx \ --with-http_slice_module \ --with-http_ssl_module \ --with-http_v2_module \ - --with-http_v2_hpack_enc \ --with-http_v3_module \ --without-http_browser_module \ --without-http_empty_gif_module \ @@ -83,7 +85,7 @@ RUN addgroup -S nginx \ --without-http_split_clients_module \ --without-http_userid_module \ --with-openssl=/usr/src/openssl \ - --with-cc-opt='-O2 -pipe' \ + --with-cc-opt='-O2 -pipe -Wno-error=discarded-qualifiers' \ --with-ld-opt='-lmimalloc' \ --add-dynamic-module=/usr/src/ngx_brotli \ --add-dynamic-module=/usr/src/ngx_zstd \ @@ -91,6 +93,7 @@ RUN addgroup -S nginx \ --add-dynamic-module=/usr/src/ngx_vts \ --add-dynamic-module=/usr/src/ngx_memc \ --add-dynamic-module=/usr/src/ngx_redis2 \ + --add-dynamic-module=/usr/src/ngx_pq \ && make -j$(getconf _NPROCESSORS_ONLN) \ && make install \ && strip /usr/sbin/nginx objs/ngx_*_module.so \ diff --git a/README.adoc b/README.adoc index 1bbae36..cd93907 100644 --- a/README.adoc +++ b/README.adoc @@ -8,4 +8,13 @@ An Arch Linux PKGBUILD is provided as well. * https://github.com/hakasenyang/openssl-patch/blob/master/nginx_io_uring.patch[io_uring support] * https://github.com/hakasenyang/openssl-patch/blob/master/remove_nginx_server_header.patch[no server header] * https://github.com/cloudflare/sslconfig/blob/master/patches/nginx__dynamic_tls_records.patch[dynamic TLS records] -* https://github.com/centminmod/centminmod/blob/130.00beta01/patches/cloudflare/nginx-1.25.0_http2-hpack.patch[HTTP/2 HPACK] + +=== Dynamic modules + +* https://github.com/google/ngx_brotli[brotli] +* https://github.com/tokers/zstd-nginx-module[zstd] +* https://github.com/grahamedgecombe/nginx-ct[ct] +* https://github.com/vozlt/nginx-module-vts[vts] +* https://github.com/openresty/memc-nginx-module[memc] +* https://github.com/openresty/redis2-nginx-module[redis2] +* https://github.com/RekGRpth/ngx_pq_module[pq] diff --git a/arch/004-hpack-enc.patch b/arch/004-hpack-enc.patch deleted file mode 120000 index 46bcc9a..0000000 --- a/arch/004-hpack-enc.patch +++ /dev/null @@ -1 +0,0 @@ -../patches/004-hpack-enc.patch \ No newline at end of file diff --git a/arch/PKGBUILD b/arch/PKGBUILD index 42d3d31..6f0baf2 100644 --- a/arch/PKGBUILD +++ b/arch/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: ObserverOfTime pkgname=nginx-custom -pkgver=1.25.0 +pkgver=1.25.3 pkgrel=1 pkgdesc='Lightweight HTTP server and IMAP/POP3 proxy server (custom build)' arch=(x86_64) @@ -9,21 +9,28 @@ url='https://nginx.org' license=(custom) conflicts=(nginx) provides=("nginx=${pkgver%+*}") -depends=(pcre2 zlib libxcrypt liburing mimalloc) -makedepends=(git zstd) +depends=(libxcrypt liburing mimalloc pcre2 zlib) +makedepends=(git postgresql zstd) +optdependes=( + 'brotli: brotli module' + 'memcached: memc module' + 'postgresql: pq module' + 'redis: redis2 module' +) backup=(etc/nginx/nginx.conf etc/nginx/uwsgi_params etc/nginx/mime.types etc/nginx/default.vhost etc/logrotate.d/nginx) source=(nginx-${pkgver}.tar.gz::https://hg.nginx.org/nginx/archive/release-${pkgver}.tar.gz - git+https://github.com/quictls/openssl#branch=openssl-3.0.10+quic + git+https://github.com/quictls/openssl#branch=openssl-3.1.4+quic ngx_brotli::git+https://github.com/google/ngx_brotli ngx_zstd::git+https://github.com/tokers/zstd-nginx-module ngx_ct::git+https://github.com/grahamedgecombe/nginx-ct ngx_vts::git+https://github.com/vozlt/nginx-module-vts ngx_memc::git+https://github.com/openresty/memc-nginx-module ngx_redis2::git+https://github.com/openresty/redis2-nginx-module + ngx_pq::git+https://github.com/RekGRpth/ngx_pq_module dhparam.pem::https://2ton.com.au/dhparam/4096 nginx.service logrotate @@ -32,25 +39,26 @@ source=(nginx-${pkgver}.tar.gz::https://hg.nginx.org/nginx/archive/release-${pkg default.vhost 001-io-uring.patch 002-no-server-header.patch - 003-dynamic-tls.patch - 004-hpack-enc.patch) -b2sums=('d72941977e4061487b43a206bc7e3b2d9d84ac60c42f792d3adef9ca38139278f68fc2cce6feaf3334f137ff59ed0ea030d2081065ee043cda5edf1cc93cd6b9' - 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' + 003-dynamic-tls.patch) +b2sums=('613d5ac8acdc7eed02c22b5db66bfd03d76cf0fb8fcfbb80ba904a6b91d1a1f7a2f59cfad0dd3005ec6ba730ea8b7bdb9fc3f0ac9971ab6188118822947b3034' + 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'e5b3af3eba36bac8c281d773cd90efb8de977a1241e246060661d5c1d436d537ff74b03d137a2bb4a7752339e98e9073ab803bc214a84906498f2383ecad07ff' '5aa8dab4d6517fc09a96f2ced5c85a67a44878da4c5cde1031a089609d3d32505d0cb45e6842a1502cc6f09e03eef08ee0ce6826b73bcfdd8087b0b695f0801c' 'cf2e3ac48f06962e3ea4293f641bf8af75d7cc4269f4af9f0c23a313dce2fde9c558c6f9fcdd5d0a5458eb49b3608d29ab1406515398d3f3732385799ecce19e' - '8d1762a84a2bab88686a37b9a74a1f5e0367f17b13f5d7bc71c70f1d4066a5b9c167e2ddd246f1581070fd88e51be2f0c7a27b369f859b6366975c4a10abf4c3' + '7a6a6b8504a6aad2ed7f022febcd8762dc0bb232074ae76ef5486ec9d0c886aed203d364cd2d32deb1b08a7a8b20bbc26f89ac8b87a295d29458f2a0b763a925' 'cde70b02ddea730c80efe8af2f9c26fdac7c48489225a41f0e82543a1e952881df0deac4a40173c6ce415bfcf8722bc623743514a7cec4b1cf93a842fe85e3fb' 'f1d39725a26859bd5a72256e301ce585fee7e6aeba75dcf52328697cb2dac4d7daaabda7f4f148a9401c10208412d4b6b350d73a89bde1e7c24802509e02d87a' 'fd58f913dd397ce7c5bc8af92d8946a48dc0686c7f4879d87b68ccf78950867c7c067060dcfc4f30daadeb81e494cc2bea6e3447637bf198de453daa97a7a533' 'ffe84842a3f5e9db9fef52d5437feb6c278cbb3d20c2d4b4a836feb0475335a0946a2418c53b38f31d428bba7755dbb5c8a5080d5ffdbe8ff1b388e97878c95e' - '7f6364c416676af03f245b3ed978e51e0f62198941d1ee287ffec5a4607e53359172a2bfea29671b5e6ad490ea1ed9129900d6b205251e6a1cdad2887a4bb475' - '20c22df12ad3983424d16668b0cd4d99364be3c1ae9894c3750917cd26ed99659cbbf1fc0d325397f8f88bef56192108b84f64a876c768ff02b595e19a7e3ab1') + '0a3ce87ad3cb3e4d9e569438d0febeb6457abd4f9032191b08f38c84ba5a628a4182541f8573c92187b895ab127ee8ff35e94020455556a320d1f7e270d6e8b1') prepare() { msg2 'Updating brotli submodule' git -C ngx_brotli submodule update --init --depth=1 + msg2 'Fixing ngx_pq config' + sed -i ngx_pq/config -e 's|`pg_config --includedir`|/usr/include/postgresql|' + cd nginx-release-$pkgver msg2 'Applying io_uring patch' @@ -61,9 +69,6 @@ prepare() { msg2 'Applying dynamic TLS records patch' patch --no-backup-if-mismatch -Np1 -i ../003-dynamic-tls.patch - - msg2 'Applying HPACK encoding patch' - patch --no-backup-if-mismatch -Np1 -i ../004-hpack-enc.patch } build() { @@ -81,8 +86,8 @@ build() { --http-client-body-temp-path=/var/cache/nginx/client_temp \ --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ - --user=nginx \ - --group=nginx \ + --user=http \ + --group=http \ --with-compat \ --with-file-aio \ --with-threads \ @@ -98,7 +103,6 @@ build() { --with-http_slice_module \ --with-http_ssl_module \ --with-http_v2_module \ - --with-http_v2_hpack_enc \ --with-http_v3_module \ --without-http_browser_module \ --without-http_empty_gif_module \ @@ -116,6 +120,7 @@ build() { --add-dynamic-module="$srcdir"/ngx_vts \ --add-dynamic-module="$srcdir"/ngx_memc \ --add-dynamic-module="$srcdir"/ngx_redis2 \ + --add-dynamic-module="$srcdir"/ngx_pq \ --with-cc-opt="$CFLAGS $CPPFLAGS" \ --with-ld-opt="$LDFLAGS -lmimalloc" @@ -155,7 +160,7 @@ package() { install -Dm644 docs/text/LICENSE "$pkgdir"/usr/share/licenses/${pkgname}/LICENSE for f in objs/ngx_*_module.so; do - install -Dm644 $f "$pkgdir"/var/lib/nginx/modules + install -Dm644 "$f" "$pkgdir"/var/lib/nginx/modules done for d in ftdetect ftplugin indent syntax; do @@ -163,8 +168,7 @@ package() { "$pkgdir/usr/share/vim/vimfiles/$d/nginx.vim" done - sed -i "$pkgdir"/etc/nginx/nginx.conf \ - -e '/^user/s|nginx|http|;/^pid/s|/var||' + sed -i "$pkgdir"/etc/nginx/nginx.conf -e '/^pid/s|/var||' printf >> "$pkgdir"/etc/nginx/uwsgi_params \ '\nuwsgi_param HTTP_EARLY_DATA $ssl_early_data if_not_empty;\n' diff --git a/config/nginx.conf b/config/nginx.conf index 3944196..97e0f7f 100644 --- a/config/nginx.conf +++ b/config/nginx.conf @@ -1,4 +1,4 @@ -user nginx; +user http; worker_processes auto; pcre_jit on; pid /var/run/nginx.pid; diff --git a/patches/003-dynamic-tls.patch b/patches/003-dynamic-tls.patch index 3ca9a66..8cdc0b4 100644 --- a/patches/003-dynamic-tls.patch +++ b/patches/003-dynamic-tls.patch @@ -218,10 +218,10 @@ diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ index 26fdccf..b14b52a 100644 --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h -@@ -67,6 +67,12 @@ typedef struct { - - u_char *file; - ngx_uint_t line; +@@ -62,6 +62,12 @@ typedef struct { + ngx_flag_t stapling_verify; + ngx_str_t stapling_file; + ngx_str_t stapling_responder; + + ngx_flag_t dyn_rec_enable; + ngx_msec_t dyn_rec_timeout; diff --git a/patches/004-hpack-enc.patch b/patches/004-hpack-enc.patch deleted file mode 100644 index 09e6732..0000000 --- a/patches/004-hpack-enc.patch +++ /dev/null @@ -1,1026 +0,0 @@ ---- a/auto/modules 2023-05-23 23:08:20.000000000 +0800 -+++ b/auto/modules 2023-05-24 00:52:16.901966472 +0800 -@@ -466,6 +466,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= ---- a/auto/options 2023-05-23 23:08:20.000000000 +0800 -+++ b/auto/options 2023-05-24 00:54:34.532597378 +0800 -@@ -61,6 +61,7 @@ - HTTP_GZIP=YES - HTTP_SSL=NO - HTTP_V2=NO -+HTTP_V2_HPACK_ENC=NO - HTTP_V3=NO - HTTP_SSI=YES - HTTP_REALIP=NO -@@ -236,6 +237,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_v3_module) HTTP_V3=YES ;; - --with-http_realip_module) HTTP_REALIP=YES ;; - --with-http_addition_module) HTTP_ADDITION=YES ;; -@@ -456,6 +458,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_v3_module enable ngx_http_v3_module - --with-http_realip_module enable ngx_http_realip_module - --with-http_addition_module enable ngx_http_addition_module ---- a/src/core/ngx_murmurhash.c 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/core/ngx_murmurhash.c 2023-05-24 00:52:16.902966499 +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; -+} ---- a/src/core/ngx_murmurhash.h 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/core/ngx_murmurhash.h 2023-05-24 00:52:16.903966525 +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_ */ ---- a/src/http/v2/ngx_http_v2.c 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/http/v2/ngx_http_v2.c 2023-05-24 00:52:16.909966683 +0800 -@@ -274,6 +274,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; -@@ -2280,6 +2282,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: ---- a/src/http/v2/ngx_http_v2_encode.c 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/http/v2/ngx_http_v2_encode.c 2023-05-24 00:52:16.909966683 +0800 -@@ -10,7 +10,7 @@ - #include - - --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) { ---- a/src/http/v2/ngx_http_v2_filter_module.c 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/http/v2/ngx_http_v2_filter_module.c 2023-05-24 00:52:16.910966710 +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; -@@ -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, -@@ -437,43 +485,16 @@ - *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); -- } -- -- 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\""); -- } -- -+ 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.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 -@@ -997,6 +963,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); -@@ -1006,12 +973,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\""); -@@ -1021,8 +993,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); -@@ -1047,11 +1018,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); ---- a/src/http/v2/ngx_http_v2.h 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/http/v2/ngx_http_v2.h 2023-05-24 00:52:16.911966736 +0800 -@@ -51,6 +51,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 - - -@@ -114,6 +122,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; -@@ -135,6 +183,8 @@ - - size_t frame_size; - -+ size_t max_hpack_table_size; -+ - ngx_queue_t waiting; - - ngx_http_v2_state_t state; -@@ -164,6 +214,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 - }; - - -@@ -207,6 +262,8 @@ - - ngx_array_t *cookies; - -+ size_t header_limit; -+ - ngx_pool_t *pool; - - unsigned waiting:1; -@@ -413,4 +470,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_ */ ---- a/src/http/v2/ngx_http_v2_table.c 2023-05-23 23:08:20.000000000 +0800 -+++ b/src/http/v2/ngx_http_v2_table.c 2023-05-24 00:52:16.912966762 +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; ihpack_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; ihpack_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