diff --git a/mio/lib/Makefile.am b/mio/lib/Makefile.am index 4339b70..80c6eb6 100644 --- a/mio/lib/Makefile.am +++ b/mio/lib/Makefile.am @@ -56,7 +56,9 @@ libmio_la_SOURCES = \ htrd.c \ htre.c \ http.c \ + http-cgi.c \ http-svr.c \ + http-thr.c \ mio-prv.h \ mio.c \ nwif.c \ diff --git a/mio/lib/Makefile.in b/mio/lib/Makefile.in index 5a7edcc..af12a21 100644 --- a/mio/lib/Makefile.in +++ b/mio/lib/Makefile.in @@ -141,13 +141,14 @@ libmio_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ am_libmio_la_OBJECTS = libmio_la-chr.lo libmio_la-dns.lo \ libmio_la-dns-cli.lo libmio_la-ecs.lo libmio_la-err.lo \ libmio_la-fmt.lo libmio_la-htb.lo libmio_la-htrd.lo \ - libmio_la-htre.lo libmio_la-http.lo libmio_la-http-svr.lo \ - libmio_la-mio.lo libmio_la-nwif.lo libmio_la-path.lo \ - libmio_la-pipe.lo libmio_la-pro.lo libmio_la-sck.lo \ - libmio_la-skad.lo libmio_la-sys.lo libmio_la-sys-ass.lo \ - libmio_la-sys-err.lo libmio_la-sys-log.lo libmio_la-sys-mux.lo \ - libmio_la-sys-tim.lo libmio_la-thr.lo libmio_la-tmr.lo \ - libmio_la-utf8.lo libmio_la-utl.lo + libmio_la-htre.lo libmio_la-http.lo libmio_la-http-cgi.lo \ + libmio_la-http-svr.lo libmio_la-http-thr.lo libmio_la-mio.lo \ + libmio_la-nwif.lo libmio_la-path.lo libmio_la-pipe.lo \ + libmio_la-pro.lo libmio_la-sck.lo libmio_la-skad.lo \ + libmio_la-sys.lo libmio_la-sys-ass.lo libmio_la-sys-err.lo \ + libmio_la-sys-log.lo libmio_la-sys-mux.lo libmio_la-sys-tim.lo \ + libmio_la-thr.lo libmio_la-tmr.lo libmio_la-utf8.lo \ + libmio_la-utl.lo libmio_la_OBJECTS = $(am_libmio_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -177,7 +178,9 @@ am__depfiles_remade = ./$(DEPDIR)/libmio_la-chr.Plo \ ./$(DEPDIR)/libmio_la-err.Plo ./$(DEPDIR)/libmio_la-fmt.Plo \ ./$(DEPDIR)/libmio_la-htb.Plo ./$(DEPDIR)/libmio_la-htrd.Plo \ ./$(DEPDIR)/libmio_la-htre.Plo \ + ./$(DEPDIR)/libmio_la-http-cgi.Plo \ ./$(DEPDIR)/libmio_la-http-svr.Plo \ + ./$(DEPDIR)/libmio_la-http-thr.Plo \ ./$(DEPDIR)/libmio_la-http.Plo ./$(DEPDIR)/libmio_la-mio.Plo \ ./$(DEPDIR)/libmio_la-nwif.Plo ./$(DEPDIR)/libmio_la-path.Plo \ ./$(DEPDIR)/libmio_la-pipe.Plo ./$(DEPDIR)/libmio_la-pro.Plo \ @@ -379,6 +382,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -442,7 +446,9 @@ libmio_la_SOURCES = \ htrd.c \ htre.c \ http.c \ + http-cgi.c \ http-svr.c \ + http-thr.c \ mio-prv.h \ mio.c \ nwif.c \ @@ -571,7 +577,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htb.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htrd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htre.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-http-cgi.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-http-svr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-http-thr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-http.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-mio.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-nwif.Plo@am__quote@ # am--include-marker @@ -691,6 +699,13 @@ libmio_la-http.lo: http.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c +libmio_la-http-cgi.lo: http-cgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-http-cgi.lo -MD -MP -MF $(DEPDIR)/libmio_la-http-cgi.Tpo -c -o libmio_la-http-cgi.lo `test -f 'http-cgi.c' || echo '$(srcdir)/'`http-cgi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-http-cgi.Tpo $(DEPDIR)/libmio_la-http-cgi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-cgi.c' object='libmio_la-http-cgi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-http-cgi.lo `test -f 'http-cgi.c' || echo '$(srcdir)/'`http-cgi.c + libmio_la-http-svr.lo: http-svr.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-http-svr.lo -MD -MP -MF $(DEPDIR)/libmio_la-http-svr.Tpo -c -o libmio_la-http-svr.lo `test -f 'http-svr.c' || echo '$(srcdir)/'`http-svr.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-http-svr.Tpo $(DEPDIR)/libmio_la-http-svr.Plo @@ -698,6 +713,13 @@ libmio_la-http-svr.lo: http-svr.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-http-svr.lo `test -f 'http-svr.c' || echo '$(srcdir)/'`http-svr.c +libmio_la-http-thr.lo: http-thr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-http-thr.lo -MD -MP -MF $(DEPDIR)/libmio_la-http-thr.Tpo -c -o libmio_la-http-thr.lo `test -f 'http-thr.c' || echo '$(srcdir)/'`http-thr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-http-thr.Tpo $(DEPDIR)/libmio_la-http-thr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-thr.c' object='libmio_la-http-thr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-http-thr.lo `test -f 'http-thr.c' || echo '$(srcdir)/'`http-thr.c + libmio_la-mio.lo: mio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-mio.lo -MD -MP -MF $(DEPDIR)/libmio_la-mio.Tpo -c -o libmio_la-mio.lo `test -f 'mio.c' || echo '$(srcdir)/'`mio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-mio.Tpo $(DEPDIR)/libmio_la-mio.Plo @@ -981,7 +1003,9 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libmio_la-htb.Plo -rm -f ./$(DEPDIR)/libmio_la-htrd.Plo -rm -f ./$(DEPDIR)/libmio_la-htre.Plo + -rm -f ./$(DEPDIR)/libmio_la-http-cgi.Plo -rm -f ./$(DEPDIR)/libmio_la-http-svr.Plo + -rm -f ./$(DEPDIR)/libmio_la-http-thr.Plo -rm -f ./$(DEPDIR)/libmio_la-http.Plo -rm -f ./$(DEPDIR)/libmio_la-mio.Plo -rm -f ./$(DEPDIR)/libmio_la-nwif.Plo @@ -1055,7 +1079,9 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libmio_la-htb.Plo -rm -f ./$(DEPDIR)/libmio_la-htrd.Plo -rm -f ./$(DEPDIR)/libmio_la-htre.Plo + -rm -f ./$(DEPDIR)/libmio_la-http-cgi.Plo -rm -f ./$(DEPDIR)/libmio_la-http-svr.Plo + -rm -f ./$(DEPDIR)/libmio_la-http-thr.Plo -rm -f ./$(DEPDIR)/libmio_la-http.Plo -rm -f ./$(DEPDIR)/libmio_la-mio.Plo -rm -f ./$(DEPDIR)/libmio_la-nwif.Plo diff --git a/mio/lib/chr.c b/mio/lib/chr.c index 8007615..8cdcccd 100644 --- a/mio/lib/chr.c +++ b/mio/lib/chr.c @@ -24,8 +24,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-chr.h" -#include "mio-utl.h" +#include +#include /* ---------------------------------------------------------- */ #include "uch-prop.h" diff --git a/mio/lib/dns.c b/mio/lib/dns.c index 2abd6d9..b20df0a 100644 --- a/mio/lib/dns.c +++ b/mio/lib/dns.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-dns.h" +#include #include "mio-prv.h" /* ----------------------------------------------------------------------- */ diff --git a/mio/lib/ecs.c b/mio/lib/ecs.c index e841176..4823629 100644 --- a/mio/lib/ecs.c +++ b/mio/lib/ecs.c @@ -24,8 +24,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-ecs.h" -#include "mio-fmt.h" +#include +#include #include "mio-prv.h" #define _FN(type,verb) mio_ ## type ## _ ## verb diff --git a/mio/lib/err.c b/mio/lib/err.c index 5587f51..35e00bd 100644 --- a/mio/lib/err.c +++ b/mio/lib/err.c @@ -25,7 +25,7 @@ */ #include "mio-prv.h" -#include "mio-fmt.h" +#include static mio_ooch_t errstr_0[] = {'n', 'o', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; static mio_ooch_t errstr_1[] = {'g', 'e', 'n', 'e', 'r', 'i', 'c', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; @@ -45,20 +45,21 @@ static mio_ooch_t errstr_14[] = {'i', 'n', 't', 'e', 'r', 'r', 'u', 'p', 't', 'e static mio_ooch_t errstr_15[] = {'p', 'i', 'p', 'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; static mio_ooch_t errstr_16[] = {'r', 'e', 's', 'o', 'u', 'r', 'c', 'e', ' ', 't', 'e', 'm', 'p', 'o', 'r', 'a', 'r', 'i', 'l', 'y', ' ', 'u', 'n', 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\0' }; static mio_ooch_t errstr_17[] = {'b', 'a', 'd', ' ', 's', 'y', 's', 't', 'e', 'm', ' ', 'h', 'a', 'n', 'd', 'l', 'e', '\0' }; -static mio_ooch_t errstr_18[] = {'t', 'o', 'o', ' ', 'm', 'a', 'n', 'y', ' ', 'o', 'p', 'e', 'n', ' ', 'f', 'i', 'l', 'e', 's', '\0' }; +static mio_ooch_t errstr_18[] = {'b', 'a', 'd', ' ', 'r', 'e', 'q', 'u', 'e', 's', 't', ' ', 'o', 'r', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '\0' }; static mio_ooch_t errstr_19[] = {'t', 'o', 'o', ' ', 'm', 'a', 'n', 'y', ' ', 'o', 'p', 'e', 'n', ' ', 'f', 'i', 'l', 'e', 's', '\0' }; -static mio_ooch_t errstr_20[] = {'I', '/', 'O', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; -static mio_ooch_t errstr_21[] = {'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ' ', 'c', 'o', 'n', 'v', 'e', 'r', 's', 'i', 'o', 'n', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; -static mio_ooch_t errstr_22[] = {'i', 'n', 's', 'u', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't', ' ', 'd', 'a', 't', 'a', ' ', 'f', 'o', 'r', ' ', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ' ', 'c', 'o', 'n', 'v', 'e', 'r', 's', 'i', 'o', 'n', '\0' }; -static mio_ooch_t errstr_23[] = {'b', 'u', 'f', 'f', 'e', 'r', ' ', 'f', 'u', 'l', 'l', '\0' }; -static mio_ooch_t errstr_24[] = {'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'r', 'e', 'f', 'u', 's', 'e', 'd', '\0' }; -static mio_ooch_t errstr_25[] = {'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'r', 'e', 's', 'e', 't', '\0' }; -static mio_ooch_t errstr_26[] = {'n', 'o', ' ', 'c', 'a', 'p', 'a', 'b', 'i', 'l', 'i', 't', 'y', '\0' }; -static mio_ooch_t errstr_27[] = {'t', 'i', 'm', 'e', 'd', ' ', 'o', 'u', 't', '\0' }; -static mio_ooch_t errstr_28[] = {'n', 'o', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '\0' }; -static mio_ooch_t errstr_29[] = {'u', 'n', 'a', 'b', 'l', 'e', ' ', 't', 'o', ' ', 'm', 'a', 'k', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', '\0' }; -static mio_ooch_t errstr_30[] = {'d', 'e', 'v', 'i', 'c', 'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; -static mio_ooch_t errstr_31[] = {'d', 'e', 'v', 'i', 'c', 'e', ' ', 'h', 'a', 'n', 'g', '-', 'u', 'p', '\0' }; +static mio_ooch_t errstr_20[] = {'t', 'o', 'o', ' ', 'm', 'a', 'n', 'y', ' ', 'o', 'p', 'e', 'n', ' ', 'f', 'i', 'l', 'e', 's', '\0' }; +static mio_ooch_t errstr_21[] = {'I', '/', 'O', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; +static mio_ooch_t errstr_22[] = {'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ' ', 'c', 'o', 'n', 'v', 'e', 'r', 's', 'i', 'o', 'n', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; +static mio_ooch_t errstr_23[] = {'i', 'n', 's', 'u', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't', ' ', 'd', 'a', 't', 'a', ' ', 'f', 'o', 'r', ' ', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', ' ', 'c', 'o', 'n', 'v', 'e', 'r', 's', 'i', 'o', 'n', '\0' }; +static mio_ooch_t errstr_24[] = {'b', 'u', 'f', 'f', 'e', 'r', ' ', 'f', 'u', 'l', 'l', '\0' }; +static mio_ooch_t errstr_25[] = {'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'r', 'e', 'f', 'u', 's', 'e', 'd', '\0' }; +static mio_ooch_t errstr_26[] = {'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'r', 'e', 's', 'e', 't', '\0' }; +static mio_ooch_t errstr_27[] = {'n', 'o', ' ', 'c', 'a', 'p', 'a', 'b', 'i', 'l', 'i', 't', 'y', '\0' }; +static mio_ooch_t errstr_28[] = {'t', 'i', 'm', 'e', 'd', ' ', 'o', 'u', 't', '\0' }; +static mio_ooch_t errstr_29[] = {'n', 'o', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', '\0' }; +static mio_ooch_t errstr_30[] = {'u', 'n', 'a', 'b', 'l', 'e', ' ', 't', 'o', ' ', 'm', 'a', 'k', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', '\0' }; +static mio_ooch_t errstr_31[] = {'d', 'e', 'v', 'i', 'c', 'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0' }; +static mio_ooch_t errstr_32[] = {'d', 'e', 'v', 'i', 'c', 'e', ' ', 'h', 'a', 'n', 'g', '-', 'u', 'p', '\0' }; static mio_ooch_t* errstr[] = { errstr_0, errstr_1, errstr_2, errstr_3, errstr_4, @@ -67,9 +68,10 @@ static mio_ooch_t* errstr[] = errstr_15, errstr_16, errstr_17, errstr_18, errstr_19, errstr_20, errstr_21, errstr_22, errstr_23, errstr_24, errstr_25, errstr_26, errstr_27, errstr_28, errstr_29, - errstr_30, errstr_31 + errstr_30, errstr_31, errstr_32 }; + /* -------------------------------------------------------------------------- * ERROR NUMBER TO STRING CONVERSION * -------------------------------------------------------------------------- */ diff --git a/mio/lib/fmt.c b/mio/lib/fmt.c index 7c3dc79..0ca04f0 100644 --- a/mio/lib/fmt.c +++ b/mio/lib/fmt.c @@ -64,7 +64,7 @@ * */ -#include "mio-fmt.h" +#include #include "mio-prv.h" diff --git a/mio/lib/generr.awk b/mio/lib/generr.hawk similarity index 100% rename from mio/lib/generr.awk rename to mio/lib/generr.hawk diff --git a/mio/lib/htrd.c b/mio/lib/htrd.c index d1d7ed4..fc6fa28 100644 --- a/mio/lib/htrd.c +++ b/mio/lib/htrd.c @@ -22,9 +22,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-htrd.h" -#include "mio-chr.h" -#include "mio-path.h" +#include +#include +#include #include "mio-prv.h" static const mio_bch_t NUL = '\0'; diff --git a/mio/lib/htre.c b/mio/lib/htre.c index 8b352dc..fd0d6a1 100644 --- a/mio/lib/htre.c +++ b/mio/lib/htre.c @@ -25,6 +25,7 @@ */ #include +#include #include "mio-prv.h" static void free_hdrval (mio_htb_t* htb, void* vptr, mio_oow_t vlen) diff --git a/mio/lib/http-cgi.c b/mio/lib/http-cgi.c new file mode 100644 index 0000000..1df474a --- /dev/null +++ b/mio/lib/http-cgi.c @@ -0,0 +1,1088 @@ +/* + * $Id$ + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "http-prv.h" +#include +#include +#include + +#include /* TODO: move file operations to sys-file.XXX */ +#include +#include +#include /* setenv, clearenv */ + +#define CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH + +enum cgi_state_res_mode_t +{ + CGI_STATE_RES_MODE_CHUNKED, + CGI_STATE_RES_MODE_CLOSE, + CGI_STATE_RES_MODE_LENGTH +}; +typedef enum cgi_state_res_mode_t cgi_state_res_mode_t; + +#define CGI_STATE_PENDING_IO_THRESHOLD 5 + +#define CGI_STATE_OVER_READ_FROM_CLIENT (1 << 0) +#define CGI_STATE_OVER_READ_FROM_PEER (1 << 1) +#define CGI_STATE_OVER_WRITE_TO_CLIENT (1 << 2) +#define CGI_STATE_OVER_WRITE_TO_PEER (1 << 3) +#define CGI_STATE_OVER_ALL (CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_READ_FROM_PEER | CGI_STATE_OVER_WRITE_TO_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER) + +struct cgi_state_t +{ + MIO_SVC_HTTS_RSRC_HEADER; + + mio_oow_t num_pending_writes_to_client; + mio_oow_t num_pending_writes_to_peer; + mio_dev_pro_t* peer; + mio_htrd_t* peer_htrd; + mio_svc_htts_cli_t* client; + mio_http_version_t req_version; /* client request */ + + unsigned int over: 4; /* must be large enough to accomodate CGI_STATE_OVER_ALL */ + unsigned int keep_alive: 1; + unsigned int req_content_length_unlimited: 1; + unsigned int ever_attempted_to_write_to_client: 1; + unsigned int client_disconnected: 1; + mio_oow_t req_content_length; /* client request content length */ + cgi_state_res_mode_t res_mode_to_cli; + + mio_dev_sck_on_read_t client_org_on_read; + mio_dev_sck_on_write_t client_org_on_write; + mio_dev_sck_on_disconnect_t client_org_on_disconnect; + mio_htrd_recbs_t client_htrd_org_recbs; +}; +typedef struct cgi_state_t cgi_state_t; + +struct cgi_peer_xtn_t +{ + cgi_state_t* state; +}; +typedef struct cgi_peer_xtn_t cgi_peer_xtn_t; + +static void cgi_state_halt_participating_devices (cgi_state_t* cgi_state) +{ + MIO_ASSERT (cgi_state->client->htts->mio, cgi_state->client != MIO_NULL); + MIO_ASSERT (cgi_state->client->htts->mio, cgi_state->client->sck != MIO_NULL); + + MIO_DEBUG4 (cgi_state->client->htts->mio, "HTTS(%p) - Halting participating devices in cgi state %p(client=%p,peer=%p)\n", cgi_state->client->htts, cgi_state, cgi_state->client->sck, cgi_state->peer); + + + mio_dev_sck_halt (cgi_state->client->sck); + /* check for peer as it may not have been started */ + if (cgi_state->peer) mio_dev_pro_halt (cgi_state->peer); +} + +static int cgi_state_write_to_client (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) +{ + cgi_state->ever_attempted_to_write_to_client = 1; + + cgi_state->num_pending_writes_to_client++; + if (mio_dev_sck_write(cgi_state->client->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_client--; + return -1; + } + + if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; + } + return 0; +} + +static int cgi_state_writev_to_client (cgi_state_t* cgi_state, mio_iovec_t* iov, mio_iolen_t iovcnt) +{ + cgi_state->ever_attempted_to_write_to_client = 1; + + cgi_state->num_pending_writes_to_client++; + if (mio_dev_sck_writev(cgi_state->client->sck, iov, iovcnt, MIO_NULL, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_client--; + return -1; + } + + if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; + } + return 0; +} + +static int cgi_state_send_final_status_to_client (cgi_state_t* cgi_state, int status_code, int force_close) +{ + mio_svc_htts_cli_t* cli = cgi_state->client; + mio_bch_t dtbuf[64]; + + mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); + + if (!force_close) force_close = !cgi_state->keep_alive; + if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nContent-Length: 0\r\n\r\n", + cgi_state->req_version.major, cgi_state->req_version.minor, + status_code, mio_http_status_to_bcstr(status_code), + cli->htts->server_name, dtbuf, + (force_close? "close": "keep-alive")) == (mio_oow_t)-1) return -1; + + return (cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)) <= -1 || + (force_close && cgi_state_write_to_client(cgi_state, MIO_NULL, 0) <= -1))? -1: 0; +} + + +static int cgi_state_write_last_chunk_to_client (cgi_state_t* cgi_state) +{ + if (!cgi_state->ever_attempted_to_write_to_client) + { + if (cgi_state_send_final_status_to_client(cgi_state, 500, 0) <= -1) return -1; + } + else + { + if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && + cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1) return -1; + } + + if (!cgi_state->keep_alive && cgi_state_write_to_client(cgi_state, MIO_NULL, 0) <= -1) return -1; + return 0; +} + +static int cgi_state_write_to_peer (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) +{ + cgi_state->num_pending_writes_to_peer++; + if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_peer--; + return -1; + } + +/* TODO: check if it's already finished or something.. */ + if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_sck_read(cgi_state->client->sck, 0) <= -1) return -1; + } + return 0; +} + +static MIO_INLINE void cgi_state_mark_over (cgi_state_t* cgi_state, int over_bits) +{ + unsigned int old_over; + + old_over = cgi_state->over; + cgi_state->over |= over_bits; + + MIO_DEBUG5 (cgi_state->htts->mio, "HTTS(%p) - client=%p peer=%p new-bits=%x over=%x\n", cgi_state->htts, cgi_state->client->sck, cgi_state->peer, (int)over_bits, (int)cgi_state->over); + + if (!(old_over & CGI_STATE_OVER_READ_FROM_CLIENT) && (cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) + { + if (mio_dev_sck_read(cgi_state->client->sck, 0) <= -1) + { + MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to disable input watching\n", cgi_state->htts, cgi_state->client->sck); + mio_dev_sck_halt (cgi_state->client->sck); + } + } + + if (!(old_over & CGI_STATE_OVER_READ_FROM_PEER) && (cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) + { + if (cgi_state->peer && mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) + { + MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting peer(%p) for failure to disable input watching\n", cgi_state->htts, cgi_state->peer); + mio_dev_pro_halt (cgi_state->peer); + } + } + + if (old_over != CGI_STATE_OVER_ALL && cgi_state->over == CGI_STATE_OVER_ALL) + { + /* ready to stop */ + if (cgi_state->peer) + { + MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting peer(%p) as it is unneeded\n", cgi_state->htts, cgi_state->peer); + mio_dev_pro_halt (cgi_state->peer); + } + + if (cgi_state->keep_alive) + { + /* how to arrange to delete this cgi_state object and put the socket back to the normal waiting state??? */ + MIO_ASSERT (cgi_state->htts->mio, cgi_state->client->rsrc == (mio_svc_htts_rsrc_t*)cgi_state); + +printf ("DETACHING FROM THE MAIN CLIENT RSRC... state -> %p\n", cgi_state->client->rsrc); + MIO_SVC_HTTS_RSRC_DETACH (cgi_state->client->rsrc); + /* cgi_state must not be access from here down as it could have been destroyed */ + } + else + { + MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for no keep-alive\n", cgi_state->htts, cgi_state->client->sck); + mio_dev_sck_shutdown (cgi_state->client->sck, MIO_DEV_SCK_SHUTDOWN_WRITE); + mio_dev_sck_halt (cgi_state->client->sck); + } + } +} + +static void cgi_state_on_kill (cgi_state_t* cgi_state) +{ +printf ("**** CGI_STATE_ON_KILL \n"); + if (cgi_state->peer) + { + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(cgi_state->peer); + cgi_peer->state = MIO_NULL; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */ + + mio_dev_pro_kill (cgi_state->peer); + cgi_state->peer = MIO_NULL; + } + + if (cgi_state->peer_htrd) + { + cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); + cgi_peer->state = MIO_NULL; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */ + + mio_htrd_close (cgi_state->peer_htrd); + cgi_state->peer_htrd = MIO_NULL; + } + + if (cgi_state->client_org_on_read) + { + cgi_state->client->sck->on_read = cgi_state->client_org_on_read; + cgi_state->client_org_on_read = MIO_NULL; + } + + if (cgi_state->client_org_on_write) + { + cgi_state->client->sck->on_write = cgi_state->client_org_on_write; + cgi_state->client_org_on_write = MIO_NULL; + } + + + if (cgi_state->client_org_on_disconnect) + { + cgi_state->client->sck->on_disconnect = cgi_state->client_org_on_disconnect; + cgi_state->client_org_on_disconnect = MIO_NULL; + } + + mio_htrd_setrecbs (cgi_state->client->htrd, &cgi_state->client_htrd_org_recbs); /* restore the callbacks */ + + if (!cgi_state->client_disconnected) + { +printf ("ENABLING INPUT WATCHING on CLIENT %p. \n", cgi_state->client->sck); + if (!cgi_state->keep_alive || mio_dev_sck_read(cgi_state->client->sck, 1) <= -1) + { + MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to enable input watching\n", cgi_state->htts, cgi_state->client->sck); + mio_dev_sck_halt (cgi_state->client->sck); + } + } + +printf ("**** CGI_STATE_ON_KILL DONE\n"); +} + +static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) +{ + mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); + cgi_state_t* cgi_state = cgi_peer->state; + + if (!cgi_state) return; /* cgi state already gone */ + + switch (sid) + { + case MIO_DEV_PRO_MASTER: + MIO_DEBUG3 (mio, "HTTS(%p) - peer %p(pid=%d) closing master\n", cgi_state->client->htts, pro, (int)pro->child_pid); + cgi_state->peer = MIO_NULL; /* clear this peer from the state */ + + MIO_ASSERT (mio, cgi_peer->state != MIO_NULL); +printf ("DETACHING FROM CGI PEER DEVICE.....................%p %d\n", cgi_peer->state, (int)cgi_peer->state->rsrc_refcnt); + MIO_SVC_HTTS_RSRC_DETACH (cgi_peer->state); + + if (cgi_state->peer_htrd) + { + /* once this peer device is closed, peer's htrd is also never used. + * it's safe to detach the extra information attached on the htrd object. */ + cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); + MIO_ASSERT (mio, cgi_peer->state != MIO_NULL); +printf ("DETACHING FROM CGI PEER HTRD.....................%p %d\n", cgi_peer->state, (int)cgi_peer->state->rsrc_refcnt); + MIO_SVC_HTTS_RSRC_DETACH (cgi_peer->state); + } + + break; + + case MIO_DEV_PRO_OUT: + MIO_ASSERT (mio, cgi_state->peer == pro); + MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->client->htts, pro, (int)pro->child_pid, sid); + + if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) + { + if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) + cgi_state_halt_participating_devices (cgi_state); + else + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); + } + break; + + case MIO_DEV_PRO_IN: + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); + break; + + case MIO_DEV_PRO_ERR: + default: + MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->client->htts, pro, (int)pro->child_pid, sid); + /* do nothing */ + break; + } +} + +static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const void* data, mio_iolen_t dlen) +{ + mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); + cgi_state_t* cgi_state = cgi_peer->state; + + MIO_ASSERT (mio, sid == MIO_DEV_PRO_OUT); /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */ + MIO_ASSERT (mio, cgi_state != MIO_NULL); + + if (dlen <= -1) + { + MIO_DEBUG3 (mio, "HTTPS(%p) - read error from peer %p(pid=%u)\n", cgi_state->client->htts, pro, (unsigned int)pro->child_pid); + goto oops; + } + + if (dlen == 0) + { + MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from peer %p(pid=%u)\n", cgi_state->client->htts, pro, (unsigned int)pro->child_pid); + + if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) + { + /* the cgi script could be misbehaviing. + * it still has to read more but EOF is read. + * otherwise client_peer_htrd_poke() should have been called */ + if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) goto oops; + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); + } + } + else + { + mio_oow_t rem; + + MIO_ASSERT (mio, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)); + + if (mio_htrd_feed(cgi_state->peer_htrd, data, dlen, &rem) <= -1) + { + MIO_DEBUG3 (mio, "HTTPS(%p) - unable to feed peer into to htrd - peer %p(pid=%u)\n", cgi_state->htts, pro, (unsigned int)pro->child_pid); + + if (!cgi_state->ever_attempted_to_write_to_client && + !(cgi_state->over & CGI_STATE_OVER_WRITE_TO_CLIENT)) + { + cgi_state_send_final_status_to_client (cgi_state, 500, 1); /* don't care about error because it jumps to oops below anyway */ + } + + goto oops; + } + + if (rem > 0) + { + /* If the script specifies Content-Length and produces longer data, it will come here */ +printf ("AAAAAAAAAAAAAAAAAa EEEEEXcessive DATA..................\n"); +/* TODO: or drop this request?? */ + } + } + + return 0; + +oops: + cgi_state_halt_participating_devices (cgi_state); + return 0; +} + +static int cgi_peer_capture_response_header (mio_htre_t* req, const mio_bch_t* key, const mio_htre_hdrval_t* val, void* ctx) +{ + mio_svc_htts_cli_t* cli = (mio_svc_htts_cli_t*)ctx; + + /* capture a header except Status, Connection, Transfer-Encoding, and Server */ + if (mio_comp_bcstr(key, "Status", 1) != 0 && + mio_comp_bcstr(key, "Connection", 1) != 0 && + mio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && + mio_comp_bcstr(key, "Server", 1) != 0 && + mio_comp_bcstr(key, "Date", 1) != 0) + { + do + { + if (mio_becs_cat(cli->sbuf, key) == (mio_oow_t)-1 || + mio_becs_cat(cli->sbuf, ": ") == (mio_oow_t)-1 || + mio_becs_cat(cli->sbuf, val->ptr) == (mio_oow_t)-1 || + mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) + { + return -1; + } + + val = val->next; + } + while (val); + } + + return 0; +} + +static int cgi_peer_htrd_peek (mio_htrd_t* htrd, mio_htre_t* req) +{ + cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); + cgi_state_t* cgi_state = cgi_peer->state; + mio_svc_htts_cli_t* cli = cgi_state->client; + mio_bch_t dtbuf[64]; + int status_code; + + if (req->attr.content_length) + { +// TOOD: remove content_length if content_length is negative or not numeric. + cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_LENGTH; + } + + if (req->attr.status) + { + } + else + { + } + + status_code = 200; + +printf ("CGI PEER HTRD PEEK...\n"); + mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); + + if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %hs\r\n", + cgi_state->req_version.major, cgi_state->req_version.minor, + status_code, mio_http_status_to_bcstr(status_code), + cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1; + + if (mio_htre_walkheaders(req, cgi_peer_capture_response_header, cli) <= -1) return -1; + + switch (cgi_state->res_mode_to_cli) + { + case CGI_STATE_RES_MODE_CHUNKED: + if (mio_becs_cat(cli->sbuf, "Transfer-Encoding: chunked\r\n") == (mio_oow_t)-1) return -1; + /*if (mio_becs_cat(cli->sbuf, "Connection: keep-alive\r\n") == (mio_oow_t)-1) return -1;*/ + break; + + case CGI_STATE_RES_MODE_CLOSE: + if (mio_becs_cat(cli->sbuf, "Connection: close\r\n") == (mio_oow_t)-1) return -1; + break; + + case CGI_STATE_RES_MODE_LENGTH: + if (mio_becs_cat(cli->sbuf, (cgi_state->keep_alive? "Connection: keep-alive\r\n": "Connection: close\r\n")) == (mio_oow_t)-1) return -1; + } + + if (mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) return -1; + + return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)); +} + +static int cgi_peer_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) +{ + /* client request got completed */ + cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); + cgi_state_t* cgi_state = cgi_peer->state; + +printf (">> PEER RESPONSE COMPLETED\n"); + + if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) return -1; + + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); + return 0; +} + +static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) +{ + cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); + cgi_state_t* cgi_state = cgi_peer->state; + + MIO_ASSERT (cgi_state->client->htts->mio, htrd == cgi_state->peer_htrd); + + switch (cgi_state->res_mode_to_cli) + { + case CGI_STATE_RES_MODE_CHUNKED: + { + mio_iovec_t iov[3]; + mio_bch_t lbuf[16]; + mio_oow_t llen; + + /* mio_fmt_uintmax_to_bcstr() null-terminates the output. only MIO_COUNTOF(lbuf) - 1 + * is enough to hold '\r' and '\n' at the back without '\0'. */ + llen = mio_fmt_uintmax_to_bcstr(lbuf, MIO_COUNTOF(lbuf) - 1, dlen, 16 | MIO_FMT_UINTMAX_UPPERCASE, 0, '\0', MIO_NULL); + lbuf[llen++] = '\r'; + lbuf[llen++] = '\n'; + + iov[0].iov_ptr = lbuf; + iov[0].iov_len = llen; + iov[1].iov_ptr = (void*)data; + iov[1].iov_len = dlen; + iov[2].iov_ptr = "\r\n"; + iov[2].iov_len = 2; + + if (cgi_state_writev_to_client(cgi_state, iov, MIO_COUNTOF(iov)) <= -1) goto oops; + break; + } + + case CGI_STATE_RES_MODE_CLOSE: + case CGI_STATE_RES_MODE_LENGTH: + if (cgi_state_write_to_client(cgi_state, data, dlen) <= -1) goto oops; + break; + } + + if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) goto oops; + } + + return 0; + +oops: + return -1; +} + +static mio_htrd_recbs_t cgi_peer_htrd_recbs = +{ + cgi_peer_htrd_peek, + cgi_peer_htrd_poke, + cgi_peer_htrd_push_content +}; + +static int cgi_client_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) +{ + /* client request got completed */ + mio_svc_htts_cli_htrd_xtn_t* htrdxtn = (mio_svc_htts_cli_htrd_xtn_t*)mio_htrd_getxtn(htrd); + mio_dev_sck_t* sck = htrdxtn->sck; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; + +printf (">> CLIENT REQUEST COMPLETED\n"); + + /* indicate EOF to the client peer */ + if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) return -1; + + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT); + return 0; +} + +static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) +{ + mio_svc_htts_cli_htrd_xtn_t* htrdxtn = (mio_svc_htts_cli_htrd_xtn_t*)mio_htrd_getxtn(htrd); + mio_dev_sck_t* sck = htrdxtn->sck; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; + + MIO_ASSERT (sck->mio, cli->sck == sck); + return cgi_state_write_to_peer(cgi_state, data, dlen); +} + +static mio_htrd_recbs_t cgi_client_htrd_recbs = +{ + MIO_NULL, + cgi_client_htrd_poke, + cgi_client_htrd_push_content +}; + +static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx) +{ + mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); + cgi_state_t* cgi_state = cgi_peer->state; + + if (cgi_state == MIO_NULL) return 0; /* there is nothing i can do. the cgi_state is being cleared or has been cleared already. */ + + MIO_ASSERT (mio, cgi_state->peer == pro); + + if (wrlen <= -1) + { + MIO_DEBUG3 (mio, "HTTS(%p) - unable to write to peer %p(pid=%u)\n", cgi_state->client->htts, pro, (int)pro->child_pid); + goto oops; + } + else if (wrlen == 0) + { + /* indicated EOF */ + /* do nothing here as i didn't incremented num_pending_writes_to_peer when making the write request */ + + cgi_state->num_pending_writes_to_peer--; + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer == 0); + MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to peer %p(pid=%u)\n", cgi_state->client->htts, pro, (int)pro->child_pid); + /* indicated EOF to the peer side. i need no more data from the client side. + * i don't need to enable input watching in the client side either */ + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); + } + else + { + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer > 0); + + cgi_state->num_pending_writes_to_peer--; + if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD) + { + if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT) && + mio_dev_sck_read(cgi_state->client->sck, 1) <= -1) goto oops; + } + + if ((cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT) && cgi_state->num_pending_writes_to_peer <= 0) + { + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); + } + } + + return 0; + +oops: + cgi_state_halt_participating_devices (cgi_state); + return 0; +} + +static void cgi_client_on_disconnect (mio_dev_sck_t* sck) +{ + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; + cgi_state->client_disconnected = 1; + cgi_state->client_org_on_disconnect (sck); +} + +static int cgi_client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) +{ + mio_t* mio = sck->mio; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; + + MIO_ASSERT (mio, sck == cli->sck); + + if (len <= -1) + { + /* read error */ + MIO_DEBUG2 (cli->htts->mio, "HTTPS(%p) - read error on client %p(%d)\n", sck, (int)sck->hnd); + goto oops; + } + + if (!cgi_state->peer) + { + /* the peer is gone */ + goto oops; /* do what? just return 0? */ + } + + if (len == 0) + { + /* EOF on the client side. arrange to close */ + MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from client %p(hnd=%d)\n", cgi_state->client->htts, sck, (int)sck->hnd); + + if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without cgi_client_htrd_poke() */ + { + if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops; + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT); + } + } + else + { + mio_oow_t rem; + + MIO_ASSERT (mio, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)); + + if (mio_htrd_feed(cli->htrd, buf, len, &rem) <= -1) goto oops; + + if (rem > 0) + { + /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ +printf ("UUUUUUUUUUUUUUUUUUUUUUUUUUGGGGGHHHHHHHHHHHH .......... CGI CLIENT GIVING EXCESSIVE DATA AFTER CONTENTS...\n"); + } + } + + return 0; + +oops: + cgi_state_halt_participating_devices (cgi_state); + return 0; +} + +static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) +{ + mio_t* mio = sck->mio; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; + + if (wrlen <= -1) + { + MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client %p(%d)\n", sck->mio, sck, (int)sck->hnd); + goto oops; + } + + if (wrlen == 0) + { + /* if the connect is keep-alive, this part may not be called */ + cgi_state->num_pending_writes_to_client--; + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client == 0); + MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to client %p(%d)\n", cgi_state->client->htts, sck, (int)sck->hnd); + /* since EOF has been indicated to the client, it must not write to the client any further. + * this also means that i don't need any data from the peer side either. + * i don't need to enable input watching on the peer side */ + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_CLIENT); + } + else + { + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client > 0); + + cgi_state->num_pending_writes_to_client--; + if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD) + { + if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER) && + mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops; + } + + if ((cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER) && cgi_state->num_pending_writes_to_client <= 0) + { + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_CLIENT); + } + } + + return 0; + +oops: + cgi_state_halt_participating_devices (cgi_state); + return 0; +} + +static MIO_INLINE int get_request_content_length (mio_htre_t* req, mio_oow_t* len) +{ + if (req->flags & MIO_HTRE_ATTR_CHUNKED) + { + /* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX". + * + * [RFC7230] + * If a message is received with both a Transfer-Encoding and a + * Content-Length header field, the Transfer-Encoding overrides the + * Content-Length. */ + return 1; /* unable to determine content-length in advance. unlimited */ + } + + if (req->flags & MIO_HTRE_ATTR_LENGTH) + { + *len = req->attr.content_length; + } + else + { + /* If no Content-Length is specified in a request, it's Content-Length: 0 */ + *len = 0; + } + return 0; /* limited to the length set in *len */ +} + +struct cgi_peer_fork_ctx_t +{ + mio_svc_htts_cli_t* cli; + mio_htre_t* req; + const mio_bch_t* docroot; + const mio_bch_t* script; + mio_bch_t* actual_script; +}; +typedef struct cgi_peer_fork_ctx_t cgi_peer_fork_ctx_t; + +static int cgi_peer_capture_request_header (mio_htre_t* req, const mio_bch_t* key, const mio_htre_hdrval_t* val, void* ctx) +{ + mio_becs_t* dbuf = (mio_becs_t*)ctx; + + if (mio_comp_bcstr(key, "Connection", 1) != 0 && + mio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && + mio_comp_bcstr(key, "Content-Length", 1) != 0 && + mio_comp_bcstr(key, "Expect", 1) != 0) + { + mio_oow_t val_offset; + mio_bch_t* ptr; + + mio_becs_clear (dbuf); + if (mio_becs_cpy(dbuf, "HTTP_") == (mio_oow_t)-1 || + mio_becs_cat(dbuf, key) == (mio_oow_t)-1 || + mio_becs_ccat(dbuf, '\0') == (mio_oow_t)-1) return -1; + + for (ptr = MIO_BECS_PTR(dbuf); *ptr; ptr++) + { + *ptr = mio_to_bch_upper(*ptr); + if (*ptr =='-') *ptr = '_'; + } + + val_offset = MIO_BECS_LEN(dbuf); + if (mio_becs_cat(dbuf, val->ptr) == (mio_oow_t)-1) return -1; + val = val->next; + while (val) + { + if (mio_becs_cat(dbuf, ",") == (mio_oow_t)-1 || + mio_becs_cat (dbuf, val->ptr) == (mio_oow_t)-1) return -1; + val = val->next; + } + + setenv (MIO_BECS_PTR(dbuf), MIO_BECS_CPTR(dbuf, val_offset), 1); + } + + return 0; +} + +static int cgi_peer_on_fork (mio_dev_pro_t* pro, void* fork_ctx) +{ + mio_t* mio = pro->mio; /* in this callback, the pro device is not fully up. however, the mio field is guaranteed to be available */ + cgi_peer_fork_ctx_t* fc = (cgi_peer_fork_ctx_t*)fork_ctx; + mio_oow_t content_length; + const mio_bch_t* qparam; + mio_bch_t* path, * lang; + mio_bch_t tmp[256]; + mio_becs_t dbuf; + + qparam = mio_htre_getqparam(fc->req); + + path = mio_dupbcstr(mio, getenv("PATH"), MIO_NULL); + lang = mio_dupbcstr(mio, getenv("LANG"), MIO_NULL); +#if defined(HAVE_CLEARENV) + clearenv (); +#else + { + extern char** environ; + /* environ = NULL; this crashed this program on NetBSD */ + if (environ) environ[0] = '\0'; + } +#endif + if (path) + { + setenv ("PATH", path, 1); + mio_freemem (mio, path); + } + + if (lang) + { + setenv ("LANG", lang, 1); + mio_freemem (mio, lang); + } + + setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1); + + mio_fmttobcstr (mio, tmp, MIO_COUNTOF(tmp), "HTTP/%d.%d", (int)mio_htre_getmajorversion(fc->req), (int)mio_htre_getminorversion(fc->req)); + setenv ("SERVER_PROTOCOL", tmp, 1); + + setenv ("DOCUMENT_ROOT", fc->docroot, 1); + setenv ("SCRIPT_NAME", fc->script, 1); + setenv ("SCRIPT_FILENAME", fc->actual_script, 1); + /* TODO: PATH_INFO */ + + setenv ("REQUEST_METHOD", mio_htre_getqmethodname(fc->req), 1); + setenv ("REQUEST_URI", mio_htre_getqpath(fc->req), 1); + if (qparam) setenv ("QUERY_STRING", qparam, 1); + + if (get_request_content_length(fc->req, &content_length) == 0) + { + mio_fmt_uintmax_to_bcstr(tmp, MIO_COUNTOF(tmp), content_length, 10, 0, '\0', MIO_NULL); + setenv ("CONTENT_LENGTH", tmp, 1); + } + else + { + /* content length unknown, neither is it 0 - this is not standard */ + setenv ("CONTENT_LENGTH", "-1", 1); + } + setenv ("SERVER_SOFTWARE", fc->cli->htts->server_name, 1); + + mio_skadtobcstr (mio, &fc->cli->sck->localaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_ADDR); + setenv ("SERVER_ADDR", tmp, 1); + + gethostname (tmp, MIO_COUNTOF(tmp)); /* if this fails, i assume tmp contains the ip address set by mio_skadtobcstr() above */ + setenv ("SERVER_NAME", tmp, 1); + + mio_skadtobcstr (mio, &fc->cli->sck->localaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_PORT); + setenv ("SERVER_PORT", tmp, 1); + + mio_skadtobcstr (mio, &fc->cli->sck->remoteaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_ADDR); + setenv ("REMOTE_ADDR", tmp, 1); + + mio_skadtobcstr (mio, &fc->cli->sck->remoteaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_PORT); + setenv ("REMOTE_PORT", tmp, 1); + + if (mio_becs_init(&dbuf, mio, 256) >= 0) + { + mio_htre_walkheaders (fc->req, cgi_peer_capture_request_header, &dbuf); + /* [NOTE] trailers are not available when this cgi resource is started. let's not call mio_htre_walktrailers() */ + mio_becs_fini (&dbuf); + } + + return 0; +} + +int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot, const mio_bch_t* script) +{ + mio_t* mio = htts->mio; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); + cgi_state_t* cgi_state = MIO_NULL; + cgi_peer_xtn_t* cgi_peer; + mio_dev_pro_make_t mi; + cgi_peer_fork_ctx_t fc; + + /* ensure that you call this function before any contents is received */ + MIO_ASSERT (mio, mio_htre_getcontentlen(req) == 0); + + MIO_MEMSET (&fc, 0, MIO_SIZEOF(fc)); + fc.cli = cli; + fc.req = req; + fc.docroot = docroot; + fc.script = script; + fc.actual_script = mio_svc_htts_dupmergepaths(htts, docroot, script); + if (!fc.actual_script) goto oops; + + MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi)); + mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/; + mi.cmd = fc.actual_script; + mi.on_read = cgi_peer_on_read; + mi.on_write = cgi_peer_on_write; + mi.on_close = cgi_peer_on_close; + mi.on_fork = cgi_peer_on_fork; + mi.fork_ctx = &fc; + + cgi_state = (cgi_state_t*)mio_svc_htts_rsrc_make(htts, MIO_SIZEOF(*cgi_state), cgi_state_on_kill); + if (MIO_UNLIKELY(!cgi_state)) goto oops; + + cgi_state->client = cli; + /*cgi_state->num_pending_writes_to_client = 0; + cgi_state->num_pending_writes_to_peer = 0;*/ + cgi_state->req_version = *mio_htre_getversion(req); + cgi_state->req_content_length_unlimited = get_request_content_length(req, &cgi_state->req_content_length); + + cgi_state->client_org_on_read = csck->on_read; + cgi_state->client_org_on_write = csck->on_write; + cgi_state->client_org_on_disconnect = csck->on_disconnect; + csck->on_read = cgi_client_on_read; + csck->on_write = cgi_client_on_write; + csck->on_disconnect = cgi_client_on_disconnect; + + MIO_ASSERT (mio, cli->rsrc == MIO_NULL); + MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cli->rsrc); + + if (access(mi.cmd, X_OK) == -1) + { + cgi_state_send_final_status_to_client (cgi_state, 403, 1); /* 403 Forbidden */ + goto oops; /* TODO: must not go to oops. just destroy the cgi_state and finalize the request .. */ + } + + cgi_state->peer = mio_dev_pro_make(mio, MIO_SIZEOF(*cgi_peer), &mi); + if (MIO_UNLIKELY(!cgi_state->peer)) goto oops; + cgi_peer = mio_dev_pro_getxtn(cgi_state->peer); + MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cgi_peer->state); + + cgi_state->peer_htrd = mio_htrd_open(mio, MIO_SIZEOF(*cgi_peer)); + if (MIO_UNLIKELY(!cgi_state->peer_htrd)) goto oops; + mio_htrd_setopt (cgi_state->peer_htrd, MIO_HTRD_SKIPINITIALLINE | MIO_HTRD_RESPONSE); + mio_htrd_setrecbs (cgi_state->peer_htrd, &cgi_peer_htrd_recbs); + + cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); + MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cgi_peer->state); + +#if !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + if (cgi_state->req_content_length_unlimited) + { + /* Transfer-Encoding is chunked. no content-length is known in advance. */ + + /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. + * option 2. send 411 Length Required immediately + * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ + + if (cgi_state_send_final_status_to_client(cgi_state, 411, 1) <= -1) goto oops; + } +#endif + + if (req->flags & MIO_HTRE_ATTR_EXPECT100) + { + /* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ + /* CAN I LET the cgi SCRIPT handle this? */ + if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + (cgi_state->req_content_length_unlimited || cgi_state->req_content_length > 0)) + { + /* + * Don't send 100 Continue if http verions is lower than 1.1 + * [RFC7231] + * A server that receives a 100-continue expectation in an HTTP/1.0 + * request MUST ignore that expectation. + * + * Don't send 100 Continue if expected content lenth is 0. + * [RFC7231] + * A server MAY omit sending a 100 (Continue) response if it has + * already received some or all of the message body for the + * corresponding request, or if the framing indicates that there is + * no message body. + */ + mio_bch_t msgbuf[64]; + mio_oow_t msglen; + + msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi_state->req_version.major, cgi_state->req_version.minor); + if (cgi_state_write_to_client(cgi_state, msgbuf, msglen) <= -1) goto oops; + cgi_state->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ + } + } + else if (req->flags & MIO_HTRE_ATTR_EXPECT) + { + /* 417 Expectation Failed */ + cgi_state_send_final_status_to_client(cgi_state, 417, 1); + goto oops; + } + +#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + if (cgi_state->req_content_length_unlimited) + { + /* change the callbacks to subscribe to contents to be uploaded */ + cgi_state->client_htrd_org_recbs = *mio_htrd_getrecbs(cgi_state->client->htrd); + cgi_client_htrd_recbs.peek = cgi_state->client_htrd_org_recbs.peek; + mio_htrd_setrecbs (cgi_state->client->htrd, &cgi_client_htrd_recbs); + } + else + { +#endif + if (cgi_state->req_content_length > 0) + { + /* change the callbacks to subscribe to contents to be uploaded */ + cgi_state->client_htrd_org_recbs = *mio_htrd_getrecbs(cgi_state->client->htrd); + cgi_client_htrd_recbs.peek = cgi_state->client_htrd_org_recbs.peek; + mio_htrd_setrecbs (cgi_state->client->htrd, &cgi_client_htrd_recbs); + } + else + { + /* no content to be uploaded from the client */ + /* indicate EOF to the peer and disable input wathching from the client */ + if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops; + cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER); + } +#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + } +#endif + + /* this may change later if Content-Length is included in the cgi output */ + if (req->flags & MIO_HTRE_ATTR_KEEPALIVE) + { + cgi_state->keep_alive = 1; + cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_CHUNKED; + /* the mode still can get switched to CGI_STATE_RES_MODE_LENGTH if the cgi script emits Content-Length */ + } + else + { + cgi_state->keep_alive = 0; + cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_CLOSE; + } + + /* TODO: store current input watching state and use it when destroying the cgi_state data */ + if (mio_dev_sck_read(csck, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) <= -1) goto oops; + mio_freemem (mio, fc.actual_script); + return 0; + +oops: + MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck); + if (cgi_state) cgi_state_halt_participating_devices (cgi_state); + if (fc.actual_script) mio_freemem (mio, fc.actual_script); + return -1; +} diff --git a/mio/lib/http-prv.h b/mio/lib/http-prv.h new file mode 100644 index 0000000..7a2a7d2 --- /dev/null +++ b/mio/lib/http-prv.h @@ -0,0 +1,101 @@ +/* + * $Id$ + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MIO_HTTP_PRV_H_ +#define _MIO_HTTP_PRV_H_ + +#include +#include +#include +#include "mio-prv.h" + +typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t; +struct mio_svc_htts_cli_t +{ + mio_svc_htts_cli_t* cli_prev; + mio_svc_htts_cli_t* cli_next; + + /* a listener socket sets htts and sck fields only */ + /* a client sockets uses all the fields in this struct */ + mio_svc_htts_t* htts; + mio_dev_sck_t* sck; + + mio_htrd_t* htrd; + mio_becs_t* sbuf; /* temporary buffer for status line formatting */ + + mio_svc_htts_rsrc_t* rsrc; +}; + +struct mio_svc_htts_cli_htrd_xtn_t +{ + mio_dev_sck_t* sck; +}; +typedef struct mio_svc_htts_cli_htrd_xtn_t mio_svc_htts_cli_htrd_xtn_t; + +struct mio_svc_htts_t +{ + MIO_SVC_HEADER; + + mio_svc_htts_proc_req_t proc_req; + + mio_dev_sck_t* lsck; + mio_svc_htts_cli_t cli; /* list head for client list */ + + mio_bch_t* server_name; + mio_bch_t server_name_buf[64]; +}; + +struct mio_svc_httc_t +{ + MIO_SVC_HEADER; +}; + +#define MIO_SVC_HTTS_CLIL_APPEND_CLI(lh,cli) do { \ + (cli)->cli_next = (lh); \ + (cli)->cli_prev = (lh)->cli_prev; \ + (cli)->cli_prev->cli_next = (cli); \ + (lh)->cli_prev = (cli); \ +} while(0) + +#define MIO_SVC_HTTS_CLIL_UNLINK_CLI(cli) do { \ + (cli)->cli_prev->cli_next = (cli)->cli_next; \ + (cli)->cli_next->cli_prev = (cli)->cli_prev; \ +} while (0) + +#define MIO_SVC_HTTS_CLIL_UNLINK_CLI_CLEAN(cli) do { \ + (cli)->cli_prev->cli_next = (cli)->cli_next; \ + (cli)->cli_next->cli_prev = (cli)->cli_prev; \ + (cli)->cli_prev = (cli); \ + (cli)->cli_next = (cli); \ +} while (0) + +#define MIO_SVC_HTTS_CLIL_INIT(lh) ((lh)->cli_next = (lh)->cli_prev = lh) +#define MIO_SVC_HTTS_CLIL_FIRST_CLI(lh) ((lh)->cli_next) +#define MIO_SVC_HTTS_CLIL_LAST_CLI(lh) ((lh)->cli_prev) +#define MIO_SVC_HTTS_CLIL_IS_EMPTY(lh) (MIO_SVC_HTTS_CLIL_FIRST_CLI(lh) == (lh)) +#define MIO_SVC_HTTS_CLIL_IS_NIL_CLI(lh,cli) ((cli) == (lh)) + +#endif diff --git a/mio/lib/http-svr.c b/mio/lib/http-svr.c index 809b76b..a04179c 100644 --- a/mio/lib/http-svr.c +++ b/mio/lib/http-svr.c @@ -1,114 +1,39 @@ -#include "mio-http.h" -#include "mio-htrd.h" -#include "mio-pro.h" /* for cgi */ -#include "mio-fmt.h" -#include "mio-chr.h" -#include "mio-path.h" -#include "mio-prv.h" +/* + * $Id$ + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. -#include /* TODO: move file operations to sys-file.XXX */ -#include -#include -#include -#include /* setenv, clearenv */ + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -#define CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH +#include "http-prv.h" -typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t; -struct mio_svc_htts_cli_t -{ - mio_svc_htts_cli_t* cli_prev; - mio_svc_htts_cli_t* cli_next; - - /* a listener socket sets htts and sck fields only */ - /* a client sockets uses all the fields in this struct */ - mio_svc_htts_t* htts; - mio_dev_sck_t* sck; - - mio_htrd_t* htrd; - mio_becs_t* sbuf; /* temporary buffer for status line formatting */ - - mio_svc_htts_rsrc_t* rsrc; -}; - -struct mio_svc_htts_t -{ - MIO_SVC_HEADER; - - mio_svc_htts_proc_req_t proc_req; - - mio_dev_sck_t* lsck; - mio_svc_htts_cli_t cli; /* list head for client list */ - - mio_bch_t* server_name; - mio_bch_t server_name_buf[64]; -}; - -struct mio_svc_httc_t -{ - MIO_SVC_HEADER; -}; - -/* ------------------------------------------------------------------------- */ - -#define CLIL_APPEND_CLI(lh,cli) do { \ - (cli)->cli_next = (lh); \ - (cli)->cli_prev = (lh)->cli_prev; \ - (cli)->cli_prev->cli_next = (cli); \ - (lh)->cli_prev = (cli); \ -} while(0) - -#define CLIL_UNLINK_CLI(cli) do { \ - (cli)->cli_prev->cli_next = (cli)->cli_next; \ - (cli)->cli_next->cli_prev = (cli)->cli_prev; \ -} while (0) - -#define CLIL_UNLINK_CLI_CLEAN(cli) do { \ - (cli)->cli_prev->cli_next = (cli)->cli_next; \ - (cli)->cli_next->cli_prev = (cli)->cli_prev; \ - (cli)->cli_prev = (cli); \ - (cli)->cli_next = (cli); \ -} while (0) - -#define CLIL_INIT(lh) ((lh)->cli_next = (lh)->cli_prev = lh) -#define CLIL_FIRST_CLI(lh) ((lh)->cli_next) -#define CLIL_LAST_CLI(lh) ((lh)->cli_prev) -#define CLIL_IS_EMPTY(lh) (CLIL_FIRST_CLI(lh) == (lh)) -#define CLIL_IS_NIL_CLI(lh,cli) ((cli) == (lh)) - -/* ------------------------------------------------------------------------- */ - -struct htrd_xtn_t -{ - mio_dev_sck_t* sck; -}; -typedef struct htrd_xtn_t htrd_xtn_t; /* ------------------------------------------------------------------------ */ - -static int test_func_handler (int rfd, int wfd) -{ - int i; - - /* you can read the post data from rfd; - * you can write result to wfd */ - write (wfd, "Content-Type: text/plain\r\n\r\n", 28); - for (i = 0 ; i < 10; i++) - { - write (wfd, "hello\n", 6); - sleep (1); - } - return -1; -} - static int client_htrd_peek_request (mio_htrd_t* htrd, mio_htre_t* req) { - htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd); + mio_svc_htts_cli_htrd_xtn_t* htrdxtn = (mio_svc_htts_cli_htrd_xtn_t*)mio_htrd_getxtn(htrd); mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck); return sckxtn->htts->proc_req(sckxtn->htts, htrdxtn->sck, req); } - static mio_htrd_recbs_t client_htrd_recbs = { client_htrd_peek_request, @@ -118,7 +43,7 @@ static mio_htrd_recbs_t client_htrd_recbs = static int init_client (mio_svc_htts_cli_t* cli, mio_dev_sck_t* sck) { - htrd_xtn_t* htrdxtn; + mio_svc_htts_cli_htrd_xtn_t* htrdxtn; /* the htts field must be filled with the same field in the listening socket upon accept() */ MIO_ASSERT (sck->mio, cli->htts != MIO_NULL); @@ -141,7 +66,7 @@ static int init_client (mio_svc_htts_cli_t* cli, mio_dev_sck_t* sck) mio_htrd_setrecbs (cli->htrd, &client_htrd_recbs); cli->sck = sck; - CLIL_APPEND_CLI (&cli->htts->cli, cli); + MIO_SVC_HTTS_CLIL_APPEND_CLI (&cli->htts->cli, cli); MIO_DEBUG3 (sck->mio, "HTTS(%p) - initialized client %p socket %p\n", cli->htts, cli, sck); return 0; @@ -181,7 +106,7 @@ static void fini_client (mio_svc_htts_cli_t* cli) cli->htrd = MIO_NULL; } - CLIL_UNLINK_CLI_CLEAN (cli); + MIO_SVC_HTTS_CLIL_UNLINK_CLI_CLEAN (cli); /* are these needed? not symmetrical if done here. * these fields are copied from the listener socket upon accept. @@ -412,7 +337,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr, mio htts->server_name = htts->server_name_buf; MIO_SVCL_APPEND_SVC (&mio->actsvc, (mio_svc_t*)htts); - CLIL_INIT (&htts->cli); + MIO_SVC_HTTS_CLIL_INIT (&htts->cli); MIO_DEBUG3 (mio, "HTTS - STARTED SERVICE %p - LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)htts->lsck->hnd); return htts; @@ -436,9 +361,9 @@ void mio_svc_htts_stop (mio_svc_htts_t* htts) * forgotten in the disconnect callback thereafter */ if (htts->lsck) mio_dev_sck_kill (htts->lsck); - while (!CLIL_IS_EMPTY(&htts->cli)) + while (!MIO_SVC_HTTS_CLIL_IS_EMPTY(&htts->cli)) { - mio_svc_htts_cli_t* cli = CLIL_FIRST_CLI(&htts->cli); + mio_svc_htts_cli_t* cli = MIO_SVC_HTTS_CLIL_FIRST_CLI(&htts->cli); mio_dev_sck_kill (cli->sck); } @@ -501,1105 +426,6 @@ static MIO_INLINE void* mio_svc_htts_rsrc_getxtn (mio_svc_htts_rsrc_t* rsrc) { r /* ----------------------------------------------------------------- */ -enum cgi_state_res_mode_t -{ - CGI_STATE_RES_MODE_CHUNKED, - CGI_STATE_RES_MODE_CLOSE, - CGI_STATE_RES_MODE_LENGTH -}; -typedef enum cgi_state_res_mode_t cgi_state_res_mode_t; - -#define CGI_STATE_PENDING_IO_THRESHOLD 5 - -#define CGI_STATE_OVER_READ_FROM_CLIENT (1 << 0) -#define CGI_STATE_OVER_READ_FROM_PEER (1 << 1) -#define CGI_STATE_OVER_WRITE_TO_CLIENT (1 << 2) -#define CGI_STATE_OVER_WRITE_TO_PEER (1 << 3) -#define CGI_STATE_OVER_ALL (CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_READ_FROM_PEER | CGI_STATE_OVER_WRITE_TO_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER) - - -struct cgi_state_t -{ - MIO_SVC_HTTS_RSRC_HEADER; - - mio_oow_t num_pending_writes_to_client; - mio_oow_t num_pending_writes_to_peer; - mio_dev_pro_t* peer; - mio_htrd_t* peer_htrd; - mio_svc_htts_cli_t* client; - mio_http_version_t req_version; /* client request */ - - unsigned int over: 4; /* must be large enough to accomodate CGI_STATE_OVER_ALL */ - unsigned int keep_alive: 1; - unsigned int req_content_length_unlimited: 1; - unsigned int ever_attempted_to_write_to_client: 1; - unsigned int client_disconnected: 1; - mio_oow_t req_content_length; /* client request content length */ - cgi_state_res_mode_t res_mode_to_cli; - - mio_dev_sck_on_read_t client_org_on_read; - mio_dev_sck_on_write_t client_org_on_write; - mio_dev_sck_on_disconnect_t client_org_on_disconnect; -}; -typedef struct cgi_state_t cgi_state_t; - -struct cgi_peer_xtn_t -{ - cgi_state_t* state; -}; -typedef struct cgi_peer_xtn_t cgi_peer_xtn_t; - -static void cgi_state_halt_participating_devices (cgi_state_t* cgi_state) -{ - MIO_ASSERT (cgi_state->client->htts->mio, cgi_state->client != MIO_NULL); - MIO_ASSERT (cgi_state->client->htts->mio, cgi_state->client->sck != MIO_NULL); - - MIO_DEBUG4 (cgi_state->client->htts->mio, "HTTS(%p) - Halting participating devices in cgi state %p(client=%p,peer=%p)\n", cgi_state->client->htts, cgi_state, cgi_state->client->sck, cgi_state->peer); - - - mio_dev_sck_halt (cgi_state->client->sck); - /* check for peer as it may not have been started */ - if (cgi_state->peer) mio_dev_pro_halt (cgi_state->peer); -} - -static int cgi_state_write_to_client (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) -{ - cgi_state->ever_attempted_to_write_to_client = 1; - - cgi_state->num_pending_writes_to_client++; - if (mio_dev_sck_write(cgi_state->client->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_client--; - return -1; - } - - if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) - { - if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; - } - return 0; -} - -static int cgi_state_writev_to_client (cgi_state_t* cgi_state, mio_iovec_t* iov, mio_iolen_t iovcnt) -{ - cgi_state->ever_attempted_to_write_to_client = 1; - - cgi_state->num_pending_writes_to_client++; - if (mio_dev_sck_writev(cgi_state->client->sck, iov, iovcnt, MIO_NULL, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_client--; - return -1; - } - - if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) - { - if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; - } - return 0; -} - -static int cgi_state_send_final_status_to_client (cgi_state_t* cgi_state, int status_code, int force_close) -{ - mio_svc_htts_cli_t* cli = cgi_state->client; - mio_bch_t dtbuf[64]; - - mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); - - if (!force_close) force_close = !cgi_state->keep_alive; - if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nContent-Length: 0\r\n\r\n", - cgi_state->req_version.major, cgi_state->req_version.minor, - status_code, mio_http_status_to_bcstr(status_code), - cli->htts->server_name, dtbuf, - (force_close? "close": "keep-alive")) == (mio_oow_t)-1) return -1; - - return (cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && cgi_state_write_to_client(cgi_state, MIO_NULL, 0) <= -1))? -1: 0; -} - - -static int cgi_state_write_last_chunk_to_client (cgi_state_t* cgi_state) -{ - if (!cgi_state->ever_attempted_to_write_to_client) - { - if (cgi_state_send_final_status_to_client(cgi_state, 500, 0) <= -1) return -1; - } - else - { - if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && - cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1) return -1; - } - - if (!cgi_state->keep_alive && cgi_state_write_to_client(cgi_state, MIO_NULL, 0) <= -1) return -1; - return 0; -} - -static int cgi_state_write_to_peer (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) -{ - cgi_state->num_pending_writes_to_peer++; - if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_peer--; - return -1; - } - -/* TODO: check if it's already finished or something.. */ - if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD) - { - if (mio_dev_sck_read(cgi_state->client->sck, 0) <= -1) return -1; - } - return 0; -} - -static MIO_INLINE void cgi_state_mark_over (cgi_state_t* cgi_state, int over_bits) -{ - unsigned int old_over; - - old_over = cgi_state->over; - cgi_state->over |= over_bits; - - MIO_DEBUG5 (cgi_state->htts->mio, "HTTS(%p) - client=%p peer=%p new-bits=%x over=%x\n", cgi_state->htts, cgi_state->client->sck, cgi_state->peer, (int)over_bits, (int)cgi_state->over); - - if (!(old_over & CGI_STATE_OVER_READ_FROM_CLIENT) && (cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) - { - if (mio_dev_sck_read(cgi_state->client->sck, 0) <= -1) - { - MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to disable input watching\n", cgi_state->htts, cgi_state->client->sck); - mio_dev_sck_halt (cgi_state->client->sck); - } - } - - if (!(old_over & CGI_STATE_OVER_READ_FROM_PEER) && (cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) - { - if (cgi_state->peer && mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) - { - MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting peer(%p) for failure to disable input watching\n", cgi_state->htts, cgi_state->peer); - mio_dev_pro_halt (cgi_state->peer); - } - } - - if (old_over != CGI_STATE_OVER_ALL && cgi_state->over == CGI_STATE_OVER_ALL) - { - /* ready to stop */ - if (cgi_state->peer) - { - MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting peer(%p) as it is unneeded\n", cgi_state->htts, cgi_state->peer); - mio_dev_pro_halt (cgi_state->peer); - } - - if (cgi_state->keep_alive) - { - /* how to arrange to delete this cgi_state object and put the socket back to the normal waiting state??? */ - MIO_ASSERT (cgi_state->htts->mio, cgi_state->client->rsrc == (mio_svc_htts_rsrc_t*)cgi_state); - -printf ("DETACHING FROM THE MAIN CLIENT RSRC... state -> %p\n", cgi_state->client->rsrc); - MIO_SVC_HTTS_RSRC_DETACH (cgi_state->client->rsrc); - /* cgi_state must not be access from here down as it could have been destroyed */ - } - else - { - MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for no keep-alive\n", cgi_state->htts, cgi_state->client->sck); - mio_dev_sck_shutdown (cgi_state->client->sck, MIO_DEV_SCK_SHUTDOWN_WRITE); - mio_dev_sck_halt (cgi_state->client->sck); - } - } -} - -static void cgi_state_on_kill (cgi_state_t* cgi_state) -{ -printf ("**** CGI_STATE_ON_KILL \n"); - if (cgi_state->peer) - { - cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(cgi_state->peer); - cgi_peer->state = MIO_NULL; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */ - - mio_dev_pro_kill (cgi_state->peer); - cgi_state->peer = MIO_NULL; - } - - if (cgi_state->peer_htrd) - { - cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); - cgi_peer->state = MIO_NULL; /* cgi_peer->state many not be NULL if the resource is killed regardless of the reference count */ - - mio_htrd_close (cgi_state->peer_htrd); - cgi_state->peer_htrd = MIO_NULL; - } - - if (cgi_state->client_org_on_read) - { - cgi_state->client->sck->on_read = cgi_state->client_org_on_read; - cgi_state->client_org_on_read = MIO_NULL; - } - - if (cgi_state->client_org_on_write) - { - cgi_state->client->sck->on_write = cgi_state->client_org_on_write; - cgi_state->client_org_on_write = MIO_NULL; - } - - - if (cgi_state->client_org_on_disconnect) - { - cgi_state->client->sck->on_disconnect = cgi_state->client_org_on_disconnect; - cgi_state->client_org_on_disconnect = MIO_NULL; - } - - mio_htrd_setrecbs (cgi_state->client->htrd, &client_htrd_recbs); /* restore the callbacks */ - - if (!cgi_state->client_disconnected) - { -printf ("ENABLING INPUT WATCHING on CLIENT %p. \n", cgi_state->client->sck); - if (!cgi_state->keep_alive || mio_dev_sck_read(cgi_state->client->sck, 1) <= -1) - { - MIO_DEBUG2 (cgi_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to enable input watching\n", cgi_state->htts, cgi_state->client->sck); - mio_dev_sck_halt (cgi_state->client->sck); - } - } - -printf ("**** CGI_STATE_ON_KILL DONE\n"); -} - -static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) -{ - mio_t* mio = pro->mio; - cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); - cgi_state_t* cgi_state = cgi_peer->state; - - if (!cgi_state) return; /* cgi state already gone */ - - switch (sid) - { - case MIO_DEV_PRO_MASTER: - MIO_DEBUG3 (mio, "HTTS(%p) - peer %p(pid=%d) closing master\n", cgi_state->client->htts, pro, (int)pro->child_pid); - cgi_state->peer = MIO_NULL; /* clear this peer from the state */ - - MIO_ASSERT (mio, cgi_peer->state != MIO_NULL); -printf ("DETACHING FROM CGI PEER DEVICE.....................%p %d\n", cgi_peer->state, (int)cgi_peer->state->rsrc_refcnt); - MIO_SVC_HTTS_RSRC_DETACH (cgi_peer->state); - - if (cgi_state->peer_htrd) - { - /* once this peer device is closed, peer's htrd is also never used. - * it's safe to detach the extra information attached on the htrd object. */ - cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); - MIO_ASSERT (mio, cgi_peer->state != MIO_NULL); -printf ("DETACHING FROM CGI PEER HTRD.....................%p %d\n", cgi_peer->state, (int)cgi_peer->state->rsrc_refcnt); - MIO_SVC_HTTS_RSRC_DETACH (cgi_peer->state); - } - - break; - - case MIO_DEV_PRO_OUT: - MIO_ASSERT (mio, cgi_state->peer == pro); - MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->client->htts, pro, (int)pro->child_pid, sid); - - if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) - { - if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) - cgi_state_halt_participating_devices (cgi_state); - else - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); - } - break; - - case MIO_DEV_PRO_IN: - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); - break; - - case MIO_DEV_PRO_ERR: - default: - MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->client->htts, pro, (int)pro->child_pid, sid); - /* do nothing */ - break; - } -} - -static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const void* data, mio_iolen_t dlen) -{ - mio_t* mio = pro->mio; - cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); - cgi_state_t* cgi_state = cgi_peer->state; - - MIO_ASSERT (mio, sid == MIO_DEV_PRO_OUT); /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */ - MIO_ASSERT (mio, cgi_state != MIO_NULL); - - if (dlen <= -1) - { - MIO_DEBUG3 (mio, "HTTPS(%p) - read error from peer %p(pid=%u)\n", cgi_state->client->htts, pro, (unsigned int)pro->child_pid); - goto oops; - } - - if (dlen == 0) - { - MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from peer %p(pid=%u)\n", cgi_state->client->htts, pro, (unsigned int)pro->child_pid); - - if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)) - { - /* the cgi script could be misbehaviing. - * it still has to read more but EOF is read. - * otherwise client_peer_htrd_poke() should have been called */ - if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) goto oops; - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); - } - } - else - { - mio_oow_t rem; - - MIO_ASSERT (mio, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER)); - - if (mio_htrd_feed(cgi_state->peer_htrd, data, dlen, &rem) <= -1) - { - MIO_DEBUG3 (mio, "HTTPS(%p) - unable to feed peer into to htrd - peer %p(pid=%u)\n", cgi_state->htts, pro, (unsigned int)pro->child_pid); - - if (!cgi_state->ever_attempted_to_write_to_client && - !(cgi_state->over & CGI_STATE_OVER_WRITE_TO_CLIENT)) - { - cgi_state_send_final_status_to_client (cgi_state, 500, 1); /* don't care about error because it jumps to oops below anyway */ - } - - goto oops; - } - - if (rem > 0) - { - /* If the script specifies Content-Length and produces longer data, it will come here */ -printf ("AAAAAAAAAAAAAAAAAa EEEEEXcessive DATA..................\n"); -/* TODO: or drop this request?? */ - } - } - - return 0; - -oops: - cgi_state_halt_participating_devices (cgi_state); - return 0; -} - -static int cgi_peer_capture_response_header (mio_htre_t* req, const mio_bch_t* key, const mio_htre_hdrval_t* val, void* ctx) -{ - mio_svc_htts_cli_t* cli = (mio_svc_htts_cli_t*)ctx; - - /* capture a header except Status, Connection, Transfer-Encoding, and Server */ - if (mio_comp_bcstr(key, "Status", 1) != 0 && - mio_comp_bcstr(key, "Connection", 1) != 0 && - mio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && - mio_comp_bcstr(key, "Server", 1) != 0 && - mio_comp_bcstr(key, "Date", 1) != 0) - { - do - { - if (mio_becs_cat(cli->sbuf, key) == (mio_oow_t)-1 || - mio_becs_cat(cli->sbuf, ": ") == (mio_oow_t)-1 || - mio_becs_cat(cli->sbuf, val->ptr) == (mio_oow_t)-1 || - mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) - { - return -1; - } - - val = val->next; - } - while (val); - } - - return 0; -} - -static int cgi_peer_htrd_peek (mio_htrd_t* htrd, mio_htre_t* req) -{ - cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); - cgi_state_t* cgi_state = cgi_peer->state; - mio_svc_htts_cli_t* cli = cgi_state->client; - mio_bch_t dtbuf[64]; - int status_code; - - if (req->attr.content_length) - { -// TOOD: remove content_length if content_length is negative or not numeric. - cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_LENGTH; - } - - if (req->attr.status) - { - } - else - { - } - - status_code = 200; - -printf ("CGI PEER HTRD PEEK...\n"); - mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); - - if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %hs\r\n", - cgi_state->req_version.major, cgi_state->req_version.minor, - status_code, mio_http_status_to_bcstr(status_code), - cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1; - - if (mio_htre_walkheaders(req, cgi_peer_capture_response_header, cli) <= -1) return -1; - - switch (cgi_state->res_mode_to_cli) - { - case CGI_STATE_RES_MODE_CHUNKED: - if (mio_becs_cat(cli->sbuf, "Transfer-Encoding: chunked\r\n") == (mio_oow_t)-1) return -1; - /*if (mio_becs_cat(cli->sbuf, "Connection: keep-alive\r\n") == (mio_oow_t)-1) return -1;*/ - break; - - case CGI_STATE_RES_MODE_CLOSE: - if (mio_becs_cat(cli->sbuf, "Connection: close\r\n") == (mio_oow_t)-1) return -1; - break; - - case CGI_STATE_RES_MODE_LENGTH: - if (mio_becs_cat(cli->sbuf, (cgi_state->keep_alive? "Connection: keep-alive\r\n": "Connection: close\r\n")) == (mio_oow_t)-1) return -1; - } - - if (mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) return -1; - - return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)); -} - -static int cgi_peer_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) -{ - /* client request got completed */ - cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); - cgi_state_t* cgi_state = cgi_peer->state; - -printf (">> PEER RESPONSE COMPLETED\n"); - - if (cgi_state_write_last_chunk_to_client(cgi_state) <= -1) return -1; - - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_PEER); - return 0; -} - -static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) -{ - cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); - cgi_state_t* cgi_state = cgi_peer->state; - - MIO_ASSERT (cgi_state->client->htts->mio, htrd == cgi_state->peer_htrd); - - switch (cgi_state->res_mode_to_cli) - { - case CGI_STATE_RES_MODE_CHUNKED: - { - mio_iovec_t iov[3]; - mio_bch_t lbuf[16]; - mio_oow_t llen; - - /* mio_fmt_uintmax_to_bcstr() null-terminates the output. only MIO_COUNTOF(lbuf) - 1 - * is enough to hold '\r' and '\n' at the back without '\0'. */ - llen = mio_fmt_uintmax_to_bcstr(lbuf, MIO_COUNTOF(lbuf) - 1, dlen, 16 | MIO_FMT_UINTMAX_UPPERCASE, 0, '\0', MIO_NULL); - lbuf[llen++] = '\r'; - lbuf[llen++] = '\n'; - - iov[0].iov_ptr = lbuf; - iov[0].iov_len = llen; - iov[1].iov_ptr = (void*)data; - iov[1].iov_len = dlen; - iov[2].iov_ptr = "\r\n"; - iov[2].iov_len = 2; - - if (cgi_state_writev_to_client(cgi_state, iov, MIO_COUNTOF(iov)) <= -1) goto oops; - break; - } - - case CGI_STATE_RES_MODE_CLOSE: - case CGI_STATE_RES_MODE_LENGTH: - if (cgi_state_write_to_client(cgi_state, data, dlen) <= -1) goto oops; - break; - } - - if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) - { - if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) goto oops; - } - - return 0; - -oops: - return -1; -} - -static mio_htrd_recbs_t cgi_peer_htrd_recbs = -{ - cgi_peer_htrd_peek, - cgi_peer_htrd_poke, - cgi_peer_htrd_push_content -}; - -static int cgi_client_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) -{ - /* client request got completed */ - htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd); - mio_dev_sck_t* sck = htrdxtn->sck; - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); - cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; - -printf (">> CLIENT REQUEST COMPLETED\n"); - - /* indicate EOF to the client peer */ - if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) return -1; - - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT); - return 0; -} - -static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) -{ - htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd); - mio_dev_sck_t* sck = htrdxtn->sck; - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); - cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; - - MIO_ASSERT (sck->mio, cli->sck == sck); - return cgi_state_write_to_peer(cgi_state, data, dlen); -} - -static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx) -{ - mio_t* mio = pro->mio; - cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); - cgi_state_t* cgi_state = cgi_peer->state; - - if (cgi_state == MIO_NULL) return 0; /* there is nothing i can do. the cgi_state is being cleared or has been cleared already. */ - - MIO_ASSERT (mio, cgi_state->peer == pro); - - if (wrlen <= -1) - { - MIO_DEBUG3 (mio, "HTTS(%p) - unable to write to peer %p(pid=%u)\n", cgi_state->client->htts, pro, (int)pro->child_pid); - goto oops; - } - else if (wrlen == 0) - { - /* indicated EOF */ - /* do nothing here as i didn't incremented num_pending_writes_to_peer when making the write request */ - - cgi_state->num_pending_writes_to_peer--; - MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer == 0); - MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to peer %p(pid=%u)\n", cgi_state->client->htts, pro, (int)pro->child_pid); - /* indicated EOF to the peer side. i need no more data from the client side. - * i don't need to enable input watching in the client side either */ - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); - } - else - { - MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer > 0); - - cgi_state->num_pending_writes_to_peer--; - if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD) - { - if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT) && - mio_dev_sck_read(cgi_state->client->sck, 1) <= -1) goto oops; - } - - if ((cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT) && cgi_state->num_pending_writes_to_peer <= 0) - { - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_PEER); - } - } - - return 0; - -oops: - cgi_state_halt_participating_devices (cgi_state); - return 0; -} - -static void cgi_client_on_disconnect (mio_dev_sck_t* sck) -{ - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); - cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; - cgi_state->client_disconnected = 1; - listener_on_disconnect (sck); -} - -static int cgi_client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) -{ - mio_t* mio = sck->mio; - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); - cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; - - MIO_ASSERT (mio, sck == cli->sck); - - if (len <= -1) - { - /* read error */ - MIO_DEBUG2 (cli->htts->mio, "HTTPS(%p) - read error on client %p(%d)\n", sck, (int)sck->hnd); - goto oops; - } - - if (!cgi_state->peer) - { - /* the peer is gone */ - goto oops; /* do what? just return 0? */ - } - - if (len == 0) - { - /* EOF on the client side. arrange to close */ - MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from client %p(hnd=%d)\n", cgi_state->client->htts, sck, (int)sck->hnd); - - if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without cgi_client_htrd_poke() */ - { - if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops; - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT); - } - } - else - { - mio_oow_t rem; - - MIO_ASSERT (mio, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)); - - if (mio_htrd_feed(cli->htrd, buf, len, &rem) <= -1) goto oops; - - if (rem > 0) - { - /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ -printf ("UUUUUUUUUUUUUUUUUUUUUUUUUUGGGGGHHHHHHHHHHHH .......... CGI CLIENT GIVING EXCESSIVE DATA AFTER CONTENTS...\n"); - } - } - - return 0; - -oops: - cgi_state_halt_participating_devices (cgi_state); - return 0; -} - -static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) -{ - mio_t* mio = sck->mio; - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); - cgi_state_t* cgi_state = (cgi_state_t*)cli->rsrc; - - if (wrlen <= -1) - { - MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client %p(%d)\n", sck->mio, sck, (int)sck->hnd); - goto oops; - } - - if (wrlen == 0) - { - /* if the connect is keep-alive, this part may not be called */ - cgi_state->num_pending_writes_to_client--; - MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client == 0); - MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to client %p(%d)\n", cgi_state->client->htts, sck, (int)sck->hnd); - /* since EOF has been indicated to the client, it must not write to the client any further. - * this also means that i don't need any data from the peer side either. - * i don't need to enable input watching on the peer side */ - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_CLIENT); - } - else - { - MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client > 0); - - cgi_state->num_pending_writes_to_client--; - if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD) - { - if (!(cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER) && - mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops; - } - - if ((cgi_state->over & CGI_STATE_OVER_READ_FROM_PEER) && cgi_state->num_pending_writes_to_client <= 0) - { - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_WRITE_TO_CLIENT); - } - } - - return 0; - -oops: - cgi_state_halt_participating_devices (cgi_state); - return 0; -} - -static MIO_INLINE int get_request_content_length (mio_htre_t* req, mio_oow_t* len) -{ - if (req->flags & MIO_HTRE_ATTR_CHUNKED) - { - /* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX". - * - * [RFC7230] - * If a message is received with both a Transfer-Encoding and a - * Content-Length header field, the Transfer-Encoding overrides the - * Content-Length. */ - return 1; /* unable to determine content-length in advance. unlimited */ - } - - if (req->flags & MIO_HTRE_ATTR_LENGTH) - { - *len = req->attr.content_length; - } - else - { - /* If no Content-Length is specified in a request, it's Content-Length: 0 */ - *len = 0; - } - return 0; /* limited to the length set in *len */ -} - -struct cgi_peer_fork_ctx_t -{ - mio_svc_htts_cli_t* cli; - mio_htre_t* req; - const mio_bch_t* docroot; - const mio_bch_t* script; - mio_bch_t* actual_script; -}; -typedef struct cgi_peer_fork_ctx_t cgi_peer_fork_ctx_t; - -static int cgi_peer_capture_request_header (mio_htre_t* req, const mio_bch_t* key, const mio_htre_hdrval_t* val, void* ctx) -{ - mio_becs_t* dbuf = (mio_becs_t*)ctx; - - if (mio_comp_bcstr(key, "Connection", 1) != 0 && - mio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && - mio_comp_bcstr(key, "Content-Length", 1) != 0 && - mio_comp_bcstr(key, "Expect", 1) != 0) - { - mio_oow_t val_offset; - mio_bch_t* ptr; - - mio_becs_clear (dbuf); - if (mio_becs_cpy(dbuf, "HTTP_") == (mio_oow_t)-1 || - mio_becs_cat(dbuf, key) == (mio_oow_t)-1 || - mio_becs_ccat(dbuf, '\0') == (mio_oow_t)-1) return -1; - - for (ptr = MIO_BECS_PTR(dbuf); *ptr; ptr++) - { - *ptr = mio_to_bch_upper(*ptr); - if (*ptr =='-') *ptr = '_'; - } - - val_offset = MIO_BECS_LEN(dbuf); - if (mio_becs_cat(dbuf, val->ptr) == (mio_oow_t)-1) return -1; - val = val->next; - while (val) - { - if (mio_becs_cat(dbuf, ",") == (mio_oow_t)-1 || - mio_becs_cat (dbuf, val->ptr) == (mio_oow_t)-1) return -1; - val = val->next; - } - - setenv (MIO_BECS_PTR(dbuf), MIO_BECS_CPTR(dbuf, val_offset), 1); - } - - return 0; -} - -static int cgi_peer_on_fork (mio_dev_pro_t* pro, void* fork_ctx) -{ - mio_t* mio = pro->mio; /* in this callback, the pro device is not fully up. however, the mio field is guaranteed to be available */ - cgi_peer_fork_ctx_t* fc = (cgi_peer_fork_ctx_t*)fork_ctx; - mio_oow_t content_length; - const mio_bch_t* qparam; - mio_bch_t* path, * lang; - mio_bch_t tmp[256]; - mio_becs_t dbuf; - - qparam = mio_htre_getqparam(fc->req); - - path = mio_dupbcstr(mio, getenv("PATH"), MIO_NULL); - lang = mio_dupbcstr(mio, getenv("LANG"), MIO_NULL); -#if defined(HAVE_CLEARENV) - clearenv (); -#else - { - extern char** environ; - /* environ = NULL; this crashed this program on NetBSD */ - if (environ) environ[0] = '\0'; - } -#endif - if (path) - { - setenv ("PATH", path, 1); - mio_freemem (mio, path); - } - - if (lang) - { - setenv ("LANG", lang, 1); - mio_freemem (mio, lang); - } - - setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1); - - mio_fmttobcstr (mio, tmp, MIO_COUNTOF(tmp), "HTTP/%d.%d", (int)mio_htre_getmajorversion(fc->req), (int)mio_htre_getminorversion(fc->req)); - setenv ("SERVER_PROTOCOL", tmp, 1); - - setenv ("DOCUMENT_ROOT", fc->docroot, 1); - setenv ("SCRIPT_NAME", fc->script, 1); - setenv ("SCRIPT_FILENAME", fc->actual_script, 1); - /* TODO: PATH_INFO */ - - setenv ("REQUEST_METHOD", mio_htre_getqmethodname(fc->req), 1); - setenv ("REQUEST_URI", mio_htre_getqpath(fc->req), 1); - if (qparam) setenv ("QUERY_STRING", qparam, 1); - - if (get_request_content_length(fc->req, &content_length) == 0) - { - mio_fmt_uintmax_to_bcstr(tmp, MIO_COUNTOF(tmp), content_length, 10, 0, '\0', MIO_NULL); - setenv ("CONTENT_LENGTH", tmp, 1); - } - else - { - /* content length unknown, neither is it 0 - this is not standard */ - setenv ("CONTENT_LENGTH", "-1", 1); - } - setenv ("SERVER_SOFTWARE", fc->cli->htts->server_name, 1); - - mio_skadtobcstr (mio, &fc->cli->sck->localaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_ADDR); - setenv ("SERVER_ADDR", tmp, 1); - - gethostname (tmp, MIO_COUNTOF(tmp)); /* if this fails, i assume tmp contains the ip address set by mio_skadtobcstr() above */ - setenv ("SERVER_NAME", tmp, 1); - - mio_skadtobcstr (mio, &fc->cli->sck->localaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_PORT); - setenv ("SERVER_PORT", tmp, 1); - - mio_skadtobcstr (mio, &fc->cli->sck->remoteaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_ADDR); - setenv ("REMOTE_ADDR", tmp, 1); - - mio_skadtobcstr (mio, &fc->cli->sck->remoteaddr, tmp, MIO_COUNTOF(tmp), MIO_SKAD_TO_BCSTR_PORT); - setenv ("REMOTE_PORT", tmp, 1); - - if (mio_becs_init(&dbuf, mio, 256) >= 0) - { - mio_htre_walkheaders (fc->req, cgi_peer_capture_request_header, &dbuf); - /* [NOTE] trailers are not available when this cgi resource is started. let's not call mio_htre_walktrailers() */ - mio_becs_fini (&dbuf); - } - - return 0; -} - -int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot, const mio_bch_t* script) -{ - mio_t* mio = htts->mio; - mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); - cgi_state_t* cgi_state = MIO_NULL; - cgi_peer_xtn_t* cgi_peer; - mio_dev_pro_make_t mi; - cgi_peer_fork_ctx_t fc; - - /* ensure that you call this function before any contents is received */ - MIO_ASSERT (mio, mio_htre_getcontentlen(req) == 0); - - MIO_MEMSET (&fc, 0, MIO_SIZEOF(fc)); - fc.cli = cli; - fc.req = req; - fc.docroot = docroot; - fc.script = script; - fc.actual_script = mio_svc_htts_dupmergepaths(htts, docroot, script); - if (!fc.actual_script) goto oops; - - MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi)); - mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/; - mi.cmd = fc.actual_script; - mi.on_read = cgi_peer_on_read; - mi.on_write = cgi_peer_on_write; - mi.on_close = cgi_peer_on_close; - mi.on_fork = cgi_peer_on_fork; - mi.fork_ctx = &fc; - - cgi_state = (cgi_state_t*)mio_svc_htts_rsrc_make(htts, MIO_SIZEOF(*cgi_state), cgi_state_on_kill); - if (MIO_UNLIKELY(!cgi_state)) goto oops; - - cgi_state->client = cli; - /*cgi_state->num_pending_writes_to_client = 0; - cgi_state->num_pending_writes_to_peer = 0;*/ - cgi_state->req_version = *mio_htre_getversion(req); - cgi_state->req_content_length_unlimited = get_request_content_length(req, &cgi_state->req_content_length); - - cgi_state->client_org_on_read = csck->on_read; - cgi_state->client_org_on_write = csck->on_write; - cgi_state->client_org_on_disconnect = csck->on_disconnect; - csck->on_read = cgi_client_on_read; - csck->on_write = cgi_client_on_write; - csck->on_disconnect = cgi_client_on_disconnect; - - MIO_ASSERT (mio, cli->rsrc == MIO_NULL); - MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cli->rsrc); - - if (access(mi.cmd, X_OK) == -1) - { - cgi_state_send_final_status_to_client (cgi_state, 403, 1); /* 403 Forbidden */ - goto oops; /* TODO: must not go to oops. just destroy the cgi_state and finalize the request .. */ - } - - cgi_state->peer = mio_dev_pro_make(mio, MIO_SIZEOF(*cgi_peer), &mi); - if (MIO_UNLIKELY(!cgi_state->peer)) goto oops; - cgi_peer = mio_dev_pro_getxtn(cgi_state->peer); - MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cgi_peer->state); - - cgi_state->peer_htrd = mio_htrd_open(mio, MIO_SIZEOF(*cgi_peer)); - if (MIO_UNLIKELY(!cgi_state->peer_htrd)) goto oops; - mio_htrd_setopt (cgi_state->peer_htrd, MIO_HTRD_SKIPINITIALLINE | MIO_HTRD_RESPONSE); - mio_htrd_setrecbs (cgi_state->peer_htrd, &cgi_peer_htrd_recbs); - - cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); - MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cgi_peer->state); - -#if !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (cgi_state->req_content_length_unlimited) - { - /* Transfer-Encoding is chunked. no content-length is known in advance. */ - - /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. - * option 2. send 411 Length Required immediately - * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ - - if (cgi_state_send_final_status_to_client(cgi_state, 411, 1) <= -1) goto oops; - } -#endif - - if (req->flags & MIO_HTRE_ATTR_EXPECT100) - { - /* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ - /* CAN I LET the cgi SCRIPT handle this? */ - if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - (cgi_state->req_content_length_unlimited || cgi_state->req_content_length > 0)) - { - /* - * Don't send 100 Continue if http verions is lower than 1.1 - * [RFC7231] - * A server that receives a 100-continue expectation in an HTTP/1.0 - * request MUST ignore that expectation. - * - * Don't send 100 Continue if expected content lenth is 0. - * [RFC7231] - * A server MAY omit sending a 100 (Continue) response if it has - * already received some or all of the message body for the - * corresponding request, or if the framing indicates that there is - * no message body. - */ - mio_bch_t msgbuf[64]; - mio_oow_t msglen; - - msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi_state->req_version.major, cgi_state->req_version.minor); - if (cgi_state_write_to_client(cgi_state, msgbuf, msglen) <= -1) goto oops; - cgi_state->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ - } - } - else if (req->flags & MIO_HTRE_ATTR_EXPECT) - { - /* 417 Expectation Failed */ - cgi_state_send_final_status_to_client(cgi_state, 417, 1); - goto oops; - } - -#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (cgi_state->req_content_length_unlimited) - { - /* Transfer-Encoding is chunked. no content-length is known in advance. */ - - /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. - * option 2. send 411 Length Required immediately - * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ - -/* TODO: must set CONTENT-LENGTH to -1 before start the process */ - /* change the callbacks to subscribe to contents to be uploaded */ - cgi_state->client->htrd->recbs.poke = cgi_client_htrd_poke; - cgi_state->client->htrd->recbs.push_content = cgi_client_htrd_push_content; - } - else - { -#endif - if (cgi_state->req_content_length > 0) - { - /* change the callbacks to subscribe to contents to be uploaded */ - cgi_state->client->htrd->recbs.poke = cgi_client_htrd_poke; - cgi_state->client->htrd->recbs.push_content = cgi_client_htrd_push_content; - } - else - { - /* no content to be uploaded from the client */ - /* indicate EOF to the peer and disable input wathching from the client */ - if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops; - cgi_state_mark_over (cgi_state, CGI_STATE_OVER_READ_FROM_CLIENT | CGI_STATE_OVER_WRITE_TO_PEER); - } -#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - } -#endif - - /* this may change later if Content-Length is included in the cgi output */ - if (req->flags & MIO_HTRE_ATTR_KEEPALIVE) - { - cgi_state->keep_alive = 1; - cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_CHUNKED; - /* the mode still can get switched to CGI_STATE_RES_MODE_LENGTH if the cgi script emits Content-Length */ - } - else - { - cgi_state->keep_alive = 0; - cgi_state->res_mode_to_cli = CGI_STATE_RES_MODE_CLOSE; - } - - /* TODO: store current input watching state and use it when destroying the cgi_state data */ - if (mio_dev_sck_read(csck, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) <= -1) goto oops; - mio_freemem (mio, fc.actual_script); - return 0; - -oops: - MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck); - if (cgi_state) cgi_state_halt_participating_devices (cgi_state); - if (fc.actual_script) mio_freemem (mio, fc.actual_script); - return -1; -} - -/* ----------------------------------------------------------------- */ - -struct thr_state_t -{ - MIO_SVC_HTTS_RSRC_HEADER; - pthread_t thr; - mio_svc_htts_thr_func_t func; - mio_dev_t* p1; /* change it to pipe */ -}; - -typedef struct thr_state_t thr_state_t; - -static void thr_state_on_kill (thr_state_t* thr_state) -{ -} - -static void* thr_state_run_func (void* arg) -{ - thr_state_t* thr_state = (thr_state_t*)arg; - -// thr_state->func (thr_state->XXX, thr_state->p1->fd); /* pass the pointer to data that can be accessed safely inside a thread. mostly these must be duplicates of core data */ - return MIO_NULL; -} - -int mio_svc_htts_dothr (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, mio_svc_htts_thr_func_t func) -{ - /*mio_t* mio = htts->mio;*/ -// mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); - thr_state_t* thr_state = MIO_NULL; - - thr_state = (thr_state_t*)mio_svc_htts_rsrc_make(htts, MIO_SIZEOF(*thr_state), thr_state_on_kill); - if (MIO_UNLIKELY(!thr_state)) goto oops; - - thr_state->func = func; - -#if 0 - thr_state->p1 = mio_dev_pipe_make(mio); - if (MIO_UNLIKELY(!thr_state->p1)) goto oops; -#endif - - if (pthread_create(&thr_state->thr, MIO_NULL, thr_state_run_func, thr_state) != 0) - { - } - - return 0; - -oops: - //if (thr_state) thr_state_halt_particiapting_devices (thr_state); - return -1; -} /* ----------------------------------------------------------------- */ diff --git a/mio/lib/http-thr.c b/mio/lib/http-thr.c new file mode 100644 index 0000000..faf3bf7 --- /dev/null +++ b/mio/lib/http-thr.c @@ -0,0 +1,81 @@ +/* + * $Id$ + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "http-prv.h" +#include +#include +#include + +#include + +struct thr_state_t +{ + MIO_SVC_HTTS_RSRC_HEADER; + pthread_t thr; + mio_svc_htts_thr_func_t func; + mio_dev_t* p1; /* change it to pipe */ +}; + +typedef struct thr_state_t thr_state_t; + +static void thr_state_on_kill (thr_state_t* thr_state) +{ +} + +static void* thr_state_run_func (void* arg) +{ + thr_state_t* thr_state = (thr_state_t*)arg; + +// thr_state->func (thr_state->XXX, thr_state->p1->fd); /* pass the pointer to data that can be accessed safely inside a thread. mostly these must be duplicates of core data */ + return MIO_NULL; +} + +int mio_svc_htts_dothr (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, mio_svc_htts_thr_func_t func) +{ + /*mio_t* mio = htts->mio;*/ +// mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); + thr_state_t* thr_state = MIO_NULL; + + thr_state = (thr_state_t*)mio_svc_htts_rsrc_make(htts, MIO_SIZEOF(*thr_state), thr_state_on_kill); + if (MIO_UNLIKELY(!thr_state)) goto oops; + + thr_state->func = func; + +#if 0 + thr_state->p1 = mio_dev_pipe_make(mio); + if (MIO_UNLIKELY(!thr_state->p1)) goto oops; +#endif + + if (pthread_create(&thr_state->thr, MIO_NULL, thr_state_run_func, thr_state) != 0) + { + } + + return 0; + +oops: + //if (thr_state) thr_state_halt_particiapting_devices (thr_state); + return -1; +} + diff --git a/mio/lib/http.c b/mio/lib/http.c index b653b64..e223b04 100644 --- a/mio/lib/http.c +++ b/mio/lib/http.c @@ -22,9 +22,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-http.h" -#include "mio-chr.h" -#include "mio-utl.h" +#include +#include +#include #include "mio-prv.h" #include diff --git a/mio/lib/mio-prv.h b/mio/lib/mio-prv.h index 19983e0..3325cc0 100644 --- a/mio/lib/mio-prv.h +++ b/mio/lib/mio-prv.h @@ -27,8 +27,8 @@ #ifndef _MIO_PRV_H_ #define _MIO_PRV_H_ -#include "mio.h" -#include "mio-utl.h" +#include +#include #if defined(__has_builtin) diff --git a/mio/lib/mio-utl.h b/mio/lib/mio-utl.h index c297940..4a89d5e 100644 --- a/mio/lib/mio-utl.h +++ b/mio/lib/mio-utl.h @@ -27,7 +27,7 @@ #ifndef _MIO_UTL_H_ #define _MIO_UTL_H_ -#include "mio-cmn.h" +#include #include /* ========================================================================= diff --git a/mio/lib/mio.c b/mio/lib/mio.c index 3495958..9949b20 100644 --- a/mio/lib/mio.c +++ b/mio/lib/mio.c @@ -25,8 +25,8 @@ */ #include "mio-prv.h" -#include "mio-fmt.h" -#include +#include +#include /* malloc, free, etc */ #define DEV_CAP_ALL_WATCHED (MIO_DEV_CAP_IN_WATCHED | MIO_DEV_CAP_OUT_WATCHED | MIO_DEV_CAP_PRI_WATCHED) diff --git a/mio/lib/mio.h b/mio/lib/mio.h index 7a71f68..dfb9465 100644 --- a/mio/lib/mio.h +++ b/mio/lib/mio.h @@ -92,6 +92,7 @@ enum mio_errnum_t MIO_EPIPE, /**< pipe error */ MIO_EAGAIN, /**< resource temporarily unavailable */ MIO_EBADHND, /**< bad system handle */ + MIO_EBADRE, /**< bad request or response */ MIO_EMFILE, /**< too many open files */ MIO_ENFILE, /**< too many open files */ diff --git a/mio/lib/nwif.c b/mio/lib/nwif.c index e110e53..7accf0c 100644 --- a/mio/lib/nwif.c +++ b/mio/lib/nwif.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-nwif.h" +#include #include "mio-prv.h" #if defined(_WIN32) diff --git a/mio/lib/path.c b/mio/lib/path.c index dc9b60f..6d387a0 100644 --- a/mio/lib/path.c +++ b/mio/lib/path.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-path.h" +#include #include "mio-prv.h" /* TODO: support the \\?\ prefix and the \\.\ prefix on windows diff --git a/mio/lib/pipe.c b/mio/lib/pipe.c index dec873d..50474b3 100644 --- a/mio/lib/pipe.c +++ b/mio/lib/pipe.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-pipe.h" +#include #include "mio-prv.h" #include diff --git a/mio/lib/pro.c b/mio/lib/pro.c index 12d003f..1a22f70 100644 --- a/mio/lib/pro.c +++ b/mio/lib/pro.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-pro.h" +#include #include "mio-prv.h" #include diff --git a/mio/lib/sck.c b/mio/lib/sck.c index de4fa36..60268d6 100644 --- a/mio/lib/sck.c +++ b/mio/lib/sck.c @@ -25,7 +25,7 @@ */ -#include "mio-sck.h" +#include #include "mio-prv.h" #include diff --git a/mio/lib/skad.c b/mio/lib/skad.c index 81ca44c..16b6c40 100644 --- a/mio/lib/skad.c +++ b/mio/lib/skad.c @@ -24,9 +24,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-skad.h" -#include "mio-nwif.h" -#include "mio-fmt.h" +#include +#include +#include #include "mio-prv.h" #include diff --git a/mio/lib/sys-prv.h b/mio/lib/sys-prv.h index d1c946a..93f71e3 100644 --- a/mio/lib/sys-prv.h +++ b/mio/lib/sys-prv.h @@ -24,8 +24,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _MIO_SYS_H_ -#define _MIO_SYS_H_ +#ifndef _MIO_SYS_PRV_H_ +#define _MIO_SYS_PRV_H_ /* this is a private header file used by sys-XXX files */ diff --git a/mio/lib/thr.c b/mio/lib/thr.c index 26e924f..ddeb84f 100644 --- a/mio/lib/thr.c +++ b/mio/lib/thr.c @@ -24,7 +24,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "mio-thr.h" +#include #include "mio-prv.h" #include diff --git a/mio/lib/utl.c b/mio/lib/utl.c index b7f8a93..2497a06 100644 --- a/mio/lib/utl.c +++ b/mio/lib/utl.c @@ -25,7 +25,7 @@ */ #include "mio-prv.h" -#include "mio-chr.h" +#include /* ========================================================================= */