changed how to handle http header fields.

fixed a few http proxying bugs.
This commit is contained in:
hyung-hwan 2012-04-11 15:18:51 +00:00
parent 654003e06d
commit 130bbe9e21
10 changed files with 717 additions and 336 deletions

View File

@ -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)

View File

@ -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;

View File

@ -25,8 +25,15 @@
#include <qse/cmn/htb.h>
#include <qse/cmn/str.h>
/*
* 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
);

View File

@ -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
);

View File

@ -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;

View File

@ -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)
);

View File

@ -21,14 +21,47 @@
#include <qse/net/htre.h>
#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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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);
}