From cb69b4c7ba398e7771cc53d2a75831950a7ebc71 Mon Sep 17 00:00:00 2001 From: rousskov <> Date: Sat, 21 Feb 1998 07:56:32 +0000 Subject: [PATCH] Dirty and buggy alex_2. --- configure | 370 +++--- configure.in | 14 +- errors/English/ERR_CACHE_ACCESS_DENIED | 32 + include/MemPool.h | 64 + include/Stack.h | 56 + include/autoconf.h.in | 3 + include/util.h | 3 +- include/version.h | 3 +- lib/Makefile.in | 4 +- lib/MemPool.c | 193 +++ lib/Stack.c | 116 ++ lib/util.c | 37 +- src/HttpBody.cc | 89 ++ src/HttpHeader.cc | 1690 ++++++++++++++++++++++++ src/HttpReply.cc | 402 ++++++ src/HttpStatusLine.cc | 213 +++ src/Makefile.in | 8 +- src/MemBuf.cc | 186 +++ src/Packer.cc | 127 ++ src/cache_manager.cc | 13 +- src/client_side.cc | 126 +- src/comm.cc | 9 +- src/defines.h | 10 + src/enums.h | 11 +- src/errorpage.cc | 93 +- src/ftp.cc | 46 +- src/globals.h | 3 +- src/http.cc | 74 +- src/main.cc | 3 +- src/mem.cc | 5 +- src/mime.cc | 15 +- src/protos.h | 10 +- src/store.cc | 52 +- src/store_client.cc | 8 +- src/store_log.cc | 16 +- src/structs.h | 39 +- src/typedefs.h | 4 + src/urn.cc | 27 +- 38 files changed, 3868 insertions(+), 306 deletions(-) create mode 100644 errors/English/ERR_CACHE_ACCESS_DENIED create mode 100644 include/MemPool.h create mode 100644 include/Stack.h create mode 100644 lib/MemPool.c create mode 100644 lib/Stack.c create mode 100644 src/HttpBody.cc create mode 100644 src/HttpHeader.cc create mode 100644 src/HttpReply.cc create mode 100644 src/HttpStatusLine.cc create mode 100644 src/MemBuf.cc create mode 100644 src/Packer.cc diff --git a/configure b/configure index 35c9076bd1..cf5a3e5d6c 100755 --- a/configure +++ b/configure @@ -47,6 +47,8 @@ ac_help="$ac_help Make cachemgr.cgi default to this host" ac_help="$ac_help --enable-arp-acl Enable use of ARP ACL lists (ether address)" +ac_help="$ac_help + --enable-alex_code Enable Alex's code" # Initialize some variables set by options. # The variables have the same names as the options, with @@ -555,7 +557,7 @@ fi -# From configure.in Revision: 1.98 +# From configure.in Revision: 1.99 ac_aux_dir= for ac_dir in aux $srcdir/aux; do if test -f $ac_dir/install-sh; then @@ -583,7 +585,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:587: checking host system type" >&5 +echo "configure:589: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -638,7 +640,7 @@ PRESET_CFLAGS="$CFLAGS" # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:642: checking for $ac_word" >&5 +echo "configure:644: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -667,7 +669,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:671: checking for $ac_word" >&5 +echo "configure:673: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -715,7 +717,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:719: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:721: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -725,11 +727,11 @@ ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:735: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -749,12 +751,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:753: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:755: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:758: checking whether we are using GNU C" >&5 +echo "configure:760: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -763,7 +765,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:767: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:769: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -778,7 +780,7 @@ if test $ac_cv_prog_gcc = yes; then ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:782: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:784: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1138,8 +1140,24 @@ EOF fi +# Check whether --enable-alex_code or --disable-alex_code was given. +if test "${enable_alex_code+set}" = set; then + enableval="$enable_alex_code" + if test "$enableval" = "yes" ; then + echo "Alex's code enabled" + cat >> confdefs.h <<\EOF +#define USE_ALEX_CODE 1 +EOF + + else + echo "Alex's code DISABLED" + fi + +fi + + echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:1143: checking how to run the C preprocessor" >&5 +echo "configure:1161: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -1154,13 +1172,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1164: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1182: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -1171,13 +1189,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1181: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1199: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -1210,7 +1228,7 @@ echo "$ac_t""$CPP" 1>&6 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:1214: checking for a BSD compatible install" >&5 +echo "configure:1232: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1262,7 +1280,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1266: checking for $ac_word" >&5 +echo "configure:1284: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1289,7 +1307,7 @@ else fi echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:1293: checking whether ln -s works" >&5 +echo "configure:1311: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1312,7 +1330,7 @@ fi # Extract the first word of "sh", so it can be a program name with args. set dummy sh; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1316: checking for $ac_word" >&5 +echo "configure:1334: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_SH'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1344,7 +1362,7 @@ fi # Extract the first word of "false", so it can be a program name with args. set dummy false; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1348: checking for $ac_word" >&5 +echo "configure:1366: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_FALSE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1376,7 +1394,7 @@ fi # Extract the first word of "true", so it can be a program name with args. set dummy true; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1380: checking for $ac_word" >&5 +echo "configure:1398: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_TRUE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1408,7 +1426,7 @@ fi # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1412: checking for $ac_word" >&5 +echo "configure:1430: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1440,7 +1458,7 @@ fi # Extract the first word of "mv", so it can be a program name with args. set dummy mv; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1444: checking for $ac_word" >&5 +echo "configure:1462: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MV'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1472,7 +1490,7 @@ fi # Extract the first word of "mkdir", so it can be a program name with args. set dummy mkdir; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1476: checking for $ac_word" >&5 +echo "configure:1494: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MKDIR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1504,7 +1522,7 @@ fi # Extract the first word of "ln", so it can be a program name with args. set dummy ln; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1508: checking for $ac_word" >&5 +echo "configure:1526: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_LN'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1536,7 +1554,7 @@ fi # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1540: checking for $ac_word" >&5 +echo "configure:1558: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1568,7 +1586,7 @@ fi # Extract the first word of "makedepend", so it can be a program name with args. set dummy makedepend; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1572: checking for $ac_word" >&5 +echo "configure:1590: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MAKEDEPEND'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1600,7 +1618,7 @@ fi # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1604: checking for $ac_word" >&5 +echo "configure:1622: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1650,12 +1668,12 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 -echo "configure:1654: checking for $ac_hdr that defines DIR" >&5 +echo "configure:1672: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include <$ac_hdr> @@ -1663,7 +1681,7 @@ int main() { DIR *dirp = 0; ; return 0; } EOF -if { (eval echo configure:1667: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1685: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else @@ -1688,7 +1706,7 @@ done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 -echo "configure:1692: checking for opendir in -ldir" >&5 +echo "configure:1710: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1696,7 +1714,7 @@ else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1729: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1729,7 +1747,7 @@ fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 -echo "configure:1733: checking for opendir in -lx" >&5 +echo "configure:1751: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1737,7 +1755,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1770: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -1771,12 +1789,12 @@ fi fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 -echo "configure:1775: checking for ANSI C header files" >&5 +echo "configure:1793: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -1784,7 +1802,7 @@ else #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1788: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1806: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1801,7 +1819,7 @@ rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -1819,7 +1837,7 @@ fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF @@ -1840,7 +1858,7 @@ if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') @@ -1851,7 +1869,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF -if { (eval echo configure:1855: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1873: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then : else @@ -1927,17 +1945,17 @@ for ac_hdr in \ do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1931: checking for $ac_hdr" >&5 +echo "configure:1949: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1941: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1959: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1965,12 +1983,12 @@ done echo $ac_n "checking for working const""... $ac_c" 1>&6 -echo "configure:1969: checking for working const" >&5 +echo "configure:1987: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2041: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else @@ -2040,14 +2058,14 @@ EOF fi echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 -echo "configure:2044: checking whether byte ordering is bigendian" >&5 +echo "configure:2062: checking whether byte ordering is bigendian" >&5 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_bigendian=unknown # See if sys/param.h defines the BYTE_ORDER macro. cat > conftest.$ac_ext < #include @@ -2058,11 +2076,11 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:2062: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2080: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* # It does; now see whether it defined to BIG_ENDIAN or not. cat > conftest.$ac_ext < #include @@ -2073,7 +2091,7 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:2077: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2095: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_bigendian=yes else @@ -2093,7 +2111,7 @@ if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2128: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_c_bigendian=no else @@ -2131,20 +2149,20 @@ fi echo $ac_n "checking if ANSI prototypes work""... $ac_c" 1>&6 -echo "configure:2135: checking if ANSI prototypes work" >&5 +echo "configure:2153: checking if ANSI prototypes work" >&5 if eval "test \"`echo '$''{'ac_cv_have_ansi_prototypes'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2166: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ansi_prototypes="yes" else @@ -2166,13 +2184,13 @@ EOF fi echo $ac_n "checking for tm->tm_gmtoff""... $ac_c" 1>&6 -echo "configure:2170: checking for tm->tm_gmtoff" >&5 +echo "configure:2188: checking for tm->tm_gmtoff" >&5 if eval "test \"`echo '$''{'ac_cv_have_tm_gmoff'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2181,7 +2199,7 @@ struct tm foo; foo.tm_gmtoff = 0; ; return 0; } EOF -if { (eval echo configure:2185: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2203: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_tm_gmoff="yes" else @@ -2203,13 +2221,13 @@ EOF fi echo $ac_n "checking for extended mallinfo""... $ac_c" 1>&6 -echo "configure:2207: checking for extended mallinfo" >&5 +echo "configure:2225: checking for extended mallinfo" >&5 if eval "test \"`echo '$''{'ac_cv_have_ext_mallinfo'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2218,7 +2236,7 @@ struct mallinfo foo; foo.mxfast = 0; ; return 0; } EOF -if { (eval echo configure:2222: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2240: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ext_mallinfo="yes" else @@ -2240,13 +2258,13 @@ EOF fi echo $ac_n "checking for struct rusage""... $ac_c" 1>&6 -echo "configure:2244: checking for struct rusage" >&5 +echo "configure:2262: checking for struct rusage" >&5 if eval "test \"`echo '$''{'ac_cv_have_struct_rusage'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2281: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_struct_rusage="yes" else @@ -2281,13 +2299,13 @@ EOF fi echo $ac_n "checking for ip->ip_hl""... $ac_c" 1>&6 -echo "configure:2285: checking for ip->ip_hl" >&5 +echo "configure:2303: checking for ip->ip_hl" >&5 if eval "test \"`echo '$''{'ac_cv_have_ip_hl'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -2304,7 +2322,7 @@ struct iphdr ip; ip.ip_hl= 0; ; return 0; } EOF -if { (eval echo configure:2308: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:2326: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_have_ip_hl="yes" else @@ -2326,7 +2344,7 @@ EOF fi echo $ac_n "checking size of int""... $ac_c" 1>&6 -echo "configure:2330: checking size of int" >&5 +echo "configure:2348: checking size of int" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2334,7 +2352,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < main() @@ -2345,7 +2363,7 @@ main() exit(0); } EOF -if { (eval echo configure:2349: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2367: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_sizeof_int=`cat conftestval` else @@ -2365,7 +2383,7 @@ EOF echo $ac_n "checking size of long""... $ac_c" 1>&6 -echo "configure:2369: checking size of long" >&5 +echo "configure:2387: checking size of long" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2373,7 +2391,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < main() @@ -2384,7 +2402,7 @@ main() exit(0); } EOF -if { (eval echo configure:2388: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2406: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_sizeof_long=`cat conftestval` else @@ -2407,19 +2425,19 @@ EOF # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:2411: checking for working alloca.h" >&5 +echo "configure:2429: checking for working alloca.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *p = alloca(2 * sizeof(int)); ; return 0; } EOF -if { (eval echo configure:2423: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2441: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_header_alloca_h=yes else @@ -2440,12 +2458,12 @@ EOF fi echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:2444: checking for alloca" >&5 +echo "configure:2462: checking for alloca" >&5 if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2490: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_func_alloca_works=yes else @@ -2500,12 +2518,12 @@ EOF echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:2504: checking whether alloca needs Cray hooks" >&5 +echo "configure:2522: checking whether alloca needs Cray hooks" >&5 if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&6 if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:2534: checking for $ac_func" >&5 +echo "configure:2552: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2580: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -2585,7 +2603,7 @@ done fi echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:2589: checking stack direction for C alloca" >&5 +echo "configure:2607: checking stack direction for C alloca" >&5 if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -2593,7 +2611,7 @@ else ac_cv_c_stack_direction=0 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:2634: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_c_stack_direction=1 else @@ -2635,12 +2653,12 @@ fi echo $ac_n "checking for pid_t""... $ac_c" 1>&6 -echo "configure:2639: checking for pid_t" >&5 +echo "configure:2657: checking for pid_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2668,12 +2686,12 @@ EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 -echo "configure:2672: checking for size_t" >&5 +echo "configure:2690: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2701,12 +2719,12 @@ EOF fi echo $ac_n "checking for ssize_t""... $ac_c" 1>&6 -echo "configure:2705: checking for ssize_t" >&5 +echo "configure:2723: checking for ssize_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_ssize_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2734,12 +2752,12 @@ EOF fi echo $ac_n "checking for off_t""... $ac_c" 1>&6 -echo "configure:2738: checking for off_t" >&5 +echo "configure:2756: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2767,12 +2785,12 @@ EOF fi echo $ac_n "checking for mode_t""... $ac_c" 1>&6 -echo "configure:2771: checking for mode_t" >&5 +echo "configure:2789: checking for mode_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -2801,7 +2819,7 @@ fi echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6 -echo "configure:2805: checking for main in -lnsl" >&5 +echo "configure:2823: checking for main in -lnsl" >&5 ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2809,14 +2827,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lnsl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2838: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2844,7 +2862,7 @@ else fi echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6 -echo "configure:2848: checking for main in -lsocket" >&5 +echo "configure:2866: checking for main in -lsocket" >&5 ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2852,14 +2870,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2881: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2887,7 +2905,7 @@ else fi echo $ac_n "checking for main in -lgnumalloc""... $ac_c" 1>&6 -echo "configure:2891: checking for main in -lgnumalloc" >&5 +echo "configure:2909: checking for main in -lgnumalloc" >&5 ac_lib_var=`echo gnumalloc'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2895,14 +2913,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lgnumalloc $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2924: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2944,7 +2962,7 @@ else ;; *) echo $ac_n "checking for main in -lmalloc""... $ac_c" 1>&6 -echo "configure:2948: checking for main in -lmalloc" >&5 +echo "configure:2966: checking for main in -lmalloc" >&5 ac_lib_var=`echo malloc'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2952,14 +2970,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lmalloc $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:2981: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -2990,7 +3008,7 @@ fi esac fi echo $ac_n "checking for main in -lbsd""... $ac_c" 1>&6 -echo "configure:2994: checking for main in -lbsd" >&5 +echo "configure:3012: checking for main in -lbsd" >&5 ac_lib_var=`echo bsd'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -2998,14 +3016,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lbsd $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3027: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3033,7 +3051,7 @@ else fi echo $ac_n "checking for main in -lregex""... $ac_c" 1>&6 -echo "configure:3037: checking for main in -lregex" >&5 +echo "configure:3055: checking for main in -lregex" >&5 ac_lib_var=`echo regex'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3041,14 +3059,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lregex $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3070: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3074,7 +3092,7 @@ case "$host" in ;; *) echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6 -echo "configure:3078: checking for inet_aton in -lresolv" >&5 +echo "configure:3096: checking for inet_aton in -lresolv" >&5 ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3082,7 +3100,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lresolv $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3115: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3109,7 +3127,7 @@ fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 echo $ac_n "checking for inet_aton in -l44bsd""... $ac_c" 1>&6 -echo "configure:3113: checking for inet_aton in -l44bsd" >&5 +echo "configure:3131: checking for inet_aton in -l44bsd" >&5 ac_lib_var=`echo 44bsd'_'inet_aton | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3117,7 +3135,7 @@ else ac_save_LIBS="$LIBS" LIBS="-l44bsd $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3150: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3160,7 +3178,7 @@ else fi echo $ac_n "checking for main in -lresolv""... $ac_c" 1>&6 -echo "configure:3164: checking for main in -lresolv" >&5 +echo "configure:3182: checking for main in -lresolv" >&5 ac_lib_var=`echo resolv'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3168,14 +3186,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lresolv $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3197: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3205,7 +3223,7 @@ fi ;; esac echo $ac_n "checking for main in -lm""... $ac_c" 1>&6 -echo "configure:3209: checking for main in -lm" >&5 +echo "configure:3227: checking for main in -lm" >&5 ac_lib_var=`echo m'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3213,14 +3231,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lm $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3242: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3249,7 +3267,7 @@ fi echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 -echo "configure:3253: checking for crypt in -lcrypt" >&5 +echo "configure:3271: checking for crypt in -lcrypt" >&5 ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3257,7 +3275,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lcrypt $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3290: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3291,7 +3309,7 @@ fi echo $ac_n "checking for main in -lpthread""... $ac_c" 1>&6 -echo "configure:3295: checking for main in -lpthread" >&5 +echo "configure:3313: checking for main in -lpthread" >&5 ac_lib_var=`echo pthread'_'main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -3299,14 +3317,14 @@ else ac_save_LIBS="$LIBS" LIBS="-lpthread $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3328: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -3409,12 +3427,12 @@ for ac_func in \ do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3413: checking for $ac_func" >&5 +echo "configure:3431: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3459: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3475,12 +3493,12 @@ case "$host" in ;; *) echo $ac_n "checking for poll""... $ac_c" 1>&6 -echo "configure:3479: checking for poll" >&5 +echo "configure:3497: checking for poll" >&5 if eval "test \"`echo '$''{'ac_cv_func_poll'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3525: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_poll=yes" else @@ -3526,7 +3544,7 @@ fi esac echo $ac_n "checking if setresuid is implemented""... $ac_c" 1>&6 -echo "configure:3530: checking if setresuid is implemented" >&5 +echo "configure:3548: checking if setresuid is implemented" >&5 if eval "test \"`echo '$''{'ac_cv_func_setresuid'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -3534,7 +3552,7 @@ else { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext < @@ -3547,7 +3565,7 @@ else } EOF -if { (eval echo configure:3551: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3569: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_func_setresuid="yes" else @@ -3571,7 +3589,7 @@ EOF fi echo $ac_n "checking if GNUregex needs to be compiled""... $ac_c" 1>&6 -echo "configure:3575: checking if GNUregex needs to be compiled" >&5 +echo "configure:3593: checking if GNUregex needs to be compiled" >&5 if test "$ac_cv_func_regcomp" = "no" ; then USE_GNUREGEX="yes" else @@ -3603,12 +3621,12 @@ for ac_func in \ do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:3607: checking for $ac_func" >&5 +echo "configure:3625: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:3653: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -3659,12 +3677,12 @@ done echo $ac_n "checking Default FD_SETSIZE value""... $ac_c" 1>&6 -echo "configure:3663: checking Default FD_SETSIZE value" >&5 +echo "configure:3681: checking Default FD_SETSIZE value" >&5 if test "$cross_compiling" = yes; then DEFAULT_FD_SETSIZE=256 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3710: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then DEFAULT_FD_SETSIZE=`cat conftestval` else @@ -3707,12 +3725,12 @@ EOF echo $ac_n "checking Maximum number of filedescriptors we can open""... $ac_c" 1>&6 -echo "configure:3711: checking Maximum number of filedescriptors we can open" >&5 +echo "configure:3729: checking Maximum number of filedescriptors we can open" >&5 if test "$cross_compiling" = yes; then SQUID_MAXFD=256 else cat > conftest.$ac_ext < @@ -3764,7 +3782,7 @@ main() { } EOF -if { (eval echo configure:3768: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3786: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then SQUID_MAXFD=`cat conftestval` else @@ -3783,12 +3801,12 @@ EOF echo $ac_n "checking Default UDP send buffer size""... $ac_c" 1>&6 -echo "configure:3787: checking Default UDP send buffer size" >&5 +echo "configure:3805: checking Default UDP send buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_UDP_SO_SNDBUF=16384 else cat > conftest.$ac_ext < @@ -3807,7 +3825,7 @@ main () } EOF -if { (eval echo configure:3811: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3829: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then SQUID_UDP_SO_SNDBUF=`cat conftestval` else @@ -3826,12 +3844,12 @@ EOF echo $ac_n "checking Default UDP receive buffer size""... $ac_c" 1>&6 -echo "configure:3830: checking Default UDP receive buffer size" >&5 +echo "configure:3848: checking Default UDP receive buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_UDP_SO_RCVBUF=16384 else cat > conftest.$ac_ext < @@ -3850,7 +3868,7 @@ main () } EOF -if { (eval echo configure:3854: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3872: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then SQUID_UDP_SO_RCVBUF=`cat conftestval` else @@ -3869,12 +3887,12 @@ EOF echo $ac_n "checking Default TCP send buffer size""... $ac_c" 1>&6 -echo "configure:3873: checking Default TCP send buffer size" >&5 +echo "configure:3891: checking Default TCP send buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_TCP_SO_SNDBUF=16384 else cat > conftest.$ac_ext < @@ -3893,7 +3911,7 @@ main () } EOF -if { (eval echo configure:3897: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3915: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then SQUID_TCP_SO_SNDBUF=`cat conftestval` else @@ -3912,12 +3930,12 @@ EOF echo $ac_n "checking Default TCP receive buffer size""... $ac_c" 1>&6 -echo "configure:3916: checking Default TCP receive buffer size" >&5 +echo "configure:3934: checking Default TCP receive buffer size" >&5 if test "$cross_compiling" = yes; then SQUID_TCP_SO_RCVBUF=16384 else cat > conftest.$ac_ext < @@ -3936,7 +3954,7 @@ main () } EOF -if { (eval echo configure:3940: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:3958: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then SQUID_TCP_SO_RCVBUF=`cat conftestval` else @@ -3955,19 +3973,19 @@ EOF echo $ac_n "checking if sys_errlist is already defined""... $ac_c" 1>&6 -echo "configure:3959: checking if sys_errlist is already defined" >&5 +echo "configure:3977: checking if sys_errlist is already defined" >&5 if eval "test \"`echo '$''{'ac_cv_needs_sys_errlist'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < int main() { char *s = sys_errlist0; ; return 0; } EOF -if { (eval echo configure:3971: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:3989: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_needs_sys_errlist="no" else @@ -3989,16 +4007,16 @@ EOF fi echo $ac_n "checking for libresolv _dns_ttl_ hack""... $ac_c" 1>&6 -echo "configure:3993: checking for libresolv _dns_ttl_ hack" >&5 +echo "configure:4011: checking for libresolv _dns_ttl_ hack" >&5 cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:4020: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF diff --git a/configure.in b/configure.in index e5978bd5ce..d3a33284c0 100644 --- a/configure.in +++ b/configure.in @@ -3,13 +3,13 @@ dnl Configuration input file for Squid dnl dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9) dnl -dnl $Id: configure.in,v 1.99 1998/02/06 00:55:59 wessels Exp $ +dnl $Id: configure.in,v 1.100 1998/02/21 00:56:32 rousskov Exp $ dnl dnl dnl AC_INIT(src/main.c) AC_CONFIG_HEADER(include/autoconf.h) -AC_REVISION($Revision: 1.99 $)dnl +AC_REVISION($Revision: 1.100 $)dnl AC_PREFIX_DEFAULT(/usr/local/squid) AC_CONFIG_AUX_DIR(aux) @@ -307,6 +307,16 @@ AC_ARG_ENABLE(arp_acl, fi ]) +AC_ARG_ENABLE(alex_code, +[ --enable-alex_code Enable Alex's code], +[ if test "$enableval" = "yes" ; then + echo "Alex's code enabled" + AC_DEFINE(USE_ALEX_CODE) + else + echo "Alex's code DISABLED" + fi +]) + dnl Check for programs AC_PROG_CPP AC_PROG_INSTALL diff --git a/errors/English/ERR_CACHE_ACCESS_DENIED b/errors/English/ERR_CACHE_ACCESS_DENIED new file mode 100644 index 0000000000..0ae61b0142 --- /dev/null +++ b/errors/English/ERR_CACHE_ACCESS_DENIED @@ -0,0 +1,32 @@ + +ERROR: Cache Access Denied + + +

