1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
25 #ifndef CURL_DISABLE_HTTP
26 /* -- WIN32 approved -- */
33 #include "urldata.h" /* it includes http_chunks.h */
34 #include "sendf.h" /* for the client write stuff */
36 #include "content_encoding.h" /* 08/29/02 jhrg */
40 #define _MPRINTF_REPLACE /* use our functions only */
41 #include <curl/mprintf.h>
43 /* The last #include file should be: */
47 * Chunk format (simplified):
49 * <HEX SIZE>[ chunk extension ] CRLF
52 * Highlights from RFC2616 section 3.6 say:
54 The chunked encoding modifies the body of a message in order to
55 transfer it as a series of chunks, each with its own size indicator,
56 followed by an OPTIONAL trailer containing entity-header fields. This
57 allows dynamically produced content to be transferred along with the
58 information necessary for the recipient to verify that it has
59 received the full message.
66 chunk = chunk-size [ chunk-extension ] CRLF
69 last-chunk = 1*("0") [ chunk-extension ] CRLF
71 chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
72 chunk-ext-name = token
73 chunk-ext-val = token | quoted-string
74 chunk-data = chunk-size(OCTET)
75 trailer = *(entity-header CRLF)
77 The chunk-size field is a string of hex digits indicating the size of
78 the chunk. The chunked encoding is ended by any chunk whose size is
79 zero, followed by the trailer, which is terminated by an empty line.
84 void Curl_httpchunk_init(struct connectdata *conn)
86 struct Curl_chunker *chunk = &conn->proto.http->chunk;
87 chunk->hexindex=0; /* start at 0 */
88 chunk->dataleft=0; /* no data left yet! */
89 chunk->state = CHUNK_HEX; /* we get hex first! */
93 * chunk_read() returns a OK for normal operations, or a positive return code
94 * for errors. STOP means this sequence of chunks is complete. The 'wrote'
95 * argument is set to tell the caller how many bytes we actually passed to the
96 * client (for byte-counting and whatever).
98 * The states and the state-machine is further explained in the header file.
100 CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
105 CURLcode result=CURLE_OK;
106 struct Curl_chunker *ch = &conn->proto.http->chunk;
107 struct Curl_transfer_keeper *k = &conn->keep;
109 size_t length = (size_t)datalen;
110 size_t *wrote = (size_t *)wrotep;
112 *wrote = 0; /* nothing's written yet */
117 if(isxdigit((int)*datap)) {
118 if(ch->hexindex < MAXNUM_SIZE) {
119 ch->hexbuffer[ch->hexindex] = *datap;
125 return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
129 if(0 == ch->hexindex) {
130 /* This is illegal data, we received junk where we expected
131 a hexadecimal digit. */
132 return CHUNKE_ILLEGAL_HEX;
134 /* length and datap are unmodified */
135 ch->hexbuffer[ch->hexindex]=0;
136 ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
137 ch->state = CHUNK_POSTHEX;
142 /* In this state, we're waiting for CRLF to arrive. We support
143 this to allow so called chunk-extensions to show up here
144 before the CRLF comes. */
146 ch->state = CHUNK_CR;
152 /* waiting for the LF */
154 /* we're now expecting data to come, unless size was zero! */
155 if(0 == ch->datasize) {
156 ch->state = CHUNK_STOP; /* stop reading! */
158 /* This was the final byte, return right now */
163 ch->state = CHUNK_DATA;
166 /* previously we got a fake CR, go back to CR waiting! */
167 ch->state = CHUNK_CR;
173 /* we get pure and fine data
175 We expect another 'datasize' of data. We have 'length' right now,
176 it can be more or less than 'datasize'. Get the smallest piece.
178 piece = (ch->datasize >= length)?length:ch->datasize;
180 /* Write the data portion available */
181 /* Added content-encoding here; untested but almost identical to the
182 tested code in transfer.c. 08/29/02 jhrg */
184 switch (conn->keep.content_encoding) {
188 result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap,
194 /* update conn->keep.str to point to the chunk data. */
195 conn->keep.str = datap;
196 result = Curl_unencode_deflate_write(conn->data, &conn->keep, piece);
200 /* update conn->keep.str to point to the chunk data. */
201 conn->keep.str = datap;
202 result = Curl_unencode_gzip_write(conn->data, &conn->keep, piece);
208 "Unrecognized content encoding type. "
209 "libcurl understands `identity', `deflate' and `gzip' "
210 "content encodings.");
211 return CHUNKE_BAD_ENCODING;
216 return CHUNKE_WRITE_ERROR;
220 ch->datasize -= piece; /* decrease amount left to expect */
221 datap += piece; /* move read pointer forward */
222 length -= piece; /* decrease space left in this round */
224 if(0 == ch->datasize)
225 /* end of data this round, we now expect a trailing CRLF */
226 ch->state = CHUNK_POSTCR;
231 ch->state = CHUNK_POSTLF;
236 return CHUNKE_BAD_CHUNK;
242 * The last one before we go back to hex state and start all
245 Curl_httpchunk_init(conn);
250 return CHUNKE_BAD_CHUNK;
254 /* If we arrive here, there is data left in the end of the buffer
255 even if there's no more chunks to read */
256 ch->dataleft = length;
257 return CHUNKE_STOP; /* return stop */
259 return CHUNKE_STATE_ERROR;
264 #endif /* CURL_DISABLE_HTTP */