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