added more header handling code for http
This commit is contained in:
		| @ -25,7 +25,8 @@ enum qse_http_errnum_t | ||||
| 	QSE_HTTP_ENOERR, | ||||
| 	QSE_HTTP_ENOMEM, | ||||
| 	QSE_HTTP_EBADREQ, | ||||
| 	QSE_HTTP_EBADHDR | ||||
| 	QSE_HTTP_EBADHDR, | ||||
| 	QSE_HTTP_ETRAENC  /* bad transfer-encoding */ | ||||
| }; | ||||
|  | ||||
| typedef enum qse_http_errnum_t qse_http_errnum_t; | ||||
| @ -56,6 +57,12 @@ struct qse_http_t | ||||
| 			QSE_HTTP_REQ_POST | ||||
| 		} method; | ||||
|  | ||||
| 		struct | ||||
| 		{ | ||||
| 			qse_byte_t* ptr; | ||||
| 			qse_size_t  len; | ||||
| 		} host; | ||||
|  | ||||
| 		struct | ||||
| 		{ | ||||
| 			qse_byte_t* ptr; | ||||
| @ -79,6 +86,14 @@ struct qse_http_t | ||||
| 			qse_htb_t tab; | ||||
| 			void* combined; | ||||
| 		} hdr; | ||||
|  | ||||
| 		/* special attributes derived from the header */ | ||||
| 		struct | ||||
| 		{ | ||||
| 			int chunked;		 | ||||
| 			int content_length; | ||||
| 			int connection_close; | ||||
| 		} attr; | ||||
| 	} req; | ||||
| }; | ||||
|  | ||||
|  | ||||
| @ -347,6 +347,16 @@ static QSE_INLINE void clear_combined_headers (qse_http_t* http) | ||||
| 	http->req.hdr.combined = QSE_NULL; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE void clear_request (qse_http_t* http) | ||||
| { | ||||
| 	/* clear necessary part of the request before  | ||||
| 	 * reading the next request */ | ||||
| 	QSE_MEMSET (&http->req.attr, 0, QSE_SIZEOF(http->req.attr)); | ||||
| 	qse_htb_clear (&http->req.hdr.tab); | ||||
| 	clear_combined_headers (http); | ||||
| 	clear_buffer (http, &http->req.raw); | ||||
| } | ||||
|  | ||||
| #define QSE_HTTP_STATE_REQ  1 | ||||
| #define QSE_HTTP_STATE_HDR  2 | ||||
| #define QSE_HTTP_STATE_POST 3 | ||||
| @ -413,7 +423,7 @@ void qse_http_fini (qse_http_t* http) | ||||
| 	fini_buffer (http, &http->req.raw); | ||||
| } | ||||
|  | ||||
| static qse_byte_t* parse_http_req (qse_http_t* http, qse_byte_t* line) | ||||
| static qse_byte_t* parse_http_reqline (qse_http_t* http, qse_byte_t* line) | ||||
| { | ||||
| 	qse_byte_t* p = line; | ||||
| 	qse_byte_t* tmp; | ||||
| @ -534,7 +544,14 @@ static qse_byte_t* parse_http_req (qse_http_t* http, qse_byte_t* line) | ||||
| 	/* if the line does not end with a new line, it is a bad request */ | ||||
| 	if (*p != QSE_T('\n')) goto badreq; | ||||
|  | ||||
| qse_printf (QSE_T("parse_http_req ....\n")); | ||||
| 	/* adjust Connection: close for HTTP 1.0 or eariler */ | ||||
| 	if (http->req.version.major < 1 ||  | ||||
| 	    (http->req.version.major == 1 && http->req.version.minor == 0)) | ||||
| 	{ | ||||
| 		http->req.attr.connection_close = 1; | ||||
| 	} | ||||
|  | ||||
| qse_printf (QSE_T("parse_http_reqline ....\n")); | ||||
| 	return ++p; | ||||
|  | ||||
| badreq: | ||||
| @ -570,38 +587,99 @@ static QSE_INLINE int compare_octets ( | ||||
| 	return (s2 < end2)? -1: 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static QSE_INLINE void capture_content_length ( | ||||
| static QSE_INLINE int capture_connection ( | ||||
| 	qse_http_t* http, qse_htb_pair_t* pair) | ||||
| { | ||||
| 	qse_printf (QSE_T("content length %.*S\n"), (int)pair->vlen, pair->vptr); | ||||
| 	int n; | ||||
|  | ||||
| 	n = compare_octets (pair->vptr, pair->vlen, "close", 5); | ||||
| 	if (n == 0) | ||||
| 	{ | ||||
| 		http->req.attr.connection_close = 1; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* don't care about other values */ | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE void capture_content_type ( | ||||
| static QSE_INLINE int capture_content_length ( | ||||
| 	qse_http_t* http, qse_htb_pair_t* pair) | ||||
| { | ||||
| 	qse_printf (QSE_T("content type %.*S\n"), (int)pair->vlen, pair->vptr); | ||||
| 	qse_size_t len = 0, off = 0, tmp; | ||||
| 	const qse_byte_t* ptr = pair->vptr; | ||||
|  | ||||
| 	while (off < pair->vlen) | ||||
| 	{ | ||||
| 		int num = digit_to_num (ptr[off]); | ||||
| 		if (num <= -1) | ||||
| 		{ | ||||
| 			/* the length contains a non-digit */ | ||||
| 			http->errnum = QSE_HTTP_EBADREQ; | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		tmp = len * 10 + num; | ||||
| 		if (tmp < len) | ||||
| 		{ | ||||
| 			/* the length has overflown */ | ||||
| 			http->errnum = QSE_HTTP_EBADREQ; | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		len = tmp; | ||||
| 		off++; | ||||
| 	} | ||||
|  | ||||
| 	if (off == 0) | ||||
| 	{ | ||||
| 		/* no length was provided */ | ||||
| 		http->errnum = QSE_HTTP_EBADREQ; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	http->req.attr.content_length = len; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE void capture_host ( | ||||
| static QSE_INLINE int capture_host ( | ||||
| 	qse_http_t* http, qse_htb_pair_t* pair) | ||||
| { | ||||
| 	qse_printf (QSE_T("host capture => %.*S\n"), (int)pair->vlen, pair->vptr); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE void capture_key_header ( | ||||
| static QSE_INLINE int capture_transfer_encoding ( | ||||
| 	qse_http_t* http, qse_htb_pair_t* pair) | ||||
| { | ||||
| 	int n; | ||||
|  | ||||
| 	n = compare_octets (pair->vptr, pair->vlen, "chunked", 7); | ||||
| 	if (n == 0) | ||||
| 	{ | ||||
| 		http->req.attr.chunked = 1; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* other encoding type not supported yet */ | ||||
| 	http->errnum = QSE_HTTP_EBADREQ; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE int capture_key_header ( | ||||
| 	qse_http_t* http, qse_htb_pair_t* pair) | ||||
| { | ||||
| 	static struct | ||||
| 	{ | ||||
| 		const qse_byte_t* ptr; | ||||
| 		qse_size_t        len; | ||||
| 		void (*handler) (qse_http_t*, qse_htb_pair_t*); | ||||
| 		int (*handler) (qse_http_t*, qse_htb_pair_t*); | ||||
| 	} hdrtab[] =  | ||||
| 	{ | ||||
| 		{ "Connection",         10, capture_connection }, | ||||
| 		{ "Content-Length",     14, capture_content_length }, | ||||
| 		{ "Content-Type",   12, capture_content_type }, | ||||
| 		{ "Host",           4,  capture_host  } | ||||
| 		{ "Host",               4,  capture_host }, | ||||
| 		{ "Transfer-Encoding",  17, capture_transfer_encoding  } | ||||
| 	}; | ||||
|  | ||||
| 	int n; | ||||
| @ -620,11 +698,14 @@ static QSE_INLINE void capture_key_header ( | ||||
| 		if (n == 0) | ||||
| 		{ | ||||
| 			/* bingo! */ | ||||
| 			hdrtab[mid].handler (http, pair); | ||||
| 			break; | ||||
| 			return hdrtab[mid].handler (http, pair); | ||||
| 		} | ||||
|  | ||||
| 		if (n > 0) { base = mid + 1; count--; } | ||||
| 	} | ||||
|  | ||||
| 	/* No callback functions were interested in this header field. */ | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct hdr_cbserter_ctx_t | ||||
| @ -648,7 +729,16 @@ static qse_htb_pair_t* hdr_cbserter ( | ||||
| 		p = qse_htb_allocpair (htb, kptr, klen, tx->vptr, tx->vlen); | ||||
|  | ||||
| 		if (p == QSE_NULL) tx->http->errnum = QSE_HTTP_ENOMEM; | ||||
| 		else capture_key_header (tx->http, p); | ||||
| 		else  | ||||
| 		{ | ||||
| 			if (capture_key_header (tx->http, p) <= -1) | ||||
| 			{ | ||||
| 				/* Destroy the pair created here | ||||
| 				 * as it is not added to the hash table yet */ | ||||
| 				qse_htb_freepair (htb, p); | ||||
| 				p = QSE_NULL; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return p; | ||||
| 	} | ||||
| @ -722,7 +812,7 @@ Change it to doubly linked for this? | ||||
| 		cmb->next = tx->http->req.hdr.combined; | ||||
| 		tx->http->req.hdr.combined = cmb; | ||||
|  | ||||
| 		capture_key_header (tx->http, pair); | ||||
| 		if (capture_key_header (tx->http, pair) <= -1) return QSE_NULL; | ||||
|  | ||||
| 		return pair; | ||||
| 	} | ||||
| @ -794,7 +884,8 @@ qse_byte_t* parse_http_header (qse_http_t* http, qse_byte_t* line) | ||||
| 	} | ||||
| 	*last = '\0'; | ||||
|  | ||||
| { | ||||
| 	/* insert the new field to the header table */ | ||||
| 	{ | ||||
| 		struct hdr_cbserter_ctx_t ctx; | ||||
|  | ||||
| 		ctx.http = http; | ||||
| @ -806,10 +897,11 @@ qse_byte_t* parse_http_header (qse_http_t* http, qse_byte_t* line) | ||||
| 			&http->req.hdr.tab, name.ptr, name.len,  | ||||
| 			hdr_cbserter, &ctx) == QSE_NULL) | ||||
| 		{ | ||||
| 		if (http->errnum == QSE_HTTP_ENOERR) http->errnum = QSE_HTTP_ENOMEM; | ||||
| 			if (http->errnum == QSE_HTTP_ENOERR)  | ||||
| 				http->errnum = QSE_HTTP_ENOMEM; | ||||
| 			return QSE_NULL; | ||||
| 		} | ||||
| } | ||||
| 	} | ||||
|  | ||||
| 	return p; | ||||
|  | ||||
| @ -868,7 +960,7 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len) | ||||
|  | ||||
| 				while (is_whspace_octet(*p)) p++; | ||||
| 				QSE_ASSERT (*p != '\0'); | ||||
| 				p = parse_http_req (http, p); | ||||
| 				p = parse_http_reqline (http, p); | ||||
| 				if (p == QSE_NULL) return -1; | ||||
| 			 | ||||
| 				do | ||||
| @ -876,17 +968,22 @@ int qse_http_feed (qse_http_t* http, const qse_byte_t* ptr, qse_size_t len) | ||||
| 					while (is_whspace_octet(*p)) p++; | ||||
| 					if (*p == '\0') break; | ||||
|  | ||||
| 					/* TODO: return error if protocol is 0.9. | ||||
| 					 * HTTP/0.9 must not get headers... */ | ||||
|  | ||||
| 					p = parse_http_header (http, p); | ||||
| 					if (p == QSE_NULL) return -1; | ||||
| 				} | ||||
| 				while (1); | ||||
| 					 | ||||
| 				if (http->req.attr.content_length > 0) | ||||
| 				{ | ||||
| qse_printf (QSE_T("content->length.. %d\n"), (int)http->req.attr.content_length); | ||||
| 				} | ||||
| 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); | ||||
| 				clear_request (http); | ||||
| 				req = ptr; /* let ptr point to the next character to '\n' */ | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user