Path: blob/master/Utilities/cmcurl/lib/content_encoding.c
5012 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***************************************************************************/23#include "curl_setup.h"2425#include "urldata.h"26#include "curlx/dynbuf.h"2728#ifdef HAVE_LIBZ29#include <cm3p/zlib.h>30#endif3132#ifdef HAVE_BROTLI33#if defined(__GNUC__) || defined(__clang__)34/* Ignore -Wvla warnings in brotli headers */35#pragma GCC diagnostic push36#pragma GCC diagnostic ignored "-Wvla"37#endif38#include <brotli/decode.h>39#if defined(__GNUC__) || defined(__clang__)40#pragma GCC diagnostic pop41#endif42#endif4344#ifdef HAVE_ZSTD45#include <zstd.h>46#endif4748#include "sendf.h"49#include "curl_trc.h"50#include "content_encoding.h"5152#define CONTENT_ENCODING_DEFAULT "identity"5354#ifndef CURL_DISABLE_HTTP5556/* allow no more than 5 "chained" compression steps */57#define MAX_ENCODE_STACK 55859#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)60#define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */61#endif6263#ifdef HAVE_LIBZ6465#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)66#error "requires zlib 1.2.5.2 or newer"67#endif6869typedef enum {70ZLIB_UNINIT, /* uninitialized */71ZLIB_INIT, /* initialized */72ZLIB_INFLATING, /* inflating started. */73ZLIB_EXTERNAL_TRAILER, /* reading external trailer */74ZLIB_INIT_GZIP /* initialized in transparent gzip mode */75} zlibInitState;7677/* Deflate and gzip writer. */78struct zlib_writer {79struct Curl_cwriter super;80zlibInitState zlib_init; /* zlib init state */81char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */82uInt trailerlen; /* Remaining trailer byte count. */83z_stream z; /* State structure for zlib. */84};8586static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)87{88(void)opaque;89/* not a typo, keep it curlx_calloc() */90return (voidpf)curlx_calloc(items, size);91}9293static void zfree_cb(voidpf opaque, voidpf ptr)94{95(void)opaque;96curlx_free(ptr);97}9899static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z)100{101if(z->msg)102failf(data, "Error while processing content unencoding: %s",103z->msg);104else105failf(data, "Error while processing content unencoding: "106"Unknown failure within decompression software.");107108return CURLE_BAD_CONTENT_ENCODING;109}110111static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z,112zlibInitState *zlib_init, CURLcode result)113{114if(*zlib_init != ZLIB_UNINIT) {115if(inflateEnd(z) != Z_OK && result == CURLE_OK)116result = process_zlib_error(data, z);117*zlib_init = ZLIB_UNINIT;118}119120return result;121}122123static CURLcode process_trailer(struct Curl_easy *data, struct zlib_writer *zp)124{125z_stream *z = &zp->z;126CURLcode result = CURLE_OK;127uInt len = z->avail_in < zp->trailerlen ? z->avail_in : zp->trailerlen;128129/* Consume expected trailer bytes. Terminate stream if exhausted.130Issue an error if unexpected bytes follow. */131132zp->trailerlen -= len;133z->avail_in -= len;134z->next_in += len;135if(z->avail_in)136result = CURLE_WRITE_ERROR;137if(result || !zp->trailerlen)138result = exit_zlib(data, z, &zp->zlib_init, result);139else {140/* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */141zp->zlib_init = ZLIB_EXTERNAL_TRAILER;142}143return result;144}145146static CURLcode inflate_stream(struct Curl_easy *data,147struct Curl_cwriter *writer, int type,148zlibInitState started)149{150struct zlib_writer *zp = (struct zlib_writer *)writer;151z_stream *z = &zp->z; /* zlib state structure */152uInt nread = z->avail_in;153z_const Bytef *orig_in = z->next_in;154bool done = FALSE;155CURLcode result = CURLE_OK; /* Curl_client_write status */156157/* Check state. */158if(zp->zlib_init != ZLIB_INIT &&159zp->zlib_init != ZLIB_INFLATING &&160zp->zlib_init != ZLIB_INIT_GZIP)161return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);162163/* because the buffer size is fixed, iteratively decompress and transfer to164the client via next_write function. */165while(!done) {166int status; /* zlib status */167done = TRUE;168169/* (re)set buffer for decompressed output for every iteration */170z->next_out = (Bytef *)zp->buffer;171z->avail_out = DECOMPRESS_BUFFER_SIZE;172173status = inflate(z, Z_BLOCK);174175/* Flush output data if some. */176if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {177if(status == Z_OK || status == Z_STREAM_END) {178zp->zlib_init = started; /* Data started. */179result = Curl_cwriter_write(data, writer->next, type, zp->buffer,180DECOMPRESS_BUFFER_SIZE - z->avail_out);181if(result) {182exit_zlib(data, z, &zp->zlib_init, result);183break;184}185}186}187188/* Dispatch by inflate() status. */189switch(status) {190case Z_OK:191/* Always loop: there may be unflushed latched data in zlib state. */192done = FALSE;193break;194case Z_BUF_ERROR:195/* No more data to flush: just exit loop. */196break;197case Z_STREAM_END:198result = process_trailer(data, zp);199break;200case Z_DATA_ERROR:201/* some servers seem to not generate zlib headers, so this is an attempt202to fix and continue anyway */203if(zp->zlib_init == ZLIB_INIT) {204if(inflateReset2(z, -MAX_WBITS) == Z_OK) {205z->next_in = orig_in;206z->avail_in = nread;207zp->zlib_init = ZLIB_INFLATING;208zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */209done = FALSE;210break;211}212zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */213}214result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));215break;216default:217result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));218break;219}220}221222/* We are about to leave this call so the `nread' data bytes will not be seen223again. If we are in a state that would wrongly allow restart in raw mode224at the next call, assume output has already started. */225if(nread && zp->zlib_init == ZLIB_INIT)226zp->zlib_init = started; /* Cannot restart anymore. */227228return result;229}230231/* Deflate handler. */232static CURLcode deflate_do_init(struct Curl_easy *data,233struct Curl_cwriter *writer)234{235struct zlib_writer *zp = (struct zlib_writer *)writer;236z_stream *z = &zp->z; /* zlib state structure */237238/* Initialize zlib */239z->zalloc = (alloc_func)zalloc_cb;240z->zfree = (free_func)zfree_cb;241242if(inflateInit(z) != Z_OK)243return process_zlib_error(data, z);244zp->zlib_init = ZLIB_INIT;245return CURLE_OK;246}247248static CURLcode deflate_do_write(struct Curl_easy *data,249struct Curl_cwriter *writer, int type,250const char *buf, size_t nbytes)251{252struct zlib_writer *zp = (struct zlib_writer *)writer;253z_stream *z = &zp->z; /* zlib state structure */254255if(!(type & CLIENTWRITE_BODY) || !nbytes)256return Curl_cwriter_write(data, writer->next, type, buf, nbytes);257258/* Set the compressed input when this function is called */259z->next_in = (z_const Bytef *)buf;260z->avail_in = (uInt)nbytes;261262if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)263return process_trailer(data, zp);264265/* Now uncompress the data */266return inflate_stream(data, writer, type, ZLIB_INFLATING);267}268269static void deflate_do_close(struct Curl_easy *data,270struct Curl_cwriter *writer)271{272struct zlib_writer *zp = (struct zlib_writer *)writer;273z_stream *z = &zp->z; /* zlib state structure */274275exit_zlib(data, z, &zp->zlib_init, CURLE_OK);276}277278static const struct Curl_cwtype deflate_encoding = {279"deflate",280NULL,281deflate_do_init,282deflate_do_write,283deflate_do_close,284sizeof(struct zlib_writer)285};286287/*288* Gzip handler.289*/290291static CURLcode gzip_do_init(struct Curl_easy *data,292struct Curl_cwriter *writer)293{294struct zlib_writer *zp = (struct zlib_writer *)writer;295z_stream *z = &zp->z; /* zlib state structure */296297/* Initialize zlib */298z->zalloc = (alloc_func)zalloc_cb;299z->zfree = (free_func)zfree_cb;300301if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)302return process_zlib_error(data, z);303304zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */305return CURLE_OK;306}307308static CURLcode gzip_do_write(struct Curl_easy *data,309struct Curl_cwriter *writer, int type,310const char *buf, size_t nbytes)311{312struct zlib_writer *zp = (struct zlib_writer *)writer;313z_stream *z = &zp->z; /* zlib state structure */314315if(!(type & CLIENTWRITE_BODY) || !nbytes)316return Curl_cwriter_write(data, writer->next, type, buf, nbytes);317318if(zp->zlib_init == ZLIB_INIT_GZIP) {319/* Let zlib handle the gzip decompression entirely */320z->next_in = (z_const Bytef *)buf;321z->avail_in = (uInt)nbytes;322/* Now uncompress the data */323return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);324}325326/* We are running with an old version: return error. */327return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);328}329330static void gzip_do_close(struct Curl_easy *data,331struct Curl_cwriter *writer)332{333struct zlib_writer *zp = (struct zlib_writer *)writer;334z_stream *z = &zp->z; /* zlib state structure */335336exit_zlib(data, z, &zp->zlib_init, CURLE_OK);337}338339static const struct Curl_cwtype gzip_encoding = {340"gzip",341"x-gzip",342gzip_do_init,343gzip_do_write,344gzip_do_close,345sizeof(struct zlib_writer)346};347348#endif /* HAVE_LIBZ */349350#ifdef HAVE_BROTLI351/* Brotli writer. */352struct brotli_writer {353struct Curl_cwriter super;354char buffer[DECOMPRESS_BUFFER_SIZE];355BrotliDecoderState *br; /* State structure for brotli. */356};357358static CURLcode brotli_map_error(BrotliDecoderErrorCode be)359{360switch(be) {361case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:362case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:363case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:364case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:365case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:366case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:367case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:368case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:369case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:370case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:371case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:372case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:373case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:374case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:375#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY /* brotli v1.1.0+ */376case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:377#endif378case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:379case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:380return CURLE_BAD_CONTENT_ENCODING;381case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:382case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:383case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:384case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:385case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:386case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:387return CURLE_OUT_OF_MEMORY;388default:389break;390}391return CURLE_WRITE_ERROR;392}393394static CURLcode brotli_do_init(struct Curl_easy *data,395struct Curl_cwriter *writer)396{397struct brotli_writer *bp = (struct brotli_writer *)writer;398(void)data;399400bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);401return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;402}403404static CURLcode brotli_do_write(struct Curl_easy *data,405struct Curl_cwriter *writer, int type,406const char *buf, size_t nbytes)407{408struct brotli_writer *bp = (struct brotli_writer *)writer;409const uint8_t *src = (const uint8_t *)buf;410uint8_t *dst;411size_t dstleft;412CURLcode result = CURLE_OK;413BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;414415if(!(type & CLIENTWRITE_BODY) || !nbytes)416return Curl_cwriter_write(data, writer->next, type, buf, nbytes);417418if(!bp->br)419return CURLE_WRITE_ERROR; /* Stream already ended. */420421while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&422result == CURLE_OK) {423dst = (uint8_t *)bp->buffer;424dstleft = DECOMPRESS_BUFFER_SIZE;425r = BrotliDecoderDecompressStream(bp->br,426&nbytes, &src, &dstleft, &dst, NULL);427result = Curl_cwriter_write(data, writer->next, type,428bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);429if(result)430break;431switch(r) {432case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:433case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:434break;435case BROTLI_DECODER_RESULT_SUCCESS:436BrotliDecoderDestroyInstance(bp->br);437bp->br = NULL;438if(nbytes)439result = CURLE_WRITE_ERROR;440break;441default:442result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));443break;444}445}446return result;447}448449static void brotli_do_close(struct Curl_easy *data,450struct Curl_cwriter *writer)451{452struct brotli_writer *bp = (struct brotli_writer *)writer;453(void)data;454455if(bp->br) {456BrotliDecoderDestroyInstance(bp->br);457bp->br = NULL;458}459}460461static const struct Curl_cwtype brotli_encoding = {462"br",463NULL,464brotli_do_init,465brotli_do_write,466brotli_do_close,467sizeof(struct brotli_writer)468};469#endif470471#ifdef HAVE_ZSTD472/* Zstd writer. */473struct zstd_writer {474struct Curl_cwriter super;475ZSTD_DStream *zds; /* State structure for zstd. */476char buffer[DECOMPRESS_BUFFER_SIZE];477};478479#ifdef ZSTD_STATIC_LINKING_ONLY480static void *Curl_zstd_alloc(void *opaque, size_t size)481{482(void)opaque;483return Curl_cmalloc(size);484}485486static void Curl_zstd_free(void *opaque, void *address)487{488(void)opaque;489Curl_cfree(address);490}491#endif492493static CURLcode zstd_do_init(struct Curl_easy *data,494struct Curl_cwriter *writer)495{496struct zstd_writer *zp = (struct zstd_writer *)writer;497498(void)data;499500#ifdef ZSTD_STATIC_LINKING_ONLY501zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {502.customAlloc = Curl_zstd_alloc,503.customFree = Curl_zstd_free,504.opaque = NULL505});506#else507zp->zds = ZSTD_createDStream();508#endif509510return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;511}512513static CURLcode zstd_do_write(struct Curl_easy *data,514struct Curl_cwriter *writer, int type,515const char *buf, size_t nbytes)516{517CURLcode result = CURLE_OK;518struct zstd_writer *zp = (struct zstd_writer *)writer;519ZSTD_inBuffer in;520ZSTD_outBuffer out;521size_t errorCode;522523if(!(type & CLIENTWRITE_BODY) || !nbytes)524return Curl_cwriter_write(data, writer->next, type, buf, nbytes);525526in.pos = 0;527in.src = buf;528in.size = nbytes;529530for(;;) {531out.pos = 0;532out.dst = zp->buffer;533out.size = DECOMPRESS_BUFFER_SIZE;534535errorCode = ZSTD_decompressStream(zp->zds, &out, &in);536if(ZSTD_isError(errorCode)) {537return CURLE_BAD_CONTENT_ENCODING;538}539if(out.pos > 0) {540result = Curl_cwriter_write(data, writer->next, type,541zp->buffer, out.pos);542if(result)543break;544}545if((in.pos == nbytes) && (out.pos < out.size))546break;547}548549return result;550}551552static void zstd_do_close(struct Curl_easy *data,553struct Curl_cwriter *writer)554{555struct zstd_writer *zp = (struct zstd_writer *)writer;556(void)data;557558if(zp->zds) {559ZSTD_freeDStream(zp->zds);560zp->zds = NULL;561}562}563564static const struct Curl_cwtype zstd_encoding = {565"zstd",566NULL,567zstd_do_init,568zstd_do_write,569zstd_do_close,570sizeof(struct zstd_writer)571};572#endif573574/* Identity handler. */575static const struct Curl_cwtype identity_encoding = {576"identity",577"none",578Curl_cwriter_def_init,579Curl_cwriter_def_write,580Curl_cwriter_def_close,581sizeof(struct Curl_cwriter)582};583584/* supported general content decoders. */585static const struct Curl_cwtype * const general_unencoders[] = {586&identity_encoding,587#ifdef HAVE_LIBZ588&deflate_encoding,589&gzip_encoding,590#endif591#ifdef HAVE_BROTLI592&brotli_encoding,593#endif594#ifdef HAVE_ZSTD595&zstd_encoding,596#endif597NULL598};599600/* supported content decoders only for transfer encodings */601static const struct Curl_cwtype * const transfer_unencoders[] = {602#ifndef CURL_DISABLE_HTTP603&Curl_httpchunk_unencoder,604#endif605NULL606};607608/* Return the list of comma-separated names of supported encodings.609*/610char *Curl_get_content_encodings(void)611{612struct dynbuf enc;613const struct Curl_cwtype * const *cep;614CURLcode result = CURLE_OK;615curlx_dyn_init(&enc, 255);616617for(cep = general_unencoders; *cep && !result; cep++) {618const struct Curl_cwtype *ce = *cep;619if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {620if(curlx_dyn_len(&enc))621result = curlx_dyn_addn(&enc, ", ", 2);622if(!result)623result = curlx_dyn_add(&enc, ce->name);624}625}626if(!result)627return curlx_dyn_ptr(&enc);628return NULL;629}630631/* Deferred error dummy writer. */632static CURLcode error_do_init(struct Curl_easy *data,633struct Curl_cwriter *writer)634{635(void)data;636(void)writer;637return CURLE_OK;638}639640static CURLcode error_do_write(struct Curl_easy *data,641struct Curl_cwriter *writer, int type,642const char *buf, size_t nbytes)643{644(void)writer;645(void)buf;646(void)nbytes;647648if(!(type & CLIENTWRITE_BODY) || !nbytes)649return Curl_cwriter_write(data, writer->next, type, buf, nbytes);650failf(data, "Unrecognized content encoding type");651return CURLE_BAD_CONTENT_ENCODING;652}653654static void error_do_close(struct Curl_easy *data,655struct Curl_cwriter *writer)656{657(void)data;658(void)writer;659}660661static const struct Curl_cwtype error_writer = {662"ce-error",663NULL,664error_do_init,665error_do_write,666error_do_close,667sizeof(struct Curl_cwriter)668};669670/* Find the content encoding by name. */671static const struct Curl_cwtype *find_unencode_writer(const char *name,672size_t len,673Curl_cwriter_phase phase)674{675const struct Curl_cwtype * const *cep;676677if(phase == CURL_CW_TRANSFER_DECODE) {678for(cep = transfer_unencoders; *cep; cep++) {679const struct Curl_cwtype *ce = *cep;680if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||681(ce->alias && curl_strnequal(name, ce->alias, len)682&& !ce->alias[len]))683return ce;684}685}686/* look among the general decoders */687for(cep = general_unencoders; *cep; cep++) {688const struct Curl_cwtype *ce = *cep;689if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||690(ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len]))691return ce;692}693return NULL;694}695696/* Setup the unencoding stack from the Content-Encoding header value.697* See RFC 7231 section 3.1.2.2. */698CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,699const char *enclist, int is_transfer)700{701Curl_cwriter_phase phase = is_transfer ?702CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;703CURLcode result;704bool has_chunked = FALSE;705706do {707const char *name;708size_t namelen;709bool is_chunked = FALSE;710711/* Parse a single encoding name. */712while(ISBLANK(*enclist) || *enclist == ',')713enclist++;714715name = enclist;716717for(namelen = 0; *enclist && *enclist != ','; enclist++)718if(*enclist > ' ')719namelen = enclist - name + 1;720721if(namelen) {722const struct Curl_cwtype *cwt;723struct Curl_cwriter *writer;724725CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",726is_transfer ? "transfer" : "content", (int)namelen, name);727is_chunked = (is_transfer && (namelen == 7) &&728curl_strnequal(name, "chunked", 7));729/* if we skip the decoding in this phase, do not look further.730* Exception is "chunked" transfer-encoding which always must happen */731if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||732(!is_transfer && data->set.http_ce_skip)) {733bool is_identity = curl_strnequal(name, "identity", 8);734/* not requested, ignore */735CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",736(int)namelen, name);737if(is_transfer && !data->set.http_te_skip) {738if(has_chunked)739failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",740(int)namelen, name);741else if(is_identity)742continue;743else744failf(data, "Unsolicited Transfer-Encoding (%.*s) found",745(int)namelen, name);746return CURLE_BAD_CONTENT_ENCODING;747}748return CURLE_OK;749}750751if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {752failf(data, "Reject response due to more than %u content encodings",753MAX_ENCODE_STACK);754return CURLE_BAD_CONTENT_ENCODING;755}756757cwt = find_unencode_writer(name, namelen, phase);758if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {759/* A 'chunked' transfer encoding has already been added.760* Ignore duplicates. See #13451.761* Also RFC 9112, ch. 6.1:762* "A sender MUST NOT apply the chunked transfer coding more than763* once to a message body."764*/765CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");766return CURLE_OK;767}768769if(is_transfer && !is_chunked &&770Curl_cwriter_get_by_name(data, "chunked")) {771/* RFC 9112, ch. 6.1:772* "If any transfer coding other than chunked is applied to a773* response's content, the sender MUST either apply chunked as the774* final transfer coding or terminate the message by closing the775* connection."776* "chunked" must be the last added to be the first in its phase,777* reject this.778*/779failf(data, "Reject response due to 'chunked' not being the last "780"Transfer-Encoding");781return CURLE_BAD_CONTENT_ENCODING;782}783784if(!cwt)785cwt = &error_writer; /* Defer error at use. */786787result = Curl_cwriter_create(&writer, data, cwt, phase);788CURL_TRC_WRITE(data, "added %s decoder %s -> %d",789is_transfer ? "transfer" : "content", cwt->name, result);790if(result)791return result;792793result = Curl_cwriter_add(data, writer);794if(result) {795Curl_cwriter_free(data, writer);796return result;797}798if(is_chunked)799has_chunked = TRUE;800}801} while(*enclist);802803return CURLE_OK;804}805806#else807/* Stubs for builds without HTTP. */808CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,809const char *enclist, int is_transfer)810{811(void)data;812(void)enclist;813(void)is_transfer;814return CURLE_NOT_BUILT_IN;815}816817char *Curl_get_content_encodings(void)818{819return curlx_strdup(CONTENT_ENCODING_DEFAULT);820}821822#endif /* CURL_DISABLE_HTTP */823824825