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