mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	updated to upstream ran all tests again.
This commit is contained in:
		
						commit
						5d77f141aa
					
				
					 25 changed files with 1048 additions and 895 deletions
				
			
		
							
								
								
									
										50
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								CHANGELOG
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,50 @@
 | 
			
		|||
2018-02-28
 | 
			
		||||
    - Fix unit test regression: enable them correctly in cmake.
 | 
			
		||||
    - Simplify connection has interface
 | 
			
		||||
2018-03-09
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Merge series of ACKs if possible
 | 
			
		||||
 | 
			
		||||
      Parsed single-range ACK frames (that is the majority of frames) are
 | 
			
		||||
      saved in the connection and their processing is deferred until the
 | 
			
		||||
      connection is ticked.  If several ACKs come in a series between
 | 
			
		||||
      adjacent ticks, we check whether the latest ACK is a strict superset
 | 
			
		||||
      of the saved ACK.  If it is, the older ACK is not processed.
 | 
			
		||||
 | 
			
		||||
      If ACK frames can be merged, they are merged and only one of them is
 | 
			
		||||
      either processed or saved.
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Speed up ACK verification by simplifying send history.
 | 
			
		||||
 | 
			
		||||
      Never generate a gap in the sent packet number sequence.  This reduces
 | 
			
		||||
      the send history to a single number instead of potentially a series of
 | 
			
		||||
      packet ranges and thereby speeds up ACK verification.
 | 
			
		||||
 | 
			
		||||
      By default, detecting a gap in the send history is not fatal: only a
 | 
			
		||||
      single warning is generated per connection.  The connection can continue
 | 
			
		||||
      to operate even if the ACK verification code is not able to detect some
 | 
			
		||||
      inconsistencies.
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Rearrange the lsquic_send_ctl struct
 | 
			
		||||
 | 
			
		||||
      The first part of struct lsquic_send_ctl now consists of members that
 | 
			
		||||
      are used in lsquic_send_ctl_got_ack() (in the absense of packet loss,
 | 
			
		||||
      which is the normal case).  To speed up reads and writes, we no longer
 | 
			
		||||
      try to save space by using 8- and 16-bit integers.  Use regular integer
 | 
			
		||||
      width for everything.
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Cache size of sent packet.
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Keep track of the largest ACKed in packet_out
 | 
			
		||||
 | 
			
		||||
      Instead of parsing our own ACK frames when packet has been acked,
 | 
			
		||||
      use the value saved in the packet_out structure when the ACK frame
 | 
			
		||||
      was generated.
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Take RTT sampling conditional out of ACK loop
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] ACK processing: only call clock_gettime() if needed
 | 
			
		||||
 | 
			
		||||
    - [OPTIMIZATION] Several code-level optimizations to ACK processing.
 | 
			
		||||
 | 
			
		||||
    - Fix: http_client: fix -I flag; switch assert() to abort()
 | 
			
		||||
 | 
			
		||||
2018-02-26
 | 
			
		||||
    - [API Change] lsquic_engine_connect() returns pointer to the connection
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,10 @@ IF(LSQUIC_PROFILE EQUAL 1)
 | 
			
		|||
    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -g -pg")
 | 
			
		||||
ENDIF()
 | 
			
		||||
 | 
			
		||||
IF(LSQUIC_COVERAGE EQUAL 1)
 | 
			
		||||
    SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fprofile-arcs -ftest-coverage")
 | 
			
		||||
ENDIF()
 | 
			
		||||
 | 
			
		||||
IF(MY_CMAKE_FLAGS MATCHES "fsanitize=address")
 | 
			
		||||
    MESSAGE(STATUS "AddressSanitizer is ON")
 | 
			
		||||
ELSE()
 | 
			
		||||
| 
						 | 
				
			
			@ -80,16 +84,8 @@ ENDIF()
 | 
			
		|||
SET (BORINGSSL_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/include)
 | 
			
		||||
SET (VCPKG_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/include )
 | 
			
		||||
 | 
			
		||||
IF(CMAKE_BUILD_TYPE MATCHES "Debug")
 | 
			
		||||
SET (BORINGSSL_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/ssl ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64/debug)
 | 
			
		||||
SET (VCPKG_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/lib)
 | 
			
		||||
SET (ZLIB_NAME "zlibd")
 | 
			
		||||
ELSE()
 | 
			
		||||
SET (BORINGSSL_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/ssl ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64)
 | 
			
		||||
SET (VCPKG_LIB ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static/debug/lib)
 | 
			
		||||
SET (ZLIB_NAME zlibd)
 | 
			
		||||
ENDIF()
 | 
			
		||||
 | 
			
		||||
SET (VCPKG_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/installed/x64-windows-static)
 | 
			
		||||
set (BORINGSSL_BASE_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../boringssl/build/x64)
 | 
			
		||||
 | 
			
		||||
ENDIF() #MSVC
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +102,7 @@ ENDIF()
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
include_directories(${BORINGSSL_INCLUDE} ${VCPKG_INCLUDE} )
 | 
			
		||||
link_directories( ${BORINGSSL_LIB} ${VCPKG_LIB} )
 | 
			
		||||
link_directories( ${BORINGSSL_LIB}  )
 | 
			
		||||
 | 
			
		||||
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
 | 
			
		||||
include_directories( include )
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +120,8 @@ add_executable(http_client
 | 
			
		|||
    test/test_common.c
 | 
			
		||||
)
 | 
			
		||||
target_link_libraries(http_client lsquic event pthread libssl.a libcrypto.a ${FIULIB} z m)
 | 
			
		||||
 | 
			
		||||
#MSVC
 | 
			
		||||
ELSE()
 | 
			
		||||
