echo_server.c revision 4429f8ea
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" 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