]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/http_chunks.c
hello world
[icculus/iodoom3.git] / neo / curl / lib / http_chunks.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, 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 http://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  * $Id: http_chunks.c,v 1.23 2004/03/04 15:25:06 bagder Exp $
22  ***************************************************************************/
23 #include "setup.h"
24
25 #ifndef CURL_DISABLE_HTTP
26 /* -- WIN32 approved -- */
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32
33 #include "urldata.h" /* it includes http_chunks.h */
34 #include "sendf.h"   /* for the client write stuff */
35
36 #include "content_encoding.h"   /* 08/29/02 jhrg */
37 #include "http.h"
38
39 #define _MPRINTF_REPLACE /* use our functions only */
40 #include <curl/mprintf.h>
41
42 /* The last #include file should be: */
43 #ifdef CURLDEBUG
44 #include "memdebug.h"
45 #endif
46
47 /* 
48  * Chunk format (simplified):
49  *
50  * <HEX SIZE>[ chunk extension ] CRLF
51  * <DATA> CRLF
52  *
53  * Highlights from RFC2616 section 3.6 say:
54
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.
61
62        Chunked-Body   = *chunk
63                         last-chunk
64                         trailer
65                         CRLF
66
67        chunk          = chunk-size [ chunk-extension ] CRLF
68                         chunk-data CRLF
69        chunk-size     = 1*HEX
70        last-chunk     = 1*("0") [ chunk-extension ] CRLF
71
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)
77
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.
81
82  */
83
84
85 void Curl_httpchunk_init(struct connectdata *conn)
86 {
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! */
91 }
92
93 /*
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).
98  *
99  * The states and the state-machine is further explained in the header file.
100  */
101 CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
102                               char *datap,
103                               ssize_t datalen,
104                               ssize_t *wrotep)
105 {
106   CURLcode result=CURLE_OK;
107   struct Curl_chunker *ch = &conn->proto.http->chunk;
108   struct Curl_transfer_keeper *k = &conn->keep;
109   size_t piece;
110   size_t length = (size_t)datalen;
111   size_t *wrote = (size_t *)wrotep;
112
113   *wrote = 0; /* nothing's written yet */
114
115   while(length) {
116     switch(ch->state) {
117     case CHUNK_HEX:
118       if(isxdigit((int)*datap)) {
119         if(ch->hexindex < MAXNUM_SIZE) {
120           ch->hexbuffer[ch->hexindex] = *datap;
121           datap++;
122           length--;
123           ch->hexindex++;
124         }
125         else {
126           return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
127         }
128       }
129       else {
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;
134         }
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;
139       }
140       break;
141
142     case 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. */
146       if(*datap == '\r')
147         ch->state = CHUNK_CR;
148       length--;
149       datap++;
150       break;
151
152     case CHUNK_CR:
153       /* waiting for the LF */
154       if(*datap == '\n') {
155         /* we're now expecting data to come, unless size was zero! */
156         if(0 == ch->datasize) {
157           ch->state = CHUNK_STOP; /* stop reading! */
158           if(1 == length) {
159             /* This was the final byte, return right now */
160             return CHUNKE_STOP;
161           }
162         }
163         else
164           ch->state = CHUNK_DATA;
165       }
166       else
167         /* previously we got a fake CR, go back to CR waiting! */
168         ch->state = CHUNK_CR;
169       datap++;
170       length--;
171       break;
172
173     case CHUNK_DATA:
174       /* we get pure and fine data
175
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.
178       */
179       piece = (ch->datasize >= length)?length:ch->datasize;
180
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 */
184 #ifdef HAVE_LIBZ
185       switch (conn->keep.content_encoding) {
186         case IDENTITY:
187 #endif
188           if(!k->ignorebody)
189             result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap,
190                                        piece);
191 #ifdef HAVE_LIBZ
192           break;
193
194         case DEFLATE: 
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);
198           break;
199
200         case GZIP:
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);
204           break;
205
206         case COMPRESS:
207         default:
208           failf (conn->data,
209                  "Unrecognized content encoding type. "
210                  "libcurl understands `identity', `deflate' and `gzip' "
211                  "content encodings.");
212           return CHUNKE_BAD_ENCODING;
213       }
214 #endif
215
216       if(result)
217         return CHUNKE_WRITE_ERROR;
218
219       *wrote += piece;
220
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 */
224
225       if(0 == ch->datasize)
226         /* end of data this round, we now expect a trailing CRLF */
227         ch->state = CHUNK_POSTCR;
228       break;
229
230     case CHUNK_POSTCR:
231       if(*datap == '\r') {
232         ch->state = CHUNK_POSTLF;
233         datap++;
234         length--;
235       }
236       else
237         return CHUNKE_BAD_CHUNK;
238       break;
239
240     case CHUNK_POSTLF:
241       if(*datap == '\n') {
242         /*
243          * The last one before we go back to hex state and start all
244          * over.
245          */
246         Curl_httpchunk_init(conn);
247         datap++;
248         length--;
249       }
250       else
251         return CHUNKE_BAD_CHUNK;
252       break;
253
254     case CHUNK_STOP:
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 */
259     default:
260       return CHUNKE_STATE_ERROR;
261     }
262   }
263   return CHUNKE_OK;
264 }
265 #endif /* CURL_DISABLE_HTTP */