summaryrefslogtreecommitdiff
path: root/modules/cache
diff options
context:
space:
mode:
authorArno Töll <debian@toell.net>2012-01-08 22:53:17 +0100
committerArno Töll <debian@toell.net>2012-01-08 22:53:17 +0100
commite072a2dd866b7cb9f14319b80326a4e7fd16fcdf (patch)
treea49dfc56d94a26011fe157835ff6cbe14edbd8a9 /modules/cache
parent0890390c00801651d08d3794e13b31a5dabbf5ef (diff)
downloadapache2-e072a2dd866b7cb9f14319b80326a4e7fd16fcdf.tar.gz
Imported Upstream version 2.3.16-beta
Diffstat (limited to 'modules/cache')
-rw-r--r--modules/cache/NWGNUcach_dsk (renamed from modules/cache/NWGNUdsk_cach)41
-rw-r--r--modules/cache/NWGNUmakefile6
-rw-r--r--modules/cache/NWGNUmod_cach30
-rw-r--r--modules/cache/NWGNUsocachdbm (renamed from modules/cache/NWGNUmem_cach)49
-rw-r--r--modules/cache/NWGNUsocachmem260
-rw-r--r--modules/cache/NWGNUsocachshmcb260
-rw-r--r--modules/cache/cache_cache.c165
-rw-r--r--modules/cache/cache_cache.h111
-rw-r--r--modules/cache/cache_common.h55
-rw-r--r--modules/cache/cache_disk_common.h68
-rw-r--r--modules/cache/cache_hash.c290
-rw-r--r--modules/cache/cache_hash.h159
-rw-r--r--modules/cache/cache_pqueue.c290
-rw-r--r--modules/cache/cache_pqueue.h170
-rw-r--r--modules/cache/cache_storage.c269
-rw-r--r--modules/cache/cache_storage.h61
-rw-r--r--modules/cache/cache_util.c778
-rw-r--r--modules/cache/cache_util.h300
-rw-r--r--modules/cache/config.m4135
-rw-r--r--modules/cache/mod_cache.c1527
-rw-r--r--modules/cache/mod_cache.dep105
-rw-r--r--modules/cache/mod_cache.h344
-rw-r--r--modules/cache/mod_cache.imp9
-rw-r--r--modules/cache/mod_cache.mak370
-rw-r--r--modules/cache/mod_cache_disk.c (renamed from modules/cache/mod_disk_cache.c)901
-rw-r--r--modules/cache/mod_cache_disk.dsp (renamed from modules/cache/mod_disk_cache.dsp)38
-rw-r--r--modules/cache/mod_cache_disk.h91
-rw-r--r--modules/cache/mod_disk_cache.dep42
-rw-r--r--modules/cache/mod_disk_cache.h95
-rw-r--r--modules/cache/mod_disk_cache.mak381
-rw-r--r--modules/cache/mod_file_cache.c51
-rw-r--r--modules/cache/mod_file_cache.dep30
-rw-r--r--modules/cache/mod_file_cache.mak353
-rw-r--r--modules/cache/mod_mem_cache.c1043
-rw-r--r--modules/cache/mod_mem_cache.dep116
-rw-r--r--modules/cache/mod_mem_cache.mak407
-rw-r--r--modules/cache/mod_socache_dbm.c588
-rw-r--r--modules/cache/mod_socache_dbm.dsp (renamed from modules/cache/mod_mem_cache.dsp)76
-rw-r--r--modules/cache/mod_socache_dc.c190
-rw-r--r--modules/cache/mod_socache_dc.dsp111
-rw-r--r--modules/cache/mod_socache_memcache.c326
-rw-r--r--modules/cache/mod_socache_memcache.dsp111
-rw-r--r--modules/cache/mod_socache_shmcb.c1023
-rw-r--r--modules/cache/mod_socache_shmcb.dsp111
44 files changed, 6389 insertions, 5547 deletions
diff --git a/modules/cache/NWGNUdsk_cach b/modules/cache/NWGNUcach_dsk
index 2df9a3ee..1c45f344 100644
--- a/modules/cache/NWGNUdsk_cach
+++ b/modules/cache/NWGNUcach_dsk
@@ -3,7 +3,7 @@
#
SUBDIRS = \
- $(EOLIST)
+ $(EOLIST)
#
# Get the 'head' of the build environment. This includes default targets and
@@ -26,8 +26,8 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
+ $(AP_WORK)/server/mpm/netware \
$(NWOS) \
- $(SERVER)/mpm/NetWare \
$(EOLIST)
#
@@ -99,10 +99,10 @@ endif
# This is used by the link 'name' directive to name the nlm. If left blank
# TARGET_nlm (see below) will be used.
#
-NLM_NAME = dsk_cach
+NLM_NAME = cach_dsk
#
-# This is used by the link '-desc ' directive.
+# This is used by the link '-desc ' directive.
# If left blank, NLM_NAME will be used.
#
NLM_DESCRIPTION = Apache $(VERSION_STR) Memory Cache Sub-Module
@@ -111,19 +111,19 @@ NLM_DESCRIPTION = Apache $(VERSION_STR) Memory Cache Sub-Module
# This is used by the '-threadname' directive. If left blank,
# NLM_NAME Thread will be used.
#
-NLM_THREAD_NAME = dsk_cach
+NLM_THREAD_NAME = $(NLM_NAME)
#
-# If this is specified, it will override VERSION value in
+# If this is specified, it will override VERSION value in
# $(AP_WORK)/build/NWGNUenvironment.inc
#
-NLM_VERSION =
+NLM_VERSION =
#
# If this is specified, it will override the default of 64K
#
NLM_STACK_SIZE = 65536
-
+
#
# If this is specified it will be used by the link '-entry' directive
@@ -144,13 +144,13 @@ NLM_CHECK_SYM =
# If this is specified it will be used by the link '-flags' directive
#
NLM_FLAGS =
-
+
#
-# If this is specified it will be linked in with the XDCData option in the def
+# If this is specified it will be linked in with the XDCData option in the def
# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
# by setting APACHE_UNIPROC in the environment
#
-XDCDATA =
+XDCDATA =
#
# Declare all target files (you must add your files here)
@@ -160,7 +160,7 @@ XDCDATA =
# If there is an NLM target, put it here
#
TARGET_nlm = \
- $(OBJDIR)/dsk_cach.nlm \
+ $(OBJDIR)/$(NLM_NAME).nlm \
$(EOLIST)
#
@@ -174,7 +174,7 @@ TARGET_lib = \
# Paths must all use the '/' character
#
FILES_nlm_objs = \
- $(OBJDIR)/mod_disk_cache.o \
+ $(OBJDIR)/mod_cache_disk.o \
$(EOLIST)
#
@@ -199,7 +199,7 @@ FILES_nlm_modules = \
# If the nlm has a msg file, put it's path here
#
FILE_nlm_msg =
-
+
#
# If the nlm has a hlp file put it's path here
#
@@ -219,16 +219,15 @@ FILES_nlm_Ximports = \
@httpd.imp \
@mod_cache.imp \
$(EOLIST)
-
-#
+
+#
# Any symbols exported to here
#
FILES_nlm_exports = \
- disk_cache_module \
+ cache_disk_module \
$(EOLIST)
-# @cache.imp \
-#
+#
# These are the OBJ files needed to create the LIB target above.
# Paths must all use the '/' character
#
@@ -244,11 +243,11 @@ libs :: $(OBJDIR) $(TARGET_lib)
nlms :: libs $(TARGET_nlm)
#
-# Updated this target to create necessary directories and copy files to the
+# Updated this target to create necessary directories and copy files to the
# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
#
install :: nlms FORCE
-
+
#
# Any specialized rules here
#
diff --git a/modules/cache/NWGNUmakefile b/modules/cache/NWGNUmakefile
index ef097b24..bc0c58fe 100644
--- a/modules/cache/NWGNUmakefile
+++ b/modules/cache/NWGNUmakefile
@@ -153,8 +153,10 @@ XDCDATA =
#
TARGET_nlm = \
$(OBJDIR)/mod_cach.nlm \
- $(OBJDIR)/mem_cach.nlm \
- $(OBJDIR)/dsk_cach.nlm \
+ $(OBJDIR)/cach_dsk.nlm \
+ $(OBJDIR)/socachdbm.nlm \
+ $(OBJDIR)/socachmem.nlm \
+ $(OBJDIR)/socachshmcb.nlm \
$(EOLIST)
#
diff --git a/modules/cache/NWGNUmod_cach b/modules/cache/NWGNUmod_cach
index 589bc71b..b17b6b69 100644
--- a/modules/cache/NWGNUmod_cach
+++ b/modules/cache/NWGNUmod_cach
@@ -3,7 +3,7 @@
#
SUBDIRS = \
- $(EOLIST)
+ $(EOLIST)
#
# Get the 'head' of the build environment. This includes default targets and
@@ -26,8 +26,8 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
+ $(AP_WORK)/server/mpm/netware \
$(NWOS) \
- $(SERVER)/mpm/NetWare \
$(EOLIST)
#
@@ -103,7 +103,7 @@ endif
NLM_NAME = mod_cach
#
-# This is used by the link '-desc ' directive.
+# This is used by the link '-desc ' directive.
# If left blank, NLM_NAME will be used.
#
NLM_DESCRIPTION = Apache $(VERSION_STR) Cache module
@@ -115,16 +115,16 @@ NLM_DESCRIPTION = Apache $(VERSION_STR) Cache module
NLM_THREAD_NAME = mod_cach
#
-# If this is specified, it will override VERSION value in
+# If this is specified, it will override VERSION value in
# $(AP_WORK)/build/NWGNUenvironment.inc
#
-NLM_VERSION =
+NLM_VERSION =
#
# If this is specified, it will override the default of 64K
#
NLM_STACK_SIZE = 65536
-
+
#
# If this is specified it will be used by the link '-entry' directive
@@ -145,13 +145,13 @@ NLM_CHECK_SYM =
# If this is specified it will be used by the link '-flags' directive
#
NLM_FLAGS =
-
+
#
-# If this is specified it will be linked in with the XDCData option in the def
+# If this is specified it will be linked in with the XDCData option in the def
# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
# by setting APACHE_UNIPROC in the environment
#
-XDCDATA =
+XDCDATA =
#
# Declare all target files (you must add your files here)
@@ -201,7 +201,7 @@ FILES_nlm_modules = \
# If the nlm has a msg file, put it's path here
#
FILE_nlm_msg =
-
+
#
# If the nlm has a hlp file put it's path here
#
@@ -221,8 +221,8 @@ FILES_nlm_Ximports = \
@httpd.imp \
@netware.imp \
$(EOLIST)
-
-#
+
+#
# Any symbols exported to here
#
FILES_nlm_exports = \
@@ -230,7 +230,7 @@ FILES_nlm_exports = \
cache_module \
$(EOLIST)
-#
+#
# These are the OBJ files needed to create the LIB target above.
# Paths must all use the '/' character
#
@@ -246,11 +246,11 @@ libs :: $(OBJDIR) $(TARGET_lib)
nlms :: libs $(TARGET_nlm)
#
-# Updated this target to create necessary directories and copy files to the
+# Updated this target to create necessary directories and copy files to the
# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
#
install :: nlms FORCE
-
+
#
# Any specialized rules here
#
diff --git a/modules/cache/NWGNUmem_cach b/modules/cache/NWGNUsocachdbm
index 20e49000..dc06af7d 100644
--- a/modules/cache/NWGNUmem_cach
+++ b/modules/cache/NWGNUsocachdbm
@@ -3,7 +3,7 @@
#
SUBDIRS = \
- $(EOLIST)
+ $(EOLIST)
#
# Get the 'head' of the build environment. This includes default targets and
@@ -26,8 +26,8 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
+ $(AP_WORK)/server/mpm/netware \
$(NWOS) \
- $(SERVER)/mpm/NetWare \
$(EOLIST)
#
@@ -60,7 +60,6 @@ XCFLAGS += \
$(EOLIST)
XDEFINES += \
- -DDEBUG \
$(EOLIST)
XLFLAGS += \
@@ -100,31 +99,31 @@ endif
# This is used by the link 'name' directive to name the nlm. If left blank
# TARGET_nlm (see below) will be used.
#
-NLM_NAME = mem_cach
+NLM_NAME = socachedbm
#
-# This is used by the link '-desc ' directive.
+# This is used by the link '-desc ' directive.
# If left blank, NLM_NAME will be used.
#
-NLM_DESCRIPTION = Apache $(VERSION_STR) Memory Cache Sub-Module
+NLM_DESCRIPTION = Apache $(VERSION_STR) Socache DBM Module
#
# This is used by the '-threadname' directive. If left blank,
# NLM_NAME Thread will be used.
#
-NLM_THREAD_NAME = mem_cach
+NLM_THREAD_NAME = socachedbm
#
-# If this is specified, it will override VERSION value in
+# If this is specified, it will override VERSION value in
# $(AP_WORK)/build/NWGNUenvironment.inc
#
-NLM_VERSION =
+NLM_VERSION =
#
# If this is specified, it will override the default of 64K
#
NLM_STACK_SIZE = 65536
-
+
#
# If this is specified it will be used by the link '-entry' directive
@@ -145,13 +144,13 @@ NLM_CHECK_SYM =
# If this is specified it will be used by the link '-flags' directive
#
NLM_FLAGS =
-
+
#
-# If this is specified it will be linked in with the XDCData option in the def
+# If this is specified it will be linked in with the XDCData option in the def
# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
# by setting APACHE_UNIPROC in the environment
#
-XDCDATA =
+XDCDATA =
#
# Declare all target files (you must add your files here)
@@ -161,7 +160,7 @@ XDCDATA =
# If there is an NLM target, put it here
#
TARGET_nlm = \
- $(OBJDIR)/mem_cach.nlm \
+ $(OBJDIR)/$(NLM_NAME).nlm \
$(EOLIST)
#
@@ -175,10 +174,7 @@ TARGET_lib = \
# Paths must all use the '/' character
#
FILES_nlm_objs = \
- $(OBJDIR)/mod_mem_cache.o \
- $(OBJDIR)/cache_hash.o \
- $(OBJDIR)/cache_pqueue.o \
- $(OBJDIR)/cache_cache.o \
+ $(OBJDIR)/mod_socache_dbm.o \
$(EOLIST)
#
@@ -196,14 +192,13 @@ FILES_nlm_libs = \
FILES_nlm_modules = \
Apache2 \
Libc \
- mod_cach \
$(EOLIST)
#
# If the nlm has a msg file, put it's path here
#
FILE_nlm_msg =
-
+
#
# If the nlm has a hlp file put it's path here
#
@@ -221,18 +216,16 @@ FILES_nlm_Ximports = \
@libc.imp \
@aprlib.imp \
@httpd.imp \
- @mod_cache.imp \
$(EOLIST)
-
-#
+
+#
# Any symbols exported to here
#
FILES_nlm_exports = \
- mem_cache_module \
+ socache_dbm_module \
$(EOLIST)
-# @cache.imp \
-#
+#
# These are the OBJ files needed to create the LIB target above.
# Paths must all use the '/' character
#
@@ -248,11 +241,11 @@ libs :: $(OBJDIR) $(TARGET_lib)
nlms :: libs $(TARGET_nlm)
#
-# Updated this target to create necessary directories and copy files to the
+# Updated this target to create necessary directories and copy files to the
# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
#
install :: nlms FORCE
-
+
#
# Any specialized rules here
#
diff --git a/modules/cache/NWGNUsocachmem b/modules/cache/NWGNUsocachmem
new file mode 100644
index 00000000..d8d10d8a
--- /dev/null
+++ b/modules/cache/NWGNUsocachmem
@@ -0,0 +1,260 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(AP_WORK)/include \
+ $(AP_WORK)/server/mpm/netware \
+ $(NWOS) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = socachemem
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Socache Memory Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = socachemem
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 65536
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/mod_socache_memcache.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ Apache2 \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @libc.imp \
+ @aprlib.imp \
+ @httpd.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ socache_memcache_module \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/cache/NWGNUsocachshmcb b/modules/cache/NWGNUsocachshmcb
new file mode 100644
index 00000000..8f11c4b6
--- /dev/null
+++ b/modules/cache/NWGNUsocachshmcb
@@ -0,0 +1,260 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APRUTIL)/include \
+ $(AP_WORK)/include \
+ $(AP_WORK)/server/mpm/netware \
+ $(NWOS) \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = socacheshmcb
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Apache $(VERSION_STR) Socache DC Module
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = socacheshmcb
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 65536
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/mod_socache_shmcb.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ Apache2 \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @libc.imp \
+ @aprlib.imp \
+ @httpd.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ socache_shmcb_module \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/cache/cache_cache.c b/modules/cache/cache_cache.c
deleted file mode 100644
index 4fc95d73..00000000
--- a/modules/cache/cache_cache.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "apr_general.h"
-
-#include "mod_cache.h"
-#include "cache_hash.h"
-#include "cache_pqueue.h"
-#include "cache_cache.h"
-
-#if APR_HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#if APR_HAVE_STRING_H
-#include <string.h>
-#endif
-
-struct cache_cache_t {
- int max_entries;
- apr_size_t max_size;
- apr_size_t current_size;
- int total_purges;
- long queue_clock;
- cache_hash_t *ht;
- cache_pqueue_t *pq;
- cache_pqueue_set_priority set_pri;
- cache_pqueue_get_priority get_pri;
- cache_cache_inc_frequency *inc_entry;
- cache_cache_get_size *size_entry;
- cache_cache_get_key *key_entry;
- cache_cache_free *free_entry;
-};
-
-cache_cache_t* cache_init(int max_entries,
- apr_size_t max_size,
- cache_pqueue_get_priority get_pri,
- cache_pqueue_set_priority set_pri,
- cache_pqueue_getpos get_pos,
- cache_pqueue_setpos set_pos,
- cache_cache_inc_frequency *inc_entry,
- cache_cache_get_size *size_entry,
- cache_cache_get_key* key_entry,
- cache_cache_free *free_entry)
-{
- cache_cache_t *tmp;
- tmp = malloc(sizeof(cache_cache_t));
- tmp->max_entries = max_entries;
- tmp->max_size = max_size;
- tmp->current_size = 0;
- tmp->total_purges = 0;
- tmp->queue_clock = 0;
- tmp->get_pri = get_pri;
- tmp->set_pri = set_pri;
- tmp->inc_entry = inc_entry;
- tmp->size_entry = size_entry;
- tmp->key_entry = key_entry;
- tmp->free_entry = free_entry;
-
- tmp->ht = cache_hash_make(max_entries);
- tmp->pq = cache_pq_init(max_entries, get_pri, get_pos, set_pos);
-
- return tmp;
-}
-
-void cache_free(cache_cache_t *c)
-{
- cache_pq_free(c->pq);
- cache_hash_free(c->ht);
- free(c);
-}
-
-
-void* cache_find(cache_cache_t* c, const char *key)
-{
- return cache_hash_get(c->ht, key, CACHE_HASH_KEY_STRING);
-}
-
-void cache_update(cache_cache_t* c, void *entry)
-{
- long old_priority;
- long new_priority;
-
- old_priority = c->set_pri(c->queue_clock, entry);
- c->inc_entry(entry);
- new_priority = c->set_pri(c->queue_clock, entry);
- cache_pq_change_priority(c->pq, old_priority, new_priority, entry);
-}
-
-void cache_insert(cache_cache_t* c, void *entry)
-{
- void *ejected = NULL;
- long priority;
-
- c->set_pri(c->queue_clock, entry);
- /* FIX: check if priority of bottom item is greater than inserted one */
- while ((cache_pq_size(c->pq) >= c->max_entries) ||
- ((c->current_size + c->size_entry(entry)) > c->max_size)) {
-
- ejected = cache_pq_pop(c->pq);
- /* FIX: If ejected is NULL, we'll segfault here */
- priority = c->get_pri(ejected);
-
- if (c->queue_clock > priority)
- c->queue_clock = priority;
-
- cache_hash_set(c->ht,
- c->key_entry(ejected),
- CACHE_HASH_KEY_STRING,
- NULL);
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Cache Purge of %s",c->key_entry(ejected));
- c->current_size -= c->size_entry(ejected);
- c->free_entry(ejected);
- c->total_purges++;
- }
- c->current_size += c->size_entry(entry);
-
- cache_pq_insert(c->pq, entry);
- cache_hash_set(c->ht, c->key_entry(entry), CACHE_HASH_KEY_STRING, entry);
-}
-
-void* cache_pop(cache_cache_t *c)
-{
- void *entry;
-
- if (!c)
- return NULL;
-
- entry = cache_pq_pop(c->pq);
-
- if (!entry)
- return NULL;
-
- c->current_size -= c->size_entry(entry);
- cache_hash_set(c->ht, c->key_entry(entry), CACHE_HASH_KEY_STRING, NULL);
-
- return entry;
-}
-
-apr_status_t cache_remove(cache_cache_t *c, void *entry)
-{
- apr_size_t entry_size = c->size_entry(entry);
- apr_status_t rc;
- rc = cache_pq_remove(c->pq, entry);
- if (rc != APR_SUCCESS)
- return rc;
-
- cache_hash_set(c->ht, c->key_entry(entry), CACHE_HASH_KEY_STRING, NULL);
- c->current_size -= entry_size;
-
- return APR_SUCCESS;
-}
diff --git a/modules/cache/cache_cache.h b/modules/cache/cache_cache.h
deleted file mode 100644
index e805cf20..00000000
--- a/modules/cache/cache_cache.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file cache_cache.h
- * @brief Cache Cache Functions
- *
- * @defgroup Cache_cache Cache Functions
- * @ingroup MOD_CACHE
- * @{
- */
-
-#ifndef CACHE_CACHE_H
-#define CACHE_CACHE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "mod_cache.h"
-
-/** ADT for the cache */
-typedef struct cache_cache_t cache_cache_t;
-
-/** callback to increment the frequency of a item */
-typedef void cache_cache_inc_frequency(void*a);
-/** callback to get the size of a item */
-typedef apr_size_t cache_cache_get_size(void*a);
-/** callback to get the key of a item */
-typedef const char* cache_cache_get_key(void *a);
-/** callback to free an entry */
-typedef void cache_cache_free(void *a);
-
-/**
- * initialize the cache ADT
- * @param max_entries the number of entries in the cache
- * @param max_size the size of the cache
- * @param get_pri callback to get a priority of a entry
- * @param set_pri callback to set a priority of a entry
- * @param get_pos callback to get the position of a entry in the cache
- * @param set_pos callback to set the position of a entry in the cache
- * @param inc_entry callback to increment the frequency of a entry
- * @param size_entry callback to get the size of a entry
- * @param key_entry callback to get the key of a entry
- * @param free_entry callback to free an entry
- */
-cache_cache_t* cache_init(int max_entries,
- apr_size_t max_size,
- cache_pqueue_get_priority get_pri,
- cache_pqueue_set_priority set_pri,
- cache_pqueue_getpos get_pos,
- cache_pqueue_setpos set_pos,
- cache_cache_inc_frequency *inc_entry,
- cache_cache_get_size *size_entry,
- cache_cache_get_key *key_entry,
- cache_cache_free *free_entry);
-
-/**
- * free up the cache
- * @param c the cache
- */
-void cache_free(cache_cache_t *c);
-/**
- * find a entry in the cache, incrementing the frequency if found
- * @param c the cache
- * @param key the key
- */
-void* cache_find(cache_cache_t* c, const char *key);
-/**
- * insert a entry into the cache
- * @param c the cache
- * @param entry the entry
- */
-void cache_update(cache_cache_t* c, void *entry);
-/**
- * insert a entry into the cache
- * @param c the cache
- * @param entry the entry
- */
-void cache_insert(cache_cache_t* c, void *entry);
-/**
- * pop the lowest priority item off
- * @param c the cache
- * @returns the entry or NULL
- */
-void* cache_pop(cache_cache_t* c);
-/**
- * remove an item from the cache
- * @param c the cache
- * @param entry the actual entry (from a find)
- */
-apr_status_t cache_remove(cache_cache_t* c, void *entry);
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !CACHE_CACHE_H */
-/** @} */
diff --git a/modules/cache/cache_common.h b/modules/cache/cache_common.h
new file mode 100644
index 00000000..cedce076
--- /dev/null
+++ b/modules/cache/cache_common.h
@@ -0,0 +1,55 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file cache_common.h
+ * @brief Common Cache structs
+ *
+ * @defgroup Cache_cache Cache Functions
+ * @ingroup MOD_CACHE
+ * @{
+ */
+
+#ifndef CACHE_COMMON_H
+#define CACHE_COMMON_H
+
+/* a cache control header breakdown */
+typedef struct cache_control {
+ unsigned int parsed:1;
+ unsigned int cache_control:1;
+ unsigned int pragma:1;
+ unsigned int no_cache:1;
+ unsigned int no_cache_header:1; /* no cache by header match */
+ unsigned int no_store:1;
+ unsigned int max_age:1;
+ unsigned int max_stale:1;
+ unsigned int min_fresh:1;
+ unsigned int no_transform:1;
+ unsigned int only_if_cached:1;
+ unsigned int public:1;
+ unsigned int private:1;
+ unsigned int private_header:1; /* private by header match */
+ unsigned int must_revalidate:1;
+ unsigned int proxy_revalidate:1;
+ unsigned int s_maxage:1;
+ apr_int64_t max_age_value; /* if positive, then set */
+ apr_int64_t max_stale_value; /* if positive, then set */
+ apr_int64_t min_fresh_value; /* if positive, then set */
+ apr_int64_t s_maxage_value; /* if positive, then set */
+} cache_control_t;
+
+#endif /* CACHE_COMMON_H */
+/** @} */
diff --git a/modules/cache/cache_disk_common.h b/modules/cache/cache_disk_common.h
new file mode 100644
index 00000000..ace569e8
--- /dev/null
+++ b/modules/cache/cache_disk_common.h
@@ -0,0 +1,68 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file cache_disk_common.h
+ * @brief Common Disk Cache vars/structs
+ *
+ * @defgroup Cache_cache Cache Functions
+ * @ingroup MOD_DISK_CACHE
+ * @{
+ */
+
+#ifndef CACHE_DIST_COMMON_H
+#define CACHE_DIST_COMMON_H
+
+#define VARY_FORMAT_VERSION 5
+#define DISK_FORMAT_VERSION 6
+
+#define CACHE_HEADER_SUFFIX ".header"
+#define CACHE_DATA_SUFFIX ".data"
+#define CACHE_VDIR_SUFFIX ".vary"
+
+#define AP_TEMPFILE_PREFIX "/"
+#define AP_TEMPFILE_BASE "aptmp"
+#define AP_TEMPFILE_SUFFIX "XXXXXX"
+#define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
+#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
+#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
+
+typedef struct {
+ /* Indicates the format of the header struct stored on-disk. */
+ apr_uint32_t format;
+ /* The HTTP status code returned for this response. */
+ int status;
+ /* The size of the entity name that follows. */
+ apr_size_t name_len;
+ /* The number of times we've cached this entity. */
+ apr_size_t entity_version;
+ /* Miscellaneous time values. */
+ apr_time_t date;
+ apr_time_t expire;
+ apr_time_t request_time;
+ apr_time_t response_time;
+ /* The ident of the body file, so we can test the body matches the header */
+ apr_ino_t inode;
+ apr_dev_t device;
+ /* Does this cached request have a body? */
+ unsigned int has_body:1;
+ unsigned int header_only:1;
+ /* The parsed cache control header */
+ cache_control_t control;
+} disk_cache_info_t;
+
+#endif /* CACHE_DIST_COMMON_H */
+/** @} */
diff --git a/modules/cache/cache_hash.c b/modules/cache/cache_hash.c
deleted file mode 100644
index 202cf9f7..00000000
--- a/modules/cache/cache_hash.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "apr_general.h"
-
-#include "mod_cache.h"
-#include "cache_hash.h"
-
-#if APR_HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#if APR_HAVE_STRING_H
-#include <string.h>
-#endif
-
-
-/*
- * The internal form of a hash table.
- *
- * The table is an array indexed by the hash of the key; collisions
- * are resolved by hanging a linked list of hash entries off each
- * element of the array. Although this is a really simple design it
- * isn't too bad given that pools have a low allocation overhead.
- */
-
-typedef struct cache_hash_entry_t cache_hash_entry_t;
-
-struct cache_hash_entry_t {
- cache_hash_entry_t *next;
- unsigned int hash;
- const void *key;
- apr_ssize_t klen;
- const void *val;
-};
-
-/*
- * Data structure for iterating through a hash table.
- *
- * We keep a pointer to the next hash entry here to allow the current
- * hash entry to be freed or otherwise mangled between calls to
- * cache_hash_next().
- */
-struct cache_hash_index_t {
- cache_hash_t *ht;
- cache_hash_entry_t *this, *next;
- int index;
-};
-
-/*
- * The size of the array is always a power of two. We use the maximum
- * index rather than the size so that we can use bitwise-AND for
- * modular arithmetic.
- * The count of hash entries may be greater depending on the chosen
- * collision rate.
- */
-struct cache_hash_t {
- cache_hash_entry_t **array;
- cache_hash_index_t iterator; /* For cache_hash_first(NULL, ...) */
- int count, max;
-};
-
-/*
- * Hash creation functions.
- */
-static cache_hash_entry_t **alloc_array(cache_hash_t *ht, int max)
-{
- return calloc(1, sizeof(*ht->array) * (max + 1));
-}
-
-cache_hash_t* cache_hash_make(apr_size_t size)
-{
- cache_hash_t *ht;
- ht = malloc(sizeof(cache_hash_t));
- if (!ht) {
- return NULL;
- }
- ht->count = 0;
- ht->max = size;
- ht->array = alloc_array(ht, ht->max);
- if (!ht->array) {
- free(ht);
- return NULL;
- }
- return ht;
-}
-
-void cache_hash_free(cache_hash_t *ht)
-{
- if (ht) {
- if (ht->array) {
- free (ht->array);
- }
- free (ht);
- }
-}
-/*
- * Hash iteration functions.
- */
-
-cache_hash_index_t* cache_hash_next(cache_hash_index_t *hi)
-{
- hi->this = hi->next;
- while (!hi->this) {
- if (hi->index > hi->ht->max)
- return NULL;
- hi->this = hi->ht->array[hi->index++];
- }
- hi->next = hi->this->next;
- return hi;
-}
-
-cache_hash_index_t* cache_hash_first(cache_hash_t *ht)
-{
- cache_hash_index_t *hi;
-
- hi = &ht->iterator;
- hi->ht = ht;
- hi->index = 0;
- hi->this = NULL;
- hi->next = NULL;
- return cache_hash_next(hi);
-}
-
-void cache_hash_this(cache_hash_index_t *hi,
- const void **key,
- apr_ssize_t *klen,
- void **val)
-{
- if (key) *key = hi->this->key;
- if (klen) *klen = hi->this->klen;
- if (val) *val = (void *)hi->this->val;
-}
-
-
-/*
- * This is where we keep the details of the hash function and control
- * the maximum collision rate.
- *
- * If val is non-NULL it creates and initializes a new hash entry if
- * there isn't already one there; it returns an updatable pointer so
- * that hash entries can be removed.
- */
-
-static cache_hash_entry_t **find_entry(cache_hash_t *ht,
- const void *key,
- apr_ssize_t klen,
- const void *val)
-{
- cache_hash_entry_t **hep, *he;
- const unsigned char *p;
- unsigned int hash;
- apr_ssize_t i;
-
- /*
- * This is the popular `times 33' hash algorithm which is used by
- * perl and also appears in Berkeley DB. This is one of the best
- * known hash functions for strings because it is both computed
- * very fast and distributes very well.
- *
- * The originator may be Dan Bernstein but the code in Berkeley DB
- * cites Chris Torek as the source. The best citation I have found
- * is "Chris Torek, Hash function for text in C, Usenet message
- * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich
- * Salz's USENIX 1992 paper about INN which can be found at
- * <http://citeseer.nj.nec.com/salz92internetnews.html>.
- *
- * The magic of number 33, i.e. why it works better than many other
- * constants, prime or not, has never been adequately explained by
- * anyone. So I try an explanation: if one experimentally tests all
- * multipliers between 1 and 256 (as I did while writing a low-level
- * data structure library some time ago) one detects that even
- * numbers are not useable at all. The remaining 128 odd numbers
- * (except for the number 1) work more or less all equally well.
- * They all distribute in an acceptable way and this way fill a hash
- * table with an average percent of approx. 86%.
- *
- * If one compares the chi^2 values of the variants (see
- * Bob Jenkins ``Hashing Frequently Asked Questions'' at
- * http://burtleburtle.net/bob/hash/hashfaq.html for a description
- * of chi^2), the number 33 not even has the best value. But the
- * number 33 and a few other equally good numbers like 17, 31, 63,
- * 127 and 129 have nevertheless a great advantage to the remaining
- * numbers in the large set of possible multipliers: their multiply
- * operation can be replaced by a faster operation based on just one
- * shift plus either a single addition or subtraction operation. And
- * because a hash function has to both distribute good _and_ has to
- * be very fast to compute, those few numbers should be preferred.
- *
- * -- Ralf S. Engelschall <rse@engelschall.com>
- */
- hash = 0;
- if (klen == CACHE_HASH_KEY_STRING) {
- for (p = key; *p; p++) {
- hash = hash * 33 + *p;
- }
- klen = p - (const unsigned char *)key;
- }
- else {
- for (p = key, i = klen; i; i--, p++) {
- hash = hash * 33 + *p;
- }
- }
-
- /* scan linked list */
- for (hep = &ht->array[hash % ht->max], he = *hep;
- he;
- hep = &he->next, he = *hep) {
- if (he->hash == hash &&
- he->klen == klen &&
- memcmp(he->key, key, klen) == 0)
- break;
- }
- if (he || !val)
- return hep;
- /* add a new entry for non-NULL values */
- he = malloc(sizeof(*he));
- if (!he) {
- return NULL;
- }
- he->next = NULL;
- he->hash = hash;
- he->key = key;
- he->klen = klen;
- he->val = val;
- *hep = he;
- ht->count++;
- return hep;
-}
-
-void* cache_hash_get(cache_hash_t *ht,
- const void *key,
- apr_ssize_t klen)
-{
- cache_hash_entry_t *he;
- he = *find_entry(ht, key, klen, NULL);
- if (he)
- return (void *)he->val;
- else
- return NULL;
-}
-
-void* cache_hash_set(cache_hash_t *ht,
- const void *key,
- apr_ssize_t klen,
- const void *val)
-{
- cache_hash_entry_t **hep, *tmp;
- const void *tval;
- hep = find_entry(ht, key, klen, val);
- /* If hep == NULL, then the malloc() in find_entry failed */
- if (hep && *hep) {
- if (!val) {
- /* delete entry */
- tval = (*hep)->val;
- tmp = *hep;
- *hep = (*hep)->next;
- free(tmp);
- --ht->count;
- }
- else {
- /* replace entry */
- tval = (*hep)->val;
- (*hep)->val = val;
- }
- /* Return the object just removed from the cache to let the
- * caller clean it up. Cast the constness away upon return.
- */
- return (void *) tval;
- }
- /* else key not present and val==NULL */
- return NULL;
-}
-
-int cache_hash_count(cache_hash_t *ht)
-{
- return ht->count;
-}
diff --git a/modules/cache/cache_hash.h b/modules/cache/cache_hash.h
deleted file mode 100644
index 13a5eb4c..00000000
--- a/modules/cache/cache_hash.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file cache_hash.h
- * @brief Cache Hash Tables
- *
- * @defgroup Cache_Hash Hash Tables
- * @ingroup MOD_CACHE
- * @{
- */
-
-#ifndef CACHE_HASH_H
-#define CACHE_HASH_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "mod_cache.h"
-
-/**
- * When passing a key to cache_hash_set or cache_hash_get, this value can be
- * passed to indicate a string-valued key, and have cache_hash compute the
- * length automatically.
- *
- * @remark cache_hash will use strlen(key) for the length. The null-terminator
- * is not included in the hash value (why throw a constant in?).
- * Since the hash table merely references the provided key (rather
- * than copying it), cache_hash_this() will return the null-term'd key.
- */
-#define CACHE_HASH_KEY_STRING (-1)
-
-/**
- * Abstract type for hash tables.
- */
-typedef struct cache_hash_t cache_hash_t;
-
-/**
- * Abstract type for scanning hash tables.
- */
-typedef struct cache_hash_index_t cache_hash_index_t;
-
-/**
- * Create a hash table.
- * @param size
- * @return The hash table just created
- */
-cache_hash_t* cache_hash_make(apr_size_t size);
-
-/**
- * Create a hash table.
- * @param *ht Pointer to the hash table to be freed.
- * @return void
- * @remark The caller should ensure that all objects have been removed
- * from the cache prior to calling cache_hash_free(). Objects
- * not removed from the cache prior to calling cache_hash_free()
- * will be unaccessable.
- */
-void cache_hash_free(cache_hash_t *ht);
-
-
-/**
- * Associate a value with a key in a hash table.
- * @param ht The hash table
- * @param key Pointer to the key
- * @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
- * @param val Value to associate with the key
- * @remark If the value is NULL the hash entry is deleted.
- * @return The value of the deleted cache entry (so the caller can clean it up).
- */
-void* cache_hash_set(cache_hash_t *ht, const void *key,
- apr_ssize_t klen, const void *val);
-
-/**
- * Look up the value associated with a key in a hash table.
- * @param ht The hash table
- * @param key Pointer to the key
- * @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
- * @return Returns NULL if the key is not present.
- */
-void* cache_hash_get(cache_hash_t *ht, const void *key,
- apr_ssize_t klen);
-
-/**
- * Start iterating over the entries in a hash table.
- * @param ht The hash table
- * @example
- */
-/**
- * <PRE>
- *
- * int sum_values(cache_hash_t *ht)
- * {
- * cache_hash_index_t *hi;
- * void *val;
- * int sum = 0;
- * for (hi = cache_hash_first(ht); hi; hi = cache_hash_next(hi)) {
- * cache_hash_this(hi, NULL, NULL, &val);
- * sum += *(int *)val;
- * }
- * return sum;
- * }
- *
- * There is no restriction on adding or deleting hash entries during an
- * iteration (although the results may be unpredictable unless all you do
- * is delete the current entry) and multiple iterations can be in
- * progress at the same time.
- * </PRE>
- */
-cache_hash_index_t* cache_hash_first(cache_hash_t *ht);
-
-/**
- * Continue iterating over the entries in a hash table.
- * @param hi The iteration state
- * @return a pointer to the updated iteration state. NULL if there are no more
- * entries.
- */
-cache_hash_index_t* cache_hash_next(cache_hash_index_t *hi);
-
-/**
- * Get the current entry's details from the iteration state.
- * @param hi The iteration state
- * @param key Return pointer for the pointer to the key.
- * @param klen Return pointer for the key length.
- * @param val Return pointer for the associated value.
- * @remark The return pointers should point to a variable that will be set to the
- * corresponding data, or they may be NULL if the data isn't interesting.
- */
-void cache_hash_this(cache_hash_index_t *hi, const void **key,
- apr_ssize_t *klen, void **val);
-
-/**
- * Get the number of key/value pairs in the hash table.
- * @param ht The hash table
- * @return The number of key/value pairs in the hash table.
- */
-int cache_hash_count(cache_hash_t *ht);
-
-
-/** @} */
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !CACHE_HASH_H */
diff --git a/modules/cache/cache_pqueue.c b/modules/cache/cache_pqueue.c
deleted file mode 100644
index 580b47e7..00000000
--- a/modules/cache/cache_pqueue.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "apr_general.h"
-
-#if APR_HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#if APR_HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#if APR_HAVE_STRING_H
-#include <string.h>
-#endif
-
-#include "cache_pqueue.h"
-#define left(i) (2*(i))
-#define right(i) ((2*(i))+1)
-#define parent(i) ((i)/2)
-/*
- * Priority queue structure
- */
-struct cache_pqueue_t
-{
- apr_ssize_t size;
- apr_ssize_t avail;
- apr_ssize_t step;
- cache_pqueue_get_priority pri;
- cache_pqueue_getpos get;
- cache_pqueue_setpos set;
- void **d;
-};
-
-cache_pqueue_t *cache_pq_init(apr_ssize_t n,
- cache_pqueue_get_priority pri,
- cache_pqueue_getpos get,
- cache_pqueue_setpos set)
-{
- cache_pqueue_t *q;
-
- if (!(q = malloc(sizeof(cache_pqueue_t)))) {
- return NULL;
- }
-
- /* Need to allocate n+1 elements since element 0 isn't used. */
- if (!(q->d = malloc(sizeof(void*) * (n+1)))) {
- free(q);
- return NULL;
- }
- q->avail = q->step = (n+1); /* see comment above about n+1 */
- q->pri = pri;
- q->size = 1;
- q->get = get;
- q->set = set;
- return q;
-}
-/*
- * cleanup
- */
-void cache_pq_free(cache_pqueue_t *q)
-{
- free(q->d);
- free(q);
-}
-/*
- * pqsize: size of the queue.
- */
-apr_ssize_t cache_pq_size(cache_pqueue_t *q)
-{
- /* queue element 0 exists but doesn't count since it isn't used. */
- return (q->size - 1);
-}
-
-static void cache_pq_bubble_up(cache_pqueue_t *q, apr_ssize_t i)
-{
- apr_ssize_t parent_node;
- void *moving_node = q->d[i];
- long moving_pri = q->pri(moving_node);
-
- for (parent_node = parent(i);
- ((i > 1) && (q->pri(q->d[parent_node]) < moving_pri));
- i = parent_node, parent_node = parent(i))
- {
- q->d[i] = q->d[parent_node];
- q->set(q->d[i], i);
- }
-
- q->d[i] = moving_node;
- q->set(moving_node, i);
-}
-
-static apr_ssize_t maxchild(cache_pqueue_t *q, apr_ssize_t i)
-{
- apr_ssize_t child_node = left(i);
-
- if (child_node >= q->size)
- return 0;
-
- if ((child_node+1 < q->size) &&
- (q->pri(q->d[child_node+1]) > q->pri(q->d[child_node])))
- {
- child_node++; /* use right child instead of left */
- }
-
- return child_node;
-}
-
-static void cache_pq_percolate_down(cache_pqueue_t *q, apr_ssize_t i)
-{
- apr_ssize_t child_node;
- void *moving_node = q->d[i];
- long moving_pri = q->pri(moving_node);
-
- while ((child_node = maxchild(q, i)) &&
- (moving_pri < q->pri(q->d[child_node])))
- {
- q->d[i] = q->d[child_node];
- q->set(q->d[i], i);
- i = child_node;
- }
-
- q->d[i] = moving_node;
- q->set(moving_node, i);
-}
-
-apr_status_t cache_pq_insert(cache_pqueue_t *q, void *d)
-{
- void *tmp;
- apr_ssize_t i;
- apr_ssize_t newsize;
-
- if (!q) return APR_EGENERAL;
-
- /* allocate more memory if necessary */
- if (q->size >= q->avail) {
- newsize = q->size + q->step;
- if (!(tmp = realloc(q->d, sizeof(void*) * newsize))) {
- return APR_EGENERAL;
- };
- q->d = tmp;
- q->avail = newsize;
- }
-
- /* insert item */
- i = q->size++;
- q->d[i] = d;
- cache_pq_bubble_up(q, i);
- return APR_SUCCESS;
-}
-
-/*
- * move a existing entry to a new priority
- */
-void cache_pq_change_priority(cache_pqueue_t *q,
- long old_priority,
- long new_priority,
- void *d)
-{
- apr_ssize_t posn;
-
- posn = q->get(d);
- if (new_priority > old_priority)
- cache_pq_bubble_up(q, posn);
- else
- cache_pq_percolate_down(q, posn);
-}
-
-apr_status_t cache_pq_remove(cache_pqueue_t *q, void *d)
-{
- apr_ssize_t posn = q->get(d);
- q->d[posn] = q->d[--q->size];
- if (q->pri(q->d[posn]) > q->pri(d))
- cache_pq_bubble_up(q, posn);
- else
- cache_pq_percolate_down(q, posn);
-
- return APR_SUCCESS;
-}
-
-void *cache_pq_pop(cache_pqueue_t *q)
-{
- void *head;
-
- if (!q || q->size == 1)
- return NULL;
-
- head = q->d[1];
- q->d[1] = q->d[--q->size];
- cache_pq_percolate_down(q, 1);
-
- return head;
-}
-
-void *cache_pq_peek(cache_pqueue_t *q)
-{
- void *d;
- if (!q || q->size == 1)
- return NULL;
- d = q->d[1];
- return d;
-}
-
-static void cache_pq_set_null( void*d, apr_ssize_t val)
-{
- /* do nothing */
-}
-
-/*
- * this is a debug function.. so it's EASY not fast
- */
-void cache_pq_dump(cache_pqueue_t *q,
- FILE*out,
- cache_pqueue_print_entry print)
-{
- int i;
-
- fprintf(stdout,"posn\tleft\tright\tparent\tmaxchild\t...\n");
- for (i = 1; i < q->size ;i++) {
- fprintf(stdout,
- "%d\t%d\t%d\t%d\t%" APR_SSIZE_T_FMT "\t",
- i,
- left(i), right(i), parent(i),
- maxchild(q, i));
- print(out, q->d[i]);
- }
-}
-
-/*
- * this is a debug function.. so it's EASY not fast
- */
-void cache_pq_print(cache_pqueue_t *q,
- FILE*out,
- cache_pqueue_print_entry print)
-{
- cache_pqueue_t *dup;
- dup = cache_pq_init(q->size, q->pri, q->get, cache_pq_set_null);
- dup->size = q->size;
- dup->avail = q->avail;
- dup->step = q->step;
-
- memcpy(dup->d, q->d, q->size*sizeof(void*));
-
- while (cache_pq_size(dup) > 1) {
- void *e = NULL;
- e = cache_pq_pop(dup);
- if (e)
- print(out, e);
- else
- break;
- }
- cache_pq_free(dup);
-}
-
-static int cache_pq_subtree_is_valid(cache_pqueue_t *q, int pos)
-{
- if (left(pos) < q->size) {
- /* has a left child */
- if (q->pri(q->d[pos]) < q->pri(q->d[left(pos)]))
- return 0;
- if (!cache_pq_subtree_is_valid(q, left(pos)))
- return 0;
- }
- if (right(pos) < q->size) {
- /* has a right child */
- if (q->pri(q->d[pos]) < q->pri(q->d[right(pos)]))
- return 0;
- if (!cache_pq_subtree_is_valid(q, right(pos)))
- return 0;
- }
- return 1;
-}
-
-int cache_pq_is_valid(cache_pqueue_t *q)
-{
- return cache_pq_subtree_is_valid(q, 1);
-}
diff --git a/modules/cache/cache_pqueue.h b/modules/cache/cache_pqueue.h
deleted file mode 100644
index 13238c03..00000000
--- a/modules/cache/cache_pqueue.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file cache_pqueue.h
- * @brief Cache Priority Queue function declarations
- *
- * @defgroup MOD_CACHE_QUEUE Priority Queue
- * @ingroup MOD_CACHE
- * @{
- */
-
-#ifndef CACHE_PQUEUE_H
-#define CACHE_PQUEUE_H
-
-#include <apr.h>
-#include <apr_errno.h>
-
-#if APR_HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** the cache priority queue handle */
-typedef struct cache_pqueue_t cache_pqueue_t;
-
-/**
- * callback function to assign a priority for a element
- * @param a the element
- * @return the score (the lower the score the longer it is kept int the queue)
- */
-typedef long (*cache_pqueue_set_priority)(long queue_clock, void *a);
-typedef long (*cache_pqueue_get_priority)(void *a);
-
-/** callback function to get a position of a element */
-typedef apr_ssize_t (*cache_pqueue_getpos)(void *a);
-
-/**
- * callback function to set a position of a element
- * @param a the element
- * @param pos the position to set it to
- */
-typedef void (*cache_pqueue_setpos)(void *a, apr_ssize_t pos);
-
-/** debug callback function to print a entry */
-typedef void (*cache_pqueue_print_entry)(FILE *out, void *a);
-
-/**
- * initialize the queue
- *
- * @param n the initial estimate of the number of queue items for which memory
- * should be preallocated
- * @param pri the callback function to run to assign a score to a element
- * @param get the callback function to get the current element's position
- * @param set the callback function to set the current element's position
- *
- * @Return the handle or NULL for insufficent memory
- */
-cache_pqueue_t *cache_pq_init(apr_ssize_t n,
- cache_pqueue_get_priority pri,
- cache_pqueue_getpos get,
- cache_pqueue_setpos set);
-/**
- * free all memory used by the queue
- * @param q the queue
- */
-void cache_pq_free(cache_pqueue_t *q);
-/**
- * return the size of the queue.
- * @param q the queue
- */
-apr_ssize_t cache_pq_size(cache_pqueue_t *q);
-
-/**
- * insert an item into the queue.
- * @param q the queue
- * @param d the item
- * @return APR_SUCCESS on success
- */
-apr_status_t cache_pq_insert(cache_pqueue_t *q, void *d);
-
-/*
- * move a existing entry to a different priority
- * @param q the queue
- * @param old the old priority
- * @param d the entry
- */
-void cache_pq_change_priority(cache_pqueue_t *q,
- long old_priority,
- long new_priority,
- void *d);
-
-/**
- * pop the highest-ranking item from the queue.
- * @param p the queue
- * @param d where to copy the entry to
- * @return NULL on error, otherwise the entry
- */
-void *cache_pq_pop(cache_pqueue_t *q);
-
-/**
- * remove an item from the queue.
- * @param p the queue
- * @param d the entry
- * @return APR_SUCCESS on success
- */
-apr_status_t cache_pq_remove(cache_pqueue_t *q, void *d);
-
-/**
- * access highest-ranking item without removing it.
- * @param q the queue
- * @param d the entry
- * @return NULL on error, otherwise the entry
- */
-void *cache_pq_peek(cache_pqueue_t *q);
-
-/**
- * print the queue
- * @internal
- * DEBUG function only
- * @param q the queue
- * @param out the output handle
- * @param the callback function to print the entry
- */
-void cache_pq_print(cache_pqueue_t *q,
- FILE *out,
- cache_pqueue_print_entry print);
-
-/**
- * dump the queue and it's internal structure
- * @internal
- * debug function only
- * @param q the queue
- * @param out the output handle
- * @param the callback function to print the entry
- */
-void cache_pq_dump(cache_pqueue_t *q,
- FILE *out,
- cache_pqueue_print_entry print);
-
-/**
- * checks that the pq is in the right order, etc
- * @internal
- * debug function only
- * @param q the queue
- */
-int cache_pq_is_valid(cache_pqueue_t *q);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !CACHE_PQUEUE_H */
-/** @} */
diff --git a/modules/cache/cache_storage.c b/modules/cache/cache_storage.c
index c9d36271..9021ec1d 100644
--- a/modules/cache/cache_storage.c
+++ b/modules/cache/cache_storage.c
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
-
#include "mod_cache.h"
+#include "cache_storage.h"
+#include "cache_util.h"
+
+APLOG_USE_MODULE(cache);
+
extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
extern module AP_MODULE_DECLARE_DATA cache_module;
@@ -28,7 +31,7 @@ extern module AP_MODULE_DECLARE_DATA cache_module;
* delete all URL entities from the cache
*
*/
-int cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
+int cache_remove_url(cache_request_rec *cache, request_rec *r)
{
cache_provider_list *list;
cache_handle_t *h;
@@ -43,12 +46,12 @@ int cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
if (!h) {
return OK;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00691)
"cache: Removing url %s from the cache", h->cache_obj->key);
/* for each specified cache type, delete the URL */
while(list) {
- list->provider->remove_url(h, p);
+ list->provider->remove_url(h, r);
list = list->next;
}
return OK;
@@ -66,24 +69,32 @@ int cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
* decide whether or not it wants to cache this particular entity.
* If the size is unknown, a size of -1 should be set.
*/
-int cache_create_entity(request_rec *r, apr_off_t size)
+int cache_create_entity(cache_request_rec *cache, request_rec *r,
+ apr_off_t size, apr_bucket_brigade *in)
{
cache_provider_list *list;
cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t));
- char *key;
apr_status_t rv;
- cache_request_rec *cache = (cache_request_rec *)
- ap_get_module_config(r->request_config, &cache_module);
- rv = cache_generate_key(r, r->pool, &key);
- if (rv != APR_SUCCESS) {
- return rv;
+ if (!cache) {
+ /* This should never happen */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00692)
+ "cache: No cache request information available for key"
+ " generation");
+ return APR_EGENERAL;
+ }
+
+ if (!cache->key) {
+ rv = cache_generate_key(r, r->pool, &cache->key);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
}
list = cache->providers;
/* for each specified cache type, delete the URL */
while (list) {
- switch (rv = list->provider->create_entity(h, r, key, size)) {
+ switch (rv = list->provider->create_entity(h, r, cache->key, size, in)) {
case OK: {
cache->handle = h;
cache->provider = list->provider;
@@ -108,8 +119,12 @@ static int set_cookie_doo_doo(void *v, const char *key, const char *val)
return 1;
}
-CACHE_DECLARE(void) ap_cache_accept_headers(cache_handle_t *h, request_rec *r,
- int preserve_orig)
+/**
+ * Take headers from the cache, and overlap them over the existing response
+ * headers.
+ */
+void cache_accept_headers(cache_handle_t *h, request_rec *r,
+ int preserve_orig)
{
apr_table_t *cookie_table, *hdr_copy;
const char *v;
@@ -180,21 +195,28 @@ CACHE_DECLARE(void) ap_cache_accept_headers(cache_handle_t *h, request_rec *r,
* This function returns OK if successful, DECLINED if no
* cached entity fits the bill.
*/
-int cache_select(request_rec *r)
+int cache_select(cache_request_rec *cache, request_rec *r)
{
cache_provider_list *list;
apr_status_t rv;
cache_handle_t *h;
- char *key;
- cache_request_rec *cache = (cache_request_rec *)
- ap_get_module_config(r->request_config, &cache_module);
- rv = cache_generate_key(r, r->pool, &key);
- if (rv != APR_SUCCESS) {
- return rv;
+ if (!cache) {
+ /* This should never happen */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00693)
+ "cache: No cache request information available for key"
+ " generation");
+ return DECLINED;
}
- if (!ap_cache_check_allowed(r)) {
+ if (!cache->key) {
+ rv = cache_generate_key(r, r->pool, &cache->key);
+ if (rv != APR_SUCCESS) {
+ return DECLINED;
+ }
+ }
+
+ if (!ap_cache_check_allowed(cache, r)) {
return DECLINED;
}
@@ -204,14 +226,15 @@ int cache_select(request_rec *r)
list = cache->providers;
while (list) {
- switch ((rv = list->provider->open_entity(h, r, key))) {
+ switch ((rv = list->provider->open_entity(h, r, cache->key))) {
case OK: {
char *vary = NULL;
- int fresh;
+ int fresh, mismatch = 0;
if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
- /* TODO: Handle this error */
- return DECLINED;
+ /* try again with next cache type */
+ list = list->next;
+ continue;
}
/*
@@ -259,28 +282,44 @@ int cache_select(request_rec *r)
}
else {
/* headers do not match, so Vary failed */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server,
- "cache_select_url(): Vary header mismatch.");
- return DECLINED;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00694) "cache_select_url(): Vary header mismatch.");
+ mismatch = 1;
}
}
+ /* no vary match, try next provider */
+ if (mismatch) {
+ /* try again with next cache type */
+ list = list->next;
+ continue;
+ }
+
cache->provider = list->provider;
cache->provider_name = list->provider_name;
/* Is our cached response fresh enough? */
- fresh = ap_cache_check_freshness(h, r);
+ fresh = cache_check_freshness(h, cache, r);
if (!fresh) {
const char *etag, *lastmod;
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
- "Cached response for %s isn't fresh. Adding/replacing "
- "conditional request headers.", r->uri);
+ /* Cache-Control: only-if-cached and revalidation required, try
+ * the next provider
+ */
+ if (cache->control_in.only_if_cached) {
+ /* try again with next cache type */
+ list = list->next;
+ continue;
+ }
- /* Make response into a conditional */
+ /* set aside the stale entry for accessing later */
cache->stale_headers = apr_table_copy(r->pool,
- r->headers_in);
+ r->headers_in);
+ cache->stale_handle = h;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00695)
+ "Cached response for %s isn't fresh. Adding/replacing "
+ "conditional request headers.", r->uri);
/* We can only revalidate with our own conditionals: remove the
* conditions from the original request.
@@ -291,13 +330,6 @@ int cache_select(request_rec *r)
apr_table_unset(r->headers_in, "If-Range");
apr_table_unset(r->headers_in, "If-Unmodified-Since");
- /*
- * Do not do Range requests with our own conditionals: If
- * we get 304 the Range does not matter and otherwise the
- * entity changed and we want to have the complete entity
- */
- apr_table_unset(r->headers_in, "Range");
-
etag = apr_table_get(h->resp_hdrs, "ETag");
lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
@@ -312,30 +344,24 @@ int cache_select(request_rec *r)
if (lastmod) {
apr_table_set(r->headers_in, "If-Modified-Since",
- lastmod);
+ lastmod);
}
- cache->stale_handle = h;
- }
- else {
- int irv;
/*
- * The copy isn't fresh enough, but we cannot revalidate.
- * So it is the same case as if there had not been a cached
- * entry at all. Thus delete the entry from cache.
+ * Do not do Range requests with our own conditionals: If
+ * we get 304 the Range does not matter and otherwise the
+ * entity changed and we want to have the complete entity
*/
- irv = cache->provider->remove_url(h, r->pool);
- if (irv != OK) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, irv, r->server,
- "cache: attempt to remove url from cache unsuccessful.");
- }
+ apr_table_unset(r->headers_in, "Range");
+
}
+ /* ready to revalidate, pretend we were never here */
return DECLINED;
}
/* Okay, this response looks okay. Merge in our stuff and go. */
- ap_cache_accept_headers(h, r, 0);
+ cache_accept_headers(h, r, 0);
cache->handle = h;
return OK;
@@ -351,35 +377,31 @@ int cache_select(request_rec *r)
}
}
}
+
+ /* if Cache-Control: only-if-cached, and not cached, return 504 */
+ if (cache->control_in.only_if_cached) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00696)
+ "cache: 'only-if-cached' requested and no cached entity, "
+ "returning 504 Gateway Timeout for: %s", r->uri);
+ return HTTP_GATEWAY_TIME_OUT;
+ }
+
return DECLINED;
}
apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
- char**key)
+ const char **key)
{
cache_server_conf *conf;
- cache_request_rec *cache;
char *port_str, *hn, *lcs;
const char *hostname, *scheme;
int i;
char *path, *querystring;
- cache = (cache_request_rec *) ap_get_module_config(r->request_config,
- &cache_module);
- if (!cache) {
- /* This should never happen */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "cache: No cache request information available for key"
- " generation");
- *key = "";
- return APR_EGENERAL;
- }
- if (cache->key) {
+ if (*key) {
/*
* We have been here before during the processing of this request.
- * So return the key we already have.
*/
- *key = apr_pstrdup(p, cache->key);
return APR_SUCCESS;
}
@@ -409,10 +431,15 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* in the reverse proxy case.
*/
if (!r->proxyreq || (r->proxyreq == PROXYREQ_REVERSE)) {
- /* Use _default_ as the hostname if none present, as in mod_vhost */
- hostname = ap_get_server_name(r);
- if (!hostname) {
- hostname = "_default_";
+ if (conf->base_uri && conf->base_uri->hostname) {
+ hostname = conf->base_uri->hostname;
+ }
+ else {
+ /* Use _default_ as the hostname if none present, as in mod_vhost */
+ hostname = ap_get_server_name(r);
+ if (!hostname) {
+ hostname = "_default_";
+ }
}
}
else if(r->parsed_uri.hostname) {
@@ -444,7 +471,12 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
scheme = lcs;
}
else {
- scheme = ap_http_scheme(r);
+ if (conf->base_uri && conf->base_uri->scheme) {
+ scheme = conf->base_uri->scheme;
+ }
+ else {
+ scheme = ap_http_scheme(r);
+ }
}
/*
@@ -455,7 +487,7 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* scheme - if available. Otherwise use the port-number of the current
* server.
*/
- if(r->proxyreq && (r->proxyreq != PROXYREQ_REVERSE)) {
+ if (r->proxyreq && (r->proxyreq != PROXYREQ_REVERSE)) {
if (r->parsed_uri.port_str) {
port_str = apr_pcalloc(p, strlen(r->parsed_uri.port_str) + 2);
port_str[0] = ':';
@@ -476,8 +508,16 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
}
}
else {
- /* Use the server port */
- port_str = apr_psprintf(p, ":%u", ap_get_server_port(r));
+ if (conf->base_uri && conf->base_uri->port_str) {
+ port_str = conf->base_uri->port_str;
+ }
+ else if (conf->base_uri && conf->base_uri->hostname) {
+ port_str = "";
+ }
+ else {
+ /* Use the server port */
+ port_str = apr_psprintf(p, ":%u", ap_get_server_port(r));
+ }
}
/*
@@ -505,28 +545,60 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
&& (*(param + len + 1) == '=')
&& !strchr(param + len + 2, '/')) {
path = apr_pstrndup(p, path, param - path);
- break;
+ continue;
}
/*
* Check if the identifier is in the querystring and cut it out.
*/
- if (querystring
- && (param = strstr(querystring, *identifier))
- && (*(param + len) == '=')
- ) {
- char *amp;
-
- if (querystring != param) {
- querystring = apr_pstrndup(p, querystring,
- param - querystring);
+ if (querystring) {
+ /*
+ * First check if the identifier is at the beginning of the
+ * querystring and followed by a '='
+ */
+ if (!strncmp(querystring, *identifier, len)
+ && (*(querystring + len) == '=')) {
+ param = querystring;
}
else {
- querystring = "";
+ char *complete;
+
+ /*
+ * In order to avoid subkey matching (PR 48401) prepend
+ * identifier with a '&' and append a '='
+ */
+ complete = apr_pstrcat(p, "&", *identifier, "=", NULL);
+ param = strstr(querystring, complete);
+ /* If we found something we are sitting on the '&' */
+ if (param) {
+ param++;
+ }
}
- if ((amp = strchr(param + len + 1, '&'))) {
- querystring = apr_pstrcat(p, querystring, amp + 1, NULL);
+ if (param) {
+ char *amp;
+
+ if (querystring != param) {
+ querystring = apr_pstrndup(p, querystring,
+ param - querystring);
+ }
+ else {
+ querystring = "";
+ }
+
+ if ((amp = strchr(param + len + 1, '&'))) {
+ querystring = apr_pstrcat(p, querystring, amp + 1, NULL);
+ }
+ else {
+ /*
+ * If querystring is not "", then we have the case
+ * that the identifier parameter we removed was the
+ * last one in the original querystring. Hence we have
+ * a trailing '&' which needs to be removed.
+ */
+ if (*querystring) {
+ querystring[strlen(querystring) - 1] = '\0';
+ }
+ }
}
- break;
}
}
}
@@ -549,10 +621,9 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* resource in the cache under a key where it is never found by the quick
* handler during following requests.
*/
- cache->key = apr_pstrdup(r->pool, *key);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "cache: Key for entity %s?%s is %s", r->uri,
- r->parsed_uri.query, *key);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00698)
+ "cache: Key for entity %s?%s is %s", r->uri,
+ r->parsed_uri.query, *key);
return APR_SUCCESS;
}
diff --git a/modules/cache/cache_storage.h b/modules/cache/cache_storage.h
new file mode 100644
index 00000000..2b67970e
--- /dev/null
+++ b/modules/cache/cache_storage.h
@@ -0,0 +1,61 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file cache_storage.h
+ * @brief Cache Storage Functions
+ *
+ * @defgroup Cache_storage Cache Storage Functions
+ * @ingroup MOD_CACHE
+ * @{
+ */
+
+#ifndef CACHE_STORAGE_H
+#define CACHE_STORAGE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mod_cache.h"
+#include "cache_util.h"
+
+/**
+ * cache_storage.c
+ */
+int cache_remove_url(cache_request_rec *cache, request_rec *r);
+int cache_create_entity(cache_request_rec *cache, request_rec *r,
+ apr_off_t size, apr_bucket_brigade *in);
+int cache_select(cache_request_rec *cache, request_rec *r);
+apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
+ const char **key);
+
+/**
+ * Merge in cached headers into the response
+ * @param h cache_handle_t
+ * @param r request_rec
+ * @param preserve_orig If 1, the values in r->headers_out are preserved.
+ * Otherwise, they are overwritten by the cached value.
+ */
+void cache_accept_headers(cache_handle_t *h, request_rec *r,
+ int preserve_orig);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !CACHE_STORAGE_H */
+/** @} */
diff --git a/modules/cache/cache_util.c b/modules/cache/cache_util.c
index 14c1165e..4b19d1ed 100644
--- a/modules/cache/cache_util.c
+++ b/modules/cache/cache_util.c
@@ -14,64 +14,105 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
-
#include "mod_cache.h"
+#include "cache_util.h"
#include <ap_provider.h>
+APLOG_USE_MODULE(cache);
+
/* -------------------------------------------------------------- */
extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
extern module AP_MODULE_DECLARE_DATA cache_module;
+#define CACHE_SEPARATOR ", "
+
/* Determine if "url" matches the hostname, scheme and port and path
* in "filter". All but the path comparisons are case-insensitive.
*/
-static int uri_meets_conditions(apr_uri_t filter, int pathlen, apr_uri_t url)
+static int uri_meets_conditions(const apr_uri_t *filter, const int pathlen,
+ const apr_uri_t *url)
{
- /* Compare the hostnames */
- if(filter.hostname) {
- if (!url.hostname) {
- return 0;
- }
- else if (strcasecmp(filter.hostname, url.hostname)) {
- return 0;
- }
- }
- /* Compare the schemes */
- if(filter.scheme) {
- if (!url.scheme) {
- return 0;
- }
- else if (strcasecmp(filter.scheme, url.scheme)) {
+ /* Scheme, hostname port and local part. The filter URI and the
+ * URI we test may have the following shapes:
+ * /<path>
+ * <scheme>[:://<hostname>[:<port>][/<path>]]
+ * That is, if there is no scheme then there must be only the path,
+ * and we check only the path; if there is a scheme, we check the
+ * scheme for equality, and then if present we match the hostname,
+ * and then if present match the port, and finally the path if any.
+ *
+ * Note that this means that "/<path>" only matches local paths,
+ * and to match proxied paths one *must* specify the scheme.
+ */
+
+ /* Is the filter is just for a local path or a proxy URI? */
+ if (!filter->scheme) {
+ if (url->scheme || url->hostname) {
return 0;
}
}
-
- /* Compare the ports */
- if(filter.port_str) {
- if (url.port_str && filter.port != url.port) {
+ else {
+ /* The URI scheme must be present and identical except for case. */
+ if (!url->scheme || strcasecmp(filter->scheme, url->scheme)) {
return 0;
}
- /* NOTE: ap_port_of_scheme will return 0 if given NULL input */
- else if (filter.port != apr_uri_port_of_scheme(url.scheme)) {
- return 0;
+
+ /* If the filter hostname is null or empty it matches any hostname,
+ * if it begins with a "*" it matches the _end_ of the URI hostname
+ * excluding the "*", if it begins with a "." it matches the _end_
+ * of the URI * hostname including the ".", otherwise it must match
+ * the URI hostname exactly. */
+
+ if (filter->hostname && filter->hostname[0]) {
+ if (filter->hostname[0] == '.') {
+ const size_t fhostlen = strlen(filter->hostname);
+ const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
+
+ if (fhostlen > uhostlen || strcasecmp(filter->hostname,
+ url->hostname + uhostlen - fhostlen)) {
+ return 0;
+ }
+ }
+ else if (filter->hostname[0] == '*') {
+ const size_t fhostlen = strlen(filter->hostname + 1);
+ const size_t uhostlen = url->hostname ? strlen(url->hostname) : 0;
+
+ if (fhostlen > uhostlen || strcasecmp(filter->hostname + 1,
+ url->hostname + uhostlen - fhostlen)) {
+ return 0;
+ }
+ }
+ else if (!url->hostname || strcasecmp(filter->hostname, url->hostname)) {
+ return 0;
+ }
}
- }
- else if(url.port_str && filter.scheme) {
- if (apr_uri_port_of_scheme(filter.scheme) == url.port) {
- return 0;
+
+ /* If the filter port is empty it matches any URL port.
+ * If the filter or URL port are missing, or the URL port is
+ * empty, they default to the port for their scheme. */
+
+ if (!(filter->port_str && !filter->port_str[0])) {
+ /* NOTE: ap_port_of_scheme will return 0 if given NULL input */
+ const unsigned fport = filter->port_str ? filter->port
+ : apr_uri_port_of_scheme(filter->scheme);
+ const unsigned uport = (url->port_str && url->port_str[0])
+ ? url->port : apr_uri_port_of_scheme(url->scheme);
+
+ if (fport != uport) {
+ return 0;
+ }
}
}
/* For HTTP caching purposes, an empty (NULL) path is equivalent to
* a single "/" path. RFCs 3986/2396
*/
- if (!url.path) {
- if (*filter.path == '/' && pathlen == 1) {
+ if (!url->path) {
+ if (*filter->path == '/' && pathlen == 1) {
return 1;
}
else {
@@ -82,63 +123,86 @@ static int uri_meets_conditions(apr_uri_t filter, int pathlen, apr_uri_t url)
/* Url has met all of the filter conditions so far, determine
* if the paths match.
*/
- return !strncmp(filter.path, url.path, pathlen);
+ return !strncmp(filter->path, url->path, pathlen);
}
-CACHE_DECLARE(cache_provider_list *)ap_cache_get_providers(request_rec *r,
- cache_server_conf *conf,
- apr_uri_t uri)
+static cache_provider_list *get_provider(request_rec *r, struct cache_enable *ent,
+ cache_provider_list *providers)
{
- cache_provider_list *providers = NULL;
- int i;
-
- /* loop through all the cacheenable entries */
- for (i = 0; i < conf->cacheenable->nelts; i++) {
- struct cache_enable *ent =
- (struct cache_enable *)conf->cacheenable->elts;
- if (uri_meets_conditions(ent[i].url, ent[i].pathlen, uri)) {
- /* Fetch from global config and add to the list. */
- cache_provider *provider;
- provider = ap_lookup_provider(CACHE_PROVIDER_GROUP, ent[i].type,
- "0");
- if (!provider) {
- /* Log an error! */
- }
- else {
- cache_provider_list *newp;
- newp = apr_pcalloc(r->pool, sizeof(cache_provider_list));
- newp->provider_name = ent[i].type;
- newp->provider = provider;
+ /* Fetch from global config and add to the list. */
+ cache_provider *provider;
+ provider = ap_lookup_provider(CACHE_PROVIDER_GROUP, ent->type,
+ "0");
+ if (!provider) {
+ /* Log an error! */
+ }
+ else {
+ cache_provider_list *newp;
+ newp = apr_pcalloc(r->pool, sizeof(cache_provider_list));
+ newp->provider_name = ent->type;
+ newp->provider = provider;
- if (!providers) {
- providers = newp;
- }
- else {
- cache_provider_list *last = providers;
+ if (!providers) {
+ providers = newp;
+ }
+ else {
+ cache_provider_list *last = providers;
- while (last->next) {
- last = last->next;
- }
- last->next = newp;
+ while (last->next) {
+ if (last->provider == provider) {
+ return providers;
}
+ last = last->next;
+ }
+ if (last->provider == provider) {
+ return providers;
}
+ last->next = newp;
}
}
- /* then loop through all the cachedisable entries
- * Looking for urls that contain the full cachedisable url and possibly
- * more.
- * This means we are disabling cachedisable url and below...
- */
+ return providers;
+}
+
+cache_provider_list *cache_get_providers(request_rec *r,
+ cache_server_conf *conf,
+ apr_uri_t uri)
+{
+ cache_dir_conf *dconf = dconf = ap_get_module_config(r->per_dir_config, &cache_module);
+ cache_provider_list *providers = NULL;
+ int i;
+
+ /* per directory cache disable */
+ if (dconf->disable) {
+ return NULL;
+ }
+
+ /* global cache disable */
for (i = 0; i < conf->cachedisable->nelts; i++) {
struct cache_disable *ent =
(struct cache_disable *)conf->cachedisable->elts;
- if (uri_meets_conditions(ent[i].url, ent[i].pathlen, uri)) {
+ if (uri_meets_conditions(&ent[i].url, ent[i].pathlen, &uri)) {
/* Stop searching now. */
return NULL;
}
}
+ /* loop through all the per directory cacheenable entries */
+ for (i = 0; i < dconf->cacheenable->nelts; i++) {
+ struct cache_enable *ent =
+ (struct cache_enable *)dconf->cacheenable->elts;
+ providers = get_provider(r, &ent[i], providers);
+ }
+
+ /* loop through all the global cacheenable entries */
+ for (i = 0; i < conf->cacheenable->nelts; i++) {
+ struct cache_enable *ent =
+ (struct cache_enable *)conf->cacheenable->elts;
+ if (uri_meets_conditions(&ent[i].url, ent[i].pathlen, &uri)) {
+ providers = get_provider(r, &ent[i], providers);
+ }
+ }
+
return providers;
}
@@ -163,6 +227,10 @@ CACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info,
resident_time = now - info->response_time;
current_age = corrected_initial_age + resident_time;
+ if (current_age < 0) {
+ current_age = 0;
+ }
+
return apr_time_sec(current_age);
}
@@ -187,8 +255,9 @@ CACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info,
* no point is it possible for this lock to permanently deny access to
* the backend.
*/
-CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
- request_rec *r, char *key) {
+apr_status_t cache_try_lock(cache_server_conf *conf, cache_request_rec *cache,
+ request_rec *r)
+{
apr_status_t status;
const char *lockname;
const char *path;
@@ -212,12 +281,12 @@ CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
}
/* create the key if it doesn't exist */
- if (!key) {
- cache_generate_key(r, r->pool, &key);
+ if (!cache->key) {
+ cache_generate_key(r, r->pool, &cache->key);
}
/* create a hashed filename from the key, and save it for later */
- lockname = ap_cache_generate_name(r->pool, 0, 0, key);
+ lockname = ap_cache_generate_name(r->pool, 0, 0, cache->key);
/* lock files represent discrete just-went-stale URLs "in flight", so
* we support a simple two level directory structure, more is overkill.
@@ -232,9 +301,9 @@ CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
path = apr_pstrcat(r->pool, conf->lockpath, dir, NULL);
if (APR_SUCCESS != (status = apr_dir_make_recursive(path,
APR_UREAD|APR_UWRITE|APR_UEXECUTE, r->pool))) {
- ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
- "Could not create a cache lock directory: %s",
- path);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00778)
+ "Could not create a cache lock directory: %s",
+ path);
return status;
}
lockname = apr_pstrcat(r->pool, path, "/", lockname, NULL);
@@ -244,16 +313,16 @@ CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
status = apr_stat(&finfo, lockname,
APR_FINFO_MTIME | APR_FINFO_NLINK, r->pool);
if (!(APR_STATUS_IS_ENOENT(status)) && APR_SUCCESS != status) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_EEXIST, r->server,
- "Could not stat a cache lock file: %s",
- lockname);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EEXIST, r, APLOGNO(00779)
+ "Could not stat a cache lock file: %s",
+ lockname);
return status;
}
if ((status == APR_SUCCESS) && (((now - finfo.mtime) > conf->lockmaxage)
|| (now < finfo.mtime))) {
- ap_log_error(APLOG_MARK, APLOG_INFO, status, r->server,
- "Cache lock file for '%s' too old, removing: %s",
- r->uri, lockname);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, status, r, APLOGNO(00780)
+ "Cache lock file for '%s' too old, removing: %s",
+ r->uri, lockname);
apr_file_remove(lockname, r->pool);
}
@@ -279,8 +348,9 @@ CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
* If an optional bucket brigade is passed, the lock will only be
* removed if the bucket brigade contains an EOS bucket.
*/
-CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
- request_rec *r, char *key, apr_bucket_brigade *bb) {
+apr_status_t cache_remove_lock(cache_server_conf *conf,
+ cache_request_rec *cache, request_rec *r, apr_bucket_brigade *bb)
+{
void *dummy;
const char *lockname;
@@ -317,12 +387,12 @@ CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
char dir[5];
/* create the key if it doesn't exist */
- if (!key) {
- cache_generate_key(r, r->pool, &key);
+ if (!cache->key) {
+ cache_generate_key(r, r->pool, &cache->key);
}
/* create a hashed filename from the key, and save it for later */
- lockname = ap_cache_generate_name(r->pool, 0, 0, key);
+ lockname = ap_cache_generate_name(r->pool, 0, 0, cache->key);
/* lock files represent discrete just-went-stale URLs "in flight", so
* we support a simple two level directory structure, more is overkill.
@@ -338,7 +408,7 @@ CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
return apr_file_remove(lockname, r->pool);
}
-CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) {
+CACHE_DECLARE(int) ap_cache_check_allowed(cache_request_rec *cache, request_rec *r) {
const char *cc_req;
const char *pragma;
cache_server_conf *conf =
@@ -364,31 +434,32 @@ CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) {
cc_req = apr_table_get(r->headers_in, "Cache-Control");
pragma = apr_table_get(r->headers_in, "Pragma");
- if (ap_cache_liststr(NULL, pragma, "no-cache", NULL)
- || ap_cache_liststr(NULL, cc_req, "no-cache", NULL)) {
+ ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);
+
+ if (cache->control_in.no_cache) {
if (!conf->ignorecachecontrol) {
return 0;
}
else {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "Incoming request is asking for an uncached version of "
- "%s, but we have been configured to ignore it and serve "
- "cached content anyway", r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ "Incoming request is asking for an uncached version of "
+ "%s, but we have been configured to ignore it and serve "
+ "cached content anyway", r->unparsed_uri);
}
}
- if (ap_cache_liststr(NULL, cc_req, "no-store", NULL)) {
+ if (cache->control_in.no_store) {
if (!conf->ignorecachecontrol) {
/* We're not allowed to serve a cached copy */
return 0;
}
else {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "Incoming request is asking for a no-store version of "
- "%s, but we have been configured to ignore it and serve "
- "cached content anyway", r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ "Incoming request is asking for a no-store version of "
+ "%s, but we have been configured to ignore it and serve "
+ "cached content anyway", r->unparsed_uri);
}
}
@@ -396,17 +467,15 @@ CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r) {
}
-CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
- request_rec *r)
+int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache,
+ request_rec *r)
{
apr_status_t status;
apr_int64_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale;
apr_int64_t minfresh;
- const char *cc_cresp, *cc_req;
+ const char *cc_req;
const char *pragma;
const char *agestr = NULL;
- const char *expstr = NULL;
- char *val;
apr_time_t age_c = 0;
cache_info *info = &(h->cache_obj->info);
const char *warn_head;
@@ -452,29 +521,30 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
cc_req = apr_table_get(r->headers_in, "Cache-Control");
pragma = apr_table_get(r->headers_in, "Pragma");
- if (ap_cache_liststr(NULL, pragma, "no-cache", NULL)
- || ap_cache_liststr(NULL, cc_req, "no-cache", NULL)) {
+ ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);
+
+ if (cache->control_in.no_cache) {
if (!conf->ignorecachecontrol) {
/* Treat as stale, causing revalidation */
return 0;
}
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "Incoming request is asking for a uncached version of "
- "%s, but we have been configured to ignore it and "
- "serve a cached response anyway",
- r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00781)
+ "Incoming request is asking for a uncached version of "
+ "%s, but we have been configured to ignore it and "
+ "serve a cached response anyway",
+ r->unparsed_uri);
}
/* These come from the cached entity. */
- cc_cresp = apr_table_get(h->resp_hdrs, "Cache-Control");
- expstr = apr_table_get(h->resp_hdrs, "Expires");
-
- if (ap_cache_liststr(NULL, cc_cresp, "no-cache", NULL)) {
+ if (h->cache_obj->info.control.no_cache
+ || h->cache_obj->info.control.no_cache_header
+ || h->cache_obj->info.control.private_header) {
/*
- * The cached entity contained Cache-Control: no-cache, so treat as
- * stale causing revalidation
+ * The cached entity contained Cache-Control: no-cache, or a
+ * no-cache with a header present, or a private with a header
+ * present, so treat as stale causing revalidation.
*/
return 0;
}
@@ -487,31 +557,23 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
age = ap_cache_current_age(info, age_c, r->request_time);
/* extract s-maxage */
- if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val)
- && val != NULL) {
- smaxage = apr_atoi64(val);
- }
- else {
- smaxage = -1;
- }
+ smaxage = h->cache_obj->info.control.s_maxage_value;
/* extract max-age from request */
- if (!conf->ignorecachecontrol
- && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val)
- && val != NULL) {
- maxage_req = apr_atoi64(val);
- }
- else {
- maxage_req = -1;
+ maxage_req = -1;
+ if (!conf->ignorecachecontrol) {
+ maxage_req = cache->control_in.max_age_value;
}
- /* extract max-age from response */
- if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val)
- && val != NULL) {
- maxage_cresp = apr_atoi64(val);
+ /*
+ * extract max-age from response, if both s-maxage and max-age, s-maxage
+ * takes priority
+ */
+ if (smaxage != -1) {
+ maxage_cresp = smaxage;
}
else {
- maxage_cresp = -1;
+ maxage_cresp = h->cache_obj->info.control.max_age_value;
}
/*
@@ -528,9 +590,9 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
}
/* extract max-stale */
- if (cc_req && ap_cache_liststr(r->pool, cc_req, "max-stale", &val)) {
- if(val != NULL) {
- maxstale = apr_atoi64(val);
+ if (cache->control_in.max_stale) {
+ if(cache->control_in.max_stale_value != -1) {
+ maxstale = cache->control_in.max_stale_value;
}
else {
/*
@@ -549,28 +611,21 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
}
/* extract min-fresh */
- if (!conf->ignorecachecontrol
- && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val)
- && val != NULL) {
- minfresh = apr_atoi64(val);
+ if (!conf->ignorecachecontrol && cache->control_in.min_fresh) {
+ minfresh = cache->control_in.min_fresh_value;
}
else {
minfresh = 0;
}
- /* override maxstale if must-revalidate or proxy-revalidate */
- if (maxstale && ((cc_cresp &&
- ap_cache_liststr(NULL, cc_cresp,
- "must-revalidate", NULL)) ||
- (cc_cresp &&
- ap_cache_liststr(NULL, cc_cresp,
- "proxy-revalidate", NULL)))) {
+ /* override maxstale if must-revalidate, proxy-revalidate or s-maxage */
+ if (maxstale && (h->cache_obj->info.control.must_revalidate
+ || h->cache_obj->info.control.proxy_revalidate || smaxage != -1)) {
maxstale = 0;
}
/* handle expiration */
- if (((smaxage != -1) && (age < (smaxage - minfresh))) ||
- ((maxage != -1) && (age < (maxage + maxstale - minfresh))) ||
+ if (((maxage != -1) && (age < (maxage + maxstale - minfresh))) ||
((smaxage == -1) && (maxage == -1) &&
(info->expire != APR_DATE_BAD) &&
(age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) {
@@ -583,24 +638,24 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
apr_psprintf(r->pool, "%lu", (unsigned long)age));
/* add warning if maxstale overrode freshness calculation */
- if (!(((smaxage != -1) && age < smaxage) ||
- ((maxage != -1) && age < maxage) ||
+ if (!(((maxage != -1) && age < maxage) ||
(info->expire != APR_DATE_BAD &&
(apr_time_sec(info->expire - info->date)) > age))) {
/* make sure we don't stomp on a previous warning */
if ((warn_head == NULL) ||
((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
- apr_table_merge(h->resp_hdrs, "Warning",
- "110 Response is stale");
+ apr_table_mergen(h->resp_hdrs, "Warning",
+ "110 Response is stale");
}
}
+
/*
* If none of Expires, Cache-Control: max-age, or Cache-Control:
* s-maxage appears in the response, and the response header age
* calculated is more than 24 hours add the warning 113
*/
- if ((maxage_cresp == -1) && (smaxage == -1) &&
- (expstr == NULL) && (age > 86400)) {
+ if ((maxage_cresp == -1) && (smaxage == -1) && (apr_table_get(
+ h->resp_hdrs, "Expires") == NULL) && (age > 86400)) {
/* Make sure we don't stomp on a previous warning, and don't dup
* a 113 marning that is already present. Also, make sure to add
@@ -608,8 +663,8 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
*/
if ((warn_head == NULL) ||
((warn_head != NULL) && (ap_strstr_c(warn_head, "113") == NULL))) {
- apr_table_merge(h->resp_hdrs, "Warning",
- "113 Heuristic expiration");
+ apr_table_mergen(h->resp_hdrs, "Warning",
+ "113 Heuristic expiration");
}
}
return 1; /* Cache object is fresh (enough) */
@@ -645,122 +700,43 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
* A lock that exceeds a maximum age will be deleted, and another
* request gets to make a new lock and try again.
*/
- status = ap_cache_try_lock(conf, r, (char *)h->cache_obj->key);
+ status = cache_try_lock(conf, cache, r);
if (APR_SUCCESS == status) {
/* we obtained a lock, follow the stale path */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "Cache lock obtained for stale cached URL, "
- "revalidating entry: %s",
- r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00782)
+ "Cache lock obtained for stale cached URL, "
+ "revalidating entry: %s",
+ r->unparsed_uri);
return 0;
}
else if (APR_EEXIST == status) {
/* lock already exists, return stale data anyway, with a warning */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "Cache already locked for stale cached URL, "
- "pretend it is fresh: %s",
- r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00783)
+ "Cache already locked for stale cached URL, "
+ "pretend it is fresh: %s",
+ r->unparsed_uri);
/* make sure we don't stomp on a previous warning */
warn_head = apr_table_get(h->resp_hdrs, "Warning");
if ((warn_head == NULL) ||
((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
- apr_table_merge(h->resp_hdrs, "Warning",
- "110 Response is stale");
+ apr_table_mergen(h->resp_hdrs, "Warning",
+ "110 Response is stale");
}
return 1;
}
else {
/* some other error occurred, just treat the object as stale */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
- "Attempt to obtain a cache lock for stale "
- "cached URL failed, revalidating entry anyway: %s",
- r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00784)
+ "Attempt to obtain a cache lock for stale "
+ "cached URL failed, revalidating entry anyway: %s",
+ r->unparsed_uri);
return 0;
}
}
-/*
- * list is a comma-separated list of case-insensitive tokens, with
- * optional whitespace around the tokens.
- * The return returns 1 if the token val is found in the list, or 0
- * otherwise.
- */
-CACHE_DECLARE(int) ap_cache_liststr(apr_pool_t *p, const char *list,
- const char *key, char **val)
-{
- apr_size_t key_len;
- const char *next;
-
- if (!list) {
- return 0;
- }
-
- key_len = strlen(key);
- next = list;
-
- for (;;) {
-
- /* skip whitespace and commas to find the start of the next key */
- while (*next && (apr_isspace(*next) || (*next == ','))) {
- next++;
- }
-
- if (!*next) {
- return 0;
- }
-
- if (!strncasecmp(next, key, key_len)) {
- /* this field matches the key (though it might just be
- * a prefix match, so make sure the match is followed
- * by either a space or an equals sign)
- */
- next += key_len;
- if (!*next || (*next == '=') || apr_isspace(*next) ||
- (*next == ',')) {
- /* valid match */
- if (val) {
- while (*next && (*next != '=') && (*next != ',')) {
- next++;
- }
- if (*next == '=') {
- next++;
- while (*next && apr_isspace(*next )) {
- next++;
- }
- if (!*next) {
- *val = NULL;
- }
- else {
- const char *val_start = next;
- while (*next && !apr_isspace(*next) &&
- (*next != ',')) {
- next++;
- }
- *val = apr_pstrmemdup(p, val_start,
- next - val_start);
- }
- }
- else {
- *val = NULL;
- }
- }
- return 1;
- }
- }
-
- /* skip to the next field */
- do {
- next++;
- if (!*next) {
- return 0;
- }
- } while (*next != ',');
- }
-}
-
/* return each comma separated token, one at a time */
CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list,
const char **str)
@@ -878,23 +854,32 @@ CACHE_DECLARE(char *)ap_cache_generate_name(apr_pool_t *p, int dirlevels,
return apr_pstrdup(p, hashfile);
}
-/* Create a new table consisting of those elements from an input
+/*
+ * Create a new table consisting of those elements from an
* headers table that are allowed to be stored in a cache.
*/
-CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers(apr_pool_t *pool,
apr_table_t *t,
server_rec *s)
{
cache_server_conf *conf;
char **header;
int i;
+ apr_table_t *headers_out;
+
+ /* Short circuit the common case that there are not
+ * (yet) any headers populated.
+ */
+ if (t == NULL) {
+ return apr_table_make(pool, 10);
+ };
/* Make a copy of the headers, and remove from
* the copy any hop-by-hop headers, as defined in Section
* 13.5.1 of RFC 2616
*/
- apr_table_t *headers_out;
headers_out = apr_table_copy(pool, t);
+
apr_table_unset(headers_out, "Connection");
apr_table_unset(headers_out, "Keep-Alive");
apr_table_unset(headers_out, "Proxy-Authenticate");
@@ -906,6 +891,7 @@ CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
conf = (cache_server_conf *)ap_get_module_config(s->module_config,
&cache_module);
+
/* Remove the user defined headers set with CacheIgnoreHeaders.
* This may break RFC 2616 compliance on behalf of the administrator.
*/
@@ -915,3 +901,277 @@ CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
}
return headers_out;
}
+
+/*
+ * Create a new table consisting of those elements from an input
+ * headers table that are allowed to be stored in a cache.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_in(request_rec *r)
+{
+ return ap_cache_cacheable_headers(r->pool, r->headers_in, r->server);
+}
+
+/*
+ * Create a new table consisting of those elements from an output
+ * headers table that are allowed to be stored in a cache;
+ * ensure there is a content type and capture any errors.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r)
+{
+ apr_table_t *headers_out;
+
+ headers_out = apr_table_overlay(r->pool, r->headers_out,
+ r->err_headers_out);
+
+ apr_table_clear(r->err_headers_out);
+
+ headers_out = ap_cache_cacheable_headers(r->pool, headers_out,
+ r->server);
+
+ if (!apr_table_get(headers_out, "Content-Type")
+ && r->content_type) {
+ apr_table_setn(headers_out, "Content-Type",
+ ap_make_content_type(r, r->content_type));
+ }
+
+ if (!apr_table_get(headers_out, "Content-Encoding")
+ && r->content_encoding) {
+ apr_table_setn(headers_out, "Content-Encoding",
+ r->content_encoding);
+ }
+
+ return headers_out;
+}
+
+/**
+ * String tokenizer that ignores separator characters within quoted strings
+ * and escaped characters, as per RFC2616 section 2.2.
+ */
+static char *cache_strqtok(char *str, const char *sep, char **last)
+{
+ char *token;
+ int quoted = 0;
+
+ if (!str) { /* subsequent call */
+ str = *last; /* start where we left off */
+ }
+
+ /* skip characters in sep (will terminate at '\0') */
+ while (*str && ap_strchr_c(sep, *str)) {
+ ++str;
+ }
+
+ if (!*str) { /* no more tokens */
+ return NULL;
+ }
+
+ token = str;
+
+ /* skip valid token characters to terminate token and
+ * prepare for the next call (will terminate at '\0)
+ * on the way, ignore all quoted strings, and within
+ * quoted strings, escaped characters.
+ */
+ *last = token;
+ while (**last) {
+ if (!quoted) {
+ if (**last == '\"') {
+ quoted = 1;
+ ++*last;
+ }
+ else if (!ap_strchr_c(sep, **last)) {
+ ++*last;
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ if (**last == '\"') {
+ quoted = 0;
+ ++*last;
+ }
+ else if (**last == '\\') {
+ ++*last;
+ if (**last) {
+ ++*last;
+ }
+ }
+ else {
+ ++*last;
+ }
+ }
+ }
+
+ if (**last) {
+ **last = '\0';
+ ++*last;
+ }
+
+ return token;
+}
+
+/**
+ * Parse the Cache-Control and Pragma headers in one go, marking
+ * which tokens appear within the header. Populate the structure
+ * passed in.
+ */
+int ap_cache_control(request_rec *r, cache_control_t *cc,
+ const char *cc_header, const char *pragma_header, apr_table_t *headers)
+{
+ char *last;
+
+ if (cc->parsed) {
+ return cc->cache_control || cc->pragma;
+ }
+
+ cc->parsed = 1;
+ cc->max_age_value = -1;
+ cc->max_stale_value = -1;
+ cc->min_fresh_value = -1;
+ cc->s_maxage_value = -1;
+
+ if (pragma_header) {
+ char *header = apr_pstrdup(r->pool, pragma_header);
+ const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
+ while (token) {
+ /* handle most common quickest case... */
+ if (!strcmp(token, "no-cache")) {
+ cc->no_cache = 1;
+ }
+ /* ...then try slowest case */
+ else if (!strcasecmp(token, "no-cache")) {
+ cc->no_cache = 1;
+ }
+ token = cache_strqtok(NULL, CACHE_SEPARATOR, &last);
+ }
+ cc->pragma = 1;
+ }
+
+ if (cc_header) {
+ char *header = apr_pstrdup(r->pool, cc_header);
+ const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
+ while (token) {
+ switch (token[0]) {
+ case 'n':
+ case 'N': {
+ /* handle most common quickest cases... */
+ if (!strcmp(token, "no-cache")) {
+ cc->no_cache = 1;
+ }
+ else if (!strcmp(token, "no-store")) {
+ cc->no_store = 1;
+ }
+ /* ...then try slowest cases */
+ else if (!strncasecmp(token, "no-cache", 8)) {
+ if (token[8] == '=') {
+ if (apr_table_get(headers, token + 9)) {
+ cc->no_cache_header = 1;
+ }
+ }
+ else if (!token[8]) {
+ cc->no_cache = 1;
+ }
+ break;
+ }
+ else if (!strcasecmp(token, "no-store")) {
+ cc->no_store = 1;
+ }
+ else if (!strcasecmp(token, "no-transform")) {
+ cc->no_transform = 1;
+ }
+ break;
+ }
+ case 'm':
+ case 'M': {
+ /* handle most common quickest cases... */
+ if (!strcmp(token, "max-age=0")) {
+ cc->max_age = 1;
+ cc->max_age_value = 0;
+ }
+ else if (!strcmp(token, "must-revalidate")) {
+ cc->must_revalidate = 1;
+ }
+ /* ...then try slowest cases */
+ else if (!strncasecmp(token, "max-age", 7)) {
+ if (token[7] == '=') {
+ cc->max_age = 1;
+ cc->max_age_value = apr_atoi64(token + 8);
+ }
+ break;
+ }
+ else if (!strncasecmp(token, "max-stale", 9)) {
+ if (token[9] == '=') {
+ cc->max_stale = 1;
+ cc->max_stale_value = apr_atoi64(token + 10);
+ }
+ else if (!token[10]) {
+ cc->max_stale = 1;
+ cc->max_stale_value = -1;
+ }
+ break;
+ }
+ else if (!strncasecmp(token, "min-fresh", 9)) {
+ if (token[9] == '=') {
+ cc->min_fresh = 1;
+ cc->min_fresh_value = apr_atoi64(token + 10);
+ }
+ break;
+ }
+ else if (!strcasecmp(token, "must-revalidate")) {
+ cc->must_revalidate = 1;
+ }
+ break;
+ }
+ case 'o':
+ case 'O': {
+ if (!strcasecmp(token, "only-if-cached")) {
+ cc->only_if_cached = 1;
+ }
+ break;
+ }
+ case 'p':
+ case 'P': {
+ /* handle most common quickest cases... */
+ if (!strcmp(token, "private")) {
+ cc->private = 1;
+ }
+ /* ...then try slowest cases */
+ else if (!strcasecmp(token, "public")) {
+ cc->public = 1;
+ }
+ else if (!strncasecmp(token, "private", 7)) {
+ if (token[7] == '=') {
+ if (apr_table_get(headers, token + 8)) {
+ cc->private_header = 1;
+ }
+ }
+ else if (!token[7]) {
+ cc->private = 1;
+ }
+ break;
+ }
+ else if (!strcasecmp(token, "proxy-revalidate")) {
+ cc->proxy_revalidate = 1;
+ }
+ break;
+ }
+ case 's':
+ case 'S': {
+ if (!strncasecmp(token, "s-maxage", 8)) {
+ if (token[8] == '=') {
+ cc->s_maxage = 1;
+ cc->s_maxage_value = apr_atoi64(token + 9);
+ }
+ break;
+ }
+ break;
+ }
+ }
+ token = cache_strqtok(NULL, CACHE_SEPARATOR, &last);
+ }
+ cc->cache_control = 1;
+ }
+
+ return (cc_header != NULL || pragma_header != NULL);
+}
diff --git a/modules/cache/cache_util.h b/modules/cache/cache_util.h
new file mode 100644
index 00000000..eec38f3a
--- /dev/null
+++ b/modules/cache/cache_util.h
@@ -0,0 +1,300 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file cache_util.h
+ * @brief Cache Storage Functions
+ *
+ * @defgroup Cache_util Cache Utility Functions
+ * @ingroup MOD_CACHE
+ * @{
+ */
+
+#ifndef CACHE_UTIL_H
+#define CACHE_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mod_cache.h"
+
+#include "apr_hooks.h"
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_md5.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_optional.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "ap_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_connection.h"
+#include "util_filter.h"
+#include "apr_uri.h"
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "apr_atomic.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define MSEC_ONE_DAY ((apr_time_t)(86400*APR_USEC_PER_SEC)) /* one day, in microseconds */
+#define MSEC_ONE_HR ((apr_time_t)(3600*APR_USEC_PER_SEC)) /* one hour, in microseconds */
+#define MSEC_ONE_MIN ((apr_time_t)(60*APR_USEC_PER_SEC)) /* one minute, in microseconds */
+#define MSEC_ONE_SEC ((apr_time_t)(APR_USEC_PER_SEC)) /* one second, in microseconds */
+
+#define DEFAULT_CACHE_MAXEXPIRE MSEC_ONE_DAY
+#define DEFAULT_CACHE_MINEXPIRE 0
+#define DEFAULT_CACHE_EXPIRE MSEC_ONE_HR
+#define DEFAULT_CACHE_LMFACTOR (0.1)
+#define DEFAULT_CACHE_MAXAGE 5
+#define DEFAULT_X_CACHE 0
+#define DEFAULT_X_CACHE_DETAIL 0
+#define DEFAULT_CACHE_STALE_ON_ERROR 1
+#define DEFAULT_CACHE_LOCKPATH "/mod_cache-lock"
+#define CACHE_LOCKNAME_KEY "mod_cache-lockname"
+#define CACHE_LOCKFILE_KEY "mod_cache-lockfile"
+#define CACHE_CTX_KEY "mod_cache-ctx"
+
+/**
+ * cache_util.c
+ */
+
+struct cache_enable {
+ apr_uri_t url;
+ const char *type;
+ apr_size_t pathlen;
+};
+
+struct cache_disable {
+ apr_uri_t url;
+ apr_size_t pathlen;
+};
+
+/* static information about the local cache */
+typedef struct {
+ apr_array_header_t *cacheenable; /* URLs to cache */
+ apr_array_header_t *cachedisable; /* URLs not to cache */
+ /** store the headers that should not be stored in the cache */
+ apr_array_header_t *ignore_headers;
+ /** store the identifiers that should not be used for key calculation */
+ apr_array_header_t *ignore_session_id;
+ const char *lockpath;
+ apr_time_t lockmaxage;
+ apr_uri_t *base_uri;
+ /** ignore client's requests for uncached responses */
+ unsigned int ignorecachecontrol:1;
+ /** ignore query-string when caching */
+ unsigned int ignorequerystring:1;
+ /** run within the quick handler */
+ unsigned int quick:1;
+ /* thundering herd lock */
+ unsigned int lock:1;
+ unsigned int x_cache:1;
+ unsigned int x_cache_detail:1;
+ /* flag if CacheIgnoreHeader has been set */
+ #define CACHE_IGNORE_HEADERS_SET 1
+ #define CACHE_IGNORE_HEADERS_UNSET 0
+ unsigned int ignore_headers_set:1;
+ /* flag if CacheIgnoreURLSessionIdentifiers has been set */
+ #define CACHE_IGNORE_SESSION_ID_SET 1
+ #define CACHE_IGNORE_SESSION_ID_UNSET 0
+ unsigned int ignore_session_id_set:1;
+ unsigned int base_uri_set:1;
+ unsigned int ignorecachecontrol_set:1;
+ unsigned int ignorequerystring_set:1;
+ unsigned int quick_set:1;
+ unsigned int lock_set:1;
+ unsigned int lockpath_set:1;
+ unsigned int lockmaxage_set:1;
+ unsigned int x_cache_set:1;
+ unsigned int x_cache_detail_set:1;
+} cache_server_conf;
+
+typedef struct {
+ /* Minimum time to keep cached files in msecs */
+ apr_time_t minex;
+ /* Maximum time to keep cached files in msecs */
+ apr_time_t maxex;
+ /* default time to keep cached file in msecs */
+ apr_time_t defex;
+ /* factor for estimating expires date */
+ double factor;
+ /* cache enabled for this location */
+ apr_array_header_t *cacheenable;
+ /* cache disabled for this location */
+ unsigned int disable:1;
+ /* set X-Cache headers */
+ unsigned int x_cache:1;
+ unsigned int x_cache_detail:1;
+ /* serve stale on error */
+ unsigned int stale_on_error:1;
+ /** ignore the last-modified header when deciding to cache this request */
+ unsigned int no_last_mod_ignore:1;
+ /** ignore expiration date from server */
+ unsigned int store_expired:1;
+ /** ignore Cache-Control: private header from server */
+ unsigned int store_private:1;
+ /** ignore Cache-Control: no-store header from client or server */
+ unsigned int store_nostore:1;
+ unsigned int minex_set:1;
+ unsigned int maxex_set:1;
+ unsigned int defex_set:1;
+ unsigned int factor_set:1;
+ unsigned int x_cache_set:1;
+ unsigned int x_cache_detail_set:1;
+ unsigned int stale_on_error_set:1;
+ unsigned int no_last_mod_ignore_set:1;
+ unsigned int store_expired_set:1;
+ unsigned int store_private_set:1;
+ unsigned int store_nostore_set:1;
+ unsigned int enable_set:1;
+ unsigned int disable_set:1;
+} cache_dir_conf;
+
+/* A linked-list of authn providers. */
+typedef struct cache_provider_list cache_provider_list;
+
+struct cache_provider_list {
+ const char *provider_name;
+ const cache_provider *provider;
+ cache_provider_list *next;
+};
+
+/* per request cache information */
+typedef struct {
+ cache_provider_list *providers; /* possible cache providers */
+ const cache_provider *provider; /* current cache provider */
+ const char *provider_name; /* current cache provider name */
+ int fresh; /* is the entity fresh? */
+ cache_handle_t *handle; /* current cache handle */
+ cache_handle_t *stale_handle; /* stale cache handle */
+ apr_table_t *stale_headers; /* original request headers. */
+ int in_checked; /* CACHE_SAVE must cache the entity */
+ int block_response; /* CACHE_SAVE must block response. */
+ apr_bucket_brigade *saved_brigade; /* copy of partial response */
+ apr_off_t saved_size; /* length of saved_brigade */
+ apr_time_t exp; /* expiration */
+ apr_time_t lastmod; /* last-modified time */
+ cache_info *info; /* current cache info */
+ ap_filter_t *save_filter; /* Enable us to restore the filter on error */
+ ap_filter_t *remove_url_filter; /* Enable us to remove the filter */
+ const char *key; /* The cache key created for this
+ * request
+ */
+ apr_off_t size; /* the content length from the headers, or -1 */
+ apr_bucket_brigade *out; /* brigade to reuse for upstream responses */
+ cache_control_t control_in; /* cache control incoming */
+} cache_request_rec;
+
+/**
+ * Check the whether the request allows a cached object to be served as per RFC2616
+ * section 14.9.4 (Cache Revalidation and Reload Controls)
+ * @param cache cache_request_rec
+ * @param r request_rec
+ * @return 0 ==> cache object may not be served, 1 ==> cache object may be served
+ */
+CACHE_DECLARE(int) ap_cache_check_allowed(cache_request_rec *cache, request_rec *r);
+
+/**
+ * Check the freshness of the cache object per RFC2616 section 13.2 (Expiration Model)
+ * @param h cache_handle_t
+ * @param cache cache_request_rec
+ * @param r request_rec
+ * @return 0 ==> cache object is stale, 1 ==> cache object is fresh
+ */
+int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache,
+ request_rec *r);
+
+/**
+ * Try obtain a cache wide lock on the given cache key.
+ *
+ * If we return APR_SUCCESS, we obtained the lock, and we are clear to
+ * proceed to the backend. If we return APR_EEXISTS, the the lock is
+ * already locked, someone else has gone to refresh the backend data
+ * already, so we must return stale data with a warning in the mean
+ * time. If we return anything else, then something has gone pear
+ * shaped, and we allow the request through to the backend regardless.
+ *
+ * This lock is created from the request pool, meaning that should
+ * something go wrong and the lock isn't deleted on return of the
+ * request headers from the backend for whatever reason, at worst the
+ * lock will be cleaned up when the request is dies or finishes.
+ *
+ * If something goes truly bananas and the lock isn't deleted when the
+ * request dies, the lock will be trashed when its max-age is reached,
+ * or when a request arrives containing a Cache-Control: no-cache. At
+ * no point is it possible for this lock to permanently deny access to
+ * the backend.
+ */
+apr_status_t cache_try_lock(cache_server_conf *conf, cache_request_rec *cache,
+ request_rec *r);
+
+/**
+ * Remove the cache lock, if present.
+ *
+ * First, try to close the file handle, whose delete-on-close should
+ * kill the file. Otherwise, just delete the file by name.
+ *
+ * If no lock name has yet been calculated, do the calculation of the
+ * lock name first before trying to delete the file.
+ *
+ * If an optional bucket brigade is passed, the lock will only be
+ * removed if the bucket brigade contains an EOS bucket.
+ */
+apr_status_t cache_remove_lock(cache_server_conf *conf,
+ cache_request_rec *cache, request_rec *r, apr_bucket_brigade *bb);
+
+cache_provider_list *cache_get_providers(request_rec *r,
+ cache_server_conf *conf, apr_uri_t uri);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !CACHE_UTIL_H */
+/** @} */
diff --git a/modules/cache/config.m4 b/modules/cache/config.m4
index 9572e06d..e0470faf 100644
--- a/modules/cache/config.m4
+++ b/modules/cache/config.m4
@@ -4,7 +4,7 @@ dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
APACHE_MODPATH_INIT(cache)
-APACHE_MODULE(file_cache, File cache, , , no)
+APACHE_MODULE(file_cache, File cache, , , most)
dnl # list of object files for mod_cache
cache_objs="dnl
@@ -12,15 +12,128 @@ mod_cache.lo dnl
cache_storage.lo dnl
cache_util.lo dnl
"
-dnl # list of object files for mod_mem_cache
-mem_cache_objs="dnl
-mod_mem_cache.lo dnl
-cache_cache.lo dnl
-cache_pqueue.lo dnl
-cache_hash.lo dnl
-"
-APACHE_MODULE(cache, dynamic file caching, $cache_objs, , no)
-APACHE_MODULE(disk_cache, disk caching module, , , no)
-APACHE_MODULE(mem_cache, memory caching module, $mem_cache_objs, , no)
+cache_disk_objs="mod_cache_disk.lo"
+
+case "$host" in
+ *os2*)
+ # OS/2 DLLs must resolve all symbols at build time
+ # and we need some from main cache module
+ cache_disk_objs="$cache_disk_objs mod_cache.la"
+ ;;
+esac
+
+APACHE_MODULE(cache, dynamic file caching. At least one storage management module (e.g. mod_cache_disk) is also necessary., $cache_objs, , most)
+APACHE_MODULE(cache_disk, disk caching module, $cache_disk_objs, , most)
+
+dnl
+dnl APACHE_CHECK_DISTCACHE
+dnl
+dnl Configure for the detected distcache installation, giving
+dnl preference to "--with-distcache=<path>" if it was specified.
+dnl
+AC_DEFUN(APACHE_CHECK_DISTCACHE,[
+if test "x$ap_distcache_configured" = "x"; then
+ dnl initialise the variables we use
+ ap_distcache_found=""
+ ap_distcache_base=""
+ ap_distcache_libs=""
+ ap_distcache_ldflags=""
+ ap_distcache_with=""
+
+ dnl Determine the distcache base directory, if any
+ AC_MSG_CHECKING([for user-provided distcache base])
+ AC_ARG_WITH(distcache, APACHE_HELP_STRING(--with-distcache=DIR, Distcache installation directory), [
+ dnl If --with-distcache specifies a directory, we use that directory or fail
+ if test "x$withval" != "xyes" -a "x$withval" != "x"; then
+ dnl This ensures $withval is actually a directory and that it is absolute
+ ap_distcache_with="yes"
+ ap_distcache_base="`cd $withval ; pwd`"
+ fi
+ ])
+ if test "x$ap_distcache_base" = "x"; then
+ AC_MSG_RESULT(none)
+ else
+ AC_MSG_RESULT($ap_distcache_base)
+ fi
+
+ dnl Run header and version checks
+ saved_CPPFLAGS="$CPPFLAGS"
+ saved_LIBS="$LIBS"
+ saved_LDFLAGS="$LDFLAGS"
+
+ if test "x$ap_distcache_base" != "x"; then
+ APR_ADDTO(CPPFLAGS, [-I$ap_distcache_base/include])
+ APR_ADDTO(INCLUDES, [-I$ap_distcache_base/include])
+ APR_ADDTO(LDFLAGS, [-L$ap_distcache_base/lib])
+ APR_ADDTO(ap_distcache_ldflags, [-L$ap_distcache_base/lib])
+ if test "x$ap_platform_runtime_link_flag" != "x"; then
+ APR_ADDTO(LDFLAGS, [$ap_platform_runtime_link_flag$ap_distcache_base/lib])
+ APR_ADDTO(ap_distcache_ldflags, [$ap_platform_runtime_link_flag$ap_distcache_base/lib])
+ fi
+ fi
+ dnl First check for mandatory headers
+ AC_CHECK_HEADERS([distcache/dc_client.h], [ap_distcache_found="yes"], [])
+ if test "$ap_distcache_found" = "yes"; then
+ dnl test for a good version
+ AC_MSG_CHECKING(for distcache version)
+ AC_TRY_COMPILE([#include <distcache/dc_client.h>],[
+#if DISTCACHE_CLIENT_API != 0x0001
+#error "distcache API version is unrecognised"
+#endif],
+ [],
+ [ap_distcache_found="no"])
+ AC_MSG_RESULT($ap_distcache_found)
+ fi
+ if test "$ap_distcache_found" != "yes"; then
+ if test "x$ap_distcache_with" = "x"; then
+ AC_MSG_WARN([...No distcache detected])
+ else
+ AC_MSG_ERROR([...No distcache detected])
+ fi
+ else
+ dnl Run library and function checks
+ AC_MSG_CHECKING(for distcache libraries)
+ ap_distcache_libs="-ldistcache -lnal"
+ APR_ADDTO(LIBS, [$ap_distcache_libs])
+
+ AC_TRY_LINK(
+ [#include <distcache/dc_client.h>],
+ [DC_CTX *foo = DC_CTX_new((const char *)0,0);],
+ [],
+ [ap_distcache_found="no"])
+ AC_MSG_RESULT($ap_distcache_found)
+ if test "$ap_distcache_found" != "yes"; then
+ if test "x$ap_distcache_base" = "x"; then
+ AC_MSG_WARN([... Error, distcache libraries were missing or unusable])
+ else
+ AC_MSG_ERROR([... Error, distcache libraries were missing or unusable])
+ fi
+ fi
+ fi
+
+ dnl restore
+ CPPFLAGS="$saved_CPPFLAGS"
+ LIBS="$saved_LIBS"
+ LDFLAGS="$saved_LDFLAGS"
+
+ dnl Adjust apache's configuration based on what we found above.
+ if test "$ap_distcache_found" = "yes"; then
+ APR_ADDTO(MOD_SOCACHE_DC_LDADD, [$ap_distcache_ldflags $ap_distcache_libs])
+ AC_DEFINE(HAVE_DISTCACHE, 1, [Define if distcache support is enabled])
+ else
+ enable_socache_dc=no
+ fi
+ ap_distcache_configured="yes"
+fi
+])
+
+APACHE_MODULE(socache_shmcb, shmcb small object cache provider, , , most)
+APACHE_MODULE(socache_dbm, dbm small object cache provider, , , most)
+APACHE_MODULE(socache_memcache, memcache small object cache provider, , , most)
+APACHE_MODULE(socache_dc, distcache small object cache provider, , , no, [
+ APACHE_CHECK_DISTCACHE
+])
+
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
APACHE_MODPATH_FINISH
diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c
index 8aeb3f73..17fc9d3f 100644
--- a/modules/cache/mod_cache.c
+++ b/modules/cache/mod_cache.c
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
-
#include "mod_cache.h"
+#include "cache_storage.h"
+#include "cache_util.h"
+
module AP_MODULE_DECLARE_DATA cache_module;
APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
@@ -27,6 +28,7 @@ APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
/* Handles for cache filters, resolved at startup to eliminate
* a name-to-function mapping on each request
*/
+static ap_filter_rec_t *cache_filter_handle;
static ap_filter_rec_t *cache_save_filter_handle;
static ap_filter_rec_t *cache_save_subreq_filter_handle;
static ap_filter_rec_t *cache_out_filter_handle;
@@ -46,18 +48,32 @@ static ap_filter_rec_t *cache_remove_url_filter_handle;
* add CACHE_SAVE filter
* If No:
* oh well.
+ *
+ * By default, the cache handler runs in the quick handler, bypassing
+ * virtually all server processing and offering the cache its optimal
+ * performance. In this mode, the cache bolts onto the front of the
+ * server, and behaves as a discrete RFC2616 caching proxy
+ * implementation.
+ *
+ * Under certain circumstances, an admin might want to run the cache as
+ * a normal handler instead of a quick handler, allowing the cache to
+ * run after the authorisation hooks, or by allowing fine control over
+ * the placement of the cache in the filter chain. This option comes at
+ * a performance penalty, and should only be used to achieve specific
+ * caching goals where the admin understands what they are doing.
*/
-static int cache_url_handler(request_rec *r, int lookup)
+static int cache_quick_handler(request_rec *r, int lookup)
{
apr_status_t rv;
const char *auth;
cache_provider_list *providers;
cache_request_rec *cache;
- cache_server_conf *conf;
apr_bucket_brigade *out;
+ apr_bucket *e;
ap_filter_t *next;
ap_filter_rec_t *cache_out_handle;
+ cache_server_conf *conf;
/* Delay initialization until we know we are handling a GET */
if (r->method_number != M_GET) {
@@ -67,20 +83,22 @@ static int cache_url_handler(request_rec *r, int lookup)
conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
&cache_module);
+ /* only run if the quick handler is enabled */
+ if (!conf->quick) {
+ return DECLINED;
+ }
+
/*
* Which cache module (if any) should handle this request?
*/
- if (!(providers = ap_cache_get_providers(r, conf, r->parsed_uri))) {
+ if (!(providers = cache_get_providers(r, conf, r->parsed_uri))) {
return DECLINED;
}
/* make space for the per request config */
- cache = (cache_request_rec *) ap_get_module_config(r->request_config,
- &cache_module);
- if (!cache) {
- cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
- ap_set_module_config(r->request_config, &cache_module, cache);
- }
+ cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
+ cache->size = -1;
+ cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
/* save away the possible providers */
cache->providers = providers;
@@ -109,7 +127,7 @@ static int cache_url_handler(request_rec *r, int lookup)
* add cache_out filter
* return OK
*/
- rv = cache_select(r);
+ rv = cache_select(cache, r);
if (rv != OK) {
if (rv == DECLINED) {
if (!lookup) {
@@ -121,7 +139,7 @@ static int cache_url_handler(request_rec *r, int lookup)
* backend without any attempt to cache. this stops
* duplicated simultaneous attempts to cache an entity.
*/
- rv = ap_cache_try_lock(conf, r, NULL);
+ rv = cache_try_lock(conf, cache, r);
if (APR_SUCCESS == rv) {
/*
@@ -130,29 +148,32 @@ static int cache_url_handler(request_rec *r, int lookup)
* or not.
*/
if (r->main) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server,
- "Adding CACHE_SAVE_SUBREQ filter for %s",
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00749) "Adding CACHE_SAVE_SUBREQ filter for %s",
r->uri);
- ap_add_output_filter_handle(cache_save_subreq_filter_handle,
- NULL, r, r->connection);
+ cache->save_filter = ap_add_output_filter_handle(
+ cache_save_subreq_filter_handle, cache, r,
+ r->connection);
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server, "Adding CACHE_SAVE filter for %s",
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00750) "Adding CACHE_SAVE filter for %s",
r->uri);
- ap_add_output_filter_handle(cache_save_filter_handle,
- NULL, r, r->connection);
+ cache->save_filter = ap_add_output_filter_handle(
+ cache_save_filter_handle, cache, r,
+ r->connection);
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
+ apr_pool_userdata_setn(cache, CACHE_CTX_KEY, NULL, r->pool);
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00751)
"Adding CACHE_REMOVE_URL filter for %s",
r->uri);
/* Add cache_remove_url filter to this request to remove a
* stale cache entry if needed. Also put the current cache
* request rec in the filter context, as the request that
- * is available later during running the filter maybe
+ * is available later during running the filter may be
* different due to an internal redirect.
*/
cache->remove_url_filter =
@@ -160,44 +181,40 @@ static int cache_url_handler(request_rec *r, int lookup)
cache, r, r->connection);
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
- r->server, "Cache locked for url, not caching "
- "response: %s", r->uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv,
+ r, APLOGNO(00752) "Cache locked for url, not caching "
+ "response: %s", r->uri);
}
}
else {
if (cache->stale_headers) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server, "Restoring request headers for %s",
- r->uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00753) "Restoring request headers for %s",
+ r->uri);
r->headers_in = cache->stale_headers;
}
-
- /* Delete our per-request configuration. */
- ap_set_module_config(r->request_config, &cache_module, NULL);
}
}
else {
/* error */
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
- "cache: error returned while checking for cached "
- "file by '%s' cache", cache->provider_name);
+ return rv;
}
return DECLINED;
}
+ /* we've got a cache hit! tell everyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
+ "cache hit");
+
/* if we are a lookup, we are exiting soon one way or another; Restore
* the headers. */
if (lookup) {
if (cache->stale_headers) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
- "Restoring request headers.");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00754)
+ "Restoring request headers.");
r->headers_in = cache->stale_headers;
}
-
- /* Delete our per-request configuration. */
- ap_set_module_config(r->request_config, &cache_module, NULL);
}
rv = ap_meets_conditions(r);
@@ -236,7 +253,7 @@ static int cache_url_handler(request_rec *r, int lookup)
else {
cache_out_handle = cache_out_filter_handle;
}
- ap_add_output_filter_handle(cache_out_handle, NULL, r, r->connection);
+ ap_add_output_filter_handle(cache_out_handle, cache, r, r->connection);
/*
* Remove all filters that are before the cache_out filter. This ensures
@@ -258,18 +275,269 @@ static int cache_url_handler(request_rec *r, int lookup)
/* kick off the filter stack */
out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- rv = ap_pass_brigade(r->output_filters, out);
- if (rv != APR_SUCCESS) {
- if (rv != AP_FILTER_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
- "cache: error returned while trying to return %s "
- "cached data",
- cache->provider_name);
+ e = apr_bucket_eos_create(out->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+
+ return ap_pass_brigade_fchk(r, out,
+ "cache_quick_handler(%s): ap_pass_brigade returned",
+ cache->provider_name);
+}
+
+/**
+ * If the two filter handles are present within the filter chain, replace
+ * the last instance of the first filter with the last instance of the
+ * second filter, and return true. If the second filter is not present at
+ * all, the first filter is removed, and false is returned. If neither
+ * filter is present, false is returned and this function does nothing.
+ * If a stop filter is specified, processing will stop once this filter is
+ * reached.
+ */
+static int cache_replace_filter(ap_filter_t *next, ap_filter_rec_t *from,
+ ap_filter_rec_t *to, ap_filter_rec_t *stop) {
+ ap_filter_t *ffrom = NULL, *fto = NULL;
+ while (next && next->frec != stop) {
+ if (next->frec == from) {
+ ffrom = next;
+ }
+ if (next->frec == to) {
+ fto = next;
+ }
+ next = next->next;
+ }
+ if (ffrom && fto) {
+ ffrom->frec = fto->frec;
+ ffrom->ctx = fto->ctx;
+ ap_remove_output_filter(fto);
+ return 1;
+ }
+ if (ffrom) {
+ ap_remove_output_filter(ffrom);
+ }
+ return 0;
+}
+
+/**
+ * Find the given filter, and return it if found, or NULL otherwise.
+ */
+static ap_filter_t *cache_get_filter(ap_filter_t *next, ap_filter_rec_t *rec) {
+ while (next) {
+ if (next->frec == rec && next->ctx) {
+ break;
}
+ next = next->next;
+ }
+ return next;
+}
+
+/**
+ * The cache handler is functionally similar to the cache_quick_hander,
+ * however a number of steps that are required by the quick handler are
+ * not required here, as the normal httpd processing has already handled
+ * these steps.
+ */
+static int cache_handler(request_rec *r)
+{
+ apr_status_t rv;
+ cache_provider_list *providers;
+ cache_request_rec *cache;
+ apr_bucket_brigade *out;
+ apr_bucket *e;
+ ap_filter_t *next;
+ ap_filter_rec_t *cache_out_handle;
+ ap_filter_rec_t *cache_save_handle;
+ cache_server_conf *conf;
+
+ /* Delay initialization until we know we are handling a GET */
+ if (r->method_number != M_GET) {
+ return DECLINED;
+ }
+
+ conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
+ &cache_module);
+
+ /* only run if the quick handler is disabled */
+ if (conf->quick) {
+ return DECLINED;
+ }
+
+ /*
+ * Which cache module (if any) should handle this request?
+ */
+ if (!(providers = cache_get_providers(r, conf, r->parsed_uri))) {
+ return DECLINED;
+ }
+
+ /* make space for the per request config */
+ cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
+ cache->size = -1;
+ cache->out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ /* save away the possible providers */
+ cache->providers = providers;
+
+ /*
+ * Try to serve this request from the cache.
+ *
+ * If no existing cache file (DECLINED)
+ * add cache_save filter
+ * If cached file (OK)
+ * clear filter stack
+ * add cache_out filter
+ * return OK
+ */
+ rv = cache_select(cache, r);
+ if (rv != OK) {
+ if (rv == DECLINED) {
+
+ /* try to obtain a cache lock at this point. if we succeed,
+ * we are the first to try and cache this url. if we fail,
+ * it means someone else is already trying to cache this
+ * url, and we should just let the request through to the
+ * backend without any attempt to cache. this stops
+ * duplicated simultaneous attempts to cache an entity.
+ */
+ rv = cache_try_lock(conf, cache, r);
+ if (APR_SUCCESS == rv) {
+
+ /*
+ * Add cache_save filter to cache this request. Choose
+ * the correct filter by checking if we are a subrequest
+ * or not.
+ */
+ if (r->main) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00756) "Adding CACHE_SAVE_SUBREQ filter for %s",
+ r->uri);
+ cache_save_handle = cache_save_subreq_filter_handle;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00757) "Adding CACHE_SAVE filter for %s",
+ r->uri);
+ cache_save_handle = cache_save_filter_handle;
+ }
+ ap_add_output_filter_handle(cache_save_handle, cache, r,
+ r->connection);
+
+ /*
+ * Did the user indicate the precise location of the
+ * CACHE_SAVE filter by inserting the CACHE filter as a
+ * marker?
+ *
+ * If so, we get cunning and replace CACHE with the
+ * CACHE_SAVE filter. This has the effect of inserting
+ * the CACHE_SAVE filter at the precise location where
+ * the admin wants to cache the content. All filters that
+ * lie before and after the original location of the CACHE
+ * filter will remain in place.
+ */
+ if (cache_replace_filter(r->output_filters,
+ cache_filter_handle, cache_save_handle,
+ ap_get_input_filter_handle("SUBREQ_CORE"))) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00758) "Replacing CACHE with CACHE_SAVE "
+ "filter for %s", r->uri);
+ }
+
+ /* save away the save filter stack */
+ cache->save_filter = cache_get_filter(r->output_filters,
+ cache_save_filter_handle);
+
+ apr_pool_userdata_setn(cache, CACHE_CTX_KEY, NULL, r->pool);
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00759)
+ "Adding CACHE_REMOVE_URL filter for %s",
+ r->uri);
+
+ /* Add cache_remove_url filter to this request to remove a
+ * stale cache entry if needed. Also put the current cache
+ * request rec in the filter context, as the request that
+ * is available later during running the filter may be
+ * different due to an internal redirect.
+ */
+ cache->remove_url_filter =
+ ap_add_output_filter_handle(cache_remove_url_filter_handle,
+ cache, r, r->connection);
+
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv,
+ r, APLOGNO(00760) "Cache locked for url, not caching "
+ "response: %s", r->uri);
+ }
+ }
+ else {
+ /* error */
+ return rv;
+ }
+ return DECLINED;
+ }
+
+ /* we've got a cache hit! tell everyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
+ "cache hit");
+
+ rv = ap_meets_conditions(r);
+ if (rv != OK) {
return rv;
}
- return OK;
+ /* Serve up the content */
+
+ /*
+ * Add cache_out filter to serve this request. Choose
+ * the correct filter by checking if we are a subrequest
+ * or not.
+ */
+ if (r->main) {
+ cache_out_handle = cache_out_subreq_filter_handle;
+ }
+ else {
+ cache_out_handle = cache_out_filter_handle;
+ }
+ ap_add_output_filter_handle(cache_out_handle, cache, r, r->connection);
+
+ /*
+ * Did the user indicate the precise location of the CACHE_OUT filter by
+ * inserting the CACHE filter as a marker?
+ *
+ * If so, we get cunning and replace CACHE with the CACHE_OUT filters.
+ * This has the effect of inserting the CACHE_OUT filter at the precise
+ * location where the admin wants to cache the content. All filters that
+ * lie *after* the original location of the CACHE filter will remain in
+ * place.
+ */
+ if (cache_replace_filter(r->output_filters, cache_filter_handle,
+ cache_out_handle, ap_get_input_filter_handle("SUBREQ_CORE"))) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r, APLOGNO(00761) "Replacing CACHE with CACHE_OUT filter for %s",
+ r->uri);
+ }
+
+ /*
+ * Remove all filters that are before the cache_out filter. This ensures
+ * that we kick off the filter stack with our cache_out filter being the
+ * first in the chain. This make sense because we want to restore things
+ * in the same manner as we saved them.
+ * There may be filters before our cache_out filter, because
+ *
+ * 1. We call ap_set_content_type during cache_select. This causes
+ * Content-Type specific filters to be added.
+ * 2. We call the insert_filter hook. This causes filters e.g. like
+ * the ones set with SetOutputFilter to be added.
+ */
+ next = r->output_filters;
+ while (next && (next->frec != cache_out_handle)) {
+ ap_remove_output_filter(next);
+ next = next->next;
+ }
+
+ /* kick off the filter stack */
+ out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ e = apr_bucket_eos_create(out->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+ return ap_pass_brigade_fchk(r, out, "cache(%s): ap_pass_brigade returned",
+ cache->provider_name);
}
/*
@@ -278,42 +546,134 @@ static int cache_url_handler(request_rec *r, int lookup)
*
* Deliver cached content (headers and body) up the stack.
*/
-static int cache_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+static int cache_out_filter(ap_filter_t *f, apr_bucket_brigade *in)
{
request_rec *r = f->r;
- cache_request_rec *cache;
-
- cache = (cache_request_rec *) ap_get_module_config(r->request_config,
- &cache_module);
+ apr_bucket *e;
+ cache_request_rec *cache = (cache_request_rec *)f->ctx;
if (!cache) {
/* user likely configured CACHE_OUT manually; they should use mod_cache
* configuration to do that */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "CACHE_OUT enabled unexpectedly");
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00762)
+ "CACHE/CACHE_OUT filter enabled while caching is disabled, ignoring");
ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
+ return ap_pass_brigade(f->next, in);
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
- "cache: running CACHE_OUT filter");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00763)
+ "cache: running CACHE_OUT filter");
+
+ /* clean out any previous response up to EOS, if any */
+ for (e = APR_BRIGADE_FIRST(in);
+ e != APR_BRIGADE_SENTINEL(in);
+ e = APR_BUCKET_NEXT(e))
+ {
+ if (APR_BUCKET_IS_EOS(e)) {
+ apr_bucket_brigade *bb = apr_brigade_create(r->pool,
+ r->connection->bucket_alloc);
- /* restore status of cached response */
- /* XXX: This exposes a bug in mem_cache, since it does not
- * restore the status into it's handle. */
- r->status = cache->handle->cache_obj->info.status;
+ /* restore status of cached response */
+ r->status = cache->handle->cache_obj->info.status;
- /* recall_headers() was called in cache_select() */
- cache->provider->recall_body(cache->handle, r->pool, bb);
+ /* recall_headers() was called in cache_select() */
+ cache->provider->recall_body(cache->handle, r->pool, bb);
+ APR_BRIGADE_PREPEND(in, bb);
- /* This filter is done once it has served up its content */
- ap_remove_output_filter(f);
+ /* This filter is done once it has served up its content */
+ ap_remove_output_filter(f);
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00764)
+ "cache: serving %s", r->uri);
+ return ap_pass_brigade(f->next, in);
+
+ }
+ apr_bucket_delete(e);
+ }
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
- "cache: serving %s", r->uri);
- return ap_pass_brigade(f->next, bb);
+ return APR_SUCCESS;
}
+/*
+ * Having jumped through all the hoops and decided to cache the
+ * response, call store_body() for each brigade, handling the
+ * case where the provider can't swallow the full brigade. In this
+ * case, we write the brigade we were passed out downstream, and
+ * loop around to try and cache some more until the in brigade is
+ * completely empty. As soon as the out brigade contains eos, call
+ * commit_entity() to finalise the cached element.
+ */
+static int cache_save_store(ap_filter_t *f, apr_bucket_brigade *in,
+ cache_server_conf *conf, cache_request_rec *cache)
+{
+ int rv = APR_SUCCESS;
+ apr_bucket *e;
+
+ /* pass the brigade in into the cache provider, which is then
+ * expected to move cached buckets to the out brigade, for us
+ * to pass up the filter stack. repeat until in is empty, or
+ * we fail.
+ */
+ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
+
+ rv = cache->provider->store_body(cache->handle, f->r, in, cache->out);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, f->r, APLOGNO(00765)
+ "cache: Cache provider's store_body failed!");
+ ap_remove_output_filter(f);
+
+ /* give someone else the chance to cache the file */
+ cache_remove_lock(conf, cache, f->r, NULL);
+
+ /* give up trying to cache, just step out the way */
+ APR_BRIGADE_PREPEND(in, cache->out);
+ return ap_pass_brigade(f->next, in);
+
+ }
+
+ /* does the out brigade contain eos? if so, we're done, commit! */
+ for (e = APR_BRIGADE_FIRST(cache->out);
+ e != APR_BRIGADE_SENTINEL(cache->out);
+ e = APR_BUCKET_NEXT(e))
+ {
+ if (APR_BUCKET_IS_EOS(e)) {
+ rv = cache->provider->commit_entity(cache->handle, f->r);
+ break;
+ }
+ }
+
+ /* conditionally remove the lock as soon as we see the eos bucket */
+ cache_remove_lock(conf, cache, f->r, cache->out);
+
+ if (APR_BRIGADE_EMPTY(cache->out)) {
+ if (APR_BRIGADE_EMPTY(in)) {
+ /* cache provider wants more data before passing the brigade
+ * upstream, oblige the provider by leaving to fetch more.
+ */
+ break;
+ }
+ else {
+ /* oops, no data out, but not all data read in either, be
+ * safe and stand down to prevent a spin.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, f->r, APLOGNO(00766)
+ "cache: Cache provider's store_body returned an "
+ "empty brigade, but didn't consume all of the"
+ "input brigade, standing down to prevent a spin");
+ ap_remove_output_filter(f);
+
+ /* give someone else the chance to cache the file */
+ cache_remove_lock(conf, cache, f->r, NULL);
+
+ return ap_pass_brigade(f->next, in);
+ }
+ }
+
+ rv = ap_pass_brigade(f->next, cache->out);
+ }
+
+ return rv;
+}
/*
* CACHE_SAVE filter
@@ -339,29 +699,32 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
{
int rv = !OK;
request_rec *r = f->r;
- cache_request_rec *cache;
+ cache_request_rec *cache = (cache_request_rec *)f->ctx;
cache_server_conf *conf;
- const char *cc_out, *cl;
+ cache_dir_conf *dconf;
+ cache_control_t control;
+ const char *cc_out, *cl, *pragma;
const char *exps, *lastmods, *dates, *etag;
apr_time_t exp, date, lastmod, now;
- apr_off_t size;
+ apr_off_t size = -1;
cache_info *info = NULL;
char *reason;
apr_pool_t *p;
apr_bucket *e;
+ apr_table_t *headers;
conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
&cache_module);
/* Setup cache_request_rec */
- cache = (cache_request_rec *) ap_get_module_config(r->request_config,
- &cache_module);
if (!cache) {
/* user likely configured CACHE_SAVE manually; they should really use
* mod_cache configuration to do that
*/
- cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
- ap_set_module_config(r->request_config, &cache_module, cache);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00767)
+ "CACHE/CACHE_SAVE filter enabled while caching is disabled, ignoring");
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, in);
}
reason = NULL;
@@ -379,32 +742,11 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
return APR_SUCCESS;
}
- /* have we already run the cachability check and set up the
+ /* have we already run the cacheability check and set up the
* cached file handle?
*/
if (cache->in_checked) {
- /* pass the brigades into the cache, then pass them
- * up the filter stack
- */
- rv = cache->provider->store_body(cache->handle, r, in);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
- "cache: Cache provider's store_body failed!");
- ap_remove_output_filter(f);
-
- /* give someone else the chance to cache the file */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
- }
- else {
-
- /* proactively remove the lock as soon as we see the eos bucket */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, in);
-
- }
-
- return ap_pass_brigade(f->next, in);
+ return cache_save_store(f, in, conf, cache);
}
/*
@@ -415,6 +757,65 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* all. This section is* run before the above section.
*/
+ dconf = ap_get_module_config(r->per_dir_config, &cache_module);
+
+ /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
+ * If a cache receives a 5xx response while attempting to revalidate an
+ * entry, it MAY either forward this response to the requesting client,
+ * or act as if the server failed to respond. In the latter case, it MAY
+ * return a previously received response unless the cached entry
+ * includes the "must-revalidate" cache-control directive (see section
+ * 14.9).
+ *
+ * This covers the case where an error was generated behind us, for example
+ * by a backend server via mod_proxy.
+ */
+ if (dconf->stale_on_error && r->status >= HTTP_INTERNAL_SERVER_ERROR) {
+
+ ap_remove_output_filter(cache->remove_url_filter);
+
+ if (cache->stale_handle
+ && !cache->stale_handle->cache_obj->info.control.must_revalidate
+ && !cache->stale_handle->cache_obj->info.control.proxy_revalidate) {
+ const char *warn_head;
+
+ /* morph the current save filter into the out filter, and serve from
+ * cache.
+ */
+ cache->handle = cache->stale_handle;
+ if (r->main) {
+ f->frec = cache_out_subreq_filter_handle;
+ }
+ else {
+ f->frec = cache_out_filter_handle;
+ }
+
+ r->headers_out = cache->stale_handle->resp_hdrs;
+
+ ap_set_content_type(r, apr_table_get(
+ cache->stale_handle->resp_hdrs, "Content-Type"));
+
+ /* add a revalidation warning */
+ warn_head = apr_table_get(r->err_headers_out, "Warning");
+ if ((warn_head == NULL) || ((warn_head != NULL)
+ && (ap_strstr_c(warn_head, "111") == NULL))) {
+ apr_table_mergen(r->err_headers_out, "Warning",
+ "111 Revalidation failed");
+ }
+
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_HIT,
+ apr_psprintf(r->pool,
+ "cache hit: %d status; stale content returned",
+ r->status));
+
+ /* give someone else the chance to cache the file */
+ cache_remove_lock(conf, cache, f->r, NULL);
+
+ /* pass brigade to our morphed out filter */
+ return ap_pass_brigade(f, in);
+ }
+ }
+
/* read expiry date; if a bad date, then leave it so the client can
* read it
*/
@@ -423,9 +824,7 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
exps = apr_table_get(r->headers_out, "Expires");
}
if (exps != NULL) {
- if (APR_DATE_BAD == (exp = apr_date_parse_http(exps))) {
- exps = NULL;
- }
+ exp = apr_date_parse_http(exps);
}
else {
exp = APR_DATE_BAD;
@@ -452,10 +851,27 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
etag = apr_table_get(r->headers_out, "Etag");
}
cc_out = apr_table_get(r->err_headers_out, "Cache-Control");
- if (cc_out == NULL) {
+ pragma = apr_table_get(r->err_headers_out, "Pragma");
+ headers = r->err_headers_out;
+ if (!cc_out && !pragma) {
cc_out = apr_table_get(r->headers_out, "Cache-Control");
+ pragma = apr_table_get(r->headers_out, "Pragma");
+ headers = r->headers_out;
}
+ /* Have we received a 304 response without any headers at all? Fall back to
+ * the original headers in the original cached request.
+ */
+ if (r->status == HTTP_NOT_MODIFIED && cache->stale_handle && !cc_out
+ && !pragma) {
+ cc_out = apr_table_get(cache->stale_handle->resp_hdrs, "Cache-Control");
+ pragma = apr_table_get(cache->stale_handle->resp_hdrs, "Pragma");
+ }
+
+ /* Parse the cache control header */
+ memset(&control, 0, sizeof(cache_control_t));
+ ap_cache_control(r, &control, cc_out, pragma, headers);
+
/*
* what responses should we not cache?
*
@@ -465,11 +881,13 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* They are tested here one by one to be clear and unambiguous.
*/
if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
+ && r->status != HTTP_PARTIAL_CONTENT
&& r->status != HTTP_MULTIPLE_CHOICES
&& r->status != HTTP_MOVED_PERMANENTLY
&& r->status != HTTP_NOT_MODIFIED) {
/* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
- * We don't cache 206, because we don't (yet) cache partial responses.
+ * We allow the caching of 206, but a cache implementation might choose
+ * to decline to cache a 206 if it doesn't know how to.
* We include 304 Not Modified here too as this is the origin server
* telling us to serve the cached copy.
*/
@@ -500,14 +918,21 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
/* if a broken Expires header is present, don't cache it */
reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
}
- else if (exp != APR_DATE_BAD && exp < r->request_time)
- {
+ else if (!dconf->store_expired && exp != APR_DATE_BAD
+ && exp < r->request_time) {
/* if a Expires header is in the past, don't cache it */
- reason = "Expires header already expired, not cacheable";
- }
- else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL &&
- !ap_cache_liststr(NULL, cc_out, "max-age", NULL) &&
- !ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)) {
+ reason = "Expires header already expired; not cacheable";
+ }
+ else if (!dconf->store_expired && (control.must_revalidate
+ || control.proxy_revalidate) && (!control.s_maxage_value
+ || (!control.s_maxage && !control.max_age_value)) && lastmods
+ == NULL && etag == NULL) {
+ /* if we're already stale, but can never revalidate, don't cache it */
+ reason
+ = "s-maxage or max-age zero and no Last-Modified or Etag; not cacheable";
+ }
+ else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL
+ && !control.max_age && !control.s_maxage) {
/* if a query string is present but no explicit expiration time,
* don't cache it (RFC 2616/13.9 & 13.2.1)
*/
@@ -520,10 +945,9 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
*/
reason = "HTTP Status 304 Not Modified";
}
- else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL
- && (exps == NULL) && (conf->no_last_mod_ignore ==0) &&
- !ap_cache_liststr(NULL, cc_out, "max-age", NULL) &&
- !ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)) {
+ else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL && (exps
+ == NULL) && (dconf->no_last_mod_ignore == 0) && !control.max_age
+ && !control.s_maxage) {
/* 200 OK response from HTTP/1.0 and up without Last-Modified,
* Etag, Expires, Cache-Control:max-age, or Cache-Control:s-maxage
* headers.
@@ -531,38 +955,25 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
/* Note: mod-include clears last_modified/expires/etags - this
* is why we have an optional function for a key-gen ;-)
*/
- reason = "No Last-Modified, Etag, Expires, Cache-Control:max-age or Cache-Control:s-maxage headers";
+ reason = "No Last-Modified; Etag; Expires; Cache-Control:max-age or Cache-Control:s-maxage headers";
}
- else if (r->header_only && !cache->stale_handle) {
- /* Forbid HEAD requests unless we have it cached already */
- reason = "HTTP HEAD request";
- }
- else if (!conf->store_nostore &&
- ap_cache_liststr(NULL, cc_out, "no-store", NULL)) {
+ else if (!dconf->store_nostore && control.no_store) {
/* RFC2616 14.9.2 Cache-Control: no-store response
* indicating do not cache, or stop now if you are
* trying to cache it.
*/
- /* FIXME: The Cache-Control: no-store could have come in on a 304,
- * FIXME: while the original request wasn't conditional. IOW, we
- * FIXME: made the the request conditional earlier to revalidate
- * FIXME: our cached response.
- */
reason = "Cache-Control: no-store present";
}
- else if (!conf->store_private &&
- ap_cache_liststr(NULL, cc_out, "private", NULL)) {
+ else if (!dconf->store_private && control.private) {
/* RFC2616 14.9.1 Cache-Control: private response
* this object is marked for this user's eyes only. Behave
* as a tunnel.
*/
- /* FIXME: See above (no-store) */
reason = "Cache-Control: private present";
}
- else if (apr_table_get(r->headers_in, "Authorization") != NULL
- && !(ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)
- || ap_cache_liststr(NULL, cc_out, "must-revalidate", NULL)
- || ap_cache_liststr(NULL, cc_out, "public", NULL))) {
+ else if (apr_table_get(r->headers_in, "Authorization")
+ && !(control.s_maxage || control.must_revalidate
+ || control.proxy_revalidate || control.public)) {
/* RFC2616 14.8 Authorisation:
* if authorisation is included in the request, we don't cache,
* but we can cache if the following exceptions are true:
@@ -572,9 +983,7 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
*/
reason = "Authorization required";
}
- else if (ap_cache_liststr(NULL,
- apr_table_get(r->headers_out, "Vary"),
- "*", NULL)) {
+ else if (ap_find_token(NULL, apr_table_get(r->headers_out, "Vary"), "*")) {
reason = "Vary header contains '*'";
}
else if (apr_table_get(r->subprocess_env, "no-cache") != NULL) {
@@ -619,29 +1028,59 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
APR_BRIGADE_INSERT_TAIL(bb, bkt);
}
else {
+ /* RFC 2616 10.3.5 states that entity headers are not supposed
+ * to be in the 304 response. Therefore, we need to combine the
+ * response headers with the cached headers *before* we update
+ * the cached headers.
+ *
+ * However, before doing that, we need to first merge in
+ * err_headers_out and we also need to strip any hop-by-hop
+ * headers that might have snuck in.
+ */
+ r->headers_out = ap_cache_cacheable_headers_out(r);
+
+ /* Merge in our cached headers. However, keep any updated values. */
+ cache_accept_headers(cache->handle, r, 1);
+
cache->provider->recall_body(cache->handle, r->pool, bb);
+
+ bkt = apr_bucket_eos_create(bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
}
cache->block_response = 1;
+ /* we've got a cache conditional hit! tell anyone who cares */
+ cache_run_cache_status(
+ cache->handle,
+ r,
+ r->headers_out,
+ AP_CACHE_REVALIDATE,
+ apr_psprintf(
+ r->pool,
+ "conditional cache hit: 304 was uncacheable though (%s); entity removed",
+ reason));
+
/* let someone else attempt to cache */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
+ cache_remove_lock(conf, cache, r, NULL);
return ap_pass_brigade(f->next, bb);
}
if (reason) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache: %s not cached. Reason: %s", r->unparsed_uri,
- reason);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00768)
+ "cache: %s not cached. Reason: %s", r->unparsed_uri,
+ reason);
+
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
+ reason);
/* remove this filter from the chain */
ap_remove_output_filter(f);
/* remove the lock file unconditionally */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
+ cache_remove_lock(conf, cache, r, NULL);
/* ship the data up the stack */
return ap_pass_brigade(f->next, in);
@@ -668,7 +1107,6 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* buckets and use their length to calculate the size
*/
int all_buckets_here=0;
- int unresolved_length = 0;
size=0;
for (e = APR_BRIGADE_FIRST(in);
e != APR_BRIGADE_SENTINEL(in);
@@ -679,7 +1117,6 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
break;
}
if (APR_BUCKET_IS_FLUSH(e)) {
- unresolved_length = 1;
continue;
}
if (e->length == (apr_size_t)-1) {
@@ -692,6 +1129,9 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
}
}
+ /* remember content length to check response size against later */
+ cache->size = size;
+
/* It's safe to cache the response.
*
* There are two possiblities at this point:
@@ -706,10 +1146,6 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
*/
/* Did we have a stale cache entry that really is stale?
- *
- * Note that for HEAD requests, we won't get the body, so for a stale
- * HEAD request, we don't remove the entity - instead we let the
- * CACHE_REMOVE_URL filter remove the stale item from the cache.
*/
if (cache->stale_handle) {
if (r->status == HTTP_NOT_MODIFIED) {
@@ -718,7 +1154,7 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
info = &cache->handle->cache_obj->info;
rv = OK;
}
- else if (!r->header_only) {
+ else {
/* Oh, well. Toss it. */
cache->provider->remove_entity(cache->stale_handle);
/* Treat the request as if it wasn't conditional. */
@@ -732,30 +1168,33 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
}
}
- /* no cache handle, create a new entity only for non-HEAD requests */
- if (!cache->handle && !r->header_only) {
- rv = cache_create_entity(r, size);
+ /* no cache handle, create a new entity */
+ if (!cache->handle) {
+ rv = cache_create_entity(cache, r, size, in);
info = apr_pcalloc(r->pool, sizeof(cache_info));
/* We only set info->status upon the initial creation. */
info->status = r->status;
}
if (rv != OK) {
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
+ "cache miss: create_entity failed");
+
/* Caching layer declined the opportunity to cache the response */
ap_remove_output_filter(f);
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
+ cache_remove_lock(conf, cache, r, NULL);
return ap_pass_brigade(f->next, in);
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache: Caching url: %s", r->unparsed_uri);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00769)
+ "cache: Caching url: %s", r->unparsed_uri);
/* We are actually caching this response. So it does not
* make sense to remove this entity any more.
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache: Removing CACHE_REMOVE_URL filter.");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00770)
+ "cache: Removing CACHE_REMOVE_URL filter.");
ap_remove_output_filter(cache->remove_url_filter);
/*
@@ -768,6 +1207,9 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* too.
*/
+ /* store away the previously parsed cache control headers */
+ memcpy(&info->control, &control, sizeof(cache_control_t));
+
/* Read the date. Generate one if one is not supplied */
dates = apr_table_get(r->err_headers_out, "Date");
if (dates == NULL) {
@@ -797,15 +1239,13 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
if (lastmod != APR_DATE_BAD && lastmod > date) {
/* if it's in the future, then replace by date */
lastmod = date;
- lastmods = dates;
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
- r->server,
- "cache: Last modified is in the future, "
- "replacing with now");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0,
+ r, APLOGNO(00771) "cache: Last modified is in the future, "
+ "replacing with now");
}
/* if no expiry date then
- * if Cache-Control: max-age present
+ * if Cache-Control: max-age
* expiry date = date + max-age
* else if lastmod
* expiry date = date + min((date - lastmod) * factor, maxexpire)
@@ -813,22 +1253,23 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* expire date = date + defaultexpire
*/
if (exp == APR_DATE_BAD) {
- char *max_age_val;
- if (ap_cache_liststr(r->pool, cc_out, "max-age", &max_age_val) &&
- max_age_val != NULL) {
+ if (control.max_age) {
apr_int64_t x;
errno = 0;
- x = apr_atoi64(max_age_val);
+ x = control.max_age_value;
if (errno) {
- x = conf->defex;
+ x = dconf->defex;
}
else {
x = x * MSEC_ONE_SEC;
}
- if (x > conf->maxex) {
- x = conf->maxex;
+ if (x < dconf->minex) {
+ x = dconf->minex;
+ }
+ if (x > dconf->maxex) {
+ x = dconf->maxex;
}
exp = date + x;
}
@@ -837,15 +1278,18 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* an expiration time of now. This causes some problems with
* freshness calculations, so we choose the else path...
*/
- apr_time_t x = (apr_time_t) ((date - lastmod) * conf->factor);
+ apr_time_t x = (apr_time_t) ((date - lastmod) * dconf->factor);
- if (x > conf->maxex) {
- x = conf->maxex;
+ if (x < dconf->minex) {
+ x = dconf->minex;
+ }
+ if (x > dconf->maxex) {
+ x = dconf->maxex;
}
exp = date + x;
}
else {
- exp = date + conf->defex;
+ exp = date + dconf->defex;
}
}
info->expire = exp;
@@ -865,14 +1309,10 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* err_headers_out and we also need to strip any hop-by-hop
* headers that might have snuck in.
*/
- r->headers_out = apr_table_overlay(r->pool, r->headers_out,
- r->err_headers_out);
- r->headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
- r->server);
- apr_table_clear(r->err_headers_out);
+ r->headers_out = ap_cache_cacheable_headers_out(r);
/* Merge in our cached headers. However, keep any updated values. */
- ap_cache_accept_headers(cache->handle, r, 1);
+ cache_accept_headers(cache->handle, r, 1);
}
/* Write away header information to cache. It is possible that we are
@@ -895,6 +1335,13 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
apr_bucket *bkt;
int status;
+ /* We're just saving response headers, so we are done. Commit
+ * the response at this point, unless there was a previous error.
+ */
+ if (rv == APR_SUCCESS) {
+ rv = cache->provider->commit_entity(cache->handle, r);
+ }
+
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
/* Restore the original request headers and see if we need to
@@ -911,6 +1358,9 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
}
else {
cache->provider->recall_body(cache->handle, r->pool, bb);
+
+ bkt = apr_bucket_eos_create(bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
}
cache->block_response = 1;
@@ -921,58 +1371,63 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* the body it is safe to try and remove the url from the cache.
*/
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
- "cache: updating headers with store_headers failed. "
- "Removing cached url.");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00772)
+ "cache: updating headers with store_headers failed. "
+ "Removing cached url.");
- rv = cache->provider->remove_url(cache->stale_handle, r->pool);
+ rv = cache->provider->remove_url(cache->stale_handle, r);
if (rv != OK) {
- /* Probably a mod_disk_cache cache area has been (re)mounted
+ /* Probably a mod_cache_disk cache area has been (re)mounted
* read-only, or that there is a permissions problem.
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
- "cache: attempt to remove url from cache unsuccessful.");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00773)
+ "cache: attempt to remove url from cache unsuccessful.");
}
+ /* we've got a cache conditional hit! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out,
+ AP_CACHE_REVALIDATE,
+ "conditional cache hit: entity refresh failed");
+
+ }
+ else {
+
+ /* we've got a cache conditional hit! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out,
+ AP_CACHE_REVALIDATE,
+ "conditional cache hit: entity refreshed");
+
}
/* let someone else attempt to cache */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
+ cache_remove_lock(conf, cache, r, NULL);
return ap_pass_brigade(f->next, bb);
}
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
- "cache: store_headers failed");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00774)
+ "cache: store_headers failed");
- ap_remove_output_filter(f);
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
- return ap_pass_brigade(f->next, in);
- }
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
+ "cache miss: store_headers failed");
- rv = cache->provider->store_body(cache->handle, r, in);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
- "cache: store_body failed");
ap_remove_output_filter(f);
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, NULL);
+ cache_remove_lock(conf, cache, r, NULL);
return ap_pass_brigade(f->next, in);
}
- /* proactively remove the lock as soon as we see the eos bucket */
- ap_cache_remove_lock(conf, r, cache->handle ?
- (char *)cache->handle->cache_obj->key : NULL, in);
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, r->headers_out, AP_CACHE_MISS,
+ "cache miss: attempting entity save");
- return ap_pass_brigade(f->next, in);
+ return cache_save_store(f, in, conf, cache);
}
/*
* CACHE_REMOVE_URL filter
- * ---------------
+ * -----------------------
*
* This filter gets added in the quick handler every time the CACHE_SAVE filter
* gets inserted. Its purpose is to remove a confirmed stale cache entry from
@@ -1005,23 +1460,309 @@ static int cache_remove_url_filter(ap_filter_t *f, apr_bucket_brigade *in)
* 1. Remove ourselves
* 2. Do nothing and bail out
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache: CACHE_REMOVE_URL enabled unexpectedly");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00775)
+ "cache: CACHE_REMOVE_URL enabled unexpectedly");
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, in);
}
/* Now remove this cache entry from the cache */
- cache_remove_url(cache, r->pool);
+ cache_remove_url(cache, r);
/* remove ourselves */
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, in);
}
+/*
+ * CACHE filter
+ * ------------
+ *
+ * This filter can be optionally inserted into the filter chain by the admin as
+ * a marker representing the precise location within the filter chain where
+ * caching is to be performed.
+ *
+ * When the filter chain is set up in the non-quick version of the URL handler,
+ * the CACHE filter is replaced by the CACHE_OUT or CACHE_SAVE filter,
+ * effectively inserting the caching filters at the point indicated by the
+ * admin. The CACHE filter is then removed.
+ *
+ * This allows caching to be performed before the content is passed to the
+ * INCLUDES filter, or to a filter that might perform transformations unique
+ * to the specific request and that would otherwise be non-cacheable.
+ */
+static int cache_filter(ap_filter_t *f, apr_bucket_brigade *in)
+{
+
+ cache_server_conf
+ *conf =
+ (cache_server_conf *) ap_get_module_config(f->r->server->module_config,
+ &cache_module);
+
+ /* was the quick handler enabled */
+ if (conf->quick) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(00776)
+ "cache: CACHE filter was added in quick handler mode and "
+ "will be ignored: %s", f->r->unparsed_uri);
+ }
+ /* otherwise we may have been bypassed, nothing to see here */
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(00777)
+ "cache: CACHE filter was added twice, or was added where "
+ "the cache has been bypassed and will be ignored: %s",
+ f->r->unparsed_uri);
+ }
+
+ /* we are just a marker, so let's just remove ourselves */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, in);
+}
+
+/**
+ * If configured, add the status of the caching attempt to the subprocess
+ * environment, and if configured, to headers in the response.
+ *
+ * The status is saved below the broad category of the status (hit, miss,
+ * revalidate), as well as a single cache-status key. This can be used for
+ * conditional logging.
+ *
+ * The status is optionally saved to an X-Cache header, and the detail of
+ * why a particular cache entry was cached (or not cached) is optionally
+ * saved to an X-Cache-Detail header. This extra detail is useful for
+ * service developers who may need to know whether their Cache-Control headers
+ * are working correctly.
+ */
+static int cache_status(cache_handle_t *h, request_rec *r,
+ apr_table_t *headers, ap_cache_status_e status, const char *reason)
+{
+ cache_server_conf
+ *conf =
+ (cache_server_conf *) ap_get_module_config(r->server->module_config,
+ &cache_module);
+
+ cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_module);
+ int x_cache = 0, x_cache_detail = 0;
+
+ switch (status) {
+ case AP_CACHE_HIT: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_HIT_ENV, reason);
+ break;
+ }
+ case AP_CACHE_REVALIDATE: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_REVALIDATE_ENV, reason);
+ break;
+ }
+ case AP_CACHE_MISS: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_MISS_ENV, reason);
+ break;
+ }
+ case AP_CACHE_INVALIDATE: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_INVALIDATE_ENV, reason);
+ break;
+ }
+ }
+
+ apr_table_setn(r->subprocess_env, AP_CACHE_STATUS_ENV, reason);
+
+ if (dconf && dconf->x_cache_set) {
+ x_cache = dconf->x_cache;
+ }
+ else {
+ x_cache = conf->x_cache;
+ }
+ if (x_cache) {
+ apr_table_setn(headers, "X-Cache",
+ apr_psprintf(r->pool, "%s from %s",
+ status == AP_CACHE_HIT ? "HIT" : status
+ == AP_CACHE_REVALIDATE ? "REVALIDATE" : "MISS",
+ r->server->server_hostname));
+ }
+
+ if (dconf && dconf->x_cache_detail_set) {
+ x_cache_detail = dconf->x_cache_detail;
+ }
+ else {
+ x_cache_detail = conf->x_cache_detail;
+ }
+ if (x_cache_detail) {
+ apr_table_setn(headers, "X-Cache-Detail", apr_psprintf(r->pool,
+ "\"%s\" from %s", reason, r->server->server_hostname));
+ }
+
+ return OK;
+}
+
+/**
+ * If an error has occurred, but we have a stale cached entry, restore the
+ * filter stack from the save filter onwards. The canned error message will
+ * be discarded in the process, and replaced with the cached response.
+ */
+static void cache_insert_error_filter(request_rec *r)
+{
+ void *dummy;
+ cache_dir_conf *dconf;
+
+ /* ignore everything except for 5xx errors */
+ if (r->status < HTTP_INTERNAL_SERVER_ERROR) {
+ return;
+ }
+
+ dconf = ap_get_module_config(r->per_dir_config, &cache_module);
+
+ if (!dconf->stale_on_error) {
+ return;
+ }
+
+ /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
+ * If a cache receives a 5xx response while attempting to revalidate an
+ * entry, it MAY either forward this response to the requesting client,
+ * or act as if the server failed to respond. In the latter case, it MAY
+ * return a previously received response unless the cached entry
+ * includes the "must-revalidate" cache-control directive (see section
+ * 14.9).
+ *
+ * This covers the case where the error was generated by our server via
+ * ap_die().
+ */
+ apr_pool_userdata_get(&dummy, CACHE_CTX_KEY, r->pool);
+ if (dummy) {
+ cache_request_rec *cache = (cache_request_rec *) dummy;
+
+ ap_remove_output_filter(cache->remove_url_filter);
+
+ if (cache->stale_handle && cache->save_filter
+ && !cache->stale_handle->cache_obj->info.control.must_revalidate
+ && !cache->stale_handle->cache_obj->info.control.proxy_revalidate) {
+ const char *warn_head;
+ cache_server_conf
+ *conf =
+ (cache_server_conf *) ap_get_module_config(r->server->module_config,
+ &cache_module);
+
+ /* morph the current save filter into the out filter, and serve from
+ * cache.
+ */
+ cache->handle = cache->stale_handle;
+ if (r->main) {
+ cache->save_filter->frec = cache_out_subreq_filter_handle;
+ }
+ else {
+ cache->save_filter->frec = cache_out_filter_handle;
+ }
+
+ r->output_filters = cache->save_filter;
+
+ r->err_headers_out = cache->stale_handle->resp_hdrs;
+
+ /* add a revalidation warning */
+ warn_head = apr_table_get(r->err_headers_out, "Warning");
+ if ((warn_head == NULL) || ((warn_head != NULL)
+ && (ap_strstr_c(warn_head, "111") == NULL))) {
+ apr_table_mergen(r->err_headers_out, "Warning",
+ "111 Revalidation failed");
+ }
+
+ cache_run_cache_status(
+ cache->handle,
+ r,
+ r->err_headers_out,
+ AP_CACHE_HIT,
+ apr_psprintf(
+ r->pool,
+ "cache hit: %d status; stale content returned",
+ r->status));
+
+ /* give someone else the chance to cache the file */
+ cache_remove_lock(conf, cache, r, NULL);
+
+ }
+ }
+
+ return;
+}
+
/* -------------------------------------------------------------- */
/* Setup configurable data */
+static void *create_dir_config(apr_pool_t *p, char *dummy)
+{
+ cache_dir_conf *dconf = apr_pcalloc(p, sizeof(cache_dir_conf));
+
+ dconf->no_last_mod_ignore = 0;
+ dconf->store_expired = 0;
+ dconf->store_private = 0;
+ dconf->store_nostore = 0;
+
+ /* maximum time to cache a document */
+ dconf->maxex = DEFAULT_CACHE_MAXEXPIRE;
+ dconf->minex = DEFAULT_CACHE_MINEXPIRE;
+ /* default time to cache a document */
+ dconf->defex = DEFAULT_CACHE_EXPIRE;
+
+ /* factor used to estimate Expires date from LastModified date */
+ dconf->factor = DEFAULT_CACHE_LMFACTOR;
+
+ dconf->x_cache = DEFAULT_X_CACHE;
+ dconf->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
+
+ dconf->stale_on_error = DEFAULT_CACHE_STALE_ON_ERROR;
+
+ /* array of providers for this URL space */
+ dconf->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
+
+ return dconf;
+}
+
+static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv) {
+ cache_dir_conf *new = (cache_dir_conf *) apr_pcalloc(p, sizeof(cache_dir_conf));
+ cache_dir_conf *add = (cache_dir_conf *) addv;
+ cache_dir_conf *base = (cache_dir_conf *) basev;
+
+ new->no_last_mod_ignore = (add->no_last_mod_ignore_set == 0) ? base->no_last_mod_ignore : add->no_last_mod_ignore;
+ new->no_last_mod_ignore_set = add->no_last_mod_ignore_set || base->no_last_mod_ignore_set;
+
+ new->store_expired = (add->store_expired_set == 0) ? base->store_expired : add->store_expired;
+ new->store_expired_set = add->store_expired_set || base->store_expired_set;
+ new->store_private = (add->store_private_set == 0) ? base->store_private : add->store_private;
+ new->store_private_set = add->store_private_set || base->store_private_set;
+ new->store_nostore = (add->store_nostore_set == 0) ? base->store_nostore : add->store_nostore;
+ new->store_nostore_set = add->store_nostore_set || base->store_nostore_set;
+
+ /* maximum time to cache a document */
+ new->maxex = (add->maxex_set == 0) ? base->maxex : add->maxex;
+ new->maxex_set = add->maxex_set || base->maxex_set;
+ new->minex = (add->minex_set == 0) ? base->minex : add->minex;
+ new->minex_set = add->minex_set || base->minex_set;
+
+ /* default time to cache a document */
+ new->defex = (add->defex_set == 0) ? base->defex : add->defex;
+ new->defex_set = add->defex_set || base->defex_set;
+
+ /* factor used to estimate Expires date from LastModified date */
+ new->factor = (add->factor_set == 0) ? base->factor : add->factor;
+ new->factor_set = add->factor_set || base->factor_set;
+
+ new->x_cache = (add->x_cache_set == 0) ? base->x_cache : add->x_cache;
+ new->x_cache_set = add->x_cache_set || base->x_cache_set;
+ new->x_cache_detail = (add->x_cache_detail_set == 0) ? base->x_cache_detail
+ : add->x_cache_detail;
+ new->x_cache_detail_set = add->x_cache_detail_set
+ || base->x_cache_detail_set;
+
+ new->stale_on_error = (add->stale_on_error_set == 0) ? base->stale_on_error
+ : add->stale_on_error;
+ new->stale_on_error_set = add->stale_on_error_set
+ || base->stale_on_error_set;
+
+ new->cacheenable = add->enable_set ? apr_array_append(p, base->cacheenable,
+ add->cacheenable) : base->cacheenable;
+ new->enable_set = add->enable_set || base->enable_set;
+ new->disable = (add->disable_set == 0) ? base->disable : add->disable;
+ new->disable_set = add->disable_set || base->disable_set;
+
+ return new;
+}
+
static void * create_cache_config(apr_pool_t *p, server_rec *s)
{
const char *tmppath;
@@ -1031,29 +1772,17 @@ static void * create_cache_config(apr_pool_t *p, server_rec *s)
ps->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
/* array of URL prefixes for which caching is disabled */
ps->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
- /* maximum time to cache a document */
- ps->maxex = DEFAULT_CACHE_MAXEXPIRE;
- ps->maxex_set = 0;
- /* default time to cache a document */
- ps->defex = DEFAULT_CACHE_EXPIRE;
- ps->defex_set = 0;
- /* factor used to estimate Expires date from LastModified date */
- ps->factor = DEFAULT_CACHE_LMFACTOR;
- ps->factor_set = 0;
- ps->no_last_mod_ignore_set = 0;
- ps->no_last_mod_ignore = 0;
ps->ignorecachecontrol = 0;
ps->ignorecachecontrol_set = 0;
- ps->store_private = 0;
- ps->store_private_set = 0;
- ps->store_nostore = 0;
- ps->store_nostore_set = 0;
/* array of headers that should not be stored in cache */
ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET;
/* flag indicating that query-string should be ignored when caching */
ps->ignorequerystring = 0;
ps->ignorequerystring_set = 0;
+ /* by default, run in the quick handler */
+ ps->quick = 1;
+ ps->quick_set = 0;
/* array of identifiers that should not be used for key calculation */
ps->ignore_session_id = apr_array_make(p, 10, sizeof(char *));
ps->ignore_session_id_set = CACHE_IGNORE_SESSION_ID_UNSET;
@@ -1064,6 +1793,8 @@ static void * create_cache_config(apr_pool_t *p, server_rec *s)
ps->lockpath = apr_pstrcat(p, tmppath, DEFAULT_CACHE_LOCKPATH, NULL);
}
ps->lockmaxage = apr_time_from_sec(DEFAULT_CACHE_MAXAGE);
+ ps->x_cache = DEFAULT_X_CACHE;
+ ps->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
return ps;
}
@@ -1081,30 +1812,11 @@ static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
ps->cacheenable = apr_array_append(p,
base->cacheenable,
overrides->cacheenable);
- /* maximum time to cache a document */
- ps->maxex = (overrides->maxex_set == 0) ? base->maxex : overrides->maxex;
- /* default time to cache a document */
- ps->defex = (overrides->defex_set == 0) ? base->defex : overrides->defex;
- /* factor used to estimate Expires date from LastModified date */
- ps->factor =
- (overrides->factor_set == 0) ? base->factor : overrides->factor;
- ps->no_last_mod_ignore =
- (overrides->no_last_mod_ignore_set == 0)
- ? base->no_last_mod_ignore
- : overrides->no_last_mod_ignore;
ps->ignorecachecontrol =
(overrides->ignorecachecontrol_set == 0)
? base->ignorecachecontrol
: overrides->ignorecachecontrol;
- ps->store_private =
- (overrides->store_private_set == 0)
- ? base->store_private
- : overrides->store_private;
- ps->store_nostore =
- (overrides->store_nostore_set == 0)
- ? base->store_nostore
- : overrides->store_nostore;
ps->ignore_headers =
(overrides->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET)
? base->ignore_headers
@@ -1129,18 +1841,47 @@ static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
(overrides->lockmaxage_set == 0)
? base->lockmaxage
: overrides->lockmaxage;
+ ps->quick =
+ (overrides->quick_set == 0)
+ ? base->quick
+ : overrides->quick;
+ ps->x_cache =
+ (overrides->x_cache_set == 0)
+ ? base->x_cache
+ : overrides->x_cache;
+ ps->x_cache_detail =
+ (overrides->x_cache_detail_set == 0)
+ ? base->x_cache_detail
+ : overrides->x_cache_detail;
+ ps->base_uri =
+ (overrides->base_uri_set == 0)
+ ? base->base_uri
+ : overrides->base_uri;
return ps;
}
-static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
- int flag)
+
+static const char *set_cache_quick_handler(cmd_parms *parms, void *dummy,
+ int flag)
{
cache_server_conf *conf;
conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config
+,
&cache_module);
- conf->no_last_mod_ignore = flag;
- conf->no_last_mod_ignore_set = 1;
+ conf->quick = flag;
+ conf->quick_set = 1;
+ return NULL;
+
+}
+
+static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
+ int flag)
+{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->no_last_mod_ignore = flag;
+ dconf->no_last_mod_ignore_set = 1;
return NULL;
}
@@ -1158,29 +1899,33 @@ static const char *set_cache_ignore_cachecontrol(cmd_parms *parms,
return NULL;
}
+static const char *set_cache_store_expired(cmd_parms *parms, void *dummy,
+ int flag)
+{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->store_expired = flag;
+ dconf->store_expired_set = 1;
+ return NULL;
+}
+
static const char *set_cache_store_private(cmd_parms *parms, void *dummy,
int flag)
{
- cache_server_conf *conf;
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
- conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
- &cache_module);
- conf->store_private = flag;
- conf->store_private_set = 1;
+ dconf->store_private = flag;
+ dconf->store_private_set = 1;
return NULL;
}
static const char *set_cache_store_nostore(cmd_parms *parms, void *dummy,
int flag)
{
- cache_server_conf *conf;
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
- conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
- &cache_module);
- conf->store_nostore = flag;
- conf->store_nostore_set = 1;
+ dconf->store_nostore = flag;
+ dconf->store_nostore_set = 1;
return NULL;
}
@@ -1193,7 +1938,7 @@ static const char *add_ignore_header(cmd_parms *parms, void *dummy,
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module);
- if (!strncasecmp(header, "None", 4)) {
+ if (!strcasecmp(header, "None")) {
/* if header None is listed clear array */
conf->ignore_headers->nelts = 0;
}
@@ -1221,7 +1966,7 @@ static const char *add_ignore_session_id(cmd_parms *parms, void *dummy,
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module);
- if (!strncasecmp(identifier, "None", 4)) {
+ if (!strcasecmp(identifier, "None")) {
/* if identifier None is listed clear array */
conf->ignore_session_id->nelts = 0;
}
@@ -1244,19 +1989,46 @@ static const char *add_cache_enable(cmd_parms *parms, void *dummy,
const char *type,
const char *url)
{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
cache_server_conf *conf;
struct cache_enable *new;
+ const char *err = ap_check_cmd_context(parms,
+ NOT_IN_DIRECTORY|NOT_IN_LIMIT|NOT_IN_FILES);
+ if (err != NULL) {
+ return err;
+ }
+
if (*type == '/') {
return apr_psprintf(parms->pool,
"provider (%s) starts with a '/'. Are url and provider switched?",
type);
}
+ if (!url) {
+ url = parms->path;
+ }
+ if (!url) {
+ return apr_psprintf(parms->pool,
+ "CacheEnable provider (%s) is missing an URL.", type);
+ }
+ if (parms->path && strncmp(parms->path, url, strlen(parms->path))) {
+ return "When in a Location, CacheEnable must specify a path or an URL below "
+ "that location.";
+ }
+
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module);
- new = apr_array_push(conf->cacheenable);
+
+ if (parms->path) {
+ new = apr_array_push(dconf->cacheenable);
+ dconf->enable_set = 1;
+ }
+ else {
+ new = apr_array_push(conf->cacheenable);
+ }
+
new->type = type;
if (apr_uri_parse(parms->pool, url, &(new->url))) {
return NULL;
@@ -1273,12 +2045,35 @@ static const char *add_cache_enable(cmd_parms *parms, void *dummy,
static const char *add_cache_disable(cmd_parms *parms, void *dummy,
const char *url)
{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
cache_server_conf *conf;
struct cache_disable *new;
+ const char *err = ap_check_cmd_context(parms,
+ NOT_IN_DIRECTORY|NOT_IN_LIMIT|NOT_IN_FILES);
+ if (err != NULL) {
+ return err;
+ }
+
conf =
(cache_server_conf *)ap_get_module_config(parms->server->module_config,
&cache_module);
+
+ if (parms->path) {
+ if (!strcmp(url, "on")) {
+ dconf->disable = 1;
+ dconf->disable_set = 1;
+ return NULL;
+ }
+ else {
+ return "CacheDisable must be followed by the word 'on' when in a Location.";
+ }
+ }
+
+ if (!url || (url[0] != '/' && !ap_strchr_c(url, ':'))) {
+ return "CacheDisable must specify a path or an URL.";
+ }
+
new = apr_array_push(conf->cachedisable);
if (apr_uri_parse(parms->pool, url, &(new->url))) {
return NULL;
@@ -1295,43 +2090,44 @@ static const char *add_cache_disable(cmd_parms *parms, void *dummy,
static const char *set_cache_maxex(cmd_parms *parms, void *dummy,
const char *arg)
{
- cache_server_conf *conf;
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
- conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
- &cache_module);
- conf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
- conf->maxex_set = 1;
+ dconf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
+ dconf->maxex_set = 1;
+ return NULL;
+}
+
+static const char *set_cache_minex(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->minex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
+ dconf->minex_set = 1;
return NULL;
}
static const char *set_cache_defex(cmd_parms *parms, void *dummy,
const char *arg)
{
- cache_server_conf *conf;
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
- conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
- &cache_module);
- conf->defex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
- conf->defex_set = 1;
+ dconf->defex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
+ dconf->defex_set = 1;
return NULL;
}
static const char *set_cache_factor(cmd_parms *parms, void *dummy,
const char *arg)
{
- cache_server_conf *conf;
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
double val;
- conf =
- (cache_server_conf *)ap_get_module_config(parms->server->module_config,
- &cache_module);
if (sscanf(arg, "%lg", &val) != 1) {
return "CacheLastModifiedFactor value must be a float";
}
- conf->factor = val;
- conf->factor_set = 1;
+ dconf->factor = val;
+ dconf->factor_set = 1;
return NULL;
}
@@ -1397,6 +2193,84 @@ static const char *set_cache_lock_maxage(cmd_parms *parms, void *dummy,
return NULL;
}
+static const char *set_cache_x_cache(cmd_parms *parms, void *dummy, int flag)
+{
+
+ if (parms->path) {
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->x_cache = flag;
+ dconf->x_cache_set = 1;
+
+ }
+ else {
+ cache_server_conf *conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+
+ conf->x_cache = flag;
+ conf->x_cache_set = 1;
+
+ }
+
+ return NULL;
+}
+
+static const char *set_cache_x_cache_detail(cmd_parms *parms, void *dummy, int flag)
+{
+
+ if (parms->path) {
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->x_cache_detail = flag;
+ dconf->x_cache_detail_set = 1;
+
+ }
+ else {
+ cache_server_conf *conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+
+ conf->x_cache_detail = flag;
+ conf->x_cache_detail_set = 1;
+
+ }
+
+ return NULL;
+}
+
+static const char *set_cache_key_base_url(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ cache_server_conf *conf;
+ apr_status_t rv;
+
+ conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+ conf->base_uri = apr_pcalloc(parms->pool, sizeof(apr_uri_t));
+ rv = apr_uri_parse(parms->pool, arg, conf->base_uri);
+ if (rv != APR_SUCCESS) {
+ return apr_psprintf(parms->pool, "Could not parse '%s' as an URL.", arg);
+ }
+ else if (!conf->base_uri->scheme && !conf->base_uri->hostname &&
+ !conf->base_uri->port_str) {
+ return apr_psprintf(parms->pool, "URL '%s' must contain at least one of a scheme, a hostname or a port.", arg);
+ }
+ conf->base_uri_set = 1;
+ return NULL;
+}
+
+static const char *set_cache_stale_on_error(cmd_parms *parms, void *dummy,
+ int flag)
+{
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->stale_on_error = flag;
+ dconf->stale_on_error_set = 1;
+ return NULL;
+}
+
static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
@@ -1422,26 +2296,35 @@ static const command_rec cache_cmds[] =
* This is more intuitive that requiring a LoadModule directive.
*/
- AP_INIT_TAKE2("CacheEnable", add_cache_enable, NULL, RSRC_CONF,
- "A cache type and partial URL prefix below which "
- "caching is enabled"),
- AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF,
+ AP_INIT_TAKE12("CacheEnable", add_cache_enable, NULL, RSRC_CONF|ACCESS_CONF,
+ "A cache type and partial URL prefix below which "
+ "caching is enabled"),
+ AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF|ACCESS_CONF,
"A partial URL prefix below which caching is disabled"),
- AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF|ACCESS_CONF,
"The maximum time in seconds to cache a document"),
- AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("CacheMinExpire", set_cache_minex, NULL, RSRC_CONF|ACCESS_CONF,
+ "The minimum time in seconds to cache a document"),
+ AP_INIT_TAKE1("CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF|ACCESS_CONF,
"The default time in seconds to cache a document"),
- AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL,
+ AP_INIT_FLAG("CacheQuickHandler", set_cache_quick_handler, NULL,
RSRC_CONF,
+ "Run the cache in the quick handler, default on"),
+ AP_INIT_FLAG("CacheIgnoreNoLastMod", set_cache_ignore_no_last_mod, NULL,
+ RSRC_CONF|ACCESS_CONF,
"Ignore Responses where there is no Last Modified Header"),
AP_INIT_FLAG("CacheIgnoreCacheControl", set_cache_ignore_cachecontrol,
NULL, RSRC_CONF,
"Ignore requests from the client for uncached content"),
+ AP_INIT_FLAG("CacheStoreExpired", set_cache_store_expired,
+ NULL, RSRC_CONF|ACCESS_CONF,
+ "Ignore expiration dates when populating cache, resulting in "
+ "an If-Modified-Since request to the backend on retrieval"),
AP_INIT_FLAG("CacheStorePrivate", set_cache_store_private,
- NULL, RSRC_CONF,
+ NULL, RSRC_CONF|ACCESS_CONF,
"Ignore 'Cache-Control: private' and store private content"),
AP_INIT_FLAG("CacheStoreNoStore", set_cache_store_nostore,
- NULL, RSRC_CONF,
+ NULL, RSRC_CONF|ACCESS_CONF,
"Ignore 'Cache-Control: no-store' and store sensitive content"),
AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF,
"A space separated list of headers that should not be "
@@ -1453,7 +2336,7 @@ static const command_rec cache_cmds[] =
NULL, RSRC_CONF, "A space separated list of session "
"identifiers that should be ignored for creating the key "
"of the cached entity."),
- AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF|ACCESS_CONF,
"The factor used to estimate Expires date from "
"LastModified date"),
AP_INIT_FLAG("CacheLock", set_cache_lock,
@@ -1465,14 +2348,30 @@ static const command_rec cache_cmds[] =
"temp directory."),
AP_INIT_TAKE1("CacheLockMaxAge", set_cache_lock_maxage, NULL, RSRC_CONF,
"Maximum age of any thundering herd lock."),
+ AP_INIT_FLAG("CacheHeader", set_cache_x_cache, NULL, RSRC_CONF | ACCESS_CONF,
+ "Add a X-Cache header to responses. Default is off."),
+ AP_INIT_FLAG("CacheDetailHeader", set_cache_x_cache_detail, NULL,
+ RSRC_CONF | ACCESS_CONF,
+ "Add a X-Cache-Detail header to responses. Default is off."),
+ AP_INIT_TAKE1("CacheKeyBaseURL", set_cache_key_base_url, NULL, RSRC_CONF,
+ "Override the base URL of reverse proxied cache keys."),
+ AP_INIT_FLAG("CacheStaleOnError", set_cache_stale_on_error,
+ NULL, RSRC_CONF|ACCESS_CONF,
+ "Serve stale content on 5xx errors if present. Defaults to on."),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
/* cache initializer */
+ /* cache quick handler */
+ ap_hook_quick_handler(cache_quick_handler, NULL, NULL, APR_HOOK_FIRST);
/* cache handler */
- ap_hook_quick_handler(cache_url_handler, NULL, NULL, APR_HOOK_FIRST);
+ ap_hook_handler(cache_handler, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ /* cache status */
+ cache_hook_cache_status(cache_status, NULL, NULL, APR_HOOK_MIDDLE);
+ /* cache error handler */
+ ap_hook_insert_error_filter(cache_insert_error_filter, NULL, NULL, APR_HOOK_MIDDLE);
/* cache filters
* XXX The cache filters need to run right after the handlers and before
* any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
@@ -1486,9 +2385,21 @@ static void register_hooks(apr_pool_t *p)
* to be run by subrequest
*/
/*
+ * CACHE is placed into the filter chain at an admin specified location,
+ * and when the cache_handler is run, the CACHE filter is swapped with
+ * the CACHE_OUT filter, or CACHE_SAVE filter as appropriate. This has
+ * the effect of offering optional fine control of where the cache is
+ * inserted into the filter chain.
+ */
+ cache_filter_handle =
+ ap_register_output_filter("CACHE",
+ cache_filter,
+ NULL,
+ AP_FTYPE_RESOURCE);
+ /*
* CACHE_SAVE must go into the filter chain after a possible DEFLATE
* filter to ensure that the compressed content is stored.
- * Incrementing filter type by 1 ensures his happens.
+ * Incrementing filter type by 1 ensures this happens.
*/
cache_save_filter_handle =
ap_register_output_filter("CACHE_SAVE",
@@ -1509,7 +2420,7 @@ static void register_hooks(apr_pool_t *p)
* CACHE_OUT must go into the filter chain after a possible DEFLATE
* filter to ensure that already compressed cache objects do not
* get compressed again. Incrementing filter type by 1 ensures
- * his happens.
+ * this happens.
*/
cache_out_filter_handle =
ap_register_output_filter("CACHE_OUT",
@@ -1538,13 +2449,23 @@ static void register_hooks(apr_pool_t *p)
ap_hook_post_config(cache_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
}
-module AP_MODULE_DECLARE_DATA cache_module =
+AP_DECLARE_MODULE(cache) =
{
STANDARD20_MODULE_STUFF,
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
+ create_dir_config, /* create per-directory config structure */
+ merge_dir_config, /* merge per-directory config structures */
create_cache_config, /* create per-server config structure */
merge_cache_config, /* merge per-server config structures */
cache_cmds, /* command apr_table_t */
register_hooks
};
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(cache_status)
+)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, cache_status,
+ (cache_handle_t *h, request_rec *r,
+ apr_table_t *headers, ap_cache_status_e status,
+ const char *reason), (h, r, headers, status, reason),
+ OK, DECLINED)
diff --git a/modules/cache/mod_cache.dep b/modules/cache/mod_cache.dep
deleted file mode 100644
index 901bd4b0..00000000
--- a/modules/cache/mod_cache.dep
+++ /dev/null
@@ -1,105 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by mod_cache.mak
-
-.\cache_storage.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\mod_cache.h"\
-
-
-.\cache_util.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_provider.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\mod_cache.h"\
-
-
-.\mod_cache.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\mod_cache.h"\
-
-
-..\..\build\win32\httpd.rc : \
- "..\..\include\ap_release.h"\
-
diff --git a/modules/cache/mod_cache.h b/modules/cache/mod_cache.h
index e20fa72e..53e80c03 100644
--- a/modules/cache/mod_cache.h
+++ b/modules/cache/mod_cache.h
@@ -26,70 +26,12 @@
#ifndef MOD_CACHE_H
#define MOD_CACHE_H
-#define CORE_PRIVATE
-
-#include "apr_hooks.h"
-#include "apr.h"
-#include "apr_lib.h"
-#include "apr_strings.h"
-#include "apr_buckets.h"
-#include "apr_md5.h"
-#include "apr_pools.h"
-#include "apr_strings.h"
-#include "apr_optional.h"
-#define APR_WANT_STRFUNC
-#include "apr_want.h"
-
#include "httpd.h"
-#include "http_config.h"
-#include "ap_config.h"
-#include "http_core.h"
-#include "http_protocol.h"
-#include "http_request.h"
-#include "http_vhost.h"
-#include "http_main.h"
-#include "http_log.h"
-#include "http_connection.h"
-#include "util_filter.h"
#include "apr_date.h"
-#include "apr_uri.h"
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#include "apr_atomic.h"
-
-#ifndef MAX
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#endif
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
+#include "apr_optional.h"
+#include "apr_hooks.h"
-#define MSEC_ONE_DAY ((apr_time_t)(86400*APR_USEC_PER_SEC)) /* one day, in microseconds */
-#define MSEC_ONE_HR ((apr_time_t)(3600*APR_USEC_PER_SEC)) /* one hour, in microseconds */
-#define MSEC_ONE_MIN ((apr_time_t)(60*APR_USEC_PER_SEC)) /* one minute, in microseconds */
-#define MSEC_ONE_SEC ((apr_time_t)(APR_USEC_PER_SEC)) /* one second, in microseconds */
-#define DEFAULT_CACHE_MAXEXPIRE MSEC_ONE_DAY
-#define DEFAULT_CACHE_EXPIRE MSEC_ONE_HR
-#define DEFAULT_CACHE_LMFACTOR (0.1)
-#define DEFAULT_CACHE_MAXAGE 5
-#define DEFAULT_CACHE_LOCKPATH "/mod_cache-lock"
-#define CACHE_LOCKNAME_KEY "mod_cache-lockname"
-#define CACHE_LOCKFILE_KEY "mod_cache-lockfile"
+#include "cache_common.h"
/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and
* CACHE_DECLARE_DATA with appropriate export and import tags for the platform
@@ -112,84 +54,30 @@
#define CACHE_DECLARE_DATA __declspec(dllimport)
#endif
-struct cache_enable {
- apr_uri_t url;
- const char *type;
- apr_size_t pathlen;
-};
-
-struct cache_disable {
- apr_uri_t url;
- apr_size_t pathlen;
-};
-
-/* static information about the local cache */
-typedef struct {
- apr_array_header_t *cacheenable; /* URLs to cache */
- apr_array_header_t *cachedisable; /* URLs not to cache */
- /* Maximum time to keep cached files in msecs */
- apr_time_t maxex;
- int maxex_set;
- /* default time to keep cached file in msecs */
- apr_time_t defex;
- int defex_set;
- /* factor for estimating expires date */
- double factor;
- int factor_set;
- /** ignore the last-modified header when deciding to cache this request */
- int no_last_mod_ignore_set;
- int no_last_mod_ignore;
- /** ignore client's requests for uncached responses */
- int ignorecachecontrol;
- int ignorecachecontrol_set;
- /** ignore Cache-Control: private header from server */
- int store_private;
- int store_private_set;
- /** ignore Cache-Control: no-store header from client or server */
- int store_nostore;
- int store_nostore_set;
- /** store the headers that should not be stored in the cache */
- apr_array_header_t *ignore_headers;
- /* flag if CacheIgnoreHeader has been set */
- #define CACHE_IGNORE_HEADERS_SET 1
- #define CACHE_IGNORE_HEADERS_UNSET 0
- int ignore_headers_set;
- /** ignore query-string when caching */
- int ignorequerystring;
- int ignorequerystring_set;
- /** store the identifiers that should not be used for key calculation */
- apr_array_header_t *ignore_session_id;
- /* flag if CacheIgnoreURLSessionIdentifiers has been set */
- #define CACHE_IGNORE_SESSION_ID_SET 1
- #define CACHE_IGNORE_SESSION_ID_UNSET 0
- int ignore_session_id_set;
- /* thundering herd lock */
- int lock;
- int lock_set;
- const char *lockpath;
- int lockpath_set;
- apr_time_t lockmaxage;
- int lockmaxage_set;
-} cache_server_conf;
-
/* cache info information */
typedef struct cache_info cache_info;
struct cache_info {
- int status;
+ /**
+ * the original time corresponding to the 'Date:' header of the request
+ * served
+ */
apr_time_t date;
+ /** a time when the cached entity is due to expire */
apr_time_t expire;
+ /** r->request_time from the same request */
apr_time_t request_time;
+ /** apr_time_now() at the time the entity was actually cached */
apr_time_t response_time;
+ /**
+ * HTTP status code of the cached entity. Though not necessarily the
+ * status code finally issued to the request.
+ */
+ int status;
+ /* cached cache-control */
+ cache_control_t control;
};
/* cache handle information */
-
-/* XXX TODO On the next structure change/MMN bump,
- * count must become an apr_off_t, representing
- * the potential size of disk cached objects.
- * Then dig for
- * "XXX Bad Temporary Cast - see cache_object_t notes"
- */
typedef struct cache_object cache_object_t;
struct cache_object {
const char *key;
@@ -197,10 +85,6 @@ struct cache_object {
cache_info info;
/* Opaque portion (specific to the implementation) of the cache object */
void *vobj;
- /* FIXME: These are only required for mod_mem_cache. */
- apr_size_t count; /* Number of body bytes written to the cache so far */
- int complete;
- apr_uint32_t refcount; /* refcount and bit flag to cleanup object */
};
typedef struct cache_handle cache_handle_t;
@@ -215,46 +99,31 @@ struct cache_handle {
typedef struct {
int (*remove_entity) (cache_handle_t *h);
apr_status_t (*store_headers)(cache_handle_t *h, request_rec *r, cache_info *i);
- apr_status_t (*store_body)(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
+ apr_status_t (*store_body)(cache_handle_t *h, request_rec *r, apr_bucket_brigade *in,
+ apr_bucket_brigade *out);
apr_status_t (*recall_headers) (cache_handle_t *h, request_rec *r);
apr_status_t (*recall_body) (cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
int (*create_entity) (cache_handle_t *h, request_rec *r,
- const char *urlkey, apr_off_t len);
+ const char *urlkey, apr_off_t len, apr_bucket_brigade *bb);
int (*open_entity) (cache_handle_t *h, request_rec *r,
const char *urlkey);
- int (*remove_url) (cache_handle_t *h, apr_pool_t *p);
+ int (*remove_url) (cache_handle_t *h, request_rec *r);
+ apr_status_t (*commit_entity)(cache_handle_t *h, request_rec *r);
+ apr_status_t (*invalidate_entity)(cache_handle_t *h, request_rec *r);
} cache_provider;
-/* A linked-list of authn providers. */
-typedef struct cache_provider_list cache_provider_list;
+typedef enum {
+ AP_CACHE_HIT,
+ AP_CACHE_REVALIDATE,
+ AP_CACHE_MISS,
+ AP_CACHE_INVALIDATE
+} ap_cache_status_e;
-struct cache_provider_list {
- const char *provider_name;
- const cache_provider *provider;
- cache_provider_list *next;
-};
-
-/* per request cache information */
-typedef struct {
- cache_provider_list *providers; /* possible cache providers */
- const cache_provider *provider; /* current cache provider */
- const char *provider_name; /* current cache provider name */
- int fresh; /* is the entitey fresh? */
- cache_handle_t *handle; /* current cache handle */
- cache_handle_t *stale_handle; /* stale cache handle */
- apr_table_t *stale_headers; /* original request headers. */
- int in_checked; /* CACHE_SAVE must cache the entity */
- int block_response; /* CACHE_SAVE must block response. */
- apr_bucket_brigade *saved_brigade; /* copy of partial response */
- apr_off_t saved_size; /* length of saved_brigade */
- apr_time_t exp; /* expiration */
- apr_time_t lastmod; /* last-modified time */
- cache_info *info; /* current cache info */
- ap_filter_t *remove_url_filter; /* Enable us to remove the filter */
- char *key; /* The cache key created for this
- * request
- */
-} cache_request_rec;
+#define AP_CACHE_HIT_ENV "cache-hit"
+#define AP_CACHE_REVALIDATE_ENV "cache-revalidate"
+#define AP_CACHE_MISS_ENV "cache-miss"
+#define AP_CACHE_INVALIDATE_ENV "cache-invalidate"
+#define AP_CACHE_STATUS_ENV "cache-status"
/* cache_util.c */
@@ -262,136 +131,61 @@ typedef struct {
CACHE_DECLARE(apr_time_t) ap_cache_current_age(cache_info *info, const apr_time_t age_value,
apr_time_t now);
-/**
- * Check the freshness of the cache object per RFC2616 section 13.2 (Expiration Model)
- * @param h cache_handle_t
- * @param r request_rec
- * @return 0 ==> cache object is stale, 1 ==> cache object is fresh
- */
-CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h, request_rec *r);
-
-/**
- * Check the whether the request allows a cached object to be served as per RFC2616
- * section 14.9.4 (Cache Revalidation and Reload Controls)
- * @param h cache_handle_t
- * @param r request_rec
- * @return 0 ==> cache object may not be served, 1 ==> cache object may be served
- */
-CACHE_DECLARE(int) ap_cache_check_allowed(request_rec *r);
-
-/**
- * Try obtain a cache wide lock on the given cache key.
- *
- * If we return APR_SUCCESS, we obtained the lock, and we are clear to
- * proceed to the backend. If we return APR_EEXISTS, the the lock is
- * already locked, someone else has gone to refresh the backend data
- * already, so we must return stale data with a warning in the mean
- * time. If we return anything else, then something has gone pear
- * shaped, and we allow the request through to the backend regardless.
- *
- * This lock is created from the request pool, meaning that should
- * something go wrong and the lock isn't deleted on return of the
- * request headers from the backend for whatever reason, at worst the
- * lock will be cleaned up when the request is dies or finishes.
- *
- * If something goes truly bananas and the lock isn't deleted when the
- * request dies, the lock will be trashed when its max-age is reached,
- * or when a request arrives containing a Cache-Control: no-cache. At
- * no point is it possible for this lock to permanently deny access to
- * the backend.
- */
-CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
- request_rec *r, char *key);
-
-/**
- * Remove the cache lock, if present.
- *
- * First, try to close the file handle, whose delete-on-close should
- * kill the file. Otherwise, just delete the file by name.
- *
- * If no lock name has yet been calculated, do the calculation of the
- * lock name first before trying to delete the file.
- *
- * If an optional bucket brigade is passed, the lock will only be
- * removed if the bucket brigade contains an EOS bucket.
- */
-CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
- request_rec *r, char *key, apr_bucket_brigade *bb);
-
-/**
- * Merge in cached headers into the response
- * @param h cache_handle_t
- * @param r request_rec
- * @param preserve_orig If 1, the values in r->headers_out are preserved.
- * Otherwise, they are overwritten by the cached value.
- */
-CACHE_DECLARE(void) ap_cache_accept_headers(cache_handle_t *h, request_rec *r,
- int preserve_orig);
-
CACHE_DECLARE(apr_time_t) ap_cache_hex2usec(const char *x);
CACHE_DECLARE(void) ap_cache_usec2hex(apr_time_t j, char *y);
-CACHE_DECLARE(char *) ap_cache_generate_name(apr_pool_t *p, int dirlevels,
- int dirlength,
+CACHE_DECLARE(char *) ap_cache_generate_name(apr_pool_t *p, int dirlevels,
+ int dirlength,
const char *name);
-CACHE_DECLARE(cache_provider_list *)ap_cache_get_providers(request_rec *r, cache_server_conf *conf, apr_uri_t uri);
-CACHE_DECLARE(int) ap_cache_liststr(apr_pool_t *p, const char *list,
- const char *key, char **val);
CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str);
-/* Create a new table consisting of those elements from a request_rec's
- * headers_out that are allowed to be stored in a cache
+/* Create a new table consisting of those elements from an
+ * headers table that are allowed to be stored in a cache.
*/
-CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers(apr_pool_t *pool,
apr_table_t *t,
server_rec *s);
-/**
- * cache_storage.c
+/* Create a new table consisting of those elements from an input
+ * headers table that are allowed to be stored in a cache.
*/
-int cache_remove_url(cache_request_rec *cache, apr_pool_t *p);
-int cache_create_entity(request_rec *r, apr_off_t size);
-int cache_select(request_rec *r);
-apr_status_t cache_generate_key_default( request_rec *r, apr_pool_t*p, char**key );
-/**
- * create a key for the cache based on the request record
- * this is the 'default' version, which can be overridden by a default function
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_in(request_rec *r);
+
+/* Create a new table consisting of those elements from an output
+ * headers table that are allowed to be stored in a cache;
+ * ensure there is a content type and capture any errors.
*/
-const char* cache_create_key( request_rec*r );
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_headers_out(request_rec *r);
-/*
-apr_status_t cache_store_entity_headers(cache_handle_t *h, request_rec *r, cache_info *info);
-apr_status_t cache_store_entity_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *bb);
+/**
+ * Parse the Cache-Control and Pragma headers in one go, marking
+ * which tokens appear within the header. Populate the structure
+ * passed in.
+ */
+int ap_cache_control(request_rec *r, cache_control_t *cc, const char *cc_header,
+ const char *pragma_header, apr_table_t *headers);
-apr_status_t cache_recall_entity_headers(cache_handle_t *h, request_rec *r);
-apr_status_t cache_recall_entity_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
-*/
/* hooks */
-/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and
- * CACHE_DECLARE_DATA with appropriate export and import tags for the platform
+/**
+ * Cache status hook.
+ * This hook is called as soon as the cache has made a decision as to whether
+ * an entity should be served from cache (hit), should be served from cache
+ * after a successful validation (revalidate), or served from the backend
+ * and potentially cached (miss).
+ *
+ * A basic implementation of this hook exists in mod_cache which writes this
+ * information to the subprocess environment, and optionally to request
+ * headers. Further implementations may add hooks as appropriate to perform
+ * more advanced processing, or to store statistics about the cache behaviour.
*/
-#if !defined(WIN32)
-#define CACHE_DECLARE(type) type
-#define CACHE_DECLARE_NONSTD(type) type
-#define CACHE_DECLARE_DATA
-#elif defined(CACHE_DECLARE_STATIC)
-#define CACHE_DECLARE(type) type __stdcall
-#define CACHE_DECLARE_NONSTD(type) type
-#define CACHE_DECLARE_DATA
-#elif defined(CACHE_DECLARE_EXPORT)
-#define CACHE_DECLARE(type) __declspec(dllexport) type __stdcall
-#define CACHE_DECLARE_NONSTD(type) __declspec(dllexport) type
-#define CACHE_DECLARE_DATA __declspec(dllexport)
-#else
-#define CACHE_DECLARE(type) __declspec(dllimport) type __stdcall
-#define CACHE_DECLARE_NONSTD(type) __declspec(dllimport) type
-#define CACHE_DECLARE_DATA __declspec(dllimport)
-#endif
+APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, cache_status, (cache_handle_t *h,
+ request_rec *r, apr_table_t *headers, ap_cache_status_e status,
+ const char *reason))
APR_DECLARE_OPTIONAL_FN(apr_status_t,
ap_cache_generate_key,
- (request_rec *r, apr_pool_t*p, char**key ));
+ (request_rec *r, apr_pool_t*p, const char **key));
#endif /*MOD_CACHE_H*/
diff --git a/modules/cache/mod_cache.imp b/modules/cache/mod_cache.imp
deleted file mode 100644
index c63910a4..00000000
--- a/modules/cache/mod_cache.imp
+++ /dev/null
@@ -1,9 +0,0 @@
- (MODCACHE)
- ap_cache_get_providers,
- ap_cache_liststr,
- ap_cache_tokstr,
- ap_cache_hex2usec,
- ap_cache_usec2hex,
- ap_cache_cacheable_hdrs_out,
- ap_cache_generate_name
-
diff --git a/modules/cache/mod_cache.mak b/modules/cache/mod_cache.mak
deleted file mode 100644
index 8eb01120..00000000
--- a/modules/cache/mod_cache.mak
+++ /dev/null
@@ -1,370 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on mod_cache.dsp
-!IF "$(CFG)" == ""
-CFG=mod_cache - Win32 Debug
-!MESSAGE No configuration specified. Defaulting to mod_cache - Win32 Debug.
-!ENDIF
-
-!IF "$(CFG)" != "mod_cache - Win32 Release" && "$(CFG)" != "mod_cache - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "mod_cache.mak" CFG="mod_cache - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "mod_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-!IF "$(CFG)" == "mod_cache - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\cache_storage.obj"
- -@erase "$(INTDIR)\cache_util.obj"
- -@erase "$(INTDIR)\mod_cache.obj"
- -@erase "$(INTDIR)\mod_cache.res"
- -@erase "$(INTDIR)\mod_cache_src.idb"
- -@erase "$(INTDIR)\mod_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_cache.exp"
- -@erase "$(OUTDIR)\mod_cache.lib"
- -@erase "$(OUTDIR)\mod_cache.pdb"
- -@erase "$(OUTDIR)\mod_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "CACHE_DECLARE_EXPORT" /D "MOD_CACHE_EXPORTS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_cache_src" /FD /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_cache.so" /d LONG_NAME="cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_cache.pdb" /debug /out:"$(OUTDIR)\mod_cache.so" /implib:"$(OUTDIR)\mod_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_cache.so /opt:ref
-LINK32_OBJS= \
- "$(INTDIR)\cache_storage.obj" \
- "$(INTDIR)\cache_util.obj" \
- "$(INTDIR)\mod_cache.obj" \
- "$(INTDIR)\mod_cache.res" \
- "..\..\srclib\apr\Release\libapr-1.lib" \
- "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
- "..\..\Release\libhttpd.lib"
-
-"$(OUTDIR)\mod_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Release\mod_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_cache.so"
- if exist .\Release\mod_cache.so.manifest mt.exe -manifest .\Release\mod_cache.so.manifest -outputresource:.\Release\mod_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\cache_storage.obj"
- -@erase "$(INTDIR)\cache_util.obj"
- -@erase "$(INTDIR)\mod_cache.obj"
- -@erase "$(INTDIR)\mod_cache.res"
- -@erase "$(INTDIR)\mod_cache_src.idb"
- -@erase "$(INTDIR)\mod_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_cache.exp"
- -@erase "$(OUTDIR)\mod_cache.lib"
- -@erase "$(OUTDIR)\mod_cache.pdb"
- -@erase "$(OUTDIR)\mod_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "CACHE_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_cache_src" /FD /EHsc /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_cache.so" /d LONG_NAME="cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_cache.pdb" /debug /out:"$(OUTDIR)\mod_cache.so" /implib:"$(OUTDIR)\mod_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_cache.so
-LINK32_OBJS= \
- "$(INTDIR)\cache_storage.obj" \
- "$(INTDIR)\cache_util.obj" \
- "$(INTDIR)\mod_cache.obj" \
- "$(INTDIR)\mod_cache.res" \
- "..\..\srclib\apr\Debug\libapr-1.lib" \
- "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
- "..\..\Debug\libhttpd.lib"
-
-"$(OUTDIR)\mod_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Debug\mod_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_cache.so"
- if exist .\Debug\mod_cache.so.manifest mt.exe -manifest .\Debug\mod_cache.so.manifest -outputresource:.\Debug\mod_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("mod_cache.dep")
-!INCLUDE "mod_cache.dep"
-!ELSE
-!MESSAGE Warning: cannot find "mod_cache.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "mod_cache - Win32 Release" || "$(CFG)" == "mod_cache - Win32 Debug"
-SOURCE=.\cache_storage.c
-
-"$(INTDIR)\cache_storage.obj" : $(SOURCE) "$(INTDIR)"
-
-
-SOURCE=.\cache_util.c
-
-"$(INTDIR)\cache_util.obj" : $(SOURCE) "$(INTDIR)"
-
-
-SOURCE=.\mod_cache.c
-
-"$(INTDIR)\mod_cache.obj" : $(SOURCE) "$(INTDIR)"
-
-
-!IF "$(CFG)" == "mod_cache - Win32 Release"
-
-"libapr - Win32 Release" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
- cd "..\..\modules\cache"
-
-"libapr - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
-
-"libapr - Win32 Debug" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libapr - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_cache - Win32 Release"
-
-"libaprutil - Win32 Release" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
-
-"libaprutil - Win32 Debug" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_cache - Win32 Release"
-
-"libhttpd - Win32 Release" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
- cd ".\modules\cache"
-
-"libhttpd - Win32 ReleaseCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
-
-"libhttpd - Win32 Debug" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
- cd ".\modules\cache"
-
-"libhttpd - Win32 DebugCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ENDIF
-
-SOURCE=..\..\build\win32\httpd.rc
-
-!IF "$(CFG)" == "mod_cache - Win32 Release"
-
-
-"$(INTDIR)\mod_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "NDEBUG" /d BIN_NAME="mod_cache.so" /d LONG_NAME="cache_module for Apache" $(SOURCE)
-
-
-!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
-
-
-"$(INTDIR)\mod_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "_DEBUG" /d BIN_NAME="mod_cache.so" /d LONG_NAME="cache_module for Apache" $(SOURCE)
-
-
-!ENDIF
-
-
-!ENDIF
-
diff --git a/modules/cache/mod_disk_cache.c b/modules/cache/mod_cache_disk.c
index f13800b3..0c65c172 100644
--- a/modules/cache/mod_disk_cache.c
+++ b/modules/cache/mod_cache_disk.c
@@ -14,17 +14,21 @@
* limitations under the License.
*/
+#include "apr_lib.h"
#include "apr_file_io.h"
#include "apr_strings.h"
#include "mod_cache.h"
-#include "mod_disk_cache.h"
+#include "mod_cache_disk.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_core.h"
#include "ap_provider.h"
#include "util_filter.h"
#include "util_script.h"
#include "util_charset.h"
/*
- * mod_disk_cache: Disk Based HTTP 1.1 Cache.
+ * mod_cache_disk: Disk Based HTTP 1.1 Cache.
*
* Flow to Find the .data file:
* Incoming client requests URI /foo/bar/baz
@@ -51,12 +55,13 @@
* CRLF
*/
-module AP_MODULE_DECLARE_DATA disk_cache_module;
+module AP_MODULE_DECLARE_DATA cache_disk_module;
/* Forward declarations */
static int remove_entity(cache_handle_t *h);
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);
-static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
+static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *in,
+ apr_bucket_brigade *out);
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
@@ -102,7 +107,7 @@ static char *data_file(apr_pool_t *p, disk_cache_conf *conf,
}
}
-static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
+static apr_status_t mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
{
apr_status_t rv;
char *p;
@@ -116,11 +121,12 @@ static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t
rv = apr_dir_make(file,
APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
- /* XXX */
+ return rv;
}
*p = '/';
++p;
}
+ return APR_SUCCESS;
}
/* htcacheclean may remove directories underneath us.
@@ -141,7 +147,9 @@ static apr_status_t safe_file_rename(disk_cache_conf *conf,
/* 1000 micro-seconds aka 0.001 seconds. */
apr_sleep(1000);
- mkdir_structure(conf, dest, pool);
+ rv = mkdir_structure(conf, dest, pool);
+ if (rv != APR_SUCCESS)
+ continue;
rv = apr_file_rename(src, dest, pool);
}
@@ -150,49 +158,57 @@ static apr_status_t safe_file_rename(disk_cache_conf *conf,
return rv;
}
-static apr_status_t file_cache_el_final(disk_cache_object_t *dobj,
+static apr_status_t file_cache_el_final(disk_cache_conf *conf, disk_cache_file_t *file,
request_rec *r)
{
- /* move the data over */
- if (dobj->tfd) {
- apr_status_t rv;
+ apr_status_t rv = APR_SUCCESS;
- apr_file_close(dobj->tfd);
+ /* This assumes that the tempfiles are on the same file system
+ * as the cache_root. If not, then we need a file copy/move
+ * rather than a rename.
+ */
- /* This assumes that the tempfile is on the same file system
- * as the cache_root. If not, then we need a file copy/move
- * rather than a rename.
- */
- rv = apr_file_rename(dobj->tempfile, dobj->datafile, r->pool);
+ /* move the file over */
+ if (file->tempfd) {
+
+ rv = safe_file_rename(conf, file->tempfile, file->file, file->pool);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
- "disk_cache: rename tempfile to datafile failed:"
- " %s -> %s", dobj->tempfile, dobj->datafile);
- apr_file_remove(dobj->tempfile, r->pool);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00699)
+ "rename tempfile to file failed:"
+ " %s -> %s", file->tempfile, file->file);
+ apr_file_remove(file->tempfile, file->pool);
}
- dobj->tfd = NULL;
+ file->tempfd = NULL;
}
- return APR_SUCCESS;
+ return rv;
}
-static apr_status_t file_cache_errorcleanup(disk_cache_object_t *dobj, request_rec *r)
-{
- /* Remove the header file and the body file. */
- apr_file_remove(dobj->hdrsfile, r->pool);
- apr_file_remove(dobj->datafile, r->pool);
-
- /* If we opened the temporary data file, close and remove it. */
- if (dobj->tfd) {
- apr_file_close(dobj->tfd);
- apr_file_remove(dobj->tempfile, r->pool);
- dobj->tfd = NULL;
+static apr_status_t file_cache_temp_cleanup(void *dummy) {
+ disk_cache_file_t *file = (disk_cache_file_t *)dummy;
+
+ /* clean up the temporary file */
+ if (file->tempfd) {
+ apr_file_remove(file->tempfile, file->pool);
+ file->tempfd = NULL;
}
+ file->tempfile = NULL;
+ file->pool = NULL;
return APR_SUCCESS;
}
+static apr_status_t file_cache_create(disk_cache_conf *conf, disk_cache_file_t *file,
+ apr_pool_t *pool)
+{
+ file->pool = pool;
+ file->tempfile = apr_pstrcat(pool, conf->cache_root, AP_TEMPFILE, NULL);
+
+ apr_pool_cleanup_register(pool, file, file_cache_temp_cleanup, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
/* These two functions get and put state information into the data
* file for an ap_cache_el, this state information will be read
@@ -203,34 +219,33 @@ static int file_cache_recall_mydata(apr_file_t *fd, cache_info *info,
{
apr_status_t rv;
char *urlbuff;
- disk_cache_info_t disk_info;
apr_size_t len;
/* read the data from the cache file */
len = sizeof(disk_cache_info_t);
- rv = apr_file_read_full(fd, &disk_info, len, &len);
+ rv = apr_file_read_full(fd, &dobj->disk_info, len, &len);
if (rv != APR_SUCCESS) {
return rv;
}
/* Store it away so we can get it later. */
- dobj->disk_info = disk_info;
+ info->status = dobj->disk_info.status;
+ info->date = dobj->disk_info.date;
+ info->expire = dobj->disk_info.expire;
+ info->request_time = dobj->disk_info.request_time;
+ info->response_time = dobj->disk_info.response_time;
- info->status = disk_info.status;
- info->date = disk_info.date;
- info->expire = disk_info.expire;
- info->request_time = disk_info.request_time;
- info->response_time = disk_info.response_time;
+ memcpy(&info->control, &dobj->disk_info.control, sizeof(cache_control_t));
/* Note that we could optimize this by conditionally doing the palloc
* depending upon the size. */
- urlbuff = apr_palloc(r->pool, disk_info.name_len + 1);
- len = disk_info.name_len;
+ urlbuff = apr_palloc(r->pool, dobj->disk_info.name_len + 1);
+ len = dobj->disk_info.name_len;
rv = apr_file_read_full(fd, urlbuff, len, &len);
if (rv != APR_SUCCESS) {
return rv;
}
- urlbuff[disk_info.name_len] = '\0';
+ urlbuff[dobj->disk_info.name_len] = '\0';
/* check that we have the same URL */
/* Would strncmp be correct? */
@@ -319,17 +334,44 @@ static void tokens_to_array(apr_pool_t *p, const char *data,
/*
* Hook and mod_cache callback functions
*/
-static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr_off_t len)
+static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr_off_t len,
+ apr_bucket_brigade *bb)
{
+ disk_cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_disk_module);
disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
cache_object_t *obj;
disk_cache_object_t *dobj;
+ apr_pool_t *pool;
if (conf->cache_root == NULL) {
return DECLINED;
}
+ /* we don't support caching of range requests (yet) */
+ if (r->status == HTTP_PARTIAL_CONTENT) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00700)
+ "URL %s partial content response not cached",
+ key);
+ return DECLINED;
+ }
+
+ /* Note, len is -1 if unknown so don't trust it too hard */
+ if (len > dconf->maxfs) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00701)
+ "URL %s failed the size check "
+ "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
+ key, len, dconf->maxfs);
+ return DECLINED;
+ }
+ if (len >= 0 && len < dconf->minfs) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00702)
+ "URL %s failed the size check "
+ "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
+ key, len, dconf->minfs);
+ return DECLINED;
+ }
+
/* Allocate and initialize cache_object_t and disk_cache_object_t */
h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj));
@@ -341,9 +383,18 @@ static int create_entity(cache_handle_t *h, request_rec *r, const char *key, apr
/* Save the cache root */
dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
dobj->root_len = conf->cache_root_len;
- dobj->datafile = data_file(r->pool, conf, dobj, key);
- dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
- dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
+
+ apr_pool_create(&pool, r->pool);
+
+ file_cache_create(conf, &dobj->hdrs, pool);
+ file_cache_create(conf, &dobj->vary, pool);
+ file_cache_create(conf, &dobj->data, pool);
+
+ dobj->data.file = data_file(r->pool, conf, dobj, key);
+ dobj->hdrs.file = header_file(r->pool, conf, dobj, key);
+ dobj->vary.file = header_file(r->pool, conf, dobj, key);
+
+ dobj->disk_info.header_only = r->header_only;
return OK;
}
@@ -356,16 +407,16 @@ static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
apr_status_t rc;
static int error_logged = 0;
disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
#ifdef APR_SENDFILE_ENABLED
- core_dir_config *coreconf = ap_get_module_config(r->per_dir_config,
- &core_module);
+ core_dir_config *coreconf = ap_get_core_module_config(r->per_dir_config);
#endif
apr_finfo_t finfo;
cache_object_t *obj;
cache_info *info;
disk_cache_object_t *dobj;
int flags;
+ apr_pool_t *pool;
h->cache_obj = NULL;
@@ -373,15 +424,15 @@ static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
if (conf->cache_root == NULL) {
if (!error_logged) {
error_logged = 1;
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "disk_cache: Cannot cache files to disk without a CacheRoot specified.");
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00703)
+ "Cannot cache files to disk without a CacheRoot specified.");
}
return DECLINED;
}
/* Create and init the cache object */
- h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
- obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
+ obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
+ dobj = apr_pcalloc(r->pool, sizeof(disk_cache_object_t));
info = &(obj->info);
@@ -392,99 +443,155 @@ static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
dobj->root = apr_pstrndup(r->pool, conf->cache_root, conf->cache_root_len);
dobj->root_len = conf->cache_root_len;
- dobj->hdrsfile = header_file(r->pool, conf, dobj, key);
+ dobj->vary.file = header_file(r->pool, conf, dobj, key);
flags = APR_READ|APR_BINARY|APR_BUFFERED;
- rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
+ rc = apr_file_open(&dobj->vary.fd, dobj->vary.file, flags, 0, r->pool);
if (rc != APR_SUCCESS) {
return DECLINED;
}
/* read the format from the cache file */
len = sizeof(format);
- apr_file_read_full(dobj->hfd, &format, len, &len);
+ apr_file_read_full(dobj->vary.fd, &format, len, &len);
if (format == VARY_FORMAT_VERSION) {
apr_array_header_t* varray;
apr_time_t expire;
len = sizeof(expire);
- apr_file_read_full(dobj->hfd, &expire, len, &len);
+ apr_file_read_full(dobj->vary.fd, &expire, len, &len);
varray = apr_array_make(r->pool, 5, sizeof(char*));
- rc = read_array(r, varray, dobj->hfd);
+ rc = read_array(r, varray, dobj->vary.fd);
if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
- "disk_cache: Cannot parse vary header file: %s",
- dobj->hdrsfile);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(00704)
+ "Cannot parse vary header file: %s",
+ dobj->vary.file);
+ apr_file_close(dobj->vary.fd);
return DECLINED;
}
- apr_file_close(dobj->hfd);
+ apr_file_close(dobj->vary.fd);
nkey = regen_key(r->pool, r->headers_in, varray, key);
dobj->hashfile = NULL;
- dobj->prefix = dobj->hdrsfile;
- dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
+ dobj->prefix = dobj->vary.file;
+ dobj->hdrs.file = header_file(r->pool, conf, dobj, nkey);
flags = APR_READ|APR_BINARY|APR_BUFFERED;
- rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
+ rc = apr_file_open(&dobj->hdrs.fd, dobj->hdrs.file, flags, 0, r->pool);
if (rc != APR_SUCCESS) {
return DECLINED;
}
}
else if (format != DISK_FORMAT_VERSION) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "cache_disk: File '%s' has a version mismatch. File had version: %d.",
- dobj->hdrsfile, format);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00705)
+ "File '%s' has a version mismatch. File had version: %d.",
+ dobj->vary.file, format);
+ apr_file_close(dobj->vary.fd);
return DECLINED;
}
else {
apr_off_t offset = 0;
+
+ /* oops, not vary as it turns out */
+ dobj->hdrs.fd = dobj->vary.fd;
+ dobj->vary.fd = NULL;
+ dobj->hdrs.file = dobj->vary.file;
+
/* This wasn't a Vary Format file, so we must seek to the
* start of the file again, so that later reads work.
*/
- apr_file_seek(dobj->hfd, APR_SET, &offset);
+ apr_file_seek(dobj->hdrs.fd, APR_SET, &offset);
nkey = key;
}
obj->key = nkey;
dobj->key = nkey;
dobj->name = key;
- dobj->datafile = data_file(r->pool, conf, dobj, nkey);
- dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
- /* Open the data file */
- flags = APR_READ|APR_BINARY;
-#ifdef APR_SENDFILE_ENABLED
- /* When we are in the quick handler we don't have the per-directory
- * configuration, so this check only takes the globel setting of
- * the EnableSendFile directive into account.
- */
- flags |= ((coreconf->enable_sendfile == ENABLE_SENDFILE_OFF)
- ? 0 : APR_SENDFILE_ENABLED);
-#endif
- rc = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, r->pool);
+ apr_pool_create(&pool, r->pool);
+
+ file_cache_create(conf, &dobj->hdrs, pool);
+ file_cache_create(conf, &dobj->vary, pool);
+ file_cache_create(conf, &dobj->data, pool);
+
+ dobj->data.file = data_file(r->pool, conf, dobj, nkey);
+
+ /* Read the bytes to setup the cache_info fields */
+ rc = file_cache_recall_mydata(dobj->hdrs.fd, info, dobj, r);
if (rc != APR_SUCCESS) {
- /* XXX: Log message */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(00706)
+ "Cannot read header file %s", dobj->hdrs.file);
+ apr_file_close(dobj->hdrs.fd);
return DECLINED;
}
- rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, dobj->fd);
- if (rc == APR_SUCCESS) {
- dobj->file_size = finfo.size;
- }
+ apr_file_close(dobj->hdrs.fd);
- /* Read the bytes to setup the cache_info fields */
- rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
- if (rc != APR_SUCCESS) {
- /* XXX log message */
+ /* Is this a cached HEAD request? */
+ if (dobj->disk_info.header_only && !r->header_only) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00707)
+ "HEAD request cached, non-HEAD requested, ignoring: %s",
+ dobj->hdrs.file);
return DECLINED;
}
- /* Initialize the cache_handle callback functions */
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "disk_cache: Recalled cached URL info header %s", dobj->name);
- return OK;
+ /* Open the data file */
+ if (dobj->disk_info.has_body) {
+ flags = APR_READ | APR_BINARY;
+#ifdef APR_SENDFILE_ENABLED
+ /* When we are in the quick handler we don't have the per-directory
+ * configuration, so this check only takes the global setting of
+ * the EnableSendFile directive into account.
+ */
+ flags |= AP_SENDFILE_ENABLED(coreconf->enable_sendfile);
+#endif
+ rc = apr_file_open(&dobj->data.fd, dobj->data.file, flags, 0, r->pool);
+ if (rc != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(00708)
+ "Cannot open data file %s", dobj->data.file);
+ apr_file_close(dobj->hdrs.fd);
+ return DECLINED;
+ }
+
+ rc = apr_file_info_get(&finfo, APR_FINFO_SIZE | APR_FINFO_IDENT,
+ dobj->data.fd);
+ if (rc == APR_SUCCESS) {
+ dobj->file_size = finfo.size;
+ }
+
+ /* Atomic check - does the body file belong to the header file? */
+ if (dobj->disk_info.inode == finfo.inode &&
+ dobj->disk_info.device == finfo.device) {
+
+ /* Initialize the cache_handle callback functions */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00709)
+ "Recalled cached URL info header %s", dobj->name);
+
+ /* make the configuration stick */
+ h->cache_obj = obj;
+ obj->vobj = dobj;
+
+ return OK;
+ }
+
+ }
+ else {
+
+ /* make the configuration stick */
+ h->cache_obj = obj;
+ obj->vobj = dobj;
+
+ return OK;
+ }
+
+ /* Oh dear, no luck matching header to the body */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00710)
+ "Cached URL info header '%s' didn't match body, ignoring this entry",
+ dobj->name);
+
+ return DECLINED;
}
static int remove_entity(cache_handle_t *h)
@@ -494,7 +601,7 @@ static int remove_entity(cache_handle_t *h)
return OK;
}
-static int remove_url(cache_handle_t *h, apr_pool_t *p)
+static int remove_url(cache_handle_t *h, request_rec *r)
{
apr_status_t rc;
disk_cache_object_t *dobj;
@@ -506,35 +613,35 @@ static int remove_url(cache_handle_t *h, apr_pool_t *p)
}
/* Delete headers file */
- if (dobj->hdrsfile) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "disk_cache: Deleting %s from cache.", dobj->hdrsfile);
+ if (dobj->hdrs.file) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00711)
+ "Deleting %s from cache.", dobj->hdrs.file);
- rc = apr_file_remove(dobj->hdrsfile, p);
+ rc = apr_file_remove(dobj->hdrs.file, r->pool);
if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
/* Will only result in an output if httpd is started with -e debug.
* For reason see log_error_core for the case s == NULL.
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
- "disk_cache: Failed to delete headers file %s from cache.",
- dobj->hdrsfile);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(00712)
+ "Failed to delete headers file %s from cache.",
+ dobj->hdrs.file);
return DECLINED;
}
}
- /* Delete data file */
- if (dobj->datafile) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "disk_cache: Deleting %s from cache.", dobj->datafile);
+ /* Delete data file */
+ if (dobj->data.file) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00713)
+ "Deleting %s from cache.", dobj->data.file);
- rc = apr_file_remove(dobj->datafile, p);
+ rc = apr_file_remove(dobj->data.file, r->pool);
if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
/* Will only result in an output if httpd is started with -e debug.
* For reason see log_error_core for the case s == NULL.
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
- "disk_cache: Failed to delete data file %s from cache.",
- dobj->datafile);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(00714)
+ "Failed to delete data file %s from cache.",
+ dobj->data.file);
return DECLINED;
}
}
@@ -543,11 +650,11 @@ static int remove_url(cache_handle_t *h, apr_pool_t *p)
if (dobj->root) {
const char *str_to_copy;
- str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
+ str_to_copy = dobj->hdrs.file ? dobj->hdrs.file : dobj->data.file;
if (str_to_copy) {
char *dir, *slash, *q;
- dir = apr_pstrdup(p, str_to_copy);
+ dir = apr_pstrdup(r->pool, str_to_copy);
/* remove filename */
slash = strrchr(dir, '/');
@@ -564,11 +671,10 @@ static int remove_url(cache_handle_t *h, apr_pool_t *p)
* we won't either delete or go above our cache root.
*/
for (q = dir + dobj->root_len; *q ; ) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "disk_cache: Deleting directory %s from cache",
- dir);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00715)
+ "Deleting directory %s from cache", dir);
- rc = apr_dir_remove(dir, p);
+ rc = apr_dir_remove(dir, r->pool);
if (rc != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rc)) {
break;
}
@@ -591,7 +697,7 @@ static apr_status_t read_array(request_rec *r, apr_array_header_t* arr,
while (1) {
rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
if (rv != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00716)
"Premature end of vary array.");
return rv;
}
@@ -660,7 +766,7 @@ static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
/* ### What about APR_EOF? */
rv = apr_file_gets(w, MAX_STRING_LEN - 1, file);
if (rv != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00717)
"Premature end of cache headers.");
return rv;
}
@@ -703,9 +809,9 @@ static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
++maybeASCII;
}
if (maybeASCII > maybeEBCDIC) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
- r->filename);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00718)
+ "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
+ r->filename);
inbytes_left = outbytes_left = cp - w;
apr_xlate_conv_buffer(ap_hdrs_from_ascii,
w, &inbytes_left, w, &outbytes_left);
@@ -741,8 +847,9 @@ static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
/* This case should not happen... */
- if (!dobj->hfd) {
- /* XXX log message */
+ if (!dobj->hdrs.fd) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00719)
+ "recalling headers; but no header fd for %s", dobj->name);
return APR_NOTFOUND;
}
@@ -750,26 +857,23 @@ static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
h->resp_hdrs = apr_table_make(r->pool, 20);
/* Call routine to read the header lines/status line */
- read_table(h, r, h->resp_hdrs, dobj->hfd);
- read_table(h, r, h->req_hdrs, dobj->hfd);
+ read_table(h, r, h->resp_hdrs, dobj->hdrs.fd);
+ read_table(h, r, h->req_hdrs, dobj->hdrs.fd);
- apr_file_close(dobj->hfd);
+ apr_file_close(dobj->hdrs.fd);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "disk_cache: Recalled headers for URL %s", dobj->name);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00720)
+ "Recalled headers for URL %s", dobj->name);
return APR_SUCCESS;
}
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
{
- apr_bucket *e;
disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
- e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, p,
- bb->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(bb, e);
- e = apr_bucket_eos_create(bb->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
+ if (dobj->data.fd) {
+ apr_brigade_insert_file(bb, dobj->data.fd, 0, dobj->file_size, p);
+ }
return APR_SUCCESS;
}
@@ -810,8 +914,25 @@ static apr_status_t store_table(apr_file_t *fd, apr_table_t *table)
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
{
+ disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
+
+ memcpy(&h->cache_obj->info, info, sizeof(cache_info));
+
+ if (r->headers_out) {
+ dobj->headers_out = ap_cache_cacheable_headers_out(r);
+ }
+
+ if (r->headers_in) {
+ dobj->headers_in = ap_cache_cacheable_headers_in(r);
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t write_headers(cache_handle_t *h, request_rec *r)
+{
disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
apr_status_t rv;
apr_size_t amt;
disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
@@ -819,13 +940,12 @@ static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info
disk_cache_info_t disk_info;
struct iovec iov[2];
- /* This is flaky... we need to manage the cache_info differently */
- h->cache_obj->info = *info;
+ memset(&disk_info, 0, sizeof(disk_cache_info_t));
- if (r->headers_out) {
+ if (dobj->headers_out) {
const char *tmp;
- tmp = apr_table_get(r->headers_out, "Vary");
+ tmp = apr_table_get(dobj->headers_out, "Vary");
if (tmp) {
apr_array_header_t* varray;
@@ -836,253 +956,409 @@ static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info
* vary format hints in the appropriate directory.
*/
if (dobj->prefix) {
- dobj->hdrsfile = dobj->prefix;
+ dobj->hdrs.file = dobj->prefix;
dobj->prefix = NULL;
}
- mkdir_structure(conf, dobj->hdrsfile, r->pool);
+ rv = mkdir_structure(conf, dobj->hdrs.file, r->pool);
- rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
+ rv = apr_file_mktemp(&dobj->vary.tempfd, dobj->vary.tempfile,
APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
- r->pool);
+ dobj->vary.pool);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00721)
+ "could not create vary file %s",
+ dobj->vary.tempfile);
return rv;
}
amt = sizeof(format);
- apr_file_write(dobj->tfd, &format, &amt);
+ rv = apr_file_write(dobj->vary.tempfd, &format, &amt);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00722)
+ "could not write to vary file %s",
+ dobj->vary.tempfile);
+ apr_file_close(dobj->vary.tempfd);
+ apr_pool_destroy(dobj->vary.pool);
+ return rv;
+ }
- amt = sizeof(info->expire);
- apr_file_write(dobj->tfd, &info->expire, &amt);
+ amt = sizeof(h->cache_obj->info.expire);
+ rv = apr_file_write(dobj->vary.tempfd, &h->cache_obj->info.expire,
+ &amt);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00723)
+ "could not write to vary file %s",
+ dobj->vary.tempfile);
+ apr_file_close(dobj->vary.tempfd);
+ apr_pool_destroy(dobj->vary.pool);
+ return rv;
+ }
varray = apr_array_make(r->pool, 6, sizeof(char*));
tokens_to_array(r->pool, tmp, varray);
- store_array(dobj->tfd, varray);
-
- apr_file_close(dobj->tfd);
+ store_array(dobj->vary.tempfd, varray);
- dobj->tfd = NULL;
-
- rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile,
- r->pool);
+ rv = apr_file_close(dobj->vary.tempfd);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
- "disk_cache: rename tempfile to varyfile failed: %s -> %s",
- dobj->tempfile, dobj->hdrsfile);
- apr_file_remove(dobj->tempfile, r->pool);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00724)
+ "could not close vary file %s",
+ dobj->vary.tempfile);
+ apr_pool_destroy(dobj->vary.pool);
return rv;
}
- dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
- tmp = regen_key(r->pool, r->headers_in, varray, dobj->name);
- dobj->prefix = dobj->hdrsfile;
+ tmp = regen_key(r->pool, dobj->headers_in, varray, dobj->name);
+ dobj->prefix = dobj->hdrs.file;
dobj->hashfile = NULL;
- dobj->datafile = data_file(r->pool, conf, dobj, tmp);
- dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
+ dobj->data.file = data_file(r->pool, conf, dobj, tmp);
+ dobj->hdrs.file = header_file(r->pool, conf, dobj, tmp);
}
}
- rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
+ rv = apr_file_mktemp(&dobj->hdrs.tempfd, dobj->hdrs.tempfile,
APR_CREATE | APR_WRITE | APR_BINARY |
- APR_BUFFERED | APR_EXCL, r->pool);
+ APR_BUFFERED | APR_EXCL, dobj->hdrs.pool);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00725)
+ "could not create header file %s",
+ dobj->hdrs.tempfile);
return rv;
}
disk_info.format = DISK_FORMAT_VERSION;
- disk_info.date = info->date;
- disk_info.expire = info->expire;
+ disk_info.date = h->cache_obj->info.date;
+ disk_info.expire = h->cache_obj->info.expire;
disk_info.entity_version = dobj->disk_info.entity_version++;
- disk_info.request_time = info->request_time;
- disk_info.response_time = info->response_time;
- disk_info.status = info->status;
+ disk_info.request_time = h->cache_obj->info.request_time;
+ disk_info.response_time = h->cache_obj->info.response_time;
+ disk_info.status = h->cache_obj->info.status;
+ disk_info.inode = dobj->disk_info.inode;
+ disk_info.device = dobj->disk_info.device;
+ disk_info.has_body = dobj->disk_info.has_body;
+ disk_info.header_only = dobj->disk_info.header_only;
disk_info.name_len = strlen(dobj->name);
+ memcpy(&disk_info.control, &h->cache_obj->info.control, sizeof(cache_control_t));
+
iov[0].iov_base = (void*)&disk_info;
iov[0].iov_len = sizeof(disk_cache_info_t);
iov[1].iov_base = (void*)dobj->name;
iov[1].iov_len = disk_info.name_len;
- rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
+ rv = apr_file_writev(dobj->hdrs.tempfd, (const struct iovec *) &iov, 2, &amt);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00726)
+ "could not write info to header file %s",
+ dobj->hdrs.tempfile);
+ apr_file_close(dobj->hdrs.tempfd);
+ apr_pool_destroy(dobj->hdrs.pool);
return rv;
}
- if (r->headers_out) {
- apr_table_t *headers_out;
-
- headers_out = apr_table_overlay(r->pool, r->headers_out,
- r->err_headers_out);
- headers_out = ap_cache_cacheable_hdrs_out(r->pool, headers_out,
- r->server);
-
- if (!apr_table_get(headers_out, "Content-Type")
- && r->content_type) {
- apr_table_setn(headers_out, "Content-Type",
- ap_make_content_type(r, r->content_type));
- }
-
- if (!apr_table_get(headers_out, "Content-Encoding")
- && r->content_encoding) {
- apr_table_setn(headers_out, "Content-Encoding",
- r->content_encoding);
- }
-
- rv = store_table(dobj->hfd, headers_out);
+ if (dobj->headers_out) {
+ rv = store_table(dobj->hdrs.tempfd, dobj->headers_out);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00727)
+ "could not write out-headers to header file %s",
+ dobj->hdrs.tempfile);
+ apr_file_close(dobj->hdrs.tempfd);
+ apr_pool_destroy(dobj->hdrs.pool);
return rv;
}
}
/* Parse the vary header and dump those fields from the headers_in. */
/* FIXME: Make call to the same thing cache_select calls to crack Vary. */
- if (r->headers_in) {
- apr_table_t *headers_in;
-
- headers_in = ap_cache_cacheable_hdrs_out(r->pool, r->headers_in,
- r->server);
- rv = store_table(dobj->hfd, headers_in);
+ if (dobj->headers_in) {
+ rv = store_table(dobj->hdrs.tempfd, dobj->headers_in);
if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00728)
+ "could not write in-headers to header file %s",
+ dobj->hdrs.tempfile);
+ apr_file_close(dobj->hdrs.tempfd);
+ apr_pool_destroy(dobj->hdrs.pool);
return rv;
}
}
- apr_file_close(dobj->hfd); /* flush and close */
-
- /* Remove old file with the same name. If remove fails, then
- * perhaps we need to create the directory tree where we are
- * about to write the new headers file.
- */
- rv = apr_file_remove(dobj->hdrsfile, r->pool);
- if (rv != APR_SUCCESS) {
- mkdir_structure(conf, dobj->hdrsfile, r->pool);
- }
-
- rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r->pool);
+ rv = apr_file_close(dobj->hdrs.tempfd); /* flush and close */
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
- "disk_cache: rename tempfile to hdrsfile failed: %s -> %s",
- dobj->tempfile, dobj->hdrsfile);
- apr_file_remove(dobj->tempfile, r->pool);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00729)
+ "could not close header file %s",
+ dobj->hdrs.tempfile);
+ apr_pool_destroy(dobj->hdrs.pool);
return rv;
}
- dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL);
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "disk_cache: Stored headers for URL %s", dobj->name);
return APR_SUCCESS;
}
static apr_status_t store_body(cache_handle_t *h, request_rec *r,
- apr_bucket_brigade *bb)
+ apr_bucket_brigade *in, apr_bucket_brigade *out)
{
apr_bucket *e;
- apr_status_t rv;
+ apr_status_t rv = APR_SUCCESS;
disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
- disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
- &disk_cache_module);
+ disk_cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_disk_module);
+ int seen_eos = 0;
- /* We write to a temp file and then atomically rename the file over
- * in file_cache_el_final().
- */
- if (!dobj->tfd) {
- rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
- APR_CREATE | APR_WRITE | APR_BINARY |
- APR_BUFFERED | APR_EXCL, r->pool);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- dobj->file_size = 0;
+ if (!dobj->offset) {
+ dobj->offset = dconf->readsize;
+ }
+ if (!dobj->timeout && dconf->readtime) {
+ dobj->timeout = apr_time_now() + dconf->readtime;
}
- for (e = APR_BRIGADE_FIRST(bb);
- e != APR_BRIGADE_SENTINEL(bb);
- e = APR_BUCKET_NEXT(e))
- {
+ if (dobj->offset) {
+ apr_brigade_partition(in, dobj->offset, &e);
+ }
+
+ while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
const char *str;
apr_size_t length, written;
+
+ e = APR_BRIGADE_FIRST(in);
+
+ /* are we done completely? if so, pass any trailing buckets right through */
+ if (dobj->done || !dobj->data.pool) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+ continue;
+ }
+
+ /* have we seen eos yet? */
+ if (APR_BUCKET_IS_EOS(e)) {
+ seen_eos = 1;
+ dobj->done = 1;
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+ break;
+ }
+
+ /* honour flush buckets, we'll get called again */
+ if (APR_BUCKET_IS_FLUSH(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+ break;
+ }
+
+ /* metadata buckets are preserved as is */
+ if (APR_BUCKET_IS_METADATA(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(out, e);
+ continue;
+ }
+
+ /* read the bucket, write to the cache */
rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(out, e);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "cache_disk: Error when reading bucket for URL %s",
- h->cache_obj->key);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00730)
+ "Error when reading bucket for URL %s",
+ h->cache_obj->key);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return rv;
}
- rv = apr_file_write_full(dobj->tfd, str, length, &written);
+
+ /* don't write empty buckets to the cache */
+ if (!length) {
+ continue;
+ }
+
+ /* Attempt to create the data file at the last possible moment, if
+ * the body is empty, we don't write a file at all, and save an inode.
+ */
+ if (!dobj->data.tempfd) {
+ apr_finfo_t finfo;
+ rv = apr_file_mktemp(&dobj->data.tempfd, dobj->data.tempfile,
+ APR_CREATE | APR_WRITE | APR_BINARY |
+ APR_BUFFERED | APR_EXCL, dobj->data.pool);
+ if (rv != APR_SUCCESS) {
+ apr_pool_destroy(dobj->data.pool);
+ return rv;
+ }
+ dobj->file_size = 0;
+ rv = apr_file_info_get(&finfo, APR_FINFO_IDENT,
+ dobj->data.tempfd);
+ if (rv != APR_SUCCESS) {
+ apr_pool_destroy(dobj->data.pool);
+ return rv;
+ }
+ dobj->disk_info.device = finfo.device;
+ dobj->disk_info.inode = finfo.inode;
+ dobj->disk_info.has_body = 1;
+ }
+
+ /* write to the cache, leave if we fail */
+ rv = apr_file_write_full(dobj->data.tempfd, str, length, &written);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
- "cache_disk: Error when writing cache file for URL %s",
- h->cache_obj->key);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00731)
+ "Error when writing cache file for URL %s",
+ h->cache_obj->key);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return rv;
}
dobj->file_size += written;
- if (dobj->file_size > conf->maxfs) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache_disk: URL %s failed the size check "
- "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
- h->cache_obj->key, dobj->file_size, conf->maxfs);
+ if (dobj->file_size > dconf->maxfs) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00732)
+ "URL %s failed the size check "
+ "(%" APR_OFF_T_FMT ">%" APR_OFF_T_FMT ")",
+ h->cache_obj->key, dobj->file_size, dconf->maxfs);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
+
+ /* have we reached the limit of how much we're prepared to write in one
+ * go? If so, leave, we'll get called again. This prevents us from trying
+ * to swallow too much data at once, or taking so long to write the data
+ * the client times out.
+ */
+ dobj->offset -= length;
+ if (dobj->offset <= 0) {
+ dobj->offset = 0;
+ break;
+ }
+ if ((dconf->readtime && apr_time_now() > dobj->timeout)) {
+ dobj->timeout = 0;
+ break;
+ }
+
}
/* Was this the final bucket? If yes, close the temp file and perform
* sanity checks.
*/
- if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+ if (seen_eos) {
const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
+ if (dobj->data.tempfd) {
+ rv = apr_file_close(dobj->data.tempfd);
+ if (rv != APR_SUCCESS) {
+ /* Buffered write failed, abandon attempt to write */
+ apr_pool_destroy(dobj->data.pool);
+ return rv;
+ }
+ }
+
if (r->connection->aborted || r->no_cache) {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "disk_cache: Discarding body for URL %s "
- "because connection has been aborted.",
- h->cache_obj->key);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00733)
+ "Discarding body for URL %s "
+ "because connection has been aborted.",
+ h->cache_obj->key);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
- if (dobj->file_size < conf->minfs) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "cache_disk: URL %s failed the size check "
- "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")",
- h->cache_obj->key, dobj->file_size, conf->minfs);
+ if (dobj->file_size < dconf->minfs) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00734)
+ "URL %s failed the size check "
+ "(%" APR_OFF_T_FMT "<%" APR_OFF_T_FMT ")",
+ h->cache_obj->key, dobj->file_size, dconf->minfs);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
if (cl_header) {
apr_int64_t cl = apr_atoi64(cl_header);
if ((errno == 0) && (dobj->file_size != cl)) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "disk_cache: URL %s didn't receive complete response, not caching",
- h->cache_obj->key);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00735)
+ "URL %s didn't receive complete response, not caching",
+ h->cache_obj->key);
/* Remove the intermediate cache file and return non-APR_SUCCESS */
- file_cache_errorcleanup(dobj, r);
+ apr_pool_destroy(dobj->data.pool);
return APR_EGENERAL;
}
}
- /* All checks were fine. Move tempfile to final destination */
- /* Link to the perm file, and close the descriptor */
- file_cache_el_final(dobj, r);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "disk_cache: Body for URL %s cached.", dobj->name);
+ /* All checks were fine, we're good to go when the commit comes */
}
return APR_SUCCESS;
}
+static apr_status_t commit_entity(cache_handle_t *h, request_rec *r)
+{
+ disk_cache_conf *conf = ap_get_module_config(r->server->module_config,
+ &cache_disk_module);
+ disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
+ apr_status_t rv;
+
+ /* write the headers to disk at the last possible moment */
+ rv = write_headers(h, r);
+
+ /* move header and data tempfiles to the final destination */
+ if (APR_SUCCESS == rv) {
+ rv = file_cache_el_final(conf, &dobj->hdrs, r);
+ }
+ if (APR_SUCCESS == rv) {
+ rv = file_cache_el_final(conf, &dobj->vary, r);
+ }
+ if (APR_SUCCESS == rv) {
+ rv = file_cache_el_final(conf, &dobj->data, r);
+ }
+
+ /* remove the cached items completely on any failure */
+ if (APR_SUCCESS != rv) {
+ remove_url(h, r);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00736)
+ "commit_entity: URL '%s' not cached due to earlier disk error.",
+ dobj->name);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00737)
+ "commit_entity: Headers and body for URL %s cached.",
+ dobj->name);
+ }
+
+ apr_pool_destroy(dobj->data.pool);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
+{
+ return APR_ENOTIMPL;
+}
+
+static void *create_dir_config(apr_pool_t *p, char *dummy)
+{
+ disk_cache_dir_conf *dconf = apr_pcalloc(p, sizeof(disk_cache_dir_conf));
+
+ dconf->maxfs = DEFAULT_MAX_FILE_SIZE;
+ dconf->minfs = DEFAULT_MIN_FILE_SIZE;
+ dconf->readsize = DEFAULT_READSIZE;
+ dconf->readtime = DEFAULT_READTIME;
+
+ return dconf;
+}
+
+static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv) {
+ disk_cache_dir_conf *new = (disk_cache_dir_conf *) apr_pcalloc(p, sizeof(disk_cache_dir_conf));
+ disk_cache_dir_conf *add = (disk_cache_dir_conf *) addv;
+ disk_cache_dir_conf *base = (disk_cache_dir_conf *) basev;
+
+ new->maxfs = (add->maxfs_set == 0) ? base->maxfs : add->maxfs;
+ new->maxfs_set = add->maxfs_set || base->maxfs_set;
+ new->minfs = (add->minfs_set == 0) ? base->minfs : add->minfs;
+ new->minfs_set = add->minfs_set || base->minfs_set;
+ new->readsize = (add->readsize_set == 0) ? base->readsize : add->readsize;
+ new->readsize_set = add->readsize_set || base->readsize_set;
+ new->readtime = (add->readtime_set == 0) ? base->readtime : add->readtime;
+ new->readtime_set = add->readtime_set || base->readtime_set;
+
+ return new;
+}
+
static void *create_config(apr_pool_t *p, server_rec *s)
{
disk_cache_conf *conf = apr_pcalloc(p, sizeof(disk_cache_conf));
@@ -1090,8 +1366,6 @@ static void *create_config(apr_pool_t *p, server_rec *s)
/* XXX: Set default values */
conf->dirlevels = DEFAULT_DIRLEVELS;
conf->dirlength = DEFAULT_DIRLENGTH;
- conf->maxfs = DEFAULT_MAX_FILE_SIZE;
- conf->minfs = DEFAULT_MIN_FILE_SIZE;
conf->cache_root = NULL;
conf->cache_root_len = 0;
@@ -1100,13 +1374,13 @@ static void *create_config(apr_pool_t *p, server_rec *s)
}
/*
- * mod_disk_cache configuration directives handlers.
+ * mod_cache_disk configuration directives handlers.
*/
static const char
*set_cache_root(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
conf->cache_root = arg;
conf->cache_root_len = strlen(arg);
/* TODO: canonicalize cache_root and strip off any trailing slashes */
@@ -1124,7 +1398,7 @@ static const char
*set_cache_dirlevels(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
int val = atoi(arg);
if (val < 1)
return "CacheDirLevels value must be an integer greater than 0";
@@ -1137,7 +1411,7 @@ static const char
*set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
- &disk_cache_module);
+ &cache_disk_module);
int val = atoi(arg);
if (val < 1)
return "CacheDirLength value must be an integer greater than 0";
@@ -1151,11 +1425,10 @@ static const char
static const char
*set_cache_minfs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
- disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
- &disk_cache_module);
+ disk_cache_dir_conf *dconf = (disk_cache_dir_conf *)in_struct_ptr;
- if (apr_strtoff(&conf->minfs, arg, NULL, 0) != APR_SUCCESS ||
- conf->minfs < 0)
+ if (apr_strtoff(&dconf->minfs, arg, NULL, 10) != APR_SUCCESS ||
+ dconf->minfs < 0)
{
return "CacheMinFileSize argument must be a non-negative integer representing the min size of a file to cache in bytes.";
}
@@ -1165,16 +1438,46 @@ static const char
static const char
*set_cache_maxfs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
- disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
- &disk_cache_module);
- if (apr_strtoff(&conf->maxfs, arg, NULL, 0) != APR_SUCCESS ||
- conf->maxfs < 0)
+ disk_cache_dir_conf *dconf = (disk_cache_dir_conf *)in_struct_ptr;
+
+ if (apr_strtoff(&dconf->maxfs, arg, NULL, 10) != APR_SUCCESS ||
+ dconf->maxfs < 0)
{
return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
}
return NULL;
}
+static const char
+*set_cache_readsize(cmd_parms *parms, void *in_struct_ptr, const char *arg)
+{
+ disk_cache_dir_conf *dconf = (disk_cache_dir_conf *)in_struct_ptr;
+
+ if (apr_strtoff(&dconf->readsize, arg, NULL, 10) != APR_SUCCESS ||
+ dconf->readsize < 0)
+ {
+ return "CacheReadSize argument must be a non-negative integer representing the max amount of data to cache in go.";
+ }
+ dconf->readsize_set = 1;
+ return NULL;
+}
+
+static const char
+*set_cache_readtime(cmd_parms *parms, void *in_struct_ptr, const char *arg)
+{
+ disk_cache_dir_conf *dconf = (disk_cache_dir_conf *)in_struct_ptr;
+ apr_off_t milliseconds;
+
+ if (apr_strtoff(&milliseconds, arg, NULL, 10) != APR_SUCCESS ||
+ milliseconds < 0)
+ {
+ return "CacheReadTime argument must be a non-negative integer representing the max amount of time taken to cache in go.";
+ }
+ dconf->readtime = apr_time_from_msec(milliseconds);
+ dconf->readtime_set = 1;
+ return NULL;
+}
+
static const command_rec disk_cache_cmds[] =
{
AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF,
@@ -1183,10 +1486,14 @@ static const command_rec disk_cache_cmds[] =
"The number of levels of subdirectories in the cache"),
AP_INIT_TAKE1("CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF,
"The number of characters in subdirectory names"),
- AP_INIT_TAKE1("CacheMinFileSize", set_cache_minfs, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("CacheMinFileSize", set_cache_minfs, NULL, RSRC_CONF | ACCESS_CONF,
"The minimum file size to cache a document"),
- AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("CacheMaxFileSize", set_cache_maxfs, NULL, RSRC_CONF | ACCESS_CONF,
"The maximum file size to cache a document"),
+ AP_INIT_TAKE1("CacheReadSize", set_cache_readsize, NULL, RSRC_CONF | ACCESS_CONF,
+ "The maximum quantity of data to attempt to read and cache in one go"),
+ AP_INIT_TAKE1("CacheReadTime", set_cache_readtime, NULL, RSRC_CONF | ACCESS_CONF,
+ "The maximum time taken to attempt to read and cache in go"),
{NULL}
};
@@ -1200,6 +1507,8 @@ static const cache_provider cache_disk_provider =
&create_entity,
&open_entity,
&remove_url,
+ &commit_entity,
+ &invalidate_entity
};
static void disk_cache_register_hook(apr_pool_t *p)
@@ -1209,10 +1518,10 @@ static void disk_cache_register_hook(apr_pool_t *p)
&cache_disk_provider);
}
-module AP_MODULE_DECLARE_DATA disk_cache_module = {
+AP_DECLARE_MODULE(cache_disk) = {
STANDARD20_MODULE_STUFF,
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
+ create_dir_config, /* create per-directory config structure */
+ merge_dir_config, /* merge per-directory config structures */
create_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
disk_cache_cmds, /* command apr_table_t */
diff --git a/modules/cache/mod_disk_cache.dsp b/modules/cache/mod_cache_disk.dsp
index 91799b85..19ca39e6 100644
--- a/modules/cache/mod_disk_cache.dsp
+++ b/modules/cache/mod_cache_disk.dsp
@@ -1,24 +1,24 @@
-# Microsoft Developer Studio Project File - Name="mod_disk_cache" - Package Owner=<4>
+# Microsoft Developer Studio Project File - Name="mod_cache_disk" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
-CFG=mod_disk_cache - Win32 Debug
+CFG=mod_cache_disk - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
-!MESSAGE NMAKE /f "mod_disk_cache.mak".
+!MESSAGE NMAKE /f "mod_cache_disk.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
-!MESSAGE NMAKE /f "mod_disk_cache.mak" CFG="mod_disk_cache - Win32 Debug"
+!MESSAGE NMAKE /f "mod_cache_disk.mak" CFG="mod_cache_disk - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
-!MESSAGE "mod_disk_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_disk_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_cache_disk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_cache_disk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
@@ -29,7 +29,7 @@ CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
+!IF "$(CFG)" == "mod_cache_disk - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
@@ -43,25 +43,25 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
-# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fd"Release\mod_disk_cache_src" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fd"Release\mod_cache_disk_src" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /fo"Release/mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache"
+# ADD RSC /l 0x409 /fo"Release/mod_cache_disk.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_cache_disk.so" /d LONG_NAME="cache_disk_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_disk_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_disk_cache.so /opt:ref
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_cache_disk.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache_disk.so /opt:ref
# Begin Special Build Tool
-TargetPath=.\Release\mod_disk_cache.so
+TargetPath=.\Release\mod_cache_disk.so
SOURCE="$(InputPath)"
PostBuild_Desc=Embed .manifest
PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
# End Special Build Tool
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
+!ELSEIF "$(CFG)" == "mod_cache_disk - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
@@ -75,19 +75,19 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
-# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fd"Debug\mod_disk_cache_src" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fd"Debug\mod_cache_disk_src" /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /fo"Debug/mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache"
+# ADD RSC /l 0x409 /fo"Debug/mod_cache_disk.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_cache_disk.so" /d LONG_NAME="cache_disk_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_disk_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_disk_cache.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_cache_disk.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache_disk.so
# Begin Special Build Tool
-TargetPath=.\Debug\mod_disk_cache.so
+TargetPath=.\Debug\mod_cache_disk.so
SOURCE="$(InputPath)"
PostBuild_Desc=Embed .manifest
PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
@@ -97,15 +97,15 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# Begin Target
-# Name "mod_disk_cache - Win32 Release"
-# Name "mod_disk_cache - Win32 Debug"
+# Name "mod_cache_disk - Win32 Release"
+# Name "mod_cache_disk - Win32 Debug"
# Begin Source File
SOURCE=.\mod_cache.h
# End Source File
# Begin Source File
-SOURCE=.\mod_disk_cache.c
+SOURCE=.\mod_cache_disk.c
# End Source File
# Begin Source File
diff --git a/modules/cache/mod_cache_disk.h b/modules/cache/mod_cache_disk.h
new file mode 100644
index 00000000..561ee3b6
--- /dev/null
+++ b/modules/cache/mod_cache_disk.h
@@ -0,0 +1,91 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOD_CACHE_DISK_H
+#define MOD_CACHE_DISK_H
+
+#include "apr_file_io.h"
+
+#include "cache_disk_common.h"
+
+/*
+ * include for mod_cache_disk: Disk Based HTTP 1.1 Cache.
+ */
+
+typedef struct {
+ apr_pool_t *pool;
+ const char *file;
+ apr_file_t *fd;
+ char *tempfile;
+ apr_file_t *tempfd;
+} disk_cache_file_t;
+
+/*
+ * disk_cache_object_t
+ * Pointed to by cache_object_t::vobj
+ */
+typedef struct disk_cache_object {
+ const char *root; /* the location of the cache directory */
+ apr_size_t root_len;
+ const char *prefix;
+ disk_cache_file_t data; /* data file structure */
+ disk_cache_file_t hdrs; /* headers file structure */
+ disk_cache_file_t vary; /* vary file structure */
+ const char *hashfile; /* Computed hash key for this URI */
+ const char *name; /* Requested URI without vary bits - suitable for mortals. */
+ const char *key; /* On-disk prefix; URI with Vary bits (if present) */
+ apr_off_t file_size; /* File size of the cached data file */
+ disk_cache_info_t disk_info; /* Header information. */
+ apr_table_t *headers_in; /* Input headers to save */
+ apr_table_t *headers_out; /* Output headers to save */
+ apr_off_t offset; /* Max size to set aside */
+ apr_time_t timeout; /* Max time to set aside */
+ unsigned int done:1; /* Is the attempt to cache complete? */
+} disk_cache_object_t;
+
+
+/*
+ * mod_cache_disk configuration
+ */
+/* TODO: Make defaults OS specific */
+#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */
+#define DEFAULT_DIRLEVELS 2
+#define DEFAULT_DIRLENGTH 2
+#define DEFAULT_MIN_FILE_SIZE 1
+#define DEFAULT_MAX_FILE_SIZE 1000000
+#define DEFAULT_READSIZE 0
+#define DEFAULT_READTIME 0
+
+typedef struct {
+ const char* cache_root;
+ apr_size_t cache_root_len;
+ int dirlevels; /* Number of levels of subdirectories */
+ int dirlength; /* Length of subdirectory names */
+} disk_cache_conf;
+
+typedef struct {
+ apr_off_t minfs; /* minimum file size for cached files */
+ apr_off_t maxfs; /* maximum file size for cached files */
+ apr_off_t readsize; /* maximum data to attempt to cache in one go */
+ apr_time_t readtime; /* maximum time taken to cache in one go */
+ unsigned int minfs_set:1;
+ unsigned int maxfs_set:1;
+ unsigned int readsize_set:1;
+ unsigned int readtime_set:1;
+} disk_cache_dir_conf;
+
+#endif /*MOD_CACHE_DISK_H*/
+
diff --git a/modules/cache/mod_disk_cache.dep b/modules/cache/mod_disk_cache.dep
deleted file mode 100644
index 2d058f7a..00000000
--- a/modules/cache/mod_disk_cache.dep
+++ /dev/null
@@ -1,42 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by mod_disk_cache.mak
-
-..\..\build\win32\httpd.rc : \
- "..\..\include\ap_release.h"\
-
-
-.\mod_disk_cache.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_provider.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_charset.h"\
- "..\..\include\util_filter.h"\
- "..\..\include\util_script.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\mod_cache.h"\
- ".\mod_disk_cache.h"\
-
diff --git a/modules/cache/mod_disk_cache.h b/modules/cache/mod_disk_cache.h
deleted file mode 100644
index 3c035543..00000000
--- a/modules/cache/mod_disk_cache.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MOD_DISK_CACHE_H
-#define MOD_DISK_CACHE_H
-
-/*
- * include for mod_disk_cache: Disk Based HTTP 1.1 Cache.
- */
-
-#define VARY_FORMAT_VERSION 3
-#define DISK_FORMAT_VERSION 4
-
-#define CACHE_HEADER_SUFFIX ".header"
-#define CACHE_DATA_SUFFIX ".data"
-#define CACHE_VDIR_SUFFIX ".vary"
-
-#define AP_TEMPFILE_PREFIX "/"
-#define AP_TEMPFILE_BASE "aptmp"
-#define AP_TEMPFILE_SUFFIX "XXXXXX"
-#define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
-#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
-#define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
-
-typedef struct {
- /* Indicates the format of the header struct stored on-disk. */
- apr_uint32_t format;
- /* The HTTP status code returned for this response. */
- int status;
- /* The size of the entity name that follows. */
- apr_size_t name_len;
- /* The number of times we've cached this entity. */
- apr_size_t entity_version;
- /* Miscellaneous time values. */
- apr_time_t date;
- apr_time_t expire;
- apr_time_t request_time;
- apr_time_t response_time;
-} disk_cache_info_t;
-
-/*
- * disk_cache_object_t
- * Pointed to by cache_object_t::vobj
- */
-typedef struct disk_cache_object {
- const char *root; /* the location of the cache directory */
- apr_size_t root_len;
- char *tempfile; /* temp file tohold the content */
- const char *prefix;
- const char *datafile; /* name of file where the data will go */
- const char *hdrsfile; /* name of file where the hdrs will go */
- const char *hashfile; /* Computed hash key for this URI */
- const char *name; /* Requested URI without vary bits - suitable for mortals. */
- const char *key; /* On-disk prefix; URI with Vary bits (if present) */
- apr_file_t *fd; /* data file */
- apr_file_t *hfd; /* headers file */
- apr_file_t *tfd; /* temporary file for data */
- apr_off_t file_size; /* File size of the cached data file */
- disk_cache_info_t disk_info; /* Header information. */
-} disk_cache_object_t;
-
-
-/*
- * mod_disk_cache configuration
- */
-/* TODO: Make defaults OS specific */
-#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */
-#define DEFAULT_DIRLEVELS 3
-#define DEFAULT_DIRLENGTH 2
-#define DEFAULT_MIN_FILE_SIZE 1
-#define DEFAULT_MAX_FILE_SIZE 1000000
-
-typedef struct {
- const char* cache_root;
- apr_size_t cache_root_len;
- int dirlevels; /* Number of levels of subdirectories */
- int dirlength; /* Length of subdirectory names */
- apr_off_t minfs; /* minimum file size for cached files */
- apr_off_t maxfs; /* maximum file size for cached files */
-} disk_cache_conf;
-
-#endif /*MOD_DISK_CACHE_H*/
diff --git a/modules/cache/mod_disk_cache.mak b/modules/cache/mod_disk_cache.mak
deleted file mode 100644
index 7e5bec53..00000000
--- a/modules/cache/mod_disk_cache.mak
+++ /dev/null
@@ -1,381 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on mod_disk_cache.dsp
-!IF "$(CFG)" == ""
-CFG=mod_disk_cache - Win32 Debug
-!MESSAGE No configuration specified. Defaulting to mod_disk_cache - Win32 Debug.
-!ENDIF
-
-!IF "$(CFG)" != "mod_disk_cache - Win32 Release" && "$(CFG)" != "mod_disk_cache - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "mod_disk_cache.mak" CFG="mod_disk_cache - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "mod_disk_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_disk_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_disk_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "mod_cache - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_disk_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_cache - Win32 ReleaseCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\mod_disk_cache.obj"
- -@erase "$(INTDIR)\mod_disk_cache.res"
- -@erase "$(INTDIR)\mod_disk_cache_src.idb"
- -@erase "$(INTDIR)\mod_disk_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_disk_cache.exp"
- -@erase "$(OUTDIR)\mod_disk_cache.lib"
- -@erase "$(OUTDIR)\mod_disk_cache.pdb"
- -@erase "$(OUTDIR)\mod_disk_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_disk_cache_src" /FD /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_disk_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_disk_cache.pdb" /debug /out:"$(OUTDIR)\mod_disk_cache.so" /implib:"$(OUTDIR)\mod_disk_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_disk_cache.so /opt:ref
-LINK32_OBJS= \
- "$(INTDIR)\mod_disk_cache.obj" \
- "$(INTDIR)\mod_disk_cache.res" \
- "..\..\srclib\apr\Release\libapr-1.lib" \
- "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
- "..\..\Release\libhttpd.lib" \
- "$(OUTDIR)\mod_cache.lib"
-
-"$(OUTDIR)\mod_disk_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Release\mod_disk_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_disk_cache.so"
- if exist .\Release\mod_disk_cache.so.manifest mt.exe -manifest .\Release\mod_disk_cache.so.manifest -outputresource:.\Release\mod_disk_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_disk_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "mod_cache - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_disk_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_cache - Win32 DebugCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\mod_disk_cache.obj"
- -@erase "$(INTDIR)\mod_disk_cache.res"
- -@erase "$(INTDIR)\mod_disk_cache_src.idb"
- -@erase "$(INTDIR)\mod_disk_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_disk_cache.exp"
- -@erase "$(OUTDIR)\mod_disk_cache.lib"
- -@erase "$(OUTDIR)\mod_disk_cache.pdb"
- -@erase "$(OUTDIR)\mod_disk_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_disk_cache_src" /FD /EHsc /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_disk_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_disk_cache.pdb" /debug /out:"$(OUTDIR)\mod_disk_cache.so" /implib:"$(OUTDIR)\mod_disk_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_disk_cache.so
-LINK32_OBJS= \
- "$(INTDIR)\mod_disk_cache.obj" \
- "$(INTDIR)\mod_disk_cache.res" \
- "..\..\srclib\apr\Debug\libapr-1.lib" \
- "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
- "..\..\Debug\libhttpd.lib" \
- "$(OUTDIR)\mod_cache.lib"
-
-"$(OUTDIR)\mod_disk_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Debug\mod_disk_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_disk_cache.so"
- if exist .\Debug\mod_disk_cache.so.manifest mt.exe -manifest .\Debug\mod_disk_cache.so.manifest -outputresource:.\Debug\mod_disk_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("mod_disk_cache.dep")
-!INCLUDE "mod_disk_cache.dep"
-!ELSE
-!MESSAGE Warning: cannot find "mod_disk_cache.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release" || "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-"libapr - Win32 Release" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
- cd "..\..\modules\cache"
-
-"libapr - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-"libapr - Win32 Debug" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libapr - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-"libaprutil - Win32 Release" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-"libaprutil - Win32 Debug" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-"libhttpd - Win32 Release" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
- cd ".\modules\cache"
-
-"libhttpd - Win32 ReleaseCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-"libhttpd - Win32 Debug" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
- cd ".\modules\cache"
-
-"libhttpd - Win32 DebugCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-"mod_cache - Win32 Release" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Release"
- cd "."
-
-"mod_cache - Win32 ReleaseCLEAN" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Release" RECURSE=1 CLEAN
- cd "."
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-"mod_cache - Win32 Debug" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Debug"
- cd "."
-
-"mod_cache - Win32 DebugCLEAN" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Debug" RECURSE=1 CLEAN
- cd "."
-
-!ENDIF
-
-SOURCE=..\..\build\win32\httpd.rc
-
-!IF "$(CFG)" == "mod_disk_cache - Win32 Release"
-
-
-"$(INTDIR)\mod_disk_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "NDEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache" $(SOURCE)
-
-
-!ELSEIF "$(CFG)" == "mod_disk_cache - Win32 Debug"
-
-
-"$(INTDIR)\mod_disk_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_disk_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "_DEBUG" /d BIN_NAME="mod_disk_cache.so" /d LONG_NAME="disk_cache_module for Apache" $(SOURCE)
-
-
-!ENDIF
-
-SOURCE=.\mod_disk_cache.c
-
-"$(INTDIR)\mod_disk_cache.obj" : $(SOURCE) "$(INTDIR)"
-
-
-
-!ENDIF
-
diff --git a/modules/cache/mod_file_cache.c b/modules/cache/mod_file_cache.c
index 17ec6f4a..8ab3abe5 100644
--- a/modules/cache/mod_file_cache.c
+++ b/modules/cache/mod_file_cache.c
@@ -86,8 +86,6 @@
#include <sys/types.h>
#endif
-#define CORE_PRIVATE
-
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
@@ -135,33 +133,33 @@ static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
fspec = ap_server_root_relative(cmd->pool, filename);
if (!fspec) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
- "mod_file_cache: invalid file path "
+ ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server, APLOGNO(00794)
+ "invalid file path "
"%s, skipping", filename);
return;
}
if ((rc = apr_stat(&tmp.finfo, fspec, APR_FINFO_MIN,
cmd->temp_pool)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
- "mod_file_cache: unable to stat(%s), skipping", fspec);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00795)
+ "unable to stat(%s), skipping", fspec);
return;
}
if (tmp.finfo.filetype != APR_REG) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
- "mod_file_cache: %s isn't a regular file, skipping", fspec);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00796)
+ "%s isn't a regular file, skipping", fspec);
return;
}
if (tmp.finfo.size > AP_MAX_SENDFILE) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
- "mod_file_cache: %s is too large to cache, skipping", fspec);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00797)
+ "%s is too large to cache, skipping", fspec);
return;
}
rc = apr_file_open(&fd, fspec, APR_READ | APR_BINARY | APR_XTHREAD,
APR_OS_DEFAULT, cmd->pool);
if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
- "mod_file_cache: unable to open(%s, O_RDONLY), skipping", fspec);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00798)
+ "unable to open(%s, O_RDONLY), skipping", fspec);
return;
}
apr_file_inherit_set(fd);
@@ -180,8 +178,8 @@ static void cache_the_file(cmd_parms *cmd, const char *filename, int mmap)
(apr_size_t)new_file->finfo.size,
APR_MMAP_READ, cmd->pool)) != APR_SUCCESS) {
apr_file_close(fd);
- ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
- "mod_file_cache: unable to mmap %s, skipping", filename);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server, APLOGNO(00799)
+ "unable to mmap %s, skipping", filename);
return;
}
apr_file_close(fd);
@@ -211,8 +209,8 @@ static const char *cachefilehandle(cmd_parms *cmd, void *dummy, const char *file
cache_the_file(cmd, filename, 0);
#else
/* Sendfile not supported by this OS */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
- "mod_file_cache: unable to cache file: %s. Sendfile is not supported on this OS", filename);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00800)
+ "unable to cache file: %s. Sendfile is not supported on this OS", filename);
#endif
return NULL;
}
@@ -222,8 +220,8 @@ static const char *cachefilemmap(cmd_parms *cmd, void *dummy, const char *filena
cache_the_file(cmd, filename, 1);
#else
/* MMAP not supported by this OS */
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
- "mod_file_cache: unable to cache file: %s. MMAP is not supported by this OS", filename);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00801)
+ "unable to cache file: %s. MMAP is not supported by this OS", filename);
#endif
return NULL;
}
@@ -297,9 +295,8 @@ static int sendfile_handler(request_rec *r, a_file *file)
apr_bucket *b;
apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
- b = apr_bucket_file_create(file->file, 0, (apr_size_t)file->finfo.size,
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, b);
+ apr_brigade_insert_file(bb, file->file, 0, file->finfo.size, r->pool);
+
b = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
@@ -361,17 +358,17 @@ static int file_cache_handler(request_rec *r)
apr_table_setn(r->headers_out, "Last-Modified", datestr);
}
- ap_set_etag(r);
- if ((errstatus = ap_meets_conditions(r)) != OK) {
- return errstatus;
- }
-
/* ap_set_content_length() always converts the same number and never
* returns an error. Accelerate it.
*/
r->clength = match->finfo.size;
apr_table_setn(r->headers_out, "Content-Length", match->sizestr);
+ ap_set_etag(r);
+ if ((errstatus = ap_meets_conditions(r)) != OK) {
+ return errstatus;
+ }
+
/* Call appropriate handler */
if (!r->header_only) {
if (match->is_mmapped == TRUE)
@@ -405,7 +402,7 @@ static void register_hooks(apr_pool_t *p)
}
-module AP_MODULE_DECLARE_DATA file_cache_module =
+AP_DECLARE_MODULE(file_cache) =
{
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
diff --git a/modules/cache/mod_file_cache.dep b/modules/cache/mod_file_cache.dep
deleted file mode 100644
index 2d8bc202..00000000
--- a/modules/cache/mod_file_cache.dep
+++ /dev/null
@@ -1,30 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by mod_file_cache.mak
-
-..\..\build\win32\httpd.rc : \
- "..\..\include\ap_release.h"\
-
-
-.\mod_file_cache.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
-
diff --git a/modules/cache/mod_file_cache.mak b/modules/cache/mod_file_cache.mak
deleted file mode 100644
index 656f1b98..00000000
--- a/modules/cache/mod_file_cache.mak
+++ /dev/null
@@ -1,353 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on mod_file_cache.dsp
-!IF "$(CFG)" == ""
-CFG=mod_file_cache - Win32 Debug
-!MESSAGE No configuration specified. Defaulting to mod_file_cache - Win32 Debug.
-!ENDIF
-
-!IF "$(CFG)" != "mod_file_cache - Win32 Release" && "$(CFG)" != "mod_file_cache - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "mod_file_cache.mak" CFG="mod_file_cache - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "mod_file_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_file_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_file_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_file_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\mod_file_cache.obj"
- -@erase "$(INTDIR)\mod_file_cache.res"
- -@erase "$(INTDIR)\mod_file_cache_src.idb"
- -@erase "$(INTDIR)\mod_file_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_file_cache.exp"
- -@erase "$(OUTDIR)\mod_file_cache.lib"
- -@erase "$(OUTDIR)\mod_file_cache.pdb"
- -@erase "$(OUTDIR)\mod_file_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_file_cache_src" /FD /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_file_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_file_cache.so" /d LONG_NAME="file_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_file_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=/nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_file_cache.pdb" /debug /out:"$(OUTDIR)\mod_file_cache.so" /implib:"$(OUTDIR)\mod_file_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_file_cache.so /opt:ref
-LINK32_OBJS= \
- "$(INTDIR)\mod_file_cache.obj" \
- "$(INTDIR)\mod_file_cache.res" \
- "..\..\srclib\apr\Release\libapr-1.lib" \
- "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
- "..\..\Release\libhttpd.lib"
-
-"$(OUTDIR)\mod_file_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Release\mod_file_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_file_cache.so"
- if exist .\Release\mod_file_cache.so.manifest mt.exe -manifest .\Release\mod_file_cache.so.manifest -outputresource:.\Release\mod_file_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ELSEIF "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_file_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_file_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\mod_file_cache.obj"
- -@erase "$(INTDIR)\mod_file_cache.res"
- -@erase "$(INTDIR)\mod_file_cache_src.idb"
- -@erase "$(INTDIR)\mod_file_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_file_cache.exp"
- -@erase "$(OUTDIR)\mod_file_cache.lib"
- -@erase "$(OUTDIR)\mod_file_cache.pdb"
- -@erase "$(OUTDIR)\mod_file_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_file_cache_src" /FD /EHsc /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_file_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_file_cache.so" /d LONG_NAME="file_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_file_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=/nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_file_cache.pdb" /debug /out:"$(OUTDIR)\mod_file_cache.so" /implib:"$(OUTDIR)\mod_file_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_file_cache.so
-LINK32_OBJS= \
- "$(INTDIR)\mod_file_cache.obj" \
- "$(INTDIR)\mod_file_cache.res" \
- "..\..\srclib\apr\Debug\libapr-1.lib" \
- "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
- "..\..\Debug\libhttpd.lib"
-
-"$(OUTDIR)\mod_file_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Debug\mod_file_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_file_cache.so"
- if exist .\Debug\mod_file_cache.so.manifest mt.exe -manifest .\Debug\mod_file_cache.so.manifest -outputresource:.\Debug\mod_file_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("mod_file_cache.dep")
-!INCLUDE "mod_file_cache.dep"
-!ELSE
-!MESSAGE Warning: cannot find "mod_file_cache.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release" || "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release"
-
-"libapr - Win32 Release" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
- cd "..\..\modules\cache"
-
-"libapr - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-"libapr - Win32 Debug" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libapr - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release"
-
-"libaprutil - Win32 Release" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-"libaprutil - Win32 Debug" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release"
-
-"libhttpd - Win32 Release" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
- cd ".\modules\cache"
-
-"libhttpd - Win32 ReleaseCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-"libhttpd - Win32 Debug" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
- cd ".\modules\cache"
-
-"libhttpd - Win32 DebugCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ENDIF
-
-SOURCE=..\..\build\win32\httpd.rc
-
-!IF "$(CFG)" == "mod_file_cache - Win32 Release"
-
-
-"$(INTDIR)\mod_file_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_file_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "NDEBUG" /d BIN_NAME="mod_file_cache.so" /d LONG_NAME="file_cache_module for Apache" $(SOURCE)
-
-
-!ELSEIF "$(CFG)" == "mod_file_cache - Win32 Debug"
-
-
-"$(INTDIR)\mod_file_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_file_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "_DEBUG" /d BIN_NAME="mod_file_cache.so" /d LONG_NAME="file_cache_module for Apache" $(SOURCE)
-
-
-!ENDIF
-
-SOURCE=.\mod_file_cache.c
-
-"$(INTDIR)\mod_file_cache.obj" : $(SOURCE) "$(INTDIR)"
-
-
-
-!ENDIF
-
diff --git a/modules/cache/mod_mem_cache.c b/modules/cache/mod_mem_cache.c
deleted file mode 100644
index 38aa8f84..00000000
--- a/modules/cache/mod_mem_cache.c
+++ /dev/null
@@ -1,1043 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Rules for managing obj->refcount:
- * refcount should be incremented when an object is placed in the cache. Insertion
- * of an object into the cache and the refcount increment should happen under
- * protection of the sconf->lock.
- *
- * refcount should be decremented when the object is removed from the cache.
- * Object should be removed from the cache and the refcount decremented while
- * under protection of the sconf->lock.
- *
- * refcount should be incremented when an object is retrieved from the cache
- * by a worker thread. The retrieval/find operation and refcount increment
- * should occur under protection of the sconf->lock
- *
- * refcount can be atomically decremented w/o protection of the sconf->lock
- * by worker threads.
- *
- * Any object whose refcount drops to 0 should be freed/cleaned up. A refcount
- * of 0 means the object is not in the cache and no worker threads are accessing
- * it.
- */
-#define CORE_PRIVATE
-#include "mod_cache.h"
-#include "cache_pqueue.h"
-#include "cache_cache.h"
-#include "ap_provider.h"
-#include "ap_mpm.h"
-#include "apr_thread_mutex.h"
-#if APR_HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#if !APR_HAS_THREADS
-#error This module does not currently compile unless you have a thread-capable APR. Sorry!
-#endif
-
-module AP_MODULE_DECLARE_DATA mem_cache_module;
-
-typedef enum {
- CACHE_TYPE_FILE = 1,
- CACHE_TYPE_HEAP,
- CACHE_TYPE_MMAP
-} cache_type_e;
-
-typedef struct mem_cache_object {
- apr_pool_t *pool;
- apr_thread_mutex_t *lock; /* pools aren't thread-safe; use this lock when accessing this pool */
- cache_type_e type;
- apr_table_t *header_out;
- apr_table_t *req_hdrs; /* for Vary negotiation */
- apr_size_t m_len;
- void *m;
- apr_os_file_t fd;
- apr_int32_t flags; /* File open flags */
- long priority; /**< the priority of this entry */
- long total_refs; /**< total number of references this entry has had */
-
- apr_uint32_t pos; /**< the position of this entry in the cache */
-
-} mem_cache_object_t;
-
-typedef struct {
- apr_thread_mutex_t *lock;
- cache_cache_t *cache_cache;
-
- /* Fields set by config directives */
- apr_size_t min_cache_object_size; /* in bytes */
- apr_size_t max_cache_object_size; /* in bytes */
- apr_size_t max_cache_size; /* in bytes */
- apr_size_t max_object_cnt;
- cache_pqueue_set_priority cache_remove_algorithm;
-
- /* maximum amount of data to buffer on a streamed response where
- * we haven't yet seen EOS */
- apr_off_t max_streaming_buffer_size;
-} mem_cache_conf;
-static mem_cache_conf *sconf;
-
-static int threaded_mpm;
-
-#define DEFAULT_MAX_CACHE_SIZE 100*1024
-#define DEFAULT_MIN_CACHE_OBJECT_SIZE 1
-#define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000
-#define DEFAULT_MAX_OBJECT_CNT 1009
-#define DEFAULT_MAX_STREAMING_BUFFER_SIZE 100000
-#define CACHEFILE_LEN 20
-
-/* Forward declarations */
-static int remove_entity(cache_handle_t *h);
-static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);
-static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
-static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);
-static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
-
-static void cleanup_cache_object(cache_object_t *obj);
-
-static long memcache_get_priority(void*a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
-
- return mobj->priority;
-}
-
-static void memcache_inc_frequency(void*a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
-
- mobj->total_refs++;
- mobj->priority = 0;
-}
-
-static void memcache_set_pos(void *a, apr_ssize_t pos)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
-
- apr_atomic_set32(&mobj->pos, pos);
-}
-static apr_ssize_t memcache_get_pos(void *a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
-
- return apr_atomic_read32(&mobj->pos);
-}
-
-static apr_size_t memcache_cache_get_size(void*a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
- return mobj->m_len;
-}
-/** callback to get the key of a item */
-static const char* memcache_cache_get_key(void*a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- return obj->key;
-}
-/**
- * memcache_cache_free()
- * memcache_cache_free is a callback that is only invoked by a thread
- * running in cache_insert(). cache_insert() runs under protection
- * of sconf->lock. By the time this function has been entered, the cache_object
- * has been ejected from the cache. decrement the refcount and if the refcount drops
- * to 0, cleanup the cache object.
- */
-static void memcache_cache_free(void*a)
-{
- cache_object_t *obj = (cache_object_t *)a;
-
- /* Decrement the refcount to account for the object being ejected
- * from the cache. If the refcount is 0, free the object.
- */
- if (!apr_atomic_dec32(&obj->refcount)) {
- cleanup_cache_object(obj);
- }
-}
-/*
- * functions return a 'negative' score since priority queues
- * dequeue the object with the highest value first
- */
-static long memcache_lru_algorithm(long queue_clock, void *a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
- if (mobj->priority == 0)
- mobj->priority = queue_clock - mobj->total_refs;
-
- /*
- * a 'proper' LRU function would just be
- * mobj->priority = mobj->total_refs;
- */
- return mobj->priority;
-}
-
-static long memcache_gdsf_algorithm(long queue_clock, void *a)
-{
- cache_object_t *obj = (cache_object_t *)a;
- mem_cache_object_t *mobj = obj->vobj;
-
- if (mobj->priority == 0)
- mobj->priority = queue_clock -
- (long)(mobj->total_refs*1000 / mobj->m_len);
-
- return mobj->priority;
-}
-
-static void cleanup_cache_object(cache_object_t *obj)
-{
- mem_cache_object_t *mobj = obj->vobj;
-
- /* Cleanup the mem_cache_object_t */
- if (mobj) {
- if (mobj->m) {
- free(mobj->m);
- }
- if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {
-#ifdef WIN32
- CloseHandle(mobj->fd);
-#else
- close(mobj->fd);
-#endif
- }
- apr_pool_destroy(mobj->pool);
- }
-}
-static apr_status_t decrement_refcount(void *arg)
-{
- cache_object_t *obj = (cache_object_t *) arg;
-
- /* If obj->complete is not set, the cache update failed and the
- * object needs to be removed from the cache then cleaned up.
- * The garbage collector may have ejected the object from the
- * cache already, so make sure it is really still in the cache
- * before attempting to remove it.
- */
- if (!obj->complete) {
- cache_object_t *tobj = NULL;
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
- tobj = cache_find(sconf->cache_cache, obj->key);
- if (tobj == obj) {
- cache_remove(sconf->cache_cache, obj);
- apr_atomic_dec32(&obj->refcount);
- }
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
- }
-
- /* If the refcount drops to 0, cleanup the cache object */
- if (!apr_atomic_dec32(&obj->refcount)) {
- cleanup_cache_object(obj);
- }
- return APR_SUCCESS;
-}
-static apr_status_t cleanup_cache_mem(void *sconfv)
-{
- cache_object_t *obj;
- mem_cache_conf *co = (mem_cache_conf*) sconfv;
-
- if (!co) {
- return APR_SUCCESS;
- }
- if (!co->cache_cache) {
- return APR_SUCCESS;
- }
-
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
- obj = cache_pop(co->cache_cache);
- while (obj) {
- /* Iterate over the cache and clean up each unreferenced entry */
- if (!apr_atomic_dec32(&obj->refcount)) {
- cleanup_cache_object(obj);
- }
- obj = cache_pop(co->cache_cache);
- }
-
- /* Cache is empty, free the cache table */
- cache_free(co->cache_cache);
-
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
- return APR_SUCCESS;
-}
-/*
- * TODO: enable directives to be overridden in various containers
- */
-static void *create_cache_config(apr_pool_t *p, server_rec *s)
-{
- sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
-
- sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE;
- sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE;
- /* Number of objects in the cache */
- sconf->max_object_cnt = DEFAULT_MAX_OBJECT_CNT;
- /* Size of the cache in bytes */
- sconf->max_cache_size = DEFAULT_MAX_CACHE_SIZE;
- sconf->cache_cache = NULL;
- sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
- sconf->max_streaming_buffer_size = DEFAULT_MAX_STREAMING_BUFFER_SIZE;
-
- return sconf;
-}
-
-static int create_entity(cache_handle_t *h, cache_type_e type_e,
- request_rec *r, const char *key, apr_off_t len)
-{
- apr_status_t rv;
- apr_pool_t *pool;
- cache_object_t *obj, *tmp_obj;
- mem_cache_object_t *mobj;
-
- if (len == -1) {
- /* Caching a streaming response. Assume the response is
- * less than or equal to max_streaming_buffer_size. We will
- * correct all the cache size counters in store_body once
- * we know exactly know how much we are caching.
- */
- len = sconf->max_streaming_buffer_size;
- }
-
- /* Note: cache_insert() will automatically garbage collect
- * objects from the cache if the max_cache_size threshold is
- * exceeded. This means mod_mem_cache does not need to implement
- * max_cache_size checks.
- */
- if (len < sconf->min_cache_object_size ||
- len > sconf->max_cache_object_size) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "mem_cache: URL %s failed the size check and will not be cached.",
- key);
- return DECLINED;
- }
-
- if (type_e == CACHE_TYPE_FILE) {
- /* CACHE_TYPE_FILE is only valid for local content handled by the
- * default handler. Need a better way to check if the file is
- * local or not.
- */
- if (!r->filename) {
- return DECLINED;
- }
- }
-
- rv = apr_pool_create(&pool, NULL);
-
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
- "mem_cache: Failed to create memory pool.");
- return DECLINED;
- }
-
- /* Allocate and initialize cache_object_t */
- obj = apr_pcalloc(pool, sizeof(*obj));
- obj->key = apr_pstrdup(pool, key);
-
- /* Allocate and init mem_cache_object_t */
- mobj = apr_pcalloc(pool, sizeof(*mobj));
- mobj->pool = pool;
-
- if (threaded_mpm) {
- apr_thread_mutex_create(&mobj->lock, APR_THREAD_MUTEX_DEFAULT, pool);
- }
-
- /* Finish initing the cache object */
- apr_atomic_set32(&obj->refcount, 1);
- mobj->total_refs = 1;
- obj->complete = 0;
- obj->vobj = mobj;
- /* Safe cast: We tested < sconf->max_cache_object_size above */
- mobj->m_len = (apr_size_t)len;
- mobj->type = type_e;
-
- /* Place the cache_object_t into the hash table.
- * Note: Perhaps we should wait to put the object in the
- * hash table when the object is complete? I add the object here to
- * avoid multiple threads attempting to cache the same content only
- * to discover at the very end that only one of them will succeed.
- * Furthermore, adding the cache object to the table at the end could
- * open up a subtle but easy to exploit DoS hole: someone could request
- * a very large file with multiple requests. Better to detect this here
- * rather than after the cache object has been completely built and
- * initialized...
- * XXX Need a way to insert into the cache w/o such coarse grained locking
- */
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
- tmp_obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
-
- if (!tmp_obj) {
- cache_insert(sconf->cache_cache, obj);
- /* Add a refcount to account for the reference by the
- * hashtable in the cache. Refcount should be 2 now, one
- * for this thread, and one for the cache.
- */
- apr_atomic_inc32(&obj->refcount);
- }
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
-
- if (tmp_obj) {
- /* This thread collided with another thread loading the same object
- * into the cache at the same time. Defer to the other thread which
- * is further along.
- */
- cleanup_cache_object(obj);
- return DECLINED;
- }
-
- apr_pool_cleanup_register(r->pool, obj, decrement_refcount,
- apr_pool_cleanup_null);
-
- /* Populate the cache handle */
- h->cache_obj = obj;
-
- return OK;
-}
-
-static int create_mem_entity(cache_handle_t *h, request_rec *r,
- const char *key, apr_off_t len)
-{
- return create_entity(h, CACHE_TYPE_HEAP, r, key, len);
-}
-
-static int create_fd_entity(cache_handle_t *h, request_rec *r,
- const char *key, apr_off_t len)
-{
- return create_entity(h, CACHE_TYPE_FILE, r, key, len);
-}
-
-static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
-{
- cache_object_t *obj;
-
- /* Look up entity keyed to 'url' */
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
- obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
- if (obj) {
- if (obj->complete) {
- request_rec *rmain=r, *rtmp;
- apr_atomic_inc32(&obj->refcount);
- /* cache is worried about overall counts, not 'open' ones */
- cache_update(sconf->cache_cache, obj);
-
- /* If this is a subrequest, register the cleanup against
- * the main request. This will prevent the cache object
- * from being cleaned up from under the request after the
- * subrequest is destroyed.
- */
- rtmp = r;
- while (rtmp) {
- rmain = rtmp;
- rtmp = rmain->main;
- }
- apr_pool_cleanup_register(rmain->pool, obj, decrement_refcount,
- apr_pool_cleanup_null);
- }
- else {
- obj = NULL;
- }
- }
-
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
-
- if (!obj) {
- return DECLINED;
- }
-
- /* Initialize the cache_handle */
- h->cache_obj = obj;
- h->req_hdrs = NULL; /* Pick these up in recall_headers() */
- return OK;
-}
-
-/* remove_entity()
- * Notes:
- * refcount should be at least 1 upon entry to this function to account
- * for this thread's reference to the object. If the refcount is 1, then
- * object has been removed from the cache by another thread and this thread
- * is the last thread accessing the object.
- */
-static int remove_entity(cache_handle_t *h)
-{
- cache_object_t *obj = h->cache_obj;
- cache_object_t *tobj = NULL;
-
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
-
- /* If the entity is still in the cache, remove it and decrement the
- * refcount. If the entity is not in the cache, do nothing. In both cases
- * decrement_refcount called by the last thread referencing the object will
- * trigger the cleanup.
- */
- tobj = cache_find(sconf->cache_cache, obj->key);
- if (tobj == obj) {
- cache_remove(sconf->cache_cache, obj);
- apr_atomic_dec32(&obj->refcount);
- }
-
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
-
- return OK;
-}
-
-/* Define request processing hook handlers */
-/* remove_url()
- * Notes:
- */
-static int remove_url(cache_handle_t *h, apr_pool_t *p)
-{
- cache_object_t *obj;
- int cleanup = 0;
-
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
-
- obj = h->cache_obj;
- if (obj) {
- cache_remove(sconf->cache_cache, obj);
- /* For performance, cleanup cache object after releasing the lock */
- cleanup = !apr_atomic_dec32(&obj->refcount);
- }
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
-
- if (cleanup) {
- cleanup_cache_object(obj);
- }
-
- return OK;
-}
-
-static apr_table_t *deep_table_copy(apr_pool_t *p, const apr_table_t *table)
-{
- const apr_array_header_t *array = apr_table_elts(table);
- apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
- apr_table_t *copy = apr_table_make(p, array->nelts);
- int i;
-
- for (i = 0; i < array->nelts; i++) {
- if (elts[i].key) {
- apr_table_add(copy, elts[i].key, elts[i].val);
- }
- }
-
- return copy;
-}
-
-static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
-{
- mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
-
- h->req_hdrs = deep_table_copy(r->pool, mobj->req_hdrs);
- h->resp_hdrs = deep_table_copy(r->pool, mobj->header_out);
-
- return OK;
-}
-
-static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
-{
- apr_bucket *b;
- mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
-
- if (mobj->type == CACHE_TYPE_FILE) {
- /* CACHE_TYPE_FILE */
- apr_file_t *file;
- apr_os_file_put(&file, &mobj->fd, mobj->flags, p);
- b = apr_bucket_file_create(file, 0, mobj->m_len, p, bb->bucket_alloc);
- }
- else {
- /* CACHE_TYPE_HEAP */
- b = apr_bucket_immortal_create(mobj->m, mobj->m_len, bb->bucket_alloc);
- }
- APR_BRIGADE_INSERT_TAIL(bb, b);
- b = apr_bucket_eos_create(bb->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, b);
-
- return APR_SUCCESS;
-}
-
-
-static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
-{
- cache_object_t *obj = h->cache_obj;
- mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
- apr_table_t *headers_out;
-
- /*
- * The cache needs to keep track of the following information:
- * - Date, LastMod, Version, ReqTime, RespTime, ContentLength
- * - The original request headers (for Vary)
- * - The original response headers (for returning with a cached response)
- * - The body of the message
- */
- if (mobj->lock) {
- apr_thread_mutex_lock(mobj->lock);
- }
- mobj->req_hdrs = deep_table_copy(mobj->pool, r->headers_in);
- if (mobj->lock) {
- apr_thread_mutex_unlock(mobj->lock);
- }
-
- /* Precompute how much storage we need to hold the headers */
- headers_out = apr_table_overlay(r->pool, r->headers_out,
- r->err_headers_out);
- headers_out = ap_cache_cacheable_hdrs_out(r->pool, headers_out,
- r->server);
-
- /* If not set in headers_out, set Content-Type */
- if (!apr_table_get(headers_out, "Content-Type")
- && r->content_type) {
- apr_table_setn(headers_out, "Content-Type",
- ap_make_content_type(r, r->content_type));
- }
-
- if (!apr_table_get(headers_out, "Content-Encoding")
- && r->content_encoding) {
- apr_table_setn(headers_out, "Content-Encoding",
- r->content_encoding);
- }
-
- if (mobj->lock) {
- apr_thread_mutex_lock(mobj->lock);
- }
- mobj->header_out = deep_table_copy(mobj->pool, headers_out);
- if (mobj->lock) {
- apr_thread_mutex_unlock(mobj->lock);
- }
-
- /* Init the info struct */
- obj->info.status = info->status;
- if (info->date) {
- obj->info.date = info->date;
- }
- if (info->response_time) {
- obj->info.response_time = info->response_time;
- }
- if (info->request_time) {
- obj->info.request_time = info->request_time;
- }
- if (info->expire) {
- obj->info.expire = info->expire;
- }
-
- return APR_SUCCESS;
-}
-
-static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
-{
- apr_status_t rv;
- cache_object_t *obj = h->cache_obj;
- cache_object_t *tobj = NULL;
- mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
- apr_read_type_e eblock = APR_BLOCK_READ;
- apr_bucket *e;
- char *cur;
- int eos = 0;
-
- if (mobj->type == CACHE_TYPE_FILE) {
- apr_file_t *file = NULL;
- int fd = 0;
- int other = 0;
-
- /* We can cache an open file descriptor if:
- * - the brigade contains one and only one file_bucket &&
- * - the brigade is complete &&
- * - the file_bucket is the last data bucket in the brigade
- */
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- if (APR_BUCKET_IS_EOS(e)) {
- eos = 1;
- }
- else if (APR_BUCKET_IS_FILE(e)) {
- apr_bucket_file *a = e->data;
- fd++;
- file = a->fd;
- }
- else {
- other++;
- }
- }
- if (fd == 1 && !other && eos) {
- apr_file_t *tmpfile;
- const char *name;
- /* Open a new XTHREAD handle to the file */
- apr_file_name_get(&name, file);
- mobj->flags = ((APR_SENDFILE_ENABLED & apr_file_flags_get(file))
- | APR_READ | APR_BINARY | APR_XTHREAD | APR_FILE_NOCLEANUP);
- rv = apr_file_open(&tmpfile, name, mobj->flags,
- APR_OS_DEFAULT, r->pool);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- apr_file_inherit_unset(tmpfile);
- apr_os_file_get(&(mobj->fd), tmpfile);
-
- /* Open for business */
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "mem_cache: Cached file: %s with key: %s", name, obj->key);
- obj->complete = 1;
- return APR_SUCCESS;
- }
-
- /* Content not suitable for fd caching. Cache in-memory instead. */
- mobj->type = CACHE_TYPE_HEAP;
- }
-
- /*
- * FD cacheing is not enabled or the content was not
- * suitable for fd caching.
- */
- if (mobj->m == NULL) {
- mobj->m = malloc(mobj->m_len);
- if (mobj->m == NULL) {
- return APR_ENOMEM;
- }
- obj->count = 0;
- }
- cur = (char*) mobj->m + obj->count;
-
- /* Iterate accross the brigade and populate the cache storage */
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- const char *s;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(e)) {
- const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
- if (cl_header) {
- apr_int64_t cl = apr_atoi64(cl_header);
- if ((errno == 0) && (obj->count != cl)) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "mem_cache: URL %s didn't receive complete response, not caching",
- h->cache_obj->key);
- return APR_EGENERAL;
- }
- }
- if (mobj->m_len > obj->count) {
- /* Caching a streamed response. Reallocate a buffer of the
- * correct size and copy the streamed response into that
- * buffer */
- mobj->m = realloc(mobj->m, obj->count);
- if (!mobj->m) {
- return APR_ENOMEM;
- }
-
- /* Now comes the crufty part... there is no way to tell the
- * cache that the size of the object has changed. We need
- * to remove the object, update the size and re-add the
- * object, all under protection of the lock.
- */
- if (sconf->lock) {
- apr_thread_mutex_lock(sconf->lock);
- }
- /* Has the object been ejected from the cache?
- */
- tobj = (cache_object_t *) cache_find(sconf->cache_cache, obj->key);
- if (tobj == obj) {
- /* Object is still in the cache, remove it, update the len field then
- * replace it under protection of sconf->lock.
- */
- cache_remove(sconf->cache_cache, obj);
- /* For illustration, cache no longer has reference to the object
- * so decrement the refcount
- * apr_atomic_dec32(&obj->refcount);
- */
- mobj->m_len = obj->count;
-
- cache_insert(sconf->cache_cache, obj);
- /* For illustration, cache now has reference to the object, so
- * increment the refcount
- * apr_atomic_inc32(&obj->refcount);
- */
- }
- else if (tobj) {
- /* Different object with the same key found in the cache. Doing nothing
- * here will cause the object refcount to drop to 0 in decrement_refcount
- * and the object will be cleaned up.
- */
-
- } else {
- /* Object has been ejected from the cache, add it back to the cache */
- mobj->m_len = obj->count;
- cache_insert(sconf->cache_cache, obj);
- apr_atomic_inc32(&obj->refcount);
- }
-
- if (sconf->lock) {
- apr_thread_mutex_unlock(sconf->lock);
- }
- }
- /* Open for business */
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
- "mem_cache: Cached url: %s", obj->key);
- obj->complete = 1;
- break;
- }
- rv = apr_bucket_read(e, &s, &len, eblock);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- if (len) {
- /* Check for buffer (max_streaming_buffer_size) overflow */
- if ((obj->count + len) > mobj->m_len) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "mem_cache: URL %s exceeds the MCacheMaxStreamingBuffer (%" APR_SIZE_T_FMT ") limit and will not be cached.",
- obj->key, mobj->m_len);
- return APR_ENOMEM;
- }
- else {
- memcpy(cur, s, len);
- cur+=len;
- obj->count+=len;
- }
- }
- /* This should not fail, but if it does, we are in BIG trouble
- * cause we just stomped all over the heap.
- */
- AP_DEBUG_ASSERT(obj->count <= mobj->m_len);
- }
- return APR_SUCCESS;
-}
-/**
- * Configuration and start-up
- */
-static int mem_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
- apr_pool_t *ptemp, server_rec *s)
-{
- /* Sanity check the cache configuration */
- if (sconf->min_cache_object_size >= sconf->max_cache_object_size) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
- "MCacheMaxObjectSize must be greater than MCacheMinObjectSize");
- return DONE;
- }
- if (sconf->max_cache_object_size >= sconf->max_cache_size) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
- "MCacheSize must be greater than MCacheMaxObjectSize");
- return DONE;
- }
- if (sconf->max_streaming_buffer_size > sconf->max_cache_object_size) {
- /* Issue a notice only if something other than the default config
- * is being used */
- if (sconf->max_streaming_buffer_size != DEFAULT_MAX_STREAMING_BUFFER_SIZE &&
- sconf->max_cache_object_size != DEFAULT_MAX_CACHE_OBJECT_SIZE) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
- "MCacheMaxStreamingBuffer must be less than or equal to MCacheMaxObjectSize. "
- "Resetting MCacheMaxStreamingBuffer to MCacheMaxObjectSize.");
- }
- sconf->max_streaming_buffer_size = sconf->max_cache_object_size;
- }
- if (sconf->max_streaming_buffer_size < sconf->min_cache_object_size) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
- "MCacheMaxStreamingBuffer must be greater than or equal to MCacheMinObjectSize. "
- "Resetting MCacheMaxStreamingBuffer to MCacheMinObjectSize.");
- sconf->max_streaming_buffer_size = sconf->min_cache_object_size;
- }
- ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
- if (threaded_mpm) {
- apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
- }
-
- sconf->cache_cache = cache_init(sconf->max_object_cnt,
- sconf->max_cache_size,
- memcache_get_priority,
- sconf->cache_remove_algorithm,
- memcache_get_pos,
- memcache_set_pos,
- memcache_inc_frequency,
- memcache_cache_get_size,
- memcache_cache_get_key,
- memcache_cache_free);
- apr_pool_cleanup_register(p, sconf, cleanup_cache_mem, apr_pool_cleanup_null);
-
- if (sconf->cache_cache)
- return OK;
-
- return -1;
-
-}
-
-static const char
-*set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
-{
- apr_size_t val;
-
- if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
- return "MCacheSize argument must be an integer representing the max cache size in KBytes.";
- }
- sconf->max_cache_size = val*1024;
- return NULL;
-}
-static const char
-*set_min_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
-{
- apr_size_t val;
-
- if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
- return "MCacheMinObjectSize value must be an positive integer (bytes)";
- }
- if (val > 0)
- sconf->min_cache_object_size = val;
- else
- return "MCacheMinObjectSize value must be an positive integer (bytes)";
- return NULL;
-}
-static const char
-*set_max_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
-{
- apr_size_t val;
-
- if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
- return "MCacheMaxObjectSize value must be an integer (bytes)";
- }
- sconf->max_cache_object_size = val;
- return NULL;
-}
-static const char
-*set_max_object_count(cmd_parms *parms, void *in_struct_ptr, const char *arg)
-{
- apr_size_t val;
-
- if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
- return "MCacheMaxObjectCount value must be an integer";
- }
- sconf->max_object_cnt = val;
- return NULL;
-}
-
-static const char
-*set_cache_removal_algorithm(cmd_parms *parms, void *name, const char *arg)
-{
- if (strcasecmp("LRU", arg)) {
- sconf->cache_remove_algorithm = memcache_lru_algorithm;
- }
- else {
- if (strcasecmp("GDSF", arg)) {
- sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
- }
- else {
- return "currently implemented algorithms are LRU and GDSF";
- }
- }
- return NULL;
-}
-
-static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
- const char *arg)
-{
- char *err;
- if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
- return "MCacheMaxStreamingBuffer value must be a number";
- }
-
- return NULL;
-}
-
-static const command_rec cache_cmds[] =
-{
- AP_INIT_TAKE1("MCacheSize", set_max_cache_size, NULL, RSRC_CONF,
- "The maximum amount of memory used by the cache in KBytes"),
- AP_INIT_TAKE1("MCacheMaxObjectCount", set_max_object_count, NULL, RSRC_CONF,
- "The maximum number of objects allowed to be placed in the cache"),
- AP_INIT_TAKE1("MCacheMinObjectSize", set_min_cache_object_size, NULL, RSRC_CONF,
- "The minimum size (in bytes) of an object to be placed in the cache"),
- AP_INIT_TAKE1("MCacheMaxObjectSize", set_max_cache_object_size, NULL, RSRC_CONF,
- "The maximum size (in bytes) of an object to be placed in the cache"),
- AP_INIT_TAKE1("MCacheRemovalAlgorithm", set_cache_removal_algorithm, NULL, RSRC_CONF,
- "The algorithm used to remove entries from the cache (default: GDSF)"),
- AP_INIT_TAKE1("MCacheMaxStreamingBuffer", set_max_streaming_buffer, NULL, RSRC_CONF,
- "Maximum number of bytes of content to buffer for a streamed response"),
- {NULL}
-};
-
-static const cache_provider cache_mem_provider =
-{
- &remove_entity,
- &store_headers,
- &store_body,
- &recall_headers,
- &recall_body,
- &create_mem_entity,
- &open_entity,
- &remove_url,
-};
-
-static const cache_provider cache_fd_provider =
-{
- &remove_entity,
- &store_headers,
- &store_body,
- &recall_headers,
- &recall_body,
- &create_fd_entity,
- &open_entity,
- &remove_url,
-};
-
-static void register_hooks(apr_pool_t *p)
-{
- ap_hook_post_config(mem_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
- /* cache initializer */
- /* cache_hook_init(cache_mem_init, NULL, NULL, APR_HOOK_MIDDLE); */
- /*
- cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE);
- cache_hook_open_entity(open_entity, NULL, NULL, APR_HOOK_MIDDLE);
- cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE);
- */
- ap_register_provider(p, CACHE_PROVIDER_GROUP, "mem", "0",
- &cache_mem_provider);
- ap_register_provider(p, CACHE_PROVIDER_GROUP, "fd", "0",
- &cache_fd_provider);
-}
-
-module AP_MODULE_DECLARE_DATA mem_cache_module =
-{
- STANDARD20_MODULE_STUFF,
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
- create_cache_config, /* create per-server config structure */
- NULL, /* merge per-server config structures */
- cache_cmds, /* command apr_table_t */
- register_hooks
-};
diff --git a/modules/cache/mod_mem_cache.dep b/modules/cache/mod_mem_cache.dep
deleted file mode 100644
index 2cbefb3f..00000000
--- a/modules/cache/mod_mem_cache.dep
+++ /dev/null
@@ -1,116 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by mod_mem_cache.mak
-
-.\cache_cache.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\cache_cache.h"\
- ".\cache_hash.h"\
- ".\cache_pqueue.h"\
- ".\mod_cache.h"\
-
-
-.\cache_hash.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\cache_hash.h"\
- ".\mod_cache.h"\
-
-
-.\cache_pqueue.c : \
- ".\cache_pqueue.h"\
-
-
-.\mod_mem_cache.c : \
- "..\..\include\ap_config.h"\
- "..\..\include\ap_mmn.h"\
- "..\..\include\ap_mpm.h"\
- "..\..\include\ap_provider.h"\
- "..\..\include\ap_regex.h"\
- "..\..\include\ap_release.h"\
- "..\..\include\http_config.h"\
- "..\..\include\http_connection.h"\
- "..\..\include\http_core.h"\
- "..\..\include\http_log.h"\
- "..\..\include\http_main.h"\
- "..\..\include\http_protocol.h"\
- "..\..\include\http_request.h"\
- "..\..\include\http_vhost.h"\
- "..\..\include\httpd.h"\
- "..\..\include\os.h"\
- "..\..\include\util_cfgtree.h"\
- "..\..\include\util_filter.h"\
- "..\..\srclib\apr-util\include\apr_date.h"\
- "..\..\srclib\apr-util\include\apr_hooks.h"\
- "..\..\srclib\apr-util\include\apr_md5.h"\
- "..\..\srclib\apr-util\include\apr_optional.h"\
- "..\..\srclib\apr-util\include\apr_optional_hooks.h"\
- "..\..\srclib\apr-util\include\apr_uri.h"\
- "..\..\srclib\apr\include\apr_atomic.h"\
- "..\..\srclib\apr\include\apr_hash.h"\
- "..\..\srclib\apr\include\apr_lib.h"\
- "..\..\srclib\apr\include\apr_mmap.h"\
- "..\..\srclib\apr\include\apr_poll.h"\
- "..\..\srclib\apr\include\apr_portable.h"\
- "..\..\srclib\apr\include\apr_strings.h"\
- ".\cache_cache.h"\
- ".\cache_pqueue.h"\
- ".\mod_cache.h"\
-
-
-..\..\build\win32\httpd.rc : \
- "..\..\include\ap_release.h"\
-
diff --git a/modules/cache/mod_mem_cache.mak b/modules/cache/mod_mem_cache.mak
deleted file mode 100644
index ece8e6f9..00000000
--- a/modules/cache/mod_mem_cache.mak
+++ /dev/null
@@ -1,407 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on mod_mem_cache.dsp
-!IF "$(CFG)" == ""
-CFG=mod_mem_cache - Win32 Debug
-!MESSAGE No configuration specified. Defaulting to mod_mem_cache - Win32 Debug.
-!ENDIF
-
-!IF "$(CFG)" != "mod_mem_cache - Win32 Release" && "$(CFG)" != "mod_mem_cache - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "mod_mem_cache.mak" CFG="mod_mem_cache - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "mod_mem_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_mem_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_mem_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "mod_cache - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_mem_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_cache - Win32 ReleaseCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\cache_cache.obj"
- -@erase "$(INTDIR)\cache_hash.obj"
- -@erase "$(INTDIR)\cache_pqueue.obj"
- -@erase "$(INTDIR)\mod_mem_cache.obj"
- -@erase "$(INTDIR)\mod_mem_cache.res"
- -@erase "$(INTDIR)\mod_mem_cache_src.idb"
- -@erase "$(INTDIR)\mod_mem_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_mem_cache.exp"
- -@erase "$(OUTDIR)\mod_mem_cache.lib"
- -@erase "$(OUTDIR)\mod_mem_cache.pdb"
- -@erase "$(OUTDIR)\mod_mem_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_mem_cache_src" /FD /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_mem_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_mem_cache.pdb" /debug /out:"$(OUTDIR)\mod_mem_cache.so" /implib:"$(OUTDIR)\mod_mem_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_mem_cache.so /opt:ref
-LINK32_OBJS= \
- "$(INTDIR)\cache_cache.obj" \
- "$(INTDIR)\cache_hash.obj" \
- "$(INTDIR)\cache_pqueue.obj" \
- "$(INTDIR)\mod_mem_cache.obj" \
- "$(INTDIR)\mod_mem_cache.res" \
- "..\..\srclib\apr\Release\libapr-1.lib" \
- "..\..\srclib\apr-util\Release\libaprutil-1.lib" \
- "..\..\Release\libhttpd.lib" \
- "$(OUTDIR)\mod_cache.lib"
-
-"$(OUTDIR)\mod_mem_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Release\mod_mem_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_mem_cache.so"
- if exist .\Release\mod_mem_cache.so.manifest mt.exe -manifest .\Release\mod_mem_cache.so.manifest -outputresource:.\Release\mod_mem_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\mod_mem_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ELSE
-
-ALL : "mod_cache - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_mem_cache.so" "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_cache - Win32 DebugCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\cache_cache.obj"
- -@erase "$(INTDIR)\cache_hash.obj"
- -@erase "$(INTDIR)\cache_pqueue.obj"
- -@erase "$(INTDIR)\mod_mem_cache.obj"
- -@erase "$(INTDIR)\mod_mem_cache.res"
- -@erase "$(INTDIR)\mod_mem_cache_src.idb"
- -@erase "$(INTDIR)\mod_mem_cache_src.pdb"
- -@erase "$(OUTDIR)\mod_mem_cache.exp"
- -@erase "$(OUTDIR)\mod_mem_cache.lib"
- -@erase "$(OUTDIR)\mod_mem_cache.pdb"
- -@erase "$(OUTDIR)\mod_mem_cache.so"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_mem_cache_src" /FD /EHsc /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-MTL=midl.exe
-MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
-RSC=rc.exe
-RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache"
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_mem_cache.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_mem_cache.pdb" /debug /out:"$(OUTDIR)\mod_mem_cache.so" /implib:"$(OUTDIR)\mod_mem_cache.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_mem_cache.so
-LINK32_OBJS= \
- "$(INTDIR)\cache_cache.obj" \
- "$(INTDIR)\cache_hash.obj" \
- "$(INTDIR)\cache_pqueue.obj" \
- "$(INTDIR)\mod_mem_cache.obj" \
- "$(INTDIR)\mod_mem_cache.res" \
- "..\..\srclib\apr\Debug\libapr-1.lib" \
- "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \
- "..\..\Debug\libhttpd.lib" \
- "$(OUTDIR)\mod_cache.lib"
-
-"$(OUTDIR)\mod_mem_cache.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-TargetPath=.\Debug\mod_mem_cache.so
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep
-
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_mem_cache.so"
- if exist .\Debug\mod_mem_cache.so.manifest mt.exe -manifest .\Debug\mod_mem_cache.so.manifest -outputresource:.\Debug\mod_mem_cache.so;2
- echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)"
-
-!ENDIF
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("mod_mem_cache.dep")
-!INCLUDE "mod_mem_cache.dep"
-!ELSE
-!MESSAGE Warning: cannot find "mod_mem_cache.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release" || "$(CFG)" == "mod_mem_cache - Win32 Debug"
-SOURCE=.\cache_cache.c
-
-"$(INTDIR)\cache_cache.obj" : $(SOURCE) "$(INTDIR)"
-
-
-SOURCE=.\cache_hash.c
-
-"$(INTDIR)\cache_hash.obj" : $(SOURCE) "$(INTDIR)"
-
-
-SOURCE=.\cache_pqueue.c
-
-"$(INTDIR)\cache_pqueue.obj" : $(SOURCE) "$(INTDIR)"
-
-
-SOURCE=.\mod_mem_cache.c
-
-"$(INTDIR)\mod_mem_cache.obj" : $(SOURCE) "$(INTDIR)"
-
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-"libapr - Win32 Release" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
- cd "..\..\modules\cache"
-
-"libapr - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-"libapr - Win32 Debug" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libapr - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-"libaprutil - Win32 Release" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 ReleaseCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-"libaprutil - Win32 Debug" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug"
- cd "..\..\modules\cache"
-
-"libaprutil - Win32 DebugCLEAN" :
- cd ".\..\..\srclib\apr-util"
- $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-"libhttpd - Win32 Release" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release"
- cd ".\modules\cache"
-
-"libhttpd - Win32 ReleaseCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-"libhttpd - Win32 Debug" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug"
- cd ".\modules\cache"
-
-"libhttpd - Win32 DebugCLEAN" :
- cd ".\..\.."
- $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN
- cd ".\modules\cache"
-
-!ENDIF
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-"mod_cache - Win32 Release" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Release"
- cd "."
-
-"mod_cache - Win32 ReleaseCLEAN" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Release" RECURSE=1 CLEAN
- cd "."
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-"mod_cache - Win32 Debug" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Debug"
- cd "."
-
-"mod_cache - Win32 DebugCLEAN" :
- cd "."
- $(MAKE) /$(MAKEFLAGS) /F ".\mod_cache.mak" CFG="mod_cache - Win32 Debug" RECURSE=1 CLEAN
- cd "."
-
-!ENDIF
-
-SOURCE=..\..\build\win32\httpd.rc
-
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
-
-
-"$(INTDIR)\mod_mem_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "NDEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache" $(SOURCE)
-
-
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
-
-
-"$(INTDIR)\mod_mem_cache.res" : $(SOURCE) "$(INTDIR)"
- $(RSC) /l 0x409 /fo"$(INTDIR)\mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /i ".\..\..\build\win32" /d "_DEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache" $(SOURCE)
-
-
-!ENDIF
-
-
-!ENDIF
-
diff --git a/modules/cache/mod_socache_dbm.c b/modules/cache/mod_socache_dbm.c
new file mode 100644
index 00000000..70fe28bf
--- /dev/null
+++ b/modules/cache/mod_socache_dbm.c
@@ -0,0 +1,588 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_config.h"
+#include "mpm_common.h"
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_time.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#include "apr_dbm.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ap_socache.h"
+
+#if AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+
+/* Use of the context structure must be thread-safe after the initial
+ * create/init; callers must hold the mutex. */
+struct ap_socache_instance_t {
+ const char *data_file;
+ /* Pool must only be used with the mutex held. */
+ apr_pool_t *pool;
+ apr_time_t last_expiry;
+ apr_interval_time_t expiry_interval;
+};
+
+/**
+ * Support for DBM library
+ */
+#define DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
+
+#define DEFAULT_DBM_PREFIX DEFAULT_REL_RUNTIMEDIR "/socache-dbm-"
+
+/* ### this should use apr_dbm_usednames. */
+#if !defined(DBM_FILE_SUFFIX_DIR) && !defined(DBM_FILE_SUFFIX_PAG)
+#if defined(DBM_SUFFIX)
+#define DBM_FILE_SUFFIX_DIR DBM_SUFFIX
+#define DBM_FILE_SUFFIX_PAG DBM_SUFFIX
+#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM))
+#define DBM_FILE_SUFFIX_DIR ".db"
+#define DBM_FILE_SUFFIX_PAG ".db"
+#else
+#define DBM_FILE_SUFFIX_DIR ".dir"
+#define DBM_FILE_SUFFIX_PAG ".pag"
+#endif
+#endif
+
+static void socache_dbm_expire(ap_socache_instance_t *ctx, server_rec *s);
+
+static apr_status_t socache_dbm_remove(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_pool_t *p);
+
+static const char *socache_dbm_create(ap_socache_instance_t **context,
+ const char *arg,
+ apr_pool_t *tmp, apr_pool_t *p)
+{
+ ap_socache_instance_t *ctx;
+
+ *context = ctx = apr_pcalloc(p, sizeof *ctx);
+
+ if (arg && *arg) {
+ ctx->data_file = ap_server_root_relative(p, arg);
+ if (!ctx->data_file) {
+ return apr_psprintf(tmp, "Invalid cache file path %s", arg);
+ }
+ }
+
+ apr_pool_create(&ctx->pool, p);
+
+ return NULL;
+}
+
+#if AP_NEED_SET_MUTEX_PERMS
+static int try_chown(apr_pool_t *p, server_rec *s,
+ const char *name, const char *suffix)
+{
+ if (suffix)
+ name = apr_pstrcat(p, name, suffix, NULL);
+ if (-1 == chown(name, ap_unixd_config.user_id,
+ (gid_t)-1 /* no gid change */ ))
+ {
+ if (errno != ENOENT)
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(errno), s, APLOGNO(00802)
+ "Can't change owner of %s", name);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+static apr_status_t socache_dbm_init(ap_socache_instance_t *ctx,
+ const char *namespace,
+ const struct ap_socache_hints *hints,
+ server_rec *s, apr_pool_t *p)
+{
+ apr_dbm_t *dbm;
+ apr_status_t rv;
+
+ /* for the DBM we need the data file */
+ if (ctx->data_file == NULL) {
+ const char *path = apr_pstrcat(p, DEFAULT_DBM_PREFIX, namespace,
+ NULL);
+
+ ctx->data_file = ap_server_root_relative(p, path);
+
+ if (ctx->data_file == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00803)
+ "could not use default path '%s' for DBM socache",
+ path);
+ return APR_EINVAL;
+ }
+ }
+
+ /* open it once to create it and to make sure it _can_ be created */
+ apr_pool_clear(ctx->pool);
+
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file,
+ APR_DBM_RWCREATE, DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00804)
+ "Cannot create socache DBM file `%s'",
+ ctx->data_file);
+ return rv;
+ }
+ apr_dbm_close(dbm);
+
+ ctx->expiry_interval = (hints && hints->expiry_interval
+ ? hints->expiry_interval : apr_time_from_sec(30));
+
+#if AP_NEED_SET_MUTEX_PERMS
+ /*
+ * We have to make sure the Apache child processes have access to
+ * the DBM file. But because there are brain-dead platforms where we
+ * cannot exactly determine the suffixes we try all possibilities.
+ */
+ if (geteuid() == 0 /* is superuser */) {
+ try_chown(p, s, ctx->data_file, NULL);
+ if (try_chown(p, s, ctx->data_file, DBM_FILE_SUFFIX_DIR))
+ if (try_chown(p, s, ctx->data_file, ".db"))
+ try_chown(p, s, ctx->data_file, ".dir");
+ if (try_chown(p, s, ctx->data_file, DBM_FILE_SUFFIX_PAG))
+ if (try_chown(p, s, ctx->data_file, ".db"))
+ try_chown(p, s, ctx->data_file, ".pag");
+ }
+#endif
+ socache_dbm_expire(ctx, s);
+
+ return APR_SUCCESS;
+}
+
+static void socache_dbm_destroy(ap_socache_instance_t *ctx, server_rec *s)
+{
+ /* the correct way */
+ unlink(apr_pstrcat(ctx->pool, ctx->data_file, DBM_FILE_SUFFIX_DIR, NULL));
+ unlink(apr_pstrcat(ctx->pool, ctx->data_file, DBM_FILE_SUFFIX_PAG, NULL));
+ /* the additional ways to be sure */
+ unlink(apr_pstrcat(ctx->pool, ctx->data_file, ".dir", NULL));
+ unlink(apr_pstrcat(ctx->pool, ctx->data_file, ".pag", NULL));
+ unlink(apr_pstrcat(ctx->pool, ctx->data_file, ".db", NULL));
+ unlink(ctx->data_file);
+
+ return;
+}
+
+static apr_status_t socache_dbm_store(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_time_t expiry,
+ unsigned char *ucaData,
+ unsigned int nData, apr_pool_t *pool)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_datum_t dbmval;
+ apr_status_t rv;
+
+ /* be careful: do not try to store too much bytes in a DBM file! */
+#ifdef PAIRMAX
+ if ((idlen + nData) >= PAIRMAX) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00805)
+ "data size too large for DBM socache: %d >= %d",
+ (idlen + nData), PAIRMAX);
+ return APR_ENOSPC;
+ }
+#else
+ if ((idlen + nData) >= 950 /* at least less than approx. 1KB */) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00806)
+ "data size too large for DBM socache: %d >= %d",
+ (idlen + nData), 950);
+ return APR_ENOSPC;
+ }
+#endif
+
+ /* create DBM key */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* create DBM value */
+ dbmval.dsize = sizeof(apr_time_t) + nData;
+ dbmval.dptr = (char *)ap_malloc(dbmval.dsize);
+ memcpy((char *)dbmval.dptr, &expiry, sizeof(apr_time_t));
+ memcpy((char *)dbmval.dptr+sizeof(apr_time_t), ucaData, nData);
+
+ /* and store it to the DBM file */
+ apr_pool_clear(ctx->pool);
+
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file,
+ APR_DBM_RWCREATE, DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00807)
+ "Cannot open socache DBM file `%s' for writing "
+ "(store)",
+ ctx->data_file);
+ free(dbmval.dptr);
+ return rv;
+ }
+ if ((rv = apr_dbm_store(dbm, dbmkey, dbmval)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00808)
+ "Cannot store socache object to DBM file `%s'",
+ ctx->data_file);
+ apr_dbm_close(dbm);
+ free(dbmval.dptr);
+ return rv;
+ }
+ apr_dbm_close(dbm);
+
+ /* free temporary buffers */
+ free(dbmval.dptr);
+
+ /* allow the regular expiring to occur */
+ socache_dbm_expire(ctx, s);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_dbm_retrieve(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *dest, unsigned int *destlen,
+ apr_pool_t *p)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_datum_t dbmval;
+ unsigned int nData;
+ apr_time_t expiry;
+ apr_time_t now;
+ apr_status_t rc;
+
+ /* allow the regular expiring to occur */
+ socache_dbm_expire(ctx, s);
+
+ /* create DBM key and values */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* and fetch it from the DBM file
+ * XXX: Should we open the dbm against r->pool so the cleanup will
+ * do the apr_dbm_close? This would make the code a bit cleaner.
+ */
+ apr_pool_clear(ctx->pool);
+ if ((rc = apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, APLOGNO(00809)
+ "Cannot open socache DBM file `%s' for reading "
+ "(fetch)",
+ ctx->data_file);
+ return rc;
+ }
+ rc = apr_dbm_fetch(dbm, dbmkey, &dbmval);
+ if (rc != APR_SUCCESS) {
+ apr_dbm_close(dbm);
+ return APR_NOTFOUND;
+ }
+ if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(apr_time_t)) {
+ apr_dbm_close(dbm);
+ return APR_EGENERAL;
+ }
+
+ /* parse resulting data */
+ nData = dbmval.dsize-sizeof(apr_time_t);
+ if (nData > *destlen) {
+ apr_dbm_close(dbm);
+ return APR_ENOSPC;
+ }
+
+ *destlen = nData;
+ memcpy(&expiry, dbmval.dptr, sizeof(apr_time_t));
+ memcpy(dest, (char *)dbmval.dptr + sizeof(apr_time_t), nData);
+
+ apr_dbm_close(dbm);
+
+ /* make sure the stuff is still not expired */
+ now = apr_time_now();
+ if (expiry <= now) {
+ socache_dbm_remove(ctx, s, id, idlen, p);
+ return APR_NOTFOUND;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_dbm_remove(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_pool_t *p)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_status_t rv;
+
+ /* create DBM key and values */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* and delete it from the DBM file */
+ apr_pool_clear(ctx->pool);
+
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00810)
+ "Cannot open socache DBM file `%s' for writing "
+ "(delete)",
+ ctx->data_file);
+ return rv;
+ }
+ apr_dbm_delete(dbm, dbmkey);
+ apr_dbm_close(dbm);
+
+ return APR_SUCCESS;
+}
+
+static void socache_dbm_expire(ap_socache_instance_t *ctx, server_rec *s)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_datum_t dbmval;
+ apr_time_t expiry;
+ int elts = 0;
+ int deleted = 0;
+ int expired;
+ apr_datum_t *keylist;
+ int keyidx;
+ int i;
+ apr_time_t now;
+ apr_status_t rv;
+
+ /*
+ * make sure the expiration for still not-accessed
+ * socache entries is done only from time to time
+ */
+ now = apr_time_now();
+
+ if (now < ctx->last_expiry + ctx->expiry_interval) {
+ return;
+ }
+
+ ctx->last_expiry = now;
+
+ /*
+ * Here we have to be very carefully: Not all DBM libraries are
+ * smart enough to allow one to iterate over the elements and at the
+ * same time delete expired ones. Some of them get totally crazy
+ * while others have no problems. So we have to do it the slower but
+ * more safe way: we first iterate over all elements and remember
+ * those which have to be expired. Then in a second pass we delete
+ * all those expired elements. Additionally we reopen the DBM file
+ * to be really safe in state.
+ */
+
+#define KEYMAX 1024
+
+ for (;;) {
+ /* allocate the key array in a memory sub pool */
+ apr_pool_clear(ctx->pool);
+
+ if ((keylist = apr_palloc(ctx->pool, sizeof(dbmkey)*KEYMAX)) == NULL) {
+ break;
+ }
+
+ /* pass 1: scan DBM database */
+ keyidx = 0;
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00811)
+ "Cannot open socache DBM file `%s' for "
+ "scanning",
+ ctx->data_file);
+ break;
+ }
+ apr_dbm_firstkey(dbm, &dbmkey);
+ while (dbmkey.dptr != NULL) {
+ elts++;
+ expired = FALSE;
+ apr_dbm_fetch(dbm, dbmkey, &dbmval);
+ if (dbmval.dsize <= sizeof(apr_time_t) || dbmval.dptr == NULL)
+ expired = TRUE;
+ else {
+ memcpy(&expiry, dbmval.dptr, sizeof(apr_time_t));
+ if (expiry <= now)
+ expired = TRUE;
+ }
+ if (expired) {
+ if ((keylist[keyidx].dptr = apr_pmemdup(ctx->pool, dbmkey.dptr, dbmkey.dsize)) != NULL) {
+ keylist[keyidx].dsize = dbmkey.dsize;
+ keyidx++;
+ if (keyidx == KEYMAX)
+ break;
+ }
+ }
+ apr_dbm_nextkey(dbm, &dbmkey);
+ }
+ apr_dbm_close(dbm);
+
+ /* pass 2: delete expired elements */
+ if (apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00812)
+ "Cannot re-open socache DBM file `%s' for "
+ "expiring",
+ ctx->data_file);
+ break;
+ }
+ for (i = 0; i < keyidx; i++) {
+ apr_dbm_delete(dbm, keylist[i]);
+ deleted++;
+ }
+ apr_dbm_close(dbm);
+
+ if (keyidx < KEYMAX)
+ break;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00813)
+ "DBM socache expiry: "
+ "old: %d, new: %d, removed: %d",
+ elts, elts-deleted, deleted);
+}
+
+static void socache_dbm_status(ap_socache_instance_t *ctx, request_rec *r,
+ int flags)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_datum_t dbmval;
+ int elts;
+ long size;
+ int avg;
+ apr_status_t rv;
+
+ elts = 0;
+ size = 0;
+
+ apr_pool_clear(ctx->pool);
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00814)
+ "Cannot open socache DBM file `%s' for status "
+ "retrival",
+ ctx->data_file);
+ return;
+ }
+ /*
+ * XXX - Check the return value of apr_dbm_firstkey, apr_dbm_fetch - TBD
+ */
+ apr_dbm_firstkey(dbm, &dbmkey);
+ for ( ; dbmkey.dptr != NULL; apr_dbm_nextkey(dbm, &dbmkey)) {
+ apr_dbm_fetch(dbm, dbmkey, &dbmval);
+ if (dbmval.dptr == NULL)
+ continue;
+ elts += 1;
+ size += dbmval.dsize;
+ }
+ apr_dbm_close(dbm);
+ if (size > 0 && elts > 0)
+ avg = (int)(size / (long)elts);
+ else
+ avg = 0;
+ ap_rprintf(r, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>");
+ ap_rprintf(r, "current entries: <b>%d</b>, current size: <b>%ld</b> bytes<br>", elts, size);
+ ap_rprintf(r, "average entry size: <b>%d</b> bytes<br>", avg);
+ return;
+}
+
+static apr_status_t socache_dbm_iterate(ap_socache_instance_t *ctx,
+ server_rec *s, void *userctx,
+ ap_socache_iterator_t *iterator,
+ apr_pool_t *pool)
+{
+ apr_dbm_t *dbm;
+ apr_datum_t dbmkey;
+ apr_datum_t dbmval;
+ apr_time_t expiry;
+ int expired;
+ apr_time_t now;
+ apr_status_t rv;
+
+ /*
+ * make sure the expired records are omitted
+ */
+ now = apr_time_now();
+ if ((rv = apr_dbm_open(&dbm, ctx->data_file, APR_DBM_RWCREATE,
+ DBM_FILE_MODE, ctx->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00815)
+ "Cannot open socache DBM file `%s' for "
+ "iterating", ctx->data_file);
+ return rv;
+ }
+ rv = apr_dbm_firstkey(dbm, &dbmkey);
+ while (rv == APR_SUCCESS && dbmkey.dptr != NULL) {
+ expired = FALSE;
+ apr_dbm_fetch(dbm, dbmkey, &dbmval);
+ if (dbmval.dsize <= sizeof(apr_time_t) || dbmval.dptr == NULL)
+ expired = TRUE;
+ else {
+ memcpy(&expiry, dbmval.dptr, sizeof(apr_time_t));
+ if (expiry <= now)
+ expired = TRUE;
+ }
+ if (!expired) {
+ rv = iterator(ctx, s, userctx,
+ (unsigned char *)dbmkey.dptr, dbmkey.dsize,
+ (unsigned char *)dbmval.dptr + sizeof(apr_time_t),
+ dbmval.dsize - sizeof(apr_time_t), pool);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00816)
+ "dbm `%s' entry iterated", ctx->data_file);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ rv = apr_dbm_nextkey(dbm, &dbmkey);
+ }
+ apr_dbm_close(dbm);
+
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00817)
+ "Failure reading first/next socache DBM file `%s' record",
+ ctx->data_file);
+ return rv;
+ }
+ return APR_SUCCESS;
+}
+
+static const ap_socache_provider_t socache_dbm = {
+ "dbm",
+ AP_SOCACHE_FLAG_NOTMPSAFE,
+ socache_dbm_create,
+ socache_dbm_init,
+ socache_dbm_destroy,
+ socache_dbm_store,
+ socache_dbm_retrieve,
+ socache_dbm_remove,
+ socache_dbm_status,
+ socache_dbm_iterate
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "dbm",
+ AP_SOCACHE_PROVIDER_VERSION,
+ &socache_dbm);
+}
+
+AP_DECLARE_MODULE(socache_dbm) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, NULL, NULL, NULL, NULL,
+ register_hooks
+};
diff --git a/modules/cache/mod_mem_cache.dsp b/modules/cache/mod_socache_dbm.dsp
index 7b8ebbb9..bcd253dc 100644
--- a/modules/cache/mod_mem_cache.dsp
+++ b/modules/cache/mod_socache_dbm.dsp
@@ -1,24 +1,24 @@
-# Microsoft Developer Studio Project File - Name="mod_mem_cache" - Package Owner=<4>
+# Microsoft Developer Studio Project File - Name="mod_socache_dbm" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
-CFG=mod_mem_cache - Win32 Debug
+CFG=mod_socache_dbm - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
-!MESSAGE NMAKE /f "mod_mem_cache.mak".
+!MESSAGE NMAKE /f "mod_socache_dbm.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
-!MESSAGE NMAKE /f "mod_mem_cache.mak" CFG="mod_mem_cache - Win32 Debug"
+!MESSAGE NMAKE /f "mod_socache_dbm.mak" CFG="mod_socache_dbm - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
-!MESSAGE "mod_mem_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "mod_mem_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_dbm - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_dbm - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
@@ -29,7 +29,7 @@ CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
-!IF "$(CFG)" == "mod_mem_cache - Win32 Release"
+!IF "$(CFG)" == "mod_socache_dbm - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
@@ -42,26 +42,26 @@ RSC=rc.exe
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
-# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_mem_cache_EXPORTS" /FD /c
-# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_mem_cache_src" /FD /c
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_socache_dbm_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_socache_dbm_src" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /fo"Release/mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache"
+# ADD RSC /l 0x409 /fo"Release/mod_socache_dbm.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_socache_dbm.so" /d LONG_NAME="socache_dbm_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_mem_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mem_cache.so /opt:ref
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_socache_dbm.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_dbm.so /opt:ref
# Begin Special Build Tool
-TargetPath=.\Release\mod_mem_cache.so
+TargetPath=.\Release\mod_socache_dbm.so
SOURCE="$(InputPath)"
PostBuild_Desc=Embed .manifest
PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
# End Special Build Tool
-!ELSEIF "$(CFG)" == "mod_mem_cache - Win32 Debug"
+!ELSEIF "$(CFG)" == "mod_socache_dbm - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
@@ -75,19 +75,19 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
-# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_mem_cache_src" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_socache_dbm_src" /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /fo"Debug/mod_mem_cache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_mem_cache.so" /d LONG_NAME="mem_cache_module for Apache"
+# ADD RSC /l 0x409 /fo"Debug/mod_socache_dbm.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_socache_dbm.so" /d LONG_NAME="socache_dbm_module for Apache"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_mem_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mem_cache.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_socache_dbm.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_dbm.so
# Begin Special Build Tool
-TargetPath=.\Debug\mod_mem_cache.so
+TargetPath=.\Debug\mod_socache_dbm.so
SOURCE="$(InputPath)"
PostBuild_Desc=Embed .manifest
PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
@@ -97,48 +97,12 @@ PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).ma
# Begin Target
-# Name "mod_mem_cache - Win32 Release"
-# Name "mod_mem_cache - Win32 Debug"
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=.\cache_cache.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\cache_hash.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\cache_pqueue.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\mod_cache.h
-# End Source File
-# End Group
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
-# Begin Source File
-
-SOURCE=.\cache_cache.c
-# End Source File
-# Begin Source File
-
-SOURCE=.\cache_hash.c
-# End Source File
-# Begin Source File
-
-SOURCE=.\cache_pqueue.c
-# End Source File
+# Name "mod_socache_dbm - Win32 Release"
+# Name "mod_socache_dbm - Win32 Debug"
# Begin Source File
-SOURCE=.\mod_mem_cache.c
+SOURCE=.\mod_socache_dbm.c
# End Source File
-# End Group
# Begin Source File
SOURCE=..\..\build\win32\httpd.rc
diff --git a/modules/cache/mod_socache_dc.c b/modules/cache/mod_socache_dc.c
new file mode 100644
index 00000000..9739a97b
--- /dev/null
+++ b/modules/cache/mod_socache_dc.c
@@ -0,0 +1,190 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_config.h"
+#include "http_protocol.h"
+
+#include "apr_strings.h"
+#include "apr_time.h"
+
+#include "ap_socache.h"
+
+#include "distcache/dc_client.h"
+
+#if !defined(DISTCACHE_CLIENT_API) || (DISTCACHE_CLIENT_API < 0x0001)
+#error "You must compile with a more recent version of the distcache-base package"
+#endif
+
+struct ap_socache_instance_t {
+ /* Configured target server: */
+ const char *target;
+ /* distcache client context: */
+ DC_CTX *dc;
+};
+
+static const char *socache_dc_create(ap_socache_instance_t **context,
+ const char *arg,
+ apr_pool_t *tmp, apr_pool_t *p)
+{
+ struct ap_socache_instance_t *ctx;
+
+ ctx = *context = apr_palloc(p, sizeof *ctx);
+
+ ctx->target = apr_pstrdup(p, arg);
+
+ return NULL;
+}
+
+static apr_status_t socache_dc_init(ap_socache_instance_t *ctx,
+ const char *namespace,
+ const struct ap_socache_hints *hints,
+ server_rec *s, apr_pool_t *p)
+{
+#if 0
+ /* If a "persistent connection" mode of operation is preferred, you *must*
+ * also use the PIDCHECK flag to ensure fork()'d processes don't interlace
+ * comms on the same connection as each other. */
+#define SESSION_CTX_FLAGS SESSION_CTX_FLAG_PERSISTENT | \
+ SESSION_CTX_FLAG_PERSISTENT_PIDCHECK | \
+ SESSION_CTX_FLAG_PERSISTENT_RETRY | \
+ SESSION_CTX_FLAG_PERSISTENT_LATE
+#else
+ /* This mode of operation will open a temporary connection to the 'target'
+ * for each cache operation - this makes it safe against fork()
+ * automatically. This mode is preferred when running a local proxy (over
+ * unix domain sockets) because overhead is negligable and it reduces the
+ * performance/stability danger of file-descriptor bloatage. */
+#define SESSION_CTX_FLAGS 0
+#endif
+ ctx->dc = DC_CTX_new(ctx->target, SESSION_CTX_FLAGS);
+ if (!ctx->dc) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00738) "distributed scache failed to obtain context");
+ return APR_EGENERAL;
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(00739) "distributed scache context initialised");
+
+ return APR_SUCCESS;
+}
+
+static void socache_dc_destroy(ap_socache_instance_t *ctx, server_rec *s)
+{
+ if (ctx && ctx->dc) {
+ DC_CTX_free(ctx->dc);
+ ctx->dc = NULL;
+ }
+}
+
+static apr_status_t socache_dc_store(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ apr_time_t expiry,
+ unsigned char *der, unsigned int der_len,
+ apr_pool_t *p)
+{
+ /* !@#$%^ - why do we deal with *absolute* time anyway???
+ * Uhm - because most things expire things at a specific time?
+ * Were the API were thought out expiry - r->request_time is a good approximation
+ */
+ expiry -= apr_time_now();
+ /* Send the serialised session to the distributed cache context */
+ if (!DC_CTX_add_session(ctx->dc, id, idlen, der, der_len,
+ apr_time_msec(expiry))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00740) "distributed scache 'store' failed");
+ return APR_EGENERAL;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00741) "distributed scache 'store' successful");
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_dc_retrieve(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *dest, unsigned int *destlen,
+ apr_pool_t *p)
+{
+ unsigned int data_len;
+
+ /* Retrieve any corresponding session from the distributed cache context */
+ if (!DC_CTX_get_session(ctx->dc, id, idlen, dest, *destlen, &data_len)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00742) "distributed scache 'retrieve' MISS");
+ return APR_NOTFOUND;
+ }
+ if (data_len > *destlen) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00743) "distributed scache 'retrieve' OVERFLOW");
+ return APR_ENOSPC;
+ }
+ *destlen = data_len;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00744) "distributed scache 'retrieve' HIT");
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_dc_remove(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_pool_t *p)
+{
+ /* Remove any corresponding session from the distributed cache context */
+ if (!DC_CTX_remove_session(ctx->dc, id, idlen)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00745) "distributed scache 'remove' MISS");
+ return APR_NOTFOUND;
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00746) "distributed scache 'remove' HIT");
+ return APR_SUCCESS;
+ }
+}
+
+static void socache_dc_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
+{
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00747)
+ "distributed scache 'socache_dc_status'");
+ ap_rprintf(r, "cache type: <b>DC (Distributed Cache)</b>, "
+ " target: <b>%s</b><br>", ctx->target);
+}
+
+static apr_status_t socache_dc_iterate(ap_socache_instance_t *instance,
+ server_rec *s, void *userctx,
+ ap_socache_iterator_t *iterator,
+ apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+static const ap_socache_provider_t socache_dc = {
+ "distcache",
+ 0,
+ socache_dc_create,
+ socache_dc_init,
+ socache_dc_destroy,
+ socache_dc_store,
+ socache_dc_retrieve,
+ socache_dc_remove,
+ socache_dc_status,
+ socache_dc_iterate
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "dc",
+ AP_SOCACHE_PROVIDER_VERSION,
+ &socache_dc);
+}
+
+AP_DECLARE_MODULE(socache_dc) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, NULL, NULL, NULL, NULL,
+ register_hooks
+};
+
diff --git a/modules/cache/mod_socache_dc.dsp b/modules/cache/mod_socache_dc.dsp
new file mode 100644
index 00000000..0ff315cc
--- /dev/null
+++ b/modules/cache/mod_socache_dc.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_socache_dc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_socache_dc - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_dc.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_dc.mak" CFG="mod_socache_dc - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_socache_dc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_dc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_socache_dc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_socache_dc_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_socache_dc_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_socache_dc.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_socache_dc.so" /d LONG_NAME="socache_dc_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_socache_dc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_dc.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_socache_dc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_socache_dc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_socache_dc_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_socache_dc.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_socache_dc.so" /d LONG_NAME="socache_dc_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_socache_dc.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_dc.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_socache_dc.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_socache_dc - Win32 Release"
+# Name "mod_socache_dc - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_socache_dc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/cache/mod_socache_memcache.c b/modules/cache/mod_socache_memcache.c
new file mode 100644
index 00000000..ccb1bde7
--- /dev/null
+++ b/modules/cache/mod_socache_memcache.c
@@ -0,0 +1,326 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+#include "httpd.h"
+#include "http_config.h"
+
+#include "apr.h"
+#include "apu_version.h"
+
+/* apr_memcache support requires >= 1.3 */
+#if APU_MAJOR_VERSION > 1 || \
+ (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION > 2)
+#define HAVE_APU_MEMCACHE 1
+#endif
+
+#ifdef HAVE_APU_MEMCACHE
+
+#include "ap_socache.h"
+#include "ap_mpm.h"
+#include "http_log.h"
+#include "apr_memcache.h"
+
+/* The underlying apr_memcache system is thread safe.. */
+#define MC_KEY_LEN 254
+
+#ifndef MC_DEFAULT_SERVER_PORT
+#define MC_DEFAULT_SERVER_PORT 11211
+#endif
+
+
+#ifndef MC_DEFAULT_SERVER_MIN
+#define MC_DEFAULT_SERVER_MIN 0
+#endif
+
+#ifndef MC_DEFAULT_SERVER_SMAX
+#define MC_DEFAULT_SERVER_SMAX 1
+#endif
+
+#ifndef MC_DEFAULT_SERVER_TTL
+#define MC_DEFAULT_SERVER_TTL 600
+#endif
+
+struct ap_socache_instance_t {
+ const char *servers;
+ apr_memcache_t *mc;
+ const char *tag;
+ apr_size_t taglen; /* strlen(tag) + 1 */
+};
+
+static const char *socache_mc_create(ap_socache_instance_t **context,
+ const char *arg,
+ apr_pool_t *tmp, apr_pool_t *p)
+{
+ ap_socache_instance_t *ctx;
+
+ *context = ctx = apr_palloc(p, sizeof *ctx);
+
+ if (!arg || !*arg) {
+ return "List of server names required to create memcache socache.";
+ }
+
+ ctx->servers = apr_pstrdup(p, arg);
+
+ return NULL;
+}
+
+static apr_status_t socache_mc_init(ap_socache_instance_t *ctx,
+ const char *namespace,
+ const struct ap_socache_hints *hints,
+ server_rec *s, apr_pool_t *p)
+{
+ apr_status_t rv;
+ int thread_limit = 0;
+ int nservers = 0;
+ char *cache_config;
+ char *split;
+ char *tok;
+
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
+
+ /* Find all the servers in the first run to get a total count */
+ cache_config = apr_pstrdup(p, ctx->servers);
+ split = apr_strtok(cache_config, ",", &tok);
+ while (split) {
+ nservers++;
+ split = apr_strtok(NULL,",", &tok);
+ }
+
+ rv = apr_memcache_create(p, nservers, 0, &ctx->mc);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00785)
+ "Failed to create Memcache Object of '%d' size.",
+ nservers);
+ return rv;
+ }
+
+ /* Now add each server to the memcache */
+ cache_config = apr_pstrdup(p, ctx->servers);
+ split = apr_strtok(cache_config, ",", &tok);
+ while (split) {
+ apr_memcache_server_t *st;
+ char *host_str;
+ char *scope_id;
+ apr_port_t port;
+
+ rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00786)
+ "Failed to Parse memcache Server: '%s'", split);
+ return rv;
+ }
+
+ if (host_str == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00787)
+ "Failed to Parse Server, "
+ "no hostname specified: '%s'", split);
+ return APR_EINVAL;
+ }
+
+ if (port == 0) {
+ port = MC_DEFAULT_SERVER_PORT;
+ }
+
+ rv = apr_memcache_server_create(p,
+ host_str, port,
+ MC_DEFAULT_SERVER_MIN,
+ MC_DEFAULT_SERVER_SMAX,
+ thread_limit,
+ MC_DEFAULT_SERVER_TTL,
+ &st);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00788)
+ "Failed to Create memcache Server: %s:%d",
+ host_str, port);
+ return rv;
+ }
+
+ rv = apr_memcache_add_server(ctx->mc, st);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00789)
+ "Failed to Add memcache Server: %s:%d",
+ host_str, port);
+ return rv;
+ }
+
+ split = apr_strtok(NULL,",", &tok);
+ }
+
+ ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
+ ctx->taglen = strlen(ctx->tag) + 1;
+
+ /* socache API constraint: */
+ AP_DEBUG_ASSERT(ctx->taglen <= 16);
+
+ return APR_SUCCESS;
+}
+
+static void socache_mc_destroy(ap_socache_instance_t *context, server_rec *s)
+{
+ /* noop. */
+}
+
+/* Converts (binary) id into a key prefixed by the predetermined
+ * namespace tag; writes output to key buffer. Returns non-zero if
+ * the id won't fit in the key buffer. */
+static int socache_mc_id2key(ap_socache_instance_t *ctx,
+ const unsigned char *id, unsigned int idlen,
+ char *key, apr_size_t keylen)
+{
+ char *cp;
+ unsigned int n;
+
+ if (idlen * 2 + ctx->taglen >= keylen)
+ return 1;
+
+ cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
+
+ for (n = 0; n < idlen; n++) {
+ apr_snprintf(cp, 3, "%02X", (unsigned) id[n]);
+ cp += 2;
+ }
+
+ *cp = '\0';
+ return 0;
+}
+
+static apr_status_t socache_mc_store(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ apr_time_t expiry,
+ unsigned char *ucaData, unsigned int nData,
+ apr_pool_t *p)
+{
+ char buf[MC_KEY_LEN];
+ apr_status_t rv;
+
+ if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
+ return APR_EINVAL;
+ }
+
+ /* In APR-util - unclear what 'timeout' is, as it was not implemented */
+ rv = apr_memcache_set(ctx->mc, buf, (char*)ucaData, nData, 0, 0);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00790)
+ "scache_mc: error setting key '%s' "
+ "with %d bytes of data", buf, nData);
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_mc_retrieve(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *dest, unsigned int *destlen,
+ apr_pool_t *p)
+{
+ apr_size_t data_len;
+ char buf[MC_KEY_LEN], *data;
+ apr_status_t rv;
+
+ if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
+ return APR_EINVAL;
+ }
+
+ /* ### this could do with a subpool, but _getp looks like it will
+ * eat memory like it's going out of fashion anyway. */
+
+ rv = apr_memcache_getp(ctx->mc, p, buf, &data, &data_len, NULL);
+ if (rv) {
+ if (rv != APR_NOTFOUND) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00791)
+ "scache_mc: 'retrieve' FAIL");
+ }
+ return rv;
+ }
+ else if (data_len > *destlen) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00792)
+ "scache_mc: 'retrieve' OVERFLOW");
+ return APR_ENOMEM;
+ }
+
+ memcpy(dest, data, data_len);
+ *destlen = data_len;
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_mc_remove(ap_socache_instance_t *ctx, server_rec *s,
+ const unsigned char *id,
+ unsigned int idlen, apr_pool_t *p)
+{
+ char buf[MC_KEY_LEN];
+ apr_status_t rv;
+
+ if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
+ return APR_EINVAL;
+ }
+
+ rv = apr_memcache_delete(ctx->mc, buf, 0);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00793)
+ "scache_mc: error deleting key '%s' ",
+ buf);
+ }
+
+ return rv;
+}
+
+static void socache_mc_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
+{
+ /* TODO: Make a mod_status handler. meh. */
+}
+
+static apr_status_t socache_mc_iterate(ap_socache_instance_t *instance,
+ server_rec *s, void *userctx,
+ ap_socache_iterator_t *iterator,
+ apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+static const ap_socache_provider_t socache_mc = {
+ "memcache",
+ 0,
+ socache_mc_create,
+ socache_mc_init,
+ socache_mc_destroy,
+ socache_mc_store,
+ socache_mc_retrieve,
+ socache_mc_remove,
+ socache_mc_status,
+ socache_mc_iterate
+};
+
+#endif /* HAVE_APU_MEMCACHE */
+
+static void register_hooks(apr_pool_t *p)
+{
+#ifdef HAVE_APU_MEMCACHE
+ ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "memcache",
+ AP_SOCACHE_PROVIDER_VERSION,
+ &socache_mc);
+#endif
+}
+
+AP_DECLARE_MODULE(socache_memcache) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, NULL, NULL, NULL, NULL,
+ register_hooks
+};
diff --git a/modules/cache/mod_socache_memcache.dsp b/modules/cache/mod_socache_memcache.dsp
new file mode 100644
index 00000000..e4f81a3c
--- /dev/null
+++ b/modules/cache/mod_socache_memcache.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_socache_memcache" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_socache_memcache - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_memcache.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_memcache.mak" CFG="mod_socache_memcache - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_socache_memcache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_memcache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_socache_memcache - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_socache_memcache_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_socache_memcache_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_socache_memcache.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_socache_memcache.so" /d LONG_NAME="socache_memcache_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_socache_memcache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_memcache.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_socache_memcache.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_socache_memcache - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_socache_memcache_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_socache_memcache.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_socache_memcache.so" /d LONG_NAME="socache_memcache_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_socache_memcache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_memcache.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_socache_memcache.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_socache_memcache - Win32 Release"
+# Name "mod_socache_memcache - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_socache_memcache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/cache/mod_socache_shmcb.c b/modules/cache/mod_socache_shmcb.c
new file mode 100644
index 00000000..ff4defd3
--- /dev/null
+++ b/modules/cache/mod_socache_shmcb.c
@@ -0,0 +1,1023 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_config.h"
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "apr_shm.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#include "apr_general.h"
+
+#include "ap_socache.h"
+
+#define SHMCB_MAX_SIZE (64 * 1024 * 1024)
+
+#define DEFAULT_SHMCB_PREFIX DEFAULT_REL_RUNTIMEDIR "/socache-shmcb-"
+
+#define DEFAULT_SHMCB_SUFFIX ".cache"
+
+/*
+ * Header structure - the start of the shared-mem segment
+ */
+typedef struct {
+ /* Stats for cache operations */
+ unsigned long stat_stores;
+ unsigned long stat_replaced;
+ unsigned long stat_expiries;
+ unsigned long stat_scrolled;
+ unsigned long stat_retrieves_hit;
+ unsigned long stat_retrieves_miss;
+ unsigned long stat_removes_hit;
+ unsigned long stat_removes_miss;
+ /* Number of subcaches */
+ unsigned int subcache_num;
+ /* How many indexes each subcache's queue has */
+ unsigned int index_num;
+ /* How large each subcache is, including the queue and data */
+ unsigned int subcache_size;
+ /* How far into each subcache the data area is (optimisation) */
+ unsigned int subcache_data_offset;
+ /* How large the data area in each subcache is (optimisation) */
+ unsigned int subcache_data_size;
+} SHMCBHeader;
+
+/*
+ * Subcache structure - the start of each subcache, followed by
+ * indexes then data
+ */
+typedef struct {
+ /* The start position and length of the cyclic buffer of indexes */
+ unsigned int idx_pos, idx_used;
+ /* Same for the data area */
+ unsigned int data_pos, data_used;
+} SHMCBSubcache;
+
+/*
+ * Index structure - each subcache has an array of these
+ */
+typedef struct {
+ /* absolute time this entry expires */
+ apr_time_t expires;
+ /* location within the subcache's data area */
+ unsigned int data_pos;
+ /* size (most logic ignores this, we keep it only to minimise memcpy) */
+ unsigned int data_used;
+ /* length of the used data which contains the id */
+ unsigned int id_len;
+ /* Used to mark explicitly-removed socache entries */
+ unsigned char removed;
+} SHMCBIndex;
+
+struct ap_socache_instance_t {
+ const char *data_file;
+ apr_size_t shm_size;
+ apr_shm_t *shm;
+ SHMCBHeader *header;
+};
+
+/* The SHM data segment is of fixed size and stores data as follows.
+ *
+ * [ SHMCBHeader | Subcaches ]
+ *
+ * The SHMCBHeader header structure stores metadata concerning the
+ * cache and the contained subcaches.
+ *
+ * Subcaches is a hash table of header->subcache_num SHMCBSubcache
+ * structures. The hash table is indexed by SHMCB_MASK(id). Each
+ * SHMCBSubcache structure has a fixed size (header->subcache_size),
+ * which is determined at creation time, and looks like the following:
+ *
+ * [ SHMCBSubcache | Indexes | Data ]
+ *
+ * Each subcache is prefixed by the SHMCBSubcache structure.
+ *
+ * The subcache's "Data" segment is a single cyclic data buffer, of
+ * total size header->subcache_data_size; data inside is referenced
+ * using byte offsets. The offset marking the beginning of the cyclic
+ * buffer is subcache->data_pos; the buffer's length is
+ * subcache->data_used.
+ *
+ * "Indexes" is an array of header->index_num SHMCBIndex structures,
+ * which is used as a cyclic queue; subcache->idx_pos gives the array
+ * index of the first in use, subcache->idx_used gives the number in
+ * use. Both ->idx_* values have a range of [0, header->index_num)
+ *
+ * Each in-use SHMCBIndex structure represents a single cached object.
+ * The ID and data segment are stored consecutively in the subcache's
+ * cyclic data buffer. The "Data" segment can thus be seen to
+ * look like this, for example
+ *
+ * offset: [ 0 1 2 3 4 5 6 ...
+ * contents:[ ID1 Data1 ID2 Data2 ID3 ...
+ *
+ * where the corresponding indices would look like:
+ *
+ * idx1 = { data_pos = 0, data_used = 3, id_len = 1, ...}
+ * idx2 = { data_pos = 3, data_used = 3, id_len = 1, ...}
+ * ...
+ */
+
+/* This macro takes a pointer to the header and a zero-based index and returns
+ * a pointer to the corresponding subcache. */
+#define SHMCB_SUBCACHE(pHeader, num) \
+ (SHMCBSubcache *)(((unsigned char *)(pHeader)) + \
+ sizeof(SHMCBHeader) + \
+ (num) * ((pHeader)->subcache_size))
+
+/* This macro takes a pointer to the header and an id and returns a
+ * pointer to the corresponding subcache. */
+#define SHMCB_MASK(pHeader, id) \
+ SHMCB_SUBCACHE((pHeader), *(id) & ((pHeader)->subcache_num - 1))
+
+/* This macro takes the same params as the last, generating two outputs for use
+ * in ap_log_error(...). */
+#define SHMCB_MASK_DBG(pHeader, id) \
+ *(id), (*(id) & ((pHeader)->subcache_num - 1))
+
+/* This macro takes a pointer to a subcache and a zero-based index and returns
+ * a pointer to the corresponding SHMCBIndex. */
+#define SHMCB_INDEX(pSubcache, num) \
+ ((SHMCBIndex *)(((unsigned char *)pSubcache) + \
+ sizeof(SHMCBSubcache)) + num)
+
+/* This macro takes a pointer to the header and a subcache and returns a
+ * pointer to the corresponding data area. */
+#define SHMCB_DATA(pHeader, pSubcache) \
+ ((unsigned char *)(pSubcache) + (pHeader)->subcache_data_offset)
+
+/*
+ * Cyclic functions - assists in "wrap-around"/modulo logic
+ */
+
+/* Addition modulo 'mod' */
+#define SHMCB_CYCLIC_INCREMENT(val,inc,mod) \
+ (((val) + (inc)) % (mod))
+
+/* Subtraction (or "distance between") modulo 'mod' */
+#define SHMCB_CYCLIC_SPACE(val1,val2,mod) \
+ ((val2) >= (val1) ? ((val2) - (val1)) : \
+ ((val2) + (mod) - (val1)))
+
+/* A "normal-to-cyclic" memcpy. */
+static void shmcb_cyclic_ntoc_memcpy(unsigned int buf_size, unsigned char *data,
+ unsigned int dest_offset, const unsigned char *src,
+ unsigned int src_len)
+{
+ if (dest_offset + src_len < buf_size)
+ /* It be copied all in one go */
+ memcpy(data + dest_offset, src, src_len);
+ else {
+ /* Copy the two splits */
+ memcpy(data + dest_offset, src, buf_size - dest_offset);
+ memcpy(data, src + buf_size - dest_offset,
+ src_len + dest_offset - buf_size);
+ }
+}
+
+/* A "cyclic-to-normal" memcpy. */static void shmcb_cyclic_cton_memcpy(unsigned int buf_size, unsigned char *dest,
+ const unsigned char *data, unsigned int src_offset,
+ unsigned int src_len)
+{
+ if (src_offset + src_len < buf_size)
+ /* It be copied all in one go */
+ memcpy(dest, data + src_offset, src_len);
+ else {
+ /* Copy the two splits */
+ memcpy(dest, data + src_offset, buf_size - src_offset);
+ memcpy(dest + buf_size - src_offset, data,
+ src_len + src_offset - buf_size);
+ }
+}
+
+/* A memcmp against a cyclic data buffer. Compares SRC of length
+ * SRC_LEN against the contents of cyclic buffer DATA (which is of
+ * size BUF_SIZE), starting at offset DEST_OFFSET. Got that? Good. */
+static int shmcb_cyclic_memcmp(unsigned int buf_size, unsigned char *data,
+ unsigned int dest_offset,
+ const unsigned char *src,
+ unsigned int src_len)
+{
+ if (dest_offset + src_len < buf_size)
+ /* It be compared all in one go */
+ return memcmp(data + dest_offset, src, src_len);
+ else {
+ /* Compare the two splits */
+ int diff;
+
+ diff = memcmp(data + dest_offset, src, buf_size - dest_offset);
+ if (diff) {
+ return diff;
+ }
+ return memcmp(data, src + buf_size - dest_offset,
+ src_len + dest_offset - buf_size);
+ }
+}
+
+
+/* Prototypes for low-level subcache operations */
+static void shmcb_subcache_expire(server_rec *, SHMCBHeader *, SHMCBSubcache *,
+ apr_time_t);
+/* Returns zero on success, non-zero on failure. */
+static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ unsigned char *data, unsigned int data_len,
+ const unsigned char *id, unsigned int id_len,
+ apr_time_t expiry);
+/* Returns zero on success, non-zero on failure. */
+static int shmcb_subcache_retrieve(server_rec *, SHMCBHeader *, SHMCBSubcache *,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *data, unsigned int *datalen);
+/* Returns zero on success, non-zero on failure. */
+static int shmcb_subcache_remove(server_rec *, SHMCBHeader *, SHMCBSubcache *,
+ const unsigned char *, unsigned int);
+
+/* Returns result of the (iterator)() call, zero is success (continue) */
+static apr_status_t shmcb_subcache_iterate(ap_socache_instance_t *instance,
+ server_rec *s,
+ void *userctx,
+ SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ ap_socache_iterator_t *iterator,
+ unsigned char **buf,
+ apr_size_t *buf_len,
+ apr_pool_t *pool,
+ apr_time_t now);
+
+/*
+ * High-Level "handlers" as per ssl_scache.c
+ * subcache internals are deferred to shmcb_subcache_*** functions lower down
+ */
+
+static const char *socache_shmcb_create(ap_socache_instance_t **context,
+ const char *arg,
+ apr_pool_t *tmp, apr_pool_t *p)
+{
+ ap_socache_instance_t *ctx;
+ char *path, *cp, *cp2;
+
+ /* Allocate the context. */
+ *context = ctx = apr_pcalloc(p, sizeof *ctx);
+
+ ctx->shm_size = 1024*512; /* 512KB */
+
+ if (!arg || *arg == '\0') {
+ /* Use defaults. */
+ return NULL;
+ }
+
+ ctx->data_file = path = ap_server_root_relative(p, arg);
+
+ cp = strrchr(path, '(');
+ cp2 = path + strlen(path) - 1;
+ if (cp) {
+ char *endptr;
+ if (*cp2 != ')') {
+ return "Invalid argument: no closing parenthesis or cache size "
+ "missing after pathname with parenthesis";
+ }
+ *cp++ = '\0';
+ *cp2 = '\0';
+
+
+ ctx->shm_size = strtol(cp, &endptr, 10);
+ if (endptr != cp2) {
+ return "Invalid argument: cache size not numerical";
+ }
+
+ if (ctx->shm_size < 8192) {
+ return "Invalid argument: size has to be >= 8192 bytes";
+
+ }
+
+ if (ctx->shm_size >= SHMCB_MAX_SIZE) {
+ return apr_psprintf(tmp,
+ "Invalid argument: size has "
+ "to be < %d bytes on this platform",
+ SHMCB_MAX_SIZE);
+
+ }
+ }
+ else if (cp2 >= path && *cp2 == ')') {
+ return "Invalid argument: no opening parenthesis";
+ }
+
+ return NULL;
+}
+
+static apr_status_t socache_shmcb_init(ap_socache_instance_t *ctx,
+ const char *namespace,
+ const struct ap_socache_hints *hints,
+ server_rec *s, apr_pool_t *p)
+{
+ void *shm_segment;
+ apr_size_t shm_segsize;
+ apr_status_t rv;
+ SHMCBHeader *header;
+ unsigned int num_subcache, num_idx, loop;
+ apr_size_t avg_obj_size, avg_id_len;
+
+ /* Create shared memory segment */
+ if (ctx->data_file == NULL) {
+ const char *path = apr_pstrcat(p, DEFAULT_SHMCB_PREFIX, namespace,
+ DEFAULT_SHMCB_SUFFIX, NULL);
+
+ ctx->data_file = ap_server_root_relative(p, path);
+ }
+
+ /* Use anonymous shm by default, fall back on name-based. */
+ rv = apr_shm_create(&ctx->shm, ctx->shm_size, NULL, p);
+ if (APR_STATUS_IS_ENOTIMPL(rv)) {
+ /* If anon shm isn't supported, fail if no named file was
+ * configured successfully; the ap_server_root_relative call
+ * above will return NULL for invalid paths. */
+ if (ctx->data_file == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00818)
+ "Could not use default path '%s' for shmcb socache",
+ ctx->data_file);
+ return APR_EINVAL;
+ }
+
+ /* For a name-based segment, remove it first in case of a
+ * previous unclean shutdown. */
+ apr_shm_remove(ctx->data_file, p);
+
+ rv = apr_shm_create(&ctx->shm, ctx->shm_size, ctx->data_file, p);
+ }
+
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00819)
+ "Could not allocate shared memory segment for shmcb "
+ "socache");
+ return rv;
+ }
+
+ shm_segment = apr_shm_baseaddr_get(ctx->shm);
+ shm_segsize = apr_shm_size_get(ctx->shm);
+ if (shm_segsize < (5 * sizeof(SHMCBHeader))) {
+ /* the segment is ridiculously small, bail out */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00820)
+ "shared memory segment too small");
+ return APR_ENOSPC;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00821)
+ "shmcb_init allocated %" APR_SIZE_T_FMT
+ " bytes of shared memory",
+ shm_segsize);
+ /* Discount the header */
+ shm_segsize -= sizeof(SHMCBHeader);
+ /* Select index size based on average object size hints, if given. */
+ avg_obj_size = hints && hints->avg_obj_size ? hints->avg_obj_size : 150;
+ avg_id_len = hints && hints->avg_id_len ? hints->avg_id_len : 30;
+ num_idx = (shm_segsize) / (avg_obj_size + avg_id_len);
+ num_subcache = 256;
+ while ((num_idx / num_subcache) < (2 * num_subcache))
+ num_subcache /= 2;
+ num_idx /= num_subcache;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00822)
+ "for %" APR_SIZE_T_FMT " bytes (%" APR_SIZE_T_FMT
+ " including header), recommending %u subcaches, "
+ "%u indexes each", shm_segsize,
+ shm_segsize + sizeof(SHMCBHeader), num_subcache, num_idx);
+ if (num_idx < 5) {
+ /* we're still too small, bail out */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00823)
+ "shared memory segment too small");
+ return APR_ENOSPC;
+ }
+ /* OK, we're sorted */
+ ctx->header = header = shm_segment;
+ header->stat_stores = 0;
+ header->stat_replaced = 0;
+ header->stat_expiries = 0;
+ header->stat_scrolled = 0;
+ header->stat_retrieves_hit = 0;
+ header->stat_retrieves_miss = 0;
+ header->stat_removes_hit = 0;
+ header->stat_removes_miss = 0;
+ header->subcache_num = num_subcache;
+ /* Convert the subcache size (in bytes) to a value that is suitable for
+ * structure alignment on the host platform, by rounding down if necessary.
+ * This assumes that sizeof(unsigned long) provides an appropriate
+ * alignment unit. */
+ header->subcache_size = ((size_t)(shm_segsize / num_subcache) &
+ ~(size_t)(sizeof(unsigned long) - 1));
+ header->subcache_data_offset = sizeof(SHMCBSubcache) +
+ num_idx * sizeof(SHMCBIndex);
+ header->subcache_data_size = header->subcache_size -
+ header->subcache_data_offset;
+ header->index_num = num_idx;
+
+ /* Output trace info */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00824)
+ "shmcb_init_memory choices follow");
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00825)
+ "subcache_num = %u", header->subcache_num);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00826)
+ "subcache_size = %u", header->subcache_size);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00827)
+ "subcache_data_offset = %u", header->subcache_data_offset);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00828)
+ "subcache_data_size = %u", header->subcache_data_size);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00829)
+ "index_num = %u", header->index_num);
+ /* The header is done, make the caches empty */
+ for (loop = 0; loop < header->subcache_num; loop++) {
+ SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
+ subcache->idx_pos = subcache->idx_used = 0;
+ subcache->data_pos = subcache->data_used = 0;
+ }
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(00830)
+ "Shared memory socache initialised");
+ /* Success ... */
+
+ return APR_SUCCESS;
+}
+
+static void socache_shmcb_destroy(ap_socache_instance_t *ctx, server_rec *s)
+{
+ if (ctx && ctx->shm) {
+ apr_shm_destroy(ctx->shm);
+ ctx->shm = NULL;
+ }
+}
+
+static apr_status_t socache_shmcb_store(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_time_t expiry,
+ unsigned char *encoded,
+ unsigned int len_encoded,
+ apr_pool_t *p)
+{
+ SHMCBHeader *header = ctx->header;
+ SHMCBSubcache *subcache = SHMCB_MASK(header, id);
+ int tryreplace;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00831)
+ "socache_shmcb_store (0x%02x -> subcache %d)",
+ SHMCB_MASK_DBG(header, id));
+ /* XXX: Says who? Why shouldn't this be acceptable, or padded if not? */
+ if (idlen < 4) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00832) "unusably short id provided "
+ "(%u bytes)", idlen);
+ return APR_EINVAL;
+ }
+ tryreplace = shmcb_subcache_remove(s, header, subcache, id, idlen);
+ if (shmcb_subcache_store(s, header, subcache, encoded,
+ len_encoded, id, idlen, expiry)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00833)
+ "can't store an socache entry!");
+ return APR_ENOSPC;
+ }
+ if (tryreplace == 0) {
+ header->stat_replaced++;
+ }
+ else {
+ header->stat_stores++;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00834)
+ "leaving socache_shmcb_store successfully");
+ return APR_SUCCESS;
+}
+
+static apr_status_t socache_shmcb_retrieve(ap_socache_instance_t *ctx,
+ server_rec *s,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *dest, unsigned int *destlen,
+ apr_pool_t *p)
+{
+ SHMCBHeader *header = ctx->header;
+ SHMCBSubcache *subcache = SHMCB_MASK(header, id);
+ int rv;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00835)
+ "socache_shmcb_retrieve (0x%02x -> subcache %d)",
+ SHMCB_MASK_DBG(header, id));
+
+ /* Get the entry corresponding to the id, if it exists. */
+ rv = shmcb_subcache_retrieve(s, header, subcache, id, idlen,
+ dest, destlen);
+ if (rv == 0)
+ header->stat_retrieves_hit++;
+ else
+ header->stat_retrieves_miss++;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00836)
+ "leaving socache_shmcb_retrieve successfully");
+
+ return rv == 0 ? APR_SUCCESS : APR_NOTFOUND;
+}
+
+static apr_status_t socache_shmcb_remove(ap_socache_instance_t *ctx,
+ server_rec *s, const unsigned char *id,
+ unsigned int idlen, apr_pool_t *p)
+{
+ SHMCBHeader *header = ctx->header;
+ SHMCBSubcache *subcache = SHMCB_MASK(header, id);
+ apr_status_t rv;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00837)
+ "socache_shmcb_remove (0x%02x -> subcache %d)",
+ SHMCB_MASK_DBG(header, id));
+ if (idlen < 4) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00838) "unusably short id provided "
+ "(%u bytes)", idlen);
+ return APR_EINVAL;
+ }
+ if (shmcb_subcache_remove(s, header, subcache, id, idlen) == 0) {
+ header->stat_removes_hit++;
+ rv = APR_SUCCESS;
+ } else {
+ header->stat_removes_miss++;
+ rv = APR_NOTFOUND;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00839)
+ "leaving socache_shmcb_remove successfully");
+
+ return rv;
+}
+
+static void socache_shmcb_status(ap_socache_instance_t *ctx,
+ request_rec *r, int flags)
+{
+ server_rec *s = r->server;
+ SHMCBHeader *header = ctx->header;
+ unsigned int loop, total = 0, cache_total = 0, non_empty_subcaches = 0;
+ apr_time_t idx_expiry, min_expiry = 0, max_expiry = 0;
+ apr_time_t now = apr_time_now();
+ double expiry_total = 0;
+ int index_pct, cache_pct;
+
+ AP_DEBUG_ASSERT(header->subcache_num > 0);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00840) "inside shmcb_status");
+ /* Perform the iteration inside the mutex to avoid corruption or invalid
+ * pointer arithmetic. The rest of our logic uses read-only header data so
+ * doesn't need the lock. */
+ /* Iterate over the subcaches */
+ for (loop = 0; loop < header->subcache_num; loop++) {
+ SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
+ shmcb_subcache_expire(s, header, subcache, now);
+ total += subcache->idx_used;
+ cache_total += subcache->data_used;
+ if (subcache->idx_used) {
+ SHMCBIndex *idx = SHMCB_INDEX(subcache, subcache->idx_pos);
+ non_empty_subcaches++;
+ idx_expiry = idx->expires;
+ expiry_total += (double)idx_expiry;
+ max_expiry = ((idx_expiry > max_expiry) ? idx_expiry : max_expiry);
+ if (!min_expiry)
+ min_expiry = idx_expiry;
+ else
+ min_expiry = ((idx_expiry < min_expiry) ? idx_expiry : min_expiry);
+ }
+ }
+ index_pct = (100 * total) / (header->index_num *
+ header->subcache_num);
+ cache_pct = (100 * cache_total) / (header->subcache_data_size *
+ header->subcache_num);
+ /* Generate HTML */
+ ap_rprintf(r, "cache type: <b>SHMCB</b>, shared memory: <b>%" APR_SIZE_T_FMT "</b> "
+ "bytes, current entries: <b>%d</b><br>",
+ ctx->shm_size, total);
+ ap_rprintf(r, "subcaches: <b>%d</b>, indexes per subcache: <b>%d</b><br>",
+ header->subcache_num, header->index_num);
+ if (non_empty_subcaches) {
+ apr_time_t average_expiry = (apr_time_t)(expiry_total / (double)non_empty_subcaches);
+ ap_rprintf(r, "time left on oldest entries' objects: ");
+ if (now < average_expiry)
+ ap_rprintf(r, "avg: <b>%d</b> seconds, (range: %d...%d)<br>",
+ (int)apr_time_sec(average_expiry - now),
+ (int)apr_time_sec(min_expiry - now),
+ (int)apr_time_sec(max_expiry - now));
+ else
+ ap_rprintf(r, "expiry_threshold: <b>Calculation error!</b><br>");
+ }
+
+ ap_rprintf(r, "index usage: <b>%d%%</b>, cache usage: <b>%d%%</b><br>",
+ index_pct, cache_pct);
+ ap_rprintf(r, "total entries stored since starting: <b>%lu</b><br>",
+ header->stat_stores);
+ ap_rprintf(r, "total entries replaced since starting: <b>%lu</b><br>",
+ header->stat_replaced);
+ ap_rprintf(r, "total entries expired since starting: <b>%lu</b><br>",
+ header->stat_expiries);
+ ap_rprintf(r, "total (pre-expiry) entries scrolled out of the cache: "
+ "<b>%lu</b><br>", header->stat_scrolled);
+ ap_rprintf(r, "total retrieves since starting: <b>%lu</b> hit, "
+ "<b>%lu</b> miss<br>", header->stat_retrieves_hit,
+ header->stat_retrieves_miss);
+ ap_rprintf(r, "total removes since starting: <b>%lu</b> hit, "
+ "<b>%lu</b> miss<br>", header->stat_removes_hit,
+ header->stat_removes_miss);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00841) "leaving shmcb_status");
+}
+
+static apr_status_t socache_shmcb_iterate(ap_socache_instance_t *instance,
+ server_rec *s, void *userctx,
+ ap_socache_iterator_t *iterator,
+ apr_pool_t *pool)
+{
+ SHMCBHeader *header = instance->header;
+ unsigned int loop;
+ apr_time_t now = apr_time_now();
+ apr_status_t rv = APR_SUCCESS;
+ apr_size_t buflen = 0;
+ unsigned char *buf = NULL;
+
+ /* Perform the iteration inside the mutex to avoid corruption or invalid
+ * pointer arithmetic. The rest of our logic uses read-only header data so
+ * doesn't need the lock. */
+ /* Iterate over the subcaches */
+ for (loop = 0; loop < header->subcache_num && rv == APR_SUCCESS; loop++) {
+ SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
+ rv = shmcb_subcache_iterate(instance, s, userctx, header, subcache,
+ iterator, &buf, &buflen, pool, now);
+ }
+ return rv;
+}
+
+/*
+ * Subcache-level cache operations
+ */
+
+static void shmcb_subcache_expire(server_rec *s, SHMCBHeader *header,
+ SHMCBSubcache *subcache, apr_time_t now)
+{
+ unsigned int loop = 0, freed = 0, expired = 0;
+ unsigned int new_idx_pos = subcache->idx_pos;
+ SHMCBIndex *idx = NULL;
+
+ while (loop < subcache->idx_used) {
+ idx = SHMCB_INDEX(subcache, new_idx_pos);
+ if (idx->removed)
+ freed++;
+ else if (idx->expires <= now)
+ expired++;
+ else
+ /* not removed and not expired yet, we're done iterating */
+ break;
+ loop++;
+ new_idx_pos = SHMCB_CYCLIC_INCREMENT(new_idx_pos, 1, header->index_num);
+ }
+ if (!loop)
+ /* Nothing to do */
+ return;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00842)
+ "expiring %u and reclaiming %u removed socache entries",
+ expired, freed);
+ if (loop == subcache->idx_used) {
+ /* We're expiring everything, piece of cake */
+ subcache->idx_used = 0;
+ subcache->data_used = 0;
+ } else {
+ /* There remain other indexes, so we can use idx to adjust 'data' */
+ unsigned int diff = SHMCB_CYCLIC_SPACE(subcache->data_pos,
+ idx->data_pos,
+ header->subcache_data_size);
+ /* Adjust the indexes */
+ subcache->idx_used -= loop;
+ subcache->idx_pos = new_idx_pos;
+ /* Adjust the data area */
+ subcache->data_used -= diff;
+ subcache->data_pos = idx->data_pos;
+ }
+ header->stat_expiries += expired;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00843)
+ "we now have %u socache entries", subcache->idx_used);
+}
+
+static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ unsigned char *data, unsigned int data_len,
+ const unsigned char *id, unsigned int id_len,
+ apr_time_t expiry)
+{
+ unsigned int data_offset, new_idx, id_offset;
+ SHMCBIndex *idx;
+ unsigned int total_len = id_len + data_len;
+
+ /* Sanity check the input */
+ if (total_len > header->subcache_data_size) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00844)
+ "inserting socache entry larger (%d) than subcache data area (%d)",
+ total_len, header->subcache_data_size);
+ return -1;
+ }
+
+ /* First reclaim space from removed and expired records. */
+ shmcb_subcache_expire(s, header, subcache, apr_time_now());
+
+ /* Loop until there is enough space to insert
+ * XXX: This should first compress out-of-order expiries and
+ * removed records, and then force-remove oldest-first
+ */
+ if (header->subcache_data_size - subcache->data_used < total_len
+ || subcache->idx_used == header->index_num) {
+ unsigned int loop = 0;
+
+ idx = SHMCB_INDEX(subcache, subcache->idx_pos);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00845)
+ "about to force-expire, subcache: idx_used=%d, "
+ "data_used=%d", subcache->idx_used, subcache->data_used);
+ do {
+ SHMCBIndex *idx2;
+
+ /* Adjust the indexes by one */
+ subcache->idx_pos = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, 1,
+ header->index_num);
+ subcache->idx_used--;
+ if (!subcache->idx_used) {
+ /* There's nothing left */
+ subcache->data_used = 0;
+ break;
+ }
+ /* Adjust the data */
+ idx2 = SHMCB_INDEX(subcache, subcache->idx_pos);
+ subcache->data_used -= SHMCB_CYCLIC_SPACE(idx->data_pos, idx2->data_pos,
+ header->subcache_data_size);
+ subcache->data_pos = idx2->data_pos;
+ /* Stats */
+ header->stat_scrolled++;
+ /* Loop admin */
+ idx = idx2;
+ loop++;
+ } while (header->subcache_data_size - subcache->data_used < total_len);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00846)
+ "finished force-expire, subcache: idx_used=%d, "
+ "data_used=%d", subcache->idx_used, subcache->data_used);
+ }
+
+ /* HERE WE ASSUME THAT THE NEW ENTRY SHOULD GO ON THE END! I'M NOT
+ * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE.
+ *
+ * We aught to fix that. httpd (never mind third party modules)
+ * does not promise to perform any processing in date order
+ * (c.f. FAQ "My log entries are not in date order!")
+ */
+ /* Insert the id */
+ id_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
+ header->subcache_data_size);
+ shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
+ SHMCB_DATA(header, subcache), id_offset,
+ id, id_len);
+ subcache->data_used += id_len;
+ /* Insert the data */
+ data_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
+ header->subcache_data_size);
+ shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
+ SHMCB_DATA(header, subcache), data_offset,
+ data, data_len);
+ subcache->data_used += data_len;
+ /* Insert the index */
+ new_idx = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, subcache->idx_used,
+ header->index_num);
+ idx = SHMCB_INDEX(subcache, new_idx);
+ idx->expires = expiry;
+ idx->data_pos = id_offset;
+ idx->data_used = total_len;
+ idx->id_len = id_len;
+ idx->removed = 0;
+ subcache->idx_used++;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00847)
+ "insert happened at idx=%d, data=(%u:%u)", new_idx,
+ id_offset, data_offset);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00848)
+ "finished insert, subcache: idx_pos/idx_used=%d/%d, "
+ "data_pos/data_used=%d/%d",
+ subcache->idx_pos, subcache->idx_used,
+ subcache->data_pos, subcache->data_used);
+ return 0;
+}
+
+static int shmcb_subcache_retrieve(server_rec *s, SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ const unsigned char *id, unsigned int idlen,
+ unsigned char *dest, unsigned int *destlen)
+{
+ unsigned int pos;
+ unsigned int loop = 0;
+ apr_time_t now = apr_time_now();
+
+ pos = subcache->idx_pos;
+
+ while (loop < subcache->idx_used) {
+ SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
+
+ /* Only consider 'idx' if the id matches, and the "removed"
+ * flag isn't set, and the record is not expired.
+ * Check the data length too to avoid a buffer overflow
+ * in case of corruption, which should be impossible,
+ * but it's cheap to be safe. */
+ if (!idx->removed
+ && idx->id_len == idlen
+ && (idx->data_used - idx->id_len) <= *destlen
+ && shmcb_cyclic_memcmp(header->subcache_data_size,
+ SHMCB_DATA(header, subcache),
+ idx->data_pos, id, idx->id_len) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00849)
+ "match at idx=%d, data=%d", pos, idx->data_pos);
+ if (idx->expires > now) {
+ unsigned int data_offset;
+
+ /* Find the offset of the data segment, after the id */
+ data_offset = SHMCB_CYCLIC_INCREMENT(idx->data_pos,
+ idx->id_len,
+ header->subcache_data_size);
+
+ *destlen = idx->data_used - idx->id_len;
+
+ /* Copy out the data */
+ shmcb_cyclic_cton_memcpy(header->subcache_data_size,
+ dest, SHMCB_DATA(header, subcache),
+ data_offset, *destlen);
+
+ return 0;
+ }
+ else {
+ /* Already stale, quietly remove and treat as not-found */
+ idx->removed = 1;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00850)
+ "shmcb_subcache_retrieve discarding expired entry");
+ return -1;
+ }
+ }
+ /* Increment */
+ loop++;
+ pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00851)
+ "shmcb_subcache_retrieve found no match");
+ return -1;
+}
+
+static int shmcb_subcache_remove(server_rec *s, SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ const unsigned char *id,
+ unsigned int idlen)
+{
+ unsigned int pos;
+ unsigned int loop = 0;
+
+ pos = subcache->idx_pos;
+ while (loop < subcache->idx_used) {
+ SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
+
+ /* Only consider 'idx' if the id matches, and the "removed"
+ * flag isn't set. */
+ if (!idx->removed && idx->id_len == idlen
+ && shmcb_cyclic_memcmp(header->subcache_data_size,
+ SHMCB_DATA(header, subcache),
+ idx->data_pos, id, idx->id_len) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00852)
+ "possible match at idx=%d, data=%d", pos, idx->data_pos);
+
+ /* Found the matching entry, remove it quietly. */
+ idx->removed = 1;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00853)
+ "shmcb_subcache_remove removing matching entry");
+ return 0;
+ }
+ /* Increment */
+ loop++;
+ pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
+ }
+
+ return -1; /* failure */
+}
+
+
+static apr_status_t shmcb_subcache_iterate(ap_socache_instance_t *instance,
+ server_rec *s,
+ void *userctx,
+ SHMCBHeader *header,
+ SHMCBSubcache *subcache,
+ ap_socache_iterator_t *iterator,
+ unsigned char **buf,
+ apr_size_t *buf_len,
+ apr_pool_t *pool,
+ apr_time_t now)
+{
+ unsigned int pos;
+ unsigned int loop = 0;
+ apr_status_t rv;
+
+ pos = subcache->idx_pos;
+ while (loop < subcache->idx_used) {
+ SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
+
+ /* Only consider 'idx' if the "removed" flag isn't set. */
+ if (!idx->removed) {
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00854)
+ "iterating idx=%d, data=%d", pos, idx->data_pos);
+ if (idx->expires > now) {
+ unsigned char *id = *buf;
+ unsigned char *dest;
+ unsigned int data_offset, dest_len;
+ apr_size_t buf_req;
+
+ /* Find the offset of the data segment, after the id */
+ data_offset = SHMCB_CYCLIC_INCREMENT(idx->data_pos,
+ idx->id_len,
+ header->subcache_data_size);
+
+ dest_len = idx->data_used - idx->id_len;
+
+ buf_req = APR_ALIGN_DEFAULT(idx->id_len + 1)
+ + APR_ALIGN_DEFAULT(dest_len + 1);
+
+ if (buf_req > *buf_len) {
+ /* Grow to ~150% of this buffer requirement on resize
+ * always using APR_ALIGN_DEFAULT sized pages
+ */
+ *buf_len = buf_req + APR_ALIGN_DEFAULT(buf_req / 2);
+ *buf = apr_palloc(pool, *buf_len);
+ id = *buf;
+ }
+
+ dest = *buf + APR_ALIGN_DEFAULT(idx->id_len + 1);
+
+ /* Copy out the data, because it's potentially cyclic */
+ shmcb_cyclic_cton_memcpy(header->subcache_data_size, id,
+ SHMCB_DATA(header, subcache),
+ idx->data_pos, idx->id_len);
+ id[idx->id_len] = '\0';
+
+ shmcb_cyclic_cton_memcpy(header->subcache_data_size, dest,
+ SHMCB_DATA(header, subcache),
+ data_offset, dest_len);
+ dest[dest_len] = '\0';
+
+ rv = iterator(instance, s, userctx, id, idx->id_len,
+ dest, dest_len, pool);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00855)
+ "shmcb entry iterated");
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+ else {
+ /* Already stale, quietly remove and treat as not-found */
+ idx->removed = 1;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00856)
+ "shmcb_subcache_iterate discarding expired entry");
+ }
+ }
+ /* Increment */
+ loop++;
+ pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
+ }
+
+ return APR_SUCCESS;
+}
+
+static const ap_socache_provider_t socache_shmcb = {
+ "shmcb",
+ AP_SOCACHE_FLAG_NOTMPSAFE,
+ socache_shmcb_create,
+ socache_shmcb_init,
+ socache_shmcb_destroy,
+ socache_shmcb_store,
+ socache_shmcb_retrieve,
+ socache_shmcb_remove,
+ socache_shmcb_status,
+ socache_shmcb_iterate
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "shmcb",
+ AP_SOCACHE_PROVIDER_VERSION,
+ &socache_shmcb);
+
+ /* Also register shmcb under the default provider name. */
+ ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP,
+ AP_SOCACHE_DEFAULT_PROVIDER,
+ AP_SOCACHE_PROVIDER_VERSION,
+ &socache_shmcb);
+}
+
+AP_DECLARE_MODULE(socache_shmcb) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, NULL, NULL, NULL, NULL,
+ register_hooks
+};
diff --git a/modules/cache/mod_socache_shmcb.dsp b/modules/cache/mod_socache_shmcb.dsp
new file mode 100644
index 00000000..822c9e24
--- /dev/null
+++ b/modules/cache/mod_socache_shmcb.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_socache_shmcb" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_socache_shmcb - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_shmcb.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_socache_shmcb.mak" CFG="mod_socache_shmcb - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_socache_shmcb - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_shmcb - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_socache_shmcb - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_socache_shmcb_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_socache_shmcb_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_socache_shmcb.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_socache_shmcb.so" /d LONG_NAME="socache_shmcb_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_socache_shmcb.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_shmcb.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_socache_shmcb.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_socache_shmcb - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_socache_shmcb_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_socache_shmcb.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_socache_shmcb.so" /d LONG_NAME="socache_shmcb_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_socache_shmcb.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_shmcb.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_socache_shmcb.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_socache_shmcb - Win32 Release"
+# Name "mod_socache_shmcb - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_socache_shmcb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project