Latest changes

- Do not send RST_STREAM when stream is closed for reading
- Raise maximum header size from 4K to 64K
- Check header name and value lengths against maximum imposed by HPACK
- Fix NULL dereference in stream flow controller
This commit is contained in:
Dmitri Tikhonov 2017-10-12 11:26:01 -04:00
parent 83287402d5
commit 0ae3fccd17
13 changed files with 173 additions and 90 deletions

View file

@ -1,3 +1,10 @@
2017-10-12
- Do not send RST_STREAM when stream is closed for reading
- Raise maximum header size from 4K to 64K
- Check header name and value lengths against maximum imposed by HPACK
- Fix NULL dereference in stream flow controller
2017-10-09 2017-10-09
- Hide handshake implementation behind a set of function pointers - Hide handshake implementation behind a set of function pointers

View file

@ -514,7 +514,7 @@ struct header_writer_ctx
} hwc_flags; } hwc_flags;
enum pseudo_header pseh_mask; enum pseudo_header pseh_mask;
char *pseh_bufs[N_PSEH]; char *pseh_bufs[N_PSEH];
uint16_t name_len, hpack_strlen_t name_len,
val_len; val_len;
}; };

View file

@ -49,6 +49,8 @@ struct frame_buf
#define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size) #define frab_left_to_write(f) ((unsigned short) sizeof((f)->frab_buf) - (f)->frab_size)
#define frab_write_to(f) ((f)->frab_buf + (f)->frab_size) #define frab_write_to(f) ((f)->frab_buf + (f)->frab_size)
#define MAX_HEADERS_SIZE (64 * 1024)
/* Make sure that frab_buf is at least five bytes long, otherwise a frame /* Make sure that frab_buf is at least five bytes long, otherwise a frame
* won't fit into two adjacent frabs. * won't fit into two adjacent frabs.
*/ */
@ -392,6 +394,19 @@ calc_headers_size (const struct lsquic_http_headers *headers)
} }
static int
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;
}
return have;
}
static int static int
check_headers_size (const struct lsquic_frame_writer *fw, check_headers_size (const struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers, const struct lsquic_http_headers *headers,
@ -435,25 +450,29 @@ check_headers_case (const struct lsquic_frame_writer *fw,
static int static int
write_headers (struct lsquic_frame_writer *fw, write_headers (struct lsquic_frame_writer *fw,
const struct lsquic_http_headers *headers, const struct lsquic_http_headers *headers,
struct header_framer_ctx *hfc, unsigned char *buf4k) struct header_framer_ctx *hfc, unsigned char *buf,
const unsigned buf_sz)
{ {
unsigned char *end; unsigned char *end;
int i, s; int i, s;
for (i = 0; i < headers->count; ++i) for (i = 0; i < headers->count; ++i)
{ {
end = lsquic_henc_encode(fw->fw_henc, buf4k, buf4k + 0x1000, end = lsquic_henc_encode(fw->fw_henc, buf, buf + buf_sz,
headers->headers[i].name.iov_base, headers->headers[i].name.iov_len, 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); headers->headers[i].value.iov_base, headers->headers[i].value.iov_len, 0);
if (!(end > buf4k)) if (end > buf)
{
s = hfc_write(hfc, buf, end - buf);
if (s < 0)
return s;
}
else
{ {
LSQ_WARN("error encoding header"); LSQ_WARN("error encoding header");
errno = EBADMSG; errno = EBADMSG;
return -1; return -1;
} }
s = hfc_write(hfc, buf4k, end - buf4k);
if (s < 0)
return s;
} }
return 0; return 0;
@ -481,6 +500,9 @@ lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
if (0 != check_headers_case(fw, headers)) if (0 != check_headers_case(fw, headers))
return -1; return -1;
if (have_oversize_strings(headers))
return -1;
if (eos) if (eos)
flags = HFHF_END_STREAM; flags = HFHF_END_STREAM;
else else
@ -501,11 +523,11 @@ lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
return s; return s;
} }
buf = lsquic_mm_get_4k(fw->fw_mm); buf = malloc(MAX_HEADERS_SIZE);
if (!buf) if (!buf)
return -1; return -1;
s = write_headers(fw, headers, &hfc, buf); s = write_headers(fw, headers, &hfc, buf, MAX_HEADERS_SIZE);
lsquic_mm_put_4k(fw->fw_mm, buf); free(buf);
if (0 == s) if (0 == s)
{ {
EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id, EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id,
@ -556,6 +578,12 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
if (extra_headers && 0 != check_headers_case(fw, extra_headers)) if (extra_headers && 0 != check_headers_case(fw, extra_headers))
return -1; return -1;
if (have_oversize_strings(&mpas))
return -1;
if (extra_headers && have_oversize_strings(extra_headers))
return -1;
hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE, hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE,
stream_id, 0); stream_id, 0);
@ -565,21 +593,21 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
if (s < 0) if (s < 0)
return s; return s;
buf = lsquic_mm_get_4k(fw->fw_mm); buf = malloc(MAX_HEADERS_SIZE);
if (!buf) if (!buf)
return -1; return -1;
s = write_headers(fw, &mpas, &hfc, buf); s = write_headers(fw, &mpas, &hfc, buf, MAX_HEADERS_SIZE);
if (s != 0) if (s != 0)
{ {
lsquic_mm_put_4k(fw->fw_mm, buf); free(buf);
return -1; return -1;
} }
if (extra_headers) if (extra_headers)
s = write_headers(fw, extra_headers, &hfc, buf); s = write_headers(fw, extra_headers, &hfc, buf, MAX_HEADERS_SIZE);
lsquic_mm_put_4k(fw->fw_mm, buf); free(buf);
if (0 == s) if (0 == s)
{ {
@ -641,6 +669,7 @@ write_settings (struct lsquic_frame_writer *fw,
return 0; return 0;
} }
int int
lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw, lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
const struct lsquic_http2_setting *settings, unsigned n_settings) const struct lsquic_http2_setting *settings, unsigned n_settings)
@ -711,3 +740,5 @@ lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
return lsquic_frame_writer_flush(fw); return lsquic_frame_writer_flush(fw);
} }

