http_chunks.c
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Project ___| | | | _ \| |
4  * / __| | | | |_) | |
5  * | (__| |_| | _ <| |___
6  * \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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 /* Check for an ASCII hex digit.
78  We avoid the use of isxdigit to accommodate non-ASCII hosts. */
79 static bool Curl_isxdigit(char digit)
80 {
81  return ( (digit >= 0x30 && digit <= 0x39) /* 0-9 */
82  || (digit >= 0x41 && digit <= 0x46) /* A-F */
83  || (digit >= 0x61 && digit <= 0x66) /* a-f */) ? TRUE : FALSE;
84 }
85 
86 void Curl_httpchunk_init(struct connectdata *conn)
87 {
88  struct Curl_chunker *chunk = &conn->chunk;
89  chunk->hexindex = 0; /* start at 0 */
90  chunk->dataleft = 0; /* no data left yet! */
91  chunk->state = CHUNK_HEX; /* we get hex first! */
92 }
93 
94 /*
95  * chunk_read() returns a OK for normal operations, or a positive return code
96  * for errors. STOP means this sequence of chunks is complete. The 'wrote'
97  * argument is set to tell the caller how many bytes we actually passed to the
98  * client (for byte-counting and whatever).
99  *
100  * The states and the state-machine is further explained in the header file.
101  *
102  * This function always uses ASCII hex values to accommodate non-ASCII hosts.
103  * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
104  */
106  char *datap,
107  ssize_t datalen,
108  ssize_t *wrotep)
109 {
111  struct Curl_easy *data = conn->data;
112  struct Curl_chunker *ch = &conn->chunk;
113  struct SingleRequest *k = &data->req;
114  size_t piece;
115  curl_off_t length = (curl_off_t)datalen;
116  size_t *wrote = (size_t *)wrotep;
117 
118  *wrote = 0; /* nothing's written yet */
119 
120  /* the original data is written to the client, but we go on with the
121  chunk read process, to properly calculate the content length*/
122  if(data->set.http_te_skip && !k->ignorebody) {
123  result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
124  if(result)
125  return CHUNKE_WRITE_ERROR;
126  }
127 
128  while(length) {
129  switch(ch->state) {
130  case CHUNK_HEX:
131  if(Curl_isxdigit(*datap)) {
132  if(ch->hexindex < MAXNUM_SIZE) {
133  ch->hexbuffer[ch->hexindex] = *datap;
134  datap++;
135  length--;
136  ch->hexindex++;
137  }
138  else {
139  return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
140  }
141  }
142  else {
143  char *endptr;
144  if(0 == ch->hexindex)
145  /* This is illegal data, we received junk where we expected
146  a hexadecimal digit. */
147  return CHUNKE_ILLEGAL_HEX;
148 
149  /* length and datap are unmodified */
150  ch->hexbuffer[ch->hexindex] = 0;
151 
152  /* convert to host encoding before calling strtoul */
153  result = Curl_convert_from_network(conn->data, ch->hexbuffer,
154  ch->hexindex);
155  if(result) {
156  /* Curl_convert_from_network calls failf if unsuccessful */
157  /* Treat it as a bad hex character */
158  return CHUNKE_ILLEGAL_HEX;
159  }
160 
161  if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
162  return CHUNKE_ILLEGAL_HEX;
163  ch->state = CHUNK_LF; /* now wait for the CRLF */
164  }
165  break;
166 
167  case CHUNK_LF:
168  /* waiting for the LF after a chunk size */
169  if(*datap == 0x0a) {
170  /* we're now expecting data to come, unless size was zero! */
171  if(0 == ch->datasize) {
172  ch->state = CHUNK_TRAILER; /* now check for trailers */
173  conn->trlPos = 0;
174  }
175  else
176  ch->state = CHUNK_DATA;
177  }
178 
179  datap++;
180  length--;
181  break;
182 
183  case CHUNK_DATA:
184  /* We expect 'datasize' of data. We have 'length' right now, it can be
185  more or less than 'datasize'. Get the smallest piece.
186  */
187  piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
188 
189  /* Write the data portion available */
190 #ifdef HAVE_LIBZ
191  switch(conn->data->set.http_ce_skip?
192  IDENTITY : data->req.auto_decoding) {
193  case IDENTITY:
194 #endif
195  if(!k->ignorebody) {
196  if(!data->set.http_te_skip)
197  result = Curl_client_write(conn, CLIENTWRITE_BODY, datap,
198  piece);
199  else
200  result = CURLE_OK;
201  }
202 #ifdef HAVE_LIBZ
203  break;
204 
205  case DEFLATE:
206  /* update data->req.keep.str to point to the chunk data. */
207  data->req.str = datap;
208  result = Curl_unencode_deflate_write(conn, &data->req,
209  (ssize_t)piece);
210  break;
211 
212  case GZIP:
213  /* update data->req.keep.str to point to the chunk data. */
214  data->req.str = datap;
215  result = Curl_unencode_gzip_write(conn, &data->req,
216  (ssize_t)piece);
217  break;
218 
219  default:
220  failf(conn->data,
221  "Unrecognized content encoding type. "
222  "libcurl understands `identity', `deflate' and `gzip' "
223  "content encodings.");
224  return CHUNKE_BAD_ENCODING;
225  }
226 #endif
227 
228  if(result)
229  return CHUNKE_WRITE_ERROR;
230 
231  *wrote += piece;
232 
233  ch->datasize -= piece; /* decrease amount left to expect */
234  datap += piece; /* move read pointer forward */
235  length -= piece; /* decrease space left in this round */
236 
237  if(0 == ch->datasize)
238  /* end of data this round, we now expect a trailing CRLF */
239  ch->state = CHUNK_POSTLF;
240  break;
241 
242  case CHUNK_POSTLF:
243  if(*datap == 0x0a) {
244  /* The last one before we go back to hex state and start all over. */
245  Curl_httpchunk_init(conn); /* sets state back to CHUNK_HEX */
246  }
247  else if(*datap != 0x0d)
248  return CHUNKE_BAD_CHUNK;
249  datap++;
250  length--;
251  break;
252 
253  case CHUNK_TRAILER:
254  if((*datap == 0x0d) || (*datap == 0x0a)) {
255  /* this is the end of a trailer, but if the trailer was zero bytes
256  there was no trailer and we move on */
257 
258  if(conn->trlPos) {
259  /* we allocate trailer with 3 bytes extra room to fit this */
260  conn->trailer[conn->trlPos++] = 0x0d;
261  conn->trailer[conn->trlPos++] = 0x0a;
262  conn->trailer[conn->trlPos] = 0;
263 
264  /* Convert to host encoding before calling Curl_client_write */
265  result = Curl_convert_from_network(conn->data, conn->trailer,
266  conn->trlPos);
267  if(result)
268  /* Curl_convert_from_network calls failf if unsuccessful */
269  /* Treat it as a bad chunk */
270  return CHUNKE_BAD_CHUNK;
271 
272  if(!data->set.http_te_skip) {
273  result = Curl_client_write(conn, CLIENTWRITE_HEADER,
274  conn->trailer, conn->trlPos);
275  if(result)
276  return CHUNKE_WRITE_ERROR;
277  }
278  conn->trlPos = 0;
279  ch->state = CHUNK_TRAILER_CR;
280  if(*datap == 0x0a)
281  /* already on the LF */
282  break;
283  }
284  else {
285  /* no trailer, we're on the final CRLF pair */
287  break; /* don't advance the pointer */
288  }
289  }
290  else {
291  /* conn->trailer is assumed to be freed in url.c on a
292  connection basis */
293  if(conn->trlPos >= conn->trlMax) {
294  /* we always allocate three extra bytes, just because when the full
295  header has been received we append CRLF\0 */
296  char *ptr;
297  if(conn->trlMax) {
298  conn->trlMax *= 2;
299  ptr = realloc(conn->trailer, conn->trlMax + 3);
300  }
301  else {
302  conn->trlMax = 128;
303  ptr = malloc(conn->trlMax + 3);
304  }
305  if(!ptr)
306  return CHUNKE_OUT_OF_MEMORY;
307  conn->trailer = ptr;
308  }
309  conn->trailer[conn->trlPos++]=*datap;
310  }
311  datap++;
312  length--;
313  break;
314 
315  case CHUNK_TRAILER_CR:
316  if(*datap == 0x0a) {
318  datap++;
319  length--;
320  }
321  else
322  return CHUNKE_BAD_CHUNK;
323  break;
324 
326  /* We enter this state when a CR should arrive so we expect to
327  have to first pass a CR before we wait for LF */
328  if((*datap != 0x0d) && (*datap != 0x0a)) {
329  /* not a CR then it must be another header in the trailer */
330  ch->state = CHUNK_TRAILER;
331  break;
332  }
333  if(*datap == 0x0d) {
334  /* skip if CR */
335  datap++;
336  length--;
337  }
338  /* now wait for the final LF */
339  ch->state = CHUNK_STOP;
340  break;
341 
342  case CHUNK_STOP:
343  if(*datap == 0x0a) {
344  length--;
345 
346  /* Record the length of any data left in the end of the buffer
347  even if there's no more chunks to read */
348  ch->dataleft = curlx_sotouz(length);
349 
350  return CHUNKE_STOP; /* return stop */
351  }
352  else
353  return CHUNKE_BAD_CHUNK;
354  }
355  }
356  return CHUNKE_OK;
357 }
358 
360 {
361  switch(code) {
362  default:
363  return "OK";
364  case CHUNKE_TOO_LONG_HEX:
365  return "Too long hexadecimal number";
366  case CHUNKE_ILLEGAL_HEX:
367  return "Illegal or missing hexadecimal sequence";
368  case CHUNKE_BAD_CHUNK:
369  return "Malformed encoding found";
370  case CHUNKE_WRITE_ERROR:
371  return "Write error";
372  case CHUNKE_BAD_ENCODING:
373  return "Bad content-encoding found";
375  return "Out of memory";
376  }
377 }
378 
379 #endif /* CURL_DISABLE_HTTP */
#define DEFLATE
Definition: urldata.h:566
#define CLIENTWRITE_BODY
Definition: sendf.h:50
ChunkyState state
Definition: http_chunks.h:85
int trlMax
Definition: urldata.h:994
struct UserDefined set
Definition: urldata.h:1762
void Curl_httpchunk_init(struct connectdata *conn)
Definition: http_chunks.c:86
#define GZIP
Definition: urldata.h:567
#define failf
Definition: sendf.h:48
char hexbuffer[MAXNUM_SIZE+1]
Definition: http_chunks.h:83
UNITTEST_START char * ptr
Definition: unit1330.c:38
CURLcode
Definition: curl.h:454
static bool Curl_isxdigit(char digit)
Definition: http_chunks.c:79
struct Curl_chunker chunk
Definition: urldata.h:798
#define realloc(ptr, size)
Definition: curl_memory.h:128
bool http_te_skip
Definition: urldata.h:1655
#define malloc(size)
Definition: curl_memory.h:124
UNITTEST_START int result
Definition: unit1304.c:49
CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap, ssize_t datalen, ssize_t *wrotep)
Definition: http_chunks.c:105
#define FALSE
struct SingleRequest req
Definition: urldata.h:1761
bool ignorebody
Definition: urldata.h:586
CURLofft curlx_strtoofft(const char *str, char **endp, int base, curl_off_t *num)
Definition: strtoofft.c:215
size_t dataleft
Definition: http_chunks.h:87
CHUNKcode
Definition: http_chunks.h:68
#define Curl_convert_from_network(a, b, c)
Definition: non-ascii.h:57
CURL_TYPEOF_CURL_OFF_T curl_off_t
Definition: system.h:420
#define CLIENTWRITE_HEADER
Definition: sendf.h:51
CURLcode Curl_unencode_gzip_write(struct connectdata *conn, struct SingleRequest *k, ssize_t nread)
Definition: curl.h:455
int trlPos
Definition: urldata.h:995
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len)
Definition: sendf.c:624
#define ssize_t
Definition: config-win32.h:382
int auto_decoding
Definition: urldata.h:563
char * trailer
Definition: urldata.h:993
TFSIMD_FORCE_INLINE tfScalar length(const Quaternion &q)
const char * Curl_chunked_strerror(CHUNKcode code)
Definition: http_chunks.c:359
char * str
Definition: urldata.h:550
#define TRUE
#define IDENTITY
Definition: urldata.h:565
#define MAXNUM_SIZE
Definition: http_chunks.h:29
bool http_ce_skip
Definition: urldata.h:1657
size_t curlx_sotouz(curl_off_t sonum)
Definition: warnless.c:347
CURLcode Curl_unencode_deflate_write(struct connectdata *conn, struct SingleRequest *req, ssize_t nread)
curl_off_t datasize
Definition: http_chunks.h:86
Definition: debug.c:29
struct Curl_easy * data
Definition: urldata.h:791


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:15