diff --git a/qse/include/qse/cmn/str.h b/qse/include/qse/cmn/str.h index 77b2a4f5..dc95be47 100644 --- a/qse/include/qse/cmn/str.h +++ b/qse/include/qse/cmn/str.h @@ -39,6 +39,8 @@ #define QSE_MBS_LEN(s) ((s)->val.len) /** string pointer */ #define QSE_MBS_PTR(s) ((s)->val.ptr) +/** pointer to a particular position */ +#define QSE_MBS_CPTR(s,idx) (&(s)->val.ptr[idx]) /** string capacity */ #define QSE_MBS_CAPA(s) ((s)->capa) /** character at the given position */ @@ -54,6 +56,8 @@ #define QSE_WCS_LEN(s) ((s)->val.len) /** string pointer */ #define QSE_WCS_PTR(s) ((s)->val.ptr) +/** pointer to a particular position */ +#define QSE_WCS_CPTR(s,idx) (&(s)->val.ptr[idx]) /** string capacity */ #define QSE_WCS_CAPA(s) ((s)->capa) /** character at the given position */ @@ -79,6 +83,7 @@ typedef qse_size_t (*qse_wcs_sizer_t) ( # define QSE_STR_CSTR(s) ((qse_cstr_t*)QSE_MBS_XSTR(s)) # define QSE_STR_LEN(s) QSE_MBS_LEN(s) # define QSE_STR_PTR(s) QSE_MBS_PTR(s) +# define QSE_STR_CPTR(s,idx) QSE_MBS_CPTR(s,idx) # define QSE_STR_CAPA(s) QSE_MBS_CAPA(s) # define QSE_STR_CHAR(s,idx) QSE_MBS_CHAR(s,idx) # define QSE_STR_LASTCHAR(s) QSE_MBS_LASTCHAR(s) @@ -89,6 +94,7 @@ typedef qse_size_t (*qse_wcs_sizer_t) ( # define QSE_STR_CSTR(s) ((qse_cstr_t*)QSE_WCS_XSTR(s)) # define QSE_STR_LEN(s) QSE_WCS_LEN(s) # define QSE_STR_PTR(s) QSE_WCS_PTR(s) +# define QSE_STR_CPTR(s,idx) QSE_WCS_CPTR(s,idx) # define QSE_STR_CAPA(s) QSE_WCS_CAPA(s) # define QSE_STR_CHAR(s,idx) QSE_WCS_CHAR(s,idx) # define QSE_STR_LASTCHAR(s) QSE_WCS_LASTCHAR(s) diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index dadb70c0..1dfb7e09 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -94,9 +94,6 @@ struct qse_htrd_t qse_htob_t raw; /* buffer to hold raw octets */ qse_htob_t tra; /* buffer for handling trailers */ } b; - - /* points to the head of the combined header list */ - void* chl; } fed; qse_htre_t re; diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index 07460c1b..89e3c3c9 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -25,8 +25,15 @@ #include #include +/* + * You should not manipulate an object of the #qse_htre_t + * type directly since it's complex. Use #qse_htrd_t to + * create an object of the qse_htre_t type. + */ + /* header and contents of request/response */ typedef struct qse_htre_t qse_htre_t; +typedef struct qse_htre_hdrval_t qse_htre_hdrval_t; enum qse_htre_state_t { @@ -41,6 +48,13 @@ typedef int (*qse_htre_concb_t) ( void* ctx ); +struct qse_htre_hdrval_t +{ + const qse_mchar_t* ptr; + qse_size_t len; + qse_htre_hdrval_t* next; +}; + struct qse_htre_t { qse_mmgr_t* mmgr; @@ -84,10 +98,10 @@ struct qse_htre_t #define QSE_HTRE_ATTR_CHUNKED (1 << 0) #define QSE_HTRE_ATTR_LENGTH (1 << 1) #define QSE_HTRE_ATTR_KEEPALIVE (1 << 2) +#define QSE_HTRE_ATTR_EXPECT100 (1 << 3) int flags; qse_size_t content_length; - const qse_mchar_t* expect; - const qse_mchar_t* status; + const qse_mchar_t* status; /* for cgi */ } attr; /* header table */ @@ -127,10 +141,10 @@ struct qse_htre_t #define qse_htre_getcontentlen(re) QSE_MBS_LEN(&(re)->content) typedef int (*qse_htre_header_walker_t) ( - qse_htre_t* re, - const qse_mchar_t* key, - const qse_mchar_t* val, - void* ctx + qse_htre_t* re, + const qse_mchar_t* key, + const qse_htre_hdrval_t* val, + void* ctx ); #ifdef __cplusplus @@ -162,12 +176,12 @@ int qse_htre_setstrfromxstr ( const qse_mxstr_t* xstr ); -const qse_mchar_t* qse_htre_getheaderval ( +const qse_htre_hdrval_t* qse_htre_getheaderval ( const qse_htre_t* re, const qse_mchar_t* key ); -const qse_mchar_t* qse_htre_gettrailerval ( +const qse_htre_hdrval_t* qse_htre_gettrailerval ( const qse_htre_t* re, const qse_mchar_t* key ); diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 6ed636dc..9a18b94d 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -218,21 +218,21 @@ struct qse_httpd_cbs_t typedef struct qse_httpd_task_t qse_httpd_task_t; typedef int (*qse_httpd_task_init_t) ( - qse_httpd_t* httpd, + qse_httpd_t* httpd, qse_httpd_client_t* client, - qse_httpd_task_t* task + qse_httpd_task_t* task ); typedef void (*qse_httpd_task_fini_t) ( - qse_httpd_t* httpd, + qse_httpd_t* httpd, qse_httpd_client_t* client, - qse_httpd_task_t* task + qse_httpd_task_t* task ); typedef int (*qse_httpd_task_main_t) ( - qse_httpd_t* httpd, + qse_httpd_t* httpd, qse_httpd_client_t* client, - qse_httpd_task_t* task + qse_httpd_task_t* task ); diff --git a/qse/lib/cmn/htb.c b/qse/lib/cmn/htb.c index d26c974b..65a8b120 100644 --- a/qse/lib/cmn/htb.c +++ b/qse/lib/cmn/htb.c @@ -189,6 +189,7 @@ static QSE_INLINE pair_t* change_pair_val ( static mancbs_t mancbs[] = { + /* == QSE_HTB_MANCBS_DEFAULT == */ { { QSE_HTB_COPIER_DEFAULT, @@ -204,6 +205,7 @@ static mancbs_t mancbs[] = QSE_HTB_HASHER_DEFAULT }, + /* == QSE_HTB_MANCBS_INLINE_COPIERS == */ { { QSE_HTB_COPIER_INLINE, @@ -219,6 +221,7 @@ static mancbs_t mancbs[] = QSE_HTB_HASHER_DEFAULT }, + /* == QSE_HTB_MANCBS_INLINE_KEY_COPIER == */ { { QSE_HTB_COPIER_INLINE, @@ -234,6 +237,7 @@ static mancbs_t mancbs[] = QSE_HTB_HASHER_DEFAULT }, + /* == QSE_HTB_MANCBS_INLINE_VALUE_COPIER == */ { { QSE_HTB_COPIER_DEFAULT, @@ -288,7 +292,6 @@ int qse_htb_init ( QSE_ASSERTX (factor >= 0 && factor <= 100, "The load factor should be between 0 and 100 inclusive. In the release mode, a value out of the range is adjusted to 100"); - /* some initial adjustment */ if (capa <= 0) capa = 1; if (factor > 100) factor = 100; diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index b75eb93f..bc8f6201 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -114,20 +114,6 @@ struct hdr_cmb_t struct hdr_cmb_t* next; }; -static QSE_INLINE void clear_combined_headers (qse_htrd_t* htrd) -{ - struct hdr_cmb_t* cmb = (struct hdr_cmb_t*)htrd->fed.chl; - - while (cmb) - { - struct hdr_cmb_t* next = cmb->next; - QSE_MMGR_FREE (htrd->mmgr, cmb); - cmb = next; - } - - htrd->fed.chl = QSE_NULL; -} - static QSE_INLINE void clear_feed (qse_htrd_t* htrd) { /* clear necessary part of the request/response before @@ -136,7 +122,6 @@ static QSE_INLINE void clear_feed (qse_htrd_t* htrd) qse_mbs_clear (&htrd->fed.b.tra); qse_mbs_clear (&htrd->fed.b.raw); - clear_combined_headers (htrd); QSE_MEMSET (&htrd->fed.s, 0, QSE_SIZEOF(htrd->fed.s)); } @@ -198,7 +183,6 @@ void qse_htrd_fini (qse_htrd_t* htrd) { qse_htre_fini (&htrd->re); - clear_combined_headers (htrd); qse_mbs_fini (&htrd->fed.b.tra); qse_mbs_fini (&htrd->fed.b.raw); #if 0 @@ -465,19 +449,19 @@ void qse_htrd_setrecbs (qse_htrd_t* htrd, const qse_htrd_recbs_t* recbs) static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; + qse_htre_hdrval_t* val; - n = qse_mbsxncasecmp ( - QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), - "close", 5); + val = QSE_HTB_VPTR(pair); + while (val->next) val = val->next; + + n = qse_mbscmp (val->ptr, QSE_MT("close")); if (n == 0) { htrd->re.attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE; return 0; } - n = qse_mbsxncasecmp ( - QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), - "keep-alive", 10); + n = qse_mbscmp (val->ptr, QSE_MT("keep-alive")); if (n == 0) { htrd->re.attr.flags |= QSE_HTRE_ATTR_KEEPALIVE; @@ -504,9 +488,15 @@ static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair) static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair) { qse_size_t len = 0, off = 0, tmp; - const qse_mchar_t* ptr = QSE_HTB_VPTR(pair); + const qse_mchar_t* ptr; + qse_htre_hdrval_t* val; - while (off < QSE_HTB_VLEN(pair)) + /* get the last content_length */ + val = QSE_HTB_VPTR(pair); + while (val->next) val = val->next; + + ptr = val->ptr; + while (off < val->len) { int num = digit_to_num (ptr[off]); if (num <= -1) @@ -550,22 +540,37 @@ static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair) static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair) { - htrd->re.attr.expect = QSE_HTB_VPTR(pair); + qse_htre_hdrval_t* val; + + val = QSE_HTB_VPTR(pair); + while (val->next) val = val->next; + + if (qse_mbscmp (val->ptr, QSE_MT("100-continue")) == 0) + htrd->re.attr.flags |= QSE_HTRE_ATTR_EXPECT100; + return 0; } static int capture_status (qse_htrd_t* htrd, qse_htb_pair_t* pair) { - htrd->re.attr.status = QSE_HTB_VPTR(pair); + qse_htre_hdrval_t* val; + + val = QSE_HTB_VPTR(pair); + while (val->next) val = val->next; + + htrd->re.attr.status = val->ptr; return 0; } static int capture_transfer_encoding (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; + qse_htre_hdrval_t* val; - n = qse_mbsxncasecmp ( - QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "chunked", 7); + val = QSE_HTB_VPTR(pair); + while (val->next) val = val->next; + + n = qse_mbscasecmp (val->ptr, QSE_MT("chunked")); if (n == 0) { /* if (htrd->re.attr.content_length > 0) */ @@ -645,10 +650,26 @@ static qse_htb_pair_t* hdr_cbserter ( { /* the key is new. let's create a new pair. */ qse_htb_pair_t* p; + qse_htre_hdrval_t *val; - p = qse_htb_allocpair (htb, kptr, klen, tx->vptr, tx->vlen); + val = QSE_MMGR_ALLOC (htb->mmgr, QSE_SIZEOF(*val)); + if (val == QSE_NULL) + { + tx->htrd->errnum = QSE_HTRD_ENOMEM; + return QSE_NULL; + } - if (p == QSE_NULL) tx->htrd->errnum = QSE_HTRD_ENOMEM; + QSE_MEMSET (val, 0, QSE_SIZEOF(*val)); + val->ptr = tx->vptr; + val->len = tx->vlen; + val->next = QSE_NULL; + + p = qse_htb_allocpair (htb, kptr, klen, val, 0); + if (p == QSE_NULL) + { + QSE_MMGR_FREE (htb->mmgr, val); + tx->htrd->errnum = QSE_HTRD_ENOMEM; + } else { if (capture_key_header (tx->htrd, p) <= -1) @@ -664,88 +685,72 @@ static qse_htb_pair_t* hdr_cbserter ( } else { - /* the key exists. let's combine values, each separated - * by a comma */ - struct hdr_cmb_t* cmb; - qse_mchar_t* ptr; - qse_size_t len; + /* RFC2616 + * Multiple message-header fields with the same field-name + * MAY be present in a message if and only if the entire + * field-value for that header field is defined as a + * comma-separated list [i.e., #(values)]. It MUST be possible + * to combine the multiple header fields into one + * "field-name: field-value" pair, without changing the semantics + * of the message, by appending each subsequent field-value + * to the first, each separated by a comma. The order in which + * header fields with the same field-name are received is therefore + * significant to the interpretation of the combined field value, + * and thus a proxy MUST NOT change the order of these field values + * when a message is forwarded. - /* TODO: reduce waste in case the same key appears again. - * - * the current implementation is not space nor performance - * efficient. it allocates a new buffer again whenever it - * encounters the same key. memory is wasted and performance - * is sacrificed. - - * hopefully, a htrd header does not include a lot of - * duplicate fields and this implmentation can afford wastage. + * RFC6265 defines the syntax for Set-Cookie and Cookie. + * this seems to be conflicting with RFC2616. + * + * Origin servers SHOULD NOT fold multiple Set-Cookie header fields + * into a single header field. The usual mechanism for folding HTTP + * headers fields (i.e., as defined in [RFC2616]) might change the + * semantics of the Set-Cookie header field because the %x2C (",") + * character is used by Set-Cookie in a way that conflicts with + * such folding. + * + * So i just maintain the list of valuea for a key instead of + * folding them. */ - /* allocate a block to combine the existing value and the new value */ - cmb = (struct hdr_cmb_t*) QSE_MMGR_ALLOC ( - tx->htrd->mmgr, - QSE_SIZEOF(*cmb) + - QSE_SIZEOF(qse_mchar_t) * (QSE_HTB_VLEN(pair) + 1 + tx->vlen + 1) - ); - if (cmb == QSE_NULL) + qse_htre_hdrval_t* val; + qse_htre_hdrval_t* tmp; + + val = (qse_htre_hdrval_t*) QSE_MMGR_ALLOC ( + tx->htrd->mmgr, QSE_SIZEOF(*val)); + if (val == QSE_NULL) { tx->htrd->errnum = QSE_HTRD_ENOMEM; return QSE_NULL; } - /* let 'ptr' point to the actual space for the combined value */ - ptr = (qse_mchar_t*)(cmb + 1); - len = 0; + QSE_MEMSET (val, 0, QSE_SIZEOF(*val)); + val->ptr = tx->vptr; + val->len = tx->vlen; + val->next = QSE_NULL; - /* fill the space with the value */ - QSE_MEMCPY (&ptr[len], QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair)); - len += QSE_HTB_VLEN(pair); - ptr[len++] = ','; - QSE_MEMCPY (&ptr[len], tx->vptr, tx->vlen); - len += tx->vlen; - ptr[len] = '\0'; +/* TODO: doubly linked list for speed-up??? */ + tmp = QSE_HTB_VPTR(pair); + QSE_ASSERT (tmp != QSE_NULL); -#if 0 -TODO: -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->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->htrd->mmgr, - ((struct hdr_cmb_t*)QSE_HTB_VPTR(pair)) - 1 - ); - } -#endif - - /* update the value pointer and length */ - QSE_HTB_VPTR(pair) = ptr; - QSE_HTB_VLEN(pair) = len; - - /* link the new combined value block */ - cmb->next = tx->htrd->fed.chl; - tx->htrd->fed.chl = cmb; + /* find the tail */ + while (tmp->next) tmp = tmp->next; + /* append it to the list*/ + tmp->next = val; if (capture_key_header (tx->htrd, pair) <= -1) return QSE_NULL; - return pair; } } -qse_mchar_t* parse_header_fields ( +qse_mchar_t* parse_header_field ( qse_htrd_t* htrd, qse_mchar_t* line, qse_htb_t* tab) { qse_mchar_t* p = line, * last; struct { qse_mchar_t* ptr; - qse_size_t len; + qse_size_t len; } name, value; #if 0 @@ -757,26 +762,26 @@ qse_mchar_t* parse_header_fields ( /* check the field name */ name.ptr = last = p; - while (*p != '\0' && *p != '\n' && *p != ':') + while (*p != QSE_MT('\0') && *p != QSE_MT('\n') && *p != QSE_MT(':')) { if (!is_space_octet(*p++)) last = p; } name.len = last - name.ptr; - if (*p != ':') goto badhdr; + if (*p != QSE_MT(':')) goto badhdr; *last = '\0'; /* skip the colon and spaces after it */ do { p++; } while (is_space_octet(*p)); value.ptr = last = p; - while (*p != '\0' && *p != '\n') + while (*p != QSE_MT('\0') && *p != QSE_MT('\n')) { if (!is_space_octet(*p++)) last = p; } value.len = last - value.ptr; - if (*p != '\n') goto badhdr; /* not ending with a new line */ + if (*p != QSE_MT('\n')) goto badhdr; /* not ending with a new line */ /* peep at the beginning of the next line to check if it is * the continuation */ @@ -785,25 +790,25 @@ qse_mchar_t* parse_header_fields ( qse_mchar_t* cpydst; cpydst = p - 1; - if (*(cpydst-1) == '\r') cpydst--; + if (*(cpydst-1) == QSE_MT('\r')) cpydst--; /* process all continued lines */ do { - while (*p != '\0' && *p != '\n') + while (*p != QSE_MT('\0') && *p != QSE_MT('\n')) { *cpydst = *p++; if (!is_space_octet(*cpydst++)) last = cpydst; } value.len = last - value.ptr; - if (*p != '\n') goto badhdr; + if (*p != QSE_MT('\n')) goto badhdr; - if (*(cpydst-1) == '\r') cpydst--; + if (*(cpydst-1) == QSE_MT('\r')) cpydst--; } while (is_purespace_octet(*++p)); } - *last = '\0'; + *last = QSE_MT('\0'); /* insert the new field to the header table */ { @@ -867,10 +872,11 @@ 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 (htrd, p, &htrd->re.hdrtab); + p = parse_header_field (htrd, p, &htrd->re.hdrtab); if (p == QSE_NULL) return -1; } while (1); + return 0; } @@ -997,7 +1003,7 @@ static const qse_mchar_t* get_trailing_headers ( /* TODO: return error if protocol is 0.9. * HTTP/0.9 must not get headers... */ - p = parse_header_fields ( + p = parse_header_field ( htrd, p, ((htrd->option & QSE_HTRD_TRAILERS)? &htrd->re.trailers: &htrd->re.hdrtab) ); diff --git a/qse/lib/net/htre.c b/qse/lib/net/htre.c index 1cc37212..e338c1d9 100644 --- a/qse/lib/net/htre.c +++ b/qse/lib/net/htre.c @@ -21,14 +21,47 @@ #include #include "../cmn/mem.h" +static void free_hdrval (qse_htb_t* htb, void* vptr, qse_size_t vlen) +{ + qse_htre_hdrval_t* val; + qse_htre_hdrval_t* tmp; + + val = vptr; + while (val) + { + tmp = val; + val = val->next; + QSE_MMGR_FREE (htb->mmgr, tmp); + } +} + int qse_htre_init (qse_htre_t* re, qse_mmgr_t* mmgr) { + static qse_htb_mancbs_t mancbs = + { + { + QSE_HTB_COPIER_DEFAULT, + QSE_HTB_COPIER_DEFAULT + }, + { + QSE_HTB_FREEER_DEFAULT, + free_hdrval + }, + QSE_HTB_COMPER_DEFAULT, + QSE_HTB_KEEPER_DEFAULT, + QSE_HTB_SIZER_DEFAULT, + QSE_HTB_HASHER_DEFAULT + }; + QSE_MEMSET (re, 0, QSE_SIZEOF(*re)); re->mmgr = mmgr; if (qse_htb_init (&re->hdrtab, mmgr, 60, 70, 1, 1) <= -1) return -1; if (qse_htb_init (&re->trailers, mmgr, 20, 70, 1, 1) <= -1) return -1; + qse_htb_setmancbs (&re->hdrtab, &mancbs); + qse_htb_setmancbs (&re->trailers, &mancbs); + qse_mbs_init (&re->content, mmgr, 0); #if 0 qse_mbs_init (&re->iniline, mmgr, 0); @@ -86,7 +119,7 @@ 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_getheaderval ( +const qse_htre_hdrval_t* qse_htre_getheaderval ( const qse_htre_t* re, const qse_mchar_t* name) { qse_htb_pair_t* pair; @@ -95,7 +128,7 @@ const qse_mchar_t* qse_htre_getheaderval ( return QSE_HTB_VPTR(pair); } -const qse_mchar_t* qse_htre_gettrailerval ( +const qse_htre_hdrval_t* qse_htre_gettrailerval ( const qse_htre_t* re, const qse_mchar_t* name) { qse_htb_pair_t* pair; @@ -148,7 +181,6 @@ int qse_htre_walktrailers ( return hwctx.ret; } - int qse_htre_addcontent ( qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len) { diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index 73f6e7ee..2c645948 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -636,6 +636,7 @@ static int task_main_dir ( set_chunklen: /* right alignment with space padding on the left */ +/* TODO: change snprintf to qse_fmtuintmaxtombs() */ x = snprintf ( ctx->buf, (SIZE_CHLEN + SIZE_CHLENCRLF) - 1, QSE_MT("%*lX"), (int)(SIZE_CHLEN + SIZE_CHLENCRLF - 2), @@ -906,7 +907,7 @@ qse_httpd_task_t* qse_httpd_entaskpath ( { qse_httpd_task_t task; task_path_t data; - const qse_mchar_t* tmp; + const qse_htre_hdrval_t* tmp; QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); data.name = name; @@ -916,7 +917,8 @@ qse_httpd_task_t* qse_httpd_entaskpath ( tmp = qse_htre_getheaderval(req, QSE_MT("Range")); if (tmp) { - if (qse_parsehttprange (tmp, &data.range) <= -1) + while (tmp->next) tmp = tmp->next; /* get the last value */ + if (qse_parsehttprange (tmp->ptr, &data.range) <= -1) { return entask_error (httpd, client, pred, 416, &data.version, data.keepalive); } @@ -1197,7 +1199,7 @@ qse_httpd_task_t* qse_httpd_entaskfile ( { qse_httpd_task_t task; task_file_t data; - const qse_mchar_t* tmp; + const qse_htre_hdrval_t* tmp; QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); data.path = path; @@ -1207,7 +1209,8 @@ qse_httpd_task_t* qse_httpd_entaskfile ( tmp = qse_htre_getheaderval(req, QSE_MT("Range")); if (tmp) { - if (qse_parsehttprange (tmp, &data.range) <= -1) + while (tmp->next) tmp = tmp->next; /* get the last value */ + if (qse_parsehttprange (tmp->ptr, &data.range) <= -1) { return qse_httpd_entaskerror (httpd, client, pred, 416, req); } @@ -1284,8 +1287,8 @@ struct task_cgi_t qse_size_t buflen; }; -typedef struct cgi_script_output_htrd_xtn_t cgi_script_output_htrd_xtn_t; -struct cgi_script_output_htrd_xtn_t +typedef struct cgi_script_htrd_xtn_t cgi_script_htrd_xtn_t; +struct cgi_script_htrd_xtn_t { task_cgi_t* cgi; qse_httpd_client_t* client; @@ -1299,8 +1302,8 @@ struct cgi_client_req_hdr_ctx_t qse_env_t* env; }; -static int cgi_walk_client_req_header ( - qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) +static int cgi_capture_client_header ( + qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) { cgi_client_req_hdr_ctx_t* hdrctx; qse_mchar_t* http_key; @@ -1316,12 +1319,21 @@ static int cgi_walk_client_req_header ( return -1; } - ret = qse_env_insertmbs (hdrctx->env, http_key, val); +/* TODO EXCLUDE VARIOUS FIELDS like transfer-encoding or should i let cgi handle transfer-encoding: chunked??? */ +/* TODO: special handling for Cookie??? */ + do + { + ret = qse_env_insertmbs (hdrctx->env, http_key, val->ptr); + if (ret <= -1) break; + val = val->next; + } + while (val); + QSE_MMGR_FREE (req->mmgr, http_key); return ret; } -static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) +static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) { task_cgi_t* cgi = (task_cgi_t*)ctx; @@ -1330,14 +1342,22 @@ static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, c qse_mbscmp (key, QSE_MT("Connection")) != 0 && qse_mbscmp (key, QSE_MT("Transfer-Encoding")) != 0) { - if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, val) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) + /* multiple items with the same keys are also + * copied back to the response buffer */ + do { - cgi->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; + if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1 || + qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1 || + qse_mbs_cat (cgi->res, val->ptr) == (qse_size_t)-1 || + qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) + { + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + + val = val->next; } + while (val); } return 0; @@ -1345,11 +1365,11 @@ static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, c static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req) { - cgi_script_output_htrd_xtn_t* xtn; + cgi_script_htrd_xtn_t* xtn; task_cgi_t* cgi; int keepalive; - xtn = (cgi_script_output_htrd_xtn_t*) qse_htrd_getxtn (htrd); + xtn = (cgi_script_htrd_xtn_t*) qse_htrd_getxtn (htrd); cgi = xtn->cgi; QSE_ASSERT (!cgi->nph); @@ -1386,7 +1406,7 @@ static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req) } else { - const qse_mchar_t* location; + const qse_htre_hdrval_t* location; qse_mchar_t buf[128]; location = qse_htre_getheaderval (req, QSE_MT("Location")); @@ -1430,6 +1450,7 @@ static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req) /* no Content-Length returned by CGI. */ if (qse_comparehttpversions (&cgi->version, &http_v11) >= 0) { + /* the client side supports chunking */ cgi->resflags |= CGI_RES_CLIENT_CHUNK; if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) { @@ -1493,8 +1514,14 @@ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); if (cgi->resflags & CGI_RES_CLIENT_CHUNK) { qse_mchar_t buf[64]; - snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)cgi->script_output_received); - if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) + + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), + cgi->script_output_received, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1 || + qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) { cgi->httpd->errnum = QSE_HTTPD_ENOMEM; return -1; @@ -1520,7 +1547,7 @@ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); return 0; } -static qse_htrd_recbs_t cgi_script_output_htrd_cbs = +static qse_htrd_recbs_t cgi_script_htrd_cbs = { cgi_htrd_peek_script_output, QSE_NULL /* not needed for CGI */ @@ -1568,8 +1595,8 @@ static qse_env_t* makecgienv ( { qse_mchar_t tmp[64]; qse_fmtuintmaxtombs ( - tmp, QSE_COUNTOF(tmp), content_length, 10, - -1, QSE_MT('\0'), QSE_NULL); + tmp, QSE_COUNTOF(tmp), content_length, + 10, -1, QSE_MT('\0'), QSE_NULL); qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), tmp); } @@ -1610,26 +1637,48 @@ static qse_env_t* makecgienv ( #if 0 ctx.httpd = httpd; ctx.env = env; - if (qse_htre_walkheaders (req, cgi_walk_client_req_header, &ctx) <= -1) return -1; + if (qse_htre_walkheaders (req, cgi_capture_client_header, &ctx) <= -1) return -1; #endif +/* TODO: memory error check */ { - const qse_mchar_t* tmp; + const qse_htre_hdrval_t* tmp; tmp = qse_htre_getheaderval(req, QSE_MT("Content-Type")); - if (tmp) qse_env_insertmbs (env, QSE_MT("CONTENT_TYPE"), tmp); + if (tmp) + { + while (tmp->next) tmp = tmp->next; + qse_env_insertmbs (env, QSE_MT("CONTENT_TYPE"), tmp->ptr); + } tmp = qse_htre_getheaderval(req, QSE_MT("Cookie")); - if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_COOKIE"), tmp); + if (tmp) + { +/* TODO: should cookie be combined into 1??? */ + while (tmp->next) tmp = tmp->next; + qse_env_insertmbs (env, QSE_MT("HTTP_COOKIE"), tmp->ptr); + } tmp = qse_htre_getheaderval(req, QSE_MT("Host")); - if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_HOST"), tmp); + if (tmp) + { + while (tmp->next) tmp = tmp->next; + qse_env_insertmbs (env, QSE_MT("HTTP_HOST"), tmp->ptr); + } tmp = qse_htre_getheaderval(req, QSE_MT("Referer")); - if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_REFERER"), tmp); + if (tmp) + { + while (tmp->next) tmp = tmp->next; + qse_env_insertmbs (env, QSE_MT("HTTP_REFERER"), tmp->ptr); + } tmp = qse_htre_getheaderval(req, QSE_MT("User-Agent")); - if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_USER_AGENT"), tmp); + if (tmp) + { + while (tmp->next) tmp = tmp->next; + qse_env_insertmbs (env, QSE_MT("HTTP_USER_AGENT"), tmp->ptr); + } } return env; @@ -2120,6 +2169,7 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); } /* set the chunk length */ +/* TODO: chagne snprintf to qse_fmtuintmaxtombs() */ snprintf (chunklen, QSE_COUNTOF(chunklen), QSE_MT("%-4lX\r\n"), (unsigned long)n); QSE_MEMCPY (&cgi->buf[cgi->buflen], @@ -2373,14 +2423,14 @@ static int task_main_cgi ( } else { - cgi_script_output_htrd_xtn_t* xtn; - cgi->script_htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_script_output_htrd_xtn_t)); + cgi_script_htrd_xtn_t* xtn; + cgi->script_htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_script_htrd_xtn_t)); if (cgi->script_htrd == QSE_NULL) goto oops; - xtn = (cgi_script_output_htrd_xtn_t*) qse_htrd_getxtn (cgi->script_htrd); + xtn = (cgi_script_htrd_xtn_t*) qse_htrd_getxtn (cgi->script_htrd); xtn->cgi = cgi; xtn->task = task; xtn->client = client; - qse_htrd_setrecbs (cgi->script_htrd, &cgi_script_output_htrd_cbs); + qse_htrd_setrecbs (cgi->script_htrd, &cgi_script_htrd_cbs); qse_htrd_setoption ( cgi->script_htrd, QSE_HTRD_SKIPINITIALLINE | @@ -2554,6 +2604,8 @@ struct task_proxy_t #define PROXY_PEER_CONNECTED (1 << 1) int peer_status; +#define PROXY_REQ_CHUNKED (1 << 0) + int reqflags; qse_htre_t* req; /* original client request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ int reqfwderr; @@ -2568,6 +2620,7 @@ struct task_proxy_t #define PROXY_RES_PEER_CHUNK (1 << 4) /* peer's output is chunked */ #define PROXY_RES_PEER_LENGTH (1 << 5) /* peer's output is set with * the content-length */ +#define PROXY_RES_PEER_LENGTH_FAKE (1 << 6) /* peer_output_length is fake */ #define PROXY_RES_AWAIT_100 (1 << 10) /* waiting for 100 continue */ #define PROXY_RES_AWAIT_RESHDR (1 << 11) /* waiting for response header */ #define PROXY_RES_AWAIT_RESCON (1 << 12) /* waiting for response content. @@ -2595,6 +2648,83 @@ struct proxy_peer_htrd_xtn_t qse_httpd_task_t* task; }; +static int proxy_add_header_to_buffer ( + task_proxy_t* proxy, qse_mbs_t* buf, const qse_mchar_t* key, const qse_htre_hdrval_t* val) +{ + QSE_ASSERT (val != QSE_NULL); + + do + { + if (qse_mbs_cat (buf, key) == (qse_size_t)-1 || + qse_mbs_cat (buf, QSE_MT(": ")) == (qse_size_t)-1 || + qse_mbs_cat (buf, val->ptr) == (qse_size_t)-1 || + qse_mbs_cat (buf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + + val = val->next; + } + while (val); + + return 0; +} + +static int proxy_capture_peer_header (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) +{ + task_proxy_t* proxy = (task_proxy_t*)ctx; + + if (qse_mbscmp (key, QSE_MT("Connection")) != 0 && + qse_mbscmp (key, QSE_MT("Transfer-Encoding")) != 0) + { + return proxy_add_header_to_buffer (proxy, proxy->res, key, val); + } + + return 0; +} + +static int proxy_capture_peer_trailer (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) +{ + task_proxy_t* proxy = (task_proxy_t*)ctx; + + if (qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0 && + qse_mbscasecmp (key, QSE_MT("Content-Length")) != 0 && + qse_mbscasecmp (key, QSE_MT("Connection")) != 0) + { + return proxy_add_header_to_buffer (proxy, proxy->res, key, val); + } + + return 0; +} + +static int proxy_capture_client_header (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) +{ + task_proxy_t* proxy = (task_proxy_t*)ctx; + + if (qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0 && + qse_mbscasecmp (key, QSE_MT("Content-Length")) != 0) + { + return proxy_add_header_to_buffer (proxy, proxy->reqfwdbuf, key, val); + } + + return 0; +} + +static int proxy_capture_client_trailer (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) +{ + task_proxy_t* proxy = (task_proxy_t*)ctx; + + if (qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0 && + qse_mbscasecmp (key, QSE_MT("Content-Length")) != 0 && + qse_mbscasecmp (key, QSE_MT("Connection")) != 0) + { + return proxy_add_header_to_buffer (proxy, proxy->reqfwdbuf, key, val); + } + + return 0; +} + static int proxy_snatch_client_input ( qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) { @@ -2618,6 +2748,18 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); */ QSE_ASSERT (len == 0); + if (proxy->reqflags & PROXY_REQ_CHUNKED) + { + /* add the 0-sized chunk and trailers */ + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("0\r\n")) == (qse_size_t)-1 || + qse_htre_walktrailers (req, proxy_capture_client_trailer, proxy) <= -1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + /* mark the there's nothing to read form the client side */ qse_htre_unsetconcb (proxy->req); proxy->req = QSE_NULL; @@ -2636,6 +2778,7 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); * for task invocation. */ task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } + } else if (!proxy->reqfwderr) { @@ -2643,10 +2786,30 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); * didn't occur previously. we store data from the client side * to the forwaring buffer only if there's no such previous * error. if an error occurred, we simply drop the data. */ - if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) + if (proxy->reqflags & PROXY_REQ_CHUNKED) { - proxy->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; + qse_mchar_t buf[64]; + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + + if (qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || + qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + else + { + if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } } qse_printf (QSE_T("!!!PROXY SNATCHED [%.*hs]\n"), len, ptr); } @@ -2680,17 +2843,19 @@ static int proxy_snatch_peer_output ( if (ptr == QSE_NULL) { /* content completed */ + QSE_ASSERT (len == 0); qse_printf (QSE_T("PROXY GOT ALL RESPONSE>>>>>>>\n")); - if (qse_mbs_cat (proxy->res, QSE_MT("0\r\n\r\n")) == (qse_size_t)-1) + + if (qse_mbs_cat (proxy->res, QSE_MT("0\r\n")) == (qse_size_t)-1 || + qse_htre_walktrailers (req, proxy_capture_peer_trailer, proxy) <= -1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) { proxy->httpd->errnum = QSE_HTTPD_ENOMEM; return -1; } -/* TODO: include trailers into proxy->res if any. for this htrd and htre need to be enhanced as well */ - proxy->resflags &= ~PROXY_RES_AWAIT_RESCON; proxy->resflags |= PROXY_RES_RECEIVED_RESCON; } @@ -2698,9 +2863,13 @@ qse_printf (QSE_T("PROXY GOT ALL RESPONSE>>>>>>>\n")); { /* append the peer response content to the response buffer */ qse_mchar_t buf[64]; - snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)len); + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); if (qse_mbs_cat (proxy->res, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1 || qse_mbs_ncat (proxy->res, ptr, len) == (qse_size_t)-1 || qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) { @@ -2713,26 +2882,6 @@ qse_printf (QSE_T("PROXY GOT ALL RESPONSE>>>>>>>\n")); return 0; } -static int proxy_capture_peer_header (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) -{ - task_proxy_t* proxy = (task_proxy_t*)ctx; - - if (qse_mbscmp (key, QSE_MT("Connection")) != 0 && - qse_mbscmp (key, QSE_MT("Transfer-Encoding")) != 0) - { - if (qse_mbs_cat (proxy->res, key) == (qse_size_t)-1 || - qse_mbs_cat (proxy->res, QSE_MT(": ")) == (qse_size_t)-1 || - qse_mbs_cat (proxy->res, val) == (qse_size_t)-1 || - qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) - { - proxy->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; - } - } - - return 0; -} - static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res) { proxy_peer_htrd_xtn_t* xtn; @@ -2741,6 +2890,8 @@ static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res) xtn = (proxy_peer_htrd_xtn_t*) qse_htrd_getxtn (htrd); proxy = xtn->proxy; + QSE_ASSERT (!(res->state & QSE_HTRE_DISCARDED)); + if (proxy->resflags & PROXY_RES_RECEIVED_RESHDR) { /* this peek handler is being called again. @@ -2758,12 +2909,16 @@ qse_printf (QSE_T("10000000000000000000000000000 CONTINUE 1000000000000000000000 proxy->resflags &= ~PROXY_RES_AWAIT_100; proxy->resflags |= PROXY_RES_RECEIVED_100; - if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;; - if (qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } /* i don't relay any headers and contents in '100 continue' * back to the client */ @@ -2787,6 +2942,17 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n")); proxy->resflags |= PROXY_RES_PEER_LENGTH; proxy->peer_output_length = res->attr.content_length; } + else if (res->state & QSE_HTRE_COMPLETED) + { + /* the response from the peer is chunked or + * should be read until disconnection. + * but the whold response has already been + * received. so i dont' have to do complex + * chunking or something when returning the + * response back to the client. */ + proxy->resflags |= PROXY_RES_PEER_LENGTH | PROXY_RES_PEER_LENGTH_FAKE; + proxy->peer_output_length = qse_htre_getcontentlen(res); + } else { if (qse_comparehttpversions (&proxy->version, &http_v11) >= 0) @@ -2812,7 +2978,10 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n")); { /* client doesn't support chunking */ keepalive = 0; - proxy->resflags |= PROXY_RES_CLIENT_DISCON; + + /* mark that the connection to client should be closed */ + proxy->resflags |= PROXY_RES_CLIENT_DISCON; + /* and push the actual disconnection task */ if (qse_httpd_entaskdisconnect (proxy->httpd, xtn->client, xtn->task) == QSE_NULL) return -1; if (res->attr.flags & QSE_HTRE_ATTR_CHUNKED) @@ -2829,20 +2998,53 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n")); qse_mchar_t vbuf[64]; snprintf (vbuf, QSE_COUNTOF(vbuf), QSE_MT("HTTP/%d.%d"), (int)proxy->version.major, (int)proxy->version.minor); - if (qse_mbs_cat (proxy->res, vbuf) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (proxy->res, vbuf) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } } else { - if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + + if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; } - if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;; - if (qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; /* end initial line */ - if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) + if (proxy->resflags & PROXY_RES_PEER_LENGTH_FAKE) + { + qse_mchar_t buf[64]; + + /* length should be added by force. + * let me add Content-Length event if it's 0 + * for less code */ + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), + proxy->peer_output_length, + 10, -1, QSE_MT('\0'), QSE_NULL); + + if (qse_mbs_cat (proxy->res, QSE_MT("Content-Length: ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) + { + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + else if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { if (qse_mbs_cat (proxy->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) { @@ -2861,7 +3063,6 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n")); /* end of headers */ if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; - /* content body begins here */ proxy->peer_output_received = qse_htre_getcontentlen(res); if ((proxy->resflags & PROXY_RES_PEER_LENGTH) && @@ -2881,10 +3082,16 @@ qse_printf (QSE_T("PROXY PEER FUCKED - RETURNING TOO MUCH...\n")); if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { qse_mchar_t buf[64]; - snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)proxy->peer_output_received); + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), + proxy->peer_output_received, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + if (qse_mbs_cat (proxy->res, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1 || qse_mbs_ncat (proxy->res, qse_htre_getcontentptr(res), qse_htre_getcontentlen(res)) == (qse_size_t)-1 || - qse_mbs_ncat (proxy->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) + qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) { proxy->httpd->errnum = QSE_HTTPD_ENOMEM; return -1; @@ -2918,7 +3125,7 @@ qse_printf (QSE_T("FINISHED READING RESPONSE...\n")); return 0; } -static qse_htrd_recbs_t proxy_htrd_cbs = +static qse_htrd_recbs_t proxy_peer_htrd_cbs = { proxy_htrd_peek_peer_output, proxy_htrd_handle_peer_output @@ -2996,18 +3203,6 @@ qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO PROXY\n")); } } -static int add_header_to_proxy_fwdbuf (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) -{ - task_proxy_t* proxy = (task_proxy_t*)ctx; - - if (qse_mbs_cat (proxy->reqfwdbuf, key) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT(": ")) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->reqfwdbuf, val) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; - - return 0; -} - static int task_init_proxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -3015,6 +3210,7 @@ static int task_init_proxy ( task_proxy_arg_t* arg; qse_size_t len; const qse_mchar_t* ptr; + int snatch_needed; proxy = (task_proxy_t*)qse_httpd_gettaskxtn (httpd, task); arg = (task_proxy_arg_t*)task->ctx; @@ -3049,84 +3245,122 @@ len = 1024; if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getverstr(arg->req)) == (qse_size_t)-1) goto oops; if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; - if (qse_htre_walkheaders (arg->req, add_header_to_proxy_fwdbuf, proxy) <= -1) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; + if (qse_htre_walkheaders (arg->req, proxy_capture_client_header, proxy) <= -1) goto oops; proxy->resflags |= PROXY_RES_AWAIT_RESHDR; - if (arg->req->attr.expect && + if ((arg->req->attr.flags & QSE_HTRE_ATTR_EXPECT100) && (arg->req->version.major > 1 || (arg->req->version.major == 1 && arg->req->version.minor >= 1))) { - if (qse_mbscasecmp(arg->req->attr.expect, QSE_MT("100-continue")) == 0) + proxy->resflags |= PROXY_RES_AWAIT_100; + } + + snatch_needed = 0; + if (arg->req->state & QSE_HTRE_DISCARDED) + { + /* no content to add */ + /*if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: 0\r\n")) == (qse_size_t)-1) goto oops;*/ + + /* i don't also add chunk traiers if the + * request content has been discarded */ + + /* end of header */ + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; + } + else if (arg->req->state & QSE_HTRE_COMPLETED) + { + qse_mchar_t buf[64]; + + len = qse_htre_getcontentlen(arg->req); + + if (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED) { - proxy->resflags |= PROXY_RES_AWAIT_100; + /* add trailers if any */ + if (qse_htre_walktrailers ( + arg->req, proxy_capture_client_trailer, proxy) <= -1) goto oops; + } + + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 10, -1, QSE_MT('\0'), QSE_NULL); + + /* force-insert content-length. content-length is added + * even if the original request dones't contain it */ + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) goto oops; + + if (len > 0) + { + /* content */ + ptr = qse_htre_getcontentptr(arg->req); + if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) goto oops; } } - - if (arg->req->state & QSE_HTRE_DISCARDED) goto done; - - len = qse_htre_getcontentlen(arg->req); - if ((arg->req->state & QSE_HTRE_COMPLETED) && len <= 0) + else if (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) { - /* the content part is completed and no content - * in the content buffer. there is nothing to forward */ - goto done; - } + qse_mchar_t buf[64]; + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), + arg->req->attr.content_length, + 10, -1, QSE_MT('\0'), QSE_NULL); - if (!(arg->req->state & QSE_HTRE_COMPLETED) && - !(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH)) - { - /* if the request is not completed and doesn't have - * content-length set, it's not really possible to - * pass the content. this function, however, allows - * such a request to entask a proxy script dropping the - * content */ - qse_htre_discardcontent (arg->req); + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) goto oops; + + len = qse_htre_getcontentlen(arg->req); + if (len > 0) + { + /* content received so far */ + ptr = qse_htre_getcontentptr(arg->req); + if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) goto oops; + } + + snatch_needed = 1; } else - { - /* create a buffer to hold request content from the client - * and copy content received already */ - ptr = qse_htre_getcontentptr(arg->req); - if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) goto oops; + { + QSE_ASSERT (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED); - if (arg->req->state & QSE_HTRE_COMPLETED) - { - /* no furthur forwarding is needed. - * even a chunked request entaksed when completed - * should reach here. if content-length is set - * the length should match len. */ - QSE_ASSERT (len > 0); - QSE_ASSERT (!(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) || - ((arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) && - arg->req->attr.content_length == len)); - } - else - { - /* proxy entasking is invoked probably from the peek handler - * that was triggered after the request header is received. - * you can know this because the request is not completed. - * In this case, arrange to forward content - * bypassing the buffer in the request object itself. */ + proxy->reqflags |= PROXY_REQ_CHUNKED; + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 /* end of header */) goto oops; -/* TODO: callback chain instead of a single pointer??? - if the request is already set up with a callback, something will go wrong. -*/ - /* set up a callback to be called when the request content - * is fed to the htrd reader. qse_htre_addcontent() that - * htrd calls invokes this callback. */ - proxy->req = arg->req; - qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task); - QSE_ASSERT (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH); + len = qse_htre_getcontentlen(arg->req); + if (len > 0) + { + qse_mchar_t buf[64]; + + ptr = qse_htre_getcontentptr(arg->req); + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + + /* chunk length and chunk content */ + if (qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || + qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; } + + snatch_needed = 1; + } + + if (snatch_needed) + { + /* set up a callback to be called when the request content + * is fed to the htrd reader. qse_htre_addcontent() that + * htrd calls invokes this callback. */ + proxy->req = arg->req; + qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task); } -done: /* no triggers yet since the main loop doesn't allow me to set * triggers in the task initializer. however the main task handler * will be invoked so long as the client handle is writable by * the main loop. */ - qse_printf (QSE_T("GOING TO PROXY [%hs]\n"), QSE_MBS_PTR(proxy->reqfwdbuf)); task->ctx = proxy; return 0; @@ -3142,6 +3376,8 @@ oops: } proxy->init_failed = 1; task->ctx = proxy; + + proxy->httpd->errnum = QSE_HTTPD_ENOMEM; return 0; } @@ -3349,21 +3585,25 @@ qse_printf (QSE_T("[proxy-3 send failure....\n")); proxy->res_consumed += n; proxy->res_pending -= n; -/* TODO: compact buffer */ } if (proxy->res_pending <= 0) { qse_mbs_clear (proxy->res); + proxy->res_consumed = 0; if ((proxy->resflags & PROXY_RES_CLIENT_CHUNK) || ((proxy->resflags & PROXY_RES_PEER_LENGTH) && proxy->peer_output_received >= proxy->peer_output_length)) { +qse_printf (QSE_T("SWITINCG TO 55555555555555555555555555 %d %d %d %d\n"), + (proxy->resflags & PROXY_RES_CLIENT_CHUNK), (proxy->resflags & PROXY_RES_PEER_LENGTH), + (int)proxy->peer_output_received, (int)proxy->peer_output_length); task->main = task_main_proxy_5; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } else { +qse_printf (QSE_T("SWITINCG TO 4444444444444444444444444444\n")); task->main = task_main_proxy_4; task->trigger[2].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; } @@ -3381,6 +3621,7 @@ static int task_main_proxy_2 ( task_proxy_t* proxy = (task_proxy_t*)task->ctx; int http_errnum = 0; +qse_printf (QSE_T("task_main_proxy_2....\n")); if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { proxy_forward_client_input_to_peer (httpd, task, 0); @@ -3411,11 +3652,18 @@ static int task_main_proxy_2 ( count = proxy->res_pending; if (count > MAX_SEND_SIZE) count = MAX_SEND_SIZE; -qse_printf (QSE_T("[proxy_2 sending %d bytes] [%.*hs]\n"), (int)count, (int)count, &QSE_MBS_CHAR(proxy->res,proxy->res_consumed)); +qse_printf (QSE_T("[proxy_2 sending %d bytes (index %d)] ["), + (int)count, (int)proxy->res_consumed); +{ +int i; +for (i = 0; i < count; i++) qse_printf (QSE_T("%hc"), QSE_MBS_CHAR(proxy->res,proxy->res_consumed+i)); +} +qse_printf (QSE_T("]\n")); + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->cbs->client.send ( httpd, client, - &QSE_MBS_CHAR(proxy->res,proxy->res_consumed), + QSE_MBS_CPTR(proxy->res,proxy->res_consumed), count ); if (n <= -1) @@ -3427,8 +3675,6 @@ qse_printf (QSE_T("[proxy-2 send failure....\n")); proxy->res_consumed += n; proxy->res_pending -= n; -/* TODO: compact buffer */ - if (proxy->res_pending <= 0) { /* '100 Continue' and payload received together @@ -3493,7 +3739,7 @@ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER CLIENT CHUNK\n")); proxy->buflen += n; -qse_printf (QSE_T("#####PROXY FEEDING [%.*hs]\n"), (int)proxy->buflen, proxy->buf); +qse_printf (QSE_T("#####PROXY FEEDING %d [%.*hs]\n"), (int)proxy->buflen, (int)proxy->buflen, proxy->buf); if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1) { /* TODO: logging */ @@ -3528,7 +3774,7 @@ qse_printf (QSE_T("#####INVALID HEADER FROM PEER [%.*hs]\n"), (int)proxy->buflen } else { -qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), &QSE_MBS_CHAR(proxy->res,proxy->res_consumed)); +qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), QSE_MBS_CPTR(proxy->res,proxy->res_consumed)); /* switch to the next phase */ task->main = task_main_proxy_3; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; @@ -3561,6 +3807,7 @@ static int task_main_proxy_1 ( int http_errnum = 500; /* wait for peer to get connected */ +qse_printf (QSE_T("task_main_proxy_1....\n")); if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE || task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) @@ -3626,8 +3873,8 @@ qse_printf (QSE_T("task_main_proxy....\n")); xtn->proxy = proxy; xtn->client = client; xtn->task = task; - qse_htrd_setrecbs (proxy->peer_htrd, &proxy_htrd_cbs); - qse_htrd_setoption (proxy->peer_htrd, QSE_HTRD_RESPONSE); + qse_htrd_setrecbs (proxy->peer_htrd, &proxy_peer_htrd_cbs); + qse_htrd_setoption (proxy->peer_htrd, QSE_HTRD_RESPONSE | QSE_HTRD_TRAILERS); proxy->res = qse_mbs_open (httpd->mmgr, 0, 256); if (proxy->res == QSE_NULL) goto oops; diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 4ac6a2c1..9f65493c 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -49,10 +49,12 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (httpd) #define CLIENT_BAD (1 << 0) #define CLIENT_READY (1 << 1) #define CLIENT_SECURE (1 << 2) -#define CLIENT_HANDLE_READ_IN_MUX (1 << 3) -#define CLIENT_HANDLE_WRITE_IN_MUX (1 << 4) +#define CLIENT_MUTE (1 << 3) +#define CLIENT_MUTE_DELETED (1 << 4) +#define CLIENT_HANDLE_READ_IN_MUX (1 << 5) +#define CLIENT_HANDLE_WRITE_IN_MUX (1 << 6) #define CLIENT_HANDLE_IN_MUX (CLIENT_HANDLE_READ_IN_MUX|CLIENT_HANDLE_WRITE_IN_MUX) -#define CLIENT_TASK_TRIGGER_IN_MUX(i) (1 << ((i) + 5)) +#define CLIENT_TASK_TRIGGER_IN_MUX(i) (1 << ((i) + 7)) static void free_server_list ( qse_httpd_t* httpd, qse_httpd_server_t* server); @@ -276,7 +278,6 @@ static qse_httpd_client_t* new_client ( { qse_httpd_client_t* client; htrd_xtn_t* xtn; - int opt; client = qse_httpd_allocmem (httpd, QSE_SIZEOF(*client)); if (client == QSE_NULL) return QSE_NULL; @@ -291,10 +292,7 @@ static qse_httpd_client_t* new_client ( return QSE_NULL; } - opt = qse_htrd_getoption (client->htrd); - opt |= QSE_HTRD_REQUEST; - opt &= ~QSE_HTRD_RESPONSE; - qse_htrd_setoption (client->htrd, opt); + qse_htrd_setoption (client->htrd, QSE_HTRD_REQUEST | QSE_HTRD_TRAILERS); if (httpd->cbs->client.accepted == QSE_NULL) client->status |= CLIENT_READY; @@ -661,7 +659,7 @@ int qse_httpd_addserver (qse_httpd_t* httpd, const qse_char_t* uri) static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client) { - qse_mchar_t buf[2048]; /* TODO: adjust this buffer size */ + qse_mchar_t buf[4096]; /* TODO: adjust this buffer size */ qse_ssize_t m; QSE_ASSERT (httpd->cbs->client.recv != QSE_NULL); @@ -674,7 +672,7 @@ reread: if (httpd->errnum == QSE_HTTPD_EAGAIN) { /* nothing to read yet. */ -qse_fprintf (QSE_STDERR, QSE_T("Warning: Nothing to read from a client %d\n"), client->handle.i); +qse_printf (QSE_T("Warning: Nothing to read from a client %d\n"), client->handle.i); return 0; /* return ok */ } else if (httpd->errnum == QSE_HTTPD_EINTR) @@ -684,25 +682,38 @@ qse_fprintf (QSE_STDERR, QSE_T("Warning: Nothing to read from a client %d\n"), c else { /* TOOD: if (httpd->errnum == QSE_HTTPD_ENOERR) httpd->errnum = QSE_HTTPD_ECALLBACK; */ -qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), client->handle.i); +qse_printf (QSE_T("Error: failed to read from a client %d\n"), client->handle.i); /* TODO: find a way to disconnect */ return -1; } } else if (m == 0) { - httpd->errnum = QSE_HTTPD_EDISCON; -qse_fprintf (QSE_STDERR, QSE_T("Debug: connection closed %d\n"), client->handle.i); - return -1; +qse_printf (QSE_T("Debug: connection closed %d - errno %d\n"), client->handle.i, errno); + if (client->task.head) + { + /* there is still more tasks to finish */ + client->status |= CLIENT_MUTE; + return 0; + } + else + { + httpd->errnum = QSE_HTTPD_EDISCON; + return -1; + } } /* feed may have called the request callback multiple times... * that's because we don't know how many valid requests * are included in 'buf' */ -qse_fprintf (QSE_STDERR, QSE_T("Debug: read from a client %d\n"), client->handle.i); - httpd->errnum = QSE_HTTPD_ENOERR; -qse_printf (QSE_T("!!!!!FEEDING [%.*hs]\n"), (int)m, buf); +qse_printf (QSE_T("!!!!!FEEDING %d from %d ["), (int)m, (int)client->handle.i); +{ +int i; +for (i = 0; i < m; i++) qse_printf (QSE_T("%hc"), buf[i]); +} +qse_printf (QSE_T("]\n")); + if (qse_htrd_feed (client->htrd, buf, m) <= -1) { if (httpd->errnum == QSE_HTTPD_ENOERR) @@ -713,7 +724,12 @@ qse_printf (QSE_T("!!!!!FEEDING [%.*hs]\n"), (int)m, buf); else httpd->errnum = QSE_HTTPD_ENOMEM; /* TODO: better translate error code */ } -qse_fprintf (QSE_STDERR, QSE_T("Error: http error while processing \n")); +qse_printf (QSE_T("Error: http error while processing %d ["), (int)client->handle.i); +{ +int i; +for (i = 0; i < m; i++) qse_printf (QSE_T("%hc"), buf[i]); +} +qse_printf (QSE_T("]\n")); return -1; } @@ -731,19 +747,31 @@ static int invoke_client_task ( /* TODO: handle comparison callback ... */ if (handle.i == client->handle.i && (mask & QSE_HTTPD_MUX_READ)) /* TODO: no direct comparision */ { - if (read_from_client (httpd, client) <= -1) + if (!(client->status & CLIENT_MUTE) && + read_from_client (httpd, client) <= -1) { /* return failure on disconnection also in order to * purge the client in perform_client_task(). * thus the following line isn't necessary. *if (httpd->errnum == QSE_HTTPD_EDISCON) return 0;*/ +qse_printf (QSE_T("ERROR: read from client [%d] failed...\n"), (int)handle.i); return -1; } } /* this client doesn't have any task */ task = client->task.head; - if (task == QSE_NULL) return 0; + if (task == QSE_NULL) + { + if (client->status & CLIENT_MUTE) + { + /* handle this delayed client disconnection */ +qse_printf (QSE_T("ERROR: mute client got no more task [%d] failed...\n"), (int)client->handle.i); + return -1; + } + + return 0; + } trigger_fired = 0; client_handle_writable = 0; @@ -793,7 +821,6 @@ qse_printf (QSE_T("task returend %d\n"), n); * from the mux. so i don't clear them explicitly here */ dequeue_task (httpd, client); - mux_mask = QSE_HTTPD_MUX_READ; mux_status = CLIENT_HANDLE_READ_IN_MUX; if (client->task.head) @@ -802,6 +829,23 @@ qse_printf (QSE_T("task returend %d\n"), n); * trigger it as if it is just entasked */ mux_mask |= QSE_HTTPD_MUX_WRITE; mux_status |= CLIENT_HANDLE_WRITE_IN_MUX; + + if (client->status & CLIENT_MUTE) + { +qse_printf (QSE_T("REMOVING XXXXX FROM READING....\n")); + mux_mask &= ~QSE_HTTPD_MUX_READ; + mux_status &= ~CLIENT_HANDLE_READ_IN_MUX; + } + } + else + { + if (client->status & CLIENT_MUTE) + { + /* no more task. but this client + * has closed connection previously */ +qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n")); + return -1; + } } if ((client->status & CLIENT_HANDLE_IN_MUX) != @@ -810,10 +854,16 @@ qse_printf (QSE_T("task returend %d\n"), n); httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle); client->status &= ~CLIENT_HANDLE_IN_MUX; - if (httpd->cbs->mux.addhnd ( - httpd, httpd->mux, client->handle, - mux_mask, perform_client_task, client) <= -1) return -1; - client->status |= mux_status; + if (mux_status) + { + if (httpd->cbs->mux.addhnd ( + httpd, httpd->mux, client->handle, + mux_mask, perform_client_task, client) <= -1) + { + return -1; + } + client->status |= mux_status; + } } QSE_MEMSET (client->trigger, 0, QSE_SIZEOF(client->trigger)); @@ -832,7 +882,8 @@ qse_printf (QSE_T("task returend %d\n"), n); QSE_HTTPD_TASK_TRIGGER_WRITABLE); } - if (QSE_MEMCMP (client->trigger, task->trigger, QSE_SIZEOF(client->trigger)) != 0) + if (QSE_MEMCMP (client->trigger, task->trigger, QSE_SIZEOF(client->trigger)) != 0 || + ((client->status & CLIENT_MUTE) && !(client->status & CLIENT_MUTE_DELETED))) { /* manipulate muxtiplexer settings if there are trigger changes */ @@ -852,14 +903,30 @@ qse_printf (QSE_T("task returend %d\n"), n); } has_trigger = 0; - client_handle_mux_mask = QSE_HTTPD_MUX_READ; + client_handle_mux_mask = 0; + client_handle_mux_status = 0; + if (client->status & CLIENT_MUTE) + { + client->status |= CLIENT_MUTE_DELETED; + } + else + { + client_handle_mux_mask |= QSE_HTTPD_MUX_READ; + client_handle_mux_status |= CLIENT_HANDLE_READ_IN_MUX; + } /* add new trigger handles */ for (i = 0; i < QSE_COUNTOF(task->trigger); i++) { trigger_mux_mask = 0; if (task->trigger[i].mask & QSE_HTTPD_TASK_TRIGGER_READ) - trigger_mux_mask |= QSE_HTTPD_MUX_READ; + { + if (task->trigger[i].handle.i != client->handle.i || + !(client->status & CLIENT_MUTE)) + { + trigger_mux_mask |= QSE_HTTPD_MUX_READ; + } + } if (task->trigger[i].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) trigger_mux_mask |= QSE_HTTPD_MUX_WRITE; @@ -878,15 +945,15 @@ qse_printf (QSE_T("task returend %d\n"), n); { if (httpd->cbs->mux.addhnd ( httpd, httpd->mux, task->trigger[i].handle, - trigger_mux_mask, perform_client_task, client) <= -1) return -1; + trigger_mux_mask, perform_client_task, client) <= -1) + { + return -1; + } client->status |= CLIENT_TASK_TRIGGER_IN_MUX(i); } } } - /* manipulate the client handle. reading is always enabled - * on the cleint handle */ - client_handle_mux_status = CLIENT_HANDLE_READ_IN_MUX; if (client_handle_mux_mask) { /* if the client handle is included in the trigger @@ -908,10 +975,16 @@ qse_printf (QSE_T("task returend %d\n"), n); httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle); client->status &= ~CLIENT_HANDLE_IN_MUX; - if (httpd->cbs->mux.addhnd ( - httpd, httpd->mux, client->handle, - client_handle_mux_mask, perform_client_task, client) <= -1) return -1; - client->status |= client_handle_mux_status; + if (client_handle_mux_mask) + { + if (httpd->cbs->mux.addhnd ( + httpd, httpd->mux, client->handle, + client_handle_mux_mask, perform_client_task, client) <= -1) + { + return -1; + } + client->status |= client_handle_mux_status; + } } QSE_MEMCPY (client->trigger, task->trigger, QSE_SIZEOF(client->trigger)); @@ -949,13 +1022,17 @@ static int perform_client_task ( qse_gettime (&client->last_active); /* TODO: error check??? */ move_client_to_tail (httpd, client); - if (invoke_client_task (httpd, client, handle, mask) <= -1) goto oops; + if (invoke_client_task (httpd, client, handle, mask) <= -1) + { +qse_printf (QSE_T("OOPS AFTER CLIENT TASK BAD XXXXXXXXXXXXXX [%d]\n"), (int)handle.i); + goto oops; + } } return 0; oops: -qse_printf (QSE_T("MARKING BAD XXXXXXXXXXXXXX\n")); +qse_printf (QSE_T("MARKING BAD XXXXXXXXXXXXXX [%d]\n"), (int)handle.i); /*purge_client (httpd, client);*/ client->status |= CLIENT_BAD; client->bad_next = httpd->client.bad; diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index e74ffdc3..777ae501 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -892,7 +892,7 @@ static qse_ssize_t client_recv ( } else { - ssize_t ret = read (client->handle.i, buf, bufsize); + ssize_t ret = recv (client->handle.i, buf, bufsize, 0); if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); return ret; } @@ -916,7 +916,7 @@ static qse_ssize_t client_send ( } else { - ssize_t ret = write (client->handle.i, buf, bufsize); + ssize_t ret = send (client->handle.i, buf, bufsize, 0); if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); return ret; } @@ -999,7 +999,14 @@ static void client_closed (qse_httpd_t* httpd, qse_httpd_client_t* client) /* ------------------------------------------------------------------- */ static qse_htb_walk_t walk (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx) { -qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)QSE_HTB_VLEN(pair), QSE_HTB_VPTR(pair)); + qse_htre_hdrval_t* val; + + val = QSE_HTB_VPTR(pair); + while (val) + { +qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)val->len, val->ptr); + val = val->next; + } return QSE_HTB_WALK_FORWARD; } @@ -1042,7 +1049,7 @@ if (qse_htre_getcontentlen(req) > 0) qse_httpd_discardcontent (httpd, req); } - if (req->attr.expect && + if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) && (req->version.major > 1 || (req->version.major == 1 && req->version.minor >= 1)) && !content_received) @@ -1051,24 +1058,14 @@ if (qse_htre_getcontentlen(req) > 0) /* "expect" in the header, version 1.1 or higher, * and no content received yet */ - if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0) - { - if (qse_httpd_entaskerror ( - httpd, client, QSE_NULL, 417, req) == QSE_NULL) return -1; - if (qse_httpd_entaskdisconnect ( - httpd, client, QSE_NULL) == QSE_NULL) return -1; - } - else - { /* TODO: determine if to return 100-continue or other errors */ { qse_ntime_t now; qse_gettime (&now); qse_printf (QSE_T("entasking continue at %lld\n"), (long long)now); } - if (qse_httpd_entaskcontinue ( - httpd, client, QSE_NULL, req) == QSE_NULL) return -1; - } + if (qse_httpd_entaskcontinue ( + httpd, client, QSE_NULL, req) == QSE_NULL) return -1; } } @@ -1131,7 +1128,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); { if (peek) { - const qse_mchar_t* auth; + const qse_htre_hdrval_t* auth; int authorized = 0; auth = qse_htre_getheaderval (req, QSE_MT("Authorization")); @@ -1139,6 +1136,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); { /* TODO: PERFORM authorization... */ /* BASE64 decode... */ + while (auth->next) auth = auth->next; authorized = 1; } @@ -1436,6 +1434,7 @@ int qse_main (int argc, qse_achar_t* argv[]) setlocale (LC_ALL, ""); qse_setdflcmgr (qse_slmbcmgr); #endif + return qse_runmain (argc, argv, httpd_main); }