Annotation of embedaddon/curl/lib/http_chunks.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2019, 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.haxx.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: ***************************************************************************/
22:
23: #include "curl_setup.h"
24:
25: #ifndef CURL_DISABLE_HTTP
26:
27: #include "urldata.h" /* it includes http_chunks.h */
28: #include "sendf.h" /* for the client write stuff */
29:
30: #include "content_encoding.h"
31: #include "http.h"
32: #include "non-ascii.h" /* for Curl_convert_to_network prototype */
33: #include "strtoofft.h"
34: #include "warnless.h"
35:
36: /* The last #include files should be: */
37: #include "curl_memory.h"
38: #include "memdebug.h"
39:
40: /*
41: * Chunk format (simplified):
42: *
43: * <HEX SIZE>[ chunk extension ] CRLF
44: * <DATA> CRLF
45: *
46: * Highlights from RFC2616 section 3.6 say:
47:
48: The chunked encoding modifies the body of a message in order to
49: transfer it as a series of chunks, each with its own size indicator,
50: followed by an OPTIONAL trailer containing entity-header fields. This
51: allows dynamically produced content to be transferred along with the
52: information necessary for the recipient to verify that it has
53: received the full message.
54:
55: Chunked-Body = *chunk
56: last-chunk
57: trailer
58: CRLF
59:
60: chunk = chunk-size [ chunk-extension ] CRLF
61: chunk-data CRLF
62: chunk-size = 1*HEX
63: last-chunk = 1*("0") [ chunk-extension ] CRLF
64:
65: chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
66: chunk-ext-name = token
67: chunk-ext-val = token | quoted-string
68: chunk-data = chunk-size(OCTET)
69: trailer = *(entity-header CRLF)
70:
71: The chunk-size field is a string of hex digits indicating the size of
72: the chunk. The chunked encoding is ended by any chunk whose size is
73: zero, followed by the trailer, which is terminated by an empty line.
74:
75: */
76:
77: #ifdef CURL_DOES_CONVERSIONS
78: /* Check for an ASCII hex digit.
79: We avoid the use of ISXDIGIT to accommodate non-ASCII hosts. */
80: static bool Curl_isxdigit_ascii(char digit)
81: {
82: return (digit >= 0x30 && digit <= 0x39) /* 0-9 */
83: || (digit >= 0x41 && digit <= 0x46) /* A-F */
84: || (digit >= 0x61 && digit <= 0x66); /* a-f */
85: }
86: #else
87: #define Curl_isxdigit_ascii(x) Curl_isxdigit(x)
88: #endif
89:
90: void Curl_httpchunk_init(struct connectdata *conn)
91: {
92: struct Curl_chunker *chunk = &conn->chunk;
93: chunk->hexindex = 0; /* start at 0 */
94: chunk->dataleft = 0; /* no data left yet! */
95: chunk->state = CHUNK_HEX; /* we get hex first! */
96: }
97:
98: /*
99: * chunk_read() returns a OK for normal operations, or a positive return code
100: * for errors. STOP means this sequence of chunks is complete. The 'wrote'
101: * argument is set to tell the caller how many bytes we actually passed to the
102: * client (for byte-counting and whatever).
103: *
104: * The states and the state-machine is further explained in the header file.
105: *
106: * This function always uses ASCII hex values to accommodate non-ASCII hosts.
107: * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
108: */
109: CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
110: char *datap,
111: ssize_t datalen,
112: ssize_t *wrotep,
113: CURLcode *extrap)
114: {
115: CURLcode result = CURLE_OK;
116: struct Curl_easy *data = conn->data;
117: struct Curl_chunker *ch = &conn->chunk;
118: struct SingleRequest *k = &data->req;
119: size_t piece;
120: curl_off_t length = (curl_off_t)datalen;
121: size_t *wrote = (size_t *)wrotep;
122:
123: *wrote = 0; /* nothing's written yet */
124:
125: /* the original data is written to the client, but we go on with the
126: chunk read process, to properly calculate the content length*/
127: if(data->set.http_te_skip && !k->ignorebody) {
128: result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
129: if(result) {
130: *extrap = result;
131: return CHUNKE_PASSTHRU_ERROR;
132: }
133: }
134:
135: while(length) {
136: switch(ch->state) {
137: case CHUNK_HEX:
138: if(Curl_isxdigit_ascii(*datap)) {
139: if(ch->hexindex < MAXNUM_SIZE) {
140: ch->hexbuffer[ch->hexindex] = *datap;
141: datap++;
142: length--;
143: ch->hexindex++;
144: }
145: else {
146: return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
147: }
148: }
149: else {
150: char *endptr;
151: if(0 == ch->hexindex)
152: /* This is illegal data, we received junk where we expected
153: a hexadecimal digit. */
154: return CHUNKE_ILLEGAL_HEX;
155:
156: /* length and datap are unmodified */
157: ch->hexbuffer[ch->hexindex] = 0;
158:
159: /* convert to host encoding before calling strtoul */
160: result = Curl_convert_from_network(conn->data, ch->hexbuffer,
161: ch->hexindex);
162: if(result) {
163: /* Curl_convert_from_network calls failf if unsuccessful */
164: /* Treat it as a bad hex character */
165: return CHUNKE_ILLEGAL_HEX;
166: }
167:
168: if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
169: return CHUNKE_ILLEGAL_HEX;
170: ch->state = CHUNK_LF; /* now wait for the CRLF */
171: }
172: break;
173:
174: case CHUNK_LF:
175: /* waiting for the LF after a chunk size */
176: if(*datap == 0x0a) {
177: /* we're now expecting data to come, unless size was zero! */
178: if(0 == ch->datasize) {
179: ch->state = CHUNK_TRAILER; /* now check for trailers */
180: conn->trlPos = 0;
181: }
182: else
183: ch->state = CHUNK_DATA;
184: }
185:
186: datap++;
187: length--;
188: break;
189:
190: case CHUNK_DATA:
191: /* We expect 'datasize' of data. We have 'length' right now, it can be
192: more or less than 'datasize'. Get the smallest piece.
193: */
194: piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
195:
196: /* Write the data portion available */
197: if(!conn->data->set.http_te_skip && !k->ignorebody) {
198: if(!conn->data->set.http_ce_skip && k->writer_stack)
199: result = Curl_unencode_write(conn, k->writer_stack, datap, piece);
200: else
201: result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece);
202:
203: if(result) {
204: *extrap = result;
205: return CHUNKE_PASSTHRU_ERROR;
206: }
207: }
208:
209: *wrote += piece;
210: ch->datasize -= piece; /* decrease amount left to expect */
211: datap += piece; /* move read pointer forward */
212: length -= piece; /* decrease space left in this round */
213:
214: if(0 == ch->datasize)
215: /* end of data this round, we now expect a trailing CRLF */
216: ch->state = CHUNK_POSTLF;
217: break;
218:
219: case CHUNK_POSTLF:
220: if(*datap == 0x0a) {
221: /* The last one before we go back to hex state and start all over. */
222: Curl_httpchunk_init(conn); /* sets state back to CHUNK_HEX */
223: }
224: else if(*datap != 0x0d)
225: return CHUNKE_BAD_CHUNK;
226: datap++;
227: length--;
228: break;
229:
230: case CHUNK_TRAILER:
231: if((*datap == 0x0d) || (*datap == 0x0a)) {
232: /* this is the end of a trailer, but if the trailer was zero bytes
233: there was no trailer and we move on */
234:
235: if(conn->trlPos) {
236: /* we allocate trailer with 3 bytes extra room to fit this */
237: conn->trailer[conn->trlPos++] = 0x0d;
238: conn->trailer[conn->trlPos++] = 0x0a;
239: conn->trailer[conn->trlPos] = 0;
240:
241: /* Convert to host encoding before calling Curl_client_write */
242: result = Curl_convert_from_network(conn->data, conn->trailer,
243: conn->trlPos);
244: if(result)
245: /* Curl_convert_from_network calls failf if unsuccessful */
246: /* Treat it as a bad chunk */
247: return CHUNKE_BAD_CHUNK;
248:
249: if(!data->set.http_te_skip) {
250: result = Curl_client_write(conn, CLIENTWRITE_HEADER,
251: conn->trailer, conn->trlPos);
252: if(result) {
253: *extrap = result;
254: return CHUNKE_PASSTHRU_ERROR;
255: }
256: }
257: conn->trlPos = 0;
258: ch->state = CHUNK_TRAILER_CR;
259: if(*datap == 0x0a)
260: /* already on the LF */
261: break;
262: }
263: else {
264: /* no trailer, we're on the final CRLF pair */
265: ch->state = CHUNK_TRAILER_POSTCR;
266: break; /* don't advance the pointer */
267: }
268: }
269: else {
270: /* conn->trailer is assumed to be freed in url.c on a
271: connection basis */
272: if(conn->trlPos >= conn->trlMax) {
273: /* we always allocate three extra bytes, just because when the full
274: header has been received we append CRLF\0 */
275: char *ptr;
276: if(conn->trlMax) {
277: conn->trlMax *= 2;
278: ptr = realloc(conn->trailer, conn->trlMax + 3);
279: }
280: else {
281: conn->trlMax = 128;
282: ptr = malloc(conn->trlMax + 3);
283: }
284: if(!ptr)
285: return CHUNKE_OUT_OF_MEMORY;
286: conn->trailer = ptr;
287: }
288: conn->trailer[conn->trlPos++]=*datap;
289: }
290: datap++;
291: length--;
292: break;
293:
294: case CHUNK_TRAILER_CR:
295: if(*datap == 0x0a) {
296: ch->state = CHUNK_TRAILER_POSTCR;
297: datap++;
298: length--;
299: }
300: else
301: return CHUNKE_BAD_CHUNK;
302: break;
303:
304: case CHUNK_TRAILER_POSTCR:
305: /* We enter this state when a CR should arrive so we expect to
306: have to first pass a CR before we wait for LF */
307: if((*datap != 0x0d) && (*datap != 0x0a)) {
308: /* not a CR then it must be another header in the trailer */
309: ch->state = CHUNK_TRAILER;
310: break;
311: }
312: if(*datap == 0x0d) {
313: /* skip if CR */
314: datap++;
315: length--;
316: }
317: /* now wait for the final LF */
318: ch->state = CHUNK_STOP;
319: break;
320:
321: case CHUNK_STOP:
322: if(*datap == 0x0a) {
323: length--;
324:
325: /* Record the length of any data left in the end of the buffer
326: even if there's no more chunks to read */
327: ch->dataleft = curlx_sotouz(length);
328:
329: return CHUNKE_STOP; /* return stop */
330: }
331: else
332: return CHUNKE_BAD_CHUNK;
333: }
334: }
335: return CHUNKE_OK;
336: }
337:
338: const char *Curl_chunked_strerror(CHUNKcode code)
339: {
340: switch(code) {
341: default:
342: return "OK";
343: case CHUNKE_TOO_LONG_HEX:
344: return "Too long hexadecimal number";
345: case CHUNKE_ILLEGAL_HEX:
346: return "Illegal or missing hexadecimal sequence";
347: case CHUNKE_BAD_CHUNK:
348: return "Malformed encoding found";
349: case CHUNKE_PASSTHRU_ERROR:
350: DEBUGASSERT(0); /* never used */
351: return "";
352: case CHUNKE_BAD_ENCODING:
353: return "Bad content-encoding found";
354: case CHUNKE_OUT_OF_MEMORY:
355: return "Out of memory";
356: }
357: }
358:
359: #endif /* CURL_DISABLE_HTTP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>