Reorganized the directory structure

This commit is contained in:
2022-09-25 09:23:29 +09:00
parent 1bac167e2d
commit 84d1c4c55f
864 changed files with 11 additions and 12 deletions

45
lib/http/Makefile.am Normal file
View File

@ -0,0 +1,45 @@
AUTOMAKE_OPTIONS = nostdinc
CPPFLAGS_ALL_COMMON = \
-I$(top_builddir)/include \
-I$(top_srcdir)/include
if WIN32
# you must adjust the value of DEFAULT_MODPOSTFIX according
# to the first number in -version-info below
CPPFLAGS_HTTPD_MOD = -DQSE_HTTPD_DEFAULT_MODPREFIX=\"libqsehttpd-\" -DQSE_HTTPD_DEFAULT_MODPOSTFIX=\"-1\"
else
CPPFLAGS_HTTPD_MOD = -DQSE_HTTPD_DEFAULT_MODPREFIX=\"$(libdir)/libqsehttpd-\" -DQSE_HTTPD_DEFAULT_MODPOSTFIX=\"\"
endif
lib_LTLIBRARIES = libqsehttp.la
libqsehttp_la_SOURCES = \
httpd.h \
upxd.h \
http.c \
htre.c \
htrd.c \
httpd.c \
httpd-cgi.c \
httpd-dir.c \
httpd-file.c \
httpd-proxy.c \
httpd-std.c \
httpd-std-dns.h \
httpd-std-mod.h \
httpd-std-urs.h \
httpd-task.c \
httpd-text.c \
upxd.c
libqsehttp_la_CPPFLAGS = $(CPPFLAGS_ALL_COMMON) $(CPPFLAGS_HTTPD_MOD) $(LTDLINCL)
libqsehttp_la_LDFLAGS = -L../si -L../cmn -version-info 1:0:0 -no-undefined
libqsehttp_la_LIBADD = -lqsesi -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)
if ENABLE_LIBLTDL
libqsehttp_la_LIBADD += $(LTDL_LIBS)
else
libqsehttp_la_LIBADD += $(DL_LIBS)
endif

867
lib/http/Makefile.in Normal file
View File

