Beispiel #1
0
/*
 * chunk_read() returns a OK for normal operations, or a positive return code
 * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
 * argument is set to tell the caller how many bytes we actually passed to the
 * client (for byte-counting and whatever).
 *
 * The states and the state-machine is further explained in the header file.
 *
 * This function always uses ASCII hex values to accommodate non-ASCII hosts.
 * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
 */
CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
                              char *datap,
                              ssize_t datalen,
                              ssize_t *wrotep)
{
  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;
  size_t piece;
  size_t length = (size_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)
    Curl_client_write(conn, CLIENTWRITE_BODY, datap,datalen);

  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(ch->hexindex < MAXNUM_SIZE) {
          ch->hexbuffer[ch->hexindex] = *datap;
          datap++;
          length--;
          ch->hexindex++;
        }
        else {
          return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
        }
      }
      else {
        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,
                                           ch->hexindex);
        if(result != CURLE_OK) {
          /* Curl_convert_from_network calls failf if unsuccessful */
          /* Treat it as a bad hex character */
          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++;
      break;

    case CHUNK_CR:
      /* waiting for the LF */
      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_STOPCR;

            /* We need to increment the datap here since we bypass the
               increment below with the immediate break */
            length--;
            datap++;

            /* This is the final byte, continue to read the final CRLF */
            break;
          }
          else {
            ch->state = CHUNK_TRAILER; /* attempt to read 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.
      */
      piece = (ch->datasize >= length)?length:ch->datasize;

      /* Write the data portion available */
#ifdef HAVE_LIBZ
      switch (conn->data->set.http_ce_skip?
              IDENTITY : data->reqdata.keep.content_encoding) {
        case IDENTITY:
#endif
          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;
      }
#endif

      if(result)
        return CHUNKE_WRITE_ERROR;

      *wrote += piece;

      ch->datasize -= piece; /* decrease amount left to expect */
      datap += piece;    /* move read pointer forward */
      length -= piece;   /* decrease space left in this round */

      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--;
      }
      else {
        return CHUNKE_BAD_CHUNK;
      }

      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);
        }
        else {
          conn->trlMax=128;
          ptr = (char*)malloc(conn->trlMax);
        }
        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--;
     }
      break;

    case CHUNK_TRAILER_CR:
      if(*datap == 0x0d) {
        ch->state = CHUNK_TRAILER_POSTCR;
        datap++;
        length--;
      }
      else
        return CHUNKE_BAD_CHUNK;
      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;
          datap++;
          length--;

          /*
           * Note that this case skips over the final STOP states since we've
           * already read the final CRLF and need to return
           */

          ch->dataleft = length;

          return CHUNKE_STOP; /* return 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 */
          if ( !data->set.http_te_skip )
            Curl_client_write(conn, CLIENTWRITE_HEADER,
                              conn->trailer, conn->trlPos);
        }
        ch->state = CHUNK_TRAILER;
        conn->trlPos=0;
        datap++;
        length--;
      }
      else
        return CHUNKE_BAD_CHUNK;
      break;

    case CHUNK_STOPCR:
      /* Read the final CRLF that ends all chunk bodies */

      if(*datap == 0x0d) {
        ch->state = CHUNK_STOP;
        datap++;
        length--;
      }
      else {
        return CHUNKE_BAD_CHUNK;
      }
      break;

    case CHUNK_STOP:
      if (*datap == 0x0a) {
        datap++;
        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 = length;
        return CHUNKE_STOP; /* return stop */
      }
      else {
        return CHUNKE_BAD_CHUNK;
      }


    default:
      return CHUNKE_STATE_ERROR;
    }
  }
  return CHUNKE_OK;
}
Beispiel #2
0
/*
 * chunk_read() returns a OK for normal operations, or a positive return code
 * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
 * argument is set to tell the caller how many bytes we actually passed to the
 * client (for byte-counting and whatever).
 *
 * The states and the state-machine is further explained in the header file.
 *
 * This function always uses ASCII hex values to accommodate non-ASCII hosts.
 * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
 */
CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
                              char *datap,
                              ssize_t datalen,
                              ssize_t *wrotep)
{
  CURLcode result=CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct Curl_chunker *ch = &conn->chunk;
  struct SingleRequest *k = &data->req;
  size_t piece;
  size_t length = (size_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:
      if(Curl_isxdigit(*datap)) {
        if(ch->hexindex < MAXNUM_SIZE) {
          ch->hexbuffer[ch->hexindex] = *datap;
          datap++;
          length--;
          ch->hexindex++;
        }
        else {
          return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
        }
      }
      else {
        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,
                                           ch->hexindex);
        if(result != CURLE_OK) {
          /* Curl_convert_from_network calls failf if unsuccessful */
          /* Treat it as a bad hex character */
          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++;
      break;

    case CHUNK_CR:
      /* waiting for the LF */
      if(*datap == 0x0a) {
        /* we're now expecting data to come, unless size was zero! */
        if(0 == ch->datasize) {
          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.
      */
      piece = (ch->datasize >= length)?length:ch->datasize;

      /* Write the data portion available */
#ifdef HAVE_LIBZ
      switch (conn->data->set.http_ce_skip?
              IDENTITY : data->req.content_encoding) {
      case IDENTITY:
#endif
        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->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

      if(result)
        return CHUNKE_WRITE_ERROR;

      *wrote += piece;

      ch->datasize -= piece; /* decrease amount left to expect */
      datap += piece;    /* move read pointer forward */
      length -= piece;   /* decrease space left in this round */

      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--;
      }
      else
        return CHUNKE_BAD_CHUNK;

      break;

    case CHUNK_TRAILER:
      if(*datap == 0x0d) {
        /* 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;

#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 */
          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;
        }
        else {
          /* no trailer, we're on the final CRLF pair */
          ch->state = CHUNK_TRAILER_POSTCR;
          break; /* don't advance the pointer */
        }
      }
      else {
        /* 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;
        }
        fprintf(stderr, "MOO: %c\n", *datap);
        conn->trailer[conn->trlPos++]=*datap;
      }
      datap++;
      length--;
      break;

    case CHUNK_TRAILER_CR:
      if(*datap == 0x0a) {
        ch->state = CHUNK_TRAILER_POSTCR;
        datap++;
        length--;
      }
      else
        return CHUNKE_BAD_CHUNK;
      break;

    case CHUNK_TRAILER_POSTCR:
      /* 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) {
        /* not a CR then it must be another header in the trailer */
        ch->state = CHUNK_TRAILER;
        break;
      }
      datap++;
      length--;
      /* now wait for the final LF */
      ch->state = CHUNK_STOP;
      break;

    case CHUNK_STOPCR:
      /* Read the final CRLF that ends all chunk bodies */

      if(*datap == 0x0d) {
        ch->state = CHUNK_STOP;
        datap++;
        length--;
      }
      else
        return CHUNKE_BAD_CHUNK;
      break;

    case CHUNK_STOP:
      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 = length;
        return CHUNKE_STOP; /* return stop */
      }
      else
        return CHUNKE_BAD_CHUNK;

    default:
      return CHUNKE_STATE_ERROR;
    }
  }
  return CHUNKE_OK;
}
/*
 * chunk_read() returns a OK for normal operations, or a positive return code
 * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
 * argument is set to tell the caller how many bytes we actually passed to the
 * client (for byte-counting and whatever).
 *
 * The states and the state-machine is further explained in the header file.
 */
CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
                              char *datap,
                              ssize_t datalen,
                              ssize_t *wrotep)
{
    CURLcode result=CURLE_OK;
    struct Curl_chunker *ch = &conn->proto.http->chunk;
    struct Curl_transfer_keeper *k = &conn->keep;
    size_t piece;
    size_t length = (size_t)datalen;
    size_t *wrote = (size_t *)wrotep;

    *wrote = 0; /* nothing's written yet */

    while(length) {
        switch(ch->state) {
        case CHUNK_HEX:
            if(isxdigit((int)*datap)) {
                if(ch->hexindex < MAXNUM_SIZE) {
                    ch->hexbuffer[ch->hexindex] = *datap;
                    datap++;
                    length--;
                    ch->hexindex++;
                }
                else {
                    return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
                }
            }
            else {
                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;
                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 == '\r')
                ch->state = CHUNK_CR;
            length--;
            datap++;
            break;

        case CHUNK_CR:
            /* waiting for the LF */
            if(*datap == '\n') {
                /* we're now expecting data to come, unless size was zero! */
                if(0 == ch->datasize) {
                    ch->state = CHUNK_STOP; /* stop reading! */
                    if(1 == length) {
                        /* This was the final byte, return right now */
                        return CHUNKE_STOP;
                    }
                }
                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.
            */
            piece = (ch->datasize >= length)?length:ch->datasize;

            /* Write the data portion available */
#ifdef HAVE_LIBZ
            switch (conn->keep.content_encoding) {
            case IDENTITY:
#endif
                if(!k->ignorebody)
                    result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap,
                                               piece);
#ifdef HAVE_LIBZ
                break;

            case DEFLATE:
                /* update conn->keep.str to point to the chunk data. */
                conn->keep.str = datap;
                result = Curl_unencode_deflate_write(conn->data, &conn->keep,
                                                     (ssize_t)piece);
                break;

            case GZIP:
                /* update conn->keep.str to point to the chunk data. */
                conn->keep.str = datap;
                result = Curl_unencode_gzip_write(conn->data, &conn->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;
            }
#endif

            if(result)
                return CHUNKE_WRITE_ERROR;

            *wrote += piece;

            ch->datasize -= piece; /* decrease amount left to expect */
            datap += piece;    /* move read pointer forward */
            length -= piece;   /* decrease space left in this round */

            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 == '\r') {
                ch->state = CHUNK_POSTLF;
                datap++;
                length--;
            }
            else
                return CHUNKE_BAD_CHUNK;
            break;

        case CHUNK_POSTLF:
            if(*datap == '\n') {
                /*
                 * The last one before we go back to hex state and start all
                 * over.
                 */
                Curl_httpchunk_init(conn);
                datap++;
                length--;
            }
            else
                return CHUNKE_BAD_CHUNK;
            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;
        }
    }
    return CHUNKE_OK;
}