1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 #ifndef CURL_DISABLE_HTTP
28
29 #include "urldata.h" /* it includes http_chunks.h */
30 #include "curl_printf.h"
31 #include "curl_trc.h"
32 #include "sendf.h" /* for the client write stuff */
33 #include "dynbuf.h"
34 #include "content_encoding.h"
35 #include "http.h"
36 #include "multiif.h"
37 #include "strtoofft.h"
38 #include "warnless.h"
39
40 /* The last #include files should be: */
41 #include "curl_memory.h"
42 #include "memdebug.h"
43
44 /*
45 * Chunk format (simplified):
46 *
47 * <HEX SIZE>[ chunk extension ] CRLF
48 * <DATA> CRLF
49 *
50 * Highlights from RFC2616 section 3.6 say:
51
52 The chunked encoding modifies the body of a message in order to
53 transfer it as a series of chunks, each with its own size indicator,
54 followed by an OPTIONAL trailer containing entity-header fields. This
55 allows dynamically produced content to be transferred along with the
56 information necessary for the recipient to verify that it has
57 received the full message.
58
59 Chunked-Body = *chunk
60 last-chunk
61 trailer
62 CRLF
63
64 chunk = chunk-size [ chunk-extension ] CRLF
65 chunk-data CRLF
66 chunk-size = 1*HEX
67 last-chunk = 1*("0") [ chunk-extension ] CRLF
68
69 chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
70 chunk-ext-name = token
71 chunk-ext-val = token | quoted-string
72 chunk-data = chunk-size(OCTET)
73 trailer = *(entity-header CRLF)
74
75 The chunk-size field is a string of hex digits indicating the size of
76 the chunk. The chunked encoding is ended by any chunk whose size is
77 zero, followed by the trailer, which is terminated by an empty line.
78
79 */
80
Curl_httpchunk_init(struct Curl_easy * data,struct Curl_chunker * ch,bool ignore_body)81 void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
82 bool ignore_body)
83 {
84 (void)data;
85 ch->hexindex = 0; /* start at 0 */
86 ch->state = CHUNK_HEX; /* we get hex first! */
87 ch->last_code = CHUNKE_OK;
88 Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
89 ch->ignore_body = ignore_body;
90 }
91
Curl_httpchunk_reset(struct Curl_easy * data,struct Curl_chunker * ch,bool ignore_body)92 void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
93 bool ignore_body)
94 {
95 (void)data;
96 ch->hexindex = 0; /* start at 0 */
97 ch->state = CHUNK_HEX; /* we get hex first! */
98 ch->last_code = CHUNKE_OK;
99 Curl_dyn_reset(&ch->trailer);
100 ch->ignore_body = ignore_body;
101 }
102
Curl_httpchunk_free(struct Curl_easy * data,struct Curl_chunker * ch)103 void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
104 {
105 (void)data;
106 Curl_dyn_free(&ch->trailer);
107 }
108
Curl_httpchunk_is_done(struct Curl_easy * data,struct Curl_chunker * ch)109 bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
110 {
111 (void)data;
112 return ch->state == CHUNK_DONE;
113 }
114
httpchunk_readwrite(struct Curl_easy * data,struct Curl_chunker * ch,struct Curl_cwriter * cw_next,const char * buf,size_t blen,size_t * pconsumed)115 static CURLcode httpchunk_readwrite(struct Curl_easy *data,
116 struct Curl_chunker *ch,
117 struct Curl_cwriter *cw_next,
118 const char *buf, size_t blen,
119 size_t *pconsumed)
120 {
121 CURLcode result = CURLE_OK;
122 size_t piece;
123
124 *pconsumed = 0; /* nothing's written yet */
125 /* first check terminal states that will not progress anywhere */
126 if(ch->state == CHUNK_DONE)
127 return CURLE_OK;
128 if(ch->state == CHUNK_FAILED)
129 return CURLE_RECV_ERROR;
130
131 /* the original data is written to the client, but we go on with the
132 chunk read process, to properly calculate the content length */
133 if(data->set.http_te_skip && !ch->ignore_body) {
134 if(cw_next)
135 result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
136 else
137 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
138 if(result) {
139 ch->state = CHUNK_FAILED;
140 ch->last_code = CHUNKE_PASSTHRU_ERROR;
141 return result;
142 }
143 }
144
145 while(blen) {
146 switch(ch->state) {
147 case CHUNK_HEX:
148 if(ISXDIGIT(*buf)) {
149 if(ch->hexindex >= CHUNK_MAXNUM_LEN) {
150 failf(data, "chunk hex-length longer than %d", CHUNK_MAXNUM_LEN);
151 ch->state = CHUNK_FAILED;
152 ch->last_code = CHUNKE_TOO_LONG_HEX; /* longer than we support */
153 return CURLE_RECV_ERROR;
154 }
155 ch->hexbuffer[ch->hexindex++] = *buf;
156 buf++;
157 blen--;
158 (*pconsumed)++;
159 }
160 else {
161 if(0 == ch->hexindex) {
162 /* This is illegal data, we received junk where we expected
163 a hexadecimal digit. */
164 failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf);
165 ch->state = CHUNK_FAILED;
166 ch->last_code = CHUNKE_ILLEGAL_HEX;
167 return CURLE_RECV_ERROR;
168 }
169
170 /* blen and buf are unmodified */
171 ch->hexbuffer[ch->hexindex] = 0;
172 if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) {
173 failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
174 ch->state = CHUNK_FAILED;
175 ch->last_code = CHUNKE_ILLEGAL_HEX;
176 return CURLE_RECV_ERROR;
177 }
178 ch->state = CHUNK_LF; /* now wait for the CRLF */
179 }
180 break;
181
182 case CHUNK_LF:
183 /* waiting for the LF after a chunk size */
184 if(*buf == 0x0a) {
185 /* we are now expecting data to come, unless size was zero! */
186 if(0 == ch->datasize) {
187 ch->state = CHUNK_TRAILER; /* now check for trailers */
188 }
189 else {
190 ch->state = CHUNK_DATA;
191 CURL_TRC_WRITE(data, "http_chunked, chunk start of %"
192 FMT_OFF_T " bytes", ch->datasize);
193 }
194 }
195
196 buf++;
197 blen--;
198 (*pconsumed)++;
199 break;
200
201 case CHUNK_DATA:
202 /* We expect 'datasize' of data. We have 'blen' right now, it can be
203 more or less than 'datasize'. Get the smallest piece.
204 */
205 piece = blen;
206 if(ch->datasize < (curl_off_t)blen)
207 piece = curlx_sotouz(ch->datasize);
208
209 /* Write the data portion available */
210 if(!data->set.http_te_skip && !ch->ignore_body) {
211 if(cw_next)
212 result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
213 buf, piece);
214 else
215 result = Curl_client_write(data, CLIENTWRITE_BODY,
216 (char *)buf, piece);
217 if(result) {
218 ch->state = CHUNK_FAILED;
219 ch->last_code = CHUNKE_PASSTHRU_ERROR;
220 return result;
221 }
222 }
223
224 *pconsumed += piece;
225 ch->datasize -= piece; /* decrease amount left to expect */
226 buf += piece; /* move read pointer forward */
227 blen -= piece; /* decrease space left in this round */
228 CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %"
229 FMT_OFF_T " bytes in chunk remain",
230 piece, ch->datasize);
231
232 if(0 == ch->datasize)
233 /* end of data this round, we now expect a trailing CRLF */
234 ch->state = CHUNK_POSTLF;
235 break;
236
237 case CHUNK_POSTLF:
238 if(*buf == 0x0a) {
239 /* The last one before we go back to hex state and start all over. */
240 Curl_httpchunk_reset(data, ch, ch->ignore_body);
241 }
242 else if(*buf != 0x0d) {
243 ch->state = CHUNK_FAILED;
244 ch->last_code = CHUNKE_BAD_CHUNK;
245 return CURLE_RECV_ERROR;
246 }
247 buf++;
248 blen--;
249 (*pconsumed)++;
250 break;
251
252 case CHUNK_TRAILER:
253 if((*buf == 0x0d) || (*buf == 0x0a)) {
254 char *tr = Curl_dyn_ptr(&ch->trailer);
255 /* this is the end of a trailer, but if the trailer was zero bytes
256 there was no trailer and we move on */
257
258 if(tr) {
259 size_t trlen;
260 result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
261 if(result) {
262 ch->state = CHUNK_FAILED;
263 ch->last_code = CHUNKE_OUT_OF_MEMORY;
264 return result;
265 }
266 tr = Curl_dyn_ptr(&ch->trailer);
267 trlen = Curl_dyn_len(&ch->trailer);
268 if(!data->set.http_te_skip) {
269 if(cw_next)
270 result = Curl_cwriter_write(data, cw_next,
271 CLIENTWRITE_HEADER|
272 CLIENTWRITE_TRAILER,
273 tr, trlen);
274 else
275 result = Curl_client_write(data,
276 CLIENTWRITE_HEADER|
277 CLIENTWRITE_TRAILER,
278 tr, trlen);
279 if(result) {
280 ch->state = CHUNK_FAILED;
281 ch->last_code = CHUNKE_PASSTHRU_ERROR;
282 return result;
283 }
284 }
285 Curl_dyn_reset(&ch->trailer);
286 ch->state = CHUNK_TRAILER_CR;
287 if(*buf == 0x0a)
288 /* already on the LF */
289 break;
290 }
291 else {
292 /* no trailer, we are on the final CRLF pair */
293 ch->state = CHUNK_TRAILER_POSTCR;
294 break; /* do not advance the pointer */
295 }
296 }
297 else {
298 result = Curl_dyn_addn(&ch->trailer, buf, 1);
299 if(result) {
300 ch->state = CHUNK_FAILED;
301 ch->last_code = CHUNKE_OUT_OF_MEMORY;
302 return result;
303 }
304 }
305 buf++;
306 blen--;
307 (*pconsumed)++;
308 break;
309
310 case CHUNK_TRAILER_CR:
311 if(*buf == 0x0a) {
312 ch->state = CHUNK_TRAILER_POSTCR;
313 buf++;
314 blen--;
315 (*pconsumed)++;
316 }
317 else {
318 ch->state = CHUNK_FAILED;
319 ch->last_code = CHUNKE_BAD_CHUNK;
320 return CURLE_RECV_ERROR;
321 }
322 break;
323
324 case CHUNK_TRAILER_POSTCR:
325 /* We enter this state when a CR should arrive so we expect to
326 have to first pass a CR before we wait for LF */
327 if((*buf != 0x0d) && (*buf != 0x0a)) {
328 /* not a CR then it must be another header in the trailer */
329 ch->state = CHUNK_TRAILER;
330 break;
331 }
332 if(*buf == 0x0d) {
333 /* skip if CR */
334 buf++;
335 blen--;
336 (*pconsumed)++;
337 }
338 /* now wait for the final LF */
339 ch->state = CHUNK_STOP;
340 break;
341
342 case CHUNK_STOP:
343 if(*buf == 0x0a) {
344 blen--;
345 (*pconsumed)++;
346 /* Record the length of any data left in the end of the buffer
347 even if there is no more chunks to read */
348 ch->datasize = blen;
349 ch->state = CHUNK_DONE;
350 CURL_TRC_WRITE(data, "http_chunk, response complete");
351 return CURLE_OK;
352 }
353 else {
354 ch->state = CHUNK_FAILED;
355 ch->last_code = CHUNKE_BAD_CHUNK;
356 CURL_TRC_WRITE(data, "http_chunk error, expected 0x0a, seeing 0x%ux",
357 (unsigned int)*buf);
358 return CURLE_RECV_ERROR;
359 }
360 case CHUNK_DONE:
361 return CURLE_OK;
362
363 case CHUNK_FAILED:
364 return CURLE_RECV_ERROR;
365 }
366
367 }
368 return CURLE_OK;
369 }
370
Curl_chunked_strerror(CHUNKcode code)371 static const char *Curl_chunked_strerror(CHUNKcode code)
372 {
373 switch(code) {
374 default:
375 return "OK";
376 case CHUNKE_TOO_LONG_HEX:
377 return "Too long hexadecimal number";
378 case CHUNKE_ILLEGAL_HEX:
379 return "Illegal or missing hexadecimal sequence";
380 case CHUNKE_BAD_CHUNK:
381 return "Malformed encoding found";
382 case CHUNKE_PASSTHRU_ERROR:
383 return "Error writing data to client";
384 case CHUNKE_BAD_ENCODING:
385 return "Bad content-encoding found";
386 case CHUNKE_OUT_OF_MEMORY:
387 return "Out of memory";
388 }
389 }
390
Curl_httpchunk_read(struct Curl_easy * data,struct Curl_chunker * ch,char * buf,size_t blen,size_t * pconsumed)391 CURLcode Curl_httpchunk_read(struct Curl_easy *data,
392 struct Curl_chunker *ch,
393 char *buf, size_t blen,
394 size_t *pconsumed)
395 {
396 return httpchunk_readwrite(data, ch, NULL, buf, blen, pconsumed);
397 }
398
399 struct chunked_writer {
400 struct Curl_cwriter super;
401 struct Curl_chunker ch;
402 };
403
cw_chunked_init(struct Curl_easy * data,struct Curl_cwriter * writer)404 static CURLcode cw_chunked_init(struct Curl_easy *data,
405 struct Curl_cwriter *writer)
406 {
407 struct chunked_writer *ctx = writer->ctx;
408
409 data->req.chunk = TRUE; /* chunks coming our way. */
410 Curl_httpchunk_init(data, &ctx->ch, FALSE);
411 return CURLE_OK;
412 }
413
cw_chunked_close(struct Curl_easy * data,struct Curl_cwriter * writer)414 static void cw_chunked_close(struct Curl_easy *data,
415 struct Curl_cwriter *writer)
416 {
417 struct chunked_writer *ctx = writer->ctx;
418 Curl_httpchunk_free(data, &ctx->ch);
419 }
420
cw_chunked_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t blen)421 static CURLcode cw_chunked_write(struct Curl_easy *data,
422 struct Curl_cwriter *writer, int type,
423 const char *buf, size_t blen)
424 {
425 struct chunked_writer *ctx = writer->ctx;
426 CURLcode result;
427 size_t consumed;
428
429 if(!(type & CLIENTWRITE_BODY))
430 return Curl_cwriter_write(data, writer->next, type, buf, blen);
431
432 consumed = 0;
433 result = httpchunk_readwrite(data, &ctx->ch, writer->next, buf, blen,
434 &consumed);
435
436 if(result) {
437 if(CHUNKE_PASSTHRU_ERROR == ctx->ch.last_code) {
438 failf(data, "Failed reading the chunked-encoded stream");
439 }
440 else {
441 failf(data, "%s in chunked-encoding",
442 Curl_chunked_strerror(ctx->ch.last_code));
443 }
444 return result;
445 }
446
447 blen -= consumed;
448 if(CHUNK_DONE == ctx->ch.state) {
449 /* chunks read successfully, download is complete */
450 data->req.download_done = TRUE;
451 if(blen) {
452 infof(data, "Leftovers after chunking: %zu bytes", blen);
453 }
454 }
455 else if((type & CLIENTWRITE_EOS) && !data->req.no_body) {
456 failf(data, "transfer closed with outstanding read data remaining");
457 return CURLE_PARTIAL_FILE;
458 }
459
460 return CURLE_OK;
461 }
462
463 /* HTTP chunked Transfer-Encoding decoder */
464 const struct Curl_cwtype Curl_httpchunk_unencoder = {
465 "chunked",
466 NULL,
467 cw_chunked_init,
468 cw_chunked_write,
469 cw_chunked_close,
470 sizeof(struct chunked_writer)
471 };
472
473 /* max length of an HTTP chunk that we want to generate */
474 #define CURL_CHUNKED_MINLEN (1024)
475 #define CURL_CHUNKED_MAXLEN (64 * 1024)
476
477 struct chunked_reader {
478 struct Curl_creader super;
479 struct bufq chunkbuf;
480 BIT(read_eos); /* we read an EOS from the next reader */
481 BIT(eos); /* we have returned an EOS */
482 };
483
cr_chunked_init(struct Curl_easy * data,struct Curl_creader * reader)484 static CURLcode cr_chunked_init(struct Curl_easy *data,
485 struct Curl_creader *reader)
486 {
487 struct chunked_reader *ctx = reader->ctx;
488 (void)data;
489 Curl_bufq_init2(&ctx->chunkbuf, CURL_CHUNKED_MAXLEN, 2, BUFQ_OPT_SOFT_LIMIT);
490 return CURLE_OK;
491 }
492
cr_chunked_close(struct Curl_easy * data,struct Curl_creader * reader)493 static void cr_chunked_close(struct Curl_easy *data,
494 struct Curl_creader *reader)
495 {
496 struct chunked_reader *ctx = reader->ctx;
497 (void)data;
498 Curl_bufq_free(&ctx->chunkbuf);
499 }
500
add_last_chunk(struct Curl_easy * data,struct Curl_creader * reader)501 static CURLcode add_last_chunk(struct Curl_easy *data,
502 struct Curl_creader *reader)
503 {
504 struct chunked_reader *ctx = reader->ctx;
505 struct curl_slist *trailers = NULL, *tr;
506 CURLcode result;
507 size_t n;
508 int rc;
509
510 if(!data->set.trailer_callback) {
511 CURL_TRC_READ(data, "http_chunk, added last, empty chunk");
512 return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n);
513 }
514
515 result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n"), &n);
516 if(result)
517 goto out;
518
519 Curl_set_in_callback(data, TRUE);
520 rc = data->set.trailer_callback(&trailers, data->set.trailer_data);
521 Curl_set_in_callback(data, FALSE);
522
523 if(rc != CURL_TRAILERFUNC_OK) {
524 failf(data, "operation aborted by trailing headers callback");
525 result = CURLE_ABORTED_BY_CALLBACK;
526 goto out;
527 }
528
529 for(tr = trailers; tr; tr = tr->next) {
530 /* only add correctly formatted trailers */
531 char *ptr = strchr(tr->data, ':');
532 if(!ptr || *(ptr + 1) != ' ') {
533 infof(data, "Malformatted trailing header, skipping trailer");
534 continue;
535 }
536
537 result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data,
538 strlen(tr->data), &n);
539 if(!result)
540 result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
541 if(result)
542 goto out;
543 }
544
545 result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
546
547 out:
548 curl_slist_free_all(trailers);
549 CURL_TRC_READ(data, "http_chunk, added last chunk with trailers "
550 "from client -> %d", result);
551 return result;
552 }
553
add_chunk(struct Curl_easy * data,struct Curl_creader * reader,char * buf,size_t blen)554 static CURLcode add_chunk(struct Curl_easy *data,
555 struct Curl_creader *reader,
556 char *buf, size_t blen)
557 {
558 struct chunked_reader *ctx = reader->ctx;
559 CURLcode result;
560 char tmp[CURL_CHUNKED_MINLEN];
561 size_t nread;
562 bool eos;
563
564 DEBUGASSERT(!ctx->read_eos);
565 blen = CURLMIN(blen, CURL_CHUNKED_MAXLEN); /* respect our buffer pref */
566 if(blen < sizeof(tmp)) {
567 /* small read, make a chunk of decent size */
568 buf = tmp;
569 blen = sizeof(tmp);
570 }
571 else {
572 /* larger read, make a chunk that will fit when read back */
573 blen -= (8 + 2 + 2); /* deduct max overhead, 8 hex + 2*crlf */
574 }
575
576 result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
577 if(result)
578 return result;
579 if(eos)
580 ctx->read_eos = TRUE;
581
582 if(nread) {
583 /* actually got bytes, wrap them into the chunkbuf */
584 char hd[11] = "";
585 int hdlen;
586 size_t n;
587
588 hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread);
589 if(hdlen <= 0)
590 return CURLE_READ_ERROR;
591 /* On a soft-limited bufq, we do not need to check that all was written */
592 result = Curl_bufq_cwrite(&ctx->chunkbuf, hd, hdlen, &n);
593 if(!result)
594 result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n);
595 if(!result)
596 result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n);
597 CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d",
598 nread, result);
599 if(result)
600 return result;
601 }
602
603 if(ctx->read_eos)
604 return add_last_chunk(data, reader);
605 return CURLE_OK;
606 }
607
cr_chunked_read(struct Curl_easy * data,struct Curl_creader * reader,char * buf,size_t blen,size_t * pnread,bool * peos)608 static CURLcode cr_chunked_read(struct Curl_easy *data,
609 struct Curl_creader *reader,
610 char *buf, size_t blen,
611 size_t *pnread, bool *peos)
612 {
613 struct chunked_reader *ctx = reader->ctx;
614 CURLcode result = CURLE_READ_ERROR;
615
616 *pnread = 0;
617 *peos = ctx->eos;
618
619 if(!ctx->eos) {
620 if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
621 /* Still getting data form the next reader, buffer is empty */
622 result = add_chunk(data, reader, buf, blen);
623 if(result)
624 return result;
625 }
626
627 if(!Curl_bufq_is_empty(&ctx->chunkbuf)) {
628 result = Curl_bufq_cread(&ctx->chunkbuf, buf, blen, pnread);
629 if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
630 /* no more data, read all, done. */
631 ctx->eos = TRUE;
632 *peos = TRUE;
633 }
634 return result;
635 }
636 }
637 /* We may get here, because we are done or because callbacks paused */
638 DEBUGASSERT(ctx->eos || !ctx->read_eos);
639 return CURLE_OK;
640 }
641
cr_chunked_total_length(struct Curl_easy * data,struct Curl_creader * reader)642 static curl_off_t cr_chunked_total_length(struct Curl_easy *data,
643 struct Curl_creader *reader)
644 {
645 /* this reader changes length depending on input */
646 (void)data;
647 (void)reader;
648 return -1;
649 }
650
651 /* HTTP chunked Transfer-Encoding encoder */
652 const struct Curl_crtype Curl_httpchunk_encoder = {
653 "chunked",
654 cr_chunked_init,
655 cr_chunked_read,
656 cr_chunked_close,
657 Curl_creader_def_needs_rewind,
658 cr_chunked_total_length,
659 Curl_creader_def_resume_from,
660 Curl_creader_def_rewind,
661 Curl_creader_def_unpause,
662 Curl_creader_def_is_paused,
663 Curl_creader_def_done,
664 sizeof(struct chunked_reader)
665 };
666
Curl_httpchunk_add_reader(struct Curl_easy * data)667 CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data)
668 {
669 struct Curl_creader *reader = NULL;
670 CURLcode result;
671
672 result = Curl_creader_create(&reader, data, &Curl_httpchunk_encoder,
673 CURL_CR_TRANSFER_ENCODE);
674 if(!result)
675 result = Curl_creader_add(data, reader);
676
677 if(result && reader)
678 Curl_creader_free(data, reader);
679 return result;
680 }
681
682 #endif /* CURL_DISABLE_HTTP */
683