revising httpd
This commit is contained in:
28
qse/lib/http/Makefile.am
Normal file
28
qse/lib/http/Makefile.am
Normal 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
650
qse/lib/http/Makefile.in
Normal 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
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
266
qse/lib/http/htre.c
Normal 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
479
qse/lib/http/http.c
Normal 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
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
469
qse/lib/http/httpd-dir.c
Normal 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
378
qse/lib/http/httpd-file.c
Normal 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
1470
qse/lib/http/httpd-proxy.c
Normal file
File diff suppressed because it is too large
Load Diff
95
qse/lib/http/httpd-resol.c
Normal file
95
qse/lib/http/httpd-resol.c
Normal 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
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
454
qse/lib/http/httpd-task.c
Normal 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
120
qse/lib/http/httpd-text.c
Normal 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
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
164
qse/lib/http/httpd.h
Normal 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
580
qse/lib/http/upxd.c
Normal 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
101
qse/lib/http/upxd.h
Normal 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
|
Reference in New Issue
Block a user