diff --git a/NEWS b/NEWS
index 6a934ded..38b2e200 100644
--- a/NEWS
+++ b/NEWS
@@ -74,6 +74,7 @@ NEWS
* [mod_dirlisting] class for dir
(fixes #2304)
* [core] define __STDC_WANT_LIB_EXT1__ (fixes #2722)
* [core] setrlimit max-fds <= rlim_max for non-root (fixes #2723)
+ * [mod_ssi] config ssi.conditional-requests
- 1.4.39 - 2016-01-02
* [core] fix memset_s call (fixes #2698)
diff --git a/doc/config/conf.d/ssi.conf b/doc/config/conf.d/ssi.conf
index 48af91c3..cd1f552c 100644
--- a/doc/config/conf.d/ssi.conf
+++ b/doc/config/conf.d/ssi.conf
@@ -1,6 +1,6 @@
#######################################################################
##
-## Server Side Includes
+## Server Side Includes
## -----------------------
##
## See http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSSI
@@ -12,5 +12,34 @@ server.modules += ( "mod_ssi" )
##
ssi.extension = ( ".shtml" )
+##
+## The ssi.conditional-requests directive only affects requests
+## handled by the SSI module and allows to declare which SSI pages
+## are cacheable and which are not. This directive can be enabled
+## or disabled globally and/or in any context.
+##
+## As the name of this directive suggests, conditional requests will
+## be handled appropriately for any SSI page for which the directive
+## is enabled. In particular, the "ETag" and "Last-Modified" headers
+## will both be sent. Conversely, these headers will NOT be sent for
+## pages for which the directive is disabled.
+##
+## The directive should be set to "enable" ONLY for requests that are
+## known to generate cacheable documents. An SSI page which only
+## includes contents from other static files and/or which uses SSI
+## commands that produce predictable output (e.g. the echo command
+## for the LAST_MODIFIED variable) is likely to be cacheable.
+##
+## The directive should be set to "disable" for ALL other documents,
+## that is, for SSI pages which depend on non-predictable input such
+## as (but not limited to) output from ssi exec commands, data from
+## the client's request headers (other than the request URI), or any
+## other non constant input such as the current date or time, the
+## client's user-agent, etc...
+##
+## Disabled by default.
+##
+#ssi.conditional-requests = "enable"
+
##
#######################################################################
diff --git a/src/mod_ssi.c b/src/mod_ssi.c
index 16f1e1b7..f214ace7 100644
--- a/src/mod_ssi.c
+++ b/src/mod_ssi.c
@@ -107,6 +107,7 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
config_values_t cv[] = {
{ "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
+ { "ssi.conditional-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
@@ -121,9 +122,11 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
s = calloc(1, sizeof(plugin_config));
s->ssi_extension = array_init();
s->content_type = buffer_init();
+ s->conditional_requests = 0;
cv[0].destination = s->ssi_extension;
cv[1].destination = s->content_type;
+ cv[2].destination = &(s->conditional_requests);
p->config_storage[i] = s;
@@ -1078,7 +1081,7 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p)
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type));
}
- {
+ if (p->conf.conditional_requests) {
/* Generate "ETag" & "Last-Modified" headers */
time_t lm_time = 0;
buffer *mtime = NULL;
@@ -1093,6 +1096,13 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p)
mtime = strftime_cache_get(srv, lm_time);
response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
+
+ if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
+ /* ok, the client already has our content,
+ * no need to send it again */
+
+ chunkqueue_reset(con->write_queue);
+ }
}
/* Reset the modified time of included files */
@@ -1112,6 +1122,7 @@ static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p
PATCH(ssi_extension);
PATCH(content_type);
+ PATCH(conditional_requests);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
@@ -1129,6 +1140,8 @@ static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p
PATCH(ssi_extension);
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) {
PATCH(content_type);
+ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.conditional-requests"))) {
+ PATCH(conditional_requests);
}
}
}
diff --git a/src/mod_ssi.h b/src/mod_ssi.h
index aeff85e4..91d2f667 100644
--- a/src/mod_ssi.h
+++ b/src/mod_ssi.h
@@ -17,6 +17,7 @@
typedef struct {
array *ssi_extension;
buffer *content_type;
+ unsigned short conditional_requests;
} plugin_config;
typedef struct {