add_executable(http_client
 | 
			
		||||
    test/http_client.c
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +131,19 @@ add_executable(http_client
 | 
			
		|||
    wincompat/getopt1.c
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(http_client lsquic event ssl crypto ${FIULIB} ${ZLIB_NAME} ws2_32)
 | 
			
		||||
target_link_libraries(http_client 
 | 
			
		||||
		debug  $(SolutionDir)src/liblsquic/debug/lsquic.lib
 | 
			
		||||
		debug  ${VCPKG_BASE_DIR}/debug/lib/event.lib
 | 
			
		||||
		debug  ${VCPKG_BASE_DIR}/debug/lib/zlibd.lib
 | 
			
		||||
		debug  ${BORINGSSL_BASE_LIB_DIR}/debug/ssl.lib 
 | 
			
		||||
		debug  ${BORINGSSL_BASE_LIB_DIR}/debug/crypto.lib
 | 
			
		||||
		ws2_32
 | 
			
		||||
		optimized  $(SolutionDir)src/liblsquic/release/lsquic.lib
 | 
			
		||||
		optimized  ${VCPKG_BASE_DIR}/lib/event.lib
 | 
			
		||||
		optimized  ${VCPKG_BASE_DIR}/lib/zlib.lib
 | 
			
		||||
		optimized  ${BORINGSSL_BASE_LIB_DIR}/ssl.lib 
 | 
			
		||||
		optimized  ${BORINGSSL_BASE_LIB_DIR}/crypto.lib
 | 
			
		||||
		${FIULIB} )
 | 
			
		||||
 | 
			
		||||
ENDIF()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1123,6 +1123,7 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
 | 
			
		|||
{
 | 
			
		||||
    ssize_t enc_sz;
 | 
			
		||||
    size_t bufsz;
 | 
			
		||||
    unsigned sent_sz;
 | 
			
		||||
    unsigned char *buf;
 | 
			
		||||
 | 
			
		||||
    bufsz = lsquic_po_header_length(packet_out->po_flags) +
 | 
			
		||||
| 
						 | 
				
			
			@ -1135,7 +1136,10 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
 | 
			
		|||
        return ENCPA_NOMEM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        enc_sz = really_encrypt_packet(conn, packet_out, buf, bufsz);
 | 
			
		||||
        sent_sz = enc_sz;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (enc_sz < 0)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1145,7 +1149,8 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
 | 
			
		|||
 | 
			
		||||
    packet_out->po_enc_data    = buf;
 | 
			
		||||
    packet_out->po_enc_data_sz = enc_sz;
 | 
			
		||||
    packet_out->po_flags |= PO_ENCRYPTED;
 | 
			
		||||
    packet_out->po_sent_sz     = sent_sz;
 | 
			
		||||
    packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ;
 | 
			
		||||
 | 
			
		||||
    return ENCPA_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,6 +94,7 @@ enum full_conn_flags {
 | 
			
		|||
    FC_FIRST_TICK     = (1 <<19),
 | 
			
		||||
    FC_TICK_CLOSE     = (1 <<20),   /* We returned TICK_CLOSE */
 | 
			
		||||
    FC_HSK_FAILED     = (1 <<21),
 | 
			
		||||
    FC_HAVE_SAVED_ACK = (1 <<22),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FC_IMMEDIATE_CLOSE_FLAGS \
 | 
			
		||||
| 
						 | 
				
			
			@ -190,6 +191,10 @@ struct full_conn
 | 
			
		|||
                            n_dup_packets,
 | 
			
		||||
                            n_err_packets;
 | 
			
		||||
        unsigned long       stream_data_sz;
 | 
			
		||||
        unsigned long       n_ticks;
 | 
			
		||||
        unsigned            n_acks_in,
 | 
			
		||||
                            n_acks_proc,
 | 
			
		||||
                            n_acks_merged[2];
 | 
			
		||||
    }                            fc_stats;
 | 
			
		||||
#endif
 | 
			
		||||
#if KEEP_CLOSED_STREAM_HISTORY
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +210,8 @@ struct full_conn
 | 
			
		|||
#endif
 | 
			
		||||
    STAILQ_HEAD(, stream_id_to_reset)
 | 
			
		||||
                                 fc_stream_ids_to_reset;
 | 
			
		||||
    struct short_ack_info        fc_saved_ack_info;
 | 
			
		||||
    lsquic_time_t                fc_saved_ack_received;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -593,6 +600,7 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
 | 
			
		|||
        conn->fc_conn.cn_esf->esf_destroy(conn->fc_conn.cn_enc_session);
 | 
			
		||||
    lsquic_malo_destroy(conn->fc_pub.packet_out_malo);
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
    LSQ_NOTICE("# ticks: %lu", conn->fc_stats.n_ticks);
 | 
			
		||||
    LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were "
 | 
			
		||||
        "dups and %u were errors; sent %u packets, avg stream data per outgoing"
 | 
			
		||||
        " packet is %lu bytes",
 | 
			
		||||
| 
						 | 
				
			
			@ -600,6 +608,9 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
 | 
			
		|||
        conn->fc_stats.n_dup_packets, conn->fc_stats.n_err_packets,
 | 
			
		||||
        conn->fc_stats.n_packets_out,
 | 
			
		||||
        conn->fc_stats.stream_data_sz / conn->fc_stats.n_packets_out);
 | 
			
		||||
    LSQ_NOTICE("ACKs: in: %u; processed: %u; merged to: new %u, old %u",
 | 
			
		||||
        conn->fc_stats.n_acks_in, conn->fc_stats.n_acks_proc,
 | 
			
		||||
        conn->fc_stats.n_acks_merged[0], conn->fc_stats.n_acks_merged[1]);
 | 
			
		||||
#endif
 | 
			
		||||
    while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1095,34 +1106,214 @@ log_invalid_ack_frame (struct full_conn *conn, const unsigned char *p,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
process_ack (struct full_conn *conn, struct ack_info *acki,
 | 
			
		||||
             lsquic_time_t received)
 | 
			
		||||
{
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
    ++conn->fc_stats.n_acks_proc;
 | 
			
		||||
#endif
 | 
			
		||||
    LSQ_DEBUG("Processing ACK");
 | 
			
		||||
    if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl, acki, received))
 | 
			
		||||
    {
 | 
			
		||||
        if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
 | 
			
		||||
            lsquic_rechist_stop_wait(&conn->fc_rechist,
 | 
			
		||||
                lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        ABORT_ERROR("Received invalid ACK");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
process_saved_ack (struct full_conn *conn, int restore_parsed_ack)
 | 
			
		||||
{
 | 
			
		||||
    struct ack_info *const acki = conn->fc_pub.mm->acki;
 | 
			
		||||
    struct lsquic_packno_range range = { 0 };
 | 
			
		||||
    unsigned n_ranges = 0, n_timestamps = 0;
 | 
			
		||||
    lsquic_time_t lack_delta = 0;
 | 
			
		||||
    int retval;
 | 
			
		||||
 | 
			
		||||
    if (restore_parsed_ack)
 | 
			
		||||
    {
 | 
			
		||||
        n_ranges     = acki->n_ranges;
 | 
			
		||||
        n_timestamps = acki->n_timestamps;
 | 
			
		||||
        lack_delta   = acki->lack_delta;
 | 
			
		||||
        range        = acki->ranges[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acki->n_ranges     = 1;
 | 
			
		||||
    acki->n_timestamps = conn->fc_saved_ack_info.sai_n_timestamps;
 | 
			
		||||
    acki->lack_delta   = conn->fc_saved_ack_info.sai_lack_delta;
 | 
			
		||||
    acki->ranges[0]    = conn->fc_saved_ack_info.sai_range;
 | 
			
		||||
 | 
			
		||||
    retval = process_ack(conn, acki, conn->fc_saved_ack_received);
 | 
			
		||||
 | 
			
		||||
    if (restore_parsed_ack)
 | 
			
		||||
    {
 | 
			
		||||
        acki->n_ranges     = n_ranges;
 | 
			
		||||
        acki->n_timestamps = n_timestamps;
 | 
			
		||||
        acki->lack_delta   = lack_delta;
 | 
			
		||||
        acki->ranges[0]    = range;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
new_ack_is_superset (const struct short_ack_info *old, const struct ack_info *new)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *new_range;
 | 
			
		||||
 | 
			
		||||
    new_range = &new->ranges[ new->n_ranges - 1 ];
 | 
			
		||||
    return new_range->low  <= old->sai_range.low
 | 
			
		||||
        && new_range->high >= old->sai_range.high;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
merge_saved_to_new (const struct short_ack_info *old, struct ack_info *new)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_packno_range *smallest_range;
 | 
			
		||||
 | 
			
		||||
    assert(new->n_ranges > 1);
 | 
			
		||||
    smallest_range = &new->ranges[ new->n_ranges - 1 ];
 | 
			
		||||
    if (old->sai_range.high <= smallest_range->high
 | 
			
		||||
        && old->sai_range.high >= smallest_range->low
 | 
			
		||||
        && old->sai_range.low < smallest_range->low)
 | 
			
		||||
    {
 | 
			
		||||
        smallest_range->low = old->sai_range.low;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
merge_new_to_saved (struct short_ack_info *old, const struct ack_info *new)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *new_range;
 | 
			
		||||
 | 
			
		||||
    assert(new->n_ranges == 1);
 | 
			
		||||
    new_range = &new->ranges[0];
 | 
			
		||||
    /* Only merge if new is higher, for simplicity.  This is also the
 | 
			
		||||
     * expected case.
 | 
			
		||||
     */
 | 
			
		||||
    if (new_range->high > old->sai_range.high
 | 
			
		||||
        && new_range->low > old->sai_range.low)
 | 
			
		||||
    {
 | 
			
		||||
        old->sai_range.high = new_range->high;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned
 | 
			
		||||
process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
 | 
			
		||||
                                            const unsigned char *p, size_t len)
 | 
			
		||||
{
 | 
			
		||||
    const int parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len,
 | 
			
		||||
                                                        conn->fc_pub.mm->acki);
 | 
			
		||||
    struct ack_info *const new_acki = conn->fc_pub.mm->acki;
 | 
			
		||||
    int parsed_len;
 | 
			
		||||
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
    ++conn->fc_stats.n_acks_in;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki);
 | 
			
		||||
    if (parsed_len < 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
    if (packet_in->pi_packno > conn->fc_max_ack_packno)
 | 
			
		||||
        goto err;
 | 
			
		||||
 | 
			
		||||
    if (packet_in->pi_packno <= conn->fc_max_ack_packno)
 | 
			
		||||
    {
 | 
			
		||||
        EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, conn->fc_pub.mm->acki);
 | 
			
		||||
        if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl,
 | 
			
		||||
                             conn->fc_pub.mm->acki, packet_in->pi_received))
 | 
			
		||||
        {
 | 
			
		||||
            conn->fc_max_ack_packno = packet_in->pi_packno;
 | 
			
		||||
            if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
 | 
			
		||||
                lsquic_rechist_stop_wait(&conn->fc_rechist,
 | 
			
		||||
                    lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            log_invalid_ack_frame(conn, p, parsed_len, conn->fc_pub.mm->acki);
 | 
			
		||||
            ABORT_ERROR("Received invalid ACK");
 | 
			
		||||
        LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
 | 
			
		||||
        return parsed_len;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
 | 
			
		||||
    conn->fc_max_ack_packno = packet_in->pi_packno;
 | 
			
		||||
 | 
			
		||||
    if (conn->fc_flags & FC_HAVE_SAVED_ACK)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("old ack [%"PRIu64"-%"PRIu64"]",
 | 
			
		||||
            conn->fc_saved_ack_info.sai_range.high,
 | 
			
		||||
            conn->fc_saved_ack_info.sai_range.low);
 | 
			
		||||
        const int is_superset = new_ack_is_superset(&conn->fc_saved_ack_info,
 | 
			
		||||
                                                    new_acki);
 | 
			
		||||
        const int is_1range = new_acki->n_ranges == 1;
 | 
			
		||||
        switch (
 | 
			
		||||
             (is_superset << 1)
 | 
			
		||||
                      | (is_1range << 0))
 | 
			
		||||
           /* |          |
 | 
			
		||||
              |          |
 | 
			
		||||
              V          V                      */ {
 | 
			
		||||
        case (0 << 1) | (0 << 0):
 | 
			
		||||
            if (merge_saved_to_new(&conn->fc_saved_ack_info, new_acki))
 | 
			
		||||
            {
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
                ++conn->fc_stats.n_acks_merged[0]
 | 
			
		||||
#endif
 | 
			
		||||
                ;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                process_saved_ack(conn, 1);
 | 
			
		||||
            conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
 | 
			
		||||
            if (0 != process_ack(conn, new_acki, packet_in->pi_received))
 | 
			
		||||
                goto err;
 | 
			
		||||
            break;
 | 
			
		||||
        case (0 << 1) | (1 << 0):
 | 
			
		||||
            if (merge_new_to_saved(&conn->fc_saved_ack_info, new_acki))
 | 
			
		||||
            {
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
                ++conn->fc_stats.n_acks_merged[1]
 | 
			
		||||
#endif
 | 
			
		||||
                ;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                process_saved_ack(conn, 1);
 | 
			
		||||
                conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
 | 
			
		||||
                conn->fc_saved_ack_info.sai_range        = new_acki->ranges[0];
 | 
			
		||||
            }
 | 
			
		||||
            conn->fc_saved_ack_info.sai_lack_delta   = new_acki->lack_delta;
 | 
			
		||||
            conn->fc_saved_ack_received              = packet_in->pi_received;
 | 
			
		||||
            break;
 | 
			
		||||
        case (1 << 1) | (0 << 0):
 | 
			
		||||
            conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
 | 
			
		||||
            if (0 != process_ack(conn, new_acki, packet_in->pi_received))
 | 
			
		||||
                goto err;
 | 
			
		||||
            break;
 | 
			
		||||
        case (1 << 1) | (1 << 0):
 | 
			
		||||
            conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
 | 
			
		||||
            conn->fc_saved_ack_info.sai_lack_delta   = new_acki->lack_delta;
 | 
			
		||||
            conn->fc_saved_ack_info.sai_range        = new_acki->ranges[0];
 | 
			
		||||
            conn->fc_saved_ack_received              = packet_in->pi_received;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
 | 
			
		||||
    else if (new_acki->n_ranges == 1)
 | 
			
		||||
    {
 | 
			
		||||
        conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
 | 
			
		||||
        conn->fc_saved_ack_info.sai_lack_delta   = new_acki->lack_delta;
 | 
			
		||||
        conn->fc_saved_ack_info.sai_range        = new_acki->ranges[0];
 | 
			
		||||
        conn->fc_saved_ack_received              = packet_in->pi_received;
 | 
			
		||||
        conn->fc_flags |= FC_HAVE_SAVED_ACK;
 | 
			
		||||
    }
 | 
			
		||||
    else if (0 != process_ack(conn, new_acki, packet_in->pi_received))
 | 
			
		||||
        goto err;
 | 
			
		||||
 | 
			
		||||
    return parsed_len;
 | 
			
		||||
 | 
			
		||||
  err:
 | 
			
		||||
    log_invalid_ack_frame(conn, p, parsed_len, new_acki);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2151,7 +2342,7 @@ generate_ack_frame (struct full_conn *conn)
 | 
			
		|||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &conn->fc_rechist, now, &has_missing);
 | 
			
		||||
            &conn->fc_rechist, now, &has_missing, &packet_out->po_ack2ed);
 | 
			
		||||
    if (w < 0) {
 | 
			
		||||
        ABORT_ERROR("generating ACK frame failed: %d", errno);
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -2306,6 +2497,10 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
 | 
			
		|||
    }                                                                   \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
#if FULL_CONN_STATS
 | 
			
		||||
    ++conn->fc_stats.n_ticks;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)
 | 
			
		||||
        && conn->fc_mem_logged_last + 1000000 <= now)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -2315,6 +2510,13 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
 | 
			
		|||
 | 
			
		||||
    assert(!(conn->fc_conn.cn_flags & LSCONN_RW_PENDING));
 | 
			
		||||
 | 
			
		||||
    if (conn->fc_flags & FC_HAVE_SAVED_ACK)
 | 
			
		||||
    {
 | 
			
		||||
        (void) /* If there is an error, we'll fail shortly */
 | 
			
		||||
            process_saved_ack(conn, 0);
 | 
			
		||||
        conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lsquic_send_ctl_tick(&conn->fc_send_ctl, now);
 | 
			
		||||
    lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
 | 
			
		||||
    CLOSE_IF_NECESSARY();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,10 +83,12 @@ typedef struct lsquic_packet_out
 | 
			
		|||
                                         * further writes are allowed.
 | 
			
		||||
                                         */
 | 
			
		||||
        PO_SCHED    = (1 <<14),         /* On scheduled queue */
 | 
			
		||||
        PO_SENT_SZ  = (1 <<15),
 | 
			
		||||
    }                  po_flags:16;
 | 
			
		||||
    enum quic_ft_bit   po_frame_types:16; /* Bitmask of QUIC_FRAME_* */
 | 
			
		||||
    unsigned short     po_data_sz;      /* Number of usable bytes in data */
 | 
			
		||||
    unsigned short     po_enc_data_sz;  /* Number of usable bytes in data */
 | 
			
		||||
    unsigned short     po_sent_sz;      /* If PO_SENT_SZ is set, real size of sent buffer. */
 | 
			
		||||
    unsigned short     po_regen_sz;     /* Number of bytes at the beginning
 | 
			
		||||
                                         * of data containing bytes that are
 | 
			
		||||
                                         * not to be retransmitted, e.g. ACK
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +96,9 @@ typedef struct lsquic_packet_out
 | 
			
		|||
                                         */
 | 
			
		||||
    unsigned short     po_n_alloc;      /* Total number of bytes allocated in po_data */
 | 
			
		||||
    unsigned char     *po_data;
 | 
			
		||||
    lsquic_packno_t    po_ack2ed;       /* If packet has ACK frame, value of
 | 
			
		||||
                                         * largest acked in it.
 | 
			
		||||
                                         */
 | 
			
		||||
 | 
			
		||||
    /* A lot of packets contain data belonging to only one stream.  Thus,
 | 
			
		||||
     * `one' is used first.  If this is not enough, any number of
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +147,23 @@ typedef struct lsquic_packet_out
 | 
			
		|||
    ((p)->po_data_sz + lsquic_po_header_length((p)->po_flags) \
 | 
			
		||||
        + QUIC_PACKET_HASH_SZ)
 | 
			
		||||
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
#define lsquic_packet_out_sent_sz(p) (                                      \
 | 
			
		||||
        __builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ?                 \
 | 
			
		||||
        (assert((p)->po_sent_sz == lsquic_packet_out_total_sz(p)),          \
 | 
			
		||||
            (p)->po_sent_sz) : lsquic_packet_out_total_sz(p))
 | 
			
		||||
#   else
 | 
			
		||||
#define lsquic_packet_out_sent_sz(p) (                                      \
 | 
			
		||||
        __builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ?                 \
 | 
			
		||||
        (p)->po_sent_sz : lsquic_packet_out_total_sz(p))
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#   define lsquic_packet_out_sent_sz(p) (                                   \
 | 
			
		||||
        (p)->po_flags & PO_SENT_SZ ?                                        \
 | 
			
		||||
        (p)->po_sent_sz : lsquic_packet_out_total_sz(p))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define lsquic_packet_out_verneg(p) \
 | 
			
		||||
    (((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,13 @@ typedef struct ack_info
 | 
			
		|||
#endif
 | 
			
		||||
} ack_info_t;
 | 
			
		||||
 | 
			
		||||
struct short_ack_info
 | 
			
		||||
{
 | 
			
		||||
    unsigned                    sai_n_timestamps;
 | 
			
		||||
    lsquic_time_t               sai_lack_delta;
 | 
			
		||||
    struct lsquic_packno_range  sai_range;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define largest_acked(acki) (+(acki)->ranges[0].high)
 | 
			
		||||
 | 
			
		||||
#define smallest_acked(acki) (+(acki)->ranges[(acki)->n_ranges - 1].low)
 | 
			
		||||
| 
						 | 
				
			
			@ -86,13 +93,11 @@ struct parse_funcs
 | 
			
		|||
    int
 | 
			
		||||
    (*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
 | 
			
		||||
                                                    ack_info_t *ack_info);
 | 
			
		||||
    lsquic_packno_t
 | 
			
		||||
    (*pf_parse_ack_high) (const unsigned char *buf, size_t buf_len);
 | 
			
		||||
    int
 | 
			
		||||
    (*pf_gen_ack_frame) (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		||||
                gaf_rechist_first_f, gaf_rechist_next_f,
 | 
			
		||||
                gaf_rechist_largest_recv_f, void *rechist, lsquic_time_t now,
 | 
			
		||||
                int *has_missing);
 | 
			
		||||
                int *has_missing, lsquic_packno_t *largest_received);
 | 
			
		||||
    int
 | 
			
		||||
    (*pf_gen_stop_waiting_frame) (unsigned char *buf, size_t buf_len,
 | 
			
		||||
                    lsquic_packno_t cur_packno, enum lsquic_packno_bits,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -267,28 +267,6 @@ gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This is a special function: it is used to extract the largest observed
 | 
			
		||||
 * packet number from ACK frame that we ourselves generated.  This allows
 | 
			
		||||
 * us to skip some checks.
 | 
			
		||||
 */
 | 
			
		||||
lsquic_packno_t
 | 
			
		||||
gquic_ietf_parse_ack_high (const unsigned char *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char type;
 | 
			
		||||
    unsigned largest_obs_len;
 | 
			
		||||
    unsigned n_blocks_len;
 | 
			
		||||
    lsquic_packno_t packno;
 | 
			
		||||
 | 
			
		||||
    type = buf[0];
 | 
			
		||||
    largest_obs_len = twobit_to_1248((type >> 2) & 3);
 | 
			
		||||
    n_blocks_len = !!(type & 0x10);
 | 
			
		||||
    assert(parse_frame_type_gquic_Q041(type) == QUIC_FRAME_ACK);
 | 
			
		||||
    assert(buf_len >= 1 + n_blocks_len + 1 + largest_obs_len);
 | 
			
		||||
    READ_UINT(packno, 64, buf + 1 + n_blocks_len + 1, largest_obs_len);
 | 
			
		||||
    return packno;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gquic_ietf_parse_ack_frame (const unsigned char *buf, size_t buf_len,
 | 
			
		||||
                                                            ack_info_t *ack)
 | 
			
		||||
| 
						 | 
				
			
			@ -383,7 +361,8 @@ int
 | 
			
		|||
gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		||||
        gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
 | 
			
		||||
        gaf_rechist_largest_recv_f rechist_largest_recv,
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing)
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing,
 | 
			
		||||
        lsquic_packno_t *largest_received)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_packno_t tmp_packno;
 | 
			
		||||
    const struct lsquic_packno_range *const first = rechist_first(rechist);
 | 
			
		||||
| 
						 | 
				
			
			@ -539,6 +518,7 @@ gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		|||
        p += ack_block_len;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *largest_received = maxno;
 | 
			
		||||
    return p - (unsigned char *) outbuf;
 | 
			
		||||
 | 
			
		||||
#undef CHECKOUT
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +535,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q041 =
 | 
			
		|||
    .pf_parse_stream_frame_header_sz  =  gquic_ietf_parse_stream_frame_header_sz,
 | 
			
		||||
    .pf_parse_stream_frame            =  gquic_ietf_parse_stream_frame,
 | 
			
		||||
    .pf_parse_ack_frame               =  gquic_ietf_parse_ack_frame,
 | 
			
		||||
    .pf_parse_ack_high                =  gquic_ietf_parse_ack_high,
 | 
			
		||||
    .pf_gen_ack_frame                 =  gquic_ietf_gen_ack_frame,
 | 
			
		||||
    .pf_gen_stop_waiting_frame        =  gquic_be_gen_stop_waiting_frame,
 | 
			
		||||
    .pf_parse_stop_waiting_frame      =  gquic_be_parse_stop_waiting_frame,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -367,26 +367,6 @@ gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This is a special function: it is used to extract the largest observed
 | 
			
		||||
 * packet number from ACK frame that we ourselves generated.  This allows
 | 
			
		||||
 * us to skip some checks.
 | 
			
		||||
 */
 | 
			
		||||
lsquic_packno_t
 | 
			
		||||
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char type;
 | 
			
		||||
    unsigned largest_obs_len;
 | 
			
		||||
    lsquic_packno_t packno;
 | 
			
		||||
 | 
			
		||||
    type = buf[0];
 | 
			
		||||
    largest_obs_len = twobit_to_1246((type >> 2) & 3);
 | 
			
		||||
    assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
 | 
			
		||||
    assert(buf_len >= 1 + largest_obs_len);
 | 
			
		||||
    READ_UINT(packno, 64, buf + 1, largest_obs_len);
 | 
			
		||||
    return packno;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
 | 
			
		||||
                                ack_info_t *ack)
 | 
			
		||||
| 
						 | 
				
			
			@ -813,7 +793,8 @@ int
 | 
			
		|||
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		||||
        gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
 | 
			
		||||
        gaf_rechist_largest_recv_f rechist_largest_recv,
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing)
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing,
 | 
			
		||||
        lsquic_packno_t *largest_received)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_packno_t tmp_packno;
 | 
			
		||||
    const struct lsquic_packno_range *const first = rechist_first(rechist);
 | 
			
		||||
| 
						 | 
				
			
			@ -966,6 +947,7 @@ gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		|||
    *p = 0;
 | 
			
		||||
    ++p;
 | 
			
		||||
 | 
			
		||||
    *largest_received = maxno;
 | 
			
		||||
    return p - (unsigned char *) outbuf;
 | 
			
		||||
 | 
			
		||||
#undef CHECKOUT
 | 
			
		||||
| 
						 | 
				
			
			@ -982,7 +964,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
 | 
			
		|||
    .pf_parse_stream_frame_header_sz  =  parse_stream_frame_header_sz_gquic,
 | 
			
		||||
    .pf_parse_stream_frame            =  gquic_be_parse_stream_frame,
 | 
			
		||||
    .pf_parse_ack_frame               =  gquic_be_parse_ack_frame,
 | 
			
		||||
    .pf_parse_ack_high                =  gquic_be_parse_ack_high,
 | 
			
		||||
    .pf_gen_ack_frame                 =  gquic_be_gen_ack_frame,
 | 
			
		||||
    .pf_gen_stop_waiting_frame        =  gquic_be_gen_stop_waiting_frame,
 | 
			
		||||
    .pf_parse_stop_waiting_frame      =  gquic_be_parse_stop_waiting_frame,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,6 +139,6 @@ int
 | 
			
		|||
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		||||
        gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
 | 
			
		||||
        gaf_rechist_largest_recv_f rechist_largest_recv,
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing);
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing, lsquic_packno_t *);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -337,24 +337,6 @@ gquic_le_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This is a special function: it is used to extract the largest observed
 | 
			
		||||
 * packet number from ACK frame that we ourselves generated.  This allows
 | 
			
		||||
 * us to skip some checks.
 | 
			
		||||
 */
 | 
			
		||||
static lsquic_packno_t
 | 
			
		||||
gquic_le_parse_ack_high (const unsigned char *buf, size_t buf_len)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char type;
 | 
			
		||||
    unsigned largest_obs_len;
 | 
			
		||||
 | 
			
		||||
    type = buf[0];
 | 
			
		||||
    largest_obs_len = flag_to_pkt_num_len(type >> 2);
 | 
			
		||||
    assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
 | 
			
		||||
    assert(buf_len >= 1 + largest_obs_len);
 | 
			
		||||
    return get_vary_len_int64(buf + 1, largest_obs_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Return parsed (used) buffer length.
 | 
			
		||||
 * If parsing failed, negative value is returned.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -705,7 +687,8 @@ static int
 | 
			
		|||
gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		||||
        gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
 | 
			
		||||
        gaf_rechist_largest_recv_f rechist_largest_recv,
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing)
 | 
			
		||||
        void *rechist, lsquic_time_t now, int *has_missing,
 | 
			
		||||
        lsquic_packno_t *largest_received)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *const first = rechist_first(rechist);
 | 
			
		||||
    if (!first)
 | 
			
		||||
