From b41c28e70af47e1a49606db9c28f622f2a0052d0 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 16 Jul 2026 11:26:33 +0200 Subject: [PATCH] lib: client writer tweaks - keep pause state at request - keep presense of content decoding writers at request - only add cw-pause writer when content decoding happens - define macro for passing data down the writer chain scorecard: add option to test with h2o server Closes #22335 --- lib/cw-out.c | 45 ++------- lib/cw-out.h | 10 -- lib/cw-pause.c | 6 +- lib/request.h | 10 +- lib/sendf.c | 175 ++++++++++++++------------------- lib/sendf.h | 24 ++--- tests/http/scorecard.py | 40 ++++++-- tests/http/testenv/__init__.py | 1 + 8 files changed, 138 insertions(+), 173 deletions(-) diff --git a/lib/cw-out.c b/lib/cw-out.c index b38fde455f..35ded4b475 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -97,7 +97,6 @@ static void cw_out_buf_free(struct cw_out_buf *cwbuf) struct cw_out_ctx { struct Curl_cwriter super; struct cw_out_buf *buf; - BIT(paused); BIT(errored); }; @@ -171,8 +170,7 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype, } } -static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, - struct Curl_easy *data, +static CURLcode cw_out_cb_write(struct Curl_easy *data, curl_write_callback wcb, void *wcb_data, cw_out_type otype, @@ -200,7 +198,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } - ctx->paused = TRUE; + data->req.writer.paused = TRUE; CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client"); result = Curl_xfer_pause_recv(data, TRUE); return result ? result : CURLE_AGAIN; @@ -245,18 +243,18 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, *pconsumed = 0; if(otype == CW_OUT_BODY_0LEN) { DEBUGASSERT(!blen); - return cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + return cw_out_cb_write(data, wcb, wcb_data, otype, buf, blen, &nwritten); } else { - while(blen && !ctx->paused) { + while(blen && !data->req.writer.paused) { if(!flush_all && blen < min_write) break; wlen = max_write ? CURLMIN(blen, max_write) : blen; if(otype == CW_OUT_BODY) result = Curl_pgrs_deliver_check(data, wlen); if(!result) - result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + result = cw_out_cb_write(data, wcb, wcb_data, otype, buf, wlen, &nwritten); if(result) return result; @@ -314,7 +312,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, if(!cwbuf) return CURLE_OK; - if(ctx->paused) + if(data->req.writer.paused) return CURLE_OK; /* write the end of the chain until it blocks or gets empty */ @@ -327,7 +325,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, return result; if(*plast) { /* could not write last, paused again? */ - DEBUGASSERT(ctx->paused); + DEBUGASSERT(data->req.writer.paused); return CURLE_OK; } } @@ -457,7 +455,7 @@ static CURLcode cw_out_do_flush(struct Curl_easy *data, if(ctx->errored) return CURLE_WRITE_ERROR; - if(ctx->paused) + if(data->req.writer.paused) return CURLE_OK; /* not doing it */ result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); @@ -486,33 +484,6 @@ const struct Curl_cwtype Curl_cwt_out = { sizeof(struct cw_out_ctx) }; -bool Curl_cw_out_is_paused(struct Curl_easy *data) -{ - struct Curl_cwriter *cw_out; - struct cw_out_ctx *ctx; - - cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); - if(!cw_out) - return FALSE; - - ctx = (struct cw_out_ctx *)cw_out; - return (bool)ctx->paused; -} - -CURLcode Curl_cw_out_unpause(struct Curl_easy *data) -{ - struct Curl_cwriter *cw_out; - CURLcode result = CURLE_OK; - - cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); - if(cw_out) { - struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; - CURL_TRC_WRITE(data, "[OUT] unpause"); - ctx->paused = FALSE; - } - return result; -} - CURLcode Curl_cw_out_done(struct Curl_easy *data) { struct Curl_cwriter *cw_out; diff --git a/lib/cw-out.h b/lib/cw-out.h index 7de6524bc5..ee2b4b6307 100644 --- a/lib/cw-out.h +++ b/lib/cw-out.h @@ -34,16 +34,6 @@ struct Curl_easy; */ extern const struct Curl_cwtype Curl_cwt_out; -/** - * Return TRUE iff 'cw-out' client write has paused data. - */ -bool Curl_cw_out_is_paused(struct Curl_easy *data); - -/** - * Flush any buffered date to the client, chunk collation still applies. - */ -CURLcode Curl_cw_out_unpause(struct Curl_easy *data); - /** * Mark EndOfStream reached and flush ALL data to the client. */ diff --git a/lib/cw-pause.c b/lib/cw-pause.c index a42f61f4ba..ccdc7657b1 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -100,7 +100,6 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, struct Curl_cwriter *cw_pause) { struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)cw_pause; - bool decoding = Curl_cwriter_is_content_decoding(data); CURLcode result = CURLE_OK; /* write the end of the chain until it blocks or gets empty */ @@ -112,7 +111,7 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, while((*plast)->next) /* got to last in list */ plast = &(*plast)->next; if(Curl_bufq_peek(&(*plast)->b, &buf, &blen)) { - wlen = (decoding && ((*plast)->type & CLIENTWRITE_BODY)) ? + wlen = ((*plast)->type & CLIENTWRITE_BODY) ? CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen; result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, wlen); @@ -151,7 +150,6 @@ static CURLcode cw_pause_write(struct Curl_easy *data, struct cw_pause_ctx *ctx = writer->ctx; CURLcode result = CURLE_OK; size_t wlen = 0; - bool decoding = Curl_cwriter_is_content_decoding(data); if(ctx->buf && !Curl_cwriter_is_paused(data)) { result = cw_pause_flush(data, writer); @@ -164,7 +162,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, DEBUGASSERT(!ctx->buf); /* content decoding might blow up size considerably, write smaller * chunks to make pausing need buffer less. */ - wlen = (decoding && (type & CLIENTWRITE_BODY)) ? + wlen = (type & CLIENTWRITE_BODY) ? CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen; if(wlen < blen) wtype &= ~CLIENTWRITE_EOS; diff --git a/lib/request.h b/lib/request.h index e67865a984..6662cf19e7 100644 --- a/lib/request.h +++ b/lib/request.h @@ -101,10 +101,16 @@ struct SingleRequest { /* Client Writer stack, handles transfer- and content-encodings, protocol * checks, pausing by client callbacks. */ - struct Curl_cwriter *writer_stack; + struct { + struct Curl_cwriter *stack; + BIT(paused); + } writer; /* Client Reader stack, handles transfer- and content-encodings, protocol * checks, pausing by client callbacks. */ - struct Curl_creader *reader_stack; + struct { + struct Curl_creader *stack; + BIT(paused); + } reader; struct bufq sendbuf; /* data which needs to be send to the server */ size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ time_t timeofdoc; diff --git a/lib/sendf.c b/lib/sendf.c index efaba08eb4..8f51a772a2 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -46,24 +46,24 @@ static void cl_reset_writer(struct Curl_easy *data) { - struct Curl_cwriter *writer = data->req.writer_stack; + struct Curl_cwriter *writer = data->req.writer.stack; while(writer) { - data->req.writer_stack = writer->next; + data->req.writer.stack = writer->next; writer->cwt->do_close(data, writer); curlx_free(writer); - writer = data->req.writer_stack; + writer = data->req.writer.stack; } } static void cl_reset_reader(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; data->req.reader_started = FALSE; while(reader) { - data->req.reader_stack = reader->next; + data->req.reader.stack = reader->next; reader->crt->do_close(data, reader); curlx_free(reader); - reader = data->req.reader_stack; + reader = data->req.reader.stack; } } @@ -95,7 +95,7 @@ void Curl_client_reset(struct Curl_easy *data) CURLcode Curl_client_start(struct Curl_easy *data) { if(data->req.rewind_read) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; CURLcode result = CURLE_OK; CURL_TRC_READ(data, "client start, rewind readers"); @@ -124,25 +124,6 @@ void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) data->req.rewind_read = !!enable; } -/* Write data using an unencoding writer stack. */ -CURLcode Curl_cwriter_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) -{ - if(!writer) - return CURLE_WRITE_ERROR; - return writer->cwt->do_write(data, writer, type, buf, nbytes); -} - -CURLcode Curl_cwriter_flush(struct Curl_easy *data, - struct Curl_cwriter *writer) -{ - if(!writer) { - return CURLE_WRITE_ERROR; - } - return writer->cwt->do_flush(data, writer); -} - CURLcode Curl_cwriter_def_init(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -340,47 +321,39 @@ static const struct Curl_cwtype cw_raw = { sizeof(struct Curl_cwriter) }; +static void cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + struct Curl_cwriter **anchor = &data->req.writer.stack; + + /* Insert the writer as first in its phase. + * Skip existing writers of lower phases. */ + while(*anchor && (*anchor)->phase < writer->phase) + anchor = &((*anchor)->next); + writer->next = *anchor; + *anchor = writer; +} + static CURLcode do_init_writer_stack(struct Curl_easy *data) { struct Curl_cwriter *writer; CURLcode result; - DEBUGASSERT(!data->req.writer_stack); - result = Curl_cwriter_create(&data->req.writer_stack, + DEBUGASSERT(!data->req.writer.stack); + result = Curl_cwriter_create(&data->req.writer.stack, data, &Curl_cwt_out, CURL_CW_CLIENT); if(result) return result; - /* This places the "pause" writer behind the "download" writer that - * is added below. Meaning the "download" can do checks on content length - * and other things *before* write outs are buffered for paused transfers. */ - result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, - CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } - if(result) - return result; - result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } if(result) return result; + cwriter_add(data, writer); result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); - if(!result) { - result = Curl_cwriter_add(data, writer); - if(result) - Curl_cwriter_free(data, writer); - } if(result) return result; + cwriter_add(data, writer); return result; } @@ -405,14 +378,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, DEBUGASSERT(!(type & CLIENTWRITE_INFO) || ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0)); - if(!data->req.writer_stack) { + if(!data->req.writer.stack) { result = do_init_writer_stack(data); if(result) return result; - DEBUGASSERT(data->req.writer_stack); + DEBUGASSERT(data->req.writer.stack); } - result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); + result = Curl_cwriter_write(data, data->req.writer.stack, type, buf, len); CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", (unsigned int)type, len, (int)result); return result; @@ -422,14 +395,14 @@ CURLcode Curl_client_flush(struct Curl_easy *data) { CURLcode result; - if(!data->req.writer_stack) { + if(!data->req.writer.stack) { result = do_init_writer_stack(data); if(result) return result; - DEBUGASSERT(data->req.writer_stack); + DEBUGASSERT(data->req.writer.stack); } - result = Curl_cwriter_flush(data, data->req.writer_stack); + result = Curl_cwriter_flush(data, data->req.writer.stack); CURL_TRC_WRITE(data, "client_flush() -> %d", (int)result); return result; } @@ -476,18 +449,33 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) struct Curl_cwriter *w; size_t n = 0; - for(w = data->req.writer_stack; w; w = w->next) { + for(w = data->req.writer.stack; w; w = w->next) { if(w->phase == phase) ++n; } return n; } +static CURLcode cwriter_ensure_pause_writer(struct Curl_easy *data) +{ + struct Curl_cwriter *writer = + Curl_cwriter_get_by_type(data, &Curl_cwt_pause); + CURLcode result = CURLE_OK; + + if(!writer) { + result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause, + CURL_CW_BEFORE_DECODE); + if(!result) + cwriter_add(data, writer); + } + return result; +} + CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer) { CURLcode result; - struct Curl_cwriter **anchor = &data->req.writer_stack; + struct Curl_cwriter **anchor = &data->req.writer.stack; if(!*anchor) { result = do_init_writer_stack(data); @@ -495,12 +483,16 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data, return result; } - /* Insert the writer as first in its phase. - * Skip existing writers of lower phases. */ - while(*anchor && (*anchor)->phase < writer->phase) - anchor = &((*anchor)->next); - writer->next = *anchor; - *anchor = writer; + if(writer->phase == CURL_CW_CONTENT_DECODE) { + /* On adding a content decoder, add the pause writer. Do this + * BEFORE the given writer as any failure will make the + * caller destroy the writer again. */ + result = cwriter_ensure_pause_writer(data); + if(result) + return result; + } + + cwriter_add(data, writer); return CURLE_OK; } @@ -508,7 +500,7 @@ struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, const char *name) { struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { + for(writer = data->req.writer.stack; writer; writer = writer->next) { if(!strcmp(name, writer->cwt->name)) return writer; } @@ -519,34 +511,17 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, const struct Curl_cwtype *cwt) { struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { + for(writer = data->req.writer.stack; writer; writer = writer->next) { if(writer->cwt == cwt) return writer; } return NULL; } -bool Curl_cwriter_is_content_decoding(struct Curl_easy *data) -{ - struct Curl_cwriter *writer; - for(writer = data->req.writer_stack; writer; writer = writer->next) { - if(writer->phase == CURL_CW_CONTENT_DECODE) - return TRUE; - } - return FALSE; -} - -bool Curl_cwriter_is_paused(struct Curl_easy *data) -{ - return Curl_cw_out_is_paused(data); -} - CURLcode Curl_cwriter_unpause(struct Curl_easy *data) { - CURLcode result = Curl_cw_out_unpause(data); - if(!result) - result = Curl_cwriter_flush(data, data->req.writer_stack); - return result; + data->req.writer.paused = FALSE; + return Curl_cwriter_flush(data, data->req.writer.stack); } CURLcode Curl_creader_read(struct Curl_easy *data, @@ -1141,9 +1116,9 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data, DEBUGASSERT(r); DEBUGASSERT(r->crt); DEBUGASSERT(r->phase == CURL_CR_CLIENT); - DEBUGASSERT(!data->req.reader_stack); + DEBUGASSERT(!data->req.reader.stack); - data->req.reader_stack = r; + data->req.reader.stack = r; clen = r->crt->total_length(data, r); /* if we do not have 0 length init, and crlf conversion is wanted, * add the reader for it */ @@ -1184,7 +1159,7 @@ CURLcode Curl_creader_add(struct Curl_easy *data, struct Curl_creader *reader) { CURLcode result; - struct Curl_creader **anchor = &data->req.reader_stack; + struct Curl_creader **anchor = &data->req.reader.stack; if(!*anchor) { result = Curl_creader_set_fread(data, data->state.infilesize); @@ -1227,11 +1202,11 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, DEBUGASSERT(eos); *nread = 0; - if(!data->req.reader_stack) { + if(!data->req.reader.stack) { result = Curl_creader_set_fread(data, data->state.infilesize); if(result) return result; - DEBUGASSERT(data->req.reader_stack); + DEBUGASSERT(data->req.reader.stack); } if(!data->req.reader_started) { Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1); @@ -1248,7 +1223,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, if(ul_avail < (curl_off_t)blen) blen = (size_t)ul_avail; } - result = Curl_creader_read(data, data->req.reader_stack, buf, blen, + result = Curl_creader_read(data, data->req.reader.stack, buf, blen, nread, eos); out: @@ -1259,7 +1234,7 @@ out: bool Curl_creader_needs_rewind(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { if(reader->crt->needs_rewind(data, reader)) { CURL_TRC_READ(data, "client reader needs rewind before next request"); @@ -1445,13 +1420,13 @@ out: curl_off_t Curl_creader_total_length(struct Curl_easy *data) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; return r ? r->crt->total_length(data, r) : -1; } curl_off_t Curl_creader_client_length(struct Curl_easy *data) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; while(r && r->phase != CURL_CR_CLIENT) r = r->next; return r ? r->crt->total_length(data, r) : -1; @@ -1459,7 +1434,7 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data) CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) { - struct Curl_creader *r = data->req.reader_stack; + struct Curl_creader *r = data->req.reader.stack; while(r && r->phase != CURL_CR_CLIENT) r = r->next; return r ? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR; @@ -1467,7 +1442,7 @@ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) CURLcode Curl_creader_unpause(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; CURLcode result = CURLE_OK; while(reader) { @@ -1482,7 +1457,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) bool Curl_creader_is_paused(struct Curl_easy *data) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { if(reader->crt->is_paused(data, reader)) @@ -1494,7 +1469,7 @@ bool Curl_creader_is_paused(struct Curl_easy *data) void Curl_creader_done(struct Curl_easy *data, int premature) { - struct Curl_creader *reader = data->req.reader_stack; + struct Curl_creader *reader = data->req.reader.stack; while(reader) { reader->crt->done(data, reader, premature); reader = reader->next; @@ -1505,7 +1480,7 @@ struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, const struct Curl_crtype *crt) { struct Curl_creader *r; - for(r = data->req.reader_stack; r; r = r->next) { + for(r = data->req.reader.stack; r; r = r->next) { if(r->crt == crt) return r; } diff --git a/lib/sendf.h b/lib/sendf.h index 0cf6245ff6..7f31b24662 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -24,6 +24,7 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "cw-out.h" /** * Type of data that is being written to the client (application) @@ -104,6 +105,7 @@ typedef enum { CURL_CW_RAW, /* raw data written, before any decoding */ CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ CURL_CW_PROTOCOL, /* after transfer, but before content decoding */ + CURL_CW_BEFORE_DECODE, /* after protocol, but before content decoding */ CURL_CW_CONTENT_DECODE, /* remove content-encodings */ CURL_CW_CLIENT /* data written to client */ } Curl_cwriter_phase; @@ -176,22 +178,16 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, const char *name); -/** - * Convenience method for calling `writer->do_write()` that - * checks for NULL writer. - */ -CURLcode Curl_cwriter_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes); -CURLcode Curl_cwriter_flush(struct Curl_easy *data, - struct Curl_cwriter *writer); +/* Convenience method for calling `writer->do_write()` that + * checks for NULL writer. */ +#define Curl_cwriter_write(d, w, t, b, n) \ + ((w) ? (w)->cwt->do_write((d), (w), (t), (b), (n)) : CURLE_WRITE_ERROR) -/** - * Return TRUE iff client writer is paused. - */ -bool Curl_cwriter_is_paused(struct Curl_easy *data); +#define Curl_cwriter_flush(d, w) \ + ((w) ? (w)->cwt->do_flush((d), (w)) : CURLE_WRITE_ERROR) -bool Curl_cwriter_is_content_decoding(struct Curl_easy *data); +/* TRUE if client writer is paused. */ +#define Curl_cwriter_is_paused(d) ((bool)(d)->req.writer.paused) /** * Unpause client writer and flush any buffered date to the client. diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 2a45450aa4..60b1a35f4c 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -40,6 +40,7 @@ from testenv import ( Dante, Env, ExecResult, + H2oServer, Httpd, NghttpxQuic, RunProfile, @@ -776,17 +777,19 @@ def run_score(args, protocol): uploads = None requests = args.requests - test_httpd = protocol != 'h3' - test_caddy = protocol == 'h3' - if args.caddy or args.httpd: - test_caddy = args.caddy - test_httpd = args.httpd - rv = 0 env = Env() env.setup() env.test_timeout = None + test_httpd = protocol != 'h3' + test_h2o = protocol == 'h3' and env.have_h2o() + test_caddy = protocol == 'h3' and not test_h2o + if args.caddy or args.httpd or args.h2o: + test_caddy = args.caddy + test_httpd = args.httpd + test_h2o = args.h2o + sockd = None socks_args = None if args.socks4 and args.socks5: @@ -803,6 +806,7 @@ def run_score(args, protocol): httpd = None nghttpx = None caddy = None + h2o = None try: cards = [] @@ -859,6 +863,26 @@ def run_score(args, protocol): card.setup_resources(server_docs, downloads) cards.append(card) + if test_h2o: + h2o = H2oServer(env=env) + h2o.clear_logs() + assert h2o.initial_start() + server_descr = f'H2o/{env.h2o_version()}' + server_port = h2o.port + server_docs = h2o.docs_dir + card = ScoreRunner(env=env, + protocol=protocol, + server_descr=server_descr, + server_port=server_port, + verbose=args.verbose, curl_verbose=args.curl_verbose, + download_parallel=args.download_parallel, + upload_parallel=args.upload_parallel, + with_flame=args.flame, + socks_args=socks_args, + limit_rate=args.limit_rate) + card.setup_resources(server_docs, downloads) + cards.append(card) + if test_caddy and env.caddy: backend = '' if uploads and httpd is None: @@ -921,6 +945,8 @@ def run_score(args, protocol): caddy.stop() if nghttpx: nghttpx.stop(wait_dead=False) + if h2o: + h2o.stop() if httpd: httpd.stop() if sockd: @@ -951,6 +977,8 @@ def main(): default=1, help="how many sample runs to make") parser.add_argument("--httpd", action='store_true', default=False, help="evaluate httpd server only") + parser.add_argument("--h2o", action='store_true', default=False, + help="evaluate h2o server only") parser.add_argument("--caddy", action='store_true', default=False, help="evaluate caddy server only") parser.add_argument("--curl-verbose", action='store_true', diff --git a/tests/http/testenv/__init__.py b/tests/http/testenv/__init__.py index 08ab2e2311..d2eea486fa 100644 --- a/tests/http/testenv/__init__.py +++ b/tests/http/testenv/__init__.py @@ -38,6 +38,7 @@ from .client import LocalClient from .dante import Dante from .dnsd import Dnsd from .env import Env +from .h2o import H2oServer, H2oProxy from .httpd import Httpd from .nghttpx import Nghttpx, NghttpxFwd, NghttpxQuic from .sshd import Sshd