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