mirror of
https://github.com/curl/curl.git
synced 2026-07-24 19:47:16 +03:00
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
This commit is contained in:
parent
f65253056a
commit
b41c28e70a
8 changed files with 138 additions and 173 deletions
45
lib/cw-out.c
45
lib/cw-out.c
|
|
@ -97,7 +97,6 @@ static void cw_out_buf_free(struct cw_out_buf *cwbuf)
|
||||||
struct cw_out_ctx {
|
struct cw_out_ctx {
|
||||||
struct Curl_cwriter super;
|
struct Curl_cwriter super;
|
||||||
struct cw_out_buf *buf;
|
struct cw_out_buf *buf;
|
||||||
BIT(paused);
|
|
||||||
BIT(errored);
|
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,
|
static CURLcode cw_out_cb_write(struct Curl_easy *data,
|
||||||
struct Curl_easy *data,
|
|
||||||
curl_write_callback wcb,
|
curl_write_callback wcb,
|
||||||
void *wcb_data,
|
void *wcb_data,
|
||||||
cw_out_type otype,
|
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");
|
failf(data, "Write callback asked for PAUSE when not supported");
|
||||||
return CURLE_WRITE_ERROR;
|
return CURLE_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
ctx->paused = TRUE;
|
data->req.writer.paused = TRUE;
|
||||||
CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
|
CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
|
||||||
result = Curl_xfer_pause_recv(data, TRUE);
|
result = Curl_xfer_pause_recv(data, TRUE);
|
||||||
return result ? result : CURLE_AGAIN;
|
return result ? result : CURLE_AGAIN;
|
||||||
|
|
@ -245,18 +243,18 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
|
||||||
*pconsumed = 0;
|
*pconsumed = 0;
|
||||||
if(otype == CW_OUT_BODY_0LEN) {
|
if(otype == CW_OUT_BODY_0LEN) {
|
||||||
DEBUGASSERT(!blen);
|
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);
|
buf, blen, &nwritten);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while(blen && !ctx->paused) {
|
while(blen && !data->req.writer.paused) {
|
||||||
if(!flush_all && blen < min_write)
|
if(!flush_all && blen < min_write)
|
||||||
break;
|
break;
|
||||||
wlen = max_write ? CURLMIN(blen, max_write) : blen;
|
wlen = max_write ? CURLMIN(blen, max_write) : blen;
|
||||||
if(otype == CW_OUT_BODY)
|
if(otype == CW_OUT_BODY)
|
||||||
result = Curl_pgrs_deliver_check(data, wlen);
|
result = Curl_pgrs_deliver_check(data, wlen);
|
||||||
if(!result)
|
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);
|
buf, wlen, &nwritten);
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -314,7 +312,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
|
||||||
|
|
||||||
if(!cwbuf)
|
if(!cwbuf)
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
if(ctx->paused)
|
if(data->req.writer.paused)
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
|
|
||||||
/* write the end of the chain until it blocks or gets empty */
|
/* 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;
|
return result;
|
||||||
if(*plast) {
|
if(*plast) {
|
||||||
/* could not write last, paused again? */
|
/* could not write last, paused again? */
|
||||||
DEBUGASSERT(ctx->paused);
|
DEBUGASSERT(data->req.writer.paused);
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,7 +455,7 @@ static CURLcode cw_out_do_flush(struct Curl_easy *data,
|
||||||
|
|
||||||
if(ctx->errored)
|
if(ctx->errored)
|
||||||
return CURLE_WRITE_ERROR;
|
return CURLE_WRITE_ERROR;
|
||||||
if(ctx->paused)
|
if(data->req.writer.paused)
|
||||||
return CURLE_OK; /* not doing it */
|
return CURLE_OK; /* not doing it */
|
||||||
|
|
||||||
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
|
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)
|
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)
|
CURLcode Curl_cw_out_done(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct Curl_cwriter *cw_out;
|
struct Curl_cwriter *cw_out;
|
||||||
|
|
|
||||||
10
lib/cw-out.h
10
lib/cw-out.h
|
|
@ -34,16 +34,6 @@ struct Curl_easy;
|
||||||
*/
|
*/
|
||||||
extern const struct Curl_cwtype Curl_cwt_out;
|
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.
|
* Mark EndOfStream reached and flush ALL data to the client.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,6 @@ static CURLcode cw_pause_flush(struct Curl_easy *data,
|
||||||
struct Curl_cwriter *cw_pause)
|
struct Curl_cwriter *cw_pause)
|
||||||
{
|
{
|
||||||
struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)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;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
/* write the end of the chain until it blocks or gets empty */
|
/* 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 */
|
while((*plast)->next) /* got to last in list */
|
||||||
plast = &(*plast)->next;
|
plast = &(*plast)->next;
|
||||||
if(Curl_bufq_peek(&(*plast)->b, &buf, &blen)) {
|
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;
|
CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
|
||||||
result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
|
result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
|
||||||
(const char *)buf, wlen);
|
(const char *)buf, wlen);
|
||||||
|
|
@ -151,7 +150,6 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||||
struct cw_pause_ctx *ctx = writer->ctx;
|
struct cw_pause_ctx *ctx = writer->ctx;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
size_t wlen = 0;
|
size_t wlen = 0;
|
||||||
bool decoding = Curl_cwriter_is_content_decoding(data);
|
|
||||||
|
|
||||||
if(ctx->buf && !Curl_cwriter_is_paused(data)) {
|
if(ctx->buf && !Curl_cwriter_is_paused(data)) {
|
||||||
result = cw_pause_flush(data, writer);
|
result = cw_pause_flush(data, writer);
|
||||||
|
|
@ -164,7 +162,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||||
DEBUGASSERT(!ctx->buf);
|
DEBUGASSERT(!ctx->buf);
|
||||||
/* content decoding might blow up size considerably, write smaller
|
/* content decoding might blow up size considerably, write smaller
|
||||||
* chunks to make pausing need buffer less. */
|
* chunks to make pausing need buffer less. */
|
||||||
wlen = (decoding && (type & CLIENTWRITE_BODY)) ?
|
wlen = (type & CLIENTWRITE_BODY) ?
|
||||||
CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
|
CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
|
||||||
if(wlen < blen)
|
if(wlen < blen)
|
||||||
wtype &= ~CLIENTWRITE_EOS;
|
wtype &= ~CLIENTWRITE_EOS;
|
||||||
|
|
|
||||||
|
|
@ -101,10 +101,16 @@ struct SingleRequest {
|
||||||
|
|
||||||
/* Client Writer stack, handles transfer- and content-encodings, protocol
|
/* Client Writer stack, handles transfer- and content-encodings, protocol
|
||||||
* checks, pausing by client callbacks. */
|
* 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
|
/* Client Reader stack, handles transfer- and content-encodings, protocol
|
||||||
* checks, pausing by client callbacks. */
|
* 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 */
|
struct bufq sendbuf; /* data which needs to be send to the server */
|
||||||
size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */
|
size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */
|
||||||
time_t timeofdoc;
|
time_t timeofdoc;
|
||||||
|
|
|
||||||
175
lib/sendf.c
175
lib/sendf.c
|
|
@ -46,24 +46,24 @@
|
||||||
|
|
||||||
static void cl_reset_writer(struct Curl_easy *data)
|
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) {
|
while(writer) {
|
||||||
data->req.writer_stack = writer->next;
|
data->req.writer.stack = writer->next;
|
||||||
writer->cwt->do_close(data, writer);
|
writer->cwt->do_close(data, writer);
|
||||||
curlx_free(writer);
|
curlx_free(writer);
|
||||||
writer = data->req.writer_stack;
|
writer = data->req.writer.stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cl_reset_reader(struct Curl_easy *data)
|
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;
|
data->req.reader_started = FALSE;
|
||||||
while(reader) {
|
while(reader) {
|
||||||
data->req.reader_stack = reader->next;
|
data->req.reader.stack = reader->next;
|
||||||
reader->crt->do_close(data, reader);
|
reader->crt->do_close(data, reader);
|
||||||
curlx_free(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)
|
CURLcode Curl_client_start(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
if(data->req.rewind_read) {
|
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;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
CURL_TRC_READ(data, "client start, rewind readers");
|
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;
|
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,
|
CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
|
||||||
struct Curl_cwriter *writer)
|
struct Curl_cwriter *writer)
|
||||||
{
|
{
|
||||||
|
|
@ -340,47 +321,39 @@ static const struct Curl_cwtype cw_raw = {
|
||||||
sizeof(struct Curl_cwriter)
|
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)
|
static CURLcode do_init_writer_stack(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct Curl_cwriter *writer;
|
struct Curl_cwriter *writer;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
DEBUGASSERT(!data->req.writer_stack);
|
DEBUGASSERT(!data->req.writer.stack);
|
||||||
result = Curl_cwriter_create(&data->req.writer_stack,
|
result = Curl_cwriter_create(&data->req.writer.stack,
|
||||||
data, &Curl_cwt_out, CURL_CW_CLIENT);
|
data, &Curl_cwt_out, CURL_CW_CLIENT);
|
||||||
if(result)
|
if(result)
|
||||||
return 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);
|
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)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
cwriter_add(data, writer);
|
||||||
|
|
||||||
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
|
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)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
cwriter_add(data, writer);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -405,14 +378,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf,
|
||||||
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
|
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
|
||||||
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
|
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
|
||||||
|
|
||||||
if(!data->req.writer_stack) {
|
if(!data->req.writer.stack) {
|
||||||
result = do_init_writer_stack(data);
|
result = do_init_writer_stack(data);
|
||||||
if(result)
|
if(result)
|
||||||
return 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",
|
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
|
||||||
(unsigned int)type, len, (int)result);
|
(unsigned int)type, len, (int)result);
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -422,14 +395,14 @@ CURLcode Curl_client_flush(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
if(!data->req.writer_stack) {
|
if(!data->req.writer.stack) {
|
||||||
result = do_init_writer_stack(data);
|
result = do_init_writer_stack(data);
|
||||||
if(result)
|
if(result)
|
||||||
return 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);
|
CURL_TRC_WRITE(data, "client_flush() -> %d", (int)result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -476,18 +449,33 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
|
||||||
struct Curl_cwriter *w;
|
struct Curl_cwriter *w;
|
||||||
size_t n = 0;
|
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)
|
if(w->phase == phase)
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
return 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,
|
CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
||||||
struct Curl_cwriter *writer)
|
struct Curl_cwriter *writer)
|
||||||
{
|
{
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
struct Curl_cwriter **anchor = &data->req.writer_stack;
|
struct Curl_cwriter **anchor = &data->req.writer.stack;
|
||||||
|
|
||||||
if(!*anchor) {
|
if(!*anchor) {
|
||||||
result = do_init_writer_stack(data);
|
result = do_init_writer_stack(data);
|
||||||
|
|
@ -495,12 +483,16 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert the writer as first in its phase.
|
if(writer->phase == CURL_CW_CONTENT_DECODE) {
|
||||||
* Skip existing writers of lower phases. */
|
/* On adding a content decoder, add the pause writer. Do this
|
||||||
while(*anchor && (*anchor)->phase < writer->phase)
|
* BEFORE the given writer as any failure will make the
|
||||||
anchor = &((*anchor)->next);
|
* caller destroy the writer again. */
|
||||||
writer->next = *anchor;
|
result = cwriter_ensure_pause_writer(data);
|
||||||
*anchor = writer;
|
if(result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
cwriter_add(data, writer);
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,7 +500,7 @@ struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct Curl_cwriter *writer;
|
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))
|
if(!strcmp(name, writer->cwt->name))
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
@ -519,34 +511,17 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
|
||||||
const struct Curl_cwtype *cwt)
|
const struct Curl_cwtype *cwt)
|
||||||
{
|
{
|
||||||
struct Curl_cwriter *writer;
|
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)
|
if(writer->cwt == cwt)
|
||||||
return writer;
|
return writer;
|
||||||
}
|
}
|
||||||
return NULL;
|
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 Curl_cwriter_unpause(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
CURLcode result = Curl_cw_out_unpause(data);
|
data->req.writer.paused = FALSE;
|
||||||
if(!result)
|
return Curl_cwriter_flush(data, data->req.writer.stack);
|
||||||
result = Curl_cwriter_flush(data, data->req.writer_stack);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLcode Curl_creader_read(struct Curl_easy *data,
|
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);
|
||||||
DEBUGASSERT(r->crt);
|
DEBUGASSERT(r->crt);
|
||||||
DEBUGASSERT(r->phase == CURL_CR_CLIENT);
|
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);
|
clen = r->crt->total_length(data, r);
|
||||||
/* if we do not have 0 length init, and crlf conversion is wanted,
|
/* if we do not have 0 length init, and crlf conversion is wanted,
|
||||||
* add the reader for it */
|
* add the reader for it */
|
||||||
|
|
@ -1184,7 +1159,7 @@ CURLcode Curl_creader_add(struct Curl_easy *data,
|
||||||
struct Curl_creader *reader)
|
struct Curl_creader *reader)
|
||||||
{
|
{
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
struct Curl_creader **anchor = &data->req.reader_stack;
|
struct Curl_creader **anchor = &data->req.reader.stack;
|
||||||
|
|
||||||
if(!*anchor) {
|
if(!*anchor) {
|
||||||
result = Curl_creader_set_fread(data, data->state.infilesize);
|
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);
|
DEBUGASSERT(eos);
|
||||||
*nread = 0;
|
*nread = 0;
|
||||||
|
|
||||||
if(!data->req.reader_stack) {
|
if(!data->req.reader.stack) {
|
||||||
result = Curl_creader_set_fread(data, data->state.infilesize);
|
result = Curl_creader_set_fread(data, data->state.infilesize);
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
DEBUGASSERT(data->req.reader_stack);
|
DEBUGASSERT(data->req.reader.stack);
|
||||||
}
|
}
|
||||||
if(!data->req.reader_started) {
|
if(!data->req.reader_started) {
|
||||||
Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1);
|
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)
|
if(ul_avail < (curl_off_t)blen)
|
||||||
blen = (size_t)ul_avail;
|
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);
|
nread, eos);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
@ -1259,7 +1234,7 @@ out:
|
||||||
|
|
||||||
bool Curl_creader_needs_rewind(struct Curl_easy *data)
|
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) {
|
while(reader) {
|
||||||
if(reader->crt->needs_rewind(data, reader)) {
|
if(reader->crt->needs_rewind(data, reader)) {
|
||||||
CURL_TRC_READ(data, "client reader needs rewind before next request");
|
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)
|
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;
|
return r ? r->crt->total_length(data, r) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_off_t Curl_creader_client_length(struct Curl_easy *data)
|
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)
|
while(r && r->phase != CURL_CR_CLIENT)
|
||||||
r = r->next;
|
r = r->next;
|
||||||
return r ? r->crt->total_length(data, r) : -1;
|
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)
|
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)
|
while(r && r->phase != CURL_CR_CLIENT)
|
||||||
r = r->next;
|
r = r->next;
|
||||||
return r ? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
|
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)
|
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;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
while(reader) {
|
while(reader) {
|
||||||
|
|
@ -1482,7 +1457,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data)
|
||||||
|
|
||||||
bool Curl_creader_is_paused(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) {
|
while(reader) {
|
||||||
if(reader->crt->is_paused(data, 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)
|
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) {
|
while(reader) {
|
||||||
reader->crt->done(data, reader, premature);
|
reader->crt->done(data, reader, premature);
|
||||||
reader = reader->next;
|
reader = reader->next;
|
||||||
|
|
@ -1505,7 +1480,7 @@ struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
|
||||||
const struct Curl_crtype *crt)
|
const struct Curl_crtype *crt)
|
||||||
{
|
{
|
||||||
struct Curl_creader *r;
|
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)
|
if(r->crt == crt)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
lib/sendf.h
24
lib/sendf.h
|
|
@ -24,6 +24,7 @@
|
||||||
*
|
*
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
#include "curl_setup.h"
|
#include "curl_setup.h"
|
||||||
|
#include "cw-out.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of data that is being written to the client (application)
|
* 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_RAW, /* raw data written, before any decoding */
|
||||||
CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
|
CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
|
||||||
CURL_CW_PROTOCOL, /* after transfer, but before content decoding */
|
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_CONTENT_DECODE, /* remove content-encodings */
|
||||||
CURL_CW_CLIENT /* data written to client */
|
CURL_CW_CLIENT /* data written to client */
|
||||||
} Curl_cwriter_phase;
|
} 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,
|
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
/**
|
/* Convenience method for calling `writer->do_write()` that
|
||||||
* Convenience method for calling `writer->do_write()` that
|
* checks for NULL writer. */
|
||||||
* 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)
|
||||||
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);
|
|
||||||
|
|
||||||
/**
|
#define Curl_cwriter_flush(d, w) \
|
||||||
* Return TRUE iff client writer is paused.
|
((w) ? (w)->cwt->do_flush((d), (w)) : CURLE_WRITE_ERROR)
|
||||||
*/
|
|
||||||
bool Curl_cwriter_is_paused(struct Curl_easy *data);
|
|
||||||
|
|
||||||
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.
|
* Unpause client writer and flush any buffered date to the client.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ from testenv import (
|
||||||
Dante,
|
Dante,
|
||||||
Env,
|
Env,
|
||||||
ExecResult,
|
ExecResult,
|
||||||
|
H2oServer,
|
||||||
Httpd,
|
Httpd,
|
||||||
NghttpxQuic,
|
NghttpxQuic,
|
||||||
RunProfile,
|
RunProfile,
|
||||||
|
|
@ -776,17 +777,19 @@ def run_score(args, protocol):
|
||||||
uploads = None
|
uploads = None
|
||||||
requests = args.requests
|
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
|
rv = 0
|
||||||
env = Env()
|
env = Env()
|
||||||
env.setup()
|
env.setup()
|
||||||
env.test_timeout = None
|
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
|
sockd = None
|
||||||
socks_args = None
|
socks_args = None
|
||||||
if args.socks4 and args.socks5:
|
if args.socks4 and args.socks5:
|
||||||
|
|
@ -803,6 +806,7 @@ def run_score(args, protocol):
|
||||||
httpd = None
|
httpd = None
|
||||||
nghttpx = None
|
nghttpx = None
|
||||||
caddy = None
|
caddy = None
|
||||||
|
h2o = None
|
||||||
try:
|
try:
|
||||||
cards = []
|
cards = []
|
||||||
|
|
||||||
|
|
@ -859,6 +863,26 @@ def run_score(args, protocol):
|
||||||
card.setup_resources(server_docs, downloads)
|
card.setup_resources(server_docs, downloads)
|
||||||
cards.append(card)
|
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:
|
if test_caddy and env.caddy:
|
||||||
backend = ''
|
backend = ''
|
||||||
if uploads and httpd is None:
|
if uploads and httpd is None:
|
||||||
|
|
@ -921,6 +945,8 @@ def run_score(args, protocol):
|
||||||
caddy.stop()
|
caddy.stop()
|
||||||
if nghttpx:
|
if nghttpx:
|
||||||
nghttpx.stop(wait_dead=False)
|
nghttpx.stop(wait_dead=False)
|
||||||
|
if h2o:
|
||||||
|
h2o.stop()
|
||||||
if httpd:
|
if httpd:
|
||||||
httpd.stop()
|
httpd.stop()
|
||||||
if sockd:
|
if sockd:
|
||||||
|
|
@ -951,6 +977,8 @@ def main():
|
||||||
default=1, help="how many sample runs to make")
|
default=1, help="how many sample runs to make")
|
||||||
parser.add_argument("--httpd", action='store_true', default=False,
|
parser.add_argument("--httpd", action='store_true', default=False,
|
||||||
help="evaluate httpd server only")
|
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,
|
parser.add_argument("--caddy", action='store_true', default=False,
|
||||||
help="evaluate caddy server only")
|
help="evaluate caddy server only")
|
||||||
parser.add_argument("--curl-verbose", action='store_true',
|
parser.add_argument("--curl-verbose", action='store_true',
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ from .client import LocalClient
|
||||||
from .dante import Dante
|
from .dante import Dante
|
||||||
from .dnsd import Dnsd
|
from .dnsd import Dnsd
|
||||||
from .env import Env
|
from .env import Env
|
||||||
|
from .h2o import H2oServer, H2oProxy
|
||||||
from .httpd import Httpd
|
from .httpd import Httpd
|
||||||
from .nghttpx import Nghttpx, NghttpxFwd, NghttpxQuic
|
from .nghttpx import Nghttpx, NghttpxFwd, NghttpxQuic
|
||||||
from .sshd import Sshd
|
from .sshd import Sshd
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue