diff --git a/qse/include/qse/cmn/str.h b/qse/include/qse/cmn/str.h index 605cfb8e..faaff09a 100644 --- a/qse/include/qse/cmn/str.h +++ b/qse/include/qse/cmn/str.h @@ -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 /** diff --git a/qse/include/qse/http/htrd.h b/qse/include/qse/http/htrd.h index c49deb2e..3dbd0a18 100644 --- a/qse/include/qse/http/htrd.h +++ b/qse/include/qse/http/htrd.h @@ -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 diff --git a/qse/lib/cmn/str-word.c b/qse/lib/cmn/str-word.c index c737a228..219e94de 100644 --- a/qse/lib/cmn/str-word.c +++ b/qse/lib/cmn/str-word.c @@ -21,32 +21,34 @@ #include #include -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); diff --git a/qse/lib/http/htrd.c b/qse/lib/http/htrd.c index 405abcdf..7fc5ed8c 100644 --- a/qse/lib/http/htrd.c +++ b/qse/lib/http/htrd.c @@ -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) { diff --git a/qse/lib/http/http.c b/qse/lib/http/http.c index 472a25d2..5ff260ba 100644 --- a/qse/lib/http/http.c +++ b/qse/lib/http/http.c @@ -504,4 +504,3 @@ qse_mchar_t* qse_perenchttpstrdup (int opt, const qse_mchar_t* str, qse_mmgr_t* return buf; } - diff --git a/qse/lib/http/httpd-proxy.c b/qse/lib/http/httpd-proxy.c index 00e9ac30..04d429bc 100644 --- a/qse/lib/http/httpd-proxy.c +++ b/qse/lib/http/httpd-proxy.c @@ -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; } diff --git a/qse/lib/http/httpd-std-dns.h b/qse/lib/http/httpd-std-dns.h index 47c53620..ac0357bc 100644 --- a/qse/lib/http/httpd-std-dns.h +++ b/qse/lib/http/httpd-std-dns.h @@ -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; } diff --git a/qse/lib/http/httpd-std-urs.h b/qse/lib/http/httpd-std-urs.h index 82158c13..a2775c23 100644 --- a/qse/lib/http/httpd-std-urs.h +++ b/qse/lib/http/httpd-std-urs.h @@ -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; } diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 00567576..94e4d583 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -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) { diff --git a/qse/lib/http/httpd.c b/qse/lib/http/httpd.c index 8a823129..af1a35bb 100644 --- a/qse/lib/http/httpd.c +++ b/qse/lib/http/httpd.c @@ -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);*/