mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.19.9
- [FEATURE] Add lsquic_stream_pwritev(). This function allows one to reduce the number of system calls required to read a file from disk by using lsquic_stream_pwritev() together with preadv(2). - [BUGFIX] When stream is reset, it is writeable -- let user collect the error. - [BUGFIX] Calculate correct conn flow control if reading ends early. - [BUGFIX] Remove stream from read and write queues on internal shutdown. This is a regression introduced in 2.19.7. - [BUGFIX] Swapped arguments in IETF RESET_FRAME generation. - Turn off mini conn history when compiling with Visual Studio; this allows the project to compile on Windows again. - http_client: Add -3 flag to stop reading from streams early; code cleanup. - Don't use -Werror.
This commit is contained in:
parent
49f1f4f620
commit
2f2f436324
25 changed files with 1545 additions and 85 deletions
|
@ -76,6 +76,11 @@ static int g_header_bypass;
|
|||
|
||||
static int s_discard_response;
|
||||
|
||||
/* If set to a non-zero value, abandon reading from stream early: read at
|
||||
* most `s_abandon_early' bytes and then close the stream.
|
||||
*/
|
||||
static long s_abandon_early;
|
||||
|
||||
struct sample_stats
|
||||
{
|
||||
unsigned n;
|
||||
|
@ -164,8 +169,6 @@ struct path_elem {
|
|||
};
|
||||
|
||||
struct http_client_ctx {
|
||||
TAILQ_HEAD(, lsquic_conn_ctx)
|
||||
conn_ctxs;
|
||||
const char *hostname;
|
||||
const char *method;
|
||||
const char *payload;
|
||||
|
@ -294,7 +297,6 @@ http_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
|
|||
conn_h->ch_n_reqs = MIN(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;
|
||||
if (!TAILQ_EMPTY(&client_ctx->hcc_path_elems))
|
||||
create_streams(client_ctx, conn_h);
|
||||
|
@ -346,7 +348,6 @@ http_client_on_conn_closed (lsquic_conn_t *conn)
|
|||
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;
|
||||
|
||||
cacos = calloc(1, sizeof(*cacos));
|
||||
|
@ -474,9 +475,16 @@ struct lsquic_stream_ctx {
|
|||
enum {
|
||||
HEADERS_SENT = (1 << 0),
|
||||
PROCESSED_HEADERS = 1 << 1,
|
||||
ABANDON = 1 << 2, /* Abandon reading from stream after sh_stop bytes
|
||||
* have been read.
|
||||
*/
|
||||
} sh_flags;
|
||||
lsquic_time_t sh_created;
|
||||
lsquic_time_t sh_ttfb;
|
||||
size_t sh_stop; /* Stop after reading this many bytes if ABANDON is set */
|
||||
size_t sh_nread; /* Number of bytes read from stream using one of
|
||||
* lsquic_stream_read* functions.
|
||||
*/
|
||||
unsigned count;
|
||||
struct lsquic_reader reader;
|
||||
};
|
||||
|
@ -524,6 +532,11 @@ http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
|
|||
lsquic_stream_wantwrite(stream, 1);
|
||||
if (randomly_reprioritize_streams)
|
||||
lsquic_stream_set_priority(stream, 1 + (random() & 0xFF));
|
||||
if (s_abandon_early)
|
||||
{
|
||||
st_h->sh_stop = random() % (s_abandon_early + 1);
|
||||
st_h->sh_flags |= ABANDON;
|
||||
}
|
||||
|
||||
return st_h;
|
||||
}
|
||||
|
@ -634,6 +647,14 @@ http_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
static size_t
|
||||
discard (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
||||
{
|
||||
lsquic_stream_ctx_t *st_h = ctx;
|
||||
|
||||
if (st_h->sh_flags & ABANDON)
|
||||
{
|
||||
if (sz > st_h->sh_stop - st_h->sh_nread)
|
||||
sz = st_h->sh_stop - st_h->sh_nread;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
@ -671,10 +692,14 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
st_h->sh_flags |= PROCESSED_HEADERS;
|
||||
}
|
||||
else if (nread = (s_discard_response
|
||||
? lsquic_stream_readf(stream, discard, NULL)
|
||||
: lsquic_stream_read(stream, buf, sizeof(buf))),
|
||||
? lsquic_stream_readf(stream, discard, st_h)
|
||||
: lsquic_stream_read(stream, buf,
|
||||
st_h->sh_flags & ABANDON
|
||||
? MIN(sizeof(buf), st_h->sh_nread - st_h->sh_stop)
|
||||
: sizeof(buf))),
|
||||
nread > 0)
|
||||
{
|
||||
st_h->sh_nread += (size_t) nread;
|
||||
s_stat_downloaded_bytes += nread;
|
||||
/* test stream_reset after some number of read bytes */
|
||||
if (client_ctx->hcc_reset_after_nbytes &&
|
||||
|
@ -713,6 +738,13 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
LSQ_DEBUG("changed stream %"PRIu64" priority from %u to %u",
|
||||
lsquic_stream_id(stream), old_prio, new_prio);
|
||||
}
|
||||
if ((st_h->sh_flags & ABANDON) && st_h->sh_nread >= st_h->sh_stop)
|
||||
{
|
||||
LSQ_DEBUG("closing stream early having read %zd bytes",
|
||||
st_h->sh_nread);
|
||||
lsquic_stream_close(stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (0 == nread)
|
||||
{
|
||||
|
@ -756,11 +788,7 @@ http_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
LSQ_INFO("%s called", __func__);
|
||||
struct http_client_ctx *const client_ctx = st_h->client_ctx;
|
||||
lsquic_conn_t *conn = lsquic_stream_conn(stream);
|
||||
lsquic_conn_ctx_t *conn_h;
|
||||
TAILQ_FOREACH(conn_h, &client_ctx->conn_ctxs, next_ch)
|
||||
if (conn_h->conn == conn)
|
||||
break;
|
||||
assert(conn_h);
|
||||
lsquic_conn_ctx_t *const conn_h = lsquic_conn_get_ctx(conn);
|
||||
--conn_h->ch_n_reqs;
|
||||
--conn_h->ch_n_cc_streams;
|
||||
if (0 == conn_h->ch_n_reqs)
|
||||
|
@ -834,6 +862,8 @@ usage (const char *prog)
|
|||
" -q FILE QIF mode: issue requests from the QIF file and validate\n"
|
||||
" server responses.\n"
|
||||
" -e TOKEN Hexadecimal string representing resume token.\n"
|
||||
" -3 MAX Close stream after reading at most MAX bytes. The actual\n"
|
||||
" number of bytes read is randominzed.\n"
|
||||
, prog);
|
||||
}
|
||||
|
||||
|
@ -1406,7 +1436,6 @@ main (int argc, char **argv)
|
|||
TAILQ_INIT(&sports);
|
||||
memset(&client_ctx, 0, sizeof(client_ctx));
|
||||
TAILQ_INIT(&client_ctx.hcc_path_elems);
|
||||
TAILQ_INIT(&client_ctx.conn_ctxs);
|
||||
client_ctx.method = "GET";
|
||||
client_ctx.hcc_concurrency = 1;
|
||||
client_ctx.hcc_cc_reqs_per_conn = 1;
|
||||
|
@ -1420,6 +1449,7 @@ main (int argc, char **argv)
|
|||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS
|
||||
"46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
|
||||
"3:" /* 3 is 133+ for "e" ("e" for "early") */
|
||||
#ifndef WIN32
|
||||
"C:"
|
||||
#endif
|
||||
|
@ -1536,6 +1566,9 @@ main (int argc, char **argv)
|
|||
http_client_if.on_sess_resume_info = http_client_on_sess_resume_info;
|
||||
client_ctx.hcc_sess_resume_file_name = optarg;
|
||||
break;
|
||||
case '3':
|
||||
s_abandon_early = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
default:
|
||||
if (0 != prog_set_opt(&prog, opt, optarg))
|
||||
exit(1);
|
||||
|
|
|
@ -278,6 +278,13 @@ static const size_t IDLE_SIZE = sizeof(on_being_idle) - 1;
|
|||
*/
|
||||
static int s_immediate_write;
|
||||
|
||||
/* Use preadv(2) in conjuction with lsquic_stream_pwritev() to reduce
|
||||
* number of system calls required to read from disk. The actual value
|
||||
* specifies maximum write size. A negative value indicates always to use
|
||||
* the remaining file size.
|
||||
*/
|
||||
static ssize_t s_pwritev;
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define V(v) (v), strlen(v)
|
||||
|
||||
|
@ -445,6 +452,7 @@ struct lsquic_stream_ctx {
|
|||
SH_HEADERS_READ = (1 << 2),
|
||||
} flags;
|
||||
struct lsquic_reader reader;
|
||||
int file_fd; /* Used by pwritev */
|
||||
|
||||
/* Fields below are used by interop callbacks: */
|
||||
enum interop_handler {
|
||||
|
@ -469,6 +477,7 @@ struct lsquic_stream_ctx {
|
|||
} interop_u;
|
||||
struct event *resume_resp;
|
||||
size_t written;
|
||||
size_t file_size; /* Used by pwritev */
|
||||
};
|
||||
|
||||
|
||||
|
@ -556,18 +565,35 @@ resume_response (evutil_socket_t fd, short what, void *arg)
|
|||
}
|
||||
|
||||
|
||||
static size_t
|
||||
bytes_left (lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
if (s_pwritev)
|
||||
return st_h->file_size - st_h->written;
|
||||
else
|
||||
return test_reader_size(st_h->reader.lsqr_ctx);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
my_preadv (void *user_data, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
lsquic_stream_ctx_t *const st_h = user_data;
|
||||
return preadv(st_h->file_fd, iov, iovcnt, st_h->written);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
if (st_h->flags & SH_HEADERS_SENT)
|
||||
{
|
||||
ssize_t nw;
|
||||
if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
|
||||
if (bytes_left(st_h) > 0)
|
||||
{
|
||||
if (st_h->server_ctx->delay_resp_sec
|
||||
&& !(st_h->flags & SH_DELAYED)
|
||||
&& st_h->written > 10000000
|
||||
&& test_reader_size(st_h->reader.lsqr_ctx) > 0)
|
||||
&& st_h->written > 10000000)
|
||||
{
|
||||
struct timeval delay = {
|
||||
.tv_sec = st_h->server_ctx->delay_resp_sec, };
|
||||
|
@ -585,7 +611,20 @@ http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
else
|
||||
LSQ_ERROR("cannot allocate event");
|
||||
}
|
||||
nw = lsquic_stream_writef(stream, &st_h->reader);
|
||||
if (s_pwritev)
|
||||
{
|
||||
size_t to_write = bytes_left(st_h);
|
||||
if (s_pwritev > 0 && (size_t) s_pwritev < to_write)
|
||||
to_write = s_pwritev;
|
||||
nw = lsquic_stream_pwritev(stream, my_preadv, st_h, to_write);
|
||||
if (nw == 0)
|
||||
goto use_reader;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_reader:
|
||||
nw = lsquic_stream_writef(stream, &st_h->reader);
|
||||
}
|
||||
if (nw < 0)
|
||||
{
|
||||
struct lsquic_conn *conn = lsquic_stream_conn(stream);
|
||||
|
@ -601,7 +640,7 @@ http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
exit(1);
|
||||
}
|
||||
}
|
||||
if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
|
||||
if (bytes_left(st_h) > 0)
|
||||
{
|
||||
st_h->written += (size_t) nw;
|
||||
lsquic_stream_wantwrite(stream, 1);
|
||||
|
@ -702,11 +741,32 @@ parse_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
|
|||
static void
|
||||
process_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
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->req_path);
|
||||
if (!st_h->reader.lsqr_ctx)
|
||||
exit(1);
|
||||
struct stat st;
|
||||
|
||||
if (s_pwritev)
|
||||
{
|
||||
st_h->file_fd = open(st_h->req_path, O_RDONLY);
|
||||
if (st_h->file_fd < 0)
|
||||
{
|
||||
LSQ_ERROR("cannot open %s for reading: %s", st_h->req_path,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (fstat(st_h->file_fd, &st) < 0)
|
||||
{
|
||||
LSQ_ERROR("fstat: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
st_h->file_size = st.st_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
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->req_path);
|
||||
if (!st_h->reader.lsqr_ctx)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (s_immediate_write)
|
||||
{
|
||||
|
@ -1369,6 +1429,24 @@ new_req (enum method method, const char *path, const char *authority)
|
|||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct gen_file_ctx *const gfc = user_data;
|
||||
size_t nread, nr;
|
||||
int i;
|
||||
|
||||
nread = 0;
|
||||
for (i = 0; i < iovcnt; ++i)
|
||||
{
|
||||
nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len);
|
||||
nread += nr;
|
||||
}
|
||||
|
||||
return (ssize_t) nread;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
||||
{
|
||||
|
@ -1379,16 +1457,25 @@ idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
struct req *req;
|
||||
ssize_t nw;
|
||||
struct header_buf hbuf;
|
||||
struct lsquic_reader reader;
|
||||
|
||||
if (st_h->flags & SH_HEADERS_SENT)
|
||||
{
|
||||
struct lsquic_reader reader =
|
||||
if (s_pwritev)
|
||||
{
|
||||
.lsqr_read = idle_read,
|
||||
.lsqr_size = idle_size,
|
||||
.lsqr_ctx = gfc,
|
||||
};
|
||||
nw = lsquic_stream_writef(stream, &reader);
|
||||
nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc,
|
||||
gfc->remain);
|
||||
if (nw == 0)
|
||||
goto with_reader;
|
||||
}
|
||||
else
|
||||
{
|
||||
with_reader:
|
||||
reader.lsqr_read = idle_read,
|
||||
reader.lsqr_size = idle_size,
|
||||
reader.lsqr_ctx = gfc,
|
||||
nw = lsquic_stream_writef(stream, &reader);
|
||||
}
|
||||
if (nw < 0)
|
||||
{
|
||||
LSQ_ERROR("error writing idle thoughts: %s", strerror(errno));
|
||||
|
@ -1507,6 +1594,10 @@ usage (const char *prog)
|
|||
" -p FILE Push request with this path\n"
|
||||
" -w SIZE Write immediately (LSWS mode). Argument specifies maximum\n"
|
||||
" size of the immediate write.\n"
|
||||
" -P SIZE Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n"
|
||||
" write to stream. Positive SIZE indicate maximum value per\n"
|
||||
" write; negative means always use remaining file size.\n"
|
||||
" Incompatible with -w.\n"
|
||||
" -y DELAY Delay response for this many seconds -- use for debugging\n"
|
||||
, prog);
|
||||
}
|
||||
|
@ -1680,7 +1771,7 @@ main (int argc, char **argv)
|
|||
prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
|
||||
&http_server_if, &server_ctx);
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:h")))
|
||||
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h")))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
|
@ -1707,6 +1798,9 @@ main (int argc, char **argv)
|
|||
case 'w':
|
||||
s_immediate_write = atoi(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
s_pwritev = strtoull(optarg, NULL, 10);
|
||||
break;
|
||||
case 'y':
|
||||
server_ctx.delay_resp_sec = atoi(optarg);
|
||||
break;
|
||||
|
@ -1734,6 +1828,12 @@ main (int argc, char **argv)
|
|||
#endif
|
||||
}
|
||||
|
||||
if (s_immediate_write && s_pwritev)
|
||||
{
|
||||
LSQ_ERROR("-w and -P are incompatible options");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (0 != prog_prep(&prog))
|
||||
{
|
||||
LSQ_ERROR("could not prep");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue