Skip to content

Commit 5650ee6

Browse files
author
Dmitri Tikhonov
committed
Release 2.27.6
- [BUGFIX] Replace dispatch read/write events assertion with a check. - [BUGFIX] gQUIC connection close: there is no HEADERS stream without HTTP flag, see issue #220. - http_client, http_server: add hq protocol support and other flags for use with QUIC Interop Runner. - Fix: use IP_PMTUDISC_PROBE (not IP_PMTUDISC_DO) on Linux to set Don't-Fragment flag on outgoing packets. - Fix send_packets_one_by_one on Windows platform when sending multiple iovs, see issue #218. - Exit echo_client on Windows immediately, see issue #219.
1 parent 65c5d50 commit 5650ee6

File tree

11 files changed

+274
-14
lines changed

11 files changed

+274
-14
lines changed

CHANGELOG

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
2021-01-27
2+
- 2.27.6
3+
- [BUGFIX] Replace dispatch read/write events assertion with a check.
4+
- [BUGFIX] gQUIC connection close: there is no HEADERS stream without
5+
HTTP flag, see issue #220.
6+
- http_client, http_server: add hq protocol support and other flags
7+
for use with QUIC Interop Runner.
8+
- Fix: use IP_PMTUDISC_PROBE (not IP_PMTUDISC_DO) on Linux to set
9+
Don't-Fragment flag on outgoing packets.
10+
- Fix send_packets_one_by_one on Windows platform when sending
11+
multiple iovs, see issue #218.
12+
- Exit echo_client on Windows immediately, see issue #219.
13+
114
2021-01-18
215
- 2.27.5
316
- [BUGFIX] Assertion in send controller when path validation fails.

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ to the LiteSpeed QUIC and HTTP/3 Library:
1414
- Aaron France -- Shared library support and Lisp bindings
1515
- Suma Subbarao -- Use callback to supply client's SSL_CTX
1616
- Paul Sheer -- Callback on arrival of CONNECTION_CLOSE frame
17+
- Weiwei Wang -- Some fixes on Windows
1718

1819
Thank you!
1920

bin/echo_client.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ main (int argc, char **argv)
208208
struct prog prog;
209209
struct echo_client_ctx client_ctx;
210210

211+
#ifdef WIN32
212+
fprintf(stderr, "%s does not work on Windows, see\n"
213+
"https://github.com/litespeedtech/lsquic/issues/219\n", argv[0]);
214+
exit(EXIT_FAILURE);
215+
#endif
216+
211217
memset(&client_ctx, 0, sizeof(client_ctx));
212218
client_ctx.prog = &prog;
213219

bin/http_client.c

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ struct http_client_ctx {
190190
unsigned hcc_n_open_conns;
191191
unsigned hcc_reset_after_nbytes;
192192
unsigned hcc_retire_cid_after_nbytes;
193+
const char *hcc_download_dir;
193194

194195
char *hcc_sess_resume_file_name;
195196

@@ -487,6 +488,7 @@ struct lsquic_stream_ctx {
487488
* lsquic_stream_read* functions.
488489
*/
489490
unsigned count;
491+
FILE *download_fh;
490492
struct lsquic_reader reader;
491493
};
492494

@@ -552,6 +554,23 @@ http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
552554
st_h->sh_flags |= ABANDON;
553555
}
554556

557+
if (st_h->client_ctx->hcc_download_dir)
558+
{
559+
char path[PATH_MAX];
560+
snprintf(path, sizeof(path), "%s/%s",
561+
st_h->client_ctx->hcc_download_dir, st_h->path);
562+
st_h->download_fh = fopen(path, "wb");
563+
if (st_h->download_fh)
564+
LSQ_NOTICE("downloading %s to %s", st_h->path, path);
565+
else
566+
{
567+
LSQ_ERROR("cannot open %s for writing: %s", path, strerror(errno));
568+
lsquic_stream_close(stream);
569+
}
570+
}
571+
else
572+
st_h->download_fh = NULL;
573+
555574
return st_h;
556575
}
557576

