reorganized the qse_http_t structure
This commit is contained in:
		@ -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,25 +20,81 @@ 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;
 | 
					
 | 
				
			||||||
 | 
					typedef struct qse_http_req_t qse_http_req_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct qse_http_req_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							QSE_HTTP_REQ_GET,
 | 
				
			||||||
 | 
							QSE_HTTP_REQ_HEAD,
 | 
				
			||||||
 | 
							QSE_HTTP_REQ_POST
 | 
				
			||||||
 | 
						} method;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							qse_byte_t* ptr;
 | 
				
			||||||
 | 
							qse_size_t  len;
 | 
				
			||||||
 | 
						} host;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							qse_byte_t* ptr;
 | 
				
			||||||
 | 
							qse_size_t  len;
 | 
				
			||||||
 | 
						} path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							qse_byte_t* ptr;
 | 
				
			||||||
 | 
							qse_size_t  len;
 | 
				
			||||||
 | 
						} args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							short major;
 | 
				
			||||||
 | 
							short minor;
 | 
				
			||||||
 | 
						} version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* header table */
 | 
				
			||||||
 | 
						qse_htb_t hdrtab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* special attributes derived from the header */
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int chunked;		
 | 
				
			||||||
 | 
							int content_length;
 | 
				
			||||||
 | 
							int connection_close;
 | 
				
			||||||
 | 
						} attr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qse_http_octb_t con;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct qse_http_reqcbs_t qse_http_reqcbs_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct qse_http_reqcbs_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int (*request) (qse_http_t* http, qse_http_req_t* req);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct qse_http_t
 | 
					struct qse_http_t
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	QSE_DEFINE_COMMON_FIELDS (http)
 | 
						QSE_DEFINE_COMMON_FIELDS (http)
 | 
				
			||||||
	qse_http_errnum_t errnum;
 | 
						qse_http_errnum_t errnum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const qse_http_reqcbs_t* reqcbs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct
 | 
						struct
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@ -53,99 +110,27 @@ struct qse_http_t
 | 
				
			|||||||
				qse_size_t count;
 | 
									qse_size_t count;
 | 
				
			||||||
				int        phase;
 | 
									int        phase;
 | 
				
			||||||
			} chunk;
 | 
								} chunk;
 | 
				
			||||||
		} state;
 | 
							} s; /* state */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		qse_http_octb_t raw;
 | 
					 | 
				
			||||||
		qse_http_octb_t con;
 | 
					 | 
				
			||||||
		qse_http_octb_t tra;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		enum
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			QSE_HTTP_REQ_GET,
 | 
					 | 
				
			||||||
			QSE_HTTP_REQ_HEAD,
 | 
					 | 
				
			||||||
			QSE_HTTP_REQ_POST
 | 
					 | 
				
			||||||
		} method;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* buffers needed to for processing a request */
 | 
				
			||||||
		struct
 | 
							struct
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			qse_byte_t* ptr;
 | 
								qse_http_octb_t raw;
 | 
				
			||||||
			qse_size_t  len;
 | 
								qse_http_octb_t tra;
 | 
				
			||||||
		} host;
 | 
							} b; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		struct
 | 
							/* points to the head of the combined header list */
 | 
				
			||||||
		{
 | 
							void* chl;
 | 
				
			||||||
			qse_byte_t* ptr;
 | 
						} reqx; 
 | 
				
			||||||
			qse_size_t  len;
 | 
					 | 
				
			||||||
		} path;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		struct
 | 
						qse_http_req_t req;
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			qse_byte_t* ptr;
 | 
					 | 
				
			||||||
			qse_size_t  len;
 | 
					 | 
				
			||||||
		} args;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			short major;
 | 
					 | 
				
			||||||
			short minor;
 | 
					 | 
				
			||||||
		} version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			qse_htb_t tab;
 | 
					 | 
				
			||||||
			void* combined;
 | 
					 | 
				
			||||||
		} hdr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* special attributes derived from the header */
 | 
					 | 
				
			||||||
		struct
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			int chunked;		
 | 
					 | 
				
			||||||
			int content_length;
 | 
					 | 
				
			||||||
			int connection_close;
 | 
					 | 
				
			||||||
		} attr;
 | 
					 | 
				
			||||||
	} req;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* returns the type of http method */
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	qse_char_t* method;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	qse_cstr_t name;
 | 
					 | 
				
			||||||
	qse_cstr_t value;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#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
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user