1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */ 20adf085aSDmitri Tikhonov/* 30adf085aSDmitri Tikhonov * md5_client.c -- This client sends one or more files to MD5 QUIC server 40adf085aSDmitri Tikhonov * for MD5 sum calculation. 50adf085aSDmitri Tikhonov */ 60adf085aSDmitri Tikhonov 70adf085aSDmitri Tikhonov#include <assert.h> 80adf085aSDmitri Tikhonov#include <errno.h> 90adf085aSDmitri Tikhonov#include <inttypes.h> 100adf085aSDmitri Tikhonov#include <stdio.h> 110adf085aSDmitri Tikhonov#include <stdlib.h> 120adf085aSDmitri Tikhonov#include <string.h> 130adf085aSDmitri Tikhonov#include <sys/queue.h> 140adf085aSDmitri Tikhonov#include <sys/types.h> 150adf085aSDmitri Tikhonov#include <sys/stat.h> 16fb3e20e0SDmitri Tikhonov 17fb3e20e0SDmitri Tikhonov#ifndef WIN32 18fb3e20e0SDmitri Tikhonov#include <unistd.h> 190adf085aSDmitri Tikhonov#include <fcntl.h> 20fb3e20e0SDmitri Tikhonov#else 21fb3e20e0SDmitri Tikhonov#include "vc_compat.h" 22fb3e20e0SDmitri Tikhonov#include "getopt.h" 23fb3e20e0SDmitri Tikhonov#endif 240adf085aSDmitri Tikhonov 250adf085aSDmitri Tikhonov#include <event2/event.h> 260adf085aSDmitri Tikhonov#include <openssl/md5.h> 270adf085aSDmitri Tikhonov 280adf085aSDmitri Tikhonov#include "lsquic.h" 290adf085aSDmitri Tikhonov#include "test_common.h" 300adf085aSDmitri Tikhonov#include "prog.h" 310adf085aSDmitri Tikhonov 320adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_logger.h" 330adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_int_types.h" 340adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_varint.h" 350adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_hq.h" 360adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_sfcw.h" 370adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_hash.h" 380adf085aSDmitri Tikhonov#include "../src/liblsquic/lsquic_stream.h" 390adf085aSDmitri Tikhonov 400adf085aSDmitri Tikhonov/* Set to non-zero value to test out what happens when reset is sent */ 410adf085aSDmitri Tikhonov#define RESET_AFTER_N_WRITES 0 420adf085aSDmitri Tikhonov 430adf085aSDmitri Tikhonovstatic int g_write_file = 1; 440adf085aSDmitri Tikhonov 450adf085aSDmitri Tikhonov#define LOCAL_BUF_SIZE 0x100 460adf085aSDmitri Tikhonov 470adf085aSDmitri Tikhonovstatic struct { 480adf085aSDmitri Tikhonov unsigned stream_id; /* If set, reset this stream ID */ 490adf085aSDmitri Tikhonov off_t offset; /* Reset it after writing this many bytes */ 500adf085aSDmitri Tikhonov} g_reset_stream; 510adf085aSDmitri Tikhonov 520adf085aSDmitri Tikhonovstruct file { 530adf085aSDmitri Tikhonov LIST_ENTRY(file) next_file; 540adf085aSDmitri Tikhonov const char *filename; 550adf085aSDmitri Tikhonov struct lsquic_reader reader; 560adf085aSDmitri Tikhonov int fd; 570adf085aSDmitri Tikhonov unsigned priority; 580adf085aSDmitri Tikhonov enum { 590adf085aSDmitri Tikhonov FILE_RESET = (1 << 0), 600adf085aSDmitri Tikhonov } file_flags; 610adf085aSDmitri Tikhonov size_t md5_off; 620adf085aSDmitri Tikhonov char md5str[MD5_DIGEST_LENGTH * 2]; 630adf085aSDmitri Tikhonov}; 640adf085aSDmitri Tikhonov 650adf085aSDmitri Tikhonovstruct lsquic_conn_ctx; 660adf085aSDmitri Tikhonov 670adf085aSDmitri Tikhonovstruct client_ctx { 680adf085aSDmitri Tikhonov struct lsquic_conn_ctx *conn_h; 690adf085aSDmitri Tikhonov LIST_HEAD(, file) files; 700adf085aSDmitri Tikhonov unsigned n_files; 710adf085aSDmitri Tikhonov struct file *cur_file; 720adf085aSDmitri Tikhonov lsquic_engine_t *engine; 730adf085aSDmitri Tikhonov struct service_port *sport; 740adf085aSDmitri Tikhonov struct prog *prog; 750adf085aSDmitri Tikhonov}; 760adf085aSDmitri Tikhonov 770adf085aSDmitri Tikhonovstruct lsquic_conn_ctx { 780adf085aSDmitri Tikhonov lsquic_conn_t *conn; 790adf085aSDmitri Tikhonov struct client_ctx *client_ctx; 800adf085aSDmitri Tikhonov}; 810adf085aSDmitri Tikhonov 820adf085aSDmitri Tikhonov 830adf085aSDmitri Tikhonovstatic lsquic_conn_ctx_t * 840adf085aSDmitri Tikhonovclient_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn) 850adf085aSDmitri Tikhonov{ 860adf085aSDmitri Tikhonov struct client_ctx *client_ctx = stream_if_ctx; 870adf085aSDmitri Tikhonov lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h)); 880adf085aSDmitri Tikhonov conn_h->conn = conn; 890adf085aSDmitri Tikhonov conn_h->client_ctx = client_ctx; 900adf085aSDmitri Tikhonov client_ctx->conn_h = conn_h; 910adf085aSDmitri Tikhonov assert(client_ctx->n_files > 0); 920adf085aSDmitri Tikhonov unsigned n = client_ctx->n_files; 930adf085aSDmitri Tikhonov while (n--) 940adf085aSDmitri Tikhonov lsquic_conn_make_stream(conn); 950adf085aSDmitri Tikhonov print_conn_info(conn); 960adf085aSDmitri Tikhonov return conn_h; 970adf085aSDmitri Tikhonov} 980adf085aSDmitri Tikhonov 990adf085aSDmitri Tikhonov 1000adf085aSDmitri Tikhonovstatic void 1010adf085aSDmitri Tikhonovclient_on_goaway_received (lsquic_conn_t *conn) 1020adf085aSDmitri Tikhonov{ 1030adf085aSDmitri Tikhonov LSQ_NOTICE("GOAWAY received"); 1040adf085aSDmitri Tikhonov} 1050adf085aSDmitri Tikhonov 1060adf085aSDmitri Tikhonov 1070adf085aSDmitri Tikhonovstatic void 1080adf085aSDmitri Tikhonovclient_on_conn_closed (lsquic_conn_t *conn) 1090adf085aSDmitri Tikhonov{ 1100adf085aSDmitri Tikhonov lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 1110adf085aSDmitri Tikhonov LSQ_NOTICE("Connection closed"); 1120adf085aSDmitri Tikhonov prog_stop(conn_h->client_ctx->prog); 1130adf085aSDmitri Tikhonov free(conn_h); 1140adf085aSDmitri Tikhonov} 1150adf085aSDmitri Tikhonov 1160adf085aSDmitri Tikhonov 1170adf085aSDmitri Tikhonovstruct lsquic_stream_ctx { 1180adf085aSDmitri Tikhonov lsquic_stream_t *stream; 1190adf085aSDmitri Tikhonov struct client_ctx *client_ctx; 1200adf085aSDmitri Tikhonov struct file *file; 1210adf085aSDmitri Tikhonov struct event *read_stdin_ev; 1220adf085aSDmitri Tikhonov struct { 1230adf085aSDmitri Tikhonov int initialized; 1240adf085aSDmitri Tikhonov size_t size, 1250adf085aSDmitri Tikhonov off; 1260adf085aSDmitri Tikhonov } small; 1270adf085aSDmitri Tikhonov}; 1280adf085aSDmitri Tikhonov 1290adf085aSDmitri Tikhonov 1300adf085aSDmitri Tikhonovstatic lsquic_stream_ctx_t * 1310adf085aSDmitri Tikhonovclient_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 1320adf085aSDmitri Tikhonov{ 1330adf085aSDmitri Tikhonov struct client_ctx *const client_ctx = stream_if_ctx; 1340adf085aSDmitri Tikhonov if (!stream) 1350adf085aSDmitri Tikhonov { 1360adf085aSDmitri Tikhonov assert(client_ctx->n_files > 0); 1370adf085aSDmitri Tikhonov LSQ_NOTICE("%s: got null stream: no more streams possible; # files: %u", 1380adf085aSDmitri Tikhonov __func__, client_ctx->n_files); 1390adf085aSDmitri Tikhonov --client_ctx->n_files; 1400adf085aSDmitri Tikhonov if (0 == client_ctx->n_files) 1410adf085aSDmitri Tikhonov { 1420adf085aSDmitri Tikhonov LSQ_DEBUG("closing connection"); 1430adf085aSDmitri Tikhonov lsquic_conn_close(client_ctx->conn_h->conn); 1440adf085aSDmitri Tikhonov } 1450adf085aSDmitri Tikhonov return NULL; 1460adf085aSDmitri Tikhonov } 1470adf085aSDmitri Tikhonov lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h)); 1480adf085aSDmitri Tikhonov st_h->stream = stream; 1490adf085aSDmitri Tikhonov st_h->client_ctx = stream_if_ctx; 1500adf085aSDmitri Tikhonov if (LIST_EMPTY(&st_h->client_ctx->files)) 1510adf085aSDmitri Tikhonov { 1520adf085aSDmitri Tikhonov /* XXX: perhaps we should not be able to write immediately: there may 1530adf085aSDmitri Tikhonov * be internal memory constraints... 1540adf085aSDmitri Tikhonov */ 1550adf085aSDmitri Tikhonov lsquic_stream_write(stream, "client request", 14); 1560adf085aSDmitri Tikhonov (void) lsquic_stream_flush(stream); 1570adf085aSDmitri Tikhonov lsquic_stream_wantwrite(stream, 0); 1580adf085aSDmitri Tikhonov lsquic_stream_wantread(stream, 1); 1590adf085aSDmitri Tikhonov } 1600adf085aSDmitri Tikhonov else 1610adf085aSDmitri Tikhonov { 1620adf085aSDmitri Tikhonov st_h->file = LIST_FIRST(&st_h->client_ctx->files); 1630adf085aSDmitri Tikhonov if (g_write_file) 1640adf085aSDmitri Tikhonov { 1650adf085aSDmitri Tikhonov st_h->file->fd = -1; 1660adf085aSDmitri Tikhonov st_h->file->reader.lsqr_read = test_reader_read; 1670adf085aSDmitri Tikhonov st_h->file->reader.lsqr_size = test_reader_size; 1680adf085aSDmitri Tikhonov st_h->file->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->file->filename); 1690adf085aSDmitri Tikhonov if (!st_h->file->reader.lsqr_ctx) 1700adf085aSDmitri Tikhonov exit(1); 1710adf085aSDmitri Tikhonov } 1720adf085aSDmitri Tikhonov else 1730adf085aSDmitri Tikhonov { 1740adf085aSDmitri Tikhonov st_h->file->fd = open(st_h->file->filename, O_RDONLY); 1750adf085aSDmitri Tikhonov if (st_h->file->fd < 0) 1760adf085aSDmitri Tikhonov { 1770adf085aSDmitri Tikhonov LSQ_ERROR("could not open %s for reading: %s", 1780adf085aSDmitri Tikhonov st_h->file->filename, strerror(errno)); 1790adf085aSDmitri Tikhonov exit(1); 1800adf085aSDmitri Tikhonov } 1810adf085aSDmitri Tikhonov } 1820adf085aSDmitri Tikhonov LIST_REMOVE(st_h->file, next_file); 1830adf085aSDmitri Tikhonov lsquic_stream_set_priority(stream, st_h->file->priority); 1840adf085aSDmitri Tikhonov lsquic_stream_wantwrite(stream, 1); 1850adf085aSDmitri Tikhonov } 1860adf085aSDmitri Tikhonov return st_h; 1870adf085aSDmitri Tikhonov} 1880adf085aSDmitri Tikhonov 1890adf085aSDmitri Tikhonov 1900adf085aSDmitri Tikhonovstatic size_t 1910adf085aSDmitri Tikhonovbuf_reader_size (void *reader_ctx) 1920adf085aSDmitri Tikhonov{ 1930adf085aSDmitri Tikhonov lsquic_stream_ctx_t *const st_h = reader_ctx; 1940adf085aSDmitri Tikhonov struct stat st; 1950adf085aSDmitri Tikhonov off_t off; 1960adf085aSDmitri Tikhonov 1970adf085aSDmitri Tikhonov if (st_h->small.initialized) 1980adf085aSDmitri Tikhonov goto initialized; 1990adf085aSDmitri Tikhonov 2000adf085aSDmitri Tikhonov if (0 != fstat(st_h->file->fd, &st)) 2010adf085aSDmitri Tikhonov { 2020adf085aSDmitri Tikhonov LSQ_ERROR("fstat failed: %s", strerror(errno)); 2030adf085aSDmitri Tikhonov goto err; 2040adf085aSDmitri Tikhonov } 2050adf085aSDmitri Tikhonov 2060adf085aSDmitri Tikhonov off = lseek(st_h->file->fd, 0, SEEK_CUR); 2070adf085aSDmitri Tikhonov if (off == (off_t) -1) 2080adf085aSDmitri Tikhonov { 2090adf085aSDmitri Tikhonov LSQ_ERROR("lseek failed: %s", strerror(errno)); 2100adf085aSDmitri Tikhonov goto err; 2110adf085aSDmitri Tikhonov } 2120adf085aSDmitri Tikhonov 2130adf085aSDmitri Tikhonov if (st.st_size < off) 2140adf085aSDmitri Tikhonov { 2150adf085aSDmitri Tikhonov LSQ_ERROR("size mismatch"); 2160adf085aSDmitri Tikhonov goto err; 2170adf085aSDmitri Tikhonov } 2180adf085aSDmitri Tikhonov 2190adf085aSDmitri Tikhonov st_h->small.initialized = 1; 2200adf085aSDmitri Tikhonov st_h->small.off = off; 2210adf085aSDmitri Tikhonov st_h->small.size = st.st_size; 2220adf085aSDmitri Tikhonov 2230adf085aSDmitri Tikhonov initialized: 2240adf085aSDmitri Tikhonov if (st_h->small.size - st_h->small.off > LOCAL_BUF_SIZE) 2250adf085aSDmitri Tikhonov return LOCAL_BUF_SIZE; 2260adf085aSDmitri Tikhonov else 2270adf085aSDmitri Tikhonov return st_h->small.size - st_h->small.off; 2280adf085aSDmitri Tikhonov 2290adf085aSDmitri Tikhonov err: 2300adf085aSDmitri Tikhonov close(st_h->file->fd); 2310adf085aSDmitri Tikhonov st_h->file->fd = 0; 2320adf085aSDmitri Tikhonov return 0; 2330adf085aSDmitri Tikhonov} 2340adf085aSDmitri Tikhonov 2350adf085aSDmitri Tikhonov 2360adf085aSDmitri Tikhonovstatic size_t 2370adf085aSDmitri Tikhonovbuf_reader_read (void *reader_ctx, void *buf, size_t count) 2380adf085aSDmitri Tikhonov{ 2390adf085aSDmitri Tikhonov lsquic_stream_ctx_t *const st_h = reader_ctx; 2400adf085aSDmitri Tikhonov ssize_t nr; 2410adf085aSDmitri Tikhonov unsigned char local_buf[LOCAL_BUF_SIZE]; 2420adf085aSDmitri Tikhonov 2430adf085aSDmitri Tikhonov assert(st_h->small.initialized); 2440adf085aSDmitri Tikhonov 2450adf085aSDmitri Tikhonov if (count > sizeof(local_buf)) 2460adf085aSDmitri Tikhonov count = sizeof(local_buf); 2470adf085aSDmitri Tikhonov 2480adf085aSDmitri Tikhonov nr = read(st_h->file->fd, local_buf, count); 2490adf085aSDmitri Tikhonov if (nr < 0) 2500adf085aSDmitri Tikhonov { 2510adf085aSDmitri Tikhonov LSQ_ERROR("read: %s", strerror(errno)); 2520adf085aSDmitri Tikhonov close(st_h->file->fd); 2530adf085aSDmitri Tikhonov st_h->file->fd = 0; 2540adf085aSDmitri Tikhonov return 0; 2550adf085aSDmitri Tikhonov } 2560adf085aSDmitri Tikhonov 2570adf085aSDmitri Tikhonov memcpy(buf, local_buf, nr); 2580adf085aSDmitri Tikhonov st_h->small.off += nr; 2590adf085aSDmitri Tikhonov return nr; 2600adf085aSDmitri Tikhonov} 2610adf085aSDmitri Tikhonov 2620adf085aSDmitri Tikhonov 2630adf085aSDmitri Tikhonovstatic void 2640adf085aSDmitri Tikhonovclient_file_on_write_buf (lsquic_stream_ctx_t *st_h) 2650adf085aSDmitri Tikhonov{ 2660adf085aSDmitri Tikhonov ssize_t nw; 2670adf085aSDmitri Tikhonov struct lsquic_reader reader = { 2680adf085aSDmitri Tikhonov .lsqr_read = buf_reader_read, 2690adf085aSDmitri Tikhonov .lsqr_size = buf_reader_size, 2700adf085aSDmitri Tikhonov .lsqr_ctx = st_h, 2710adf085aSDmitri Tikhonov }; 2720adf085aSDmitri Tikhonov 2730adf085aSDmitri Tikhonov if (g_reset_stream.stream_id == lsquic_stream_id(st_h->stream) && 2740adf085aSDmitri Tikhonov lseek(st_h->file->fd, 0, SEEK_CUR) >= g_reset_stream.offset) 2750adf085aSDmitri Tikhonov { 27699a1ad0fSDmitri Tikhonov /* Note: this is an internal function */ 27799a1ad0fSDmitri Tikhonov lsquic_stream_maybe_reset(st_h->stream, 27899a1ad0fSDmitri Tikhonov 0x01 /* QUIC_INTERNAL_ERROR */, 1); 2790adf085aSDmitri Tikhonov g_reset_stream.stream_id = 0; /* Reset only once */ 2800adf085aSDmitri Tikhonov } 2810adf085aSDmitri Tikhonov 2820adf085aSDmitri Tikhonov nw = lsquic_stream_writef(st_h->stream, &reader); 2830adf085aSDmitri Tikhonov if (-1 == nw) 2840adf085aSDmitri Tikhonov { 2850adf085aSDmitri Tikhonov if (ECONNRESET == errno) 2860adf085aSDmitri Tikhonov st_h->file->file_flags |= FILE_RESET; 2870adf085aSDmitri Tikhonov LSQ_WARN("lsquic_stream_read: %s", strerror(errno)); 2880adf085aSDmitri Tikhonov lsquic_stream_close(st_h->stream); 2890adf085aSDmitri Tikhonov return; 2900adf085aSDmitri Tikhonov } 2910adf085aSDmitri Tikhonov 2920adf085aSDmitri Tikhonov#if RESET_AFTER_N_WRITES 2930adf085aSDmitri Tikhonov static int write_count = 0; 2940adf085aSDmitri Tikhonov if (write_count++ > RESET_AFTER_N_WRITES) 2950adf085aSDmitri Tikhonov lsquic_stream_reset(st_h->stream, 0); 2960adf085aSDmitri Tikhonov#endif 2970adf085aSDmitri Tikhonov 2980adf085aSDmitri Tikhonov if (0 == nw) 2990adf085aSDmitri Tikhonov { 3000adf085aSDmitri Tikhonov (void) close(st_h->file->fd); 3010adf085aSDmitri Tikhonov if (0 == lsquic_stream_shutdown(st_h->stream, 1)) 3020adf085aSDmitri Tikhonov lsquic_stream_wantread(st_h->stream, 1); 3030adf085aSDmitri Tikhonov else 3040adf085aSDmitri Tikhonov { 3050adf085aSDmitri Tikhonov if (ECONNRESET == errno) 3060adf085aSDmitri Tikhonov st_h->file->file_flags |= FILE_RESET; 3070adf085aSDmitri Tikhonov LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno)); 3080adf085aSDmitri Tikhonov lsquic_stream_close(st_h->stream); 3090adf085aSDmitri Tikhonov } 3100adf085aSDmitri Tikhonov } 3110adf085aSDmitri Tikhonov} 3120adf085aSDmitri Tikhonov 3130adf085aSDmitri Tikhonov 3140adf085aSDmitri Tikhonovstatic void 3150adf085aSDmitri Tikhonovclient_file_on_write_efficient (lsquic_stream_t *stream, 3160adf085aSDmitri Tikhonov lsquic_stream_ctx_t *st_h) 3170adf085aSDmitri Tikhonov{ 3180adf085aSDmitri Tikhonov ssize_t nw; 3190adf085aSDmitri Tikhonov 3200adf085aSDmitri Tikhonov nw = lsquic_stream_writef(stream, &st_h->file->reader); 3210adf085aSDmitri Tikhonov if (nw < 0) 3220adf085aSDmitri Tikhonov { 3230adf085aSDmitri Tikhonov LSQ_ERROR("write error: %s", strerror(errno)); 3240adf085aSDmitri Tikhonov exit(1); 3250adf085aSDmitri Tikhonov } 3260adf085aSDmitri Tikhonov if (nw == 0) 3270adf085aSDmitri Tikhonov { 3280adf085aSDmitri Tikhonov destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx); 3290adf085aSDmitri Tikhonov st_h->file->reader.lsqr_ctx = NULL; 3300adf085aSDmitri Tikhonov if (0 == lsquic_stream_shutdown(st_h->stream, 1)) 3310adf085aSDmitri Tikhonov lsquic_stream_wantread(st_h->stream, 1); 3320adf085aSDmitri Tikhonov else 3330adf085aSDmitri Tikhonov { 3340adf085aSDmitri Tikhonov if (ECONNRESET == errno) 3350adf085aSDmitri Tikhonov st_h->file->file_flags |= FILE_RESET; 3360adf085aSDmitri Tikhonov LSQ_WARN("lsquic_stream_shutdown: %s", strerror(errno)); 3370adf085aSDmitri Tikhonov lsquic_stream_close(st_h->stream); 3380adf085aSDmitri Tikhonov } 3390adf085aSDmitri Tikhonov } 3400adf085aSDmitri Tikhonov} 3410adf085aSDmitri Tikhonov 3420adf085aSDmitri Tikhonov 3430adf085aSDmitri Tikhonovstatic void 3440adf085aSDmitri Tikhonovclient_file_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 3450adf085aSDmitri Tikhonov{ 3460adf085aSDmitri Tikhonov if (g_write_file) 3470adf085aSDmitri Tikhonov client_file_on_write_efficient(stream, st_h); 3480adf085aSDmitri Tikhonov else 3490adf085aSDmitri Tikhonov client_file_on_write_buf(st_h); 3500adf085aSDmitri Tikhonov} 3510adf085aSDmitri Tikhonov 3520adf085aSDmitri Tikhonov 3530adf085aSDmitri Tikhonovstatic void 3540adf085aSDmitri Tikhonovclient_file_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 3550adf085aSDmitri Tikhonov{ 3560adf085aSDmitri Tikhonov char buf; 3570adf085aSDmitri Tikhonov /* We expect to read in 32-character MD5 string */ 3580adf085aSDmitri Tikhonov size_t ntoread = sizeof(st_h->file->md5str) - st_h->file->md5_off; 3590adf085aSDmitri Tikhonov if (0 == ntoread) 3600adf085aSDmitri Tikhonov { 3610adf085aSDmitri Tikhonov lsquic_stream_wantread(stream, 0); 3620adf085aSDmitri Tikhonov /* XXX What about an error (due to RST_STREAM) here: how are we to 3630adf085aSDmitri Tikhonov * handle it? 3640adf085aSDmitri Tikhonov */ 3650adf085aSDmitri Tikhonov /* Expect a FIN */ 3660adf085aSDmitri Tikhonov if (0 == lsquic_stream_read(stream, &buf, sizeof(buf))) 3670adf085aSDmitri Tikhonov { 3680adf085aSDmitri Tikhonov LSQ_NOTICE("%.*s %s", (int) sizeof(st_h->file->md5str), 3690adf085aSDmitri Tikhonov st_h->file->md5str, 3700adf085aSDmitri Tikhonov st_h->file->filename); 3710adf085aSDmitri Tikhonov fflush(stdout); 3720adf085aSDmitri Tikhonov LSQ_DEBUG("# of files: %d", st_h->client_ctx->n_files); 3730adf085aSDmitri Tikhonov lsquic_stream_shutdown(stream, 0); 3740adf085aSDmitri Tikhonov } 3750adf085aSDmitri Tikhonov else 3760adf085aSDmitri Tikhonov LSQ_ERROR("expected FIN from stream!"); 3770adf085aSDmitri Tikhonov } 3780adf085aSDmitri Tikhonov else 3790adf085aSDmitri Tikhonov { 3800adf085aSDmitri Tikhonov ssize_t nr = lsquic_stream_read(stream, 3810adf085aSDmitri Tikhonov st_h->file->md5str + st_h->file->md5_off, ntoread); 3820adf085aSDmitri Tikhonov if (-1 == nr) 3830adf085aSDmitri Tikhonov { 3840adf085aSDmitri Tikhonov if (ECONNRESET == errno) 3850adf085aSDmitri Tikhonov st_h->file->file_flags |= FILE_RESET; 3860adf085aSDmitri Tikhonov LSQ_WARN("lsquic_stream_read: %s", strerror(errno)); 3870adf085aSDmitri Tikhonov lsquic_stream_close(stream); 3880adf085aSDmitri Tikhonov return; 3890adf085aSDmitri Tikhonov } 3900adf085aSDmitri Tikhonov else 3910adf085aSDmitri Tikhonov st_h->file->md5_off += nr; 3920adf085aSDmitri Tikhonov } 3930adf085aSDmitri Tikhonov} 3940adf085aSDmitri Tikhonov 3950adf085aSDmitri Tikhonov 3960adf085aSDmitri Tikhonovstatic void 3970adf085aSDmitri Tikhonovclient_file_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 3980adf085aSDmitri Tikhonov{ 3990adf085aSDmitri Tikhonov --st_h->client_ctx->n_files; 4000adf085aSDmitri Tikhonov LSQ_NOTICE("%s called for stream %"PRIu64", # files: %u", __func__, 4010adf085aSDmitri Tikhonov lsquic_stream_id(stream), st_h->client_ctx->n_files); 4020adf085aSDmitri Tikhonov if (0 == st_h->client_ctx->n_files) 4030adf085aSDmitri Tikhonov lsquic_conn_close(st_h->client_ctx->conn_h->conn); 4040adf085aSDmitri Tikhonov if (!(st_h->file->file_flags & FILE_RESET) && 0 == RESET_AFTER_N_WRITES) 4050adf085aSDmitri Tikhonov assert(st_h->file->md5_off == sizeof(st_h->file->md5str)); 4060adf085aSDmitri Tikhonov if (st_h->file->reader.lsqr_ctx) 4070adf085aSDmitri Tikhonov { 4080adf085aSDmitri Tikhonov destroy_lsquic_reader_ctx(st_h->file->reader.lsqr_ctx); 4090adf085aSDmitri Tikhonov st_h->file->reader.lsqr_ctx = NULL; 4100adf085aSDmitri Tikhonov } 4110adf085aSDmitri Tikhonov if (st_h->file->fd >= 0) 4120adf085aSDmitri Tikhonov (void) close(st_h->file->fd); 4130adf085aSDmitri Tikhonov free(st_h->file); 4140adf085aSDmitri Tikhonov free(st_h); 4150adf085aSDmitri Tikhonov} 4160adf085aSDmitri Tikhonov 4170adf085aSDmitri Tikhonov 4180adf085aSDmitri Tikhonovconst struct lsquic_stream_if client_file_stream_if = { 4190adf085aSDmitri Tikhonov .on_new_conn = client_on_new_conn, 4200adf085aSDmitri Tikhonov .on_goaway_received = client_on_goaway_received, 4210adf085aSDmitri Tikhonov .on_conn_closed = client_on_conn_closed, 4220adf085aSDmitri Tikhonov .on_new_stream = client_on_new_stream, 4230adf085aSDmitri Tikhonov .on_read = client_file_on_read, 4240adf085aSDmitri Tikhonov .on_write = client_file_on_write, 4250adf085aSDmitri Tikhonov .on_close = client_file_on_close, 4260adf085aSDmitri Tikhonov}; 4270adf085aSDmitri Tikhonov 4280adf085aSDmitri Tikhonov 4290adf085aSDmitri Tikhonovstatic void 4300adf085aSDmitri Tikhonovusage (const char *prog) 4310adf085aSDmitri Tikhonov{ 4320adf085aSDmitri Tikhonov const char *const slash = strrchr(prog, '/'); 4330adf085aSDmitri Tikhonov if (slash) 4340adf085aSDmitri Tikhonov prog = slash + 1; 4350adf085aSDmitri Tikhonov printf( 4360adf085aSDmitri Tikhonov"Usage: %s [opts]\n" 4370adf085aSDmitri Tikhonov"\n" 4380adf085aSDmitri Tikhonov"Options:\n" 4390adf085aSDmitri Tikhonov" -f FILE File to send to the server -- must be specified at least\n" 4400adf085aSDmitri Tikhonov" once.\n" 4410adf085aSDmitri Tikhonov" -b Use buffering API for sending files over rather than\n" 4420adf085aSDmitri Tikhonov" the efficient version.\n" 4430adf085aSDmitri Tikhonov" -p PRIORITY Applicatble to previous file specified with -f\n" 4440adf085aSDmitri Tikhonov" -r STREAM_ID:OFFSET\n" 4450adf085aSDmitri Tikhonov" Reset stream STREAM_ID after sending more that OFFSET bytes.\n" 4460adf085aSDmitri Tikhonov , prog); 4470adf085aSDmitri Tikhonov} 4480adf085aSDmitri Tikhonov 4490adf085aSDmitri Tikhonov 4500adf085aSDmitri Tikhonovint 4510adf085aSDmitri Tikhonovmain (int argc, char **argv) 4520adf085aSDmitri Tikhonov{ 4530adf085aSDmitri Tikhonov int opt, s; 4540adf085aSDmitri Tikhonov struct sport_head sports; 4550adf085aSDmitri Tikhonov struct prog prog; 4560adf085aSDmitri Tikhonov struct client_ctx client_ctx; 4570adf085aSDmitri Tikhonov struct file *file; 4580adf085aSDmitri Tikhonov 4590adf085aSDmitri Tikhonov file = NULL; 4600adf085aSDmitri Tikhonov memset(&client_ctx, 0, sizeof(client_ctx)); 4610adf085aSDmitri Tikhonov client_ctx.prog = &prog; 4620adf085aSDmitri Tikhonov 4630adf085aSDmitri Tikhonov TAILQ_INIT(&sports); 4640adf085aSDmitri Tikhonov prog_init(&prog, 0, &sports, &client_file_stream_if, &client_ctx); 4654429f8eaSDmitri Tikhonov prog.prog_api.ea_alpn = "md5"; 4660adf085aSDmitri Tikhonov 4670adf085aSDmitri Tikhonov while (-1 != (opt = getopt(argc, argv, PROG_OPTS "bhr:f:p:"))) 4680adf085aSDmitri Tikhonov { 4690adf085aSDmitri Tikhonov switch (opt) { 4700adf085aSDmitri Tikhonov case 'p': 4710adf085aSDmitri Tikhonov if (file) 4720adf085aSDmitri Tikhonov file->priority = atoi(optarg); 4730adf085aSDmitri Tikhonov else 4740adf085aSDmitri Tikhonov { 4750adf085aSDmitri Tikhonov fprintf(stderr, "No file to apply priority to\n"); 4760adf085aSDmitri Tikhonov exit(1); 4770adf085aSDmitri Tikhonov } 4780adf085aSDmitri Tikhonov break; 4790adf085aSDmitri Tikhonov case 'b': 4800adf085aSDmitri Tikhonov g_write_file = 0; 4810adf085aSDmitri Tikhonov break; 4820adf085aSDmitri Tikhonov case 'f': 4830adf085aSDmitri Tikhonov file = calloc(1, sizeof(*file)); 4840adf085aSDmitri Tikhonov LIST_INSERT_HEAD(&client_ctx.files, file, next_file); 4850adf085aSDmitri Tikhonov ++client_ctx.n_files; 4860adf085aSDmitri Tikhonov file->filename = optarg; 4870adf085aSDmitri Tikhonov break; 4880adf085aSDmitri Tikhonov case 'r': 4890adf085aSDmitri Tikhonov g_reset_stream.stream_id = atoi(optarg); 4900adf085aSDmitri Tikhonov g_reset_stream.offset = atoi(strchr(optarg, ':') + 1); 4910adf085aSDmitri Tikhonov break; 4920adf085aSDmitri Tikhonov case 'h': 4930adf085aSDmitri Tikhonov usage(argv[0]); 4940adf085aSDmitri Tikhonov prog_print_common_options(&prog, stdout); 4950adf085aSDmitri Tikhonov exit(0); 4960adf085aSDmitri Tikhonov default: 4970adf085aSDmitri Tikhonov if (0 != prog_set_opt(&prog, opt, optarg)) 4980adf085aSDmitri Tikhonov exit(1); 4990adf085aSDmitri Tikhonov } 5000adf085aSDmitri Tikhonov } 5010adf085aSDmitri Tikhonov 5020adf085aSDmitri Tikhonov if (LIST_EMPTY(&client_ctx.files)) 5030adf085aSDmitri Tikhonov { 5040adf085aSDmitri Tikhonov fprintf(stderr, "please specify one of more files using -f\n"); 5050adf085aSDmitri Tikhonov exit(1); 5060adf085aSDmitri Tikhonov } 5070adf085aSDmitri Tikhonov 5080adf085aSDmitri Tikhonov if (0 != prog_prep(&prog)) 5090adf085aSDmitri Tikhonov { 5100adf085aSDmitri Tikhonov LSQ_ERROR("could not prep"); 5110adf085aSDmitri Tikhonov exit(EXIT_FAILURE); 5120adf085aSDmitri Tikhonov } 5130adf085aSDmitri Tikhonov client_ctx.sport = TAILQ_FIRST(&sports); 5140adf085aSDmitri Tikhonov 5150adf085aSDmitri Tikhonov if (0 != prog_connect(&prog, NULL, 0)) 5160adf085aSDmitri Tikhonov { 5170adf085aSDmitri Tikhonov LSQ_ERROR("could not connect"); 5180adf085aSDmitri Tikhonov exit(EXIT_FAILURE); 5190adf085aSDmitri Tikhonov } 5200adf085aSDmitri Tikhonov 5210adf085aSDmitri Tikhonov LSQ_DEBUG("entering event loop"); 5220adf085aSDmitri Tikhonov 5230adf085aSDmitri Tikhonov s = prog_run(&prog); 5240adf085aSDmitri Tikhonov prog_cleanup(&prog); 5250adf085aSDmitri Tikhonov 5260adf085aSDmitri Tikhonov exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE); 5270adf085aSDmitri Tikhonov} 528