Latest changes:

- Switch to using ls-hpack 1.1
- [BUGFIX] Do not ignore stream resets after receiving FIN
This commit is contained in:
Dmitri Tikhonov 2018-05-02 09:42:32 -04:00
parent bf6b47adbf
commit bea6482295
22 changed files with 1691 additions and 12978 deletions

View file

@ -1,3 +1,8 @@
2018-05-02
- Switch to using ls-hpack 1.1
- [BUGFIX] Do not ignore stream resets after receiving FIN
2018-04-27
- HPACK: do not allow header block to end with table size update.

View file

@ -47,16 +47,15 @@ SET(lsquic_STAT_SRCS
lsquic_str.c
lsquic_arr.c
lsquic_hash.c
lsquic_hpack_common.c
lsquic_hpack_dec.c
lsquic_hpack_enc.c
lsquic_xxhash.c
lsquic_buf.c
lsquic_min_heap.c
lshpack.c
)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DXXH_HEADER_NAME=\\\"lsquic_xxhash.h\\\"")
add_library(lsquic STATIC ${lsquic_STAT_SRCS} )

File diff suppressed because it is too large Load diff

163
src/liblsquic/lshpack.h Normal file
View file

@ -0,0 +1,163 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
MIT License
Copyright (c) 2018 LiteSpeed Technologies Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef LITESPEED_HPACK_H
#define LITESPEED_HPACK_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/**
* Strings up to 65535 characters in length are supported.
*/
typedef uint16_t lshpack_strlen_t;
/** Maximum length is defined for convenience */
#define LSHPACK_MAX_STRLEN UINT16_MAX
struct lshpack_enc;
struct lshpack_dec;
/**
* Initialization routine allocates memory. -1 is returned if memory
* could not be allocated. 0 is returned on success.
*/
int
lshpack_enc_init (struct lshpack_enc *);
/**
* Clean up HPACK encoder, freeing all allocated memory.
*/
void
lshpack_enc_cleanup (struct lshpack_enc *);
/**
* @brief Encode one name/value pair
*
* @param[in,out] henc - A pointer to a valid HPACK API struct
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[in] name - A pointer to the item name
* @param[in] name_len - The item name's length
* @param[in] value - A pointer to the item value
* @param[in] value_len - The item value's length
* @param[in] indexed_type - 0, Add, 1,: without, 2: never
*
* @return The (possibly advanced) dst pointer. If the destination
* pointer was not advanced, an error must have occurred.
*/
unsigned char *
lshpack_enc_encode (struct lshpack_enc *henc, unsigned char *dst,
unsigned char *dst_end, const char *name, lshpack_strlen_t name_len,
const char *value, lshpack_strlen_t value_len, int indexed_type);
void
lshpack_enc_set_max_capacity (struct lshpack_enc *, unsigned);
/**
* Initialize HPACK decoder structure.
*/
void
lshpack_dec_init (struct lshpack_dec *);
/**
* Clean up HPACK decoder structure, freeing all allocated memory.
*/
void
lshpack_dec_cleanup (struct lshpack_dec *);
/*
* Returns 0 on success, a negative value on failure.
*
* If 0 is returned, `src' is advanced. Calling with a zero-length input
* buffer results in an error.
*/
int
lshpack_dec_decode (struct lshpack_dec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, lshpack_strlen_t *name_len,
lshpack_strlen_t *val_len);
void
lshpack_dec_set_max_capacity (struct lshpack_dec *, unsigned);
/* Some internals follow. Struct definitions are exposed to save a malloc.
* These structures are not very complicated.
*/
#include <sys/queue.h>
struct lshpack_enc_table_entry;
STAILQ_HEAD(lshpack_enc_head, lshpack_enc_table_entry);
struct lshpack_double_enc_head;
struct lshpack_enc
{
unsigned hpe_cur_capacity;
unsigned hpe_max_capacity;
/* Each new dynamic table entry gets the next number. It is used to
* calculate the entry's position in the decoder table without having
* to maintain an actual array.
*/
unsigned hpe_next_id;
/* Dynamic table entries (struct enc_table_entry) live in two hash
* tables: name/value hash table and name hash table. These tables
* are the same size.
*/
unsigned hpe_nelem;
unsigned hpe_nbits;
struct lshpack_enc_head
hpe_all_entries;
struct lshpack_double_enc_head
*hpe_buckets;
};
struct lshpack_arr
{
unsigned nalloc,
nelem,
off;
uintptr_t *els;
};
struct lshpack_dec
{
unsigned hpd_max_capacity; /* Maximum set by caller */
unsigned hpd_cur_max_capacity; /* Adjusted at runtime */
unsigned hpd_cur_capacity;
struct lshpack_arr hpd_dyn_table;
};
#ifdef __cplusplus
}
#endif
#endif

View file

