VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/http_chunks.c@ 106542

最後變更 在這個檔案從106542是 104083,由 vboxsync 提交於 11 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette