Annotation of embedaddon/curl/lib/http_chunks.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>