@ -14,8 +14,7 @@
#include <string.h>
#include <sys/queue.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_dec.h"
#include "lshpack.h"
#include "lsquic.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
@ -123,7 +122,7 @@ struct reader_state
struct lsquic_frame_reader
{
struct lsquic_mm *fr_mm;
struct lsquic_hdec *fr_hdec;
struct lshpack_dec *fr_hdec;
struct lsquic_stream *fr_stream;
fr_stream_read_f fr_read;
const struct frame_reader_callbacks
@ -189,7 +188,7 @@ lsquic_frame_reader_new (enum frame_reader_flags flags,
unsigned max_headers_sz,
struct lsquic_mm *mm,
struct lsquic_stream *stream, fr_stream_read_f read,
struct lsquic_hdec *hdec,
struct lshpack_dec *hdec,
const struct frame_reader_callbacks *cb,
void *frame_reader_cb_ctx)
{
@ -516,7 +515,7 @@ struct header_writer_ctx
} hwc_flags;
enum pseudo_header pseh_mask;
char *pseh_bufs[N_PSEH];
hpack_strlen_t name_len,
lshpack_strlen_t name_len,
val_len;
};
@ -935,7 +934,7 @@ decode_and_pass_payload (struct lsquic_frame_reader *fr)
while (comp < end)
{
s = lsquic_hdec_decode(fr->fr_hdec, &comp, end,
s = lshpack_dec_decode(fr->fr_hdec, &comp, end,
hwc.buf, hwc.buf + 16 * 1024,
&hwc.name_len, &hwc.val_len);
if (s == 0)

View file

@ -12,7 +12,7 @@
#include <vc_compat.h>
#endif
struct lsquic_hdec;
struct lshpack_dec;
struct lsquic_mm;
struct lsquic_stream;
struct lsquic_frame_reader;
@ -110,7 +110,7 @@ typedef ssize_t (*fr_stream_read_f)(struct lsquic_stream *, void *, size_t);
struct lsquic_frame_reader *
lsquic_frame_reader_new (enum frame_reader_flags, unsigned max_headers_sz,
struct lsquic_mm *, struct lsquic_stream *,
fr_stream_read_f, struct lsquic_hdec *,
fr_stream_read_f, struct lshpack_dec *,
const struct frame_reader_callbacks *,
void *fr_cb_ctx);

View file

@ -18,8 +18,7 @@
#include <string.h>
#include <sys/queue.h>
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lshpack.h"
#include "lsquic_mm.h"
#include "lsquic.h"
@ -67,7 +66,7 @@ struct lsquic_frame_writer
struct lsquic_stream *fw_stream;
fw_write_f fw_write;
struct lsquic_mm *fw_mm;
struct lsquic_henc *fw_henc;
struct lshpack_enc *fw_henc;
struct frame_buf_head fw_frabs;
unsigned fw_max_frame_sz;
uint32_t fw_max_header_list_sz; /* 0 means unlimited */
@ -88,7 +87,7 @@ struct lsquic_frame_writer
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
unsigned max_frame_sz, struct lsquic_henc *henc, fw_write_f write,
unsigned max_frame_sz, struct lshpack_enc *henc, fw_write_f write,
int is_server)
{
struct lsquic_frame_writer *fw;
@ -394,8 +393,8 @@ have_oversize_strings (const struct lsquic_http_headers *headers)
int i, have;
for (i = 0, have = 0; i < headers->count; ++i)
{
have |= headers->headers[i].name.iov_len > HPACK_MAX_STRLEN;
have |= headers->headers[i].value.iov_len > HPACK_MAX_STRLEN;
have |= headers->headers[i].name.iov_len > LSHPACK_MAX_STRLEN;
have |= headers->headers[i].value.iov_len > LSHPACK_MAX_STRLEN;
}
return have;
}
@ -452,7 +451,7 @@ write_headers (struct lsquic_frame_writer *fw,
for (i = 0; i < headers->count; ++i)
{
end = lsquic_henc_encode(fw->fw_henc, buf, buf + buf_sz,
end = lshpack_enc_encode(fw->fw_henc, buf, buf + buf_sz,
headers->headers[i].name.iov_base, headers->headers[i].name.iov_len,
headers->headers[i].value.iov_base, headers->headers[i].value.iov_len, 0);
if (end > buf)

View file

@ -10,7 +10,7 @@
#include <stdint.h>
struct iovec;
struct lsquic_henc;
struct lshpack_enc;
struct lsquic_mm;
struct lsquic_frame_writer;
struct lsquic_stream;
@ -21,7 +21,7 @@ typedef ssize_t (*fw_write_f)(struct lsquic_stream *, const void *, size_t);
struct lsquic_frame_writer *
lsquic_frame_writer_new (struct lsquic_mm *, struct lsquic_stream *,
unsigned max_frame_sz, struct lsquic_henc *,
unsigned max_frame_sz, struct lshpack_enc *,
fw_write_f, int is_server);
void

View file

@ -17,9 +17,7 @@
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_frame_writer.h"
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_hpack_dec.h"
#include "lshpack.h"
#include "lsquic.h"
#include "lsquic_headers_stream.h"
@ -44,8 +42,8 @@ struct headers_stream
*hs_settings;
void *hs_cb_ctx;
struct lsquic_mm *hs_mm;
struct lsquic_henc hs_henc;
struct lsquic_hdec hs_hdec;
struct lshpack_enc hs_henc;
struct lshpack_dec hs_hdec;
enum {
HS_IS_SERVER = (1 << 0),
HS_HENC_INITED = (1 << 1),
@ -77,8 +75,8 @@ static lsquic_stream_ctx_t *
headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct headers_stream *hs = stream_if_ctx;
lsquic_hdec_init(&hs->hs_hdec);
if (0 != lsquic_henc_init(&hs->hs_henc))
lshpack_dec_init(&hs->hs_hdec);
if (0 != lshpack_enc_init(&hs->hs_henc))
{
LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
return NULL;
@ -223,8 +221,8 @@ lsquic_headers_stream_destroy (struct headers_stream *hs)
if (hs->hs_fw)
lsquic_frame_writer_destroy(hs->hs_fw);
if (hs->hs_flags & HS_HENC_INITED)
lsquic_henc_cleanup(&hs->hs_henc);
lsquic_hdec_cleanup(&hs->hs_hdec);
lshpack_enc_cleanup(&hs->hs_henc);
lshpack_dec_cleanup(&hs->hs_hdec);
free(hs);
}
@ -319,7 +317,7 @@ headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
else
{
LSQ_INFO("update hpack table size to %u", setting_value);
lsquic_henc_set_max_capacity(&hs->hs_henc, setting_value);
lshpack_enc_set_max_capacity(&hs->hs_henc, setting_value);
}
break;
case SETTINGS_MAX_HEADER_LIST_SIZE:
@ -391,13 +389,7 @@ lsquic_headers_stream_mem_used (const struct headers_stream *hs)
size = sizeof(*hs);
size += lsquic_frame_reader_mem_used(hs->hs_fr);
size += lsquic_frame_writer_mem_used(hs->hs_fw);
size -= sizeof(hs->hs_hdec);
size += lsquic_hdec_mem_used(&hs->hs_hdec);
if (hs->hs_flags & HS_HENC_INITED)
{
size -= sizeof(hs->hs_henc);
size += lsquic_henc_mem_used(&hs->hs_henc);
}
/* XXX: get rid of this mem_used business as we no longer use it? */
return size;
}

View file

@ -1,57 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HPACK_COMMON_H
#define LSQUIC_HPACK_COMMON_H
#include "lsquic_hpack_types.h"
#define HPACK_STATIC_TABLE_SIZE 61
#define INITIAL_DYNAMIC_TABLE_SIZE 4096
/* RFC 7541, Section 4.1:
*
* " The size of the dynamic table is the sum of the size of its entries.
* "
* " The size of an entry is the sum of its name's length in octets (as
* " defined in Section 5.2), its value's length in octets, and 32.
*/
#define DYNAMIC_ENTRY_OVERHEAD 32
/**
* @typedef hpack_hdr_tbl_t
* @brief A struct for the static table (name - value)
*/
typedef struct hpack_hdr_tbl_s
{
const char *name;
hpack_strlen_t name_len;
const char *val;
hpack_strlen_t val_len;
} hpack_hdr_tbl_t;
/**
* @typedef hpack_huff_encode_t
* @brief Huffman encode struct
*/
typedef struct hpack_huff_encode_s
{
uint32_t code;
int bits;
} hpack_huff_encode_t;
/**
* @typedef hpack_huff_decode_t
* @brief Huffman decode struct
*/
typedef struct hpack_huff_decode_s
{
uint8_t state;
uint8_t flags;
uint8_t sym;
} hpack_huff_decode_t;
extern const hpack_huff_decode_t lsquic_hpack_huff_decode_tables[256][16];
extern const hpack_huff_encode_t lsquic_hpack_huff_encode_tables[257];
extern const hpack_hdr_tbl_t lsquic_hpack_stx_tab[HPACK_STATIC_TABLE_SIZE];
#endif

View file

@ -1,446 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.c - HPACK decoder
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#ifdef WIN32
#include <vc_compat.h>
#endif
#include "lsquic_arr.h"
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_dec.h"
/* Dynamic table entry: */
struct dec_table_entry
{
uint16_t dte_name_len;
uint16_t dte_val_len;
char dte_buf[0]; /* Contains both name and value */
};
#define DTE_NAME(dte) ((dte)->dte_buf)
#define DTE_VALUE(dte) (&(dte)->dte_buf[(dte)->dte_name_len])
enum
{
HPACK_HUFFMAN_FLAG_ACCEPTED = 0x01,
HPACK_HUFFMAN_FLAG_SYM = 0x02,
HPACK_HUFFMAN_FLAG_FAIL = 0x04,
};
typedef struct hpack_huff_decode_status_s
{
uint8_t state;
uint8_t eos;
} hpack_huff_decode_status_t;
void
lsquic_hdec_init (struct lsquic_hdec *dec)
{
memset(dec, 0, sizeof(*dec));
dec->hpd_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
dec->hpd_cur_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
lsquic_arr_init(&dec->hpd_dyn_table);
}
void
lsquic_hdec_cleanup (struct lsquic_hdec *dec)
{
uintptr_t val;
while (lsquic_arr_count(&dec->hpd_dyn_table) > 0)
{
val = lsquic_arr_pop(&dec->hpd_dyn_table);
free((struct dec_table_entry *) val);
}
lsquic_arr_cleanup(&dec->hpd_dyn_table);
}
//https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value)
{
uint32_t B, M;
uint8_t prefix_max = (1 << prefix_bits) - 1;
*value = (*(*src)++ & prefix_max);
if (*value < prefix_max)
return 0;
/* To optimize the loop for the normal case, the overflow is checked
* outside the loop. The decoder is limited to 28-bit integer values,
* which is far above limitations imposed by the APIs (16-bit integers).
*/
M = 0;
do
{
if ((*src) >= src_end)
return -1;
B = *(*src)++;
*value = *value + ((B & 0x7f) << M);
M += 7;
}
while (B & 0x80);
return -(M > sizeof(*value) * 8);
}
static void
hdec_drop_oldest_entry (struct lsquic_hdec *dec)
{
struct dec_table_entry *entry;
entry = (void *) lsquic_arr_shift(&dec->hpd_dyn_table);
dec->hpd_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->dte_name_len
+ entry->dte_val_len;
free(entry);
}
static void
hdec_remove_overflow_entries (struct lsquic_hdec *dec)
{
while (dec->hpd_cur_capacity > dec->hpd_cur_max_capacity)
hdec_drop_oldest_entry(dec);
}
static void
hdec_update_max_capacity (struct lsquic_hdec *dec, uint32_t new_capacity)
{
dec->hpd_cur_max_capacity = new_capacity;
hdec_remove_overflow_entries(dec);
}
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *dec, unsigned max_capacity)
{
dec->hpd_max_capacity = max_capacity;
hdec_update_max_capacity(dec, max_capacity);
}
static unsigned char *
hdec_huff_dec4bits (uint8_t src_4bits, unsigned char *dst,
hpack_huff_decode_status_t *status)
{
const hpack_huff_decode_t cur_dec_code =
lsquic_hpack_huff_decode_tables[status->state][src_4bits];
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_FAIL) {
return NULL; //failed
}
if (cur_dec_code.flags & HPACK_HUFFMAN_FLAG_SYM)
{
*dst = cur_dec_code.sym;
dst++;
}
status->state = cur_dec_code.state;
status->eos = ((cur_dec_code.flags & HPACK_HUFFMAN_FLAG_ACCEPTED) != 0);
return dst;
}
static int
hdec_huff_decode (const unsigned char *src, int src_len,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
const unsigned char *src_end = src + src_len;
unsigned char *p_dst = dst;
unsigned char *dst_end = dst + dst_len;
hpack_huff_decode_status_t status = { 0, 1 };
while (p_src != src_end)
{
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src >> 4, p_dst, &status))
== NULL)
return -1;
if (p_dst == dst_end)
return -2;
if ((p_dst = hdec_huff_dec4bits(*p_src & 0xf, p_dst, &status))
== NULL)
return -1;
++p_src;
}
if (!status.eos)
return -1;
return p_dst - dst;
}
//reutrn the length in the dst, also update the src
#ifdef NDEBUG
static
#endif
int
hdec_dec_str (unsigned char *dst, size_t dst_len, const unsigned char **src,
const unsigned char *src_end)
{
if ((*src) == src_end)
return 0;
int is_huffman = (*(*src) & 0x80);
uint32_t len;
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &len))
return -2; //wrong int
int ret = 0;
if ((uint32_t)(src_end - (*src)) < len) {
return -2; //wrong int
}
if (is_huffman)
{
ret = hdec_huff_decode(*src, len, dst, dst_len);
if (ret < 0)
return -3; //Wrong code
(*src) += len;
}
else
{
if (dst_len < (size_t)(src_end - (*src)))
ret = -3; //dst not enough space
else
{
memcpy(dst, (*src), len);
(*src) += len;
ret = len;
}
}
return ret;
}
/* hpd_dyn_table is a dynamic array. New entries are pushed onto it,
* while old entries are shifted from it.
*/
static struct dec_table_entry *
hdec_get_table_entry (struct lsquic_hdec *dec, uint32_t index)
{
uintptr_t val;
index -= HPACK_STATIC_TABLE_SIZE;
if (index == 0 || index > lsquic_arr_count(&dec->hpd_dyn_table))
return NULL;
index = lsquic_arr_count(&dec->hpd_dyn_table) - index;
val = lsquic_arr_get(&dec->hpd_dyn_table, index);
return (struct dec_table_entry *) val;
}
#ifdef NDEBUG
static
#endif
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len)
{
struct dec_table_entry *entry;
size_t size;
size = sizeof(*entry) + name_len + val_len;
entry = malloc(size);
if (!entry)
return -1;
if (0 != lsquic_arr_push(&dec->hpd_dyn_table, (uintptr_t) entry))
{
free(entry);
return -1;
}
dec->hpd_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + val_len;
entry->dte_name_len = name_len;
entry->dte_val_len = val_len;
memcpy(DTE_NAME(entry), name, name_len);
memcpy(DTE_VALUE(entry), val, val_len);
return 0;
}
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len)
{
struct dec_table_entry *entry;
uint32_t index, new_capacity;
int indexed_type, len;
if ((*src) == src_end)
return -1;
while ((*(*src) & 0xe0) == 0x20) //001 xxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 5, &new_capacity))
return -1;
if (new_capacity > dec->hpd_max_capacity)
return -1;
hdec_update_max_capacity(dec, new_capacity);
if (*src == src_end)
return -1;
}
/* lsquic_hdec_dec_int() sets `index' and advances `src'. If we do not call
* it, we set `index' and advance `src' ourselves:
*/
if (*(*src) & 0x80) //1 xxxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 7, &index))
return -1;
indexed_type = 3; //need to parse value
}
else if (*(*src) > 0x40) //01 xxxxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 6, &index))
return -1;
indexed_type = 0;
}
else if (*(*src) == 0x40) //custmized //0100 0000
{
indexed_type = 0;
index = 0;
++(*src);
}
//Never indexed
else if (*(*src) == 0x10) //00010000
{
indexed_type = 2;
index = 0;
++(*src);
}
else if ((*(*src) & 0xf0) == 0x10) //0001 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 2;
}
//without indexed
else if (*(*src) == 0x00) //0000 0000
{
indexed_type = 1;
index = 0;
++(*src);
}
else // 0000 xxxx
{
if (0 != lsquic_hdec_dec_int(src, src_end, 4, &index))
return -1;
indexed_type = 1;
}
char *const name = dst;
if (index > 0)
{
if (index <= HPACK_STATIC_TABLE_SIZE) //static table
{
if (lsquic_hpack_stx_tab[index - 1].name_len > dst_end - dst)
return -1;
*name_len = lsquic_hpack_stx_tab[index - 1].name_len;
memcpy(name, lsquic_hpack_stx_tab[index - 1].name, *name_len);
if (indexed_type == 3)
{
if (lsquic_hpack_stx_tab[index - 1].name_len +
lsquic_hpack_stx_tab[index - 1].val_len > dst_end - dst)
return -1;
*val_len = lsquic_hpack_stx_tab[index - 1].val_len;
memcpy(name + *name_len, lsquic_hpack_stx_tab[index - 1].val, *val_len);
return 0;
}
}
else
{
entry = hdec_get_table_entry(dec, index);
if (entry == NULL)
return -1;
if (entry->dte_name_len > dst_end - dst)
return -1;
*name_len = entry->dte_name_len;
memcpy(name, DTE_NAME(entry), *name_len);
if (indexed_type == 3)
{
if (entry->dte_name_len + entry->dte_val_len > dst_end - dst)
return -1;
*val_len = entry->dte_val_len;
memcpy(name + *name_len, DTE_VALUE(entry), *val_len);
return 0;
}
}
}
else
{
len = hdec_dec_str((unsigned char *)name, dst_end - dst, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*name_len = len;
}
len = hdec_dec_str((unsigned char *)name + *name_len,
dst_end - dst - *name_len, src, src_end);
if (len < 0)
return len; //error
if (len > UINT16_MAX)
return -2;
*val_len = len;
if (indexed_type == 0)
{
if (0 != lsquic_hdec_push_entry(dec, name, *name_len,
name + *name_len, *val_len))
return -1; //error
}
return 0;
}
size_t
lsquic_hdec_mem_used (const struct lsquic_hdec *dec)
{
const struct dec_table_entry *entry;
size_t size;
unsigned i;
size = sizeof(*dec);
for (i = 0; i < lsquic_arr_count(&dec->hpd_dyn_table); ++i)
{
entry = (void *) lsquic_arr_get(&dec->hpd_dyn_table, i);
size += sizeof(*entry) + entry->dte_val_len + entry->dte_name_len;
}
size -= sizeof(dec->hpd_dyn_table);
size += lsquic_arr_mem_used(&dec->hpd_dyn_table);
return size;
}

