reorganized the qse_http_t structure

This commit is contained in:
hyung-hwan 2010-11-29 08:26:51 +00:00
parent 5b4845db55
commit 4491055c84
2 changed files with 203 additions and 357 deletions

View File

@ -9,6 +9,7 @@
#include <qse/macros.h> #include <qse/macros.h>
#include <qse/cmn/htb.h> #include <qse/cmn/htb.h>
typedef struct qse_http_t qse_http_t;
typedef struct qse_http_octb_t qse_http_octb_t; typedef struct qse_http_octb_t qse_http_octb_t;
@ -19,46 +20,22 @@ struct qse_http_octb_t
qse_byte_t* data; qse_byte_t* data;
}; };
enum qse_http_errnum_t enum qse_http_errnum_t
{ {
QSE_HTTP_ENOERR, QSE_HTTP_ENOERR,
QSE_HTTP_ENOMEM, QSE_HTTP_ENOMEM,
QSE_HTTP_EBADREQ, QSE_HTTP_EBADREQ,
QSE_HTTP_EBADHDR, QSE_HTTP_EBADHDR,
QSE_HTTP_ETRAENC /* bad transfer-encoding */ QSE_HTTP_EREQCBS
}; };
typedef enum qse_http_errnum_t qse_http_errnum_t; typedef enum qse_http_errnum_t qse_http_errnum_t;
typedef struct qse_http_t qse_http_t;
struct qse_http_t typedef struct qse_http_req_t qse_http_req_t;
struct qse_http_req_t
{ {
QSE_DEFINE_COMMON_FIELDS (http)
qse_http_errnum_t errnum;
struct
{
struct
{
int crlf; /* crlf status */
qse_size_t plen; /* raw request length excluding crlf */
qse_size_t need; /* number of octets needed for contents */
struct
{
qse_size_t len;
qse_size_t count;
int phase;
} chunk;
} state;
qse_http_octb_t raw;
qse_http_octb_t con;
qse_http_octb_t tra;
enum enum
{ {
QSE_HTTP_REQ_GET, QSE_HTTP_REQ_GET,
@ -90,11 +67,8 @@ struct qse_http_t
short minor; short minor;
} version; } version;
struct /* header table */
{ qse_htb_t hdrtab;
qse_htb_t tab;
void* combined;
} hdr;
/* special attributes derived from the header */ /* special attributes derived from the header */
struct struct
@ -103,49 +77,60 @@ struct qse_http_t
int content_length; int content_length;
int connection_close; int connection_close;
} attr; } attr;
} req;
qse_http_octb_t con;
}; };
/* returns the type of http method */ typedef struct qse_http_reqcbs_t qse_http_reqcbs_t;
typedef struct qse_http_req_t qse_http_req_t;
typedef struct qse_http_hdr_t qse_http_hdr_t;
struct qse_http_req_t struct qse_http_reqcbs_t
{ {
qse_char_t* method; int (*request) (qse_http_t* http, qse_http_req_t* req);
struct
{
qse_char_t* ptr;
qse_size_t len;
} path;
struct
{
qse_char_t* ptr;
qse_size_t len;
} args;
struct
{
char major;
char minor;
} vers;
}; };
struct qse_http_hdr_t
struct qse_http_t
{ {
qse_cstr_t name; QSE_DEFINE_COMMON_FIELDS (http)
qse_cstr_t value; qse_http_errnum_t errnum;
const qse_http_reqcbs_t* reqcbs;
struct
{
struct
{
int crlf; /* crlf status */
qse_size_t plen; /* raw request length excluding crlf */
qse_size_t need; /* number of octets needed for contents */
struct
{
qse_size_t len;
qse_size_t count;
int phase;
} chunk;
} s; /* state */
/* buffers needed to for processing a request */
struct
{
qse_http_octb_t raw;
qse_http_octb_t tra;
} b;
/* points to the head of the combined header list */
void* chl;
} reqx;
qse_http_req_t req;
}; };
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
qse_char_t* qse_parsehttpreq (qse_char_t* buf, qse_http_req_t* req);
qse_char_t* qse_parsehttphdr (qse_char_t* buf, qse_http_hdr_t* hdr);
QSE_DEFINE_COMMON_FUNCTIONS (http) QSE_DEFINE_COMMON_FUNCTIONS (http)
/** /**
@ -176,6 +161,25 @@ void qse_http_clear (
qse_http_t* http qse_http_t* http
); );
const qse_http_reqcbs_t* qse_http_getreqcbs (
qse_http_t* http
);
void qse_http_setreqcbs (
qse_http_t* http,
const qse_http_reqcbs_t* reqcbs
);
/**
* The qse_http_feed() function accepts http request octets and invokes a
* callback function if it has processed a proper http request.
*/
int qse_http_feed (
qse_http_t* http, /**< http */
const qse_byte_t* req, /**< request octets */
qse_size_t len /**< number of octets */
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -26,190 +26,6 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (http)
static const qse_byte_t NUL = '\0'; static const qse_byte_t NUL = '\0';
static QSE_INLINE int is_http_space (qse_char_t c)
{
return QSE_ISSPACE(c) && c != QSE_T('\r') && c != QSE_T('\n');
}
#define is_http_ctl(c) QSE_ISCNTRL(c)
static QSE_INLINE int is_http_separator (qse_char_t c)
{
return c == QSE_T('(') ||
c == QSE_T(')') ||
c == QSE_T('<') ||
c == QSE_T('>') ||
c == QSE_T('@') ||
c == QSE_T(',') ||
c == QSE_T(';') ||
c == QSE_T(':') ||
c == QSE_T('\\') ||
c == QSE_T('\"') ||
c == QSE_T('/') ||
c == QSE_T('[') ||
c == QSE_T(']') ||
c == QSE_T('?') ||
c == QSE_T('=') ||
c == QSE_T('{') ||
c == QSE_T('}') ||
c == QSE_T('\t') ||
c == QSE_T(' ');
}
static QSE_INLINE int is_http_token (qse_char_t c)
{
return QSE_ISPRINT(c) && !is_http_ctl(c) && !is_http_separator(c);
}
static QSE_INLINE int dig_to_num (qse_char_t c)
{
if (c >= QSE_T('0') && c <= QSE_T('9')) return c - QSE_T('0');
if (c >= QSE_T('A') && c <= QSE_T('Z')) return c - QSE_T('A') + 10;
if (c >= QSE_T('a') && c <= QSE_T('z')) return c - QSE_T('a') + 10;
return -1;
}
qse_char_t* qse_parsehttpreq (qse_char_t* octb, qse_http_req_t* req)
{
qse_char_t* p = octb, * x;
/* ignore leading spaces */
while (is_http_space(*p)) p++;
/* the method should start with an alphabet */
if (!QSE_ISALPHA(*p)) return QSE_NULL;
/* scan the method */
req->method = p; while (QSE_ISALPHA(*p)) p++;
/* the method should be followed by a space */
if (!is_http_space(*p)) return QSE_NULL;
/* null-terminate the method */
*p++ = QSE_T('\0');
/* skip spaces */
while (is_http_space(*p)) p++;
/* scan the url */
req->path.ptr = p;
req->args.ptr = QSE_NULL;
x = p;
while (QSE_ISPRINT(*p) && !QSE_ISSPACE(*p))
{
if (*p == QSE_T('%') && QSE_ISXDIGIT(*(p+1)) && QSE_ISXDIGIT(*(p+2)))
{
*x++ = (dig_to_num(*(p+1)) << 4) + dig_to_num(*(p+2));
p += 3;
}
else if (*p == QSE_T('?') && req->args.ptr == QSE_NULL)
{
/* ? must be explicit to be a argument instroducer.
* %3f is just a literal. */
req->path.len = x - req->path.ptr;
*x++ = QSE_T('\0');
req->args.ptr = x;
p++;
}
else *x++ = *p++;
}
/* the url should be followed by a space */
if (!is_http_space(*p)) return QSE_NULL;
/* null-terminate the url and store the length */
if (req->args.ptr != QSE_NULL)
req->args.len = x - req->args.ptr;
else
req->path.len = x - req->path.ptr;
*x++ = QSE_T('\0');
/* path should start with a slash */
if (req->path.len <= 0 || req->path.ptr[0] != QSE_T('/')) return QSE_NULL;
/* skip spaces */
do { p++; } while (is_http_space(*p));
/* check http version */
if ((p[0] == QSE_T('H') || p[0] == QSE_T('h')) &&
(p[1] == QSE_T('T') || p[1] == QSE_T('t')) &&
(p[2] == QSE_T('T') || p[2] == QSE_T('t')) &&
(p[3] == QSE_T('P') || p[3] == QSE_T('p')) &&
p[4] == QSE_T('/') && p[6] == QSE_T('.'))
{
if (!QSE_ISDIGIT(p[5])) return QSE_NULL;
if (!QSE_ISDIGIT(p[7])) return QSE_NULL;
req->vers.major = p[5] - QSE_T('0');
req->vers.minor = p[7] - QSE_T('0');
p += 8;
}
else return QSE_NULL;
while (QSE_ISSPACE(*p))
{
if (*p++ == QSE_T('\n')) goto ok;
}
/* not terminating with a new line.
* maybe garbage after the request line */
if (*p != QSE_T('\0')) return QSE_NULL;
ok:
/* returns the next position */
return p;
}
qse_char_t* qse_parsehttphdr (qse_char_t* octb, qse_http_hdr_t* hdr)
{
qse_char_t* p = octb, * last;
/* ignore leading spaces including CR and NL */
while (QSE_ISSPACE(*p)) p++;
if (*p == QSE_T('\0'))
{
/* no more header line */
QSE_MEMSET (hdr, 0, QSE_SIZEOF(*hdr));
return p;
}
if (!is_http_token(*p)) return QSE_NULL;
hdr->name.ptr = p;
do { p++; } while (is_http_token(*p));
last = p;
hdr->name.len = last - hdr->name.ptr;
while (is_http_space(*p)) p++;
if (*p != QSE_T(':')) return QSE_NULL;
*last = QSE_T('\0');
do { p++; } while (is_http_space(*p));
hdr->value.ptr = last = p;
while (QSE_ISPRINT(*p))
{
if (!QSE_ISSPACE(*p++)) last = p;
}
hdr->value.len = last - hdr->value.ptr;
while (QSE_ISSPACE(*p))
{
if (*p++ == QSE_T('\n')) goto ok;
}
/* not terminating with a new line.
* maybe garbage after the header line */
if (*p != QSE_T('\0')) return QSE_NULL;
ok:
*last = QSE_T('\0');
return p;
}
static QSE_INLINE int is_whspace_octet (qse_byte_t c) static QSE_INLINE int is_whspace_octet (qse_byte_t c)
{ {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'; return c == ' ' || c == '\t' || c == '\r' || c == '\n';
@ -338,7 +154,7 @@ struct hdr_cmb_t
static QSE_INLINE void clear_combined_headers (qse_http_t* http) static QSE_INLINE void clear_combined_headers (qse_http_t* http)
{ {
struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)http->req.hdr.combined; struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)http->reqx.chl;
while (cmb) while (cmb)
{ {
@ -347,23 +163,23 @@ static QSE_INLINE void clear_combined_headers (qse_http_t* http)
cmb = next; cmb = next;
} }
http->req.hdr.combined = QSE_NULL; http->reqx.chl = QSE_NULL;
} }
static QSE_INLINE void clear_request (qse_http_t* http) static QSE_INLINE void clear_request (qse_http_t* http)
{ {
/* clear necessary part of the request before /* clear necessary part of the request before
* reading the next request */ * reading the next request */
QSE_MEMSET (&http->req.state, 0, QSE_SIZEOF(http->req.state));
QSE_MEMSET (&http->req.attr, 0, QSE_SIZEOF(http->req.attr)); QSE_MEMSET (&http->req.attr, 0, QSE_SIZEOF(http->req.attr));
qse_htb_clear (&http->req.hdr.tab); qse_htb_clear (&http->req.hdrtab);
clear_combined_headers (http); clear_combined_headers (http);
clear_buffer (http, &http->req.tra);
clear_buffer (http, &http->req.con); clear_buffer (http, &http->req.con);
clear_buffer (http, &http->req.raw); clear_buffer (http, &http->reqx.b.tra);
clear_buffer (http, &http->reqx.b.raw);
QSE_MEMSET (&http->reqx.s, 0, QSE_SIZEOF(http->reqx.s));
} }
#define QSE_HTTP_STATE_REQ 1 #define QSE_HTTP_STATE_REQ 1
@ -411,15 +227,15 @@ qse_http_t* qse_http_init (qse_http_t* http, qse_mmgr_t* mmgr)
QSE_MEMSET (http, 0, QSE_SIZEOF(*http)); QSE_MEMSET (http, 0, QSE_SIZEOF(*http));
http->mmgr = mmgr; http->mmgr = mmgr;
init_buffer (http, &http->req.raw); init_buffer (http, &http->reqx.b.raw);
init_buffer (http, &http->reqx.b.tra);
init_buffer (http, &http->req.con); init_buffer (http, &http->req.con);
init_buffer (http, &http->req.tra);
if (qse_htb_init (&http->req.hdr.tab, mmgr, 60, 70, 1, 1) == QSE_NULL) if (qse_htb_init (&http->req.hdrtab, mmgr, 60, 70, 1, 1) == QSE_NULL)
{ {
fini_buffer (http, &http->req.tra);
fini_buffer (http, &http->req.con); fini_buffer (http, &http->req.con);
fini_buffer (http, &http->req.raw); fini_buffer (http, &http->reqx.b.tra);
fini_buffer (http, &http->reqx.b.raw);
return QSE_NULL; return QSE_NULL;
} }
@ -428,11 +244,11 @@ 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.tab); qse_htb_fini (&http->req.hdrtab);
clear_combined_headers (http); clear_combined_headers (http);
fini_buffer (http, &http->req.tra);
fini_buffer (http, &http->req.con); fini_buffer (http, &http->req.con);
fini_buffer (http, &http->req.raw); fini_buffer (http, &http->reqx.b.tra);
fini_buffer (http, &http->reqx.b.raw);
} }
static qse_byte_t* parse_reqline (qse_http_t* http, qse_byte_t* line) static qse_byte_t* parse_reqline (qse_http_t* http, qse_byte_t* line)
@ -577,6 +393,16 @@ void qse_http_clear (qse_http_t* http)
clear_request (http); clear_request (http);
} }
const qse_http_reqcbs_t* qse_http_getreqcbs (qse_http_t* http)
{
return http->reqcbs;
}
void qse_http_setreqcbs (qse_http_t* http, const qse_http_reqcbs_t* reqcbs)
{
http->reqcbs = reqcbs;
}
#define octet_tolower(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) | 0x20) : (c)) #define octet_tolower(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) | 0x20) : (c))
#define octet_toupper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~0x20) : (c)) #define octet_toupper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~0x20) : (c))
@ -832,8 +658,8 @@ Not easy to unlink when using a singly linked list...
Change it to doubly linked for this? Change it to doubly linked for this?
/* let's destroy the old buffer at least */ /* let's destroy the old buffer at least */
if (!(ptr >= tx->http->req.raw.data && ptr < if (!(ptr >= tx->http->reqx.b.raw.data && ptr <
&tx->http->req.raw.data[tx->http->req.raw.size])) &tx->http->reqx.b.raw.data[tx->http->reqx.b.raw.size]))
{ {
/* NOTE the range check in 'if' assumes that raw.data is never /* NOTE the range check in 'if' assumes that raw.data is never
* relocated for resizing */ * relocated for resizing */
@ -850,8 +676,8 @@ Change it to doubly linked for this?
pair->vlen = len; pair->vlen = len;
/* link the new combined value block */ /* link the new combined value block */
cmb->next = tx->http->req.hdr.combined; cmb->next = tx->http->reqx.chl;
tx->http->req.hdr.combined = cmb; tx->http->reqx.chl = cmb;
if (capture_key_header (tx->http, pair) <= -1) return QSE_NULL; if (capture_key_header (tx->http, pair) <= -1) return QSE_NULL;
@ -935,7 +761,7 @@ qse_byte_t* parse_header_fields (qse_http_t* http, qse_byte_t* line)
http->errnum = QSE_HTTP_ENOERR; http->errnum = QSE_HTTP_ENOERR;
if (qse_htb_cbsert ( if (qse_htb_cbsert (
&http->req.hdr.tab, name.ptr, name.len, &http->req.hdrtab, name.ptr, name.len,
hdr_cbserter, &ctx) == QSE_NULL) hdr_cbserter, &ctx) == QSE_NULL)
{ {
if (http->errnum == QSE_HTTP_ENOERR) if (http->errnum == QSE_HTTP_ENOERR)
@ -964,12 +790,12 @@ static QSE_INLINE int parse_request (
qse_byte_t* p; qse_byte_t* p;
/* add the actual request */ /* add the actual request */
if (push_to_buffer (http, &http->req.raw, req, rlen) <= -1) return -1; if (push_to_buffer (http, &http->reqx.b.raw, req, rlen) <= -1) return -1;
/* add the terminating null for easier parsing */ /* add the terminating null for easier parsing */
if (push_to_buffer (http, &http->req.raw, &NUL, 1) <= -1) return -1; if (push_to_buffer (http, &http->reqx.b.raw, &NUL, 1) <= -1) return -1;
p = http->req.raw.data; p = http->reqx.b.raw.data;
while (is_whspace_octet(*p)) p++; while (is_whspace_octet(*p)) p++;
QSE_ASSERT (*p != '\0'); QSE_ASSERT (*p != '\0');
@ -1007,10 +833,10 @@ static const qse_byte_t* getchunklen (qse_http_t* http, const qse_byte_t* ptr, q
const qse_byte_t* end = ptr + len; const qse_byte_t* end = ptr + len;
/* this function must be called in the GET_CHUNK_LEN context */ /* this function must be called in the GET_CHUNK_LEN context */
QSE_ASSERT (http->req.state.chunk.phase == GET_CHUNK_LEN); QSE_ASSERT (http->reqx.s.chunk.phase == GET_CHUNK_LEN);
//qse_printf (QSE_T("CALLING getchunklen [%d]\n"), *ptr); //qse_printf (QSE_T("CALLING getchunklen [%d]\n"), *ptr);
if (http->req.state.chunk.count <= 0) if (http->reqx.s.chunk.count <= 0)
{ {
/* skip leading spaces if the first character of /* skip leading spaces if the first character of
* the chunk length has not been read yet */ * the chunk length has not been read yet */
@ -1022,8 +848,8 @@ static const qse_byte_t* getchunklen (qse_http_t* http, const qse_byte_t* ptr, q
int n = xdigit_to_num (*ptr); int n = xdigit_to_num (*ptr);
if (n <= -1) break; if (n <= -1) break;
http->req.state.chunk.len = http->req.state.chunk.len * 16 + n; http->reqx.s.chunk.len = http->reqx.s.chunk.len * 16 + n;
http->req.state.chunk.count++; http->reqx.s.chunk.count++;
ptr++; ptr++;
} }
@ -1036,27 +862,27 @@ static const qse_byte_t* getchunklen (qse_http_t* http, const qse_byte_t* ptr, q
{ {
/* the chunk length line ended properly */ /* the chunk length line ended properly */
if (http->req.state.chunk.count <= 0) if (http->reqx.s.chunk.count <= 0)
{ {
/* empty line - no more chunk */ /* empty line - no more chunk */
//qse_printf (QSE_T("empty line chunk done....\n")); //qse_printf (QSE_T("empty line chunk done....\n"));
http->req.state.chunk.phase = GET_CHUNK_DONE; http->reqx.s.chunk.phase = GET_CHUNK_DONE;
} }
else if (http->req.state.chunk.len <= 0) else if (http->reqx.s.chunk.len <= 0)
{ {
/* length explicity specified to 0 /* length explicity specified to 0
get trailing headers .... */ get trailing headers .... */
http->req.state.chunk.phase = GET_CHUNK_TRAILERS; http->reqx.s.chunk.phase = GET_CHUNK_TRAILERS;
//qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n")); //qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n"));
} }
else else
{ {
/* ready to read the chunk data... */ /* ready to read the chunk data... */
http->req.state.chunk.phase = GET_CHUNK_DATA; http->reqx.s.chunk.phase = GET_CHUNK_DATA;
//qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n")); //qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n"));
} }
http->req.state.need = http->req.state.chunk.len; http->reqx.s.need = http->reqx.s.chunk.len;
ptr++; ptr++;
} }
else else
@ -1088,26 +914,26 @@ static const qse_byte_t* get_trailing_headers (
return -1; return -1;
case '\n': case '\n':
if (http->req.state.crlf <= 1) if (http->reqx.s.crlf <= 1)
{ {
http->req.state.crlf = 2; http->reqx.s.crlf = 2;
break; break;
} }
else else
{ {
qse_byte_t* p; qse_byte_t* p;
QSE_ASSERT (http->req.state.crlf <= 3); QSE_ASSERT (http->reqx.s.crlf <= 3);
http->req.state.crlf = 0; http->reqx.s.crlf = 0;
if (push_to_buffer ( if (push_to_buffer (
http, &http->req.tra, req, ptr - req) <= -1) http, &http->reqx.b.tra, req, ptr - req) <= -1)
return QSE_NULL; return QSE_NULL;
if (push_to_buffer ( if (push_to_buffer (
http, &http->req.tra, &NUL, 1) <= -1) http, &http->reqx.b.tra, &NUL, 1) <= -1)
return QSE_NULL; return QSE_NULL;
p = http->req.tra.data; p = http->reqx.b.tra.data;
do do
{ {
@ -1122,23 +948,24 @@ static const qse_byte_t* get_trailing_headers (
} }
while (1); while (1);
http->req.state.chunk.phase = GET_CHUNK_DONE; http->reqx.s.chunk.phase = GET_CHUNK_DONE;
goto done; goto done;
} }
case '\r': case '\r':
if (http->req.state.crlf == 0 || http->req.state.crlf == 2) if (http->reqx.s.crlf == 0 || http->reqx.s.crlf == 2)
http->req.state.crlf++; http->reqx.s.crlf++;
else http->req.state.crlf = 1; else http->reqx.s.crlf = 1;
break; break;
default: default:
/* mark that neither CR nor LF was seen */ /* mark that neither CR nor LF was seen */
http->req.state.crlf = 0; http->reqx.s.crlf = 0;
} }
} }
if (push_to_buffer (http, &http->req.tra, req, ptr - req) <= -1) return QSE_NULL; if (push_to_buffer (http, &http->reqx.b.tra, req, ptr - req) <= -1)
return QSE_NULL;
done: done:
return ptr; return ptr;
@ -1152,14 +979,14 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
const qse_byte_t* ptr = req; const qse_byte_t* ptr = req;
/* does this goto drop code maintainability? */ /* does this goto drop code maintainability? */
if (http->req.state.need > 0) goto content_resume; if (http->reqx.s.need > 0) goto content_resume;
switch (http->req.state.chunk.phase) switch (http->reqx.s.chunk.phase)
{ {
case GET_CHUNK_LEN: case GET_CHUNK_LEN:
goto dechunk_resume; goto dechunk_resume;
case GET_CHUNK_DATA: case GET_CHUNK_DATA:
/* this won't be reached as http->req.state.need /* this won't be reached as http->reqx.s.need
* is greater than 0 if GET_CHUNK_DATA is true */ * is greater than 0 if GET_CHUNK_DATA is true */
goto content_resume; goto content_resume;
@ -1174,7 +1001,7 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
{ {
register qse_byte_t b = *ptr++; register qse_byte_t b = *ptr++;
if (http->req.state.plen <= 0 && is_whspace_octet(b)) if (http->reqx.s.plen <= 0 && is_whspace_octet(b))
{ {
/* let's drop leading whitespaces across multiple /* let's drop leading whitespaces across multiple
* lines */ * lines */
@ -1185,38 +1012,38 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
switch (b) switch (b)
{ {
case '\0': case '\0':
/* guarantee that the request does not contain a null /* guarantee that the request does not contain
* character */ * a null character */
http->errnum = QSE_HTTP_EBADREQ; http->errnum = QSE_HTTP_EBADREQ;
return -1; return -1;
case '\n': case '\n':
if (http->req.state.crlf <= 1) if (http->reqx.s.crlf <= 1)
{ {
/* http->req.state.crlf == 0 /* http->reqx.s.crlf == 0
* => CR was not seen * => CR was not seen
* http->req.state.crlf == 1 * http->reqx.s.crlf == 1
* => CR was seen * => CR was seen
* whatever the current case is, * whatever the current case is,
* mark the first LF is seen here. * mark the first LF is seen here.
*/ */
http->req.state.crlf = 2; http->reqx.s.crlf = 2;
} }
else else
{ {
/* http->req.state.crlf == 2 /* http->reqx.s.crlf == 2
* => no 2nd CR before LF * => no 2nd CR before LF
* http->req.state.crlf == 3 * http->reqx.s.crlf == 3
* => 2nd CR before LF * => 2nd CR before LF
*/ */
/* we got a complete request. */ /* we got a complete request. */
QSE_ASSERT (http->req.state.crlf <= 3); QSE_ASSERT (http->reqx.s.crlf <= 3);
/* reset the crlf state */ /* reset the crlf state */
http->req.state.crlf = 0; http->reqx.s.crlf = 0;
/* reset the raw request length */ /* reset the raw request length */
http->req.state.plen = 0; http->reqx.s.plen = 0;
if (parse_request (http, req, ptr - req) <= -1) if (parse_request (http, req, ptr - req) <= -1)
return -1; return -1;
@ -1227,27 +1054,27 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
QSE_ASSERT (http->req.attr.content_length <= 0); QSE_ASSERT (http->req.attr.content_length <= 0);
dechunk_start: dechunk_start:
http->req.state.chunk.phase = GET_CHUNK_LEN; http->reqx.s.chunk.phase = GET_CHUNK_LEN;
http->req.state.chunk.len = 0; http->reqx.s.chunk.len = 0;
http->req.state.chunk.count = 0; http->reqx.s.chunk.count = 0;
dechunk_resume: dechunk_resume:
ptr = getchunklen (http, ptr, end - ptr); ptr = getchunklen (http, ptr, end - ptr);
if (ptr == QSE_NULL) return -1; if (ptr == QSE_NULL) return -1;
if (http->req.state.chunk.phase == GET_CHUNK_LEN) if (http->reqx.s.chunk.phase == GET_CHUNK_LEN)
{ {
/* still in the GET_CHUNK_LEN state. /* still in the GET_CHUNK_LEN state.
* the length has been partially read. */ * the length has been partially read. */
goto feedme_more; goto feedme_more;
} }
else if (http->req.state.chunk.phase == GET_CHUNK_TRAILERS) else if (http->reqx.s.chunk.phase == GET_CHUNK_TRAILERS)
{ {
dechunk_get_trailers: dechunk_get_trailers:
ptr = get_trailing_headers (http, ptr, end); ptr = get_trailing_headers (http, ptr, end);
if (ptr == QSE_NULL) return -1; if (ptr == QSE_NULL) return -1;
if (http->req.state.chunk.phase == GET_CHUNK_TRAILERS) if (http->reqx.s.chunk.phase == GET_CHUNK_TRAILERS)
{ {
/* still in the same state. /* still in the same state.
* the trailers have not been processed fully */ * the trailers have not been processed fully */
@ -1258,10 +1085,10 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
else else
{ {
/* we need to read as many octets as Content-Length */ /* we need to read as many octets as Content-Length */
http->req.state.need = http->req.attr.content_length; http->reqx.s.need = http->req.attr.content_length;
} }
if (http->req.state.need > 0) if (http->reqx.s.need > 0)
{ {
/* content-length or chunked data length specified */ /* content-length or chunked data length specified */
@ -1270,11 +1097,11 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
content_resume: content_resume:
avail = end - ptr; avail = end - ptr;
if (avail < http->req.state.need) if (avail < http->reqx.s.need)
{ {
/* the data is not as large as needed */ /* 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->req.state.need -= avail; http->reqx.s.need -= avail;
/* we didn't get a complete content yet */ /* we didn't get a complete content yet */
goto feedme_more; goto feedme_more;
} }
@ -1283,16 +1110,16 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
/* we got all or more than needed */ /* we got all or more than needed */
if (push_to_buffer ( if (push_to_buffer (
http, &http->req.con, ptr, http, &http->req.con, ptr,
http->req.state.need) <= -1) return -1; http->reqx.s.need) <= -1) return -1;
ptr += http->req.state.need; ptr += http->reqx.s.need;
http->req.state.need = 0; http->reqx.s.need = 0;
} }
} }
if (http->req.state.chunk.phase == GET_CHUNK_DATA) if (http->reqx.s.chunk.phase == GET_CHUNK_DATA)
{ {
QSE_ASSERT (http->req.state.need == 0); QSE_ASSERT (http->reqx.s.need == 0);
http->req.state.chunk.phase = GET_CHUNK_CRLF; http->reqx.s.chunk.phase = GET_CHUNK_CRLF;
dechunk_crlf: dechunk_crlf:
while (ptr < end && is_space_octet(*ptr)) ptr++; while (ptr < end && is_space_octet(*ptr)) ptr++;
@ -1313,9 +1140,9 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
* need to be reset when a jump is made * need to be reset when a jump is made
* to dechunk_resume upon the next call * to dechunk_resume upon the next call
*/ */
http->req.state.chunk.phase = GET_CHUNK_LEN; http->reqx.s.chunk.phase = GET_CHUNK_LEN;
http->req.state.chunk.len = 0; http->reqx.s.chunk.len = 0;
http->req.state.chunk.count = 0; http->reqx.s.chunk.count = 0;
goto feedme_more; goto feedme_more;
} }
@ -1333,12 +1160,28 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* req, qse_size_t len)
} }
} }
qse_htb_walk (&http->req.hdr.tab, walk, QSE_NULL);
QSE_ASSERTX (http->reqcbs != QSE_NULL,
"Set the request callback before feeding data");
http->errnum = QSE_HTTP_ENOERR;
if (http->reqcbs->request (http, &http->req) <= -1)
{
if (http->errnum == QSE_HTTP_ENOERR)
http->errnum = QSE_HTTP_EREQCBS;
/* need to clear request on error?
clear_request (http); */
return -1;
}
#if 0
qse_htb_walk (&http->req.hdrtab, walk, QSE_NULL);
if (http->req.con.size > 0) if (http->req.con.size > 0)
{ {
qse_printf (QSE_T("content = [%.*S]\n"), (int)http->req.con.size, 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... */
#endif
clear_request (http); clear_request (http);
@ -1349,28 +1192,27 @@ if (http->req.con.size > 0)
break; break;
case '\r': case '\r':
if (http->req.state.crlf == 0 || http->req.state.crlf == 2) if (http->reqx.s.crlf == 0 || http->reqx.s.crlf == 2)
http->req.state.crlf++; http->reqx.s.crlf++;
else http->req.state.crlf = 1; else http->reqx.s.crlf = 1;
break; break;
default: default:
/* increment length of a request in raw /* increment length of a request in raw
* excluding crlf */ * excluding crlf */
http->req.state.plen++; http->reqx.s.plen++;
/* mark that neither CR nor LF was seen */ /* mark that neither CR nor LF was seen */
http->req.state.crlf = 0; http->reqx.s.crlf = 0;
} }
} }
if (ptr > req) if (ptr > req)
{ {
/* enbuffer the incomplete request */ /* enbuffer the incomplete request */
if (push_to_buffer (http, &http->req.raw, req, ptr - req) <= -1) return -1; if (push_to_buffer (http, &http->reqx.b.raw, req, ptr - req) <= -1) return -1;
} }
feedme_more: feedme_more:
return 0; return 0;
} }