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