added code to handle a chunked request
This commit is contained in:
parent
d4f7ac1ca9
commit
c03b2467af
@ -43,6 +43,13 @@ struct qse_http_t
|
|||||||
int crlf; /* crlf status */
|
int crlf; /* crlf status */
|
||||||
qse_size_t plen; /* raw request length excluding crlf */
|
qse_size_t plen; /* raw request length excluding crlf */
|
||||||
qse_size_t need; /* number of octets needed for contents */
|
qse_size_t need; /* number of octets needed for contents */
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
qse_size_t len;
|
||||||
|
qse_size_t count;
|
||||||
|
int phase;
|
||||||
|
} chunk;
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|
@ -642,10 +642,25 @@ static QSE_INLINE int capture_content_length (
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (http->req.attr.chunked && len > 0)
|
||||||
|
{
|
||||||
|
/* content-length is greater than 0
|
||||||
|
* while transfer-encoding: chunked is specified. */
|
||||||
|
http->errnum = QSE_HTTP_EBADREQ;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
http->req.attr.content_length = len;
|
http->req.attr.content_length = len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QSE_INLINE int capture_content_type (
|
||||||
|
qse_http_t* http, qse_htb_pair_t* pair)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T("content type capture => %.*S\n"), (int)pair->vlen, pair->vptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static QSE_INLINE int capture_host (
|
static QSE_INLINE int capture_host (
|
||||||
qse_http_t* http, qse_htb_pair_t* pair)
|
qse_http_t* http, qse_htb_pair_t* pair)
|
||||||
{
|
{
|
||||||
@ -661,11 +676,19 @@ static QSE_INLINE int capture_transfer_encoding (
|
|||||||
n = compare_octets (pair->vptr, pair->vlen, "chunked", 7);
|
n = compare_octets (pair->vptr, pair->vlen, "chunked", 7);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
{
|
{
|
||||||
|
if (http->req.attr.content_length > 0)
|
||||||
|
{
|
||||||
|
/* content-length is greater than 0
|
||||||
|
* while transfer-encoding: chunked is specified. */
|
||||||
|
goto badreq;
|
||||||
|
}
|
||||||
|
|
||||||
http->req.attr.chunked = 1;
|
http->req.attr.chunked = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* other encoding type not supported yet */
|
/* other encoding type not supported yet */
|
||||||
|
badreq:
|
||||||
http->errnum = QSE_HTTP_EBADREQ;
|
http->errnum = QSE_HTTP_EBADREQ;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -682,6 +705,7 @@ static QSE_INLINE int capture_key_header (
|
|||||||
{
|
{
|
||||||
{ "Connection", 10, capture_connection },
|
{ "Connection", 10, capture_connection },
|
||||||
{ "Content-Length", 14, capture_content_length },
|
{ "Content-Length", 14, capture_content_length },
|
||||||
|
{ "Content-Type", 12, capture_content_type },
|
||||||
{ "Host", 4, capture_host },
|
{ "Host", 4, capture_host },
|
||||||
{ "Transfer-Encoding", 17, capture_transfer_encoding }
|
{ "Transfer-Encoding", 17, capture_transfer_encoding }
|
||||||
};
|
};
|
||||||
@ -959,16 +983,79 @@ static QSE_INLINE int parse_request (
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const qse_byte_t* getchunklen (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
||||||
|
{
|
||||||
|
const qse_byte_t* end = ptr + len;
|
||||||
|
|
||||||
|
if (http->state.chunk.count == 0)
|
||||||
|
{
|
||||||
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ptr < end)
|
||||||
|
{
|
||||||
|
int n = xdigit_to_num (*ptr);
|
||||||
|
if (n <= -1) break;
|
||||||
|
|
||||||
|
http->state.chunk.len = http->state.chunk.len * 16 + n;
|
||||||
|
http->state.chunk.count++;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http->state.chunk.count > 0)
|
||||||
|
{
|
||||||
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ptr < end)
|
||||||
|
{
|
||||||
|
if (*ptr == '\n')
|
||||||
|
{
|
||||||
|
http->state.need = http->state.chunk.len;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
http->errnum = QSE_HTTP_EBADREQ;
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chunk parsing phases */
|
||||||
|
#define GET_CHUNK_DONE 0
|
||||||
|
#define GET_CHUNK_LEN 1
|
||||||
|
#define GET_CHUNK_DATA 2
|
||||||
|
#define GET_CHUNK_CRLF 3
|
||||||
|
#define GET_CHUNK_TRAILERS 4
|
||||||
|
|
||||||
/* feed the percent encoded string */
|
/* feed the percent encoded string */
|
||||||
int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
||||||
{
|
{
|
||||||
const qse_byte_t* end = ptr + len;
|
const qse_byte_t* end = ptr + len;
|
||||||
const qse_byte_t* req = ptr;
|
const qse_byte_t* req = ptr;
|
||||||
|
|
||||||
if (http->state.need > 0)
|
/* does this goto drop code maintainability? */
|
||||||
|
if (http->state.need > 0) goto content_resume;
|
||||||
|
switch (http->state.chunk.phase)
|
||||||
{
|
{
|
||||||
/* does this goto drop code maintainability? */
|
case GET_CHUNK_LEN:
|
||||||
goto get_content;
|
goto dechunk_resume;
|
||||||
|
|
||||||
|
case GET_CHUNK_DATA:
|
||||||
|
/* this won't be reached as http->state.need
|
||||||
|
* is greater than 0 if GET_CHUNK_DATA is true */
|
||||||
|
goto content_resume;
|
||||||
|
|
||||||
|
case GET_CHUNK_CRLF:
|
||||||
|
goto dechunk_crlf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
case GET_CHUNK_TRAILERS:
|
||||||
|
goto ....
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ptr < end)
|
while (ptr < end)
|
||||||
@ -1011,21 +1098,59 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
|||||||
if (parse_request (http, req, ptr - req) <= -1)
|
if (parse_request (http, req, ptr - req) <= -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (http->req.attr.content_length > 0)
|
if (http->req.attr.chunked)
|
||||||
{
|
{
|
||||||
/* let's get content */
|
/* transfer-encoding: chunked */
|
||||||
|
QSE_ASSERT (http->req.attr.content_length <= 0);
|
||||||
|
|
||||||
|
dechunk_start:
|
||||||
|
http->state.chunk.phase = GET_CHUNK_LEN;
|
||||||
|
http->state.chunk.len = 0;
|
||||||
|
http->state.chunk.count = 0;
|
||||||
|
|
||||||
|
dechunk_resume:
|
||||||
|
ptr = getchunklen (http, ptr, end - ptr);
|
||||||
|
if (ptr == QSE_NULL) return -1;
|
||||||
|
|
||||||
|
if (http->state.chunk.count <= 0)
|
||||||
|
{
|
||||||
|
/* empty line - end of the chunk */
|
||||||
|
http->state.chunk.phase = GET_CHUNK_DONE;
|
||||||
|
}
|
||||||
|
else if (http->state.need <= 0)
|
||||||
|
{
|
||||||
|
/* length explicity specified to 0
|
||||||
|
get trailing headers .... */
|
||||||
|
/*http->state.chunk.phase = GET_CHUNK_TRAILERS;*/
|
||||||
|
http->state.chunk.phase = GET_CHUNK_DATA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* let's piggy back on the normal content-length data acquisition */
|
||||||
|
http->state.chunk.phase = GET_CHUNK_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
http->state.need = http->req.attr.content_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http->state.need > 0)
|
||||||
|
{
|
||||||
|
/* content-length or chunked data length specified */
|
||||||
|
|
||||||
qse_size_t avail;
|
qse_size_t avail;
|
||||||
|
|
||||||
http->state.need = http->req.attr.content_length;
|
content_resume:
|
||||||
|
|
||||||
get_content:
|
|
||||||
avail = end - ptr;
|
avail = end - ptr;
|
||||||
|
|
||||||
if (avail < http->state.need)
|
if (avail < http->state.need)
|
||||||
{
|
{
|
||||||
|
/* the data is not as large as needed */
|
||||||
if (push_to_buffer (http, &http->req.con, ptr, avail) <= -1) return -1;
|
if (push_to_buffer (http, &http->req.con, ptr, avail) <= -1) return -1;
|
||||||
http->state.need -= avail;
|
http->state.need -= avail;
|
||||||
goto done;
|
/* we didn't get a complete content yet */
|
||||||
|
goto abort;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1036,11 +1161,39 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (http->state.chunk.phase == GET_CHUNK_DATA)
|
||||||
|
{
|
||||||
|
QSE_ASSERT (http->state.need == 0);
|
||||||
|
http->state.chunk.phase = GET_CHUNK_CRLF;
|
||||||
|
|
||||||
|
dechunk_crlf:
|
||||||
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
||||||
|
if (ptr < end)
|
||||||
|
{
|
||||||
|
if (*ptr == '\n')
|
||||||
|
{
|
||||||
|
/* end of chunk data. let's decode the next chunk */
|
||||||
|
ptr++;
|
||||||
|
goto dechunk_start;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* redundant character ... */
|
||||||
|
http->errnum = QSE_HTTP_EBADREQ;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* data not enough */
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qse_htb_walk (&http->req.hdr.tab, walk, QSE_NULL);
|
qse_htb_walk (&http->req.hdr.tab, walk, QSE_NULL);
|
||||||
if (http->req.attr.content_length > 0)
|
if (http->req.con.size > 0)
|
||||||
{
|
{
|
||||||
qse_printf (QSE_T("content = [%.*S]\n"), (int)http->req.attr.content_length, http->req.con.data);
|
qse_printf (QSE_T("content = [%.*S]\n"), (int)http->req.con.size, http->req.con.data);
|
||||||
}
|
}
|
||||||
/* TODO: do the main job here... before the raw buffer is cleared out... */
|
/* TODO: do the main job here... before the raw buffer is cleared out... */
|
||||||
|
|
||||||
@ -1079,6 +1232,7 @@ if (http->req.attr.content_length > 0)
|
|||||||
if (push_to_buffer (http, &http->req.raw, req, ptr - req) <= -1) return -1;
|
if (push_to_buffer (http, &http->req.raw, req, ptr - req) <= -1) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
abort:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user