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;
|
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;
|
typedef struct qse_http_t qse_http_t;
|
||||||
|
|
||||||
struct qse_http_t
|
struct qse_http_t
|
||||||
@ -53,7 +39,7 @@ struct qse_http_t
|
|||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
//qse_size_t pending;
|
/*qse_size_t pending;*/
|
||||||
|
|
||||||
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 */
|
||||||
@ -88,9 +74,12 @@ struct qse_http_t
|
|||||||
short minor;
|
short minor;
|
||||||
} version;
|
} version;
|
||||||
|
|
||||||
qse_htb_t hdr;
|
struct
|
||||||
|
{
|
||||||
|
qse_htb_t tab;
|
||||||
|
void* combined;
|
||||||
|
} hdr;
|
||||||
} req;
|
} req;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* returns the type of http method */
|
/* returns the type of http method */
|
||||||
|
@ -301,7 +301,9 @@ static QSE_INLINE int push_to_buffer (
|
|||||||
|
|
||||||
do
|
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)
|
if (tmp)
|
||||||
{
|
{
|
||||||
(octb)->capa = ncapa;
|
(octb)->capa = ncapa;
|
||||||
@ -326,6 +328,25 @@ static QSE_INLINE int push_to_buffer (
|
|||||||
return 0;
|
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_REQ 1
|
||||||
#define QSE_HTTP_STATE_HDR 2
|
#define QSE_HTTP_STATE_HDR 2
|
||||||
#define QSE_HTTP_STATE_POST 3
|
#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;
|
http->state.plen = 0;
|
||||||
init_buffer (http, &http->req.raw);
|
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);
|
fini_buffer (http, &http->req.raw);
|
||||||
return QSE_NULL;
|
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)
|
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);
|
fini_buffer (http, &http->req.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +543,99 @@ qse_printf (QSE_T("BADREQ\n"));
|
|||||||
return QSE_NULL;
|
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* parse_http_header (qse_http_t* http, qse_byte_t* line)
|
||||||
{
|
{
|
||||||
qse_byte_t* p = line, * last;
|
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';
|
*last = '\0';
|
||||||
|
|
||||||
/* add the field name and value into a hash table */
|
{
|
||||||
if (qse_htb_insert (&http->req.hdr,
|
struct cbserter_ctx_t ctx;
|
||||||
name.ptr, name.len, value.ptr, value.len) == QSE_NULL)
|
|
||||||
|
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 QSE_NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return p;
|
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);
|
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... */
|
/* 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);
|
clear_buffer (http, &http->req.raw);
|
||||||
req = ptr; /* let ptr point to the next character to '\n' */
|
req = ptr; /* let ptr point to the next character to '\n' */
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user