diff --git a/cherokee/connection.c b/cherokee/connection.c index 0c2c72c5c..d851dbe25 100644 --- a/cherokee/connection.c +++ b/cherokee/connection.c @@ -616,16 +616,29 @@ build_response_header_authentication (cherokee_connection_t *conn, cherokee_buff } +ret_t +cherokee_connection_read_post (cherokee_connection_t *conn) +{ + /* Shortcut + */ + if (conn->handler->read_post == NULL) { + return ret_ok; + } + + return cherokee_handler_read_post (conn->handler); +} + + void cherokee_connection_add_expiration_header (cherokee_connection_t *conn, cherokee_buffer_t *buffer, cherokee_boolean_t use_maxage) { - time_t exp_time; - struct tm exp_tm; - char bufstr[DTM_SIZE_GMTTM_STR + 2]; - size_t szlen = 0; - cherokee_boolean_t first_prop = true; + time_t exp_time; + struct tm exp_tm; + char bufstr[DTM_SIZE_GMTTM_STR + 2]; + size_t szlen = 0; + cherokee_boolean_t first_prop = true; /* Expires, and Cache-Control: max-age */ @@ -801,22 +814,6 @@ build_response_header (cherokee_connection_t *conn, } } - /* Expiration - */ - if (conn->expiration != cherokee_expiration_none) { - /* Front-line Cache (in) */ - if (conn->flcache.mode == flcache_mode_in) { - cherokee_buffer_clean (tmp1); - cherokee_connection_add_expiration_header (conn, tmp1, false); - cherokee_buffer_add_buffer (buffer, tmp1); - cherokee_buffer_add_buffer (&conn->flcache.header, tmp1); - } - /* Straight */ - else { - cherokee_connection_add_expiration_header (conn, buffer, true); - } - } - /* Redirected connections */ if (conn->redirect.len >= 1) { @@ -840,25 +837,36 @@ build_response_header (cherokee_connection_t *conn, cherokee_encoder_add_headers (conn->encoder, buffer); } } - - /* Headers ops - */ - if (conn->header_ops) { - cherokee_header_op_render (conn->header_ops, buffer); - } } -ret_t -cherokee_connection_read_post (cherokee_connection_t *conn) +static void +build_response_header_final (cherokee_connection_t *conn, + cherokee_buffer_t *buffer) { - /* Shortcut + cherokee_buffer_t *tmp1 = THREAD_TMP_BUF1(CONN_THREAD(conn)); + + /* Expiration */ - if (conn->handler->read_post == NULL) { - return ret_ok; + if (conn->expiration != cherokee_expiration_none) { + /* Add expiration headers if Front-Line Cache is not + * enabled. If it were, the header was already stored + * in the response file. + */ + if (conn->flcache.mode == flcache_mode_undef) { + cherokee_connection_add_expiration_header (conn, buffer, true); + } else if (conn->flcache.mode == flcache_mode_in) { + cherokee_header_del_entry (buffer, "Cache-Control", 13); + cherokee_connection_add_expiration_header (conn, buffer, true); + } } - return cherokee_handler_read_post (conn->handler); + /* Headers ops: + * This must be the last operation + */ + if (conn->header_ops) { + cherokee_header_op_render (conn->header_ops, &conn->buffer); + } } @@ -944,7 +952,7 @@ cherokee_connection_build_header (cherokee_connection_t *conn) } out: - /* Add the server headers + /* Add the server headers (phase 1) */ build_response_header (conn, &conn->buffer); @@ -952,11 +960,9 @@ cherokee_connection_build_header (cherokee_connection_t *conn) */ cherokee_buffer_add_buffer (&conn->buffer, &conn->header_buffer); - /* Headers ops + /* Add the server headers (phase 2) */ - if (conn->header_ops) { - cherokee_header_op_render (conn->header_ops, &conn->buffer); - } + build_response_header_final (conn, &conn->buffer); /* EOH */ diff --git a/cherokee/flcache.c b/cherokee/flcache.c index bdb0ef332..cfc5025ec 100644 --- a/cherokee/flcache.c +++ b/cherokee/flcache.c @@ -340,7 +340,7 @@ cherokee_flcache_conn_init (cherokee_flcache_conn_t *flcache_conn) flcache_conn->header_sent = 0; flcache_conn->response_sent = 0; flcache_conn->avl_node_ref = NULL; - flcache_conn->mode = flcache_mdoe_undef; + flcache_conn->mode = flcache_mode_undef; flcache_conn->fd = -1; cherokee_buffer_init (&flcache_conn->header); @@ -407,14 +407,17 @@ inspect_header (cherokee_flcache_conn_t *flcache_conn, const char *header_end; char chr_end; char *p, *q; - cherokee_avl_flcache_node_t *node = flcache_conn->avl_node_ref; - cherokee_boolean_t via_found = false; - cherokee_buffer_t *tmp = THREAD_TMP_BUF2(CONN_THREAD(conn)); - cherokee_boolean_t do_cache = false; + cherokee_boolean_t overwrite_control; + cherokee_avl_flcache_node_t *node = flcache_conn->avl_node_ref; + cherokee_boolean_t via_found = false; + cherokee_buffer_t *tmp = THREAD_TMP_BUF2(CONN_THREAD(conn)); + cherokee_boolean_t do_cache = false; begin = header->buf; header_end = header->buf + header->len; + overwrite_control = (conn->expiration != cherokee_expiration_none); + while ((begin < header_end)) { end = cherokee_header_get_next_line (begin); if (end == NULL) { @@ -427,10 +430,16 @@ inspect_header (cherokee_flcache_conn_t *flcache_conn, /* Expire */ if (strncasecmp (begin, "Expires:", 8) == 0) { + /* Cache control overridden */ + if (overwrite_control) { + goto remove_line; + } + + /* Regular Cache control */ value = begin + 8; while ((*value == ' ') && (value < end)) value++; - node->valid_until = 0; + node->valid_until = 0; cherokee_dtm_str2time (value, end - value, &node->valid_until); if (node->valid_until > cherokee_bogonow_now + 1) { @@ -441,20 +450,19 @@ inspect_header (cherokee_flcache_conn_t *flcache_conn, /* Content-length */ else if (strncasecmp (begin, "Content-Length:", 15) == 0) { - *end = chr_end; - - while ((*end == CHR_CR) || (*end == CHR_LF)) - end++; - - cherokee_buffer_remove_chunk (header, begin - header->buf, end - begin); - - end = begin; - continue; + goto remove_line; } /* Cache-Control */ else if (strncasecmp (begin, "Cache-Control:", 14) == 0) { + + /* Cache control overridden */ + if (overwrite_control) { + goto remove_line; + } + + /* Regular Cache control */ value = begin + 8; while ((*value == ' ') && (value < end)) value++; @@ -560,6 +568,18 @@ inspect_header (cherokee_flcache_conn_t *flcache_conn, while ((*end == CHR_CR) || (*end == CHR_LF)) end++; begin = end; + continue; + + remove_line: + *end = chr_end; + + while ((*end == CHR_CR) || (*end == CHR_LF)) + end++; + + cherokee_buffer_remove_chunk (header, begin - header->buf, end - begin); + + end = begin; + continue; } /* Check the caching policy @@ -578,6 +598,12 @@ inspect_header (cherokee_flcache_conn_t *flcache_conn, cherokee_buffer_add_str (header, " (Cherokee/"PACKAGE_VERSION")" CRLF); } + /* Overwritten Cache-Control / Expiration + */ + if (overwrite_control) { + cherokee_connection_add_expiration_header (conn, header, false); + } + return ret_ok; } @@ -858,7 +884,7 @@ cherokee_flcache_conn_clean (cherokee_flcache_conn_t *flcache_conn) */ flcache_conn->header_sent = 0; flcache_conn->response_sent = 0; - flcache_conn->mode = flcache_mdoe_undef; + flcache_conn->mode = flcache_mode_undef; if (flcache_conn->fd != -1) { cherokee_fd_close (flcache_conn->fd); diff --git a/cherokee/flcache.h b/cherokee/flcache.h index aba997865..757e32595 100644 --- a/cherokee/flcache.h +++ b/cherokee/flcache.h @@ -46,7 +46,7 @@ typedef struct cherokee_flcache_conn cherokee_flcache_conn_t; */ typedef enum { - flcache_mdoe_undef, + flcache_mode_undef, flcache_mode_in, flcache_mode_out, flcache_mode_error diff --git a/cherokee/handler_file.c b/cherokee/handler_file.c index 09c293a7a..339bd6083 100644 --- a/cherokee/handler_file.c +++ b/cherokee/handler_file.c @@ -484,7 +484,7 @@ cherokee_handler_file_custom_init (cherokee_handler_file_t *fhdl, (conn->encoder_new_func == NULL) && (HDL_FILE_PROP(fhdl)->use_cache) && (conn->socket.is_tls == non_TLS) && - (conn->flcache.mode == flcache_mdoe_undef) && + (conn->flcache.mode == flcache_mode_undef) && (http_method_with_body (conn->header.method)) && (fhdl->info->st_size <= srv->iocache->max_file_size) && (fhdl->info->st_size >= srv->iocache->min_file_size)); @@ -624,7 +624,7 @@ cherokee_handler_file_custom_init (cherokee_handler_file_t *fhdl, (conn->encoder == NULL) && (conn->encoder_new_func == NULL) && (conn->socket.is_tls == non_TLS) && - (conn->flcache.mode == flcache_mdoe_undef) && + (conn->flcache.mode == flcache_mode_undef) && (fhdl->info->st_size >= srv->sendfile.min) && (fhdl->info->st_size < srv->sendfile.max)); diff --git a/cherokee/util.c b/cherokee/util.c index a0fc97466..29c1c8890 100644 --- a/cherokee/util.c +++ b/cherokee/util.c @@ -2033,6 +2033,42 @@ cherokee_header_get_next_line (char *string) return end1; } +ret_t +cherokee_header_del_entry (cherokee_buffer_t *header, + const char *header_name, + int header_name_len) +{ + char *end; + char *begin; + const char *header_end; + + begin = header->buf; + header_end = header->buf + header->len; + + while ((begin < header_end)) { + end = cherokee_header_get_next_line (begin); + if (end == NULL) { + break; + } + + /* Is it the header? */ + if (strncasecmp (begin, header_name, header_name_len) == 0) { + while ((*end == CHR_CR) || (*end == CHR_LF)) + end++; + + cherokee_buffer_remove_chunk (header, begin - header->buf, end - begin); + return ret_ok; + } + + /* Next line */ + while ((*end == CHR_CR) || (*end == CHR_LF)) + end++; + begin = end; + } + + return ret_not_found; +} + #ifndef HAVE_STRNSTR char * diff --git a/cherokee/util.h b/cherokee/util.h index 5777d0819..7ceac1d9e 100644 --- a/cherokee/util.h +++ b/cherokee/util.h @@ -227,7 +227,11 @@ ret_t cherokee_split_arguments (cherokee_buffer_t *request, ret_t cherokee_parse_query_string (cherokee_buffer_t *qstring, cherokee_avl_t *arguments); -char *cherokee_header_get_next_line (char *line); +char *cherokee_header_get_next_line (char *line); +ret_t cherokee_header_del_entry (cherokee_buffer_t *header, + const char *header_name, + int header_name_len); + CHEROKEE_END_DECLS diff --git a/qa/285-Flcache-overwrite.py b/qa/285-Flcache-overwrite.py new file mode 100644 index 000000000..ca773c12b --- /dev/null +++ b/qa/285-Flcache-overwrite.py @@ -0,0 +1,56 @@ +from base import * + +DIR = "flcache-replace1" +FILE = "file.txt" +CONTENT = "Front-line cache boosts Cherokee performance" + +CONF = """ +vserver!1!rule!2850!match = directory +vserver!1!rule!2850!match!directory = /%(DIR)s +vserver!1!rule!2850!flcache = allow +vserver!1!rule!2850!flcache!policy = all_but_forbidden +vserver!1!rule!2850!expiration = time +vserver!1!rule!2850!expiration!caching = public +vserver!1!rule!2850!expiration!time = 300 +vserver!1!rule!2850!handler = cgi +""" %(globals()) + +CGI_CODE = """#!/bin/sh + +echo "Content-Type: text/plain" +echo "Cache-Control: no-store, no-cache" +echo + +echo "%(CONTENT)s" +""" %(globals()) + + +class TestEntry (TestBase): + def __init__ (self): + TestBase.__init__ (self, __file__) + self.request = "GET /%s/%s HTTP/1.0\r\n" %(DIR, FILE) +\ + "Connection: close\r\n" + self.expected_error = 200 + + +class Test (TestCollection): + def __init__ (self): + TestCollection.__init__ (self, __file__) + + self.name = "Front-line: Overwrite Cache-Control" + self.conf = CONF + self.proxy_suitable = True + + def Prepare (self, www): + d = self.Mkdir (www, DIR) + self.WriteFile (d, FILE, 0755, CGI_CODE) + + # First request + obj = self.Add (TestEntry()) + obj.expected_content = ['X-Cache: MISS', CONTENT, "public"] + obj.forbidden_content = ['no-store', 'no-cache'] + + # Second request + obj = self.Add (TestEntry()) + obj.expected_content = ['X-Cache: HIT', CONTENT, "public"] + obj.forbidden_content = ['no-store', 'no-cache'] diff --git a/qa/Makefile.am b/qa/Makefile.am index 17e3faa62..074b77f46 100644 --- a/qa/Makefile.am +++ b/qa/Makefile.am @@ -305,7 +305,8 @@ run-tests.py \ 281-Flcache-expired1.py \ 282-Flcache-expired2.py \ 283-Flcache-expired3.py \ -284-SSI-include-recursive.py +284-SSI-include-recursive.py \ +285-Flcache-overwrite.py test: python -m compileall .