From: Stefan Eissing Date: Wed, 20 Sep 2023 09:59:16 +0000 (+0200) Subject: lib: move handling of `data->req.writer_stack` into Curl_client_write() X-Git-Tag: curl-8_4_0~99 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0bd9e137e3f0e53f03a7cd108be866bdf5963c32;p=thirdparty%2Fcurl.git lib: move handling of `data->req.writer_stack` into Curl_client_write() - move definitions from content_encoding.h to sendf.h - move create/cleanup/add code into sendf.c - installed content_encoding writers will always be called on Curl_client_write(CLIENTWRITE_BODY) - Curl_client_cleanup() frees writers and tempbuffers from paused transfers, irregardless of protocol Closes #11908 --- diff --git a/lib/c-hyper.c b/lib/c-hyper.c index c558955c9d..5726ff1cc3 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -246,11 +246,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) if(0 == len) return HYPER_ITER_CONTINUE; Curl_debug(data, CURLINFO_DATA_IN, buf, len); - if(!data->set.http_ce_skip && k->writer_stack) - /* content-encoded data */ - result = Curl_unencode_write(data, k->writer_stack, buf, len); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); if(result) { data->state.hresult = result; diff --git a/lib/content_encoding.c b/lib/content_encoding.c index efbe7cb18d..be7c075e94 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -280,9 +280,6 @@ static CURLcode deflate_init_writer(struct Curl_easy *data, struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ - if(!writer->downstream) - return CURLE_WRITE_ERROR; - /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; @@ -337,9 +334,6 @@ static CURLcode gzip_init_writer(struct Curl_easy *data, struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ - if(!writer->downstream) - return CURLE_WRITE_ERROR; - /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; @@ -647,9 +641,6 @@ static CURLcode brotli_init_writer(struct Curl_easy *data, struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; - if(!writer->downstream) - return CURLE_WRITE_ERROR; - bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; } @@ -741,9 +732,6 @@ static CURLcode zstd_init_writer(struct Curl_easy *data, (void)data; - if(!writer->downstream) - return CURLE_WRITE_ERROR; - zp->zds = ZSTD_createDStream(); zp->decomp = NULL; return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; @@ -822,8 +810,9 @@ static const struct content_encoding zstd_encoding = { static CURLcode identity_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { - (void) data; - return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; + (void)data; + (void)writer; + return CURLE_OK; } static CURLcode identity_unencode_write(struct Curl_easy *data, @@ -903,51 +892,13 @@ char *Curl_all_content_encodings(void) } -/* Real client writer: no downstream. */ -static CURLcode client_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; -} - -static CURLcode client_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - struct SingleRequest *k = &data->req; - - (void) writer; - - if(!nbytes || k->ignorebody) - return CURLE_OK; - - return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes); -} - -static void client_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding client_encoding = { - NULL, - NULL, - client_init_writer, - client_unencode_write, - client_close_writer, - sizeof(struct contenc_writer) -}; - - /* Deferred error dummy writer. */ static CURLcode error_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { - (void) data; - return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; + (void)data; + (void)writer; + return CURLE_OK; } static CURLcode error_unencode_write(struct Curl_easy *data, @@ -984,31 +935,6 @@ static const struct content_encoding error_encoding = { sizeof(struct contenc_writer) }; -/* Create an unencoding writer stage using the given handler. */ -static struct contenc_writer * -new_unencoding_writer(struct Curl_easy *data, - const struct content_encoding *handler, - struct contenc_writer *downstream, - int order) -{ - struct contenc_writer *writer; - - DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer)); - writer = (struct contenc_writer *) calloc(1, handler->writersize); - - if(writer) { - writer->handler = handler; - writer->downstream = downstream; - writer->order = order; - if(handler->init_writer(data, writer)) { - free(writer); - writer = NULL; - } - } - - return writer; -} - /* Write data using an unencoding writer stack. "nbytes" is not allowed to be 0. */ CURLcode Curl_unencode_write(struct Curl_easy *data, @@ -1017,23 +943,11 @@ CURLcode Curl_unencode_write(struct Curl_easy *data, { if(!nbytes) return CURLE_OK; + if(!writer) + return CURLE_WRITE_ERROR; return writer->handler->unencode_write(data, writer, buf, nbytes); } -/* Close and clean-up the connection's writer stack. */ -void Curl_unencode_cleanup(struct Curl_easy *data) -{ - struct SingleRequest *k = &data->req; - struct contenc_writer *writer = k->writer_stack; - - while(writer) { - k->writer_stack = writer->downstream; - writer->handler->close_writer(data, writer); - free(writer); - writer = k->writer_stack; - } -} - /* Find the content encoding by name. */ static const struct content_encoding *find_encoding(const char *name, size_t len) @@ -1049,9 +963,6 @@ static const struct content_encoding *find_encoding(const char *name, return NULL; } -/* allow no more than 5 "chained" compression steps */ -#define MAX_ENCODE_STACK 5 - /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, @@ -1059,6 +970,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, { struct SingleRequest *k = &data->req; unsigned int order = is_transfer? 2: 1; + CURLcode result; do { const char *name; @@ -1085,41 +997,19 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, if(is_transfer && !data->set.http_transfer_encoding) /* not requested, ignore */ return CURLE_OK; - encoding = find_encoding(name, namelen); - - if(!k->writer_stack) { - k->writer_stack = new_unencoding_writer(data, &client_encoding, - NULL, 0); - - if(!k->writer_stack) - return CURLE_OUT_OF_MEMORY; - } + encoding = find_encoding(name, namelen); if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ - if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %u content encodings", - MAX_ENCODE_STACK); - return CURLE_BAD_CONTENT_ENCODING; - } - /* Stack the unencoding stage. */ - if(order >= k->writer_stack->order) { - writer = new_unencoding_writer(data, encoding, - k->writer_stack, order); - if(!writer) - return CURLE_OUT_OF_MEMORY; - k->writer_stack = writer; - } - else { - struct contenc_writer *w = k->writer_stack; - while(w->downstream && order < w->downstream->order) - w = w->downstream; - writer = new_unencoding_writer(data, encoding, - w->downstream, order); - if(!writer) - return CURLE_OUT_OF_MEMORY; - w->downstream = writer; + result = Curl_client_create_writer(&writer, data, encoding, order); + if(result) + return result; + + result = Curl_client_add_writer(data, writer); + if(result) { + Curl_client_free_writer(data, writer); + return result; } } } while(*enclist); @@ -1149,11 +1039,6 @@ CURLcode Curl_unencode_write(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; } -void Curl_unencode_cleanup(struct Curl_easy *data) -{ - (void) data; -} - char *Curl_all_content_encodings(void) { return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ diff --git a/lib/content_encoding.h b/lib/content_encoding.h index 56e7f97f70..ef7930cb32 100644 --- a/lib/content_encoding.h +++ b/lib/content_encoding.h @@ -25,26 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" -struct contenc_writer { - const struct content_encoding *handler; /* Encoding handler. */ - struct contenc_writer *downstream; /* Downstream writer. */ - unsigned int order; /* Ordering within writer stack. */ -}; - -/* Content encoding writer. */ -struct content_encoding { - const char *name; /* Encoding name. */ - const char *alias; /* Encoding name alias. */ - CURLcode (*init_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - CURLcode (*unencode_write)(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); - void (*close_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - size_t writersize; -}; +struct contenc_writer; +char *Curl_all_content_encodings(void); CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer); @@ -52,6 +35,5 @@ CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes); void Curl_unencode_cleanup(struct Curl_easy *data); -char *Curl_all_content_encodings(void); #endif /* HEADER_CURL_CONTENT_ENCODING_H */ diff --git a/lib/http.c b/lib/http.c index cced2ef8e3..40ef70df50 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1602,8 +1602,6 @@ CURLcode Curl_http_done(struct Curl_easy *data, data->state.authhost.multipass = FALSE; data->state.authproxy.multipass = FALSE; - Curl_unencode_cleanup(data); - /* set the proper values (possibly modified on POST) */ conn->seek_func = data->set.seek_func; /* restore */ conn->seek_client = data->set.seek_client; /* restore */ diff --git a/lib/http_chunks.c b/lib/http_chunks.c index bda00d3833..2a401d14c1 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -175,10 +175,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, /* Write the data portion available */ if(!data->set.http_te_skip && !k->ignorebody) { - if(!data->set.http_ce_skip && k->writer_stack) - result = Curl_unencode_write(data, k->writer_stack, datap, piece); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece); + result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece); if(result) { *extrap = result; diff --git a/lib/multi.c b/lib/multi.c index 49be772a31..3170adcd7d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -665,7 +665,6 @@ static CURLcode multi_done(struct Curl_easy *data, { CURLcode result; struct connectdata *conn = data->conn; - unsigned int i; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d", @@ -721,12 +720,7 @@ static CURLcode multi_done(struct Curl_easy *data, Curl_safefree(data->state.ulbuf); - /* if the transfer was completed in a paused state there can be buffered - data left to free */ - for(i = 0; i < data->state.tempcount; i++) { - Curl_dyn_free(&data->state.tempwrite[i].b); - } - data->state.tempcount = 0; + Curl_client_cleanup(data); CONNCACHE_LOCK(data); Curl_detach_connection(data); diff --git a/lib/sendf.c b/lib/sendf.c index 9f30ffc8c8..0482c5da4e 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -40,6 +40,7 @@ #include "sendf.h" #include "cfilters.h" #include "connect.h" +#include "content_encoding.h" #include "vtls/vtls.h" #include "vssh/ssh.h" #include "easyif.h" @@ -403,6 +404,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY)); /* INFO is only INFO */ DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO)); + + if(type == CLIENTWRITE_BODY) { + if(data->req.ignorebody) + return CURLE_OK; + + if(data->req.writer_stack && !data->set.http_ce_skip) + return Curl_unencode_write(data, data->req.writer_stack, ptr, len); + } return chop_write(data, type, FALSE, ptr, len); } @@ -438,6 +447,138 @@ CURLcode Curl_client_unpause(struct Curl_easy *data) return result; } +void Curl_client_cleanup(struct Curl_easy *data) +{ + struct contenc_writer *writer = data->req.writer_stack; + size_t i; + + while(writer) { + data->req.writer_stack = writer->downstream; + writer->handler->close_writer(data, writer); + free(writer); + writer = data->req.writer_stack; + } + + for(i = 0; i < data->state.tempcount; i++) { + Curl_dyn_free(&data->state.tempwrite[i].b); + } + data->state.tempcount = 0; + +} + +/* Real client writer: no downstream. */ +static CURLcode client_cew_init(struct Curl_easy *data, + struct contenc_writer *writer) +{ + (void) data; + (void)writer; + return CURLE_OK; +} + +static CURLcode client_cew_write(struct Curl_easy *data, + struct contenc_writer *writer, + const char *buf, size_t nbytes) +{ + (void)writer; + if(!nbytes || data->req.ignorebody) + return CURLE_OK; + return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes); +} + +static void client_cew_close(struct Curl_easy *data, + struct contenc_writer *writer) +{ + (void) data; + (void) writer; +} + +static const struct content_encoding client_cew = { + NULL, + NULL, + client_cew_init, + client_cew_write, + client_cew_close, + sizeof(struct contenc_writer) +}; + +/* Create an unencoding writer stage using the given handler. */ +CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, + struct Curl_easy *data, + const struct content_encoding *ce_handler, + int order) +{ + struct contenc_writer *writer; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer)); + writer = (struct contenc_writer *) calloc(1, ce_handler->writersize); + if(!writer) + goto out; + + writer->handler = ce_handler; + writer->order = order; + result = ce_handler->init_writer(data, writer); + +out: + *pwriter = result? NULL : writer; + if(result) + free(writer); + return result; +} + +void Curl_client_free_writer(struct Curl_easy *data, + struct contenc_writer *writer) +{ + if(writer) { + writer->handler->close_writer(data, writer); + free(writer); + } +} + +/* allow no more than 5 "chained" compression steps */ +#define MAX_ENCODE_STACK 5 + + +static CURLcode init_writer_stack(struct Curl_easy *data) +{ + DEBUGASSERT(!data->req.writer_stack); + return Curl_client_create_writer(&data->req.writer_stack, + data, &client_cew, 0); +} + +CURLcode Curl_client_add_writer(struct Curl_easy *data, + struct contenc_writer *writer) +{ + CURLcode result; + + if(!data->req.writer_stack) { + result = init_writer_stack(data); + if(result) + return result; + } + + if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Stack the unencoding stage. */ + if(writer->order >= data->req.writer_stack->order) { + writer->downstream = data->req.writer_stack; + data->req.writer_stack = writer; + } + else { + struct contenc_writer *w = data->req.writer_stack; + while(w->downstream && writer->order < w->downstream->order) + w = w->downstream; + writer->downstream = w->downstream; + w->downstream = writer; + } + return CURLE_OK; +} + + /* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. diff --git a/lib/sendf.h b/lib/sendf.h index f55110f963..3fb07f64a9 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -54,6 +54,40 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; CURLcode Curl_client_unpause(struct Curl_easy *data); +void Curl_client_cleanup(struct Curl_easy *data); + +struct contenc_writer { + const struct content_encoding *handler; /* Encoding handler. */ + struct contenc_writer *downstream; /* Downstream writer. */ + unsigned int order; /* Ordering within writer stack. */ +}; + +/* Content encoding writer. */ +struct content_encoding { + const char *name; /* Encoding name. */ + const char *alias; /* Encoding name alias. */ + CURLcode (*init_writer)(struct Curl_easy *data, + struct contenc_writer *writer); + CURLcode (*unencode_write)(struct Curl_easy *data, + struct contenc_writer *writer, + const char *buf, size_t nbytes); + void (*close_writer)(struct Curl_easy *data, + struct contenc_writer *writer); + size_t writersize; +}; + + +CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, + struct Curl_easy *data, + const struct content_encoding *ce_handler, + int order); + +void Curl_client_free_writer(struct Curl_easy *data, + struct contenc_writer *writer); + +CURLcode Curl_client_add_writer(struct Curl_easy *data, + struct contenc_writer *writer); + /* internal read-function, does plain socket, SSL and krb4 */ CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd, diff --git a/lib/transfer.c b/lib/transfer.c index b9ce2b5131..92ce0dfd9a 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -700,19 +700,15 @@ static CURLcode readwrite_data(struct Curl_easy *data, in http_chunks.c. Make sure that ALL_CONTENT_ENCODINGS contains all the encodings handled here. */ - if(data->set.http_ce_skip || !k->writer_stack) { - if(!k->ignorebody && nread) { + if(!k->ignorebody && nread) { #ifndef CURL_DISABLE_POP3 - if(conn->handler->protocol & PROTO_FAMILY_POP3) - result = Curl_pop3_write(data, k->str, nread); - else + if(conn->handler->protocol & PROTO_FAMILY_POP3) + result = Curl_pop3_write(data, k->str, nread); + else #endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, - nread); - } + result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, + nread); } - else if(!k->ignorebody && nread) - result = Curl_unencode_write(data, k->writer_stack, k->str, nread); } k->badheader = HEADER_NORMAL; /* taken care of now */ diff --git a/lib/url.c b/lib/url.c index 6d60477779..f9120ee03e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2033,13 +2033,13 @@ void Curl_free_request_state(struct Curl_easy *data) { Curl_safefree(data->req.p.http); Curl_safefree(data->req.newurl); - #ifndef CURL_DISABLE_DOH if(data->req.doh) { Curl_close(&data->req.doh->probe[0].easy); Curl_close(&data->req.doh->probe[1].easy); } #endif + Curl_client_cleanup(data); }