113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_HTTP
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "urldata.h" /* it includes http_chunks.h */
3013498266Sopenharmony_ci#include "sendf.h"   /* for the client write stuff */
3113498266Sopenharmony_ci#include "dynbuf.h"
3213498266Sopenharmony_ci#include "content_encoding.h"
3313498266Sopenharmony_ci#include "http.h"
3413498266Sopenharmony_ci#include "strtoofft.h"
3513498266Sopenharmony_ci#include "warnless.h"
3613498266Sopenharmony_ci
3713498266Sopenharmony_ci/* The last #include files should be: */
3813498266Sopenharmony_ci#include "curl_memory.h"
3913498266Sopenharmony_ci#include "memdebug.h"
4013498266Sopenharmony_ci
4113498266Sopenharmony_ci/*
4213498266Sopenharmony_ci * Chunk format (simplified):
4313498266Sopenharmony_ci *
4413498266Sopenharmony_ci * <HEX SIZE>[ chunk extension ] CRLF
4513498266Sopenharmony_ci * <DATA> CRLF
4613498266Sopenharmony_ci *
4713498266Sopenharmony_ci * Highlights from RFC2616 section 3.6 say:
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci   The chunked encoding modifies the body of a message in order to
5013498266Sopenharmony_ci   transfer it as a series of chunks, each with its own size indicator,
5113498266Sopenharmony_ci   followed by an OPTIONAL trailer containing entity-header fields. This
5213498266Sopenharmony_ci   allows dynamically produced content to be transferred along with the
5313498266Sopenharmony_ci   information necessary for the recipient to verify that it has
5413498266Sopenharmony_ci   received the full message.
5513498266Sopenharmony_ci
5613498266Sopenharmony_ci       Chunked-Body   = *chunk
5713498266Sopenharmony_ci                        last-chunk
5813498266Sopenharmony_ci                        trailer
5913498266Sopenharmony_ci                        CRLF
6013498266Sopenharmony_ci
6113498266Sopenharmony_ci       chunk          = chunk-size [ chunk-extension ] CRLF
6213498266Sopenharmony_ci                        chunk-data CRLF
6313498266Sopenharmony_ci       chunk-size     = 1*HEX
6413498266Sopenharmony_ci       last-chunk     = 1*("0") [ chunk-extension ] CRLF
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci       chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
6713498266Sopenharmony_ci       chunk-ext-name = token
6813498266Sopenharmony_ci       chunk-ext-val  = token | quoted-string
6913498266Sopenharmony_ci       chunk-data     = chunk-size(OCTET)
7013498266Sopenharmony_ci       trailer        = *(entity-header CRLF)
7113498266Sopenharmony_ci
7213498266Sopenharmony_ci   The chunk-size field is a string of hex digits indicating the size of
7313498266Sopenharmony_ci   the chunk. The chunked encoding is ended by any chunk whose size is
7413498266Sopenharmony_ci   zero, followed by the trailer, which is terminated by an empty line.
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci */
7713498266Sopenharmony_ci
7813498266Sopenharmony_civoid Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
7913498266Sopenharmony_ci                         bool ignore_body)
8013498266Sopenharmony_ci{
8113498266Sopenharmony_ci  (void)data;
8213498266Sopenharmony_ci  ch->hexindex = 0;      /* start at 0 */
8313498266Sopenharmony_ci  ch->state = CHUNK_HEX; /* we get hex first! */
8413498266Sopenharmony_ci  ch->last_code = CHUNKE_OK;
8513498266Sopenharmony_ci  Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
8613498266Sopenharmony_ci  ch->ignore_body = ignore_body;
8713498266Sopenharmony_ci}
8813498266Sopenharmony_ci
8913498266Sopenharmony_civoid Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
9013498266Sopenharmony_ci                          bool ignore_body)
9113498266Sopenharmony_ci{
9213498266Sopenharmony_ci  (void)data;
9313498266Sopenharmony_ci  ch->hexindex = 0;      /* start at 0 */
9413498266Sopenharmony_ci  ch->state = CHUNK_HEX; /* we get hex first! */
9513498266Sopenharmony_ci  ch->last_code = CHUNKE_OK;
9613498266Sopenharmony_ci  Curl_dyn_reset(&ch->trailer);
9713498266Sopenharmony_ci  ch->ignore_body = ignore_body;
9813498266Sopenharmony_ci}
9913498266Sopenharmony_ci
10013498266Sopenharmony_civoid Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
10113498266Sopenharmony_ci{
10213498266Sopenharmony_ci  (void)data;
10313498266Sopenharmony_ci  Curl_dyn_free(&ch->trailer);
10413498266Sopenharmony_ci}
10513498266Sopenharmony_ci
10613498266Sopenharmony_cibool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
10713498266Sopenharmony_ci{
10813498266Sopenharmony_ci  (void)data;
10913498266Sopenharmony_ci  return ch->state == CHUNK_DONE;
11013498266Sopenharmony_ci}
11113498266Sopenharmony_ci
11213498266Sopenharmony_cistatic CURLcode httpchunk_readwrite(struct Curl_easy *data,
11313498266Sopenharmony_ci                                    struct Curl_chunker *ch,
11413498266Sopenharmony_ci                                    struct Curl_cwriter *cw_next,
11513498266Sopenharmony_ci                                    const char *buf, size_t blen,
11613498266Sopenharmony_ci                                    size_t *pconsumed)
11713498266Sopenharmony_ci{
11813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
11913498266Sopenharmony_ci  size_t piece;
12013498266Sopenharmony_ci
12113498266Sopenharmony_ci  *pconsumed = 0; /* nothing's written yet */
12213498266Sopenharmony_ci  /* first check terminal states that will not progress anywhere */
12313498266Sopenharmony_ci  if(ch->state == CHUNK_DONE)
12413498266Sopenharmony_ci    return CURLE_OK;
12513498266Sopenharmony_ci  if(ch->state == CHUNK_FAILED)
12613498266Sopenharmony_ci    return CURLE_RECV_ERROR;
12713498266Sopenharmony_ci
12813498266Sopenharmony_ci  /* the original data is written to the client, but we go on with the
12913498266Sopenharmony_ci     chunk read process, to properly calculate the content length */
13013498266Sopenharmony_ci  if(data->set.http_te_skip && !ch->ignore_body) {
13113498266Sopenharmony_ci    if(cw_next)
13213498266Sopenharmony_ci      result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
13313498266Sopenharmony_ci    else
13413498266Sopenharmony_ci      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
13513498266Sopenharmony_ci    if(result) {
13613498266Sopenharmony_ci      ch->state = CHUNK_FAILED;
13713498266Sopenharmony_ci      ch->last_code = CHUNKE_PASSTHRU_ERROR;
13813498266Sopenharmony_ci      return result;
13913498266Sopenharmony_ci    }
14013498266Sopenharmony_ci  }
14113498266Sopenharmony_ci
14213498266Sopenharmony_ci  while(blen) {
14313498266Sopenharmony_ci    switch(ch->state) {
14413498266Sopenharmony_ci    case CHUNK_HEX:
14513498266Sopenharmony_ci      if(ISXDIGIT(*buf)) {
14613498266Sopenharmony_ci        if(ch->hexindex >= CHUNK_MAXNUM_LEN) {
14713498266Sopenharmony_ci          failf(data, "chunk hex-length longer than %d", CHUNK_MAXNUM_LEN);
14813498266Sopenharmony_ci          ch->state = CHUNK_FAILED;
14913498266Sopenharmony_ci          ch->last_code = CHUNKE_TOO_LONG_HEX; /* longer than we support */
15013498266Sopenharmony_ci          return CURLE_RECV_ERROR;
15113498266Sopenharmony_ci        }
15213498266Sopenharmony_ci        ch->hexbuffer[ch->hexindex++] = *buf;
15313498266Sopenharmony_ci        buf++;
15413498266Sopenharmony_ci        blen--;
15513498266Sopenharmony_ci      }
15613498266Sopenharmony_ci      else {
15713498266Sopenharmony_ci        char *endptr;
15813498266Sopenharmony_ci        if(0 == ch->hexindex) {
15913498266Sopenharmony_ci          /* This is illegal data, we received junk where we expected
16013498266Sopenharmony_ci             a hexadecimal digit. */
16113498266Sopenharmony_ci          failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf);
16213498266Sopenharmony_ci          ch->state = CHUNK_FAILED;
16313498266Sopenharmony_ci          ch->last_code = CHUNKE_ILLEGAL_HEX;
16413498266Sopenharmony_ci          return CURLE_RECV_ERROR;
16513498266Sopenharmony_ci        }
16613498266Sopenharmony_ci
16713498266Sopenharmony_ci        /* blen and buf are unmodified */
16813498266Sopenharmony_ci        ch->hexbuffer[ch->hexindex] = 0;
16913498266Sopenharmony_ci        if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) {
17013498266Sopenharmony_ci          failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
17113498266Sopenharmony_ci          ch->state = CHUNK_FAILED;
17213498266Sopenharmony_ci          ch->last_code = CHUNKE_ILLEGAL_HEX;
17313498266Sopenharmony_ci          return CURLE_RECV_ERROR;
17413498266Sopenharmony_ci        }
17513498266Sopenharmony_ci        ch->state = CHUNK_LF; /* now wait for the CRLF */
17613498266Sopenharmony_ci      }
17713498266Sopenharmony_ci      break;
17813498266Sopenharmony_ci
17913498266Sopenharmony_ci    case CHUNK_LF:
18013498266Sopenharmony_ci      /* waiting for the LF after a chunk size */
18113498266Sopenharmony_ci      if(*buf == 0x0a) {
18213498266Sopenharmony_ci        /* we're now expecting data to come, unless size was zero! */
18313498266Sopenharmony_ci        if(0 == ch->datasize) {
18413498266Sopenharmony_ci          ch->state = CHUNK_TRAILER; /* now check for trailers */
18513498266Sopenharmony_ci        }
18613498266Sopenharmony_ci        else
18713498266Sopenharmony_ci          ch->state = CHUNK_DATA;
18813498266Sopenharmony_ci      }
18913498266Sopenharmony_ci
19013498266Sopenharmony_ci      buf++;
19113498266Sopenharmony_ci      blen--;
19213498266Sopenharmony_ci      break;
19313498266Sopenharmony_ci
19413498266Sopenharmony_ci    case CHUNK_DATA:
19513498266Sopenharmony_ci      /* We expect 'datasize' of data. We have 'blen' right now, it can be
19613498266Sopenharmony_ci         more or less than 'datasize'. Get the smallest piece.
19713498266Sopenharmony_ci      */
19813498266Sopenharmony_ci      piece = blen;
19913498266Sopenharmony_ci      if(ch->datasize < (curl_off_t)blen)
20013498266Sopenharmony_ci        piece = curlx_sotouz(ch->datasize);
20113498266Sopenharmony_ci
20213498266Sopenharmony_ci      /* Write the data portion available */
20313498266Sopenharmony_ci      if(!data->set.http_te_skip && !ch->ignore_body) {
20413498266Sopenharmony_ci        if(cw_next)
20513498266Sopenharmony_ci          result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
20613498266Sopenharmony_ci                                      buf, piece);
20713498266Sopenharmony_ci        else
20813498266Sopenharmony_ci          result = Curl_client_write(data, CLIENTWRITE_BODY,
20913498266Sopenharmony_ci                                    (char *)buf, piece);
21013498266Sopenharmony_ci        if(result) {
21113498266Sopenharmony_ci          ch->state = CHUNK_FAILED;
21213498266Sopenharmony_ci          ch->last_code = CHUNKE_PASSTHRU_ERROR;
21313498266Sopenharmony_ci          return result;
21413498266Sopenharmony_ci        }
21513498266Sopenharmony_ci      }
21613498266Sopenharmony_ci
21713498266Sopenharmony_ci      *pconsumed += piece;
21813498266Sopenharmony_ci      ch->datasize -= piece; /* decrease amount left to expect */
21913498266Sopenharmony_ci      buf += piece;    /* move read pointer forward */
22013498266Sopenharmony_ci      blen -= piece;   /* decrease space left in this round */
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci      if(0 == ch->datasize)
22313498266Sopenharmony_ci        /* end of data this round, we now expect a trailing CRLF */
22413498266Sopenharmony_ci        ch->state = CHUNK_POSTLF;
22513498266Sopenharmony_ci      break;
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci    case CHUNK_POSTLF:
22813498266Sopenharmony_ci      if(*buf == 0x0a) {
22913498266Sopenharmony_ci        /* The last one before we go back to hex state and start all over. */
23013498266Sopenharmony_ci        Curl_httpchunk_reset(data, ch, ch->ignore_body);
23113498266Sopenharmony_ci      }
23213498266Sopenharmony_ci      else if(*buf != 0x0d) {
23313498266Sopenharmony_ci        ch->state = CHUNK_FAILED;
23413498266Sopenharmony_ci        ch->last_code = CHUNKE_BAD_CHUNK;
23513498266Sopenharmony_ci        return CURLE_RECV_ERROR;
23613498266Sopenharmony_ci      }
23713498266Sopenharmony_ci      buf++;
23813498266Sopenharmony_ci      blen--;
23913498266Sopenharmony_ci      break;
24013498266Sopenharmony_ci
24113498266Sopenharmony_ci    case CHUNK_TRAILER:
24213498266Sopenharmony_ci      if((*buf == 0x0d) || (*buf == 0x0a)) {
24313498266Sopenharmony_ci        char *tr = Curl_dyn_ptr(&ch->trailer);
24413498266Sopenharmony_ci        /* this is the end of a trailer, but if the trailer was zero bytes
24513498266Sopenharmony_ci           there was no trailer and we move on */
24613498266Sopenharmony_ci
24713498266Sopenharmony_ci        if(tr) {
24813498266Sopenharmony_ci          size_t trlen;
24913498266Sopenharmony_ci          result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
25013498266Sopenharmony_ci          if(result) {
25113498266Sopenharmony_ci            ch->state = CHUNK_FAILED;
25213498266Sopenharmony_ci            ch->last_code = CHUNKE_OUT_OF_MEMORY;
25313498266Sopenharmony_ci            return result;
25413498266Sopenharmony_ci          }
25513498266Sopenharmony_ci          tr = Curl_dyn_ptr(&ch->trailer);
25613498266Sopenharmony_ci          trlen = Curl_dyn_len(&ch->trailer);
25713498266Sopenharmony_ci          if(!data->set.http_te_skip) {
25813498266Sopenharmony_ci            if(cw_next)
25913498266Sopenharmony_ci              result = Curl_cwriter_write(data, cw_next,
26013498266Sopenharmony_ci                                          CLIENTWRITE_HEADER|
26113498266Sopenharmony_ci                                          CLIENTWRITE_TRAILER,
26213498266Sopenharmony_ci                                          tr, trlen);
26313498266Sopenharmony_ci            else
26413498266Sopenharmony_ci              result = Curl_client_write(data,
26513498266Sopenharmony_ci                                         CLIENTWRITE_HEADER|
26613498266Sopenharmony_ci                                         CLIENTWRITE_TRAILER,
26713498266Sopenharmony_ci                                         tr, trlen);
26813498266Sopenharmony_ci            if(result) {
26913498266Sopenharmony_ci              ch->state = CHUNK_FAILED;
27013498266Sopenharmony_ci              ch->last_code = CHUNKE_PASSTHRU_ERROR;
27113498266Sopenharmony_ci              return result;
27213498266Sopenharmony_ci            }
27313498266Sopenharmony_ci          }
27413498266Sopenharmony_ci          Curl_dyn_reset(&ch->trailer);
27513498266Sopenharmony_ci          ch->state = CHUNK_TRAILER_CR;
27613498266Sopenharmony_ci          if(*buf == 0x0a)
27713498266Sopenharmony_ci            /* already on the LF */
27813498266Sopenharmony_ci            break;
27913498266Sopenharmony_ci        }
28013498266Sopenharmony_ci        else {
28113498266Sopenharmony_ci          /* no trailer, we're on the final CRLF pair */
28213498266Sopenharmony_ci          ch->state = CHUNK_TRAILER_POSTCR;
28313498266Sopenharmony_ci          break; /* don't advance the pointer */
28413498266Sopenharmony_ci        }
28513498266Sopenharmony_ci      }
28613498266Sopenharmony_ci      else {
28713498266Sopenharmony_ci        result = Curl_dyn_addn(&ch->trailer, buf, 1);
28813498266Sopenharmony_ci        if(result) {
28913498266Sopenharmony_ci          ch->state = CHUNK_FAILED;
29013498266Sopenharmony_ci          ch->last_code = CHUNKE_OUT_OF_MEMORY;
29113498266Sopenharmony_ci          return result;
29213498266Sopenharmony_ci        }
29313498266Sopenharmony_ci      }
29413498266Sopenharmony_ci      buf++;
29513498266Sopenharmony_ci      blen--;
29613498266Sopenharmony_ci      break;
29713498266Sopenharmony_ci
29813498266Sopenharmony_ci    case CHUNK_TRAILER_CR:
29913498266Sopenharmony_ci      if(*buf == 0x0a) {
30013498266Sopenharmony_ci        ch->state = CHUNK_TRAILER_POSTCR;
30113498266Sopenharmony_ci        buf++;
30213498266Sopenharmony_ci        blen--;
30313498266Sopenharmony_ci      }
30413498266Sopenharmony_ci      else {
30513498266Sopenharmony_ci        ch->state = CHUNK_FAILED;
30613498266Sopenharmony_ci        ch->last_code = CHUNKE_BAD_CHUNK;
30713498266Sopenharmony_ci        return CURLE_RECV_ERROR;
30813498266Sopenharmony_ci      }
30913498266Sopenharmony_ci      break;
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci    case CHUNK_TRAILER_POSTCR:
31213498266Sopenharmony_ci      /* We enter this state when a CR should arrive so we expect to
31313498266Sopenharmony_ci         have to first pass a CR before we wait for LF */
31413498266Sopenharmony_ci      if((*buf != 0x0d) && (*buf != 0x0a)) {
31513498266Sopenharmony_ci        /* not a CR then it must be another header in the trailer */
31613498266Sopenharmony_ci        ch->state = CHUNK_TRAILER;
31713498266Sopenharmony_ci        break;
31813498266Sopenharmony_ci      }
31913498266Sopenharmony_ci      if(*buf == 0x0d) {
32013498266Sopenharmony_ci        /* skip if CR */
32113498266Sopenharmony_ci        buf++;
32213498266Sopenharmony_ci        blen--;
32313498266Sopenharmony_ci      }
32413498266Sopenharmony_ci      /* now wait for the final LF */
32513498266Sopenharmony_ci      ch->state = CHUNK_STOP;
32613498266Sopenharmony_ci      break;
32713498266Sopenharmony_ci
32813498266Sopenharmony_ci    case CHUNK_STOP:
32913498266Sopenharmony_ci      if(*buf == 0x0a) {
33013498266Sopenharmony_ci        blen--;
33113498266Sopenharmony_ci        /* Record the length of any data left in the end of the buffer
33213498266Sopenharmony_ci           even if there's no more chunks to read */
33313498266Sopenharmony_ci        ch->datasize = blen;
33413498266Sopenharmony_ci        ch->state = CHUNK_DONE;
33513498266Sopenharmony_ci        return CURLE_OK;
33613498266Sopenharmony_ci      }
33713498266Sopenharmony_ci      else {
33813498266Sopenharmony_ci        ch->state = CHUNK_FAILED;
33913498266Sopenharmony_ci        ch->last_code = CHUNKE_BAD_CHUNK;
34013498266Sopenharmony_ci        return CURLE_RECV_ERROR;
34113498266Sopenharmony_ci      }
34213498266Sopenharmony_ci    case CHUNK_DONE:
34313498266Sopenharmony_ci      return CURLE_OK;
34413498266Sopenharmony_ci
34513498266Sopenharmony_ci    case CHUNK_FAILED:
34613498266Sopenharmony_ci      return CURLE_RECV_ERROR;
34713498266Sopenharmony_ci    }
34813498266Sopenharmony_ci
34913498266Sopenharmony_ci  }
35013498266Sopenharmony_ci  return CURLE_OK;
35113498266Sopenharmony_ci}
35213498266Sopenharmony_ci
35313498266Sopenharmony_cistatic const char *Curl_chunked_strerror(CHUNKcode code)
35413498266Sopenharmony_ci{
35513498266Sopenharmony_ci  switch(code) {
35613498266Sopenharmony_ci  default:
35713498266Sopenharmony_ci    return "OK";
35813498266Sopenharmony_ci  case CHUNKE_TOO_LONG_HEX:
35913498266Sopenharmony_ci    return "Too long hexadecimal number";
36013498266Sopenharmony_ci  case CHUNKE_ILLEGAL_HEX:
36113498266Sopenharmony_ci    return "Illegal or missing hexadecimal sequence";
36213498266Sopenharmony_ci  case CHUNKE_BAD_CHUNK:
36313498266Sopenharmony_ci    return "Malformed encoding found";
36413498266Sopenharmony_ci  case CHUNKE_PASSTHRU_ERROR:
36513498266Sopenharmony_ci    return "Error writing data to client";
36613498266Sopenharmony_ci  case CHUNKE_BAD_ENCODING:
36713498266Sopenharmony_ci    return "Bad content-encoding found";
36813498266Sopenharmony_ci  case CHUNKE_OUT_OF_MEMORY:
36913498266Sopenharmony_ci    return "Out of memory";
37013498266Sopenharmony_ci  }
37113498266Sopenharmony_ci}
37213498266Sopenharmony_ci
37313498266Sopenharmony_ciCURLcode Curl_httpchunk_read(struct Curl_easy *data,
37413498266Sopenharmony_ci                             struct Curl_chunker *ch,
37513498266Sopenharmony_ci                             char *buf, size_t blen,
37613498266Sopenharmony_ci                             size_t *pconsumed)
37713498266Sopenharmony_ci{
37813498266Sopenharmony_ci  return httpchunk_readwrite(data, ch, NULL, buf, blen, pconsumed);
37913498266Sopenharmony_ci}
38013498266Sopenharmony_ci
38113498266Sopenharmony_cistruct chunked_writer {
38213498266Sopenharmony_ci  struct Curl_cwriter super;
38313498266Sopenharmony_ci  struct Curl_chunker ch;
38413498266Sopenharmony_ci};
38513498266Sopenharmony_ci
38613498266Sopenharmony_cistatic CURLcode cw_chunked_init(struct Curl_easy *data,
38713498266Sopenharmony_ci                                struct Curl_cwriter *writer)
38813498266Sopenharmony_ci{
38913498266Sopenharmony_ci  struct chunked_writer *ctx = (struct chunked_writer *)writer;
39013498266Sopenharmony_ci
39113498266Sopenharmony_ci  data->req.chunk = TRUE;      /* chunks coming our way. */
39213498266Sopenharmony_ci  Curl_httpchunk_init(data, &ctx->ch, FALSE);
39313498266Sopenharmony_ci  return CURLE_OK;
39413498266Sopenharmony_ci}
39513498266Sopenharmony_ci
39613498266Sopenharmony_cistatic void cw_chunked_close(struct Curl_easy *data,
39713498266Sopenharmony_ci                             struct Curl_cwriter *writer)
39813498266Sopenharmony_ci{
39913498266Sopenharmony_ci  struct chunked_writer *ctx = (struct chunked_writer *)writer;
40013498266Sopenharmony_ci  Curl_httpchunk_free(data, &ctx->ch);
40113498266Sopenharmony_ci}
40213498266Sopenharmony_ci
40313498266Sopenharmony_cistatic CURLcode cw_chunked_write(struct Curl_easy *data,
40413498266Sopenharmony_ci                                 struct Curl_cwriter *writer, int type,
40513498266Sopenharmony_ci                                 const char *buf, size_t blen)
40613498266Sopenharmony_ci{
40713498266Sopenharmony_ci  struct chunked_writer *ctx = (struct chunked_writer *)writer;
40813498266Sopenharmony_ci  CURLcode result;
40913498266Sopenharmony_ci  size_t consumed;
41013498266Sopenharmony_ci
41113498266Sopenharmony_ci  if(!(type & CLIENTWRITE_BODY))
41213498266Sopenharmony_ci    return Curl_cwriter_write(data, writer->next, type, buf, blen);
41313498266Sopenharmony_ci
41413498266Sopenharmony_ci  consumed = 0;
41513498266Sopenharmony_ci  result = httpchunk_readwrite(data, &ctx->ch, writer->next, buf, blen,
41613498266Sopenharmony_ci                               &consumed);
41713498266Sopenharmony_ci
41813498266Sopenharmony_ci  if(result) {
41913498266Sopenharmony_ci    if(CHUNKE_PASSTHRU_ERROR == ctx->ch.last_code) {
42013498266Sopenharmony_ci      failf(data, "Failed reading the chunked-encoded stream");
42113498266Sopenharmony_ci    }
42213498266Sopenharmony_ci    else {
42313498266Sopenharmony_ci      failf(data, "%s in chunked-encoding",
42413498266Sopenharmony_ci            Curl_chunked_strerror(ctx->ch.last_code));
42513498266Sopenharmony_ci    }
42613498266Sopenharmony_ci    return result;
42713498266Sopenharmony_ci  }
42813498266Sopenharmony_ci
42913498266Sopenharmony_ci  blen -= consumed;
43013498266Sopenharmony_ci  if(CHUNK_DONE == ctx->ch.state) {
43113498266Sopenharmony_ci    /* chunks read successfully, download is complete */
43213498266Sopenharmony_ci    data->req.download_done = TRUE;
43313498266Sopenharmony_ci    if(blen) {
43413498266Sopenharmony_ci      infof(data, "Leftovers after chunking: %zu bytes", blen);
43513498266Sopenharmony_ci    }
43613498266Sopenharmony_ci  }
43713498266Sopenharmony_ci  else if((type & CLIENTWRITE_EOS) && !data->req.no_body) {
43813498266Sopenharmony_ci    failf(data, "transfer closed with outstanding read data remaining");
43913498266Sopenharmony_ci    return CURLE_PARTIAL_FILE;
44013498266Sopenharmony_ci  }
44113498266Sopenharmony_ci
44213498266Sopenharmony_ci  return CURLE_OK;
44313498266Sopenharmony_ci}
44413498266Sopenharmony_ci
44513498266Sopenharmony_ci/* HTTP chunked Transfer-Encoding decoder */
44613498266Sopenharmony_ciconst struct Curl_cwtype Curl_httpchunk_unencoder = {
44713498266Sopenharmony_ci  "chunked",
44813498266Sopenharmony_ci  NULL,
44913498266Sopenharmony_ci  cw_chunked_init,
45013498266Sopenharmony_ci  cw_chunked_write,
45113498266Sopenharmony_ci  cw_chunked_close,
45213498266Sopenharmony_ci  sizeof(struct chunked_writer)
45313498266Sopenharmony_ci};
45413498266Sopenharmony_ci
45513498266Sopenharmony_ci#endif /* CURL_DISABLE_HTTP */
456