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 }