mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Latest changes:
- Switch to using ls-hpack 1.1 - [BUGFIX] Do not ignore stream resets after receiving FIN
This commit is contained in:
parent
bf6b47adbf
commit
bea6482295
22 changed files with 1691 additions and 12978 deletions
|
@ -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.
|
||||
|
|
|
@ -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
163
src/liblsquic/lshpack.h
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 "
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue