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