@ -0,0 +1,867 @@
# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
@ENABLE_LIBLTDL_TRUE@am__append_1 = $(LTDL_LIBS)
@ENABLE_LIBLTDL_FALSE@am__append_2 = $(DL_LIBS)
subdir = lib/http
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_sign.m4 \
$(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/ax_cxx_namespace.m4 \
$(top_srcdir)/m4/ax_lib_mysql.m4 $(top_srcdir)/m4/ax_numval.m4 \
$(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/m4/lx_find_mpi.m4 \
$(top_srcdir)/m4/qse_try_cflags.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/qse/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
@ENABLE_LIBLTDL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
@ENABLE_LIBLTDL_FALSE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1)
libqsehttp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
am_libqsehttp_la_OBJECTS = libqsehttp_la-http.lo libqsehttp_la-htre.lo \
libqsehttp_la-htrd.lo libqsehttp_la-httpd.lo \
libqsehttp_la-httpd-cgi.lo libqsehttp_la-httpd-dir.lo \
libqsehttp_la-httpd-file.lo libqsehttp_la-httpd-proxy.lo \
libqsehttp_la-httpd-std.lo libqsehttp_la-httpd-task.lo \
libqsehttp_la-httpd-text.lo libqsehttp_la-upxd.lo
libqsehttp_la_OBJECTS = $(am_libqsehttp_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
libqsehttp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libqsehttp_la_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/ac/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/libqsehttp_la-htrd.Plo \
./$(DEPDIR)/libqsehttp_la-htre.Plo \
./$(DEPDIR)/libqsehttp_la-http.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-cgi.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-dir.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-file.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-proxy.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-std.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-task.Plo \
./$(DEPDIR)/libqsehttp_la-httpd-text.Plo \
./$(DEPDIR)/libqsehttp_la-httpd.Plo \
./$(DEPDIR)/libqsehttp_la-upxd.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libqsehttp_la_SOURCES)
DIST_SOURCES = $(libqsehttp_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/ac/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
BUILD_MODE = @BUILD_MODE@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DL_LIBS = @DL_LIBS@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO = @ECHO@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GREP = @GREP@
HAVE_CXX = @HAVE_CXX@
HAVE_CXX11 = @HAVE_CXX11@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBM = @LIBM@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTDL_LIBS = @LTDL_LIBS@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MPICC = @MPICC@
MPI_CFLAGS = @MPI_CFLAGS@
MPI_CLDFLAGS = @MPI_CLDFLAGS@
MYSQL_CFLAGS = @MYSQL_CFLAGS@
MYSQL_CONFIG = @MYSQL_CONFIG@
MYSQL_LDFLAGS = @MYSQL_LDFLAGS@
MYSQL_LIBS = @MYSQL_LIBS@
MYSQL_VERSION = @MYSQL_VERSION@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
PACKAGE_VERSION_PATCH = @PACKAGE_VERSION_PATCH@
PATH_SEPARATOR = @PATH_SEPARATOR@
PTHREAD_CC = @PTHREAD_CC@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LIBS = @PTHREAD_LIBS@
QSE_PROJECT_AUTHOR = @QSE_PROJECT_AUTHOR@
QSE_PROJECT_URL = @QSE_PROJECT_URL@
QUADMATH_LIBS = @QUADMATH_LIBS@
RANLIB = @RANLIB@
RM = @RM@
RMDIR = @RMDIR@
SED = @SED@
SENDFILE_LIBS = @SENDFILE_LIBS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SOCKET_LIBS = @SOCKET_LIBS@
SSL_LIBS = @SSL_LIBS@
STRIP = @STRIP@
TRUE = @TRUE@
UCI_LIBS = @UCI_LIBS@
UNICOWS_LIBS = @UNICOWS_LIBS@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
ax_pthread_config = @ax_pthread_config@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = nostdinc
CPPFLAGS_ALL_COMMON = \
-I$(top_builddir)/include \
-I$(top_srcdir)/include
@WIN32_FALSE@CPPFLAGS_HTTPD_MOD = -DQSE_HTTPD_DEFAULT_MODPREFIX=\"$(libdir)/libqsehttpd-\" -DQSE_HTTPD_DEFAULT_MODPOSTFIX=\"\"
# you must adjust the value of DEFAULT_MODPOSTFIX according
# to the first number in -version-info below
@WIN32_TRUE@CPPFLAGS_HTTPD_MOD = -DQSE_HTTPD_DEFAULT_MODPREFIX=\"libqsehttpd-\" -DQSE_HTTPD_DEFAULT_MODPOSTFIX=\"-1\"
lib_LTLIBRARIES = libqsehttp.la
libqsehttp_la_SOURCES = \
httpd.h \
upxd.h \
http.c \
htre.c \
htrd.c \
httpd.c \
httpd-cgi.c \
httpd-dir.c \
httpd-file.c \
httpd-proxy.c \
httpd-std.c \
httpd-std-dns.h \
httpd-std-mod.h \
httpd-std-urs.h \
httpd-task.c \
httpd-text.c \
upxd.c
libqsehttp_la_CPPFLAGS = $(CPPFLAGS_ALL_COMMON) $(CPPFLAGS_HTTPD_MOD) $(LTDLINCL)
libqsehttp_la_LDFLAGS = -L../si -L../cmn -version-info 1:0:0 -no-undefined
libqsehttp_la_LIBADD = -lqsesi -lqsecmn $(SOCKET_LIBS) \
$(SENDFILE_LIBS) $(SSL_LIBS) $(am__append_1) $(am__append_2)
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/http/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/http/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
list2="$$list2 $$p"; \
else :; fi; \
done; \
test -z "$$list2" || { \
echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
for p in $$list; do \
$(am__strip_dir) \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
done
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libqsehttp.la: $(libqsehttp_la_OBJECTS) $(libqsehttp_la_DEPENDENCIES) $(EXTRA_libqsehttp_la_DEPENDENCIES)
$(AM_V_CCLD)$(libqsehttp_la_LINK) -rpath $(libdir) $(libqsehttp_la_OBJECTS) $(libqsehttp_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-htrd.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-htre.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-http.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-cgi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-dir.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-file.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-proxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-std.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-task.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd-text.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-httpd.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libqsehttp_la-upxd.Plo@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@echo '# dummy' >$@-t && $(am__mv) $@-t $@
am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libqsehttp_la-http.lo: http.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-http.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-http.Tpo -c -o libqsehttp_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-http.Tpo $(DEPDIR)/libqsehttp_la-http.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libqsehttp_la-http.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
libqsehttp_la-htre.lo: htre.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-htre.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-htre.Tpo -c -o libqsehttp_la-htre.lo `test -f 'htre.c' || echo '$(srcdir)/'`htre.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-htre.Tpo $(DEPDIR)/libqsehttp_la-htre.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htre.c' object='libqsehttp_la-htre.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-htre.lo `test -f 'htre.c' || echo '$(srcdir)/'`htre.c
libqsehttp_la-htrd.lo: htrd.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-htrd.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-htrd.Tpo -c -o libqsehttp_la-htrd.lo `test -f 'htrd.c' || echo '$(srcdir)/'`htrd.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-htrd.Tpo $(DEPDIR)/libqsehttp_la-htrd.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htrd.c' object='libqsehttp_la-htrd.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-htrd.lo `test -f 'htrd.c' || echo '$(srcdir)/'`htrd.c
libqsehttp_la-httpd.lo: httpd.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd.Tpo -c -o libqsehttp_la-httpd.lo `test -f 'httpd.c' || echo '$(srcdir)/'`httpd.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd.Tpo $(DEPDIR)/libqsehttp_la-httpd.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd.c' object='libqsehttp_la-httpd.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd.lo `test -f 'httpd.c' || echo '$(srcdir)/'`httpd.c
libqsehttp_la-httpd-cgi.lo: httpd-cgi.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-cgi.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-cgi.Tpo -c -o libqsehttp_la-httpd-cgi.lo `test -f 'httpd-cgi.c' || echo '$(srcdir)/'`httpd-cgi.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-cgi.Tpo $(DEPDIR)/libqsehttp_la-httpd-cgi.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-cgi.c' object='libqsehttp_la-httpd-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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-cgi.lo `test -f 'httpd-cgi.c' || echo '$(srcdir)/'`httpd-cgi.c
libqsehttp_la-httpd-dir.lo: httpd-dir.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-dir.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-dir.Tpo -c -o libqsehttp_la-httpd-dir.lo `test -f 'httpd-dir.c' || echo '$(srcdir)/'`httpd-dir.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-dir.Tpo $(DEPDIR)/libqsehttp_la-httpd-dir.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-dir.c' object='libqsehttp_la-httpd-dir.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-dir.lo `test -f 'httpd-dir.c' || echo '$(srcdir)/'`httpd-dir.c
libqsehttp_la-httpd-file.lo: httpd-file.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-file.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-file.Tpo -c -o libqsehttp_la-httpd-file.lo `test -f 'httpd-file.c' || echo '$(srcdir)/'`httpd-file.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-file.Tpo $(DEPDIR)/libqsehttp_la-httpd-file.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-file.c' object='libqsehttp_la-httpd-file.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-file.lo `test -f 'httpd-file.c' || echo '$(srcdir)/'`httpd-file.c
libqsehttp_la-httpd-proxy.lo: httpd-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-proxy.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-proxy.Tpo -c -o libqsehttp_la-httpd-proxy.lo `test -f 'httpd-proxy.c' || echo '$(srcdir)/'`httpd-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-proxy.Tpo $(DEPDIR)/libqsehttp_la-httpd-proxy.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-proxy.c' object='libqsehttp_la-httpd-proxy.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-proxy.lo `test -f 'httpd-proxy.c' || echo '$(srcdir)/'`httpd-proxy.c
libqsehttp_la-httpd-std.lo: httpd-std.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-std.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-std.Tpo -c -o libqsehttp_la-httpd-std.lo `test -f 'httpd-std.c' || echo '$(srcdir)/'`httpd-std.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-std.Tpo $(DEPDIR)/libqsehttp_la-httpd-std.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-std.c' object='libqsehttp_la-httpd-std.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-std.lo `test -f 'httpd-std.c' || echo '$(srcdir)/'`httpd-std.c
libqsehttp_la-httpd-task.lo: httpd-task.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-task.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-task.Tpo -c -o libqsehttp_la-httpd-task.lo `test -f 'httpd-task.c' || echo '$(srcdir)/'`httpd-task.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-task.Tpo $(DEPDIR)/libqsehttp_la-httpd-task.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-task.c' object='libqsehttp_la-httpd-task.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-task.lo `test -f 'httpd-task.c' || echo '$(srcdir)/'`httpd-task.c
libqsehttp_la-httpd-text.lo: httpd-text.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-httpd-text.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-httpd-text.Tpo -c -o libqsehttp_la-httpd-text.lo `test -f 'httpd-text.c' || echo '$(srcdir)/'`httpd-text.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-httpd-text.Tpo $(DEPDIR)/libqsehttp_la-httpd-text.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd-text.c' object='libqsehttp_la-httpd-text.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-httpd-text.lo `test -f 'httpd-text.c' || echo '$(srcdir)/'`httpd-text.c
libqsehttp_la-upxd.lo: upxd.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libqsehttp_la-upxd.lo -MD -MP -MF $(DEPDIR)/libqsehttp_la-upxd.Tpo -c -o libqsehttp_la-upxd.lo `test -f 'upxd.c' || echo '$(srcdir)/'`upxd.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libqsehttp_la-upxd.Tpo $(DEPDIR)/libqsehttp_la-upxd.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='upxd.c' object='libqsehttp_la-upxd.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) $(libqsehttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libqsehttp_la-upxd.lo `test -f 'upxd.c' || echo '$(srcdir)/'`upxd.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES)
installdirs:
for dir in "$(DESTDIR)$(libdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
mostlyclean-am
distclean: distclean-am
-rm -f ./$(DEPDIR)/libqsehttp_la-htrd.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-htre.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-http.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-cgi.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-dir.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-file.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-proxy.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-std.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-task.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-text.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-upxd.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-libLTLIBRARIES
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libqsehttp_la-htrd.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-htre.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-http.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-cgi.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-dir.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-file.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-proxy.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-std.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-task.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd-text.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-httpd.Plo
-rm -f ./$(DEPDIR)/libqsehttp_la-upxd.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-libLTLIBRARIES
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
ctags ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-libLTLIBRARIES install-man install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

1754
lib/http/htrd.c Normal file

File diff suppressed because it is too large Load Diff

320
lib/http/htre.c Normal file
View File

@ -0,0 +1,320 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 <qse/http/htre.h>
#include "../cmn/mem-prv.h"
static void free_hdrval (qse_htb_t* htb, void* vptr, qse_size_t vlen)
{
qse_htre_hdrval_t* val;
qse_htre_hdrval_t* tmp;
val = vptr;
while (val)
{
tmp = val;
val = val->next;
QSE_MMGR_FREE (htb->mmgr, tmp);
}
}
int qse_htre_init (qse_htre_t* re, qse_mmgr_t* mmgr)
{
static qse_htb_style_t style =
{
{
QSE_HTB_COPIER_DEFAULT,
QSE_HTB_COPIER_DEFAULT
},
{
QSE_HTB_FREEER_DEFAULT,
free_hdrval
},
QSE_HTB_COMPER_DEFAULT,
QSE_HTB_KEEPER_DEFAULT,
QSE_HTB_SIZER_DEFAULT,
QSE_HTB_HASHER_DEFAULT
};
QSE_MEMSET (re, 0, QSE_SIZEOF(*re));
re->mmgr = mmgr;
if (qse_htb_init (&re->hdrtab, mmgr, 60, 70, 1, 1) <= -1) return -1;
if (qse_htb_init (&re->trailers, mmgr, 20, 70, 1, 1) <= -1) return -1;
qse_htb_setstyle (&re->hdrtab, &style);
qse_htb_setstyle (&re->trailers, &style);
qse_mbs_init (&re->content, mmgr, 0);
#if 0
qse_mbs_init (&re->iniline, mmgr, 0);
#endif
return 0;
}
void qse_htre_fini (qse_htre_t* re)
{
#if 0
qse_mbs_fini (&re->iniline);
#endif
qse_mbs_fini (&re->content);
qse_htb_fini (&re->trailers);
qse_htb_fini (&re->hdrtab);
if (re->orgqpath.buf)
QSE_MMGR_FREE (re->mmgr, re->orgqpath.buf);
}
void qse_htre_clear (qse_htre_t* re)
{
if (!(re->state & QSE_HTRE_COMPLETED) &&
!(re->state & QSE_HTRE_DISCARDED))
{
if (re->concb)
{
re->concb (re, QSE_NULL, 0, re->concb_ctx); /* indicate end of content */
qse_htre_unsetconcb (re);
}
}
re->state = 0;
re->flags = 0;
re->orgqpath.ptr = QSE_NULL;
re->orgqpath.len = 0;
QSE_MEMSET (&re->version, 0, QSE_SIZEOF(re->version));
QSE_MEMSET (&re->attr, 0, QSE_SIZEOF(re->attr));
qse_htb_clear (&re->hdrtab);
qse_htb_clear (&re->trailers);
qse_mbs_clear (&re->content);
#if 0
qse_mbs_clear (&re->iniline);
#endif
}
const qse_htre_hdrval_t* qse_htre_getheaderval (
const qse_htre_t* re, const qse_mchar_t* name)
{
qse_htb_pair_t* pair;
pair = qse_htb_search (&re->hdrtab, name, qse_mbslen(name));
if (pair == QSE_NULL) return QSE_NULL;
return QSE_HTB_VPTR(pair);
}
const qse_htre_hdrval_t* qse_htre_gettrailerval (
const qse_htre_t* re, const qse_mchar_t* name)
{
qse_htb_pair_t* pair;
pair = qse_htb_search (&re->trailers, name, qse_mbslen(name));
if (pair == QSE_NULL) return QSE_NULL;
return QSE_HTB_VPTR(pair);
}
struct header_walker_ctx_t
{
qse_htre_t* re;
qse_htre_header_walker_t walker;
void* ctx;
int ret;
};
static qse_htb_walk_t walk_headers (
qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx)
{
struct header_walker_ctx_t* hwctx = (struct header_walker_ctx_t*)ctx;
if (hwctx->walker (hwctx->re, QSE_HTB_KPTR(pair), QSE_HTB_VPTR(pair), hwctx->ctx) <= -1)
{
hwctx->ret = -1;
return QSE_HTB_WALK_STOP;
}
return QSE_HTB_WALK_FORWARD;
}
int qse_htre_walkheaders (
qse_htre_t* re, qse_htre_header_walker_t walker, void* ctx)
{
struct header_walker_ctx_t hwctx;
hwctx.re = re;
hwctx.walker = walker;
hwctx.ctx = ctx;
hwctx.ret = 0;
qse_htb_walk (&re->hdrtab, walk_headers, &hwctx);
return hwctx.ret;
}
int qse_htre_walktrailers (
qse_htre_t* re, qse_htre_header_walker_t walker, void* ctx)
{
struct header_walker_ctx_t hwctx;
hwctx.re = re;
hwctx.walker = walker;
hwctx.ctx = ctx;
hwctx.ret = 0;
qse_htb_walk (&re->trailers, walk_headers, &hwctx);
return hwctx.ret;
}
int qse_htre_addcontent (
qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len)
{
/* see comments in qse_htre_discardcontent() */
if (re->state & (QSE_HTRE_COMPLETED | QSE_HTRE_DISCARDED)) return 0; /* skipped */
if (re->concb)
{
/* if the callback is set, the content goes to the callback. */
if (re->concb (re, ptr, len, re->concb_ctx) <= -1) return -1;
}
else
{
/* if the callback is not set, the contents goes to the internal buffer */
if (qse_mbs_ncat (&re->content, ptr, len) == (qse_size_t)-1) return -1;
}
return 1; /* added successfully */
}
void qse_htre_completecontent (qse_htre_t* re)
{
/* see comments in qse_htre_discardcontent() */
if (!(re->state & QSE_HTRE_COMPLETED) &&
!(re->state & QSE_HTRE_DISCARDED))
{
re->state |= QSE_HTRE_COMPLETED;
if (re->concb)
{
/* indicate end of content */
re->concb (re, QSE_NULL, 0, re->concb_ctx);
}
}
}
void qse_htre_discardcontent (qse_htre_t* re)
{
/* you can't discard this if it's completed.
* you can't complete this if it's discarded
* you can't add contents to this if it's completed or discarded
*/
if (!(re->state & QSE_HTRE_COMPLETED) &&
!(re->state & QSE_HTRE_DISCARDED))
{
re->state |= QSE_HTRE_DISCARDED;
/* qse_htre_addcontent()...
* qse_thre_setconcb()...
* qse_htre_discardcontent()... <-- POINT A.
*
* at point A, the content must contain something
* and concb is also set. for simplicity,
* clear the content buffer and invoke the callback
*
* likewise, you may produce many weird combinations
* of these functions. however, these functions are
* designed to serve a certain usage pattern not including
* weird combinations.
*/
qse_mbs_clear (&re->content);
if (re->concb)
{
/* indicate end of content */
re->concb (re, QSE_NULL, 0, re->concb_ctx);
}
}
}
void qse_htre_unsetconcb (qse_htre_t* re)
{
re->concb = QSE_NULL;
re->concb_ctx = QSE_NULL;
}
void qse_htre_setconcb (qse_htre_t* re, qse_htre_concb_t concb, void* ctx)
{
re->concb = concb;
re->concb_ctx = ctx;
}
int qse_htre_perdecqpath (qse_htre_t* re)
{
qse_size_t dec_count;
/* percent decode the query path*/
if (re->type != QSE_HTRE_Q || (re->flags & QSE_HTRE_QPATH_PERDEC)) return -1;
QSE_ASSERT (re->orgqpath.len <= 0);
QSE_ASSERT (re->orgqpath.ptr == QSE_NULL);
if (qse_isperencedhttpstr(re->u.q.path.ptr))
{
/* the string is percent-encoded. keep the original request
* in a separately allocated buffer */
if (re->orgqpath.buf && re->u.q.path.len <= re->orgqpath.capa)
{
re->orgqpath.len = qse_mbscpy (re->orgqpath.buf, re->u.q.path.ptr);
re->orgqpath.ptr = re->orgqpath.buf;
}
else
{
if (re->orgqpath.buf)
{
QSE_MMGR_FREE (re->mmgr, re->orgqpath.buf);
re->orgqpath.capa = 0;
}
re->orgqpath.buf = qse_mbsxdup (re->u.q.path.ptr, re->u.q.path.len, re->mmgr);
if (!re->orgqpath.buf) return -1;
re->orgqpath.capa = re->u.q.path.len;
re->orgqpath.ptr = re->orgqpath.buf;
re->orgqpath.len = re->orgqpath.capa;
/* orgqpath.buf and orgqpath.ptr are the same here. the caller
* is free to change orgqpath.ptr to point to a differnt position
* in the buffer. */
}
}
re->u.q.path.len = qse_perdechttpstr (re->u.q.path.ptr, re->u.q.path.ptr, &dec_count);
if (dec_count > 0)
{
/* this assertion is to ensure that qse_isperencedhttpstr()
* returned true when dec_count is greater than 0 */
QSE_ASSERT (re->orgqpath.buf != QSE_NULL);
QSE_ASSERT (re->orgqpath.ptr != QSE_NULL);
re->flags |= QSE_HTRE_QPATH_PERDEC;
}
return 0;
}

540
lib/http/http.c Normal file
View File

@ -0,0 +1,540 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 <qse/http/http.h>
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/htb.h>
#include "../cmn/mem-prv.h"
int qse_comparehttpversions (
const qse_http_version_t* v1,
const qse_http_version_t* v2)
{
if (v1->major == v2->major) return v1->minor - v2->minor;
return v1->major - v2->major;
}
const qse_mchar_t* qse_httpstatustombs (int code)
{
const qse_mchar_t* msg;
switch (code)
{
case 100: msg = QSE_MT("Continue"); break;
case 101: msg = QSE_MT("Switching Protocols"); break;
case 200: msg = QSE_MT("OK"); break;
case 201: msg = QSE_MT("Created"); break;
case 202: msg = QSE_MT("Accepted"); break;
case 203: msg = QSE_MT("Non-Authoritative Information"); break;
case 204: msg = QSE_MT("No Content"); break;
case 205: msg = QSE_MT("Reset Content"); break;
case 206: msg = QSE_MT("Partial Content"); break;
case 300: msg = QSE_MT("Multiple Choices"); break;
case 301: msg = QSE_MT("Moved Permanently"); break;
case 302: msg = QSE_MT("Found"); break;
case 303: msg = QSE_MT("See Other"); break;
case 304: msg = QSE_MT("Not Modified"); break;
case 305: msg = QSE_MT("Use Proxy"); break;
case 307: msg = QSE_MT("Temporary Redirect"); break;
case 308: msg = QSE_MT("Permanent Redirect"); break;
case 400: msg = QSE_MT("Bad Request"); break;
case 401: msg = QSE_MT("Unauthorized"); break;
case 402: msg = QSE_MT("Payment Required"); break;
case 403: msg = QSE_MT("Forbidden"); break;
case 404: msg = QSE_MT("Not Found"); break;
case 405: msg = QSE_MT("Method Not Allowed"); break;
case 406: msg = QSE_MT("Not Acceptable"); break;
case 407: msg = QSE_MT("Proxy Authentication Required"); break;
case 408: msg = QSE_MT("Request Timeout"); break;
case 409: msg = QSE_MT("Conflict"); break;
case 410: msg = QSE_MT("Gone"); break;
case 411: msg = QSE_MT("Length Required"); break;
case 412: msg = QSE_MT("Precondition Failed"); break;
case 413: msg = QSE_MT("Request Entity Too Large"); break;
case 414: msg = QSE_MT("Request-URI Too Long"); break;
case 415: msg = QSE_MT("Unsupported Media Type"); break;
case 416: msg = QSE_MT("Requested Range Not Satisfiable"); break;
case 417: msg = QSE_MT("Expectation Failed"); break;
case 426: msg = QSE_MT("Upgrade Required"); break;
case 428: msg = QSE_MT("Precondition Required"); break;
case 429: msg = QSE_MT("Too Many Requests"); break;
case 431: msg = QSE_MT("Request Header Fields Too Large"); break;
case 500: msg = QSE_MT("Internal Server Error"); break;
case 501: msg = QSE_MT("Not Implemented"); break;
case 502: msg = QSE_MT("Bad Gateway"); break;
case 503: msg = QSE_MT("Service Unavailable"); break;
case 504: msg = QSE_MT("Gateway Timeout"); break;
case 505: msg = QSE_MT("HTTP Version Not Supported"); break;
default: msg = QSE_MT("Unknown Error"); break;
}
return msg;
}
const qse_mchar_t* qse_httpmethodtombs (qse_http_method_t type)
{
/* keep this table in the same order as qse_httpd_method_t enumerators */
static qse_mchar_t* names[] =
{
QSE_MT("OTHER"),
QSE_MT("HEAD"),
QSE_MT("GET"),
QSE_MT("POST"),
QSE_MT("PUT"),
QSE_MT("DELETE"),
QSE_MT("OPTIONS"),
QSE_MT("TRACE"),
QSE_MT("CONNECT")
};
return (type < 0 || type >= QSE_COUNTOF(names))? QSE_NULL: names[type];
}
struct mtab_t
{
const qse_mchar_t* name;
qse_http_method_t type;
};
static struct mtab_t mtab[] =
{
/* keep this table sorted by name for binary search */
{ QSE_MT("CONNECT"), QSE_HTTP_CONNECT },
{ QSE_MT("DELETE"), QSE_HTTP_DELETE },
{ QSE_MT("GET"), QSE_HTTP_GET },
{ QSE_MT("HEAD"), QSE_HTTP_HEAD },
{ QSE_MT("OPTIONS"), QSE_HTTP_OPTIONS },
{ QSE_MT("POST"), QSE_HTTP_POST },
{ QSE_MT("PUT"), QSE_HTTP_PUT },
{ QSE_MT("TRACE"), QSE_HTTP_TRACE }
};
qse_http_method_t qse_mbstohttpmethod (const qse_mchar_t* name)
{
/* perform binary search */
/* declaring left, right, mid to be of int is ok
* because we know mtab is small enough. */
int left = 0, right = QSE_COUNTOF(mtab) - 1, mid;
while (left <= right)
{
int n;
struct mtab_t* entry;
/*mid = (left + right) / 2;*/
mid = left + (right - left) / 2;
entry = &mtab[mid];
n = qse_mbscmp (name, entry->name);
if (n < 0)
{
/* if left, right, mid were of qse_size_t,
* you would need the following line.
if (mid == 0) break;
*/
right = mid - 1;
}
else if (n > 0) left = mid + 1;
else return entry->type;
}
return QSE_HTTP_OTHER;
}
qse_http_method_t qse_mcstrtohttpmethod (const qse_mcstr_t* name)
{
/* perform binary search */
/* declaring left, right, mid to be of int is ok
* because we know mtab is small enough. */
int left = 0, right = QSE_COUNTOF(mtab) - 1, mid;
while (left <= right)
{
int n;
struct mtab_t* entry;
/*mid = (left + right) / 2;*/
mid = left + (right - left) / 2;
entry = &mtab[mid];
n = qse_mbsxcmp (name->ptr, name->len, entry->name);
if (n < 0)
{
/* if left, right, mid were of qse_size_t,
* you would need the following line.
if (mid == 0) break;
*/
right = mid - 1;
}
else if (n > 0) left = mid + 1;
else return entry->type;
}
return QSE_HTTP_OTHER;
}
int qse_parsehttprange (const qse_mchar_t* str, qse_http_range_t* range)
{
/* NOTE: this function does not support a range set
* like bytes=1-20,30-50 */
qse_http_range_int_t from, to;
int type = QSE_HTTP_RANGE_PROPER;
if (str[0] != QSE_MT('b') ||
str[1] != QSE_MT('y') ||
str[2] != QSE_MT('t') ||
str[3] != QSE_MT('e') ||
str[4] != QSE_MT('s') ||
str[5] != QSE_MT('=')) return -1;
str += 6;
from = 0;
if (QSE_ISMDIGIT(*str))
{
do
{
from = from * 10 + (*str - QSE_MT('0'));
str++;
}
while (QSE_ISMDIGIT(*str));
}
else type = QSE_HTTP_RANGE_SUFFIX;
if (*str != QSE_MT('-')) return -1;
str++;
if (QSE_ISMDIGIT(*str))
{
to = 0;
do
{
to = to * 10 + (*str - QSE_MT('0'));
str++;
}
while (QSE_ISMDIGIT(*str));
}
else to = QSE_TYPE_MAX(qse_http_range_int_t);
if (from > to) return -1;
range->type = type;
range->from = from;
range->to = to;
return 0;
}
typedef struct mname_t mname_t;
struct mname_t
{
const qse_mchar_t* s;
const qse_mchar_t* l;
};
static mname_t wday_name[] =
{
{ QSE_MT("Sun"), QSE_MT("Sunday") },
{ QSE_MT("Mon"), QSE_MT("Monday") },
{ QSE_MT("Tue"), QSE_MT("Tuesday") },
{ QSE_MT("Wed"), QSE_MT("Wednesday") },
{ QSE_MT("Thu"), QSE_MT("Thursday") },
{ QSE_MT("Fri"), QSE_MT("Friday") },
{ QSE_MT("Sat"), QSE_MT("Saturday") }
};
static mname_t mon_name[] =
{
{ QSE_MT("Jan"), QSE_MT("January") },
{ QSE_MT("Feb"), QSE_MT("February") },
{ QSE_MT("Mar"), QSE_MT("March") },
{ QSE_MT("Apr"), QSE_MT("April") },
{ QSE_MT("May"), QSE_MT("May") },
{ QSE_MT("Jun"), QSE_MT("June") },
{ QSE_MT("Jul"), QSE_MT("July") },
{ QSE_MT("Aug"), QSE_MT("August") },
{ QSE_MT("Sep"), QSE_MT("September") },
{ QSE_MT("Oct"), QSE_MT("October") },
{ QSE_MT("Nov"), QSE_MT("November") },
{ QSE_MT("Dec"), QSE_MT("December") }
};
int qse_parsehttptime (const qse_mchar_t* str, qse_ntime_t* nt)
{
qse_btime_t bt;
const qse_mchar_t* word;
qse_size_t wlen, i;
/* TODO: support more formats */
QSE_MEMSET (&bt, 0, QSE_SIZEOF(bt));
/* weekday */
while (QSE_ISMSPACE(*str)) str++;
for (word = str; QSE_ISMALPHA(*str); str++);
wlen = str - word;
for (i = 0; i < QSE_COUNTOF(wday_name); i++)
{
if (qse_mbsxcmp (word, wlen, wday_name[i].s) == 0)
{
bt.wday = i;
break;
}
}
if (i >= QSE_COUNTOF(wday_name)) return -1;
/* comma - i'm just loose as i don't care if it doesn't exist */
while (QSE_ISMSPACE(*str)) str++;
if (*str == QSE_MT(',')) str++;
/* day */
while (QSE_ISMSPACE(*str)) str++;
if (!QSE_ISMDIGIT(*str)) return -1;
do bt.mday = bt.mday * 10 + *str++ - QSE_MT('0'); while (QSE_ISMDIGIT(*str));
/* month */
while (QSE_ISMSPACE(*str)) str++;
for (word = str; QSE_ISMALPHA(*str); str++);
wlen = str - word;
for (i = 0; i < QSE_COUNTOF(mon_name); i++)
{
if (qse_mbsxcmp (word, wlen, mon_name[i].s) == 0)
{
bt.mon = i;
break;
}
}
if (i >= QSE_COUNTOF(mon_name)) return -1;
/* year */
while (QSE_ISMSPACE(*str)) str++;
if (!QSE_ISMDIGIT(*str)) return -1;
do bt.year = bt.year * 10 + *str++ - QSE_MT('0'); while (QSE_ISMDIGIT(*str));
bt.year -= QSE_BTIME_YEAR_BASE;
/* hour */
while (QSE_ISMSPACE(*str)) str++;
if (!QSE_ISMDIGIT(*str)) return -1;
do bt.hour = bt.hour * 10 + *str++ - QSE_MT('0'); while (QSE_ISMDIGIT(*str));
if (*str != QSE_MT(':')) return -1;
str++;
/* min */
while (QSE_ISMSPACE(*str)) str++;
if (!QSE_ISMDIGIT(*str)) return -1;
do bt.min = bt.min * 10 + *str++ - QSE_MT('0'); while (QSE_ISMDIGIT(*str));
if (*str != QSE_MT(':')) return -1;
str++;
/* sec */
while (QSE_ISMSPACE(*str)) str++;
if (!QSE_ISMDIGIT(*str)) return -1;
do bt.sec = bt.sec * 10 + *str++ - QSE_MT('0'); while (QSE_ISMDIGIT(*str));
/* GMT */
while (QSE_ISMSPACE(*str)) str++;
for (word = str; QSE_ISMALPHA(*str); str++);
wlen = str - word;
if (qse_mbsxcmp (word, wlen, QSE_MT("GMT")) != 0) return -1;
while (QSE_ISMSPACE(*str)) str++;
if (*str != QSE_MT('\0')) return -1;
return qse_timegm (&bt, nt);
}
qse_mchar_t* qse_fmthttptime (
const qse_ntime_t* nt, qse_mchar_t* buf, qse_size_t bufsz)
{
qse_btime_t bt;
qse_gmtime (nt, &bt);
qse_mbsxfmt (
buf, bufsz,
QSE_MT("%s, %d %s %d %02d:%02d:%02d GMT"),
wday_name[bt.wday].s,
bt.mday,
mon_name[bt.mon].s,
bt.year + QSE_BTIME_YEAR_BASE,
bt.hour, bt.min, bt.sec
);
return buf;
}
int qse_isperencedhttpstr (const qse_mchar_t* str)
{
const qse_mchar_t* p = str;
while (*p != QSE_MT('\0'))
{
if (*p == QSE_MT('%') && *(p + 1) != QSE_MT('\0') && *(p + 2) != QSE_MT('\0'))
{
int q = QSE_MXDIGITTONUM (*(p + 1));
if (q >= 0)
{
/* return true if the first valid percent-encoded sequence is found */
int w = QSE_MXDIGITTONUM (*(p + 2));
if (w >= 0) return 1;
}
}
p++;
}
return 1;
}
qse_size_t qse_perdechttpstr (const qse_mchar_t* str, qse_mchar_t* buf, qse_size_t* ndecs)
{
const qse_mchar_t* p = str;
qse_mchar_t* out = buf;
qse_size_t dec_count = 0;
while (*p != QSE_MT('\0'))
{
if (*p == QSE_MT('%') && *(p + 1) != QSE_MT('\0') && *(p + 2) != QSE_MT('\0'))
{
int q = QSE_MXDIGITTONUM (*(p + 1));
if (q >= 0)
{
int w = QSE_MXDIGITTONUM (*(p + 2));
if (w >= 0)
{
/* we don't care if it contains a null character */
*out++ = ((q << 4) + w);
p += 3;
dec_count++;
continue;
}
}
}
*out++ = *p++;
}
*out = QSE_MT('\0');
if (ndecs) *ndecs = dec_count;
return out - buf;
}
#define IS_UNRESERVED(c) \
(((c) >= QSE_MT('A') && (c) <= QSE_MT('Z')) || \
((c) >= QSE_MT('a') && (c) <= QSE_MT('z')) || \
((c) >= QSE_MT('0') && (c) <= QSE_MT('9')) || \
(c) == QSE_MT('-') || (c) == QSE_MT('_') || \
(c) == QSE_MT('.') || (c) == QSE_MT('~'))
#define TO_HEX(v) (QSE_MT("0123456789ABCDEF")[(v) & 15])
qse_size_t qse_perenchttpstr (int opt, const qse_mchar_t* str, qse_mchar_t* buf, qse_size_t* nencs)
{
const qse_mchar_t* p = str;
qse_mchar_t* out = buf;
qse_size_t enc_count = 0;
/* this function doesn't accept the size of the buffer. the caller must
* ensure that the buffer is large enough */
if (opt & QSE_PERENCHTTPSTR_KEEP_SLASH)
{
while (*p != QSE_MT('\0'))
{
if (IS_UNRESERVED(*p) || *p == QSE_MT('/')) *out++ = *p;
else
{
*out++ = QSE_MT('%');
*out++ = TO_HEX (*p >> 4);
*out++ = TO_HEX (*p & 15);
enc_count++;
}
p++;
}
}
else
{
while (*p != QSE_MT('\0'))
{
if (IS_UNRESERVED(*p)) *out++ = *p;
else
{
*out++ = QSE_MT('%');
*out++ = TO_HEX (*p >> 4);
*out++ = TO_HEX (*p & 15);
enc_count++;
}
p++;
}
}
*out = QSE_MT('\0');
if (nencs) *nencs = enc_count;
return out - buf;
}
qse_mchar_t* qse_perenchttpstrdup (int opt, const qse_mchar_t* str, qse_mmgr_t* mmgr)
{
qse_mchar_t* buf;
qse_size_t len = 0;
qse_size_t count = 0;
/* count the number of characters that should be encoded */
if (opt & QSE_PERENCHTTPSTR_KEEP_SLASH)
{
for (len = 0; str[len] != QSE_MT('\0'); len++)
{
if (!IS_UNRESERVED(str[len]) && str[len] != QSE_MT('/')) count++;
}
}
else
{
for (len = 0; str[len] != QSE_MT('\0'); len++)
{
if (!IS_UNRESERVED(str[len])) count++;
}
}
/* if there are no characters to escape, just return the original string */
if (count <= 0) return (qse_mchar_t*)str;
/* allocate a buffer of an optimal size for escaping, otherwise */
buf = QSE_MMGR_ALLOC (mmgr, (len + (count * 2) + 1) * QSE_SIZEOF(*buf));
if (buf == QSE_NULL) return QSE_NULL;
/* perform actual escaping */
qse_perenchttpstr (opt, str, buf, QSE_NULL);
return buf;
}

1683
lib/http/httpd-cgi.c Normal file

File diff suppressed because it is too large Load Diff

689
lib/http/httpd-dir.c Normal file
View File

@ -0,0 +1,689 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 "httpd.h"
#include "../cmn/mem-prv.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
typedef struct task_dir_t task_dir_t;
struct task_dir_t
{
qse_mcstr_t path;
qse_mcstr_t qpath;
qse_mcstr_t head;
qse_mcstr_t foot;
qse_http_version_t version;
int keepalive;
int method;
qse_httpd_hnd_t handle;
};
typedef struct task_dseg_t task_dseg_t;
struct task_dseg_t
{
qse_http_version_t version;
int keepalive;
int chunked;
qse_mcstr_t path;
qse_mcstr_t qpath;
qse_mcstr_t head;
qse_mcstr_t foot;
qse_httpd_hnd_t handle;
qse_httpd_dirent_t dent;
#define HEADER_ADDED (1 << 0)
#define FOOTER_ADDED (1 << 1)
#define FOOTER_PENDING (1 << 2)
#define DIRENT_PENDING (1 << 3)
#define DIRENT_NOMORE (1 << 4)
int state;
qse_size_t tcount; /* total directory entries */
qse_size_t dcount; /* the number of items in the buffer */
qse_mchar_t buf[4096*2]; /* is this large enough? */
int bufpos;
int buflen;
int bufrem;
qse_size_t chunklen;
};
static int task_init_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
task_dseg_t* arg = (task_dseg_t*)task->ctx;
QSE_MEMCPY (xtn, arg, QSE_SIZEOF(*xtn));
xtn->path.ptr = (qse_mchar_t*)(xtn + 1);
qse_mbscpy ((qse_mchar_t*)xtn->path.ptr, arg->path.ptr);
xtn->qpath.ptr = xtn->path.ptr + xtn->path.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->qpath.ptr, arg->qpath.ptr);
xtn->head.ptr = xtn->qpath.ptr + xtn->qpath.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->head.ptr, arg->head.ptr);
xtn->foot.ptr = xtn->head.ptr + xtn->head.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->foot.ptr, arg->foot.ptr);
task->ctx = xtn;
return 0;
}
static void task_fini_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
httpd->opt.scb.dir.close (httpd, ctx->handle);
}
#define SIZE_CHLEN 4 /* the space size to hold the hexadecimal chunk length */
#define SIZE_CHLENCRLF 2 /* the space size to hold CRLF after the chunk length */
#define SIZE_CHENDCRLF 2 /* the sapce size to hold CRLF after the chunk data */
static QSE_INLINE void close_chunk_data (task_dseg_t* ctx, qse_size_t len)
{
ctx->chunklen = len;
/* CHENDCRLF - there is always space for these two.
* reserved with SIZE_CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
static void fill_chunk_length (task_dseg_t* ctx)
{
int x;
/* right alignment with space padding on the left */
x = qse_fmtuintmaxtombs (
ctx->buf, (SIZE_CHLEN + SIZE_CHLENCRLF) - 1,
(ctx->chunklen - (SIZE_CHLEN + SIZE_CHLENCRLF)), /* value to convert */
16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, /* uppercase hexadecimal letters */
-1, /* no particular precision - want space filled if less than 4 digits */
QSE_MT(' '), /* fill with space */
QSE_NULL
);
/* i don't check the buffer size error because i've secured the
* suffient space for the chunk length at the beginning of the buffer */
/* CHLENCRLF */
ctx->buf[x] = QSE_MT('\r');
ctx->buf[x+1] = QSE_MT('\n');
/* skip leading space padding */
QSE_ASSERT (ctx->bufpos == 0);
while (ctx->buf[ctx->bufpos] == QSE_MT(' ')) ctx->bufpos++;
}
static qse_size_t format_dirent (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_httpd_dirent_t* dirent,
qse_mchar_t* buf, int bufsz)
{
qse_size_t x;
qse_mchar_t* encname;
qse_mchar_t* escname;
qse_btime_t bt;
qse_mchar_t tmbuf[32];
qse_mchar_t fszbuf[64];
/* TODO: better buffer management in case there are
* a lot of file names to escape. */
/* perform percent-encoding for the anchor */
encname = qse_perenchttpstrdup (0, dirent->name, qse_httpd_getmmgr(httpd));
if (encname == QSE_NULL)
{
httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
/* perform html escaping for the text */
escname = qse_httpd_escapehtml (httpd, dirent->name);
if (escname == QSE_NULL)
{
if (encname != dirent->name) QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), encname);
return -1;
}
qse_localtime (&dirent->stat.mtime, &bt);
qse_mbsxfmt (tmbuf, QSE_COUNTOF(tmbuf),
QSE_MT("%04d-%02d-%02d %02d:%02d:%02d"),
bt.year + QSE_BTIME_YEAR_BASE, bt.mon + 1, bt.mday,
bt.hour, bt.min, bt.sec);
if (dirent->stat.isdir)
{
fszbuf[0] = QSE_MT('\0');
}
else
{
qse_fmtuintmaxtombs (
fszbuf, QSE_COUNTOF(fszbuf),
dirent->stat.size, 10, -1, QSE_MT('\0'), QSE_NULL
);
}
x = qse_mbsxfmts (
buf, bufsz,
QSE_MT("<tr><td class='name'><a href='%s%s' class='%s'>%s%s</a></td><td class='time'>%s</td><td class='size'>%s</td></tr>\n"),
encname,
(dirent->stat.isdir? QSE_MT("/"): QSE_MT("")),
(dirent->stat.isdir? QSE_MT("dir"): QSE_MT("file")),
escname,
(dirent->stat.isdir? QSE_MT("/"): QSE_MT("")),
tmbuf, fszbuf
);
if (escname != dirent->name) qse_httpd_freemem (httpd, escname);
if (encname != dirent->name) QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), encname);
if (x == (qse_size_t)-1) httpd->errnum = QSE_HTTPD_ENOBUF;
return x;
}
static int add_footer (qse_httpd_t* httpd, qse_httpd_client_t* client, task_dseg_t* ctx)
{
qse_size_t x;
int rem;
rem = ctx->chunked? (ctx->bufrem - 5): ctx->bufrem;
if (rem < 1)
{
httpd->errnum = QSE_HTTPD_ENOBUF;
return -1;
}
x = qse_mbsxfmts (&ctx->buf[ctx->buflen], rem, QSE_MT("</table></div>\n<div class='footer'>%s</div>\n</body></html>"), ctx->foot.ptr);
if (x == (qse_size_t)-1)
{
httpd->errnum = QSE_HTTPD_ENOBUF;
return -1;
}
QSE_ASSERT (x < rem);
ctx->buflen += x;
ctx->bufrem -= x;
if (ctx->chunked)
{
qse_mbscpy (&ctx->buf[ctx->buflen], QSE_MT("\r\n0\r\n"));
ctx->buflen += 5;
ctx->bufrem -= 5;
/* -5 for \r\n0\r\n added above */
if (ctx->chunked) close_chunk_data (ctx, ctx->buflen - 5);
}
return 0;
}
static int task_main_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
qse_ssize_t n;
qse_size_t x;
if (ctx->bufpos < ctx->buflen)
{
/* buffer contents not fully sent yet */
goto send_dirlist;
}
/* the buffer size is fixed to QSE_COUNTOF(ctx->buf).
*
* the number of digits need to hold the the size converted to
* a hexadecimal notation is roughly (log16(QSE_COUNTOF(ctx->buf) + 1).
* it should be safter to use ceil(log16(QSE_COUNTOF(ctx->buf)) + 1
* for precision issues.
*
* 16**X = QSE_COUNTOF(ctx->buf).
* X = log16(QSE_COUNTOF(ctx->buf).
* X + 1 is a required number of digits.
*
* Since log16 is not provided, we should use a natural log function
* whose base is the constant e (2.718).
*
* log16(n) = log(n) / log(16)
*
* The final fomula is here.
*
* X = ceil((log(QSE_COUNTOF(ctx->buf)) / log(16))) + 1;
*
* However, i won't use these floating-point opertions.
* instead i'll reserve a hardcoded size. so when you change
* the size of the buffer arrray, you should check this size.
*/
/* initialize buffer */
ctx->dcount = 0; /* reset the entry counter */
ctx->bufpos = 0;
if (ctx->chunked)
{
/* reserve space to fill with the chunk length
* 4 for the actual chunk length and +2 for \r\n */
ctx->buflen = SIZE_CHLEN + SIZE_CHLENCRLF;
/* free space remaing in the buffer for the chunk data */
ctx->bufrem = QSE_COUNTOF(ctx->buf) - ctx->buflen - SIZE_CHENDCRLF;
}
else
{
ctx->buflen = 0;
ctx->bufrem = QSE_COUNTOF(ctx->buf);
}
if (ctx->state & FOOTER_PENDING)
{
/* only footers yet to be sent */
if (add_footer (httpd, client, ctx) <= -1)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increase the buffer size */
return -1;
}
ctx->state &= ~FOOTER_PENDING;
ctx->state |= FOOTER_ADDED;
if (ctx->chunked) fill_chunk_length (ctx);
goto send_dirlist;
}
if (!(ctx->state & HEADER_ADDED))
{
/* compose the header since this is the first time. */
int is_root;
qse_mchar_t* qpath_esc;
is_root = (qse_mbscmp (ctx->qpath.ptr, QSE_MT("/")) == 0);
qpath_esc = qse_httpd_escapehtml (httpd, ctx->qpath.ptr);
if (qpath_esc == QSE_NULL) return -1;
/* TODO: page encoding?? utf-8??? or derive name from cmgr or current locale??? */
/* TODO: SUPPORT character set. DON't HARD_CODE it to UTF8 */
x = qse_mbsxfmts (&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset='UTF-8'\">\n<head>%s</head>\n<body>\n<div class='header'>%s</div>\n<div class='body'><table>%s"),
ctx->head.ptr, qpath_esc,
(is_root? QSE_MT(""): QSE_MT("<tr><td class='name'><a href='../' class='dir'>..</a></td><td class='time'></td><td class='size'></td></tr>\n"))
);
if (qpath_esc != ctx->qpath.ptr) qse_httpd_freemem (httpd, qpath_esc);
if (x == (qse_size_t)-1)
{
/* return an error if the buffer is too small to
* hold the header(httpd->errnum == QSE_HTTPD_ENOBUF).
* i need to increase the buffer size. or i have make
* the buffer dynamic. */
httpd->errnum = QSE_HTTPD_ENOBUF;
return -1;
}
QSE_ASSERT (x < ctx->bufrem);
ctx->buflen += x;
ctx->bufrem -= x;
ctx->state |= HEADER_ADDED;
ctx->dcount++;
}
if (ctx->state & DIRENT_PENDING)
{
ctx->state &= ~DIRENT_PENDING;
}
else
{
if (httpd->opt.scb.dir.read (httpd, ctx->handle, &ctx->dent) <= 0)
ctx->state |= DIRENT_NOMORE;
}
do
{
if (ctx->state & DIRENT_NOMORE)
{
/* no more directory entry */
if (add_footer (httpd, client, ctx) <= -1)
{
/* failed to add the footer part */
if (ctx->chunked)
{
close_chunk_data (ctx, ctx->buflen);
fill_chunk_length (ctx);
}
ctx->state |= FOOTER_PENDING;
}
else if (ctx->chunked) fill_chunk_length (ctx);
break;
}
if (qse_mbscmp (ctx->dent.name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent.name, QSE_MT("..")) != 0)
{
x = format_dirent (httpd, client, &ctx->dent, &ctx->buf[ctx->buflen], ctx->bufrem);
if (x == (qse_size_t)-1)
{
/* buffer not large enough to hold this entry */
if (ctx->dcount <= 0)
{
/* neither directory entry nor the header
* has been added to the buffer so far. and
* this attempt has failed. the buffer size must
* be too small. you must increase it */
httpd->errnum = QSE_HTTPD_EINTERN;
return -1;
}
if (ctx->chunked)
{
close_chunk_data (ctx, ctx->buflen);
fill_chunk_length (ctx);
}
ctx->state |= DIRENT_PENDING;
break;
}
else
{
QSE_ASSERT (x < ctx->bufrem);
ctx->buflen += x;
ctx->bufrem -= x;
ctx->dcount++;
ctx->tcount++;
}
}
if (httpd->opt.scb.dir.read (httpd, ctx->handle, &ctx->dent) <= 0)
ctx->state |= DIRENT_NOMORE;
}
while (1);
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen - ctx->bufpos);
if (n <= -1) return -1;
/* NOTE if (n == 0), it will enter an infinite loop */
ctx->bufpos += n;
return (ctx->bufpos < ctx->buflen || (ctx->state & FOOTER_PENDING) || !(ctx->state & DIRENT_NOMORE))? 1: 0;
}
static qse_httpd_task_t* entask_directory_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_httpd_hnd_t handle, task_dir_t* dir)
{
qse_httpd_task_t task;
task_dseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.version = dir->version;
data.keepalive = dir->keepalive;
data.chunked = dir->keepalive;
data.path = dir->path;
data.qpath = dir->qpath;
data.head = dir->head;
data.foot = dir->foot;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dseg;
task.main = task_main_dseg;
task.fini = task_fini_dseg;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.path.len + 1 + data.qpath.len + 1 + data.head.len + 1 + data.foot.len + 1);
}
/*------------------------------------------------------------------------*/
static int task_init_getdir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
task_dir_t* arg = (task_dir_t*)task->ctx;
/* deep-copy the context data to the extension area */
QSE_MEMCPY (xtn, arg, QSE_SIZEOF(*xtn));
xtn->path.ptr = (qse_mchar_t*)(xtn + 1);
qse_mbscpy ((qse_mchar_t*)xtn->path.ptr, arg->path.ptr);
xtn->qpath.ptr = xtn->path.ptr + xtn->path.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->qpath.ptr, arg->qpath.ptr);
xtn->head.ptr = xtn->qpath.ptr + xtn->qpath.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->head.ptr, arg->head.ptr);
xtn->foot.ptr = xtn->head.ptr + xtn->head.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->foot.ptr, arg->foot.ptr);
/* switch the context to the extension area */
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_getdir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* dir;
qse_httpd_task_t* x;
dir = (task_dir_t*)task->ctx;
x = task;
/* arrange to return the header part first */
if (dir->method == QSE_HTTP_HEAD)
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n"),
dir->version.major, dir->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close"))
);
httpd->opt.scb.dir.close (httpd, dir->handle);
return (x == QSE_NULL)? -1: 0;
}
else
{
int keepalive_ignored = 0;
if (dir->keepalive && qse_comparehttpversions (&dir->version, &qse_http_v11) < 0)
{
/* this task does chunking when keepalive is set.
* chunking is not supported for http 1.0 or earlier.
* force switch to the close mode */
dir->keepalive = 0;
keepalive_ignored = 1;
}
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\n%s\r\n"),
dir->version.major, dir->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT(""))
);
if (x)
{
#if 0
if (httpd->opt.trait & QSE_HTTPD_LOGACC)
{
qse_httpd_reqsum_t reqsum;
acc.remote = remote;
acc.qpath = qpath;
acc.status = 200;
acc.version = ...;
acc.method = ...;
httpd->opt.rcb.logacc (httpd, &reqsum);
}
#endif
/* arrange to send the actual directory contents */
x = entask_directory_segment (httpd, client, x, dir->handle, dir);
if (keepalive_ignored && x)
x = qse_httpd_entaskdisconnect (httpd, client, x);
if (x) return 0;
}
httpd->opt.scb.dir.close (httpd, dir->handle);
return -1;
}
}
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_httpd_rsrc_dir_t* dir,
qse_htre_t* req)
{
int method;
method = qse_htre_getqmethodtype(req);
/* i don't need contents for directories */
qse_htre_discardcontent (req);
switch (method)
{
case QSE_HTTP_OPTIONS:
return qse_httpd_entaskallow (httpd, client, pred,
QSE_MT("OPTIONS,GET,HEAD,POST,PUT,DELETE"), req);
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
{
task_dir_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
/* check if the directory stream can be opened before
* creating an actual task. */
if (httpd->opt.scb.dir.open (httpd, dir->path, &data.handle) <= -1)
{
/* arrange a status code to return */
int status;
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
return qse_httpd_entaskerror (httpd, client, pred, status, req);
}
else
{
/* create a directory listing task */
qse_httpd_task_t task, * x;
data.path.ptr = (qse_mchar_t*)dir->path;
data.path.len = qse_mbslen(data.path.ptr);
data.qpath.ptr = qse_htre_getqpath(req);
data.qpath.len = qse_mbslen(data.qpath.ptr);
data.head.ptr = dir->head? (qse_mchar_t*)dir->head: QSE_MT("<style type='text/css'>body { background-color:#d0e4fe; font-size: 0.9em; } div.header { font-weight: bold; margin-bottom: 5px; } div.footer { border-top: 1px solid #99AABB; text-align: right; } table { font-size: 0.9em; } td { white-space: nowrap; } td.size { text-align: right; }</style>");
data.head.len = qse_mbslen(data.head.ptr);
data.foot.ptr = dir->foot? dir->foot: qse_httpd_getname(httpd);
data.foot.len = qse_mbslen(data.foot.ptr);
data.version = *qse_htre_getversion(req);
data.keepalive = req->flags & QSE_HTRE_ATTR_KEEPALIVE;
data.method = method;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_getdir;
task.main = task_main_getdir;
task.ctx = &data;
x = qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_dir_t) + data.path.len + 1 + data.qpath.len + 1 +
data.head.len + 1 + data.foot.len + 1);
if (x == QSE_NULL)
{
httpd->opt.scb.dir.close (httpd, data.handle);
}
return x;
}
}
case QSE_HTTP_PUT:
{
int status = 201; /* 201 Created */
if (httpd->opt.scb.dir.make (httpd, dir->path) <= -1)
{
if (httpd->errnum == QSE_HTTPD_EEXIST)
{
/* an entry with the same name exists.
* if it is a directory, it's considered ok.
* if not, send '403 forbidden' indicating you can't
* change a file to a directory */
qse_httpd_stat_t st;
status = (httpd->opt.scb.dir.stat (httpd, dir->path, &st) <= -1)? 403: 204;
}
else
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
}
return qse_httpd_entaskerror (httpd, client, pred, status, req);
}
case QSE_HTTP_DELETE:
{
int status = 200;
if (httpd->opt.scb.dir.purge (httpd, dir->path) <= -1)
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
return qse_httpd_entaskerror (httpd, client, pred, status, req);
}
default:
/* Method not allowed */
return qse_httpd_entaskerror (httpd, client, pred, 405, req);
}
}

656
lib/http/httpd-file.c Normal file
View File

@ -0,0 +1,656 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 "httpd.h"
#include "../cmn/mem-prv.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#define ETAG_LEN_MAX 127
#define PUTFILE_INIT_FAILED (1 << 0)
#define PUTFILE_WRITE_FAILED (1 << 1)
typedef struct task_file_t task_file_t;
struct task_file_t
{
qse_mcstr_t path;
qse_http_version_t version;
int keepalive;
int method;
union
{
struct
{
qse_mcstr_t mime;
qse_mchar_t if_none_match[ETAG_LEN_MAX + 1];
qse_ntime_t if_modified_since;
qse_http_range_t range;
} get;
struct
{
qse_httpd_t* httpd;
int flags;
int status;
qse_htre_t* req;
qse_httpd_hnd_t handle;
} put;
} u;
};
/*------------------------------------------------------------------------*/
typedef struct task_getfseg_t task_getfseg_t;
struct task_getfseg_t
{
qse_httpd_hnd_t handle;
qse_foff_t left;
qse_foff_t offset;
};
static int task_init_getfseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_getfseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
task->ctx = xtn;
return 0;
}
static void task_fini_getfseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_getfseg_t* ctx = (task_getfseg_t*)task->ctx;
httpd->opt.scb.file.close (httpd, ctx->handle);
}
static int task_main_getfseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_getfseg_t* ctx = (task_getfseg_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
/* HANDLE EGAIN specially??? */
if (httpd->errnum != QSE_HTTPD_EAGAIN)
{
/* TODO: logging */
return -1;
}
goto more_work;
}
if (n == 0 && count > 0)
{
/* The file could be truncated when this condition is set.
* The content-length sent in the header can't be fulfilled.
* So let's return an error here so that the main loop abort
* the connection. */
/* TODO: any logging....??? */
return -1;
}
ctx->left -= n;
if (ctx->left <= 0) return 0;
more_work:
return 1; /* more work to do */
}
static qse_httpd_task_t* entask_getfseg (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_httpd_hnd_t handle, qse_foff_t offset, qse_foff_t size)
{
qse_httpd_task_t task;
task_getfseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.offset = offset;
data.left = size;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_getfseg;
task.main = task_main_getfseg;
task.fini = task_fini_getfseg;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
}
/*------------------------------------------------------------------------*/
static int task_init_getfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = qse_httpd_gettaskxtn (httpd, task);
task_file_t* arg = (task_file_t*)task->ctx;
QSE_MEMCPY (file, arg, QSE_SIZEOF(*file));
file->path.ptr = (qse_mchar_t*)(file + 1);
qse_mbscpy ((qse_mchar_t*)file->path.ptr, arg->path.ptr);
if (arg->u.get.mime.ptr)
{
file->u.get.mime.ptr = file->path.ptr + file->path.len + 1;
qse_mbscpy ((qse_mchar_t*)file->u.get.mime.ptr, arg->u.get.mime.ptr);
}
task->ctx = file;
return 0;
}
static QSE_INLINE int task_main_getfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file;
qse_httpd_task_t* x;
qse_httpd_hnd_t handle;
int fileopen = 0;
qse_httpd_stat_t st;
file = (task_file_t*)task->ctx;
x = task;
/* TODO: if you should deal with files on a network-mounted drive,
setting a trigger or non-blocking I/O are needed. */
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.stat (httpd, file->path.ptr, &st) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entaskerrorwithmvk (
httpd, client, x, http_errnum,
file->method, &file->version, file->keepalive);
goto no_file_send;
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.ropen (httpd, file->path.ptr, &handle) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entaskerrorwithmvk (
httpd, client, x, http_errnum,
file->method, &file->version, file->keepalive);
goto no_file_send;
}
fileopen = 1;
if (file->u.get.range.type != QSE_HTTP_RANGE_NONE)
{
qse_mchar_t tmp[4][64];
qse_mchar_t etag[ETAG_LEN_MAX + 1];
qse_size_t etag_len;
if (file->u.get.range.type == QSE_HTTP_RANGE_SUFFIX)
{
if (file->u.get.range.to > st.size) file->u.get.range.to = st.size;
file->u.get.range.from = st.size - file->u.get.range.to;
file->u.get.range.to = file->u.get.range.to + file->u.get.range.from;
if (st.size > 0) file->u.get.range.to--;
}
if (file->u.get.range.from >= st.size)
{
x = qse_httpd_entaskerrorwithmvk (
httpd, client, x, 416, file->method, &file->version, file->keepalive);
goto no_file_send;
}
if (file->u.get.range.to >= st.size) file->u.get.range.to = st.size - 1;
qse_fmtuintmaxtombs (tmp[0], QSE_COUNTOF(tmp[0]), (file->u.get.range.to - file->u.get.range.from + 1), 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[1], QSE_COUNTOF(tmp[1]), file->u.get.range.from, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[2], QSE_COUNTOF(tmp[2]), file->u.get.range.to, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[3], QSE_COUNTOF(tmp[3]), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag), st.mtime.sec, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag), st.mtime.nsec, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.size, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.ino, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL);
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nContent-Range: bytes %s-%s/%s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\nETag: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")),
(file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
((file->method == QSE_HTTP_HEAD)? QSE_MT("0"): tmp[0]),
tmp[1], tmp[2], tmp[3],
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag
);
if (x)
{
if (file->method == QSE_HTTP_HEAD) goto no_file_send;
x = entask_getfseg (
httpd, client, x, handle,
file->u.get.range.from,
(file->u.get.range.to - file->u.get.range.from + 1)
);
}
}
else
{
qse_mchar_t b_fsize[64];
qse_mchar_t etag[ETAG_LEN_MAX + 1];
qse_size_t etag_len;
etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag), st.mtime.sec, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag), st.mtime.nsec, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.size, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.ino, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL);
if ((file->u.get.if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->u.get.if_none_match) == 0) ||
(file->u.get.if_modified_since.sec > 0 && st.mtime.sec <= file->u.get.if_modified_since.sec))
{
/* i've converted milliseconds to seconds before timestamp comparison
* because st.mtime has the actual milliseconds less than 1 second
* while if_modified_since doesn't have such small milliseconds */
x = qse_httpd_entask_nomod (httpd, client, x, file->method, &file->version, file->keepalive);
goto no_file_send;
}
qse_fmtuintmaxtombs (b_fsize, QSE_COUNTOF(b_fsize), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
/* wget 1.8.2 set 'Connection: keep-alive' in the http 1.0 header.
* if the reply doesn't contain 'Connection: keep-alive', it didn't
* close connection.*/
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\nETag: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")),
(file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
((file->method == QSE_HTTP_HEAD)? QSE_MT("0"): b_fsize),
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag
);
if (x)
{
if (file->method == QSE_HTTP_HEAD) goto no_file_send;
x = entask_getfseg (httpd, client, x, handle, 0, st.size);
}
}
if (x) return 0;
httpd->opt.scb.file.close (httpd, handle);
return -1;
no_file_send:
if (fileopen) httpd->opt.scb.file.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
/*------------------------------------------------------------------------*/
static int write_file (qse_httpd_t* httpd, qse_httpd_hnd_t handle, const qse_mchar_t* ptr, qse_size_t len)
{
qse_ssize_t n;
qse_size_t pos = 0;
/* this implementation assumes that file writing will never get
* blocked in practice. so no i/o multiplexing is performed over
* file descriptors */
while (pos < len)
{
n = httpd->opt.scb.file.write (httpd, handle, &ptr[pos], len - pos);
if (n <= 0) return -1;
pos += n;
}
return 0;
}
static int putfile_snatch_client_input (
qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx)
{
qse_httpd_task_t* task;
task_file_t* file;
task = (qse_httpd_task_t*)ctx;
file = (task_file_t*)task->ctx;
if (ptr == QSE_NULL)
{
/*
* this callback is called with ptr of QSE_NULL
* when the request is completed or discarded.
* and this indicates that there's nothing more to read
* from the client side. this can happen when the end of
* a request is seen or when an error occurs
*/
QSE_ASSERT (len == 0);
/* mark the there's nothing to read form the client side */
qse_htre_unsetconcb (file->u.put.req);
file->u.put.req = QSE_NULL;
/* since there is no more to read from the client side.
* the trigger is not needed any more. */
task->trigger.v[0].mask = 0;
}
else if (!(file->u.put.flags & PUTFILE_WRITE_FAILED))
{
if (write_file (file->u.put.httpd, file->u.put.handle, ptr, len) <= -1)
{
file->u.put.flags |= PUTFILE_WRITE_FAILED;
file->u.put.status = 500;
}
}
return 0;
}
static int task_init_putfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = qse_httpd_gettaskxtn (httpd, task);
task_file_t* arg = (task_file_t*)task->ctx;
qse_httpd_stat_t st;
/* zero out the task's extension area */
QSE_MEMCPY (file, arg, QSE_SIZEOF(*file));
file->u.put.req = QSE_NULL;
/* copy in the path name to the area */
file->path.ptr = (qse_mchar_t*)(file + 1);
qse_mbscpy ((qse_mchar_t*)file->path.ptr, arg->path.ptr);
file->u.put.status = 204; /* 200 should also be ok to indicate success. */
task->ctx = file; /* switch the task context to the extension area */
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.stat (httpd, file->path.ptr, &st) <= -1)
{
if (httpd->errnum == QSE_HTTPD_ENOENT)
{
/* stat found no such file. so if the request is achived
* successfully, it should send '201 Created' */
file->u.put.status = 201;
}
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.wopen (httpd, file->path.ptr, &file->u.put.handle) <= -1)
{
file->u.put.status = (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
goto oops;
}
if (write_file (httpd, file->u.put.handle, qse_htre_getcontentptr(arg->u.put.req), qse_htre_getcontentlen(arg->u.put.req)) <= -1)
{
httpd->opt.scb.file.close (httpd, file->u.put.handle);
file->u.put.status = 500;
goto oops;
}
if (!(arg->u.put.req->state & QSE_HTRE_DISCARDED) &&
!(arg->u.put.req->state & QSE_HTRE_COMPLETED))
{
/* set up a callback to be called when the request content
* is fed to the htrd reader. qse_htre_addcontent() that
* htrd calls invokes this callback. */
file->u.put.req = arg->u.put.req;
qse_htre_setconcb (file->u.put.req, putfile_snatch_client_input, task);
}
/* no triggers yet since the main loop doesn't allow me to set
* triggers in the task initializer. however the main task handler
* will be invoked so long as the client handle is writable by
* the main loop. */
return 0;
oops:
/* since a new task can't be added in the initializer,
* i mark that initialization failed and let task_main_putfile()
* add an error task */
qse_htre_discardcontent (arg->u.put.req);
file->u.put.flags |= PUTFILE_INIT_FAILED;
return 0;
}
static void task_fini_putfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = (task_file_t*)task->ctx;
if (!(file->u.put.flags & PUTFILE_INIT_FAILED))
httpd->opt.scb.file.close (httpd, file->u.put.handle);
}
static int task_main_putfile_2 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = (task_file_t*)task->ctx;
if (file->u.put.req)
{
/* file->u.put.req is set to a non-NULL value if snatching
* is needed in the task_init_putfile(). and it's reset to
* QSE_NULL when snatching is over in putfile_snatch_client_input().
* i set a trigger so that the task is executed
* while there is input from the client side */
/*task->trigger.v[0].mask = QSE_HTTPD_TASK_TRIGGER_READ;
task->trigger.v[0].handle = client->handle;*/
task->trigger.cmask = QSE_HTTPD_TASK_TRIGGER_READ;
return 1; /* trigger me when a client sends data */
}
/* snatching is over. writing error may have occurred as well.
* file->u.put.status should hold a proper status code */
if (qse_httpd_entaskerrorwithmvk (
httpd, client, task, file->u.put.status,
file->method, &file->version, file->keepalive) == QSE_NULL) return -1;
return 0; /* no more data to read. task over */
}
static int task_main_putfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = (task_file_t*)task->ctx;
if (!(file->u.put.flags & PUTFILE_INIT_FAILED) && file->u.put.req)
{
/* initialization was successful and snatching is required.
* switch to the next phase. */
task->main = task_main_putfile_2;
return task_main_putfile_2 (httpd, client, task);
}
/* snatching is not required or initialization error has occurred.
* file->u.put.status should hold a proper status code.
*
* note: if initialization error occurred and there is contents for the
* client to send, this reply may get to the client before it finishes
* sending the contents. */
if (qse_httpd_entaskerrorwithmvk (
httpd, client, task, file->u.put.status,
file->method, &file->version, file->keepalive) == QSE_NULL) return -1;
return 0; /* task over */
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskfile (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_mchar_t* mime,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_file_t data;
const qse_htre_hdrval_t* tmp;
qse_size_t xtnsize;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = (qse_mchar_t*)path;
data.path.len = qse_mbslen(path);
data.version = *qse_htre_getversion(req);
data.keepalive = (req->flags & QSE_HTRE_ATTR_KEEPALIVE);
data.method = qse_htre_getqmethodtype(req);
xtnsize = QSE_SIZEOF(task_file_t) + data.path.len + 1;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
switch (data.method)
{
case QSE_HTTP_OPTIONS:
qse_htre_discardcontent (req);
return qse_httpd_entaskallow (httpd, client, pred,
QSE_MT("OPTIONS,GET,HEAD,POST,PUT,DELETE"), req);
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
qse_htre_discardcontent (req);
if (mime)
{
data.u.get.mime.ptr = (qse_mchar_t*)mime;
data.u.get.mime.len = qse_mbslen(mime);
xtnsize += data.u.get.mime.len + 1;
}
tmp = qse_htre_getheaderval(req, QSE_MT("If-None-Match"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
qse_mbsxcpy (data.u.get.if_none_match, QSE_COUNTOF(data.u.get.if_none_match), tmp->ptr);
}
if (data.u.get.if_none_match[0] == QSE_MT('\0'))
{
/* Both ETag and Last-Modified are included in the reply.
* If the client understand ETag, it can choose to include
* If-None-Match in the request. If it understands Last-Modified,
* it can choose to include If-Modified-Since. I don't care
* the client understands both and include both of them
* in the request.
*
* I check If-None-Match if it's included.
* I check If-Modified-Since if If-None-Match is not included.
*/
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.u.get.if_modified_since) <= -1)
{
data.u.get.if_modified_since.sec = 0;
data.u.get.if_modified_since.nsec = 0;
}
}
}
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.u.get.range) <= -1)
{
return qse_httpd_entaskerror (httpd, client, pred, 416, req);
}
}
else
{
data.u.get.range.type = QSE_HTTP_RANGE_NONE;
}
task.init = task_init_getfile;
task.main = task_main_getfile;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, xtnsize);
case QSE_HTTP_PUT:
/* note that no partial update is supported for PUT */
data.u.put.httpd = httpd;
data.u.put.req = req;
task.init = task_init_putfile;
task.main = task_main_putfile;
task.fini = task_fini_putfile;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, xtnsize);
case QSE_HTTP_DELETE:
{
int status = 200;
qse_htre_discardcontent (req);
if (httpd->opt.scb.file.purge (httpd, path) <= -1)
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
return qse_httpd_entaskerror (httpd, client, pred, status, req);
}
default:
/* Method not allowed */
qse_htre_discardcontent (req);
return qse_httpd_entaskerror (httpd, client, pred, 405, req);
}
}

