http: decode transfer encoding first

The unencoding stack is added to as Transfer-Encoding and
Content-Encoding fields are encountered with no distinction between the
two, meaning the stack will be incorrect if, e.g., the message has both
fields and a non-chunked Transfer-Encoding comes first. This commit
fixes this by ordering the stack with transfer encodings first.

Reviewed-by: Patrick Monnerat
Closes #10187
This commit is contained in:
Josh Brobst 2022-12-31 14:41:44 -05:00 committed by Daniel Stenberg
parent fc9f22b46e
commit aa6e7a1f45
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 239 additions and 15 deletions

View file

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2023, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -977,7 +977,8 @@ static const struct content_encoding error_encoding = {
static struct contenc_writer *
new_unencoding_writer(struct Curl_easy *data,
const struct content_encoding *handler,
struct contenc_writer *downstream)
struct contenc_writer *downstream,
int order)
{
struct contenc_writer *writer;
@ -987,6 +988,7 @@ new_unencoding_writer(struct Curl_easy *data,
if(writer) {
writer->handler = handler;
writer->downstream = downstream;
writer->order = order;
if(handler->init_writer(data, writer)) {
free(writer);
writer = NULL;
@ -1042,10 +1044,11 @@ static const struct content_encoding *find_encoding(const char *name,
/* Set-up the unencoding stack from the Content-Encoding header value.
* See RFC 7231 section 3.1.2.2. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int maybechunked)
const char *enclist, int is_transfer)
{
struct SingleRequest *k = &data->req;
int counter = 0;
unsigned int order = is_transfer? 2: 1;
do {
const char *name;
@ -1062,7 +1065,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
namelen = enclist - name + 1;
/* Special case: chunked encoding is handled at the reader level. */
if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) {
k->chunk = TRUE; /* chunks coming our way. */
Curl_httpchunk_init(data); /* init our chunky engine. */
}
@ -1071,7 +1074,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
struct contenc_writer *writer;
if(!k->writer_stack) {
k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL);
k->writer_stack = new_unencoding_writer(data, &client_encoding,
NULL, 0);
if(!k->writer_stack)
return CURLE_OUT_OF_MEMORY;
@ -1086,10 +1090,23 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
return CURLE_BAD_CONTENT_ENCODING;
}
/* Stack the unencoding stage. */
writer = new_unencoding_writer(data, encoding, k->writer_stack);
if(!writer)
return CURLE_OUT_OF_MEMORY;
k->writer_stack = writer;
if(order >= k->writer_stack->order) {
writer = new_unencoding_writer(data, encoding,
k->writer_stack, order);
if(!writer)
return CURLE_OUT_OF_MEMORY;
k->writer_stack = writer;
}
else {
struct contenc_writer *w = k->writer_stack;
while(w->downstream && order < w->downstream->order)
w = w->downstream;
writer = new_unencoding_writer(data, encoding,
w->downstream, order);
if(!writer)
return CURLE_OUT_OF_MEMORY;
w->downstream = writer;
}
}
} while(*enclist);
@ -1099,11 +1116,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
#else
/* Stubs for builds without HTTP. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int maybechunked)
const char *enclist, int is_transfer)
{
(void) data;
(void) enclist;
(void) maybechunked;
(void) is_transfer;
return CURLE_NOT_BUILT_IN;
}