View file

@ -2,6 +2,8 @@
#ifndef LSQUIC_HPACK_COMMON_H #ifndef LSQUIC_HPACK_COMMON_H
#define LSQUIC_HPACK_COMMON_H #define LSQUIC_HPACK_COMMON_H
#include "lsquic_hpack_types.h"
#define HPACK_STATIC_TABLE_SIZE 61 #define HPACK_STATIC_TABLE_SIZE 61
#define INITIAL_DYNAMIC_TABLE_SIZE 4096 #define INITIAL_DYNAMIC_TABLE_SIZE 4096
@ -21,9 +23,9 @@
typedef struct hpack_hdr_tbl_s typedef struct hpack_hdr_tbl_s
{ {
const char *name; const char *name;
uint16_t name_len; hpack_strlen_t name_len;
const char *val; const char *val;
uint16_t val_len; hpack_strlen_t val_len;
} hpack_hdr_tbl_t; } hpack_hdr_tbl_t;
/** /**

View file

@ -6,6 +6,8 @@
#ifndef LSQUIC_HPACK_DEC_H #ifndef LSQUIC_HPACK_DEC_H
#define LSQUIC_HPACK_DEC_H #define LSQUIC_HPACK_DEC_H
#include "lsquic_hpack_types.h"
struct lsquic_hdec struct lsquic_hdec
{ {
unsigned hpd_max_capacity; /* Maximum set by caller */ unsigned hpd_max_capacity; /* Maximum set by caller */
@ -34,7 +36,8 @@ lsquic_hdec_cleanup (struct lsquic_hdec *);
int int
lsquic_hdec_decode (struct lsquic_hdec *dec, lsquic_hdec_decode (struct lsquic_hdec *dec,
const unsigned char **src, const unsigned char *src_end, const unsigned char **src, const unsigned char *src_end,
char *dst, char *const dst_end, uint16_t *name_len, uint16_t *val_len); char *dst, char *const dst_end, hpack_strlen_t *name_len,
hpack_strlen_t *val_len);
void void
lsquic_hdec_set_max_capacity (struct lsquic_hdec *, unsigned); lsquic_hdec_set_max_capacity (struct lsquic_hdec *, unsigned);
@ -46,7 +49,8 @@ lsquic_hdec_dec_int (const unsigned char **src, const unsigned char *src_end,
int int
lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name, lsquic_hdec_push_entry (struct lsquic_hdec *dec, const char *name,
uint16_t name_len, const char *val, uint16_t val_len); hpack_strlen_t name_len, const char *val,
hpack_strlen_t val_len);
#endif #endif
#endif #endif

View file