View file

@ -1,53 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hdec.h - HPACK decoder
*/
#ifndef LSQUIC_HPACK_DEC_H
#define LSQUIC_HPACK_DEC_H
#include "lsquic_hpack_types.h"
struct lsquic_hdec
{
unsigned hpd_max_capacity; /* Maximum set by caller */
unsigned hpd_cur_max_capacity; /* Adjusted at runtime */
unsigned hpd_cur_capacity;
struct lsquic_arr hpd_dyn_table;
};
void
lsquic_hdec_init (struct lsquic_hdec *);
void
lsquic_hdec_cleanup (struct lsquic_hdec *);
/*
* Returns 0 on success, a negative value on failure.
*
* If 0 is returned, `src' is advanced. Calling with a zero-length input
* buffer results in an error.
*/
int
lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, hpack_strlen_t *name_len,
hpack_strlen_t *val_len);
void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *, unsigned);
size_t
lsquic_hdec_mem_used (const struct lsquic_hdec *);
#ifndef NDEBUG
int
lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
uint8_t prefix_bits, uint32_t *value);
int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
hpack_strlen_t name_len, const char *val,
hpack_strlen_t val_len);
#endif
#endif

View file

@ -1,878 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.c - HPACK encoder
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifdef WIN32
#include <vc_compat.h>
#endif
#include "lsquic_hpack_common.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_xxhash.h"
struct double_enc_head
{
struct enc_head by_name;
struct enc_head by_nameval;
};
struct enc_table_entry
{
/* An entry always lives on all three lists */
STAILQ_ENTRY(enc_table_entry) ete_next_nameval,
ete_next_name,
ete_next_all;
unsigned ete_id;
unsigned ete_nameval_hash;
unsigned ete_name_hash;
hpack_strlen_t ete_name_len;
hpack_strlen_t ete_val_len;
char ete_buf[0];
};
#define ETE_NAME(ete) ((ete)->ete_buf)
#define ETE_VALUE(ete) (&(ete)->ete_buf[(ete)->ete_name_len])
#define N_BUCKETS(n_bits) (1U << (n_bits))
#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
int
lsquic_henc_init (struct lsquic_henc *enc)
{
struct double_enc_head *buckets;
unsigned nbits = 2;
unsigned i;
buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
if (!buckets)
return -1;
for (i = 0; i < N_BUCKETS(nbits); ++i)
{
STAILQ_INIT(&buckets[i].by_name);
STAILQ_INIT(&buckets[i].by_nameval);
}
memset(enc, 0, sizeof(*enc));
STAILQ_INIT(&enc->hpe_all_entries);
enc->hpe_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
enc->hpe_buckets = buckets;
/* The initial value of the entry ID is completely arbitrary. As long as
* there are fewer than 2^32 dynamic table entries, the math to calculate
* the entry ID works. To prove to ourselves that the wraparound works
* and to have the unit tests cover it, we initialize the next ID so that
* it is just about to wrap around.
*/
enc->hpe_next_id = ~0 - 3;
enc->hpe_nbits = nbits;
enc->hpe_nelem = 0;
return 0;
}
void
lsquic_henc_cleanup (struct lsquic_henc *enc)
{
struct enc_table_entry *entry, *next;
for (entry = STAILQ_FIRST(&enc->hpe_all_entries); entry; entry = next)
{
next = STAILQ_NEXT(entry, ete_next_all);
free(entry);
}
free(enc->hpe_buckets);
}
//not find return 0, otherwise return the index
#ifdef NDEBUG
static
#endif
unsigned
lsquic_henc_get_stx_tab_id (const char *name, hpack_strlen_t name_len,
const char *val, hpack_strlen_t val_len, int *val_matched)
{
if (name_len < 3)
return 0;
*val_matched = 0;
//check value first
int i = -1;
switch (*val)
{
case 'G':
i = 1;
break;
case 'P':
i = 2;
break;
case '/':
if (val_len == 1)
i = 3;
else if (val_len == 11)
i = 4;
break;
case 'h':
if (val_len == 4)
i = 5;
else if (val_len == 5)
i = 6;
break;
case '2':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 7;
break;
case '4':
i = 8;
break;
case '6':
i = 9;
break;
default:
break;
}
}
break;
case '3':
i = 10;
break;
case '4':
if (val_len == 3)
{
switch (*(val + 2))
{
case '0':
i = 11;
break;
case '4':
i = 12;
default:
break;
}
}
break;
case '5':
i = 13;
break;
case 'g':
i = 15;
break;
default:
break;
}
if (i > 0 && lsquic_hpack_stx_tab[i].val_len == val_len
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(val, lsquic_hpack_stx_tab[i].val, val_len) == 0
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
{
*val_matched = 1;
return i + 1;
}
//macth name only checking
i = -1;
switch (*name)
{
case ':':
switch (*(name + 1))
{
case 'a':
i = 0;
break;
case 'm':
i = 1;
break;
case 'p':
i = 3;
break;
case 's':
if (*(name + 2) == 'c') //:scheme
i = 5;
else
i = 7;
break;
default:
break;
}
break;
case 'a':
switch (name_len)
{
case 3:
i = 20; //age
break;
case 5:
i = 21; //allow
break;
case 6:
i = 18; //accept
break;
case 13:
if (*(name + 1) == 'u')
i = 22; //authorization
else
i = 17; //accept-ranges
break;
case 14:
i = 14; //accept-charset
break;
case 15:
if (*(name + 7) == 'l')
i = 16; //accept-language,
else
i = 15;// accept-encoding
break;
case 27:
i = 19;//access-control-allow-origin
break;
default:
break;
}
break;
case 'c':
switch (name_len)
{
case 6:
i = 31; //cookie
break;
case 12:
i = 30; //content-type
break;
case 13:
if (*(name + 1) == 'a')
i = 23; //cache-control
else
i = 29; //content-range
break;
case 14:
i = 27; //content-length
break;
case 16:
switch (*(name + 9))
{
case 'n':
i = 25 ;//content-encoding
break;
case 'a':
i = 26; //content-language
break;
case 'o':
i = 28; //content-location
default:
break;
}
break;
case 19:
i = 24; //content-disposition
break;
}
break;
case 'd':
i = 32 ;//date
break;
case 'e':
switch (name_len)
{
case 4:
i = 33; //etag
break;
case 6:
i = 34;
break;
case 7:
i = 35;
break;
default:
break;
}
break;
case 'f':
i = 36; //from
break;
case 'h':
i = 37; //host
break;
case 'i':
switch (name_len)
{
case 8:
if (*(name + 3) == 'm')
i = 38; //if-match
else
i = 41; //if-range
break;
case 13:
i = 40; //if-none-match
break;
case 17:
i = 39; //if-modified-since
break;
case 19:
i = 42; //if-unmodified-since
break;
default:
break;
}
break;
case 'l':
switch (name_len)
{
case 4:
i = 44; //link
break;
case 8:
i = 45; //location
break;
case 13:
i = 43; //last-modified
break;
default:
break;
}
break;
case 'm':
i = 46; //max-forwards
break;
case 'p':
if (name_len == 18)
i = 47; //proxy-authenticate
else
i = 48; //proxy-authorization
break;
case 'r':
if (name_len >= 5)
{
switch (*(name + 4))
{
case 'e':
if (name_len == 5)
i = 49; //range
else
i = 51; //refresh
break;
case 'r':
i = 50; //referer
break;
case 'y':
i = 52; //retry-after
break;
default:
break;
}
}
break;
case 's':
switch (name_len)
{
case 6:
i = 53; //server
break;
case 10:
i = 54; //set-cookie
break;
case 25:
i = 55; //strict-transport-security
break;
default:
break;
}
break;
case 't':
i = 56;//transfer-encoding
break;
case 'u':
i = 57; //user-agent
break;
case 'v':
if (name_len == 4)
i = 58;
else
i = 59;
break;
case 'w':
i = 60;
break;
default:
break;
}
if (i >= 0
&& lsquic_hpack_stx_tab[i].name_len == name_len
&& memcmp(name, lsquic_hpack_stx_tab[i].name, name_len) == 0)
return i + 1;
return 0;
}
/* Given a dynamic entry, return its table ID */
static unsigned
henc_calc_table_id (const struct lsquic_henc *enc,
const struct enc_table_entry *entry)
{
return HPACK_STATIC_TABLE_SIZE
+ (enc->hpe_next_id - entry->ete_id)
;
}
static unsigned
henc_find_table_id (struct lsquic_henc *enc, const char *name,
hpack_strlen_t name_len, const char *value, hpack_strlen_t value_len,
int *val_matched)
{
struct enc_table_entry *entry;
unsigned name_hash, nameval_hash, buckno, static_table_id;
XXH32_state_t hash_state;
/* First, look for a match in the static table: */
static_table_id = lsquic_henc_get_stx_tab_id(name, name_len, value,
value_len, val_matched);
if (static_table_id > 0 && *val_matched)
return static_table_id;
/* Search by name and value: */
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_nameval, ete_next_nameval)
if (nameval_hash == entry->ete_nameval_hash &&
name_len == entry->ete_name_len &&
value_len == entry->ete_val_len &&
0 == memcmp(name, ETE_NAME(entry), name_len) &&
0 == memcmp(value, ETE_VALUE(entry), value_len))
{
*val_matched = 1;
return henc_calc_table_id(enc, entry);
}
/* Name/value match is not found, but if the caller found a matching
* static table entry, no need to continue to search:
*/
if (static_table_id > 0)
return static_table_id;
/* Search by name only: */
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_FOREACH(entry, &enc->hpe_buckets[buckno].by_name, ete_next_name)
if (name_hash == entry->ete_name_hash &&
name_len == entry->ete_name_len &&
0 == memcmp(name, ETE_NAME(entry), name_len))
{
*val_matched = 0;
return henc_calc_table_id(enc, entry);
}
return 0;
}
////https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.1
static unsigned char *
henc_enc_int (unsigned char *dst, unsigned char *const end, uint32_t value,
uint8_t prefix_bits)
{
unsigned char *const dst_orig = dst;
/* This function assumes that at least one byte is available */
assert(dst < end);
if (value < (uint32_t)(1 << prefix_bits) - 1)
*dst++ |= value;
else
{
*dst++ |= (1 << prefix_bits) - 1;
value -= (1 << prefix_bits) - 1;
while (value >= 128)
{
if (dst < end)
{
*dst++ = (0x80 | value);
value >>= 7;
}
else
return dst_orig;
}
if (dst < end)
*dst++ = value;
else
return dst_orig;
}
return dst;
}
#ifdef NDEBUG
static
#endif
int
henc_huffman_enc (const unsigned char *src, const unsigned char *const src_end,
unsigned char *dst, int dst_len)
{
const unsigned char *p_src = src;
unsigned char *p_dst = dst;
unsigned char *dst_end = p_dst + dst_len;
uint64_t bits = 0;
int bits_left = 40;
hpack_huff_encode_t cur_enc_code;
assert(dst_len > 0);
while (p_src != src_end)
{
cur_enc_code = lsquic_hpack_huff_encode_tables[(int) *p_src++];
assert(bits_left >= cur_enc_code.bits); // (possible negative shift, undefined behavior)
bits |= (uint64_t)cur_enc_code.code << (bits_left - cur_enc_code.bits);
bits_left -= cur_enc_code.bits;
while (bits_left <= 32)
{
*p_dst++ = bits >> 32;
bits <<= 8;
bits_left += 8;
if (p_dst == dst_end)
return -1; //dst does not have enough space
}
}
if (bits_left != 40)
{
assert(bits_left < 40 && bits_left > 0);
bits |= ((uint64_t)1 << bits_left) - 1;
*p_dst++ = bits >> 32;
}
return p_dst - dst;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, hpack_strlen_t str_len)
{
unsigned char size_buf[4];
unsigned char *p;
unsigned size_len;
int rc;
if (dst_len > 1)
/* We guess that the string size fits into a single byte -- meaning
* compressed string of size 126 and smaller -- which is the normal
* case. Thus, we immediately write compressed string to the output
* buffer. If our guess is not correct, we fix it later.
*/
rc = henc_huffman_enc(str, str + str_len, dst + 1, dst_len - 1);
else if (dst_len == 1)
/* Here, the call can only succeed if the string to encode is empty. */
rc = 0;
else
return -1;
/*
* Check if need huffman encoding or not
* Comment: (size_t)rc <= str_len = means if same length, still use Huffman
* ^
*/
if (rc > 0 && (size_t)rc <= str_len)
{
if (rc < 127)
{
*dst = 0x80 | rc;
return 1 + rc;
}
size_buf[0] = 0x80;
str_len = rc;
str = dst + 1;
}
else if (str_len <= dst_len - 1)
{
if (str_len < 127)
{
*dst = str_len;
memcpy(dst + 1, str, str_len);
return 1 + str_len;
}
size_buf[0] = 0x00;
}
else
return -1;
/* The guess of one-byte size was incorrect. Perform necessary
* adjustments.
*/
p = henc_enc_int(size_buf, size_buf + sizeof(size_buf), str_len, 7);
if (p == size_buf)
return -1;
size_len = p - size_buf;
assert(size_len > 1);
/* Check if there is enough room in the output buffer for both
* encoded size and the string.
*/
if (size_len + str_len > dst_len)
return -1;
memmove(dst + size_len, str, str_len);
memcpy(dst, size_buf, size_len);
return size_len + str_len;
}
static void
henc_drop_oldest_entry (struct lsquic_henc *enc)
{
struct enc_table_entry *entry;
unsigned buckno;
entry = STAILQ_FIRST(&enc->hpe_all_entries);
assert(entry);
STAILQ_REMOVE_HEAD(&enc->hpe_all_entries, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_nameval_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_nameval));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_nameval, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, entry->ete_name_hash);
assert(entry == STAILQ_FIRST(&enc->hpe_buckets[buckno].by_name));
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[buckno].by_name, ete_next_name);
enc->hpe_cur_capacity -= DYNAMIC_ENTRY_OVERHEAD + entry->ete_name_len
+ entry->ete_val_len;
--enc->hpe_nelem;
free(entry);
}
static void
henc_remove_overflow_entries (struct lsquic_henc *enc)
{
while (enc->hpe_cur_capacity > enc->hpe_max_capacity)
henc_drop_oldest_entry(enc);
}
static int
henc_grow_tables (struct lsquic_henc *enc)
{
struct double_enc_head *new_buckets, *new[2];
struct enc_table_entry *entry;
unsigned n, old_nbits;
int idx;
old_nbits = enc->hpe_nbits;
new_buckets = malloc(sizeof(enc->hpe_buckets[0])
* N_BUCKETS(old_nbits + 1));
if (!new_buckets)
return -1;
for (n = 0; n < N_BUCKETS(old_nbits); ++n)
{
new[0] = &new_buckets[n];
new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
STAILQ_INIT(&new[0]->by_name);
STAILQ_INIT(&new[1]->by_name);
STAILQ_INIT(&new[0]->by_nameval);
STAILQ_INIT(&new[1]->by_nameval);
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_name)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_name, ete_next_name);
idx = (BUCKNO(old_nbits + 1, entry->ete_name_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_name, entry, ete_next_name);
}
while ((entry = STAILQ_FIRST(&enc->hpe_buckets[n].by_nameval)))
{
STAILQ_REMOVE_HEAD(&enc->hpe_buckets[n].by_nameval, ete_next_nameval);
idx = (BUCKNO(old_nbits + 1, entry->ete_nameval_hash) >> old_nbits) & 1;
STAILQ_INSERT_TAIL(&new[idx]->by_nameval, entry, ete_next_nameval);
}
}
free(enc->hpe_buckets);
enc->hpe_nbits = old_nbits + 1;
enc->hpe_buckets = new_buckets;
return 0;
}
#ifdef NDEBUG
static
#endif
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
hpack_strlen_t name_len, const char *value,
hpack_strlen_t value_len)
{
unsigned name_hash, nameval_hash, buckno;
struct enc_table_entry *entry;
XXH32_state_t hash_state;
size_t size;
if (enc->hpe_nelem >= N_BUCKETS(enc->hpe_nbits) / 2 &&
0 != henc_grow_tables(enc))
return -1;
size = sizeof(*entry) + name_len + value_len;
entry = malloc(size);
if (!entry)
return -1;
XXH32_reset(&hash_state, (uintptr_t) enc);
XXH32_update(&hash_state, &name_len, sizeof(name_len));
XXH32_update(&hash_state, name, name_len);
name_hash = XXH32_digest(&hash_state);
XXH32_update(&hash_state, &value_len, sizeof(value_len));
XXH32_update(&hash_state, value, value_len);
nameval_hash = XXH32_digest(&hash_state);
entry->ete_name_hash = name_hash;
entry->ete_nameval_hash = nameval_hash;
entry->ete_name_len = name_len;
entry->ete_val_len = value_len;
entry->ete_id = enc->hpe_next_id++;
memcpy(ETE_NAME(entry), name, name_len);
memcpy(ETE_VALUE(entry), value, value_len);
STAILQ_INSERT_TAIL(&enc->hpe_all_entries, entry, ete_next_all);
buckno = BUCKNO(enc->hpe_nbits, nameval_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_nameval, entry, ete_next_nameval);
buckno = BUCKNO(enc->hpe_nbits, name_hash);
STAILQ_INSERT_TAIL(&enc->hpe_buckets[buckno].by_name, entry, ete_next_name);
enc->hpe_cur_capacity += DYNAMIC_ENTRY_OVERHEAD + name_len + value_len;
++enc->hpe_nelem;
henc_remove_overflow_entries(enc);
return 0;
}
unsigned char *
lsquic_henc_encode (struct lsquic_henc *enc, unsigned char *dst,
unsigned char *dst_end, const char *name, hpack_strlen_t name_len,
const char *value, hpack_strlen_t value_len, int indexed_type)
{
//indexed_type: 0, Add, 1,: without, 2: never
static const char indexed_prefix_number[] = {0x40, 0x00, 0x10};
unsigned char *const dst_org = dst;
int val_matched, rc;
unsigned table_id;
assert(indexed_type >= 0 && indexed_type <= 2);
if (dst_end <= dst)
return dst_org;
table_id = henc_find_table_id(enc, name, name_len, value, value_len,
&val_matched);
if (table_id > 0)
{
if (val_matched)
{
*dst = 0x80;
dst = henc_enc_int(dst, dst_end, table_id, 7);
/* No need to check return value: we pass it up as-is because
* the behavior is the same.
*/
return dst;
}
else
{
*dst = indexed_prefix_number[indexed_type];
dst = henc_enc_int(dst, dst_end, table_id, ((indexed_type == 0) ? 6 : 4));
if (dst == dst_org)
return dst_org;
}
}
else
{
*dst++ = indexed_prefix_number[indexed_type];
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)name, name_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
}
rc = lsquic_henc_enc_str(dst, dst_end - dst, (const unsigned char *)value, value_len);
if (rc < 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
dst += rc;
if (indexed_type == 0)
{
rc = lsquic_henc_push_entry(enc, name, name_len, value, value_len);
if (rc != 0)
return dst_org; //Failed to enc this header, return unchanged ptr.
}
return dst;
}
void
lsquic_henc_set_max_capacity (struct lsquic_henc *enc, unsigned max_capacity)
{
enc->hpe_max_capacity = max_capacity;
henc_remove_overflow_entries(enc);
}
#ifndef NDEBUG
void
lsquic_henc_iter_reset (struct lsquic_henc *enc)
{
enc->hpe_iter = STAILQ_FIRST(&enc->hpe_all_entries);
}
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc,
struct enc_dyn_table_entry *retval)
{
const struct enc_table_entry *entry;
entry = enc->hpe_iter;
if (!entry)
return -1;
enc->hpe_iter = STAILQ_NEXT(entry, ete_next_all);
retval->name = ETE_NAME(entry);
retval->value = ETE_VALUE(entry);
retval->name_len = entry->ete_name_len;
retval->value_len = entry->ete_val_len;
retval->entry_id = henc_calc_table_id(enc, entry);
return 0;
}
#endif
size_t
lsquic_henc_mem_used (const struct lsquic_henc *enc)
{
const struct enc_table_entry *entry;
size_t size;
size = sizeof(*enc);
STAILQ_FOREACH(entry, &enc->hpe_all_entries, ete_next_all)
size += sizeof(*entry) + entry->ete_name_len + entry->ete_val_len;
size += N_BUCKETS(enc->hpe_nbits) * sizeof(enc->hpe_buckets[0]);
return size;
}

