From 246dc4f5b771acbdb86553e294df59ffb53ac3bd Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 25 Jan 2012 15:39:02 +0000 Subject: [PATCH] fixed of not setting cmgr properly in awk/std.c added encoding options to the awk command. added directory functions to httpd --- qse/cmd/awk/awk.c | 42 ++++- qse/configure | 11 ++ qse/configure.ac | 1 + qse/include/qse/cmn/tre.h | 10 +- qse/include/qse/config.h.in | 3 + qse/include/qse/net/htre.h | 2 +- qse/include/qse/net/httpd.h | 24 +-- qse/lib/awk/misc.c | 2 +- qse/lib/awk/std.c | 7 + qse/lib/cmn/mbwc.c | 7 +- qse/lib/cmn/tre-match-backtrack.c | 34 ++-- qse/lib/cmn/tre-match-parallel.c | 2 +- qse/lib/net/htrd.c | 90 +++++------ qse/lib/net/httpd.c | 225 ++++++++++++-------------- qse/lib/net/httpd_task.c | 261 ++++++++++++++++++++++++------ qse/lib/sed/sed.c | 3 +- qse/samples/net/http01.c | 31 ++-- 17 files changed, 481 insertions(+), 274 deletions(-) diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c index 73288ee7..b8650450 100644 --- a/qse/cmd/awk/awk.c +++ b/qse/cmd/awk/awk.c @@ -75,6 +75,8 @@ struct arg_t qse_htb_t* gvm; /* global variable map */ qse_char_t* fs; /* field separator */ qse_char_t* call; /* function to call */ + qse_cmgr_t* script_cmgr; + qse_cmgr_t* console_cmgr; int opton; int optoff; @@ -429,13 +431,19 @@ static void print_usage (QSE_FILE* out, const qse_char_t* argv0) qse_fprintf (out, QSE_T(" -F/--field-separator string set a field separator(FS)\n")); qse_fprintf (out, QSE_T(" -v/--assign var=value add a global variable with a value\n")); qse_fprintf (out, QSE_T(" -m/--memory-limit number limit the memory usage (bytes)\n")); +#if defined(QSE_CHAR_IS_WCHAR) + qse_fprintf (out, QSE_T(" --script-encoding string specify script encoding name\n")); + qse_fprintf (out, QSE_T(" --console-encoding string specify console encoding name\n")); +#endif #if defined(QSE_BUILD_DEBUG) qse_fprintf (out, QSE_T(" -X number fail the number'th memory allocation\n")); #endif - for (j = 0; opttab[j].name != QSE_NULL; j++) + for (j = 0; opttab[j].name; j++) { - qse_fprintf (out, QSE_T(" --%-18s on/off %s\n"), opttab[j].name, opttab[j].desc); + qse_fprintf (out, + QSE_T(" --%-18s on/off %s\n"), + opttab[j].name, opttab[j].desc); } } @@ -468,6 +476,9 @@ static int comparg (int argc, qse_char_t* argv[], struct arg_t* arg) { QSE_T(":assign"), QSE_T('v') }, { QSE_T(":memory-limit"), QSE_T('m') }, + { QSE_T(":script-encoding"), QSE_T('\0') }, + { QSE_T(":console-encoding"), QSE_T('\0') }, + { QSE_T("help"), QSE_T('h') }, { QSE_NULL, QSE_T('\0') } }; @@ -621,7 +632,7 @@ static int comparg (int argc, qse_char_t* argv[], struct arg_t* arg) { /* a long option with no corresponding short option */ qse_size_t i; - for (i = 0; opttab[i].name != QSE_NULL; i++) + for (i = 0; opttab[i].name; i++) { if (qse_strcmp (opt.lngopt, opttab[i].name) == 0) { @@ -637,6 +648,25 @@ static int comparg (int argc, qse_char_t* argv[], struct arg_t* arg) break; } } + + if (qse_strcmp(opt.lngopt, QSE_T("script-encoding")) == 0) + { + arg->script_cmgr = qse_getcmgrbyname (opt.arg); + if (arg->script_cmgr == QSE_NULL) + { + print_err (QSE_T("unknown script encoding - %s\n"), opt.arg); + goto oops; + } + } + else if (qse_strcmp(opt.lngopt, QSE_T("console-encoding")) == 0) + { + arg->console_cmgr = qse_getcmgrbyname (opt.arg); + if (arg->console_cmgr == QSE_NULL) + { + print_err (QSE_T("unknown console encoding - %s\n"), opt.arg); + goto oops; + } + } break; } @@ -874,14 +904,14 @@ static int awk_main (int argc, qse_char_t* argv[]) else { psin.u.file.path = arg.isp.files[0]; - psin.u.file.cmgr = QSE_NULL; + psin.u.file.cmgr = arg.script_cmgr; } if (arg.osf != QSE_NULL) { psout.type = QSE_AWK_PARSESTD_FILE; psout.u.file.path = arg.osf; - psout.u.file.cmgr = QSE_NULL; + psout.u.file.cmgr = arg.script_cmgr; } #if defined(QSE_BUILD_DEBUG) @@ -954,7 +984,7 @@ static int awk_main (int argc, qse_char_t* argv[]) rtx = qse_awk_rtx_openstd ( awk, 0, QSE_T("qseawk"), - (const qse_char_t*const*)arg.icf, QSE_NULL, QSE_NULL); + (const qse_char_t*const*)arg.icf, QSE_NULL, arg.console_cmgr); if (rtx == QSE_NULL) { print_awkerr (awk); diff --git a/qse/configure b/qse/configure index c3e741d3..a4d51533 100755 --- a/qse/configure +++ b/qse/configure @@ -15518,6 +15518,17 @@ _ACEOF fi done +for ac_func in fdopendir +do : + ac_fn_c_check_func "$LINENO" "fdopendir" "ac_cv_func_fdopendir" +if test "x$ac_cv_func_fdopendir" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FDOPENDIR 1 +_ACEOF + +fi +done + OLDLIBS="$LIBS" LIBS="$LIBM $LIBS" diff --git a/qse/configure.ac b/qse/configure.ac index 233d6ba3..684f99a7 100644 --- a/qse/configure.ac +++ b/qse/configure.ac @@ -100,6 +100,7 @@ AC_CHECK_FUNCS([timegm timelocal]) AC_CHECK_FUNCS([utime utimes]) AC_CHECK_FUNCS([sysconf]) AC_CHECK_FUNCS([backtrace backtrace_symbols]) +AC_CHECK_FUNCS([fdopendir]) OLDLIBS="$LIBS" LIBS="$LIBM $LIBS" diff --git a/qse/include/qse/cmn/tre.h b/qse/include/qse/cmn/tre.h index 914bef45..c1023017 100644 --- a/qse/include/qse/cmn/tre.h +++ b/qse/include/qse/cmn/tre.h @@ -84,9 +84,13 @@ enum qse_tre_cflag_t enum qse_tre_eflag_t { - QSE_TRE_NOTBOL = (1 << 0), - QSE_TRE_NOTEOL = (1 << 1), - QSE_TRE_BACKTRACKING = (1 << 2) + QSE_TRE_BACKTRACKING = (1 << 0), + + /* you can use QSE_TRE_IGNORECASE for execution */ + /*QSE_TRE_IGNORECASE = (1 << 1),*/ + + QSE_TRE_NOTBOL = (1 << 2), + QSE_TRE_NOTEOL = (1 << 3) }; typedef struct qse_tre_strsrc_t qse_tre_strsrc_t; diff --git a/qse/include/qse/config.h.in b/qse/include/qse/config.h.in index d03218f8..f91acad5 100644 --- a/qse/include/qse/config.h.in +++ b/qse/include/qse/config.h.in @@ -61,6 +61,9 @@ /* Define to 1 if you have the `expl' function. */ #undef HAVE_EXPL +/* Define to 1 if you have the `fdopendir' function. */ +#undef HAVE_FDOPENDIR + /* Define to 1 if you have the `fmod' function. */ #undef HAVE_FMOD diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index 78b4ed0b..fc9c3ad9 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -44,7 +44,7 @@ struct qse_htre_t int chunked; int content_length_set; qse_size_t content_length; - int connection_close; + int keepalive; int expect_continue; /* indicates if the content has been filled */ diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index d490306f..e37f6fe8 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -46,19 +46,13 @@ typedef enum qse_httpd_errnum_t qse_httpd_errnum_t; typedef struct qse_httpd_cbs_t qse_httpd_cbs_t; struct qse_httpd_cbs_t { - struct - { - const qse_mchar_t* (*getmimetype) (qse_httpd_t* httpd, const qse_mchar_t* path); - - qse_ubi_t (*open) (qse_httpd_t* httpd, const qse_mchar_t* path); - void (*close) (qse_httpd_t* httpd, qse_ubi_t handle); - int (*getsize) (qse_httpd_t* httpd, qse_ubi_t handle, qse_foff_t* size); - } file; - int (*handle_request) ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); int (*handle_expect_continue) ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); + + const qse_mchar_t* (*getmimetype) (qse_httpd_t* httpd, const qse_mchar_t* path); + int (*listdir) (qse_httpd_t* httpd, const qse_mchar_t* path); }; typedef struct qse_httpd_task_t qse_httpd_task_t; @@ -142,7 +136,7 @@ void qse_httpd_stop ( ); -int qse_httpd_addlisteners ( +int qse_httpd_addlistener ( qse_httpd_t* httpd, const qse_char_t* uri ); @@ -200,13 +194,21 @@ qse_httpd_task_t* qse_httpd_entaskfile ( qse_foff_t size ); +qse_httpd_task_t* qse_httpd_entaskdir ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + const qse_httpd_task_t* pred, + qse_ubi_t handle +); + qse_httpd_task_t* qse_httpd_entaskpath ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_httpd_task_t* pred, const qse_mchar_t* name, const qse_http_range_t* range, - const qse_http_version_t* version + const qse_http_version_t* version, + int keepalive ); qse_httpd_task_t* qse_httpd_entaskcgi ( diff --git a/qse/lib/awk/misc.c b/qse/lib/awk/misc.c index 3349d5d2..cf6032f7 100644 --- a/qse/lib/awk/misc.c +++ b/qse/lib/awk/misc.c @@ -1089,7 +1089,7 @@ int qse_awk_matchrex ( x = qse_matchrex ( awk->mmgr, awk->rex.depth.max.match, code, option, str, substr, match, &err); - if (x < 0) *errnum = QSE_AWK_REXERRTOERR(err); + if (x <= -1) *errnum = QSE_AWK_REXERRTOERR(err); return x; } diff --git a/qse/lib/awk/std.c b/qse/lib/awk/std.c index afb75d45..5a40139e 100644 --- a/qse/lib/awk/std.c +++ b/qse/lib/awk/std.c @@ -997,6 +997,7 @@ static int open_rio_console (qse_awk_rtx_t* rtx, qse_awk_rio_arg_t* riod) if (sio == QSE_NULL) return -1; if (rxtn->c.cmgr) qse_sio_setcmgr (sio, rxtn->c.cmgr); + riod->handle = sio; rxtn->c.in.count++; return 1; @@ -1114,6 +1115,8 @@ static int open_rio_console (qse_awk_rtx_t* rtx, qse_awk_rio_arg_t* riod) return -1; } + if (rxtn->c.cmgr) qse_sio_setcmgr (sio, rxtn->c.cmgr); + if (qse_awk_rtx_setfilename ( rtx, file, qse_strlen(file)) <= -1) { @@ -1146,6 +1149,8 @@ static int open_rio_console (qse_awk_rtx_t* rtx, qse_awk_rio_arg_t* riod) ); if (sio == QSE_NULL) return -1; + if (rxtn->c.cmgr) qse_sio_setcmgr (sio, rxtn->c.cmgr); + riod->handle = sio; rxtn->c.out.count++; return 1; @@ -1177,6 +1182,8 @@ static int open_rio_console (qse_awk_rtx_t* rtx, qse_awk_rio_arg_t* riod) QSE_SIO_WRITE | QSE_SIO_CREATE | QSE_SIO_TRUNCATE | QSE_SIO_IGNOREMBWCERR); if (sio == QSE_NULL) return -1; + + if (rxtn->c.cmgr) qse_sio_setcmgr (sio, rxtn->c.cmgr); if (qse_awk_rtx_setofilename ( rtx, file, qse_strlen(file)) <= -1) diff --git a/qse/lib/cmn/mbwc.c b/qse/lib/cmn/mbwc.c index 320321d8..3a176d64 100644 --- a/qse/lib/cmn/mbwc.c +++ b/qse/lib/cmn/mbwc.c @@ -58,6 +58,11 @@ void qse_setdflcmgr (qse_cmgr_t* cmgr) dfl_cmgr = (cmgr? cmgr: &builtin_cmgr[0]); } +/* TODO: +qse_addcmgr (const qse_char_t* name, qse_cmgr_t* cmgr); +qse_delcmgr (const qse_char_t* name); +*/ + qse_cmgr_t* qse_getcmgrbyname (const qse_char_t* name) { if (name) @@ -66,7 +71,7 @@ qse_cmgr_t* qse_getcmgrbyname (const qse_char_t* name) if (qse_strcmp(name, QSE_T("")) == 0) return dfl_cmgr; if (qse_strcmp(name, QSE_T("utf8")) == 0) return qse_utf8cmgr; if (qse_strcmp(name, QSE_T("slmb")) == 0) return qse_slmbcmgr; - /* TODO: add more */ + /* TODO: add more - handle those added with qse_addcmgr() */ } return QSE_NULL; } diff --git a/qse/lib/cmn/tre-match-backtrack.c b/qse/lib/cmn/tre-match-backtrack.c index c1ce2293..4ac31c25 100644 --- a/qse/lib/cmn/tre-match-backtrack.c +++ b/qse/lib/cmn/tre-match-backtrack.c @@ -132,28 +132,22 @@ typedef struct tre_backtrack_struct s = tre_bt_mem_alloc(mem, sizeof(*s)); \ if (!s) \ { \ - tre_bt_mem_destroy(mem); \ - if (tags) \ - xfree(_mmgr,tags); \ - if (pmatch) \ - xfree(_mmgr,pmatch); \ - if (states_seen) \ - xfree(_mmgr,states_seen); \ + tre_bt_mem_destroy(mem); \ + if (tags) xfree(_mmgr,tags); \ + if (pmatch) xfree(_mmgr,pmatch); \ + if (states_seen) xfree(_mmgr,states_seen); \ return REG_ESPACE; \ } \ s->prev = stack; \ s->next = NULL; \ - s->item.tags = tre_bt_mem_alloc(mem, \ - sizeof(*tags) * tnfa->num_tags); \ - if (!s->item.tags) \ - { \ - tre_bt_mem_destroy(mem); \ - if (tags) \ - xfree(_mmgr,tags); \ - if (pmatch) \ - xfree(_mmgr,pmatch); \ - if (states_seen) \ - xfree(_mmgr,states_seen); \ + s->item.tags = tre_bt_mem_alloc(mem, \ + sizeof(*tags) * tnfa->num_tags); \ + if (!s->item.tags) \ + { \ + tre_bt_mem_destroy(mem); \ + if (tags) xfree(_mmgr,tags); \ + if (pmatch) xfree(_mmgr,pmatch); \ + if (states_seen) xfree(_mmgr,states_seen); \ return REG_ESPACE; \ } \ stack->next = s; \ @@ -539,8 +533,8 @@ retry: trans_i->code_min, trans_i->code_max, trans_i->code_min, trans_i->code_max, trans_i->assertions, trans_i->state_id)); - if (trans_i->code_min <= (tre_cint_t)prev_c - && trans_i->code_max >= (tre_cint_t)prev_c) + if (trans_i->code_min <= (tre_cint_t)prev_c && + trans_i->code_max >= (tre_cint_t)prev_c) { if (trans_i->assertions && (CHECK_ASSERTIONS(trans_i->assertions) diff --git a/qse/lib/cmn/tre-match-parallel.c b/qse/lib/cmn/tre-match-parallel.c index 2eac0722..5d5ab61f 100644 --- a/qse/lib/cmn/tre-match-parallel.c +++ b/qse/lib/cmn/tre-match-parallel.c @@ -409,7 +409,7 @@ tre_tnfa_run_parallel(qse_mmgr_t* mmgr, const tre_tnfa_t *tnfa, const void *stri { /* Does this transition match the input symbol? */ if (trans_i->code_min <= (tre_cint_t)prev_c && - trans_i->code_max >= (tre_cint_t)prev_c) + trans_i->code_max >= (tre_cint_t)prev_c) { if (trans_i->assertions && (CHECK_ASSERTIONS(trans_i->assertions) diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index 78b0ba18..5ae5adf6 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -275,11 +275,11 @@ static qse_mchar_t* parse_initial_line ( if (qse_htre_setsmessagefromcstr (&htrd->re, &tmp) <= -1) goto outofmem; - /* adjust Connection: close for HTTP 1.0 or eariler */ - if (htrd->re.version.major < 1 || - (htrd->re.version.major == 1 && htrd->re.version.minor == 0)) + /* adjust Connection: Keep-Alive for HTTP 1.1 or later */ + if (htrd->re.version.major > 1 || + (htrd->re.version.major == 1 && htrd->re.version.minor >= 1)) { - htrd->re.attr.connection_close = 1; + htrd->re.attr.keepalive = 1; } } else @@ -379,11 +379,11 @@ static qse_mchar_t* parse_initial_line ( /* skip trailing spaces on the line */ while (is_space_octet(*p)) p++; - /* adjust Connection: close for HTTP 1.0 or eariler */ - if (htrd->re.version.major < 1 || - (htrd->re.version.major == 1 && htrd->re.version.minor == 0)) + /* adjust Connection: Keep-Alive for HTTP 1.1 or later */ + if (htrd->re.version.major > 1 || + (htrd->re.version.major == 1 && htrd->re.version.minor >= 1)) { - htrd->re.attr.connection_close = 1; + htrd->re.attr.keepalive = 1; } } @@ -427,58 +427,46 @@ void qse_htrd_setrecbs (qse_htrd_t* htrd, const qse_htrd_recbs_t* recbs) htrd->recbs = recbs; } -#define octet_tolower(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) | 0x20) : (c)) -#define octet_toupper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~0x20) : (c)) - -static QSE_INLINE int compare_octets ( - const qse_mchar_t* s1, qse_size_t len1, - const qse_mchar_t* s2, qse_size_t len2) -{ - qse_char_t c1, c2; - const qse_mchar_t* end1 = s1 + len1; - const qse_mchar_t* end2 = s2 + len2; - - while (s1 < end1) - { - c1 = octet_toupper (*s1); - if (s2 < end2) - { - c2 = octet_toupper (*s2); - if (c1 > c2) return 1; - if (c1 < c2) return -1; - } - else return 1; - s1++; s2++; - } - - return (s2 < end2)? -1: 0; -} - -static QSE_INLINE int capture_connection ( - qse_htrd_t* htrd, qse_htb_pair_t* pair) +static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; - n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "close", 5); + n = qse_mbsxncasecmp ( + QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), + "close", 5); if (n == 0) { - htrd->re.attr.connection_close = 1; + htrd->re.attr.keepalive = 0; return 0; } - n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "Keep-Alive", 10); + n = qse_mbsxncasecmp ( + QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), + "Keep-Alive", 10); if (n == 0) { - htrd->re.attr.connection_close = 0; + htrd->re.attr.keepalive = 1; return 0; } - /* don't care about other values */ + /* Basically i don't care about other values. + * but for HTTP 1.0, other values will set connection to 'close'. + * + * Other values include even Keep-Alive specified multiple times. + * Connection: Keep-Alive + * Connection: Keep-Alive + * For the second Keep-Alive, this function sees 'Keep-Alive,Keep-Alive' + * That's because values of the same keys are concatenated. + */ + if (htrd->re.version.major < 1 || + (htrd->re.version.major == 1 && htrd->re.version.minor <= 0)) + { + htrd->re.attr.keepalive = 0; + } return 0; } -static QSE_INLINE int capture_content_length ( - 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); @@ -525,12 +513,12 @@ static QSE_INLINE int capture_content_length ( return 0; } -static QSE_INLINE int capture_expect ( - qse_htrd_t* htrd, qse_htb_pair_t* pair) +static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; - n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "100-continue", 12); + n = qse_mbsxncasecmp ( + QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "100-continue", 12); if (n == 0) { @@ -542,12 +530,12 @@ static QSE_INLINE int capture_expect ( return 0; } -static QSE_INLINE int capture_transfer_encoding ( - qse_htrd_t* htrd, qse_htb_pair_t* pair) +static int capture_transfer_encoding (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; - n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "chunked", 7); + n = qse_mbsxncasecmp ( + QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "chunked", 7); if (n == 0) { /* if (htrd->re.attr.content_length > 0) */ @@ -591,7 +579,7 @@ static QSE_INLINE int capture_key_header ( { mid = base + count / 2; - n = compare_octets ( + n = qse_mbsxncasecmp ( QSE_HTB_KPTR(pair), QSE_HTB_KLEN(pair), hdrtab[mid].ptr, hdrtab[mid].len ); diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 47b869cd..2f6e0b6f 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -27,6 +27,7 @@ #include "../cmn/mem.h" #include #include +#include #include #include @@ -451,7 +452,8 @@ static void delete_from_client_array (qse_httpd_t* httpd, int fd) { purge_tasks_locked (httpd, &array->data[fd]); #if defined(HAVE_PTHREAD) - if (httpd->threaded) pthread_mutex_destroy (&array->data[fd].task.mutex); + if (httpd->threaded) + pthread_mutex_destroy (&array->data[fd].task.mutex); #endif qse_htrd_close (array->data[fd].htrd); @@ -610,7 +612,6 @@ httpd->cbs.on_error (httpd, l).... */ } } - static int make_fd_set_from_client_array ( qse_httpd_t* httpd, fd_set* r, fd_set* w) { @@ -955,136 +956,123 @@ static void free_listener_list (qse_httpd_t* httpd, listener_t* l) } } -static listener_t* parse_listener_string ( - qse_httpd_t* httpd, const qse_char_t* str, listener_t** ltail) +static listener_t* parse_listener_uri ( + qse_httpd_t* httpd, const qse_char_t* uri) { - const qse_char_t* p = str; - listener_t* lhead = QSE_NULL, * ltmp = QSE_NULL, * lend = QSE_NULL; + const qse_char_t* p = uri; + listener_t* ltmp = QSE_NULL; + qse_cstr_t tmp; + qse_mchar_t* host; + int x; - do + ltmp = qse_httpd_allocmem (httpd, QSE_SIZEOF(*ltmp)); + if (ltmp == QSE_NULL) goto oops; /* alloc set error number. so goto oops */ + + QSE_MEMSET (ltmp, 0, QSE_SIZEOF(*ltmp)); + ltmp->handle = -1; + + /* check the protocol part */ + tmp.ptr = p; + while (*p != QSE_T(':')) { - qse_cstr_t tmp; - qse_mchar_t* host; - int x; + if (*p == QSE_T('\0')) goto oops_einval; + p++; + } + tmp.len = p - tmp.ptr; + if (qse_strxcmp (tmp.ptr, tmp.len, QSE_T("http")) == 0) + { + ltmp->secure = 0; + ltmp->port = DEFAULT_PORT; + } + else if (qse_strxcmp (tmp.ptr, tmp.len, QSE_T("https")) == 0) + { + ltmp->secure = 1; + ltmp->port = DEFAULT_SECURE_PORT; + } + else goto oops; + + p++; /* skip : */ + if (*p != QSE_T('/')) goto oops_einval; + p++; /* skip / */ + if (*p != QSE_T('/')) goto oops_einval; + p++; /* skip / */ - /* skip spaces */ - while (QSE_ISSPACE(*p)) p++; +#ifdef AF_INET6 + if (*p == QSE_T('[')) + { + /* IPv6 address */ + p++; /* skip [ */ - ltmp = qse_httpd_allocmem (httpd, QSE_SIZEOF(*ltmp)); - if (ltmp == QSE_NULL) goto oops; /* alloc set error number. so goto oops */ - - QSE_MEMSET (ltmp, 0, QSE_SIZEOF(*ltmp)); - ltmp->handle = -1; - - /* check the protocol part */ - tmp.ptr = p; - while (*p != QSE_T(':')) + for (tmp.ptr = p; *p != QSE_T(']'); p++) { if (*p == QSE_T('\0')) goto oops_einval; - p++; + } + + tmp.len = p - tmp.ptr; + ltmp->family = AF_INET6; + + p++; /* skip ] */ + if (*p != QSE_T(':') && *p != QSE_T('\0')) goto oops_einval; + } + else + { +#endif + /* host name or IPv4 address */ + for (tmp.ptr = p; ; p++) + { + if (*p == QSE_T(':') || *p == QSE_T('\0')) break; } tmp.len = p - tmp.ptr; - if (qse_strxcmp (tmp.ptr, tmp.len, QSE_T("http")) == 0) - { - ltmp->secure = 0; - ltmp->port = DEFAULT_PORT; - } - else if (qse_strxcmp (tmp.ptr, tmp.len, QSE_T("https")) == 0) - { - ltmp->secure = 1; - ltmp->port = DEFAULT_SECURE_PORT; - } - else goto oops; - - p++; /* skip : */ - if (*p != QSE_T('/')) goto oops_einval; - p++; /* skip / */ - if (*p != QSE_T('/')) goto oops_einval; - p++; /* skip / */ - + ltmp->family = AF_INET; #ifdef AF_INET6 - if (*p == QSE_T('[')) - { - /* IPv6 address */ - p++; /* skip [ */ - tmp.ptr = p; - while (*p != QSE_T(']')) - { - if (*p == QSE_T('\0')) goto oops_einval; - p++; - } - tmp.len = p - tmp.ptr; - - ltmp->family = AF_INET6; - p++; /* skip ] */ - } - else - { -#endif - /* host name or IPv4 address */ - tmp.ptr = p; - while (!QSE_ISSPACE(*p) && - *p != QSE_T(':') && - *p != QSE_T('\0')) p++; - tmp.len = p - tmp.ptr; - ltmp->family = AF_INET; -#ifdef AF_INET6 - } + } #endif - ltmp->host = qse_strxdup (tmp.ptr, tmp.len, httpd->mmgr); - if (ltmp->host == QSE_NULL) goto oops_enomem; + ltmp->host = qse_strxdup (tmp.ptr, tmp.len, httpd->mmgr); + if (ltmp->host == QSE_NULL) goto oops_enomem; #ifdef QSE_CHAR_IS_WCHAR - host = qse_wcstombsdup (ltmp->host, httpd->mmgr); - if (host == QSE_NULL) goto oops_enomem; + host = qse_wcstombsdup (ltmp->host, httpd->mmgr); + if (host == QSE_NULL) goto oops_enomem; #else - host = ltmp->host; + host = ltmp->host; #endif - x = inet_pton (ltmp->family, host, <mp->addr); + x = inet_pton (ltmp->family, host, <mp->addr); #ifdef QSE_CHAR_IS_WCHAR - qse_httpd_freemem (httpd, host); + qse_httpd_freemem (httpd, host); #endif - if (x != 1) - { - /* TODO: need to support host names??? - if (getaddrinfo... ).... + if (x != 1) + { + /* TODO: need to support host names??? + if (getaddrinfo... ).... or CALL a user callback for name resolution? - if (httpd->cbs.resolve_hostname (httpd, ltmp->host) <= -1) must call this with host before freeing it up???? - */ - goto oops_einval; - } + if (httpd->cbs.resolve_hostname (httpd, ltmp->host) <= -1) must call this with host before freeing it up???? + */ + goto oops_einval; + } - if (*p == QSE_T(':')) - { - unsigned int port = 0; - /* port number */ - p++; + if (*p == QSE_T(':')) + { + unsigned int port = 0; - tmp.ptr = p; - while (QSE_ISDIGIT(*p)) - { - port = port * 10 + (*p - QSE_T('0')); - p++; - } - tmp.len = p - tmp.ptr; - if (tmp.len > 5 || port > QSE_TYPE_MAX(unsigned short)) goto oops_einval; - ltmp->port = port; - } + /* port number */ + p++; - /* skip spaces */ - while (QSE_ISSPACE(*p)) p++; + for (tmp.ptr = p; QSE_ISDIGIT(*p); p++) + port = port * 10 + (*p - QSE_T('0')); - if (lhead == QSE_NULL) lend = ltmp; - ltmp->next = lhead; - lhead = ltmp; - ltmp = QSE_NULL; - } - while (*p != QSE_T('\0')); + tmp.len = p - tmp.ptr; + if (tmp.len <= 0 || + tmp.len >= 6 || + port > QSE_TYPE_MAX(unsigned short)) goto oops_einval; + ltmp->port = port; + } - if (ltail) *ltail = lend; - return lhead; + /* skip spaces */ + while (QSE_ISSPACE(*p)) p++; + + return ltmp; oops_einval: httpd->errnum = QSE_HTTPD_EINVAL; @@ -1096,19 +1084,18 @@ oops_enomem: oops: if (ltmp) free_listener (httpd, ltmp); - if (lhead) free_listener_list (httpd, lhead); return QSE_NULL; } -static int add_listeners (qse_httpd_t* httpd, const qse_char_t* uri) +static int add_listener (qse_httpd_t* httpd, const qse_char_t* uri) { - listener_t* lh, * lt; + listener_t* lsn; - lh = parse_listener_string (httpd, uri, <); - if (lh == QSE_NULL) return -1; + lsn = parse_listener_uri (httpd, uri); + if (lsn == QSE_NULL) return -1; - lt->next = httpd->listener.list; - httpd->listener.list = lh; + lsn->next = httpd->listener.list; + httpd->listener.list = lsn; /* TODO: mutex protection... if in the activated state... @@ -1124,7 +1111,7 @@ static int delete_listeners (qse_httpd_t* httpd, const qse_char_t* uri) { listener_t* lh, * li, * hl; - lh = parse_listener_string (httpd, uri, QSE_NULL); + lh = parse_listener_uri (httpd, uri, QSE_NULL); if (lh == QSE_NULL) return -1; for (li = lh; li; li = li->next) @@ -1142,21 +1129,21 @@ static int delete_listeners (qse_httpd_t* httpd, const qse_char_t* uri) } #endif -int qse_httpd_addlisteners (qse_httpd_t* httpd, const qse_char_t* uri) +int qse_httpd_addlistener (qse_httpd_t* httpd, const qse_char_t* uri) { #if defined(HAVE_PTHREAD) int n; pthread_mutex_lock (&httpd->listener.mutex); - n = add_listeners (httpd, uri); + n = add_listener (httpd, uri); pthread_mutex_unlock (&httpd->listener.mutex); return n; #else - return add_listeners (httpd, uri); + return add_listener (httpd, uri); #endif } #if 0 -int qse_httpd_dellisteners (qse_httpd_t* httpd, const qse_char_t* uri) +int qse_httpd_dellistener (qse_httpd_t* httpd, const qse_char_t* uri) { int n; pthread_mutex_lock (&httpd->listener.mutex); diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index bbed2473..a0d7e03a 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -33,6 +33,7 @@ #include #include #include +#include #define MAX_SEND_SIZE 4096 @@ -112,7 +113,6 @@ static qse_ssize_t xsendfile ( } #endif - /*------------------------------------------------------------------------*/ static int task_main_disconnect ( @@ -496,6 +496,125 @@ qse_httpd_task_t* qse_httpd_entaskfile ( return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data)); } +/*------------------------------------------------------------------------*/ +typedef struct task_dir_t task_dir_t; +struct task_dir_t +{ + qse_ubi_t handle; + qse_size_t count; + int eod; + + qse_mchar_t buf[4096]; + qse_size_t buflen; + qse_size_t bufsent; + +}; + +static int task_init_dir ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task); + + QSE_MEMSET (xtn, 0, QSE_SIZEOF(*xtn)); + xtn->handle = *(qse_ubi_t*)task->ctx; +qse_printf (QSE_T(">>>> handle %p\n"), xtn->handle.ptr); + task->ctx = xtn; + return 0; +} + +static void task_fini_dir ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_dir_t* ctx = (task_file_t*)task->ctx; + closedir (ctx->handle.ptr); +} + +static int task_main_dir ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_dir_t* ctx = (task_dir_t*)task->ctx; + qse_ssize_t n; + char buf[100]; + + if (ctx->bufsent < ctx->buflen) goto send_dirlist; + + ctx->buflen = 0; + ctx->bufsent = 0; + + if (ctx->count == 0) + { + ctx->buflen += snprintf ( + &ctx->buf[ctx->buflen], + QSE_COUNTOF(ctx->buf) - ctx->buflen, + "Directory Listingindex of xxxx
    " + ); + } + + do + { + struct dirent* ent; + + ent = readdir (ctx->handle.ptr); + if (ent == QSE_NULL) + { + // TODO: check if errno has changed from before readdir(). + // and return -1 if so. + ctx->eod = 1; + ctx->buflen += snprintf ( + &ctx->buf[ctx->buflen], + QSE_COUNTOF(ctx->buf) - ctx->buflen, + "
