1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
20adf085aSDmitri Tikhonov/*
30adf085aSDmitri Tikhonov * echo_client.c -- This is really a "line client:" it connects to QUIC server
40adf085aSDmitri Tikhonov * and sends it stuff, line by line.  It works in tandem with echo_server.
50adf085aSDmitri Tikhonov */
60adf085aSDmitri Tikhonov
70adf085aSDmitri Tikhonov#include <assert.h>
80adf085aSDmitri Tikhonov#include <errno.h>
90adf085aSDmitri Tikhonov#include <stdio.h>
100adf085aSDmitri Tikhonov#include <stdlib.h>
110adf085aSDmitri Tikhonov#include <string.h>
120adf085aSDmitri Tikhonov#include <sys/queue.h>
130adf085aSDmitri Tikhonov#include <sys/types.h>
140adf085aSDmitri Tikhonov#include <sys/stat.h>
15fb3e20e0SDmitri Tikhonov
16fb3e20e0SDmitri Tikhonov#ifndef WIN32
170adf085aSDmitri Tikhonov#include <fcntl.h>
18fb3e20e0SDmitri Tikhonov#include <unistd.h>
19fb3e20e0SDmitri Tikhonov#define Read read
20fb3e20e0SDmitri Tikhonov#else
21fb3e20e0SDmitri Tikhonov#include "vc_compat.h"
22fb3e20e0SDmitri Tikhonov#include "getopt.h"
23fb3e20e0SDmitri Tikhonov#include <io.h>
24fb3e20e0SDmitri Tikhonov#define Read _read
25fb3e20e0SDmitri Tikhonov#define STDIN_FILENO 0
26fb3e20e0SDmitri Tikhonov#endif
270adf085aSDmitri Tikhonov
280adf085aSDmitri Tikhonov#include <event2/event.h>
290adf085aSDmitri Tikhonov
300adf085aSDmitri Tikhonov#include "lsquic.h"
310adf085aSDmitri Tikhonov#include "test_common.h"
320adf085aSDmitri Tikhonov#include "prog.h"
330adf085aSDmitri Tikhonov
340adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_logger.h"
350adf085aSDmitri Tikhonov
360adf085aSDmitri Tikhonovstruct lsquic_conn_ctx;
370adf085aSDmitri Tikhonov
380adf085aSDmitri Tikhonovstruct echo_client_ctx {
390adf085aSDmitri Tikhonov    struct lsquic_conn_ctx  *conn_h;
400adf085aSDmitri Tikhonov    struct prog                 *prog;
410adf085aSDmitri Tikhonov};
420adf085aSDmitri Tikhonov
430adf085aSDmitri Tikhonovstruct lsquic_conn_ctx {
440adf085aSDmitri Tikhonov    lsquic_conn_t       *conn;
450adf085aSDmitri Tikhonov    struct echo_client_ctx   *client_ctx;
460adf085aSDmitri Tikhonov};
470adf085aSDmitri Tikhonov
480adf085aSDmitri Tikhonov
490adf085aSDmitri Tikhonovstatic lsquic_conn_ctx_t *
500adf085aSDmitri Tikhonovecho_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
510adf085aSDmitri Tikhonov{
520adf085aSDmitri Tikhonov    struct echo_client_ctx *client_ctx = stream_if_ctx;
530adf085aSDmitri Tikhonov    lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
540adf085aSDmitri Tikhonov    conn_h->conn = conn;
550adf085aSDmitri Tikhonov    conn_h->client_ctx = client_ctx;
560adf085aSDmitri Tikhonov    client_ctx->conn_h = conn_h;
570adf085aSDmitri Tikhonov    lsquic_conn_make_stream(conn);
580adf085aSDmitri Tikhonov    return conn_h;
590adf085aSDmitri Tikhonov}
600adf085aSDmitri Tikhonov
610adf085aSDmitri Tikhonov
620adf085aSDmitri Tikhonovstatic void
630adf085aSDmitri Tikhonovecho_client_on_conn_closed (lsquic_conn_t *conn)
640adf085aSDmitri Tikhonov{
650adf085aSDmitri Tikhonov    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
660adf085aSDmitri Tikhonov    LSQ_NOTICE("Connection closed");
670adf085aSDmitri Tikhonov    prog_stop(conn_h->client_ctx->prog);
680adf085aSDmitri Tikhonov    free(conn_h);
690adf085aSDmitri Tikhonov}
700adf085aSDmitri Tikhonov
710adf085aSDmitri Tikhonov
720adf085aSDmitri Tikhonovstruct lsquic_stream_ctx {
730adf085aSDmitri Tikhonov    lsquic_stream_t     *stream;
740adf085aSDmitri Tikhonov    struct echo_client_ctx   *client_ctx;
750adf085aSDmitri Tikhonov    struct event        *read_stdin_ev;
760adf085aSDmitri Tikhonov    char                 buf[0x100];
770adf085aSDmitri Tikhonov    size_t               buf_off;
780adf085aSDmitri Tikhonov};
790adf085aSDmitri Tikhonov
800adf085aSDmitri Tikhonov
810adf085aSDmitri Tikhonovstatic void
82fb3e20e0SDmitri Tikhonovread_stdin (evutil_socket_t fd, short what, void *ctx)
830adf085aSDmitri Tikhonov{
840adf085aSDmitri Tikhonov    ssize_t nr;
850adf085aSDmitri Tikhonov    lsquic_stream_ctx_t *st_h = ctx;
860adf085aSDmitri Tikhonov
87fb3e20e0SDmitri Tikhonov    nr = Read(fd, st_h->buf + st_h->buf_off++, 1);
880adf085aSDmitri Tikhonov    LSQ_DEBUG("read %zd bytes from stdin", nr);
890adf085aSDmitri Tikhonov    if (0 == nr)
900adf085aSDmitri Tikhonov    {
910adf085aSDmitri Tikhonov        lsquic_stream_shutdown(st_h->stream, 2);
920adf085aSDmitri Tikhonov    }
930adf085aSDmitri Tikhonov    else if (-1 == nr)
940adf085aSDmitri Tikhonov    {
950adf085aSDmitri Tikhonov        perror("read");
960adf085aSDmitri Tikhonov        exit(1);
970adf085aSDmitri Tikhonov    }
980adf085aSDmitri Tikhonov    else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
990adf085aSDmitri Tikhonov    {
1000adf085aSDmitri Tikhonov        LSQ_DEBUG("read newline: wantwrite");
1010adf085aSDmitri Tikhonov        lsquic_stream_wantwrite(st_h->stream, 1);
1020adf085aSDmitri Tikhonov        lsquic_engine_process_conns(st_h->client_ctx->prog->prog_engine);
1030adf085aSDmitri Tikhonov    }
1040adf085aSDmitri Tikhonov    else if (st_h->buf_off == sizeof(st_h->buf))
1050adf085aSDmitri Tikhonov    {
1060adf085aSDmitri Tikhonov        LSQ_NOTICE("line too long");
1070adf085aSDmitri Tikhonov        exit(2);
1080adf085aSDmitri Tikhonov    }
1090adf085aSDmitri Tikhonov    else
1100adf085aSDmitri Tikhonov        event_add(st_h->read_stdin_ev, NULL);
1110adf085aSDmitri Tikhonov}
1120adf085aSDmitri Tikhonov
1130adf085aSDmitri Tikhonov
1140adf085aSDmitri Tikhonovstatic lsquic_stream_ctx_t *
1150adf085aSDmitri Tikhonovecho_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
1160adf085aSDmitri Tikhonov{
1170adf085aSDmitri Tikhonov    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
1180adf085aSDmitri Tikhonov    st_h->stream = stream;
1190adf085aSDmitri Tikhonov    st_h->client_ctx = stream_if_ctx;
1200adf085aSDmitri Tikhonov    st_h->buf_off = 0;
1210adf085aSDmitri Tikhonov    st_h->read_stdin_ev = event_new(prog_eb(st_h->client_ctx->prog),
1220adf085aSDmitri Tikhonov                                    STDIN_FILENO, EV_READ, read_stdin, st_h);
1230adf085aSDmitri Tikhonov    event_add(st_h->read_stdin_ev, NULL);
1240adf085aSDmitri Tikhonov    return st_h;
1250adf085aSDmitri Tikhonov}
1260adf085aSDmitri Tikhonov
1270adf085aSDmitri Tikhonov
1280adf085aSDmitri Tikhonovstatic void
1290adf085aSDmitri Tikhonovecho_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1300adf085aSDmitri Tikhonov{
1310adf085aSDmitri Tikhonov    char c;
1320adf085aSDmitri Tikhonov    size_t nr;
1330adf085aSDmitri Tikhonov
1340adf085aSDmitri Tikhonov    nr = lsquic_stream_read(stream, &c, 1);
1350adf085aSDmitri Tikhonov    if (0 == nr)
1360adf085aSDmitri Tikhonov    {
1370adf085aSDmitri Tikhonov        lsquic_stream_shutdown(stream, 2);
1380adf085aSDmitri Tikhonov        return;
1390adf085aSDmitri Tikhonov    }
1400adf085aSDmitri Tikhonov    printf("%c", c);
1410adf085aSDmitri Tikhonov    fflush(stdout);
1420adf085aSDmitri Tikhonov    if ('\n' == c)
1430adf085aSDmitri Tikhonov    {
1440adf085aSDmitri Tikhonov        event_add(st_h->read_stdin_ev, NULL);
1450adf085aSDmitri Tikhonov        lsquic_stream_wantread(stream, 0);
1460adf085aSDmitri Tikhonov    }
1470adf085aSDmitri Tikhonov}
1480adf085aSDmitri Tikhonov
1490adf085aSDmitri Tikhonov
1500adf085aSDmitri Tikhonovstatic void
1510adf085aSDmitri Tikhonovecho_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1520adf085aSDmitri Tikhonov{
1530adf085aSDmitri Tikhonov    /* Here we make an assumption that we can write the whole buffer.
1540adf085aSDmitri Tikhonov     * Don't do it in a real program.
1550adf085aSDmitri Tikhonov     */
1560adf085aSDmitri Tikhonov    lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
1570adf085aSDmitri Tikhonov    st_h->buf_off = 0;
1580adf085aSDmitri Tikhonov
1590adf085aSDmitri Tikhonov    lsquic_stream_flush(stream);
1600adf085aSDmitri Tikhonov    lsquic_stream_wantwrite(stream, 0);
1610adf085aSDmitri Tikhonov    lsquic_stream_wantread(stream, 1);
1620adf085aSDmitri Tikhonov}
1630adf085aSDmitri Tikhonov
1640adf085aSDmitri Tikhonov
1650adf085aSDmitri Tikhonovstatic void
1660adf085aSDmitri Tikhonovecho_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1670adf085aSDmitri Tikhonov{
1680adf085aSDmitri Tikhonov    LSQ_NOTICE("%s called", __func__);
1690adf085aSDmitri Tikhonov    if (st_h->read_stdin_ev)
1700adf085aSDmitri Tikhonov    {
1710adf085aSDmitri Tikhonov        event_del(st_h->read_stdin_ev);
1720adf085aSDmitri Tikhonov        event_free(st_h->read_stdin_ev);
1730adf085aSDmitri Tikhonov    }
1740adf085aSDmitri Tikhonov    free(st_h);
1750adf085aSDmitri Tikhonov    lsquic_conn_close(lsquic_stream_conn(stream));
1760adf085aSDmitri Tikhonov}
1770adf085aSDmitri Tikhonov
1780adf085aSDmitri Tikhonov
1790adf085aSDmitri Tikhonovconst struct lsquic_stream_if client_echo_stream_if = {
1800adf085aSDmitri Tikhonov    .on_new_conn            = echo_client_on_new_conn,
1810adf085aSDmitri Tikhonov    .on_conn_closed         = echo_client_on_conn_closed,
1820adf085aSDmitri Tikhonov    .on_new_stream          = echo_client_on_new_stream,
1830adf085aSDmitri Tikhonov    .on_read                = echo_client_on_read,
1840adf085aSDmitri Tikhonov    .on_write               = echo_client_on_write,
1850adf085aSDmitri Tikhonov    .on_close               = echo_client_on_close,
1860adf085aSDmitri Tikhonov};
1870adf085aSDmitri Tikhonov
1880adf085aSDmitri Tikhonov
1890adf085aSDmitri Tikhonovstatic void
1900adf085aSDmitri Tikhonovusage (const char *prog)
1910adf085aSDmitri Tikhonov{
1920adf085aSDmitri Tikhonov    const char *const slash = strrchr(prog, '/');
1930adf085aSDmitri Tikhonov    if (slash)
1940adf085aSDmitri Tikhonov        prog = slash + 1;
1950adf085aSDmitri Tikhonov    LSQ_NOTICE(
1960adf085aSDmitri Tikhonov"Usage: %s [opts]\n"
1970adf085aSDmitri Tikhonov"\n"
1980adf085aSDmitri Tikhonov"Options:\n"
1990adf085aSDmitri Tikhonov            , prog);
2000adf085aSDmitri Tikhonov}
2010adf085aSDmitri Tikhonov
2020adf085aSDmitri Tikhonov
2030adf085aSDmitri Tikhonovint
2040adf085aSDmitri Tikhonovmain (int argc, char **argv)
2050adf085aSDmitri Tikhonov{
2060adf085aSDmitri Tikhonov    int opt, s;
2070adf085aSDmitri Tikhonov    struct sport_head sports;
2080adf085aSDmitri Tikhonov    struct prog prog;
2090adf085aSDmitri Tikhonov    struct echo_client_ctx client_ctx;
2100adf085aSDmitri Tikhonov
2115650ee6cSDmitri Tikhonov#ifdef WIN32
2125650ee6cSDmitri Tikhonov    fprintf(stderr, "%s does not work on Windows, see\n"
2135650ee6cSDmitri Tikhonov        "https://github.com/litespeedtech/lsquic/issues/219\n", argv[0]);
2145650ee6cSDmitri Tikhonov    exit(EXIT_FAILURE);
2155650ee6cSDmitri Tikhonov#endif
2165650ee6cSDmitri Tikhonov
2170adf085aSDmitri Tikhonov    memset(&client_ctx, 0, sizeof(client_ctx));
2180adf085aSDmitri Tikhonov    client_ctx.prog = &prog;
2190adf085aSDmitri Tikhonov
2200adf085aSDmitri Tikhonov    TAILQ_INIT(&sports);
2210adf085aSDmitri Tikhonov    prog_init(&prog, 0, &sports, &client_echo_stream_if, &client_ctx);
2224429f8eaSDmitri Tikhonov    prog.prog_api.ea_alpn = "echo";
2230adf085aSDmitri Tikhonov
2240adf085aSDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h")))
2250adf085aSDmitri Tikhonov    {
2260adf085aSDmitri Tikhonov        switch (opt) {
2270adf085aSDmitri Tikhonov        case 'h':
2280adf085aSDmitri Tikhonov            usage(argv[0]);
2290adf085aSDmitri Tikhonov            prog_print_common_options(&prog, stdout);
2300adf085aSDmitri Tikhonov            exit(0);
2310adf085aSDmitri Tikhonov        default:
2320adf085aSDmitri Tikhonov            if (0 != prog_set_opt(&prog, opt, optarg))
2330adf085aSDmitri Tikhonov                exit(1);
2340adf085aSDmitri Tikhonov        }
2350adf085aSDmitri Tikhonov    }
2360adf085aSDmitri Tikhonov
237fb3e20e0SDmitri Tikhonov#ifndef WIN32
2380adf085aSDmitri Tikhonov    int flags = fcntl(STDIN_FILENO, F_GETFL);
2390adf085aSDmitri Tikhonov    flags |= O_NONBLOCK;
2400adf085aSDmitri Tikhonov    if (0 != fcntl(STDIN_FILENO, F_SETFL, flags))
2410adf085aSDmitri Tikhonov    {
2420adf085aSDmitri Tikhonov        perror("fcntl");
2430adf085aSDmitri Tikhonov        exit(1);
2440adf085aSDmitri Tikhonov    }
245fb3e20e0SDmitri Tikhonov#else
246fb3e20e0SDmitri Tikhonov    {
247fb3e20e0SDmitri Tikhonov        u_long on = 1;
248fb3e20e0SDmitri Tikhonov        ioctlsocket(STDIN_FILENO, FIONBIO, &on);
249fb3e20e0SDmitri Tikhonov    }
250fb3e20e0SDmitri Tikhonov#endif
2510adf085aSDmitri Tikhonov
2520adf085aSDmitri Tikhonov    if (0 != prog_prep(&prog))
2530adf085aSDmitri Tikhonov    {
2540adf085aSDmitri Tikhonov        LSQ_ERROR("could not prep");
2550adf085aSDmitri Tikhonov        exit(EXIT_FAILURE);
2560adf085aSDmitri Tikhonov    }
2570adf085aSDmitri Tikhonov    if (0 != prog_connect(&prog, NULL, 0))
2580adf085aSDmitri Tikhonov    {
2590adf085aSDmitri Tikhonov        LSQ_ERROR("could not connect");
2600adf085aSDmitri Tikhonov        exit(EXIT_FAILURE);
2610adf085aSDmitri Tikhonov    }
2620adf085aSDmitri Tikhonov
2630adf085aSDmitri Tikhonov    LSQ_DEBUG("entering event loop");
2640adf085aSDmitri Tikhonov
2650adf085aSDmitri Tikhonov    s = prog_run(&prog);
2660adf085aSDmitri Tikhonov    prog_cleanup(&prog);
2670adf085aSDmitri Tikhonov
2680adf085aSDmitri Tikhonov    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
2690adf085aSDmitri Tikhonov}
270