curl_global_init_mem() allows the memory functions to be replaced.
[platform/upstream/curl.git] / 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$
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 #include "memory.h"
39
40 #define _MPRINTF_REPLACE /* use our functions only */
41 #include <curl/mprintf.h>
42
43 /* The last #include file should be: */
44 #include "memdebug.h"
45
46 /* 
47  * Chunk format (simplified):
48  *
49  * <HEX SIZE>[ chunk extension ] CRLF
50  * <DATA> CRLF
51  *
52  * Highlights from RFC2616 section 3.6 say:
53
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.
60
61        Chunked-Body   = *chunk
62                         last-chunk
63                         trailer
64                         CRLF
65
66        chunk          = chunk-size [ chunk-extension ] CRLF
67                         chunk-data CRLF
68        chunk-size     = 1*HEX
69        last-chunk     = 1*("0") [ chunk-extension ] CRLF
70
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)
76
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.
80
81  */
82
83
84 void Curl_httpchunk_init(struct connectdata *conn)
85 {
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! */
90 }
91
92 /*
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).
97  *
98  * The states and the state-machine is further explained in the header file.
99  */
100 CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
101                               char *datap,
102                               ssize_t datalen,
103                               ssize_t *wrotep)
104 {
105   CURLcode result=CURLE_OK;
106   struct Curl_chunker *ch = &conn->proto.http->chunk;
107   struct Curl_transfer_keeper *k = &conn->keep;
108   size_t piece;
109   size_t length = (size_t)datalen;
110   size_t *wrote = (size_t *)wrotep;
111
112   *wrote = 0; /* nothing's written yet */
113
114   while(length) {
115     switch(ch->state) {
116     case CHUNK_HEX:
117       if(isxdigit((int)*datap)) {
118         if(ch->hexindex < MAXNUM_SIZE) {
119           ch->hexbuffer[ch->hexindex] = *datap;
120           datap++;
121           length--;
122           ch->hexindex++;
123         }
124         else {
125           return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
126         }
127       }
128       else {
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;
133         }
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;
138       }
139       break;
140
141     case 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. */
145       if(*datap == '\r')
146         ch->state = CHUNK_CR;
147       length--;
148       datap++;
149       break;
150
151     case CHUNK_CR:
152       /* waiting for the LF */
153       if(*datap == '\n') {
154         /* we're now expecting data to come, unless size was zero! */
155         if(0 == ch->datasize) {
156           ch->state = CHUNK_STOP; /* stop reading! */
157           if(1 == length) {
158             /* This was the final byte, return right now */
159             return CHUNKE_STOP;
160           }
161         }
162         else
163           ch->state = CHUNK_DATA;
164       }
165       else
166         /* previously we got a fake CR, go back to CR waiting! */
167         ch->state = CHUNK_CR;
168       datap++;
169       length--;
170       break;
171
172     case CHUNK_DATA:
173       /* we get pure and fine data
174
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.
177       */
178       piece = (ch->datasize >= length)?length:ch->datasize;
179
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 */
183 #ifdef HAVE_LIBZ
184       switch (conn->keep.content_encoding) {
185         case IDENTITY:
186 #endif
187           if(!k->ignorebody)
188             result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap,
189                                        piece);
190 #ifdef HAVE_LIBZ
191           break;
192
193         case DEFLATE: 
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);
197           break;
198
199         case GZIP:
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);
203           break;
204
205         case COMPRESS:
206         default:
207           failf (conn->data,
208                  "Unrecognized content encoding type. "
209                  "libcurl understands `identity', `deflate' and `gzip' "
210                  "content encodings.");
211           return CHUNKE_BAD_ENCODING;
212       }
213 #endif
214
215       if(result)
216         return CHUNKE_WRITE_ERROR;
217
218       *wrote += piece;
219
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 */
223
224       if(0 == ch->datasize)
225         /* end of data this round, we now expect a trailing CRLF */
226         ch->state = CHUNK_POSTCR;
227       break;
228
229     case CHUNK_POSTCR:
230       if(*datap == '\r') {
231         ch->state = CHUNK_POSTLF;
232         datap++;
233         length--;
234       }
235       else
236         return CHUNKE_BAD_CHUNK;
237       break;
238
239     case CHUNK_POSTLF:
240       if(*datap == '\n') {
241         /*
242          * The last one before we go back to hex state and start all
243          * over.
244          */
245         Curl_httpchunk_init(conn);
246         datap++;
247         length--;
248       }
249       else
250         return CHUNKE_BAD_CHUNK;
251       break;
252
253     case CHUNK_STOP:
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 */
258     default:
259       return CHUNKE_STATE_ERROR;
260     }
261   }
262   return CHUNKE_OK;
263 }
264 #endif /* CURL_DISABLE_HTTP */