diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c
index 7ce38686..66374492 100644
--- a/qse/cmd/awk/awk.c
+++ b/qse/cmd/awk/awk.c
@@ -350,8 +350,7 @@ static void dprint_return (qse_awk_rtx_t* rtx, qse_awk_val_t* ret)
}
#ifdef ENABLE_CALLBACK
-static void on_statement (
- qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, void* data)
+static void on_statement (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
{
dprint (QSE_T("running %d at line %d\n"), (int)nde->type, (int)nde->loc.line);
}
@@ -975,13 +974,18 @@ static int awk_main (int argc, qse_char_t* argv[])
qse_awk_t* awk = QSE_NULL;
qse_awk_rtx_t* rtx = QSE_NULL;
qse_awk_val_t* retv;
-#ifdef ENABLE_CALLBACK
- qse_awk_rcb_t rcb;
-#endif
int i;
struct arg_t arg;
int ret = -1;
+#ifdef ENABLE_CALLBACK
+ static qse_awk_rtx_ecb_t rtx_ecb =
+ {
+ QSE_FV(.close, QSE_NULL),
+ QSE_FV(.stmt, on_statement)
+ };
+#endif
+
/* TODO: change it to support multiple source files */
qse_awk_parsestd_t psin;
qse_awk_parsestd_t psout;
@@ -1080,12 +1084,6 @@ static int awk_main (int argc, qse_char_t* argv[])
goto oops;
}
-#ifdef ENABLE_CALLBACK
- qse_memset (&rcb, 0, QSE_SIZEOF(rcb));
- rcb.stmt = on_statement;
- rcb.ctx = &arg;
-#endif
-
rtx = qse_awk_rtx_openstd (
awk, 0, QSE_T("qseawk"),
(const qse_char_t*const*)arg.icf.ptr,
@@ -1104,7 +1102,7 @@ static int awk_main (int argc, qse_char_t* argv[])
app_rtx = rtx;
#ifdef ENABLE_CALLBACK
- qse_awk_rtx_pushrcb (rtx, &rcb);
+ qse_awk_rtx_pushecb (rtx, &rtx_ecb);
#endif
set_intr_run ();
diff --git a/qse/include/qse/awk/awk.h b/qse/include/qse/awk/awk.h
index e0e0c19a..8dcb1f70 100644
--- a/qse/include/qse/awk/awk.h
+++ b/qse/include/qse/awk/awk.h
@@ -617,6 +617,8 @@ struct qse_awk_prm_t
};
typedef struct qse_awk_prm_t qse_awk_prm_t;
+/* ------------------------------------------------------------------------ */
+
/**
* The qse_awk_sio_t type defines a script stream handler set.
* The qse_awk_parse() function calls the input and output handler to parse
@@ -664,6 +666,8 @@ struct qse_awk_sio_t
};
typedef struct qse_awk_sio_t qse_awk_sio_t;
+/* ------------------------------------------------------------------------ */
+
/**
* The qse_awk_rio_t type defines a runtime I/O handler set.
* @sa qse_awk_rtx_t
@@ -676,56 +680,93 @@ struct qse_awk_rio_t
};
typedef struct qse_awk_rio_t qse_awk_rio_t;
+/* ------------------------------------------------------------------------ */
+
/**
- * The qse_awk_rcb_close_t type defines the callback function
+ * The qse_awk_ecb_close_t type defines the callback function
+ * called when an awk object is closed.
+ */
+typedef void (*qse_awk_ecb_close_t) (
+ qse_awk_t* awk /**< awk */
+);
+
+/**
+ * The qse_awk_ecb_clear_t type defines the callback function
+ * called when an awk object is cleared.
+ */
+typedef void (*qse_awk_ecb_clear_t) (
+ qse_awk_t* awk /**< awk */
+);
+
+/**
+ * The qse_awk_ecb_t type defines an event callback set.
+ * You can register a callback function set with
+ * qse_awk_pushecb(). The callback functions in the registered
+ * set are called in the reverse order of registration.
+ */
+typedef struct qse_awk_ecb_t qse_awk_ecb_t;
+struct qse_awk_ecb_t
+{
+ /**
+ * called by qse_awk_close().
+ */
+ qse_awk_ecb_close_t close;
+
+ /**
+ * called by qse_awk_clear().
+ */
+ qse_awk_ecb_clear_t clear;
+
+ /* internal use only. don't touch this field */
+ qse_awk_ecb_t* next;
+};
+
+/* ------------------------------------------------------------------------ */
+
+/**
+ * The qse_awk_rtx_ecb_close_t type defines the callback function
* called when the runtime context is closed.
*/
-typedef void (*qse_awk_rcb_close_t) (
- qse_awk_rtx_t* rtx, /**< runtime context */
- void* ctx /**< user-defined data */
+typedef void (*qse_awk_rtx_ecb_close_t) (
+ qse_awk_rtx_t* rtx /**< runtime context */
);
/**
- * The qse_awk_rcb_stmt_t type defines the callback function for each
+ * The qse_awk_rtx_ecb_stmt_t type defines the callback function for each
* statement.
*/
-typedef void (*qse_awk_rcb_stmt_t) (
+typedef void (*qse_awk_rtx_ecb_stmt_t) (
qse_awk_rtx_t* rtx, /**< runtime context */
- qse_awk_nde_t* nde, /**< node */
- void* ctx /**< user-defined data */
+ qse_awk_nde_t* nde /**< node */
);
/**
- * The qse_awk_rcb_t type defines a runtime callback set. You can
- * register a callback function set with qse_awk_rtx_pushrcb().
- * The callback functions in the set registered are called in a
- * proper context in the reverse order of registeration.
+ * The qse_awk_rtx_ecb_t type defines an event callback set for a
+ * runtime context. You can register a callback function set with
+ * qse_awk_rtx_pushecb(). The callback functions in the registered
+ * set are called in the reverse order of registration.
*/
-typedef struct qse_awk_rcb_t qse_awk_rcb_t;
-struct qse_awk_rcb_t
+typedef struct qse_awk_rtx_ecb_t qse_awk_rtx_ecb_t;
+struct qse_awk_rtx_ecb_t
{
/**
* called by qse_awk_rtx_close().
*/
- qse_awk_rcb_close_t close;
+ qse_awk_rtx_ecb_close_t close;
/**
* called by qse_awk_rtx_loop() and qse_awk_rtx_call() for
* each statement executed.
*/
- qse_awk_rcb_stmt_t stmt;
-
- /**
- * A caller may store a user-defined data pointer into this field. This
- * is passed to an actual callback.
- */
- void* ctx;
+ qse_awk_rtx_ecb_stmt_t stmt;
/* internal use only. don't touch this field */
- qse_awk_rcb_t* next;
+ qse_awk_rtx_ecb_t* next;
};
+/* ------------------------------------------------------------------------ */
+
/**
* The qse_awk_option_t type defines various options to change the behavior
* of #qse_awk_t.
@@ -1381,6 +1422,23 @@ void qse_awk_setmaxdepth (
qse_size_t depth /**< maximum depth */
);
+/**
+ * The qse_awk_popecb() function pops an awk event callback set
+ * and returns the pointer to it. If no callback set can be popped,
+ * it returns #QSE_NULL.
+ */
+qse_awk_ecb_t* qse_awk_popecb (
+ qse_awk_t* awk /**< awk */
+);
+
+/**
+ * The qse_awk_pushecb() function register a runtime callback set.
+ */
+void qse_awk_pushecb (
+ qse_awk_t* awk, /**< awk */
+ qse_awk_ecb_t* ecb /**< callback set */
+);
+
/**
* The qse_awk_addgbl() function adds an intrinsic global variable.
* @return the ID of the global variable added on success, -1 on failure.
@@ -1717,20 +1775,20 @@ void qse_awk_rtx_setrio (
);
/**
- * The qse_awk_rtx_poprcb() function pops a runtime callback set
+ * The qse_awk_rtx_popecb() function pops a runtime callback set
* and returns the pointer to it. If no callback set can be popped,
* it returns #QSE_NULL.
*/
-qse_awk_rcb_t* qse_awk_rtx_poprcb (
+qse_awk_rtx_ecb_t* qse_awk_rtx_popecb (
qse_awk_rtx_t* rtx /**< runtime context */
);
/**
- * The qse_awk_rtx_pushrcb() function register a runtime callback set.
+ * The qse_awk_rtx_pushecb() function register a runtime callback set.
*/
-void qse_awk_rtx_pushrcb (
- qse_awk_rtx_t* rtx, /**< runtime context */
- qse_awk_rcb_t* rcb /**< callback set */
+void qse_awk_rtx_pushecb (
+ qse_awk_rtx_t* rtx, /**< runtime context */
+ qse_awk_rtx_ecb_t* ecb /**< callback set */
);
/**
diff --git a/qse/include/qse/macros.h b/qse/include/qse/macros.h
index d6038591..adc66126 100644
--- a/qse/include/qse/macros.h
+++ b/qse/include/qse/macros.h
@@ -216,6 +216,15 @@
} \
)
+/**
+ * The QSE_FV() macro is used to specify a initial value
+ * for a field of an aggregate type.
+ */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
+# define QSE_FV(field,value) field = value
+#else
+# define QSE_FV(field,value) value
+#endif
/* number of characters to number of bytes */
#define QSE_NCTONB(x) ((x)*sizeof(qse_char_t))
diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h
index 741e6953..f3bb3f5f 100644
--- a/qse/include/qse/net/httpd.h
+++ b/qse/include/qse/net/httpd.h
@@ -305,6 +305,32 @@ struct qse_httpd_client_t
} task;
};
+/**
+ * The qse_httpd_ecb_close_t type defines the callback function
+ * called when an httpd object is closed.
+ */
+typedef void (*qse_httpd_ecb_close_t) (
+ qse_httpd_t* httpd /**< httpd */
+);
+
+/**
+ * The qse_httpd_ecb_t type defines an event callback set.
+ * You can register a callback function set with
+ * qse_httpd_pushecb(). The callback functions in the registered
+ * set are called in the reverse order of registration.
+ */
+typedef struct qse_httpd_ecb_t qse_httpd_ecb_t;
+struct qse_httpd_ecb_t
+{
+ /**
+ * called by qse_httpd_close().
+ */
+ qse_httpd_ecb_close_t close;
+
+ /* internal use only. don't touch this field */
+ qse_httpd_ecb_t* next;
+};
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -344,6 +370,23 @@ void qse_httpd_setoption (
int option
);
+/**
+ * The qse_httpd_popecb() function pops an httpd event callback set
+ * and returns the pointer to it. If no callback set can be popped,
+ * it returns #QSE_NULL.
+ */
+qse_httpd_ecb_t* qse_httpd_popecb (
+ qse_httpd_t* httpd /**< httpd */
+);
+
+/**
+ * The qse_httpd_pushecb() function register a runtime callback set.
+ */
+void qse_httpd_pushecb (
+ qse_httpd_t* httpd, /**< httpd */
+ qse_httpd_ecb_t* ecb /**< callback set */
+);
+
/**
* The qse_httpd_loop() function starts a httpd server loop.
*/
@@ -360,7 +403,6 @@ void qse_httpd_stop (
qse_httpd_t* httpd
);
-
int qse_httpd_addserver (
qse_httpd_t* httpd,
const qse_char_t* uri
@@ -502,6 +544,23 @@ void qse_httpd_freemem (
void* ptr
);
+/* -------------------------------------------- */
+
+
+qse_httpd_t* qse_httpd_openstd (
+ qse_size_t xtnsize
+);
+
+qse_httpd_t* qse_httpd_openstdwithmmgr (
+ qse_mmgr_t* mmgr,
+ qse_size_t xtnsize
+);
+
+void* qse_httpd_getxtnstd (
+ qse_httpd_t* httpd
+);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/qse/include/qse/sed/sed.h b/qse/include/qse/sed/sed.h
index 0ca1f959..d4620b2b 100644
--- a/qse/include/qse/sed/sed.h
+++ b/qse/include/qse/sed/sed.h
@@ -345,6 +345,32 @@ typedef int (*qse_sed_lformatter_t) (
int (*cwriter) (qse_sed_t*, qse_char_t)
);
+/**
+ * The qse_sed_ecb_close_t type defines the callback function
+ * called when an sed object is closed.
+ */
+typedef void (*qse_sed_ecb_close_t) (
+ qse_sed_t* sed /**< sed */
+);
+
+/**
+ * The qse_sed_ecb_t type defines an event callback set.
+ * You can register a callback function set with
+ * qse_sed_pushecb(). The callback functions in the registered
+ * set are called in the reverse order of registration.
+ */
+typedef struct qse_sed_ecb_t qse_sed_ecb_t;
+struct qse_sed_ecb_t
+{
+ /**
+ * called by qse_sed_close().
+ */
+ qse_sed_ecb_close_t close;
+
+ /* internal use only. don't touch this field */
+ qse_sed_ecb_t* next;
+};
+
#ifdef QSE_ENABLE_SEDTRACER
enum qse_sed_exec_op_t
{
@@ -515,6 +541,23 @@ void qse_sed_seterror (
const qse_sed_loc_t* errloc /**< error location */
);
+/**
+ * The qse_sed_popecb() function pops an sed event callback set
+ * and returns the pointer to it. If no callback set can be popped,
+ * it returns #QSE_NULL.
+ */
+qse_sed_ecb_t* qse_sed_popecb (
+ qse_sed_t* sed /**< sed */
+);
+
+/**
+ * The qse_sed_pushecb() function register a runtime callback set.
+ */
+void qse_sed_pushecb (
+ qse_sed_t* sed, /**< sed */
+ qse_sed_ecb_t* ecb /**< callback set */
+);
+
/**
* The qse_sed_comp() function compiles editing commands into an internal form.
* @return 0 on success, -1 on error
diff --git a/qse/lib/awk/awk.c b/qse/lib/awk/awk.c
index 23fc56e3..bd7be116 100644
--- a/qse/lib/awk/awk.c
+++ b/qse/lib/awk/awk.c
@@ -262,8 +262,14 @@ oops:
int qse_awk_close (qse_awk_t* awk)
{
+ qse_awk_ecb_t* ecb;
+
if (qse_awk_clear (awk) <= -1) return -1;
/*qse_awk_clrfnc (awk);*/
+
+ for (ecb = awk->ecb; ecb; ecb = ecb->next)
+ if (ecb->close) ecb->close (awk);
+
qse_htb_close (awk->fnc.user);
qse_lda_close (awk->parse.params);
@@ -287,6 +293,11 @@ int qse_awk_close (qse_awk_t* awk)
int qse_awk_clear (qse_awk_t* awk)
{
+ qse_awk_ecb_t* ecb;
+
+ for (ecb = awk->ecb; ecb; ecb = ecb->next)
+ if (ecb->clear) ecb->clear (awk);
+
awk->stopall = QSE_FALSE;
clear_token (&awk->tok);
@@ -439,3 +450,18 @@ void qse_awk_setmaxdepth (qse_awk_t* awk, int types, qse_size_t depth)
awk->parse.depth.max.incl = depth;
}
}
+
+
+qse_awk_ecb_t* qse_awk_popecb (qse_awk_t* awk)
+{
+ qse_awk_ecb_t* top = awk->ecb;
+ if (top) awk->ecb = top->next;
+ return top;
+}
+
+void qse_awk_pushecb (qse_awk_t* awk, qse_awk_ecb_t* ecb)
+{
+ ecb->next = awk->ecb;
+ awk->ecb = ecb;
+}
+
diff --git a/qse/lib/awk/awk.h b/qse/lib/awk/awk.h
index 7edb51db..e0492e8f 100644
--- a/qse/lib/awk/awk.h
+++ b/qse/lib/awk/awk.h
@@ -235,6 +235,7 @@ struct qse_awk_t
qse_awk_errinf_t errinf;
qse_bool_t stopall;
+ qse_awk_ecb_t* ecb;
};
struct qse_awk_chain_t
@@ -371,7 +372,7 @@ struct qse_awk_rtx_t
qse_awk_errinf_t errinf;
qse_awk_t* awk;
- qse_awk_rcb_t* rcb;
+ qse_awk_rtx_ecb_t* ecb;
};
diff --git a/qse/lib/awk/run.c b/qse/lib/awk/run.c
index 67e55c5f..ea83b127 100644
--- a/qse/lib/awk/run.c
+++ b/qse/lib/awk/run.c
@@ -744,10 +744,10 @@ qse_awk_rtx_t* qse_awk_rtx_open (
void qse_awk_rtx_close (qse_awk_rtx_t* rtx)
{
- qse_awk_rcb_t* rcb;
+ qse_awk_rtx_ecb_t* ecb;
- for (rcb = rtx->rcb; rcb; rcb = rcb->next)
- if (rcb->close) rcb->close (rtx, rcb->ctx);
+ for (ecb = rtx->ecb; ecb; ecb = ecb->next)
+ if (ecb->close) ecb->close (rtx);
/* NOTE:
* the close callbacks are called before data in rtx
@@ -784,17 +784,17 @@ void qse_awk_rtx_setrio (qse_awk_rtx_t* rtx, const qse_awk_rio_t* rio)
rtx->rio.handler[QSE_AWK_RIO_CONSOLE] = rio->console;
}
-qse_awk_rcb_t* qse_awk_rtx_poprcb (qse_awk_rtx_t* rtx)
+qse_awk_rtx_ecb_t* qse_awk_rtx_popecb (qse_awk_rtx_t* rtx)
{
- qse_awk_rcb_t* top = rtx->rcb;
- if (top) rtx->rcb = top->next;
+ qse_awk_rtx_ecb_t* top = rtx->ecb;
+ if (top) rtx->ecb = top->next;
return top;
}
-void qse_awk_rtx_pushrcb (qse_awk_rtx_t* rtx, qse_awk_rcb_t* rcb)
+void qse_awk_rtx_pushecb (qse_awk_rtx_t* rtx, qse_awk_rtx_ecb_t* ecb)
{
- rcb->next = rtx->rcb;
- rtx->rcb = rcb;
+ ecb->next = rtx->ecb;
+ rtx->ecb = ecb;
}
static void free_namedval (qse_htb_t* map, void* dptr, qse_size_t dlen)
@@ -1826,10 +1826,10 @@ static int run_block0 (qse_awk_rtx_t* rtx, qse_awk_nde_blk_t* nde)
}
#define ON_STATEMENT(rtx,nde) QSE_BLOCK ( \
- qse_awk_rcb_t* rcb; \
+ qse_awk_rtx_ecb_t* ecb; \
if ((rtx)->awk->stopall) (rtx)->exit_level = EXIT_ABORT; \
- for (rcb = (rtx)->rcb; rcb; rcb = rcb->next) \
- if (rcb->stmt) rcb->stmt (rtx, nde, rcb->ctx); \
+ for (ecb = (rtx)->ecb; ecb; ecb = ecb->next) \
+ if (ecb->stmt) ecb->stmt (rtx, nde); \
)
static int run_statement (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
diff --git a/qse/lib/awk/std.c b/qse/lib/awk/std.c
index 50a4c430..35706d9c 100644
--- a/qse/lib/awk/std.c
+++ b/qse/lib/awk/std.c
@@ -1478,7 +1478,7 @@ static qse_ssize_t awk_rio_console (
return -1;
}
-static void fini_rxtn (qse_awk_rtx_t* rtx, void* ctx)
+static void fini_rxtn (qse_awk_rtx_t* rtx)
{
rxtn_t* rxtn = (rxtn_t*) QSE_XTN (rtx);
@@ -1876,11 +1876,10 @@ qse_awk_rtx_t* qse_awk_rtx_openstd (
const qse_char_t*const ocf[],
qse_cmgr_t* cmgr)
{
- static qse_awk_rcb_t rcb =
+ static qse_awk_rtx_ecb_t ecb =
{
- fini_rxtn,
- QSE_NULL,
- QSE_NULL
+ QSE_FV (.close, fini_rxtn),
+ QSE_FV (.stmt, QSE_NULL)
};
qse_awk_rtx_t* rtx;
@@ -1921,7 +1920,7 @@ qse_awk_rtx_t* qse_awk_rtx_openstd (
rxtn->cmgrtab_inited = 1;
}
- qse_awk_rtx_pushrcb (rtx, &rcb);
+ qse_awk_rtx_pushecb (rtx, &ecb);
rxtn->seed = (qse_gettime (&now) <= -1)? 0u: (qse_long_t)now;
/* i don't care if the seed becomes negative or overflows.
diff --git a/qse/lib/net/Makefile.am b/qse/lib/net/Makefile.am
index 9cbd51c2..49bf4013 100644
--- a/qse/lib/net/Makefile.am
+++ b/qse/lib/net/Makefile.am
@@ -16,6 +16,7 @@ libqsenet_la_SOURCES = \
httpd-cgi.c \
httpd-proxy.c \
httpd-resol.c \
+ httpd-std.c \
httpd-task.c \
upxd.c
diff --git a/qse/lib/net/Makefile.in b/qse/lib/net/Makefile.in
index 1755e151..5b091304 100644
--- a/qse/lib/net/Makefile.in
+++ b/qse/lib/net/Makefile.in
@@ -79,8 +79,8 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libqsenet_la_DEPENDENCIES =
am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
- httpd-cgi.lo httpd-proxy.lo httpd-resol.lo httpd-task.lo \
- upxd.lo
+ httpd-cgi.lo httpd-proxy.lo httpd-resol.lo httpd-std.lo \
+ httpd-task.lo upxd.lo
libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS)
libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -273,6 +273,7 @@ libqsenet_la_SOURCES = \
httpd-cgi.c \
httpd-proxy.c \
httpd-resol.c \
+ httpd-std.c \
httpd-task.c \
upxd.c
@@ -358,6 +359,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-cgi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-proxy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-std.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-task.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd.Plo@am__quote@
diff --git a/qse/lib/net/httpd-std.c b/qse/lib/net/httpd-std.c
new file mode 100644
index 00000000..668f8689
--- /dev/null
+++ b/qse/lib/net/httpd-std.c
@@ -0,0 +1,1627 @@
+/*
+ * $Id$
+ *
+ Copyright 2006-2012 Chung, Hyung-Hwan.
+ This file is part of QSE.
+
+ QSE is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ QSE is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with QSE. If not, see .
+ */
+
+#include "httpd.h"
+#include "../cmn/mem.h"
+
+#if defined(_WIN32)
+# include
+# include /* sockaddr_in6 */
+# include
+
+#elif defined(__OS2__)
+ /* TODO */
+
+#elif defined(__DOS__)
+ /* TODO */
+
+#else
+# include "../cmn/syscall.h"
+# include
+# include
+# include
+# include
+# if defined(HAVE_SYS_SENDFILE_H)
+# include
+# endif
+# if defined(HAVE_EPOLL) && defined(HAVE_SYS_EPOLL_H)
+# include
+# endif
+#endif
+
+
+#if defined(HAVE_SSL)
+# include
+# include
+# include
+#endif
+
+#include /* TODO: remove this */
+
+/* ------------------------------------------------------------------- */
+
+#if defined(_WIN32)
+static qse_httpd_errnum_t syserr_to_errnum (DWORD e)
+{
+
+ switch (e)
+ {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ return QSE_HTTPD_ENOMEM;
+
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_NAME:
+ return QSE_HTTPD_EINVAL;
+
+ case ERROR_ACCESS_DENIED:
+ return QSE_HTTPD_EACCES;
+
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return QSE_HTTPD_ENOENT;
+
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ return QSE_HTTPD_EEXIST;
+
+ default:
+ return QSE_HTTPD_ESYSERR;
+ }
+}
+#elif defined(__OS2__)
+static qse_httpd_errnum_t syserr_to_errnum (APIRET e)
+{
+ switch (e)
+ {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return QSE_HTTPD_ENOMEM;
+
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_NAME:
+ return QSE_HTTPD_EINVAL;
+
+ case ERROR_ACCESS_DENIED:
+ return QSE_HTTPD_EACCES;
+
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return QSE_HTTPD_ENOENT;
+
+ case ERROR_ALREADY_EXISTS:
+ return QSE_HTTPD_EEXIST;
+
+ default:
+ return QSE_HTTPD_ESYSERR;
+ }
+}
+#elif defined(__DOS__)
+static qse_httpd_errnum_t syserr_to_errnum (int e)
+{
+ switch (e)
+ {
+ case ENOMEM:
+ return QSE_HTTPD_ENOMEM;
+
+ case EINVAL:
+ return QSE_HTTPD_EINVAL;
+
+ case EACCES:
+ return QSE_HTTPD_EACCES;
+
+ case ENOENT:
+ return QSE_HTTPD_ENOENT;
+
+ case EEXIST:
+ return QSE_HTTPD_EEXIST;
+
+ default:
+ return QSE_HTTPD_ESYSERR;
+ }
+}
+
+#else
+static qse_httpd_errnum_t syserr_to_errnum (int e)
+{
+ switch (e)
+ {
+ case ENOMEM:
+ return QSE_HTTPD_ENOMEM;
+
+ case EINVAL:
+ return QSE_HTTPD_EINVAL;
+
+ case EACCES:
+ case ECONNREFUSED:
+ return QSE_HTTPD_EACCES;
+
+ case ENOENT:
+ return QSE_HTTPD_ENOENT;
+
+ case EEXIST:
+ return QSE_HTTPD_EEXIST;
+
+ case EINTR:
+ return QSE_HTTPD_EINTR;
+
+ case EAGAIN:
+ /*case EWOULDBLOCK:*/
+ return QSE_HTTPD_EAGAIN;
+
+ default:
+ return QSE_HTTPD_ESYSERR;
+ }
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+
+#define MAX_SEND_SIZE 4096
+
+#if defined(_WIN32)
+ /* TODO */
+ /* TODO: WIN32 TransmitFile */
+#elif defined(__OS2__)
+ /* TODO */
+#elif defined(__DOS__)
+ /* TODO */
+
+#elif defined(HAVE_SENDFILE) && defined(HAVE_SENDFILE64)
+# if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
+# define xsendfile(out,in,offset,count) sendfile64(out,in,offset,count)
+# else
+# define xsendfile(out,in,offset,count) sendfile(out,in,offset,count)
+# endif
+
+#elif defined(HAVE_SENDFILE)
+# define xsendfile(out,in,offset,count) sendfile(out,in,offset,count)
+
+#elif defined(HAVE_SENDFILE64)
+# define xsendfile(out,in,offset,count) sendfile64(out,in,offset,count)
+
+#elif defined(HAVE_SENDFILEV) || defined(HAVE_SENDFILEV64)
+
+static qse_ssize_t xsendfile (
+ int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
+{
+#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
+ struct sendfilevec64 vec;
+#else
+ struct sendfilevec vec;
+#endif
+ size_t xfer;
+ ssize_t n;
+
+ vec.sfv_fd = in_fd;
+ vec.sfv_flag = 0;
+ if (offset)
+ {
+ vec.sfv_off = *offset;
+ }
+ else
+ {
+ vec.sfv_off = QSE_LSEEK (in_fd, 0, SEEK_CUR);
+ if (vec.sfv_off == (off_t)-1) return (qse_ssize_t)-1;
+ }
+ vec.sfv_len = count;
+
+#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
+ n = sendfilev64 (out_fd, &vec, 1, &xfer);
+#else
+ n = sendfilev (out_fd, &vec, 1, &xfer);
+#endif
+ if (offset) *offset = *offset + xfer;
+
+/* TODO: xfer contains number of byte written even on failure
+on success xfer == n.
+on failure xfer != n.
+ */
+ return n;
+}
+
+#else
+
+static qse_ssize_t xsendfile (
+ int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
+{
+ qse_mchar_t buf[MAX_SEND_SIZE];
+ qse_ssize_t n;
+
+ if (offset && QSE_LSEEK (in_fd, *offset, SEEK_SET) != *offset)
+ return (qse_ssize_t)-1;
+
+ if (count > QSE_COUNTOF(buf)) count = QSE_COUNTOF(buf);
+ n = read (in_fd, buf, count);
+ if (n == (qse_ssize_t)-1 || n == 0) return n;
+
+ n = send (out_fd, buf, n, 0);
+ if (n > 0 && offset) *offset = *offset + n;
+
+ return n;
+}
+
+#endif
+
+/* ------------------------------------------------------------------- */
+
+#if defined(HAVE_SSL)
+static qse_ssize_t xsendfile_ssl (
+ SSL* out, int in_fd, qse_foff_t* offset, qse_size_t count)
+{
+ qse_mchar_t buf[MAX_SEND_SIZE];
+ qse_ssize_t n;
+
+ if (offset && QSE_LSEEK (in_fd, *offset, SEEK_SET) != *offset)
+ return (qse_ssize_t)-1;
+
+ if (count > QSE_COUNTOF(buf)) count = QSE_COUNTOF(buf);
+ n = read (in_fd, buf, count);
+ if (n == (qse_ssize_t)-1 || n == 0) return n;
+
+ n = SSL_write (out, buf, count);
+ if (n > 0 && offset) *offset = *offset + n;
+
+ return n;
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+
+typedef struct httpd_xtn_t httpd_xtn_t;
+struct httpd_xtn_t
+{
+#if defined(HAVE_SSL)
+ SSL_CTX* ssl_ctx;
+#endif
+};
+
+#if defined(HAVE_SSL)
+static int init_xtn_ssl (
+ httpd_xtn_t* xtn,
+ const qse_mchar_t* pemfile,
+ const qse_mchar_t* keyfile/*,
+ const qse_mchar_t* chainfile*/)
+{
+ SSL_CTX* ctx;
+
+ SSL_library_init ();
+ SSL_load_error_strings ();
+ /*SSLeay_add_ssl_algorithms();*/
+
+ ctx = SSL_CTX_new (SSLv23_server_method());
+ if (ctx == QSE_NULL) return -1;
+
+ /*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/
+
+ if (SSL_CTX_use_certificate_file (ctx, pemfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_check_private_key (ctx) == 0 /*||
+ SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/)
+ {
+ qse_mchar_t buf[128];
+ ERR_error_string_n(ERR_get_error(), buf, QSE_COUNTOF(buf));
+ qse_fprintf (QSE_STDERR, QSE_T("Error: %hs\n"), buf);
+ SSL_CTX_free (ctx);
+ return -1;
+ }
+
+
+/* TODO: CRYPTO_set_id_callback ();
+ TODO: CRYPTO_set_locking_callback ();*/
+
+ xtn->ssl_ctx = ctx;
+ return 0;
+}
+
+static void fini_xtn_ssl (httpd_xtn_t* xtn)
+{
+/* TODO: CRYPTO_set_id_callback (QSE_NULL);
+ TODO: CRYPTO_set_locking_callback (QSE_NULL); */
+ SSL_CTX_free (xtn->ssl_ctx);
+
+
+ /*ERR_remove_state ();*/
+ ENGINE_cleanup ();
+
+ ERR_free_strings ();
+ EVP_cleanup ();
+ CRYPTO_cleanup_all_ex_data ();
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+
+static void cleanup_standard_httpd (qse_httpd_t* httpd)
+{
+ httpd_xtn_t* xtn;
+
+ xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
+
+#if defined(HAVE_SSL)
+ if (xtn->ssl_ctx) fini_xtn_ssl (xtn);
+#endif
+}
+
+qse_httpd_t* qse_httpd_openstd (qse_size_t xtnsize)
+{
+ return qse_httpd_openstdwithmmgr (QSE_MMGR_GETDFL(), xtnsize);
+}
+
+qse_httpd_t* qse_httpd_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize)
+{
+ qse_httpd_t* httpd;
+ httpd_xtn_t* xtn;
+
+ static qse_httpd_ecb_t std_ecb =
+ {
+ QSE_FV(.close, cleanup_standard_httpd)
+ };
+
+ httpd = qse_httpd_open (mmgr, QSE_SIZEOF(httpd_xtn_t) + xtnsize);
+ if (httpd == QSE_NULL) return QSE_NULL;
+
+ xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
+
+#if defined(HAVE_SSL)
+ xtn->ssl_ctx = QSE_NULL;
+ init_xtn_ssl (xtn, "http01.pem", "http01.key");
+#endif
+
+ qse_httpd_pushecb (httpd, &std_ecb);
+ return httpd;
+}
+
+void* qse_httpd_getxtnstd (qse_httpd_t* httpd)
+{
+ return (void*)((httpd_xtn_t*)QSE_XTN(httpd) + 1);
+}
+
+/* ------------------------------------------------------------------- */
+
+static int sockaddr_to_nwad (
+ const struct sockaddr_storage* addr, qse_nwad_t* nwad)
+{
+ int addrsize = -1;
+
+ switch (addr->ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in* in;
+ in = (struct sockaddr_in*)addr;
+ addrsize = QSE_SIZEOF(*in);
+
+ memset (nwad, 0, QSE_SIZEOF(*nwad));
+ nwad->type = QSE_NWAD_IN4;
+ nwad->u.in4.addr.value = in->sin_addr.s_addr;
+ nwad->u.in4.port = in->sin_port;
+ break;
+ }
+
+#if defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6* in;
+ in = (struct sockaddr_in6*)addr;
+ addrsize = QSE_SIZEOF(*in);
+
+ memset (nwad, 0, QSE_SIZEOF(*nwad));
+ nwad->type = QSE_NWAD_IN6;
+ memcpy (&nwad->u.in6.addr, &in->sin6_addr, QSE_SIZEOF(nwad->u.in6.addr));
+ nwad->u.in6.scope = in->sin6_scope_id;
+ nwad->u.in6.port = in->sin6_port;
+ break;
+ }
+#endif
+ }
+
+ return addrsize;
+}
+
+static int nwad_to_sockaddr (
+ const qse_nwad_t* nwad, struct sockaddr_storage* addr)
+{
+ int addrsize = -1;
+
+ switch (nwad->type)
+ {
+ case QSE_NWAD_IN4:
+ {
+ struct sockaddr_in* in;
+
+ in = (struct sockaddr_in*)addr;
+ addrsize = QSE_SIZEOF(*in);
+ memset (in, 0, addrsize);
+
+ in->sin_family = AF_INET;
+ in->sin_addr.s_addr = nwad->u.in4.addr.value;
+ in->sin_port = nwad->u.in4.port;
+ break;
+ }
+
+ case QSE_NWAD_IN6:
+ {
+#if defined(AF_INET6)
+ struct sockaddr_in6* in;
+
+ in = (struct sockaddr_in6*)addr;
+ addrsize = QSE_SIZEOF(*in);
+ memset (in, 0, addrsize);
+
+ in->sin6_family = AF_INET6;
+ memcpy (&in->sin6_addr, &nwad->u.in6.addr, QSE_SIZEOF(nwad->u.in6.addr));
+ in->sin6_scope_id = nwad->u.in6.scope;
+ in->sin6_port = nwad->u.in6.port;
+#endif
+ break;
+ }
+ }
+
+ return addrsize;
+}
+
+/* ------------------------------------------------------------------- */
+
+static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server)
+{
+ int fd = -1, flag;
+/* TODO: if AF_INET6 is not defined sockaddr_storage is not available...
+ * create your own union or somehting similar... */
+ struct sockaddr_storage addr;
+ int addrsize;
+
+ addrsize = nwad_to_sockaddr (&server->nwad, &addr);
+ if (addrsize <= -1)
+ {
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
+ return -1;
+ }
+
+ fd = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd <= -1) goto oops;
+
+ flag = fcntl (fd, F_GETFD);
+ if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC);
+
+ flag = 1;
+ setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &flag, QSE_SIZEOF(flag));
+
+#if defined(IP_TRANSPARENT)
+ /* remove the ip routing restriction that a packet can only
+ * be sent using a local ip address. this option is useful
+ * if transparency is achieved with TPROXY */
+ flag = 1;
+ setsockopt (fd, SOL_IP, IP_TRANSPARENT, &flag, QSE_SIZEOF(flag));
+#endif
+
+ /* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the
+ * address size for AF_INET. */
+ /*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/
+ if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1)
+ {
+#if defined(IPV6_V6ONLY)
+ if (errno == EADDRINUSE && addr.ss_family == AF_INET6)
+ {
+ int on = 1;
+ setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) goto oops;
+ }
+ else goto oops;
+#else
+ goto oops;
+#endif
+ }
+ if (listen (fd, 10) <= -1) goto oops;
+
+ flag = fcntl (fd, F_GETFL);
+ if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK);
+
+ server->handle.i = fd;
+ return 0;
+
+oops:
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ if (fd >= 0) close (fd);
+ return -1;
+}
+
+static void server_close (qse_httpd_t* httpd, qse_httpd_server_t* server)
+{
+ close (server->handle.i);
+}
+
+static int server_accept (
+ qse_httpd_t* httpd,
+ qse_httpd_server_t* server, qse_httpd_client_t* client)
+{
+ struct sockaddr_storage addr;
+
+#ifdef HAVE_SOCKLEN_T
+ socklen_t addrlen;
+#else
+ int addrlen;
+#endif
+ int fd, flag;
+
+ addrlen = QSE_SIZEOF(addr);
+ fd = accept (server->handle.i, (struct sockaddr*)&addr, &addrlen);
+ if (fd <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum (errno));
+ return -1;
+ }
+
+#if 0
+ if (fd >= FD_SETSIZE)
+ {
+qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
+ /*TODO: qse_httpd_seterrnum (httpd, QSE_HTTPD_EXXXXX);*/
+ close (fd);
+ return -1;
+ }
+#endif
+ flag = fcntl (fd, F_GETFD);
+ if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC);
+
+ flag = fcntl (fd, F_GETFL);
+ if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK);
+
+
+ if (sockaddr_to_nwad (&addr, &client->remote_addr) <= -1)
+ {
+/* TODO: logging */
+ client->remote_addr = server->nwad;
+ }
+
+ addrlen = QSE_SIZEOF(addr);
+ if (getsockname (fd, (struct sockaddr*)&addr, &addrlen) <= -1 ||
+ sockaddr_to_nwad (&addr, &client->local_addr) <= -1)
+ {
+/* TODO: logging */
+ client->local_addr = server->nwad;
+ }
+
+#if defined(SO_ORIGINAL_DST)
+ addrlen = QSE_SIZEOF(addr);
+ if (getsockopt (fd, SOL_IP, SO_ORIGINAL_DST, (char*)&addr, &addrlen) <= -1 ||
+ sockaddr_to_nwad (&addr, &client->orgdst_addr) <= -1)
+ {
+ client->orgdst_addr = client->local_addr;
+ }
+#endif
+
+ client->handle.i = fd;
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+
+static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
+{
+ int fd = -1, flag;
+/* TODO: if AF_INET6 is not defined sockaddr_storage is not available...
+ * create your own union or somehting similar... */
+ struct sockaddr_storage addr;
+ int addrsize;
+ int connected = 1;
+
+ addrsize = nwad_to_sockaddr (&peer->nwad, &addr);
+ if (addrsize <= -1)
+ {
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
+ return -1;
+ }
+
+ fd = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd <= -1) goto oops;
+
+ flag = fcntl (fd, F_GETFD);
+ if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC);
+
+ flag = fcntl (fd, F_GETFL);
+ if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK);
+
+ if (connect (fd, (struct sockaddr*)&addr, addrsize) <= -1)
+ {
+ if (errno != EINPROGRESS) goto oops;
+ connected = 0;
+ }
+
+ /* restore flags */
+ if (fcntl (fd, F_SETFL, flag) <= -1) goto oops;
+
+ peer->handle.i = fd;
+ return connected;
+
+oops:
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ if (fd >= 0) close (fd);
+ return -1;
+}
+
+static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
+{
+ close (peer->handle.i);
+}
+
+static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
+{
+#ifdef HAVE_SOCKLEN_T
+ socklen_t len;
+#else
+ int len;
+#endif
+ int ret;
+
+ len = QSE_SIZEOF(ret);
+ if (getsockopt (peer->handle.i, SOL_SOCKET, SO_ERROR, &ret, &len) <= -1) return -1;
+
+ if (ret == EINPROGRESS) return 0;
+ if (ret != 0)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum (ret));
+ return -1;
+ }
+
+ return 1; /* connection completed */
+}
+
+static qse_ssize_t peer_recv (
+ qse_httpd_t* httpd, qse_httpd_peer_t* peer,
+ qse_mchar_t* buf, qse_size_t bufsize)
+{
+ ssize_t ret = read (peer->handle.i, buf, bufsize);
+ if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return ret;
+}
+
+static qse_ssize_t peer_send (
+ qse_httpd_t* httpd, qse_httpd_peer_t* peer,
+ const qse_mchar_t* buf, qse_size_t bufsize)
+{
+ ssize_t ret = write (peer->handle.i, buf, bufsize);
+ if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return ret;
+}
+
+/* ------------------------------------------------------------------- */
+
+struct mux_ev_t
+{
+ qse_ubi_t handle;
+ int reqmask;
+ qse_httpd_muxcb_t cbfun;
+ void* cbarg;
+};
+
+struct mux_t
+{
+ int fd;
+
+ struct
+ {
+ struct epoll_event* ptr;
+ qse_size_t len;
+ qse_size_t capa;
+ } ee;
+
+ struct
+ {
+ struct mux_ev_t** ptr;
+ qse_size_t capa;
+ } mev;
+};
+
+#define MUX_EV_ALIGN 64
+
+static void* mux_open (qse_httpd_t* httpd)
+{
+ struct mux_t* mux;
+
+ mux = qse_httpd_allocmem (httpd, QSE_SIZEOF(*mux));
+ if (mux == QSE_NULL) return QSE_NULL;
+
+ memset (mux, 0, QSE_SIZEOF(*mux));
+
+#if defined(HAVE_EPOLL_CREATE1) && defined(O_CLOEXEC)
+ mux->fd = epoll_create1 (O_CLOEXEC);
+#else
+ mux->fd = epoll_create (100);
+#endif
+ if (mux->fd <= -1)
+ {
+ qse_httpd_freemem (httpd, mux);
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return QSE_NULL;
+ }
+
+#if defined(HAVE_EPOLL_CREATE1) && defined(O_CLOEXEC)
+ /* nothing else to do */
+#else
+ {
+ int flag = fcntl (mux->fd, F_GETFD);
+ if (flag >= 0) fcntl (mux->fd, F_SETFD, flag | FD_CLOEXEC);
+ }
+#endif
+
+ return mux;
+}
+
+static void mux_close (qse_httpd_t* httpd, void* vmux)
+{
+ struct mux_t* mux = (struct mux_t*)vmux;
+ if (mux->ee.ptr) qse_httpd_freemem (httpd, mux->ee.ptr);
+ if (mux->mev.ptr)
+ {
+ qse_size_t i;
+ for (i = 0; i < mux->mev.capa; i++)
+ if (mux->mev.ptr[i]) qse_httpd_freemem (httpd, mux->mev.ptr[i]);
+ qse_httpd_freemem (httpd, mux->mev.ptr);
+ }
+ close (mux->fd);
+ qse_httpd_freemem (httpd, mux);
+}
+
+static int mux_addhnd (
+ qse_httpd_t* httpd, void* vmux, qse_ubi_t handle,
+ int mask, qse_httpd_muxcb_t cbfun, void* cbarg)
+{
+ struct mux_t* mux = (struct mux_t*)vmux;
+ struct epoll_event ev;
+ struct mux_ev_t* mev;
+
+ ev.events = 0;
+ if (mask & QSE_HTTPD_MUX_READ) ev.events |= EPOLLIN;
+ if (mask & QSE_HTTPD_MUX_WRITE) ev.events |= EPOLLOUT;
+
+ if (ev.events == 0 || handle.i <= -1)
+ {
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);
+ return -1;
+ }
+
+ if (handle.i >= mux->mev.capa)
+ {
+ struct mux_ev_t** tmp;
+ qse_size_t tmpcapa, i;
+
+ tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN);
+
+ tmp = (struct mux_ev_t**) qse_httpd_reallocmem (
+ httpd, mux->mev.ptr,
+ QSE_SIZEOF(*mux->mev.ptr) * tmpcapa);
+ if (tmp == QSE_NULL) return -1;
+
+ for (i = mux->mev.capa; i < tmpcapa; i++) tmp[i] = QSE_NULL;
+ mux->mev.ptr = tmp;
+ mux->mev.capa = tmpcapa;
+ }
+
+ if (mux->mev.ptr[handle.i] == QSE_NULL)
+ {
+ /* the location of the data passed to epoll_ctl()
+ * must not change unless i update the info with epoll()
+ * whenever there is reallocation. so i simply
+ * make mux-mev.ptr reallocatable but auctual
+ * data fixed once allocated. */
+ mux->mev.ptr[handle.i] = qse_httpd_allocmem (
+ httpd, QSE_SIZEOF(*mux->mev.ptr[handle.i]));
+ if (mux->mev.ptr[handle.i] == QSE_NULL) return -1;
+ }
+
+ if (mux->ee.len >= mux->ee.capa)
+ {
+ struct epoll_event* tmp;
+
+ tmp = qse_httpd_reallocmem (
+ httpd, mux->ee.ptr,
+ QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2);
+ if (tmp == QSE_NULL) return -1;
+
+ mux->ee.ptr = tmp;
+ mux->ee.capa = (mux->ee.capa + 1) * 2;
+ }
+
+ mev = mux->mev.ptr[handle.i];
+ mev->handle = handle;
+ mev->reqmask = mask;
+ mev->cbfun = cbfun;
+ mev->cbarg = cbarg;
+
+ ev.data.ptr = mev;
+
+ if (epoll_ctl (mux->fd, EPOLL_CTL_ADD, handle.i, &ev) <= -1)
+ {
+ /* don't rollback ee.ptr */
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ mux->ee.len++;
+ return 0;
+}
+
+static int mux_delhnd (qse_httpd_t* httpd, void* vmux, qse_ubi_t handle)
+{
+ struct mux_t* mux = (struct mux_t*)vmux;
+
+ if (epoll_ctl (mux->fd, EPOLL_CTL_DEL, handle.i, QSE_NULL) <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ mux->ee.len--;
+ return 0;
+}
+
+static int mux_poll (qse_httpd_t* httpd, void* vmux, qse_ntime_t timeout)
+{
+ struct mux_t* mux = (struct mux_t*)vmux;
+ struct mux_ev_t* mev;
+ int mask, nfds, i;
+
+ nfds = epoll_wait (mux->fd, mux->ee.ptr, mux->ee.len, timeout);
+ if (nfds <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ for (i = 0; i < nfds; i++)
+ {
+ mev = mux->ee.ptr[i].data.ptr;
+
+ mask = 0;
+
+ if (mux->ee.ptr[i].events & EPOLLIN)
+ mask |= QSE_HTTPD_MUX_READ;
+ if (mux->ee.ptr[i].events & EPOLLOUT)
+ mask |= QSE_HTTPD_MUX_WRITE;
+
+ if (mux->ee.ptr[i].events & EPOLLHUP)
+ {
+ if (mev->reqmask & QSE_HTTPD_MUX_READ)
+ mask |= QSE_HTTPD_MUX_READ;
+ if (mev->reqmask & QSE_HTTPD_MUX_WRITE)
+ mask |= QSE_HTTPD_MUX_WRITE;
+ }
+
+ mev->cbfun (httpd, mux, mev->handle, mask, mev->cbarg);
+ }
+ return 0;
+}
+
+static int mux_readable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
+{
+ fd_set r;
+ struct timeval tv, * tvp;
+
+ if (msec >= 0)
+ {
+ tv.tv_sec = (msec / 1000);
+ tv.tv_usec = ((msec % 1000) * 1000);
+ tvp = &tv;
+ }
+ else tvp = QSE_NULL;
+
+ FD_ZERO (&r);
+ FD_SET (handle.i, &r);
+
+ return select (handle.i + 1, &r, QSE_NULL, QSE_NULL, tvp);
+}
+
+static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
+{
+ fd_set w;
+ struct timeval tv, * tvp;
+
+ if (msec >= 0)
+ {
+ tv.tv_sec = (msec / 1000);
+ tv.tv_usec = ((msec % 1000) * 1000);
+ tvp = &tv;
+ }
+ else tvp = QSE_NULL;
+
+ FD_ZERO (&w);
+ FD_SET (handle.i, &w);
+
+ return select (handle.i + 1, QSE_NULL, &w, QSE_NULL, tvp);
+}
+
+/* ------------------------------------------------------------------- */
+
+static int file_executable (qse_httpd_t* httpd, const qse_mchar_t* path)
+{
+ if (access (path, X_OK) == -1)
+ return (errno == EACCES)? 0 /*no*/: -1 /*error*/;
+ return 1; /* yes */
+}
+
+static int file_stat (
+ qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_stat_t* hst)
+{
+ struct stat st;
+
+/* TODO: lstat? or stat? */
+ if (stat (path, &st) <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ /* stating for a file. it should be a regular file.
+ * i don't allow other file types. */
+ if (!S_ISREG(st.st_mode))
+ {
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES);
+ return -1;
+ }
+
+ memset (hst, 0, QSE_SIZEOF(*hst));
+
+ hst->size = st.st_size;
+#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
+ hst->mtime = QSE_SECNSEC_TO_MSEC(st.st_mtim.tv_sec,st.st_mtim.tv_nsec);
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
+ hst->mtime = QSE_SECNSEC_TO_MSEC(st.st_mtimespec.tv_sec,st.st_mtimespec.tv_nsec);
+#else
+ hst->mtime = st.st_mtime * QSE_MSECS_PER_SEC;
+#endif
+
+ hst->mime = qse_mbsend (path, QSE_MT(".html"))? QSE_MT("text/html"):
+ qse_mbsend (path, QSE_MT(".txt"))? QSE_MT("text/plain"):
+ qse_mbsend (path, QSE_MT(".jpg"))? QSE_MT("image/jpeg"):
+ qse_mbsend (path, QSE_MT(".mp4"))? QSE_MT("video/mp4"):
+ qse_mbsend (path, QSE_MT(".mp3"))? QSE_MT("audio/mpeg"): QSE_NULL;
+ return 0;
+}
+
+static int file_ropen (
+ qse_httpd_t* httpd, const qse_mchar_t* path, qse_ubi_t* handle)
+{
+ int fd;
+ int flags;
+
+ flags = O_RDONLY;
+#if defined(O_LARGEFILE)
+ flags |= O_LARGEFILE;
+#endif
+
+qse_printf (QSE_T("opening file [%hs] for reading\n"), path);
+ fd = open (path, flags, 0);
+ if (fd <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ flags = fcntl (fd, F_GETFD);
+ if (flags >= 0) fcntl (fd, F_SETFD, flags | FD_CLOEXEC);
+
+ handle->i = fd;
+qse_printf (QSE_T("opened file %hs\n"), path);
+ return 0;
+}
+
+static int file_wopen (
+ qse_httpd_t* httpd, const qse_mchar_t* path,
+ qse_ubi_t* handle)
+{
+ int fd;
+ int flags;
+
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+#if defined(O_LARGEFILE)
+ flags |= O_LARGEFILE;
+#endif
+
+qse_printf (QSE_T("opening file [%hs] for writing\n"), path);
+ fd = open (path, flags, 0644);
+ if (fd <= -1)
+ {
+ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return -1;
+ }
+
+ handle->i = fd;
+ return 0;
+}
+
+static void file_close (qse_httpd_t* httpd, qse_ubi_t handle)
+{
+qse_printf (QSE_T("closing file %d\n"), handle.i);
+ close (handle.i);
+}
+
+static qse_ssize_t file_read (
+ qse_httpd_t* httpd, qse_ubi_t handle,
+ qse_mchar_t* buf, qse_size_t len)
+{
+ return read (handle.i, buf, len);
+}
+
+static qse_ssize_t file_write (
+ qse_httpd_t* httpd, qse_ubi_t handle,
+ const qse_mchar_t* buf, qse_size_t len)
+{
+ return write (handle.i, buf, len);
+}
+
+/* ------------------------------------------------------------------- */
+static void client_close (
+ qse_httpd_t* httpd, qse_httpd_client_t* client)
+{
+ close (client->handle.i);
+}
+
+static void client_shutdown (
+ qse_httpd_t* httpd, qse_httpd_client_t* client)
+{
+#if defined(SHUT_RDWR)
+ shutdown (client->handle.i, SHUT_RDWR);
+#else
+ shutdown (client->handle.i, 2);
+#endif
+}
+
+static qse_ssize_t client_recv (
+ qse_httpd_t* httpd, qse_httpd_client_t* client,
+ qse_mchar_t* buf, qse_size_t bufsize)
+{
+ if (client->secure)
+ {
+#if defined(HAVE_SSL)
+ int ret = SSL_read (client->handle2.ptr, buf, bufsize);
+ if (ret <= -1)
+ {
+ if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_READ)
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN);
+ else
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
+ }
+ return ret;
+#else
+ return -1;
+#endif
+ }
+ else
+ {
+ ssize_t ret = recv (client->handle.i, buf, bufsize, 0);
+ if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return ret;
+ }
+}
+
+static qse_ssize_t client_send (
+ qse_httpd_t* httpd, qse_httpd_client_t* client,
+ const qse_mchar_t* buf, qse_size_t bufsize)
+{
+ if (client->secure)
+ {
+#if defined(HAVE_SSL)
+ int ret = SSL_write (client->handle2.ptr, buf, bufsize);
+ if (ret <= -1)
+ {
+ if (SSL_get_error(client->handle2.ptr,ret) == SSL_ERROR_WANT_WRITE)
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN);
+ else
+ qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
+ }
+ return ret;
+#else
+ return -1;
+#endif
+ }
+ else
+ {
+ ssize_t ret = send (client->handle.i, buf, bufsize, 0);
+ if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
+ return ret;
+ }
+}
+
+static qse_ssize_t client_sendfile (
+ qse_httpd_t* httpd, qse_httpd_client_t* client,
+ qse_ubi_t handle, qse_foff_t* offset, qse_size_t count)
+{
+ if (client->secure)
+ {
+#if defined(HAVE_SSL)
+ return xsendfile_ssl (client->handle2.ptr, handle.i, offset, count);
+#else
+ return -1;
+#endif
+ }
+ else
+ {
+ return xsendfile (client->handle.i, handle.i, offset, count);
+ }
+}
+
+static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client)
+{
+ httpd_xtn_t* xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
+
+ if (client->secure)
+ {
+#if defined(HAVE_SSL)
+ int ret;
+ SSL* ssl;
+
+ if (client->handle2.ptr)
+ {
+ ssl = client->handle2.ptr;
+ }
+ else if (!xtn->ssl_ctx)
+ {
+ /* no ssl */
+qse_printf (QSE_T("NO SSL\n"));
+qse_fflush (QSE_STDOUT);
+ return -1;
+ }
+ else
+ {
+ ssl = SSL_new (xtn->ssl_ctx);
+ if (ssl == QSE_NULL) return -1;
+
+ client->handle2.ptr = ssl;
+
+qse_printf (QSE_T("SSL ACCEPTING %d\n"), client->handle.i);
+qse_fflush (QSE_STDOUT);
+ if (SSL_set_fd (ssl, client->handle.i) == 0)
+ {
+ /* don't free ssl here since client_closed()
+ * will free it */
+ return -1;
+ }
+ }
+
+ ret = SSL_accept (ssl);
+ if (ret <= 0)
+ {
+ if (SSL_get_error(ssl,ret) == SSL_ERROR_WANT_READ)
+ {
+ /* handshaking isn't complete. */
+ return 0;
+ }
+
+ qse_fprintf (QSE_STDERR, QSE_T("Error: SSL ACCEPT ERROR\n"));
+ /* SSL_free (ssl); */
+ return -1;
+ }
+#else
+ return -1;
+#endif
+ }
+
+ return 1; /* accept completed */
+}
+
+static void client_closed (qse_httpd_t* httpd, qse_httpd_client_t* client)
+{
+ if (client->secure)
+ {
+#if defined(HAVE_SSL)
+ if (client->handle2.ptr)
+ {
+ SSL_shutdown ((SSL*)client->handle2.ptr); /* is this needed? */
+ SSL_free ((SSL*)client->handle2.ptr);
+ }
+#endif
+ }
+}
+
+/* ------------------------------------------------------------------- */
+static qse_htb_walk_t walk (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx)
+{
+ qse_htre_hdrval_t* val;
+
+ val = QSE_HTB_VPTR(pair);
+ while (val)
+ {
+qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)val->len, val->ptr);
+ val = val->next;
+ }
+ return QSE_HTB_WALK_FORWARD;
+}
+
+static int process_request (
+ qse_httpd_t* httpd, qse_httpd_client_t* client,
+ qse_htre_t* req, int peek)
+{
+ int method;
+ qse_httpd_task_t* task;
+ int content_received;
+
+ method = qse_htre_getqmethodtype(req);
+ content_received = (qse_htre_getcontentlen(req) > 0);
+
+ /* percent-decode the query path to the original buffer
+ * since i'm not gonna need it in the original form
+ * any more */
+ qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req));
+
+qse_printf (QSE_T("================================\n"));
+qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
+ (unsigned long)time(NULL),
+ (peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
+ qse_htre_getqpath(req),
+ qse_htre_getmajorversion(req),
+ qse_htre_getminorversion(req),
+ qse_htre_getverstr(req),
+ qse_htre_getqmethodname(req)
+);
+if (qse_htre_getqparam(req))
+ qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparam(req));
+
+qse_htb_walk (&req->hdrtab, walk, QSE_NULL);
+if (qse_htre_getcontentlen(req) > 0)
+{
+ qse_printf (QSE_T("CONTENT before discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
+}
+
+ if (peek)
+ {
+ if (method != QSE_HTTP_POST && method != QSE_HTTP_PUT)
+ {
+ /* i'll discard request contents if the method is none of
+ * post and put */
+ qse_httpd_discardcontent (httpd, req);
+ }
+
+ if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) &&
+ (req->version.major > 1 ||
+ (req->version.major == 1 && req->version.minor >= 1)) &&
+ !content_received)
+ {
+/* TODO: check method.... */
+ /* "expect" in the header, version 1.1 or higher,
+ * and no content received yet */
+
+ /* TODO: determine if to return 100-continue or other errors */
+{
+qse_ntime_t now;
+qse_gettime (&now);
+qse_printf (QSE_T("entasking continue at %lld\n"), (long long)now);
+}
+ if (qse_httpd_entaskcontinue (
+ httpd, client, QSE_NULL, req) == QSE_NULL) return -1;
+ }
+ }
+
+if (qse_htre_getcontentlen(req) > 0)
+{
+ qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
+}
+
+ if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
+ {
+ const qse_mchar_t* qpath = qse_htre_getqpath(req);
+ const qse_mchar_t* dot = qse_mbsrchr (qpath, QSE_MT('.'));
+
+ if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
+ {
+ if (peek)
+ {
+ /* cgi */
+#if 0
+ if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
+ {
+qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
+ #if 0
+ req->attr.keepalive = 0;
+ task = qse_httpd_entaskerror (
+ httpd, client, QSE_NULL, 411, req);
+ /* 411 can't keep alive */
+ if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
+ #endif
+ }
+ else
+#endif
+
+ /*if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))*/
+ if (method == QSE_HTTP_POST &&
+ !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
+ !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
+ {
+ req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
+ task = qse_httpd_entaskerror (
+ httpd, client, QSE_NULL, 411, req);
+ /* 411 can't keep alive */
+ if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
+ }
+ else
+ {
+ task = qse_httpd_entaskcgi (
+ httpd, client, QSE_NULL, qpath, req);
+ if (task == QSE_NULL) goto oops;
+ }
+ }
+#if 0
+ else
+ {
+ /* to support the chunked request,
+ * i need to wait until it's completed and invoke cgi */
+ if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
+ {
+qse_printf (QSE_T("Entasking chunked CGI...\n"));
+ task = qse_httpd_entaskcgi (
+ httpd, client, QSE_NULL, qpath, req);
+ if (task == QSE_NULL) goto oops;
+ }
+ }
+#endif
+ return 0;
+ }
+ else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
+ {
+ if (peek)
+ {
+ const qse_htre_hdrval_t* auth;
+ int authorized = 0;
+
+ auth = qse_htre_getheaderval (req, QSE_MT("Authorization"));
+ if (auth)
+ {
+ /* TODO: PERFORM authorization... */
+ /* BASE64 decode... */
+ while (auth->next) auth = auth->next;
+ authorized = 1;
+ }
+
+ if (authorized)
+ {
+ /* nph-cgi */
+ task = qse_httpd_entasknph (
+ httpd, client, QSE_NULL, qpath, req);
+ }
+ else
+ {
+ task = qse_httpd_entaskauth (
+ httpd, client, QSE_NULL, QSE_MT("Secure Area"), req);
+ }
+ if (task == QSE_NULL) goto oops;
+ }
+ return 0;
+ }
+ else
+ {
+#if 0
+ if (!peek)
+ {
+ /* file or directory */
+ task = qse_httpd_entaskfile (
+ httpd, client, QSE_NULL, qpath, req);
+ if (task == QSE_NULL) goto oops;
+ }
+#else
+ if (peek)
+ {
+ qse_httpd_discardcontent (httpd, req);
+ task = qse_httpd_entaskfile (
+ httpd, client, QSE_NULL, qpath, req);
+ if (task == QSE_NULL) goto oops;
+ }
+#endif
+ }
+ }
+ else
+ {
+ if (!peek)
+ {
+ task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
+ if (task == QSE_NULL) goto oops;
+ }
+ }
+
+ if (!(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
+ {
+ if (!peek)
+ {
+ task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
+ if (task == QSE_NULL) goto oops;
+ }
+ }
+
+ return 0;
+
+oops:
+ /*qse_httpd_markbadclient (httpd, client);*/
+ return -1;
+}
+
+static int proxy_request (
+ qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek)
+{
+ qse_httpd_task_t* task;
+
+#if 0
+ const qse_mchar_t* qpath;
+
+ qpath = qse_htre_qpathptr (eq);
+ if (qpath[0] == QSE_MT('/'))
+ {
+ host = qse_htre_getheaderval (req, QSE_MT("Host"));
+ if (host == QSE_NULL)
+ {
+qse_printf (QSE_T("Host not included....\n"));
+ goto oops;
+ }
+ }
+ else
+ {
+ const qse_mchar_t* host;
+ qse_parseuri ();
+ }
+#endif
+
+
+#if 0
+ if (peek)
+ {
+ if (req->attr.expect &&
+ (req->version.major > 1 ||
+ (req->version.major == 1 && req->version.minor >= 1)) &&
+ !content_received)
+ {
+/* TODO: check method.... */
+ /* "expect" in the header, version 1.1 or higher,
+ * and no content received yet */
+
+ if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0)
+ {
+ if (qse_httpd_entaskerror (
+ httpd, client, QSE_NULL, 417, req) == QSE_NULL) return -1;
+ if (qse_httpd_entaskdisconnect (
+ httpd, client, QSE_NULL) == QSE_NULL) return -1;
+ }
+ else
+ {
+ /* TODO: determine if to return 100-continue or other errors */
+ if (qse_httpd_entaskcontinue (
+ httpd, client, QSE_NULL, req) == QSE_NULL) return -1;
+ }
+ }
+ }
+#endif
+
+ if (peek)
+ {
+ qse_nwad_t nwad;
+
+#if 0
+ if (qse_nwadequal (&client->local_addr, &client->orgdst_addr))
+ {
+ //qse_strtonwad (QSE_T("192.168.1.55:9000"), &nwad);
+ //qse_strtonwad (QSE_T("1.234.53.142:80"), &nwad);
+ }
+ else
+ {
+#endif
+ nwad = client->orgdst_addr;
+#if 0
+ }
+#endif
+ task = qse_httpd_entaskproxy (httpd, client, QSE_NULL, &nwad, req);
+ if (task == QSE_NULL) goto oops;
+ }
+
+ if (!(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
+ {
+ if (!peek)
+ {
+ task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
+ if (task == QSE_NULL) goto oops;
+ }
+ }
+
+ return 0;
+
+oops:
+ return -1;
+}
+
+static int peek_request (
+ qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
+{
+ if (memcmp (&client->local_addr, &client->orgdst_addr, sizeof(client->orgdst_addr)) == 0)
+ {
+ return process_request (httpd, client, req, 1);
+ }
+ else
+ {
+ return proxy_request (httpd, client, req, 1);
+ }
+}
+
+static int handle_request (
+ qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
+{
+ if (memcmp (&client->local_addr, &client->orgdst_addr, sizeof(client->orgdst_addr)) == 0)
+ {
+ return process_request (httpd, client, req, 0);
+ }
+ else
+ {
+ return proxy_request (httpd, client, req, 0);
+ }
+}
+
+int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path)
+{
+ return 404;
+}
+
+static qse_httpd_cbs_t httpd_standard_callbacks =
+{
+ /* server */
+ { server_open, server_close, server_accept },
+
+ { peer_open,
+ peer_close,
+ peer_connected,
+ peer_recv,
+ peer_send },
+
+ /* multiplexer */
+ { mux_open,
+ mux_close,
+ mux_addhnd,
+ mux_delhnd,
+ mux_poll,
+
+ mux_readable,
+ mux_writable
+ },
+
+ /* file operation */
+ { file_executable,
+ file_stat,
+ file_ropen,
+ file_wopen,
+ file_close,
+ file_read,
+ file_write
+ },
+
+ /* client connection */
+ { client_close,
+ client_shutdown,
+ client_recv,
+ client_send,
+ client_sendfile,
+ client_accepted,
+ client_closed },
+
+ /* http request */
+ peek_request,
+ handle_request,
+
+ list_directory
+};
+
+int qse_httpd_loopstd (qse_httpd_t* httpd, qse_ntime_t timeout)
+{
+ return qse_httpd_loop (httpd, &httpd_standard_callbacks, timeout);
+}
diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c
index c3312fff..682df6ae 100644
--- a/qse/lib/net/httpd.c
+++ b/qse/lib/net/httpd.c
@@ -84,6 +84,11 @@ qse_httpd_t* qse_httpd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
void qse_httpd_close (qse_httpd_t* httpd)
{
+ qse_httpd_ecb_t* ecb;
+
+ for (ecb = httpd->ecb; ecb; ecb = ecb->next)
+ if (ecb->close) ecb->close (httpd);
+
qse_httpd_fini (httpd);
QSE_MMGR_FREE (httpd->mmgr, httpd);
}
@@ -128,6 +133,23 @@ void qse_httpd_setoption (qse_httpd_t* httpd, int option)
httpd->option = option;
}
+/* --------------------------------------------------- */
+
+qse_httpd_ecb_t* qse_httpd_popecb (qse_httpd_t* httpd)
+{
+ qse_httpd_ecb_t* top = httpd->ecb;
+ if (top) httpd->ecb = top->next;
+ return top;
+}
+
+void qse_httpd_pushecb (qse_httpd_t* httpd, qse_httpd_ecb_t* ecb)
+{
+ ecb->next = httpd->ecb;
+ httpd->ecb = ecb;
+}
+
+/* --------------------------------------------------- */
+
QSE_INLINE void* qse_httpd_allocmem (qse_httpd_t* httpd, qse_size_t size)
{
void* ptr = QSE_MMGR_ALLOC (httpd->mmgr, size);
diff --git a/qse/lib/net/httpd.h b/qse/lib/net/httpd.h
index 9f516769..a8264f2c 100644
--- a/qse/lib/net/httpd.h
+++ b/qse/lib/net/httpd.h
@@ -29,7 +29,8 @@ struct qse_httpd_t
{
QSE_DEFINE_COMMON_FIELDS (httpd)
qse_httpd_errnum_t errnum;
- qse_httpd_cbs_t* cbs;
+ qse_httpd_ecb_t* ecb; /* event callbacks */
+ qse_httpd_cbs_t* cbs;
int option;
int stopreq;
diff --git a/qse/lib/sed/sed.c b/qse/lib/sed/sed.c
index e75566fa..5781049c 100644
--- a/qse/lib/sed/sed.c
+++ b/qse/lib/sed/sed.c
@@ -75,6 +75,11 @@ qse_sed_t* qse_sed_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
void qse_sed_close (qse_sed_t* sed)
{
+ qse_sed_ecb_t* ecb;
+
+ for (ecb = sed->ecb; ecb; ecb = ecb->next)
+ if (ecb->close) ecb->close (sed);
+
qse_sed_fini (sed);
QSE_MMGR_FREE (sed->mmgr, sed);
}
@@ -4142,6 +4147,19 @@ void qse_sed_setlinenum (qse_sed_t* sed, qse_size_t num)
sed->e.in.num = num;
}
+qse_sed_ecb_t* qse_sed_popecb (qse_sed_t* sed)
+{
+ qse_sed_ecb_t* top = sed->ecb;
+ if (top) sed->ecb = top->next;
+ return top;
+}
+
+void qse_sed_pushecb (qse_sed_t* sed, qse_sed_ecb_t* ecb)
+{
+ ecb->next = sed->ecb;
+ sed->ecb = ecb;
+}
+
#ifdef QSE_ENABLE_SEDTRACER
qse_sed_exec_tracer_t qse_sed_getexectracer (qse_sed_t* sed)
{
diff --git a/qse/lib/sed/sed.h b/qse/lib/sed/sed.h
index 3eb7e31d..8012f1d3 100644
--- a/qse/lib/sed/sed.h
+++ b/qse/lib/sed/sed.h
@@ -99,6 +99,8 @@ struct qse_sed_t
} rex;
} depth;
+ qse_sed_ecb_t* ecb;
+
/** source text pointers */
struct
{