mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.4.7
- Add echo client and server to the distibution. - Add MD5 client and server to the distibution. - Fix http_client: check command-line arguments better, prevent crash. - Fix IETF conn: can_write_ack() should only care about APP PNS. - Client: delay stream creation until handshake succeds. - Reset HTTP stream whose write end is closed prematurely. - Fix tickable(): mirror behavior of tick() wrt buffered packets. - Log reason why engine is tickable.
This commit is contained in:
parent
ad08470cea
commit
0adf085acf
27 changed files with 1668 additions and 92 deletions
11
CHANGELOG
11
CHANGELOG
|
@ -1,3 +1,14 @@
|
|||
2019-10-15
|
||||
- 2.4.7
|
||||
- Add echo client and server to the distibution.
|
||||
- Add MD5 client and server to the distibution.
|
||||
- Fix http_client: check command-line arguments better, prevent crash.
|
||||
- Fix IETF conn: can_write_ack() should only care about APP PNS.
|
||||
- Client: delay stream creation until handshake succeds.
|
||||
- Reset HTTP stream whose write end is closed prematurely.
|
||||
- Fix tickable(): mirror behavior of tick() wrt buffered packets.
|
||||
- Log reason why engine is tickable.
|
||||
|
||||
2019-10-11
|
||||
- 2.4.6
|
||||
- Minor code cleanup and logging improvements.
|
||||
|
|
|
@ -200,6 +200,10 @@ ELSE()
|
|||
ENDIF()
|
||||
|
||||
add_executable(http_server test/http_server.c test/prog.c test/test_common.c test/test_cert.c)
|
||||
add_executable(md5_server test/md5_server.c test/prog.c test/test_common.c test/test_cert.c)
|
||||
add_executable(md5_client test/md5_client.c test/prog.c test/test_common.c test/test_cert.c)
|
||||
add_executable(echo_server test/echo_server.c test/prog.c test/test_common.c test/test_cert.c)
|
||||
add_executable(echo_client test/echo_client.c test/prog.c test/test_common.c test/test_cert.c)
|
||||
|
||||
|
||||
SET(LIBS lsquic ${EVENT_LIB} ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB} ${LIBS})
|
||||
|
@ -230,6 +234,10 @@ ENDIF()
|
|||
|
||||
TARGET_LINK_LIBRARIES(http_client ${LIBS})
|
||||
TARGET_LINK_LIBRARIES(http_server ${LIBS})
|
||||
TARGET_LINK_LIBRARIES(md5_server ${LIBS})
|
||||
TARGET_LINK_LIBRARIES(md5_client ${LIBS})
|
||||
TARGET_LINK_LIBRARIES(echo_server ${LIBS})
|
||||
TARGET_LINK_LIBRARIES(echo_client ${LIBS})
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
|
17
EXAMPLES.txt
17
EXAMPLES.txt
|
@ -7,6 +7,23 @@ LSQUIC comes with several examples of how the library is used.
|
|||
The client and server programs described below are built on a common
|
||||
framework and share many options.
|
||||
|
||||
Echo client and server
|
||||
----------------------
|
||||
|
||||
Echo client and server (see test/echo_{client,server}.c) are for simple
|
||||
line-based request and reply communication. Only one stream per connection
|
||||
is supported for simplicity. The client reads input from stdin.
|
||||
|
||||
MD5 client and server
|
||||
---------------------
|
||||
|
||||
See test/md5_{client,server}.c
|
||||
|
||||
MD5 server accepts connections, computes MD5 sum of streams' (one or more)
|
||||
payload, and sends back the checksum. MD5 client sends one or more file
|
||||
contents to the server. Both client and server support various options to
|
||||
exercise different aspects of LSQUIC.
|
||||
|
||||
HTTP client and server
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 4
|
||||
#define LSQUIC_PATCH_VERSION 6
|
||||
#define LSQUIC_PATCH_VERSION 7
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
|
|
@ -32,7 +32,7 @@ lsquic_alarmset_init_alarm (lsquic_alarmset_t *alset, enum alarm_id al_id,
|
|||
}
|
||||
|
||||
|
||||
static const char *const lsquic_alid2str[] =
|
||||
const char *const lsquic_alid2str[] =
|
||||
{
|
||||
[AL_HANDSHAKE] = "HANDSHAKE",
|
||||
[AL_RETX_INIT] = "RETX_INIT",
|
||||
|
@ -75,20 +75,22 @@ lsquic_alarmset_ring_expired (lsquic_alarmset_t *alset, lsquic_time_t now)
|
|||
|
||||
|
||||
lsquic_time_t
|
||||
lsquic_alarmset_mintime (const lsquic_alarmset_t *alset)
|
||||
lsquic_alarmset_mintime (const lsquic_alarmset_t *alset, enum alarm_id *idp)
|
||||
{
|
||||
lsquic_time_t expiry;
|
||||
enum alarm_id al_id;
|
||||
enum alarm_id al_id, ret_id;
|
||||
|
||||
if (alset->as_armed_set)
|
||||
{
|
||||
expiry = UINT64_MAX;
|
||||
for (al_id = 0; al_id < MAX_LSQUIC_ALARMS; ++al_id)
|
||||
for (al_id = 0, ret_id = 0; al_id < MAX_LSQUIC_ALARMS; ++al_id)
|
||||
if ((alset->as_armed_set & (1 << al_id))
|
||||
&& alset->as_expiry[al_id] < expiry)
|
||||
{
|
||||
expiry = alset->as_expiry[al_id];
|
||||
ret_id = al_id;
|
||||
}
|
||||
*idp = ret_id;
|
||||
return expiry;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -94,6 +94,8 @@ void
|
|||
lsquic_alarmset_ring_expired (lsquic_alarmset_t *, lsquic_time_t now);
|
||||
|
||||
lsquic_time_t
|
||||
lsquic_alarmset_mintime (const lsquic_alarmset_t *);
|
||||
lsquic_alarmset_mintime (const lsquic_alarmset_t *, enum alarm_id *);
|
||||
|
||||
extern const char *const lsquic_alid2str[];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_packet_common.h"
|
||||
#include "lsquic_alarmset.h"
|
||||
#include "lsquic_malo.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_conn.h"
|
||||
|
@ -107,7 +109,7 @@ attq_swap (struct attq *q, unsigned a, unsigned b)
|
|||
|
||||
int
|
||||
attq_add (struct attq *q, struct lsquic_conn *conn,
|
||||
lsquic_time_t advisory_time)
|
||||
lsquic_time_t advisory_time, enum ae_why why)
|
||||
{
|
||||
struct attq_elem *el, **heap;
|
||||
unsigned n, i;
|
||||
|
@ -129,6 +131,7 @@ attq_add (struct attq *q, struct lsquic_conn *conn,
|
|||
if (!el)
|
||||
return -1;
|
||||
el->ae_adv_time = advisory_time;
|
||||
el->ae_why = why;
|
||||
|
||||
/* The only place linkage between conn and attq_elem occurs: */
|
||||
el->ae_conn = conn;
|
||||
|
@ -256,11 +259,29 @@ attq_count_before (struct attq *q, lsquic_time_t cutoff)
|
|||
}
|
||||
|
||||
|
||||
const lsquic_time_t *
|
||||
attq_next_time (struct attq *q)
|
||||
const struct attq_elem *
|
||||
attq_next (struct attq *q)
|
||||
{
|
||||
if (q->aq_nelem > 0)
|
||||
return &q->aq_heap[0]->ae_adv_time;
|
||||
return q->aq_heap[0];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
lsquic_attq_why2str (enum ae_why why)
|
||||
{
|
||||
switch (why)
|
||||
{
|
||||
case AEW_PACER:
|
||||
return "PACER";
|
||||
case AEW_MINI_EXPIRE:
|
||||
return "MINI-EXPIRE";
|
||||
default:
|
||||
why -= N_AEWS;
|
||||
if ((unsigned) why < (unsigned) MAX_LSQUIC_ALARMS)
|
||||
return lsquic_alid2str[why];
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,15 @@ struct attq_elem
|
|||
struct lsquic_conn *ae_conn;
|
||||
lsquic_time_t ae_adv_time;
|
||||
unsigned ae_heap_idx;
|
||||
/* The "why" describes why the connection is in the Advisory Tick Time
|
||||
* Queue. Values past the range describe different alarm types (see
|
||||
* enum alarm_id).
|
||||
*/
|
||||
enum ae_why {
|
||||
AEW_PACER,
|
||||
AEW_MINI_EXPIRE,
|
||||
N_AEWS
|
||||
} ae_why;
|
||||
};
|
||||
|
||||
|
||||
|
@ -29,7 +38,8 @@ attq_destroy (struct attq *);
|
|||
|
||||
/* Return 0 on success, -1 on failure (malloc) */
|
||||
int
|
||||
attq_add (struct attq *, struct lsquic_conn *, lsquic_time_t advisory_time);
|
||||
attq_add (struct attq *, struct lsquic_conn *, lsquic_time_t advisory_time,
|
||||
enum ae_why);
|
||||
|
||||
void
|
||||
attq_remove (struct attq *, struct lsquic_conn *);
|
||||
|
@ -40,7 +50,10 @@ attq_pop (struct attq *, lsquic_time_t cutoff);
|
|||
unsigned
|
||||
attq_count_before (struct attq *, lsquic_time_t cutoff);
|
||||
|
||||
const lsquic_time_t *
|
||||
attq_next_time (struct attq *);
|
||||
const struct attq_elem *
|
||||
attq_next (struct attq *);
|
||||
|
||||
const char *
|
||||
lsquic_attq_why2str (enum ae_why);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -123,7 +123,7 @@ struct conn_iface
|
|||
(*ci_is_tickable) (struct lsquic_conn *);
|
||||
|
||||
lsquic_time_t
|
||||
(*ci_next_tick_time) (struct lsquic_conn *);
|
||||
(*ci_next_tick_time) (struct lsquic_conn *, unsigned *why);
|
||||
|
||||
int
|
||||
(*ci_can_write_ack) (struct lsquic_conn *);
|
||||
|
|
|
@ -2138,6 +2138,14 @@ iquic_esf_alg_keysize (enc_session_t *enc_session_p)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
iquic_esf_zero_rtt_enabled (enc_session_t *enc_session_p)
|
||||
{
|
||||
struct enc_sess_iquic *const enc_sess = enc_session_p;
|
||||
return enc_sess->esi_zero_rtt_buf != NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iquic_esfi_reset_dcid (enc_session_t *enc_session_p,
|
||||
const lsquic_cid_t *old_dcid, const lsquic_cid_t *new_dcid)
|
||||
|
@ -2204,6 +2212,7 @@ const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1 =
|
|||
.esf_cipher = iquic_esf_cipher,
|
||||
.esf_keysize = iquic_esf_keysize,
|
||||
.esf_alg_keysize = iquic_esf_alg_keysize,
|
||||
.esf_is_zero_rtt_enabled = iquic_esf_zero_rtt_enabled,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1179,7 +1179,7 @@ lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub,
|
|||
|
||||
void
|
||||
lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
|
||||
lsquic_conn_t *conn, lsquic_time_t tick_time)
|
||||
lsquic_conn_t *conn, lsquic_time_t tick_time, unsigned why)
|
||||
{
|
||||
lsquic_engine_t *const engine = (lsquic_engine_t *) enpub;
|
||||
if (conn->cn_flags & LSCONN_TICKABLE)
|
||||
|
@ -1194,11 +1194,11 @@ lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
|
|||
if (lsquic_conn_adv_time(conn) != tick_time)
|
||||
{
|
||||
attq_remove(engine->attq, conn);
|
||||
if (0 != attq_add(engine->attq, conn, tick_time))
|
||||
if (0 != attq_add(engine->attq, conn, tick_time, why))
|
||||
engine_decref_conn(engine, conn, LSCONN_ATTQ);
|
||||
}
|
||||
}
|
||||
else if (0 == attq_add(engine->attq, conn, tick_time))
|
||||
else if (0 == attq_add(engine->attq, conn, tick_time, why))
|
||||
engine_incref_conn(conn, LSCONN_ATTQ);
|
||||
}
|
||||
|
||||
|
@ -2355,7 +2355,7 @@ process_connections (lsquic_engine_t *engine, conn_iter_f next_conn,
|
|||
{
|
||||
lsquic_conn_t *conn;
|
||||
enum tick_st tick_st;
|
||||
unsigned i;
|
||||
unsigned i, why;
|
||||
lsquic_time_t next_tick_time;
|
||||
struct conns_stailq closed_conns;
|
||||
struct conns_tailq ticked_conns;
|
||||
|
@ -2453,10 +2453,10 @@ process_connections (lsquic_engine_t *engine, conn_iter_f next_conn,
|
|||
}
|
||||
else if (!(conn->cn_flags & LSCONN_ATTQ))
|
||||
{
|
||||
next_tick_time = conn->cn_if->ci_next_tick_time(conn);
|
||||
next_tick_time = conn->cn_if->ci_next_tick_time(conn, &why);
|
||||
if (next_tick_time)
|
||||
{
|
||||
if (0 == attq_add(engine->attq, conn, next_tick_time))
|
||||
if (0 == attq_add(engine->attq, conn, next_tick_time, why))
|
||||
engine_incref_conn(conn, LSCONN_ATTQ);
|
||||
}
|
||||
else
|
||||
|
@ -2590,38 +2590,80 @@ lsquic_engine_cooldown (lsquic_engine_t *engine)
|
|||
int
|
||||
lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff)
|
||||
{
|
||||
const lsquic_time_t *next_attq_time;
|
||||
const struct attq_elem *next_attq;
|
||||
lsquic_time_t now, next_time;
|
||||
const struct lsquic_conn *conn;
|
||||
const lsquic_cid_t *cid;
|
||||
const enum lsq_log_level L = LSQ_LOG_DEBUG; /* Easy toggle */
|
||||
|
||||
ENGINE_CALLS_INCR(engine);
|
||||
|
||||
if (((engine->flags & ENG_PAST_DEADLINE)
|
||||
if ((engine->flags & ENG_PAST_DEADLINE)
|
||||
&& lsquic_mh_count(&engine->conns_out))
|
||||
|| (engine->pr_queue && prq_have_pending(engine->pr_queue))
|
||||
|| lsquic_mh_count(&engine->conns_tickable))
|
||||
{
|
||||
conn = lsquic_mh_peek(&engine->conns_out);
|
||||
cid = lsquic_conn_log_cid(conn);
|
||||
LSQ_LOGC(L, "next advisory tick is now: went past deadline last time "
|
||||
"and have %u outgoing connection%.*s (%"CID_FMT" first)",
|
||||
lsquic_mh_count(&engine->conns_out),
|
||||
lsquic_mh_count(&engine->conns_out) != 1, "s", CID_BITS(cid));
|
||||
*diff = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
next_attq_time = attq_next_time(engine->attq);
|
||||
if (engine->pr_queue && prq_have_pending(engine->pr_queue))
|
||||
{
|
||||
LSQ_LOG(L, "next advisory tick is now: have pending PRQ elements");
|
||||
*diff = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lsquic_mh_count(&engine->conns_tickable))
|
||||
{
|
||||
conn = lsquic_mh_peek(&engine->conns_tickable);
|
||||
cid = lsquic_conn_log_cid(conn);
|
||||
LSQ_LOGC(L, "next advisory tick is now: have %u tickable "
|
||||
"connection%.*s (%"CID_FMT" first)",
|
||||
lsquic_mh_count(&engine->conns_tickable),
|
||||
lsquic_mh_count(&engine->conns_tickable) != 1, "s", CID_BITS(cid));
|
||||
*diff = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
next_attq = attq_next(engine->attq);
|
||||
if (engine->pub.enp_flags & ENPUB_CAN_SEND)
|
||||
{
|
||||
if (next_attq_time)
|
||||
next_time = *next_attq_time;
|
||||
if (next_attq)
|
||||
next_time = next_attq->ae_adv_time;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (next_attq_time)
|
||||
next_time = MIN(*next_attq_time, engine->resume_sending_at);
|
||||
if (next_attq)
|
||||
{
|
||||
next_time = next_attq->ae_adv_time;
|
||||
if (engine->resume_sending_at < next_time)
|
||||
{
|
||||
next_time = engine->resume_sending_at;
|
||||
next_attq = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
next_time = engine->resume_sending_at;
|
||||
}
|
||||
|
||||
now = lsquic_time_now();
|
||||
*diff = (int) ((int64_t) next_time - (int64_t) now);
|
||||
if (next_attq)
|
||||
{
|
||||
cid = lsquic_conn_log_cid(next_attq->ae_conn);
|
||||
LSQ_LOGC(L, "next advisory tick is %d usec away: conn %"CID_FMT
|
||||
": %s", *diff, CID_BITS(cid),
|
||||
lsquic_attq_why2str(next_attq->ae_why));
|
||||
}
|
||||
else
|
||||
LSQ_LOG(L, "next advisory tick is %d usec away: resume sending", *diff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *,
|
|||
*/
|
||||
void
|
||||
lsquic_engine_add_conn_to_attq (struct lsquic_engine_public *enpub,
|
||||
lsquic_conn_t *, lsquic_time_t);
|
||||
lsquic_conn_t *, lsquic_time_t, unsigned why);
|
||||
|
||||
void
|
||||
lsquic_engine_retire_cid (struct lsquic_engine_public *,
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "lsquic_version.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_handshake.h"
|
||||
#include "lsquic_attq.h"
|
||||
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_conn_public.h"
|
||||
|
@ -1326,11 +1327,21 @@ full_conn_ci_n_avail_streams (const lsquic_conn_t *lconn)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
handshake_done_or_doing_zero_rtt (const struct full_conn *conn)
|
||||
{
|
||||
return (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
|| conn->fc_conn.cn_esf_c->esf_is_zero_rtt_enabled(
|
||||
conn->fc_conn.cn_enc_session);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
full_conn_ci_make_stream (struct lsquic_conn *lconn)
|
||||
{
|
||||
struct full_conn *conn = (struct full_conn *) lconn;
|
||||
if (full_conn_ci_n_avail_streams(lconn) > 0)
|
||||
if (handshake_done_or_doing_zero_rtt(conn)
|
||||
&& full_conn_ci_n_avail_streams(lconn) > 0)
|
||||
{
|
||||
if (!new_stream(conn, generate_stream_id(conn), SCF_CALL_ON_NEW))
|
||||
ABORT_ERROR("could not create new stream: %s", strerror(errno));
|
||||
|
@ -3440,9 +3451,7 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
|
|||
}
|
||||
|
||||
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 0);
|
||||
if (!(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) &&
|
||||
!conn->fc_conn.cn_esf_c->esf_is_zero_rtt_enabled(
|
||||
conn->fc_conn.cn_enc_session))
|
||||
if (!handshake_done_or_doing_zero_rtt(conn))
|
||||
{
|
||||
process_hsk_stream_write_events(conn);
|
||||
goto end_write;
|
||||
|
@ -3623,12 +3632,14 @@ full_conn_ci_hsk_done (lsquic_conn_t *lconn, enum lsquic_hsk_status status)
|
|||
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done)
|
||||
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn,
|
||||
status);
|
||||
if ((status == LSQ_HSK_OK || status == LSQ_HSK_0RTT_OK)
|
||||
&& conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_zero_rtt_info)
|
||||
if (status == LSQ_HSK_OK || status == LSQ_HSK_0RTT_OK)
|
||||
{
|
||||
conn->fc_conn.cn_esf.g->esf_maybe_dispatch_zero_rtt(
|
||||
conn->fc_conn.cn_enc_session, &conn->fc_conn,
|
||||
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_zero_rtt_info);
|
||||
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_zero_rtt_info)
|
||||
conn->fc_conn.cn_esf.g->esf_maybe_dispatch_zero_rtt(
|
||||
conn->fc_conn.cn_enc_session, &conn->fc_conn,
|
||||
conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_zero_rtt_info);
|
||||
if (conn->fc_n_delayed_streams)
|
||||
create_delayed_streams(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4227,7 +4238,8 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn)
|
|||
LSQ_DEBUG("tickable: flags: 0x%X", conn->fc_flags & send_flags);
|
||||
goto check_can_send;
|
||||
}
|
||||
if (lsquic_send_ctl_has_buffered(&conn->fc_send_ctl))
|
||||
if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
&& lsquic_send_ctl_has_buffered(&conn->fc_send_ctl))
|
||||
{
|
||||
LSQ_DEBUG("tickable: has buffered packets");
|
||||
goto check_can_send;
|
||||
|
@ -4237,9 +4249,7 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn)
|
|||
LSQ_DEBUG("tickable: there are sending streams");
|
||||
goto check_can_send;
|
||||
}
|
||||
if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) ||
|
||||
conn->fc_conn.cn_esf_c->esf_is_zero_rtt_enabled(
|
||||
conn->fc_conn.cn_enc_session))
|
||||
if (handshake_done_or_doing_zero_rtt(conn))
|
||||
{
|
||||
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
|
||||
next_write_stream)
|
||||
|
@ -4283,12 +4293,13 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn)
|
|||
|
||||
|
||||
static lsquic_time_t
|
||||
full_conn_ci_next_tick_time (lsquic_conn_t *lconn)
|
||||
full_conn_ci_next_tick_time (lsquic_conn_t *lconn, unsigned *why)
|
||||
{
|
||||
struct full_conn *conn = (struct full_conn *) lconn;
|
||||
lsquic_time_t alarm_time, pacer_time, now;
|
||||
enum alarm_id al_id;
|
||||
|
||||
alarm_time = lsquic_alarmset_mintime(&conn->fc_alset);
|
||||
alarm_time = lsquic_alarmset_mintime(&conn->fc_alset, &al_id);
|
||||
pacer_time = lsquic_send_ctl_next_pacer_time(&conn->fc_send_ctl);
|
||||
|
||||
if (pacer_time && LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
|
||||
|
@ -4302,14 +4313,28 @@ full_conn_ci_next_tick_time (lsquic_conn_t *lconn)
|
|||
if (alarm_time && pacer_time)
|
||||
{
|
||||
if (alarm_time < pacer_time)
|
||||
{
|
||||
*why = N_AEWS + al_id;
|
||||
return alarm_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
*why = AEW_PACER;
|
||||
return pacer_time;
|
||||
}
|
||||
}
|
||||
else if (alarm_time)
|
||||
{
|
||||
*why = N_AEWS + al_id;
|
||||
return alarm_time;
|
||||
else
|
||||
}
|
||||
else if (pacer_time)
|
||||
{
|
||||
*why = AEW_PACER;
|
||||
return pacer_time;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "lsquic.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_packet_common.h"
|
||||
#include "lsquic_packet_ietf.h"
|
||||
#include "lsquic_packet_in.h"
|
||||
|
@ -1301,7 +1302,8 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
|||
|
||||
|
||||
static int
|
||||
should_generate_ack (struct ietf_full_conn *conn)
|
||||
should_generate_ack (struct ietf_full_conn *conn,
|
||||
enum ifull_conn_flags ack_queued)
|
||||
{
|
||||
unsigned lost_acks;
|
||||
|
||||
|
@ -1312,7 +1314,7 @@ should_generate_ack (struct ietf_full_conn *conn)
|
|||
if (lost_acks)
|
||||
conn->ifc_flags |= lost_acks << IFCBIT_ACK_QUED_SHIFT;
|
||||
|
||||
return (conn->ifc_flags & IFC_ACK_QUEUED) != 0;
|
||||
return (conn->ifc_flags & ack_queued) != 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1320,7 +1322,7 @@ static int
|
|||
ietf_full_conn_ci_can_write_ack (struct lsquic_conn *lconn)
|
||||
{
|
||||
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
||||
return should_generate_ack(conn);
|
||||
return should_generate_ack(conn, IFC_ACK_QUED_APP);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3226,7 +3228,7 @@ ietf_full_conn_ci_is_tickable (struct lsquic_conn *lconn)
|
|||
}
|
||||
|
||||
if ((conn->ifc_enpub->enp_flags & ENPUB_CAN_SEND)
|
||||
&& (should_generate_ack(conn) ||
|
||||
&& (should_generate_ack(conn, IFC_ACK_QUEUED) ||
|
||||
!lsquic_send_ctl_sched_is_blocked(&conn->ifc_send_ctl)))
|
||||
{
|
||||
/* XXX What about queued ACKs: why check but not make tickable? */
|
||||
|
@ -3235,7 +3237,9 @@ ietf_full_conn_ci_is_tickable (struct lsquic_conn *lconn)
|
|||
LSQ_DEBUG("tickable: send flags: 0x%X", conn->ifc_send_flags);
|
||||
goto check_can_send;
|
||||
}
|
||||
if (lsquic_send_ctl_has_buffered(&conn->ifc_send_ctl))
|
||||
if (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE ?
|
||||
lsquic_send_ctl_has_buffered(&conn->ifc_send_ctl) :
|
||||
lsquic_send_ctl_has_buffered_high(&conn->ifc_send_ctl))
|
||||
{
|
||||
LSQ_DEBUG("tickable: has buffered packets");
|
||||
goto check_can_send;
|
||||
|
@ -3683,12 +3687,13 @@ ietf_full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
|
|||
|
||||
|
||||
static lsquic_time_t
|
||||
ietf_full_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
||||
ietf_full_conn_ci_next_tick_time (struct lsquic_conn *lconn, unsigned *why)
|
||||
{
|
||||
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
||||
lsquic_time_t alarm_time, pacer_time, now;
|
||||
enum alarm_id al_id;
|
||||
|
||||
alarm_time = lsquic_alarmset_mintime(&conn->ifc_alset);
|
||||
alarm_time = lsquic_alarmset_mintime(&conn->ifc_alset, &al_id);
|
||||
pacer_time = lsquic_send_ctl_next_pacer_time(&conn->ifc_send_ctl);
|
||||
|
||||
if (pacer_time && LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
|
||||
|
@ -3702,14 +3707,28 @@ ietf_full_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
|||
if (alarm_time && pacer_time)
|
||||
{
|
||||
if (alarm_time < pacer_time)
|
||||
{
|
||||
*why = N_AEWS + al_id;
|
||||
return alarm_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
*why = AEW_PACER;
|
||||
return pacer_time;
|
||||
}
|
||||
}
|
||||
else if (alarm_time)
|
||||
{
|
||||
*why = N_AEWS + al_id;
|
||||
return alarm_time;
|
||||
else
|
||||
}
|
||||
else if (pacer_time)
|
||||
{
|
||||
*why = AEW_PACER;
|
||||
return pacer_time;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -5981,7 +6000,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
|||
have_delayed_packets =
|
||||
lsquic_send_ctl_maybe_squeeze_sched(&conn->ifc_send_ctl);
|
||||
|
||||
if (should_generate_ack(conn))
|
||||
if (should_generate_ack(conn, IFC_ACK_QUEUED))
|
||||
{
|
||||
if (have_delayed_packets)
|
||||
lsquic_send_ctl_reset_packnos(&conn->ifc_send_ctl);
|
||||
|
@ -6263,12 +6282,21 @@ ietf_full_conn_ci_n_avail_streams (const struct lsquic_conn *lconn)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
handshake_done_or_doing_zero_rtt (const struct ietf_full_conn *conn)
|
||||
{
|
||||
return (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
|| conn->ifc_conn.cn_esf_c->esf_is_zero_rtt_enabled(
|
||||
conn->ifc_conn.cn_enc_session);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ietf_full_conn_ci_make_stream (struct lsquic_conn *lconn)
|
||||
{
|
||||
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
|
||||
|
||||
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
if (handshake_done_or_doing_zero_rtt(conn)
|
||||
&& ietf_full_conn_ci_n_avail_streams(lconn) > 0)
|
||||
{
|
||||
if (0 != create_bidi_stream_out(conn))
|
||||
|
|
|
@ -29,6 +29,8 @@ lsquic_mh_insert (struct min_heap *, struct lsquic_conn *conn, uint64_t val);
|
|||
struct lsquic_conn *
|
||||
lsquic_mh_pop (struct min_heap *);
|
||||
|
||||
#define lsquic_mh_peek(heap) ((heap)->mh_elems[0].mhe_conn)
|
||||
|
||||
#define lsquic_mh_count(heap) (+(heap)->mh_nelem)
|
||||
|
||||
#define lsquic_mh_nalloc(heap) (+(heap)->mh_nalloc)
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
#include "lsquic_rechist.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
#include "lsquic_qtags.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_alarmset.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_MINI_CONN
|
||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&mc->mc_conn)
|
||||
|
@ -1892,7 +1894,7 @@ mini_conn_ci_is_tickable (struct lsquic_conn *lconn)
|
|||
|
||||
|
||||
static lsquic_time_t
|
||||
mini_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
||||
mini_conn_ci_next_tick_time (struct lsquic_conn *lconn, unsigned *why)
|
||||
{
|
||||
struct mini_conn *mc = (struct mini_conn *) lconn;
|
||||
lsquic_packet_out_t *packet_out;
|
||||
|
@ -1905,11 +1907,18 @@ mini_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
|||
{
|
||||
retx_time = packet_out->po_sent + calc_retx_timeout(mc);
|
||||
if (retx_time < exp_time)
|
||||
{
|
||||
*why = N_AEWS + AL_RETX_HSK;
|
||||
return retx_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
*why = AEW_MINI_EXPIRE;
|
||||
return exp_time;
|
||||
}
|
||||
}
|
||||
|
||||
*why = AEW_MINI_EXPIRE;
|
||||
return exp_time;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "lsquic_trans_params.h"
|
||||
#include "lsquic_ietf.h"
|
||||
#include "lsquic_packet_ietf.h"
|
||||
#include "lsquic_attq.h"
|
||||
#include "lsquic_alarmset.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_MINI_CONN
|
||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->imc_conn)
|
||||
|
@ -532,7 +534,7 @@ imico_calc_retx_timeout (const struct ietf_mini_conn *conn)
|
|||
|
||||
|
||||
static lsquic_time_t
|
||||
ietf_mini_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
||||
ietf_mini_conn_ci_next_tick_time (struct lsquic_conn *lconn, unsigned *why)
|
||||
{
|
||||
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
|
||||
const struct lsquic_packet_out *packet_out;
|
||||
|
@ -546,11 +548,18 @@ ietf_mini_conn_ci_next_tick_time (struct lsquic_conn *lconn)
|
|||
{
|
||||
retx_time = packet_out->po_sent + imico_calc_retx_timeout(conn);
|
||||
if (retx_time < exp_time)
|
||||
{
|
||||
*why = N_AEWS + AL_RETX_HSK;
|
||||
return retx_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
*why = AEW_MINI_EXPIRE;
|
||||
return exp_time;
|
||||
}
|
||||
}
|
||||
|
||||
*why = AEW_MINI_EXPIRE;
|
||||
return exp_time;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "lsquic_enc_sess.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_malo.h"
|
||||
#include "lsquic_attq.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_SENDCTL
|
||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(ctl->sc_conn_pub->lconn)
|
||||
|
@ -1294,7 +1295,8 @@ lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl)
|
|||
{
|
||||
ctl->sc_flags &= ~SC_SCHED_TICK;
|
||||
lsquic_engine_add_conn_to_attq(ctl->sc_enpub,
|
||||
ctl->sc_conn_pub->lconn, pacer_next_sched(&ctl->sc_pacer));
|
||||
ctl->sc_conn_pub->lconn, pacer_next_sched(&ctl->sc_pacer),
|
||||
AEW_PACER);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -281,6 +281,9 @@ lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *, enum buf_packet_type);
|
|||
TAILQ_FIRST(&(ctl)->sc_buffered_packets[BPT_HIGHEST_PRIO].bpq_packets) \
|
||||
|| TAILQ_FIRST(&(ctl)->sc_buffered_packets[BPT_OTHER_PRIO].bpq_packets ))
|
||||
|
||||
#define lsquic_send_ctl_has_buffered_high(ctl) ( \
|
||||
!TAILQ_EMPTY(&(ctl)->sc_buffered_packets[BPT_HIGHEST_PRIO].bpq_packets))
|
||||
|
||||
#define lsquic_send_ctl_invalidate_bpt_cache(ctl) do { \
|
||||
(ctl)->sc_cached_bpt.stream_id = UINT64_MAX; \
|
||||
} while (0)
|
||||
|
|
|
@ -1536,7 +1536,13 @@ stream_shutdown_write (lsquic_stream_t *stream)
|
|||
&& !stream_is_incoming_unidir(stream)
|
||||
&& !(stream->sm_qflags & SMQF_SEND_RST))
|
||||
{
|
||||
if (stream->sm_n_buffered == 0)
|
||||
if ((stream->sm_bflags & SMBF_USE_HEADERS)
|
||||
&& !(stream->stream_flags & STREAM_HEADERS_SENT))
|
||||
{
|
||||
LSQ_DEBUG("headers not sent, send a reset");
|
||||
lsquic_stream_reset(stream, 0);
|
||||
}
|
||||
else if (stream->sm_n_buffered == 0)
|
||||
{
|
||||
if (0 == lsquic_send_ctl_turn_on_fin(stream->conn_pub->send_ctl,
|
||||
stream))
|
||||
|
|
245
test/echo_client.c
Normal file
245
test/echo_client.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
/*
|
||||
* echo_client.c -- This is really a "line client:" it connects to QUIC server
|
||||
* and sends it stuff, line by line. It works in tandem with echo_server.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "test_common.h"
|
||||
#include "prog.h"
|
||||
|
||||
#include "../src/liblsquic/lsquic_logger.h"
|
||||
|
||||
struct lsquic_conn_ctx;
|
||||
|
||||
struct echo_client_ctx {
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
struct prog *prog;
|
||||
};
|
||||
|
||||
struct lsquic_conn_ctx {
|
||||
lsquic_conn_t *conn;
|
||||
struct echo_client_ctx *client_ctx;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_conn_ctx_t *
|
||||
echo_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
|
||||
{
|
||||
struct echo_client_ctx *client_ctx = stream_if_ctx;
|
||||
lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
|
||||
conn_h->conn = conn;
|
||||
conn_h->client_ctx = client_ctx;
|
||||
client_ctx->conn_h = conn_h;
|
||||
lsquic_conn_make_stream(conn);
|
||||
return conn_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_client_on_conn_closed (lsquic_conn_t *conn)
|
||||
{
|
||||
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
|
||||
LSQ_NOTICE("Connection closed");
|
||||
prog_stop(conn_h->client_ctx->prog);
|
||||
free(conn_h);
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream_ctx {
|
||||
lsquic_stream_t *stream;
|
||||
struct echo_client_ctx *client_ctx;
|
||||
struct event *read_stdin_ev;
|
||||
char buf[0x100];
|
||||
size_t buf_off;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
read_stdin (int fd, short what, void *ctx)
|
||||
{
|
||||
ssize_t nr;
|
||||
lsquic_stream_ctx_t *st_h = ctx;
|
||||
|
||||
nr = read(fd, st_h->buf + st_h->buf_off++, 1);
|
||||
LSQ_DEBUG("read %zd bytes from stdin", nr);
|
||||
if (0 == nr)
|
||||
{
|
||||
lsquic_stream_shutdown(st_h->stream, 2);
|
||||
}
|
||||
else if (-1 == nr)
|
||||
{
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
|
||||
{
|
||||
LSQ_DEBUG("read newline: wantwrite");
|
||||
lsquic_stream_wantwrite(st_h->stream, 1);
|
||||
lsquic_engine_process_conns(st_h->client_ctx->prog->prog_engine);
|
||||
}
|
||||
else if (st_h->buf_off == sizeof(st_h->buf))
|
||||
{
|
||||
LSQ_NOTICE("line too long");
|
||||
exit(2);
|
||||
}
|
||||
else
|
||||
event_add(st_h->read_stdin_ev, NULL);
|
||||
}
|
||||
|
||||
|
||||
static lsquic_stream_ctx_t *
|
||||
echo_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
|
||||
st_h->stream = stream;
|
||||
st_h->client_ctx = stream_if_ctx;
|
||||
st_h->buf_off = 0;
|
||||
st_h->read_stdin_ev = event_new(prog_eb(st_h->client_ctx->prog),
|
||||
STDIN_FILENO, EV_READ, read_stdin, st_h);
|
||||
event_add(st_h->read_stdin_ev, NULL);
|
||||
return st_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
char c;
|
||||
size_t nr;
|
||||
|
||||
nr = lsquic_stream_read(stream, &c, 1);
|
||||
if (0 == nr)
|
||||
{
|
||||
lsquic_stream_shutdown(stream, 2);
|
||||
return;
|
||||
}
|
||||
printf("%c", c);
|
||||
fflush(stdout);
|
||||
if ('\n' == c)
|
||||
{
|
||||
event_add(st_h->read_stdin_ev, NULL);
|
||||
lsquic_stream_wantread(stream, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
/* Here we make an assumption that we can write the whole buffer.
|
||||
* Don't do it in a real program.
|
||||
*/
|
||||
lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
|
||||
st_h->buf_off = 0;
|
||||
|
||||
lsquic_stream_flush(stream);
|
||||
lsquic_stream_wantwrite(stream, 0);
|
||||
lsquic_stream_wantread(stream, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
LSQ_NOTICE("%s called", __func__);
|
||||
if (st_h->read_stdin_ev)
|
||||
{
|
||||
event_del(st_h->read_stdin_ev);
|
||||
event_free(st_h->read_stdin_ev);
|
||||
}
|
||||
free(st_h);
|
||||
lsquic_conn_close(lsquic_stream_conn(stream));
|
||||
}
|
||||
|
||||
|
||||
const struct lsquic_stream_if client_echo_stream_if = {
|
||||
.on_new_conn = echo_client_on_new_conn,
|
||||
.on_conn_closed = echo_client_on_conn_closed,
|
||||
.on_new_stream = echo_client_on_new_stream,
|
||||
.on_read = echo_client_on_read,
|
||||
.on_write = echo_client_on_write,
|
||||
.on_close = echo_client_on_close,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage (const char *prog)
|
||||
{
|
||||
const char *const slash = strrchr(prog, '/');
|
||||
if (slash)
|
||||
prog = slash + 1;
|
||||
LSQ_NOTICE(
|
||||
"Usage: %s [opts]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
, prog);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int opt, s;
|
||||
struct sport_head sports;
|
||||
struct prog prog;
|
||||
struct echo_client_ctx client_ctx;
|
||||
|
||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||
client_ctx.prog = &prog;
|
||||
|
||||
TAILQ_INIT(&sports);
|
||||
prog_init(&prog, 0, &sports, &client_echo_stream_if, &client_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
prog_print_common_options(&prog, stdout);
|
||||
exit(0);
|
||||
default:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
|
||||
{
|
||||
perror("fcntl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (0 != prog_prep(&prog))
|
||||
{
|
||||
LSQ_ERROR("could not prep");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (0 != prog_connect(&prog, NULL, 0))
|
||||
{
|
||||
LSQ_ERROR("could not connect");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("entering event loop");
|
||||
|
||||
s = prog_run(&prog);
|
||||
prog_cleanup(&prog);
|
||||
|
||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
228
test/echo_server.c
Normal file
228
test/echo_server.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
/*
|
||||
* echo_server.c -- QUIC server that echoes back input line by line
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "test_common.h"
|
||||
#include "prog.h"
|
||||
|
||||
#include "../src/liblsquic/lsquic_logger.h"
|
||||
|
||||
|
||||
struct lsquic_conn_ctx;
|
||||
|
||||
struct echo_server_ctx {
|
||||
TAILQ_HEAD(, lsquic_conn_ctx) conn_ctxs;
|
||||
unsigned max_reqs;
|
||||
int n_conn;
|
||||
struct sport_head sports;
|
||||
struct prog *prog;
|
||||
};
|
||||
|
||||
struct lsquic_conn_ctx {
|
||||
TAILQ_ENTRY(lsquic_conn_ctx) next_connh;
|
||||
lsquic_conn_t *conn;
|
||||
struct echo_server_ctx *server_ctx;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_conn_ctx_t *
|
||||
echo_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
|
||||
{
|
||||
struct echo_server_ctx *server_ctx = stream_if_ctx;
|
||||
lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
|
||||
conn_h->conn = conn;
|
||||
conn_h->server_ctx = server_ctx;
|
||||
TAILQ_INSERT_TAIL(&server_ctx->conn_ctxs, conn_h, next_connh);
|
||||
LSQ_NOTICE("New connection!");
|
||||
print_conn_info(conn);
|
||||
return conn_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_server_on_conn_closed (lsquic_conn_t *conn)
|
||||
{
|
||||
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
|
||||
if (conn_h->server_ctx->n_conn)
|
||||
{
|
||||
--conn_h->server_ctx->n_conn;
|
||||
LSQ_NOTICE("Connection closed, remaining: %d", conn_h->server_ctx->n_conn);
|
||||
if (0 == conn_h->server_ctx->n_conn)
|
||||
prog_stop(conn_h->server_ctx->prog);
|
||||
}
|
||||
else
|
||||
LSQ_NOTICE("Connection closed");
|
||||
TAILQ_REMOVE(&conn_h->server_ctx->conn_ctxs, conn_h, next_connh);
|
||||
free(conn_h);
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream_ctx {
|
||||
lsquic_stream_t *stream;
|
||||
struct echo_server_ctx *server_ctx;
|
||||
char buf[0x100];
|
||||
size_t buf_off;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_stream_ctx_t *
|
||||
echo_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
lsquic_stream_ctx_t *st_h = malloc(sizeof(*st_h));
|
||||
st_h->stream = stream;
|
||||
st_h->server_ctx = stream_if_ctx;
|
||||
st_h->buf_off = 0;
|
||||
lsquic_stream_wantread(stream, 1);
|
||||
return st_h;
|
||||
}
|
||||
|
||||
|
||||
static struct lsquic_conn_ctx *
|
||||
find_conn_h (const struct echo_server_ctx *server_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
lsquic_conn_t *conn;
|
||||
|
||||
conn = lsquic_stream_conn(stream);
|
||||
TAILQ_FOREACH(conn_h, &server_ctx->conn_ctxs, next_connh)
|
||||
if (conn_h->conn == conn)
|
||||
return conn_h;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_server_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
size_t nr;
|
||||
|
||||
nr = lsquic_stream_read(stream, st_h->buf + st_h->buf_off++, 1);
|
||||
if (0 == nr)
|
||||
{
|
||||
LSQ_NOTICE("EOF: closing connection");
|
||||
lsquic_stream_shutdown(stream, 2);
|
||||
conn_h = find_conn_h(st_h->server_ctx, stream);
|
||||
lsquic_conn_close(conn_h->conn);
|
||||
}
|
||||
else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
|
||||
{
|
||||
/* Found end of line: echo it back */
|
||||
lsquic_stream_wantwrite(stream, 1);
|
||||
lsquic_stream_wantread(stream, 0);
|
||||
}
|
||||
else if (st_h->buf_off == sizeof(st_h->buf))
|
||||
{
|
||||
/* Out of buffer space: line too long */
|
||||
LSQ_NOTICE("run out of buffer space");
|
||||
lsquic_stream_shutdown(stream, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Keep reading */;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
|
||||
st_h->buf_off = 0;
|
||||
lsquic_stream_flush(stream);
|
||||
lsquic_stream_wantwrite(stream, 0);
|
||||
lsquic_stream_wantread(stream, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
echo_server_on_stream_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
LSQ_NOTICE("%s called", __func__);
|
||||
conn_h = find_conn_h(st_h->server_ctx, stream);
|
||||
LSQ_WARN("%s: TODO: free connection handler %p", __func__, conn_h);
|
||||
free(st_h);
|
||||
}
|
||||
|
||||
|
||||
const struct lsquic_stream_if server_echo_stream_if = {
|
||||
.on_new_conn = echo_server_on_new_conn,
|
||||
.on_conn_closed = echo_server_on_conn_closed,
|
||||
.on_new_stream = echo_server_on_new_stream,
|
||||
.on_read = echo_server_on_read,
|
||||
.on_write = echo_server_on_write,
|
||||
.on_close = echo_server_on_stream_close,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage (const char *prog)
|
||||
{
|
||||
const char *const slash = strrchr(prog, '/');
|
||||
if (slash)
|
||||
prog = slash + 1;
|
||||
printf(
|
||||
"Usage: %s [opts]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
, prog);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int opt, s;
|
||||
struct prog prog;
|
||||
struct echo_server_ctx server_ctx;
|
||||
|
||||
memset(&server_ctx, 0, sizeof(server_ctx));
|
||||
server_ctx.prog = &prog;
|
||||
TAILQ_INIT(&server_ctx.sports);
|
||||
TAILQ_INIT(&server_ctx.conn_ctxs);
|
||||
|
||||
prog_init(&prog, LSENG_SERVER, &server_ctx.sports,
|
||||
&server_echo_stream_if, &server_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "hn:")))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
server_ctx.n_conn = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
prog_print_common_options(&prog, stdout);
|
||||
exit(0);
|
||||
default:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != prog_prep(&prog))
|
||||
{
|
||||
LSQ_ERROR("could not prep");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("entering event loop");
|
||||
|
||||
s = prog_run(&prog);
|
||||
prog_cleanup(&prog);
|
||||
|
||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
|
@ -1563,6 +1563,12 @@ main (int argc, char **argv)
|
|||
LSQ_ERROR("could not prep");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!(client_ctx.hostname || prog.prog_hostname))
|
||||
{
|
||||
fprintf(stderr, "Specify hostname (used for SNI and :authority) via "
|
||||
"-H option\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (was_empty && token)
|
||||
sport_set_token(TAILQ_LAST(&sports, sport_head), token);
|
||||
|
||||
|
|
518
test/md5_client.c
Normal file
518
test/md5_client.c
Normal file
|
@ -0,0 +1,518 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
/*
|
||||
* md5_client.c -- This client sends one or more files to MD5 QUIC server
|
||||
* for MD5 sum calculation.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <openssl/md5.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "test_common.h"
|
||||
#include "prog.h"
|
||||
|
||||
#include "../src/liblsquic/lsquic_logger.h"
|
||||
#include "../src/liblsquic/lsquic_int_types.h"
|
||||
#include "../src/liblsquic/lsquic_varint.h"
|
||||
#include "../src/liblsquic/lsquic_hq.h"
|
||||
#include "../src/liblsquic/lsquic_sfcw.h"
|
||||
#include "../src/liblsquic/lsquic_hash.h"
|
||||
#include "../src/liblsquic/lsquic_stream.h"
|
||||
|
||||
/* Set to non-zero value to test out what happens when reset is sent */
|
||||
#define RESET_AFTER_N_WRITES 0
|
||||
|
||||
static int g_write_file = 1;
|
||||
|
||||
#define LOCAL_BUF_SIZE 0x100
|
||||
|
||||
static struct {
|
||||
unsigned stream_id; /* If set, reset this stream ID */
|
||||
off_t offset; /* Reset it after writing this many bytes */
|
||||
} g_reset_stream;
|
||||
|
||||
struct file {
|
||||
LIST_ENTRY(file) next_file;
|
||||
const char *filename;
|
||||
struct lsquic_reader reader;
|
||||
int fd;
|
||||
unsigned priority;
|
||||
enum {
|
||||
FILE_RESET = (1 << 0),
|
||||
} file_flags;
|
||||
size_t md5_off;
|
||||
char md5str[MD5_DIGEST_LENGTH * 2];
|
||||
};
|
||||
|
||||
struct lsquic_conn_ctx;
|
||||
|
||||
struct client_ctx {
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
LIST_HEAD(, file) files;
|
||||
unsigned n_files;
|
||||
struct file *cur_file;
|
||||
lsquic_engine_t *engine;
|
||||
struct service_port *sport;
|
||||
struct prog *prog;
|
||||
};
|
||||
|
||||
struct lsquic_conn_ctx {
|
||||
lsquic_conn_t *conn;
|
||||
struct client_ctx *client_ctx;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_conn_ctx_t *
|
||||
client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
|
||||
{
|
||||
struct client_ctx *client_ctx = stream_if_ctx;
|
||||
lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
|
||||
conn_h->conn = conn;
|
||||
conn_h->client_ctx = client_ctx;
|
||||
client_ctx->conn_h = conn_h;
|
||||
assert(client_ctx->n_files > 0);
|
||||
unsigned n = client_ctx->n_files;
|
||||
while (n--)
|
||||
lsquic_conn_make_stream(conn);
|
||||
print_conn_info(conn);
|
||||
return conn_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_on_goaway_received (lsquic_conn_t *conn)
|
||||
{
|
||||
LSQ_NOTICE("GOAWAY received");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_on_conn_closed (lsquic_conn_t *conn)
|
||||
{
|
||||
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
|
||||
LSQ_NOTICE("Connection closed");
|
||||
prog_stop(conn_h->client_ctx->prog);
|
||||
free(conn_h);
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream_ctx {
|
||||
lsquic_stream_t *stream;
|
||||
struct client_ctx *client_ctx;
|
||||
struct file *file;
|
||||
struct event *read_stdin_ev;
|
||||
struct {
|
||||
int initialized;
|
||||
size_t size,
|
||||
off;
|
||||
} small;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_stream_ctx_t *
|
||||
client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
struct client_ctx *const client_ctx = stream_if_ctx;
|
||||
if (!stream)
|
||||
{
|
||||
assert(client_ctx->n_files > 0);
|
||||
LSQ_NOTICE("%s: got null stream: no more streams possible; # files: %u",
|
||||
__func__, client_ctx->n_files);
|
||||
--client_ctx->n_files;
|
||||
if (0 == client_ctx->n_files)
|
||||
{
|
||||
LSQ_DEBUG("closing connection");
|
||||
lsquic_conn_close(client_ctx->conn_h->conn);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
|
||||
st_h->stream = stream;
|
||||
st_h->client_ctx = stream_if_ctx;
|
||||
if (LIST_EMPTY(&st_h->client_ctx->files))
|
||||
{
|
||||
/* XXX: perhaps we should not be able to write immediately: there may
|
||||
* be internal memory constraints...
|
||||
*/
|
||||
lsquic_stream_write(stream, "client request", 14);
|
||||
(void) lsquic_stream_flush(stream);
|
||||
lsquic_stream_wantwrite(stream, 0);
|
||||
lsquic_stream_wantread(stream, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
st_h->file = LIST_FIRST(&st_h->client_ctx->files);
|
||||
if (g_write_file)
|
||||
{
|
||||
st_h->file->fd = -1;
|
||||
st_h->file->reader.lsqr_read = test_reader_read;
|
||||
st_h->file->reader.lsqr_size = test_reader_size;
|
||||
st_h->file->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->file->filename);
|
||||
if (!st_h->file->reader.lsqr_ctx)
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
st_h->file->fd = open(st_h->file->filename, O_RDONLY);
|
||||
if (st_h->file->fd < 0)
|
||||
{
|
||||
LSQ_ERROR("could not open %s for reading: %s",
|
||||
st_h->file->filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
LIST_REMOVE(st_h->file, next_file);
|
||||
lsquic_stream_set_priority(stream, st_h->file->priority);
|
||||
lsquic_stream_wantwrite(stream, 1);
|
||||
}
|
||||
return st_h;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
buf_reader_size (void *reader_ctx)
|
||||
{
|
||||
lsquic_stream_ctx_t *const st_h = reader_ctx;
|
||||
struct stat st;
|
||||
off_t off;
|
||||
|
||||
if (st_h->small.initialized)
|
||||
goto initialized;
|
||||
|
||||
if (0 != fstat(st_h->file->fd, &st))
|
||||
{
|
||||
LSQ_ERROR("fstat failed: %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
off = lseek(st_h->file->fd, 0, SEEK_CUR);
|
||||
if (off == (off_t) -1)
|
||||
{
|
||||
LSQ_ERROR("lseek failed: %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (st.st_size < off)
|
||||
{
|
||||
LSQ_ERROR("size mismatch");
|
||||
goto err;
|
||||
}
|
||||
|
||||
st_h->small.initialized = 1;
|
||||
st_h->small.off = off;
|
||||
st_h->small.size = st.st_size;
|
||||
|
||||
initialized:
|
||||
if (st_h->small.size - st_h->small.off > LOCAL_BUF_SIZE)
|
||||
return LOCAL_BUF_SIZE;
|
||||
else
|
||||
return st_h->small.size - st_h->small.off;
|
||||
|
||||
err:
|
||||
close(st_h->file->fd);
|
||||
st_h->file->fd = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
buf_reader_read (void *reader_ctx, void *buf, size_t count)
|
||||
{
|
||||
lsquic_stream_ctx_t *const st_h = reader_ctx;
|
||||
ssize_t nr;
|
||||
unsigned char local_buf[LOCAL_BUF_SIZE];
|
||||
|
||||
assert(st_h->small.initialized);
|
||||
|
||||
if (count > sizeof(local_buf))
|
||||
count = sizeof(local_buf);
|
||||
|
||||
nr = read(st_h->file->fd, local_buf, count);
|
||||
if (nr < 0)
|
||||
{
|
||||
LSQ_ERROR("read: %s", strerror(errno));
|
||||
close(st_h->file->fd);
|
||||
st_h->file->fd = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buf, local_buf, nr);
|
||||
st_h->small.off += nr;
|
||||
return nr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_file_on_write_buf (lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
ssize_t nw;
|
||||
struct lsquic_reader reader = {
|
||||
.lsqr_read = buf_reader_read,
|
||||
.lsqr_size = buf_reader_size,
|
||||
.lsqr_ctx = st_h,
|
||||
};
|
||||
|
||||
if (g_reset_stream.stream_id == lsquic_stream_id(st_h->stream) &&
|
||||
lseek(st_h->file->fd, 0, SEEK_CUR) >= g_reset_stream.offset)
|
||||
{
|
||||
lsquic_stream_reset(st_h->stream, 0x01 /* QUIC_INTERNAL_ERROR */);
|
||||
g_reset_stream.stream_id = 0; /* Reset only once */
|
||||
}
|
||||
|
||||
nw = lsquic_stream_writef(st_h->stream, &reader);
|
||||
if (-1 == nw)
|
||||
{
|
||||
if (ECONNRESET == errno)
|
||||
st_h->file->file_flags |= FILE_RESET;
|
||||
LSQ_WARN("lsquic_stream_read: %s", strerror(errno));
|
||||
lsquic_stream_close(st_h->stream);
|
||||
return;
|
||||
}
|
||||
|
||||
#if RESET_AFTER_N_WRITES
|
||||
static int write_count = 0;
|
||||
if (write_count++ > RESET_AFTER_N_WRITES)
|
||||
lsquic_stream_reset(st_h->stream, 0);
|
||||
#endif
|
||||
|
||||
if (0 == nw)
|
||||
{
|
||||
(void) close(st_h->file->fd);
|
||||
if (0 == lsquic_stream_shutdown(st_h->stream, 1))
|
||||
lsquic_stream_wantread(st_h->stream, 1);
|
||||
else
|
||||
{
|
||||
if (ECONNRESET == errno)
|
||||
st_h->file->file_flags |= FILE_RESET;
|
||||
LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno));
|
||||
lsquic_stream_close(st_h->stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_file_on_write_efficient (lsquic_stream_t *stream,
|
||||
lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
ssize_t nw;
|
||||
|
||||
nw = lsquic_stream_writef(stream, &st_h->file->reader);
|
||||
if (nw < 0)
|
||||
{
|
||||
LSQ_ERROR("write error: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (nw == 0)
|
||||
{
|
||||
destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx);
|
||||
st_h->file->reader.lsqr_ctx = NULL;
|
||||
if (0 == lsquic_stream_shutdown(st_h->stream, 1))
|
||||
lsquic_stream_wantread(st_h->stream, 1);
|
||||
else
|
||||
{
|
||||
if (ECONNRESET == errno)
|
||||
st_h->file->file_flags |= FILE_RESET;
|
||||
LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno));
|
||||
lsquic_stream_close(st_h->stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_file_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
if (g_write_file)
|
||||
client_file_on_write_efficient(stream, st_h);
|
||||
else
|
||||
client_file_on_write_buf(st_h);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_file_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
char buf;
|
||||
/* We expect to read in 32-character MD5 string */
|
||||
size_t ntoread = sizeof(st_h->file->md5str) - st_h->file->md5_off;
|
||||
if (0 == ntoread)
|
||||
{
|
||||
lsquic_stream_wantread(stream, 0);
|
||||
/* XXX What about an error (due to RST_STREAM) here: how are we to
|
||||
* handle it?
|
||||
*/
|
||||
/* Expect a FIN */
|
||||
if (0 == lsquic_stream_read(stream, &buf, sizeof(buf)))
|
||||
{
|
||||
LSQ_NOTICE("%.*s %s", (int) sizeof(st_h->file->md5str),
|
||||
st_h->file->md5str,
|
||||
st_h->file->filename);
|
||||
fflush(stdout);
|
||||
LSQ_DEBUG("# of files: %d", st_h->client_ctx->n_files);
|
||||
lsquic_stream_shutdown(stream, 0);
|
||||
}
|
||||
else
|
||||
LSQ_ERROR("expected FIN from stream!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ssize_t nr = lsquic_stream_read(stream,
|
||||
st_h->file->md5str + st_h->file->md5_off, ntoread);
|
||||
if (-1 == nr)
|
||||
{
|
||||
if (ECONNRESET == errno)
|
||||
st_h->file->file_flags |= FILE_RESET;
|
||||
LSQ_WARN("lsquic_stream_read: %s", strerror(errno));
|
||||
lsquic_stream_close(stream);
|
||||
return;
|
||||
}
|
||||
else
|
||||
st_h->file->md5_off += nr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_file_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
--st_h->client_ctx->n_files;
|
||||
LSQ_NOTICE("%s called for stream %"PRIu64", # files: %u", __func__,
|
||||
lsquic_stream_id(stream), st_h->client_ctx->n_files);
|
||||
if (0 == st_h->client_ctx->n_files)
|
||||
lsquic_conn_close(st_h->client_ctx->conn_h->conn);
|
||||
if (!(st_h->file->file_flags & FILE_RESET) && 0 == RESET_AFTER_N_WRITES)
|
||||
assert(st_h->file->md5_off == sizeof(st_h->file->md5str));
|
||||
if (st_h->file->reader.lsqr_ctx)
|
||||
{
|
||||
destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx);
|
||||
st_h->file->reader.lsqr_ctx = NULL;
|
||||
}
|
||||
if (st_h->file->fd >= 0)
|
||||
(void) close(st_h->file->fd);
|
||||
free(st_h->file);
|
||||
free(st_h);
|
||||
}
|
||||
|
||||
|
||||
const struct lsquic_stream_if client_file_stream_if = {
|
||||
.on_new_conn = client_on_new_conn,
|
||||
.on_goaway_received = client_on_goaway_received,
|
||||
.on_conn_closed = client_on_conn_closed,
|
||||
.on_new_stream = client_on_new_stream,
|
||||
.on_read = client_file_on_read,
|
||||
.on_write = client_file_on_write,
|
||||
.on_close = client_file_on_close,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage (const char *prog)
|
||||
{
|
||||
const char *const slash = strrchr(prog, '/');
|
||||
if (slash)
|
||||
prog = slash + 1;
|
||||
printf(
|
||||
"Usage: %s [opts]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -f FILE File to send to the server -- must be specified at least\n"
|
||||
" once.\n"
|
||||
" -b Use buffering API for sending files over rather than\n"
|
||||
" the efficient version.\n"
|
||||
" -p PRIORITY Applicatble to previous file specified with -f\n"
|
||||
" -r STREAM_ID:OFFSET\n"
|
||||
" Reset stream STREAM_ID after sending more that OFFSET bytes.\n"
|
||||
, prog);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int opt, s;
|
||||
struct sport_head sports;
|
||||
struct prog prog;
|
||||
struct client_ctx client_ctx;
|
||||
struct file *file;
|
||||
|
||||
file = NULL;
|
||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||
client_ctx.prog = &prog;
|
||||
|
||||
TAILQ_INIT(&sports);
|
||||
prog_init(&prog, 0, &sports, &client_file_stream_if, &client_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "bhr:f:p:")))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
if (file)
|
||||
file->priority = atoi(optarg);
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "No file to apply priority to\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
g_write_file = 0;
|
||||
break;
|
||||
case 'f':
|
||||
file = calloc(1, sizeof(*file));
|
||||
LIST_INSERT_HEAD(&client_ctx.files, file, next_file);
|
||||
++client_ctx.n_files;
|
||||
file->filename = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
g_reset_stream.stream_id = atoi(optarg);
|
||||
g_reset_stream.offset = atoi(strchr(optarg, ':') + 1);
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
prog_print_common_options(&prog, stdout);
|
||||
exit(0);
|
||||
default:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (LIST_EMPTY(&client_ctx.files))
|
||||
{
|
||||
fprintf(stderr, "please specify one of more files using -f\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (0 != prog_prep(&prog))
|
||||
{
|
||||
LSQ_ERROR("could not prep");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
client_ctx.sport = TAILQ_FIRST(&sports);
|
||||
|
||||
if (0 != prog_connect(&prog, NULL, 0))
|
||||
{
|
||||
LSQ_ERROR("could not connect");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("entering event loop");
|
||||
|
||||
s = prog_run(&prog);
|
||||
prog_cleanup(&prog);
|
||||
|
||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
340
test/md5_server.c
Normal file
340
test/md5_server.c
Normal file
|
@ -0,0 +1,340 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
/*
|
||||
* md5_server.c -- Read one or more streams from the client and return
|
||||
* MD5 sum of the payload.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/md5.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "test_common.h"
|
||||
#include "prog.h"
|
||||
|
||||
#include "../src/liblsquic/lsquic_logger.h"
|
||||
|
||||
|
||||
static int g_really_calculate_md5 = 1;
|
||||
|
||||
/* Turn on to test whether stream reset is being sent when stream is closed
|
||||
* prematurely.
|
||||
*/
|
||||
static struct {
|
||||
unsigned stream_id;
|
||||
unsigned long limit;
|
||||
unsigned long n_read;
|
||||
} g_premature_close;
|
||||
|
||||
struct lsquic_conn_ctx;
|
||||
|
||||
struct server_ctx {
|
||||
TAILQ_HEAD(, lsquic_conn_ctx) conn_ctxs;
|
||||
unsigned max_reqs;
|
||||
int n_conn;
|
||||
time_t expiry;
|
||||
struct sport_head sports;
|
||||
struct prog *prog;
|
||||
};
|
||||
|
||||
struct lsquic_conn_ctx {
|
||||
TAILQ_ENTRY(lsquic_conn_ctx) next_connh;
|
||||
lsquic_conn_t *conn;
|
||||
unsigned n_reqs, n_closed;
|
||||
struct server_ctx *server_ctx;
|
||||
};
|
||||
|
||||
|
||||
static lsquic_conn_ctx_t *
|
||||
server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
|
||||
{
|
||||
struct server_ctx *server_ctx = stream_if_ctx;
|
||||
lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
|
||||
conn_h->conn = conn;
|
||||
conn_h->server_ctx = server_ctx;
|
||||
TAILQ_INSERT_TAIL(&server_ctx->conn_ctxs, conn_h, next_connh);
|
||||
LSQ_NOTICE("New connection!");
|
||||
print_conn_info(conn);
|
||||
return conn_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
server_on_conn_closed (lsquic_conn_t *conn)
|
||||
{
|
||||
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
|
||||
int stopped;
|
||||
|
||||
if (conn_h->server_ctx->expiry && conn_h->server_ctx->expiry < time(NULL))
|
||||
{
|
||||
LSQ_NOTICE("reached engine expiration time, shut down");
|
||||
prog_stop(conn_h->server_ctx->prog);
|
||||
stopped = 1;
|
||||
}
|
||||
else
|
||||
stopped = 0;
|
||||
|
||||
if (conn_h->server_ctx->n_conn)
|
||||
{
|
||||
--conn_h->server_ctx->n_conn;
|
||||
LSQ_NOTICE("Connection closed, remaining: %d", conn_h->server_ctx->n_conn);
|
||||
if (0 == conn_h->server_ctx->n_conn && !stopped)
|
||||
prog_stop(conn_h->server_ctx->prog);
|
||||
}
|
||||
else
|
||||
LSQ_NOTICE("Connection closed");
|
||||
TAILQ_REMOVE(&conn_h->server_ctx->conn_ctxs, conn_h, next_connh);
|
||||
free(conn_h);
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream_ctx {
|
||||
lsquic_stream_t *stream;
|
||||
struct server_ctx *server_ctx;
|
||||
MD5_CTX md5ctx;
|
||||
unsigned char md5sum[MD5_DIGEST_LENGTH];
|
||||
char md5str[MD5_DIGEST_LENGTH * 2 + 1];
|
||||
};
|
||||
|
||||
|
||||
static struct lsquic_conn_ctx *
|
||||
find_conn_h (const struct server_ctx *server_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
lsquic_conn_t *conn;
|
||||
|
||||
conn = lsquic_stream_conn(stream);
|
||||
TAILQ_FOREACH(conn_h, &server_ctx->conn_ctxs, next_connh)
|
||||
if (conn_h->conn == conn)
|
||||
return conn_h;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static lsquic_stream_ctx_t *
|
||||
server_md5_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
lsquic_stream_ctx_t *st_h = malloc(sizeof(*st_h));
|
||||
st_h->stream = stream;
|
||||
st_h->server_ctx = stream_if_ctx;
|
||||
lsquic_stream_wantread(stream, 1);
|
||||
if (g_really_calculate_md5)
|
||||
MD5_Init(&st_h->md5ctx);
|
||||
conn_h = find_conn_h(st_h->server_ctx, stream);
|
||||
assert(conn_h);
|
||||
conn_h->n_reqs++;
|
||||
LSQ_NOTICE("request #%u", conn_h->n_reqs);
|
||||
if (st_h->server_ctx->max_reqs &&
|
||||
conn_h->n_reqs >= st_h->server_ctx->max_reqs)
|
||||
{
|
||||
/* The assert guards the assumption that after the we mark the
|
||||
* connection as going away, no new streams are opened and thus
|
||||
* this callback is not called.
|
||||
*/
|
||||
assert(conn_h->n_reqs == st_h->server_ctx->max_reqs);
|
||||
LSQ_NOTICE("reached maximum requests: %u, going away",
|
||||
st_h->server_ctx->max_reqs);
|
||||
lsquic_conn_going_away(conn_h->conn);
|
||||
}
|
||||
return st_h;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
server_md5_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
char buf[0x1000];
|
||||
ssize_t nr;
|
||||
|
||||
nr = lsquic_stream_read(stream, buf, sizeof(buf));
|
||||
if (-1 == nr)
|
||||
{
|
||||
/* This should never return an error if we only call read() once
|
||||
* per callback.
|
||||
*/
|
||||
perror("lsquic_stream_read");
|
||||
lsquic_stream_shutdown(stream, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_premature_close.limit &&
|
||||
g_premature_close.stream_id == lsquic_stream_id(stream))
|
||||
{
|
||||
g_premature_close.n_read += nr;
|
||||
if (g_premature_close.n_read > g_premature_close.limit)
|
||||
{
|
||||
LSQ_WARN("Done after reading %lu bytes", g_premature_close.n_read);
|
||||
lsquic_stream_shutdown(stream, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr)
|
||||
{
|
||||
if (g_really_calculate_md5)
|
||||
MD5_Update(&st_h->md5ctx, buf, nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
lsquic_stream_wantread(stream, 0);
|
||||
if (g_really_calculate_md5)
|
||||
{
|
||||
MD5_Final(st_h->md5sum, &st_h->md5ctx);
|
||||
snprintf(st_h->md5str, sizeof(st_h->md5str),
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
, st_h->md5sum[0]
|
||||
, st_h->md5sum[1]
|
||||
, st_h->md5sum[2]
|
||||
, st_h->md5sum[3]
|
||||
, st_h->md5sum[4]
|
||||
, st_h->md5sum[5]
|
||||
, st_h->md5sum[6]
|
||||
, st_h->md5sum[7]
|
||||
, st_h->md5sum[8]
|
||||
, st_h->md5sum[9]
|
||||
, st_h->md5sum[10]
|
||||
, st_h->md5sum[11]
|
||||
, st_h->md5sum[12]
|
||||
, st_h->md5sum[13]
|
||||
, st_h->md5sum[14]
|
||||
, st_h->md5sum[15]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(st_h->md5str, '0', sizeof(st_h->md5str) - 1);
|
||||
st_h->md5str[sizeof(st_h->md5str) - 1] = '\0';
|
||||
}
|
||||
lsquic_stream_wantwrite(stream, 1);
|
||||
lsquic_stream_shutdown(stream, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
server_md5_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
ssize_t nw;
|
||||
nw = lsquic_stream_write(stream, st_h->md5str, sizeof(st_h->md5str) - 1);
|
||||
if (-1 == nw)
|
||||
{
|
||||
perror("lsquic_stream_write");
|
||||
return;
|
||||
}
|
||||
lsquic_stream_wantwrite(stream, 0);
|
||||
lsquic_stream_shutdown(stream, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
server_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
struct lsquic_conn_ctx *conn_h;
|
||||
LSQ_NOTICE("%s called", __func__);
|
||||
conn_h = find_conn_h(st_h->server_ctx, stream);
|
||||
conn_h->n_closed++;
|
||||
if (st_h->server_ctx->max_reqs &&
|
||||
conn_h->n_closed >= st_h->server_ctx->max_reqs)
|
||||
{
|
||||
assert(conn_h->n_closed == st_h->server_ctx->max_reqs);
|
||||
LSQ_NOTICE("closing connection after completing %u requests",
|
||||
conn_h->n_closed);
|
||||
lsquic_conn_close(conn_h->conn);
|
||||
}
|
||||
free(st_h);
|
||||
}
|
||||
|
||||
|
||||
const struct lsquic_stream_if server_md5_stream_if = {
|
||||
.on_new_conn = server_on_new_conn,
|
||||
.on_conn_closed = server_on_conn_closed,
|
||||
.on_new_stream = server_md5_on_new_stream,
|
||||
.on_read = server_md5_on_read,
|
||||
.on_write = server_md5_on_write,
|
||||
.on_close = server_on_close,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage (const char *prog)
|
||||
{
|
||||
const char *const slash = strrchr(prog, '/');
|
||||
if (slash)
|
||||
prog = slash + 1;
|
||||
printf(
|
||||
"Usage: %s [opts]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -e EXPIRY Stop engine after this many seconds. The expiration is\n"
|
||||
" checked when connections are closed.\n"
|
||||
, prog);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int opt, s;
|
||||
struct prog prog;
|
||||
struct server_ctx server_ctx;
|
||||
|
||||
memset(&server_ctx, 0, sizeof(server_ctx));
|
||||
TAILQ_INIT(&server_ctx.conn_ctxs);
|
||||
server_ctx.prog = &prog;
|
||||
TAILQ_INIT(&server_ctx.sports);
|
||||
prog_init(&prog, LSENG_SERVER, &server_ctx.sports,
|
||||
&server_md5_stream_if, &server_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "hr:Fn:e:p:")))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'F':
|
||||
g_really_calculate_md5 = 0;
|
||||
break;
|
||||
case 'p':
|
||||
g_premature_close.stream_id = atoi(optarg);
|
||||
g_premature_close.limit = atoi(strchr(optarg, ':') + 1);
|
||||
break;
|
||||
case 'r':
|
||||
server_ctx.max_reqs = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
server_ctx.expiry = time(NULL) + atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
server_ctx.n_conn = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
prog_print_common_options(&prog, stdout);
|
||||
exit(0);
|
||||
default:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != prog_prep(&prog))
|
||||
{
|
||||
LSQ_ERROR("could not prep");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("entering event loop");
|
||||
|
||||
s = prog_run(&prog);
|
||||
prog_cleanup(&prog);
|
||||
|
||||
exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
|
@ -58,8 +58,9 @@ test_attq_ordering (enum sort_action sa)
|
|||
{
|
||||
struct attq *q;
|
||||
struct lsquic_conn *conns, *conn;
|
||||
const struct attq_elem *next_attq;
|
||||
lsquic_time_t prev;
|
||||
const lsquic_time_t *t;
|
||||
lsquic_time_t t;
|
||||
unsigned i;
|
||||
int s;
|
||||
|
||||
|
@ -88,7 +89,7 @@ test_attq_ordering (enum sort_action sa)
|
|||
conns = calloc(sizeof(curiosity), sizeof(conns[0]));
|
||||
for (i = 0; i < sizeof(curiosity); ++i)
|
||||
{
|
||||
s = attq_add(q, &conns[i], (lsquic_time_t) curiosity[i]);
|
||||
s = attq_add(q, &conns[i], (lsquic_time_t) curiosity[i], 0);
|
||||
assert(s == 0);
|
||||
}
|
||||
|
||||
|
@ -116,17 +117,18 @@ test_attq_ordering (enum sort_action sa)
|
|||
|
||||
for (i = 0; i < sizeof(curiosity); ++i)
|
||||
{
|
||||
t = attq_next_time(q);
|
||||
assert(t);
|
||||
next_attq = attq_next(q);
|
||||
assert(next_attq);
|
||||
t = next_attq->ae_adv_time;
|
||||
if (i > 0)
|
||||
assert(*t >= prev);
|
||||
prev = *t;
|
||||
assert(t >= prev);
|
||||
prev = t;
|
||||
conn = attq_pop(q, ~0ULL);
|
||||
assert(conn);
|
||||
}
|
||||
|
||||
t = attq_next_time(q);
|
||||
assert(!t);
|
||||
next_attq = attq_next(q);
|
||||
assert(!next_attq);
|
||||
conn = attq_pop(q, ~0ULL);
|
||||
assert(!conn);
|
||||
|
||||
|
@ -145,12 +147,12 @@ test_attq_removal_1 (void)
|
|||
q = attq_create();
|
||||
conns = calloc(6, sizeof(conns[0]));
|
||||
|
||||
attq_add(q, &conns[0], 1);
|
||||
attq_add(q, &conns[1], 4);
|
||||
attq_add(q, &conns[2], 2);
|
||||
attq_add(q, &conns[3], 5);
|
||||
attq_add(q, &conns[4], 6);
|
||||
attq_add(q, &conns[5], 3);
|
||||
attq_add(q, &conns[0], 1, 0);
|
||||
attq_add(q, &conns[1], 4, 0);
|
||||
attq_add(q, &conns[2], 2, 0);
|
||||
attq_add(q, &conns[3], 5, 0);
|
||||
attq_add(q, &conns[4], 6, 0);
|
||||
attq_add(q, &conns[5], 3, 0);
|
||||
|
||||
attq_remove(q, &conns[3]);
|
||||
|
||||
|
@ -169,15 +171,15 @@ test_attq_removal_2 (void)
|
|||
q = attq_create();
|
||||
conns = calloc(9, sizeof(conns[0]));
|
||||
|
||||
attq_add(q, &conns[0], 1);
|
||||
attq_add(q, &conns[1], 5);
|
||||
attq_add(q, &conns[2], 6);
|
||||
attq_add(q, &conns[3], 9);
|
||||
attq_add(q, &conns[4], 11);
|
||||
attq_add(q, &conns[5], 8);
|
||||
attq_add(q, &conns[6], 15);
|
||||
attq_add(q, &conns[7], 17);
|
||||
attq_add(q, &conns[8], 21);
|
||||
attq_add(q, &conns[0], 1, 0);
|
||||
attq_add(q, &conns[1], 5, 0);
|
||||
attq_add(q, &conns[2], 6, 0);
|
||||
attq_add(q, &conns[3], 9, 0);
|
||||
attq_add(q, &conns[4], 11, 0);
|
||||
attq_add(q, &conns[5], 8, 0);
|
||||
attq_add(q, &conns[6], 15, 0);
|
||||
attq_add(q, &conns[7], 17, 0);
|
||||
attq_add(q, &conns[8], 21, 0);
|
||||
|
||||
attq_remove(q, &conns[1]);
|
||||
|
||||
|
@ -196,15 +198,15 @@ test_attq_removal_3 (void)
|
|||
q = attq_create();
|
||||
conns = calloc(9, sizeof(conns[0]));
|
||||
|
||||
attq_add(q, &conns[0], 1);
|
||||
attq_add(q, &conns[1], 9);
|
||||
attq_add(q, &conns[2], 22);
|
||||
attq_add(q, &conns[3], 17);
|
||||
attq_add(q, &conns[4], 11);
|
||||
attq_add(q, &conns[5], 33);
|
||||
attq_add(q, &conns[6], 27);
|
||||
attq_add(q, &conns[7], 21);
|
||||
attq_add(q, &conns[8], 19);
|
||||
attq_add(q, &conns[0], 1, 0);
|
||||
attq_add(q, &conns[1], 9, 0);
|
||||
attq_add(q, &conns[2], 22, 0);
|
||||
attq_add(q, &conns[3], 17, 0);
|
||||
attq_add(q, &conns[4], 11, 0);
|
||||
attq_add(q, &conns[5], 33, 0);
|
||||
attq_add(q, &conns[6], 27, 0);
|
||||
attq_add(q, &conns[7], 21, 0);
|
||||
attq_add(q, &conns[8], 19, 0);
|
||||
|
||||
attq_remove(q, &conns[1]);
|
||||
|
||||
|
|
|
@ -2014,6 +2014,33 @@ test_insert_edge_cases (void)
|
|||
}
|
||||
|
||||
|
||||
/* When HTTP stream is closed unexpectedly, send a reset instead of creating
|
||||
* an empty STREAM frame with a FIN bit set.
|
||||
*/
|
||||
static void
|
||||
test_unexpected_http_close (void)
|
||||
{
|
||||
struct test_objs tobjs;
|
||||
lsquic_stream_t *stream;
|
||||
int s;
|
||||
|
||||
stream_ctor_flags |= SCF_HTTP;
|
||||
init_test_objs(&tobjs, 0x4000, 0x4000, NULL);
|
||||
|
||||
stream = new_stream(&tobjs, 123);
|
||||
assert(stream->sm_bflags & SMBF_USE_HEADERS); /* Self-check */
|
||||
s = lsquic_stream_close(stream);
|
||||
assert(s == 0);
|
||||
assert(stream->sm_qflags & SMQF_SEND_RST);
|
||||
assert(stream->sm_qflags & SMQF_CALL_ONCLOSE);
|
||||
assert(!lsquic_send_ctl_has_buffered(&tobjs.send_ctl));
|
||||
|
||||
lsquic_stream_destroy(stream);
|
||||
deinit_test_objs(&tobjs);
|
||||
stream_ctor_flags &= ~SCF_HTTP;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_writing_to_stream_schedule_stream_packets_immediately (void)
|
||||
{
|
||||
|
@ -3060,6 +3087,7 @@ main (int argc, char **argv)
|
|||
test_reading_from_stream2();
|
||||
test_overlaps();
|
||||
test_insert_edge_cases();
|
||||
test_unexpected_http_close();
|
||||
|
||||
{
|
||||
int idx[6];
|
||||
|
|
Loading…
Reference in a new issue