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.
21 * $Id: http_chunks.c,v 1.23 2004/03/04 15:25:06 bagder Exp $
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 */
39 #define _MPRINTF_REPLACE /* use our functions only */
40 #include <curl/mprintf.h>
42 /* The last #include file should be: */
48 * Chunk format (simplified):
50 * <HEX SIZE>[ chunk extension ] CRLF
53 * Highlights from RFC2616 section 3.6 say:
55 The chunked encoding modifies the body of a message in order to
56 transfer it as a series of chunks, each with its own size indicator,
57 followed by an OPTIONAL trailer containing entity-header fields. This
58 allows dynamically produced content to be transferred along with the
59 information necessary for the recipient to verify that it has
60 received the full message.
67 chunk = chunk-size [ chunk-extension ] CRLF
70 last-chunk = 1*("0") [ chunk-extension ] CRLF
72 chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
73 chunk-ext-name = token
74 chunk-ext-val = token | quoted-string
75 chunk-data = chunk-size(OCTET)
76 trailer = *(entity-header CRLF)
78 The chunk-size field is a string of hex digits indicating the size of
79 the chunk. The chunked encoding is ended by any chunk whose size is
80 zero, followed by the trailer, which is terminated by an empty line.
85 void Curl_httpchunk_init(struct connectdata *conn)
87 struct Curl_chunker *chunk = &conn->proto.http->chunk;
88 chunk->hexindex=0; /* start at 0 */
89 chunk->dataleft=0; /* no data left yet! */
90 chunk->state = CHUNK_HEX; /* we get hex first! */
94 * chunk_read() returns a OK for normal operations, or a positive return code
95 * for errors. STOP means this sequence of chunks is complete. The 'wrote'
96 * argument is set to tell the caller how many bytes we actually passed to the
97 * client (for byte-counting and whatever).
99 * The states and the state-machine is further explained in the header file.
101 CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
106 CURLcode result=CURLE_OK;
107 struct Curl_chunker *ch = &conn->proto.http->chunk;
108 struct Curl_transfer_keeper *k = &conn->keep;
110 size_t length = (size_t)datalen;
111 size_t *wrote = (size_t *)wrotep;
113 *wrote = 0; /* nothing's written yet */
118 if(isxdigit((int)*datap)) {
119 if(ch->hexindex < MAXNUM_SIZE) {
120 ch->hexbuffer[ch->hexindex] = *datap;
126 return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
130 if(0 == ch->hexindex) {
131 /* This is illegal data, we received junk where we expected
132 a hexadecimal digit. */
133 return CHUNKE_ILLEGAL_HEX;
135 /* length and datap are unmodified */
136 ch->hexbuffer[ch->hexindex]=0;
137 ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
138 ch->state = CHUNK_POSTHEX;
143 /* In this state, we're waiting for CRLF to arrive. We support
144 this to allow so called chunk-extensions to show up here
145 before the CRLF comes. */
147 ch->state = CHUNK_CR;
153 /* waiting for the LF */
155 /* we're now expecting data to come, unless size was zero! */
156 if(0 == ch->datasize) {
157 ch->state = CHUNK_STOP; /* stop reading! */
159 /* This was the final byte, return right now */
164 ch->state = CHUNK_DATA;
167 /* previously we got a fake CR, go back to CR waiting! */
168 ch->state = CHUNK_CR;
174 /* we get pure and fine data
176 We expect another 'datasize' of data. We have 'length' right now,
177 it can be more or less than 'datasize'. Get the smallest piece.
179 piece = (ch->datasize >= length)?length:ch->datasize;
181 /* Write the data portion available */
182 /* Added content-encoding here; untested but almost identical to the
183 tested code in transfer.c. 08/29/02 jhrg */
185 switch (conn->keep.content_encoding) {
189 result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap,
195 /* update conn->keep.str to point to the chunk data. */
196 conn->keep.str = datap;
197 result = Curl_unencode_deflate_write(conn->data, &conn->keep, piece);
201 /* update conn->keep.str to point to the chunk data. */
202 conn->keep.str = datap;
203 result = Curl_unencode_gzip_write(conn->data, &conn->keep, piece);
209 "Unrecognized content encoding type. "
210 "libcurl understands `identity', `deflate' and `gzip' "
211 "content encodings.");
212 return CHUNKE_BAD_ENCODING;
217 return CHUNKE_WRITE_ERROR;
221 ch->datasize -= piece; /* decrease amount left to expect */
222 datap += piece; /* move read pointer forward */
223 length -= piece; /* decrease space left in this round */
225 if(0 == ch->datasize)
226 /* end of data this round, we now expect a trailing CRLF */
227 ch->state = CHUNK_POSTCR;
232 ch->state = CHUNK_POSTLF;
237 return CHUNKE_BAD_CHUNK;
243 * The last one before we go back to hex state and start all
246 Curl_httpchunk_init(conn);
251 return CHUNKE_BAD_CHUNK;
255 /* If we arrive here, there is data left in the end of the buffer
256 even if there's no more chunks to read */
257 ch->dataleft = length;
258 return CHUNKE_STOP; /* return stop */
260 return CHUNKE_STATE_ERROR;
265 #endif /* CURL_DISABLE_HTTP */