| 
						 | 
				
			
			@ -841,6 +824,7 @@ gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		|||
    *p = 0;
 | 
			
		||||
    ++p;
 | 
			
		||||
 | 
			
		||||
    *largest_received = maxno;
 | 
			
		||||
    return p - (unsigned char *) outbuf;
 | 
			
		||||
 | 
			
		||||
#undef CHECKOUT
 | 
			
		||||
| 
						 | 
				
			
			@ -857,7 +841,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_le =
 | 
			
		|||
    .pf_parse_stream_frame_header_sz  =  parse_stream_frame_header_sz_gquic,
 | 
			
		||||
    .pf_parse_stream_frame            =  gquic_le_parse_stream_frame,
 | 
			
		||||
    .pf_parse_ack_frame               =  gquic_le_parse_ack_frame,
 | 
			
		||||
    .pf_parse_ack_high                =  gquic_le_parse_ack_high,
 | 
			
		||||
    .pf_gen_ack_frame                 =  gquic_le_gen_ack_frame,
 | 
			
		||||
    .pf_gen_stop_waiting_frame        =  gquic_le_gen_stop_waiting_frame,
 | 
			
		||||
    .pf_parse_stop_waiting_frame      =  gquic_le_parse_stop_waiting_frame,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -410,12 +410,9 @@ send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
 | 
			
		|||
 | 
			
		||||
static void
 | 
			
		||||
send_ctl_unacked_remove (struct lsquic_send_ctl *ctl,
 | 
			
		||||
                         struct lsquic_packet_out *packet_out)
 | 
			
		||||
                     struct lsquic_packet_out *packet_out, unsigned packet_sz)
 | 
			
		||||
{
 | 
			
		||||
    unsigned packet_sz;
 | 
			
		||||
 | 
			
		||||
    TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
 | 
			
		||||
    packet_sz = lsquic_packet_out_total_sz(packet_out);
 | 
			
		||||
    assert(ctl->sc_bytes_unacked_all >= packet_sz);
 | 
			
		||||
    ctl->sc_bytes_unacked_all -= packet_sz;
 | 
			
		||||
    ctl->sc_n_in_flight_all  -= 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -479,47 +476,56 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
 | 
			
		|||
            sizeof(frames), packet_out->po_frame_types));
 | 
			
		||||
    if (account)
 | 
			
		||||
        ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
 | 
			
		||||
    if (0 == lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno))
 | 
			
		||||
    lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno);
 | 
			
		||||
    send_ctl_unacked_append(ctl, packet_out);
 | 
			
		||||
    if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
 | 
			
		||||
    {
 | 
			
		||||
        send_ctl_unacked_append(ctl, packet_out);
 | 
			
		||||
        if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
 | 
			
		||||
        {
 | 
			
		||||
            if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
 | 
			
		||||
                set_retx_alarm(ctl);
 | 
			
		||||
            if (ctl->sc_n_in_flight_retx == 1)
 | 
			
		||||
                ctl->sc_flags |= SC_WAS_QUIET;
 | 
			
		||||
        }
 | 
			
		||||
        /* TODO: Do we really want to use those for RTT info? Revisit this. */
 | 
			
		||||
        /* Hold on to packets that are not retransmittable because we need them
 | 
			
		||||
         * to sample RTT information.  They are released when ACK is received.
 | 
			
		||||
         */
 | 
			
		||||
#if LSQUIC_SEND_STATS
 | 
			
		||||
        ++ctl->sc_stats.n_total_sent;
 | 
			
		||||
#endif
 | 
			
		||||
        return 0;
 | 
			
		||||
        if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
 | 
			
		||||
            set_retx_alarm(ctl);
 | 
			
		||||
        if (ctl->sc_n_in_flight_retx == 1)
 | 
			
		||||
            ctl->sc_flags |= SC_WAS_QUIET;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return -1;
 | 
			
		||||
    /* TODO: Do we really want to use those for RTT info? Revisit this. */
 | 
			
		||||
    /* Hold on to packets that are not retransmittable because we need them
 | 
			
		||||
     * to sample RTT information.  They are released when ACK is received.
 | 
			
		||||
     */
 | 
			
		||||
#if LSQUIC_SEND_STATS
 | 
			
		||||
    ++ctl->sc_stats.n_total_sent;
 | 
			
		||||
#endif
 | 
			
		||||
    lsquic_send_ctl_sanity_check(ctl);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
take_rtt_sample (lsquic_send_ctl_t *ctl, const lsquic_packet_out_t *packet_out,
 | 
			
		||||
take_rtt_sample (lsquic_send_ctl_t *ctl,
 | 
			
		||||
                 lsquic_time_t now, lsquic_time_t lack_delta)
 | 
			
		||||
{
 | 
			
		||||
    assert(packet_out->po_sent);
 | 
			
		||||
    lsquic_time_t measured_rtt = now - packet_out->po_sent;
 | 
			
		||||
    if (packet_out->po_packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
 | 
			
		||||
    const lsquic_packno_t packno = ctl->sc_largest_acked_packno;
 | 
			
		||||
    const lsquic_time_t sent = ctl->sc_largest_acked_sent_time;
 | 
			
		||||
    const lsquic_time_t measured_rtt = now - sent;
 | 
			
		||||
    if (packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
 | 
			
		||||
    {
 | 
			
		||||
        ctl->sc_max_rtt_packno = packet_out->po_packno;
 | 
			
		||||
        ctl->sc_max_rtt_packno = packno;
 | 
			
		||||
        lsquic_rtt_stats_update(&ctl->sc_conn_pub->rtt_stats, measured_rtt, lack_delta);
 | 
			
		||||
        LSQ_DEBUG("packno %"PRIu64"; rtt: %"PRIu64"; delta: %"PRIu64"; "
 | 
			
		||||
            "new srtt: %"PRIu64, packet_out->po_packno, measured_rtt, lack_delta,
 | 
			
		||||
            "new srtt: %"PRIu64, packno, measured_rtt, lack_delta,
 | 
			
		||||
            lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
send_ctl_release_enc_data (struct lsquic_send_ctl *ctl,
 | 
			
		||||
                                        struct lsquic_packet_out *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
 | 
			
		||||
                                            packet_out->po_enc_data);
 | 
			
		||||
    packet_out->po_flags &= ~PO_ENCRYPTED;
 | 
			
		||||
    packet_out->po_enc_data = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Returns true if packet was rescheduled, false otherwise.  In the latter
 | 
			
		||||
 * case, you should not dereference packet_out after the function returns.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -527,14 +533,13 @@ static int
 | 
			
		|||
send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
 | 
			
		||||
                                            lsquic_packet_out_t *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    unsigned packet_sz;
 | 
			
		||||
 | 
			
		||||
    assert(ctl->sc_n_in_flight_all);
 | 
			
		||||
    send_ctl_unacked_remove(ctl, packet_out);
 | 
			
		||||
    if (packet_out->po_flags & PO_ENCRYPTED) {
 | 
			
		||||
        ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
 | 
			
		||||
                                                packet_out->po_enc_data);
 | 
			
		||||
        packet_out->po_flags &= ~PO_ENCRYPTED;
 | 
			
		||||
        packet_out->po_enc_data = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    packet_sz = lsquic_packet_out_sent_sz(packet_out);
 | 
			
		||||
    send_ctl_unacked_remove(ctl, packet_out, packet_sz);
 | 
			
		||||
    if (packet_out->po_flags & PO_ENCRYPTED)
 | 
			
		||||
        send_ctl_release_enc_data(ctl, packet_out);
 | 
			
		||||
    if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
 | 
			
		||||
    {
 | 
			
		||||
        ctl->sc_flags |= SC_LOST_ACK;
 | 
			
		||||
| 
						 | 
				
			
			@ -652,102 +657,110 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
 | 
			
		|||
{
 | 
			
		||||
    struct lsquic_packets_tailq acked_acks =
 | 
			
		||||
                                    TAILQ_HEAD_INITIALIZER(acked_acks);
 | 
			
		||||
#if !LSQUIC_CAN_REORDER
 | 
			
		||||
    const struct lsquic_packno_range *range =
 | 
			
		||||
                                    &acki->ranges[ acki->n_ranges - 1 ];
 | 
			
		||||
#endif
 | 
			
		||||
    lsquic_packet_out_t *packet_out, *next;
 | 
			
		||||
    lsquic_time_t now = lsquic_time_now();
 | 
			
		||||
    lsquic_packno_t high;
 | 
			
		||||
    int rtt_updated = 0;
 | 
			
		||||
    lsquic_time_t now = 0;
 | 
			
		||||
    lsquic_packno_t smallest_unacked;
 | 
			
		||||
    lsquic_packno_t ack2ed[2];
 | 
			
		||||
    unsigned packet_sz;
 | 
			
		||||
    int app_limited;
 | 
			
		||||
    unsigned n;
 | 
			
		||||
    signed char do_rtt, skip_checks;
 | 
			
		||||
 | 
			
		||||
    LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
 | 
			
		||||
                        largest_acked(acki), acki->lack_delta);
 | 
			
		||||
    packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
    __builtin_prefetch(packet_out);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
#   define UNLIKELY(cond) __builtin_expect(cond, 0)
 | 
			
		||||
#else
 | 
			
		||||
#   define UNLIKELY(cond) cond
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
    if (UNLIKELY(LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)))
 | 
			
		||||
#endif
 | 
			
		||||
        LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
 | 
			
		||||
                            largest_acked(acki), acki->lack_delta);
 | 
			
		||||
 | 
			
		||||
    /* Validate ACK first: */
 | 
			
		||||
    for (n = 0; n < acki->n_ranges; ++n)
 | 
			
		||||
        if (!lsquic_senhist_sent_range(&ctl->sc_senhist, acki->ranges[n].low,
 | 
			
		||||
                                                      acki->ranges[n].high))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
 | 
			
		||||
                "was never sent", acki->ranges[n].low, acki->ranges[n].high);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    if (UNLIKELY(largest_acked(acki)
 | 
			
		||||
                                > lsquic_senhist_largest(&ctl->sc_senhist)))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
 | 
			
		||||
            "was never sent", acki->ranges[0].low, acki->ranges[0].high);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ctl->sc_flags & SC_WAS_QUIET)
 | 
			
		||||
    if (UNLIKELY(ctl->sc_flags & SC_WAS_QUIET))
 | 
			
		||||
    {
 | 
			
		||||
        ctl->sc_flags &= ~SC_WAS_QUIET;
 | 
			
		||||
        LSQ_DEBUG("ACK comes after a period of quiescence");
 | 
			
		||||
        if (!now)
 | 
			
		||||
            now = lsquic_time_now();
 | 
			
		||||
        lsquic_cubic_was_quiet(&ctl->sc_cubic, now);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Peer is acking packets that have been acked already.  Schedule ACK
 | 
			
		||||
     * and STOP_WAITING frame to chop the range if we get two of these in
 | 
			
		||||
     * a row.
 | 
			
		||||
     */
 | 
			
		||||
    if (lsquic_send_ctl_smallest_unacked(ctl) > smallest_acked(acki))
 | 
			
		||||
        ++ctl->sc_n_stop_waiting;
 | 
			
		||||
    else
 | 
			
		||||
        ctl->sc_n_stop_waiting = 0;
 | 
			
		||||
    if (UNLIKELY(!packet_out))
 | 
			
		||||
        goto no_unacked_packets;
 | 
			
		||||
 | 
			
		||||
    app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
 | 
			
		||||
        is the "maximum burst" parameter */
 | 
			
		||||
        < lsquic_cubic_get_cwnd(&ctl->sc_cubic);
 | 
			
		||||
    smallest_unacked = packet_out->po_packno;
 | 
			
		||||
    ack2ed[1] = 0;
 | 
			
		||||
 | 
			
		||||
    for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
 | 
			
		||||
            packet_out
 | 
			
		||||
#if !LSQUIC_CAN_REORDER
 | 
			
		||||
                       && packet_out->po_packno <= largest_acked(acki)
 | 
			
		||||
#endif
 | 
			
		||||
                                                                      ;
 | 
			
		||||
                packet_out = next)
 | 
			
		||||
    if (packet_out->po_packno > largest_acked(acki))
 | 
			
		||||
        goto detect_losses;
 | 
			
		||||
 | 
			
		||||
    do_rtt = 0, skip_checks = 0;
 | 
			
		||||
    app_limited = -1;
 | 
			
		||||
    do
 | 
			
		||||
    {
 | 
			
		||||
        next = TAILQ_NEXT(packet_out, po_next);
 | 
			
		||||
#if LSQUIC_CAN_REORDER
 | 
			
		||||
        if (!in_acked_range(acki, packet_out->po_packno))
 | 
			
		||||
            continue;
 | 
			
		||||
#else
 | 
			
		||||
        /* This is faster than binary search in the normal case when the number
 | 
			
		||||
         * of ranges is not much larger than the number of unacked packets.
 | 
			
		||||
         */
 | 
			
		||||
        while (range->high < packet_out->po_packno)
 | 
			
		||||
            --range;
 | 
			
		||||
        if (range->low > packet_out->po_packno)
 | 
			
		||||
            continue;
 | 
			
		||||
#endif
 | 
			
		||||
        ctl->sc_largest_acked_packno    = packet_out->po_packno;
 | 
			
		||||
        ctl->sc_largest_acked_sent_time = packet_out->po_sent;
 | 
			
		||||
        if (packet_out->po_packno == largest_acked(acki))
 | 
			
		||||
        {
 | 
			
		||||
            take_rtt_sample(ctl, packet_out, ack_recv_time, acki->lack_delta);
 | 
			
		||||
            ++rtt_updated;
 | 
			
		||||
        }
 | 
			
		||||
        lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
 | 
			
		||||
                         app_limited, lsquic_packet_out_total_sz(packet_out));
 | 
			
		||||
        LSQ_DEBUG("Got ACK for packet %"PRIu64", remove from unacked queue",
 | 
			
		||||
            packet_out->po_packno);
 | 
			
		||||
        assert(ctl->sc_n_in_flight_all);
 | 
			
		||||
        send_ctl_unacked_remove(ctl, packet_out);
 | 
			
		||||
        lsquic_packet_out_ack_streams(packet_out);
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
        __builtin_prefetch(next);
 | 
			
		||||
#endif
 | 
			
		||||
        if ((ctl->sc_flags & SC_NSTP) &&
 | 
			
		||||
                    (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)))
 | 
			
		||||
            TAILQ_INSERT_TAIL(&acked_acks, packet_out, po_next);
 | 
			
		||||
        else
 | 
			
		||||
        if (skip_checks)
 | 
			
		||||
            goto after_checks;
 | 
			
		||||
        /* This is faster than binary search in the normal case when the number
 | 
			
		||||
         * of ranges is not much larger than the number of unacked packets.
 | 
			
		||||
         */
 | 
			
		||||
        while (UNLIKELY(range->high < packet_out->po_packno))
 | 
			
		||||
            --range;
 | 
			
		||||
        if (range->low <= packet_out->po_packno)
 | 
			
		||||
        {
 | 
			
		||||
            skip_checks = range == acki->ranges;
 | 
			
		||||
            if (app_limited < 0)
 | 
			
		||||
                app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
 | 
			
		||||
                    is the "maximum burst" parameter */
 | 
			
		||||
                    < lsquic_cubic_get_cwnd(&ctl->sc_cubic);
 | 
			
		||||
            if (!now)
 | 
			
		||||
                now = lsquic_time_now();
 | 
			
		||||
  after_checks:
 | 
			
		||||
            packet_sz = lsquic_packet_out_sent_sz(packet_out);
 | 
			
		||||
            ctl->sc_largest_acked_packno    = packet_out->po_packno;
 | 
			
		||||
            ctl->sc_largest_acked_sent_time = packet_out->po_sent;
 | 
			
		||||
            send_ctl_unacked_remove(ctl, packet_out, packet_sz);
 | 
			
		||||
            ack2ed[!!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))]
 | 
			
		||||
                = packet_out->po_ack2ed;
 | 
			
		||||
            do_rtt |= packet_out->po_packno == largest_acked(acki);
 | 
			
		||||
            lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
 | 
			
		||||
                             app_limited, packet_sz);
 | 
			
		||||
            lsquic_packet_out_ack_streams(packet_out);
 | 
			
		||||
            lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
 | 
			
		||||
        }
 | 
			
		||||
        packet_out = next;
 | 
			
		||||
    }
 | 
			
		||||
    while (packet_out && packet_out->po_packno <= largest_acked(acki));
 | 
			
		||||
 | 
			
		||||
    if (rtt_updated)
 | 
			
		||||
    if (do_rtt)
 | 
			
		||||
    {
 | 
			
		||||
        take_rtt_sample(ctl, ack_recv_time, acki->lack_delta);
 | 
			
		||||
        ctl->sc_n_consec_rtos = 0;
 | 
			
		||||
        ctl->sc_n_hsk = 0;
 | 
			
		||||
        ctl->sc_n_tlp = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  detect_losses:
 | 
			
		||||
    send_ctl_detect_losses(ctl, ack_recv_time);
 | 
			
		||||
    if (send_ctl_first_unacked_retx_packet(ctl))
 | 
			
		||||
        set_retx_alarm(ctl);
 | 
			
		||||
| 
						 | 
				
			
			@ -758,29 +771,28 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
 | 
			
		|||
    }
 | 
			
		||||
    lsquic_send_ctl_sanity_check(ctl);
 | 
			
		||||
 | 
			
		||||
    /* Processing of packets that contain acked ACK frames is deferred because
 | 
			
		||||
     * we only need to process one of them: the last one, which we know to
 | 
			
		||||
     * contain the largest value.
 | 
			
		||||
     */
 | 
			
		||||
    packet_out = TAILQ_LAST(&acked_acks, lsquic_packets_tailq);
 | 
			
		||||
    if (packet_out)
 | 
			
		||||
    {
 | 
			
		||||
        high = ctl->sc_conn_pub->lconn->cn_pf->pf_parse_ack_high(
 | 
			
		||||
                                packet_out->po_data, packet_out->po_data_sz);
 | 
			
		||||
        if (high > ctl->sc_largest_ack2ed)
 | 
			
		||||
            ctl->sc_largest_ack2ed = high;
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            next = TAILQ_PREV(packet_out, lsquic_packets_tailq, po_next);
 | 
			
		||||
            lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
 | 
			
		||||
        }
 | 
			
		||||
        while ((packet_out = next));
 | 
			
		||||
    }
 | 
			
		||||
    if ((ctl->sc_flags & SC_NSTP) && ack2ed[1] > ctl->sc_largest_ack2ed)
 | 
			
		||||
        ctl->sc_largest_ack2ed = ack2ed[1];
 | 
			
		||||
 | 
			
		||||
    if (ctl->sc_n_in_flight_retx == 0)
 | 
			
		||||
        ctl->sc_flags |= SC_WAS_QUIET;
 | 
			
		||||
 | 
			
		||||
  update_n_stop_waiting:
 | 
			
		||||
    if (smallest_unacked > smallest_acked(acki))
 | 
			
		||||
        /* Peer is acking packets that have been acked already.  Schedule ACK
 | 
			
		||||
         * and STOP_WAITING frame to chop the range if we get two of these in
 | 
			
		||||
         * a row.
 | 
			
		||||
         */
 | 
			
		||||
        ++ctl->sc_n_stop_waiting;
 | 
			
		||||
    else
 | 
			
		||||
        ctl->sc_n_stop_waiting = 0;
 | 
			
		||||
    lsquic_send_ctl_sanity_check(ctl);
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  no_unacked_packets:
 | 
			
		||||
    smallest_unacked = lsquic_senhist_largest(&ctl->sc_senhist) + 1;
 | 
			
		||||
    ctl->sc_flags |= SC_WAS_QUIET;
 | 
			
		||||
    goto update_n_stop_waiting;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -793,7 +805,7 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
 | 
			
		|||
     * on purpose).  Thus, the first packet on the unacked packets list has
 | 
			
		||||
     * the smallest packet number of all packets on that list.
 | 
			
		||||
     */
 | 
			
		||||
         if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
 | 
			
		||||
    if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
 | 
			
		||||
        return packet_out->po_packno;
 | 
			
		||||
    else
 | 
			
		||||
        return lsquic_senhist_largest(&ctl->sc_senhist) + 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -966,7 +978,7 @@ void
 | 
			
		|||
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packet_out *packet_out;
 | 
			
		||||
    unsigned count, sched_bytes;
 | 
			
		||||
    unsigned count, bytes;
 | 
			
		||||
 | 
			
		||||
    assert(!send_ctl_first_unacked_retx_packet(ctl) ||
 | 
			
		||||
                    lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX));
 | 
			
		||||
