From b7c4acf78e7539cc70599976a4f4e7d005fbafab Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Thu, 11 Nov 2010 08:28:38 +0000 Subject: [PATCH] added more header handling code for http --- qse/include/qse/utl/http.h | 17 +++- qse/lib/utl/http.c | 169 +++++++++++++++++++++++++++++-------- 2 files changed, 149 insertions(+), 37 deletions(-) diff --git a/qse/include/qse/utl/http.h b/qse/include/qse/utl/http.h index 1df4aed7..be8b210a 100644 --- a/qse/include/qse/utl/http.h +++ b/qse/include/qse/utl/http.h @@ -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; }; diff --git a/qse/lib/utl/http.c b/qse/lib/utl/http.c index d9a9334e..b812dcb2 100644 --- a/qse/lib/utl/http.c +++ b/qse/lib/utl/http.c @@ -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[] = { - { "Content-Length", 14, capture_content_length }, - { "Content-Type", 12, capture_content_type }, - { "Host", 4, capture_host } + { "Connection", 10, capture_connection }, + { "Content-Length", 14, capture_content_length }, + { "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,22 +884,24 @@ qse_byte_t* parse_http_header (qse_http_t* http, qse_byte_t* line) } *last = '\0'; -{ - struct hdr_cbserter_ctx_t ctx; - - ctx.http = http; - ctx.vptr = value.ptr; - ctx.vlen = value.len; - - http->errnum = QSE_HTTP_ENOERR; - if (qse_htb_cbsert ( - &http->req.hdr.tab, name.ptr, name.len, - hdr_cbserter, &ctx) == QSE_NULL) + /* insert the new field to the header table */ { - if (http->errnum == QSE_HTTP_ENOERR) http->errnum = QSE_HTTP_ENOMEM; - return QSE_NULL; + struct hdr_cbserter_ctx_t ctx; + + ctx.http = http; + ctx.vptr = value.ptr; + ctx.vlen = value.len; + + http->errnum = QSE_HTTP_ENOERR; + if (qse_htb_cbsert ( + &http->req.hdr.tab, name.ptr, name.len, + hdr_cbserter, &ctx) == QSE_NULL) + { + 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' */ } }