2594
lib/http/httpd-proxy.c Normal file

File diff suppressed because it is too large Load Diff

1064
lib/http/httpd-std-dns.h Normal file

File diff suppressed because it is too large Load Diff

215
lib/http/httpd-std-mod.h Normal file
View File

@ -0,0 +1,215 @@
/* Override the global definition QSE_ENABLE_STATIC_MODULE
* for httpd on platforms with mature dynamic loading support.
*/
#if defined(QSE_ENABLE_STATIC_MODULE) && \
(defined(USE_LTDL) || defined(_WIN32) || defined(__OS2__))
# undef QSE_ENABLE_STATIC_MODULE
#endif
static void* mod_open (qse_httpd_t* httpd, const qse_char_t* sysname)
{
#if defined(USE_LTDL)
void* h;
qse_mchar_t* modpath;
#if defined(QSE_CHAR_IS_MCHAR)
modpath = sysname;
#else
modpath = qse_wcstombsdup (sysname, QSE_NULL, qse_httpd_getmmgr(httpd));
if (!modpath)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return QSE_NULL;
}
#endif
h = lt_dlopenext (modpath);
if (!h) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
#if defined(QSE_CHAR_IS_MCHAR)
/* do nothing */
#else
QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), modpath);
#endif
return h;
#elif defined(_WIN32)
HMODULE h;
h = LoadLibrary (sysname);
if (!h) qse_httpd_seterrnum (httpd, syserr_to_errnum(GetLastError()));
QSE_ASSERT (QSE_SIZEOF(h) <= QSE_SIZEOF(void*));
return h;
#elif defined(__OS2__)
HMODULE h;
qse_mchar_t* modpath;
char errbuf[CCHMAXPATH];
APIRET rc;
#if defined(QSE_CHAR_IS_MCHAR)
modpath = sysname;
#else
modpath = qse_wcstombsdup (sysname, QSE_NULL, qse_httpd_getmmgr(httpd));
if (!modpath)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return QSE_NULL;
}
#endif
/* DosLoadModule() seems to have severe limitation on
* the file name it can load (max-8-letters.xxx) */
rc = DosLoadModule (errbuf, QSE_COUNTOF(errbuf) - 1, modpath, &h);
if (rc != NO_ERROR)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(rc));
h = QSE_NULL;
}
#if defined(QSE_CHAR_IS_MCHAR)
/* do nothing */
#else
QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), modpath);
#endif
QSE_ASSERT (QSE_SIZEOF(h) <= QSE_SIZEOF(void*));
return h;
#elif defined(__DOS__) && defined(QSE_ENABLE_DOS_DYNAMIC_MODULE)
/* the DOS code here is not generic enough. it's for a specific
* dos-extender only. the best is to disable dynamic loading
* when building for DOS. */
void* h;
qse_mchar_t* modpath;
#if defined(QSE_CHAR_IS_MCHAR)
modpath = sysname;
#else
modpath = qse_wcstombsdup (sysname, QSE_NULL, qse_httpd_getmmgr(httpd));
if (!modpath)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return QSE_NULL;
}
#endif
h = LoadModule (modpath);
if (!h) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
#if defined(QSE_CHAR_IS_MCHAR)
/* do nothing */
#else
QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), modpath);
#endif
QSE_ASSERT (QSE_SIZEOF(h) <= QSE_SIZEOF(void*));
return h;
#elif defined(USE_DLFCN)
void* h;
qse_mchar_t* modpath;
#if defined(QSE_CHAR_IS_MCHAR)
modpath = sysname;
#else
modpath = qse_wcstombsdup (sysname, QSE_NULL, qse_httpd_getmmgr(httpd));
if (!modpath)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return QSE_NULL;
}
#endif
h = dlopen(modpath, RTLD_NOW);
if (!h) qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
#if defined(QSE_CHAR_IS_MCHAR)
/* do nothing */
#else
QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), modpath);
#endif
return h;
#else
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
return QSE_NULL;
#endif
}
static void mod_close (qse_httpd_t* httpd, void* handle)
{
#if defined(USE_LTDL)
lt_dlclose (handle);
#elif defined(_WIN32)
FreeLibrary ((HMODULE)handle);
#elif defined(__OS2__)
DosFreeModule ((HMODULE)handle);
#elif defined(__DOS__) && defined(QSE_ENABLE_DOS_DYNAMIC_MODULE)
FreeModule (handle);
#elif defined(USE_DLFCN)
dlclose (handle);
#else
/* nothing to do */
#endif
}
static void* mod_symbol (qse_httpd_t* httpd, void* handle, const qse_char_t* name)
{
void* s;
qse_mchar_t* mname;
#if defined(QSE_CHAR_IS_MCHAR)
mname = name;
#else
mname = qse_wcstombsdup (name, QSE_NULL, qse_httpd_getmmgr(httpd));
if (!mname)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return QSE_NULL;
}
#endif
#if defined(USE_LTDL)
s = lt_dlsym (handle, mname);
if (s == QSE_NULL) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
#elif defined(_WIN32)
s = GetProcAddress ((HMODULE)handle, mname);
if (s == QSE_NULL) qse_httpd_seterrnum (httpd, syserr_to_errnum(GetLastError()));
#elif defined(__OS2__)
{
APIRET rc;
rc = DosQueryProcAddr ((HMODULE)handle, 0, mname, (PFN*)&s);
if (rc != NO_ERROR)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(rc));
s = QSE_NULL;
}
}
#elif defined(__DOS__) && defined(QSE_ENABLE_DOS_DYNAMIC_MODULE)
s = GetProcAddress (handle, mname);
if (s == QSE_NULL) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
#elif defined(USE_DLFCN)
s = dlsym (handle, mname);
if (s == QSE_NULL) qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
#else
s = QSE_NULL;
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
#endif
#if defined(QSE_CHAR_IS_MCHAR)
/* nothing to do */
#else
QSE_MMGR_FREE (qse_httpd_getmmgr(httpd), mname);
#endif
return s;
}

642
lib/http/httpd-std-urs.h Normal file
View File

@ -0,0 +1,642 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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.
*/
/*
* This file holds url rewriting support code and is included by httpd-std.c
*/
#define URS_SEQ_RANGE_SIZE (QSE_TYPE_MAX(qse_uint16_t) - 2)
#define URS_MAX_URL_LEN 50000
typedef struct urs_hdr_t urs_hdr_t;
typedef struct urs_pkt_t urs_pkt_t;
typedef struct urs_ctx_t urs_ctx_t;
typedef struct urs_req_t urs_req_t;
#include <qse/pack1.h>
struct urs_hdr_t
{
qse_uint16_t seq; /* in network-byte order */
qse_uint16_t rcode; /* response code */
qse_uint32_t urlsum; /* checksum of url in the request */
qse_uint16_t pktlen; /* packet header size + url length */
};
struct urs_pkt_t
{
struct urs_hdr_t hdr;
qse_mchar_t url[1];
};
#include <qse/unpack.h>
struct urs_ctx_t
{
qse_httpd_t* httpd;
qse_httpd_urs_t* urs;
qse_skad_t skad;
int skadlen;
int urs_socket; /* default urs socket to use */
qse_uint16_t seq; /* TODO: change to uint32_t??? */
urs_req_t* reqs[1024]; /* TOOD: choose the right size */
qse_uint16_t req_count;
qse_uint8_t rcvbuf[QSE_SIZEOF(urs_hdr_t) + URS_MAX_URL_LEN + 1];
qse_uint8_t fmtbuf[QSE_SIZEOF(urs_hdr_t) + URS_MAX_URL_LEN + 1];
#if defined(AF_UNIX)
struct sockaddr_un unix_bind_addr;
#endif
};
struct urs_req_t
{
qse_uint16_t seq; /* in host-byte order */
qse_uint32_t pktlen;
urs_pkt_t* pkt;
qse_httpd_rewrite_t rewrite;
void* ctx;
urs_ctx_t* dc;
qse_skad_t urs_skad;
int urs_skadlen;
int urs_socket;
int urs_retries;
qse_ntime_t urs_tmout;
qse_tmr_index_t tmr_tmout;
urs_req_t* prev;
urs_req_t* next;
};
static int urs_open (qse_httpd_t* httpd, qse_httpd_urs_t* urs)
{
qse_nwad_t nwad;
urs_ctx_t* dc;
httpd_xtn_t* httpd_xtn = GET_HTTPD_XTN(httpd);
int type, proto = 0; /*IPPROTO_UDP;*/ /*IPPROTO_SCTP*/
urs->handle[0] = QSE_INVALID_SCKHND;
urs->handle[1] = QSE_INVALID_SCKHND;
urs->handle[2] = QSE_INVALID_SCKHND;
dc = (urs_ctx_t*) qse_httpd_callocmem (httpd, QSE_SIZEOF(urs_ctx_t));
if (dc == NULL) goto oops;
dc->httpd = httpd;
dc->urs = urs;
nwad = httpd_xtn->urs.nwad;
if (nwad.type != QSE_NWAD_NX && qse_getnwadport(&nwad) == 0)
qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_URSSTD_DEFAULT_PORT));
#if defined(QSE_HTTPD_DEBUG)
{
qse_mchar_t tmp[128];
qse_nwadtombs (&nwad, tmp, QSE_COUNTOF(tmp), QSE_NWADTOMBS_ALL);
HTTPD_DBGOUT1 ("Default URS server set to [%hs]\n", tmp);
}
#endif
#if defined(IPPROTO_SCTP)
type = (proto == IPPROTO_SCTP)? SOCK_SEQPACKET: SOCK_DGRAM;
#else
type = SOCK_DGRAM;
#endif
urs->handle[0] = open_client_socket (httpd, AF_INET, type, proto);
#if defined(AF_INET6)
urs->handle[1] = open_client_socket (httpd, AF_INET6, type, proto);
#endif
#if defined(AF_UNIX)
urs->handle[2] = open_client_socket (httpd, AF_UNIX, type, 0);
#endif
if (qse_is_sck_valid(urs->handle[2]))
{
#if defined(AF_UNIX)
qse_ntime_t now;
qse_gettime (&now);
QSE_MEMSET (&dc->unix_bind_addr, 0, QSE_SIZEOF(dc->unix_bind_addr));
dc->unix_bind_addr.sun_family = AF_UNIX;
/* TODO: make the location(/tmp) or the prefix(.urs-) of the socket file configurable??? */
qse_mbsxfmt (
dc->unix_bind_addr.sun_path,
QSE_COUNTOF(dc->unix_bind_addr.sun_path),
QSE_MT("/tmp/.urs-%x-%lu"), (int)QSE_GETPID(), (unsigned long int)urs->handle[2]);
QSE_UNLINK (dc->unix_bind_addr.sun_path);
if (bind (urs->handle[2], (struct sockaddr*)&dc->unix_bind_addr, QSE_SIZEOF(dc->unix_bind_addr)) <= -1)
{
qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM());
qse_close_sck (urs->handle[2]);
urs->handle[2] = QSE_INVALID_SCKHND;
}
#endif
}
if (!qse_is_sck_valid(urs->handle[0]) &&
!qse_is_sck_valid(urs->handle[1]) &&
!qse_is_sck_valid(urs->handle[2]))
{
/* don't set the error number here.
* open_client_socket() or bind() above should set the error number */
goto oops;
}
/* carry on regardless of success or failure */
dc->skadlen = qse_nwadtoskad (&nwad, &dc->skad);
/* determine which socket to use when sending a request to the default server */
if (dc->skadlen >= 0)
{
switch (nwad.type)
{
case QSE_NWAD_IN4:
dc->urs_socket = urs->handle[0];
break;
case QSE_NWAD_IN6:
dc->urs_socket = urs->handle[1];
break;
case QSE_NWAD_LOCAL:
dc->urs_socket = urs->handle[2];
break;
default:
/* unsupported address for the default server */
dc->urs_socket = QSE_INVALID_SCKHND;
break;
}
}
else
{
dc->urs_socket = QSE_INVALID_SCKHND;
}
#if 0
if (proto == IPPROTO_SCTP)
{
/* TODO: error handling */
if (qse_is_sck_valid(urs->handle[0])) listen (urs->handle[0], 99);
if (qse_is_sck_valid(urs->handle[1])) listen (urs->handle[1], 99);
/* handle[2] is a unix socket. no special handling for SCTP */
}
#endif
urs->handle_count = 3;
if (qse_is_sck_valid(urs->handle[0])) urs->handle_mask |= (1 << 0);
if (qse_is_sck_valid(urs->handle[1])) urs->handle_mask |= (1 << 1);
if (qse_is_sck_valid(urs->handle[2])) urs->handle_mask |= (1 << 2);
urs->ctx = dc;
return 0;
oops:
if (qse_is_sck_valid(urs->handle[0])) qse_close_sck (urs->handle[0]);
if (qse_is_sck_valid(urs->handle[1])) qse_close_sck (urs->handle[1]);
if (qse_is_sck_valid(urs->handle[2]))
{
qse_close_sck (urs->handle[2]);
#if defined(AF_UNIX)
QSE_UNLINK (dc->unix_bind_addr.sun_path);
#endif
}
if (dc) qse_httpd_freemem (httpd, dc);
return -1;
}
static void urs_remove_tmr_tmout (qse_httpd_t* httpd, urs_req_t* req)
{
if (req->tmr_tmout != QSE_TMR_INVALID_INDEX)
{
qse_httpd_remove_timer_event (httpd, req->tmr_tmout);
req->tmr_tmout = QSE_TMR_INVALID_INDEX;
}
}
static void urs_close (qse_httpd_t* httpd, qse_httpd_urs_t* urs)
{
urs_ctx_t* dc = (urs_ctx_t*)urs->ctx;
qse_size_t i;
for (i = 0; i < QSE_COUNTOF(dc->reqs); i++)
{
urs_req_t* next_req;
while (dc->reqs[i])
{
next_req = dc->reqs[i]->next;
urs_remove_tmr_tmout (httpd, dc->reqs[i]);
qse_httpd_freemem (httpd, dc->reqs[i]);
dc->reqs[i] = next_req;
dc->req_count--;
}
}
QSE_ASSERT (dc->req_count == 0);
if (qse_is_sck_valid(urs->handle[0])) qse_close_sck (urs->handle[0]);
if (qse_is_sck_valid(urs->handle[1])) qse_close_sck (urs->handle[1]);
if (qse_is_sck_valid(urs->handle[2]))
{
qse_close_sck (urs->handle[2]);
#if defined(AF_UNIX)
QSE_UNLINK (dc->unix_bind_addr.sun_path);
#endif
}
qse_httpd_freemem (httpd, urs->ctx);
}
static int urs_recv (qse_httpd_t* httpd, qse_httpd_urs_t* urs, qse_httpd_hnd_t handle)
{
urs_ctx_t* dc = (urs_ctx_t*)urs->ctx;
/*httpd_xtn_t* httpd_xtn = GET_HTTPD_XTN(httpd);*/
qse_skad_t fromaddr;
qse_sck_len_t fromlen;
qse_uint16_t xid;
qse_ssize_t len, url_len;
urs_pkt_t* pkt;
urs_req_t* req;
qse_mchar_t* spc;
/* TODO: use recvmsg with MSG_ERRQUEUE... set socket option IP_RECVERR... */
fromlen = QSE_SIZEOF(fromaddr);
len = recvfrom (handle, dc->rcvbuf, QSE_SIZEOF(dc->rcvbuf) - 1, 0, (struct sockaddr*)&fromaddr, &fromlen);
/* TODO: check if fromaddr matches the dc->skad... */
pkt = (urs_pkt_t*)dc->rcvbuf;
if (len >= QSE_SIZEOF(urs_hdr_t))
{
pkt->hdr.pktlen = qse_ntoh16(pkt->hdr.pktlen);
if (len == pkt->hdr.pktlen)
{
url_len = pkt->hdr.pktlen - QSE_SIZEOF(urs_hdr_t);
xid = qse_ntoh16(pkt->hdr.seq) % QSE_COUNTOF(dc->reqs);
for (req = dc->reqs[xid]; req; req = req->next)
{
if (req->pkt->hdr.seq == pkt->hdr.seq && req->pkt->hdr.urlsum == pkt->hdr.urlsum)
{
/* null-terminate the url for easier processing */
pkt->url[url_len] = QSE_MT('\0');
/* drop trailers starting from the first space onwards */
spc = qse_mbschr (pkt->url, QSE_MT(' '));
if (spc) *spc = QSE_MT('\0');
urs_remove_tmr_tmout (httpd, req);
req->rewrite (httpd, req->pkt->url, pkt->url, req->ctx);
/* detach the request off dc->reqs */
if (req == dc->reqs[xid]) dc->reqs[xid] = req->next;
else req->prev->next = req->next;
if (req->next) req->next->prev = req->prev;
qse_httpd_freemem (httpd, req);
dc->req_count--;
break;
}
}
}
}
return 0;
}
static void tmr_urs_tmout_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, qse_tmr_event_t* evt)
{
urs_req_t* req = (urs_req_t*)evt->ctx;
QSE_ASSERT (req->tmr_tmout == old_index);
req->tmr_tmout = new_index;
}
static void tmr_urs_tmout_handle (qse_tmr_t* tmr, const qse_ntime_t* now, qse_tmr_event_t* evt)
{
/* destory the unanswered request if timed out */
urs_req_t* req = (urs_req_t*)evt->ctx;
urs_ctx_t* dc = req->dc;
qse_uint16_t xid;
/* when this handler is called, the event should be removed from the timer */
QSE_ASSERT (req->tmr_tmout == QSE_TMR_INVALID_INDEX);
/* ---------------------------------------------------------------
* resend
*---------------------------------------------------------------- */
if (req->urs_retries > 0)
{
/*httpd_xtn_t* httpd_xtn = GET_HTTPD_XTN(dc->httpd);*/
qse_tmr_event_t tmout_event;
QSE_MEMSET (&tmout_event, 0, QSE_SIZEOF(tmout_event));
qse_gettime (&tmout_event.when);
qse_add_ntime (&tmout_event.when, &req->urs_tmout, &tmout_event.when);
tmout_event.ctx = req;
tmout_event.handler = tmr_urs_tmout_handle;
tmout_event.updater = tmr_urs_tmout_update;
if (sendto (req->urs_socket, (void*)req->pkt, req->pktlen, 0, (struct sockaddr*)&req->urs_skad, req->urs_skadlen) != req->pktlen)
{
/* error. fall thru */
qse_httpd_seterrnum (dc->httpd, SKERR_TO_ERRNUM());
/* Unix datagram socket seems to fail with EAGAIN often
* even with increased SO_SNDBUF size. */
if (dc->httpd->errnum == QSE_HTTPD_EAGAIN && req->urs_retries > 1)
{
/* TODO: check writability of req->urs_socket instead of just retrying... */
goto send_ok;
}
}
else
{
send_ok:
QSE_ASSERT (tmr == dc->httpd->tmr);
if (qse_httpd_insert_timer_event (dc->httpd, &tmout_event, &req->tmr_tmout) >= 0)
{
req->urs_retries--;
return; /* resend ok */
}
}
}
printf ("urs timed out....\n");
/* ---------------------------------------------------------------
* timed out + no resend
*---------------------------------------------------------------- */
xid = req->seq % QSE_COUNTOF(dc->reqs);
/* detach the request off dc->reqs */
if (req == dc->reqs[xid]) dc->reqs[xid] = req->next;
else req->prev->next = req->next;
if (req->next) req->next->prev = req->prev;
/* urs timed out. report that name resolution failed */
req->rewrite (dc->httpd, req->pkt->url, QSE_NULL, req->ctx);
/* i don't cache the items that have timed out */
qse_httpd_freemem (dc->httpd, req);
dc->req_count--;
}
static int urs_send (qse_httpd_t* httpd, qse_httpd_urs_t* urs, const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, const qse_httpd_urs_server_t* urs_server, void* ctx)
{
urs_ctx_t* dc = (urs_ctx_t*)urs->ctx;
httpd_xtn_t* httpd_xtn = GET_HTTPD_XTN(httpd);
qse_uint16_t xid;
qse_uint32_t seq;
urs_req_t* req = QSE_NULL;
qse_size_t url_len;
qse_tmr_event_t tmout_event;
if (dc->req_count >= QSE_COUNTOF(dc->reqs))
{
/* too many pending requests */
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOBUF);
goto oops;
}
url_len = qse_mbslen(url);
if (url_len > URS_MAX_URL_LEN) /* TODO: change the limit */
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);
goto oops;
}
seq = ((qse_uint32_t)dc->seq + 1) % URS_SEQ_RANGE_SIZE;
dc->seq = seq;
xid = seq % QSE_COUNTOF(dc->reqs);
req = qse_httpd_callocmem (httpd, QSE_SIZEOF(*req) + QSE_SIZEOF(urs_hdr_t) + url_len + 1);
if (req == QSE_NULL) goto oops;
req->tmr_tmout = QSE_TMR_INVALID_INDEX;
req->seq = seq;
req->pkt = (urs_pkt_t*)(req + 1);
req->pktlen = QSE_SIZEOF(urs_hdr_t) + url_len;
req->pkt->hdr.seq = qse_hton16(seq);
req->pkt->hdr.pktlen = qse_hton16(req->pktlen);
req->pkt->hdr.urlsum = hash_string (url);
qse_mbscpy (req->pkt->url, url);
req->rewrite = rewrite;
req->ctx = ctx;
req->urs_retries = httpd_xtn->urs.retries;
req->urs_tmout = httpd_xtn->urs.tmout;
if (urs_server)
{
if (urs_server->retries >= 0) req->urs_retries = urs_server->retries;
if (urs_server->tmout.sec >= 0) req->urs_tmout = urs_server->tmout;
req->urs_skadlen = qse_nwadtoskad (&urs_server->nwad, &req->urs_skad);
if (req->urs_skadlen <= -1) goto default_urs_server;
switch (urs_server->nwad.type)
{
case QSE_NWAD_IN4:
req->urs_socket = urs->handle[0];
break;
case QSE_NWAD_IN6:
req->urs_socket = urs->handle[1];
break;
case QSE_NWAD_LOCAL:
req->urs_socket = urs->handle[2];
break;
default:
/* TODO: should it return failure with QSE_HTTPD_EINVAL? */
goto default_urs_server;
}
}
else
{
default_urs_server:
if (dc->skadlen >= 0)
{
/* the default url rewrite server address set in urs_open
* is valid. */
req->urs_skad = dc->skad;
req->urs_skadlen = dc->skadlen;
req->urs_socket = dc->urs_socket;
}
else
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOURS);
goto oops;
}
}
QSE_MEMSET (&tmout_event, 0, QSE_SIZEOF(tmout_event));
qse_gettime (&tmout_event.when);
qse_add_ntime (&tmout_event.when, &req->urs_tmout, &tmout_event.when);
tmout_event.ctx = req;
tmout_event.handler = tmr_urs_tmout_handle;
tmout_event.updater = tmr_urs_tmout_update;
if (qse_httpd_insert_timer_event (httpd, &tmout_event, &req->tmr_tmout) <= -1) goto oops;
/*
{
struct msghdr msg;
struct iovec iov;
QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg));
msg.msg_name = &req->urs_skad;
msg.msg_namelen = req->urs_skadlen;
iov.iov_base = req->pkt;
iov.iov_len = req->pktlen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (sendmsg (req->urs_socket, &msg, 0) != req->pktlen)
}
*/
if (sendto (req->urs_socket, (void*)req->pkt, req->pktlen, 0, (struct sockaddr*)&req->urs_skad, req->urs_skadlen) != req->pktlen)
{
qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM());
printf ("URS SENDTO FAILURE........................\n"); /* TODO: logging */
/* Unix datagram socket seems to fail with EAGAIN often
* even with increased SO_SNDBUF size. */
if (httpd->errnum == QSE_HTTPD_EAGAIN && req->urs_retries > 0)
{
/* TODO: check writability of req->urs_socket instead of just retrying... */
goto send_ok;
}
goto oops;
}
send_ok:
req->dc = dc;
/* link the request to the front of the chain */
if (dc->reqs[xid]) dc->reqs[xid]->prev = req;
req->next = dc->reqs[xid];
dc->reqs[xid] = req;
/* increment the number of pending requests */
dc->req_count++;
return 0;
oops:
if (req)
{
urs_remove_tmr_tmout (httpd, req);
qse_httpd_freemem (httpd, req);
}
return -1;
}
static int urs_prerewrite (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, const qse_mchar_t* host, qse_mchar_t** url)
{
const qse_mchar_t* qpath;
int mtype;
const qse_mchar_t* mname;
const qse_mchar_t* quest;
const qse_mchar_t* qparam;
const qse_mchar_t* proto;
const qse_mchar_t* host_ptr = QSE_NULL;
qse_mchar_t cliaddrbuf[MAX_NWAD_TEXT_SIZE];
qse_size_t total_len;
qse_mchar_t* url_to_rewrite;
qpath = qse_htre_getqpath(req);
qparam = qse_htre_getqparam(req);
mtype = qse_htre_getqmethodtype(req);
mname = qse_htre_getqmethodname(req);
total_len = qse_mbslen(qpath) + qse_mbslen(mname);
if (qparam)
{
quest = QSE_MT("?");
total_len = total_len + 1 + qse_mbslen(qparam);
}
else
{
qparam = QSE_MT("");
quest = QSE_MT("");
}
if (host)
{
/* use the host name set explicitly by the caller */
host_ptr = host;
total_len += qse_mbslen(host_ptr);
}
else
{
/* find the host name in the http header */
const qse_htre_hdrval_t* hosthv;
hosthv = qse_htre_getheaderval(req, QSE_MT("Host"));
if (hosthv)
{
/* the first host header only */
host_ptr = hosthv->ptr;
total_len += hosthv->len;
}
}
total_len += qse_nwadtombs (&client->remote_addr, cliaddrbuf, QSE_COUNTOF(cliaddrbuf), QSE_NWADTOMBS_ADDR);
total_len += 128; /* extra space */
url_to_rewrite = qse_httpd_allocmem (httpd, total_len);
if (url_to_rewrite == QSE_NULL) return -1;
if (mtype == QSE_HTTP_CONNECT || !host_ptr)
{
host_ptr = QSE_MT("");
proto = QSE_MT("");
}
else if (client->status & QSE_HTTPD_CLIENT_SECURE)
{
proto = QSE_MT("https://");
}
else
{
proto = QSE_MT("http://");
}
/* URL client-ip/client-fqdn ident method */
qse_mbsxfmt (url_to_rewrite, total_len, QSE_MT("%s%s%s%s%s %s/- - %s"), proto, host_ptr, qpath, quest, qparam, cliaddrbuf, mname);
*url = url_to_rewrite;
return 1;
}

3781
lib/http/httpd-std.c Normal file

File diff suppressed because it is too large Load Diff

422
lib/http/httpd-task.c Normal file
View File

@ -0,0 +1,422 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 "httpd.h"
#include <qse/cmn/str.h>
#include "../cmn/mem-prv.h"
/* TODO:
* many functions in this file use qse_size_t.
* so the size data transfers is limited by this type.
* break this barrier... */
/*------------------------------------------------------------------------*/
static int task_main_disconnect (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
httpd->opt.scb.client.shutdown (httpd, client);
return 0;
}
qse_httpd_task_t* qse_httpd_entaskdisconnect (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred)
{
qse_httpd_task_t task;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.main = task_main_disconnect;
return qse_httpd_entask (httpd, client, pred, &task, 0);
}
/*------------------------------------------------------------------------*/
/* TODO: send wide character string when QSE_CHAR_IS_WCHAR */
/*------------------------------------------------------------------------*/
typedef struct task_format_t task_format_t;
struct task_format_t
{
qse_mchar_t* org;
const qse_mchar_t* ptr;
qse_size_t left;
};
static int task_init_format (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_format_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
task->ctx = xtn;
return 0;
}
static void task_fini_format (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_format_t* ctx = (task_format_t*)task->ctx;
qse_httpd_freemem (httpd, ctx->org);
}
static int task_main_format (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_format_t* ctx = (task_format_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (httpd, client, ctx->ptr, count);
if (n <= -1)
{
if (httpd->errnum != QSE_HTTPD_EAGAIN) return -1;
}
else if (n > 0)
{
ctx->left -= n;
if (ctx->left <= 0) return 0;
ctx->ptr += n;
}
return 1; /* more work to do */
}
qse_httpd_task_t* qse_httpd_entaskformat (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* fmt, ...)
{
qse_httpd_task_t task;
task_format_t data;
va_list ap;
qse_mchar_t* buf;
int bytes_req, l;
va_start (ap, fmt);
bytes_req = qse_mbsxvfmt (QSE_NULL, 0, fmt, ap);
va_end (ap);
buf = (qse_mchar_t*) qse_httpd_allocmem (
httpd, (bytes_req + 1) * QSE_SIZEOF(*buf));
if (buf == QSE_NULL) return QSE_NULL;
va_start (ap, fmt);
l = qse_mbsxvfmt (buf, bytes_req + 1, fmt, ap);
va_end (ap);
if (l != bytes_req)
{
/* something got wrong ... */
qse_httpd_freemem (httpd, buf);
httpd->errnum = QSE_HTTPD_EINTERN;
return QSE_NULL;
}
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.org = buf;
data.ptr = buf;
data.left = l;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_format;
task.fini = task_fini_format;
task.main = task_main_format;
task.ctx = &data;
return qse_httpd_entask (
httpd, client, pred, &task, QSE_SIZEOF(data));
}
/* TODO: send wide character string when QSE_CHAR_IS_WCHAR */
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entask_status (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, void* extra,
qse_http_method_t method, const qse_http_version_t* version,
int keepalive)
{
const qse_mchar_t* msg;
const qse_mchar_t* extrapre = QSE_MT("");
const qse_mchar_t* extrapst = QSE_MT("");
const qse_mchar_t* extraval = QSE_MT("");
qse_mchar_t text[1024]; /* TODO: make this buffer dynamic or scalable */
text[0] = QSE_MT('\0');
msg = qse_httpstatustombs (code);
switch (code)
{
case 301: /* Moved Permanently */
case 302: /* Found */
case 303: /* See Other (since HTTP/1.1) */
case 307: /* Temporary Redirect (since HTTP/1.1) */
case 308: /* Permanent Redirect (Experimental RFC; RFC 7238) */
{
qse_httpd_rsrc_reloc_t* reloc;
reloc = (qse_httpd_rsrc_reloc_t*)extra;
extrapre = QSE_MT("Location: ");
extrapst = (reloc->flags & QSE_HTTPD_RSRC_RELOC_APPENDSLASH)? QSE_MT("/\r\n"): QSE_MT("\r\n");
extraval = reloc->target;
break;
}
case 304:
case 200:
case 201:
case 202:
case 203:
case 204:
case 205:
case 206:
/* nothing to do */
break;
default:
if (method != QSE_HTTP_HEAD &&
httpd->opt.rcb.fmterr (httpd, client, code, text, QSE_COUNTOF(text)) <= -1) return QSE_NULL;
if (code == 401)
{
extrapre = QSE_MT("WWW-Authenticate: Basic realm=\"");
extrapst = QSE_MT("\"\r\n");
extraval = (const qse_mchar_t*)extra;
}
break;
}
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %u\r\n%s%s%s\r\n%s"),
version->major, version->minor,
code, msg, qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(unsigned int)qse_mbslen(text), /* unsigned int should be large enough since text is small */
extrapre, extraval, extrapst, text);
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskerrorwithmvk (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code,
qse_http_method_t method,
const qse_http_version_t* version,
int keepalive)
{
return qse_httpd_entask_status (httpd, client, pred, code, QSE_NULL, method, version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskerror (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, qse_htre_t* req)
{
return qse_httpd_entask_status (
httpd, client, pred, code, QSE_NULL,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->flags & QSE_HTRE_ATTR_KEEPALIVE)
);
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskcontinue (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_htre_t* req)
{
const qse_http_version_t* version = qse_htre_getversion(req);
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d 100 Continue\r\n\r\n"),
version->major, version->minor);
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskauth (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* realm, qse_htre_t* req)
{
return qse_httpd_entask_status (
httpd, client, pred, 401, (void*)realm,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->flags & QSE_HTRE_ATTR_KEEPALIVE));
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskreloc (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_httpd_rsrc_reloc_t* reloc, qse_htre_t* req)
{
int code;
if (reloc->flags & QSE_HTTPD_RSRC_RELOC_KEEPMETHOD)
{
code = (reloc->flags & QSE_HTTPD_RSRC_RELOC_PERMANENT)? 308: 307;
}
else
{
/* NOTE: 302 can be 303 for HTTP/1.1 */
code = (reloc->flags & QSE_HTTPD_RSRC_RELOC_PERMANENT)? 301: 302;
}
return qse_httpd_entask_status (
httpd, client, pred, code, (void*)reloc,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->flags & QSE_HTRE_ATTR_KEEPALIVE));
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entask_nomod (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* pred,
qse_http_method_t method, const qse_http_version_t* version, int keepalive)
{
return qse_httpd_entask_status (
httpd, client, pred, 304, QSE_NULL, method, version, keepalive);
}
qse_httpd_task_t* qse_httpd_entasknomod (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_htre_t* req)
{
return qse_httpd_entask_status (
httpd, client, pred, 304, QSE_NULL,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->flags & QSE_HTRE_ATTR_KEEPALIVE));
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskallow (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* allow, qse_htre_t* req)
{
int code = 200;
const qse_mchar_t* msg;
const qse_http_version_t* version;
int keepalive;
msg = qse_httpstatustombs (code);
version = qse_htre_getversion(req);
keepalive = (req->flags & QSE_HTRE_ATTR_KEEPALIVE);
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nAllow: %s\r\nContent-Length: 0\r\n\r\n"),
version->major, version->minor,
code, msg, qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
allow
);
}
/*------------------------------------------------------------------------*/
#if 0
qse_httpd_task_t* qse_httpd_entaskconnect (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_nwad_t* nwad,
qse_htre_t* req)
{
return -1;
}
#endif
qse_httpd_task_t* qse_httpd_entaskrsrc (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_httpd_rsrc_t* rsrc,
qse_htre_t* req)
{
qse_httpd_task_t* task;
switch (rsrc->type)
{
case QSE_HTTPD_RSRC_AUTH:
task = qse_httpd_entaskauth (httpd, client, pred, rsrc->u.auth.realm, req);
break;
case QSE_HTTPD_RSRC_CGI:
task = qse_httpd_entaskcgi (httpd, client, pred, &rsrc->u.cgi, req);
break;
case QSE_HTTPD_RSRC_DIR:
task = qse_httpd_entaskdir (httpd, client, pred, &rsrc->u.dir, req);
break;
case QSE_HTTPD_RSRC_ERROR:
task = qse_httpd_entaskerror (httpd, client, pred, rsrc->u.error.code, req);
break;
case QSE_HTTPD_RSRC_FILE:
task = qse_httpd_entaskfile (httpd, client, pred, rsrc->u.file.path, rsrc->u.file.mime, req);
break;
case QSE_HTTPD_RSRC_PROXY:
task = qse_httpd_entaskproxy (httpd, client, pred, &rsrc->u.proxy, req);
break;
case QSE_HTTPD_RSRC_RELOC:
task = qse_httpd_entaskreloc (httpd, client, pred, &rsrc->u.reloc, req);
break;
case QSE_HTTPD_RSRC_TEXT:
task = qse_httpd_entasktext (httpd, client, pred, rsrc->u.text.ptr, rsrc->u.text.mime, req);
break;
default:
qse_httpd_discardcontent (httpd, req);
task = QSE_NULL;
httpd->errnum = QSE_HTTPD_EINVAL;
break;
}
return task;
}

204
lib/http/httpd-text.c Normal file
View File

@ -0,0 +1,204 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 "httpd.h"
#include "../cmn/mem-prv.h"
#include <qse/cmn/fmt.h>
typedef struct task_text_t task_text_t;
struct task_text_t
{
const qse_mchar_t* ptr;
qse_size_t left;
};
static int task_init_text (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_text_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
QSE_MEMCPY (xtn + 1, xtn->ptr, xtn->left);
xtn->ptr = (qse_mchar_t*)(xtn + 1);
task->ctx = xtn;
return 0;
}
static int task_main_text (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_text_t* ctx = (task_text_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
/* TODO: do i need to add code to skip this send if count is 0? */
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (httpd, client, ctx->ptr, count);
if (n <= -1)
{
if (httpd->errnum != QSE_HTTPD_EAGAIN) return -1;
}
else if (n > 0)
{
ctx->left -= n;
if (ctx->left <= 0) return 0;
ctx->ptr += n;
}
return 1; /* more work to do */
}
qse_httpd_task_t* qse_httpd_entasktextwithmvk (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* text,
const qse_mchar_t* mime,
qse_http_method_t method,
const qse_http_version_t* version,
int keepalive)
{
qse_size_t tlen;
qse_mchar_t b_tlen[64];
qse_httpd_task_t task;
task_text_t data;
switch (method)
{
case QSE_HTTP_HEAD:
tlen = 0;
break;
case QSE_HTTP_GET:
case QSE_HTTP_POST:
tlen = qse_mbslen(text);
break;
default:
/* Method not allowed */
return qse_httpd_entaskerrorwithmvk (httpd, client, pred, 405, method, version, keepalive);
}
qse_fmtuintmaxtombs (b_tlen, QSE_COUNTOF(b_tlen), tlen, 10, -1, QSE_MT('\0'), QSE_NULL);
pred = qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: %s\r\nContent-Length: %s\r\n\r\n"),
version->major, version->minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
mime, b_tlen
);
if (pred == QSE_NULL) return QSE_NULL;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.ptr = text;
data.left = tlen;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_text;
task.main = task_main_text;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.left);
}
qse_httpd_task_t* qse_httpd_entasktext (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* text,
const qse_mchar_t* mime,
qse_htre_t* req)
{
qse_http_method_t method;
qse_http_version_t* version;
method = qse_htre_getqmethodtype (req);
version = qse_htre_getversion (req);
qse_htre_discardcontent (req);
return qse_httpd_entasktextwithmvk (httpd, client, pred, text, mime, method, version, (req->flags & QSE_HTRE_ATTR_KEEPALIVE));
#if 0
qse_size_t tlen;
qse_mchar_t b_tlen[64];
qse_http_method_t method;
qse_http_version_t* version;
qse_httpd_task_t task;
task_text_t data;
method = qse_htre_getqmethodtype (req);
version = qse_htre_getversion (req);
qse_htre_discardcontent (req);
switch (method)
{
case QSE_HTTP_HEAD:
tlen = 0;
break;
case QSE_HTTP_GET:
case QSE_HTTP_POST:
tlen = qse_mbslen(text);
break;
default:
/* Method not allowed */
return qse_httpd_entaskerror (httpd, client, pred, 405, req);
}
qse_fmtuintmaxtombs (b_tlen, QSE_COUNTOF(b_tlen), tlen, 10, -1, QSE_MT('\0'), QSE_NULL);
pred = qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: %s\r\nContent-Length: %s\r\n\r\n"),
version->major, version->minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
((req->flags & QSE_HTRE_ATTR_KEEPALIVE)? QSE_MT("keep-alive"): QSE_MT("close")),
mime, b_tlen
);
if (pred == QSE_NULL) return QSE_NULL;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.ptr = text;
data.left = tlen;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_text;
task.main = task_main_text;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.left);
#endif
}