ERROR

+

Cache Access Denied

+
+

+While trying to retrieve the URL: +%U +

+The following error was encountered: +

    +
  • + +Cache Access Denied. + +
+

+ +

Sorry, you are not currently allowed to request: +

    %U
+from this cache until you have authenticated yourself. +

+ +

+You need to use Netscape version 2.0 or greater, or Microsoft Internet +Explorer 3.0, or an HTTP/1.1 compliant browser for this to work. Please +contact the cache administrator if you have +difficulties authenticating yourself or +change your default password. +

diff --git a/include/MemPool.h b/include/MemPool.h new file mode 100644 index 0000000000..a890cb0e11 --- /dev/null +++ b/include/MemPool.h @@ -0,0 +1,64 @@ +/* + * $Id: MemPool.h,v 1.2 1998/02/21 00:56:34 rousskov Exp $ + * + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _MEM_POOL_H_ +#define _MEM_POOL_H_ + +#include "Stack.h" + +/* see MemPool.c for documentation */ + +struct _MemPool { + /* public, read only */ + char *name; /* an optional label or name for this pool */ + size_t obj_size; + + /* protected, do not use these, use interface functions instead */ + char *buf; + Stack *static_stack; + Stack *dynamic_stack; + + size_t alloc_count; + size_t free_count; + size_t alloc_high_water; + + /* private, never touch this */ + char *_buf_end; +}; + +typedef struct _MemPool MemPool; + +extern MemPool *memPoolCreate(size_t preallocCnt, size_t dynStackCnt, size_t objSz, const char *poolName); +extern void memPoolDestroy(MemPool *mp); +extern void *memPoolGetObj(MemPool *mp); +extern void memPoolPutObj(MemPool *mp, void *obj); +extern const char *memPoolReport(MemPool *mp); + + +#endif /* ndef _MEM_POOL_H_ */ diff --git a/include/Stack.h b/include/Stack.h new file mode 100644 index 0000000000..042c201194 --- /dev/null +++ b/include/Stack.h @@ -0,0 +1,56 @@ +/* + * $Id: Stack.h,v 1.2 1998/02/21 00:56:35 rousskov Exp $ + * + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _STACK_H_ +#define _STACK_H_ + +/* see Stack.c for more documentation */ + +struct _Stack { + /* public, read only */ + size_t capacity; + int is_full; /* true if the stack is full */ + + u_num32 push_count; /* always grows, might overflow, use for stats only */ + u_num32 pop_count; /* always grows, might overflow, use for stats only */ + + /* protected, do not use these, use interface functions instead */ + size_t count; + void **buf; +}; + +typedef struct _Stack Stack; + +extern Stack *stackCreate(size_t capacity); +extern void stackDestroy(Stack *s); +extern void *stackPop(Stack *s); +extern void stackPush(Stack *s, void *obj); + + +#endif /* ndef _STACK_H_ */ diff --git a/include/autoconf.h.in b/include/autoconf.h.in index a31c2fe47b..2326734248 100644 --- a/include/autoconf.h.in +++ b/include/autoconf.h.in @@ -86,6 +86,9 @@ /* Define to use async disk I/O operations */ #undef USE_ASYNC_IO +/* Define to use alex's code */ +#undef USE_ALEX_CODE + /* * If you want to use Squid's ICMP features (highly recommended!) then * define this. When USE_ICMP is defined, Squid will send ICMP pings diff --git a/include/util.h b/include/util.h index 64cdc2f4a1..4a4b53aa24 100644 --- a/include/util.h +++ b/include/util.h @@ -1,5 +1,5 @@ /* - * $Id: util.h,v 1.39 1998/02/13 18:26:52 wessels Exp $ + * $Id: util.h,v 1.40 1998/02/21 00:56:36 rousskov Exp $ * * AUTHOR: Harvest Derived * @@ -138,6 +138,7 @@ extern int tvSubMsec(struct timeval, struct timeval); extern int tvSubUsec(struct timeval, struct timeval); extern double tvSubDsec(struct timeval, struct timeval); extern char *xstrncpy(char *, const char *, size_t); +extern size_t xcountws(const char *str); extern time_t parse_rfc1123(const char *str); extern void *xcalloc(int, size_t); extern void *xmalloc(size_t); diff --git a/include/version.h b/include/version.h index 0b0f13ba9c..01baa8e2ae 100644 --- a/include/version.h +++ b/include/version.h @@ -1,4 +1,5 @@ -/* $Id: version.h,v 1.100 1998/02/13 18:47:35 wessels Exp $ +/* + * $Id: version.h,v 1.101 1998/02/21 00:56:37 rousskov Exp $ * * SQUID_VERSION - String for version id of this distribution */ diff --git a/lib/Makefile.in b/lib/Makefile.in index 55148d4f11..a6c216e653 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -1,5 +1,5 @@ # -# $Id: Makefile.in,v 1.32 1998/02/02 19:39:26 wessels Exp $ +# $Id: Makefile.in,v 1.33 1998/02/21 00:56:37 rousskov Exp $ # prefix = @prefix@ top_srcdir = @top_srcdir@ @@ -37,6 +37,8 @@ UTILOBJS = rfc1123.o \ radix.o \ String.o \ stub_memaccount.o \ + MemPool.o \ + Stack.o \ $(LIBOBJS) REGEXOBJS = GNUregex.o DLMALLOCOBJS = malloc-2.6.4.o diff --git a/lib/MemPool.c b/lib/MemPool.c new file mode 100644 index 0000000000..ae8d1a498c --- /dev/null +++ b/lib/MemPool.c @@ -0,0 +1,193 @@ +/* + * $Id: MemPool.c,v 1.2 1998/02/21 00:56:38 rousskov Exp $ + * + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * MemPool handles allocation and accounting for fixed size objects or buffers. + * It optimizes run-time allocation using a pre-allocated continuos pool of free + * buffers. MemPool does not know about the contents of a buffer. + */ + +/* + * Warning: Never use *alloc or *free on objects maintained by MemPool! + */ + +/* + * To-do: + * - src/stmem routines could use some of lib/MemPool stuff + */ + + +#if 0 + +Synopsis: + + /* + * creating a pool named "urls" for objects of size objSz with objCnt + * pre-allocated objects and 10% for dynamic stack. Any of the first two + * parameters can be 0. If name is NULL "anonymous" will be used instead + */ + MemPool *mp1 = memPoolCreate(objCnt, objCnt/10, objSz, "urls"); + + /* + * getting a new object from a pool; object buffer is initialized with 0s + */ + void *buf = memPoolGetObj(mp1); + + /* + * returning an object back + */ + memPoolPutObj(mp1, buf); + + /* + * accounting: generate report as an ASCII string + * warning: static buffer is used, strdup your copy! + */ + char *report = xstrdup(memPoolReport()); + + /* destroy your pools when done playing! */ + memPoolDestroy(mp1); + +#endif /* synopsis */ + +#include "config.h" +#if HAVE_ASSERT_H +#include +#endif +#include "util.h" +#include "snprintf.h" +#include "MemPool.h" + + +MemPool * +memPoolCreate(size_t preallocCnt, size_t dynStackCnt, size_t objSz, const char *poolName) +{ + MemPool *mp = xcalloc(1, sizeof(MemPool)); + mp->buf = xcalloc(preallocCnt, objSz); + mp->obj_size = objSz; + mp->name = xstrdup(poolName ? poolName : "anonymous"); + mp->_buf_end = mp->buf + objSz*preallocCnt; /* internal, never dereference this! */ + mp->static_stack = stackCreate(preallocCnt); + mp->dynamic_stack = stackCreate(dynStackCnt); + /* other members are initialized with 0 because of calloc() */ + /* push all pre-allocated memory on stack because it is currently free */ + while(preallocCnt-- > 0) + stackPush(mp->static_stack, mp->buf + objSz*preallocCnt); + return mp; +} + +void +memPoolDestroy(MemPool *mp) +{ + assert(mp); + /* could also warn if some objects are left */ + stackDestroy(mp->static_stack); + stackDestroy(mp->dynamic_stack); + xfree(mp->buf); + xfree(mp->name); + xfree(mp); +} + +/* + * find a free buffer: + * if none on the pool stack, use alloc stack; + * if none there, use alloc + * never fails + */ +void * +memPoolGetObj2(MemPool *mp) +{ + assert(mp); + if (mp->static_stack->count) + return stackPop(mp->static_stack); + else + if (mp->dynamic_stack->count) + return stackPop(mp->dynamic_stack); + /* have to alloc, monitor high whater mark */ + if (++mp->alloc_count - mp->free_count > mp->alloc_high_water) + mp->alloc_high_water = mp->alloc_count - mp->free_count; + return xcalloc(1, mp->obj_size); +} + +void * +memPoolGetObj(MemPool *mp) +{ + void *obj = memPoolGetObj2(mp); + /*printf("memPoolGetObj: %p : %d -> %d , %d >= %d\n", obj, mp->static_stack->count, mp->dynamic_stack->count, mp->alloc_count, mp->free_count);*/ + return obj; +} + +/* + * return object to the pool; put on the corresponding stack or free if + * corresponding stack is full + */ +void +memPoolPutObj(MemPool *mp, void *obj) +{ + assert(mp); + /*printf("memPoolPutObj: %p : %d >= %d\n", obj, mp->alloc_count, mp->free_count);*/ + /* static object? */ + if (mp->buf <= (char*)obj && mp->_buf_end > (char*)obj) { + assert(!mp->static_stack->is_full); /* never full if we got here! */ + stackPush(mp->static_stack, obj); + } else + /* dynamic object, but stack may be full */ + if (!mp->dynamic_stack->is_full) { + assert(mp->alloc_count); + stackPush(mp->dynamic_stack, obj); + } else { + /* free-ing is the last option */ + mp->free_count++; + assert(mp->free_count <= mp->alloc_count); + xfree(obj); /* do this after assert */ + } +} + +const char * +memPoolReport(MemPool *mp) +{ + static char buf[512]; /* we do not use LOCALL_ARRAY in squid/lib, do we? */ + + assert(mp); + snprintf(buf, sizeof(buf), + "pool %s: obj_sz: %ud cap: %ud/%ud " + "stat: +%uld-%uld dyn: +%uld-%uld alloc: +%uld/-%uld<%uld", + mp->name, + mp->obj_size, + mp->static_stack->capacity, + mp->dynamic_stack->capacity, + mp->static_stack->push_count, + mp->static_stack->pop_count, + mp->dynamic_stack->push_count, + mp->dynamic_stack->pop_count, + mp->alloc_count, + mp->free_count, + mp->alloc_high_water); + + return buf; +} diff --git a/lib/Stack.c b/lib/Stack.c new file mode 100644 index 0000000000..cc55683bd6 --- /dev/null +++ b/lib/Stack.c @@ -0,0 +1,116 @@ +/* + * $Id: Stack.c,v 1.2 1998/02/21 00:56:38 rousskov Exp $ + * + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Stack is a (void*) stack with fixed capacity with limited accounting. + * Errors are handled with asserts. + */ + + +/* + * To-do: + * - stack that grows as needed if a given delta is non zero + */ + + +#if 0 + +Synopsis: + + /* + * creating a stack that can hold up to objCnt pointers. + * If objCnt is zero, the stack is always full (disabled) + */ + Stack *s1 = stackCreate(objCnt); + Stack *s2 = stackCreate(objCnt*2); + + /* + * pop/push works as expected; it is OK to push a null pointer + */ + if (!s2->is_full && s1->count) + stackPush(s2, stackPop(s1)); + + /* destroying a stack */ + stackDestroy(s1); + +#endif /* Synopsis */ + +#include "config.h" +#if HAVE_ASSERT_H +#include +#endif +#include "util.h" +#include "Stack.h" + +/* performance hack instead of non-ANSI inline function */ +#define stackIsFull(s) (s->count >= s->capacity) + +Stack * +stackCreate(size_t capacity) +{ + Stack *s = xcalloc(1, sizeof(Stack)); + s->buf = capacity > 0 ? xcalloc(capacity, sizeof(void*)) : NULL; + s->capacity = capacity; + s->count = 0; + s->is_full = stackIsFull(s); + /* other members are set to 0 in calloc */ + return s; +} + +void +stackDestroy(Stack *s) +{ + assert(s); + /* could also warn if some objects are left */ + if (s->buf) + xfree(s->buf); + xfree(s); +} + +void * +stackPop(Stack *s) +{ + void *popped; + assert(s); + assert(s->count); + popped = s->buf[--s->count]; + s->is_full = stackIsFull(s); + s->pop_count++; /* might overflow eventually, but ok */ + return popped; +} + +void +stackPush(Stack *s, void *obj) +{ + assert(s); + assert(!s->is_full); + s->buf[s->count++] = obj; + s->is_full = stackIsFull(s); + s->push_count++; /* might overflow eventually, but ok */ +} diff --git a/lib/util.c b/lib/util.c index bc962a0f30..083bca4903 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,6 +1,6 @@ /* - * $Id: util.c,v 1.44 1998/02/18 08:21:31 wessels Exp $ + * $Id: util.c,v 1.45 1998/02/21 00:56:39 rousskov Exp $ * * DEBUG: * AUTHOR: Harvest Derived @@ -587,11 +587,10 @@ xstrdup(const char *s) } exit(1); } - sz = strlen(s); - p = xmalloc((size_t) sz + 1); - memcpy(p, s, sz); /* copy string */ - p[sz] = '\0'; /* terminate string */ - return (p); + sz = strlen(s)+1; + p = xmalloc(sz); + memcpy(p, s, sz); /* copy string, including terminating character */ + return p; } /* @@ -655,17 +654,31 @@ tvSubDsec(struct timeval t1, struct timeval t2) /* * xstrncpy() - similar to strncpy(3) but terminates string - * always with '\0' if n != 0, and doesn't do padding + * always with '\0' if (n != 0 and dst != NULL), + * and doesn't do padding */ char * xstrncpy(char *dst, const char *src, size_t n) { - if (n == 0) - return dst; - if (src == NULL) + if (!n || !dst) return dst; - while (--n != 0 && *src != '\0') - *dst++ = *src++; + if (src) + while (--n != 0 && *src != '\0') + *dst++ = *src++; *dst = '\0'; return dst; } + +/* returns the number of leading white spaces in str; handy in skipping ws */ +size_t +xcountws(const char *str) +{ + size_t count = 0; + if (str) { + while (isspace(*str)) { + str++; + count++; + } + } + return count; +} diff --git a/src/HttpBody.cc b/src/HttpBody.cc new file mode 100644 index 0000000000..ad3e827692 --- /dev/null +++ b/src/HttpBody.cc @@ -0,0 +1,89 @@ +/* + * $Id: HttpBody.cc,v 1.2 1998/02/21 00:56:40 rousskov Exp $ + * + * DEBUG: section ?? HTTP Body + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "squid.h" + + +/* local constants */ + +/* local routines */ + + +void +httpBodyInit(HttpBody *body) +{ + body->buf = NULL; + body->size = 0; + body->freefunc = NULL; +} + +void +httpBodyClean(HttpBody *body) +{ + assert(body); + if (body->buf) { + assert(body->freefunc); + (*body->freefunc)(body->buf); + } + body->buf = NULL; + body->size = 0; +} + +void +httpBodySet(HttpBody *body, const char *buf, int size, FREE *freefunc) +{ + assert(body); + assert(!body->buf); + assert(buf); + assert(size); + assert(buf[size-1] == '\0'); /* paranoid */ + if (!freefunc) { /* they want us to make our own copy */ + body->buf = xmalloc(size); + xmemcpy(body->buf, buf, size); + freefunc = &xfree; + } + body->freefunc = freefunc; + body->size = size; +} + +void +httpBodyPackInto(const HttpBody *body, Packer *p) +{ + assert(body && p); + /* assume it was a 0-terminating buffer */ + if (body->size) + packerAppend(p, body->buf, body->size-1); +} + +const char * +httpBodyPtr(const HttpBody *body) +{ + return body->buf ? body->buf : ""; +} diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc new file mode 100644 index 0000000000..b55fddda47 --- /dev/null +++ b/src/HttpHeader.cc @@ -0,0 +1,1690 @@ +/* + * $Id: HttpHeader.cc,v 1.2 1998/02/21 00:56:41 rousskov Exp $ + * + * DEBUG: section 55 General HTTP Header + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "squid.h" +#include "MemPool.h" +#include "HttpHeader.h" + +/* + On naming conventions: + + HTTP/1.1 defines message-header as + + message-header = field-name ":" [ field-value ] CRLF + field-name = token + field-value = *( field-content | LWS ) + + HTTP/1.1 does not give a name name a group of all message-headers in a message. + Squid 1.1 seems to refer to that group _plus_ start-line as "headers". + + HttpHeader is an object that represents all message-headers in a message. + HttpHeader does not manage start-line. + + HttpHeader is implemented as a collection of header "entries" + An entry is a (field_id, field) pair where + - field_id is one of the http_hdr_type ids, + - field is a compiled(parsed) image of message-header. +*/ + + +/* + * local types + */ + +/* HTTP/1.1 extension-header */ +struct _HttpHeaderExtField { + char *name; /* field-name from HTTP/1.1 (no column after name!) */ + char *value; /* field-value from HTTP/1.1 */ +}; + +/* possible types for fields */ +typedef enum { + ftInvalid = HDR_ENUM_END, /* to catch nasty errors with hdr_id<->fld_type clashes */ + ftInt, + ftPChar, + ftDate_1123, + ftPSCC, + ftPExtField, +} field_type; + +/* + * HttpHeader entry + * ( the concrete type of entry.field is Headers[id].type ) + */ +struct _HttpHeaderEntry { + field_store field; + http_hdr_type id; +}; + +/* constant attributes of fields */ +typedef struct { + const char *name; + http_hdr_type id; + field_type type; + int name_len; +} field_attrs_t; + +/* use HttpHeaderPos as opaque type, do not interpret */ +typedef ssize_t HttpHeaderPos; +/* use this and only this to initialize HttpHeaderPos */ +#define HttpHeaderInitPos (-1) + + +#if 0 /* moved to HttpHeader.h */ +typedef struct _HttpHeaderEntry HttpHeaderEntry; +struct _HttpHeader { + /* public, read only */ + int emask; /* bits set for present entries */ + + /* protected, do not use these, use interface functions instead */ + int capacity; /* max #entries before we have to grow */ + int ucount; /* #entries used, including holes */ + HttpHeaderEntry *entries; +}; +#endif + + +/* + * local constants and vars + */ + +/* + * A table with major attributes for every known field. + * We calculate name lengths and reorganize this array on start up. + * After reorganization, field id can be used as an index to the table. + */ +static field_attrs_t Headers[] = { + { "Accept", HDR_ACCEPT, ftPChar }, + { "Age", HDR_AGE, ftInt }, + { "Cache-Control", HDR_CACHE_CONTROL, ftPSCC }, + { "Connection", HDR_CONNECTION, ftPChar }, /* for now */ + { "Content-Encoding", HDR_CONTENT_ENCODING,ftPChar }, + { "Content-Length", HDR_CONTENT_LENGTH, ftInt }, + { "Content-MD5", HDR_CONTENT_MD5, ftPChar }, /* for now */ + { "Content-Type", HDR_CONTENT_TYPE, ftPChar }, + { "Date", HDR_DATE, ftDate_1123 }, + { "Etag", HDR_ETAG, ftPChar }, /* for now */ + { "Expires", HDR_EXPIRES, ftDate_1123 }, + { "Host", HDR_HOST, ftPChar }, + { "If-Modified-Since", HDR_IMS, ftDate_1123 }, + { "Last-Modified", HDR_LAST_MODIFIED, ftDate_1123 }, + { "Location", HDR_LOCATION, ftPChar }, + { "Max-Forwards", HDR_MAX_FORWARDS, ftInt }, + { "Proxy-Authenticate",HDR_PROXY_AUTHENTICATE,ftPChar }, + { "Public", HDR_PUBLIC, ftPChar }, + { "Retry-After", HDR_RETRY_AFTER, ftPChar }, /* for now */ + /* fix this: make count-but-treat as OTHER mask @?@ @?@ */ + { "Set-Cookie:", HDR_SET_COOKIE, ftPChar }, + { "Upgrade", HDR_UPGRADE, ftPChar }, /* for now */ + { "Warning", HDR_WARNING, ftPChar }, /* for now */ + { "WWW-Authenticate", HDR_WWW_AUTHENTICATE,ftPChar }, + { "Proxy-Connection", HDR_PROXY_KEEPALIVE, ftInt }, /* true/false */ + { "Other:", HDR_OTHER, ftPExtField } /* ':' will not allow matches */ +}; + +/* this table is used for parsing server cache control header */ +static field_attrs_t SccAttrs[] = { + { "public", SCC_PUBLIC }, + { "private", SCC_PRIVATE }, + { "no-cache", SCC_NO_CACHE }, + { "no-store", SCC_NO_STORE }, + { "no-transform", SCC_NO_TRANSFORM }, + { "must-revalidate", SCC_MUST_REVALIDATE }, + { "proxy-revalidate", SCC_PROXY_REVALIDATE }, + { "max-age", SCC_MAX_AGE } +}; + +/* + * headers with field values defined as #(values) in HTTP/1.1 + * + * We have listed all possible list headers according to + * draft-ietf-http-v11-spec-rev-01.txt. Headers that are currently not + * recognized, are commented out. + */ +static int ListHeadersMask = 0; /* set run-time using ListHeaders */ +static http_hdr_type ListHeaders[] = { + HDR_ACCEPT, + /* HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, */ + /* HDR_ACCEPT_RANGES, */ + /* HDR_ALLOW, */ + HDR_CACHE_CONTROL, HDR_CONNECTION, + HDR_CONTENT_ENCODING, + /* HDR_CONTENT_LANGUAGE, HDR_IF_MATCH, HDR_IF_NONE_MATCH, + HDR_PRAGMA, HDR_TRANSFER_ENCODING, */ + HDR_UPGRADE, /* HDR_VARY, */ + /* HDR_VIA, HDR_WARNING, */ + HDR_WWW_AUTHENTICATE, + /* HDR_EXPECT, HDR_TE, HDR_TRAILER */ +}; + +static int ReplyHeadersMask = 0; /* set run-time using ReplyHeaders */ +static http_hdr_type ReplyHeaders[] = { + HDR_ACCEPT, HDR_AGE, HDR_CACHE_CONTROL, HDR_CONTENT_LENGTH, + HDR_CONTENT_MD5, HDR_CONTENT_TYPE, HDR_DATE, HDR_ETAG, HDR_EXPIRES, + HDR_LAST_MODIFIED, HDR_LOCATION, HDR_MAX_FORWARDS, HDR_PUBLIC, HDR_RETRY_AFTER, + HDR_SET_COOKIE, HDR_UPGRADE, HDR_WARNING, HDR_PROXY_KEEPALIVE, HDR_OTHER +}; + +static int RequestHeadersMask = 0; /* set run-time using RequestHeaders */ +static http_hdr_type RequestHeaders[] = { + HDR_OTHER +}; + +static const char *KnownSplitableFields[] = { + "Connection", "Range" +}; +/* if you must have KnownSplitableFields empty, set KnownSplitableFieldCount to 0 */ +static const int KnownSplitableFieldCount = sizeof(KnownSplitableFields)/sizeof(*KnownSplitableFields); + +/* headers accounting */ +#define INIT_FIELDS_PER_HEADER 8 +static u_num32 shortHeadersCount = 0; +static u_num32 longHeadersCount = 0; + +typedef struct { + const char *label; + int parsed; + int misc[HDR_ENUM_END]; +} HttpHeaderStats; + +#if 0 /* not used, add them later @?@ */ +static struct { + int parsed; + int misc[HDR_MISC_END]; + int cc[SCC_ENUM_END]; +} ReplyHeaderStats; + +#endif /* if 0 */ + +/* recycle bin for short strings (32KB only) */ +static const size_t shortStrSize = 32; /* max size of a recyclable string */ +static const size_t shortStrPoolCount = (32*1024)/32; /* sync this with shortStrSize */ +static MemPool *shortStrings = NULL; + +/* long strings accounting */ +static u_num32 longStrAllocCount = 0; +static u_num32 longStrFreeCount = 0; +static u_num32 longStrHighWaterCount = 0; +static size_t longStrAllocSize = 0; +static size_t longStrFreeSize = 0; +static size_t longStrHighWaterSize = 0; + + +/* local routines */ + +#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END) + +static void httpHeaderInitAttrTable(field_attrs_t *table, int count); +static int httpHeaderCalcMask(const int *enums, int count); +static HttpHeaderEntry *httpHeaderGetEntry(const HttpHeader *hdr, HttpHeaderPos *pos); +static void httpHeaderDelAt(HttpHeader *hdr, HttpHeaderPos pos); +static void httpHeaderAddParsedEntry(HttpHeader *hdr, HttpHeaderEntry *e); +static void httpHeaderAddNewEntry(HttpHeader *hdr, const HttpHeaderEntry *e); +static void httpHeaderSet(HttpHeader *hdr, http_hdr_type id, const field_store value); +static void httpHeaderSyncMasks(HttpHeader *hdr, const HttpHeaderEntry *e, int add); +static void httpHeaderSyncStats(HttpHeader *hdr, const HttpHeaderEntry *e); +static int httpHeaderIdByName(const char *name, int name_len, const field_attrs_t *attrs, int end, int mask); +static void httpHeaderGrow(HttpHeader *hdr); + +static void httpHeaderEntryInit(HttpHeaderEntry *e, http_hdr_type id, field_store field); +static void httpHeaderEntryClean(HttpHeaderEntry *e); +static int httpHeaderEntryParseInit(HttpHeaderEntry *e, const char *field_start, const char *field_end, int mask); +static int httpHeaderEntryParseExtFieldInit(HttpHeaderEntry *e, int id, const HttpHeaderExtField *f); +static int httpHeaderEntryParseByTypeInit(HttpHeaderEntry *e, int id, const HttpHeaderExtField *f); +static HttpHeaderEntry httpHeaderEntryClone(const HttpHeaderEntry *e); +static void httpHeaderEntryPackInto(const HttpHeaderEntry *e, Packer *p); +static void httpHeaderEntryPackByType(const HttpHeaderEntry *e, Packer *p); +static void httpHeaderEntryJoinWith(HttpHeaderEntry *e, const HttpHeaderEntry *newe); +static int httpHeaderEntryIsValid(const HttpHeaderEntry *e); +static const char *httpHeaderEntryName(const HttpHeaderEntry *e); + +static void httpHeaderFieldInit(field_store *field); +static field_store httpHeaderFieldDup(field_type type, field_store value); +static field_store httpHeaderFieldBadValue(field_type type); + +static HttpScc *httpSccCreate(); +static HttpScc *httpSccParseCreate(const char *str); +static void httpSccParseInit(HttpScc *scc, const char *str); +static void httpSccDestroy(HttpScc *scc); +static HttpScc *httpSccDup(HttpScc *scc); +static void httpSccPackValueInto(HttpScc *scc, Packer *p); +static void httpSccJoinWith(HttpScc *scc, HttpScc *new_scc); + +static HttpHeaderExtField *httpHeaderExtFieldCreate(const char *name, const char *value); +static HttpHeaderExtField *httpHeaderExtFieldParseCreate(const char *field_start, const char *field_end); +static void httpHeaderExtFieldDestroy(HttpHeaderExtField *f); +static HttpHeaderExtField *httpHeaderExtFieldDup(HttpHeaderExtField *f); + +static void httpHeaderStoreAReport(StoreEntry *e, void (*reportPacker)(Packer *p)); +static void httpHeaderPackReport(Packer *p); +static void httpHeaderPackReqReport(Packer *p); +static void httpHeaderPackRepReport(Packer *p); + + +#if 0 +static void httpHeaderAddField(HttpHeader *hdr, HttpHeaderField *fld); +static void httpHeaderAddSingleField(HttpHeader *hdr, HttpHeaderField *fld); +static void httpHeaderAddListField(HttpHeader *hdr, HttpHeaderField *fld); +static void httpHeaderCountField(HttpHeader *hdr, HttpHeaderField *fld); +static void httpHeaderCountSCCField(HttpHeader *hdr, HttpHeaderField *fld); +static int httpHeaderFindFieldType(HttpHeaderField *fld, const field_attrs_t *attrs, int end, int mask); +static HttpHeaderField *httpHeaderFieldCreate(const char *name, const char *value); +static HttpHeaderField *httpHeaderFieldParseCreate(const char *field_start, const char *field_end); +static void httpHeaderFieldDestroy(HttpHeaderField *f); +static size_t httpHeaderFieldBufSize(const HttpHeaderField *fld); +static int httpHeaderFieldIsList(const HttpHeaderField *fld); +static void httpHeaderStoreAReport(Packer *p, HttpHeaderStats *stats); +#endif + +static char *dupShortStr(const char *str); +static char *dupShortBuf(const char *str, size_t len); +static char *appShortStr(char *str, const char *app_str); +static char *allocShortBuf(size_t size); +static void freeShortString(char *str); + +static int strListGetItem(const char *str, char del, const char **item, int *ilen, const char **pos); +static const char *getStringPrefix(const char *str); + + +/* delete this when everybody remembers that ':' is not a part of a name */ +#define conversion_period_name_check(name) assert(!strchr((name), ':')) + +/* handy to determine the #elements in a static array */ +#define countof(arr) (sizeof(arr)/sizeof(*arr)) + +/* + * Module initialization routines + */ + +void +httpHeaderInitModule() +{ + /* paranoid check if smbd put a big object into field_store */ + assert(sizeof(field_store) == 4); + /* have to force removal of const here */ + httpHeaderInitAttrTable((field_attrs_t *)Headers, countof(Headers)); + httpHeaderInitAttrTable((field_attrs_t *)SccAttrs, countof(SccAttrs)); + /* create masks */ + ListHeadersMask = httpHeaderCalcMask((const int*)ListHeaders, countof(ListHeaders)); + ReplyHeadersMask = httpHeaderCalcMask((const int*)ReplyHeaders, countof(ReplyHeaders)); + RequestHeadersMask = httpHeaderCalcMask((const int*)RequestHeaders, countof(RequestHeaders)); + /* create a pool of short strings @?@ we never destroy it! */ + shortStrings = memPoolCreate(shortStrPoolCount, shortStrPoolCount/10, shortStrSize, "shortStr"); +} + +static void +httpHeaderInitAttrTable(field_attrs_t *table, int count) +{ + int i; + assert(table); + assert(count > 1); /* to protect from buggy "countof" implementations */ + + /* reorder so that .id becomes an index */ + for (i = 0; i < count;) { + const int id = table[i].id; + assert(id >= 0 && id < count); /* sanity check */ + assert(id >= i); /* entries prior to i have been indexed already */ + if (id != i) { /* out of order */ + const field_attrs_t fa = table[id]; + assert(fa.id != id); /* avoid endless loops */ + table[id] = table[i]; /* swap */ + table[i] = fa; + } else + i++; /* make progress */ + } + + /* calculate name lengths */ + for (i = 0; i < count; ++i) { + assert(table[i].name); + table[i].name_len = strlen(table[i].name); + tmp_debug(here) ("hdr table entry[%d]: %s (%d)\n", i, table[i].name, table[i].name_len); + assert(table[i].name_len); + } +} + +/* calculates a bit mask of a given array (move this to lib/uitils) @?@ */ +static int +httpHeaderCalcMask(const int *enums, int count) +{ + int i; + int mask = 0; + assert(enums); + assert(count < sizeof(int)*8); /* check for overflow */ + + for (i = 0; i < count; ++i) { + assert(enums[i] < sizeof(int)*8); /* check for overflow again */ + assert(!EBIT_TEST(mask,enums[i])); /* check for duplicates */ + EBIT_SET(mask, enums[i]); + } + return mask; +} + + +/* + * HttpHeader Implementation + */ + + +HttpHeader * +httpHeaderCreate() +{ + HttpHeader *hdr = xmalloc(sizeof(HttpHeader)); + httpHeaderInit(hdr); + return hdr; +} + + +/* "create" for non-alloc objects; also used by real Create */ +void +httpHeaderInit(HttpHeader *hdr) +{ + assert(hdr); + memset(hdr, 0, sizeof(*hdr)); + tmp_debug(here) ("init hdr: %p\n", hdr); +} + +void +httpHeaderClean(HttpHeader *hdr) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + + tmp_debug(here) ("cleaning hdr: %p\n", hdr); + assert(hdr); + + if (hdr->capacity > INIT_FIELDS_PER_HEADER) + longHeadersCount++; + else + shortHeadersCount++; + + while (httpHeaderGetEntry(hdr, &pos)) + httpHeaderDelAt(hdr, pos); + xfree(hdr->entries); + hdr->emask = 0; + hdr->entries = NULL; + hdr->capacity = hdr->ucount = 0; +} + +void +httpHeaderDestroy(HttpHeader *hdr) +{ + httpHeaderClean(hdr); + xfree(hdr); +} + +/* create a copy of self */ +HttpHeader * +httpHeaderClone(HttpHeader *hdr) +{ + HttpHeader *clone = httpHeaderCreate(); + HttpHeaderEntry *e; + HttpHeaderPos pos = HttpHeaderInitPos; + + tmp_debug(here) ("cloning hdr: %p -> %p\n", hdr, clone); + + while ((e = httpHeaderGetEntry(hdr, &pos))) { + HttpHeaderEntry e_clone = httpHeaderEntryClone(e); + httpHeaderAddNewEntry(clone, &e_clone); + } + + return clone; +} + +/* just handy in parsing: resets and returns false */ +static int +httpHeaderReset(HttpHeader *hdr) { + httpHeaderClean(hdr); + httpHeaderInit(hdr); + return 0; +} + +/* + * Note: currently, in most cases, we discard a field if we cannot parse it. We + * also truncate some field values (e.g. content-type). Thus, we may not + * forward exactly what was received. However, Squid keeps a copy of "raw" + * headers anyway, so we are safe until that changes. A possible alternative + * would be to store any buggy field as HDR_OTHER, but that still leaves a + * problem with truncated fields. The later one requires a better parser and + * additional storage, I guess. + */ +int +httpHeaderParse(HttpHeader *hdr, const char *header_start, const char *header_end) +{ + const char *field_start = header_start; + HttpHeaderEntry e; + int mask = 0; + + assert(hdr); + assert(header_start && header_end); + tmp_debug(here) ("parsing hdr: %p\n", hdr); + /* select appropriate field mask */ + mask = (/* fix this @?@ @?@ */ 1 ) ? ReplyHeadersMask : RequestHeadersMask; + /* commonn format headers are ":[ws]" lines delimited by */ + while (field_start < header_end) { + const char *field_end = field_start + strcspn(field_start, "\r\n"); + /*tmp_debug(here) ("found end of field: %d\n", (int)*field_end);*/ + if (!*field_end) + return httpHeaderReset(hdr); /* missing */ + /* + * If we fail to parse a field, we ignore that field. We also could + * claim that the whole header is invalid. The latter is safer, but less + * robust. Note that we should be able to parse any commonn format field + */ + if (!httpHeaderEntryParseInit(&e, field_start, field_end, mask)) + debug(55, 1) ("warning: ignoring unparseable http header field near '%s'\n", + getStringPrefix(field_start)); + else + httpHeaderAddParsedEntry(hdr, &e); + /* + * Note that we init() e, bit never clean() it which is equivalent to * + * creating a fresh entry on each loop iteration; thus, it is safe to * + * add e without dup()-ing it. + */ + field_start = field_end; + /* skip CRLF */ + if (*field_start == '\r') field_start++; + if (*field_start == '\n') field_start++; + } + return 1; /* even if no fields where found, they could be optional! */ +} + +/* + * packs all the entries into the buffer, + * returns number of bytes packed including terminating '\0' + */ +void +httpHeaderPackInto(const HttpHeader *hdr, Packer *p) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + const HttpHeaderEntry *e; + assert(hdr && p); + tmp_debug(here) ("packing hdr: %p\n", hdr); + /* pack all entries one by one */ + while ((e = httpHeaderGetEntry(hdr, &pos))) { + httpHeaderEntryPackInto(e, p); + } +} + +/* returns next valid entry */ +static HttpHeaderEntry * +httpHeaderGetEntry(const HttpHeader *hdr, HttpHeaderPos *pos) +{ + assert(hdr && pos); + assert(*pos >= HttpHeaderInitPos && *pos < hdr->capacity); + tmp_debug(here) ("searching next e in hdr %p from %d\n", hdr, *pos); + for ((*pos)++; *pos < hdr->ucount; (*pos)++) { + HttpHeaderEntry *e = hdr->entries + *pos; + if (httpHeaderEntryIsValid(e)) { + tmp_debug(here)("%p returning: %s at %d\n", + hdr, httpHeaderEntryName(e), *pos); + return e; + } + } + tmp_debug(here) ("failed to find entry in hdr %p\n", hdr); + return NULL; +} + +/* + * returns a pointer to a specified entry and updates pos; + * note that we search from the very begining so it does not make much sense to + * ask for HDR_OTHER entries since there could be more than one. + */ +static HttpHeaderEntry * +httpHeaderFindEntry(const HttpHeader *hdr, http_hdr_type id, HttpHeaderPos *pos) +{ + HttpHeaderPos p; + HttpHeaderEntry *e; + int is_absent; + assert(hdr); + assert_eid(id); + assert(id != HDR_OTHER); + + tmp_debug(here) ("finding entry %d in hdr %p\n", id, hdr); + /* check mask first @?@ @?@ remove double checking and asserts when done */ + is_absent = (id != HDR_OTHER && !EBIT_TEST(hdr->emask, id)); + if (!pos) pos = &p; + *pos = HttpHeaderInitPos; + while ((e = httpHeaderGetEntry(hdr, pos))) { + if (e->id == id) { + assert(!is_absent); + return e; + } + } + assert(!EBIT_TEST(hdr->emask, id)); + return NULL; +} + +/* + * deletes all field(s) with a given name if any, returns #fields deleted; + * used to process Connection: header and delete fields in "paranoid" setup + */ +int +httpHeaderDelFields(HttpHeader *hdr, const char *name) +{ + int count = 0; + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + + tmp_debug(here) ("deleting '%s' fields in hdr %p\n", name, hdr); + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (!strcmp(httpHeaderEntryName(e), name)) { + httpHeaderDelAt(hdr, pos); + count++; + } + } + return count; +} + +/* + * deletes an entry at pos and leaves a gap; leaving a gap makes it + * possible to iterate(search) and delete fields at the same time + */ +static void +httpHeaderDelAt(HttpHeader *hdr, HttpHeaderPos pos) +{ + HttpHeaderEntry *e; + assert(hdr); + assert(pos >= 0 && pos < hdr->ucount); + e = hdr->entries + pos; + tmp_debug(here) ("%p deling entry at %d: id: %d (%p:%p)\n", + hdr, pos, e->id, hdr->entries, e); + /* sync masks */ + httpHeaderSyncMasks(hdr, e, 0); + httpHeaderEntryClean(e); + if (pos == hdr->ucount) + hdr->ucount--; +} + +/* + * adds parsed entry (joins entries if neeeded); assumes e.value is dup()-ed and + * clean()s it if needed. Thus, "e" should be treated as uninitialized after + * this function returns. + */ +static void +httpHeaderAddParsedEntry(HttpHeader *hdr, HttpHeaderEntry *e) +{ + HttpHeaderEntry *olde; + assert(hdr); + assert_eid(e->id); + + tmp_debug(here) ("%p adding parsed entry %d\n", hdr, e->id); + + /* there is no good reason to add invalid entries */ + if (!httpHeaderEntryIsValid(e)) + return; + + olde = (e->id == HDR_OTHER) ? NULL : httpHeaderFindEntry(hdr, e->id, NULL); + if (olde) { + if (EBIT_TEST(ListHeadersMask, e->id)) + httpHeaderEntryJoinWith(olde, e); + else + debug(55, 1) ("ignoring duplicate header: %s\n", httpHeaderEntryName(e)); + httpHeaderEntryClean(e); + } else { + /* actual add */ + httpHeaderAddNewEntry(hdr, e); + } + tmp_debug(here) ("%p done adding parsed entry %d\n", hdr, e->id); +} + +/* + * adds a new entry (low level append, does not check if entry is new) note: we + * copy e value, thus, e can point to a tmp variable (but e->field is not dupped!) + */ +static void +httpHeaderAddNewEntry(HttpHeader *hdr, const HttpHeaderEntry *e) +{ + assert(hdr && e); + if (hdr->ucount >= hdr->capacity) + httpHeaderGrow(hdr); + tmp_debug(here) ("%p adding entry: %d at %d, (%p:%p)\n", + hdr, e->id, hdr->ucount, + hdr->entries, hdr->entries + hdr->ucount); + hdr->entries[hdr->ucount++] = *e; + /* sync masks */ + httpHeaderSyncMasks(hdr, e, 1); + /* sync accounting */ + httpHeaderSyncStats(hdr, e); +} + +#if 0 /* save for parts */ +/* + * Splits list field and appends all entries separately; + * Warning: This is internal function, never call this directly, + * only for httpHeaderAddField use. + */ +static void +httpHeaderAddListField(HttpHeader *hdr, HttpHeaderField *fld) +{ + const char *v; + assert(hdr); + assert(fld); + /* + * Note: assume that somebody already checked that we can split. The danger + * is in splitting something that is not a list field but contains ','s in + * its value. + */ + /* we got a fld.value that is a list of values separated by ',' */ + v = strtok(fld->value, ","); + httpHeaderAddSingleField(hdr, fld); /* first strtok() did its job! */ + while ((v = strtok(NULL, ","))) { + /* ltrim and skip empty fields */ + while (isspace(*v) || *v == ',') v++; + if (*v) + httpHeaderAddSingleField(hdr, httpHeaderFieldCreate(fld->name, v)); + } +} +#endif + +/* + * Global (user level) routines + */ + +/* test if a field is present */ +int httpHeaderHas(const HttpHeader *hdr, http_hdr_type id) +{ + assert(hdr); + assert_eid(id); + assert(id != HDR_OTHER); + tmp_debug(here) ("%p lookup for %d\n", hdr, id); + return EBIT_TEST(hdr->emask, id); + +#ifdef SLOW_BUT_SAFE + return httpHeaderFindEntry(hdr, id, NULL) != NULL; +#endif +} + +/* delete a field if any */ +void httpHeaderDel(HttpHeader *hdr, http_hdr_type id) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + assert(id != HDR_OTHER); + tmp_debug(here) ("%p del-by-id %d\n", hdr, id); + if (httpHeaderFindEntry(hdr, id, &pos)) { + httpHeaderDelAt(hdr, pos); + } +} + +/* + * set a field + * setting an invaid value is equivalent to deleting a field + * (if field is not present, it is added; otherwise, old content is destroyed). + */ +static void +httpHeaderSet(HttpHeader *hdr, http_hdr_type id, const field_store value) +{ + HttpHeaderPos pos; + HttpHeaderEntry e; + assert(hdr); + assert_eid(id); + + tmp_debug(here) ("%p sets with id: %d\n", hdr, id); + if (httpHeaderFindEntry(hdr, id, &pos)) /* delete old entry */ + httpHeaderDelAt(hdr, pos); + + httpHeaderEntryInit(&e, id, httpHeaderFieldDup(Headers[id].type, value)); + if (httpHeaderEntryIsValid(&e)) + httpHeaderAddNewEntry(hdr, &e); + else + httpHeaderEntryClean(&e); +} + +void +httpHeaderSetInt(HttpHeader *hdr, http_hdr_type id, int number) +{ + field_store value; + assert_eid(id); + assert(Headers[id].type == ftInt); /* must be of an appropriatre type */ + value.v_int = number; + httpHeaderSet(hdr, id, value); +} + +void +httpHeaderSetTime(HttpHeader *hdr, http_hdr_type id, time_t time) +{ + field_store value; + assert_eid(id); + assert(Headers[id].type == ftDate_1123); /* must be of an appropriatre type */ + value.v_time = time; + httpHeaderSet(hdr, id, value); +} +void +httpHeaderSetStr(HttpHeader *hdr, http_hdr_type id, const char *str) +{ + field_store value; + assert_eid(id); + assert(Headers[id].type == ftPChar); /* must be of a string type */ + value.v_pcchar = str; + httpHeaderSet(hdr, id, value); +} + +/* add extension header (these fields are not parsed/analyzed/joined, etc.) */ +void +httpHeaderAddExt(HttpHeader *hdr, const char *name, const char* value) +{ + HttpHeaderExtField *ext = httpHeaderExtFieldCreate(name, value); + HttpHeaderEntry e; + + tmp_debug(here) ("%p ads exte '%s:%s'\n", hdr, name, value); + httpHeaderEntryInit(&e, HDR_OTHER, ext); + httpHeaderAddNewEntry(hdr, &e); +} + +/* get a value of a field (not lvalue though) */ +field_store +httpHeaderGet(const HttpHeader *hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + assert_eid(id); + assert(id != HDR_OTHER); /* there is no single value for HDR_OTHER */ + + tmp_debug(here) ("%p get for id %d\n", hdr, id); + if ((e = httpHeaderFindEntry(hdr, id, NULL))) + return e->field; + else + return httpHeaderFieldBadValue(Headers[id].type); +} + +const char * +httpHeaderGetStr(const HttpHeader *hdr, http_hdr_type id) +{ + assert_eid(id); + assert(Headers[id].type == ftPChar); /* must be of an apropriate type */ + return httpHeaderGet(hdr, id).v_pchar; +} + +time_t +httpHeaderGetTime(const HttpHeader *hdr, http_hdr_type id) +{ + assert_eid(id); + assert(Headers[id].type == ftDate_1123); /* must be of an apropriate type */ + return httpHeaderGet(hdr, id).v_time; +} + +HttpScc * +httpHeaderGetScc(const HttpHeader *hdr) +{ + return httpHeaderGet(hdr, HDR_CACHE_CONTROL).v_pscc; +} + +/* updates header masks */ +static void +httpHeaderSyncMasks(HttpHeader *hdr, const HttpHeaderEntry *e, int add) +{ + int isSet; + assert(hdr && e); + assert_eid(e->id); + + /* we cannot mask HDR_OTHER because it may not be unique */ + if (e->id == HDR_OTHER) + return; + isSet = EBIT_TEST(hdr->emask, e->id) != 0; + add = add != 0; + assert(isSet ^ add); + add ? EBIT_SET(hdr->emask, e->id) : EBIT_CLR(hdr->emask, e->id); +} + +/* updates header stats */ +static void +httpHeaderSyncStats(HttpHeader *hdr, const HttpHeaderEntry *e) +{ +#if 0 /* implement it @?@ */ + assert(0); /* implement it */ + /* add Req/Pep detection here @?@ */ + int type = httpHeaderFindFieldType(fld, + HdrFieldAttrs, HDR_ENUM_END, + (1) ? ReplyHeadersMask : RequestHeadersMask); + /* exception */ + if (type == HDR_PROXY_KEEPALIVE && strcasecmp("Keep-Alive", fld->value)) + type = -1; + if (type < 0) + type = HDR_OTHER; + /* @?@ update stats for req/resp:type @?@ */ + /* process scc @?@ check if we need to do that for requests or not */ + if (1 && type == HDR_CACHE_CONTROL) + httpHeaderCountSCCField(hdr, fld); +#endif +} + +#if 0 /* move it */ +/* updates scc mask and stats for an scc field */ +static void +httpHeaderCountSCCField(HttpHeader *hdr, HttpHeaderField *fld) +{ + int type = httpHeaderFindFieldType(fld, + SccFieldAttrs, SCC_ENUM_END, -1); + if (type < 0) + type = SCC_OTHER; + /* update mask */ + EBIT_SET(hdr->scc_mask, type); + /* @?@ update stats for scc @?@ */ + SccFieldAttrs[type].dummy.test1++; +} +#endif + +static int +httpHeaderIdByName(const char *name, int name_len, const field_attrs_t *attrs, int end, int mask) +{ + int i; + for (i = 0; i < end; ++i) { + if (mask < 0 || EBIT_TEST(mask, i)) { + if (name_len >= 0 && name_len != attrs[i].name_len) + continue; + if (!strncasecmp(name, attrs[i].name, + name_len < 0 ? attrs[i].name_len+1 : name_len)) + return i; + } + } + return -1; +} + +/* doubles the size of the fields index, starts with INIT_FIELDS_PER_HEADER */ +static void +httpHeaderGrow(HttpHeader *hdr) +{ + int new_cap; + int new_size; + assert(hdr); + new_cap = (hdr->capacity) ? 2*hdr->capacity : INIT_FIELDS_PER_HEADER; + new_size = new_cap*sizeof(HttpHeaderEntry); + + tmp_debug(here) ("%p grow (%p) %d->%d\n", hdr, hdr->entries, hdr->capacity, new_cap); + hdr->entries = hdr->entries ? + xrealloc(hdr->entries, new_size) : + xmalloc(new_size); + memset(hdr->entries+hdr->capacity, 0, (new_cap-hdr->capacity)*sizeof(HttpHeaderEntry)); + hdr->capacity = new_cap; + tmp_debug(here) ("%p grew (%p)\n", hdr, hdr->entries); +} + +/* + * HttpHeaderEntry + */ + +static void +httpHeaderEntryInit(HttpHeaderEntry *e, http_hdr_type id, field_store field) +{ + assert(e); + assert_eid(id); + e->id = id; + e->field = field; +} + +static void +httpHeaderEntryClean(HttpHeaderEntry *e) { + assert(e); + assert_eid(e->id); + /* type-based cleanup */ + switch (Headers[e->id].type) { + case ftInvalid: + case ftInt: + case ftDate_1123: + /* no special cleaning is necessary */ + break; + case ftPChar: + freeShortString(e->field.v_pchar); + break; + case ftPSCC: + if (e->field.v_pscc) + httpSccDestroy(e->field.v_pscc); + break; + case ftPExtField: + if (e->field.v_pefield) + httpHeaderExtFieldDestroy(e->field.v_pefield); + break; + default: + assert(0); /* somebody added a new type? */ + } + /* we have to do that so entry will be _invlaid_ */ + e->id = -1; + e->field.v_pchar = NULL; +} + +/* parses and inits header entry, returns true on success */ +static int +httpHeaderEntryParseInit(HttpHeaderEntry *e, const char *field_start, const char *field_end, int mask) +{ + HttpHeaderExtField *f; + int id; + int result; + + /* first assume it is just an extension field */ + f = httpHeaderExtFieldParseCreate(field_start, field_end); + if (!f) /* parsing failure */ + return 0; + id = httpHeaderIdByName(f->name, -1, Headers, countof(Headers), mask); + if (id < 0) + id = HDR_OTHER; + if (id == HDR_OTHER) { + /* hm.. it is an extension field indeed */ + httpHeaderEntryInit(e, id, f); + return 1; + } + /* ok, we got something interesting, parse it further */ + result = httpHeaderEntryParseExtFieldInit(e, id, f); + /* do not need it anymore */ + httpHeaderExtFieldDestroy(f); + return result; +} + +static int +httpHeaderEntryParseExtFieldInit(HttpHeaderEntry *e, int id, const HttpHeaderExtField *f) +{ + /* + * check for exceptions first (parsing is not determined by value type) + * then parse using value type if needed + */ + switch (id) { + case HDR_PROXY_KEEPALIVE: + /* we treat Proxy-Connection as "keep alive" only if it says so */ + e->field.v_int = !strcasecmp(f->value, "Keep-Alive"); + break; + default: + /* if we got here, it is something that can be parsed based on value type */ + if (!httpHeaderEntryParseByTypeInit(e, id, f)) + return 0; + } + /* parsing was successful, post-processing maybe required */ + switch (id) { + case HDR_CONTENT_TYPE: { + /* cut off "; parameter" from Content-Type @?@ why? */ + const int l = strcspn(e->field.v_pchar, ";\t "); + if (l > 0) + e->field.v_pchar[l] = '\0'; + break; + } + case HDR_EXPIRES: + /* + * The HTTP/1.0 specs says that robust implementations should + * consider bad or malformed Expires header as equivalent to + * "expires immediately." + */ + if (!httpHeaderEntryIsValid(e)) + e->field.v_time = squid_curtime; + /* + * real expiration value also depends on max-age too, but it is not + * of our business (HttpReply should handle it) + */ + break; + } + return 1; +} + +static int +httpHeaderEntryParseByTypeInit(HttpHeaderEntry *e, int id, const HttpHeaderExtField *f) +{ + int type; + field_store field; + assert(e && f); + assert_eid(id); + type = Headers[id].type; + + httpHeaderFieldInit(&field); + switch(type) { + case ftInt: + field.v_int = atoi(f->value); + if (!field.v_int && !isdigit(*f->value)) { + debug(55, 1) ("cannot parse an int header field: id: %d, field: '%s: %s'\n", + id, f->name, f->value); + return 0; + } + break; + + case ftPChar: + field.v_pchar = dupShortStr(f->value); + break; + + case ftDate_1123: + field.v_time = parse_rfc1123(f->value); + /* + * if parse_rfc1123 fails we fall through anyway so upper levels + * will notice invalid date + */ + break; + + case ftPSCC: + field.v_pscc = httpSccParseCreate(f->value); + if (!field.v_pscc) { + debug(55, 0) ("failed to parse scc hdr: id: %d, field: '%s: %s'\n", + id, f->name, f->value); + return 0; + } + break; + + default: + debug(55, 0) ("something went wrong with hdr field type analysis: id: %d, type: %d, field: '%s: %s'\n", + id, type, f->name, f->value); + return 0; + } + /* success, do actual init */ + httpHeaderEntryInit(e, id, field); + return 1; +} + + +static HttpHeaderEntry +httpHeaderEntryClone(const HttpHeaderEntry *e) +{ + HttpHeaderEntry clone; + assert(e); + assert_eid(e->id); + httpHeaderEntryInit(&clone, e->id, + httpHeaderFieldDup(Headers[e->id].type, e->field)); + return clone; +} + +static void +httpHeaderEntryPackInto(const HttpHeaderEntry *e, Packer *p) +{ + assert(e && p); + + /* swap the field_name: */ + packerPrintf(p, "%s: ", httpHeaderEntryName(e)); + /* + * swap the value + * check for exceptions (packing is not determined by value type) + * then swap using value type + */ + switch (e->id) { + case HDR_PROXY_KEEPALIVE: + packerPrintf(p, "%s", "Keep-Alive"); + break; + default: + /* if we got here, it is something that can be swap based on value type */ + httpHeaderEntryPackByType(e, p); + } + /* add CRLF */ + packerPrintf(p, "%s", "\r\n"); +} + +static void +httpHeaderEntryPackByType(const HttpHeaderEntry *e, Packer *p) +{ + field_type type; + assert(e && p); + assert_eid(e->id); + type = Headers[e->id].type; + switch(type) { + case ftInt: + packerPrintf(p, "%d", e->field.v_int); + break; + case ftPChar: + packerPrintf(p, "%s", e->field.v_pchar); + break; + case ftDate_1123: + packerPrintf(p, "%s", mkrfc1123(e->field.v_time)); + break; + case ftPSCC: + httpSccPackValueInto(e->field.v_pscc, p); + break; + case ftPExtField: + packerPrintf(p, "%s", e->field.v_pefield->value); + break; + default: + assert(0 && type); /* pack for invalid/unknown type */ + } +} + +static void +httpHeaderEntryJoinWith(HttpHeaderEntry *e, const HttpHeaderEntry *newe) +{ + field_type type; + assert(e && newe); + assert_eid(e->id); + assert(e->id == newe->id); + + /* type-based join */ + type = Headers[e->id].type; + switch(type) { + case ftPChar: + e->field.v_pchar = appShortStr(e->field.v_pchar, newe->field.v_pchar); + break; + case ftPSCC: + httpSccJoinWith(e->field.v_pscc, newe->field.v_pscc); + break; + default: + debug(55, 0) ("join for invalid/unknown type: id: %d, type: %d\n", e->id, type); + assert(0); + } +} + + +static int +httpHeaderEntryIsValid(const HttpHeaderEntry *e) +{ + assert(e); + if (e->id == -1) + return 0; + assert_eid(e->id); + /* type-based analysis */ + switch(Headers[e->id].type) { + case ftInvalid: + return 0; + case ftInt: + return e->field.v_int >= 0; + case ftPChar: + return e->field.v_pchar != NULL; + break; + case ftDate_1123: + return e->field.v_time >= 0; + break; + case ftPSCC: + return e->field.v_pscc != NULL; + break; + case ftPExtField: + return e->field.v_pefield != NULL; + break; + default: + assert(0); /* query for invalid/unknown type */ + } + return 0; /* not reached */ +} + +static const char * +httpHeaderEntryName(const HttpHeaderEntry *e) +{ + assert(e); + assert_eid(e->id); + + return (e->id == HDR_OTHER) ? + e->field.v_pefield->name : Headers[e->id].name; +} + +/* + * HttpHeaderField + */ + +static void +httpHeaderFieldInit(field_store *field) +{ + assert(field); + memset(field, 0, sizeof(field_store)); +} + +static field_store +httpHeaderFieldDup(field_type type, field_store value) +{ + /* type based duplication */ + switch(type) { + case ftInt: + return value.v_int; + case ftPChar: + return dupShortStr(value.v_pchar); + break; + case ftDate_1123: + return value.v_time; + break; + case ftPSCC: + return httpSccDup(value.v_pscc); + break; + case ftPExtField: + return httpHeaderExtFieldDup(value.v_pefield); + break; + default: + assert(0); /* dup of invalid/unknown type */ + } + return NULL; /* not reached */ +} + +/* + * bad value table; currently bad values are determined by field type, but this + * can be changed in the future to reflect dependence on entry id if any + */ +static field_store +httpHeaderFieldBadValue(field_type type) +{ + switch(type) { + case ftInt: + case ftDate_1123: + return -1; + case ftPChar: + case ftPSCC: + case ftPExtField: + return NULL; + case ftInvalid: + default: + assert(0); /* query for invalid/unknown type */ + } + return NULL; /* not reached */ +} + +/* + * HttpScc (server cache control) + */ + +static HttpScc * +httpSccCreate() +{ + HttpScc *scc = memAllocate(MEM_HTTP_SCC, 1); + scc->max_age = -1; + return scc; +} + +/* creates an scc object from a 0-terminating string*/ +static HttpScc * +httpSccParseCreate(const char *str) +{ + HttpScc *scc = httpSccCreate(); + httpSccParseInit(scc, str); + return scc; +} + +/* parses a 0-terminating string and inits scc */ +static void +httpSccParseInit(HttpScc *scc, const char *str) +{ + const char *item; + const char *p; /* '=' parameter */ + const char *pos = NULL; + int type; + int ilen; + assert(scc && str); + + /* iterate through comma separated list */ + while(strListGetItem(str, ',', &item, &ilen, &pos)) { + /* strip '=' statements @?@ */ + if ((p = strchr(item, '=')) && (p-item < ilen)) + ilen = p++ - item; + /* find type */ + type = httpHeaderIdByName(item, ilen, + SccAttrs, SCC_ENUM_END, -1); + if (type < 0) { + debug(55, 0) ("cc: unknown cache-directive: near '%s' in '%s'\n", item, str); + continue; + } + if (EBIT_TEST(scc->mask, type)) { + debug(55, 0) ("cc: ignoring duplicate cache-directive: near '%s' in '%s'\n", item, str); + continue; + } + /* update mask */ + EBIT_SET(scc->mask, type); + /* post-processing special cases */ + switch (type) { + case SCC_MAX_AGE: + if (p) + scc->max_age = (time_t) atoi(p); + if (scc->max_age < 0) { + debug(55, 0) ("scc: invalid max-age specs near '%s'\n", item); + scc->max_age = -1; + EBIT_CLR(scc->mask, type); + } + break; + default: + /* note that we ignore most of '=' specs @?@ */ + break; + } + } + return; +} + +static void +httpSccDestroy(HttpScc *scc) +{ + assert(scc); + memFree(MEM_HTTP_SCC, scc); +} + +static HttpScc * +httpSccDup(HttpScc *scc) +{ + HttpScc *dup; + assert(scc); + dup = httpSccCreate(); + dup->mask = scc->mask; + dup->max_age = scc->max_age; + return dup; +} + +static void +httpSccPackValueInto(HttpScc *scc, Packer *p) +{ + http_scc_type flag; + int pcount = 0; + assert(scc && p); + if (scc->max_age >= 0) { + packerPrintf(p, "max-age=%d", scc->max_age); + pcount++; + } + for (flag = 0; flag < SCC_ENUM_END; flag++) { + if (EBIT_TEST(scc->mask, flag)) { + packerPrintf(p, pcount ? ", %s" : "%s", SccAttrs[flag].name); + pcount++; + } + } +} + +static void +httpSccJoinWith(HttpScc *scc, HttpScc *new_scc) +{ + assert(scc && new_scc); + if (scc->max_age < 0) + scc->max_age = new_scc->max_age; + scc->mask |= new_scc->mask; +} + + + +/* + * HttpHeaderExtField + */ + +static HttpHeaderExtField * +httpHeaderExtFieldCreate(const char *name, const char *value) +{ + HttpHeaderExtField *f = xcalloc(1, sizeof(HttpHeaderExtField)); + f->name = dupShortStr(name); + f->value = dupShortStr(value); + return f; +} + +/* parses ext field; returns fresh ext field on success and NULL on failure */ +static HttpHeaderExtField * +httpHeaderExtFieldParseCreate(const char *field_start, const char *field_end) +{ + HttpHeaderExtField *f = NULL; + /* note: name_start == field_start */ + const char *name_end = strchr(field_start, ':'); + const char *value_start; + /* note: value_end == field_end */ + + if (!name_end || name_end <= field_start || name_end > field_end) + return NULL; + + tmp_debug(here) ("got field len: %d\n", field_end-field_start); + + value_start = name_end + 1; /* skip ':' */ + /* skip white space */ + while (value_start < field_end && isspace(*value_start)) + value_start++; + + /* cut off "; parameter" from Content-Type @?@ why? */ + if (!strncasecmp(field_start, "Content-Type:", 13)) { + const int l = strcspn(value_start, ";\t "); + if (l > 0 && value_start + l < field_end) + field_end = value_start + l; + } + + f = xcalloc(1, sizeof(HttpHeaderExtField)); + f->name = dupShortBuf(field_start, name_end-field_start); + f->value = dupShortBuf(value_start, field_end-value_start); + tmp_debug(here) ("%p got field: '%s: %s'\n", f, f->name, f->value); + return f; +} + +static void +httpHeaderExtFieldDestroy(HttpHeaderExtField *f) +{ + assert(f); + freeShortString(f->name); + freeShortString(f->value); + xfree(f); +} + +static HttpHeaderExtField * +httpHeaderExtFieldDup(HttpHeaderExtField *f) +{ + assert(f); + return httpHeaderExtFieldCreate(f->name, f->value); +} + +#if 0 /* save for parts */ + +/* + * returns the space requred to put a field (and terminating !) into a + * buffer + */ +static size_t +httpHeaderFieldBufSize(const HttpHeaderExtField *fld) +{ + return strlen(fld->name)+2+strlen(fld->value)+2; +} + +/* + * returns true if fld.name is a "known" splitable field; + * always call this function to check because the detection algortihm may change + */ +static int +httpHeaderFieldIsList(const HttpHeaderExtField *fld) { + int i; + assert(fld); + /* "onten" should not match "Content"! */ + for (i = 0; i < KnownSplitableFieldCount; ++i) + if (strcasecmp(KnownSplitableFields[i], fld->name)) + return 1; + return 0; +} + +#endif + +static void +httpHeaderStoreAReport(StoreEntry *e, void (*reportPacker)(Packer *p)) +{ + Packer p; + assert(e); + packerToStoreInit(&p, e); + (*reportPacker)(&p); + packerClean(&p); +} + +void +httpHeaderStoreReport(StoreEntry *e) +{ + httpHeaderStoreAReport(e, &httpHeaderPackReport); +} + +void +httpHeaderStoreReqReport(StoreEntry *e) +{ + httpHeaderStoreAReport(e, &httpHeaderPackReqReport); +} + +void +httpHeaderStoreRepReport(StoreEntry *e) +{ + httpHeaderStoreAReport(e, &httpHeaderPackRepReport); +} + + +static void +httpHeaderPackReport(Packer *p) +{ + assert(p); + + httpHeaderPackRepReport(p); + httpHeaderPackReqReport(p); + + /* low level totals; reformat this? @?@ */ + packerPrintf(p, + "hdrs totals: %uld+%uld %s lstr: +%uld-%uld<(%uld=%uld)\n", + shortHeadersCount, + longHeadersCount, + memPoolReport(shortStrings), + longStrAllocCount, + longStrFreeCount, + longStrHighWaterCount, + longStrHighWaterSize); +} + +static void +httpHeaderPackRepReport(Packer *p) +{ + assert(p); +#if 0 /* implement this */ + httpHeaderPackAReport(p, &ReplyHeaderStats); + for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++) + storeAppendPrintf(entry, "Cache-Control %s: %d\n", + HttpServerCCStr[i], + ReplyHeaderStats.cc[i]); +#endif +} + +static void +httpHeaderPackReqReport(Packer *p) +{ + assert(p); +#if 0 /* implement this */ + httpHeaderPackAReport(p, &RequestHeaderStats); +#endif +} + +#if 0 /* implement this */ +static void +httpHeaderPackAReport(Packer *p, HttpHeaderStats *stats) +{ + assert(p); + assert(stats); + assert(0); + http_server_cc_t i; + http_hdr_misc_t j; + storeAppendPrintf(entry, "HTTP Reply Headers:\n"); + storeAppendPrintf(entry, " Headers parsed: %d\n", + ReplyHeaderStats.parsed); + for (j = HDR_AGE; j < HDR_MISC_END; j++) + storeAppendPrintf(entry, "%21.21s: %d\n", + HttpHdrMiscStr[j], + ReplyHeaderStats.misc[j]); +} +#endif + +/* "short string" routines below are trying to recycle memory for short strings */ +static char * +dupShortStr(const char *str) +{ + return dupShortBuf(str, strlen(str)); +} + +static char * +dupShortBuf(const char *str, size_t len) +{ + char *buf; + assert(str); + assert(len >= 0); + buf = allocShortBuf(len + 1); + assert(buf); + if (len) + xmemcpy(buf, str, len); /* may not have terminating 0 */ + buf[len] = '\0'; /* terminate */ + tmp_debug(here) ("dupped short buf[%d]: '%s'\n", len, buf); + return buf; +} + +static char * +appShortStr(char *str, const char *app_str) +{ + const size_t size = strlen(str)+strlen(app_str)+1; + char *buf = allocShortBuf(size); + snprintf(buf, size, "%s, %s", str, app_str); + freeShortString(str); + return buf; +} + +static char * +allocShortBuf(size_t sz) +{ + char *buf = NULL; + assert(shortStrings); + /* tmp_debug(here) ("allocating short buffer of size %d (max: %d)\n", sz, shortStrings->obj_size); @?@ */ + if (sz > shortStrings->obj_size) { + buf = xmalloc(sz); + longStrAllocCount++; + longStrAllocSize += sz; + if (longStrHighWaterCount < longStrAllocCount - longStrFreeCount) + longStrHighWaterCount = longStrAllocCount - longStrFreeCount; + if (longStrHighWaterSize < longStrAllocSize - longStrFreeSize) + longStrHighWaterSize = longStrAllocSize - longStrFreeSize; + } else + buf = memPoolGetObj(shortStrings); + return buf; +} + +static void +freeShortString(char *str) +{ + assert(shortStrings); + if (str) { + const size_t sz = strlen(str)+1; + /* tmp_debug(here) ("freeing short str of size %d (max: %d)'%s'\n", sz, shortStrings->obj_size, str); @?@ */ + if (sz > shortStrings->obj_size) { + tmp_debug(here) ("LONG short string[%d>%d]: %s\n", sz, shortStrings->obj_size, str); + xfree(str); + longStrFreeCount++; + longStrFreeSize += sz; + } else + memPoolPutObj(shortStrings, str); + } +} + +/* + * other routines (move these into lib if you need them somewhere else?) + */ + +/* + * iterates through a 0-terminated string of items separated by 'del' + * white space around 'del' is considered to be a part of 'del' + * like strtok, but preserves the source + * + * returns true if next item is found + * init pos with NULL to start iteration + */ +static int +strListGetItem(const char *str, char del, const char **item, int *ilen, const char **pos) +{ + size_t len; + assert(str && item && pos); + if (*pos) + if (!**pos) /* end of string */ + return 0; + else + (*pos)++; + else + *pos = str; + + /* skip leading ws (ltrim) */ + *pos += xcountws(*pos); + *item = *pos; /* remember item's start */ + /* find next delimiter */ + *pos = strchr(*item, del); + if (!*pos) /* last item */ + *pos = *item + strlen(*item); + len = *pos - *item; /* *pos points to del or '\0' */ + /* rtrim */ + while (len > 0 && isspace((*item)[len-1])) len--; + if (ilen) + *ilen = len; + return len > 0; +} + +/* handy to printf prefixes of potentially very long buffers */ +static const char * +getStringPrefix(const char *str) { +#define SHORT_PREFIX_SIZE 256 + LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE); + xstrncpy(buf, str, SHORT_PREFIX_SIZE); + return buf; +} diff --git a/src/HttpReply.cc b/src/HttpReply.cc new file mode 100644 index 0000000000..2f3bda5c9d --- /dev/null +++ b/src/HttpReply.cc @@ -0,0 +1,402 @@ +/* + * $Id: HttpReply.cc,v 1.2 1998/02/21 00:56:43 rousskov Exp $ + * + * DEBUG: section ?? HTTP Reply + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* tmp hack, delete it @?@ */ +#define Const + +#include "squid.h" + + +/* local constants */ + +/* local routines */ +static int httpReplyParseStep(HttpReply *rep, const char *parse_start, int atEnd); +static int httpReplyParseError(HttpReply *rep); +static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end); +static int httpReplyIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end); + + +HttpReply * +httpReplyCreate() +{ + HttpReply *rep = memAllocate(MEM_HTTPREPLY, 1); + tmp_debug(here) ("creating rep: %p\n", rep); + httpReplyInit(rep); + return rep; +} + +void +httpReplyInit(HttpReply *rep) +{ + assert(rep); + rep->hdr_sz = 0; + rep->pstate = psReadyToParseStartLine; + httpBodyInit(&rep->body); + httpHeaderInit(&rep->hdr); + httpStatusLineInit(&rep->sline); +} + +void +httpReplyClean(HttpReply *rep) +{ + assert(rep); + httpBodyClean(&rep->body); + httpHeaderClean(&rep->hdr); + httpStatusLineClean(&rep->sline); +} + +void +httpReplyDestroy(HttpReply *rep) +{ + assert(rep); + tmp_debug(here) ("destroying rep: %p\n", rep); + httpReplyClean(rep); + memFree(MEM_HTTPREPLY, rep); +} + +void +httpReplyReset(HttpReply *rep) +{ + httpReplyClean(rep); + httpReplyInit(rep); +} + +/* parses a buffer that may not be 0-terminated */ +int +httpReplyParse(HttpReply *rep, const char *buf) +{ + /* + * this extra buffer/copy will be eliminated when headers become meta-data + * in store. Currently we have to xstrncpy the buffer becuase store.c may + * feed a non 0-terminated buffer to us @?@. + */ + char *headers = memAllocate(MEM_4K_BUF, 1); + int success; + /* reset current state, because we are not used in incremental fashion */ + httpReplyReset(rep); + /* put a 0-terminator */ + xstrncpy(headers, buf, 4096); + success = httpReplyParseStep(rep, headers, 0); + memFree(MEM_4K_BUF, headers); + return success == 1; +} + +void +httpReplyPackInto(const HttpReply *rep, Packer *p) +{ + assert(rep); + httpStatusLinePackInto(&rep->sline, p); + httpHeaderPackInto(&rep->hdr, p); + packerAppend(p, "\r\n", 2); + httpBodyPackInto(&rep->body, p); +} + +/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ +MemBuf +httpReplyPack(const HttpReply *rep) +{ + MemBuf mb; + Packer p; + assert(rep); + + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpReplyPackInto(rep, &p); + packerClean(&p); + return mb; +} + +/* swap: create swap-based packer, pack, destroy packer */ +void +httpReplySwapOut(const HttpReply *rep, StoreEntry *e) +{ + Packer p; + assert(rep && e); + + packerToStoreInit(&p, e); + httpReplyPackInto(rep, &p); + packerClean(&p); +} + +MemBuf +httpPackedReply(double ver, http_status status, const char *ctype, + int clen, time_t lmt, time_t expires) +{ + HttpReply *rep = httpReplyCreate(); + MemBuf mb; + httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires); + mb = httpReplyPack(rep); + httpReplyDestroy(rep); + return mb; +} + +MemBuf +httpPacked304Reply(const HttpReply *rep) +{ + MemBuf mb; + assert(rep); + +#if 0 + /* construct reply */ + assert(0); /* implement: rep304 = httpReply304Create(rep); */ + + mb = httpReplyPack(rep304); + httpReplyDestroy(rep304); +#endif + + memBufDefInit(&mb); + + memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n"); + + if (httpHeaderHas(&rep->hdr, HDR_DATE)) + memBufPrintf(&mb, "Date: %s\r\n", mkrfc1123( + httpHeaderGetTime(&rep->hdr, HDR_DATE))); + + if (httpHeaderHas(&rep->hdr, HDR_CONTENT_TYPE)) + memBufPrintf(&mb, "Content-type: %s\r\n", + httpHeaderGetStr(&rep->hdr, HDR_CONTENT_TYPE)); + + if (httpHeaderHas(&rep->hdr, HDR_CONTENT_LENGTH)) + memBufPrintf(&mb, "Content-Length: %d\r\n", + httpReplyContentLen(rep)); + + if (httpHeaderHas(&rep->hdr, HDR_EXPIRES)) + memBufPrintf(&mb, "Expires: %s\r\n", mkrfc1123( + httpHeaderGetTime(&rep->hdr, HDR_EXPIRES))); + + if (httpHeaderHas(&rep->hdr, HDR_LAST_MODIFIED)) + memBufPrintf(&mb, "Last-modified: %s\r\n", mkrfc1123( + httpHeaderGetTime(&rep->hdr, HDR_LAST_MODIFIED))); + + memBufAppend(&mb, "\r\n", 2); + return mb; +} + +void +httpReplySetHeaders(HttpReply *reply, double ver, http_status status, const char *reason, + const char *ctype, int clen, time_t lmt, time_t expires) +{ + HttpHeader *hdr; + assert(reply); + httpStatusLineSet(&reply->sline, ver, status, reason); + hdr = &reply->hdr; + httpHeaderAddExt(hdr, "Server", full_appname_string); + httpHeaderAddExt(hdr, "MIME-Version", "1.0"); /* do we need this? @?@ */ + httpHeaderSetTime(hdr, HDR_DATE, squid_curtime); + if (ctype) + httpHeaderSetStr(hdr, HDR_CONTENT_TYPE, ctype); + if (clen > 0) + httpHeaderSetInt(hdr, HDR_CONTENT_LENGTH, clen); + if (expires >= 0) + httpHeaderSetTime(hdr, HDR_EXPIRES, expires); + if (lmt > 0) /* this used to be lmt != 0 @?@ */ + httpHeaderSetTime(hdr, HDR_LAST_MODIFIED, lmt); +} + +void +httpReplyUpdateOnNotModified(HttpReply *rep, HttpReply *freshRep) +{ +#if 0 /* this is what we want: */ + rep->cache_control = freshRep->cache_control; + rep->misc_headers = freshRep->misc_headers; + if (freshRep->date > -1) + rep->date = freshRep->date; + if (freshRep->last_modified > -1) + rep->last_modified = freshRep->last_modified; + if (freshRep->expires > -1) + rep->expires = freshRep->expires; +#endif + time_t date; + time_t expires; + time_t lmt; + assert(rep && freshRep); + /* save precious info */ + date = httpHeaderGetTime(&rep->hdr, HDR_DATE); + expires= httpReplyExpires(rep); + lmt = httpHeaderGetTime(&rep->hdr, HDR_LAST_MODIFIED); + /* clean old headers */ + httpHeaderClean(&rep->hdr); + /* clone */ + rep->hdr = *httpHeaderClone(&freshRep->hdr); + /* restore missing info if needed */ + if (!httpHeaderHas(&rep->hdr, HDR_DATE)) + httpHeaderSetTime(&rep->hdr, HDR_DATE, date); + if (!httpHeaderHas(&rep->hdr, HDR_EXPIRES)) + httpHeaderSetTime(&rep->hdr, HDR_EXPIRES, expires); + if (!httpHeaderHas(&rep->hdr, HDR_LAST_MODIFIED)) + httpHeaderSetTime(&rep->hdr, HDR_LAST_MODIFIED, lmt); +} + +int +httpReplyContentLen(const HttpReply *rep) { + assert(rep); + return httpHeaderGet(&rep->hdr, HDR_CONTENT_LENGTH).v_int; +} + +/* should we return "" or NULL if no content-type? Return NULL for now @?@ */ +const char * +httpReplyContentType(const HttpReply *rep) { + assert(rep); + return httpHeaderGetStr(&rep->hdr, HDR_CONTENT_TYPE); +} + +/* does it make sense to cache these computations ? @?@ */ +time_t +httpReplyExpires(const HttpReply *rep) +{ + HttpScc *scc; + time_t exp = -1; + assert(rep); + /* The max-age directive takes priority over Expires, check it first */ + scc = httpHeaderGetScc(&rep->hdr); + if (scc) + exp = scc -> max_age; + if (exp < 0) + exp = httpHeaderGetTime(&rep->hdr, HDR_EXPIRES); + return exp; +} + +int +httpReplyHasScc(const HttpReply *rep, http_scc_type type) +{ + HttpScc *scc; + assert(rep); + assert(type >= 0 && type < SCC_ENUM_END); + + scc = httpHeaderGetScc(&rep->hdr); + return scc && /* scc header is present */ + EBIT_TEST(scc->mask, type); +} + + +/* internal routines */ + + +/* + * parses a 0-terminating buffer into HttpReply. + * Returns: + * +1 -- success + * 0 -- need more data (partial parse) + * -1 -- parse error + */ +static int +httpReplyParseStep(HttpReply *rep, const char *buf, int atEnd) +{ + const char *parse_start = buf; + const char *blk_start, *blk_end; + const char **parse_end_ptr = &blk_end; + assert(rep); + assert(parse_start); + assert(rep->pstate < psParsed); + + *parse_end_ptr = parse_start; + if (rep->pstate == psReadyToParseStartLine) { + if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end)) + return 0; + if (!httpStatusLineParse(&rep->sline, blk_start, blk_end)) + return httpReplyParseError(rep); + + *parse_end_ptr = parse_start; + rep->hdr_sz = *parse_end_ptr - buf; + rep->pstate++; + } + + if (rep->pstate == psReadyToParseHeaders) { + if (!httpReplyIsolateHeaders(&parse_start, &blk_start, &blk_end)) + if (atEnd) + blk_start = parse_start, blk_end = blk_start + strlen(blk_start); + else + return 0; + if (!httpHeaderParse(&rep->hdr, blk_start, blk_end)) + return httpReplyParseError(rep); + + *parse_end_ptr = parse_start; + rep->hdr_sz = *parse_end_ptr - buf; + rep->pstate++; + } + + /* could check here for a _small_ body that we could parse right away?? @?@ */ + + return 1; +} + + +/* handy: resets and returns -1 */ +static int +httpReplyParseError(HttpReply *rep) +{ + assert(rep); + /* reset */ + httpReplyReset(rep); + /* indicate an error */ + rep->sline.status = HTTP_INVALID_HEADER; + return -1; +} + +/* find first CRLF */ +static int +httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end) +{ + int slen = strcspn(*parse_start, "\r\n"); + if (!(*parse_start)[slen]) /* no CRLF found */ + return 0; + + *blk_start = *parse_start; + *blk_end = *blk_start + slen; + if (**blk_end == '\r') /* CR */ + (*blk_end)++; + if (**blk_end == '\n') /* LF */ + (*blk_end)++; + + *parse_start = *blk_end; + return 1; +} + +/* find end of headers */ +static int +httpReplyIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end) +{ + /* adopted with mods from mime_headers_end() */ + const char *p1 = strstr(*parse_start, "\n\r\n"); + const char *p2 = strstr(*parse_start, "\n\n"); + const char *end = NULL; + + if (p1 && p2) + end = p1 < p2 ? p1 : p2; + else + end = p1 ? p1 : p2; + + if (end) { + *blk_start = *parse_start; + *blk_end = end + 1; + *parse_start = end + (end == p1 ? 3 : 2); + } + return end != NULL; +} diff --git a/src/HttpStatusLine.cc b/src/HttpStatusLine.cc new file mode 100644 index 0000000000..f334318c82 --- /dev/null +++ b/src/HttpStatusLine.cc @@ -0,0 +1,213 @@ +/* + * $Id: HttpStatusLine.cc,v 1.2 1998/02/21 00:56:44 rousskov Exp $ + * + * DEBUG: section ?? HTTP Status-line + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "squid.h" + + +/* local constants */ +const char *HttpStatusLineFormat = "HTTP/%3.1f %3d %s\r\n"; + +/* local routines */ +static const char *httpStatusString(http_status status); + +void +httpStatusLineInit(HttpStatusLine *sline) { + httpStatusLineSet(sline, 0.0, 0, NULL); +} + +void +httpStatusLineClean(HttpStatusLine *sline) { + httpStatusLineSet(sline, 0.0, 500, NULL); +} + +void httpStatusLineSet(HttpStatusLine *sline, double version, http_status status, const char *reason) { + assert(sline); + sline->version = version; + sline->status = status; + /* Note: no xstrdup for 'reason', assumes constant 'reasons' */ + sline->reason = reason; +} + +void +httpStatusLinePackInto(const HttpStatusLine *sline, Packer *p) +{ + assert(sline && p); + tmp_debug(here) ("packing sline %p using %p:\n", sline, p); + tmp_debug(here) (HttpStatusLineFormat, sline->version, sline->status, + sline->reason ? sline->reason : httpStatusString(sline->status)); + packerPrintf(p, HttpStatusLineFormat, + sline->version, sline->status, + sline->reason ? sline->reason : httpStatusString(sline->status)); +} + +int +httpStatusLineParse(HttpStatusLine *sline, const char *start, const char *end) { + assert(sline); + sline->status = HTTP_INVALID_HEADER; /* Squid header parsing error */ + if (strncasecmp(start, "HTTP/", 5)) + return 0; + start += 5; + if (!isdigit(*start)) + return 0; + sline->version = atof(start); + if (!(start = strchr(start, ' '))) + return 0; + sline->status = atoi(++start); + /* we ignore 'reason-phrase' */ + return 1; /* success */ +} + +static const char * +httpStatusString(http_status status) +{ + /* why not to return matching string instead of using "p" ? @?@ */ + const char *p = NULL; + switch (status) { + case 0: + p = "Init"; /* we init .status with code 0 */ + break; + case 100: + p = "Continue"; + break; + case 101: + p = "Switching Protocols"; + break; + case 200: + p = "OK"; + break; + case 201: + p = "Created"; + break; + case 202: + p = "Accepted"; + break; + case 203: + p = "Non-Authoritative Information"; + break; + case 204: + p = "No Content"; + break; + case 205: + p = "Reset Content"; + break; + case 206: + p = "Partial Content"; + break; + case 300: + p = "Multiple Choices"; + break; + case 301: + p = "Moved Permanently"; + break; + case 302: + p = "Moved Temporarily"; + break; + case 303: + p = "See Other"; + break; + case 304: + p = "Not Modified"; + break; + case 305: + p = "Use Proxy"; + break; + case 400: + p = "Bad Request"; + break; + case 401: + p = "Unauthorized"; + break; + case 402: + p = "Payment Required"; + break; + case 403: + p = "Forbidden"; + break; + case 404: + p = "Not Found"; + break; + case 405: + p = "Method Not Allowed"; + break; + case 406: + p = "Not Acceptable"; + break; + case 407: + p = "Proxy Authentication Required"; + break; + case 408: + p = "Request Time-out"; + break; + case 409: + p = "Conflict"; + break; + case 410: + p = "Gone"; + break; + case 411: + p = "Length Required"; + break; + case 412: + p = "Precondition Failed"; + break; + case 413: + p = "Request Entity Too Large"; + break; + case 414: + p = "Request-URI Too Large"; + break; + case 415: + p = "Unsupported Media Type"; + break; + case 500: + p = "Internal Server Error"; + break; + case 501: + p = "Not Implemented"; + break; + case 502: + p = "Bad Gateway"; + break; + case 503: + p = "Service Unavailable"; + break; + case 504: + p = "Gateway Time-out"; + break; + case 505: + p = "HTTP Version not supported"; + break; + default: + p = "Unknown"; + debug(11, 0) ("Unknown HTTP status code: %d\n", status); + break; + } + return p; +} diff --git a/src/Makefile.in b/src/Makefile.in index 2dfce42d22..4aa8b56eba 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.in,v 1.122 1998/02/19 23:09:46 wessels Exp $ +# $Id: Makefile.in,v 1.123 1998/02/21 00:56:46 rousskov Exp $ # # Uncomment and customize the following to suit your needs: # @@ -89,6 +89,10 @@ OBJS = \ hash.o \ http.o \ http-anon.o \ + HttpStatusLine.o \ + HttpHeader.o \ + HttpBody.o \ + HttpReply.o \ icmp.o \ icp_v2.o \ icp_v3.o \ @@ -97,11 +101,13 @@ OBJS = \ ipcache.o \ main.o \ mem.o \ + MemBuf.o \ mime.o \ multicast.o \ neighbors.o \ net_db.o \ cache_manager.o \ + Packer.o \ pass.o \ pconn.o \ peer_select.o \ diff --git a/src/MemBuf.cc b/src/MemBuf.cc new file mode 100644 index 0000000000..23c39374af --- /dev/null +++ b/src/MemBuf.cc @@ -0,0 +1,186 @@ +/* + * $Id: MemBuf.cc,v 1.2 1998/02/21 00:56:47 rousskov Exp $ + * + * DEBUG: section ?? Memory Buffer with printf + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* see MemBuf.h for documentation */ + +/* + * To-Do: uses memory pools for .buf recycling @?@ + */ + + +#include "squid.h" + +/* local routines */ +static void memBufGrow(MemBuf *mb, mb_size_t min_cap); + + + +void +memBufInit(MemBuf *mb, mb_size_t szInit, mb_size_t szMax) +{ + assert(mb); + assert(szInit > 0 && szMax > 0); + + mb->buf = NULL; + mb->size = 0; + mb->max_capacity = szMax; + mb->capacity = 0; + mb->freefunc = NULL; + + memBufGrow(mb, szInit); +} + +void +memBufClean(MemBuf *mb) +{ + assert(mb); + assert(mb->buf); + assert(mb->freefunc); /* not frozen */ + + (*mb->freefunc)(mb->buf); /* freeze */ + mb->buf = NULL; + mb->size = mb->capacity = 0; +} + +void +memBufAppend(MemBuf *mb, const char *buf, mb_size_t sz) +{ + assert(mb && buf && sz >= 0); + assert(mb->buf); + assert(mb->freefunc); /* not frozen */ + + if (sz > 0) { + if (mb->size + sz > mb->capacity) + memBufGrow(mb, mb->size + sz); + assert(mb->size + sz <= mb->capacity); /* paranoid */ + xmemcpy(mb->buf + mb->size, buf, sz); + mb->size += sz; + } +} + +#ifdef __STDC__ +void +memBufPrintf(MemBuf *mb, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); +#else +void +memBufPrintf(va_alist) + va_dcl +{ + va_list args; + MemBuf *mb = NULL; + const char *fmt = NULL; + mb_size_t sz = 0; + va_start(args); + mb = va_arg(args, MemBuf *); + fmt = va_arg(args, char *); +#endif + memBufVPrintf(mb, fmt, args); + va_end(args); +} + + +void +memBufVPrintf(MemBuf *mb, const char *fmt, va_list vargs) +{ + mb_size_t sz = 0; + assert(mb && fmt); + assert(mb->buf); + assert(mb->freefunc); /* not frozen */ + /* @?@ we do not init buf with '\0', do we have to for vsnprintf?? @?@ */ + /* assert in Grow should quit first, but we do not want to have a scare (1) loop */ + while (mb->capacity <= mb->max_capacity) { + mb_size_t free_space = mb->capacity - mb->size; + /* put as much as we can */ + sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs) + 1; + /* check for possible overflow @?@ can vsnprintf cut more than needed off? */ + if (sz + 32 >= free_space) /* magic constant 32, ARGH! @?@ */ + memBufGrow(mb, mb->capacity+1); + else + break; + } + mb->size += sz-1; /* note that we cut 0-terminator as store does @?@ @?@ */ +} + +FREE * +memBufFreeFunc(MemBuf *mb) +{ + FREE *ff; + assert(mb); + assert(mb->buf); + assert(mb->freefunc); /* not frozen */ + + ff = mb->freefunc; + mb->freefunc = NULL; /* freeze */ + return ff; +} + +/* grows (doubles) internal buffer to satisfy required minimal capacity */ +static void +memBufGrow(MemBuf *mb, mb_size_t min_cap) +{ + mb_size_t new_cap; + assert(mb); + assert(mb->capacity < min_cap); + + /* determine next capacity */ + new_cap = mb->capacity; + if (new_cap > 0) + while (new_cap < min_cap) new_cap *= 2; /* double */ + else + new_cap = min_cap; + + /* last chance to fit before we assert(!overflow) */ + if (new_cap > mb->max_capacity) + new_cap = mb->max_capacity; + + assert(new_cap <= mb->max_capacity); /* no overflow */ + assert(new_cap > mb->capacity); /* progress */ + + /* finally [re]allocate memory */ + if (!mb->buf) { + mb->buf = xmalloc(new_cap); + mb->freefunc = &xfree; + } else { + assert(mb->freefunc); + mb->buf = realloc(mb->buf, new_cap); + } + memset(mb->buf+mb->size, 0, new_cap-mb->size); /* just in case */ + mb->capacity = new_cap; +} + +void +memBufReport(MemBuf *mb) +{ + assert(mb); + memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); +} diff --git a/src/Packer.cc b/src/Packer.cc new file mode 100644 index 0000000000..47d02282b0 --- /dev/null +++ b/src/Packer.cc @@ -0,0 +1,127 @@ +/* + * $Id: Packer.cc,v 1.2 1998/02/21 00:56:48 rousskov Exp $ + * + * DEBUG: section ?? Packer: Uniform interface to "Storing" modules + * AUTHOR: Alex Rousskov + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* see Packer.h for documentation */ + +/* + * To-Do: + */ + + +#include "squid.h" + +/* local types */ + +/* local routines */ + +/* local constants and vars */ + +/* + * We do have one potential problem here. Both append_f and vprintf_f types + * cannot match real functions precisely (at least because of the difference in + * the type of the first parameter). Thus, we have to use type cast. If somebody + * changes the prototypes of real functions, Packer will not notice that because + * of the type cast. + * + * Solution: we use the constants below to *hard code* current prototypes of + * real functions. If real prototypes change, these constants will produce a + * warning (e.g., "warning: assignment from incompatible pointer type"). + */ + +/* append()'s */ +static void (*const store_append)(StoreEntry *, const char *, int) = &storeAppend; +static void (*const memBuf_append)(MemBuf *, const char *, mb_size_t) = &memBufAppend; + +/* vprintf()'s */ +static void (*const store_vprintf)(StoreEntry *, const char *, va_list ap) = &storeAppendVPrintf; +static void (*const memBuf_vprintf)(MemBuf *, const char *, va_list ap) = &memBufVPrintf; + + +void +packerToStoreInit(Packer *p, StoreEntry *e) +{ + assert(p && e); + p->append = (append_f)store_append; + p->vprintf = (vprintf_f)storeAppendVPrintf; + p->real_handler = e; +} + +void +packerToMemInit(Packer *p, MemBuf *mb) +{ + assert(p && mb); + p->append = (append_f)memBuf_append; + p->vprintf = (vprintf_f)memBuf_vprintf; + p->real_handler = mb; +} + +void +packerClean(Packer *p) +{ + assert(p); + /* it is not really necessary to do this, but, just in case... */ + p->append = NULL; + p->vprintf = NULL; + p->real_handler = NULL; +} + +void +packerAppend(Packer *p, const char *buf, int sz) +{ + assert(p); + assert(p->real_handler && p->append); + p->append(p->real_handler, buf, sz); +} + +#ifdef __STDC__ +void +packerPrintf(Packer *p, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); +#else +void +packerPrintf(va_alist) + va_dcl +{ + va_list args; + Packer *p = NULL; + const char *fmt = NULL; + int sz = 0; + va_start(args); + p = va_arg(args, Packer *); + fmt = va_arg(args, char *); +#endif + assert(p); + assert(p->real_handler && p->vprintf); + tmp_debug(here) ("printf: fmt: '%s'\n", fmt); + p->vprintf(p->real_handler, fmt, args); + va_end(args); +} diff --git a/src/cache_manager.cc b/src/cache_manager.cc index b67222de80..9f9c3e48cb 100644 --- a/src/cache_manager.cc +++ b/src/cache_manager.cc @@ -1,6 +1,6 @@ /* - * $Id: cache_manager.cc,v 1.2 1998/02/19 23:11:48 wessels Exp $ + * $Id: cache_manager.cc,v 1.3 1998/02/21 00:56:50 rousskov Exp $ * * DEBUG: section 16 Cache Manager Objects * AUTHOR: Duane Wessels @@ -140,7 +140,9 @@ cachemgrStart(int fd, StoreEntry * entry) { cachemgrStateData *mgr = NULL; ErrorState *err = NULL; +#if 0 char *hdr; +#endif action_table *a; debug(16, 3) ("objectcacheStart: '%s'\n", storeUrl(entry)); if ((mgr = cachemgrParse(storeUrl(entry))) == NULL) { @@ -168,6 +170,14 @@ cachemgrStart(int fd, StoreEntry * entry) a = cachemgrFindAction(mgr->action); assert(a != NULL); storeBuffer(entry); + { + HttpReply *rep = httpReplyCreate(); + httpReplySetHeaders(rep, (double) 1.0, HTTP_OK, NULL, + "text/plain", -1 /* C-Len */, squid_curtime /* LMT */, squid_curtime); + httpReplySwapOut(rep, entry); + httpReplyDestroy(rep); + } +#if 0 hdr = httpReplyHeader((double) 1.0, HTTP_OK, "text/plain", @@ -176,6 +186,7 @@ cachemgrStart(int fd, StoreEntry * entry) squid_curtime); storeAppend(entry, hdr, strlen(hdr)); storeAppend(entry, "\r\n", 2); +#endif a->handler(entry); storeBufferFlush(entry); storeComplete(entry); diff --git a/src/client_side.cc b/src/client_side.cc index feec850bea..d15f211f86 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.211 1998/02/19 23:07:36 wessels Exp $ + * $Id: client_side.cc,v 1.212 1998/02/21 00:56:50 rousskov Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -32,8 +32,8 @@ #include "squid.h" static const char *const crlf = "\r\n"; -static const char *const proxy_auth_line = -"Proxy-Authenticate: Basic realm=\"Squid proxy-caching web server\"\r\n"; +static const char *const proxy_auth_challenge = + "Basic realm=\"Squid proxy-caching web server\""; #define REQUEST_BUF_SIZE 4096 #define FAILURE_MODE_TIME 300 @@ -46,7 +46,9 @@ static PF clientReadRequest; static PF connStateFree; static PF requestTimeout; static STCB clientGetHeadersForIMS; +#if 0 static char *clientConstruct304reply(struct _http_reply *); +#endif static int CheckQuickAbort2(const clientHttpRequest *); static int clientCheckTransferDone(clientHttpRequest *); static void CheckQuickAbort(clientHttpRequest *); @@ -64,7 +66,11 @@ static STCB clientCacheHit; static void clientParseRequestHeaders(clientHttpRequest *); static void clientProcessRequest(clientHttpRequest *); static void clientProcessExpired(void *data); +#if 0 static char *clientConstructProxyAuthReply(clientHttpRequest * http); +#else +static HttpReply *clientConstructProxyAuthReply(clientHttpRequest * http); +#endif static int clientCachable(clientHttpRequest * http); static int clientHierarchical(clientHttpRequest * http); static int isTcpHit(log_type code); @@ -108,6 +114,7 @@ clientAccessCheck(void *data) aclNBCheck(http->acl_checklist, clientAccessCheckDone, http); } +#if 0 /* reimplemented using new interfaces */ static char * clientConstructProxyAuthReply(clientHttpRequest * http) { @@ -156,6 +163,20 @@ clientConstructProxyAuthReply(clientHttpRequest * http) content); return buf; } +#endif + +static HttpReply * +clientConstructProxyAuthReply(clientHttpRequest *http) +{ + ErrorState *err = errorCon(ERR_CACHE_ACCESS_DENIED, HTTP_PROXY_AUTHENTICATION_REQUIRED); + HttpReply *rep; + err->request = requestLink(http->request); + rep = errorBuildReply(err); + errorStateFree(err); + /* add Authenticate header */ + httpHeaderSetStr(&rep->hdr, HDR_PROXY_AUTHENTICATE, proxy_auth_challenge); + return rep; +} StoreEntry * clientCreateStoreEntry(clientHttpRequest * h, method_t m, int flags) @@ -184,7 +205,6 @@ clientAccessCheckDone(int answer, void *data) { clientHttpRequest *http = data; char *redirectUrl = NULL; - char *buf; ErrorState *err = NULL; debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http->uri, answer); http->acl_checklist = NULL; @@ -196,9 +216,19 @@ clientAccessCheckDone(int answer, void *data) } else if (answer == ACCESS_REQ_PROXY_AUTH) { http->al.http.code = HTTP_PROXY_AUTHENTICATION_REQUIRED; http->log_type = LOG_TCP_DENIED; - buf = clientConstructProxyAuthReply(http); http->entry = clientCreateStoreEntry(http, http->request->method, 0); +#if 0 + const char *buf = clientConstructProxyAuthReply(http); storeAppend(http->entry, buf, strlen(buf)); +#else + { + /* create appropreate response */ + HttpReply *rep = clientConstructProxyAuthReply(http); + httpReplySwapOut(rep, http->entry); + /* do not need it anymore */ + httpReplyDestroy(rep); + } +#endif } else { debug(33, 5) ("Access Denied: %s\n", http->uri); http->log_type = LOG_TCP_DENIED; @@ -299,8 +329,9 @@ clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * r { /* If the reply is anything but "Not Modified" then * we must forward it to the client */ - if (new_entry->mem_obj->reply->code != 304) { - debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", new_entry->mem_obj->reply->code); + const http_status status = new_entry->mem_obj->reply->sline.status; + if (status != 304) { + debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status); return 0; } /* If the client did not send IMS in the request, then it @@ -342,7 +373,7 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) storeUnlockObject(entry); entry = http->entry = http->old_entry; entry->refcount++; - } else if (mem->reply->code == 0) { + } else if (mem->reply->sline.status == 0) { debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url); if (entry->store_status == STORE_ABORTED) debug(33, 0) ("clientHandleIMSReply: entry->swap_status == STORE_ABORTED\n"); @@ -368,7 +399,7 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) * www.thegist.com (Netscape/1.13) returns a content-length for * 304's which seems to be the length of the 304 HEADERS!!! and * not the body they refer to. */ - storeCopyNotModifiedReplyHeaders(entry->mem_obj, oldentry->mem_obj); + httpReplyUpdateOnNotModified(entry->mem_obj->reply, oldentry->mem_obj->reply); storeTimestampsSet(oldentry); storeUnregister(entry, http); storeUnlockObject(entry); @@ -381,7 +412,7 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) } else { /* the client can handle this reply, whatever it is */ http->log_type = LOG_TCP_REFRESH_MISS; - if (mem->reply->code == 304) { + if (mem->reply->sline.status == 304) { http->old_entry->timestamp = squid_curtime; http->old_entry->refcount++; http->log_type = LOG_TCP_REFRESH_HIT; @@ -417,9 +448,8 @@ modifiedSince(StoreEntry * entry, request_t * request) if (entry->lastmod < 0) return 1; /* Find size of the object */ - if (mem->reply->content_length >= 0) - object_length = mem->reply->content_length; - else + object_length = httpReplyContentLen(mem->reply); + if (object_length < 0) object_length = contentLen(entry); if (entry->lastmod > request->ims) { debug(33, 3) ("--> YES: entry newer than client\n"); @@ -472,10 +502,18 @@ void clientPurgeRequest(clientHttpRequest * http) { int fd = http->conn->fd; +#if 0 char *msg; +#endif StoreEntry *entry; ErrorState *err = NULL; const cache_key *k; +#if 0 + int len; + FREE *freefunc; +#endif + MemBuf mb; + debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge); if (!Config.onoff.enable_purge) { http->log_type = LOG_TCP_DENIED; @@ -494,10 +532,15 @@ clientPurgeRequest(clientHttpRequest * http) storeRelease(entry); http->http_code = HTTP_OK; } +#if 0 /* new interface */ msg = httpReplyHeader(1.0, http->http_code, NULL, 0, 0, -1); if ((int) strlen(msg) < 8190) strcat(msg, "\r\n"); comm_write(fd, xstrdup(msg), strlen(msg), clientWriteComplete, http, xfree); +#else + mb = httpPackedReply(1.0, http->http_code, NULL, 0, 0, -1); + comm_write_mbuf(fd, mb, clientWriteComplete, http); +#endif } int @@ -554,8 +597,8 @@ httpRequestFree(void *data) http->al.icp.opcode = 0; http->al.url = http->uri; if (mem) { - http->al.http.code = mem->reply->code; - http->al.http.content_type = mem->reply->content_type; + http->al.http.code = mem->reply->sline.status; + http->al.http.content_type = httpReplyContentType(mem->reply); } http->al.cache.caddr = conn->log_addr; http->al.cache.size = http->out.size; @@ -1013,7 +1056,11 @@ clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *da } else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) { debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd); /* We're finished case */ +#if 0 if (http->entry->mem_obj->reply->content_length < 0 || !done || +#else + if (httpReplyContentLen(http->entry->mem_obj->reply) < 0 || !done || +#endif EBIT_TEST(entry->flag, ENTRY_BAD_LENGTH)) { /* * Client connection closed due to unknown or invalid @@ -1075,7 +1122,10 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) clientHttpRequest *http = data; StoreEntry *entry = http->entry; MemObject *mem; +#if 0 + MemObject *mem; char *reply = NULL; +#endif debug(33, 3) ("clientGetHeadersForIMS: %s, %d bytes\n", http->uri, (int) size); assert(size <= SM_PAGE_SIZE); @@ -1096,7 +1146,7 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) return; } mem = entry->mem_obj; - if (mem->reply->code == 0) { + if (mem->reply->sline.status == 0) { if (entry->mem_status == IN_MEMORY) { clientProcessMiss(http); return; @@ -1124,7 +1174,7 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) } /* All headers are available, check if object is modified or not */ /* --------------------------------------------------------------- - * Removed check for reply->code != 200 because of a potential + * Removed check for reply->sline.status != 200 because of a potential * problem with ICP. We will return a HIT for any public, cached * object. This includes other responses like 301, 410, as coded in * http.c. It is Bad(tm) to return UDP_HIT and then, if the reply @@ -1137,9 +1187,9 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) * ---------------------------------------------------------------- */ #ifdef CHECK_REPLY_CODE_NOTEQUAL_200 /* Only objects with statuscode==200 can be "Not modified" */ - if (mem->reply->code != 200) { + if (mem->reply->sline.status != 200) { debug(33, 4) ("clientGetHeadersForIMS: Reply code %d!=200\n", - mem->reply->code); + mem->reply->sline.status); clientProcessMiss(http); return; } @@ -1159,6 +1209,7 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) return; } debug(33, 4) ("clientGetHeadersForIMS: Not modified '%s'\n", storeUrl(entry)); +#if 0 /* use new interfaces */ reply = clientConstruct304reply(mem->reply); comm_write(http->conn->fd, xstrdup(reply), @@ -1166,6 +1217,10 @@ clientGetHeadersForIMS(void *data, char *buf, ssize_t size) clientHandleIMSComplete, http, xfree); +#else + comm_write_mbuf(http->conn->fd, httpPacked304Reply(mem->reply), + clientHandleIMSComplete, http); +#endif } static void @@ -1244,7 +1299,9 @@ clientProcessRequest(clientHttpRequest * http) StoreEntry *entry = NULL; request_t *r = http->request; int fd = http->conn->fd; +#if 0 char *hdr; +#endif debug(33, 4) ("clientProcessRequest: %s '%s'\n", RequestMethodStr[r->method], url); @@ -1260,6 +1317,7 @@ clientProcessRequest(clientHttpRequest * http) http->entry = clientCreateStoreEntry(http, r->method, 0); storeReleaseRequest(http->entry); storeBuffer(http->entry); +#if 0 /* use new interface */ hdr = httpReplyHeader(1.0, HTTP_OK, "text/plain", @@ -1268,6 +1326,14 @@ clientProcessRequest(clientHttpRequest * http) squid_curtime); storeAppend(http->entry, hdr, strlen(hdr)); storeAppend(http->entry, "\r\n", 2); +#else + { + HttpReply *rep = httpReplyCreate(); + httpReplySetHeaders(rep, 1.0, HTTP_OK, NULL, "text/plain", r->headers_sz, 0, squid_curtime); + httpReplySwapOut(rep, http->entry); + httpReplyDestroy(rep); + } +#endif storeAppend(http->entry, r->headers, r->headers_sz); storeComplete(http->entry); return; @@ -1874,7 +1940,11 @@ CheckQuickAbort2(const clientHttpRequest * http) return 1; if (http->entry->mem_obj == NULL) return 1; +#if 0 expectlen = http->entry->mem_obj->reply->content_length; +#else + expectlen = httpReplyContentLen(http->entry->mem_obj->reply); +#endif curlen = http->entry->mem_obj->inmem_hi; minlen = Config.quickAbort.min; if (minlen < 0) @@ -1924,6 +1994,7 @@ clientCheckTransferDone(clientHttpRequest * http) MemObject *mem; http_reply *reply; int sendlen; + int clen; if (entry == NULL) return 0; /* @@ -1945,13 +2016,13 @@ clientCheckTransferDone(clientHttpRequest * http) reply = mem->reply; if (reply->hdr_sz == 0) return 0; /* haven't found end of headers yet */ - else if (reply->code == HTTP_OK) + else if (reply->sline.status == HTTP_OK) sending = SENDING_BODY; - else if (reply->code == HTTP_NO_CONTENT) + else if (reply->sline.status == HTTP_NO_CONTENT) sending = SENDING_HDRSONLY; - else if (reply->code == HTTP_NOT_MODIFIED) + else if (reply->sline.status == HTTP_NOT_MODIFIED) sending = SENDING_HDRSONLY; - else if (reply->code < HTTP_OK) + else if (reply->sline.status < HTTP_OK) sending = SENDING_HDRSONLY; else if (http->request->method == METHOD_HEAD) sending = SENDING_HDRSONLY; @@ -1963,12 +2034,13 @@ clientCheckTransferDone(clientHttpRequest * http) * then we must wait for the object to become STORE_OK or * STORE_ABORTED. */ + clen = httpReplyContentLen(reply); if (sending == SENDING_HDRSONLY) sendlen = reply->hdr_sz; - else if (reply->content_length < 0) + else if (clen < 0) return 0; else - sendlen = reply->content_length + reply->hdr_sz; + sendlen = clen + reply->hdr_sz; /* * Now that we have the expected length, did we send it all? */ @@ -1978,8 +2050,9 @@ clientCheckTransferDone(clientHttpRequest * http) return 1; } +#if 0 /* moved to HttpReply */ static char * -clientConstruct304reply(struct _http_reply *source) +clientConstruct304reply(http_reply *source) { LOCAL_ARRAY(char, line, 256); LOCAL_ARRAY(char, reply, 8192); @@ -2009,6 +2082,7 @@ clientConstruct304reply(struct _http_reply *source) strcat(reply, "\r\n"); return reply; } +#endif /* * This function is designed to serve a fairly specific purpose. diff --git a/src/comm.cc b/src/comm.cc index 8bf055ff93..eb45cd9652 100644 --- a/src/comm.cc +++ b/src/comm.cc @@ -1,6 +1,6 @@ /* - * $Id: comm.cc,v 1.229 1998/02/18 00:38:53 wessels Exp $ + * $Id: comm.cc,v 1.230 1998/02/21 00:56:52 rousskov Exp $ * * DEBUG: section 5 Socket Functions * AUTHOR: Harvest Derived @@ -1446,6 +1446,13 @@ comm_write(int fd, char *buf, int size, CWCB * handler, void *handler_data, FREE 0); } +/* a wrapper around comm_write to allow for MemBuf to comm_written in a snap */ +void +comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data) +{ + comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb)); +} + int ignoreErrno(int ierrno) { diff --git a/src/defines.h b/src/defines.h index d37484543a..0d373d8c7f 100644 --- a/src/defines.h +++ b/src/defines.h @@ -49,6 +49,16 @@ #define debug(SECTION, LEVEL) \ ((LEVEL) > debugLevels[SECTION]) ? (void) 0 : _db_print #endif + +/* useful for temporary debuging messages, delete it later @?@ */ +#define here __FILE__,__LINE__ +#define dev_null 1 ? ((void)0) : +#ifdef HAVE_SYSLOG +#define tmp_debug(fl) _db_level = 0, dev_null _db_print("%s:%d: ",fl), dev_null _db_print +#else +#define tmp_debug(fl) _dev_null db_print("%s[%d]: ",fl), dev_null _db_print +#endif + #define safe_free(x) if (x) { xxfree(x); x = NULL; } #define DISK_OK (0) diff --git a/src/enums.h b/src/enums.h index 6b02b40b3c..88128f3012 100644 --- a/src/enums.h +++ b/src/enums.h @@ -43,6 +43,7 @@ typedef enum { ERR_FTP_FAILURE, ERR_URN_RESOLVE, ERR_ACCESS_DENIED, + ERR_CACHE_ACCESS_DENIED, ERR_MAX } err_type; @@ -141,7 +142,9 @@ typedef enum { MGR_REDIRECTORS, MGR_REFRESH, MGR_REMOVE, + MGR_REQUEST_HDRS, MGR_REPLY_HDRS, + MGR_MSG_HDRS, MGR_SERVER_LIST, MGR_NON_PEERS, MGR_SHUTDOWN, @@ -296,7 +299,8 @@ typedef enum { HTTP_BAD_GATEWAY = 502, HTTP_SERVICE_UNAVAILABLE = 503, HTTP_GATEWAY_TIMEOUT = 504, - HTTP_HTTP_VERSION_NOT_SUPPORTED = 505 + HTTP_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_INVALID_HEADER = 600 /* Squid header parsing error */ } http_status; /* These are for StoreEntry->flag, which is defined as a SHORT */ @@ -410,7 +414,12 @@ typedef enum { MEM_FQDNCACHE_ENTRY, MEM_HASH_LINK, MEM_HASH_TABLE, +#if 0 /* renamed to detect all old uses */ MEM_HTTP_REPLY, +#else + MEM_HTTPREPLY, +#endif + MEM_HTTP_SCC, MEM_HTTPSTATEDATA, MEM_ICPUDPDATA, MEM_CLIENTHTTPREQUEST, diff --git a/src/errorpage.cc b/src/errorpage.cc index 9cecf03ce2..71d9fd1409 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -1,6 +1,6 @@ /* - * $Id: errorpage.cc,v 1.116 1998/02/02 21:15:00 wessels Exp $ + * $Id: errorpage.cc,v 1.117 1998/02/21 00:56:54 rousskov Exp $ * * DEBUG: section 4 Error Generation * AUTHOR: Duane Wessels @@ -40,9 +40,8 @@ static char *error_text[ERR_MAX]; -static void errorStateFree(ErrorState * err); +static const char *errorBuildContent(ErrorState * err, int *len); static const char *errorConvert(char token, ErrorState * err); -static const char *errorBuildBuf(ErrorState * err, int *len); static CWCB errorSendComplete; /* @@ -118,15 +117,25 @@ errorCon(err_type type, http_status status) void errorAppendEntry(StoreEntry * entry, ErrorState * err) { +#if 0 const char *buf; - MemObject *mem = entry->mem_obj; int len; +#else + HttpReply *rep; +#endif + MemObject *mem = entry->mem_obj; assert(entry->store_status == STORE_PENDING); assert(mem != NULL); assert(mem->inmem_hi == 0); +#if 0 buf = errorBuildBuf(err, &len); storeAppend(entry, buf, len); - mem->reply->code = err->http_status; +#else + rep = errorBuildReply(err); + httpReplySwapOut(rep, entry); + httpReplyDestroy(rep); +#endif + mem->reply->sline.status = err->http_status; storeComplete(entry); storeNegativeCache(entry); storeReleaseRequest(entry); @@ -155,8 +164,12 @@ errorAppendEntry(StoreEntry * entry, ErrorState * err) void errorSend(int fd, ErrorState * err) { - const char *buf; + HttpReply *rep; +#if 0 + FREE *freefunc; + char *buf; int len; +#endif debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err); assert(fd >= 0); /* @@ -165,10 +178,17 @@ errorSend(int fd, ErrorState * err) */ if (err->request) err->request->err_type = err->type; - buf = errorBuildBuf(err, &len); + /* moved in front of errorBuildBuf @?@ */ EBIT_SET(err->flags, ERR_FLAG_CBDATA); cbdataAdd(err, MEM_NONE); +#if 0 + buf = errorBuildBuf(err, &len); comm_write(fd, xstrdup(buf), len, errorSendComplete, err, xfree); +#else + rep = errorBuildReply(err); + comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err); + httpReplyDestroy(rep); +#endif } /* @@ -194,7 +214,7 @@ errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data errorStateFree(err); } -static void +void errorStateFree(ErrorState * err) { requestUnlink(err->request); @@ -339,7 +359,63 @@ errorConvert(char token, ErrorState * err) return p; } +/* allocates and initializes an error response */ +HttpReply * +errorBuildReply(ErrorState *err) +{ + int clen; + HttpReply *rep = httpReplyCreate(); + const char *content = errorBuildContent(err, &clen); + /* no LMT for error pages; error pages expire immediately */ + httpReplySetHeaders(rep, 1.0, err->http_status, NULL, "text/html", clen, 0, squid_curtime); + httpBodySet(&rep->body, content, clen+1, NULL); + return rep; +} + static const char * +errorBuildContent(ErrorState * err, int *len) +{ + LOCAL_ARRAY(char, content, ERROR_BUF_SZ); + int clen; + char *m; + char *mx; + char *p; + const char *t; + assert(err != NULL); + assert(err->type > ERR_NONE && err->type < ERR_MAX); + mx = m = xstrdup(error_text[err->type]); + clen = 0; + while ((p = strchr(m, '%'))) { + *p = '\0'; /* terminate */ + xstrncpy(content + clen, m, ERROR_BUF_SZ - clen); /* copy */ + clen += (p - m); /* advance */ + if (clen >= ERROR_BUF_SZ) + break; + p++; + m = p + 1; + t = errorConvert(*p, err); /* convert */ + xstrncpy(content + clen, t, ERROR_BUF_SZ - clen); /* copy */ + clen += strlen(t); /* advance */ + if (clen >= ERROR_BUF_SZ) + break; + } + if (clen < ERROR_BUF_SZ && m != NULL) { + xstrncpy(content + clen, m, ERROR_BUF_SZ - clen); + clen += strlen(m); + } + if (clen >= ERROR_BUF_SZ) { + clen = ERROR_BUF_SZ - 1; + *(content + clen) = '\0'; + } + assert(clen == strlen(content)); + if (len) + *len = clen; + xfree(mx); + return content; +} + +#if 0 /* we use httpReply instead of a buffer now */ +const char * errorBuildBuf(ErrorState * err, int *len) { LOCAL_ARRAY(char, buf, ERROR_BUF_SZ); @@ -390,3 +466,4 @@ errorBuildBuf(ErrorState * err, int *len) xfree(mx); return buf; } +#endif diff --git a/src/ftp.cc b/src/ftp.cc index 443448059c..bac074a529 100644 --- a/src/ftp.cc +++ b/src/ftp.cc @@ -1,6 +1,6 @@ /* - * $Id: ftp.cc,v 1.194 1998/02/18 00:38:54 wessels Exp $ + * $Id: ftp.cc,v 1.195 1998/02/21 00:56:55 rousskov Exp $ * * DEBUG: section 9 File Transfer Protocol (FTP) * AUTHOR: Harvest Derived @@ -139,7 +139,11 @@ static char *ftpGetBasicAuth(const char *); static void ftpLoginParser(const char *, FtpStateData *); static wordlist *ftpParseControlReply(char *buf, size_t len, int *code); static void ftpAppendSuccessHeader(FtpStateData * ftpState); +#if 0 static char *ftpAuthRequired(const request_t *, const char *); +#else +static void ftpAuthRequired(HttpReply *reply, request_t *request, const char *realm); +#endif static STABH ftpAbort; static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState); @@ -919,7 +923,9 @@ ftpStart(request_t * request, StoreEntry * entry) LOCAL_ARRAY(char, realm, 8192); const char *url = storeUrl(entry); FtpStateData *ftpState = xcalloc(1, sizeof(FtpStateData)); +#if 0 char *response; +#endif int fd; ErrorState *err; cbdataAdd(ftpState, MEM_NONE); @@ -939,9 +945,19 @@ ftpStart(request_t * request, StoreEntry * entry) snprintf(realm, 8192, "ftp %s port %d", ftpState->user, request->port); } +#if 0 response = ftpAuthRequired(request, realm); storeAppend(entry, response, strlen(response)); httpParseReplyHeaders(response, entry->mem_obj->reply); +#else + { + HttpReply *reply = entry->mem_obj->reply; + assert(reply); + /* create appropreate reply */ + ftpAuthRequired(reply, request, realm); + httpReplySwapOut(reply, entry); + } +#endif storeComplete(entry); ftpStateFree(-1, ftpState); return; @@ -1944,6 +1960,7 @@ ftpAppendSuccessHeader(FtpStateData * ftpState) } } storeBuffer(e); +#if 0 /* old code */ storeAppendPrintf(e, "HTTP/1.0 200 Gatewaying\r\n"); reply->code = 200; reply->version = 1.0; @@ -1966,6 +1983,16 @@ ftpAppendSuccessHeader(FtpStateData * ftpState) reply->last_modified = ftpState->mdtm; } storeAppendPrintf(e, "\r\n"); +#else + httpReplyReset(reply); + /* set standard stuff */ + httpReplySetHeaders(reply, 1.0, HTTP_OK, "Gatewaying", + mime_type, ftpState->size, ftpState->mdtm, -2); + /* additional info */ + if (mime_enc) + httpHeaderSetStr(&reply->hdr, HDR_CONTENT_ENCODING, mime_enc); + httpReplySwapOut(reply, e); +#endif storeBufferFlush(e); reply->hdr_sz = e->mem_obj->inmem_hi; storeTimestampsSet(e); @@ -1984,6 +2011,7 @@ ftpAbort(void *data) comm_close(ftpState->ctrl.fd); } +#if 0 /* use new interfaces instead */ static char * ftpAuthRequired(const request_t * request, const char *realm) { @@ -2032,6 +2060,22 @@ ftpAuthRequired(const request_t * request, const char *realm) l += snprintf(buf + l, s - l, "\r\n%s", content); return buf; } +#endif + +static void +ftpAuthRequired(HttpReply *reply, request_t *request, const char *realm) +{ + ErrorState *err = errorCon(ERR_ACCESS_DENIED, HTTP_UNAUTHORIZED); + HttpReply *rep; + err->request = requestLink(request); + rep = errorBuildReply(err); + /* add Authenticate header */ + httpHeaderSetStr(&rep->hdr, HDR_WWW_AUTHENTICATE, realm); + errorStateFree(err); + /* substitute, should be OK because we clean it @?@ */ + httpReplyClean(reply); + *reply = *rep; /* @?@ warning is generated due to hdr_sz being constant */ +} char * ftpUrlWith2f(const request_t * request) diff --git a/src/globals.h b/src/globals.h index 654800827c..15badfad18 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,6 +1,6 @@ /* - * $Id: globals.h,v 1.38 1998/02/20 21:03:44 wessels Exp $ + * $Id: globals.h,v 1.39 1998/02/21 00:56:56 rousskov Exp $ */ extern FILE *debug_log; /* NULL */ @@ -26,6 +26,7 @@ extern const char *const dash_str; /* "-" */ extern const char *const localhost; /* "127.0.0.1" */ extern const char *const null_string; /* "" */ extern const char *const version_string; /* SQUID_VERSION */ +extern const char *const full_appname_string; /* "Squid/" SQUID_VERSION */ extern const char *const w_space; /* " \t\n\r" */ extern const char *fdTypeStr[]; extern const char *hier_strings[]; diff --git a/src/http.cc b/src/http.cc index 25e276684e..3b2ded92e0 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1,6 +1,6 @@ /* - * $Id: http.cc,v 1.238 1998/02/19 23:09:52 wessels Exp $ + * $Id: http.cc,v 1.239 1998/02/21 00:56:56 rousskov Exp $ * * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) * AUTHOR: Harvest Derived @@ -113,6 +113,7 @@ static const char *const crlf = "\r\n"; +#if 0 /* moved to HttpHeader */ typedef enum { SCC_PUBLIC, SCC_PRIVATE, @@ -124,6 +125,7 @@ typedef enum { SCC_MAXAGE, SCC_ENUM_END } http_server_cc_t; +#endif enum { CCC_NOCACHE, @@ -135,6 +137,7 @@ enum { CCC_ENUM_END }; +#if 0 /* moved to HttpHeader.h */ typedef enum { HDR_ACCEPT, HDR_AGE, @@ -197,6 +200,7 @@ static struct { int misc[HDR_MISC_END]; int cc[SCC_ENUM_END]; } ReplyHeaderStats; +#endif /* if 0 */ static CNCB httpConnectDone; static CWCB httpSendComplete; @@ -208,7 +212,9 @@ static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, si static void httpCacheNegatively(StoreEntry *); static void httpMakePrivate(StoreEntry *); static void httpMakePublic(StoreEntry *); +#if 0 /* moved to HttpResponse */ static char *httpStatusString(int status); +#endif static STABH httpAbort; static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *); static int httpSocketOpen(StoreEntry *, request_t *); @@ -290,6 +296,7 @@ httpCacheNegatively(StoreEntry * entry) } +#if 0 /* Build a reply structure from HTTP reply headers */ void httpParseReplyHeaders(const char *buf, struct _http_reply *reply) @@ -420,17 +427,20 @@ httpParseReplyHeaders(const char *buf, struct _http_reply *reply) memFree(MEM_4K_BUF, headers); memFree(MEM_4K_BUF, line); } +#endif /* 0 */ static int httpCachableReply(HttpStateData * httpState) { - struct _http_reply *reply = httpState->entry->mem_obj->reply; - if (EBIT_TEST(reply->cache_control, SCC_PRIVATE)) + HttpHeader *hdr = &httpState->entry->mem_obj->reply->hdr; + const HttpScc *scc = httpHeaderGetScc(hdr); + const int scc_mask = (scc) ? scc->mask : 0; + if (EBIT_TEST(scc_mask, SCC_PRIVATE)) return 0; - if (EBIT_TEST(reply->cache_control, SCC_NOCACHE)) + if (EBIT_TEST(scc_mask, SCC_NO_CACHE)) return 0; if (EBIT_TEST(httpState->request->flags, REQ_AUTH)) - if (!EBIT_TEST(reply->cache_control, SCC_PROXYREVALIDATE)) + if (!EBIT_TEST(scc_mask, SCC_PROXY_REVALIDATE)) return 0; /* * Dealing with cookies is quite a bit more complicated @@ -438,9 +448,10 @@ httpCachableReply(HttpStateData * httpState) * header from the reply but still cache the reply body. * More confusion at draft-ietf-http-state-mgmt-05.txt. */ - if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE)) + /* With new headers the above stripping should be easy to do? @?@ */ + if (httpHeaderHas(hdr, HDR_SET_COOKIE)) return 0; - switch (reply->code) { + switch (httpState->entry->mem_obj->reply->sline.status) { /* Responses that are cacheable */ case 200: /* OK */ case 203: /* Non-Authoritative Information */ @@ -448,13 +459,14 @@ httpCachableReply(HttpStateData * httpState) case 301: /* Moved Permanently */ case 410: /* Gone */ /* don't cache objects from peers w/o LMT, Date, or Expires */ - if (reply->date > -1) + /* check that is it enough to check headers @?@ */ + if (httpHeaderHas(hdr, HDR_DATE)) return 1; - else if (reply->last_modified > -1) + else if (httpHeaderHas(hdr, HDR_LAST_MODIFIED)) return 1; else if (!httpState->peer) return 1; - else if (reply->expires > -1) + else if (httpHeaderHas(hdr, HDR_EXPIRES)) return 1; else return 0; @@ -462,12 +474,13 @@ httpCachableReply(HttpStateData * httpState) break; /* Responses that only are cacheable if the server says so */ case 302: /* Moved temporarily */ - if (reply->expires > -1) + if (httpHeaderHas(hdr, HDR_EXPIRES)) return 1; else return 0; /* NOTREACHED */ break; +/* @?@ should we replace these magic numbers with http_status enums? */ /* Errors can be negatively cached */ case 204: /* No Content */ case 305: /* Use Proxy (proxy redirect) */ @@ -499,6 +512,7 @@ httpCachableReply(HttpStateData * httpState) /* NOTREACHED */ } +/* rewrite this later using new interfaces @?@ */ void httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) { @@ -506,7 +520,7 @@ httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) StoreEntry *entry = httpState->entry; int room; int hdr_len; - struct _http_reply *reply = entry->mem_obj->reply; + HttpReply *reply = entry->mem_obj->reply; debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", storeKeyText(entry->key)); if (httpState->reply_hdr == NULL) @@ -519,7 +533,7 @@ httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) { debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr); httpState->reply_hdr_state += 2; - reply->code = 555; + reply->sline.status = 555; return; } t = httpState->reply_hdr + hdr_len; @@ -535,10 +549,12 @@ httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", httpState->reply_hdr); /* Parse headers into reply structure */ - httpParseReplyHeaders(httpState->reply_hdr, reply); + /* Old code never parsed headers if mime_headers_end failed, was it intentional ? @?@ @?@ */ + /* what happens if we fail to parse here? @?@ @?@ */ + httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */ storeTimestampsSet(entry); /* Check if object is cacheable or not based on reply code */ - debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->code); + debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); switch (httpCachableReply(httpState)) { case 1: httpMakePublic(entry); @@ -553,12 +569,12 @@ httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) assert(0); break; } - if (EBIT_TEST(reply->cache_control, SCC_PROXYREVALIDATE)) + if (httpReplyHasScc(reply, SCC_PROXY_REVALIDATE)) EBIT_SET(entry->flag, ENTRY_REVALIDATE); if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) if (httpState->peer) httpState->peer->stats.n_keepalives_sent++; - if (EBIT_TEST(reply->misc_headers, HDR_PROXY_KEEPALIVE)) + if (httpHeaderHas(&reply->hdr, HDR_PROXY_KEEPALIVE)) if (httpState->peer) httpState->peer->stats.n_keepalives_recv++; } @@ -569,7 +585,7 @@ httpPconnTransferDone(HttpStateData * httpState) { /* return 1 if we got the last of the data on a persistent connection */ MemObject *mem = httpState->entry->mem_obj; - struct _http_reply *reply = mem->reply; + HttpReply *reply = mem->reply; debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); /* * If we didn't send a Keepalive request header, then this @@ -578,7 +594,7 @@ httpPconnTransferDone(HttpStateData * httpState) if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) return 0; debug(11, 5) ("httpPconnTransferDone: content_length=%d\n", - reply->content_length); + httpReplyContentLen(reply)); /* * Deal with gross HTTP stuff * - If we haven't seen the end of the reply headers, we can't @@ -592,13 +608,13 @@ httpPconnTransferDone(HttpStateData * httpState) */ if (httpState->reply_hdr_state < 2) return 0; - else if (reply->code == HTTP_OK) + else if (reply->sline.status == HTTP_OK) (void) 0; /* common case, continue */ - else if (reply->code == HTTP_NO_CONTENT) + else if (reply->sline.status == HTTP_NO_CONTENT) return 1; - else if (reply->code == HTTP_NOT_MODIFIED) + else if (reply->sline.status == HTTP_NOT_MODIFIED) return 1; - else if (reply->code < HTTP_OK) + else if (reply->sline.status < HTTP_OK) return 1; else if (httpState->request->method == METHOD_HEAD) return 1; @@ -607,9 +623,9 @@ httpPconnTransferDone(HttpStateData * httpState) * persistent. If there is a content length, then we must * wait until we've seen the end of the body. */ - if (reply->content_length < 0) + if (httpReplyContentLen(reply) < 0) return 0; - else if (mem->inmem_hi < reply->content_length + reply->hdr_sz) + else if (mem->inmem_hi < httpReplyContentLen(reply) + reply->hdr_sz) return 0; else return 1; @@ -1117,6 +1133,7 @@ httpConnectDone(int fd, int status, void *data) } } +#if 0 /* moved to httpHeader */ void httpReplyHeaderStats(StoreEntry * entry) { @@ -1134,13 +1151,14 @@ httpReplyHeaderStats(StoreEntry * entry) HttpServerCCStr[i], ReplyHeaderStats.cc[i]); } +#endif void httpInit(void) { cachemgrRegister("reply_headers", "HTTP Reply Header Histograms", - httpReplyHeaderStats, 0); + httpHeaderStoreRepReport, 0); } static void @@ -1151,6 +1169,7 @@ httpAbort(void *data) comm_close(httpState->fd); } +#if 0 /* moved to httpResponse.c */ static char * httpStatusString(int status) { @@ -1274,7 +1293,9 @@ httpStatusString(int status) } return p; } +#endif +#if 0 /* moved to HttpResponse.c */ char * httpReplyHeader(double ver, http_status status, @@ -1302,3 +1323,4 @@ httpReplyHeader(double ver, l += snprintf(buf + l, s - l, "Content-Type: %s\r\n", ctype); return buf; } +#endif diff --git a/src/main.cc b/src/main.cc index f7cc870935..63660ed5f1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.224 1998/02/20 21:02:42 wessels Exp $ + * $Id: main.cc,v 1.225 1998/02/21 00:56:57 rousskov Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -441,6 +441,7 @@ mainInitialize(void) dnsOpenServers(); redirectOpenServers(); useragentOpenLog(); + httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */ errorInitialize(); accessLogInit(); diff --git a/src/mem.cc b/src/mem.cc index b4cffd2d41..812aa1bcec 100644 --- a/src/mem.cc +++ b/src/mem.cc @@ -1,6 +1,6 @@ /* - * $Id: mem.cc,v 1.5 1998/02/19 23:28:39 wessels Exp $ + * $Id: mem.cc,v 1.6 1998/02/21 00:56:58 rousskov Exp $ * * DEBUG: section 13 Memory Pool Management * AUTHOR: Harvest Derived @@ -187,7 +187,8 @@ memInit(void) memDataInit(MEM_HIERARCHYLOGENTRY, "HierarchyLogEntry", sizeof(HierarchyLogEntry), 0); memDataInit(MEM_HTTPSTATEDATA, "HttpStateData", sizeof(HttpStateData), 0); - memDataInit(MEM_HTTP_REPLY, "http_reply", sizeof(http_reply), 0); + memDataInit(MEM_HTTPREPLY, "http_reply", sizeof(http_reply), 0); + memDataInit(MEM_HTTP_SCC, "HttpScc", sizeof(HttpScc), 0); memDataInit(MEM_ICPUDPDATA, "icpUdpData", sizeof(icpUdpData), 0); memDataInit(MEM_ICP_COMMON_T, "icp_common_t", sizeof(icp_common_t), 0); memDataInit(MEM_ICP_PING_DATA, "icp_ping_data", sizeof(icp_ping_data), 0); diff --git a/src/mime.cc b/src/mime.cc index 9ba779a9c5..6079ff804d 100644 --- a/src/mime.cc +++ b/src/mime.cc @@ -1,6 +1,6 @@ /* - * $Id: mime.cc,v 1.49 1998/02/13 19:59:01 wessels Exp $ + * $Id: mime.cc,v 1.50 1998/02/21 00:56:59 rousskov Exp $ * * DEBUG: section 25 MIME Parsing * AUTHOR: Harvest Derived @@ -396,7 +396,9 @@ mimeLoadIconFile(const char *icon) { int fd; int n; +#if 0 int l; +#endif int flags; struct stat sb; StoreEntry *e; @@ -429,6 +431,7 @@ mimeLoadIconFile(const char *icon) METHOD_GET); assert(e != NULL); e->mem_obj->request = requestLink(urlParse(METHOD_GET, url)); +#if 0 /* use new interface */ buf = memAllocate(MEM_4K_BUF, 1); l = 0; l += snprintf(buf + l, SM_PAGE_SIZE - l, "HTTP/1.0 200 OK\r\n"); @@ -441,7 +444,15 @@ mimeLoadIconFile(const char *icon) l += snprintf(buf + l, SM_PAGE_SIZE - l, "\r\n"); httpParseReplyHeaders(buf, e->mem_obj->reply); storeAppend(e, buf, l); - while ((n = read(fd, buf, SM_PAGE_SIZE)) > 0) +#else + httpReplyReset(e->mem_obj->reply); + httpReplySetHeaders(e->mem_obj->reply, 1.0, 200, NULL, + type, (int) sb.st_size, sb.st_mtime, squid_curtime + 86400); + httpReplySwapOut(e->mem_obj->reply, e); + /* read the file into the buffer and append it to store */ + buf = memAllocate(MEM_4K_BUF, 1); +#endif + while ((n = read(fd, buf, 4096)) > 0) storeAppend(e, buf, n); file_close(fd); storeSetPublicKey(e); diff --git a/src/protos.h b/src/protos.h index d9a58c2009..f98081fbef 100644 --- a/src/protos.h +++ b/src/protos.h @@ -108,6 +108,7 @@ extern void comm_write(int fd, CWCB * handler, void *handler_data, FREE *); +extern void comm_write_mbuf(int fd, MemBuf mb, CWCB *handler, void *handler_data); extern void commCallCloseHandlers(int fd); extern int commSetTimeout(int fd, int, PF *, void *); extern void commSetDefer(int fd, DEFER * func, void *); @@ -199,9 +200,11 @@ extern HASHHASH hash4; extern int httpCachable(method_t); extern void httpStart(request_t *, StoreEntry *, peer *); -extern void httpParseReplyHeaders(const char *, struct _http_reply *); +extern void httpParseReplyHeaders(const char *, http_reply *); extern void httpProcessReplyHeader(HttpStateData *, const char *, int); +#if 0 extern void httpReplyHeaderStats(StoreEntry *); +#endif extern size_t httpBuildRequestHeader(request_t * request, request_t * orig_request, StoreEntry * entry, @@ -212,12 +215,14 @@ extern size_t httpBuildRequestHeader(request_t * request, int flags); extern int httpAnonAllowed(const char *line); extern int httpAnonDenied(const char *line); +#if 0 extern char *httpReplyHeader(double ver, http_status status, char *ctype, int clen, time_t lmt, time_t expires); +#endif extern void httpInit(void); @@ -449,6 +454,7 @@ extern void storeAppendPrintf(StoreEntry *, const char *,...); #else extern void storeAppendPrintf(); #endif +extern void storeAppendVPrintf(StoreEntry *, const char *, va_list ap); extern int storeCheckCachable(StoreEntry * e); extern void storeUnlinkFileno(int fileno); extern void storeSetPrivateKey(StoreEntry *); @@ -616,8 +622,10 @@ extern void useragentRotateLog(void); extern void logUserAgent(const char *, const char *); extern peer_t parseNeighborType(const char *s); +extern HttpReply *errorBuildReply(ErrorState * err); extern void errorSend(int fd, ErrorState *); extern void errorAppendEntry(StoreEntry *, ErrorState *); +void errorStateFree(ErrorState * err); extern void errorInitialize(void); extern void errorFree(void); extern ErrorState *errorCon(err_type, http_status); diff --git a/src/store.cc b/src/store.cc index 71eb00962d..53e2f4d0d8 100644 --- a/src/store.cc +++ b/src/store.cc @@ -1,6 +1,6 @@ /* - * $Id: store.cc,v 1.383 1998/02/20 16:03:20 wessels Exp $ + * $Id: store.cc,v 1.384 1998/02/21 00:57:02 rousskov Exp $ * * DEBUG: section 20 Storeage Manager * AUTHOR: Harvest Derived @@ -176,11 +176,7 @@ static MemObject * new_MemObject(const char *url, const char *log_url) { MemObject *mem = memAllocate(MEM_MEMOBJECT, 1); - mem->reply = memAllocate(MEM_HTTP_REPLY, 1); - mem->reply->date = -2; - mem->reply->expires = -2; - mem->reply->last_modified = -2; - mem->reply->content_length = -1; + mem->reply = httpReplyCreate(); mem->url = xstrdup(url); mem->log_url = xstrdup(log_url); mem->swapout.fd = -1; @@ -214,7 +210,7 @@ destroy_MemObject(StoreEntry * e) storeUnregister(e, mem->clients->callback_data); #endif assert(mem->clients == NULL); - memFree(MEM_HTTP_REPLY, mem->reply); + httpReplyDestroy(mem->reply); safe_free(mem->url); safe_free(mem->log_url); requestUnlink(mem->request); @@ -458,6 +454,7 @@ storeAppend(StoreEntry * e, const char *buf, int len) debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", len, storeKeyText(e->key)); + tmp_debug(here) ("bytes: '%.20s'\n", buf); /* @?@ @?@ */ storeGetMemSpace(len); stmemAppend(mem->data, buf, len); mem->inmem_hi += len; @@ -473,7 +470,6 @@ void storeAppendPrintf(StoreEntry * e, const char *fmt,...) { va_list args; - LOCAL_ARRAY(char, buf, 4096); va_start(args, fmt); #else void @@ -483,15 +479,22 @@ storeAppendPrintf(va_alist) va_list args; StoreEntry *e = NULL; const char *fmt = NULL; - LOCAL_ARRAY(char, buf, 4096); va_start(args); e = va_arg(args, StoreEntry *); fmt = va_arg(args, char *); #endif + storeAppendVPrintf(e, fmt, args); + va_end(args); +} + +/* used be storeAppendPrintf and Packer */ +void +storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs) +{ + LOCAL_ARRAY(char, buf, 4096); buf[0] = '\0'; - vsnprintf(buf, 4096, fmt, args); + vsnprintf(buf, 4096, fmt, vargs); storeAppend(e, buf, strlen(buf)); - va_end(args); } int @@ -746,16 +749,18 @@ storeEntryValidLength(const StoreEntry * e) { int diff; http_reply *reply; + int clen; assert(e->mem_obj != NULL); reply = e->mem_obj->reply; + clen = httpReplyContentLen(reply); debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key)); debug(20, 5) ("storeEntryValidLength: object_len = %d\n", objectLen(e)); debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n", reply->hdr_sz); debug(20, 5) ("storeEntryValidLength: content_length = %d\n", - reply->content_length); - if (reply->content_length < 0) { + clen); + if (clen < 0) { debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n", storeKeyText(e->key)); return 1; @@ -770,11 +775,11 @@ storeEntryValidLength(const StoreEntry * e) storeKeyText(e->key)); return 1; } - if (reply->code == HTTP_NOT_MODIFIED) + if (reply->sline.status == HTTP_NOT_MODIFIED) return 1; - if (reply->code == HTTP_NO_CONTENT) + if (reply->sline.status == HTTP_NO_CONTENT) return 1; - diff = reply->hdr_sz + reply->content_length - objectLen(e); + diff = reply->hdr_sz + clen - objectLen(e); if (diff == 0) return 1; debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n", @@ -966,14 +971,25 @@ void storeTimestampsSet(StoreEntry * entry) { time_t served_date = -1; - struct _http_reply *reply = entry->mem_obj->reply; + HttpReply *reply = entry->mem_obj->reply; +#if 0 /* new interface */ served_date = reply->date > -1 ? reply->date : squid_curtime; entry->expires = reply->expires; if (reply->last_modified > -1) entry->lastmod = reply->last_modified; else entry->lastmod = served_date; +#else + served_date = httpHeaderGetTime(&reply->hdr, HDR_DATE); + if (served_date < 0) + served_date = squid_curtime; + entry->expires = httpReplyExpires(reply); + entry->lastmod = httpHeaderGetTime(&reply->hdr, HDR_LAST_MODIFIED); + if (entry->lastmod < 0) + entry->lastmod = served_date; +#endif entry->timestamp = served_date; + } void @@ -1080,6 +1096,7 @@ storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url) e->mem_obj = new_MemObject(url, log_url); } +#if 0 /* moved to HttpReply.c (has nothing to do with store.c) */ void storeCopyNotModifiedReplyHeaders(MemObject * oldmem, MemObject * newmem) { @@ -1094,6 +1111,7 @@ storeCopyNotModifiedReplyHeaders(MemObject * oldmem, MemObject * newmem) if (newreply->expires > -1) oldreply->expires = newreply->expires; } +#endif /* this just sets DELAY_SENDING */ void diff --git a/src/store_client.cc b/src/store_client.cc index b0842c60d4..725f80b64b 100644 --- a/src/store_client.cc +++ b/src/store_client.cc @@ -245,8 +245,8 @@ storeClientReadBody(int fd, const char *buf, int len, int flagnotused, void *dat sc->disk_op_in_progress = 0; assert(sc->callback != NULL); debug(20, 3) ("storeClientReadBody: FD %d, len %d\n", fd, len); - if (sc->copy_offset == 0 && len > 0 && mem->reply->code == 0) - httpParseReplyHeaders(sc->copy_buf, mem->reply); + if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0) + httpReplyParse(mem->reply, sc->copy_buf); sc->callback = NULL; callback(sc->callback_data, sc->copy_buf, len); } @@ -305,8 +305,8 @@ storeClientReadHeader(int fd, const char *buf, int len, int flagnotused, void *d copy_sz); xmemcpy(sc->copy_buf, buf + swap_hdr_sz, copy_sz); memFree(MEM_DISK_BUF, (void *) buf); - if (sc->copy_offset == 0 && len > 0 && mem->reply->code == 0) - httpParseReplyHeaders(sc->copy_buf, mem->reply); + if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0) + httpReplyParse(mem->reply, sc->copy_buf); sc->callback = NULL; callback(sc->callback_data, sc->copy_buf, copy_sz); return; diff --git a/src/store_log.cc b/src/store_log.cc index f6bab8673f..9a9210e0da 100644 --- a/src/store_log.cc +++ b/src/store_log.cc @@ -16,7 +16,8 @@ storeLog(int tag, const StoreEntry * e) { LOCAL_ARRAY(char, logmsg, MAX_URL << 1); MemObject *mem = e->mem_obj; - struct _http_reply *reply; + HttpReply *reply; + const char *ctype; if (storelog_fd < 0) return; if (mem == NULL) @@ -27,17 +28,18 @@ storeLog(int tag, const StoreEntry * e) mem->log_url = xstrdup(mem->url); } reply = mem->reply; + ctype = httpHeaderGetStr(&reply->hdr, HDR_CONTENT_TYPE); snprintf(logmsg, MAX_URL << 1, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n", (int) current_time.tv_sec, (int) current_time.tv_usec / 1000, storeLogTags[tag], e->swap_file_number, - reply->code, - (int) reply->date, - (int) reply->last_modified, - (int) reply->expires, - reply->content_type[0] ? reply->content_type : "unknown", - reply->content_length, + reply->sline.status, + (int) httpHeaderGetTime(&reply->hdr, HDR_DATE), + (int) httpHeaderGetTime(&reply->hdr, HDR_LAST_MODIFIED), + (int) httpReplyExpires(reply), + ctype ? ctype : "unknown", + httpReplyContentLen(reply), (int) (mem->inmem_hi - mem->reply->hdr_sz), RequestMethodStr[mem->method], mem->log_url); diff --git a/src/structs.h b/src/structs.h index 4249a67a09..417662ac5b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,7 +1,6 @@ - struct _acl_ip_data { struct in_addr addr1; /* if addr2 non-zero then its a range */ struct in_addr addr2; @@ -398,6 +397,7 @@ struct _fde { PF *timeout_handler; time_t timeout; void *timeout_data; + void *lifetime_data; close_handler *close_handler; /* linked list */ DEFER *defer_check; /* check if we should defer read */ void *defer_data; @@ -429,19 +429,34 @@ struct _hash_table { hash_link *current_ptr; }; +#if ! USE_ALEX_CODE +#error must USE_ALEX_CODE +#endif + +#include "MemBuf.h" +#include "Packer.h" +#include "HttpReply.h" + +# if 0 /* tmp moved to HttpReply.h */ +#define Const const struct _http_reply { double version; int code; int content_length; - size_t hdr_sz; - int cache_control; - int misc_headers; - time_t date; - time_t expires; - time_t last_modified; - char content_type[HTTP_REPLY_FIELD_SZ]; - char user_agent[HTTP_REPLY_FIELD_SZ << 2]; + int hdr_sz; /* includes _stored_ status-line, headers, and */ + /* Note: fields below may not match info stored on disk */ + Const int cache_control; + Const int misc_headers; + Const time_t date; + Const time_t expires; + Const time_t last_modified; + Const char content_type[HTTP_REPLY_FIELD_SZ]; +#if 0 /* unused 512 bytes? */ + Const char user_agent[HTTP_REPLY_FIELD_SZ << 2]; +#endif }; +#endif + struct _HttpStateData { StoreEntry *entry; @@ -736,11 +751,13 @@ struct _icp_common_t { u_num32 shostid; /* sender host id */ }; +#if 0 /* this struct is not used */ struct _Stack { void **base; void **top; int stack_size; }; +#endif struct _Meta_data { int hot_vm; @@ -804,7 +821,11 @@ struct _MemObject { int fd; void *ctrl; } swapout; +#if 0 struct _http_reply *reply; +#else + HttpReply *reply; +#endif request_t *request; struct timeval start_ping; IRCB *icp_reply_callback; diff --git a/src/typedefs.h b/src/typedefs.h index ed02e80cdc..93dce7a4bd 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -41,7 +41,11 @@ typedef struct _fileMap fileMap; typedef struct _fqdncache_entry fqdncache_entry; typedef struct _hash_link hash_link; typedef struct _hash_table hash_table; +#if 0 /* use new interfaces */ typedef struct _http_reply http_reply; +#else +typedef struct _HttpReply http_reply; +#endif typedef struct _HttpStateData HttpStateData; typedef struct _icpUdpData icpUdpData; typedef struct _clientHttpRequest clientHttpRequest; diff --git a/src/urn.cc b/src/urn.cc index f13da37fa3..a6cd173799 100644 --- a/src/urn.cc +++ b/src/urn.cc @@ -152,7 +152,11 @@ urnHandleReply(void *data, char *buf, ssize_t size) StoreEntry *e = urnState->entry; StoreEntry *urlres_e = urnState->urlres_e; char *s = NULL; +#if 0 char *hdr; +#else + HttpReply *rep; +#endif wordlist *w; wordlist *urls; wordlist *min_w; @@ -192,10 +196,10 @@ urnHandleReply(void *data, char *buf, ssize_t size) return; } assert(urlres_e->mem_obj->reply); - httpParseReplyHeaders(buf, urlres_e->mem_obj->reply); + httpReplyParse(urlres_e->mem_obj->reply, buf); debug(52, 3) ("mem->reply exists, code=%d.\n", - urlres_e->mem_obj->reply->code); - if (urlres_e->mem_obj->reply->code != HTTP_OK) { + urlres_e->mem_obj->reply->sline.status); + if (urlres_e->mem_obj->reply->sline.status != HTTP_OK) { debug(52, 3) ("urnHandleReply: failed.\n"); err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND); err->request = requestLink(urnState->request); @@ -241,7 +245,8 @@ urnHandleReply(void *data, char *buf, ssize_t size) "\n", appname, version_string, getMyHostname()); stringAppend(S, line, l); - hdr = httpReplyHeader(1.0, +#if 0 /* use new interface */ + hdr = httpReplyHeader(1.0, HTTP_MOVED_TEMPORARILY, "text/html", stringLength(S), @@ -257,6 +262,20 @@ urnHandleReply(void *data, char *buf, ssize_t size) } storeAppend(e, "\r\n", 2); storeAppend(e, S->buf, stringLength(S)); +#else + rep = e->mem_obj->reply; + httpReplyReset(rep); + httpReplySetHeaders(rep, 1.0, HTTP_MOVED_TEMPORARILY, NULL, + "text/html", stringLength(S), 0, squid_curtime); + if (EBIT_TEST(urnState->flags, URN_FORCE_MENU)) { + debug(51, 3) ("urnHandleReply: forcing menu\n"); + } else + if (min_w) { + httpHeaderSetStr(&rep->hdr, HDR_LOCATION, min_w->key); + } + httpBodySet(&rep->body, S->buf, stringLength(S)+1, NULL); + httpReplySwapOut(rep, e); +#endif storeComplete(e); memFree(MEM_4K_BUF, buf); wordlistDestroy(&urls); -- 2.39.5