implemented proper handling of duplicate http header fields
This commit is contained in:
parent
1c82886941
commit
edff7c59d6
@ -30,20 +30,6 @@ enum qse_http_errnum_t
|
||||
|
||||
typedef enum qse_http_errnum_t qse_http_errnum_t;
|
||||
|
||||
/*
|
||||
struct qse_http_req_t
|
||||
{
|
||||
enum
|
||||
{
|
||||
QSE_HTTP_REQ_HEAD,
|
||||
QSE_HTTP_REQ_GET,
|
||||
QSE_HTTP_REQ_POST
|
||||
} method;
|
||||
|
||||
qse_char_t path[];
|
||||
};
|
||||
*/
|
||||
|
||||
typedef struct qse_http_t qse_http_t;
|
||||
|
||||
struct qse_http_t
|
||||
@ -53,7 +39,7 @@ struct qse_http_t
|
||||
|
||||
struct
|
||||
{
|
||||
//qse_size_t pending;
|
||||
/*qse_size_t pending;*/
|
||||
|
||||
int crlf; /* crlf status */
|
||||
qse_size_t plen; /* raw request length excluding crlf */
|
||||
@ -88,9 +74,12 @@ struct qse_http_t
|
||||
short minor;
|
||||
} version;
|
||||
|
||||
qse_htb_t hdr;
|
||||
struct
|
||||
{
|
||||
qse_htb_t tab;
|
||||
void* combined;
|
||||
} hdr;
|
||||
} req;
|
||||
|
||||
};
|
||||
|
||||
/* returns the type of http method */
|
||||
|
@ -301,7 +301,9 @@ static QSE_INLINE int push_to_buffer (
|
||||
|
||||
do
|
||||
{
|
||||
void* tmp = QSE_MMGR_REALLOC ((http)->mmgr, (octb)->data, ncapa * QSE_SIZEOF(*ptr));
|
||||
void* tmp = QSE_MMGR_REALLOC (
|
||||
(http)->mmgr, (octb)->data, ncapa * QSE_SIZEOF(*ptr)
|
||||
);
|
||||
if (tmp)
|
||||
{
|
||||
(octb)->capa = ncapa;
|
||||
@ -326,6 +328,25 @@ static QSE_INLINE int push_to_buffer (
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hdr_cmb_t
|
||||
{
|
||||
struct hdr_cmb_t* next;
|
||||
};
|
||||
|
||||
static QSE_INLINE void clear_combined_headers (qse_http_t* http)
|
||||
{
|
||||
struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)http->req.hdr.combined;
|
||||
|
||||
while (cmb)
|
||||
{
|
||||
struct hdr_cmb_t* next = cmb->next;
|
||||
QSE_MMGR_FREE (http->mmgr, cmb);
|
||||
cmb = next;
|
||||
}
|
||||
|
||||
http->req.hdr.combined = QSE_NULL;
|
||||
}
|
||||
|
||||
#define QSE_HTTP_STATE_REQ 1
|
||||
#define QSE_HTTP_STATE_HDR 2
|
||||
#define QSE_HTTP_STATE_POST 3
|
||||
@ -376,7 +397,7 @@ qse_http_t* qse_http_init (qse_http_t* http, qse_mmgr_t* mmgr)
|
||||
http->state.plen = 0;
|
||||
init_buffer (http, &http->req.raw);
|
||||
|
||||
if (qse_htb_init (&http->req.hdr, mmgr, 60, 70, 1, 1) == QSE_NULL)
|
||||
if (qse_htb_init (&http->req.hdr.tab, mmgr, 60, 70, 1, 1) == QSE_NULL)
|
||||
{
|
||||
fini_buffer (http, &http->req.raw);
|
||||
return QSE_NULL;
|
||||
@ -387,7 +408,8 @@ qse_http_t* qse_http_init (qse_http_t* http, qse_mmgr_t* mmgr)
|
||||
|
||||
void qse_http_fini (qse_http_t* http)
|
||||
{
|
||||
qse_htb_fini (&http->req.hdr);
|
||||
qse_htb_fini (&http->req.hdr.tab);
|
||||
clear_combined_headers (http);
|
||||
fini_buffer (http, &http->req.raw);
|
||||
}
|
||||
|
||||
@ -521,6 +543,99 @@ qse_printf (QSE_T("BADREQ\n"));
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
struct cbserter_ctx_t
|
||||
{
|
||||
qse_http_t* http;
|
||||
void* vptr;
|
||||
qse_size_t vlen;
|
||||
};
|
||||
|
||||
static qse_htb_pair_t* cbserter (
|
||||
qse_htb_t* htb, qse_htb_pair_t* pair,
|
||||
void* kptr, qse_size_t klen, void* ctx)
|
||||
{
|
||||
struct cbserter_ctx_t* tx = (struct cbserter_ctx_t*)ctx;
|
||||
|
||||
if (pair == QSE_NULL)
|
||||
{
|
||||
/* the key is new. create a new pair with the key and the value */
|
||||
qse_htb_pair_t* p;
|
||||
p = qse_htb_allocpair (htb, kptr, klen, tx->vptr, tx->vlen);
|
||||
if (p == QSE_NULL) tx->http->errnum = QSE_HTTP_ENOMEM;
|
||||
return p;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the key exists. let's combine values, each separated by a comma */
|
||||
struct hdr_cmb_t* cmb;
|
||||
qse_byte_t* ptr;
|
||||
qse_size_t len;
|
||||
|
||||
/* TODO: reduce waste in case the same key appears again and again.
|
||||
*
|
||||
* the current implementation is not space nor performance efficient.
|
||||
* it allocates a new buffer again whenever it encounters the
|
||||
* same key. memory is wasted and performance is sacrificed.
|
||||
*
|
||||
* hopefully, a http header does not include a lot of duplicate
|
||||
* fields and this implmentation can afford wastage.
|
||||
*/
|
||||
|
||||
/* allocate a block to combine the existing value and the new value */
|
||||
cmb = (struct hdr_cmb_t*) QSE_MMGR_ALLOC (
|
||||
tx->http->mmgr,
|
||||
QSE_SIZEOF(*cmb) +
|
||||
QSE_SIZEOF(qse_byte_t) * (pair->vlen + 1 + tx->vlen + 1)
|
||||
);
|
||||
if (cmb == QSE_NULL)
|
||||
{
|
||||
tx->http->errnum = QSE_HTTP_ENOMEM;
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
/* let 'ptr' point to the actual space for the combined value */
|
||||
ptr = (qse_byte_t*)(cmb + 1);
|
||||
len = 0;
|
||||
|
||||
/* fill the space with the value */
|
||||
QSE_MEMCPY (&ptr[len], pair->vptr, pair->vlen);
|
||||
len += pair->vlen;
|
||||
ptr[len++] = ',';
|
||||
QSE_MEMCPY (&ptr[len], tx->vptr, tx->vlen);
|
||||
len += tx->vlen;
|
||||
ptr[len] = '\0';
|
||||
|
||||
#if 0
|
||||
TODO:
|
||||
Not easy to unlink when using a singly linked list...
|
||||
Change it to doubly linked for this?
|
||||
|
||||
/* let's destroy the old buffer at least */
|
||||
if (!(ptr >= tx->http->req.raw.data && ptr <
|
||||
&tx->http->req.raw.data[tx->http->req.raw.size]))
|
||||
{
|
||||
/* NOTE the range check in 'if' assumes that raw.data is never
|
||||
* relocated for resizing */
|
||||
|
||||
QSE_MMGR_FREE (
|
||||
tx->http->mmgr,
|
||||
((struct hdr_cmb_t*)pair->vptr) - 1
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* update the value pointer and length */
|
||||
pair->vptr = ptr;
|
||||
pair->vlen = len;
|
||||
|
||||
/* link the new combined value block */
|
||||
cmb->next = tx->http->req.hdr.combined;
|
||||
tx->http->req.hdr.combined = cmb;
|
||||
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
qse_byte_t* parse_http_header (qse_http_t* http, qse_byte_t* line)
|
||||
{
|
||||
qse_byte_t* p = line, * last;
|
||||
@ -587,13 +702,21 @@ qse_byte_t* parse_http_header (qse_http_t* http, qse_byte_t* line)
|
||||
}
|
||||
*last = '\0';
|
||||
|
||||
/* add the field name and value into a hash table */
|
||||
if (qse_htb_insert (&http->req.hdr,
|
||||
name.ptr, name.len, value.ptr, value.len) == QSE_NULL)
|
||||
{
|
||||
struct cbserter_ctx_t ctx;
|
||||
|
||||
ctx.http = http;
|
||||
ctx.vptr = value.ptr;
|
||||
ctx.vlen = value.len;
|
||||
|
||||
http->errnum = QSE_HTTP_ENOERR;
|
||||
if (qse_htb_cbsert (
|
||||
&http->req.hdr.tab, name.ptr, name.len, cbserter, &ctx) == QSE_NULL)
|
||||
{
|
||||
http->errnum = QSE_HTTP_ENOMEM;
|
||||
if (http->errnum == QSE_HTTP_ENOERR) http->errnum = QSE_HTTP_ENOMEM;
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
@ -665,9 +788,11 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len)
|
||||
}
|
||||
while (1);
|
||||
|
||||
qse_htb_walk (&http->req.hdr, walk, QSE_NULL);
|
||||
qse_htb_walk (&http->req.hdr.tab, walk, QSE_NULL);
|
||||
/* TODO: do the main job here... before the raw buffer is cleared out... */
|
||||
|
||||
qse_htb_clear (&http->req.hdr.tab);
|
||||
clear_combined_headers (http);
|
||||
clear_buffer (http, &http->req.raw);
|
||||
req = ptr; /* let ptr point to the next character to '\n' */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user