@@ -785,7 +804,8 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
785804
st_h->sh_flags |= PROCESSED_HEADERS;
786805
}
787806
if (!s_discard_response)
788-
fwrite(buf, 1, nread, stdout);
807+
fwrite(buf, 1, nread, st_h->download_fh
808+
? st_h->download_fh : stdout);
789809
if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
790810
{
791811
if ((1 << lsquic_conn_quic_version(lsquic_stream_conn(stream)))
@@ -887,6 +907,8 @@ http_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
887907
}
888908
if (st_h->reader.lsqr_ctx)
889909
destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
910+
if (st_h->download_fh)
911+
fclose(st_h->download_fh);
890912
free(st_h);
891913
}
892914

@@ -902,6 +924,65 @@ static struct lsquic_stream_if http_client_if = {
902924
};
903925

904926

927+
/* XXX This function assumes we can send the request in one shot. This is
928+
* not a realistic assumption to make in general, but will work for our
929+
* limited use case (QUIC Interop Runner).
930+
*/
931+
static void
932+
hq_client_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
933+
{
934+
if (st_h->client_ctx->payload)
935+
{
936+
LSQ_ERROR("payload is not supported in HQ client");
937+
lsquic_stream_close(stream);
938+
return;
939+
}
940+
941+
lsquic_stream_write(stream, "GET ", 4);
942+
lsquic_stream_write(stream, st_h->path, strlen(st_h->path));
943+
lsquic_stream_write(stream, "\r\n", 2);
944+
lsquic_stream_shutdown(stream, 1);
945+
lsquic_stream_wantread(stream, 1);
946+
}
947+
948+
949+
static size_t
950+
hq_client_print_to_file (void *user_data, const unsigned char *buf,
951+
size_t buf_len, int fin_unused)
952+
{
953+
fwrite(buf, 1, buf_len, user_data);
954+
return buf_len;
955+
}
956+
957+
958+
static void
959+
hq_client_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
960+
{
961+
FILE *out = st_h->download_fh ? st_h->download_fh : stdout;
962+
ssize_t nread;
963+
964+
nread = lsquic_stream_readf(stream, hq_client_print_to_file, out);
965+
if (nread <= 0)
966+
{
967+
if (nread < 0)
968+
LSQ_WARN("error reading response for %s: %s", st_h->path,
969+
strerror(errno));
970+
lsquic_stream_close(stream);
971+
}
972+
}
973+
974+
975+
/* The "hq" set of callbacks differs only in the read and write routines */
976+
static struct lsquic_stream_if hq_client_if = {
977+
.on_new_conn = http_client_on_new_conn,
978+
.on_conn_closed = http_client_on_conn_closed,
979+
.on_new_stream = http_client_on_new_stream,
980+
.on_read = hq_client_on_read,
981+
.on_write = hq_client_on_write,
982+
.on_close = http_client_on_close,
983+
};
984+
985+
905986
static void
906987
usage (const char *prog)
907988
{
@@ -947,6 +1028,8 @@ usage (const char *prog)
9471028
" -9 SPEC Priority specification. May be specified several times.\n"
9481029
" SPEC takes the form stream_id:nread:UI, where U is\n"
9491030
" urgency and I is incremental. Matched \\d+:\\d+:[0-7][01]\n"
1031+
" -7 DIR Save fetched resources into this directory.\n"
1032+
" -Q ALPN Use hq ALPN. Specify, for example, \"h3-29\".\n"
9501033
, prog);
9511034
}
9521035

@@ -1535,6 +1618,8 @@ main (int argc, char **argv)
15351618
"46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
15361619
"3:" /* 3 is 133+ for "e" ("e" for "early") */
15371620
"9:" /* 9 sort of looks like P... */
1621+
"7:" /* Download directory */
1622+
"Q:" /* ALPN, e.g. h3-29 */
15381623
#ifndef WIN32
15391624
"C:"
15401625
#endif
@@ -1694,6 +1779,15 @@ main (int argc, char **argv)
16941779
s_priority_specs = priority_specs;
16951780
break;
16961781
}
1782+
case '7':
1783+
client_ctx.hcc_download_dir = optarg;
1784+
break;
1785+
case 'Q':
1786+
/* XXX A bit hacky, as `prog' has already been initialized... */
1787+
prog.prog_engine_flags &= ~LSENG_HTTP;
1788+
prog.prog_api.ea_alpn = optarg;
1789+
prog.prog_api.ea_stream_if = &hq_client_if;
1790+
break;
16971791
default:
16981792
if (0 != prog_set_opt(&prog, opt, optarg))
16991793
exit(1);

bin/http_server.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,116 @@ const struct lsquic_stream_if http_server_if = {
10661066
};
10671067

10681068

1069+
/* XXX Assume we can always read the request in one shot. This is not a
1070+
* good assumption to make in a real product.
1071+
*/
1072+
static void
1073+
hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1074+
{
1075+
char buf[0x400];
1076+
ssize_t nread;
1077+
char *path, *end, *filename;
1078+
1079+
nread = lsquic_stream_read(stream, buf, sizeof(buf));
1080+
if (nread >= (ssize_t) sizeof(buf))
1081+
{
1082+
LSQ_WARN("request too large, at least %zd bytes", sizeof(buf));
1083+
lsquic_stream_close(stream);
1084+
return;
1085+
}
1086+
else if (nread < 0)
1087+
{
1088+
LSQ_WARN("error reading request from stream: %s", strerror(errno));
1089+
lsquic_stream_close(stream);
1090+
return;
1091+
}
1092+
buf[nread] = '\0';
1093+
path = strchr(buf, ' ');
1094+
if (!path)
1095+
{
1096+
LSQ_WARN("invalid request (no space character): `%s'", buf);
1097+
lsquic_stream_close(stream);
1098+
return;
1099+
}
1100+
if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3)))
1101+
{
1102+
LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf);
1103+
lsquic_stream_close(stream);
1104+
return;
1105+
}
1106+
++path;
1107+
for (end = path + nread - 5; end > path
1108+
&& (*end == '\r' || *end == '\n'); --end)
1109+
*end = '\0';
1110+
LSQ_NOTICE("parsed out request path: %s", path);
1111+
1112+
filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1);
1113+
strcpy(filename, st_h->server_ctx->document_root);
1114+
strcat(filename, "/");
1115+
strcat(filename, path);
1116+
LSQ_NOTICE("file to fetch: %s", filename);
1117+
/* XXX This copy pasta is getting a bit annoying now: two mallocs of the
1118+
* same thing?
1119+
*/
1120+
st_h->req_filename = filename;
1121+
st_h->req_path = strdup(filename);
1122+
st_h->reader.lsqr_read = test_reader_read;
1123+
st_h->reader.lsqr_size = test_reader_size;
1124+
st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
1125+
if (!st_h->reader.lsqr_ctx)
1126+
{
1127+
lsquic_stream_close(stream);
1128+
return;
1129+
}
1130+
lsquic_stream_shutdown(stream, 0);
1131+
lsquic_stream_wantwrite(stream, 1);
1132+
}
1133+
1134+
1135+
static void
1136+
hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1137+
{
1138+
ssize_t nw;
1139+
1140+
nw = lsquic_stream_writef(stream, &st_h->reader);
1141+
if (nw < 0)
1142+
{
1143+
struct lsquic_conn *conn = lsquic_stream_conn(stream);
1144+
lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
1145+
if (conn_h->flags & RECEIVED_GOAWAY)
1146+
{
1147+
LSQ_NOTICE("cannot write: goaway received");
1148+
lsquic_stream_close(stream);
1149+
}
1150+
else
1151+
{
1152+
LSQ_ERROR("write error: %s", strerror(errno));
1153+
lsquic_stream_close(stream);
1154+
}
1155+
}
1156+
else if (bytes_left(st_h) > 0)
1157+
{
1158+
st_h->written += (size_t) nw;
1159+
lsquic_stream_wantwrite(stream, 1);
1160+
}
1161+
else
1162+
{
1163+
lsquic_stream_shutdown(stream, 1);
1164+
lsquic_stream_wantread(stream, 1);
1165+
}
1166+
}
1167+
1168+
1169+
const struct lsquic_stream_if hq_server_if = {
1170+
.on_new_conn = http_server_on_new_conn,
1171+
.on_conn_closed = http_server_on_conn_closed,
1172+
.on_new_stream = http_server_on_new_stream,
1173+
.on_read = hq_server_on_read,
1174+
.on_write = hq_server_on_write,
1175+
.on_close = http_server_on_close,
1176+
};
1177+
1178+
10691179
#if HAVE_REGEX
10701180
struct req_map
10711181
{
@@ -1655,6 +1765,7 @@ usage (const char *prog)
16551765
" Incompatible with -w.\n"
16561766
#endif
16571767
" -y DELAY Delay response for this many seconds -- use for debugging\n"
1768+
" -Q ALPN Use hq mode; ALPN could be \"hq-29\", for example.\n"
16581769
, prog);
16591770
}
16601771

