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