transfer: readwrite improvements

- changed header/chunk/handler->readwrite prototypes to accept `buf`,
  `blen` and a `pconsumed` pointer. They now get the buffer to work on
  and report back how many bytes they consumed
- eliminated `k->str` in SingleRequest
- improved excess data handling to properly calculate with any body data
  left in the headerb buffer
- eliminated `k->badheader` enum to only be a bool

Closes #12283
This commit is contained in:
Stefan Eissing 2023-11-06 17:06:06 +01:00 committed by Daniel Stenberg
parent d9e7643ced
commit 1cd2f0072f
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 269 additions and 246 deletions

View file

@ -98,9 +98,9 @@ void Curl_httpchunk_init(struct Curl_easy *data)
* For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
*/
CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
char *datap,
ssize_t datalen,
ssize_t *wrote,
char *buf,
size_t blen,
size_t *pconsumed,
CURLcode *extrap)
{
CURLcode result = CURLE_OK;
@ -108,28 +108,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
struct Curl_chunker *ch = &conn->chunk;
struct SingleRequest *k = &data->req;
size_t piece;
curl_off_t length = (curl_off_t)datalen;
*wrote = 0; /* nothing's written yet */
*pconsumed = 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(data, CLIENTWRITE_BODY, datap, datalen);
result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
if(result) {
*extrap = result;
return CHUNKE_PASSTHRU_ERROR;
}
}
while(length) {
while(blen) {
switch(ch->state) {
case CHUNK_HEX:
if(ISXDIGIT(*datap)) {
if(ISXDIGIT(*buf)) {
if(ch->hexindex < CHUNK_MAXNUM_LEN) {
ch->hexbuffer[ch->hexindex] = *datap;
datap++;
length--;
ch->hexbuffer[ch->hexindex] = *buf;
buf++;
blen--;
ch->hexindex++;
}
else {
@ -143,7 +142,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
a hexadecimal digit. */
return CHUNKE_ILLEGAL_HEX;
/* length and datap are unmodified */
/* blen and buf are unmodified */
ch->hexbuffer[ch->hexindex] = 0;
if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
@ -154,7 +153,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
case CHUNK_LF:
/* waiting for the LF after a chunk size */
if(*datap == 0x0a) {
if(*buf == 0x0a) {
/* we're now expecting data to come, unless size was zero! */
if(0 == ch->datasize) {
ch->state = CHUNK_TRAILER; /* now check for trailers */
@ -163,19 +162,21 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
ch->state = CHUNK_DATA;
}
datap++;
length--;
buf++;
blen--;
break;
case CHUNK_DATA:
/* We expect 'datasize' of data. We have 'length' right now, it can be
/* We expect 'datasize' of data. We have 'blen' right now, it can be
more or less than 'datasize'. Get the smallest piece.
*/
piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
piece = blen;
if(ch->datasize < (curl_off_t)blen)
piece = curlx_sotouz(ch->datasize);
/* Write the data portion available */
if(!data->set.http_te_skip && !k->ignorebody) {
result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece);
if(result) {
*extrap = result;
@ -183,10 +184,10 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
}
}
*wrote += piece;
*pconsumed += piece;
ch->datasize -= piece; /* decrease amount left to expect */
datap += piece; /* move read pointer forward */
length -= piece; /* decrease space left in this round */
buf += piece; /* move read pointer forward */
blen -= piece; /* decrease space left in this round */
if(0 == ch->datasize)
/* end of data this round, we now expect a trailing CRLF */
@ -194,18 +195,18 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
break;
case CHUNK_POSTLF:
if(*datap == 0x0a) {
if(*buf == 0x0a) {
/* The last one before we go back to hex state and start all over. */
Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */
}
else if(*datap != 0x0d)
else if(*buf != 0x0d)
return CHUNKE_BAD_CHUNK;
datap++;
length--;
buf++;
blen--;
break;
case CHUNK_TRAILER:
if((*datap == 0x0d) || (*datap == 0x0a)) {
if((*buf == 0x0d) || (*buf == 0x0a)) {
char *tr = Curl_dyn_ptr(&conn->trailer);
/* this is the end of a trailer, but if the trailer was zero bytes
there was no trailer and we move on */
@ -229,7 +230,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
}
Curl_dyn_reset(&conn->trailer);
ch->state = CHUNK_TRAILER_CR;
if(*datap == 0x0a)
if(*buf == 0x0a)
/* already on the LF */
break;
}
@ -240,19 +241,19 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
}
}
else {
result = Curl_dyn_addn(&conn->trailer, datap, 1);
result = Curl_dyn_addn(&conn->trailer, buf, 1);
if(result)
return CHUNKE_OUT_OF_MEMORY;
}
datap++;
length--;
buf++;
blen--;
break;
case CHUNK_TRAILER_CR:
if(*datap == 0x0a) {
if(*buf == 0x0a) {
ch->state = CHUNK_TRAILER_POSTCR;
datap++;
length--;
buf++;
blen--;
}
else
return CHUNKE_BAD_CHUNK;
@ -261,27 +262,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
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) && (*datap != 0x0a)) {
if((*buf != 0x0d) && (*buf != 0x0a)) {
/* not a CR then it must be another header in the trailer */
ch->state = CHUNK_TRAILER;
break;
}
if(*datap == 0x0d) {
if(*buf == 0x0d) {
/* skip if CR */
datap++;
length--;
buf++;
blen--;
}
/* now wait for the final LF */
ch->state = CHUNK_STOP;
break;
case CHUNK_STOP:
if(*datap == 0x0a) {
length--;
if(*buf == 0x0a) {
blen--;
/* Record the length of any data left in the end of the buffer
even if there's no more chunks to read */
ch->datasize = curlx_sotouz(length);
ch->datasize = blen;
return CHUNKE_STOP; /* return stop */
}