revising httpd

This commit is contained in:
2013-02-18 13:45:50 +00:00
parent bbdf168ee8
commit 5bc774db3a
56 changed files with 1774 additions and 953 deletions

28
qse/lib/http/Makefile.am Normal file
View File

@ -0,0 +1,28 @@
AUTOMAKE_OPTIONS = nostdinc
AM_CPPFLAGS = \
-I$(top_builddir)/include \
-I$(top_srcdir)/include \
-I$(includedir)
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-resol.c \
httpd-std.c \
httpd-task.c \
httpd-text.c \
upxd.c
libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)

650
qse/lib/http/Makefile.in Normal file
View File

@ -0,0 +1,650 @@
# Makefile.in generated by automake 1.11.3 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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@
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@
subdir = lib/http
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/argz.m4 \
$(top_srcdir)/m4/ax_numval.m4 $(top_srcdir)/m4/ax_pthread.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltdl.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)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
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 =
libqsehttp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
am_libqsehttp_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
httpd-cgi.lo httpd-dir.lo httpd-file.lo httpd-proxy.lo \
httpd-resol.lo httpd-std.lo httpd-task.lo httpd-text.lo \
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
libqsehttp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libqsehttp_la_LDFLAGS) $(LDFLAGS) -o $@
DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/ac/depcomp
am__depfiles_maybe = depfiles
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_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
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_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
SOURCES = $(libqsehttp_la_SOURCES)
DIST_SOURCES = $(libqsehttp_la_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
ARGZ_H = @ARGZ_H@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
BUILD_MODE = @BUILD_MODE@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CHAR_MODE = @CHAR_MODE@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
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@
INCLTDL = @INCLTDL@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBADD_DL = @LIBADD_DL@
LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
LIBADD_DLOPEN = @LIBADD_DLOPEN@
LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
LIBLTDL = @LIBLTDL@
LIBM = @LIBM@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTDLDEPS = @LTDLDEPS@
LTDLINCL = @LTDLINCL@
LTDLOPEN = @LTDLOPEN@
LTLIBOBJS = @LTLIBOBJS@
LT_CONFIG_H = @LT_CONFIG_H@
LT_DLLOADERS = @LT_DLLOADERS@
LT_DLPREOPEN = @LT_DLPREOPEN@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MPICC = @MPICC@
MPI_CFLAGS = @MPI_CFLAGS@
MPI_CLDFLAGS = @MPI_CLDFLAGS@
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@
QSE_SIZEOF_CHAR = @QSE_SIZEOF_CHAR@
QSE_SIZEOF_DOUBLE = @QSE_SIZEOF_DOUBLE@
QSE_SIZEOF_FLOAT = @QSE_SIZEOF_FLOAT@
QSE_SIZEOF_INT = @QSE_SIZEOF_INT@
QSE_SIZEOF_LONG = @QSE_SIZEOF_LONG@
QSE_SIZEOF_LONG_DOUBLE = @QSE_SIZEOF_LONG_DOUBLE@
QSE_SIZEOF_LONG_LONG = @QSE_SIZEOF_LONG_LONG@
QSE_SIZEOF_OFF64_T = @QSE_SIZEOF_OFF64_T@
QSE_SIZEOF_OFF_T = @QSE_SIZEOF_OFF_T@
QSE_SIZEOF_SHORT = @QSE_SIZEOF_SHORT@
QSE_SIZEOF_VOID_P = @QSE_SIZEOF_VOID_P@
QSE_SIZEOF_WCHAR_T = @QSE_SIZEOF_WCHAR_T@
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@
ltdl_LIBOBJS = @ltdl_LIBOBJS@
ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sys_symbol_underscore = @sys_symbol_underscore@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = nostdinc
AM_CPPFLAGS = \
-I$(top_builddir)/include \
-I$(top_srcdir)/include \
-I$(includedir)
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-resol.c \
httpd-std.c \
httpd-task.c \
httpd-text.c \
upxd.c
libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)
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
.PRECIOUS: 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__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
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)
test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@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 " $(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)'; for p in $$list; do \
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
test "$$dir" != "$$p" || dir=.; \
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
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)/htrd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htre.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-cgi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-dir.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-file.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-proxy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-std.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-task.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-text.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd.Plo@am__quote@
.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 $<
.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 `$(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 $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
mkid -fID $$unique
tags: TAGS
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
set x; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
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
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
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"
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(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 -rf ./$(DEPDIR)
-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 -rf ./$(DEPDIR)
-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 all all-am check check-am clean clean-generic \
clean-libLTLIBRARIES clean-libtool ctags 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 uninstall uninstall-am uninstall-libLTLIBRARIES
# 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:

1603
qse/lib/http/htrd.c Normal file

File diff suppressed because it is too large Load Diff

266
qse/lib/http/htre.c Normal file
View File

@ -0,0 +1,266 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include <qse/http/htre.h>
#include "../cmn/mem.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_mancbs_t mancbs =
{
{
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_setmancbs (&re->hdrtab, &mancbs);
qse_htb_setmancbs (&re->trailers, &mancbs);
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);
}
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;
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
re->state = 0;
}
int qse_htre_setstrfromcstr (
qse_htre_t* re, qse_mbs_t* str, const qse_mcstr_t* cstr)
{
return (qse_mbs_ncpy (str, cstr->ptr, cstr->len) == (qse_size_t)-1)? -1: 0;
}
int qse_htre_setstrfromxstr (
qse_htre_t* re, qse_mbs_t* str, const qse_mxstr_t* xstr)
{
return (qse_mbs_ncpy (str, xstr->ptr, xstr->len) == (qse_size_t)-1)? -1: 0;
}
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;
}

479
qse/lib/http/http.c Normal file
View File

@ -0,0 +1,479 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#include <qse/http/http.h>
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/htb.h>
#include "../cmn/mem.h"
#include <stdio.h> /* for snprintf. TODO: remove this. */
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 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;
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;
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_ISDIGIT(*str))
{
do
{
from = from * 10 + (*str - QSE_MT('0'));
str++;
}
while (QSE_ISDIGIT(*str));
}
else type = QSE_HTTP_RANGE_SUFFIX;
if (*str != QSE_MT('-')) return -1;
str++;
if (QSE_ISDIGIT(*str))
{
to = 0;
do
{
to = to * 10 + (*str - QSE_MT('0'));
str++;
}
while (QSE_ISDIGIT(*str));
}
else to = QSE_TYPE_MAX(qse_ulong_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);
/* TODO: avoid using snprintf () */
#if defined(_MSC_VER) || (defined(__WATCOMC__) && (__WATCOMC__ < 1200))
_snprintf (buf, bufsz,
#else
snprintf (buf, bufsz,
#endif
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;
}
qse_size_t qse_perdechttpstr (const qse_mchar_t* str, qse_mchar_t* buf)
{
const qse_mchar_t* p = str;
qse_mchar_t* out = buf;
while (*p != QSE_T('\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)
{
/* unlike the path part, we don't care if it
* contains a null character */
*out++ = ((q << 4) + w);
p += 3;
continue;
}
}
}
*out++ = *p++;
}
*out = QSE_MT('\0');
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('-') || (c) == QSE_T('_') || \
(c) == QSE_MT('.') || (c) == QSE_T('~'))
#define TO_HEX(v) (QSE_MT("0123456789ABCDEF")[(v) & 15])
qse_size_t qse_perenchttpstr (const qse_mchar_t* str, qse_mchar_t* buf)
{
const qse_mchar_t* p = str;
qse_mchar_t* out = buf;
while (*p != QSE_T('\0'))
{
if (IS_UNRESERVED(*p)) *out++ = *p;
else
{
*out++ = QSE_MT('%');
*out++ = TO_HEX (*p >> 4);
*out++ = TO_HEX (*p & 15);
}
p++;
}
*out = QSE_MT('\0');
return out - buf;
}
qse_mchar_t* qse_perenchttpstrdup (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 */
for (len = 0; str[len] != QSE_T('\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 (str, buf);
return buf;
}

1540
qse/lib/http/httpd-cgi.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,469 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "../cmn/mem.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_http_version_t version;
int keepalive;
};
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_ubi_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];
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);
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->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 int add_footer (qse_httpd_t* httpd, qse_httpd_client_t* client, task_dseg_t* ctx)
{
int x, rem;
rem = ctx->chunked? (ctx->buflen - 5): ctx->buflen;
if (rem < 1)
{
httpd->errnum = QSE_HTTPD_ENOBUF;
return -1;
}
x = httpd->rcb->format_dir (
httpd, client, QSE_NULL, QSE_NULL,
&ctx->buf[ctx->buflen], rem);
if (x <= -1) 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;
int 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 increate 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. */
x = httpd->rcb->format_dir (
httpd, client, ctx->qpath.ptr, QSE_NULL,
&ctx->buf[ctx->buflen], ctx->bufrem);
if (x <= -1)
{
/* return an error if the buffer is too small to
* hold the header(httpd->errnum == QSE_HTTPD_ENOBUF).
* i need to increate the buffer size. or i have make
* the buffer dynamic. */
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->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 = httpd->rcb->format_dir (
httpd, client, ctx->qpath.ptr, &ctx->dent,
&ctx->buf[ctx->buflen], ctx->bufrem);
if (x <= -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->scb->dir.read (httpd, ctx->handle, &ctx->dent) <= 0)
ctx->state |= DIRENT_NOMORE;
}
while (1);
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->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_ubi_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;
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);
}
/*------------------------------------------------------------------------*/
static int task_init_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
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);
/* switch the context to the extension area */
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* dir;
qse_httpd_task_t* x;
qse_ubi_t handle;
dir = (task_dir_t*)task->ctx;
x = task;
if (qse_mbsend (dir->path.ptr, QSE_MT("/")))
{
if (httpd->scb->dir.open (httpd, dir->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_entask_err (
httpd, client, x, http_errnum,
&dir->version, dir->keepalive);
return (x == QSE_NULL)? -1: 0;
}
else
{
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) x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0;
httpd->scb->dir.close (httpd, handle);
return -1;
}
}
else
{
x = qse_httpd_entask_redir (
httpd, client, x, dir->qpath.ptr,
&dir->version, dir->keepalive);
return (x == QSE_NULL)? -1: 0;
}
}
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_dir_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = 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.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dir;
task.main = task_main_dir;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_dir_t) + data.path.len + 1 + data.qpath.len + 1);
}

378
qse/lib/http/httpd-file.c Normal file
View File

@ -0,0 +1,378 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "../cmn/mem.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#define ETAG_LEN_MAX 127
typedef struct task_file_t task_file_t;
struct task_file_t
{
qse_mcstr_t path;
qse_mcstr_t mime;
qse_http_range_t range;
qse_mchar_t if_none_match[ETAG_LEN_MAX + 1];
qse_ntime_t if_modified_since;
qse_http_version_t version;
int keepalive;
};
typedef struct task_fseg_t task_fseg_t;
struct task_fseg_t
{
qse_ubi_t handle;
qse_foff_t left;
qse_foff_t offset;
};
static int task_init_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
task->ctx = xtn;
return 0;
}
static void task_fini_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
httpd->scb->file.close (httpd, ctx->handle);
}
static int task_main_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->scb->client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
/* HANDLE EGAIN specially??? */
return -1; /* TODO: any logging */
}
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;
return 1; /* more work to do */
}
static qse_httpd_task_t* entask_file_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_ubi_t handle, qse_foff_t offset, qse_foff_t size)
{
qse_httpd_task_t task;
task_fseg_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_fseg;
task.main = task_main_fseg;
task.fini = task_fini_fseg;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
}
/*------------------------------------------------------------------------*/
static int task_init_file (
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->mime.ptr)
{
file->mime.ptr = file->path.ptr + file->path.len + 1;
qse_mbscpy ((qse_mchar_t*)file->mime.ptr, arg->mime.ptr);
}
task->ctx = file;
return 0;
}
static QSE_INLINE int task_main_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file;
qse_httpd_task_t* x;
qse_ubi_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->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_entask_err (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->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_entask_err (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
fileopen = 1;
if (file->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->range.type == QSE_HTTP_RANGE_SUFFIX)
{
if (file->range.to > st.size) file->range.to = st.size;
file->range.from = st.size - file->range.to;
file->range.to = file->range.to + file->range.from;
if (st.size > 0) file->range.to--;
}
if (file->range.from >= st.size)
{
x = qse_httpd_entask_err (
httpd, client, x, 416, &file->version, file->keepalive);
goto no_file_send;
}
if (file->range.to >= st.size) file->range.to = st.size - 1;
qse_fmtuintmaxtombs (tmp[0], QSE_COUNTOF(tmp[0]), (file->range.to - file->range.from + 1), 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[1], QSE_COUNTOF(tmp[1]), file->range.from, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[2], QSE_COUNTOF(tmp[2]), file->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->mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->mime.len > 0? file->mime.ptr: QSE_MT("")),
(file->mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
tmp[0], tmp[1], tmp[2], tmp[3], etag
);
if (x)
{
x = entask_file_segment (
httpd, client, x,
handle,
file->range.from,
(file->range.to - file->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->if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->if_none_match) == 0) ||
(file->if_modified_since.sec > 0 && st.mtime.sec <= file->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->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->mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->mime.len > 0? file->mime.ptr: QSE_MT("")),
(file->mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
b_fsize,
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag
);
if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size);
}
if (x) return 0;
httpd->scb->file.close (httpd, handle);
return -1;
no_file_send:
if (fileopen) httpd->scb->file.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
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_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = path;
data.path.len = qse_mbslen(path);
if (mime)
{
data.mime.ptr = mime;
data.mime.len = qse_mbslen(mime);
}
data.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
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.range) <= -1)
{
return qse_httpd_entaskerr (httpd, client, pred, 416, req);
}
}
else
{
data.range.type = QSE_HTTP_RANGE_NONE;
}
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.if_none_match, QSE_COUNTOF(data.if_none_match), tmp->ptr);
}
if (data.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.if_modified_since) <= -1)
{
data.if_modified_since.sec = 0;
data.if_modified_since.nsec = 0;
}
}
}
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_file;
task.main = task_main_file;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_file_t) + data.path.len + 1 + data.mime.len + 1);
}

1470
qse/lib/http/httpd-proxy.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
/* UNSUPPORTED YET.. */
/* TODO: IMPLEMENT THIS */
#else
#include "httpd.h"
#include "../cmn/mem.h"
typedef struct task_resol_arg_t task_resol_arg_t;
struct task_resol_arg_t
{
const qse_mchar_t* path;
qse_htre_t* req;
int nph;
};
typedef struct task_resol_t task_resol_t;
struct task_resol_t
{
int init_failed;
qse_httpd_t* httpd;
const qse_mchar_t* path;
qse_http_version_t version;
int keepalive; /* taken from the request */
};
static int task_init_resol (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_resol_t* resol;
resol = (task_resol_t*)qse_httpd_gettaskxtn (httpd, task);
QSE_MEMSET (resol, 0, QSE_SIZEOF(*resol));
resol->httpd = httpd;
task->ctx = resol;
return 0;
}
static void task_fini_resol (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_resol_t* resol = (task_resol_t*)task->ctx;
qse_printf (QSE_T("task_fini_resol\n"));
}
static int task_main_resol (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
return 0;
}
qse_httpd_task_t* qse_httpd_entaskresol (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* host)
{
qse_httpd_task_t task;
task_resol_arg_t arg;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_resol;
task.fini = task_fini_resol;
task.main = task_main_resol;
task.ctx = &arg;
return qse_httpd_entask (
httpd, client, pred, &task, QSE_SIZEOF(task_resol_t)
);
}
#endif

2714
qse/lib/http/httpd-std.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,454 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#include "../cmn/mem.h"
#include <stdarg.h>
#include <stdio.h> /* TODO: remove this */
/* 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->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;
n = httpd->scb->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
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 n[2];
qse_mchar_t* buf;
int bytes_req, l;
va_start (ap, fmt);
bytes_req = vsnprintf (n, 1, fmt, ap);
va_end (ap);
if (bytes_req == -1)
{
qse_size_t capa = 256;
buf = (qse_mchar_t*) qse_httpd_allocmem (
httpd, (capa + 1) * QSE_SIZEOF(*buf));
if (buf == QSE_NULL) return QSE_NULL;
/* an old vsnprintf behaves differently from C99 standard.
* thus, it returns -1 when it can't write all the input given. */
for (;;)
{
va_start (ap, fmt);
l = vsnprintf (buf, capa + 1, fmt, ap);
va_end (ap);
if (l == -1)
{
qse_httpd_freemem (httpd, buf);
capa = capa * 2;
buf = (qse_mchar_t*) qse_httpd_allocmem (httpd, (capa + 1) * QSE_SIZEOF(*buf));
if (buf == QSE_NULL) return QSE_NULL;
}
else break;
}
}
else
{
/* vsnprintf returns the number of characters that would
* have been written not including the terminating '\0'
* if the _data buffer were large enough */
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 = vsnprintf (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;
qse_printf (QSE_T("SEND: [%.*hs]\n"), (int)l, buf);
return qse_httpd_entask (
httpd, client, pred, &task, QSE_SIZEOF(data));
}
/* TODO: send wide character string when QSE_CHAR_IS_WCHAR */
/*------------------------------------------------------------------------*/
typedef struct status_reloc_t status_reloc_t;
struct status_reloc_t
{
const qse_mchar_t* dst;
int redir;
};
static qse_httpd_task_t* entask_status (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, void* extra,
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 */
msg = qse_httpstatustombs (code);
if (code == 301 || code == 307)
{
status_reloc_t* reloc;
reloc = (status_reloc_t*)extra;
extrapre = QSE_MT("Location: ");
extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n");
extraval = reloc->dst;
text[0] = QSE_MT('\0');
}
else if (code == 304)
{
text[0] = QSE_MT('\0');
}
else
{
if (httpd->rcb->format_err (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;
}
}
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_entask_err (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code,
const qse_http_version_t* version, int keepalive)
{
return entask_status (httpd, client, pred, code, QSE_NULL, version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskerr (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, qse_htre_t* req)
{
return entask_status (
httpd, client, pred, code, QSE_NULL,
qse_htre_getversion(req), (req->attr.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 entask_status (
httpd, client, pred, 401, (void*)realm,
qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entask_reloc (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst,
const qse_http_version_t* version, int keepalive)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 0;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskreloc (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 0;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
}
qse_httpd_task_t* qse_httpd_entask_redir (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst,
const qse_http_version_t* version, int keepalive)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 1;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskredir (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 1;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
qse_htre_getversion(req),
(req->attr.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, const qse_http_version_t* version, int keepalive)
{
return entask_status (
httpd, client, pred, 304,
QSE_NULL, 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 entask_status (
httpd, client, pred, 304,
QSE_NULL, qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
}
/*------------------------------------------------------------------------*/
#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:
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskdir (httpd, client, pred, rsrc->u.dir.path, req);
break;
case QSE_HTTPD_RSRC_ERR:
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskerr (httpd, client, pred, rsrc->u.err.code, req);
break;
case QSE_HTTPD_RSRC_FILE:
qse_httpd_discardcontent (httpd, req);
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.dst, &rsrc->u.proxy.src, req);
break;
case QSE_HTTPD_RSRC_RELOC:
task = qse_httpd_entaskreloc (httpd, client, pred, rsrc->u.reloc.dst, req);
break;
case QSE_HTTPD_RSRC_REDIR:
task = qse_httpd_entaskredir (httpd, client, pred, rsrc->u.redir.dst, 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;
}

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

@ -0,0 +1,120 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "../cmn/mem.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? */
n = httpd->scb->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
ctx->left -= n;
if (ctx->left <= 0) return 0;
ctx->ptr += n;
return 1; /* more work to do */
}
qse_httpd_task_t* qse_httpd_entask_text (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* ptr,
qse_size_t len)
{
qse_httpd_task_t task;
task_text_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.ptr = ptr;
data.left = len;
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_size_t tlen;
qse_mchar_t b_tlen[64];
qse_http_version_t* version;
version = qse_htre_getversion (req);
tlen = qse_mbslen(text);
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->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)? QSE_MT("keep-alive"): QSE_MT("close")),
mime, b_tlen
);
if (pred == QSE_NULL) return QSE_NULL;
return qse_httpd_entask_text (httpd, client, pred, text, tlen);
}

1285
qse/lib/http/httpd.c Normal file

File diff suppressed because it is too large Load Diff

164
qse/lib/http/httpd.h Normal file
View File

@ -0,0 +1,164 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _QSE_LIB_HTTP_HTTPD_H_
#define _QSE_LIB_HTTP_HTTPD_H_
/* private header file for httpd */
#include <qse/http/httpd.h>
#include <qse/cmn/stdio.h> /* TODO: remove this.. only for debugging at this moment */
struct qse_httpd_t
{
qse_mmgr_t* mmgr;
qse_httpd_errnum_t errnum;
qse_httpd_ecb_t* ecb; /* event callbacks */
qse_httpd_scb_t* scb; /* system callbacks */
qse_httpd_rcb_t* rcb; /* request callbacks */
struct
{
int trait;
} opt;
int stopreq: 1;
int reconfigreq: 1;
qse_mchar_t sname[128]; /* server name for the server header */
qse_mchar_t gtbuf[10][64]; /* GMT time buffers */
struct
{
struct
{
qse_httpd_client_t* head;
qse_httpd_client_t* tail;
} 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;
};
#define MAX_SEND_SIZE 4096
/* client->status */
#define CLIENT_BAD (1 << 0)
#define CLIENT_READY (1 << 1)
#define CLIENT_SECURE (1 << 2)
#define CLIENT_PENDING (1 << 3)
#define CLIENT_MUTE (1 << 4)
#define CLIENT_MUTE_DELETED (1 << 5)
#define CLIENT_HANDLE_READ_IN_MUX (1 << 6)
#define CLIENT_HANDLE_WRITE_IN_MUX (1 << 7)
#define CLIENT_HANDLE_IN_MUX (CLIENT_HANDLE_READ_IN_MUX|CLIENT_HANDLE_WRITE_IN_MUX)
#define CLIENT_TASK_TRIGGER_IN_MUX(i) (1 << ((i) + 8))
#if defined(_MSC_VER) || defined(__BORLANDC__) || (defined(__WATCOMC__) && (__WATCOMC__ < 1200))
# define snprintf _snprintf
# define vsnprintf _vsnprintf
#endif
#ifdef __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_err (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
int code,
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,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_reloc (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* dst,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_redir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* dst,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_text (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* ptr,
qse_size_t len
);
#ifdef __cplusplus
}
#endif
#endif

580
qse/lib/http/upxd.c Normal file
View File

@ -0,0 +1,580 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#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 (upxd->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_ubi_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_ubi_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;
}

101
qse/lib/http/upxd.h Normal file
View File

@ -0,0 +1,101 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#ifndef _QSE_LIB_NET_UPXD_H_
#define _QSE_LIB_NET_UPXD_H_
#include <qse/http/upxd.h>
#include "../cmn/mem.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];
};
#ifdef __cplusplus
extern "C" {
#endif
int qse_upxd_init (qse_upxd_t* upxd, qse_mmgr_t* mmgr);
void qse_upxd_fini (qse_upxd_t* upxd);
#ifdef __cplusplus
}
#endif
#endif