00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 #include "ink_config.h"
00032 
00033 #include <sys/types.h>
00034 
00035 #ifdef HAVE_PCRE_PCRE_H
00036 #include <pcre/pcre.h>
00037 #else
00038 #include <pcre.h>
00039 #endif
00040 
00041 #include "CacheControl.h"
00042 #include "ControlMatcher.h"
00043 #include "Main.h"
00044 #include "Error.h"
00045 #include "P_EventSystem.h"
00046 #include "ProxyConfig.h"
00047 #include "HTTP.h"
00048 #include "HttpConfig.h"
00049 #include "StatSystem.h"
00050 #include "P_Cache.h"
00051 
00052 static const char modulePrefix[] = "[CacheControl]";
00053 
00054 # define TWEAK_CACHE_RESPONSES_TO_COOKIES "cache-responses-to-cookies"
00055 
00056 static const char *CC_directive_str[CC_NUM_TYPES] = {
00057   "INVALID",
00058   "REVALIDATE_AFTER",
00059   "NEVER_CACHE",
00060   "STANDARD_CACHE",
00061   "IGNORE_NO_CACHE",
00062   "CLUSTER_CACHE_LOCAL",
00063   "IGNORE_CLIENT_NO_CACHE",
00064   "IGNORE_SERVER_NO_CACHE",
00065   "PIN_IN_CACHE",
00066   "TTL_IN_CACHE"
00067  
00068 };
00069 
00070 typedef ControlMatcher<CacheControlRecord, CacheControlResult> CC_table;
00071 
00072 
00073 static Ptr<ProxyMutex> reconfig_mutex;
00074 CC_table *CacheControlTable = NULL;
00075 
00076 void
00077 CC_delete_table()
00078 {
00079   delete CacheControlTable;
00080 }
00081 
00082 
00083 
00084 
00085 
00086 struct CC_FreerContinuation;
00087 typedef int (CC_FreerContinuation::*CC_FreerContHandler) (int, void *);
00088 struct CC_FreerContinuation: public Continuation
00089 {
00090   CC_table *p;
00091   int freeEvent(int , Event * )
00092   {
00093     Debug("cache_control", "Deleting old table");
00094     delete p;
00095     delete this;
00096     return EVENT_DONE;
00097   }
00098   CC_FreerContinuation(CC_table * ap):Continuation(NULL), p(ap)
00099   {
00100     SET_HANDLER((CC_FreerContHandler) & CC_FreerContinuation::freeEvent);
00101   }
00102 };
00103 
00104 
00105 
00106 
00107 
00108 
00109 
00110 struct CC_UpdateContinuation: public Continuation
00111 {
00112   int file_update_handler(int , void * )
00113   {
00114     reloadCacheControl();
00115     delete this;
00116     return EVENT_DONE;
00117   }
00118   CC_UpdateContinuation(ProxyMutex * m):Continuation(m)
00119   {
00120     SET_HANDLER(&CC_UpdateContinuation::file_update_handler);
00121   }
00122 };
00123 
00124 int
00125 cacheControlFile_CB(const char * , RecDataT ,
00126                     RecData , void * )
00127 {
00128   eventProcessor.schedule_imm(new CC_UpdateContinuation(reconfig_mutex), ET_CACHE);
00129   return 0;
00130 }
00131 
00132 
00133 
00134 
00135 bool
00136 host_rule_in_CacheControlTable()
00137 {
00138   return (CacheControlTable->hostMatch ? true : false);
00139 }
00140 
00141 bool
00142 ip_rule_in_CacheControlTable()
00143 {
00144   return (CacheControlTable->ipMatch ? true : false);
00145 }
00146 
00147 void
00148 initCacheControl()
00149 {
00150   ink_assert(CacheControlTable == NULL);
00151   reconfig_mutex = new_ProxyMutex();
00152   CacheControlTable = new CC_table("proxy.config.cache.control.filename", modulePrefix, &http_dest_tags);
00153   REC_RegisterConfigUpdateFunc("proxy.config.cache.control.filename", cacheControlFile_CB, NULL);
00154 }
00155 
00156 
00157 
00158 
00159 
00160 
00161 
00162 void
00163 reloadCacheControl()
00164 {
00165   CC_table *newTable;
00166 
00167   Debug("cache_control", "cache.config updated, reloading");
00168   eventProcessor.schedule_in(new CC_FreerContinuation(CacheControlTable), CACHE_CONTROL_TIMEOUT, ET_CACHE);
00169   newTable = new CC_table("proxy.config.cache.control.filename", modulePrefix, &http_dest_tags);
00170   ink_atomic_swap(&CacheControlTable, newTable);
00171 }
00172 
00173 void
00174 getCacheControl(CacheControlResult *result, HttpRequestData *rdata, OverridableHttpConfigParams *h_txn_conf, char *tag)
00175 {
00176   rdata->tag = tag;
00177   CacheControlTable->Match(rdata, result);
00178 
00179   if (h_txn_conf->cache_cluster_cache_local) {
00180     result->cluster_cache_local = true;
00181   }
00182 
00183   if (h_txn_conf->cache_ignore_client_no_cache) {
00184     result->ignore_client_no_cache = true;
00185   }
00186 
00187   if (h_txn_conf->cache_ignore_server_no_cache) {
00188     result->ignore_server_no_cache = true;
00189   }
00190 
00191   if (!h_txn_conf->cache_ignore_client_cc_max_age) {
00192     result->ignore_client_cc_max_age = false;
00193   }
00194 }
00195 
00196 bool 
00197 getClusterCacheLocal(URL *url, char * )
00198 {
00199   HttpRequestData rdata;
00200   CacheControlResult result;
00201   HTTPHdr req_hdr;  
00202 
00203   req_hdr.create(HTTP_TYPE_REQUEST, NULL);
00204   req_hdr.url_set(url);
00205   rdata.hdr = &req_hdr;
00206   CacheControlTable->Match(&rdata, &result);
00207   req_hdr.clear();
00208   return result.cluster_cache_local;
00209 }
00210 
00211 
00212 
00213 
00214 
00215 
00216 
00217 
00218 
00219 
00220 void
00221 CacheControlResult::Print()
00222 {
00223   printf("\t reval: %d, never-cache: %d, pin: %d, cluster-cache-c: %d ignore-c: %d ignore-s: %d\n",
00224          revalidate_after, never_cache, pin_in_cache_for, cluster_cache_local, ignore_client_no_cache,
00225          ignore_server_no_cache);
00226 }
00227 
00228 
00229 
00230 
00231 
00232 void
00233 CacheControlRecord::Print()
00234 {
00235   switch (this->directive) {
00236   case CC_REVALIDATE_AFTER:
00237     printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_REVALIDATE_AFTER], this->time_arg);
00238     break;
00239   case CC_PIN_IN_CACHE:
00240     printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_PIN_IN_CACHE], this->time_arg);
00241     break;
00242   case CC_TTL_IN_CACHE:
00243     printf("\t\tDirective: %s : %d\n", CC_directive_str[CC_TTL_IN_CACHE], this->time_arg);
00244     break;
00245   case CC_CLUSTER_CACHE_LOCAL:
00246   case CC_IGNORE_CLIENT_NO_CACHE:
00247   case CC_IGNORE_SERVER_NO_CACHE:
00248   case CC_NEVER_CACHE:
00249   case CC_STANDARD_CACHE:
00250   case CC_IGNORE_NO_CACHE:
00251     printf("\t\tDirective: %s\n", CC_directive_str[this->directive]);
00252     break;
00253   case CC_INVALID:
00254   case CC_NUM_TYPES:
00255     printf("\t\tDirective: INVALID\n");
00256     break;
00257   }
00258   if (cache_responses_to_cookies >= 0)
00259     printf("\t\t  - " TWEAK_CACHE_RESPONSES_TO_COOKIES ":%d\n",
00260       cache_responses_to_cookies
00261     );
00262   ControlBase::Print();
00263 }
00264 
00265 
00266 
00267 
00268 
00269 
00270 
00271 
00272 
00273 
00274 char *
00275 CacheControlRecord::Init(matcher_line * line_info)
00276 {
00277   int time_in;
00278   char *errBuf;
00279   const int errBufLen = 1024;
00280   const char *tmp;
00281   char *label;
00282   char *val;
00283   bool d_found = false;
00284 
00285   this->line_num = line_info->line_num;
00286 
00287   
00288   for (int i = 0; i < MATCHER_MAX_TOKENS && line_info->num_el ; ++i) {
00289     bool used = false;
00290     label = line_info->line[0][i];
00291     val = line_info->line[1][i];
00292     if (!label) continue;
00293 
00294     if (strcasecmp(label, TWEAK_CACHE_RESPONSES_TO_COOKIES) == 0) {
00295       char* ptr = 0;
00296       int v = strtol(val, &ptr, 0);
00297       if (!ptr || v < 0 || v > 4) {
00298         errBuf = static_cast<char*>(ats_malloc(errBufLen * sizeof(char)));
00299         snprintf(errBuf, errBufLen, "Value for " TWEAK_CACHE_RESPONSES_TO_COOKIES
00300                  " must be an integer in the range 0..4");
00301         return errBuf;
00302       } else {
00303         cache_responses_to_cookies = v;
00304       }
00305       used = true;
00306     }
00307 
00308     
00309     if (used) {
00310       line_info->line[0][i] = 0;
00311       --(line_info->num_el);
00312     }
00313   }
00314 
00315   
00316   for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
00317     label = line_info->line[0][i];
00318     val = line_info->line[1][i];
00319 
00320     if (label == NULL) {
00321       continue;
00322     }
00323 
00324     if (strcasecmp(label, "action") == 0) {
00325       if (strcasecmp(val, "never-cache") == 0) {
00326         directive = CC_NEVER_CACHE;
00327         d_found = true;
00328       } else if (strcasecmp(val, "standard-cache") == 0) {
00329         directive = CC_STANDARD_CACHE;
00330         d_found = true;
00331       } else if (strcasecmp(val, "ignore-no-cache") == 0) {
00332         directive = CC_IGNORE_NO_CACHE;
00333         d_found = true;
00334       } else if (strcasecmp(val, "cluster-cache-local") == 0) {
00335         directive = CC_CLUSTER_CACHE_LOCAL;;
00336         d_found = true;
00337       } else if (strcasecmp(val, "ignore-client-no-cache") == 0) {
00338         directive = CC_IGNORE_CLIENT_NO_CACHE;
00339         d_found = true;
00340       } else if (strcasecmp(val, "ignore-server-no-cache") == 0) {
00341         directive = CC_IGNORE_SERVER_NO_CACHE;
00342         d_found = true;
00343       } else {
00344         errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00345         snprintf(errBuf, errBufLen, "%s Invalid action at line %d in cache.config", modulePrefix, line_num);
00346         return errBuf;
00347       }
00348     } else {
00349 
00350       if (strcasecmp(label, "revalidate") == 0) {
00351         directive = CC_REVALIDATE_AFTER;
00352         d_found = true;
00353       } else if (strcasecmp(label, "pin-in-cache") == 0) {
00354         directive = CC_PIN_IN_CACHE;
00355         d_found = true;
00356       } else if (strcasecmp(label, "ttl-in-cache") == 0) {
00357         directive = CC_TTL_IN_CACHE;
00358         d_found = true;
00359       }
00360       
00361       if (d_found == true) {
00362         tmp = processDurationString(val, &time_in);
00363         if (tmp == NULL) {
00364           this->time_arg = time_in;
00365 
00366         } else {
00367           errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00368           snprintf(errBuf, errBufLen, "%s %s at line %d in cache.config", modulePrefix, tmp, line_num);
00369           return errBuf;
00370         }
00371       }
00372     }
00373 
00374     if (d_found == true) {
00375       
00376       line_info->line[0][i] = NULL;
00377       line_info->num_el--;
00378       break;
00379     }
00380   }
00381 
00382   if (d_found == false) {
00383     errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00384     snprintf(errBuf, errBufLen, "%s No directive in cache.config at line %d", modulePrefix, line_num);
00385     return errBuf;
00386   }
00387   
00388   if (line_info->num_el > 0) {
00389     tmp = ProcessModifiers(line_info);
00390 
00391     if (tmp != NULL) {
00392       errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00393       snprintf(errBuf, errBufLen, "%s %s at line %d in cache.config", modulePrefix, tmp, line_num);
00394       return errBuf;
00395     }
00396   }
00397 
00398   return NULL;
00399 }
00400 
00401 
00402 
00403 
00404 
00405 
00406 void
00407 CacheControlRecord::UpdateMatch(CacheControlResult * result, RequestData * rdata)
00408 {
00409   bool match = false;
00410   HttpRequestData *h_rdata = (HttpRequestData *) rdata;
00411 
00412   switch (this->directive) {
00413   case CC_REVALIDATE_AFTER:
00414     if (this->CheckForMatch(h_rdata, result->reval_line) == true) {
00415       result->revalidate_after = time_arg;
00416       result->reval_line = this->line_num;
00417       match = true;
00418     }
00419     break;
00420   case CC_NEVER_CACHE:
00421     if (this->CheckForMatch(h_rdata, result->never_line) == true) {
00422       result->never_cache = true;
00423       result->never_line = this->line_num;
00424       match = true;
00425     }
00426     break;
00427   case CC_STANDARD_CACHE:
00428     
00429     if (this->CheckForMatch(h_rdata, result->never_line) == true) {
00430       result->never_cache = false;
00431       result->never_line = this->line_num;
00432       match = true;
00433     }
00434     break;
00435   case CC_IGNORE_NO_CACHE:
00436     
00437     
00438   case CC_IGNORE_CLIENT_NO_CACHE:
00439     if (this->CheckForMatch(h_rdata, result->ignore_client_line) == true) {
00440       result->ignore_client_no_cache = true;
00441       result->ignore_client_line = this->line_num;
00442       match = true;
00443     }
00444     if (this->directive != CC_IGNORE_NO_CACHE) {
00445       break;
00446     }
00447     
00448   case CC_IGNORE_SERVER_NO_CACHE:
00449     if (this->CheckForMatch(h_rdata, result->ignore_server_line) == true) {
00450       result->ignore_server_no_cache = true;
00451       result->ignore_server_line = this->line_num;
00452       match = true;
00453     }
00454     break;
00455   case CC_CLUSTER_CACHE_LOCAL:
00456     if (this->CheckForMatch(h_rdata, result->cluster_cache_local_line) == true) {
00457       result->cluster_cache_local = true;
00458       result->cluster_cache_local_line = this->line_num;
00459       match = true;
00460     }
00461     break;
00462   case CC_PIN_IN_CACHE:
00463     if (this->CheckForMatch(h_rdata, result->pin_line) == true) {
00464       result->pin_in_cache_for = time_arg;
00465       result->pin_line = this->line_num;
00466       match = true;
00467     }
00468     break;
00469   case CC_TTL_IN_CACHE:
00470     if (this->CheckForMatch(h_rdata, result->ttl_line) == true) {
00471       result->ttl_in_cache = time_arg;
00472       result->ttl_line = this->line_num;
00473       
00474       result->never_cache = false;
00475       result->never_line = this->line_num;
00476       match = true;
00477     }
00478     break;
00479   case CC_INVALID:
00480   case CC_NUM_TYPES:
00481   default:
00482     
00483     Warning("Impossible directive in CacheControlRecord::UpdateMatch");
00484     ink_assert(0);
00485     break;
00486   }
00487 
00488   if (cache_responses_to_cookies >= 0)
00489     result->cache_responses_to_cookies = cache_responses_to_cookies;
00490 
00491   if (match == true) {
00492     char crtc_debug[80];
00493     if (result->cache_responses_to_cookies >= 0)
00494       snprintf(crtc_debug, sizeof(crtc_debug), " [" TWEAK_CACHE_RESPONSES_TO_COOKIES "=%d]",
00495                result->cache_responses_to_cookies);
00496     else
00497       crtc_debug[0] = 0;
00498       
00499     Debug("cache_control", "Matched with for %s at line %d%s", CC_directive_str[this->directive],
00500           this->line_num, crtc_debug);
00501   }
00502 }