1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
20adf085aSDmitri Tikhonov/*
30adf085aSDmitri Tikhonov * echo_server.c -- QUIC server that echoes back input line by line
40adf085aSDmitri Tikhonov */
50adf085aSDmitri Tikhonov
60adf085aSDmitri Tikhonov#include <assert.h>
70adf085aSDmitri Tikhonov#include <signal.h>
80adf085aSDmitri Tikhonov#include <stdio.h>
90adf085aSDmitri Tikhonov#include <stdlib.h>
100adf085aSDmitri Tikhonov#include <string.h>
110adf085aSDmitri Tikhonov#include <sys/queue.h>
120adf085aSDmitri Tikhonov#include <time.h>
13fb3e20e0SDmitri Tikhonov#ifndef WIN32
140adf085aSDmitri Tikhonov#include <unistd.h>
15fb3e20e0SDmitri Tikhonov#include <netinet/in.h>
16fb3e20e0SDmitri Tikhonov#else
17fb3e20e0SDmitri Tikhonov#include "vc_compat.h"
18fb3e20e0SDmitri Tikhonov#include "getopt.h"
19fb3e20e0SDmitri Tikhonov#endif
200adf085aSDmitri Tikhonov
210adf085aSDmitri Tikhonov#include "lsquic.h"
220adf085aSDmitri Tikhonov#include "test_common.h"
234429f8eaSDmitri Tikhonov#include "../src/liblsquic/lsquic_hash.h"
244429f8eaSDmitri Tikhonov#include "test_cert.h"
250adf085aSDmitri Tikhonov#include "prog.h"
260adf085aSDmitri Tikhonov
270adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_logger.h"
280adf085aSDmitri Tikhonov
290adf085aSDmitri Tikhonov
300adf085aSDmitri Tikhonovstruct lsquic_conn_ctx;
310adf085aSDmitri Tikhonov
320adf085aSDmitri Tikhonovstruct echo_server_ctx {
330adf085aSDmitri Tikhonov    TAILQ_HEAD(, lsquic_conn_ctx)   conn_ctxs;
340adf085aSDmitri Tikhonov    unsigned max_reqs;
350adf085aSDmitri Tikhonov    int n_conn;
360adf085aSDmitri Tikhonov    struct sport_head sports;
370adf085aSDmitri Tikhonov    struct prog *prog;
380adf085aSDmitri Tikhonov};
390adf085aSDmitri Tikhonov
400adf085aSDmitri Tikhonovstruct lsquic_conn_ctx {
410adf085aSDmitri Tikhonov    TAILQ_ENTRY(lsquic_conn_ctx)    next_connh;
420adf085aSDmitri Tikhonov    lsquic_conn_t       *conn;
430adf085aSDmitri Tikhonov    struct echo_server_ctx   *server_ctx;
440adf085aSDmitri Tikhonov};
450adf085aSDmitri Tikhonov
460adf085aSDmitri Tikhonov
470adf085aSDmitri Tikhonovstatic lsquic_conn_ctx_t *
480adf085aSDmitri Tikhonovecho_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
490adf085aSDmitri Tikhonov{
500adf085aSDmitri Tikhonov    struct echo_server_ctx *server_ctx = stream_if_ctx;
510adf085aSDmitri Tikhonov    lsquic_conn_ctx_t *conn_h = calloc(1, sizeof(*conn_h));
520adf085aSDmitri Tikhonov    conn_h->conn = conn;
530adf085aSDmitri Tikhonov    conn_h->server_ctx = server_ctx;
540adf085aSDmitri Tikhonov    TAILQ_INSERT_TAIL(&server_ctx->conn_ctxs, conn_h, next_connh);
550adf085aSDmitri Tikhonov    LSQ_NOTICE("New connection!");
560adf085aSDmitri Tikhonov    print_conn_info(conn);
570adf085aSDmitri Tikhonov    return conn_h;
580adf085aSDmitri Tikhonov}
590adf085aSDmitri Tikhonov
600adf085aSDmitri Tikhonov
610adf085aSDmitri Tikhonovstatic void
620adf085aSDmitri Tikhonovecho_server_on_conn_closed (lsquic_conn_t *conn)
630adf085aSDmitri Tikhonov{
640adf085aSDmitri Tikhonov    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
650adf085aSDmitri Tikhonov    if (conn_h->server_ctx->n_conn)
660adf085aSDmitri Tikhonov    {
670adf085aSDmitri Tikhonov        --conn_h->server_ctx->n_conn;
680adf085aSDmitri Tikhonov        LSQ_NOTICE("Connection closed, remaining: %d", conn_h->server_ctx->n_conn);
690adf085aSDmitri Tikhonov        if (0 == conn_h->server_ctx->n_conn)
700adf085aSDmitri Tikhonov            prog_stop(conn_h->server_ctx->prog);
710adf085aSDmitri Tikhonov    }
720adf085aSDmitri Tikhonov    else
730adf085aSDmitri Tikhonov        LSQ_NOTICE("Connection closed");
740adf085aSDmitri Tikhonov    TAILQ_REMOVE(&conn_h->server_ctx->conn_ctxs, conn_h, next_connh);
750adf085aSDmitri Tikhonov    free(conn_h);
760adf085aSDmitri Tikhonov}
770adf085aSDmitri Tikhonov
780adf085aSDmitri Tikhonov
790adf085aSDmitri Tikhonovstruct lsquic_stream_ctx {
800adf085aSDmitri Tikhonov    lsquic_stream_t     *stream;
810adf085aSDmitri Tikhonov    struct echo_server_ctx   *server_ctx;
820adf085aSDmitri Tikhonov    char                 buf[0x100];
830adf085aSDmitri Tikhonov    size_t               buf_off;
840adf085aSDmitri Tikhonov};
850adf085aSDmitri Tikhonov
860adf085aSDmitri Tikhonov
870adf085aSDmitri Tikhonovstatic lsquic_stream_ctx_t *
880adf085aSDmitri Tikhonovecho_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
890adf085aSDmitri Tikhonov{
900adf085aSDmitri Tikhonov    lsquic_stream_ctx_t *st_h = malloc(sizeof(*st_h));
910adf085aSDmitri Tikhonov    st_h->stream = stream;
920adf085aSDmitri Tikhonov    st_h->server_ctx = stream_if_ctx;
930adf085aSDmitri Tikhonov    st_h->buf_off = 0;
940adf085aSDmitri Tikhonov    lsquic_stream_wantread(stream, 1);
950adf085aSDmitri Tikhonov    return st_h;
960adf085aSDmitri Tikhonov}
970adf085aSDmitri Tikhonov
980adf085aSDmitri Tikhonov
990adf085aSDmitri Tikhonovstatic struct lsquic_conn_ctx *
1000adf085aSDmitri Tikhonovfind_conn_h (const struct echo_server_ctx *server_ctx, lsquic_stream_t *stream)
1010adf085aSDmitri Tikhonov{
1020adf085aSDmitri Tikhonov    struct lsquic_conn_ctx *conn_h;
1030adf085aSDmitri Tikhonov    lsquic_conn_t *conn;
1040adf085aSDmitri Tikhonov
1050adf085aSDmitri Tikhonov    conn = lsquic_stream_conn(stream);
1060adf085aSDmitri Tikhonov    TAILQ_FOREACH(conn_h, &server_ctx->conn_ctxs, next_connh)
1070adf085aSDmitri Tikhonov        if (conn_h->conn == conn)
1080adf085aSDmitri Tikhonov            return conn_h;
1090adf085aSDmitri Tikhonov    return NULL;
1100adf085aSDmitri Tikhonov}
1110adf085aSDmitri Tikhonov
1120adf085aSDmitri Tikhonov
1130adf085aSDmitri Tikhonovstatic void
1140adf085aSDmitri Tikhonovecho_server_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1150adf085aSDmitri Tikhonov{
1160adf085aSDmitri Tikhonov    struct lsquic_conn_ctx *conn_h;
1170adf085aSDmitri Tikhonov    size_t nr;
1180adf085aSDmitri Tikhonov
1190adf085aSDmitri Tikhonov    nr = lsquic_stream_read(stream, st_h->buf + st_h->buf_off++, 1);
1200adf085aSDmitri Tikhonov    if (0 == nr)
1210adf085aSDmitri Tikhonov    {
1220adf085aSDmitri Tikhonov        LSQ_NOTICE("EOF: closing connection");
1230adf085aSDmitri Tikhonov        lsquic_stream_shutdown(stream, 2);
1240adf085aSDmitri Tikhonov        conn_h = find_conn_h(st_h->server_ctx, stream);
1250adf085aSDmitri Tikhonov        lsquic_conn_close(conn_h->conn);
1260adf085aSDmitri Tikhonov    }
1270adf085aSDmitri Tikhonov    else if ('\n' == st_h->buf[ st_h->buf_off - 1 ])
1280adf085aSDmitri Tikhonov    {
1290adf085aSDmitri Tikhonov        /* Found end of line: echo it back */
1300adf085aSDmitri Tikhonov        lsquic_stream_wantwrite(stream, 1);
1310adf085aSDmitri Tikhonov        lsquic_stream_wantread(stream, 0);
1320adf085aSDmitri Tikhonov    }
1330adf085aSDmitri Tikhonov    else if (st_h->buf_off == sizeof(st_h->buf))
1340adf085aSDmitri Tikhonov    {
1350adf085aSDmitri Tikhonov        /* Out of buffer space: line too long */
1360adf085aSDmitri Tikhonov        LSQ_NOTICE("run out of buffer space");
1370adf085aSDmitri Tikhonov        lsquic_stream_shutdown(stream, 2);
1380adf085aSDmitri Tikhonov    }
1390adf085aSDmitri Tikhonov    else
1400adf085aSDmitri Tikhonov    {
1410adf085aSDmitri Tikhonov        /* Keep reading */;
1420adf085aSDmitri Tikhonov    }
1430adf085aSDmitri Tikhonov}
1440adf085aSDmitri Tikhonov
1450adf085aSDmitri Tikhonov
1460adf085aSDmitri Tikhonovstatic void
1470adf085aSDmitri Tikhonovecho_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1480adf085aSDmitri Tikhonov{
1490adf085aSDmitri Tikhonov    lsquic_stream_write(stream, st_h->buf, st_h->buf_off);
1500adf085aSDmitri Tikhonov    st_h->buf_off = 0;
1510adf085aSDmitri Tikhonov    lsquic_stream_flush(stream);
1520adf085aSDmitri Tikhonov    lsquic_stream_wantwrite(stream, 0);
1530adf085aSDmitri Tikhonov    lsquic_stream_wantread(stream, 1);
1540adf085aSDmitri Tikhonov}
1550adf085aSDmitri Tikhonov
1560adf085aSDmitri Tikhonov
1570adf085aSDmitri Tikhonovstatic void
1580adf085aSDmitri Tikhonovecho_server_on_stream_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1590adf085aSDmitri Tikhonov{
1600adf085aSDmitri Tikhonov    struct lsquic_conn_ctx *conn_h;
1610adf085aSDmitri Tikhonov    LSQ_NOTICE("%s called", __func__);
1620adf085aSDmitri Tikhonov    conn_h = find_conn_h(st_h->server_ctx, stream);
1630adf085aSDmitri Tikhonov    LSQ_WARN("%s: TODO: free connection handler %p", __func__, conn_h);
1640adf085aSDmitri Tikhonov    free(st_h);
1650adf085aSDmitri Tikhonov}
1660adf085aSDmitri Tikhonov
1670adf085aSDmitri Tikhonov
1680adf085aSDmitri Tikhonovconst struct lsquic_stream_if server_echo_stream_if = {
1690adf085aSDmitri Tikhonov    .on_new_conn            = echo_server_on_new_conn,
1700adf085aSDmitri Tikhonov    .on_conn_closed         = echo_server_on_conn_closed,
1710adf085aSDmitri Tikhonov    .on_new_stream          = echo_server_on_new_stream,
1720adf085aSDmitri Tikhonov    .on_read                = echo_server_on_read,
1730adf085aSDmitri Tikhonov    .on_write               = echo_server_on_write,
1740adf085aSDmitri Tikhonov    .on_close               = echo_server_on_stream_close,
1750adf085aSDmitri Tikhonov};
1760adf085aSDmitri Tikhonov
1770adf085aSDmitri Tikhonov
1780adf085aSDmitri Tikhonovstatic void
1790adf085aSDmitri Tikhonovusage (const char *prog)
1800adf085aSDmitri Tikhonov{
1810adf085aSDmitri Tikhonov    const char *const slash = strrchr(prog, '/');
1820adf085aSDmitri Tikhonov    if (slash)
1830adf085aSDmitri Tikhonov        prog = slash + 1;
1840adf085aSDmitri Tikhonov    printf(
1850adf085aSDmitri Tikhonov"Usage: %s [opts]\n"
1860adf085aSDmitri Tikhonov"\n"
1870adf085aSDmitri Tikhonov"Options:\n"
1880adf085aSDmitri Tikhonov                , prog);
1890adf085aSDmitri Tikhonov}
1900adf085aSDmitri Tikhonov
1910adf085aSDmitri Tikhonov
1920adf085aSDmitri Tikhonovint
1930adf085aSDmitri Tikhonovmain (int argc, char **argv)
1940adf085aSDmitri Tikhonov{
1950adf085aSDmitri Tikhonov    int opt, s;
1960adf085aSDmitri Tikhonov    struct prog prog;
1970adf085aSDmitri Tikhonov    struct echo_server_ctx server_ctx;
1980adf085aSDmitri Tikhonov
1990adf085aSDmitri Tikhonov    memset(&server_ctx, 0, sizeof(server_ctx));
2000adf085aSDmitri Tikhonov    server_ctx.prog = &prog;
2010adf085aSDmitri Tikhonov    TAILQ_INIT(&server_ctx.sports);
2020adf085aSDmitri Tikhonov    TAILQ_INIT(&server_ctx.conn_ctxs);
2030adf085aSDmitri Tikhonov
2040adf085aSDmitri Tikhonov    prog_init(&prog, LSENG_SERVER, &server_ctx.sports,
2050adf085aSDmitri Tikhonov                                        &server_echo_stream_if, &server_ctx);
2060adf085aSDmitri Tikhonov
2070adf085aSDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "hn:")))
2080adf085aSDmitri Tikhonov    {
2090adf085aSDmitri Tikhonov        switch (opt) {
2100adf085aSDmitri Tikhonov        case 'n':
2110adf085aSDmitri Tikhonov            server_ctx.n_conn = atoi(optarg);
2120adf085aSDmitri Tikhonov            break;
2130adf085aSDmitri Tikhonov        case 'h':
2140adf085aSDmitri Tikhonov            usage(argv[0]);
2150adf085aSDmitri Tikhonov            prog_print_common_options(&prog, stdout);
2160adf085aSDmitri Tikhonov            exit(0);
2170adf085aSDmitri Tikhonov        default:
2180adf085aSDmitri Tikhonov            if (0 != prog_set_opt(&prog, opt, optarg))
2190adf085aSDmitri Tikhonov                exit(1);
2200adf085aSDmitri Tikhonov        }
2210adf085aSDmitri Tikhonov    }
2220adf085aSDmitri Tikhonov
2234429f8eaSDmitri Tikhonov    add_alpn("echo");
2240adf085aSDmitri Tikhonov    if (0 != prog_prep(&prog))
2250adf085aSDmitri Tikhonov    {
2260adf085aSDmitri Tikhonov        LSQ_ERROR("could not prep");
2270adf085aSDmitri Tikhonov        exit(EXIT_FAILURE);
2280adf085aSDmitri Tikhonov    }
2290adf085aSDmitri Tikhonov
2300adf085aSDmitri Tikhonov    LSQ_DEBUG("entering event loop");
2310adf085aSDmitri Tikhonov
2320adf085aSDmitri Tikhonov    s = prog_run(&prog);
2330adf085aSDmitri Tikhonov    prog_cleanup(&prog);
2340adf085aSDmitri Tikhonov
2350adf085aSDmitri Tikhonov    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
2360adf085aSDmitri Tikhonov}
237