@@ -1811,7 +1922,7 @@ main (int argc, char **argv)
18111922
prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
18121923
&http_server_if, &server_ctx);
18131924

1814-
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h")))
1925+
while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:hQ:")))
18151926
{
18161927
switch (opt) {
18171928
case 'n':
@@ -1854,6 +1965,12 @@ main (int argc, char **argv)
18541965
usage(argv[0]);
18551966
prog_print_common_options(&prog, stdout);
18561967
exit(0);
1968+
case 'Q':
1969+
/* XXX A bit hacky, as `prog' has already been initialized... */
1970+
prog.prog_engine_flags &= ~LSENG_HTTP;
1971+
prog.prog_api.ea_stream_if = &hq_server_if;
1972+
add_alpn(optarg);
1973+
break;
18571974
default:
18581975
if (0 != prog_set_opt(&prog, opt, optarg))
18591976
exit(1);

bin/test_common.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
947947
if (AF_INET == sa_local->sa_family)
948948
{
949949
#if __linux__
950-
on = IP_PMTUDISC_DO;
950+
on = IP_PMTUDISC_PROBE;
951951
s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
952952
sizeof(on));
953953
#else
@@ -1136,7 +1136,7 @@ sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
11361136
{
11371137
int on;
11381138
#if __linux__
1139-
on = IP_PMTUDISC_DO;
1139+
on = IP_PMTUDISC_PROBE;
11401140
s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
11411141
sizeof(on));
11421142
#elif WIN32

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
# The short X.Y version
2727
version = u'2.27'
2828
# The full version, including alpha/beta/rc tags
29-
release = u'2.27.5'
29+
release = u'2.27.6'
3030

3131

3232
# -- General configuration ---------------------------------------------------

include/lsquic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extern "C" {
2525

2626
#define LSQUIC_MAJOR_VERSION 2
2727
#define LSQUIC_MINOR_VERSION 27
28-
#define LSQUIC_PATCH_VERSION 5
28+
#define LSQUIC_PATCH_VERSION 6
2929

3030
/**
3131
* Engine flags:

src/liblsquic/lsquic_full_conn.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2615,10 +2615,12 @@ maybe_close_conn (struct full_conn *conn)
26152615
struct lsquic_stream *stream;
26162616
struct lsquic_hash_elem *el;
26172617
#endif
2618+
const unsigned n_special_streams = N_SPECIAL_STREAMS
2619+
- !(conn->fc_flags & FC_HTTP);
26182620

26192621
if ((conn->fc_flags & (FC_CLOSING|FC_GOAWAY_SENT|FC_SERVER))
26202622
== (FC_GOAWAY_SENT|FC_SERVER)
2621-
&& lsquic_hash_count(conn->fc_pub.all_streams) == N_SPECIAL_STREAMS)
2623+
&& lsquic_hash_count(conn->fc_pub.all_streams) == n_special_streams)
26222624
{
26232625
#ifndef NDEBUG
26242626
for (el = lsquic_hash_first(conn->fc_pub.all_streams); el;

0 commit comments

Comments
 (0)