View file

@ -1,107 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hpack_enc.h - HPACK encoder
*/
#ifndef LSQUIC_HPACK_ENC_H
#define LSQUIC_HPACK_ENC_H 1
#include "lsquic_hpack_types.h"
struct enc_table_entry;
#ifndef NDEBUG
struct enc_dyn_table_entry
{
const char *name, /* Not NUL-terminated */
*value; /* Not NUL-terminated */
unsigned name_len,
value_len;
unsigned entry_id;
};
#endif
STAILQ_HEAD(enc_head, enc_table_entry);
struct double_enc_head;
struct lsquic_henc
{
unsigned hpe_cur_capacity;
unsigned hpe_max_capacity;
/* Each new dynamic table entry gets the next number. It is used to
* calculate the entry's position in the decoder table without having
* to maintain an actual array.
*/
unsigned hpe_next_id;
/* Dynamic table entries (struct enc_table_entry) live in two hash
* tables: name/value hash table and name hash table. These tables
* are the same size.
*/
unsigned hpe_nelem;
unsigned hpe_nbits;
struct enc_head hpe_all_entries;
struct double_enc_head
*hpe_buckets;
#ifndef NDEBUG
const struct enc_table_entry
*hpe_iter;
#endif
};
/* Initialization routine allocates memory. -1 is returned if memory
* could not be allocated. 0 is returned on success.
*/
int
lsquic_henc_init (struct lsquic_henc *);
void
lsquic_henc_cleanup (struct lsquic_henc *);
/** @lsquic_hpack_encode
* @brief HPACK encode one name/value item
* @param[in,out] henc - A pointer to a valid HPACK API struct
* @param[out] dst - A pointer to destination buffer
* @param[out] dst_end - A pointer to end of destination buffer
* @param[in] name - A pointer to the item name
* @param[in] name_len - The item name's length
* @param[in] value - A pointer to the item value
* @param[in] value_len - The item value's length
* @param[in] indexed_type - 0, Add, 1,: without, 2: never
* @return The (possibly advanced) dst pointer
*/
unsigned char *
lsquic_henc_encode (struct lsquic_henc *henc, unsigned char *dst,
unsigned char *dst_end, const char *name, hpack_strlen_t name_len,
const char *value, hpack_strlen_t value_len, int indexed_type);
void
lsquic_henc_set_max_capacity (struct lsquic_henc *, unsigned);
size_t
lsquic_henc_mem_used (const struct lsquic_henc *);
#ifndef NDEBUG
unsigned
lsquic_henc_get_stx_tab_id (const char *name, hpack_strlen_t name_len,
const char *val, hpack_strlen_t val_len, int *val_matched);
int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
hpack_strlen_t name_len, const char *value, hpack_strlen_t value_len);
int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, hpack_strlen_t str_len);
void
lsquic_henc_iter_reset (struct lsquic_henc *enc);
/* Returns 0 if entry is found */
int
lsquic_henc_iter_next (struct lsquic_henc *enc, struct enc_dyn_table_entry *);
#endif
#endif

