enhanced proxying to handle protocol upgrade.

enhanced qse_mbsword() and its family to accept an extra delimiter
This commit is contained in:
hyung-hwan 2014-10-08 14:07:55 +00:00
parent 8a32cbc4f1
commit 1df4e0bc9f
10 changed files with 310 additions and 187 deletions

View File

@ -1584,40 +1584,52 @@ QSE_EXPORT qse_wchar_t* qse_wcsxnrcasestr (
const qse_mchar_t* qse_mbsword (
const qse_mchar_t* str,
const qse_mchar_t* word
const qse_mchar_t* word,
qse_mchar_t extra_delim
);
const qse_wchar_t* qse_wcsword (
const qse_wchar_t* str,
const qse_wchar_t* word
const qse_wchar_t* word,
qse_wchar_t extra_delim
);
/**
* The qse_mbsxword() function finds a whole word in a string.
* The word can be delimited by white spaces or an extra delimiter
* \a extra_delim. Pass QSE_MT('\0') if no extra delimiter is
* needed.
*/
const qse_mchar_t* qse_mbsxword (
const qse_mchar_t* str,
qse_size_t len,
const qse_mchar_t* word
const qse_mchar_t* word,
qse_mchar_t extra_delim
);
/**
* The qse_wcsxword() function finds a whole word in a string.
* The word can be delimited by white spaces or an extra delimiter
* \a extra_delim. Pass QSE_WT('\0') if no extra delimiter is
* needed.
*/
const qse_wchar_t* qse_wcsxword (
const qse_wchar_t* str,
qse_size_t len,
const qse_wchar_t* word
const qse_wchar_t* word,
qse_wchar_t extra_delim
);
const qse_mchar_t* qse_mbscaseword (
const qse_mchar_t* str,
const qse_mchar_t* word
const qse_mchar_t* word,
qse_mchar_t extra_delim
);
const qse_wchar_t* qse_wcscaseword (
const qse_wchar_t* str,
const qse_wchar_t* word
const qse_wchar_t* word,
qse_wchar_t extra_delim
);
/**
@ -1627,7 +1639,8 @@ const qse_wchar_t* qse_wcscaseword (
const qse_mchar_t* qse_mbsxcaseword (
const qse_mchar_t* str,
qse_size_t len,
const qse_mchar_t* word
const qse_mchar_t* word,
qse_mchar_t extra_delim
);
/**
@ -1637,19 +1650,20 @@ const qse_mchar_t* qse_mbsxcaseword (
const qse_wchar_t* qse_wcsxcaseword (
const qse_wchar_t* str,
qse_size_t len,
const qse_wchar_t* word
const qse_wchar_t* word,
qse_wchar_t extra_delim
);
#if defined(QSE_CHAR_IS_MCHAR)
# define qse_strword(str,word) qse_mbsword(str,word)
# define qse_strxword(str,len,word) qse_mbsxword(str,len,word)
# define qse_strcaseword(str,word) qse_mbscaseword(str,word)
# define qse_strxcaseword(str,len,word) qse_mbsxcaseword(str,len,word)
# define qse_strword(str,word,edelim) qse_mbsword(str,word,edelim)
# define qse_strxword(str,len,word,edelim) qse_mbsxword(str,len,word,edelim)
# define qse_strcaseword(str,word,edelim) qse_mbscaseword(str,word,edelim)
# define qse_strxcaseword(str,len,word,edelim) qse_mbsxcaseword(str,len,word,edelim)
#else
# define qse_strword(str,word) qse_wcsword(str,word)
# define qse_strxword(str,len,word) qse_wcsxword(str,len,word)
# define qse_strcaseword(str,word) qse_wcscaseword(str,word)
# define qse_strxcaseword(str,len,word) qse_wcsxcaseword(str,len,word)
# define qse_strword(str,word,edelim) qse_wcsword(str,word,edelim)
# define qse_strxword(str,len,word,edelim) qse_wcsxword(str,len,word,edelim)
# define qse_strcaseword(str,word,edelim) qse_wcscaseword(str,word,edelim)
# define qse_strxcaseword(str,len,word,edelim) qse_wcsxcaseword(str,len,word,edelim)
#endif
/**

View File

@ -38,7 +38,8 @@ enum qse_htrd_errnum_t
QSE_HTRD_EBADRE,
QSE_HTRD_EBADHDR,
QSE_HTRD_ERECBS,
QSE_HTRD_ECONCB
QSE_HTRD_ECONCB,
QSE_HTRD_ESUSPENDED
};
typedef enum qse_htrd_errnum_t qse_htrd_errnum_t;
@ -56,8 +57,7 @@ enum qse_htrd_option_t
QSE_HTRD_REQUEST = (1 << 4), /**< parse input as a request */
QSE_HTRD_RESPONSE = (1 << 5), /**< parse input as a response */
QSE_HTRD_TRAILERS = (1 << 6), /**< store trailers in a separate table */
QSE_HTRD_STRICT = (1 << 7), /**< be more picky */
QSE_HTRD_DUMMY = (1 << 8) /**< be dummy */
QSE_HTRD_STRICT = (1 << 7) /**< be more picky */
};
typedef enum qse_htrd_option_t qse_htrd_option_t;
@ -75,6 +75,7 @@ struct qse_htrd_t
qse_mmgr_t* mmgr;
qse_htrd_errnum_t errnum;
int option;
int flags;
const qse_htrd_recbs_t* recbs;
@ -149,7 +150,7 @@ QSE_EXPORT void qse_htrd_clear (
);
QSE_EXPORT int qse_htrd_getoption (
qse_htrd_t* htrd
qse_htrd_t* htrd
);
QSE_EXPORT void qse_htrd_setoption (
@ -185,6 +186,22 @@ QSE_EXPORT int qse_htrd_halt (
qse_htrd_t* htrd
);
QSE_EXPORT void qse_htrd_suspend (
qse_htrd_t* htrd
);
QSE_EXPORT void qse_htrd_resume (
qse_htrd_t* htrd
);
QSE_EXPORT void qse_htrd_dummify (
qse_htrd_t* htrd
);
QSE_EXPORT void qse_htrd_undummify (
qse_htrd_t* htrd
);
QSE_EXPORT int qse_htrd_scanqparam (
qse_htrd_t* http,
const qse_mcstr_t* cstr

View File

@ -21,32 +21,34 @@
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
const qse_mchar_t* qse_mbsword (
const qse_mchar_t* str, const qse_mchar_t* word)
#define IS_MDELIM(x,delim) (QSE_ISMSPACE(x) || (x) == delim)
#define IS_WDELIM(x,delim) (QSE_ISWSPACE(x) || (x) == delim)
const qse_mchar_t* qse_mbsword (const qse_mchar_t* str, const qse_mchar_t* word, qse_mchar_t extra_delim)
{
/* find a full word in a string */
const qse_mchar_t* ptr = str;
if (extra_delim == QSE_MT('\0')) extra_delim = QSE_MT(' ');
do
{
const qse_mchar_t* s;
while (QSE_ISMSPACE(*ptr)) ptr++;
while (IS_MDELIM(*ptr,extra_delim)) ptr++;
if (*ptr == QSE_MT('\0')) return QSE_NULL;
s = ptr;
while (*ptr != QSE_MT('\0') && !QSE_ISMSPACE(*ptr)) ptr++;
while (*ptr != QSE_MT('\0') && !IS_MDELIM(*ptr,extra_delim)) ptr++;
if (qse_mbsxcmp (s, ptr-s, word) == 0) return s;
if (qse_mbsxcmp (s, ptr - s, word) == 0) return s;
}
while (*ptr != QSE_MT('\0'));
return QSE_NULL;
}
const qse_mchar_t* qse_mbsxword (
const qse_mchar_t* str, qse_size_t len, const qse_mchar_t* word)
const qse_mchar_t* qse_mbsxword (const qse_mchar_t* str, qse_size_t len, const qse_mchar_t* word, qse_mchar_t extra_delim)
{
/* find a full word in a string */
@ -54,93 +56,93 @@ const qse_mchar_t* qse_mbsxword (
const qse_mchar_t* end = str + len;
const qse_mchar_t* s;
if (extra_delim == QSE_MT('\0')) extra_delim = QSE_MT(' ');
do
{
while (ptr < end && QSE_ISMSPACE(*ptr)) ptr++;
while (ptr < end && IS_MDELIM(*ptr,extra_delim)) ptr++;
if (ptr >= end) return QSE_NULL;
s = ptr;
while (ptr < end && !QSE_ISMSPACE(*ptr)) ptr++;
while (ptr < end && !IS_MDELIM(*ptr,extra_delim)) ptr++;
if (qse_mbsxcmp (s, ptr-s, word) == 0) return s;
if (qse_mbsxcmp (s, ptr - s, word) == 0) return s;
}
while (ptr < end);
return QSE_NULL;
}
const qse_mchar_t* qse_mbscaseword (
const qse_mchar_t* str, const qse_mchar_t* word)
const qse_mchar_t* qse_mbscaseword (const qse_mchar_t* str, const qse_mchar_t* word, qse_mchar_t extra_delim)
{
/* find a full word in a string */
const qse_mchar_t* ptr = str;
if (extra_delim == QSE_MT('\0')) extra_delim = QSE_MT(' ');
do
{
const qse_mchar_t* s;
while (QSE_ISMSPACE(*ptr)) ptr++;
while (IS_MDELIM(*ptr,extra_delim)) ptr++;
if (*ptr == QSE_MT('\0')) return QSE_NULL;
s = ptr;
while (*ptr != QSE_MT('\0') && !QSE_ISMSPACE(*ptr)) ptr++;
while (*ptr != QSE_MT('\0') && !IS_MDELIM(*ptr,extra_delim)) ptr++;
if (qse_mbsxcasecmp (s, ptr-s, word) == 0) return s;
if (qse_mbsxcasecmp (s, ptr - s, word) == 0) return s;
}
while (*ptr != QSE_MT('\0'));
return QSE_NULL;
}
const qse_mchar_t* qse_mbsxcaseword (
const qse_mchar_t* str, qse_size_t len, const qse_mchar_t* word)
const qse_mchar_t* qse_mbsxcaseword (const qse_mchar_t* str, qse_size_t len, const qse_mchar_t* word, qse_mchar_t extra_delim)
{
const qse_mchar_t* ptr = str;
const qse_mchar_t* end = str + len;
const qse_mchar_t* s;
if (extra_delim == QSE_MT('\0')) extra_delim = QSE_MT(' ');
do
{
while (ptr < end && QSE_ISMSPACE(*ptr)) ptr++;
while (ptr < end && IS_MDELIM(*ptr,extra_delim)) ptr++;
if (ptr >= end) return QSE_NULL;
s = ptr;
while (ptr < end && !QSE_ISMSPACE(*ptr)) ptr++;
while (ptr < end && !IS_MDELIM(*ptr,extra_delim)) ptr++;
if (qse_mbsxcasecmp (s, ptr-s, word) == 0) return s;
if (qse_mbsxcasecmp (s, ptr - s, word) == 0) return s;
}
while (ptr < end);
return QSE_NULL;
}
const qse_wchar_t* qse_wcsword (
const qse_wchar_t* str, const qse_wchar_t* word)
const qse_wchar_t* qse_wcsword (const qse_wchar_t* str, const qse_wchar_t* word, qse_wchar_t extra_delim)
{
/* find a full word in a string */
const qse_wchar_t* ptr = str;
if (extra_delim == QSE_WT('\0')) extra_delim = QSE_WT(' ');
do
{
const qse_wchar_t* s;
while (QSE_ISWSPACE(*ptr)) ptr++;
while (IS_WDELIM(*ptr,extra_delim)) ptr++;
if (*ptr == QSE_WT('\0')) return QSE_NULL;
s = ptr;
while (*ptr != QSE_WT('\0') && !QSE_ISWSPACE(*ptr)) ptr++;
while (*ptr != QSE_WT('\0') && !IS_WDELIM(*ptr,extra_delim)) ptr++;
if (qse_wcsxcmp (s, ptr-s, word) == 0) return s;
if (qse_wcsxcmp (s, ptr - s, word) == 0) return s;
}
while (*ptr != QSE_WT('\0'));
return QSE_NULL;
}
const qse_wchar_t* qse_wcsxword (
const qse_wchar_t* str, qse_size_t len, const qse_wchar_t* word)
const qse_wchar_t* qse_wcsxword (const qse_wchar_t* str, qse_size_t len, const qse_wchar_t* word, qse_wchar_t extra_delim)
{
/* find a full word in a string */
@ -148,61 +150,62 @@ const qse_wchar_t* qse_wcsxword (
const qse_wchar_t* end = str + len;
const qse_wchar_t* s;
if (extra_delim == QSE_WT('\0')) extra_delim = QSE_WT(' ');
do
{
while (ptr < end && QSE_ISWSPACE(*ptr)) ptr++;
while (ptr < end && IS_WDELIM(*ptr,extra_delim)) ptr++;
if (ptr >= end) return QSE_NULL;
s = ptr;
while (ptr < end && !QSE_ISWSPACE(*ptr)) ptr++;
while (ptr < end && !IS_WDELIM(*ptr,extra_delim)) ptr++;
if (qse_wcsxcmp (s, ptr-s, word) == 0) return s;
if (qse_wcsxcmp (s, ptr - s, word) == 0) return s;
}
while (ptr < end);
return QSE_NULL;
}
const qse_wchar_t* qse_wcscaseword (
const qse_wchar_t* str, const qse_wchar_t* word)
const qse_wchar_t* qse_wcscaseword (const qse_wchar_t* str, const qse_wchar_t* word, qse_wchar_t extra_delim)
{
/* find a full word in a string */
const qse_wchar_t* ptr = str;
if (extra_delim == QSE_WT('\0')) extra_delim = QSE_WT(' ');
do
{
const qse_wchar_t* s;
while (QSE_ISWSPACE(*ptr)) ptr++;
while (IS_WDELIM(*ptr,extra_delim)) ptr++;
if (*ptr == QSE_WT('\0')) return QSE_NULL;
s = ptr;
while (*ptr != QSE_WT('\0') && !QSE_ISWSPACE(*ptr)) ptr++;
while (*ptr != QSE_WT('\0') && !IS_WDELIM(*ptr,extra_delim)) ptr++;
if (qse_wcsxcasecmp (s, ptr-s, word) == 0) return s;
if (qse_wcsxcasecmp (s, ptr - s, word) == 0) return s;
}
while (*ptr != QSE_WT('\0'));
return QSE_NULL;
}
const qse_wchar_t* qse_wcsxcaseword (
const qse_wchar_t* str, qse_size_t len, const qse_wchar_t* word)
const qse_wchar_t* qse_wcsxcaseword (const qse_wchar_t* str, qse_size_t len, const qse_wchar_t* word, qse_wchar_t extra_delim)
{
const qse_wchar_t* ptr = str;
const qse_wchar_t* end = str + len;
const qse_wchar_t* s;
if (extra_delim == QSE_WT('\0')) extra_delim = QSE_WT(' ');
do
{
while (ptr < end && QSE_ISWSPACE(*ptr)) ptr++;
while (ptr < end && IS_WDELIM(*ptr,extra_delim)) ptr++;
if (ptr >= end) return QSE_NULL;
s = ptr;
while (ptr < end && !QSE_ISWSPACE(*ptr)) ptr++;
while (ptr < end && !IS_WDELIM(*ptr,extra_delim)) ptr++;
if (qse_wcsxcasecmp (s, ptr-s, word) == 0) return s;
if (qse_wcsxcasecmp (s, ptr - s, word) == 0) return s;
}
while (ptr < end);

View File

@ -25,8 +25,13 @@
static const qse_mchar_t NUL = QSE_MT('\0');
/* for htrd->fed.s.flags */
#define CONSUME_UNTIL_CLOSE (1 << 0)
/* for htrd->flags */
#define FEEDING_SUSPENDED (1 << 0)
#define FEEDING_DUMMIFIED (1 << 1)
static QSE_INLINE int is_whspace_octet (qse_mchar_t c)
{
return c == QSE_MT(' ') || c == QSE_MT('\t') || c == QSE_MT('\r') || c == QSE_MT('\n');
@ -457,6 +462,7 @@ badre:
void qse_htrd_clear (qse_htrd_t* htrd)
{
clear_feed (htrd);
htrd->flags = 0;
}
qse_mmgr_t* qse_htrd_getmmgr (qse_htrd_t* htrd)
@ -491,21 +497,21 @@ 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;
val = QSE_HTB_VPTR(pair);
while (val->next) val = val->next;
n = qse_mbscmp (val->ptr, QSE_MT("close"));
if (n == 0)
/* The value for Connection: may get comma-separated.
* so use qse_mbscaseword() instead of qse_mbscmp(). */
if (qse_mbscaseword (val->ptr, QSE_MT("close"), QSE_MT(',')))
{
htrd->re.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
return 0;
}
n = qse_mbscmp (val->ptr, QSE_MT("keep-alive"));
if (n == 0)
if (qse_mbscaseword (val->ptr, QSE_MT("keep-alive"), QSE_MT(',')))
{
htrd->re.flags |= QSE_HTRE_ATTR_KEEPALIVE;
return 0;
@ -758,7 +764,7 @@ static qse_htb_pair_t* hdr_cbserter (
* 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
* So i just maintain the list of values for a key instead of
* folding them.
*/
@ -826,7 +832,7 @@ qse_mchar_t* parse_header_field (
{
/* ignore a line without a colon */
p++;
return p;
return p;
}
}
goto badhdr;
@ -945,7 +951,6 @@ static QSE_INLINE int parse_initial_line_and_headers (
}
while (1);
return 0;
}
@ -1061,10 +1066,10 @@ static const qse_mchar_t* get_trailing_headers (
{
while (is_whspace_octet(*p)) p++;
if (*p == '\0') break;
/* TODO: return error if protocol is 0.9.
* HTTP/0.9 must not get headers... */
p = parse_header_field (
htrd, p,
((htrd->option & QSE_HTRD_TRAILERS)? &htrd->re.trailers: &htrd->re.hdrtab)
@ -1107,7 +1112,14 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
QSE_ASSERT (len > 0);
if (htrd->option & QSE_HTRD_DUMMY)
if (htrd->flags & FEEDING_SUSPENDED)
{
htrd->errnum = QSE_HTRD_ESUSPENDED;
return -1;
}
/*if (htrd->option & QSE_HTRD_DUMMY)*/
if (htrd->flags & FEEDING_DUMMIFIED)
{
/* treat everything as contents.
* i don't care about headers or whatsoever. */
@ -1159,13 +1171,13 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
switch (b)
{
case '\0':
case QSE_MT('\0'):
/* guarantee that the request does not contain
* a null character */
htrd->errnum = QSE_HTRD_EBADRE;
return -1;
case '\n':
case QSE_MT('\n'):
{
if (htrd->fed.s.crlf <= 1)
{
@ -1302,7 +1314,6 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
{
htrd->fed.s.need = htrd->re.attr.content_length;
}
}
if (htrd->fed.s.need > 0)
@ -1348,7 +1359,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
htrd->fed.s.need = 0;
}
}
if (htrd->fed.s.chunk.phase == GET_CHUNK_DATA)
{
QSE_ASSERT (htrd->fed.s.need == 0);
@ -1358,7 +1369,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
while (ptr < end && is_space_octet(*ptr)) ptr++;
if (ptr < end)
{
if (*ptr == '\n')
if (*ptr == QSE_MT('\n'))
{
/* end of chunk data. */
ptr++;
@ -1442,10 +1453,17 @@ qse_printf (QSE_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"),
clear_feed (htrd);
if (ptr >= end) return 0; /* no more feeds to handle */
if (htrd->option & QSE_HTRD_DUMMY)
if (htrd->flags & FEEDING_SUSPENDED)
{
htrd->errnum = QSE_HTRD_ESUSPENDED;
return -1;
}
/*if (htrd->option & QSE_HTRD_DUMMY)*/
if (htrd->flags & FEEDING_DUMMIFIED)
{
/* once the mode changes to RAW in a callback,
* left-over is pused as contents */
* left-over is pushed as contents */
if (ptr < end)
return push_content (htrd, ptr, end - ptr);
else
@ -1466,7 +1484,7 @@ qse_printf (QSE_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"),
break;
}
case '\r':
case QSE_MT('\r'):
if (htrd->fed.s.crlf == 0 || htrd->fed.s.crlf == 2)
htrd->fed.s.crlf++;
else htrd->fed.s.crlf = 1;
@ -1533,6 +1551,26 @@ int qse_htrd_halt (qse_htrd_t* htrd)
return 0;
}
void qse_htrd_suspend (qse_htrd_t* htrd)
{
htrd->flags |= FEEDING_SUSPENDED;
}
void qse_htrd_resume (qse_htrd_t* htrd)
{
htrd->flags &= ~FEEDING_SUSPENDED;
}
void qse_htrd_dummify (qse_htrd_t* htrd)
{
htrd->flags |= FEEDING_DUMMIFIED;
}
void qse_htrd_undummify (qse_htrd_t* htrd)
{
htrd->flags &= ~FEEDING_DUMMIFIED;
}
#if 0
int qse_htrd_scanqparam (qse_htrd_t* htrd, const qse_mcstr_t* cstr)
{

View File

@ -504,4 +504,3 @@ qse_mchar_t* qse_perenchttpstrdup (int opt, const qse_mchar_t* str, qse_mmgr_t*
return buf;
}

View File

@ -53,9 +53,12 @@ struct task_proxy_t
#define PROXY_URL_PREREWRITTEN (1 << 12) /* URL has been prerewritten in prerewrite(). */
#define PROXY_URL_REWRITTEN (1 << 13)
#define PROXY_URL_REDIRECTED (1 << 14)
#define PROXY_X_FORWARDED_FOR (1 << 15) /* X-Forwarded-For added */
#define PROXY_VIA (1 << 16) /* Via added to the request */
#define PROXY_VIA_RETURNING (1 << 17) /* Via added to the response */
#define PROXY_X_FORWARDED_FOR (1 << 15) /* X-Forwarded-For: added */
#define PROXY_VIA (1 << 16) /* Via: added to the request */
#define PROXY_VIA_RETURNING (1 << 17) /* Via: added to the response */
#define PROXY_UPGRADE_REQUESTED (1 << 18)
#define PROXY_PROTOCOL_SWITCHED (1 << 19)
#define PROXY_GOT_BAD_REQUEST (1 << 20)
int flags;
qse_httpd_t* httpd;
qse_httpd_client_t* client;
@ -340,8 +343,8 @@ static int proxy_snatch_client_input_raw (
/* this function is never called with ptr of QSE_NULL
* because this callback is set manually after the request
* has been discarded or completed in task_init_proxy() and
* qse_htre_completecontent or qse-htre_discardcontent() is
* not called again. Unlinkw proxy_snatch_client_input(),
* qse_htre_completecontent() or qse_htre_discardcontent() is
* not called again. Unlike proxy_snatch_client_input(),
* it doesn't care about EOF indicated by ptr of QSE_NULL. */
if (ptr && !(proxy->reqflags & PROXY_REQ_FWDERR))
{
@ -660,7 +663,6 @@ static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res)
}
/* end initial line */
if (proxy->resflags & PROXY_RES_PEER_LENGTH_FAKE)
{
/* length should be added by force.
@ -778,6 +780,37 @@ static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res)
}
}
if (proxy->flags & PROXY_UPGRADE_REQUESTED)
{
QSE_ASSERT (proxy->req != QSE_NULL);
if (qse_htre_getscodeval(res) == 101)
{
/* Unlike raw proxying entasked for CONNECT for which disconnection
* is supposed to be scheduled by the caller, protocol upgrade
* can be requested over a normal http stream. A stream whose
* protocol has been switched must not be sustained after the
* task is over. */
if (qse_httpd_entaskdisconnect (httpd, proxy->client, xtn->task) == QSE_NULL) return -1;
proxy->flags |= PROXY_PROTOCOL_SWITCHED;
}
else
{
/* the update request is not granted. restore the reader
* to the original state so that HTTP packets can be handled
* later on. */
qse_htrd_undummify (proxy->client->htrd);
qse_htre_unsetconcb (proxy->req);
proxy->req = QSE_NULL;
}
/* let the reader accept data to be fed */
qse_htrd_resume (proxy->client->htrd);
/*task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE;*/ /* peer */
proxy->task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_READ; /* client-side */
}
proxy->res_pending = QSE_MBS_LEN(proxy->res) - proxy->res_consumed;
return 0;
}
@ -815,11 +848,6 @@ static void proxy_forward_client_input_to_peer (qse_httpd_t* httpd, qse_httpd_ta
/* normal forwarding */
qse_ssize_t n;
#if 0
qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"),
(int)QSE_MBS_LEN(proxy->reqfwdbuf),
QSE_MBS_PTR(proxy->reqfwdbuf));
#endif
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.peer.send (
httpd, &proxy->peer,
@ -854,13 +882,15 @@ to the head all the time.. grow the buffer to a certain limit. */
qse_mbs_del (proxy->reqfwdbuf, 0, n);
if (QSE_MBS_LEN(proxy->reqfwdbuf) <= 0)
{
if (proxy->req == QSE_NULL) goto done;
else task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE;
/* the forwarding buffer is emptied after sending. */
if (!proxy->req) goto done;
task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE;
}
}
}
}
else if (proxy->req == QSE_NULL)
else if (!proxy->req)
{
done:
/* there is nothing to read from the client side and
@ -1011,7 +1041,7 @@ static int task_init_proxy (
/* the caller must make sure that the actual content is discarded or completed
* and the following data is treated as contents */
QSE_ASSERT (arg->req->state & (QSE_HTRE_DISCARDED | QSE_HTRE_COMPLETED));
QSE_ASSERT (qse_htrd_getoption(client->htrd) & QSE_HTRD_DUMMY);
/*QSE_ASSERT (qse_htrd_getoption(client->htrd) & QSE_HTRD_DUMMY);*/
proxy->req = arg->req;
qse_htre_setconcb (proxy->req, proxy_snatch_client_input_raw, task);
@ -1228,13 +1258,55 @@ qse_mbs_ncat (proxy->reqfwdbuf, spc, QSE_COUNTOF(spc));
snatch_needed = 1;
}
if (snatch_needed)
if (qse_htre_getheaderval(arg->req, QSE_MT("Upgrade")))
{
/* 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. */
/* Upgrade: is found in the request header */
const qse_htre_hdrval_t* hv;
hv = qse_htre_getheaderval(arg->req, QSE_MT("Connection"));
while (hv)
{
if (qse_mbscaseword (hv->ptr, QSE_MT("Upgrade"), QSE_MT(','))) break;
hv = hv->next;
}
if (!hv) goto no_upgrade;
if (snatch_needed)
{
/* The upgrade request can't have contents.
* Not allowing contents makes implementation easier. */
httpd->errnum = QSE_HTTPD_EBADREQ;
proxy->flags |= PROXY_GOT_BAD_REQUEST;
goto oops;
}
/* cause feeding of client data to fail. i do this because
* it's unknown if upgrade will get granted or not.
* if it's granted, the client input should be treated
* as an octet string. If not, it should still be handled
* as HTTP. */
qse_htrd_suspend (client->htrd);
/* prearrange to not parse client input when feeding is resumed */
qse_htrd_dummify (client->htrd);
proxy->flags |= PROXY_UPGRADE_REQUESTED;
proxy->req = arg->req;
qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task);
/* prearrange to capature client input when feeding is resumed */
qse_htre_setconcb (proxy->req, proxy_snatch_client_input_raw, proxy->task);
}
else
{
no_upgrade:
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);
}
}
}
@ -1253,9 +1325,6 @@ nomem_oops:
/* goto oops */
oops:
printf ("init_proxy failed...........................................\n");
/* since a new task can't be added in the initializer,
* i mark that initialization failed and let task_main_proxy()
* add an error task */
@ -1276,7 +1345,6 @@ printf ("init_proxy failed...........................................\n");
proxy->flags |= PROXY_INIT_FAILED;
task->ctx = proxy;
return 0;
}
@ -1290,6 +1358,16 @@ static void task_fini_proxy (
if (proxy->peer_status & PROXY_PEER_OPEN)
httpd->opt.scb.peer.close (httpd, &proxy->peer);
if ((proxy->flags & (PROXY_UPGRADE_REQUESTED | PROXY_PROTOCOL_SWITCHED)) == PROXY_UPGRADE_REQUESTED)
{
/* upgrade requested but protocol switching not completed yet.
* this can happen because dummification is performed before
* the 101 response is received. */
/* no harm to call this multiple times */
qse_htrd_undummify (proxy->client->htrd);
}
if (proxy->res) qse_mbs_close (proxy->res);
if (proxy->peer_htrd) qse_htrd_close (proxy->peer_htrd);
if (proxy->reqfwdbuf) qse_mbs_close (proxy->reqfwdbuf);
@ -1311,24 +1389,12 @@ printf ("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask
task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask);
#endif
#if 0
if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
/* if the client side is readable */
proxy_forward_client_input_to_peer (httpd, task, 0);
}
else if (task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
/* if the peer side is writable while the client side is not readable*/
proxy_forward_client_input_to_peer (httpd, task, 1);
}
#endif
proxy_forward_client_input_to_peer (httpd, task);
if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ proxy->buflen > 0)
{
/* wrote to the client socket as long as there's something to
* write. it's safe to do so as the socket is non-blocking.
/* write to the client socket as long as there's something.
* it's safe to do so as the socket is non-blocking.
* i commented out the check in the 'if' condition above */
/* TODO: check if proxy outputs more than content-length if it is set... */
@ -1352,7 +1418,7 @@ printf ("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask
}
/* if forwarding didn't finish, something is not really right...
* so long as the output from CGI is finished, no more forwarding
* so long as the output from peer is finished, no more forwarding
* is performed */
return (proxy->buflen > 0 || proxy->req ||
(proxy->reqfwdbuf && QSE_MBS_LEN(proxy->reqfwdbuf) > 0))? 1: 0;
@ -1362,23 +1428,13 @@ static int task_main_proxy_4 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_proxy_t* proxy = (task_proxy_t*)task->ctx;
#if 0
printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n",
printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=%d\n",
task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask);
#endif
proxy_forward_client_input_to_peer (httpd, task);
/*
if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
proxy_forward_client_input_to_peer (httpd, task, 0);
}
else if (task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
proxy_forward_client_input_to_peer (httpd, task, 1);
}
*/
if ((task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) &&
proxy->buflen < QSE_SIZEOF(proxy->buf))
@ -1436,6 +1492,12 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask
qse_htre_unsetconcb (proxy->req);
proxy->req = QSE_NULL;
}
else if (proxy->flags & PROXY_PROTOCOL_SWITCHED)
{
qse_htrd_undummify (proxy->client->htrd);
qse_htre_unsetconcb (proxy->req);
proxy->req = QSE_NULL;
}
return 1;
}
@ -1443,7 +1505,7 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask
{
proxy->buflen += n;
proxy->peer_output_received += n;
if (proxy->resflags & PROXY_RES_PEER_LENGTH)
{
QSE_ASSERT (!(proxy->flags & PROXY_RAW));
@ -1507,22 +1569,12 @@ static int task_main_proxy_3 (
task_proxy_t* proxy = (task_proxy_t*)task->ctx;
#if 0
qse_printf (QSE_T("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"),
#if 0
printf ("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n",
task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask);
#endif
proxy_forward_client_input_to_peer (httpd, task);
/*
if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
proxy_forward_client_input_to_peer (httpd, task, 0);
}
else if (task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
proxy_forward_client_input_to_peer (httpd, task, 1);
}
*/
if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) &&*/ proxy->res_pending > 0)
{
@ -1566,6 +1618,12 @@ qse_printf (QSE_T("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigg
qse_mbs_clear (proxy->res);
proxy->res_consumed = 0;
if (proxy->flags & PROXY_PROTOCOL_SWITCHED)
{
task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_READ;
goto read_more;
}
if ((proxy->resflags & PROXY_RES_CLIENT_CHUNK) ||
((proxy->resflags & PROXY_RES_PEER_LENGTH) && proxy->peer_output_received >= proxy->peer_output_length))
{
@ -1575,6 +1633,7 @@ qse_printf (QSE_T("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigg
}
else
{
read_more:
/* there are still more to read from the peer.
* arrange to read the remaining contents from the peer */
task->main = task_main_proxy_4;
@ -1596,24 +1655,7 @@ static int task_main_proxy_2 (
task_proxy_t* proxy = (task_proxy_t*)task->ctx;
int http_errnum = 500;
#if 0
printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n",
task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask);
#endif
proxy_forward_client_input_to_peer (httpd, task);
#if 0
if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
/* client is readable */
proxy_forward_client_input_to_peer (httpd, task, 0);
}
else if (task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
/* client is not readable but peer is writable */
proxy_forward_client_input_to_peer (httpd, task, 1);
}
#endif
if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ proxy->res_pending > 0)
{
@ -1624,8 +1666,8 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n",
* '100 Continue' to the client.
*
* attempt to write to the client regardless of writability of
* the cleint socket as it is non-blocking. see the check commented
* in the 'if' condition above. */
* the cleint socket as it is non-blocking. see the check commented
* out in the 'if' condition above. */
qse_ssize_t n;
qse_size_t count;
@ -1729,14 +1771,6 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n",
proxy->buflen += n;
}
#if 0
qse_printf (QSE_T("#####PROXY FEEDING %d [\n"), (int)proxy->buflen);
{
int i;
for (i = 0; i < proxy->buflen; i++) qse_printf (QSE_T("%hc"), proxy->buf[i]);
}
qse_printf (QSE_T("]\n"));
#endif
if (proxy->buflen > 0)
{
if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1)
@ -1749,7 +1783,13 @@ qse_printf (QSE_T("]\n"));
proxy->buflen = 0;
}
if (QSE_MBS_LEN(proxy->res) > 0)
if (proxy->flags & PROXY_PROTOCOL_SWITCHED)
{
task->trigger.cmask = QSE_HTTPD_TASK_TRIGGER_READ;
if (QSE_MBS_LEN(proxy->res) > 0) task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
task->main = task_main_proxy_3;
}
else if (QSE_MBS_LEN(proxy->res) > 0)
{
if (proxy->resflags & PROXY_RES_RECEIVED_RESCON)
{
@ -1842,10 +1882,17 @@ static int task_main_proxy_1 (
{
/* connected to the peer now */
proxy->peer_status |= PROXY_PEER_CONNECTED;
if (proxy->req)
if (!(proxy->flags & PROXY_UPGRADE_REQUESTED) && proxy->req)
{
/* need to read from the client-side as
* the content has not been received in full. */
* the content has not been received in full.
*
* proxy->req is set to the original request when snatching is
* required. it's also set to the original request when protocol
* upgrade is requested. however, a upgrade request containing
* contents is treated as a bad request. so i don't arrange
* to read from the client side when PROXY_UPGRADE_REQUESTED
* is on. */
task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_READ;
}
@ -2124,7 +2171,12 @@ static int task_main_proxy (
if (proxy->flags & PROXY_INIT_FAILED)
{
if (proxy->flags & PROXY_PEER_NAME_UNRESOLVED) http_errnum = 404; /* 404 Not Found */
if (proxy->flags & PROXY_GOT_BAD_REQUEST)
{
http_errnum = 400; /* 400 Bad Request */
proxy->keepalive = 0; /* force Connect: close in the response */
}
else if (proxy->flags & PROXY_PEER_NAME_UNRESOLVED) http_errnum = 404; /* 404 Not Found */
goto oops;
}
@ -2194,7 +2246,7 @@ static int task_main_proxy (
* for a socket descriptor into 1 event.
*
* if this happens, qse_http_resolvename() can be called
* multiple times.
* multiple times. set this flag to prevent double resolution.
*/
proxy->flags |= PROXY_PEER_NAME_RESOLVING;
@ -2268,10 +2320,18 @@ static int task_main_proxy (
{
/* peer connected already */
proxy->peer_status |= PROXY_PEER_CONNECTED;
if (proxy->req)
if (!(proxy->flags & PROXY_UPGRADE_REQUESTED) && proxy->req)
{
/* need to read from the client-side as
* the content has not been received in full. */
* the content has not been received in full.
*
* proxy->req is set to the original request when snatching is
* required. it's also set to the original request when protocol
* upgrade is requested. however, a upgrade request containing
* contents is treated as a bad request. so i don't arrange
* to read from the client side when PROXY_UPGRADE_REQUESTED
* is on.
*/
task->trigger.cmask = QSE_HTTPD_TASK_TRIGGER_READ;
}

View File

@ -792,9 +792,6 @@ resolved:
static void tmr_dns_tmout_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx)
{
dns_req_t* req = (dns_req_t*)ctx;
printf (">>tmr_dns_tmout_updated req->>%p\n", req);
printf (">>tmr_dns_tmout_updated existing->%d, old->%d new->%d\n", (int)req->tmr_tmout, (int)old_index, (int)new_index);
QSE_ASSERT (req->tmr_tmout == old_index);
req->tmr_tmout = new_index;
}

View File

@ -347,7 +347,6 @@ static void tmr_urs_tmout_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse
{
urs_req_t* req = (urs_req_t*)ctx;
printf (">>tmr_urs_tmout_updated existing=%d old=%d new=%d\n", (int)req->tmr_tmout, (int)old_index, (int)new_index);
QSE_ASSERT (req->tmr_tmout == old_index);
req->tmr_tmout = new_index;
}

View File

@ -2242,7 +2242,7 @@ printf ("SWITCHING HTRD TO DUMMY.... %s\n", qse_htre_getqpath(req));
/* Switch the http read to a dummy mode so that the subsqeuent
* input(request) is just treated as data to the request just
* completed */
qse_htrd_setoption (client->htrd, qse_htrd_getoption(client->htrd) | QSE_HTRD_DUMMY);
qse_htrd_dummify (client->htrd);
if (server_xtn->makersrc (httpd, client, req, &rsrc) <= -1)
{

View File

@ -711,7 +711,6 @@ qse_printf (QSE_T("failed to accept from server [%s] [%d]\n"), tmp, server->hand
static void tmr_idle_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx)
{
qse_httpd_client_t* client = (qse_httpd_client_t*)ctx;
printf ("tmr_idle updated old_index %d new_index %d tmr_idle %d\n", (int)old_index, (int)new_index, (int)client->tmr_idle);
QSE_ASSERT (client->tmr_idle == old_index);
client->tmr_idle = new_index;
}
@ -720,7 +719,6 @@ static void tmr_idle_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx)
{
qse_httpd_client_t* client = (qse_httpd_client_t*)ctx;
printf ("check if client is idle...\n");
if (qse_cmptime(now, &client->last_active) >= 0)
{
qse_ntime_t diff;
@ -728,14 +726,12 @@ printf ("check if client is idle...\n");
if (qse_cmptime(&diff, &client->server->httpd->opt.idle_limit) >= 0)
{
/* this client is idle */
printf ("client is idle purging....\n");
purge_client (client->server->httpd, client);
}
else
{
qse_tmr_event_t idle_event;
printf ("client is NOT idle....\n");
QSE_ASSERT (client->server->httpd->tmr == tmr);
/*qse_gettime (&idle_event.when);*/