| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| 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. |
| */ |
| |
| /***************************************************************************** |
| * |
| * CacheControl.cc - Implementation to Cache Control systtem |
| * |
| * |
| ****************************************************************************/ |
| |
| #include "ink_config.h" |
| #include "ink_unused.h" /* MAGIC_EDITING_TAG */ |
| |
| #include <sys/types.h> |
| |
| #ifdef HAVE_PCRE_PCRE_H |
| #include <pcre/pcre.h> |
| #else |
| #include <pcre.h> |
| #endif |
| |
| #include "CacheControl.h" |
| #include "ControlMatcher.h" |
| #include "Main.h" |
| #include "Error.h" |
| #include "P_EventSystem.h" |
| #include "ProxyConfig.h" |
| #include "HTTP.h" |
| #include "HttpConfig.h" |
| #include "StatSystem.h" |
| #include "P_Cache.h" |
| |
| #ifndef INK_NO_ACL |
| static const char modulePrefix[] = "[CacheControl]"; |
| |
| static const char *CC_directive_str[CC_NUM_TYPES] = { |
| "INVALID", |
| "REVALIDATE_AFTER", |
| "NEVER_CACHE", |
| "STANDARD_CACHE", |
| "IGNORE_NO_CACHE", |
| "IGNORE_CLIENT_NO_CACHE", |
| "IGNORE_SERVER_NO_CACHE", |
| "PIN_IN_CACHE", |
| "TTL_IN_CACHE" |
| // "CACHE_AUTH_CONTENT" |
| }; |
| |
| typedef ControlMatcher<CacheControlRecord, CacheControlResult> CC_table; |
| |
| // Global Ptrs |
| static Ptr<ProxyMutex> reconfig_mutex = NULL; |
| CC_table *CacheControlTable = NULL; |
| |
| void |
| CC_delete_table() |
| { |
| delete CacheControlTable; |
| } |
| |
| // struct CC_FreerContinuation |
| // Continuation to free old cache control lists after |
| // a timeout |
| // |
| struct CC_FreerContinuation; |
| typedef int (CC_FreerContinuation::*CC_FreerContHandler) (int, void *); |
| struct CC_FreerContinuation: public Continuation |
| { |
| CC_table *p; |
| int freeEvent(int event, Event * e) |
| { |
| NOWARN_UNUSED(event); |
| NOWARN_UNUSED(e); |
| Debug("cache_control", "Deleting old table"); |
| delete p; |
| delete this; |
| return EVENT_DONE; |
| } |
| CC_FreerContinuation(CC_table * ap):Continuation(NULL), p(ap) |
| { |
| SET_HANDLER((CC_FreerContHandler) & CC_FreerContinuation::freeEvent); |
| } |
| }; |
| |
| |
| // struct CC_UpdateContinuation |
| // |
| // Used to read the cache.conf file after the manager signals |
| // a change |
| // |
| struct CC_UpdateContinuation: public Continuation |
| { |
| int file_update_handler(int etype, void *data) |
| { |
| NOWARN_UNUSED(etype); |
| NOWARN_UNUSED(data); |
| reloadCacheControl(); |
| delete this; |
| return EVENT_DONE; |
| } |
| CC_UpdateContinuation(ProxyMutex * m):Continuation(m) |
| { |
| SET_HANDLER(&CC_UpdateContinuation::file_update_handler); |
| } |
| }; |
| |
| int |
| cacheControlFile_CB(const char *name, RecDataT data_type, RecData data, void *cookie) |
| { |
| NOWARN_UNUSED(name); |
| NOWARN_UNUSED(data_type); |
| NOWARN_UNUSED(data); |
| NOWARN_UNUSED(cookie); |
| eventProcessor.schedule_imm(NEW(new CC_UpdateContinuation(reconfig_mutex)), ET_CACHE); |
| return 0; |
| } |
| |
| // |
| // Begin API functions |
| // |
| bool |
| host_rule_in_CacheControlTable() |
| { |
| return (CacheControlTable->hostMatch ? true : false); |
| } |
| |
| bool |
| ip_rule_in_CacheControlTable() |
| { |
| return (CacheControlTable->ipMatch ? true : false); |
| } |
| |
| void |
| initCacheControl() |
| { |
| ink_assert(CacheControlTable == NULL); |
| reconfig_mutex = new_ProxyMutex(); |
| CacheControlTable = NEW(new CC_table("proxy.config.cache.control.filename", modulePrefix, &http_dest_tags)); |
| REC_RegisterConfigUpdateFunc("proxy.config.cache.control.filename", cacheControlFile_CB, NULL); |
| } |
| |
| // void reloadCacheControl() |
| // |
| // Called when the cache.conf file changes. Since it called |
| // infrequently, we do the load of new file as blocking I/O and |
| // lock aquire is also blocking |
| // |
| void |
| reloadCacheControl() |
| { |
| CC_table *newTable; |
| |
| Debug("cache_control", "cache.config updated, reloading"); |
| eventProcessor.schedule_in(NEW(new CC_FreerContinuation(CacheControlTable)), CACHE_CONTROL_TIMEOUT, ET_CACHE); |
| newTable = NEW(new CC_table("proxy.config.cache.control.filename", modulePrefix, &http_dest_tags)); |
| ink_atomic_swap_ptr(&CacheControlTable, newTable); |
| } |
| |
| void |
| getCacheControl(CacheControlResult *result, HttpRequestData *rdata, OverridableHttpConfigParams *h_txn_conf, char *tag) |
| { |
| rdata->tag = tag; |
| CacheControlTable->Match(rdata, result); |
| |
| if (h_txn_conf->cache_ignore_client_no_cache) { |
| result->ignore_client_no_cache = true; |
| } |
| |
| if (h_txn_conf->cache_ignore_server_no_cache) { |
| result->ignore_server_no_cache = true; |
| } |
| |
| if (!h_txn_conf->cache_ignore_client_cc_max_age) { |
| result->ignore_client_cc_max_age = false; |
| } |
| } |
| |
| // |
| // End API functions |
| // |
| |
| |
| // void CacheControlResult::Print() |
| // |
| // Debugging Method |
| // |
| void |
| CacheControlResult::Print() |
| { |
| printf("\t reval: %d, never-cache: %d, pin: %d, ignore-c: %d ignore-s: %d\n", |
| revalidate_after, never_cache, pin_in_cache_for, ignore_client_no_cache, ignore_server_no_cache); |
| } |
| |
| // void CacheControlRecord::Print() |
| // |
| // Debugging Method |
| // |
| void |
| CacheControlRecord::Print() |
| { |
| switch (this->directive) { |
| case CC_REVALIDATE_AFTER: |
| printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_REVALIDATE_AFTER], this->time_arg); |
| break; |
| case CC_PIN_IN_CACHE: |
| printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_PIN_IN_CACHE], this->time_arg); |
| break; |
| case CC_TTL_IN_CACHE: |
| printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_TTL_IN_CACHE], this->time_arg); |
| break; |
| case CC_IGNORE_CLIENT_NO_CACHE: |
| case CC_IGNORE_SERVER_NO_CACHE: |
| case CC_NEVER_CACHE: |
| case CC_STANDARD_CACHE: |
| case CC_IGNORE_NO_CACHE: |
| // case CC_CACHE_AUTH_CONTENT: |
| // printf("\t\tDirective: %s\n", CC_directive_str[this->directive]); |
| // break; |
| case CC_INVALID: |
| case CC_NUM_TYPES: |
| printf("\t\tDirective: INVALID\n"); |
| break; |
| } |
| ControlBase::Print(); |
| } |
| |
| // bool CacheControlRecord::Init(matcher_line* line_info) |
| // |
| // matcher_line* line_info - contains parsed label/value |
| // pairs of the current cache.config line |
| // |
| // Returns NULL if everything is OK |
| // Otherwise, returns an error string that the caller MUST |
| // DEALLOCATE with free() |
| // |
| char * |
| CacheControlRecord::Init(matcher_line * line_info) |
| { |
| int time_in; |
| char *errBuf; |
| const int errBufLen = 1024; |
| const char *tmp; |
| char *label; |
| char *val; |
| bool d_found = false; |
| |
| this->line_num = line_info->line_num; |
| |
| for (int i = 0; i < MATCHER_MAX_TOKENS; i++) { |
| label = line_info->line[0][i]; |
| val = line_info->line[1][i]; |
| |
| if (label == NULL) { |
| continue; |
| } |
| |
| if (strcasecmp(label, "action") == 0) { |
| if (strcasecmp(val, "never-cache") == 0) { |
| directive = CC_NEVER_CACHE; |
| d_found = true; |
| } else if (strcasecmp(val, "standard-cache") == 0) { |
| directive = CC_STANDARD_CACHE; |
| d_found = true; |
| } else if (strcasecmp(val, "ignore-no-cache") == 0) { |
| directive = CC_IGNORE_NO_CACHE; |
| d_found = true; |
| } else if (strcasecmp(val, "ignore-client-no-cache") == 0) { |
| directive = CC_IGNORE_CLIENT_NO_CACHE; |
| d_found = true; |
| } else if (strcasecmp(val, "ignore-server-no-cache") == 0) { |
| directive = CC_IGNORE_SERVER_NO_CACHE; |
| d_found = true; |
| //} else if (strcasecmp(val, "cache-auth-content") == 0) { |
| // directive = CC_CACHE_AUTH_CONTENT; |
| // d_found = true; |
| } else { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s Invalid action at line %d in cache.config", modulePrefix, line_num); |
| return errBuf; |
| } |
| } else { |
| |
| if (strcasecmp(label, "revalidate") == 0) { |
| directive = CC_REVALIDATE_AFTER; |
| d_found = true; |
| } else if (strcasecmp(label, "pin-in-cache") == 0) { |
| directive = CC_PIN_IN_CACHE; |
| d_found = true; |
| } else if (strcasecmp(label, "ttl-in-cache") == 0) { |
| directive = CC_TTL_IN_CACHE; |
| d_found = true; |
| } |
| // Process the time argument for the remaining directives |
| if (d_found == true) { |
| tmp = processDurationString(val, &time_in); |
| if (tmp == NULL) { |
| this->time_arg = time_in; |
| |
| } else { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s %s at line %d in cache.config", modulePrefix, tmp, line_num); |
| return errBuf; |
| } |
| } |
| } |
| |
| if (d_found == true) { |
| // Consume the label/value pair we used |
| line_info->line[0][i] = NULL; |
| line_info->num_el--; |
| break; |
| } |
| } |
| |
| if (d_found == false) { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s No directive in cache.config at line %d", modulePrefix, line_num); |
| return errBuf; |
| } |
| // Process any modifiers to the directive, if they exist |
| if (line_info->num_el > 0) { |
| tmp = ProcessModifiers(line_info); |
| |
| if (tmp != NULL) { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s %s at line %d in cache.config", modulePrefix, tmp, line_num); |
| return errBuf; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| // void CacheControlRecord::UpdateMatch(CacheControlResult* result, RD* rdata) |
| // |
| // Updates the parameters in result if the this element |
| // appears later in the file |
| // |
| void |
| CacheControlRecord::UpdateMatch(CacheControlResult * result, RD * rdata) |
| { |
| bool match = false; |
| HttpRequestData *h_rdata = (HttpRequestData *) rdata; |
| |
| switch (this->directive) { |
| case CC_REVALIDATE_AFTER: |
| if (this->CheckForMatch(h_rdata, result->reval_line) == true) { |
| result->revalidate_after = time_arg; |
| result->reval_line = this->line_num; |
| match = true; |
| } |
| break; |
| case CC_NEVER_CACHE: |
| if (this->CheckForMatch(h_rdata, result->never_line) == true) { |
| result->never_cache = true; |
| result->never_line = this->line_num; |
| match = true; |
| } |
| break; |
| case CC_STANDARD_CACHE: |
| // Standard cache just overrides never-cache |
| if (this->CheckForMatch(h_rdata, result->never_line) == true) { |
| result->never_cache = false; |
| result->never_line = this->line_num; |
| match = true; |
| } |
| break; |
| case CC_IGNORE_NO_CACHE: |
| // We cover both client & server cases for this directive |
| // FALLTHROUGH |
| case CC_IGNORE_CLIENT_NO_CACHE: |
| if (this->CheckForMatch(h_rdata, result->ignore_client_line) == true) { |
| result->ignore_client_no_cache = true; |
| result->ignore_client_line = this->line_num; |
| match = true; |
| } |
| if (this->directive != CC_IGNORE_NO_CACHE) { |
| break; |
| } |
| // FALLTHROUGH |
| case CC_IGNORE_SERVER_NO_CACHE: |
| if (this->CheckForMatch(h_rdata, result->ignore_server_line) == true) { |
| result->ignore_server_no_cache = true; |
| result->ignore_server_line = this->line_num; |
| match = true; |
| } |
| break; |
| case CC_PIN_IN_CACHE: |
| if (this->CheckForMatch(h_rdata, result->pin_line) == true) { |
| result->pin_in_cache_for = time_arg; |
| result->pin_line = this->line_num; |
| match = true; |
| } |
| break; |
| case CC_TTL_IN_CACHE: |
| if (this->CheckForMatch(h_rdata, result->ttl_line) == true) { |
| result->ttl_in_cache = time_arg; |
| result->ttl_line = this->line_num; |
| // ttl-in-cache overrides never-cache |
| result->never_cache = false; |
| result->never_line = this->line_num; |
| match = true; |
| } |
| break; |
| // case CC_CACHE_AUTH_CONTENT: |
| // if (this->CheckForMatch(h_rdata, result->cache_auth_line) == true) { |
| // result->cache_auth_content = true; |
| // result->cache_auth_line = this->line_num; |
| // match = true; |
| // } |
| // break; |
| case CC_INVALID: |
| case CC_NUM_TYPES: |
| default: |
| // Should not get here |
| Warning("Impossible directive in CacheControlRecord::UpdateMatch"); |
| ink_assert(0); |
| break; |
| } |
| |
| if (match == true) { |
| Debug("cache_control", "Matched with for %s at line %d", CC_directive_str[this->directive], this->line_num); |
| } |
| } |
| #else |
| #include "CacheControl.h" |
| |
| void |
| CacheControlRecord::Print() |
| { |
| } |
| |
| char * |
| CacheControlRecord::Init(matcher_line * line_info) |
| { |
| return NULL; |
| } |
| |
| void |
| CacheControlRecord::UpdateMatch(CacheControlResult * result, RD * rdata) |
| { |
| } |
| #endif // INK_NO_ACL |