| 
						 | 
				
			
			@ -976,20 +988,24 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
 | 
			
		|||
        assert(lsquic_time_now() < ctl->sc_alset->as_expiry[AL_RETX] + MAX_RTO_DELAY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    count = 0;
 | 
			
		||||
    count = 0, bytes = 0;
 | 
			
		||||
    TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
 | 
			
		||||
    {
 | 
			
		||||
        bytes += lsquic_packet_out_sent_sz(packet_out);
 | 
			
		||||
        ++count;
 | 
			
		||||
    }
 | 
			
		||||
    assert(count == ctl->sc_n_in_flight_all);
 | 
			
		||||
    assert(bytes == ctl->sc_bytes_unacked_all);
 | 
			
		||||
 | 
			
		||||
    count = 0, sched_bytes = 0;
 | 
			
		||||
    count = 0, bytes = 0;
 | 
			
		||||
    TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
 | 
			
		||||
    {
 | 
			
		||||
        assert(packet_out->po_flags & PO_SCHED);
 | 
			
		||||
        sched_bytes += lsquic_packet_out_total_sz(packet_out);
 | 
			
		||||
        bytes += lsquic_packet_out_total_sz(packet_out);
 | 
			
		||||
        ++count;
 | 
			
		||||
    }
 | 
			
		||||
    assert(count == ctl->sc_n_scheduled);
 | 
			
		||||
    assert(sched_bytes == ctl->sc_bytes_scheduled);
 | 
			
		||||
    assert(bytes == ctl->sc_bytes_scheduled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1204,6 +1220,7 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
 | 
			
		|||
    oldno = packet_out->po_packno;
 | 
			
		||||
    packno = send_ctl_next_packno(ctl);
 | 
			
		||||
 | 
			
		||||
    packet_out->po_flags &= ~PO_SENT_SZ;
 | 
			
		||||
    packet_out->po_frame_types &= ~QFRAME_REGEN_MASK;
 | 
			
		||||
    assert(packet_out->po_frame_types);
 | 
			
		||||
    packet_out->po_packno = packno;
 | 
			
		||||
| 
						 | 
				
			
			@ -1284,9 +1301,11 @@ lsquic_send_ctl_set_tcid0 (lsquic_send_ctl_t *ctl, int tcid0)
 | 
			
		|||
void
 | 
			
		||||
lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_packet_out *packet_out = NULL, *next = NULL;
 | 
			
		||||
    struct lsquic_packet_out *packet_out, *next = NULL;
 | 
			
		||||
    struct lsquic_packet_out *pre_dropped;
 | 
			
		||||
    unsigned n, adj;
 | 
			
		||||
 | 
			
		||||
    pre_dropped = NULL;
 | 
			
		||||
    for (packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets); packet_out;
 | 
			
		||||
                                                            packet_out = next)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1300,6 +1319,9 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
 | 
			
		|||
            ctl->sc_bytes_scheduled -= adj;
 | 
			
		||||
            if (0 == packet_out->po_frame_types)
 | 
			
		||||
            {
 | 
			
		||||
                if (!pre_dropped)
 | 
			
		||||
                    pre_dropped = TAILQ_PREV(packet_out, lsquic_packets_tailq,
 | 
			
		||||
                                                                    po_next);
 | 
			
		||||
                LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
 | 
			
		||||
                    "stream %"PRIu32, packet_out->po_packno, stream_id);
 | 
			
		||||
                send_ctl_sched_remove(ctl, packet_out);
 | 
			
		||||
| 
						 | 
				
			
			@ -1308,6 +1330,21 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Need to assign new packet numbers to all packets following the first
 | 
			
		||||
     * dropped packet to eliminate packet number gap.
 | 
			
		||||
     */
 | 
			
		||||
    if (pre_dropped)
 | 
			
		||||
    {
 | 
			
		||||
        ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
 | 
			
		||||
        for (packet_out = TAILQ_NEXT(pre_dropped, po_next); packet_out;
 | 
			
		||||
                                packet_out = TAILQ_NEXT(packet_out, po_next))
 | 
			
		||||
        {
 | 
			
		||||
            packet_out->po_flags |= PO_REPACKNO;
 | 
			
		||||
            if (packet_out->po_flags & PO_ENCRYPTED)
 | 
			
		||||
                send_ctl_release_enc_data(ctl, packet_out);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
 | 
			
		||||
                                sizeof(ctl->sc_buffered_packets[0]); ++n)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1339,7 +1376,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
 | 
			
		|||
 * packets.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
#if  __GNUC__
 | 
			
		||||
#if __GNUC__
 | 
			
		||||
__attribute__((weak))
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1420,12 +1457,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
 | 
			
		|||
        if (packet_out->po_regen_sz < packet_out->po_data_sz)
 | 
			
		||||
        {
 | 
			
		||||
            if (packet_out->po_flags & PO_ENCRYPTED)
 | 
			
		||||
            {
 | 
			
		||||
                ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
 | 
			
		||||
                                                    packet_out->po_enc_data);
 | 
			
		||||
                packet_out->po_enc_data = NULL;
 | 
			
		||||
                packet_out->po_flags &= ~PO_ENCRYPTED;
 | 
			
		||||
            }
 | 
			
		||||
                send_ctl_release_enc_data(ctl, packet_out);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -1490,6 +1522,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
 | 
			
		|||
        lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
 | 
			
		||||
    }
 | 
			
		||||
    assert(0 == ctl->sc_n_scheduled);
 | 
			
		||||
    ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
 | 
			
		||||
    LSQ_DEBUG("dropped %u scheduled packet%s", n, n != 0 ? "s" : "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,23 +28,51 @@ struct buf_packet_q
 | 
			
		|||
    unsigned                        bpq_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum send_ctl_flags {
 | 
			
		||||
    SC_TCID0        = (1 << 0),
 | 
			
		||||
    SC_LOST_ACK     = (1 << 1),
 | 
			
		||||
    SC_NSTP         = (1 << 2),
 | 
			
		||||
    SC_PACE         = (1 << 3),
 | 
			
		||||
    SC_SCHED_TICK   = (1 << 4),
 | 
			
		||||
    SC_BUFFER_STREAM= (1 << 5),
 | 
			
		||||
    SC_WAS_QUIET    = (1 << 6),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct lsquic_send_ctl {
 | 
			
		||||
    /* The first section consists of struct members which are used in the
 | 
			
		||||
     * time-critical lsquic_send_ctl_got_ack() in the approximate order
 | 
			
		||||
     * of usage.
 | 
			
		||||
     */
 | 
			
		||||
    lsquic_senhist_t                sc_senhist;
 | 
			
		||||
    enum send_ctl_flags             sc_flags;
 | 
			
		||||
    unsigned                        sc_n_stop_waiting;
 | 
			
		||||
    struct lsquic_packets_tailq     sc_unacked_packets;
 | 
			
		||||
    lsquic_packno_t                 sc_largest_acked_packno;
 | 
			
		||||
    lsquic_time_t                   sc_largest_acked_sent_time;
 | 
			
		||||
    unsigned                        sc_bytes_out;
 | 
			
		||||
    unsigned                        sc_bytes_unacked_retx;
 | 
			
		||||
    unsigned                        sc_bytes_scheduled;
 | 
			
		||||
    unsigned                        sc_pack_size;
 | 
			
		||||
    struct lsquic_cubic             sc_cubic;
 | 
			
		||||
    struct lsquic_engine_public    *sc_enpub;
 | 
			
		||||
    unsigned                        sc_bytes_unacked_all;
 | 
			
		||||
    unsigned                        sc_n_in_flight_all;
 | 
			
		||||
    unsigned                        sc_n_in_flight_retx;
 | 
			
		||||
    unsigned                        sc_n_consec_rtos;
 | 
			
		||||
    unsigned                        sc_n_hsk;
 | 
			
		||||
    unsigned                        sc_n_tlp;
 | 
			
		||||
    struct lsquic_alarmset         *sc_alset;
 | 
			
		||||
 | 
			
		||||
    /* Second section: everything else. */
 | 
			
		||||
    struct lsquic_packets_tailq     sc_scheduled_packets,
 | 
			
		||||
                                    sc_unacked_packets,
 | 
			
		||||
                                    sc_lost_packets;
 | 
			
		||||
    struct buf_packet_q             sc_buffered_packets[BPT_OTHER_PRIO + 1];
 | 
			
		||||
    struct lsquic_engine_public    *sc_enpub;
 | 
			
		||||
    struct lsquic_alarmset         *sc_alset;
 | 
			
		||||
    const struct ver_neg           *sc_ver_neg;
 | 
			
		||||
    struct lsquic_conn_public      *sc_conn_pub;
 | 
			
		||||
    struct lsquic_cubic             sc_cubic;
 | 
			
		||||
    struct pacer                    sc_pacer;
 | 
			
		||||
    lsquic_senhist_t                sc_senhist;
 | 
			
		||||
    lsquic_packno_t                 sc_cur_packno;
 | 
			
		||||
    lsquic_packno_t                 sc_largest_sent_at_cutback;
 | 
			
		||||
    lsquic_packno_t                 sc_max_rtt_packno;
 | 
			
		||||
    lsquic_packno_t                 sc_largest_acked_packno;
 | 
			
		||||
    lsquic_time_t                   sc_largest_acked_sent_time;
 | 
			
		||||
    /* sc_largest_ack2ed is the packet number sent by peer that we acked and
 | 
			
		||||
     * we know that our ACK was received by peer.  This is used to determine
 | 
			
		||||
     * the receive history cutoff point for the purposes of generating ACK
 | 
			
		||||
| 
						 | 
				
			
			@ -59,28 +87,8 @@ typedef struct lsquic_send_ctl {
 | 
			
		|||
        uint32_t                stream_id;
 | 
			
		||||
        enum buf_packet_type    packet_type;
 | 
			
		||||
    }                               sc_cached_bpt;
 | 
			
		||||
    unsigned                        sc_bytes_out;
 | 
			
		||||
    unsigned                        sc_bytes_unacked_all;
 | 
			
		||||
    unsigned                        sc_bytes_unacked_retx;
 | 
			
		||||
    unsigned                        sc_n_consec_rtos;
 | 
			
		||||
    unsigned                        sc_next_limit;
 | 
			
		||||
    unsigned                        sc_n_in_flight_all;
 | 
			
		||||
    unsigned                        sc_n_in_flight_retx;
 | 
			
		||||
    unsigned                        sc_n_scheduled;
 | 
			
		||||
    unsigned                        sc_bytes_scheduled;
 | 
			
		||||
    unsigned                        sc_n_stop_waiting;
 | 
			
		||||
    unsigned short                  sc_pack_size;
 | 
			
		||||
    enum {
 | 
			
		||||
        SC_TCID0        = (1 << 0),
 | 
			
		||||
        SC_LOST_ACK     = (1 << 1),
 | 
			
		||||
        SC_NSTP         = (1 << 2),
 | 
			
		||||
        SC_PACE         = (1 << 3),
 | 
			
		||||
        SC_SCHED_TICK   = (1 << 4),
 | 
			
		||||
        SC_BUFFER_STREAM= (1 << 5),
 | 
			
		||||
        SC_WAS_QUIET    = (1 << 6),
 | 
			
		||||
    }                               sc_flags:8;
 | 
			
		||||
    unsigned char                   sc_n_hsk;
 | 
			
		||||
    unsigned char                   sc_n_tlp;
 | 
			
		||||
#if LSQUIC_SEND_STATS
 | 
			
		||||
    struct {
 | 
			
		||||
        unsigned            n_total_sent,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,126 +3,18 @@
 | 
			
		|||
 * lsquic_senhist.c -- Sent history implementation
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic_int_types.h"
 | 
			
		||||
#include "lsquic_senhist.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_init (lsquic_senhist_t *hist)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_packints_init(&hist->sh_pints);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_cleanup (lsquic_senhist_t *hist)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_packints_cleanup(&hist->sh_pints);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* At the time of this writing, the only reason the sequence of sent
 | 
			
		||||
 * packet numbers could contain a hole is elision of stream frames from
 | 
			
		||||
 * scheduled, but delayed packets.  If such packet becomes empty after
 | 
			
		||||
 * elision, it is dropped from the queue.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The fast insert is used in the normal case, when packets are sent
 | 
			
		||||
 * out in the same order in which they are scheduled: that is, their
 | 
			
		||||
 * packet numbers are always increasing.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
senhist_add_fast (lsquic_senhist_t *hist, lsquic_packno_t packno)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_interval *pi;
 | 
			
		||||
 | 
			
		||||
    pi = TAILQ_FIRST(&hist->sh_pints.pk_intervals);
 | 
			
		||||
    if (pi)
 | 
			
		||||
    {
 | 
			
		||||
        /* Check that packet numbers are always increasing */
 | 
			
		||||
        assert(packno > pi->range.high);
 | 
			
		||||
        if (packno == pi->range.high + 1)
 | 
			
		||||
        {
 | 
			
		||||
            ++pi->range.high;
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pi = malloc(sizeof(*pi));
 | 
			
		||||
    if (!pi)
 | 
			
		||||
        return -1;
 | 
			
		||||
    pi->range.high = packno;
 | 
			
		||||
    pi->range.low = packno;
 | 
			
		||||
    TAILQ_INSERT_HEAD(&hist->sh_pints.pk_intervals, pi, next_pi);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
lsquic_senhist_add (lsquic_senhist_t *hist, lsquic_packno_t packno)
 | 
			
		||||
{
 | 
			
		||||
        return senhist_add_fast(hist, packno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
lsquic_senhist_sent_range (lsquic_senhist_t *hist, lsquic_packno_t low,
 | 
			
		||||
                                                   lsquic_packno_t high)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *range;
 | 
			
		||||
    for (range = lsquic_packints_first(&hist->sh_pints); range;
 | 
			
		||||
                            range = lsquic_packints_next(&hist->sh_pints))
 | 
			
		||||
        if (range->low <= low && range->high >= high)
 | 
			
		||||
            return 1;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lsquic_packno_t
 | 
			
		||||
lsquic_senhist_largest (lsquic_senhist_t *hist)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *range;
 | 
			
		||||
    range = lsquic_packints_first(&hist->sh_pints);
 | 
			
		||||
    if (range)
 | 
			
		||||
        return range->high;
 | 
			
		||||
    else
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz)
 | 
			
		||||
{
 | 
			
		||||
    const struct lsquic_packno_range *range;
 | 
			
		||||
    size_t off;
 | 
			
		||||
    int n;
 | 
			
		||||
    for (off = 0, range = lsquic_packints_first(&hist->sh_pints);
 | 
			
		||||
            range && off < bufsz;
 | 
			
		||||
                off += n, range = lsquic_packints_next(&hist->sh_pints))
 | 
			
		||||
    {
 | 
			
		||||
        n = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
 | 
			
		||||
                                                    range->high, range->low);
 | 
			
		||||
        if (n < 0 || (size_t) n >= bufsz - off)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    if (bufsz > 0)
 | 
			
		||||
        buf[off] = '\0';
 | 
			
		||||
    if (hist->sh_last_sent)
 | 
			
		||||
        snprintf(buf, bufsz, "[1-%"PRIu64"]", hist->sh_last_sent);
 | 
			
		||||
    else
 | 
			
		||||
        snprintf(buf, bufsz, "[]");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
lsquic_senhist_mem_used (const struct lsquic_senhist *hist)
 | 
			
		||||
{
 | 
			
		||||
    return sizeof(*hist)
 | 
			
		||||
         - sizeof(hist->sh_pints)
 | 
			
		||||
         + lsquic_packints_mem_used(&hist->sh_pints);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,49 +2,61 @@
 | 
			
		|||
/*
 | 
			
		||||
 * lsquic_senhist.h -- History sent packets.
 | 
			
		||||
 *
 | 
			
		||||
 * We only keep track of packet numbers in order to verify ACKs.
 | 
			
		||||
 * We need to keep track of packet numbers in order to verify ACKs.  To
 | 
			
		||||
 * speed up processing, we make sure that there is never a gap in the
 | 
			
		||||
 * packet number sequence we generate.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef LSQUIC_SENHIST_H
 | 
			
		||||
#define LSQUIC_SENHIST_H 1
 | 
			
		||||
 | 
			
		||||
#include "lsquic_packints.h"
 | 
			
		||||
#ifndef LSQUIC_SENHIST_FATAL
 | 
			
		||||
#   define LSQUIC_SENHIST_FATAL 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct lsquic_senhist {
 | 
			
		||||
    /* These ranges are ordered from high to low.  While searching this
 | 
			
		||||
     * structure is O(n), I expect that in practice, a very long search
 | 
			
		||||
     * could only happen once before the connection is terminated,
 | 
			
		||||
     * because:
 | 
			
		||||
     *  a) either the packet number far away is real, but it was so long
 | 
			
		||||
     *     ago that it would have timed out by now (RTO); or
 | 
			
		||||
     *  b) the peer sends an invalid ACK.
 | 
			
		||||
     */
 | 
			
		||||
    struct packints             sh_pints;
 | 
			
		||||
    lsquic_packno_t             sh_last_sent;
 | 
			
		||||
#if !LSQUIC_SENHIST_FATAL
 | 
			
		||||
    enum {
 | 
			
		||||
        SH_WARNED   = 1 << 0,   /* Warn once */
 | 
			
		||||
    }                           sh_flags;
 | 
			
		||||
#endif
 | 
			
		||||
} lsquic_senhist_t;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_init (lsquic_senhist_t *);
 | 
			
		||||
#define lsquic_senhist_init(hist) do {                                  \
 | 
			
		||||
    (hist)->sh_last_sent = 0;                                           \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_cleanup (lsquic_senhist_t *);
 | 
			
		||||
#define lsquic_senhist_cleanup(hist)
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
lsquic_senhist_add (lsquic_senhist_t *, lsquic_packno_t);
 | 
			
		||||
 | 
			
		||||
/* Returns true if history contains all packets numbers in this range.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
lsquic_senhist_sent_range (lsquic_senhist_t *, lsquic_packno_t low,
 | 
			
		||||
                                               lsquic_packno_t high);
 | 
			
		||||
#if LSQUIC_SENHIST_FATAL
 | 
			
		||||
#define lsquic_senhist_add(hist, packno) do {                           \
 | 
			
		||||
    assert((hist)->sh_last_sent == packno - 1);                         \
 | 
			
		||||
    (hist)->sh_last_sent = packno;                                      \
 | 
			
		||||
} while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define lsquic_senhist_add(hist, packno) do {                           \
 | 
			
		||||
    if ((hist)->sh_last_sent == packno - 1)                             \
 | 
			
		||||
        (hist)->sh_last_sent = packno;                                  \
 | 
			
		||||
    else                                                                \
 | 
			
		||||
    {                                                                   \
 | 
			
		||||
        if (!((hist)->sh_flags & SH_WARNED))                            \
 | 
			
		||||
        {                                                               \
 | 
			
		||||
            LSQ_WARN("send history gap %"PRIu64" - %"PRIu64,            \
 | 
			
		||||
                (hist)->sh_last_sent, packno);                          \
 | 
			
		||||
            (hist)->sh_flags |= SH_WARNED;                              \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
        (hist)->sh_last_sent = packno;                                  \
 | 
			
		||||
    }                                                                   \
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Returns 0 if no packets have been sent yet */
 | 
			
		||||
lsquic_packno_t
 | 
			
		||||
lsquic_senhist_largest (lsquic_senhist_t *hist);
 | 
			
		||||
#define lsquic_senhist_largest(hist) (+(hist)->sh_last_sent)
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz);
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
lsquic_senhist_mem_used (const struct lsquic_senhist *);
 | 
			
		||||
#define lsquic_senhist_mem_used(hist) (sizeof(*(hist)))
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,365 +46,366 @@ static int promise_fd = -1;
 | 
			
		|||
struct lsquic_conn_ctx;
 | 
			
		||||
 | 
			
		||||
struct path_elem {
 | 
			
		||||
	TAILQ_ENTRY(path_elem)      next_pe;
 | 
			
		||||
	const char                 *path;
 | 
			
		||||
    TAILQ_ENTRY(path_elem)      next_pe;
 | 
			
		||||
    const char                 *path;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct http_client_ctx {
 | 
			
		||||
	TAILQ_HEAD(, lsquic_conn_ctx)
 | 
			
		||||
								 conn_ctxs;
 | 
			
		||||
	struct service_port         *sport;
 | 
			
		||||
	const char                  *hostname;
 | 
			
		||||
	const char                  *method;
 | 
			
		||||
	const char                  *payload;
 | 
			
		||||
	char                         payload_size[20];
 | 
			
		||||
    TAILQ_HEAD(, lsquic_conn_ctx)
 | 
			
		||||
                                 conn_ctxs;
 | 
			
		||||
    struct service_port         *sport;
 | 
			
		||||
    const char                  *hostname;
 | 
			
		||||
    const char                  *method;
 | 
			
		||||
    const char                  *payload;
 | 
			
		||||
    char                         payload_size[20];
 | 
			
		||||
 | 
			
		||||
	/* hcc_path_elems holds a list of paths which are to be requested from
 | 
			
		||||
	 * the server.  Each new request gets the next path from the list (the
 | 
			
		||||
	 * iterator is stored in hcc_cur_pe); when the end is reached, the
 | 
			
		||||
	 * iterator wraps around.
 | 
			
		||||
	 */
 | 
			
		||||
	TAILQ_HEAD(, path_elem)      hcc_path_elems;
 | 
			
		||||
	struct path_elem            *hcc_cur_pe;
 | 
			
		||||
    /* hcc_path_elems holds a list of paths which are to be requested from
 | 
			
		||||
     * the server.  Each new request gets the next path from the list (the
 | 
			
		||||
     * iterator is stored in hcc_cur_pe); when the end is reached, the
 | 
			
		||||
     * iterator wraps around.
 | 
			
		||||
     */
 | 
			
		||||
    TAILQ_HEAD(, path_elem)      hcc_path_elems;
 | 
			
		||||
    struct path_elem            *hcc_cur_pe;
 | 
			
		||||
 | 
			
		||||
	unsigned                     hcc_total_n_reqs;
 | 
			
		||||
	unsigned                     hcc_reqs_per_conn;
 | 
			
		||||
	unsigned                     hcc_concurrency;
 | 
			
		||||
	unsigned                     hcc_n_open_conns;
 | 
			
		||||
    unsigned                     hcc_total_n_reqs;
 | 
			
		||||
    unsigned                     hcc_reqs_per_conn;
 | 
			
		||||
    unsigned                     hcc_concurrency;
 | 
			
		||||
    unsigned                     hcc_n_open_conns;
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
		HCC_DISCARD_RESPONSE    = (1 << 0),
 | 
			
		||||
		HCC_SEEN_FIN            = (1 << 1),
 | 
			
		||||
		HCC_ABORT_ON_INCOMPLETE = (1 << 2),
 | 
			
		||||
	}                            hcc_flags;
 | 
			
		||||
	struct prog                 *prog;
 | 
			
		||||
    enum {
 | 
			
		||||
        HCC_DISCARD_RESPONSE    = (1 << 0),
 | 
			
		||||
        HCC_SEEN_FIN            = (1 << 1),
 | 
			
		||||
        HCC_ABORT_ON_INCOMPLETE = (1 << 2),
 | 
			
		||||
    }                            hcc_flags;
 | 
			
		||||
    struct prog                 *prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lsquic_conn_ctx {
 | 
			
		||||
	TAILQ_ENTRY(lsquic_conn_ctx) next_ch;
 | 
			
		||||
	lsquic_conn_t       *conn;
 | 
			
		||||
	struct http_client_ctx   *client_ctx;
 | 
			
		||||
	unsigned             ch_n_reqs;    /* This number gets decremented as streams are closed and
 | 
			
		||||
										* incremented as push promises are accepted.
 | 
			
		||||
										*/
 | 
			
		||||
    TAILQ_ENTRY(lsquic_conn_ctx) next_ch;
 | 
			
		||||
    lsquic_conn_t       *conn;
 | 
			
		||||
    struct http_client_ctx   *client_ctx;
 | 
			
		||||
    unsigned             ch_n_reqs;    /* This number gets decremented as streams are closed and
 | 
			
		||||
                                        * incremented as push promises are accepted.
 | 
			
		||||
                                        */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
create_connections (struct http_client_ctx *client_ctx)
 | 
			
		||||
{
 | 
			
		||||
	while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency &&
 | 
			
		||||
		   client_ctx->hcc_total_n_reqs > 0)
 | 
			
		||||
		if (0 != prog_connect(client_ctx->prog))
 | 
			
		||||
		{
 | 
			
		||||
			LSQ_ERROR("connection failed");
 | 
			
		||||
			exit(EXIT_FAILURE);
 | 
			
		||||
		}
 | 
			
		||||
    while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency &&
 | 
			
		||||
           client_ctx->hcc_total_n_reqs > 0)
 | 
			
		||||
        if (0 != prog_connect(client_ctx->prog))
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("connection failed");
 | 
			
		||||
            exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_conn_ctx_t *
 | 
			
		||||
http_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct http_client_ctx *client_ctx = stream_if_ctx;
 | 
			
		||||
	lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
 | 
			
		||||
	conn_h->conn = conn;
 | 
			
		||||
	conn_h->client_ctx = client_ctx;
 | 
			
		||||
	conn_h->ch_n_reqs = client_ctx->hcc_total_n_reqs <
 | 
			
		||||
											client_ctx->hcc_reqs_per_conn ?
 | 
			
		||||
		client_ctx->hcc_total_n_reqs : client_ctx->hcc_reqs_per_conn;
 | 
			
		||||
	client_ctx->hcc_total_n_reqs -= conn_h->ch_n_reqs;
 | 
			
		||||
	TAILQ_INSERT_TAIL(&client_ctx->conn_ctxs, conn_h, next_ch);
 | 
			
		||||
	++conn_h->client_ctx->hcc_n_open_conns;
 | 
			
		||||
	lsquic_conn_make_stream(conn);
 | 
			
		||||
	return conn_h;
 | 
			
		||||
    struct http_client_ctx *client_ctx = stream_if_ctx;
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
 | 
			
		||||
    conn_h->conn = conn;
 | 
			
		||||
    conn_h->client_ctx = client_ctx;
 | 
			
		||||
    conn_h->ch_n_reqs = client_ctx->hcc_total_n_reqs <
 | 
			
		||||
                                            client_ctx->hcc_reqs_per_conn ?
 | 
			
		||||
        client_ctx->hcc_total_n_reqs : client_ctx->hcc_reqs_per_conn;
 | 
			
		||||
    client_ctx->hcc_total_n_reqs -= conn_h->ch_n_reqs;
 | 
			
		||||
    TAILQ_INSERT_TAIL(&client_ctx->conn_ctxs, conn_h, next_ch);
 | 
			
		||||
    ++conn_h->client_ctx->hcc_n_open_conns;
 | 
			
		||||
    lsquic_conn_make_stream(conn);
 | 
			
		||||
    return conn_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
http_client_on_conn_closed (lsquic_conn_t *conn)
 | 
			
		||||
{
 | 
			
		||||
	lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
	enum LSQUIC_CONN_STATUS status;
 | 
			
		||||
	char errmsg[80];
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
    enum LSQUIC_CONN_STATUS status;
 | 
			
		||||
    char errmsg[80];
 | 
			
		||||
 | 
			
		||||
	status = lsquic_conn_status(conn, errmsg, sizeof(errmsg));
 | 
			
		||||
	LSQ_INFO("Connection closed.  Status: %d.  Message: %s", status,
 | 
			
		||||
		errmsg[0] ? errmsg : "<not set>");
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
	if (conn_h->client_ctx->hcc_flags & HCC_ABORT_ON_INCOMPLETE)
 | 
			
		||||
		assert(conn_h->client_ctx->hcc_flags & HCC_SEEN_FIN);
 | 
			
		||||
#endif
 | 
			
		||||
	TAILQ_REMOVE(&conn_h->client_ctx->conn_ctxs, conn_h, next_ch);
 | 
			
		||||
	--conn_h->client_ctx->hcc_n_open_conns;
 | 
			
		||||
	create_connections(conn_h->client_ctx);
 | 
			
		||||
	if (0 == conn_h->client_ctx->hcc_n_open_conns)
 | 
			
		||||
	{
 | 
			
		||||
		LSQ_INFO("All connections are closed: stop engine");
 | 
			
		||||
		prog_stop(conn_h->client_ctx->prog);
 | 
			
		||||
	}
 | 
			
		||||
	free(conn_h);
 | 
			
		||||
    status = lsquic_conn_status(conn, errmsg, sizeof(errmsg));
 | 
			
		||||
    LSQ_INFO("Connection closed.  Status: %d.  Message: %s", status,
 | 
			
		||||
        errmsg[0] ? errmsg : "<not set>");
 | 
			
		||||
    if (conn_h->client_ctx->hcc_flags & HCC_ABORT_ON_INCOMPLETE)
 | 
			
		||||
    {
 | 
			
		||||
        if (!(conn_h->client_ctx->hcc_flags & HCC_SEEN_FIN))
 | 
			
		||||
            abort();
 | 
			
		||||
    }
 | 
			
		||||
    TAILQ_REMOVE(&conn_h->client_ctx->conn_ctxs, conn_h, next_ch);
 | 
			
		||||
    --conn_h->client_ctx->hcc_n_open_conns;
 | 
			
		||||
    create_connections(conn_h->client_ctx);
 | 
			
		||||
    if (0 == conn_h->client_ctx->hcc_n_open_conns)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("All connections are closed: stop engine");
 | 
			
		||||
        prog_stop(conn_h->client_ctx->prog);
 | 
			
		||||
    }
 | 
			
		||||
    free(conn_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct lsquic_stream_ctx {
 | 
			
		||||
	lsquic_stream_t     *stream;
 | 
			
		||||
	struct http_client_ctx   *client_ctx;
 | 
			
		||||
	const char          *path;
 | 
			
		||||
	enum {
 | 
			
		||||
		HEADERS_SENT    = (1 << 0),
 | 
			
		||||
	}                    sh_flags;
 | 
			
		||||
	unsigned             count;
 | 
			
		||||
	struct lsquic_reader reader;
 | 
			
		||||
    lsquic_stream_t     *stream;
 | 
			
		||||
    struct http_client_ctx   *client_ctx;
 | 
			
		||||
    const char          *path;
 | 
			
		||||
    enum {
 | 
			
		||||
        HEADERS_SENT    = (1 << 0),
 | 
			
		||||
    }                    sh_flags;
 | 
			
		||||
    unsigned             count;
 | 
			
		||||
    struct lsquic_reader reader;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static lsquic_stream_ctx_t *
 | 
			
		||||
http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
	const int pushed = lsquic_stream_is_pushed(stream);
 | 
			
		||||
    const int pushed = lsquic_stream_is_pushed(stream);
 | 
			
		||||
 | 
			
		||||
	if (pushed)
 | 
			
		||||
	{
 | 
			
		||||
		LSQ_INFO("not accepting server push");
 | 
			
		||||
		lsquic_stream_refuse_push(stream);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
    if (pushed)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("not accepting server push");
 | 
			
		||||
        lsquic_stream_refuse_push(stream);
 | 
			
		||||
        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 (st_h->client_ctx->hcc_cur_pe)
 | 
			
		||||
	{
 | 
			
		||||
		st_h->client_ctx->hcc_cur_pe = TAILQ_NEXT(
 | 
			
		||||
										st_h->client_ctx->hcc_cur_pe, next_pe);
 | 
			
		||||
		if (!st_h->client_ctx->hcc_cur_pe)  /* Wrap around */
 | 
			
		||||
			st_h->client_ctx->hcc_cur_pe =
 | 
			
		||||
								TAILQ_FIRST(&st_h->client_ctx->hcc_path_elems);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		st_h->client_ctx->hcc_cur_pe = TAILQ_FIRST(
 | 
			
		||||
											&st_h->client_ctx->hcc_path_elems);
 | 
			
		||||
	st_h->path = st_h->client_ctx->hcc_cur_pe->path;
 | 
			
		||||
	if (st_h->client_ctx->payload)
 | 
			
		||||
	{
 | 
			
		||||
		st_h->reader.lsqr_read = test_reader_read;
 | 
			
		||||
		st_h->reader.lsqr_size = test_reader_size;
 | 
			
		||||
		st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->client_ctx->payload);
 | 
			
		||||
		if (!st_h->reader.lsqr_ctx)
 | 
			
		||||
			exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		st_h->reader.lsqr_ctx = NULL;
 | 
			
		||||
	LSQ_INFO("created new stream, path: %s", st_h->path);
 | 
			
		||||
	lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
 | 
			
		||||
    st_h->stream = stream;
 | 
			
		||||
    st_h->client_ctx = stream_if_ctx;
 | 
			
		||||
    if (st_h->client_ctx->hcc_cur_pe)
 | 
			
		||||
    {
 | 
			
		||||
        st_h->client_ctx->hcc_cur_pe = TAILQ_NEXT(
 | 
			
		||||
                                        st_h->client_ctx->hcc_cur_pe, next_pe);
 | 
			
		||||
        if (!st_h->client_ctx->hcc_cur_pe)  /* Wrap around */
 | 
			
		||||
            st_h->client_ctx->hcc_cur_pe =
 | 
			
		||||
                                TAILQ_FIRST(&st_h->client_ctx->hcc_path_elems);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        st_h->client_ctx->hcc_cur_pe = TAILQ_FIRST(
 | 
			
		||||
                                            &st_h->client_ctx->hcc_path_elems);
 | 
			
		||||
    st_h->path = st_h->client_ctx->hcc_cur_pe->path;
 | 
			
		||||
    if (st_h->client_ctx->payload)
 | 
			
		||||
    {
 | 
			
		||||
        st_h->reader.lsqr_read = test_reader_read;
 | 
			
		||||
        st_h->reader.lsqr_size = test_reader_size;
 | 
			
		||||
        st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->client_ctx->payload);
 | 
			
		||||
        if (!st_h->reader.lsqr_ctx)
 | 
			
		||||
            exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        st_h->reader.lsqr_ctx = NULL;
 | 
			
		||||
    LSQ_INFO("created new stream, path: %s", st_h->path);
 | 
			
		||||
    lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
 | 
			
		||||
	return st_h;
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
send_headers (lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
	lsquic_http_header_t headers_arr[] = {
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = ":method",       .iov_len = 7, },
 | 
			
		||||
			.value = { .iov_base = (void *) st_h->client_ctx->method,
 | 
			
		||||
					   .iov_len = strlen(st_h->client_ctx->method), },
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = ":scheme",       .iov_len = 7, },
 | 
			
		||||
			.value = { .iov_base = "HTTP",          .iov_len = 4, }
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = ":path",         .iov_len = 5, },
 | 
			
		||||
			.value = { .iov_base = (void *) st_h->path,
 | 
			
		||||
					   .iov_len = strlen(st_h->path), },
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { ":authority",     10, },
 | 
			
		||||
			.value = { .iov_base = (void *) st_h->client_ctx->hostname,
 | 
			
		||||
					   .iov_len = strlen(st_h->client_ctx->hostname), },
 | 
			
		||||
		},
 | 
			
		||||
		/*
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { "host",      4 },
 | 
			
		||||
			.value = { .iov_base = (void *) st_h->client_ctx->hostname,
 | 
			
		||||
					   .iov_len = strlen(st_h->client_ctx->hostname), },
 | 
			
		||||
		},
 | 
			
		||||
		*/
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = "user-agent",    .iov_len = 10, },
 | 
			
		||||
			.value = { .iov_base = (char *) st_h->client_ctx->prog->prog_settings.es_ua,
 | 
			
		||||
					   .iov_len  = strlen(st_h->client_ctx->prog->prog_settings.es_ua), },
 | 
			
		||||
		},
 | 
			
		||||
		/* The following headers only gets sent if there is request payload: */
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = "content-type", .iov_len = 12, },
 | 
			
		||||
			.value = { .iov_base = "application/octet-stream", .iov_len = 24, },
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			.name  = { .iov_base = "content-length", .iov_len = 14, },
 | 
			
		||||
			.value = { .iov_base = (void *) st_h->client_ctx->payload_size,
 | 
			
		||||
					   .iov_len = strlen(st_h->client_ctx->payload_size), },
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
	lsquic_http_headers_t headers = {
 | 
			
		||||
		.count = sizeof(headers_arr) / sizeof(headers_arr[0]),
 | 
			
		||||
		.headers = headers_arr,
 | 
			
		||||
	};
 | 
			
		||||
	if (!st_h->client_ctx->payload)
 | 
			
		||||
		headers.count -= 2;
 | 
			
		||||
	if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
 | 
			
		||||
									st_h->client_ctx->payload == NULL))
 | 
			
		||||
	{
 | 
			
		||||
		LSQ_ERROR("cannot send headers: %s", strerror(errno));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
    lsquic_http_header_t headers_arr[] = {
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = ":method",       .iov_len = 7, },
 | 
			
		||||
            .value = { .iov_base = (void *) st_h->client_ctx->method,
 | 
			
		||||
                       .iov_len = strlen(st_h->client_ctx->method), },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = ":scheme",       .iov_len = 7, },
 | 
			
		||||
            .value = { .iov_base = "HTTP",          .iov_len = 4, }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = ":path",         .iov_len = 5, },
 | 
			
		||||
            .value = { .iov_base = (void *) st_h->path,
 | 
			
		||||
                       .iov_len = strlen(st_h->path), },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { ":authority",     10, },
 | 
			
		||||
            .value = { .iov_base = (void *) st_h->client_ctx->hostname,
 | 
			
		||||
                       .iov_len = strlen(st_h->client_ctx->hostname), },
 | 
			
		||||
        },
 | 
			
		||||
        /*
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { "host",      4 },
 | 
			
		||||
            .value = { .iov_base = (void *) st_h->client_ctx->hostname,
 | 
			
		||||
                       .iov_len = strlen(st_h->client_ctx->hostname), },
 | 
			
		||||
        },
 | 
			
		||||
        */
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = "user-agent",    .iov_len = 10, },
 | 
			
		||||
            .value = { .iov_base = (char *) st_h->client_ctx->prog->prog_settings.es_ua,
 | 
			
		||||
                       .iov_len  = strlen(st_h->client_ctx->prog->prog_settings.es_ua), },
 | 
			
		||||
        },
 | 
			
		||||
        /* The following headers only gets sent if there is request payload: */
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = "content-type", .iov_len = 12, },
 | 
			
		||||
            .value = { .iov_base = "application/octet-stream", .iov_len = 24, },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name  = { .iov_base = "content-length", .iov_len = 14, },
 | 
			
		||||
            .value = { .iov_base = (void *) st_h->client_ctx->payload_size,
 | 
			
		||||
                       .iov_len = strlen(st_h->client_ctx->payload_size), },
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
    lsquic_http_headers_t headers = {
 | 
			
		||||
        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
 | 
			
		||||
        .headers = headers_arr,
 | 
			
		||||
    };
 | 
			
		||||
    if (!st_h->client_ctx->payload)
 | 
			
		||||
        headers.count -= 2;
 | 
			
		||||
    if (0 != lsquic_stream_send_headers(st_h->stream, &headers,
 | 
			
		||||
                                    st_h->client_ctx->payload == NULL))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("cannot send headers: %s", strerror(errno));
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
http_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t nw;
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
 | 
			
		||||
	if (st_h->sh_flags & HEADERS_SENT)
 | 
			
		||||
	{
 | 
			
		||||
		if (st_h->client_ctx->payload && test_reader_size(st_h->reader.lsqr_ctx) > 0)
 | 
			
		||||
		{
 | 
			
		||||
			nw = lsquic_stream_writef(stream, &st_h->reader);
 | 
			
		||||
			if (nw < 0)
 | 
			
		||||
			{
 | 
			
		||||
				LSQ_ERROR("write error: %s", strerror(errno));
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
			if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
 | 
			
		||||
			{
 | 
			
		||||
				lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
				lsquic_stream_wantread(stream, 1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
			lsquic_stream_wantread(stream, 1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		st_h->sh_flags |= HEADERS_SENT;
 | 
			
		||||
		send_headers(st_h);
 | 
			
		||||
	}
 | 
			
		||||
    if (st_h->sh_flags & HEADERS_SENT)
 | 
			
		||||
    {
 | 
			
		||||
        if (st_h->client_ctx->payload && test_reader_size(st_h->reader.lsqr_ctx) > 0)
 | 
			
		||||
        {
 | 
			
		||||
            nw = lsquic_stream_writef(stream, &st_h->reader);
 | 
			
		||||
            if (nw < 0)
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_ERROR("write error: %s", strerror(errno));
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
 | 
			
		||||
            {
 | 
			
		||||
                lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
                lsquic_stream_wantread(stream, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
            lsquic_stream_wantread(stream, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        st_h->sh_flags |= HEADERS_SENT;
 | 
			
		||||
        send_headers(st_h);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
	struct http_client_ctx *const client_ctx = st_h->client_ctx;
 | 
			
		||||
	ssize_t nread;
 | 
			
		||||
	unsigned old_prio, new_prio;
 | 
			
		||||
	unsigned char buf[0x200];
 | 
			
		||||
	unsigned nreads = 0;
 | 
			
		||||
    struct http_client_ctx *const client_ctx = st_h->client_ctx;
 | 
			
		||||
    ssize_t nread;
 | 
			
		||||
    unsigned old_prio, new_prio;
 | 
			
		||||
    unsigned char buf[0x200];
 | 
			
		||||
    unsigned nreads = 0;
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
	srand(GetTickCount());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	do
 | 
			
		||||
	{
 | 
			
		||||
		nread = lsquic_stream_read(stream, buf, sizeof(buf));
 | 
			
		||||
		if (nread > 0)
 | 
			
		||||
		{
 | 
			
		||||
			if (!(client_ctx->hcc_flags & HCC_DISCARD_RESPONSE))
 | 
			
		||||
				write(STDOUT_FILENO, buf, nread);
 | 
			
		||||
			if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
 | 
			
		||||
			{
 | 
			
		||||
				old_prio = lsquic_stream_priority(stream);
 | 
			
		||||
				new_prio = random() & 0xFF;
 | 
			
		||||
    do
 | 
			
		||||
    {
 | 
			
		||||
        nread = lsquic_stream_read(stream, buf, sizeof(buf));
 | 
			
		||||
        if (nread > 0)
 | 
			
		||||
        {
 | 
			
		||||
            if (!(client_ctx->hcc_flags & HCC_DISCARD_RESPONSE))
 | 
			
		||||
                write(STDOUT_FILENO, buf, nread);
 | 
			
		||||
            if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                old_prio = lsquic_stream_priority(stream);
 | 
			
		||||
                new_prio = random() & 0xFF;
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
				const int s =
 | 
			
		||||
                const int s =
 | 
			
		||||
#endif
 | 
			
		||||
				lsquic_stream_set_priority(stream, new_prio);
 | 
			
		||||
				assert(s == 0);
 | 
			
		||||
				LSQ_NOTICE("changed stream %u priority from %u to %u",
 | 
			
		||||
								lsquic_stream_id(stream), old_prio, new_prio);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (0 == nread)
 | 
			
		||||
		{
 | 
			
		||||
			client_ctx->hcc_flags |= HCC_SEEN_FIN;
 | 
			
		||||
			lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		else if (client_ctx->prog->prog_settings.es_rw_once && EWOULDBLOCK == errno)
 | 
			
		||||
		{
 | 
			
		||||
			LSQ_NOTICE("emptied the buffer in 'once' mode");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			LSQ_ERROR("could not read: %s", strerror(errno));
 | 
			
		||||
			exit(2);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	while (client_ctx->prog->prog_settings.es_rw_once
 | 
			
		||||
			&& nreads++ < 3 /* Emulate just a few reads */);
 | 
			
		||||
                lsquic_stream_set_priority(stream, new_prio);
 | 
			
		||||
                assert(s == 0);
 | 
			
		||||
                LSQ_NOTICE("changed stream %u priority from %u to %u",
 | 
			
		||||
                                lsquic_stream_id(stream), old_prio, new_prio);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (0 == nread)
 | 
			
		||||
        {
 | 
			
		||||
            client_ctx->hcc_flags |= HCC_SEEN_FIN;
 | 
			
		||||
            lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        else if (client_ctx->prog->prog_settings.es_rw_once && EWOULDBLOCK == errno)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_NOTICE("emptied the buffer in 'once' mode");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("could not read: %s", strerror(errno));
 | 
			
		||||
            exit(2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    while (client_ctx->prog->prog_settings.es_rw_once
 | 
			
		||||
            && nreads++ < 3 /* Emulate just a few reads */);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
http_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
	const int pushed = lsquic_stream_is_pushed(stream);
 | 
			
		||||
	if (pushed)
 | 
			
		||||
	{
 | 
			
		||||
		assert(NULL == st_h);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
    const int pushed = lsquic_stream_is_pushed(stream);
 | 
			
		||||
    if (pushed)
 | 
			
		||||
    {
 | 
			
		||||
        assert(NULL == st_h);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	LSQ_INFO("%s called", __func__);
 | 
			
		||||
	lsquic_conn_t *conn = lsquic_stream_conn(stream);
 | 
			
		||||
	lsquic_conn_ctx_t *conn_h;
 | 
			
		||||
	TAILQ_FOREACH(conn_h, &st_h->client_ctx->conn_ctxs, next_ch)
 | 
			
		||||
		if (conn_h->conn == conn)
 | 
			
		||||
			break;
 | 
			
		||||
	assert(conn_h);
 | 
			
		||||
	--conn_h->ch_n_reqs;
 | 
			
		||||
	if (0 == conn_h->ch_n_reqs)
 | 
			
		||||
	{
 | 
			
		||||
		LSQ_INFO("all requests completed, closing connection");
 | 
			
		||||
		lsquic_conn_close(conn_h->conn);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		lsquic_conn_make_stream(conn);
 | 
			
		||||
	if (st_h->reader.lsqr_ctx)
 | 
			
		||||
		destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
 | 
			
		||||
	free(st_h);
 | 
			
		||||
    LSQ_INFO("%s called", __func__);
 | 
			
		||||
    lsquic_conn_t *conn = lsquic_stream_conn(stream);
 | 
			
		||||
    lsquic_conn_ctx_t *conn_h;
 | 
			
		||||
    TAILQ_FOREACH(conn_h, &st_h->client_ctx->conn_ctxs, next_ch)
 | 
			
		||||
        if (conn_h->conn == conn)
 | 
			
		||||
            break;
 | 
			
		||||
    assert(conn_h);
 | 
			
		||||
    --conn_h->ch_n_reqs;
 | 
			
		||||
    if (0 == conn_h->ch_n_reqs)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("all requests completed, closing connection");
 | 
			
		||||
        lsquic_conn_close(conn_h->conn);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        lsquic_conn_make_stream(conn);
 | 
			
		||||
    if (st_h->reader.lsqr_ctx)
 | 
			
		||||
        destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
 | 
			
		||||
    free(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if http_client_if = {
 | 
			
		||||
	.on_new_conn            = http_client_on_new_conn,
 | 
			
		||||
	.on_conn_closed         = http_client_on_conn_closed,
 | 
			
		||||
	.on_new_stream          = http_client_on_new_stream,
 | 
			
		||||
	.on_read                = http_client_on_read,
 | 
			
		||||
	.on_write               = http_client_on_write,
 | 
			
		||||
	.on_close               = http_client_on_close,
 | 
			
		||||
    .on_new_conn            = http_client_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = http_client_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = http_client_on_new_stream,
 | 
			
		||||
    .on_read                = http_client_on_read,
 | 
			
		||||
    .on_write               = http_client_on_write,
 | 
			
		||||
    .on_close               = http_client_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
	const char *const slash = strrchr(prog, '/');
 | 
			
		||||
	if (slash)
 | 
			
		||||
		prog = slash + 1;
 | 
			
		||||
	printf(
 | 
			
		||||
    const char *const slash = strrchr(prog, '/');
 | 
			
		||||
    if (slash)
 | 
			
		||||
        prog = slash + 1;
 | 
			
		||||
    printf(
 | 
			
		||||
"Usage: %s [opts]\n"
 | 
			
		||||
"\n"
 | 
			
		||||
"Options:\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -420,121 +421,121 @@ usage (const char *prog)
 | 
			
		|||
"                 content-length\n"
 | 
			
		||||
"   -K          Discard server response\n"
 | 
			
		||||
"   -I          Abort on incomplete reponse from server\n"
 | 
			
		||||
			, prog);
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int opt, s;
 | 
			
		||||
	struct http_client_ctx client_ctx;
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	struct path_elem *pe;
 | 
			
		||||
	struct sport_head sports;
 | 
			
		||||
	struct prog prog;
 | 
			
		||||
    int opt, s;
 | 
			
		||||
    struct http_client_ctx client_ctx;
 | 
			
		||||
    struct stat st;
 | 
			
		||||
    struct path_elem *pe;
 | 
			
		||||
    struct sport_head sports;
 | 
			
		||||
    struct prog prog;
 | 
			
		||||
 | 
			
		||||
	TAILQ_INIT(&sports);
 | 
			
		||||
	memset(&client_ctx, 0, sizeof(client_ctx));
 | 
			
		||||
	client_ctx.hcc_concurrency = 1;
 | 
			
		||||
	TAILQ_INIT(&client_ctx.hcc_path_elems);
 | 
			
		||||
	TAILQ_INIT(&client_ctx.conn_ctxs);
 | 
			
		||||
	client_ctx.hostname = "localhost";
 | 
			
		||||
	client_ctx.method = "GET";
 | 
			
		||||
	client_ctx.hcc_concurrency = 1;
 | 
			
		||||
	client_ctx.hcc_reqs_per_conn = 1;
 | 
			
		||||
	client_ctx.hcc_total_n_reqs = 1;
 | 
			
		||||
	client_ctx.prog = &prog;
 | 
			
		||||
    TAILQ_INIT(&sports);
 | 
			
		||||
    memset(&client_ctx, 0, sizeof(client_ctx));
 | 
			
		||||
    client_ctx.hcc_concurrency = 1;
 | 
			
		||||
    TAILQ_INIT(&client_ctx.hcc_path_elems);
 | 
			
		||||
    TAILQ_INIT(&client_ctx.conn_ctxs);
 | 
			
		||||
    client_ctx.hostname = "localhost";
 | 
			
		||||
    client_ctx.method = "GET";
 | 
			
		||||
    client_ctx.hcc_concurrency = 1;
 | 
			
		||||
    client_ctx.hcc_reqs_per_conn = 1;
 | 
			
		||||
    client_ctx.hcc_total_n_reqs = 1;
 | 
			
		||||
    client_ctx.prog = &prog;
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
    WSADATA wsd;
 | 
			
		||||
    WSAStartup(MAKEWORD(2, 2), &wsd);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
 | 
			
		||||
    prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx);
 | 
			
		||||
 | 
			
		||||
	while (-1 != (opt = getopt(argc, argv, PROG_OPTS "r:R:Ku:EP:M:n:H:p:h")))
 | 
			
		||||
	{
 | 
			
		||||
		switch (opt) {
 | 
			
		||||
		case 'I':
 | 
			
		||||
			client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'K':
 | 
			
		||||
			client_ctx.hcc_flags |= HCC_DISCARD_RESPONSE;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'u':   /* Accept p<U>sh promise */
 | 
			
		||||
			promise_fd = open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
 | 
			
		||||
			if (promise_fd < 0)
 | 
			
		||||
			{
 | 
			
		||||
				perror("open");
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
			prog.prog_settings.es_support_push = 1;     /* Pokes into prog */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'E':   /* E: randomly reprioritize str<E>ams.  Now, that's
 | 
			
		||||
					 * pretty random. :)
 | 
			
		||||
					 */
 | 
			
		||||
			randomly_reprioritize_streams = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'n':
 | 
			
		||||
			client_ctx.hcc_concurrency = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'P':
 | 
			
		||||
			client_ctx.payload = optarg;
 | 
			
		||||
			if (0 != stat(optarg, &st))
 | 
			
		||||
			{
 | 
			
		||||
				perror("stat");
 | 
			
		||||
				exit(2);
 | 
			
		||||
			}
 | 
			
		||||
			sprintf(client_ctx.payload_size, "%jd", (intmax_t) st.st_size);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'M':
 | 
			
		||||
			client_ctx.method = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'r':
 | 
			
		||||
			client_ctx.hcc_total_n_reqs = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'R':
 | 
			
		||||
			client_ctx.hcc_reqs_per_conn = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'H':
 | 
			
		||||
			client_ctx.hostname = optarg;
 | 
			
		||||
			prog.prog_hostname = optarg;            /* Pokes into prog */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'p':
 | 
			
		||||
			pe = calloc(1, sizeof(*pe));
 | 
			
		||||
			pe->path = optarg;
 | 
			
		||||
			TAILQ_INSERT_TAIL(&client_ctx.hcc_path_elems, pe, next_pe);
 | 
			
		||||
			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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "r:R:IKu:EP:M:n:H:p:h")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'I':
 | 
			
		||||
            client_ctx.hcc_flags |= HCC_ABORT_ON_INCOMPLETE;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'K':
 | 
			
		||||
            client_ctx.hcc_flags |= HCC_DISCARD_RESPONSE;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'u':   /* Accept p<U>sh promise */
 | 
			
		||||
            promise_fd = open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
 | 
			
		||||
            if (promise_fd < 0)
 | 
			
		||||
            {
 | 
			
		||||
                perror("open");
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            prog.prog_settings.es_support_push = 1;     /* Pokes into prog */
 | 
			
		||||
            break;
 | 
			
		||||
        case 'E':   /* E: randomly reprioritize str<E>ams.  Now, that's
 | 
			
		||||
                     * pretty random. :)
 | 
			
		||||
                     */
 | 
			
		||||
            randomly_reprioritize_streams = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'n':
 | 
			
		||||
            client_ctx.hcc_concurrency = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'P':
 | 
			
		||||
            client_ctx.payload = optarg;
 | 
			
		||||
            if (0 != stat(optarg, &st))
 | 
			
		||||
            {
 | 
			
		||||
                perror("stat");
 | 
			
		||||
                exit(2);
 | 
			
		||||
            }
 | 
			
		||||
            sprintf(client_ctx.payload_size, "%jd", (intmax_t) st.st_size);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'M':
 | 
			
		||||
            client_ctx.method = optarg;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'r':
 | 
			
		||||
            client_ctx.hcc_total_n_reqs = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'R':
 | 
			
		||||
            client_ctx.hcc_reqs_per_conn = atoi(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'H':
 | 
			
		||||
            client_ctx.hostname = optarg;
 | 
			
		||||
            prog.prog_hostname = optarg;            /* Pokes into prog */
 | 
			
		||||
            break;
 | 
			
		||||
        case 'p':
 | 
			
		||||
            pe = calloc(1, sizeof(*pe));
 | 
			
		||||
            pe->path = optarg;
 | 
			
		||||
            TAILQ_INSERT_TAIL(&client_ctx.hcc_path_elems, pe, next_pe);
 | 
			
		||||
            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 (TAILQ_EMPTY(&client_ctx.hcc_path_elems))
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "Specify at least one path using -p option\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
    if (TAILQ_EMPTY(&client_ctx.hcc_path_elems))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Specify at least one path using -p option\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if (0 != prog_prep(&prog))
 | 
			
		||||
	{
 | 
			
		||||
		LSQ_ERROR("could not prep");
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
    if (0 != prog_prep(&prog))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("could not prep");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	create_connections(&client_ctx);
 | 
			
		||||
    create_connections(&client_ctx);
 | 
			
		||||
 | 
			
		||||
	LSQ_DEBUG("entering event loop");
 | 
			
		||||
    LSQ_DEBUG("entering event loop");
 | 
			
		||||
 | 
			
		||||
	s = prog_run(&prog);
 | 
			
		||||
	prog_cleanup(&prog);
 | 
			
		||||
	if (promise_fd >= 0)
 | 
			
		||||
		(void) close(promise_fd);
 | 
			
		||||
    s = prog_run(&prog);
 | 
			
		||||
    prog_cleanup(&prog);
 | 
			
		||||
    if (promise_fd >= 0)
 | 
			
		||||
        (void) close(promise_fd);
 | 
			
		||||
 | 
			
		||||
	exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,10 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value")
 | 
			
		|||
set(MIN_LIBS_LIST "m ${FIULIB}")
 | 
			
		||||
set(LIBS_LIST "pthread libssl.a libcrypto.a z ${MIN_LIBS_LIST}")
 | 
			
		||||
ELSE()
 | 
			
		||||
SET (UGH_LIB_LIST debug  $(SolutionDir)src/liblsquic/debug/lsquic.lib debug  ${VCPKG_BASE_DIR}/debug/lib/event.lib debug  ${VCPKG_BASE_DIR}/debug/lib/zlibd.lib debug  ${BORINGSSL_BASE_LIB_DIR}/debug/ssl.lib debug  ${BORINGSSL_BASE_LIB_DIR}/debug/crypto.lib optimized  $(SolutionDir)src/liblsquic/release/lsquic.lib optimized  ${VCPKG_BASE_DIR}/lib/event.lib optimized  ${VCPKG_BASE_DIR}/lib/zlib.lib optimized  ${BORINGSSL_BASE_LIB_DIR}/ssl.lib optimized  ${BORINGSSL_BASE_LIB_DIR}/crypto.lib)
 | 
			
		||||
 | 
			
		||||
set(MIN_LIBS_LIST ${FIULIB} ws2_32)
 | 
			
		||||
set(LIBS_LIST ssl crypto ${ZLIB_NAME} ${MIN_LIBS_LIST})
 | 
			
		||||
set(LIBS_LIST ${UGH_LIB_LIST} ${MIN_LIBS_LIST})
 | 
			
		||||
ENDIF()
 | 
			
		||||
 | 
			
		||||
enable_testing()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
{
 | 
			
		||||
    lsquic_rechist_t rechist;
 | 
			
		||||
    lsquic_time_t now = lsquic_time_now();
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_init(&rechist, 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,14 +46,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing);
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -102,18 +102,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -147,18 +147,18 @@ test3 (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
    assert(largest == 0x03);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -185,18 +185,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(1 == ack_high);
 | 
			
		||||
        assert(largest == 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 3; i <= 5; ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -215,18 +215,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(5 == ack_high);
 | 
			
		||||
        assert(largest == 5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
| 
						 | 
				
			
			@ -267,16 +267,18 @@ test_4byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0x23456789);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -316,16 +318,18 @@ test_6byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0xABCD23456789ULL);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
{
 | 
			
		||||
    lsquic_rechist_t rechist;
 | 
			
		||||
    lsquic_time_t now = lsquic_time_now();
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_init(&rechist, 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,14 +47,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing);
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -104,18 +104,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -150,18 +150,18 @@ test3 (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
    assert(largest == 0x03);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -189,18 +189,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(1 == ack_high);
 | 
			
		||||
        assert(largest == 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 3; i <= 5; ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -220,18 +220,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(5 == ack_high);
 | 
			
		||||
        assert(largest == 5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
| 
						 | 
				
			
			@ -270,16 +270,18 @@ test_4byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0x23456789);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -317,16 +319,18 @@ test_8byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0xABCD23456789ULL);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
{
 | 
			
		||||
    lsquic_rechist_t rechist;
 | 
			
		||||
    lsquic_time_t now = lsquic_time_now();
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_init(&rechist, 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,14 +46,13 @@ test1 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock */
 | 
			
		|||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing);
 | 
			
		||||
        &rechist, now + 0x7FF8000, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -102,18 +102,18 @@ test2 (void) /* Inverse of quic_framer_test.cc -- NewAckFrameOneAckBlock, minus
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
    assert(largest == 0x1234);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -147,18 +147,18 @@ test3 (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
    assert(largest == 0x03);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -185,18 +185,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has no missing packets", has_missing == 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(1 == ack_high);
 | 
			
		||||
        assert(largest == 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 3; i <= 5; ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -215,18 +215,18 @@ test4 (void)
 | 
			
		|||
        };
 | 
			
		||||
        unsigned char outbuf[0x100];
 | 
			
		||||
        int has_missing = -1;
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
        assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
        assert(("ACK frame contents are as expected",
 | 
			
		||||
            0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
        assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
        lsquic_packno_t ack_high = pf->pf_parse_ack_high(outbuf, sizeof(outbuf));
 | 
			
		||||
        assert(5 == ack_high);
 | 
			
		||||
        assert(largest == 5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
| 
						 | 
				
			
			@ -267,16 +267,18 @@ test_4byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0x23456789);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -316,16 +318,18 @@ test_6byte_packnos (void)
 | 
			
		|||
    unsigned char outbuf[0x100];
 | 
			
		||||
 | 
			
		||||
    int has_missing = -1;
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    int w = pf->pf_gen_ack_frame(outbuf, sizeof(outbuf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(("ACK frame generation successful", w > 0));
 | 
			
		||||
    assert(("ACK frame length is correct", w == sizeof(expected_ack_frame)));
 | 
			
		||||
    assert(("ACK frame contents are as expected",
 | 
			
		||||
        0 == memcmp(outbuf, expected_ack_frame, sizeof(expected_ack_frame))));
 | 
			
		||||
    assert(("ACK frame has missing packets", has_missing > 0));
 | 
			
		||||
    assert(largest == 0xABCD23456789ULL);
 | 
			
		||||
 | 
			
		||||
    lsquic_rechist_cleanup(&rechist);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,8 +50,6 @@ test1 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 0x1234", n == 0x1234));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        size_t sz;
 | 
			
		||||
| 
						 | 
				
			
			@ -113,8 +111,6 @@ test2 (void)
 | 
			
		|||
    assert(("Number of timestamps is 2", acki.n_timestamps == 2));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4254", n == 4254));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 4; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -166,8 +162,6 @@ test3 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4", n == 4));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(6 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -218,8 +212,6 @@ test4 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 2", n == 2));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -361,11 +353,12 @@ test_max_ack (void)
 | 
			
		|||
 | 
			
		||||
    memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(sz[0] > 0);
 | 
			
		||||
    assert(sz[0] <= (int) sizeof(buf));
 | 
			
		||||
    assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			@ -414,11 +407,12 @@ test_ack_truncation (void)
 | 
			
		|||
    for (bufsz = 200; bufsz < 210; ++bufsz)
 | 
			
		||||
    {
 | 
			
		||||
        memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(sz[0] > 0);
 | 
			
		||||
        assert(sz[0] <= (int) bufsz);
 | 
			
		||||
        assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,8 +51,6 @@ test1 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 0x1234", n == 0x1234));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        size_t sz;
 | 
			
		||||
| 
						 | 
				
			
			@ -115,8 +113,6 @@ test2 (void)
 | 
			
		|||
    assert(("Number of timestamps is 2", acki.n_timestamps == 2));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4254", n == 4254));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 4; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -169,8 +165,6 @@ test3 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4", n == 4));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(6 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -222,8 +216,6 @@ test4 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 2", n == 2));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -361,11 +353,12 @@ test_max_ack (void)
 | 
			
		|||
 | 
			
		||||
    memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(sz[0] > 0);
 | 
			
		||||
    assert(sz[0] <= (int) sizeof(buf));
 | 
			
		||||
    assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			@ -414,11 +407,12 @@ test_ack_truncation (void)
 | 
			
		|||
    for (bufsz = 200; bufsz < 210; ++bufsz)
 | 
			
		||||
    {
 | 
			
		||||
        memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(sz[0] > 0);
 | 
			
		||||
        assert(sz[0] <= (int) bufsz);
 | 
			
		||||
        assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,8 +49,6 @@ test1 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 0x1234", n == 0x1234));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        size_t sz;
 | 
			
		||||
| 
						 | 
				
			
			@ -112,8 +110,6 @@ test2 (void)
 | 
			
		|||
    assert(("Number of timestamps is 2", acki.n_timestamps == 2));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4254", n == 4254));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(0x1234 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 4; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -165,8 +161,6 @@ test3 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 4", n == 4));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(6 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -217,8 +211,6 @@ test4 (void)
 | 
			
		|||
    assert(("Number of timestamps is 0", acki.n_timestamps == 0));
 | 
			
		||||
    unsigned n = n_acked(&acki);
 | 
			
		||||
    assert(("Number of acked packets is 2", n == 2));
 | 
			
		||||
    lsquic_packno_t ack_high = pf->pf_parse_ack_high(ack_buf, sizeof(ack_buf));
 | 
			
		||||
    assert(3 == ack_high);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < 2; ++n)
 | 
			
		||||
        assert(("Range checks out", ranges[n].high == acki.ranges[n].high
 | 
			
		||||
| 
						 | 
				
			
			@ -360,11 +352,12 @@ test_max_ack (void)
 | 
			
		|||
 | 
			
		||||
    memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
    lsquic_packno_t largest = 0;
 | 
			
		||||
    sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
 | 
			
		||||
        (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
        (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
        (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
        &rechist, now, &has_missing);
 | 
			
		||||
        &rechist, now, &has_missing, &largest);
 | 
			
		||||
    assert(sz[0] > 0);
 | 
			
		||||
    assert(sz[0] <= (int) sizeof(buf));
 | 
			
		||||
    assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			@ -413,11 +406,12 @@ test_ack_truncation (void)
 | 
			
		|||
    for (bufsz = 200; bufsz < 210; ++bufsz)
 | 
			
		||||
    {
 | 
			
		||||
        memset(buf, 0xAA, sizeof(buf));
 | 
			
		||||
        lsquic_packno_t largest = 0;
 | 
			
		||||
        sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
 | 
			
		||||
            (gaf_rechist_first_f)        lsquic_rechist_first,
 | 
			
		||||
            (gaf_rechist_next_f)         lsquic_rechist_next,
 | 
			
		||||
            (gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
 | 
			
		||||
            &rechist, now, &has_missing);
 | 
			
		||||
            &rechist, now, &has_missing, &largest);
 | 
			
		||||
        assert(sz[0] > 0);
 | 
			
		||||
        assert(sz[0] <= (int) bufsz);
 | 
			
		||||
        assert(has_missing);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,6 +83,9 @@ lsquic_stream_acked (lsquic_stream_t *stream)
 | 
			
		|||
static void
 | 
			
		||||
elide_single_stream_frame (void)
 | 
			
		||||
{
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
#endif
 | 
			
		||||
    struct lsquic_engine_public enpub;
 | 
			
		||||
    lsquic_stream_t streams[1];
 | 
			
		||||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,52 +3,27 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include "lsquic_int_types.h"
 | 
			
		||||
#include "lsquic_senhist.h"
 | 
			
		||||
#include "lsquic_logger.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (void)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_senhist hist;
 | 
			
		||||
    struct lsquic_senhist hist = { 0 };
 | 
			
		||||
    lsquic_packno_t packno;
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    lsquic_senhist_init(&hist);
 | 
			
		||||
 | 
			
		||||
    assert(0 == lsquic_senhist_largest(&hist));
 | 
			
		||||
 | 
			
		||||
    for (packno = 1; packno < 100; ++packno)
 | 
			
		||||
        lsquic_senhist_add(&hist, packno);
 | 
			
		||||
 | 
			
		||||
    for (packno = 1; packno < 100; ++packno)
 | 
			
		||||
    {
 | 
			
		||||
        s = lsquic_senhist_sent_range(&hist, packno, packno);
 | 
			
		||||
        assert(s);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Note break in the sequence at 100 */
 | 
			
		||||
    for (packno = 101; packno < 200; ++packno)
 | 
			
		||||
        lsquic_senhist_add(&hist, packno);
 | 
			
		||||
 | 
			
		||||
    for (packno = 1; packno < 100; ++packno)
 | 
			
		||||
    {
 | 
			
		||||
        s = lsquic_senhist_sent_range(&hist, packno, packno);
 | 
			
		||||
        assert(s);
 | 
			
		||||
    }
 | 
			
		||||
    s = lsquic_senhist_sent_range(&hist, 100, 100);
 | 
			
		||||
    assert(0 == s);
 | 
			
		||||
    for (packno = 101; packno < 200; ++packno)
 | 
			
		||||
    {
 | 
			
		||||
        s = lsquic_senhist_sent_range(&hist, packno, packno);
 | 
			
		||||
        assert(s);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s = lsquic_senhist_sent_range(&hist, 1, 99);
 | 
			
		||||
    assert(s);
 | 
			
		||||
    s = lsquic_senhist_sent_range(&hist, 101, 199);
 | 
			
		||||
    assert(s);
 | 
			
		||||
    s = lsquic_senhist_sent_range(&hist, 1, 199);
 | 
			
		||||
    assert(0 == s);
 | 
			
		||||
    assert(99 == lsquic_senhist_largest(&hist));
 | 
			
		||||
 | 
			
		||||
    lsquic_senhist_cleanup(&hist);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
- only debug and release are expected in the Cmakelists.txt. If you need a different config, please follow the model in that file to add it.
 | 
			
		||||
 | 
			
		||||
- vcpkg does not have boringssl, so you'll have to build it yourself. Follow the instructions at the boringssl repository.
 | 
			
		||||
  With the caveat that you should do it from a VC command prompt for the correct architecture and make sure to set all 
 | 
			
		||||
  the paths for perl,ninja,etc. correctly. Also watch out for C runtime library mismatches. The easiest fix for me was to
 | 
			
		||||
  change the flags in the CMake cache file.
 | 
			
		||||
  the paths for perl,ninja,etc. correctly. Also watch out for C runtime library mismatches with the externals you link.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- zlib and libevent do exist in vcpkg. 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue