[PATCH] HTTP/2: add support for HPACK encoding
Vlad Krasnov
vlad at cloudflare.com
Mon Jul 24 19:33:55 UTC 2017
> On Jul 24, 2017, at 11:42 AM, Valentin V. Bartenev <vbart at nginx.com> wrote:
>
> On Thursday 22 June 2017 18:23:46 Vlad Krasnov via nginx-devel wrote:
>> # HG changeset patch
>> # User Vlad Krasnov <vlad at cloudflare.com <mailto:vlad at cloudflare.com>>
>> # Date 1498167669 25200
>> # Thu Jun 22 14:41:09 2017 -0700
>> # Node ID 895cea03ac21fb18d2c2ba32389cd67dc74ddbd0
>> # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9
>> HTTP/2: add support for HPACK encoding
>>
>> Add support for full HPACK encoding as per RFC7541.
>> This modification improves header compression ratio by 5-10% for the first
>> response, and by 40-95% for consequential responses on the connection.
>> The implementation is similar to the one used by Cloudflare.
>>
>
> First of all, there's no way for a patch to be committed with so
> many style issues.
>
> Please, examine how existing nginx sources are formatted and mimic
> this style.
OK.
>
>
>
>> diff -r a39bc74873fa -r 895cea03ac21 auto/modules
>> --- a/auto/modules Mon Jun 19 14:25:42 2017 +0300
>> +++ b/auto/modules Thu Jun 22 14:41:09 2017 -0700
>> @@ -436,6 +436,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 -r a39bc74873fa -r 895cea03ac21 auto/options
>> --- a/auto/options Mon Jun 19 14:25:42 2017 +0300
>> +++ b/auto/options Thu Jun 22 14:41:09 2017 -0700
>> @@ -59,6 +59,7 @@
>> HTTP_GZIP=YES
>> HTTP_SSL=NO
>> HTTP_V2=NO
>> +HTTP_V2_HPACK_ENC=NO
>> HTTP_SSI=YES
>> HTTP_POSTPONE=NO
>> HTTP_REALIP=NO
>> @@ -221,6 +222,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 ;;
>
> What's the reason for conditional compilation?
Internal reasons, but I decided to keep it in the patch, because maybe not everyone wants that overhead.
>
>
>> --with-http_realip_module) HTTP_REALIP=YES ;;
>> --with-http_addition_module) HTTP_ADDITION=YES ;;
>> --with-http_xslt_module) HTTP_XSLT=YES ;;
>> @@ -430,6 +432,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 -r a39bc74873fa -r 895cea03ac21 src/core/ngx_murmurhash.c
>> --- a/src/core/ngx_murmurhash.c Mon Jun 19 14:25:42 2017 +0300
>> +++ b/src/core/ngx_murmurhash.c Thu Jun 22 14:41:09 2017 -0700
>> @@ -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 -r a39bc74873fa -r 895cea03ac21 src/core/ngx_murmurhash.h
>> --- a/src/core/ngx_murmurhash.h Mon Jun 19 14:25:42 2017 +0300
>> +++ b/src/core/ngx_murmurhash.h Thu Jun 22 14:41:09 2017 -0700
>> @@ -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 -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2.c
>> --- a/src/http/v2/ngx_http_v2.c Mon Jun 19 14:25:42 2017 +0300
>> +++ b/src/http/v2/ngx_http_v2.c Thu Jun 22 14:41:09 2017 -0700
>> @@ -245,6 +245,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->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
>> @@ -2018,6 +2020,17 @@
>> h2c->frame_size = value;
>> break;
>>
>> + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:
>> +
>> + 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:
>> break;
>> }
>> diff -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2.h
>> --- a/src/http/v2/ngx_http_v2.h Mon Jun 19 14:25:42 2017 +0300
>> +++ b/src/http/v2/ngx_http_v2.h Thu Jun 22 14:41:09 2017 -0700
>> @@ -49,6 +49,13 @@
>> #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 */
>>
>> typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t;
>> typedef struct ngx_http_v2_node_s ngx_http_v2_node_t;
>> @@ -110,6 +117,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 */
>
> Have you considered rbtree as an option?
Yeah, I don’t think it would be faster.
>
>
>> + 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
>> +
>> +
>
> Well, it means that even idle connection will consume 18+ KB more
> memory than before.
>
> That doesn't look like a generic solution suitable for most of our users.
> It should be at least configurable.
>
I can make it configurable fairly easily, but then it will require an extra allocation.
Cheers,
Vlad
> wbr, Valentin V. Bartenev
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org <mailto:nginx-devel at nginx.org>
> http://mailman.nginx.org/mailman/listinfo/nginx-devel <http://mailman.nginx.org/mailman/listinfo/nginx-devel>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20170724/f2ee440d/attachment-0001.html>
More information about the nginx-devel
mailing list