quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

content_encoding.c (25122B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #include "urldata.h"
     28 #include <curl/curl.h>
     29 #include <stddef.h>
     30 
     31 #ifdef HAVE_LIBZ
     32 #include <zlib.h>
     33 #endif
     34 
     35 #ifdef HAVE_BROTLI
     36 #if defined(__GNUC__) || defined(__clang__)
     37 /* Ignore -Wvla warnings in brotli headers */
     38 #pragma GCC diagnostic push
     39 #pragma GCC diagnostic ignored "-Wvla"
     40 #endif
     41 #include <brotli/decode.h>
     42 #if defined(__GNUC__) || defined(__clang__)
     43 #pragma GCC diagnostic pop
     44 #endif
     45 #endif
     46 
     47 #ifdef HAVE_ZSTD
     48 #include <zstd.h>
     49 #endif
     50 
     51 #include "sendf.h"
     52 #include "http.h"
     53 #include "content_encoding.h"
     54 #include "strdup.h"
     55 
     56 /* The last 3 #include files should be in this order */
     57 #include "curl_printf.h"
     58 #include "curl_memory.h"
     59 #include "memdebug.h"
     60 
     61 #define CONTENT_ENCODING_DEFAULT  "identity"
     62 
     63 #ifndef CURL_DISABLE_HTTP
     64 
     65 /* allow no more than 5 "chained" compression steps */
     66 #define MAX_ENCODE_STACK 5
     67 
     68 #if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)
     69 #define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */
     70 #endif
     71 
     72 #ifdef HAVE_LIBZ
     73 
     74 #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)
     75 #error "requires zlib 1.2.5.2 or newer"
     76 #endif
     77 
     78 typedef enum {
     79   ZLIB_UNINIT,               /* uninitialized */
     80   ZLIB_INIT,                 /* initialized */
     81   ZLIB_INFLATING,            /* inflating started. */
     82   ZLIB_EXTERNAL_TRAILER,     /* reading external trailer */
     83   ZLIB_INIT_GZIP             /* initialized in transparent gzip mode */
     84 } zlibInitState;
     85 
     86 /* Deflate and gzip writer. */
     87 struct zlib_writer {
     88   struct Curl_cwriter super;
     89   zlibInitState zlib_init;   /* zlib init state */
     90   char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */
     91   uInt trailerlen;           /* Remaining trailer byte count. */
     92   z_stream z;                /* State structure for zlib. */
     93 };
     94 
     95 
     96 static voidpf
     97 zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
     98 {
     99   (void) opaque;
    100   /* not a typo, keep it calloc() */
    101   return (voidpf) calloc(items, size);
    102 }
    103 
    104 static void
    105 zfree_cb(voidpf opaque, voidpf ptr)
    106 {
    107   (void) opaque;
    108   free(ptr);
    109 }
    110 
    111 static CURLcode
    112 process_zlib_error(struct Curl_easy *data, z_stream *z)
    113 {
    114   if(z->msg)
    115     failf(data, "Error while processing content unencoding: %s",
    116           z->msg);
    117   else
    118     failf(data, "Error while processing content unencoding: "
    119           "Unknown failure within decompression software.");
    120 
    121   return CURLE_BAD_CONTENT_ENCODING;
    122 }
    123 
    124 static CURLcode
    125 exit_zlib(struct Curl_easy *data,
    126           z_stream *z, zlibInitState *zlib_init, CURLcode result)
    127 {
    128   if(*zlib_init != ZLIB_UNINIT) {
    129     if(inflateEnd(z) != Z_OK && result == CURLE_OK)
    130       result = process_zlib_error(data, z);
    131     *zlib_init = ZLIB_UNINIT;
    132   }
    133 
    134   return result;
    135 }
    136 
    137 static CURLcode process_trailer(struct Curl_easy *data,
    138                                 struct zlib_writer *zp)
    139 {
    140   z_stream *z = &zp->z;
    141   CURLcode result = CURLE_OK;
    142   uInt len = z->avail_in < zp->trailerlen ? z->avail_in : zp->trailerlen;
    143 
    144   /* Consume expected trailer bytes. Terminate stream if exhausted.
    145      Issue an error if unexpected bytes follow. */
    146 
    147   zp->trailerlen -= len;
    148   z->avail_in -= len;
    149   z->next_in += len;
    150   if(z->avail_in)
    151     result = CURLE_WRITE_ERROR;
    152   if(result || !zp->trailerlen)
    153     result = exit_zlib(data, z, &zp->zlib_init, result);
    154   else {
    155     /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */
    156     zp->zlib_init = ZLIB_EXTERNAL_TRAILER;
    157   }
    158   return result;
    159 }
    160 
    161 static CURLcode inflate_stream(struct Curl_easy *data,
    162                                struct Curl_cwriter *writer, int type,
    163                                zlibInitState started)
    164 {
    165   struct zlib_writer *zp = (struct zlib_writer *) writer;
    166   z_stream *z = &zp->z;         /* zlib state structure */
    167   uInt nread = z->avail_in;
    168   z_const Bytef *orig_in = z->next_in;
    169   bool done = FALSE;
    170   CURLcode result = CURLE_OK;   /* Curl_client_write status */
    171 
    172   /* Check state. */
    173   if(zp->zlib_init != ZLIB_INIT &&
    174      zp->zlib_init != ZLIB_INFLATING &&
    175      zp->zlib_init != ZLIB_INIT_GZIP)
    176     return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
    177 
    178   /* because the buffer size is fixed, iteratively decompress and transfer to
    179      the client via next_write function. */
    180   while(!done) {
    181     int status;                   /* zlib status */
    182     done = TRUE;
    183 
    184     /* (re)set buffer for decompressed output for every iteration */
    185     z->next_out = (Bytef *) zp->buffer;
    186     z->avail_out = DECOMPRESS_BUFFER_SIZE;
    187 
    188     status = inflate(z, Z_BLOCK);
    189 
    190     /* Flush output data if some. */
    191     if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
    192       if(status == Z_OK || status == Z_STREAM_END) {
    193         zp->zlib_init = started;      /* Data started. */
    194         result = Curl_cwriter_write(data, writer->next, type, zp->buffer,
    195                                     DECOMPRESS_BUFFER_SIZE - z->avail_out);
    196         if(result) {
    197           exit_zlib(data, z, &zp->zlib_init, result);
    198           break;
    199         }
    200       }
    201     }
    202 
    203     /* Dispatch by inflate() status. */
    204     switch(status) {
    205     case Z_OK:
    206       /* Always loop: there may be unflushed latched data in zlib state. */
    207       done = FALSE;
    208       break;
    209     case Z_BUF_ERROR:
    210       /* No more data to flush: just exit loop. */
    211       break;
    212     case Z_STREAM_END:
    213       result = process_trailer(data, zp);
    214       break;
    215     case Z_DATA_ERROR:
    216       /* some servers seem to not generate zlib headers, so this is an attempt
    217          to fix and continue anyway */
    218       if(zp->zlib_init == ZLIB_INIT) {
    219         if(inflateReset2(z, -MAX_WBITS) == Z_OK) {
    220           z->next_in = orig_in;
    221           z->avail_in = nread;
    222           zp->zlib_init = ZLIB_INFLATING;
    223           zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */
    224           done = FALSE;
    225           break;
    226         }
    227         zp->zlib_init = ZLIB_UNINIT;    /* inflateEnd() already called. */
    228       }
    229       result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
    230       break;
    231     default:
    232       result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
    233       break;
    234     }
    235   }
    236 
    237   /* We are about to leave this call so the `nread' data bytes will not be seen
    238      again. If we are in a state that would wrongly allow restart in raw mode
    239      at the next call, assume output has already started. */
    240   if(nread && zp->zlib_init == ZLIB_INIT)
    241     zp->zlib_init = started;      /* Cannot restart anymore. */
    242 
    243   return result;
    244 }
    245 
    246 
    247 /* Deflate handler. */
    248 static CURLcode deflate_do_init(struct Curl_easy *data,
    249                                 struct Curl_cwriter *writer)
    250 {
    251   struct zlib_writer *zp = (struct zlib_writer *) writer;
    252   z_stream *z = &zp->z;     /* zlib state structure */
    253 
    254   /* Initialize zlib */
    255   z->zalloc = (alloc_func) zalloc_cb;
    256   z->zfree = (free_func) zfree_cb;
    257 
    258   if(inflateInit(z) != Z_OK)
    259     return process_zlib_error(data, z);
    260   zp->zlib_init = ZLIB_INIT;
    261   return CURLE_OK;
    262 }
    263 
    264 static CURLcode deflate_do_write(struct Curl_easy *data,
    265                                  struct Curl_cwriter *writer, int type,
    266                                  const char *buf, size_t nbytes)
    267 {
    268   struct zlib_writer *zp = (struct zlib_writer *) writer;
    269   z_stream *z = &zp->z;     /* zlib state structure */
    270 
    271   if(!(type & CLIENTWRITE_BODY) || !nbytes)
    272     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
    273 
    274   /* Set the compressed input when this function is called */
    275   z->next_in = (z_const Bytef *)buf;
    276   z->avail_in = (uInt)nbytes;
    277 
    278   if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
    279     return process_trailer(data, zp);
    280 
    281   /* Now uncompress the data */
    282   return inflate_stream(data, writer, type, ZLIB_INFLATING);
    283 }
    284 
    285 static void deflate_do_close(struct Curl_easy *data,
    286                              struct Curl_cwriter *writer)
    287 {
    288   struct zlib_writer *zp = (struct zlib_writer *) writer;
    289   z_stream *z = &zp->z;     /* zlib state structure */
    290 
    291   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
    292 }
    293 
    294 static const struct Curl_cwtype deflate_encoding = {
    295   "deflate",
    296   NULL,
    297   deflate_do_init,
    298   deflate_do_write,
    299   deflate_do_close,
    300   sizeof(struct zlib_writer)
    301 };
    302 
    303 
    304 /* Gzip handler. */
    305 static CURLcode gzip_do_init(struct Curl_easy *data,
    306                              struct Curl_cwriter *writer)
    307 {
    308   struct zlib_writer *zp = (struct zlib_writer *) writer;
    309   z_stream *z = &zp->z;     /* zlib state structure */
    310 
    311   /* Initialize zlib */
    312   z->zalloc = (alloc_func) zalloc_cb;
    313   z->zfree = (free_func) zfree_cb;
    314 
    315   if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)
    316     return process_zlib_error(data, z);
    317 
    318   zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
    319   return CURLE_OK;
    320 }
    321 
    322 static CURLcode gzip_do_write(struct Curl_easy *data,
    323                               struct Curl_cwriter *writer, int type,
    324                               const char *buf, size_t nbytes)
    325 {
    326   struct zlib_writer *zp = (struct zlib_writer *) writer;
    327   z_stream *z = &zp->z;     /* zlib state structure */
    328 
    329   if(!(type & CLIENTWRITE_BODY) || !nbytes)
    330     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
    331 
    332   if(zp->zlib_init == ZLIB_INIT_GZIP) {
    333     /* Let zlib handle the gzip decompression entirely */
    334     z->next_in = (z_const Bytef *)buf;
    335     z->avail_in = (uInt)nbytes;
    336     /* Now uncompress the data */
    337     return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
    338   }
    339 
    340   /* We are running with an old version: return error. */
    341   return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
    342 }
    343 
    344 static void gzip_do_close(struct Curl_easy *data,
    345                           struct Curl_cwriter *writer)
    346 {
    347   struct zlib_writer *zp = (struct zlib_writer *) writer;
    348   z_stream *z = &zp->z;     /* zlib state structure */
    349 
    350   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
    351 }
    352 
    353 static const struct Curl_cwtype gzip_encoding = {
    354   "gzip",
    355   "x-gzip",
    356   gzip_do_init,
    357   gzip_do_write,
    358   gzip_do_close,
    359   sizeof(struct zlib_writer)
    360 };
    361 
    362 #endif /* HAVE_LIBZ */
    363 
    364 #ifdef HAVE_BROTLI
    365 /* Brotli writer. */
    366 struct brotli_writer {
    367   struct Curl_cwriter super;
    368   char buffer[DECOMPRESS_BUFFER_SIZE];
    369   BrotliDecoderState *br;    /* State structure for brotli. */
    370 };
    371 
    372 static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
    373 {
    374   switch(be) {
    375   case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:
    376   case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:
    377   case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:
    378   case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:
    379   case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:
    380   case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:
    381   case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:
    382   case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:
    383   case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:
    384   case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:
    385   case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:
    386   case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:
    387   case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:
    388   case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:
    389 #ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY
    390   case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:
    391 #endif
    392 #ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET
    393   case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:
    394 #endif
    395   case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:
    396     return CURLE_BAD_CONTENT_ENCODING;
    397   case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:
    398   case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:
    399   case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:
    400   case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:
    401   case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:
    402   case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:
    403     return CURLE_OUT_OF_MEMORY;
    404   default:
    405     break;
    406   }
    407   return CURLE_WRITE_ERROR;
    408 }
    409 
    410 static CURLcode brotli_do_init(struct Curl_easy *data,
    411                                struct Curl_cwriter *writer)
    412 {
    413   struct brotli_writer *bp = (struct brotli_writer *) writer;
    414   (void) data;
    415 
    416   bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
    417   return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;
    418 }
    419 
    420 static CURLcode brotli_do_write(struct Curl_easy *data,
    421                                 struct Curl_cwriter *writer, int type,
    422                                 const char *buf, size_t nbytes)
    423 {
    424   struct brotli_writer *bp = (struct brotli_writer *) writer;
    425   const uint8_t *src = (const uint8_t *) buf;
    426   uint8_t *dst;
    427   size_t dstleft;
    428   CURLcode result = CURLE_OK;
    429   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
    430 
    431   if(!(type & CLIENTWRITE_BODY) || !nbytes)
    432     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
    433 
    434   if(!bp->br)
    435     return CURLE_WRITE_ERROR;  /* Stream already ended. */
    436 
    437   while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
    438         result == CURLE_OK) {
    439     dst = (uint8_t *) bp->buffer;
    440     dstleft = DECOMPRESS_BUFFER_SIZE;
    441     r = BrotliDecoderDecompressStream(bp->br,
    442                                       &nbytes, &src, &dstleft, &dst, NULL);
    443     result = Curl_cwriter_write(data, writer->next, type,
    444                                 bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);
    445     if(result)
    446       break;
    447     switch(r) {
    448     case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
    449     case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
    450       break;
    451     case BROTLI_DECODER_RESULT_SUCCESS:
    452       BrotliDecoderDestroyInstance(bp->br);
    453       bp->br = NULL;
    454       if(nbytes)
    455         result = CURLE_WRITE_ERROR;
    456       break;
    457     default:
    458       result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));
    459       break;
    460     }
    461   }
    462   return result;
    463 }
    464 
    465 static void brotli_do_close(struct Curl_easy *data,
    466                             struct Curl_cwriter *writer)
    467 {
    468   struct brotli_writer *bp = (struct brotli_writer *) writer;
    469   (void) data;
    470 
    471   if(bp->br) {
    472     BrotliDecoderDestroyInstance(bp->br);
    473     bp->br = NULL;
    474   }
    475 }
    476 
    477 static const struct Curl_cwtype brotli_encoding = {
    478   "br",
    479   NULL,
    480   brotli_do_init,
    481   brotli_do_write,
    482   brotli_do_close,
    483   sizeof(struct brotli_writer)
    484 };
    485 #endif
    486 
    487 #ifdef HAVE_ZSTD
    488 /* Zstd writer. */
    489 struct zstd_writer {
    490   struct Curl_cwriter super;
    491   ZSTD_DStream *zds;    /* State structure for zstd. */
    492   char buffer[DECOMPRESS_BUFFER_SIZE];
    493 };
    494 
    495 #ifdef ZSTD_STATIC_LINKING_ONLY
    496 static void *Curl_zstd_alloc(void *opaque, size_t size)
    497 {
    498   (void)opaque;
    499   return Curl_cmalloc(size);
    500 }
    501 
    502 static void Curl_zstd_free(void *opaque, void *address)
    503 {
    504   (void)opaque;
    505   Curl_cfree(address);
    506 }
    507 #endif
    508 
    509 static CURLcode zstd_do_init(struct Curl_easy *data,
    510                              struct Curl_cwriter *writer)
    511 {
    512   struct zstd_writer *zp = (struct zstd_writer *) writer;
    513 
    514   (void)data;
    515 
    516 #ifdef ZSTD_STATIC_LINKING_ONLY
    517   zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {
    518     .customAlloc = Curl_zstd_alloc,
    519     .customFree  = Curl_zstd_free,
    520     .opaque      = NULL
    521   });
    522 #else
    523   zp->zds = ZSTD_createDStream();
    524 #endif
    525 
    526   return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
    527 }
    528 
    529 static CURLcode zstd_do_write(struct Curl_easy *data,
    530                               struct Curl_cwriter *writer, int type,
    531                               const char *buf, size_t nbytes)
    532 {
    533   CURLcode result = CURLE_OK;
    534   struct zstd_writer *zp = (struct zstd_writer *) writer;
    535   ZSTD_inBuffer in;
    536   ZSTD_outBuffer out;
    537   size_t errorCode;
    538 
    539   if(!(type & CLIENTWRITE_BODY) || !nbytes)
    540     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
    541 
    542   in.pos = 0;
    543   in.src = buf;
    544   in.size = nbytes;
    545 
    546   for(;;) {
    547     out.pos = 0;
    548     out.dst = zp->buffer;
    549     out.size = DECOMPRESS_BUFFER_SIZE;
    550 
    551     errorCode = ZSTD_decompressStream(zp->zds, &out, &in);
    552     if(ZSTD_isError(errorCode)) {
    553       return CURLE_BAD_CONTENT_ENCODING;
    554     }
    555     if(out.pos > 0) {
    556       result = Curl_cwriter_write(data, writer->next, type,
    557                                   zp->buffer, out.pos);
    558       if(result)
    559         break;
    560     }
    561     if((in.pos == nbytes) && (out.pos < out.size))
    562       break;
    563   }
    564 
    565   return result;
    566 }
    567 
    568 static void zstd_do_close(struct Curl_easy *data,
    569                           struct Curl_cwriter *writer)
    570 {
    571   struct zstd_writer *zp = (struct zstd_writer *) writer;
    572   (void)data;
    573 
    574   if(zp->zds) {
    575     ZSTD_freeDStream(zp->zds);
    576     zp->zds = NULL;
    577   }
    578 }
    579 
    580 static const struct Curl_cwtype zstd_encoding = {
    581   "zstd",
    582   NULL,
    583   zstd_do_init,
    584   zstd_do_write,
    585   zstd_do_close,
    586   sizeof(struct zstd_writer)
    587 };
    588 #endif
    589 
    590 /* Identity handler. */
    591 static const struct Curl_cwtype identity_encoding = {
    592   "identity",
    593   "none",
    594   Curl_cwriter_def_init,
    595   Curl_cwriter_def_write,
    596   Curl_cwriter_def_close,
    597   sizeof(struct Curl_cwriter)
    598 };
    599 
    600 /* supported general content decoders. */
    601 static const struct Curl_cwtype * const general_unencoders[] = {
    602   &identity_encoding,
    603 #ifdef HAVE_LIBZ
    604   &deflate_encoding,
    605   &gzip_encoding,
    606 #endif
    607 #ifdef HAVE_BROTLI
    608   &brotli_encoding,
    609 #endif
    610 #ifdef HAVE_ZSTD
    611   &zstd_encoding,
    612 #endif
    613   NULL
    614 };
    615 
    616 /* supported content decoders only for transfer encodings */
    617 static const struct Curl_cwtype * const transfer_unencoders[] = {
    618 #ifndef CURL_DISABLE_HTTP
    619   &Curl_httpchunk_unencoder,
    620 #endif
    621   NULL
    622 };
    623 
    624 /* Provide a list of comma-separated names of supported encodings.
    625 */
    626 void Curl_all_content_encodings(char *buf, size_t blen)
    627 {
    628   size_t len = 0;
    629   const struct Curl_cwtype * const *cep;
    630   const struct Curl_cwtype *ce;
    631 
    632   DEBUGASSERT(buf);
    633   DEBUGASSERT(blen);
    634   buf[0] = 0;
    635 
    636   for(cep = general_unencoders; *cep; cep++) {
    637     ce = *cep;
    638     if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT))
    639       len += strlen(ce->name) + 2;
    640   }
    641 
    642   if(!len) {
    643     if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))
    644       strcpy(buf, CONTENT_ENCODING_DEFAULT);
    645   }
    646   else if(blen > len) {
    647     char *p = buf;
    648     for(cep = general_unencoders; *cep; cep++) {
    649       ce = *cep;
    650       if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {
    651         strcpy(p, ce->name);
    652         p += strlen(p);
    653         *p++ = ',';
    654         *p++ = ' ';
    655       }
    656     }
    657     p[-2] = '\0';
    658   }
    659 }
    660 
    661 /* Deferred error dummy writer. */
    662 static CURLcode error_do_init(struct Curl_easy *data,
    663                               struct Curl_cwriter *writer)
    664 {
    665   (void)data;
    666   (void)writer;
    667   return CURLE_OK;
    668 }
    669 
    670 static CURLcode error_do_write(struct Curl_easy *data,
    671                                struct Curl_cwriter *writer, int type,
    672                                const char *buf, size_t nbytes)
    673 {
    674   (void) writer;
    675   (void) buf;
    676   (void) nbytes;
    677 
    678   if(!(type & CLIENTWRITE_BODY) || !nbytes)
    679     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
    680   else {
    681     char all[256];
    682     (void)Curl_all_content_encodings(all, sizeof(all));
    683     failf(data, "Unrecognized content encoding type. "
    684           "libcurl understands %s content encodings.", all);
    685   }
    686   return CURLE_BAD_CONTENT_ENCODING;
    687 }
    688 
    689 static void error_do_close(struct Curl_easy *data,
    690                            struct Curl_cwriter *writer)
    691 {
    692   (void) data;
    693   (void) writer;
    694 }
    695 
    696 static const struct Curl_cwtype error_writer = {
    697   "ce-error",
    698   NULL,
    699   error_do_init,
    700   error_do_write,
    701   error_do_close,
    702   sizeof(struct Curl_cwriter)
    703 };
    704 
    705 /* Find the content encoding by name. */
    706 static const struct Curl_cwtype *find_unencode_writer(const char *name,
    707                                                       size_t len,
    708                                                       Curl_cwriter_phase phase)
    709 {
    710   const struct Curl_cwtype * const *cep;
    711 
    712   if(phase == CURL_CW_TRANSFER_DECODE) {
    713     for(cep = transfer_unencoders; *cep; cep++) {
    714       const struct Curl_cwtype *ce = *cep;
    715       if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||
    716          (ce->alias && curl_strnequal(name, ce->alias, len)
    717                     && !ce->alias[len]))
    718         return ce;
    719     }
    720   }
    721   /* look among the general decoders */
    722   for(cep = general_unencoders; *cep; cep++) {
    723     const struct Curl_cwtype *ce = *cep;
    724     if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||
    725        (ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len]))
    726       return ce;
    727   }
    728   return NULL;
    729 }
    730 
    731 /* Setup the unencoding stack from the Content-Encoding header value.
    732  * See RFC 7231 section 3.1.2.2. */
    733 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
    734                                      const char *enclist, int is_transfer)
    735 {
    736   Curl_cwriter_phase phase = is_transfer ?
    737     CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;
    738   CURLcode result;
    739   bool has_chunked = FALSE;
    740 
    741   do {
    742     const char *name;
    743     size_t namelen;
    744     bool is_chunked = FALSE;
    745 
    746     /* Parse a single encoding name. */
    747     while(ISBLANK(*enclist) || *enclist == ',')
    748       enclist++;
    749 
    750     name = enclist;
    751 
    752     for(namelen = 0; *enclist && *enclist != ','; enclist++)
    753       if(*enclist > ' ')
    754         namelen = enclist - name + 1;
    755 
    756     if(namelen) {
    757       const struct Curl_cwtype *cwt;
    758       struct Curl_cwriter *writer;
    759 
    760       CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
    761                      is_transfer ? "transfer" : "content", (int)namelen, name);
    762       is_chunked = (is_transfer && (namelen == 7) &&
    763                     curl_strnequal(name, "chunked", 7));
    764       /* if we skip the decoding in this phase, do not look further.
    765        * Exception is "chunked" transfer-encoding which always must happen */
    766       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
    767          (!is_transfer && data->set.http_ce_skip)) {
    768         bool is_identity = curl_strnequal(name, "identity", 8);
    769         /* not requested, ignore */
    770         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
    771                        (int)namelen, name);
    772         if(is_transfer && !data->set.http_te_skip) {
    773           if(has_chunked)
    774             failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",
    775                   (int)namelen, name);
    776           else if(is_identity)
    777             continue;
    778           else
    779             failf(data, "Unsolicited Transfer-Encoding (%.*s) found",
    780                   (int)namelen, name);
    781           return CURLE_BAD_CONTENT_ENCODING;
    782         }
    783         return CURLE_OK;
    784       }
    785 
    786       if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
    787         failf(data, "Reject response due to more than %u content encodings",
    788               MAX_ENCODE_STACK);
    789         return CURLE_BAD_CONTENT_ENCODING;
    790       }
    791 
    792       cwt = find_unencode_writer(name, namelen, phase);
    793       if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
    794         /* A 'chunked' transfer encoding has already been added.
    795          * Ignore duplicates. See #13451.
    796          * Also RFC 9112, ch. 6.1:
    797          * "A sender MUST NOT apply the chunked transfer coding more than
    798          *  once to a message body."
    799          */
    800         CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");
    801         return CURLE_OK;
    802       }
    803 
    804       if(is_transfer && !is_chunked &&
    805          Curl_cwriter_get_by_name(data, "chunked")) {
    806         /* RFC 9112, ch. 6.1:
    807          * "If any transfer coding other than chunked is applied to a
    808          *  response's content, the sender MUST either apply chunked as the
    809          *  final transfer coding or terminate the message by closing the
    810          *  connection."
    811          * "chunked" must be the last added to be the first in its phase,
    812          *  reject this.
    813          */
    814         failf(data, "Reject response due to 'chunked' not being the last "
    815               "Transfer-Encoding");
    816         return CURLE_BAD_CONTENT_ENCODING;
    817       }
    818 
    819       if(!cwt)
    820         cwt = &error_writer;  /* Defer error at use. */
    821 
    822       result = Curl_cwriter_create(&writer, data, cwt, phase);
    823       CURL_TRC_WRITE(data, "added %s decoder %s -> %d",
    824                      is_transfer ? "transfer" : "content", cwt->name, result);
    825       if(result)
    826         return result;
    827 
    828       result = Curl_cwriter_add(data, writer);
    829       if(result) {
    830         Curl_cwriter_free(data, writer);
    831         return result;
    832       }
    833       if(is_chunked)
    834         has_chunked = TRUE;
    835     }
    836   } while(*enclist);
    837 
    838   return CURLE_OK;
    839 }
    840 
    841 #else
    842 /* Stubs for builds without HTTP. */
    843 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
    844                                      const char *enclist, int is_transfer)
    845 {
    846   (void) data;
    847   (void) enclist;
    848   (void) is_transfer;
    849   return CURLE_NOT_BUILT_IN;
    850 }
    851 
    852 void Curl_all_content_encodings(char *buf, size_t blen)
    853 {
    854   DEBUGASSERT(buf);
    855   DEBUGASSERT(blen);
    856   if(blen < sizeof(CONTENT_ENCODING_DEFAULT))
    857     buf[0] = 0;
    858   else
    859     strcpy(buf, CONTENT_ENCODING_DEFAULT);
    860 }
    861 
    862 #endif /* CURL_DISABLE_HTTP */