@ -29,8 +29,8 @@ struct enc_table_entry
unsigned ete_id; unsigned ete_id;
unsigned ete_nameval_hash; unsigned ete_nameval_hash;
unsigned ete_name_hash; unsigned ete_name_hash;
uint16_t ete_name_len; hpack_strlen_t ete_name_len;
uint16_t ete_val_len; hpack_strlen_t ete_val_len;
char ete_buf[0]; char ete_buf[0];
}; };
@ -93,8 +93,8 @@ lsquic_henc_cleanup (struct lsquic_henc *enc)
static static
#endif #endif
unsigned unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len, lsquic_henc_get_stx_tab_id (const char *name, hpack_strlen_t name_len,
const char *val, uint16_t val_len, int *val_matched) const char *val, hpack_strlen_t val_len, int *val_matched)
{ {
if (name_len < 3) if (name_len < 3)
return 0; return 0;
@ -428,7 +428,7 @@ henc_calc_table_id (const struct lsquic_henc *enc,
static unsigned static unsigned
henc_find_table_id (struct lsquic_henc *enc, const char *name, henc_find_table_id (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len, hpack_strlen_t name_len, const char *value, hpack_strlen_t value_len,
int *val_matched) int *val_matched)
{ {
struct enc_table_entry *entry; struct enc_table_entry *entry;
@ -561,7 +561,7 @@ static
#endif #endif
int int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len, lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len) const unsigned char *str, hpack_strlen_t str_len)
{ {
unsigned char size_buf[4]; unsigned char size_buf[4];
unsigned char *p; unsigned char *p;
@ -710,7 +710,8 @@ static
#endif #endif
int int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name, lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len) hpack_strlen_t name_len, const char *value,
hpack_strlen_t value_len)
{ {
unsigned name_hash, nameval_hash, buckno; unsigned name_hash, nameval_hash, buckno;
struct enc_table_entry *entry; struct enc_table_entry *entry;
@ -757,8 +758,8 @@ lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
unsigned char * unsigned char *
lsquic_henc_encode (struct lsquic_henc *enc, unsigned char *dst, lsquic_henc_encode (struct lsquic_henc *enc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len, unsigned char *dst_end, const char *name, hpack_strlen_t name_len,
const char *value, uint16_t value_len, int indexed_type) const char *value, hpack_strlen_t value_len, int indexed_type)
{ {
//indexed_type: 0, Add, 1,: without, 2: never //indexed_type: 0, Add, 1,: without, 2: never
static const char indexed_prefix_number[] = {0x40, 0x00, 0x10}; static const char indexed_prefix_number[] = {0x40, 0x00, 0x10};

View file

@ -6,6 +6,8 @@
#ifndef LSQUIC_HPACK_ENC_H #ifndef LSQUIC_HPACK_ENC_H
#define LSQUIC_HPACK_ENC_H 1 #define LSQUIC_HPACK_ENC_H 1
#include "lsquic_hpack_types.h"
struct enc_table_entry; struct enc_table_entry;
#ifndef NDEBUG #ifndef NDEBUG
@ -72,24 +74,24 @@ lsquic_henc_cleanup (struct lsquic_henc *);
*/ */
unsigned char * unsigned char *
lsquic_henc_encode (struct lsquic_henc *henc, unsigned char *dst, lsquic_henc_encode (struct lsquic_henc *henc, unsigned char *dst,
unsigned char *dst_end, const char *name, uint16_t name_len, unsigned char *dst_end, const char *name, hpack_strlen_t name_len,
const char *value, uint16_t value_len, int indexed_type); const char *value, hpack_strlen_t value_len, int indexed_type);
void void
lsquic_henc_set_max_capacity (struct lsquic_henc *, unsigned); lsquic_henc_set_max_capacity (struct lsquic_henc *, unsigned);
#ifndef NDEBUG #ifndef NDEBUG
unsigned unsigned
lsquic_henc_get_stx_tab_id (const char *name, uint16_t name_len, lsquic_henc_get_stx_tab_id (const char *name, hpack_strlen_t name_len,
const char *val, uint16_t val_len, int *val_matched); const char *val, hpack_strlen_t val_len, int *val_matched);
int int
lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name, lsquic_henc_push_entry (struct lsquic_henc *enc, const char *name,
uint16_t name_len, const char *value, uint16_t value_len); hpack_strlen_t name_len, const char *value, hpack_strlen_t value_len);
int int
lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len, lsquic_henc_enc_str (unsigned char *const dst, size_t dst_len,
const unsigned char *str, uint16_t str_len); const unsigned char *str, hpack_strlen_t str_len);
void void
lsquic_henc_iter_reset (struct lsquic_henc *enc); lsquic_henc_iter_reset (struct lsquic_henc *enc);

View file

@ -0,0 +1,10 @@
/* Copyright (c) 2017 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

@ -49,18 +49,27 @@ sfcw_maybe_increase_max_window (struct lsquic_sfcw *fc)
if (new_max_window > fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw) if (new_max_window > fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw)
new_max_window = fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw; new_max_window = fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw;
/* Do not increase past the connection's maximum window size. The if (fc->sf_cfcw)
* connection's window will be increased separately, if possible. {
* /* Do not increase past the connection's maximum window size. The
* The reference implementation has the logic backwards: Imagine * connection's window will be increased separately, if possible.
* several concurrent streams that are not being read from fast *
* enough by the user code. Each of them uses only a fraction * The reference implementation has the logic backwards: Imagine
* of bandwidth. Does it mean that the connection window must * several concurrent streams that are not being read from fast
* increase? No. * enough by the user code. Each of them uses only a fraction
*/ * of bandwidth. Does it mean that the connection window must
max_conn_window = lsquic_cfcw_get_max_recv_window(fc->sf_cfcw); * increase? No.
if (new_max_window > max_conn_window) */
new_max_window = max_conn_window; max_conn_window = lsquic_cfcw_get_max_recv_window(fc->sf_cfcw);
if (new_max_window > max_conn_window)
new_max_window = max_conn_window;
}
else
{
/* This means that this stream is not affected by connection flow
* controller. No need to adjust under connection window.
*/
}
if (new_max_window > fc->sf_max_recv_win) if (new_max_window > fc->sf_max_recv_win)
{ {

View file

@ -197,7 +197,6 @@ sm_history_append (lsquic_stream_t *stream, enum stream_history_event sh_event)
stream->sm_hist_buf); stream->sm_hist_buf);
} }
# define SM_HISTORY_APPEND(stream, event) sm_history_append(stream, event) # define SM_HISTORY_APPEND(stream, event) sm_history_append(stream, event)
# define SM_HISTORY_DUMP_REMAINING(stream) do { \ # define SM_HISTORY_DUMP_REMAINING(stream) do { \
if (stream->sm_hist_idx & SM_HIST_IDX_MASK) \ if (stream->sm_hist_idx & SM_HIST_IDX_MASK) \
@ -577,8 +576,6 @@ lsquic_stream_frame_in (lsquic_stream_t *stream, stream_frame_t *frame)
max_off = frame->data_frame.df_offset + frame->data_frame.df_size; max_off = frame->data_frame.df_offset + frame->data_frame.df_size;
if (0 != lsquic_stream_update_sfcw(stream, max_off)) if (0 != lsquic_stream_update_sfcw(stream, max_off))
return -1; return -1;
if ((stream->stream_flags & STREAM_U_READ_DONE))
lsquic_stream_reset_ext(stream, 1, 0);
if (frame->data_frame.df_fin) if (frame->data_frame.df_fin)
{ {
SM_HISTORY_APPEND(stream, SHE_FIN_IN); SM_HISTORY_APPEND(stream, SHE_FIN_IN);
@ -902,24 +899,12 @@ lsquic_stream_tosend_fin (const lsquic_stream_t *stream)
} }
static int
readable_data_frame_remains (lsquic_stream_t *stream)
{
return !stream->data_in->di_if->di_empty(stream->data_in);
}
static void static void
stream_shutdown_read (lsquic_stream_t *stream) stream_shutdown_read (lsquic_stream_t *stream)
{ {
if (!(stream->stream_flags & STREAM_U_READ_DONE)) if (!(stream->stream_flags & STREAM_U_READ_DONE))
{ {
SM_HISTORY_APPEND(stream, SHE_SHUTDOWN_READ); SM_HISTORY_APPEND(stream, SHE_SHUTDOWN_READ);
if (stream->uh || readable_data_frame_remains(stream))
{
LSQ_INFO("read shut down, but there is still data to be read");
lsquic_stream_reset_ext(stream, 1, 1);
}
stream->stream_flags |= STREAM_U_READ_DONE; stream->stream_flags |= STREAM_U_READ_DONE;
stream_wantread(stream, 0); stream_wantread(stream, 0);
maybe_finish_stream(stream); maybe_finish_stream(stream);
@ -1314,7 +1299,6 @@ maybe_mark_as_blocked (lsquic_stream_t *stream)
} }
} }
void void
lsquic_stream_dispatch_rw_events (lsquic_stream_t *stream) lsquic_stream_dispatch_rw_events (lsquic_stream_t *stream)
{ {
@ -1384,7 +1368,6 @@ sbt_write (struct stream_buf_tosend *sbt, const void *buf, size_t len)
return ntowrite; return ntowrite;
} }
static size_t static size_t
sbt_read_buf (struct stream_buf_tosend *sbt, void *buf, size_t len) sbt_read_buf (struct stream_buf_tosend *sbt, void *buf, size_t len)
{ {
@ -2403,5 +2386,3 @@ lsquic_stream_refuse_push (lsquic_stream_t *stream)
else else
return -1; return -1;
} }

View file

@ -9,6 +9,7 @@
#define LSQUIC_STREAM_DEFAULT_PRIO 16 /* RFC 7540, Section 5.3.5 */ #define LSQUIC_STREAM_DEFAULT_PRIO 16 /* RFC 7540, Section 5.3.5 */
struct lsquic_stream_if; struct lsquic_stream_if;
struct lsquic_stream_ctx; struct lsquic_stream_ctx;
struct lsquic_conn_public; struct lsquic_conn_public;
@ -18,6 +19,7 @@ struct uncompressed_headers;
TAILQ_HEAD(lsquic_streams_tailq, lsquic_stream); TAILQ_HEAD(lsquic_streams_tailq, lsquic_stream);
TAILQ_HEAD(sbts_tailq, stream_buf_tosend); TAILQ_HEAD(sbts_tailq, stream_buf_tosend);
#ifndef LSQUIC_KEEP_STREAM_HISTORY #ifndef LSQUIC_KEEP_STREAM_HISTORY
# ifdef NDEBUG # ifdef NDEBUG
# define LSQUIC_KEEP_STREAM_HISTORY 0 # define LSQUIC_KEEP_STREAM_HISTORY 0
@ -26,12 +28,14 @@ TAILQ_HEAD(sbts_tailq, stream_buf_tosend);
# endif # endif
#endif #endif
#if LSQUIC_KEEP_STREAM_HISTORY #if LSQUIC_KEEP_STREAM_HISTORY
#define SM_HIST_BITS 6 #define SM_HIST_BITS 6
#define SM_HIST_IDX_MASK ((1 << SM_HIST_BITS) - 1) #define SM_HIST_IDX_MASK ((1 << SM_HIST_BITS) - 1)
typedef unsigned char sm_hist_idx_t; typedef unsigned char sm_hist_idx_t;
#endif #endif
struct lsquic_stream struct lsquic_stream
{ {
uint32_t id; uint32_t id;
@ -143,6 +147,7 @@ struct lsquic_stream
#endif #endif
}; };
enum stream_ctor_flags enum stream_ctor_flags
{ {
SCF_CALL_ON_NEW = (1 << 0), /* Call on_new_stream() immediately */ SCF_CALL_ON_NEW = (1 << 0), /* Call on_new_stream() immediately */
@ -156,6 +161,7 @@ enum stream_ctor_flags
SCF_DISP_RW_ONCE = (1 << 3), SCF_DISP_RW_ONCE = (1 << 3),
}; };
lsquic_stream_t * lsquic_stream_t *
lsquic_stream_new_ext (uint32_t id, struct lsquic_conn_public *conn_pub, lsquic_stream_new_ext (uint32_t id, struct lsquic_conn_public *conn_pub,
const struct lsquic_stream_if *, void *stream_if_ctx, const struct lsquic_stream_if *, void *stream_if_ctx,

View file

@ -150,6 +150,47 @@ test_one_header (void)
} }
static void
test_oversize_header (void)
{
struct lsquic_henc 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);
lsquic_mm_init(&mm);
fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
output_navail, output_flush, 0);
reset_output(0);
value = malloc(big_len);
memset(value, 'A', big_len);
struct lsquic_http_header header_arr[] =
{
{ .name = IOV(":status"), .value = IOV("302") },
{ .name = IOV("some-header"),
.value = { .iov_base = value, .iov_len = big_len, } },
};
struct lsquic_http_headers headers = {
.count = sizeof(header_arr) / sizeof(header_arr[0]),
.headers = header_arr,
};
s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
assert(-1 == s);
lsquic_frame_writer_destroy(fw);
lsquic_henc_cleanup(&henc);
lsquic_mm_cleanup(&mm);
free(value);
}
static void static void
test_continuations (void) test_continuations (void)
{ {
@ -533,6 +574,7 @@ int
main (void) main (void)
{ {
test_one_header(); test_one_header();
test_oversize_header();
test_continuations(); test_continuations();
test_settings_normal(); test_settings_normal();
test_settings_short(); test_settings_short();

View file

@ -204,7 +204,6 @@ permute_and_run (uint64_t run_id,
} }
} }
static void static void
test_write_file (const char *filename) test_write_file (const char *filename)
{ {
@ -592,7 +591,7 @@ test_rem_FIN_loc_FIN (struct test_objs *tobjs)
/* Server: we read data and close the read side before reading FIN, which /* Server: we read data and close the read side before reading FIN, which
* results in stream being reset. * DOES NOT result in stream being reset.
*/ */
static void static void
test_rem_data_loc_close (struct test_objs *tobjs) test_rem_data_loc_close (struct test_objs *tobjs)
@ -610,40 +609,30 @@ test_rem_data_loc_close (struct test_objs *tobjs)
n = lsquic_stream_read(stream, buf, 60); n = lsquic_stream_read(stream, buf, 60);
assert(60 == n); assert(60 == n);
s = lsquic_stream_shutdown(stream, 0); /* This causes a reset */ s = lsquic_stream_shutdown(stream, 0);
assert(0 == s); assert(0 == s);
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
assert((stream->stream_flags & (STREAM_SERVICE_FLAGS)) assert(!((stream->stream_flags & (STREAM_SERVICE_FLAGS))
== STREAM_CALL_ONCLOSE); == STREAM_CALL_ONCLOSE));
n = lsquic_stream_write(stream, buf, 100);
assert(n == -1); /* Cannot write to reset stream */
n = lsquic_stream_read(stream, buf, 60); n = lsquic_stream_read(stream, buf, 60);
assert(n == -1); /* Cannot read from reset stream */ assert(n == -1); /* Cannot read from closed stream */
/* Close write side */
s = lsquic_stream_shutdown(stream, 1); s = lsquic_stream_shutdown(stream, 1);
assert(-1 == s); assert(0 == s);
/* Reset is scheduled to be sent out: */ /* STREAM frame is scheduled to be sent out: */
assert(!TAILQ_EMPTY(&tobjs->conn_pub.sending_streams)); assert(!TAILQ_EMPTY(&tobjs->conn_pub.sending_streams));
assert((stream->stream_flags & (STREAM_SENDING_FLAGS)) assert((stream->stream_flags & (STREAM_SENDING_FLAGS))
== STREAM_SEND_RST); == STREAM_SEND_DATA);
lsquic_stream_call_on_close(stream);
assert(!(stream->stream_flags & (STREAM_SERVICE_FLAGS)));
/* reset frame has been sent */
lsquic_stream_rst_frame_sent(stream);
assert(TAILQ_EMPTY(&tobjs->conn_pub.sending_streams));
assert(!(stream->stream_flags & (STREAM_SENDING_FLAGS)));
s = lsquic_stream_rst_in(stream, 100, 1); s = lsquic_stream_rst_in(stream, 100, 1);
assert(0 == s); assert(0 == s);
assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); assert(!TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
assert((stream->stream_flags & (STREAM_SERVICE_FLAGS)) assert((stream->stream_flags & (STREAM_SERVICE_FLAGS))
== STREAM_FREE_STREAM); == STREAM_CALL_ONCLOSE);
lsquic_stream_destroy(stream); lsquic_stream_destroy(stream);
assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams)); assert(TAILQ_EMPTY(&tobjs->conn_pub.service_streams));
@ -653,6 +642,7 @@ test_rem_data_loc_close (struct test_objs *tobjs)
} }
/* Client: we send some data and FIN, but remote end sends some data and /* Client: we send some data and FIN, but remote end sends some data and
* then resets the stream. The client gets an error when it reads from * then resets the stream. The client gets an error when it reads from
* stream, after which it closes and destroys the stream. * stream, after which it closes and destroys the stream.
@ -948,6 +938,7 @@ test_unlimited_stream_flush_data (struct test_objs *tobjs)
} }
/* Write a little data to the stream, packetize the data, then reset the /* Write a little data to the stream, packetize the data, then reset the
* stream: connection cap should NOT go back up. * stream: connection cap should NOT go back up.
* *
@ -1947,8 +1938,5 @@ main (int argc, char **argv)
test_flushing(); test_flushing();
return 0; return 0;
} }