Path: blob/master/Utilities/cmcurl/lib/content_encoding.c
3153 views
/***************************************************************************1* _ _ ____ _2* Project ___| | | | _ \| |3* / __| | | | |_) | |4* | (__| |_| | _ <| |___5* \___|\___/|_| \_\_____|6*7* Copyright (C) Daniel Stenberg, <[email protected]>, et al.8*9* This software is licensed as described in the file COPYING, which10* you should have received as part of this distribution. The terms11* are also available at https://curl.se/docs/copyright.html.12*13* You may opt to use, copy, modify, merge, publish, distribute and/or sell14* copies of the Software, and permit persons to whom the Software is15* furnished to do so, under the terms of the COPYING file.16*17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY18* KIND, either express or implied.19*20* SPDX-License-Identifier: curl21*22***************************************************************************/2324#include "curl_setup.h"2526#include "urldata.h"27#include <curl/curl.h>28#include <stddef.h>2930#ifdef HAVE_LIBZ31#include <cm3p/zlib.h>32#endif3334#ifdef HAVE_BROTLI35#if defined(__GNUC__) || defined(__clang__)36/* Ignore -Wvla warnings in brotli headers */37#pragma GCC diagnostic push38#pragma GCC diagnostic ignored "-Wvla"39#endif40#include <brotli/decode.h>41#if defined(__GNUC__) || defined(__clang__)42#pragma GCC diagnostic pop43#endif44#endif4546#ifdef HAVE_ZSTD47#include <zstd.h>48#endif4950#include "sendf.h"51#include "http.h"52#include "content_encoding.h"53#include "strdup.h"5455/* The last 3 #include files should be in this order */56#include "curl_printf.h"57#include "curl_memory.h"58#include "memdebug.h"5960#define CONTENT_ENCODING_DEFAULT "identity"6162#ifndef CURL_DISABLE_HTTP6364/* allow no more than 5 "chained" compression steps */65#define MAX_ENCODE_STACK 56667#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)68#define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */69#endif7071#ifdef HAVE_LIBZ7273#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)74#error "requires zlib 1.2.5.2 or newer"75#endif7677typedef enum {78ZLIB_UNINIT, /* uninitialized */79ZLIB_INIT, /* initialized */80ZLIB_INFLATING, /* inflating started. */81ZLIB_EXTERNAL_TRAILER, /* reading external trailer */82ZLIB_INIT_GZIP /* initialized in transparent gzip mode */83} zlibInitState;8485/* Deflate and gzip writer. */86struct zlib_writer {87struct Curl_cwriter super;88zlibInitState zlib_init; /* zlib init state */89char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */90uInt trailerlen; /* Remaining trailer byte count. */91z_stream z; /* State structure for zlib. */92};939495static voidpf96zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)97{98(void) opaque;99/* not a typo, keep it calloc() */100return (voidpf) calloc(items, size);101}102103static void104zfree_cb(voidpf opaque, voidpf ptr)105{106(void) opaque;107free(ptr);108}109110static CURLcode111process_zlib_error(struct Curl_easy *data, z_stream *z)112{113if(z->msg)114failf(data, "Error while processing content unencoding: %s",115z->msg);116else117failf(data, "Error while processing content unencoding: "118"Unknown failure within decompression software.");119120return CURLE_BAD_CONTENT_ENCODING;121}122123static CURLcode124exit_zlib(struct Curl_easy *data,125z_stream *z, zlibInitState *zlib_init, CURLcode result)126{127if(*zlib_init != ZLIB_UNINIT) {128if(inflateEnd(z) != Z_OK && result == CURLE_OK)129result = process_zlib_error(data, z);130*zlib_init = ZLIB_UNINIT;131}132133return result;134}135136static CURLcode process_trailer(struct Curl_easy *data,137struct zlib_writer *zp)138{139z_stream *z = &zp->z;140CURLcode result = CURLE_OK;141uInt len = z->avail_in < zp->trailerlen ? z->avail_in : zp->trailerlen;142143/* Consume expected trailer bytes. Terminate stream if exhausted.144Issue an error if unexpected bytes follow. */145146zp->trailerlen -= len;147z->avail_in -= len;148z->next_in += len;149if(z->avail_in)150result = CURLE_WRITE_ERROR;151if(result || !zp->trailerlen)152result = exit_zlib(data, z, &zp->zlib_init, result);153else {154/* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */155zp->zlib_init = ZLIB_EXTERNAL_TRAILER;156}157return result;158}159160static CURLcode inflate_stream(struct Curl_easy *data,161struct Curl_cwriter *writer, int type,162zlibInitState started)163{164struct zlib_writer *zp = (struct zlib_writer *) writer;165z_stream *z = &zp->z; /* zlib state structure */166uInt nread = z->avail_in;167z_const Bytef *orig_in = z->next_in;168bool done = FALSE;169CURLcode result = CURLE_OK; /* Curl_client_write status */170171/* Check state. */172if(zp->zlib_init != ZLIB_INIT &&173zp->zlib_init != ZLIB_INFLATING &&174zp->zlib_init != ZLIB_INIT_GZIP)175return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);176177/* because the buffer size is fixed, iteratively decompress and transfer to178the client via next_write function. */179while(!done) {180int status; /* zlib status */181done = TRUE;182183/* (re)set buffer for decompressed output for every iteration */184z->next_out = (Bytef *) zp->buffer;185z->avail_out = DECOMPRESS_BUFFER_SIZE;186187status = inflate(z, Z_BLOCK);188189/* Flush output data if some. */190if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {191if(status == Z_OK || status == Z_STREAM_END) {192zp->zlib_init = started; /* Data started. */193result = Curl_cwriter_write(data, writer->next, type, zp->buffer,194DECOMPRESS_BUFFER_SIZE - z->avail_out);195if(result) {196exit_zlib(data, z, &zp->zlib_init, result);197break;198}199}200}201202/* Dispatch by inflate() status. */203switch(status) {204case Z_OK:205/* Always loop: there may be unflushed latched data in zlib state. */206done = FALSE;207break;208case Z_BUF_ERROR:209/* No more data to flush: just exit loop. */210break;211case Z_STREAM_END:212result = process_trailer(data, zp);213break;214case Z_DATA_ERROR:215/* some servers seem to not generate zlib headers, so this is an attempt216to fix and continue anyway */217if(zp->zlib_init == ZLIB_INIT) {218if(inflateReset2(z, -MAX_WBITS) == Z_OK) {219z->next_in = orig_in;220z->avail_in = nread;221zp->zlib_init = ZLIB_INFLATING;222zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */223done = FALSE;224break;225}226zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */227}228result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));229break;230default:231result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));232break;233}234}235236/* We are about to leave this call so the `nread' data bytes will not be seen237again. If we are in a state that would wrongly allow restart in raw mode238at the next call, assume output has already started. */239if(nread && zp->zlib_init == ZLIB_INIT)240zp->zlib_init = started; /* Cannot restart anymore. */241242return result;243}244245246/* Deflate handler. */247static CURLcode deflate_do_init(struct Curl_easy *data,248struct Curl_cwriter *writer)249{250struct zlib_writer *zp = (struct zlib_writer *) writer;251z_stream *z = &zp->z; /* zlib state structure */252253/* Initialize zlib */254z->zalloc = (alloc_func) zalloc_cb;255z->zfree = (free_func) zfree_cb;256257if(inflateInit(z) != Z_OK)258return process_zlib_error(data, z);259zp->zlib_init = ZLIB_INIT;260return CURLE_OK;261}262263static CURLcode deflate_do_write(struct Curl_easy *data,264struct Curl_cwriter *writer, int type,265const char *buf, size_t nbytes)266{267struct zlib_writer *zp = (struct zlib_writer *) writer;268z_stream *z = &zp->z; /* zlib state structure */269270if(!(type & CLIENTWRITE_BODY) || !nbytes)271return Curl_cwriter_write(data, writer->next, type, buf, nbytes);272273/* Set the compressed input when this function is called */274z->next_in = (z_const Bytef *)buf;275z->avail_in = (uInt)nbytes;276277if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)278return process_trailer(data, zp);279280/* Now uncompress the data */281return inflate_stream(data, writer, type, ZLIB_INFLATING);282}283284static void deflate_do_close(struct Curl_easy *data,285struct Curl_cwriter *writer)286{287struct zlib_writer *zp = (struct zlib_writer *) writer;288z_stream *z = &zp->z; /* zlib state structure */289290exit_zlib(data, z, &zp->zlib_init, CURLE_OK);291}292293static const struct Curl_cwtype deflate_encoding = {294"deflate",295NULL,296deflate_do_init,297deflate_do_write,298deflate_do_close,299sizeof(struct zlib_writer)300};301302303/* Gzip handler. */304static CURLcode gzip_do_init(struct Curl_easy *data,305struct Curl_cwriter *writer)306{307struct zlib_writer *zp = (struct zlib_writer *) writer;308z_stream *z = &zp->z; /* zlib state structure */309310/* Initialize zlib */311z->zalloc = (alloc_func) zalloc_cb;312z->zfree = (free_func) zfree_cb;313314if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)315return process_zlib_error(data, z);316317zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */318return CURLE_OK;319}320321static CURLcode gzip_do_write(struct Curl_easy *data,322struct Curl_cwriter *writer, int type,323const char *buf, size_t nbytes)324{325struct zlib_writer *zp = (struct zlib_writer *) writer;326z_stream *z = &zp->z; /* zlib state structure */327328if(!(type & CLIENTWRITE_BODY) || !nbytes)329return Curl_cwriter_write(data, writer->next, type, buf, nbytes);330331if(zp->zlib_init == ZLIB_INIT_GZIP) {332/* Let zlib handle the gzip decompression entirely */333z->next_in = (z_const Bytef *)buf;334z->avail_in = (uInt)nbytes;335/* Now uncompress the data */336return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);337}338339/* We are running with an old version: return error. */340return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);341}342343static void gzip_do_close(struct Curl_easy *data,344struct Curl_cwriter *writer)345{346struct zlib_writer *zp = (struct zlib_writer *) writer;347z_stream *z = &zp->z; /* zlib state structure */348349exit_zlib(data, z, &zp->zlib_init, CURLE_OK);350}351352static const struct Curl_cwtype gzip_encoding = {353"gzip",354"x-gzip",355gzip_do_init,356gzip_do_write,357gzip_do_close,358sizeof(struct zlib_writer)359};360361#endif /* HAVE_LIBZ */362363#ifdef HAVE_BROTLI364/* Brotli writer. */365struct brotli_writer {366struct Curl_cwriter super;367char buffer[DECOMPRESS_BUFFER_SIZE];368BrotliDecoderState *br; /* State structure for brotli. */369};370371static CURLcode brotli_map_error(BrotliDecoderErrorCode be)372{373switch(be) {374case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:375case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:376case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:377case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:378case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:379case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:380case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:381case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:382case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:383case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:384case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:385case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:386case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:387case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:388#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY389case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:390#endif391#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET392case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:393#endif394case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:395return CURLE_BAD_CONTENT_ENCODING;396case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:397case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:398case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:399case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:400case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:401case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:402return CURLE_OUT_OF_MEMORY;403default:404break;405}406return CURLE_WRITE_ERROR;407}408409static CURLcode brotli_do_init(struct Curl_easy *data,410struct Curl_cwriter *writer)411{412struct brotli_writer *bp = (struct brotli_writer *) writer;413(void) data;414415bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);416return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;417}418419static CURLcode brotli_do_write(struct Curl_easy *data,420struct Curl_cwriter *writer, int type,421const char *buf, size_t nbytes)422{423struct brotli_writer *bp = (struct brotli_writer *) writer;424const uint8_t *src = (const uint8_t *) buf;425uint8_t *dst;426size_t dstleft;427CURLcode result = CURLE_OK;428BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;429430if(!(type & CLIENTWRITE_BODY) || !nbytes)431return Curl_cwriter_write(data, writer->next, type, buf, nbytes);432433if(!bp->br)434return CURLE_WRITE_ERROR; /* Stream already ended. */435436while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&437result == CURLE_OK) {438dst = (uint8_t *) bp->buffer;439dstleft = DECOMPRESS_BUFFER_SIZE;440r = BrotliDecoderDecompressStream(bp->br,441&nbytes, &src, &dstleft, &dst, NULL);442result = Curl_cwriter_write(data, writer->next, type,443bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);444if(result)445break;446switch(r) {447case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:448case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:449break;450case BROTLI_DECODER_RESULT_SUCCESS:451BrotliDecoderDestroyInstance(bp->br);452bp->br = NULL;453if(nbytes)454result = CURLE_WRITE_ERROR;455break;456default:457result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));458break;459}460}461return result;462}463464static void brotli_do_close(struct Curl_easy *data,465struct Curl_cwriter *writer)466{467struct brotli_writer *bp = (struct brotli_writer *) writer;468(void) data;469470if(bp->br) {471BrotliDecoderDestroyInstance(bp->br);472bp->br = NULL;473}474}475476static const struct Curl_cwtype brotli_encoding = {477"br",478NULL,479brotli_do_init,480brotli_do_write,481brotli_do_close,482sizeof(struct brotli_writer)483};484#endif485486#ifdef HAVE_ZSTD487/* Zstd writer. */488struct zstd_writer {489struct Curl_cwriter super;490ZSTD_DStream *zds; /* State structure for zstd. */491char buffer[DECOMPRESS_BUFFER_SIZE];492};493494#ifdef ZSTD_STATIC_LINKING_ONLY495static void *Curl_zstd_alloc(void *opaque, size_t size)496{497(void)opaque;498return Curl_cmalloc(size);499}500501static void Curl_zstd_free(void *opaque, void *address)502{503(void)opaque;504Curl_cfree(address);505}506#endif507508static CURLcode zstd_do_init(struct Curl_easy *data,509struct Curl_cwriter *writer)510{511struct zstd_writer *zp = (struct zstd_writer *) writer;512513(void)data;514515#ifdef ZSTD_STATIC_LINKING_ONLY516zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {517.customAlloc = Curl_zstd_alloc,518.customFree = Curl_zstd_free,519.opaque = NULL520});521#else522zp->zds = ZSTD_createDStream();523#endif524525return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;526}527528static CURLcode zstd_do_write(struct Curl_easy *data,529struct Curl_cwriter *writer, int type,530const char *buf, size_t nbytes)531{532CURLcode result = CURLE_OK;533struct zstd_writer *zp = (struct zstd_writer *) writer;534ZSTD_inBuffer in;535ZSTD_outBuffer out;536size_t errorCode;537538if(!(type & CLIENTWRITE_BODY) || !nbytes)539return Curl_cwriter_write(data, writer->next, type, buf, nbytes);540541in.pos = 0;542in.src = buf;543in.size = nbytes;544545for(;;) {546out.pos = 0;547out.dst = zp->buffer;548out.size = DECOMPRESS_BUFFER_SIZE;549550errorCode = ZSTD_decompressStream(zp->zds, &out, &in);551if(ZSTD_isError(errorCode)) {552return CURLE_BAD_CONTENT_ENCODING;553}554if(out.pos > 0) {555result = Curl_cwriter_write(data, writer->next, type,556zp->buffer, out.pos);557if(result)558break;559}560if((in.pos == nbytes) && (out.pos < out.size))561break;562}563564return result;565}566567static void zstd_do_close(struct Curl_easy *data,568struct Curl_cwriter *writer)569{570struct zstd_writer *zp = (struct zstd_writer *) writer;571(void)data;572573if(zp->zds) {574ZSTD_freeDStream(zp->zds);575zp->zds = NULL;576}577}578579static const struct Curl_cwtype zstd_encoding = {580"zstd",581NULL,582zstd_do_init,583zstd_do_write,584zstd_do_close,585sizeof(struct zstd_writer)586};587#endif588589/* Identity handler. */590static const struct Curl_cwtype identity_encoding = {591"identity",592"none",593Curl_cwriter_def_init,594Curl_cwriter_def_write,595Curl_cwriter_def_close,596sizeof(struct Curl_cwriter)597};598599/* supported general content decoders. */600static const struct Curl_cwtype * const general_unencoders[] = {601&identity_encoding,602#ifdef HAVE_LIBZ603&deflate_encoding,604&gzip_encoding,605#endif606#ifdef HAVE_BROTLI607&brotli_encoding,608#endif609#ifdef HAVE_ZSTD610&zstd_encoding,611#endif612NULL613};614615/* supported content decoders only for transfer encodings */616static const struct Curl_cwtype * const transfer_unencoders[] = {617#ifndef CURL_DISABLE_HTTP618&Curl_httpchunk_unencoder,619#endif620NULL621};622623/* Provide a list of comma-separated names of supported encodings.624*/625void Curl_all_content_encodings(char *buf, size_t blen)626{627size_t len = 0;628const struct Curl_cwtype * const *cep;629const struct Curl_cwtype *ce;630631DEBUGASSERT(buf);632DEBUGASSERT(blen);633buf[0] = 0;634635for(cep = general_unencoders; *cep; cep++) {636ce = *cep;637if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT))638len += strlen(ce->name) + 2;639}640641if(!len) {642if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))643strcpy(buf, CONTENT_ENCODING_DEFAULT);644}645else if(blen > len) {646char *p = buf;647for(cep = general_unencoders; *cep; cep++) {648ce = *cep;649if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {650strcpy(p, ce->name);651p += strlen(p);652*p++ = ',';653*p++ = ' ';654}655}656p[-2] = '\0';657}658}659660/* Deferred error dummy writer. */661static CURLcode error_do_init(struct Curl_easy *data,662struct Curl_cwriter *writer)663{664(void)data;665(void)writer;666return CURLE_OK;667}668669static CURLcode error_do_write(struct Curl_easy *data,670struct Curl_cwriter *writer, int type,671const char *buf, size_t nbytes)672{673(void) writer;674(void) buf;675(void) nbytes;676677if(!(type & CLIENTWRITE_BODY) || !nbytes)678return Curl_cwriter_write(data, writer->next, type, buf, nbytes);679else {680char all[256];681(void)Curl_all_content_encodings(all, sizeof(all));682failf(data, "Unrecognized content encoding type. "683"libcurl understands %s content encodings.", all);684}685return CURLE_BAD_CONTENT_ENCODING;686}687688static void error_do_close(struct Curl_easy *data,689struct Curl_cwriter *writer)690{691(void) data;692(void) writer;693}694695static const struct Curl_cwtype error_writer = {696"ce-error",697NULL,698error_do_init,699error_do_write,700error_do_close,701sizeof(struct Curl_cwriter)702};703704/* Find the content encoding by name. */705static const struct Curl_cwtype *find_unencode_writer(const char *name,706size_t len,707Curl_cwriter_phase phase)708{709const struct Curl_cwtype * const *cep;710711if(phase == CURL_CW_TRANSFER_DECODE) {712for(cep = transfer_unencoders; *cep; cep++) {713const struct Curl_cwtype *ce = *cep;714if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||715(ce->alias && curl_strnequal(name, ce->alias, len)716&& !ce->alias[len]))717return ce;718}719}720/* look among the general decoders */721for(cep = general_unencoders; *cep; cep++) {722const struct Curl_cwtype *ce = *cep;723if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||724(ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len]))725return ce;726}727return NULL;728}729730/* Setup the unencoding stack from the Content-Encoding header value.731* See RFC 7231 section 3.1.2.2. */732CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,733const char *enclist, int is_transfer)734{735Curl_cwriter_phase phase = is_transfer ?736CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;737CURLcode result;738bool has_chunked = FALSE;739740do {741const char *name;742size_t namelen;743bool is_chunked = FALSE;744745/* Parse a single encoding name. */746while(ISBLANK(*enclist) || *enclist == ',')747enclist++;748749name = enclist;750751for(namelen = 0; *enclist && *enclist != ','; enclist++)752if(*enclist > ' ')753namelen = enclist - name + 1;754755if(namelen) {756const struct Curl_cwtype *cwt;757struct Curl_cwriter *writer;758759CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",760is_transfer ? "transfer" : "content", (int)namelen, name);761is_chunked = (is_transfer && (namelen == 7) &&762curl_strnequal(name, "chunked", 7));763/* if we skip the decoding in this phase, do not look further.764* Exception is "chunked" transfer-encoding which always must happen */765if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||766(!is_transfer && data->set.http_ce_skip)) {767bool is_identity = curl_strnequal(name, "identity", 8);768/* not requested, ignore */769CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",770(int)namelen, name);771if(is_transfer && !data->set.http_te_skip) {772if(has_chunked)773failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",774(int)namelen, name);775else if(is_identity)776continue;777else778failf(data, "Unsolicited Transfer-Encoding (%.*s) found",779(int)namelen, name);780return CURLE_BAD_CONTENT_ENCODING;781}782return CURLE_OK;783}784785if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {786failf(data, "Reject response due to more than %u content encodings",787MAX_ENCODE_STACK);788return CURLE_BAD_CONTENT_ENCODING;789}790791cwt = find_unencode_writer(name, namelen, phase);792if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {793/* A 'chunked' transfer encoding has already been added.794* Ignore duplicates. See #13451.795* Also RFC 9112, ch. 6.1:796* "A sender MUST NOT apply the chunked transfer coding more than797* once to a message body."798*/799CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");800return CURLE_OK;801}802803if(is_transfer && !is_chunked &&804Curl_cwriter_get_by_name(data, "chunked")) {805/* RFC 9112, ch. 6.1:806* "If any transfer coding other than chunked is applied to a807* response's content, the sender MUST either apply chunked as the808* final transfer coding or terminate the message by closing the809* connection."810* "chunked" must be the last added to be the first in its phase,811* reject this.812*/813failf(data, "Reject response due to 'chunked' not being the last "814"Transfer-Encoding");815return CURLE_BAD_CONTENT_ENCODING;816}817818if(!cwt)819cwt = &error_writer; /* Defer error at use. */820821result = Curl_cwriter_create(&writer, data, cwt, phase);822CURL_TRC_WRITE(data, "added %s decoder %s -> %d",823is_transfer ? "transfer" : "content", cwt->name, result);824if(result)825return result;826827result = Curl_cwriter_add(data, writer);828if(result) {829Curl_cwriter_free(data, writer);830return result;831}832if(is_chunked)833has_chunked = TRUE;834}835} while(*enclist);836837return CURLE_OK;838}839840#else841/* Stubs for builds without HTTP. */842CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,843const char *enclist, int is_transfer)844{845(void) data;846(void) enclist;847(void) is_transfer;848return CURLE_NOT_BUILT_IN;849}850851void Curl_all_content_encodings(char *buf, size_t blen)852{853DEBUGASSERT(buf);854DEBUGASSERT(blen);855if(blen < sizeof(CONTENT_ENCODING_DEFAULT))856buf[0] = 0;857else858strcpy(buf, CONTENT_ENCODING_DEFAULT);859}860861#endif /* CURL_DISABLE_HTTP */862863864