View file

@ -1,10 +0,0 @@
/* Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HPACK_TYPES_H
#define LSQUIC_HPACK_TYPES_H 1
typedef uint16_t hpack_strlen_t;
#define HPACK_MAX_STRLEN (((1 << (sizeof(hpack_strlen_t) * 8 - 1)) | \
(((1 << (sizeof(hpack_strlen_t) * 8 - 1)) - 1))))
#endif

View file

@ -586,16 +586,6 @@ lsquic_stream_rst_in (lsquic_stream_t *stream, uint64_t offset,
*/
stream->stream_flags |= STREAM_RST_RECVD;
if ((stream->stream_flags & STREAM_FIN_RECVD) &&
/* Pushed streams have fake STREAM_FIN_RECVD set, thus
* we need a special check:
*/
!lsquic_stream_is_pushed(stream))
{
LSQ_DEBUG("ignore RST_STREAM frame after FIN is received");
return 0;
}
if (lsquic_sfcw_get_max_recv_off(&stream->fc) > offset)
{
LSQ_INFO("stream %u: RST_STREAM invalid: its offset 0x%"PRIX64" is "

View file

@ -2,6 +2,7 @@
include_directories( BEFORE ../../ssl/include )
include_directories( ../../include )
include_directories( ../../src/liblsquic )
include_directories( ../../src/liblsquic/ls-hpack/include )
enable_testing()
@ -210,10 +211,6 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_test(frame_rw test_frame_rw)
ENDIF()
add_executable(test_hpack test_hpack.c)
target_link_libraries(test_hpack lsquic m ${FIULIB})
add_test(hpack test_hpack)
add_executable(test_hkdf test_hkdf.c)
target_link_libraries(test_hkdf lsquic pthread libssl.a libcrypto.a m ${FIULIB})
add_test(hkdf test_hkdf)
@ -443,10 +440,6 @@ add_executable(test_frame_chop test_frame_chop.c ../../wincompat/getopt.c ../../
target_link_libraries(test_frame_chop lsquic ${LIBS_LIST})
add_test(frame_chop test_frame_chop)
add_executable(test_hpack test_hpack.c)
target_link_libraries(test_hpack lsquic ${MIN_LIBS_LIST})
add_test(hpack test_hpack)
add_executable(test_hkdf test_hkdf.c)
target_link_libraries(test_hkdf lsquic ${LIBS_LIST})
add_test(hkdf test_hkdf)

View file

@ -21,7 +21,7 @@
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_hpack_enc.h"
#include "lshpack.h"
#include "lsquic_logger.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
@ -95,11 +95,11 @@ test_chop (unsigned max_write_sz)
struct lsquic_frame_writer *fw;
struct lsquic_stream *stream;
struct lsquic_mm mm;
struct lsquic_henc henc;
struct lshpack_enc henc;
int s;
lsquic_mm_init(&mm);
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
stream = stream_new(max_write_sz);
fw = lsquic_frame_writer_new(&mm, stream, 0, &henc, stream_write, 0);
@ -161,7 +161,7 @@ test_chop (unsigned max_write_sz)
lsquic_frame_writer_destroy(fw);
stream_destroy(stream);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}

View file

@ -13,8 +13,7 @@
#include "lsquic.h"
#include "lsquic_frame_common.h"
#include "lsquic_arr.h"
#include "lsquic_hpack_dec.h"
#include "lshpack.h"
#include "lsquic_mm.h"
#include "lsquic_logger.h"
@ -1063,12 +1062,12 @@ test_one_frt (const struct frame_reader_test *frt)
{
struct lsquic_frame_reader *fr;
unsigned short exp_off;
struct lsquic_hdec hdec;
struct lshpack_dec hdec;
struct lsquic_mm mm;
int s;
lsquic_mm_init(&mm);
lsquic_hdec_init(&hdec);
lshpack_dec_init(&hdec);
memset(&input, 0, sizeof(input));
memcpy(input.in_buf, frt->frt_buf, frt->frt_bufsz);
input.in_sz = frt->frt_bufsz;
@ -1105,7 +1104,7 @@ test_one_frt (const struct frame_reader_test *frt)
lsquic_frame_reader_destroy(fr);
}
while (input.in_max_sz < input.in_max_req_sz);
lsquic_hdec_cleanup(&hdec);
lshpack_dec_cleanup(&hdec);
lsquic_mm_cleanup(&mm);
}

View file

@ -21,9 +21,7 @@
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_arr.h"
#include "lsquic_hpack_enc.h"
#include "lsquic_hpack_dec.h"
#include "lshpack.h"
#include "lsquic_logger.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
@ -186,13 +184,13 @@ test_rw (unsigned max_frame_sz)
struct lsquic_stream *stream;
struct uncompressed_headers *uh;
struct lsquic_mm mm;
struct lsquic_henc henc;
struct lsquic_hdec hdec;
struct lshpack_enc henc;
struct lshpack_dec hdec;
int s;
lsquic_mm_init(&mm);
lsquic_henc_init(&henc);
lsquic_hdec_init(&hdec);
lshpack_enc_init(&henc);
lshpack_dec_init(&hdec);
stream = stream_new();
stream->sm_max_sz = 1;
@ -236,8 +234,8 @@ test_rw (unsigned max_frame_sz)
lsquic_frame_writer_destroy(fw);
stream_destroy(stream);
lsquic_henc_cleanup(&henc);
lsquic_hdec_cleanup(&hdec);
lshpack_enc_cleanup(&henc);
lshpack_dec_cleanup(&hdec);
lsquic_mm_cleanup(&mm);
}

View file

@ -10,7 +10,7 @@
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_hpack_enc.h"
#include "lshpack.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_writer.h"
@ -54,12 +54,12 @@ output_write (struct lsquic_stream *stream, const void *buf, size_t sz)
static void
test_max_frame_size (void)
{
struct lsquic_henc henc;
struct lshpack_enc henc;
struct lsquic_mm mm;
struct lsquic_frame_writer *fw;
unsigned max_size;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
for (max_size = 1; max_size < 6 /* one settings frame */; ++max_size)
@ -73,7 +73,7 @@ test_max_frame_size (void)
assert(fw);
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}
@ -81,12 +81,12 @@ test_max_frame_size (void)
static void
test_one_header (void)
{
struct lsquic_henc henc;
struct lshpack_enc henc;
struct lsquic_frame_writer *fw;
int s;
struct lsquic_mm mm;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 0);
reset_output(0);
@ -131,7 +131,7 @@ test_one_header (void)
sizeof(struct http_prio_frame), "\x48\x82\x64\x02", 4));
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}
@ -139,14 +139,14 @@ test_one_header (void)
static void
test_oversize_header (void)
{
struct lsquic_henc henc;
struct lshpack_enc henc;
struct lsquic_frame_writer *fw;
int s;
struct lsquic_mm mm;
const size_t big_len = 100 * 1000;
char *value;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 0);
reset_output(0);
@ -170,7 +170,7 @@ test_oversize_header (void)
assert(-1 == s);
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
free(value);
}
@ -180,11 +180,11 @@ static void
test_continuations (void)
{
struct lsquic_frame_writer *fw;
struct lsquic_henc henc;
struct lshpack_enc henc;
int s;
struct lsquic_mm mm;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 6, &henc, output_write, 0);
reset_output(0);
@ -258,7 +258,7 @@ perl tools/henc.pl :status 302 x-some-header some-value | hexdump -C
assert(0 == memcmp(output.buf + 60, expected_buf + 60, 14));
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}
@ -426,10 +426,10 @@ test_errors (void)
{
struct lsquic_frame_writer *fw;
struct lsquic_mm mm;
struct lsquic_henc henc;
struct lshpack_enc henc;
int s;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 1);
reset_output(0);
@ -466,7 +466,7 @@ test_errors (void)
}
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}
@ -474,12 +474,12 @@ test_errors (void)
static void
test_push_promise (void)
{
struct lsquic_henc henc;
struct lshpack_enc henc;
struct lsquic_frame_writer *fw;
int s;
struct lsquic_mm mm;
lsquic_henc_init(&henc);
lshpack_enc_init(&henc);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 1);
reset_output(0);
@ -543,7 +543,7 @@ perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :sc
sizeof(exp_headers)));
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lshpack_enc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
}

File diff suppressed because it is too large Load diff