echo_client.c revision 5650ee6c
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * echo_client.c -- This is really a "line client:" it connects to QUIC server 4 * and sends it stuff, line by line. It works in tandem with echo_server. 5 */ 6 7#include <assert.h> 8#include <errno.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <sys/queue.h> 13#include <sys/types.h> 14#include <sys/stat.h> 15 16#ifndef WIN32 17#include <fcntl.h> 18#include <unistd.h> 19#define Read read 20#else 21#include "vc_compat.h" 22#include "getopt.h" 23#include <io.h> 24#define Read _read 25#define STDIN_FILENO 0 26#endif 27 28#include <event2/event.h> 29 30#include "lsquic.h" 31#include "test_common.h" 32#include "prog.h" 33 34#include "../src/liblsquic/lsquic_logger.h" 35 36struct lsquic_conn_ctx; 37 38struct echo_client_ctx { 39 struct lsquic_conn_ctx *conn_h; 40 struct prog *prog; 41}; 42 43struct lsquic_conn_ctx { 44 lsquic_conn_t *conn; 45 struct echo_client_ctx *client_ctx; 46}; 47 48 49static lsquic_conn_ctx_t * 50echo_client_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn) 51{ 52 struct echo_client_ctx *client_ctx = stream_if_ctx; 53 lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h)); 54 conn_h->conn = conn; 55 conn_h->client_ctx = client_ctx; 56 client_ctx->conn_h = conn_h; 57 lsquic_conn_make_stream(conn); 58 return conn_h; 59} 60 61 62static void 63echo_client_on_conn_closed (lsquic_conn_t *conn) 64{ 65 lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 66 LSQ_NOTICE("Connection closed"); 67 prog_stop(conn_h->client_ctx->prog); 68 free(conn_h); 69} 70 71 72struct lsquic_stream_ctx { 73 lsquic_stream_t *stream; 74 struct echo_client_ctx *client_ctx; 75 struct event *read_stdin_ev; 76 char buf[0x100]; 77 size_t buf_off; 78}; 79 80 81static void 82read_stdin (evutil_socket_t fd, short what, void *ctx) 83{ 84 ssize_t nr; 85 lsquic_stream_ctx_t *st_h = ctx; 86 87 nr = Read(fd, st_h->buf + st_h->buf_off++, 1); 88 LSQ_DEBUG("read %zd bytes from stdin", nr); 89 if (0 == nr) 90 { 91 lsquic_stream_shutdown(st_h->stream, 2); 92 } 93 else if (-1 == nr) 94 { 95 perror("read"); 96 exit(1); 97 } 98 else if ('\n' == st_h->buf[ st_h->buf_off - 1 ]) 99 { 100 LSQ_DEBUG("read newline: wantwrite"); 101 lsquic_stream_wantwrite(st_h->stream, 1); 102 lsquic_engine_process_conns(st_h->client_ctx->prog->prog_engine); 103 } 104 else if (st_h->buf_off == sizeof(st_h->buf)) 105 { 106 LSQ_NOTICE("line too long"); 107 exit(2); 108 } 109 else 110 event_add(st_h->read_stdin_ev, NULL); 111} 112 113 114static lsquic_stream_ctx_t * 115echo_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 116{ 117 lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h)); 118 st_h->stream = stream; 119 st_h->client_ctx = stream_if_ctx; 120 st_h->buf_off = 0; 121 st_h->read_stdin_ev = event_new(prog_eb(st_h->client_ctx->prog), 122 STDIN_FILENO, EV_READ, read_stdin, st_h); 123 event_add(st_h->read_stdin_ev, NULL); 124 return st_h; 125} 126 127 128static void 129echo_client_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 130{ 131 char c; 132 size_t nr; 133 134 nr = lsquic_stream_read(stream, &c, 1); 135 if (0 == nr) 136 { 137 lsquic_stream_shutdown(stream, 2); 138 return; 139 } 140 printf("%c", c); 141 fflush(stdout); 142 if ('\n' == c) 143 { 144 event_add(st_h->read_stdin_ev, NULL); 145 lsquic_stream_wantread(stream, 0); 146 } 147} 148 149 150static void 151echo_client_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 152{ 153 /* Here we make an assumption that we can write the whole buffer. 154 * Don't do it in a real program. 155 */ 156 lsquic_stream_write(stream, st_h->buf, st_h->buf_off); 157 st_h->buf_off = 0; 158 159 lsquic_stream_flush(stream); 160 lsquic_stream_wantwrite(stream, 0); 161 lsquic_stream_wantread(stream, 1); 162} 163 164 165static void 166echo_client_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 167{ 168 LSQ_NOTICE("%s called", __func__); 169 if (st_h->read_stdin_ev) 170 { 171 event_del(st_h->read_stdin_ev); 172 event_free(st_h->read_stdin_ev); 173 } 174 free(st_h); 175 lsquic_conn_close(lsquic_stream_conn(stream)); 176} 177 178 179const struct lsquic_stream_if client_echo_stream_if = { 180 .on_new_conn = echo_client_on_new_conn, 181 .on_conn_closed = echo_client_on_conn_closed, 182 .on_new_stream = echo_client_on_new_stream, 183 .on_read = echo_client_on_read, 184 .on_write = echo_client_on_write, 185 .on_close = echo_client_on_close, 186}; 187 188 189static void 190usage (const char *prog) 191{ 192 const char *const slash = strrchr(prog, '/'); 193 if (slash) 194 prog = slash + 1; 195 LSQ_NOTICE( 196"Usage: %s [opts]\n" 197"\n" 198"Options:\n" 199 , prog); 200} 201 202 203int 204main (int argc, char **argv) 205{ 206 int opt, s; 207 struct sport_head sports; 208 struct prog prog; 209 struct echo_client_ctx client_ctx; 210 211#ifdef WIN32 212 fprintf(stderr, "%s does not work on Windows, see\n" 213 "https://github.com/litespeedtech/lsquic/issues/219\n", argv[0]); 214 exit(EXIT_FAILURE); 215#endif 216 217 memset(&client_ctx, 0, sizeof(client_ctx)); 218 client_ctx.prog = &prog; 219 220 TAILQ_INIT(&sports); 221 prog_init(&prog, 0, &sports, &client_echo_stream_if, &client_ctx); 222 prog.prog_api.ea_alpn = "echo"; 223 224 while (-1 != (opt = getopt(argc, argv, PROG_OPTS "h"))) 225 { 226 switch (opt) { 227 case 'h': 228 usage(argv[0]); 229 prog_print_common_options(&prog, stdout); 230 exit(0); 231 default: 232 if (0 != prog_set_opt(&prog, opt, optarg)) 233 exit(1); 234 } 235 } 236 237#ifndef WIN32 238 int flags = fcntl(STDIN_FILENO, F_GETFL); 239 flags |= O_NONBLOCK; 240 if (0 != fcntl(STDIN_FILENO, F_SETFL, flags)) 241 { 242 perror("fcntl"); 243 exit(1); 244 } 245#else 246 { 247 u_long on = 1; 248 ioctlsocket(STDIN_FILENO, FIONBIO, &on); 249 } 250#endif 251 252 if (0 != prog_prep(&prog)) 253 { 254 LSQ_ERROR("could not prep"); 255 exit(EXIT_FAILURE); 256 } 257 if (0 != prog_connect(&prog, NULL, 0)) 258 { 259 LSQ_ERROR("could not connect"); 260 exit(EXIT_FAILURE); 261 } 262 263 LSQ_DEBUG("entering event loop"); 264 265 s = prog_run(&prog); 266 prog_cleanup(&prog); 267 268 exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE); 269} 270