diff options
Diffstat (limited to 'Utilities/cmcurl/lib/http_chunks.c')
-rw-r--r-- | Utilities/cmcurl/lib/http_chunks.c | 343 |
1 files changed, 184 insertions, 159 deletions
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 1b03a55..61a6098 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -18,25 +18,21 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * $Id$ ***************************************************************************/ -#include "setup.h" + +#include "curl_setup.h" #ifndef CURL_DISABLE_HTTP -/* -- WIN32 approved -- */ -#include <stdio.h> -#include <string.h> -#include <stdarg.h> -#include <stdlib.h> -#include <ctype.h> #include "urldata.h" /* it includes http_chunks.h */ #include "sendf.h" /* for the client write stuff */ #include "content_encoding.h" #include "http.h" -#include "memory.h" -#include "easyif.h" /* for Curl_convert_to_network prototype */ +#include "curl_memory.h" +#include "non-ascii.h" /* for Curl_convert_to_network prototype */ +#include "strtoofft.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -81,12 +77,20 @@ */ +/* Check for an ASCII hex digit. + We avoid the use of isxdigit to accommodate non-ASCII hosts. */ +static bool Curl_isxdigit(char digit) +{ + return ( (digit >= 0x30 && digit <= 0x39) /* 0-9 */ + || (digit >= 0x41 && digit <= 0x46) /* A-F */ + || (digit >= 0x61 && digit <= 0x66) /* a-f */ ) ? TRUE : FALSE; +} void Curl_httpchunk_init(struct connectdata *conn) { - struct Curl_chunker *chunk = &conn->data->reqdata.proto.http->chunk; - chunk->hexindex=0; /* start at 0 */ - chunk->dataleft=0; /* no data left yet! */ + struct Curl_chunker *chunk = &conn->chunk; + chunk->hexindex=0; /* start at 0 */ + chunk->dataleft=0; /* no data left yet! */ chunk->state = CHUNK_HEX; /* we get hex first! */ } @@ -108,22 +112,26 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, { CURLcode result=CURLE_OK; struct SessionHandle *data = conn->data; - struct Curl_chunker *ch = &data->reqdata.proto.http->chunk; - struct Curl_transfer_keeper *k = &data->reqdata.keep; + struct Curl_chunker *ch = &conn->chunk; + struct SingleRequest *k = &data->req; size_t piece; - size_t length = (size_t)datalen; + curl_off_t length = (curl_off_t)datalen; size_t *wrote = (size_t *)wrotep; *wrote = 0; /* nothing's written yet */ + /* the original data is written to the client, but we go on with the + chunk read process, to properly calculate the content length*/ + if(data->set.http_te_skip && !k->ignorebody) { + result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen); + if(result) + return CHUNKE_WRITE_ERROR; + } + while(length) { switch(ch->state) { case CHUNK_HEX: - /* Check for an ASCII hex digit. - We avoid the use of isxdigit to accommodate non-ASCII hosts. */ - if((*datap >= 0x30 && *datap <= 0x39) /* 0-9 */ - || (*datap >= 0x41 && *datap <= 0x46) /* A-F */ - || (*datap >= 0x61 && *datap <= 0x66)) { /* a-f */ + if(Curl_isxdigit(*datap)) { if(ch->hexindex < MAXNUM_SIZE) { ch->hexbuffer[ch->hexindex] = *datap; datap++; @@ -135,107 +143,91 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, } } else { - if(0 == ch->hexindex) { + char *endptr; + if(0 == ch->hexindex) /* This is illegal data, we received junk where we expected a hexadecimal digit. */ return CHUNKE_ILLEGAL_HEX; - } + /* length and datap are unmodified */ ch->hexbuffer[ch->hexindex]=0; -#ifdef CURL_DOES_CONVERSIONS + /* convert to host encoding before calling strtoul */ - result = Curl_convert_from_network(conn->data, - ch->hexbuffer, + result = Curl_convert_from_network(conn->data, ch->hexbuffer, ch->hexindex); - if(result != CURLE_OK) { + if(result) { /* Curl_convert_from_network calls failf if unsuccessful */ /* Treat it as a bad hex character */ - return(CHUNKE_ILLEGAL_HEX); + return CHUNKE_ILLEGAL_HEX ; } -#endif /* CURL_DOES_CONVERSIONS */ - ch->datasize=strtoul(ch->hexbuffer, NULL, 16); - ch->state = CHUNK_POSTHEX; - } - break; - case CHUNK_POSTHEX: - /* In this state, we're waiting for CRLF to arrive. We support - this to allow so called chunk-extensions to show up here - before the CRLF comes. */ - if(*datap == 0x0d) - ch->state = CHUNK_CR; - length--; - datap++; + ch->datasize=curlx_strtoofft(ch->hexbuffer, &endptr, 16); + if((ch->datasize == CURL_OFF_T_MAX) && (errno == ERANGE)) + /* overflow is an error */ + return CHUNKE_ILLEGAL_HEX; + ch->state = CHUNK_LF; /* now wait for the CRLF */ + } break; - case CHUNK_CR: - /* waiting for the LF */ + case CHUNK_LF: + /* waiting for the LF after a chunk size */ if(*datap == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { - if (conn->bits.trailerHdrPresent!=TRUE) { - /* No Trailer: header found - revert to original Curl processing */ - ch->state = CHUNK_STOP; - if (1 == length) { - /* This is the final byte, return right now */ - return CHUNKE_STOP; - } - } - else { - ch->state = CHUNK_TRAILER; /* attempt to read trailers */ - conn->trlPos=0; - } + ch->state = CHUNK_TRAILER; /* now check for trailers */ + conn->trlPos=0; } else ch->state = CHUNK_DATA; } - else - /* previously we got a fake CR, go back to CR waiting! */ - ch->state = CHUNK_CR; + datap++; length--; break; case CHUNK_DATA: - /* we get pure and fine data - - We expect another 'datasize' of data. We have 'length' right now, - it can be more or less than 'datasize'. Get the smallest piece. + /* We expect 'datasize' of data. We have 'length' right now, it can be + more or less than 'datasize'. Get the smallest piece. */ - piece = (ch->datasize >= length)?length:ch->datasize; + piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize); /* Write the data portion available */ #ifdef HAVE_LIBZ - switch (data->reqdata.keep.content_encoding) { - case IDENTITY: + switch (conn->data->set.http_ce_skip? + IDENTITY : data->req.auto_decoding) { + case IDENTITY: #endif - if(!k->ignorebody) + if(!k->ignorebody) { + if(!data->set.http_te_skip) result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece); + else + result = CURLE_OK; + } #ifdef HAVE_LIBZ - break; - - case DEFLATE: - /* update data->reqdata.keep.str to point to the chunk data. */ - data->reqdata.keep.str = datap; - result = Curl_unencode_deflate_write(conn, &data->reqdata.keep, - (ssize_t)piece); - break; - - case GZIP: - /* update data->reqdata.keep.str to point to the chunk data. */ - data->reqdata.keep.str = datap; - result = Curl_unencode_gzip_write(conn, &data->reqdata.keep, - (ssize_t)piece); - break; - - case COMPRESS: - default: - failf (conn->data, - "Unrecognized content encoding type. " - "libcurl understands `identity', `deflate' and `gzip' " - "content encodings."); - return CHUNKE_BAD_ENCODING; + break; + + case DEFLATE: + /* update data->req.keep.str to point to the chunk data. */ + data->req.str = datap; + result = Curl_unencode_deflate_write(conn, &data->req, + (ssize_t)piece); + break; + + case GZIP: + /* update data->req.keep.str to point to the chunk data. */ + data->req.str = datap; + result = Curl_unencode_gzip_write(conn, &data->req, + (ssize_t)piece); + break; + + case COMPRESS: + default: + failf (conn->data, + "Unrecognized content encoding type. " + "libcurl understands `identity', `deflate' and `gzip' " + "content encodings."); + return CHUNKE_BAD_ENCODING; } #endif @@ -250,62 +242,84 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, if(0 == ch->datasize) /* end of data this round, we now expect a trailing CRLF */ - ch->state = CHUNK_POSTCR; - break; - - case CHUNK_POSTCR: - if(*datap == 0x0d) { ch->state = CHUNK_POSTLF; - datap++; - length--; - } - else - return CHUNKE_BAD_CHUNK; break; case CHUNK_POSTLF: if(*datap == 0x0a) { - /* - * The last one before we go back to hex state and start all - * over. - */ - Curl_httpchunk_init(conn); - datap++; - length--; + /* The last one before we go back to hex state and start all over. */ + Curl_httpchunk_init(conn); /* sets state back to CHUNK_HEX */ } - else + else if(*datap != 0x0d) return CHUNKE_BAD_CHUNK; + datap++; + length--; break; case CHUNK_TRAILER: - /* conn->trailer is assumed to be freed in url.c on a - connection basis */ - if (conn->trlPos >= conn->trlMax) { - char *ptr; - if(conn->trlMax) { - conn->trlMax *= 2; - ptr = (char*)realloc(conn->trailer,conn->trlMax); + if((*datap == 0x0d) || (*datap == 0x0a)) { + /* this is the end of a trailer, but if the trailer was zero bytes + there was no trailer and we move on */ + + if(conn->trlPos) { + /* we allocate trailer with 3 bytes extra room to fit this */ + conn->trailer[conn->trlPos++]=0x0d; + conn->trailer[conn->trlPos++]=0x0a; + conn->trailer[conn->trlPos]=0; + + /* Convert to host encoding before calling Curl_client_write */ + result = Curl_convert_from_network(conn->data, conn->trailer, + conn->trlPos); + if(result) + /* Curl_convert_from_network calls failf if unsuccessful */ + /* Treat it as a bad chunk */ + return CHUNKE_BAD_CHUNK; + + if(!data->set.http_te_skip) { + result = Curl_client_write(conn, CLIENTWRITE_HEADER, + conn->trailer, conn->trlPos); + if(result) + return CHUNKE_WRITE_ERROR; + } + conn->trlPos=0; + ch->state = CHUNK_TRAILER_CR; + if(*datap == 0x0a) + /* already on the LF */ + break; } else { - conn->trlMax=128; - ptr = (char*)malloc(conn->trlMax); + /* no trailer, we're on the final CRLF pair */ + ch->state = CHUNK_TRAILER_POSTCR; + break; /* don't advance the pointer */ } - if(!ptr) - return CHUNKE_OUT_OF_MEMORY; - conn->trailer = ptr; } - conn->trailer[conn->trlPos++]=*datap; - - if(*datap == 0x0d) - ch->state = CHUNK_TRAILER_CR; else { - datap++; - length--; - } + /* conn->trailer is assumed to be freed in url.c on a + connection basis */ + if(conn->trlPos >= conn->trlMax) { + /* we always allocate three extra bytes, just because when the full + header has been received we append CRLF\0 */ + char *ptr; + if(conn->trlMax) { + conn->trlMax *= 2; + ptr = realloc(conn->trailer, conn->trlMax + 3); + } + else { + conn->trlMax=128; + ptr = malloc(conn->trlMax + 3); + } + if(!ptr) + return CHUNKE_OUT_OF_MEMORY; + conn->trailer = ptr; + } + conn->trailer[conn->trlPos++]=*datap; + } + datap++; + length--; break; case CHUNK_TRAILER_CR: - if(*datap == 0x0d) { + if(*datap == 0x0a) { ch->state = CHUNK_TRAILER_POSTCR; datap++; length--; @@ -315,46 +329,57 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, break; case CHUNK_TRAILER_POSTCR: - if (*datap == 0x0a) { - conn->trailer[conn->trlPos++]=0x0a; - conn->trailer[conn->trlPos]=0; - if (conn->trlPos==2) { - ch->state = CHUNK_STOP; - return CHUNKE_STOP; - } - else { -#ifdef CURL_DOES_CONVERSIONS - /* Convert to host encoding before calling Curl_client_write */ - result = Curl_convert_from_network(conn->data, - conn->trailer, - conn->trlPos); - if(result != CURLE_OK) { - /* Curl_convert_from_network calls failf if unsuccessful */ - /* Treat it as a bad chunk */ - return(CHUNKE_BAD_CHUNK); - } -#endif /* CURL_DOES_CONVERSIONS */ - Curl_client_write(conn, CLIENTWRITE_HEADER, - conn->trailer, conn->trlPos); - } + /* We enter this state when a CR should arrive so we expect to + have to first pass a CR before we wait for LF */ + if((*datap != 0x0d) && (*datap != 0x0a)) { + /* not a CR then it must be another header in the trailer */ ch->state = CHUNK_TRAILER; - conn->trlPos=0; + break; + } + if(*datap == 0x0d) { + /* skip if CR */ datap++; length--; } - else - return CHUNKE_BAD_CHUNK; + /* now wait for the final LF */ + ch->state = CHUNK_STOP; break; case CHUNK_STOP: - /* If we arrive here, there is data left in the end of the buffer - even if there's no more chunks to read */ - ch->dataleft = length; - return CHUNKE_STOP; /* return stop */ - default: - return CHUNKE_STATE_ERROR; + if(*datap == 0x0a) { + length--; + + /* Record the length of any data left in the end of the buffer + even if there's no more chunks to read */ + ch->dataleft = curlx_sotouz(length); + + return CHUNKE_STOP; /* return stop */ + } + else + return CHUNKE_BAD_CHUNK; } } return CHUNKE_OK; } + +const char *Curl_chunked_strerror(CHUNKcode code) +{ + switch (code) { + default: + return "OK"; + case CHUNKE_TOO_LONG_HEX: + return "Too long hexadecimal number"; + case CHUNKE_ILLEGAL_HEX: + return "Illegal or missing hexadecimal sequence"; + case CHUNKE_BAD_CHUNK: + return "Malformed encoding found"; + case CHUNKE_WRITE_ERROR: + return "Write error"; + case CHUNKE_BAD_ENCODING: + return "Bad content-encoding found"; + case CHUNKE_OUT_OF_MEMORY: + return "Out of memory"; + } +} + #endif /* CURL_DISABLE_HTTP */ |