"); + break; + } + else + { + // TODO: check if snprintf has truncated.... + ctx->buflen += snprintf ( + &ctx->buf[ctx->buflen], + QSE_COUNTOF(ctx->buf) - ctx->buflen, + "
  • %s%s
  • ", + ent->d_name, + (ent->d_type == DT_DIR? "/": ""), + ent->d_name, + (ent->d_type == DT_DIR? "/": "") + ); + } + + ctx->count++; + } + while (1); + +send_dirlist: + + snprintf (buf, QSE_COUNTOF(buf), "%lX\r\n", (unsigned long)(ctx->buflen - ctx->bufsent)); + send (client->handle.i, buf, strlen(buf), 0); + + n = send (client->handle.i, ctx->buf, ctx->buflen, 0); + if (n <= -1) return -1; + + send (client->handle.i, "0\r\n", 3, 0); + + ctx->bufsent += n; + return (ctx->bufsent < ctx->buflen || !ctx->eod)? 1: 0; +} + +qse_httpd_task_t* qse_httpd_entaskdir ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + const qse_httpd_task_t* pred, + qse_ubi_t handle) +{ + qse_httpd_task_t task; + + QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); + task.init = task_init_dir; + task.main = task_main_dir; + task.fini = task_fini_dir; + task.ctx = &handle; + + return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_dir_t)); +} + /*------------------------------------------------------------------------*/ #include @@ -508,6 +627,7 @@ struct task_path_t const qse_mchar_t* name; qse_http_range_t range; qse_http_version_t version; + int keepalive; }; static int task_init_path ( @@ -521,6 +641,46 @@ static int task_init_path ( return 0; } +static qse_httpd_task_t* entask_path_error ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task, int code) +{ + task_path_t* data = (task_path_t*)task->ctx; + + const qse_mchar_t* smsg; + const qse_mchar_t* lmsg; + + switch (code) + { + case 403: + smsg = QSE_MT("Forbidden"); + lmsg = QSE_MT("Directory Listing ForbiddenDIRECTORY LISTING FORBIDDEN"); + break; + + case 404: + smsg = QSE_MT("Not Found"); + lmsg = QSE_MT("Not FoundREQUESTED PATH NOT FOUND"); + break; + + case 416: + smsg = QSE_MT("Requested Range Not Satisfiable"); + lmsg = QSE_MT("Requested Range Not SatsfiableREQUESTED RANGE NOT SATISFIABLE"); + break; + + default: + smsg = QSE_MT("Unknown"); + lmsg = QSE_MT("Unknown ErrorUNKNOWN ERROR"); + break; + } + + return qse_httpd_entaskformat ( + httpd, client, task, + QSE_MT("HTTP/%d.%d %d %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + data->version.major, data->version.minor, code, smsg, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), + (int)qse_mbslen(lmsg) + 4, lmsg + ); +} + static int task_main_path ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -529,45 +689,48 @@ static int task_main_path ( struct stat st; qse_httpd_task_t* x = task; -qse_printf (QSE_T("opending file %hs\n"), data->name); +qse_printf (QSE_T("opening file %hs\n"), data->name); handle.i = open (data->name, O_RDONLY); if (handle.i <= -1) { - const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - data->version.major, data->version.minor, - (int)qse_mbslen(msg) + 4, msg - ); + x = entask_path_error (httpd, client, x, 404); goto no_file_send; } fcntl (handle.i, F_SETFD, FD_CLOEXEC); if (fstat (handle.i, &st) <= -1) { - const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); - - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - data->version.major, data->version.minor, - (int)qse_mbslen(msg) + 4, msg - ); + x = entask_path_error (httpd, client, x, 404); goto no_file_send; } if (S_ISDIR(st.st_mode)) { -/* TODO: directory listing */ - const qse_mchar_t* msg = QSE_MT("Directory Listing
  • file1
  • file2
  • file3"); + qse_ubi_t dir; - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - data->version.major, data->version.minor, - (int)qse_mbslen(msg) + 4, msg - ); + /*#if defined(HAVE_FDOPENDIR) + dir.ptr = fdopendir (handle.i); + #else */ + dir.ptr = opendir (data->name); + /*#endif */ + if (dir.ptr) + { +qse_printf (QSE_T(">>>> entask dir handle %p\n"), dir.ptr); + x = qse_httpd_entaskformat ( + httpd, client, x, + QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Location: %s\r\nTransfer-Encoding: chunked\r\n\r\n"), + data->version.major, data->version.minor, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), + data->name + ); + + x = qse_httpd_entaskdir (httpd, client, x, dir); + if (x == QSE_NULL) closedir (dir.ptr); + } + else + { + x = entask_path_error (httpd, client, x, 403); + } goto no_file_send; } @@ -587,33 +750,26 @@ qse_printf (QSE_T("opending file %hs\n"), data->name); if (data->range.from >= st.st_size) { - const qse_mchar_t* msg; - - msg = QSE_MT("Requested range not satisfiableREQUESTED RANGE NOT SATISFIABLE"); - x = qse_httpd_entaskformat ( - httpd, client, x, - QSE_MT("HTTP/%d.%d 416 Requested range not satisfiable\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - data->version.major, data->version.minor, - (int)qse_mbslen(msg) + 4, msg - ); + x = entask_path_error (httpd, client, x, 416); goto no_file_send; } if (data->range.to >= st.st_size) data->range.to = st.st_size - 1; - if (httpd->cbs->file.getmimetype) + if (httpd->cbs->getmimetype) { httpd->errnum = QSE_HTTPD_ENOERR; - mime_type = httpd->cbs->file.getmimetype (httpd, data->name); + mime_type = httpd->cbs->getmimetype (httpd, data->name); /*TODO: how to handle an error... */ } #if (QSE_SIZEOF_LONG_LONG > 0) x = qse_httpd_entaskformat ( httpd, client, x, - QSE_MT("HTTP/%d.%d 206 Partial content\r\n%s%s%sContent-Length: %llu\r\nContent-Location: %s\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"), + QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Location: %s\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"), data->version.major, data->version.minor, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), (mime_type? QSE_MT("Content-Type: "): QSE_MT("")), (mime_type? mime_type: QSE_MT("")), (mime_type? QSE_MT("\r\n"): QSE_MT("")), @@ -626,9 +782,10 @@ qse_printf (QSE_T("opending file %hs\n"), data->name); #else x = qse_httpd_entaskformat ( httpd, client, x, - QSE_MT("HTTP/%d.%d 206 Partial content\r\n%s%s%sContent-Length: %lu\r\nContent-Location: %s\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"), + QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Location: %s\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"), data->version.major, data->version.minor, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), (mime_type? QSE_MT("Content-Type: "): QSE_MT("")), (mime_type? mime_type: QSE_MT("")), (mime_type? QSE_MT("\r\n"): QSE_MT("")), @@ -642,10 +799,10 @@ qse_printf (QSE_T("opending file %hs\n"), data->name); if (x) { x = qse_httpd_entaskfile ( - httpd, client, x, - handle, - data->range.from, - (data->range.to - data->range.from + 1) + httpd, client, x, + handle, + data->range.from, + (data->range.to - data->range.from + 1) ); } } @@ -654,19 +811,22 @@ qse_printf (QSE_T("opending file %hs\n"), data->name); /* TODO: int64 format.... don't hard code it llu */ const qse_mchar_t* mime_type = QSE_NULL; - if (httpd->cbs->file.getmimetype) + if (httpd->cbs->getmimetype) { httpd->errnum = QSE_HTTPD_ENOERR; - mime_type = httpd->cbs->file.getmimetype (httpd, data->name); + mime_type = httpd->cbs->getmimetype (httpd, data->name); /*TODO: how to handle an error... */ } + /* wget 1.8.2 set 'Connection: Keep-Alive' in the http 1.0 header. + * if the reply doesn't contain 'Connection: Keep-Alive', it didn't + * close connection.*/ #if (QSE_SIZEOF_LONG_LONG > 0) x = qse_httpd_entaskformat ( httpd, client, x, - QSE_MT("HTTP/%d.%d 200 OK\r\n%s%s%sContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), - data->version.major, - data->version.minor, + QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), + data->version.major, data->version.minor, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), (mime_type? QSE_MT("Content-Type: "): QSE_MT("")), (mime_type? mime_type: QSE_MT("")), (mime_type? QSE_MT("\r\n"): QSE_MT("")), @@ -676,9 +836,10 @@ qse_printf (QSE_T("opending file %hs\n"), data->name); #else x = qse_httpd_entaskformat ( httpd, client, x, - QSE_MT("HTTP/%d.%d 200 OK\r\n%s%s%sContent-Length: %lu\r\nContent-Location: %s\r\n\r\n"), + QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Location: %s\r\n\r\n"), data->version.major, data->version.minor, + (data->keepalive? QSE_MT("Keep-Alive"): QSE_MT("Close")), (mime_type? QSE_MT("Content-Type: "): QSE_MT("")), (mime_type? mime_type: QSE_MT("")), (mime_type? QSE_MT("\r\n"): QSE_MT("")), @@ -706,7 +867,8 @@ qse_httpd_task_t* qse_httpd_entaskpath ( const qse_httpd_task_t* pred, const qse_mchar_t* name, const qse_http_range_t* range, - const qse_http_version_t* verison) + const qse_http_version_t* verison, + int keepalive) { qse_httpd_task_t task; task_path_t data; @@ -716,6 +878,7 @@ qse_httpd_task_t* qse_httpd_entaskpath ( if (range) data.range = *range; else data.range.type = QSE_HTTP_RANGE_NONE; data.version = *verison; + data.keepalive = keepalive; QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); task.init = task_init_path; diff --git a/qse/lib/sed/sed.c b/qse/lib/sed/sed.c index 5c907b1d..ddcec936 100644 --- a/qse/lib/sed/sed.c +++ b/qse/lib/sed/sed.c @@ -30,8 +30,9 @@ #else # if defined(QSE_CHAR_IS_MCHAR) && defined(USE_REGEX) # include +# else +# include # endif -# include #endif QSE_IMPLEMENT_COMMON_FUNCTIONS (sed) diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index a4a263b5..e27489d0 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -101,7 +101,8 @@ qse_httpd_entaskstatictext (httpd, client, QSE_NULL, QSE_MT("HTTP/1.1 416 Reques httpd, client, QSE_NULL, qse_htre_getqpathptr(req), (rangestr? &range: QSE_NULL), - qse_htre_getversion(req) + qse_htre_getversion(req), + req->attr.keepalive ); if (x == QSE_NULL) goto oops; } @@ -120,7 +121,7 @@ qse_httpd_entaskstatictext (httpd, client, QSE_NULL, QSE_MT("HTTP/1.1 416 Reques } done: - if (req->attr.connection_close) + if (!req->attr.keepalive) { x = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); if (x == QSE_NULL) goto oops; @@ -149,6 +150,11 @@ const qse_mchar_t* get_mime_type (qse_httpd_t* httpd, const qse_mchar_t* path) return QSE_NULL; } +int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path) +{ + return 404; +} + static qse_httpd_t* httpd = NULL; static void sigint (int sig) @@ -158,9 +164,10 @@ static void sigint (int sig) static qse_httpd_cbs_t httpd_cbs = { - { get_mime_type, QSE_NULL, }, handle_request, - handle_expect_continue + handle_expect_continue, + get_mime_type, + list_directory }; int httpd_main (int argc, qse_char_t* argv[]) @@ -168,9 +175,9 @@ int httpd_main (int argc, qse_char_t* argv[]) int n; httpd_xtn_t* xtn; - if (argc != 2) + if (argc <= 1) { - qse_fprintf (QSE_STDERR, QSE_T("Usage: %s \n"), argv[0]); + qse_fprintf (QSE_STDERR, QSE_T("Usage: %s ...\n"), argv[0]); return -1; } @@ -184,11 +191,15 @@ int httpd_main (int argc, qse_char_t* argv[]) xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); xtn->orgcbs = qse_httpd_getcbs (httpd); - if (qse_httpd_addlisteners (httpd, argv[1]) <= -1) + for (n = 1; n < argc; n++) { - qse_fprintf (QSE_STDERR, QSE_T("Failed to add httpd listeners\n")); - qse_httpd_close (httpd); - return -1; + if (qse_httpd_addlistener (httpd, argv[n]) <= -1) + { + qse_fprintf (QSE_STDERR, + QSE_T("Failed to add httpd listener - %s\n"), argv[n]); + qse_httpd_close (httpd); + return -1; + } } qse_httpd_setcbs (httpd, &httpd_cbs);