2369
lib/http/httpd.c Normal file

File diff suppressed because it is too large Load Diff

220
lib/http/httpd.h Normal file
View File

@ -0,0 +1,220 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 _QSE_LIB_HTTP_HTTPD_H_
#define _QSE_LIB_HTTP_HTTPD_H_
/* private header file for httpd */
#include <qse/http/httpd.h>
#if defined(NDEBUG)
/* To include debugging messages while NDEBUG is set,
* you can define QSE_HTTPD_DEBUG externally in CFLAGS or something.
*/
#else
/* debugging mode */
# define QSE_HTTPD_DEBUG 1
#endif
#if defined(QSE_HTTPD_DEBUG)
# include <qse/si/sio.h>
# include <qse/cmn/path.h>
# define HTTPD_DBGOUT0(fmt) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__))
# define HTTPD_DBGOUT1(fmt,a1) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__), (a1))
# define HTTPD_DBGOUT2(fmt,a1,a2) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__), (a1), (a2))
# define HTTPD_DBGOUT3(fmt,a1,a2,a3) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__), (a1), (a2), (a3))
# define HTTPD_DBGOUT4(fmt,a1,a2,a3,a4) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__), (a1), (a2), (a3), (a4))
# define HTTPD_DBGOUT5(fmt,a1,a2,a3,a4,a5) qse_putmbsf("%06d %-20hs " fmt, (int)__LINE__, qse_mbsbasename(__FILE__), (a1), (a2), (a3), (a4), (a5))
#else
# define HTTPD_DBGOUT0(fmt)
# define HTTPD_DBGOUT1(fmt,a1)
# define HTTPD_DBGOUT2(fmt,a1,a2)
# define HTTPD_DBGOUT3(fmt,a1,a2,a3)
# define HTTPD_DBGOUT4(fmt,a1,a2,a3,a4)
# define HTTPD_DBGOUT5(fmt,a1,a2,a3,a4,a5)
#endif
#define QSE_HTTPD_DEFAULT_PORT 80
#define QSE_HTTPD_DEFAULT_SECURE_PORT 443
struct qse_httpd_t
{
QSE_HTTPD_HDR;
qse_httpd_errnum_t errnum;
qse_httpd_ecb_t* ecb; /* event callbacks */
qse_tmr_t* tmr;
struct
{
int trait;
qse_ntime_t tmout; /* poll timeout */
qse_ntime_t idle_limit;
qse_cstr_t mod[2]; /* module prefix and postfix */
qse_httpd_scb_t scb; /* system callbacks */
qse_httpd_rcb_t rcb; /* request callbacks */
} opt;
int stopreq: 1;
int impedereq: 1;
int dnsactive: 1;
int ursactive: 1;
qse_mchar_t sname[128]; /* server name for the server header */
qse_mchar_t gtbuf[10][64]; /* GMT time buffers */
qse_httpd_mod_t* modlist;
struct
{
struct
{
qse_httpd_client_t* head;
qse_httpd_client_t* tail;
qse_size_t count;
} list;
struct
{
qse_httpd_client_t* head;
qse_httpd_client_t* tail;
} tasked;
qse_httpd_client_t* bad;
} client;
struct
{
struct
{
qse_httpd_server_t* head;
qse_httpd_server_t* tail;
} list;
qse_size_t navail;
qse_size_t nactive;
} server;
void* mux;
qse_httpd_dns_t dns;
qse_httpd_urs_t urs;
};
/* qse_httpd_real_task_t is a private type to hide some private fields
* from being exposed by qse_httpd_task_t.
*/
typedef struct qse_httpd_real_task_t qse_httpd_real_task_t;
struct qse_httpd_real_task_t
{
qse_httpd_task_t core;
qse_httpd_real_task_t* prev;
qse_httpd_real_task_t* next;
};
#define MAX_SEND_SIZE (4096 * 4)
#define MAX_RECV_SIZE (4096 * 2)
#define MAX_NWAD_TEXT_SIZE 96
#if defined(__cplusplus)
extern "C" {
#endif
extern qse_http_version_t qse_http_v11;
int qse_httpd_init (
qse_httpd_t* httpd,
qse_mmgr_t* mmgr
);
void qse_httpd_fini (
qse_httpd_t* httpd
);
qse_httpd_task_t* qse_httpd_entask_status (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
int code,
void* extra,
qse_http_method_t method,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_nomod (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_http_method_t method,
const qse_http_version_t* version,
int keepalive
);
int qse_httpd_activatetasktrigger (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* task
);
int qse_httpd_inactivatetasktrigger (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* task
);
int qse_httpd_insert_timer_event (
qse_httpd_t* httpd,
const qse_tmr_event_t* event,
qse_tmr_index_t* index
);
void qse_httpd_remove_timer_event (
qse_httpd_t* httpd,
qse_tmr_index_t index
);
qse_httpd_peer_t* qse_httpd_cacheproxypeer (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_peer_t* tmpl
);
qse_httpd_peer_t* qse_httpd_decacheproxypeer (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_nwad_t* nwad,
const qse_nwad_t* local,
int secure
);
#if defined(__cplusplus)
}
#endif
#endif

586
lib/http/upxd.c Normal file
View File

@ -0,0 +1,586 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 "upxd.h"
#include <qse/cmn/str.h>
static void disable_all_servers (qse_upxd_t* upxd);
static void free_all_servers (qse_upxd_t* upxd);
static qse_upxd_server_session_t* find_server_session (
qse_upxd_t* upxd, qse_upxd_server_t* server, qse_nwad_t* from);
static void release_session (
qse_upxd_t* upxd, qse_upxd_server_session_t* session);
qse_upxd_t* qse_upxd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
{
qse_upxd_t* upxd;
upxd = (qse_upxd_t*) QSE_MMGR_ALLOC (
mmgr, QSE_SIZEOF(*upxd) + xtnsize
);
if (upxd == QSE_NULL) return QSE_NULL;
if (qse_upxd_init (upxd, mmgr) <= -1)
{
QSE_MMGR_FREE (mmgr, upxd);
return QSE_NULL;
}
return upxd;
}
void qse_upxd_close (qse_upxd_t* upxd)
{
qse_upxd_fini (upxd);
QSE_MMGR_FREE (upxd->mmgr, upxd);
}
int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr)
{
QSE_MEMSET (upxd, 0, QSE_SIZEOF(*upxd));
upxd->mmgr = mmgr;
return 0;
}
void qse_upxd_fini (qse_upxd_t* upxd)
{
if (upxd->server.nactive > 0) disable_all_servers (upxd);
if (upxd->mux)
{
upxd->cbs->mux.close (upxd, upxd->mux);
upxd->mux = QSE_NULL;
}
free_all_servers (upxd);
}
qse_mmgr_t* qse_upxd_getmmgr (qse_upxd_t* upxd)
{
return upxd->mmgr;
}
void* qse_upxd_getxtn (qse_upxd_t* upxd)
{
return QSE_XTN (upxd);
}
qse_upxd_errnum_t qse_upxd_geterrnum (qse_upxd_t* upxd)
{
return upxd->errnum;
}
void qse_upxd_seterrnum (qse_upxd_t* upxd, qse_upxd_errnum_t errnum)
{
upxd->errnum = errnum;
}
QSE_INLINE void* qse_upxd_allocmem (qse_upxd_t* upxd, qse_size_t size)
{
void* ptr = QSE_MMGR_ALLOC (upxd->mmgr, size);
if (ptr == QSE_NULL) upxd->errnum = QSE_UPXD_ENOMEM;
return ptr;
}
QSE_INLINE void* qse_upxd_reallocmem (
qse_upxd_t* upxd, void* ptr, qse_size_t size)
{
void* nptr = QSE_MMGR_REALLOC (upxd->mmgr, ptr, size);
if (nptr == QSE_NULL) upxd->errnum = QSE_UPXD_ENOMEM;
return nptr;
}
QSE_INLINE void qse_upxd_freemem (qse_upxd_t* upxd, void* ptr)
{
QSE_MMGR_FREE (upxd->mmgr, ptr);
}
static int perform_session_task (
qse_upxd_t* upxd, void* mux, qse_upxd_hnd_t handle, void* cbarg)
{
qse_upxd_server_session_t* session;
qse_upxd_server_t* server;
qse_ssize_t n;
session = (qse_upxd_server_session_t*)cbarg;
server = session->inner.server;
qse_gettime (&session->modified);
/* this handler should set the 'from' field of server->scok */
n = upxd->cbs->sock.recv (
upxd, &session->peer, upxd->rbuf, QSE_SIZEOF(upxd->rbuf));
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
/* TODO: inspect if session->inner.to matches session->sock.from.
drop it if they don't match if a certain option (QSE_UPXD_STRICT)
is set??? */
/* send the peer's packet back to the client */
server->local.to = session->inner.client;
n = upxd->cbs->sock.send (upxd, &server->local, upxd->rbuf, n);
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
return 0;
}
static int perform_server_task (
qse_upxd_t* upxd, void* mux, qse_upxd_hnd_t handle, void* cbarg)
{
qse_upxd_server_t* server;
qse_upxd_server_session_t* session;
qse_ssize_t n;
server = (qse_upxd_server_t*)cbarg;
/* this handler should set the 'from' field of server->scok */
n = upxd->cbs->sock.recv (
upxd, &server->local, upxd->rbuf, QSE_SIZEOF(upxd->rbuf));
if (n <= -1) return -1;
/* get the existing session or create a new session based on
* server->local->from */
session = find_server_session (upxd, server, &server->local.from);
if (session == QSE_NULL)
{
qse_upxd_session_t interim;
QSE_MEMSET (&interim, 0, QSE_SIZEOF(interim));
interim.client = server->local.from;
upxd->cbs->session.error (upxd, &interim);
return -1;
}
n = upxd->cbs->sock.send (upxd, &session->peer, upxd->rbuf, n);
if (n <= -1)
{
upxd->cbs->session.error (upxd, &session->inner);
release_session (upxd, session);
return -1;
}
return 0;
}
static qse_upxd_server_session_t* find_server_session (
qse_upxd_t* upxd, qse_upxd_server_t* server, qse_nwad_t* from)
{
qse_upxd_server_session_t* session;
/* TODO: make it indexable or hashable with 'from'
* don't perform linear search */
/* find an existing session made for the source address 'from' */
for (session = server->session.list; session; session = session->next)
{
if (QSE_MEMCMP (&session->inner.client, from, QSE_SIZEOF(*from)) == 0)
{
qse_gettime (&session->modified);
return session;
}
}
/* there is no session found for the source address 'from'.
* let's create a new session. */
session = qse_upxd_allocmem (upxd, QSE_SIZEOF(*session));
if (session == QSE_NULL) return QSE_NULL;
QSE_MEMSET (session, 0, QSE_SIZEOF(*session));
if (qse_gettime (&session->created) <= -1)
{
qse_upxd_freemem (upxd, session);
upxd->errnum = QSE_UPXD_ESYSERR;
return QSE_NULL;
}
session->modified = session->created;
session->inner.server = server;
session->inner.client = *from;
/* set the default dormancy */
session->inner.config.dormancy.sec = QSE_UPXD_SESSION_DORMANCY;
session->inner.config.dormancy.nsec = 0;
/* call the configurationc callback for configuration data */
if (upxd->cbs->session.config (upxd, &session->inner) <= -1)
{
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
/* set up the peer socket with the configuration data */
session->peer.bind = session->inner.config.bind;
session->peer.to = session->inner.config.peer;
if (session->inner.config.dev[0] != QSE_T('\0'))
session->peer.dev = session->inner.config.dev;
if (upxd->cbs->sock.open (upxd, &session->peer) <= -1)
{
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
if (upxd->cbs->mux.addhnd (
upxd, upxd->mux, session->peer.handle,
perform_session_task, session) <= -1)
{
upxd->cbs->sock.close (upxd, &session->peer);
qse_upxd_freemem (upxd, session);
return QSE_NULL;
}
/* insert the session into the head of the session list */
if (server->session.list)
server->session.list->prev = session;
session->next = server->session.list;
server->session.list = session;
return session;
}
static void release_session (
qse_upxd_t* upxd, qse_upxd_server_session_t* session)
{
qse_upxd_server_t* server;
server = session->inner.server;
QSE_ASSERT (server != QSE_NULL);
upxd->cbs->mux.delhnd (upxd, upxd->mux, session->peer.handle);
upxd->cbs->sock.close (upxd, &session->peer);
/* remove the session from the session list */
if (session->next) session->next->prev = session->prev;
if (session->prev) session->prev->next = session->next;
else server->session.list = session->next;
/* destroy the session */
qse_upxd_freemem (upxd, session);
}
static int enable_server (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
QSE_ASSERT (upxd->cbs != QSE_NULL);
QSE_ASSERT (!(server->flags & QSE_UPXD_SERVER_ENABLED));
if (upxd->cbs->sock.open (upxd, &server->local) <= -1)
{
return -1;
}
if (upxd->cbs->mux.addhnd (
upxd, upxd->mux, server->local.handle,
perform_server_task, server) <= -1)
{
upxd->cbs->sock.close (upxd, &server->local);
return -1;
}
server->flags |= QSE_UPXD_SERVER_ENABLED;
upxd->server.nactive++;
return 0;
}
static void disable_server (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
qse_upxd_server_session_t* session;
QSE_ASSERT (upxd->cbs != QSE_NULL);
QSE_ASSERT (server->flags & QSE_UPXD_SERVER_ENABLED);
session = server->session.list;
while (session)
{
qse_upxd_server_session_t* next = session->next;
release_session (upxd, session);
session = next;
}
upxd->cbs->mux.delhnd (upxd, upxd->mux, server->local.handle);
upxd->cbs->sock.close (upxd, &server->local);
server->flags &= ~QSE_UPXD_SERVER_ENABLED;
upxd->server.nactive--;
}
static void enable_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
for (server = upxd->server.list; server; server = server->next)
{
if (!(server->flags & QSE_UPXD_SERVER_ENABLED))
{
enable_server (upxd, server);
}
}
}
static void disable_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
server = upxd->server.list;
while (server)
{
if (server->flags & QSE_UPXD_SERVER_ENABLED)
disable_server (upxd, server);
server = server->next;
}
}
static void free_all_servers (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
qse_upxd_server_t* next;
server = upxd->server.list;
while (server)
{
next = server->next;
QSE_MMGR_FREE (upxd->mmgr, server);
server = next;
}
upxd->server.list = QSE_NULL;
}
qse_upxd_server_t* qse_upxd_addserver (
qse_upxd_t* upxd, const qse_nwad_t* nwad, const qse_char_t* dev)
{
qse_upxd_server_t* server;
if (dev && qse_strlen(dev) >= QSE_COUNTOF(server->dev))
{
upxd->errnum = QSE_UPXD_EINVAL;
return QSE_NULL;
}
server = QSE_MMGR_ALLOC (upxd->mmgr, QSE_SIZEOF(*server));
if (server == QSE_NULL)
{
upxd->errnum = QSE_UPXD_ENOMEM;
return QSE_NULL;
}
QSE_MEMSET (server, 0, QSE_SIZEOF(*server));
if (dev)
{
qse_strxcpy (server->dev, QSE_COUNTOF(server->dev), dev);
server->local.dev = server->dev;
}
server->local.bind = *nwad;
/* chain it to the head of the list */
if (upxd->server.list)
upxd->server.list->prev = server;
server->next = upxd->server.list;
upxd->server.list = server;
return server;
}
void qse_upxd_delserver (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{
if (server->flags & QSE_UPXD_SERVER_ENABLED)
disable_server (upxd, server);
/* unchain the session from the list */
if (server->next) server->next->prev = server->prev;
if (server->prev) server->prev->next = server->next;
else upxd->server.list = server->next;
}
void* qse_upxd_getserverctx (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{
return server->ctx;
}
void qse_upxd_setserverctx (
qse_upxd_t* upxd, qse_upxd_server_t* server, void* ctx)
{
server->ctx = ctx;
}
qse_upxd_cbs_t* qse_upxd_getcbs (qse_upxd_t* upxd)
{
return upxd->cbs;
}
void qse_upxd_setcbs (qse_upxd_t* upxd, qse_upxd_cbs_t* cbs)
{
upxd->cbs = cbs;
}
static QSE_INLINE void purge_idle_sessions_in_server (
qse_upxd_t* upxd, qse_upxd_server_t* server)
{
qse_upxd_server_session_t* session;
qse_upxd_server_session_t* next;
qse_ntime_t now;
qse_gettime (&now);
session = server->session.list;
while (session)
{
next = session->next;
if (session->inner.config.dormancy.sec > 0 &&
now.sec > session->modified.sec &&
now.sec - session->modified.sec > session->inner.config.dormancy.sec)
{
release_session (upxd, session);
}
session = next;
}
}
static void purge_idle_sessions (qse_upxd_t* upxd)
{
qse_upxd_server_t* server;
for (server = upxd->server.list; server; server = server->next)
{
if (server->flags & QSE_UPXD_SERVER_ENABLED)
{
purge_idle_sessions_in_server (upxd, server);
}
}
}
int qse_upxd_loop (qse_upxd_t* upxd, qse_ntime_t timeout)
{
int retv = -1;
QSE_ASSERTX (upxd->cbs != QSE_NULL,
"Call qse_upxd_setcbs() before calling qse_upxd_loop()");
if (upxd->cbs == QSE_NULL)
{
upxd->errnum = QSE_UPXD_EINVAL;
goto oops;
}
if (upxd->mux)
{
/* close the mutiplexer if it's open */
upxd->cbs->mux.close (upxd, upxd->mux);
upxd->mux = QSE_NULL;
}
upxd->stopreq = 0;
upxd->mux = upxd->cbs-> mux.open (upxd);
if (upxd->mux == QSE_NULL) goto oops;
enable_all_servers (upxd);
while (!upxd->stopreq)
{
int count;
count = upxd->cbs->mux.poll (upxd, upxd->mux, timeout);
if (count <= -1)
{
/* TODO: anything? */
}
purge_idle_sessions (upxd);
enable_all_servers (upxd);
}
retv = 0;
oops:
if (upxd->server.nactive > 0) disable_all_servers (upxd);
if (upxd->mux)
{
upxd->cbs->mux.close (upxd, upxd->mux);
upxd->mux = QSE_NULL;
}
return retv;
}
void qse_upxd_stop (qse_upxd_t* upxd)
{
upxd->stopreq = 1;
}
int qse_upxd_enableserver (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
if (server->flags & QSE_UPXD_SERVER_ENABLED)
{
upxd->errnum = QSE_UPXD_EINVAL;
return -1;
}
return enable_server (upxd, server);
}
int qse_upxd_disableserver (qse_upxd_t* upxd, qse_upxd_server_t* server)
{
if (!(server->flags & QSE_UPXD_SERVER_ENABLED))
{
upxd->errnum = QSE_UPXD_EINVAL;
return -1;
}
disable_server (upxd, server);
return 0;
}
int qse_upxd_poll (qse_upxd_t* upxd, qse_ntime_t timeout)
{
int ret;
QSE_ASSERTX (upxd->cbs != QSE_NULL,
"Call qse_upxd_setcbs() before calling qse_upxd_loop()");
if (upxd->mux == QSE_NULL)
{
upxd->mux = upxd->cbs-> mux.open (upxd);
if (upxd->mux == QSE_NULL) return -1;
}
ret = upxd->cbs->mux.poll (upxd, upxd->mux, timeout);
purge_idle_sessions (upxd);
return ret;
}

107
lib/http/upxd.h Normal file
View File

@ -0,0 +1,107 @@
/*
* $Id$
*
Copyright (c) 2006-2019 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 WARRANTIES
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 _QSE_LIB_NET_UPXD_H_
#define _QSE_LIB_NET_UPXD_H_
#include <qse/http/upxd.h>
#include "../cmn/mem-prv.h"
typedef struct qse_upxd_server_session_t qse_upxd_server_session_t;
struct qse_upxd_server_t
{
qse_upxd_server_t* next;
qse_upxd_server_t* prev;
#define QSE_UPXD_SERVER_ENABLED (1 << 0)
int flags;
/* the socket can be bound to this interface.
* sock->dev points to this buffer when necessary. */
qse_char_t dev[QSE_UPXD_SESSION_DEV_LEN + 1];
/* list of sessions beloning to this server */
struct
{
qse_upxd_server_session_t* list;
qse_size_t count;
} session;
qse_upxd_sock_t local;
/* user-defined context data that can be set
* with qse_upxd_setserverctx() */
void* ctx;
};
struct qse_upxd_server_session_t
{
/* internal fields */
qse_upxd_server_session_t* next;
qse_upxd_server_session_t* prev;
/* timestamps for housekeeping */
qse_ntime_t created;
qse_ntime_t modified;
/* socket used to talk with a peer */
qse_upxd_sock_t peer;
/* exposed to a caller via callbacks */
qse_upxd_session_t inner;
};
struct qse_upxd_t
{
qse_mmgr_t* mmgr;
qse_upxd_errnum_t errnum;
int stopreq;
qse_upxd_cbs_t* cbs;
struct
{
qse_upxd_server_t* list;
qse_size_t nactive;
} server;
void* mux;
qse_uint8_t rbuf[65535];
};
#if defined(__cplusplus)
extern "C" {
#endif
int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr);
void qse_upxd_fini (qse_upxd_t* upxd);
#if defined(__cplusplus)
}
#endif
#endif