diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index 69bf2da8..1458013f 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -39,7 +39,9 @@ typedef enum qse_htrd_errnum_t qse_htrd_errnum_t; enum qse_htrd_option_t { - QSE_HTRD_LEADINGEMPTYLINES = (1 << 0) + QSE_HTRD_LEADINGEMPTYLINES = (1 << 0), + QSE_HTRD_REQUEST = (1 << 1), + QSE_HTRD_RESPONSE = (1 << 2) }; typedef enum qse_htrd_option_t qse_htrd_option_t; diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index ff464fc4..16b11de3 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -143,6 +143,10 @@ int qse_htre_setstrfromxstr ( const qse_mxstr_t* xstr ); +const qse_mchar_t* qse_htre_gethdrval ( + qse_htre_t* re, const qse_mchar_t* key +); + #ifdef __cplusplus } #endif diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index 12f69840..b6520596 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -15,7 +15,7 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with QSE. If not, see . + License along with QSE. If not, see . */ #include @@ -84,12 +84,12 @@ static QSE_INLINE int xdigit_to_num (qse_htoc_t c) } static QSE_INLINE int push_to_buffer ( - qse_htrd_t* http, qse_htob_t* octb, + qse_htrd_t* htrd, qse_htob_t* octb, const qse_htoc_t* ptr, qse_size_t len) { if (qse_mbs_ncat (octb, ptr, len) == (qse_size_t)-1) { - http->errnum = QSE_HTRD_ENOMEM; + htrd->errnum = QSE_HTRD_ENOMEM; return -1; } return 0; @@ -100,31 +100,31 @@ struct hdr_cmb_t struct hdr_cmb_t* next; }; -static QSE_INLINE void clear_combined_headers (qse_htrd_t* http) +static QSE_INLINE void clear_combined_headers (qse_htrd_t* htrd) { - struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)http->fed.chl; + struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)htrd->fed.chl; while (cmb) { struct hdr_cmb_t* next = cmb->next; - QSE_MMGR_FREE (http->mmgr, cmb); + QSE_MMGR_FREE (htrd->mmgr, cmb); cmb = next; } - http->fed.chl = QSE_NULL; + htrd->fed.chl = QSE_NULL; } -static QSE_INLINE void clear_feed (qse_htrd_t* http) +static QSE_INLINE void clear_feed (qse_htrd_t* htrd) { /* clear necessary part of the request/response before * reading the next request/response */ - qse_htre_clear (&http->re); + qse_htre_clear (&htrd->re); - qse_mbs_clear (&http->fed.b.tra); - qse_mbs_clear (&http->fed.b.raw); - clear_combined_headers (http); + qse_mbs_clear (&htrd->fed.b.tra); + qse_mbs_clear (&htrd->fed.b.raw); + clear_combined_headers (htrd); - QSE_MEMSET (&http->fed.s, 0, QSE_SIZEOF(http->fed.s)); + QSE_MEMSET (&htrd->fed.s, 0, QSE_SIZEOF(htrd->fed.s)); } #define QSE_HTRD_STATE_REQ 1 @@ -133,7 +133,7 @@ static QSE_INLINE void clear_feed (qse_htrd_t* http) qse_htrd_t* qse_htrd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) { - qse_htrd_t* http; + qse_htrd_t* htrd; if (mmgr == QSE_NULL) { @@ -145,60 +145,61 @@ qse_htrd_t* qse_htrd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) if (mmgr == QSE_NULL) return QSE_NULL; } - http = (qse_htrd_t*) QSE_MMGR_ALLOC ( + htrd = (qse_htrd_t*) QSE_MMGR_ALLOC ( mmgr, QSE_SIZEOF(qse_htrd_t) + xtnsize ); - if (http == QSE_NULL) return QSE_NULL; + if (htrd == QSE_NULL) return QSE_NULL; - if (qse_htrd_init (http, mmgr) == QSE_NULL) + if (qse_htrd_init (htrd, mmgr) == QSE_NULL) { - QSE_MMGR_FREE (http->mmgr, http); + QSE_MMGR_FREE (htrd->mmgr, htrd); return QSE_NULL; } - return http; + return htrd; } -void qse_htrd_close (qse_htrd_t* http) +void qse_htrd_close (qse_htrd_t* htrd) { - qse_htrd_fini (http); - QSE_MMGR_FREE (http->mmgr, http); + qse_htrd_fini (htrd); + QSE_MMGR_FREE (htrd->mmgr, htrd); } -qse_htrd_t* qse_htrd_init (qse_htrd_t* http, qse_mmgr_t* mmgr) +qse_htrd_t* qse_htrd_init (qse_htrd_t* htrd, qse_mmgr_t* mmgr) { if (mmgr == QSE_NULL) mmgr = QSE_MMGR_GETDFL(); - QSE_MEMSET (http, 0, QSE_SIZEOF(*http)); - http->mmgr = mmgr; + QSE_MEMSET (htrd, 0, QSE_SIZEOF(*htrd)); + htrd->mmgr = mmgr; + htrd->option = QSE_HTRD_REQUEST | QSE_HTRD_RESPONSE; - qse_mbs_init (&http->tmp.qparam, http->mmgr, 0); - qse_mbs_init (&http->fed.b.raw, http->mmgr, 0); - qse_mbs_init (&http->fed.b.tra, http->mmgr, 0); + qse_mbs_init (&htrd->tmp.qparam, htrd->mmgr, 0); + qse_mbs_init (&htrd->fed.b.raw, htrd->mmgr, 0); + qse_mbs_init (&htrd->fed.b.tra, htrd->mmgr, 0); - if (qse_htre_init (&http->re, mmgr) == QSE_NULL) + if (qse_htre_init (&htrd->re, mmgr) == QSE_NULL) { - qse_mbs_fini (&http->fed.b.tra); - qse_mbs_fini (&http->fed.b.raw); - qse_mbs_fini (&http->tmp.qparam); + qse_mbs_fini (&htrd->fed.b.tra); + qse_mbs_fini (&htrd->fed.b.raw); + qse_mbs_fini (&htrd->tmp.qparam); return QSE_NULL; } - return http; + return htrd; } -void qse_htrd_fini (qse_htrd_t* http) +void qse_htrd_fini (qse_htrd_t* htrd) { - qse_htre_fini (&http->re); + qse_htre_fini (&htrd->re); - clear_combined_headers (http); - qse_mbs_fini (&http->fed.b.tra); - qse_mbs_fini (&http->fed.b.raw); - qse_mbs_fini (&http->tmp.qparam); + clear_combined_headers (htrd); + qse_mbs_fini (&htrd->fed.b.tra); + qse_mbs_fini (&htrd->fed.b.raw); + qse_mbs_fini (&htrd->tmp.qparam); } static qse_htoc_t* parse_initial_line ( - qse_htrd_t* http, qse_htoc_t* line) + qse_htrd_t* htrd, qse_htoc_t* line) { qse_htoc_t* p = line; qse_mcstr_t tmp; @@ -217,19 +218,21 @@ static qse_htoc_t* parse_initial_line ( do { p++; } while (is_upalpha_octet(*p)); tmp.len = p - tmp.ptr; - http->retype = QSE_HTRD_RETYPE_Q; - if (qse_gethttpmethodtypefromstr (&tmp, &mtype) >= 0) + htrd->retype = QSE_HTRD_RETYPE_Q; + if ((htrd->option & QSE_HTRD_REQUEST) && + qse_gethttpmethodtypefromstr (&tmp, &mtype) >= 0) { - qse_htre_setqmethod (&http->re, mtype); + qse_htre_setqmethod (&htrd->re, mtype); } - else if (qse_mbsxcmp (tmp.ptr, tmp.len, "HTTP") == 0) + else if ((htrd->option & QSE_HTRD_RESPONSE) && + qse_mbsxcmp (tmp.ptr, tmp.len, "HTTP") == 0) { /* it begins with HTTP. it may be a response */ - http->retype = QSE_HTRD_RETYPE_S; + htrd->retype = QSE_HTRD_RETYPE_S; } else goto badre; - if (http->retype == QSE_HTRD_RETYPE_S) + if (htrd->retype == QSE_HTRD_RETYPE_S) { int n, status; @@ -239,15 +242,15 @@ static qse_htoc_t* parse_initial_line ( int w = digit_to_num(p[3]); if (q >= 0 && w >= 0) { - http->re.version.major = q; - http->re.version.minor = w; + htrd->re.version.major = q; + htrd->re.version.minor = w; p += 4; } else goto badre; } else goto badre; - /* http version must be followed by space */ + /* htrd version must be followed by space */ if (!is_space_octet(*p)) goto badre; /* skip spaces */ @@ -267,7 +270,7 @@ static qse_htoc_t* parse_initial_line ( /* status code must be followed by space */ if (!is_space_octet(*p)) goto badre; - qse_htre_setsstatus (&http->re, status); + qse_htre_setsstatus (&htrd->re, status); /* skip spaces */ do p++; while (is_space_octet(*p)); @@ -276,13 +279,13 @@ static qse_htoc_t* parse_initial_line ( while (*p != '\0' && *p != '\n') p++; tmp.len = p - tmp.ptr; - if (qse_htre_setsmessagefromcstr (&http->re, &tmp) <= -1) goto outofmem; + if (qse_htre_setsmessagefromcstr (&htrd->re, &tmp) <= -1) goto outofmem; /* adjust Connection: close for HTTP 1.0 or eariler */ - if (http->re.version.major < 1 || - (http->re.version.major == 1 && http->re.version.minor == 0)) + if (htrd->re.version.major < 1 || + (htrd->re.version.major == 1 && htrd->re.version.minor == 0)) { - http->re.attr.connection_close = 1; + htrd->re.attr.connection_close = 1; } } else @@ -351,16 +354,16 @@ static qse_htoc_t* parse_initial_line ( if (param.ptr) { param.len = out - param.ptr; - if (qse_htre_setqparamfromcstr (&http->re, ¶m) <= -1) goto outofmem; + if (qse_htre_setqparamfromcstr (&htrd->re, ¶m) <= -1) goto outofmem; } else tmp.len = out - tmp.ptr; - if (qse_htre_setqpathfromcstr (&http->re, &tmp) <= -1) goto outofmem; + if (qse_htre_setqpathfromcstr (&htrd->re, &tmp) <= -1) goto outofmem; /* skip spaces after the url part */ do { p++; } while (is_space_octet(*p)); - /* check http version */ + /* check htrd version */ if ((p[0] == 'H' || p[0] == 'h') && (p[1] == 'T' || p[1] == 't') && (p[2] == 'T' || p[2] == 't') && @@ -371,8 +374,8 @@ static qse_htoc_t* parse_initial_line ( int w = digit_to_num(p[7]); if (q >= 0 && w >= 0) { - http->re.version.major = q; - http->re.version.minor = w; + htrd->re.version.major = q; + htrd->re.version.minor = w; p += 8; } else goto badre; @@ -383,10 +386,10 @@ static qse_htoc_t* parse_initial_line ( while (is_space_octet(*p)) p++; /* adjust Connection: close for HTTP 1.0 or eariler */ - if (http->re.version.major < 1 || - (http->re.version.major == 1 && http->re.version.minor == 0)) + if (htrd->re.version.major < 1 || + (htrd->re.version.major == 1 && htrd->re.version.minor == 0)) { - http->re.attr.connection_close = 1; + htrd->re.attr.connection_close = 1; } } @@ -396,38 +399,38 @@ static qse_htoc_t* parse_initial_line ( return ++p; badre: - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return QSE_NULL; outofmem: - http->errnum = QSE_HTRD_ENOMEM; + htrd->errnum = QSE_HTRD_ENOMEM; return QSE_NULL; } -void qse_htrd_clear (qse_htrd_t* http) +void qse_htrd_clear (qse_htrd_t* htrd) { - clear_feed (http); + clear_feed (htrd); } -int qse_htrd_getoption (qse_htrd_t* http) +int qse_htrd_getoption (qse_htrd_t* htrd) { - return http->option; + return htrd->option; } -void qse_htrd_setoption (qse_htrd_t* http, int opts) +void qse_htrd_setoption (qse_htrd_t* htrd, int opts) { - http->option = opts; + htrd->option = opts; } -const qse_htrd_recbs_t* qse_htrd_getrecbs (qse_htrd_t* http) +const qse_htrd_recbs_t* qse_htrd_getrecbs (qse_htrd_t* htrd) { - return http->recbs; + return htrd->recbs; } -void qse_htrd_setrecbs (qse_htrd_t* http, const qse_htrd_recbs_t* recbs) +void qse_htrd_setrecbs (qse_htrd_t* htrd, const qse_htrd_recbs_t* recbs) { - http->recbs = recbs; + htrd->recbs = recbs; } #define octet_tolower(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) | 0x20) : (c)) @@ -458,21 +461,21 @@ static QSE_INLINE int compare_octets ( } static QSE_INLINE int capture_connection ( - qse_htrd_t* http, qse_htb_pair_t* pair) + qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "close", 5); if (n == 0) { - http->re.attr.connection_close = 1; + htrd->re.attr.connection_close = 1; return 0; } n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "Keep-Alive", 10); if (n == 0) { - http->re.attr.connection_close = 0; + htrd->re.attr.connection_close = 0; return 0; } @@ -481,7 +484,7 @@ static QSE_INLINE int capture_connection ( } static QSE_INLINE int capture_content_length ( - qse_htrd_t* http, qse_htb_pair_t* pair) + qse_htrd_t* htrd, qse_htb_pair_t* pair) { qse_size_t len = 0, off = 0, tmp; const qse_htoc_t* ptr = QSE_HTB_VPTR(pair); @@ -492,7 +495,7 @@ static QSE_INLINE int capture_content_length ( if (num <= -1) { /* the length contains a non-digit */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } @@ -500,7 +503,7 @@ static QSE_INLINE int capture_content_length ( if (tmp < len) { /* the length has overflown */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } @@ -511,24 +514,24 @@ static QSE_INLINE int capture_content_length ( if (off == 0) { /* no length was provided */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } - if (http->re.attr.chunked && len > 0) + if (htrd->re.attr.chunked && len > 0) { /* content-length is greater than 0 * while transfer-encoding: chunked is specified. */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } - http->re.attr.content_length = len; + htrd->re.attr.content_length = len; return 0; } static QSE_INLINE int capture_expect ( - qse_htrd_t* http, qse_htb_pair_t* pair) + qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; @@ -536,7 +539,7 @@ static QSE_INLINE int capture_expect ( if (n == 0) { - http->re.attr.expect_continue = 1; + htrd->re.attr.expect_continue = 1; return 0; } @@ -545,32 +548,32 @@ static QSE_INLINE int capture_expect ( } static QSE_INLINE int capture_transfer_encoding ( - qse_htrd_t* http, qse_htb_pair_t* pair) + qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "chunked", 7); if (n == 0) { - if (http->re.attr.content_length > 0) + if (htrd->re.attr.content_length > 0) { /* content-length is greater than 0 * while transfer-encoding: chunked is specified. */ goto badre; } - http->re.attr.chunked = 1; + htrd->re.attr.chunked = 1; return 0; } /* other encoding type not supported yet */ badre: - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } static QSE_INLINE int capture_key_header ( - qse_htrd_t* http, qse_htb_pair_t* pair) + qse_htrd_t* htrd, qse_htb_pair_t* pair) { static struct { @@ -601,7 +604,7 @@ static QSE_INLINE int capture_key_header ( if (n == 0) { /* bingo! */ - return hdrtab[mid].handler (http, pair); + return hdrtab[mid].handler (htrd, pair); } if (n > 0) { base = mid + 1; count--; } @@ -613,7 +616,7 @@ static QSE_INLINE int capture_key_header ( struct hdr_cbserter_ctx_t { - qse_htrd_t* http; + qse_htrd_t* htrd; void* vptr; qse_size_t vlen; }; @@ -631,10 +634,10 @@ 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_HTRD_ENOMEM; + if (p == QSE_NULL) tx->htrd->errnum = QSE_HTRD_ENOMEM; else { - if (capture_key_header (tx->http, p) <= -1) + if (capture_key_header (tx->htrd, p) <= -1) { /* Destroy the pair created here * as it is not added to the hash table yet */ @@ -660,19 +663,19 @@ static qse_htb_pair_t* hdr_cbserter ( * encounters the same key. memory is wasted and performance * is sacrificed. - * hopefully, a http header does not include a lot of + * hopefully, a htrd header does not include a lot of * duplicate fields and this implmentation can afford wastage. */ /* allocate a block to combine the existing value and the new value */ cmb = (struct hdr_cmb_t*) QSE_MMGR_ALLOC ( - tx->http->mmgr, + tx->htrd->mmgr, QSE_SIZEOF(*cmb) + QSE_SIZEOF(qse_htoc_t) * (QSE_HTB_VLEN(pair) + 1 + tx->vlen + 1) ); if (cmb == QSE_NULL) { - tx->http->errnum = QSE_HTRD_ENOMEM; + tx->htrd->errnum = QSE_HTRD_ENOMEM; return QSE_NULL; } @@ -694,14 +697,14 @@ Not easy to unlink when using a singly linked list... Change it to doubly linked for this? /* let's destroy the old buffer at least */ - if (!(ptr >= tx->http->fed.b.raw.data && ptr < - &tx->http->fed.b.raw.data[tx->http->fed.b.raw.size])) + if (!(ptr >= tx->htrd->fed.b.raw.data && ptr < + &tx->htrd->fed.b.raw.data[tx->htrd->fed.b.raw.size])) { /* NOTE the range check in 'if' assumes that raw.data is never * relocated for resizing */ QSE_MMGR_FREE ( - tx->http->mmgr, + tx->htrd->mmgr, ((struct hdr_cmb_t*)QSE_HTB_VPTR(pair)) - 1 ); } @@ -712,16 +715,16 @@ Change it to doubly linked for this? QSE_HTB_VLEN(pair) = len; /* link the new combined value block */ - cmb->next = tx->http->fed.chl; - tx->http->fed.chl = cmb; + cmb->next = tx->htrd->fed.chl; + tx->htrd->fed.chl = cmb; - if (capture_key_header (tx->http, pair) <= -1) return QSE_NULL; + if (capture_key_header (tx->htrd, pair) <= -1) return QSE_NULL; return pair; } } -qse_htoc_t* parse_header_fields (qse_htrd_t* http, qse_htoc_t* line) +qse_htoc_t* parse_header_fields (qse_htrd_t* htrd, qse_htoc_t* line) { qse_htoc_t* p = line, * last; struct @@ -791,17 +794,17 @@ qse_htoc_t* parse_header_fields (qse_htrd_t* http, qse_htoc_t* line) { struct hdr_cbserter_ctx_t ctx; - ctx.http = http; + ctx.htrd = htrd; ctx.vptr = value.ptr; ctx.vlen = value.len; - http->errnum = QSE_HTRD_ENOERR; + htrd->errnum = QSE_HTRD_ENOERR; if (qse_htb_cbsert ( - &http->re.hdrtab, name.ptr, name.len, + &htrd->re.hdrtab, name.ptr, name.len, hdr_cbserter, &ctx) == QSE_NULL) { - if (http->errnum == QSE_HTRD_ENOERR) - http->errnum = QSE_HTRD_ENOMEM; + if (htrd->errnum == QSE_HTRD_ENOERR) + htrd->errnum = QSE_HTRD_ENOMEM; return QSE_NULL; } } @@ -809,24 +812,24 @@ qse_htoc_t* parse_header_fields (qse_htrd_t* http, qse_htoc_t* line) return p; badhdr: - http->errnum = QSE_HTRD_EBADHDR; + htrd->errnum = QSE_HTRD_EBADHDR; return QSE_NULL; } static QSE_INLINE int parse_initial_line_and_headers ( - qse_htrd_t* http, const qse_htoc_t* req, qse_size_t rlen) + qse_htrd_t* htrd, const qse_htoc_t* req, qse_size_t rlen) { qse_htoc_t* p; /* add the actual request */ - if (push_to_buffer (http, &http->fed.b.raw, req, rlen) <= -1) return -1; + if (push_to_buffer (htrd, &htrd->fed.b.raw, req, rlen) <= -1) return -1; /* add the terminating null for easier parsing */ - if (push_to_buffer (http, &http->fed.b.raw, &NUL, 1) <= -1) return -1; + if (push_to_buffer (htrd, &htrd->fed.b.raw, &NUL, 1) <= -1) return -1; - p = QSE_MBS_PTR(&http->fed.b.raw); + p = QSE_MBS_PTR(&htrd->fed.b.raw); - if (http->option & QSE_HTRD_LEADINGEMPTYLINES) + if (htrd->option & QSE_HTRD_LEADINGEMPTYLINES) while (is_whspace_octet(*p)) p++; else while (is_space_octet(*p)) p++; @@ -834,7 +837,7 @@ static QSE_INLINE int parse_initial_line_and_headers ( QSE_ASSERT (*p != '\0'); /* parse the initial line */ - p = parse_initial_line (http, p); + p = parse_initial_line (htrd, p); if (p == QSE_NULL) return -1; /* parse header fields */ @@ -846,7 +849,7 @@ static QSE_INLINE int parse_initial_line_and_headers ( /* TODO: return error if protocol is 0.9. * HTTP/0.9 must not get headers... */ - p = parse_header_fields (http, p); + p = parse_header_fields (htrd, p); if (p == QSE_NULL) return -1; } while (1); @@ -861,15 +864,15 @@ static QSE_INLINE int parse_initial_line_and_headers ( #define GET_CHUNK_CRLF 3 #define GET_CHUNK_TRAILERS 4 -static const qse_htoc_t* getchunklen (qse_htrd_t* http, const qse_htoc_t* ptr, qse_size_t len) +static const qse_htoc_t* getchunklen (qse_htrd_t* htrd, const qse_htoc_t* ptr, qse_size_t len) { const qse_htoc_t* end = ptr + len; /* this function must be called in the GET_CHUNK_LEN context */ - QSE_ASSERT (http->fed.s.chunk.phase == GET_CHUNK_LEN); + QSE_ASSERT (htrd->fed.s.chunk.phase == GET_CHUNK_LEN); //qse_printf (QSE_T("CALLING getchunklen [%d]\n"), *ptr); - if (http->fed.s.chunk.count <= 0) + if (htrd->fed.s.chunk.count <= 0) { /* skip leading spaces if the first character of * the chunk length has not been read yet */ @@ -881,8 +884,8 @@ static const qse_htoc_t* getchunklen (qse_htrd_t* http, const qse_htoc_t* ptr, q int n = xdigit_to_num (*ptr); if (n <= -1) break; - http->fed.s.chunk.len = http->fed.s.chunk.len * 16 + n; - http->fed.s.chunk.count++; + htrd->fed.s.chunk.len = htrd->fed.s.chunk.len * 16 + n; + htrd->fed.s.chunk.count++; ptr++; } @@ -895,33 +898,33 @@ static const qse_htoc_t* getchunklen (qse_htrd_t* http, const qse_htoc_t* ptr, q { /* the chunk length line ended properly */ - if (http->fed.s.chunk.count <= 0) + if (htrd->fed.s.chunk.count <= 0) { /* empty line - no more chunk */ //qse_printf (QSE_T("empty line chunk done....\n")); - http->fed.s.chunk.phase = GET_CHUNK_DONE; + htrd->fed.s.chunk.phase = GET_CHUNK_DONE; } - else if (http->fed.s.chunk.len <= 0) + else if (htrd->fed.s.chunk.len <= 0) { /* length explicity specified to 0 get trailing headers .... */ - http->fed.s.chunk.phase = GET_CHUNK_TRAILERS; + htrd->fed.s.chunk.phase = GET_CHUNK_TRAILERS; //qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n")); } else { /* ready to read the chunk data... */ - http->fed.s.chunk.phase = GET_CHUNK_DATA; + htrd->fed.s.chunk.phase = GET_CHUNK_DATA; //qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n")); } - http->fed.s.need = http->fed.s.chunk.len; + htrd->fed.s.need = htrd->fed.s.chunk.len; ptr++; } else { //qse_printf (QSE_T("XXXXXXXXXXXXXXXXXxxx [%c]\n"), *ptr); - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return QSE_NULL; } } @@ -930,7 +933,7 @@ static const qse_htoc_t* getchunklen (qse_htrd_t* http, const qse_htoc_t* ptr, q } static const qse_htoc_t* get_trailing_headers ( - qse_htrd_t* http, const qse_htoc_t* req, const qse_htoc_t* end) + qse_htrd_t* htrd, const qse_htoc_t* req, const qse_htoc_t* end) { const qse_htoc_t* ptr = req; @@ -943,30 +946,30 @@ static const qse_htoc_t* get_trailing_headers ( case '\0': /* guarantee that the request does not contain a null * character */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return QSE_NULL; case '\n': - if (http->fed.s.crlf <= 1) + if (htrd->fed.s.crlf <= 1) { - http->fed.s.crlf = 2; + htrd->fed.s.crlf = 2; break; } else { qse_htoc_t* p; - QSE_ASSERT (http->fed.s.crlf <= 3); - http->fed.s.crlf = 0; + QSE_ASSERT (htrd->fed.s.crlf <= 3); + htrd->fed.s.crlf = 0; if (push_to_buffer ( - http, &http->fed.b.tra, req, ptr - req) <= -1) + htrd, &htrd->fed.b.tra, req, ptr - req) <= -1) return QSE_NULL; if (push_to_buffer ( - http, &http->fed.b.tra, &NUL, 1) <= -1) + htrd, &htrd->fed.b.tra, &NUL, 1) <= -1) return QSE_NULL; - p = QSE_MBS_PTR(&http->fed.b.tra); + p = QSE_MBS_PTR(&htrd->fed.b.tra); do { @@ -976,28 +979,28 @@ static const qse_htoc_t* get_trailing_headers ( /* TODO: return error if protocol is 0.9. * HTTP/0.9 must not get headers... */ - p = parse_header_fields (http, p); + p = parse_header_fields (htrd, p); if (p == QSE_NULL) return QSE_NULL; } while (1); - http->fed.s.chunk.phase = GET_CHUNK_DONE; + htrd->fed.s.chunk.phase = GET_CHUNK_DONE; goto done; } case '\r': - if (http->fed.s.crlf == 0 || http->fed.s.crlf == 2) - http->fed.s.crlf++; - else http->fed.s.crlf = 1; + if (htrd->fed.s.crlf == 0 || htrd->fed.s.crlf == 2) + htrd->fed.s.crlf++; + else htrd->fed.s.crlf = 1; break; default: /* mark that neither CR nor LF was seen */ - http->fed.s.crlf = 0; + htrd->fed.s.crlf = 0; } } - if (push_to_buffer (http, &http->fed.b.tra, req, ptr - req) <= -1) + if (push_to_buffer (htrd, &htrd->fed.b.tra, req, ptr - req) <= -1) return QSE_NULL; done: @@ -1005,27 +1008,27 @@ done: } /* feed the percent encoded string */ -int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) +int qse_htrd_feed (qse_htrd_t* htrd, const qse_htoc_t* req, qse_size_t len) { const qse_htoc_t* end = req + len; const qse_htoc_t* ptr = req; /* does this goto drop code maintainability? */ - if (http->fed.s.need > 0) + if (htrd->fed.s.need > 0) { - /* we're in need of as many octets as http->fed.s.need + /* we're in need of as many octets as htrd->fed.s.need * for contents body. make a proper jump to resume * content handling */ goto content_resume; } - switch (http->fed.s.chunk.phase) + switch (htrd->fed.s.chunk.phase) { case GET_CHUNK_LEN: goto dechunk_resume; case GET_CHUNK_DATA: - /* this won't be reached as http->fed.s.need + /* this won't be reached as htrd->fed.s.need * is greater than 0 if GET_CHUNK_DATA is true */ goto content_resume; @@ -1040,8 +1043,8 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) { register qse_htoc_t b = *ptr++; - if (http->option & QSE_HTRD_LEADINGEMPTYLINES && - http->fed.s.plen <= 0 && is_whspace_octet(b)) + if (htrd->option & QSE_HTRD_LEADINGEMPTYLINES && + htrd->fed.s.plen <= 0 && is_whspace_octet(b)) { /* let's drop leading whitespaces across multiple * lines */ @@ -1054,44 +1057,44 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) case '\0': /* guarantee that the request does not contain * a null character */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; case '\n': { - if (http->fed.s.crlf <= 1) + if (htrd->fed.s.crlf <= 1) { - /* http->fed.s.crlf == 0 + /* htrd->fed.s.crlf == 0 * => CR was not seen - * http->fed.s.crlf == 1 + * htrd->fed.s.crlf == 1 * => CR was seen * whatever the current case is, * mark the first LF is seen here. */ - http->fed.s.crlf = 2; + htrd->fed.s.crlf = 2; } else { - /* http->fed.s.crlf == 2 + /* htrd->fed.s.crlf == 2 * => no 2nd CR before LF - * http->fed.s.crlf == 3 + * htrd->fed.s.crlf == 3 * => 2nd CR before LF */ /* we got a complete request. */ - QSE_ASSERT (http->fed.s.crlf <= 3); + QSE_ASSERT (htrd->fed.s.crlf <= 3); /* reset the crlf state */ - http->fed.s.crlf = 0; + htrd->fed.s.crlf = 0; /* reset the raw request length */ - http->fed.s.plen = 0; + htrd->fed.s.plen = 0; - if (parse_initial_line_and_headers (http, req, ptr - req) <= -1) + if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1) return -1; - if (http->retype == QSE_HTRD_RETYPE_Q && - http->re.attr.expect_continue && - http->recbs->expect_continue && ptr >= end) + if (htrd->retype == QSE_HTRD_RETYPE_Q && + htrd->re.attr.expect_continue && + htrd->recbs->expect_continue && ptr >= end) { int n; @@ -1102,51 +1105,51 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) * not fed here? */ - n = http->recbs->expect_continue (http, &http->re); + n = htrd->recbs->expect_continue (htrd, &htrd->re); if (n <= -1) { - if (http->errnum == QSE_HTRD_ENOERR) - http->errnum = QSE_HTRD_ERECBS; + if (htrd->errnum == QSE_HTRD_ENOERR) + htrd->errnum = QSE_HTRD_ERECBS; /* need to clear request on error? - clear_feed (http); */ + clear_feed (htrd); */ return -1; } /* the expect_continue handler can set discard to non-zero * to force discard the contents body - http->re.discard = 1; + htrd->re.discard = 1; */ } - if (http->re.attr.chunked) + if (htrd->re.attr.chunked) { /* transfer-encoding: chunked */ - QSE_ASSERT (http->re.attr.content_length <= 0); + QSE_ASSERT (htrd->re.attr.content_length <= 0); dechunk_start: - http->fed.s.chunk.phase = GET_CHUNK_LEN; - http->fed.s.chunk.len = 0; - http->fed.s.chunk.count = 0; + htrd->fed.s.chunk.phase = GET_CHUNK_LEN; + htrd->fed.s.chunk.len = 0; + htrd->fed.s.chunk.count = 0; dechunk_resume: - ptr = getchunklen (http, ptr, end - ptr); + ptr = getchunklen (htrd, ptr, end - ptr); if (ptr == QSE_NULL) return -1; - if (http->fed.s.chunk.phase == GET_CHUNK_LEN) + if (htrd->fed.s.chunk.phase == GET_CHUNK_LEN) { /* still in the GET_CHUNK_LEN state. * the length has been partially read. */ goto feedme_more; } - else if (http->fed.s.chunk.phase == GET_CHUNK_TRAILERS) + else if (htrd->fed.s.chunk.phase == GET_CHUNK_TRAILERS) { dechunk_get_trailers: - ptr = get_trailing_headers (http, ptr, end); + ptr = get_trailing_headers (htrd, ptr, end); if (ptr == QSE_NULL) return -1; - if (http->fed.s.chunk.phase == GET_CHUNK_TRAILERS) + if (htrd->fed.s.chunk.phase == GET_CHUNK_TRAILERS) { /* still in the same state. * the trailers have not been processed fully */ @@ -1158,10 +1161,10 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) { /* we need to read as many octets as * Content-Length */ - http->fed.s.need = http->re.attr.content_length; + htrd->fed.s.need = htrd->re.attr.content_length; } - if (http->fed.s.need > 0) + if (htrd->fed.s.need > 0) { /* content-length or chunked data length * specified */ @@ -1171,11 +1174,11 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) content_resume: avail = end - ptr; - if (avail < http->fed.s.need) + if (avail < htrd->fed.s.need) { /* the data is not as large as needed */ - if (push_to_buffer (http, &http->re.content, ptr, avail) <= -1) return -1; - http->fed.s.need -= avail; + if (push_to_buffer (htrd, &htrd->re.content, ptr, avail) <= -1) return -1; + htrd->fed.s.need -= avail; /* we didn't get a complete content yet */ goto feedme_more; } @@ -1183,17 +1186,17 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) { /* we got all or more than needed */ if (push_to_buffer ( - http, &http->re.content, ptr, - http->fed.s.need) <= -1) return -1; - ptr += http->fed.s.need; - http->fed.s.need = 0; + htrd, &htrd->re.content, ptr, + htrd->fed.s.need) <= -1) return -1; + ptr += htrd->fed.s.need; + htrd->fed.s.need = 0; } } - if (http->fed.s.chunk.phase == GET_CHUNK_DATA) + if (htrd->fed.s.chunk.phase == GET_CHUNK_DATA) { - QSE_ASSERT (http->fed.s.need == 0); - http->fed.s.chunk.phase = GET_CHUNK_CRLF; + QSE_ASSERT (htrd->fed.s.need == 0); + htrd->fed.s.chunk.phase = GET_CHUNK_CRLF; dechunk_crlf: while (ptr < end && is_space_octet(*ptr)) ptr++; @@ -1214,16 +1217,16 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) * need to be reset when a jump is made * to dechunk_resume upon the next call */ - http->fed.s.chunk.phase = GET_CHUNK_LEN; - http->fed.s.chunk.len = 0; - http->fed.s.chunk.count = 0; + htrd->fed.s.chunk.phase = GET_CHUNK_LEN; + htrd->fed.s.chunk.len = 0; + htrd->fed.s.chunk.count = 0; goto feedme_more; } else { /* redundant character ... */ - http->errnum = QSE_HTRD_EBADRE; + htrd->errnum = QSE_HTRD_EBADRE; return -1; } } @@ -1234,43 +1237,43 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) } } - if (!http->re.discard) + if (!htrd->re.discard) { int n; - http->errnum = QSE_HTRD_ENOERR; + htrd->errnum = QSE_HTRD_ENOERR; - if (http->retype == QSE_HTRD_RETYPE_S) + if (htrd->retype == QSE_HTRD_RETYPE_S) { QSE_ASSERTX ( - http->recbs->response != QSE_NULL, + htrd->recbs->response != QSE_NULL, "set response callback before feeding" ); - n = http->recbs->response (http, &http->re); + n = htrd->recbs->response (htrd, &htrd->re); } else { QSE_ASSERTX ( - http->recbs->request != QSE_NULL, + htrd->recbs->request != QSE_NULL, "set request callback before feeding" ); - n = http->recbs->request (http, &http->re); + n = htrd->recbs->request (htrd, &htrd->re); } if (n <= -1) { - if (http->errnum == QSE_HTRD_ENOERR) - http->errnum = QSE_HTRD_ERECBS; + if (htrd->errnum == QSE_HTRD_ENOERR) + htrd->errnum = QSE_HTRD_ERECBS; /* need to clear request on error? - clear_feed (http); */ + clear_feed (htrd); */ return -1; } } - clear_feed (http); + clear_feed (htrd); /* let ptr point to the next character to LF or * the optional contents */ @@ -1280,37 +1283,37 @@ int qse_htrd_feed (qse_htrd_t* http, const qse_htoc_t* req, qse_size_t len) } case '\r': - if (http->fed.s.crlf == 0 || http->fed.s.crlf == 2) - http->fed.s.crlf++; - else http->fed.s.crlf = 1; + if (htrd->fed.s.crlf == 0 || htrd->fed.s.crlf == 2) + htrd->fed.s.crlf++; + else htrd->fed.s.crlf = 1; break; default: /* increment length of a request in raw * excluding crlf */ - http->fed.s.plen++; + htrd->fed.s.plen++; /* mark that neither CR nor LF was seen */ - http->fed.s.crlf = 0; + htrd->fed.s.crlf = 0; } } if (ptr > req) { /* enbuffer the incomplete request */ - if (push_to_buffer (http, &http->fed.b.raw, req, ptr - req) <= -1) return -1; + if (push_to_buffer (htrd, &htrd->fed.b.raw, req, ptr - req) <= -1) return -1; } feedme_more: return 0; } -int qse_htrd_scanqparam (qse_htrd_t* http, const qse_mcstr_t* cstr) +int qse_htrd_scanqparam (qse_htrd_t* htrd, const qse_mcstr_t* cstr) { qse_mcstr_t key, val; const qse_htoc_t* p, * end; qse_htoc_t* out; - if (cstr == QSE_NULL) cstr = qse_htre_getqparamcstr(&http->re); + if (cstr == QSE_NULL) cstr = qse_htre_getqparamcstr(&htrd->re); p = cstr->ptr; if (p == QSE_NULL) return 0; /* no param string to scan */ @@ -1320,11 +1323,11 @@ int qse_htrd_scanqparam (qse_htrd_t* http, const qse_mcstr_t* cstr) /* a key and a value pair including two terminating null * can't exceed the the qparamstrlen + 2. only +1 below as there is * one more space for an internal terminating null */ - qse_mbs_setlen (&http->tmp.qparam, cstr->len + 1); + qse_mbs_setlen (&htrd->tmp.qparam, cstr->len + 1); /* let out point to the beginning of the qparam buffer. * the loop below emits percent-decode key and value to this buffer. */ - out = QSE_MBS_PTR(&http->tmp.qparam); + out = QSE_MBS_PTR(&htrd->tmp.qparam); key.ptr = out; key.len = 0; val.ptr = QSE_NULL; val.len = 0; @@ -1351,15 +1354,15 @@ int qse_htrd_scanqparam (qse_htrd_t* http, const qse_mcstr_t* cstr) } QSE_ASSERTX ( - http->recbs->qparamstr != QSE_NULL, + htrd->recbs->qparamstr != QSE_NULL, "set request parameter string callback before scanning" ); - http->errnum = QSE_HTRD_ENOERR; - if (http->recbs->qparamstr (http, &key, &val) <= -1) + htrd->errnum = QSE_HTRD_ENOERR; + if (htrd->recbs->qparamstr (htrd, &key, &val) <= -1) { - if (http->errnum == QSE_HTRD_ENOERR) - http->errnum = QSE_HTRD_ERECBS; + if (htrd->errnum == QSE_HTRD_ENOERR) + htrd->errnum = QSE_HTRD_ERECBS; return -1; } @@ -1367,7 +1370,7 @@ int qse_htrd_scanqparam (qse_htrd_t* http, const qse_mcstr_t* cstr) if (p >= end) break; p++; - out = QSE_MBS_PTR(&http->tmp.qparam); + out = QSE_MBS_PTR(&htrd->tmp.qparam); key.ptr = out; key.len = 0; val.ptr = QSE_NULL; val.len = 0; } @@ -1406,6 +1409,6 @@ int qse_htrd_scanqparam (qse_htrd_t* http, const qse_mcstr_t* cstr) } while (1); - qse_mbs_clear (&http->tmp.qparam); + qse_mbs_clear (&htrd->tmp.qparam); return 0; } diff --git a/qse/lib/net/htre.c b/qse/lib/net/htre.c index f58554c3..d5bebfbb 100644 --- a/qse/lib/net/htre.c +++ b/qse/lib/net/htre.c @@ -56,3 +56,12 @@ int qse_htre_setstrfromxstr ( return (qse_mbs_ncpy (str, xstr->ptr, xstr->len) == (qse_size_t)-1)? -1: 0; } +const qse_mchar_t* qse_htre_gethdrval ( + qse_htre_t* re, const qse_mchar_t* name) +{ + qse_htb_pair_t* pair; + pair = qse_htb_search (&re->hdrtab, name, qse_mbslen(name)); + if (pair == QSE_NULL) return pair; + return QSE_HTB_VPTR(pair); +} + diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 08233565..8672a524 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -610,7 +610,7 @@ static void delete_from_client_array (qse_httpd_t* httpd, int fd) qse_htrd_close (array->data[fd].htrd); array->data[fd].htrd = QSE_NULL; - close (array->data[fd].fd); + close (array->data[fd].handle.i); array->size--; } } @@ -636,6 +636,7 @@ static qse_httpd_client_t* insert_into_client_array (qse_httpd_t* httpd, int fd, { htrd_xtn_t* xtn; client_array_t* array = &httpd->client.array; + int opt; if (fd >= array->capa) { @@ -655,10 +656,17 @@ static qse_httpd_client_t* insert_into_client_array (qse_httpd_t* httpd, int fd, QSE_ASSERT (array->data[fd].htrd == QSE_NULL); - array->data[fd].fd = fd; - array->data[fd].addr = *addr; array->data[fd].htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(*xtn)); if (array->data[fd].htrd == QSE_NULL) return QSE_NULL; + opt = qse_htrd_getoption (array->data[fd].htrd); + opt |= QSE_HTRD_REQUEST; + opt &= ~QSE_HTRD_RESPONSE; + qse_htrd_setoption (array->data[fd].htrd, opt); + + array->data[fd].bad = 0; + array->data[fd].handle.i = fd; + array->data[fd].addr = *addr; + pthread_mutex_init (&array->data[fd].task.mutex, NULL); xtn = (htrd_xtn_t*)qse_htrd_getxtn (array->data[fd].htrd); @@ -765,14 +773,14 @@ static int make_fd_set_from_client_array (qse_httpd_t* httpd, fd_set* r, fd_set* { if (r) { - FD_SET (ca->data[fd].fd, r); - if (ca->data[fd].fd > max) max = ca->data[fd].fd; + FD_SET (ca->data[fd].handle.i, r); + if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; } - if (w && ca->data[fd].task.queue.count > 0) + if (w && (ca->data[fd].task.queue.count > 0 || ca->data[fd].bad)) { /* add it to the set if it has a response to send */ - FD_SET (ca->data[fd].fd, w); - if (ca->data[fd].fd > max) max = ca->data[fd].fd; + FD_SET (ca->data[fd].handle.i, w); + if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; } } } @@ -793,7 +801,7 @@ static void perform_task (qse_httpd_t* httpd, qse_httpd_client_t* client) if (n <= -1) { dequeue_task_locked (httpd, client); - shutdown (client->fd, SHUT_RDWR); + shutdown (client->handle.i, SHUT_RDWR); } else if (n == 0) { @@ -853,9 +861,14 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %S\n"), strerro if (!client->htrd) continue; - if (FD_ISSET(client->fd, &w)) + if (FD_ISSET(client->handle.i, &w)) { - if (client->task.queue.count > 0) perform_task (httpd, client); + if (client->bad) + { + /*send (client->handle, i, "INTERNAL SERVER ERROR..", ...);*/ + shutdown (client->handle.i, 0); + } + else if (client->task.queue.count > 0) perform_task (httpd, client); } } @@ -871,13 +884,13 @@ static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client) qse_ssize_t m; reread: - m = read (client->fd, buf, QSE_SIZEOF(buf)); + m = read (client->handle.i, buf, QSE_SIZEOF(buf)); if (m <= -1) { if (errno != EINTR) { httpd->errnum = QSE_HTTPD_ESOCKET; -qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), client->fd); +qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), client->handle.i); return -1; } goto reread; @@ -885,7 +898,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), clie else if (m == 0) { httpd->errnum = QSE_HTTPD_EDISCON; -qse_fprintf (QSE_STDERR, QSE_T("Debug: connection closed %d\n"), client->fd); +qse_fprintf (QSE_STDERR, QSE_T("Debug: connection closed %d\n"), client->handle.i); return -1; } @@ -974,7 +987,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n")); if (!client->htrd) continue; - if (FD_ISSET(client->fd, &r)) + if (FD_ISSET(client->handle.i, &r)) { /* got input */ if (read_from_client (httpd, client) <= -1) @@ -1237,7 +1250,15 @@ int qse_httpd_entask ( { int ret; ret = enqueue_task_locked (httpd, client, task, xtnsize); - if (ret >= 0) pthread_cond_signal (&httpd->client.cond); + if (ret <= -1) client->bad = 1; /* mark this client bad */ + else pthread_cond_signal (&httpd->client.cond); return ret; } +void qse_httpd_markclientbad (qse_httpd_t* httpd, qse_httpd_client_t* client) +{ + /* mark that something is wrong in processing requests from this client. + * this client could be bad... or the system could encounter some errors + * like memory allocation failure */ + client->bad = 1; +} diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index a4cc5d0e..a39792e2 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -20,7 +20,7 @@ static int task_main_disconnect ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - shutdown (client->fd, SHUT_RDWR); + shutdown (client->handle.i, SHUT_RDWR); return 0; } @@ -67,7 +67,7 @@ static int httpd_main_sendtext ( if (count >= ctx->left) count = ctx->left; n = send ( - client->fd, + client->handle.i, ctx->ptr, count, 0 @@ -143,7 +143,7 @@ static int httpd_main_sendfmt ( if (count >= ctx->left) count = ctx->left; n = send ( - client->fd, + client->handle.i, ctx->ptr, count, 0 @@ -287,7 +287,7 @@ static int httpd_main_sendfile ( if (count >= ctx->left) count = ctx->left; n = sendfile ( - client->fd, + client->handle.i, ctx->fd, &ctx->offset, count @@ -295,6 +295,12 @@ static int httpd_main_sendfile ( if (n <= -1) return -1; + if (n == 0 && count > 0) + { +/* TODO: .... */ + /* anything to do in this case? can this happen if the file has been truncated during transfer.... */ + } + ctx->left -= n; if (ctx->left <= 0) return 0; diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index d13b60ed..da4916c8 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -53,7 +53,6 @@ qse_printf (QSE_T("content = [%.*S]\n"), QSE_MBS_PTR(&req->content)); } - method = qse_htre_getqmethod (req); if (method == QSE_HTTP_GET || method == QSE_HTTP_POST) @@ -63,50 +62,89 @@ qse_printf (QSE_T("content = [%.*S]\n"), fd = open (qse_htre_getqpathptr(req), O_RDONLY); if (fd <= -1) { - return -1; + const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + req->version.major, req->version.minor, + (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops; } else { struct stat st; if (fstat (fd, &st) <= -1) { + const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); + close (fd); - return -1; - } - else if (st.st_size <= 0) - { - close (fd); - return -1; + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + req->version.major, req->version.minor, + (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops; } else { - int n; -#if 0 -qse_mchar_t text[128]; -snprintf (text, sizeof(text), - "HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n", - qse_htre_getmajorversion(req), - qse_htre_getminorversion(req), - (unsigned long long)st.st_size, - qse_htre_getqpathptr(req) -); - n = qse_httpd_entasksendfmt (httpd, client, text); -#endif - n = qse_httpd_entasksendfmt (httpd, client, - "HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n", - qse_htre_getmajorversion(req), - qse_htre_getminorversion(req), - (unsigned long long)st.st_size, - qse_htre_getqpathptr(req) - ); - if (n <= -1) return -1; + const qse_mchar_t* range; + if (st.st_size <= 0) st.st_size = 0; - if (qse_httpd_entasksendfile (httpd, client, fd, 0, st.st_size) <= -1) return -1; + range = qse_htre_gethdrval (req, "Range"); + if (range) + { +qse_printf (QSE_T("PARTIAL>>>> %S\n"), range); +#if 0 + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %sContent-Range: bytes %lld-%lld\r\n\r\n"), + qse_htre_getmajorversion(req), + qse_htre_getminorversion(req), + (unsigned long long)st.st_size, + qse_htre_getqpathptr(req)) <= -1) + { + close (fd); + goto oops; + } +#endif + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), + qse_htre_getmajorversion(req), + qse_htre_getminorversion(req), + (unsigned long long)st.st_size, + qse_htre_getqpathptr(req)) <= -1) + { + close (fd); + goto oops; + } + } + else + { +/* TODO: int64 format.... don't hard code it llu */ + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), + qse_htre_getmajorversion(req), + qse_htre_getminorversion(req), + (unsigned long long)st.st_size, + qse_htre_getqpathptr(req)) <= -1) + { + close (fd); + goto oops; + } + } + + if (qse_httpd_entasksendfile (httpd, client, fd, 0, st.st_size) <= -1) + { + close (fd); + goto oops; + } } } } - + else + { + const qse_mchar_t* msg = QSE_MT("Method not allowedREQUEST METHOD NOT ALLOWED"); + if (qse_httpd_entasksendfmt (httpd, client, + QSE_MT("HTTP/%d.%d 405 Method not allowed\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + req->version.major, req->version.minor, + (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops; + } if (req->attr.connection_close) { @@ -114,6 +152,10 @@ snprintf (text, sizeof(text), } return 0; + +oops: + qse_httpd_markclientbad (httpd, client); + return 0; } static int handle_expect_continue (