diff options
| author | Arno Töll <debian@toell.net> | 2012-01-08 22:53:17 +0100 |
|---|---|---|
| committer | Arno Töll <debian@toell.net> | 2012-01-08 22:53:17 +0100 |
| commit | e072a2dd866b7cb9f14319b80326a4e7fd16fcdf (patch) | |
| tree | a49dfc56d94a26011fe157835ff6cbe14edbd8a9 /modules/cache | |
| parent | 0890390c00801651d08d3794e13b31a5dabbf5ef (diff) | |
| download | apache2-e072a2dd866b7cb9f14319b80326a4e7fd16fcdf.tar.gz | |
Imported Upstream version 2.3.16-beta
Diffstat (limited to 'modules/cache')
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 |
