mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									65c5d50287
								
							
						
					
					
						commit
						5650ee6cd6
					
				
					 11 changed files with 274 additions and 14 deletions
				
			
		| 
						 | 
				
			
			@ -208,6 +208,12 @@ main (int argc, char **argv)
 | 
			
		|||
    struct prog prog;
 | 
			
		||||
    struct echo_client_ctx client_ctx;
 | 
			
		||||
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
    fprintf(stderr, "%s does not work on Windows, see\n"
 | 
			
		||||
        "https://github.com/litespeedtech/lsquic/issues/219\n", argv[0]);
 | 
			
		||||
    exit(EXIT_FAILURE);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memset(&client_ctx, 0, sizeof(client_ctx));
 | 
			
		||||
    client_ctx.prog = &prog;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,6 +190,7 @@ struct http_client_ctx {
 | 
			
		|||
    unsigned                     hcc_n_open_conns;
 | 
			
		||||
    unsigned                     hcc_reset_after_nbytes;
 | 
			
		||||
    unsigned                     hcc_retire_cid_after_nbytes;
 | 
			
		||||
    const char                  *hcc_download_dir;
 | 
			
		||||
    
 | 
			
		||||
    char                        *hcc_sess_resume_file_name;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -487,6 +488,7 @@ struct lsquic_stream_ctx {
 | 
			
		|||
                                     * lsquic_stream_read* functions.
 | 
			
		||||
                                     */
 | 
			
		||||
    unsigned             count;
 | 
			
		||||
    FILE                *download_fh;
 | 
			
		||||
    struct lsquic_reader reader;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -552,6 +554,23 @@ http_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
 | 
			
		|||
        st_h->sh_flags |= ABANDON;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (st_h->client_ctx->hcc_download_dir)
 | 
			
		||||
    {
 | 
			
		||||
        char path[PATH_MAX];
 | 
			
		||||
        snprintf(path, sizeof(path), "%s/%s",
 | 
			
		||||
                            st_h->client_ctx->hcc_download_dir, st_h->path);
 | 
			
		||||
        st_h->download_fh = fopen(path, "wb");
 | 
			
		||||
        if (st_h->download_fh)
 | 
			
		||||
            LSQ_NOTICE("downloading %s to %s", st_h->path, path);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("cannot open %s for writing: %s", path, strerror(errno));
 | 
			
		||||
            lsquic_stream_close(stream);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        st_h->download_fh = NULL;
 | 
			
		||||
 | 
			
		||||
    return st_h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -785,7 +804,8 @@ http_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		|||
                st_h->sh_flags |= PROCESSED_HEADERS;
 | 
			
		||||
            }
 | 
			
		||||
            if (!s_discard_response)
 | 
			
		||||
                fwrite(buf, 1, nread, stdout);
 | 
			
		||||
                fwrite(buf, 1, nread, st_h->download_fh
 | 
			
		||||
                                    ? st_h->download_fh : stdout);
 | 
			
		||||
            if (randomly_reprioritize_streams && (st_h->count++ & 0x3F) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                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)
 | 
			
		|||
    }
 | 
			
		||||
    if (st_h->reader.lsqr_ctx)
 | 
			
		||||
        destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
 | 
			
		||||
    if (st_h->download_fh)
 | 
			
		||||
        fclose(st_h->download_fh);
 | 
			
		||||
    free(st_h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -902,6 +924,65 @@ static struct lsquic_stream_if http_client_if = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* XXX This function assumes we can send the request in one shot.  This is
 | 
			
		||||
 * not a realistic assumption to make in general, but will work for our
 | 
			
		||||
 * limited use case (QUIC Interop Runner).
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
hq_client_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    if (st_h->client_ctx->payload)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_ERROR("payload is not supported in HQ client");
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lsquic_stream_write(stream, "GET ", 4);
 | 
			
		||||
    lsquic_stream_write(stream, st_h->path, strlen(st_h->path));
 | 
			
		||||
    lsquic_stream_write(stream, "\r\n", 2);
 | 
			
		||||
    lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
    lsquic_stream_wantread(stream, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static size_t
 | 
			
		||||
hq_client_print_to_file (void *user_data, const unsigned char *buf,
 | 
			
		||||
                                                size_t buf_len, int fin_unused)
 | 
			
		||||
{
 | 
			
		||||
    fwrite(buf, 1, buf_len, user_data);
 | 
			
		||||
    return buf_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
hq_client_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    FILE *out = st_h->download_fh ? st_h->download_fh : stdout;
 | 
			
		||||
    ssize_t nread;
 | 
			
		||||
 | 
			
		||||
    nread = lsquic_stream_readf(stream, hq_client_print_to_file, out);
 | 
			
		||||
    if (nread <= 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (nread < 0)
 | 
			
		||||
            LSQ_WARN("error reading response for %s: %s", st_h->path,
 | 
			
		||||
                                                        strerror(errno));
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The "hq" set of callbacks differs only in the read and write routines */
 | 
			
		||||
static struct lsquic_stream_if hq_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                = hq_client_on_read,
 | 
			
		||||
    .on_write               = hq_client_on_write,
 | 
			
		||||
    .on_close               = http_client_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
usage (const char *prog)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -947,6 +1028,8 @@ usage (const char *prog)
 | 
			
		|||
"   -9 SPEC     Priority specification.  May be specified several times.\n"
 | 
			
		||||
"                 SPEC takes the form stream_id:nread:UI, where U is\n"
 | 
			
		||||
"                 urgency and I is incremental.  Matched \\d+:\\d+:[0-7][01]\n"
 | 
			
		||||
"   -7 DIR      Save fetched resources into this directory.\n"
 | 
			
		||||
"   -Q ALPN     Use hq ALPN.  Specify, for example, \"h3-29\".\n"
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1535,6 +1618,8 @@ main (int argc, char **argv)
 | 
			
		|||
                                    "46Br:R:IKu:EP:M:n:w:H:p:0:q:e:hatT:b:d:"
 | 
			
		||||
                            "3:"    /* 3 is 133+ for "e" ("e" for "early") */
 | 
			
		||||
                            "9:"    /* 9 sort of looks like P... */
 | 
			
		||||
                            "7:"    /* Download directory */
 | 
			
		||||
                            "Q:"    /* ALPN, e.g. h3-29 */
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
                                                                      "C:"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1694,6 +1779,15 @@ main (int argc, char **argv)
 | 
			
		|||
            s_priority_specs = priority_specs;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case '7':
 | 
			
		||||
            client_ctx.hcc_download_dir = optarg;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'Q':
 | 
			
		||||
            /* XXX A bit hacky, as `prog' has already been initialized... */
 | 
			
		||||
            prog.prog_engine_flags &= ~LSENG_HTTP;
 | 
			
		||||
            prog.prog_api.ea_alpn      = optarg;
 | 
			
		||||
            prog.prog_api.ea_stream_if = &hq_client_if;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1066,6 +1066,116 @@ const struct lsquic_stream_if http_server_if = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* XXX Assume we can always read the request in one shot.  This is not a
 | 
			
		||||
 * good assumption to make in a real product.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    char buf[0x400];
 | 
			
		||||
    ssize_t nread;
 | 
			
		||||
    char *path, *end, *filename;
 | 
			
		||||
 | 
			
		||||
    nread = lsquic_stream_read(stream, buf, sizeof(buf));
 | 
			
		||||
    if (nread >= (ssize_t) sizeof(buf))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("request too large, at least %zd bytes", sizeof(buf));
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    else if (nread < 0)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("error reading request from stream: %s", strerror(errno));
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    buf[nread] = '\0';
 | 
			
		||||
    path = strchr(buf, ' ');
 | 
			
		||||
    if (!path)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("invalid request (no space character): `%s'", buf);
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3)))
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf);
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    ++path;
 | 
			
		||||
    for (end = path + nread - 5; end > path
 | 
			
		||||
                                    && (*end == '\r' || *end == '\n'); --end)
 | 
			
		||||
        *end = '\0';
 | 
			
		||||
    LSQ_NOTICE("parsed out request path: %s", path);
 | 
			
		||||
 | 
			
		||||
    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1);
 | 
			
		||||
    strcpy(filename, st_h->server_ctx->document_root);
 | 
			
		||||
    strcat(filename, "/");
 | 
			
		||||
    strcat(filename, path);
 | 
			
		||||
    LSQ_NOTICE("file to fetch: %s", filename);
 | 
			
		||||
    /* XXX This copy pasta is getting a bit annoying now: two mallocs of the
 | 
			
		||||
     * same thing?
 | 
			
		||||
     */
 | 
			
		||||
    st_h->req_filename = filename;
 | 
			
		||||
    st_h->req_path = strdup(filename);
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_close(stream);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    lsquic_stream_shutdown(stream, 0);
 | 
			
		||||
    lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nw;
 | 
			
		||||
 | 
			
		||||
    nw = lsquic_stream_writef(stream, &st_h->reader);
 | 
			
		||||
    if (nw < 0)
 | 
			
		||||
    {
 | 
			
		||||
        struct lsquic_conn *conn = lsquic_stream_conn(stream);
 | 
			
		||||
        lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
 | 
			
		||||
        if (conn_h->flags & RECEIVED_GOAWAY)
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_NOTICE("cannot write: goaway received");
 | 
			
		||||
            lsquic_stream_close(stream);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            LSQ_ERROR("write error: %s", strerror(errno));
 | 
			
		||||
            lsquic_stream_close(stream);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (bytes_left(st_h) > 0)
 | 
			
		||||
    {
 | 
			
		||||
        st_h->written += (size_t) nw;
 | 
			
		||||
        lsquic_stream_wantwrite(stream, 1);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_stream_shutdown(stream, 1);
 | 
			
		||||
        lsquic_stream_wantread(stream, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct lsquic_stream_if hq_server_if = {
 | 
			
		||||
    .on_new_conn            = http_server_on_new_conn,
 | 
			
		||||
    .on_conn_closed         = http_server_on_conn_closed,
 | 
			
		||||
    .on_new_stream          = http_server_on_new_stream,
 | 
			
		||||
    .on_read                = hq_server_on_read,
 | 
			
		||||
    .on_write               = hq_server_on_write,
 | 
			
		||||
    .on_close               = http_server_on_close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if HAVE_REGEX
 | 
			
		||||
struct req_map
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1655,6 +1765,7 @@ usage (const char *prog)
 | 
			
		|||
"                 Incompatible with -w.\n"
 | 
			
		||||
#endif
 | 
			
		||||
"   -y DELAY    Delay response for this many seconds -- use for debugging\n"
 | 
			
		||||
"   -Q ALPN     Use hq mode; ALPN could be \"hq-29\", for example.\n"
 | 
			
		||||
            , prog);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1811,7 +1922,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:P:h")))
 | 
			
		||||
    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:hQ:")))
 | 
			
		||||
    {
 | 
			
		||||
        switch (opt) {
 | 
			
		||||
        case 'n':
 | 
			
		||||
| 
						 | 
				
			
			@ -1854,6 +1965,12 @@ main (int argc, char **argv)
 | 
			
		|||
            usage(argv[0]);
 | 
			
		||||
            prog_print_common_options(&prog, stdout);
 | 
			
		||||
            exit(0);
 | 
			
		||||
        case 'Q':
 | 
			
		||||
            /* XXX A bit hacky, as `prog' has already been initialized... */
 | 
			
		||||
            prog.prog_engine_flags &= ~LSENG_HTTP;
 | 
			
		||||
            prog.prog_api.ea_stream_if = &hq_server_if;
 | 
			
		||||
            add_alpn(optarg);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            if (0 != prog_set_opt(&prog, opt, optarg))
 | 
			
		||||
                exit(1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -947,7 +947,7 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
 | 
			
		|||
        if (AF_INET == sa_local->sa_family)
 | 
			
		||||
        {
 | 
			
		||||
#if __linux__
 | 
			
		||||
            on = IP_PMTUDISC_DO;
 | 
			
		||||
            on = IP_PMTUDISC_PROBE;
 | 
			
		||||
            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
 | 
			
		||||
                                                                sizeof(on));
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -1136,7 +1136,7 @@ sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
 | 
			
		|||
        {
 | 
			
		||||
        int on;
 | 
			
		||||
#if __linux__
 | 
			
		||||
            on = IP_PMTUDISC_DO;
 | 
			
		||||
            on = IP_PMTUDISC_PROBE;
 | 
			
		||||
            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
 | 
			
		||||
                                                                sizeof(on));
 | 
			
		||||
#elif WIN32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue