00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "libts.h"
00024 #include "P_EventSystem.h"
00025 #include "ParentSelection.h"
00026 #include "ControlMatcher.h"
00027 #include "Main.h"
00028 #include "Error.h"
00029 #include "ProxyConfig.h"
00030 #include "HTTP.h"
00031 #include "HttpTransact.h"
00032
00033 #define PARENT_RegisterConfigUpdateFunc REC_RegisterConfigUpdateFunc
00034 #define PARENT_ReadConfigInteger REC_ReadConfigInteger
00035 #define PARENT_ReadConfigStringAlloc REC_ReadConfigStringAlloc
00036
00037 typedef ControlMatcher<ParentRecord, ParentResult> P_table;
00038
00039
00040 static const char modulePrefix[] = "[ParentSelection]";
00041 static ConfigUpdateHandler<ParentConfig> * parentConfigUpdate = NULL;
00042
00043
00044 static const char *file_var = "proxy.config.http.parent_proxy.file";
00045 static const char *default_var = "proxy.config.http.parent_proxies";
00046 static const char *retry_var = "proxy.config.http.parent_proxy.retry_time";
00047 static const char *enable_var = "proxy.config.http.parent_proxy_routing_enable";
00048 static const char *threshold_var = "proxy.config.http.parent_proxy.fail_threshold";
00049 static const char *dns_parent_only_var = "proxy.config.http.no_dns_just_forward_to_parent";
00050
00051 static const char *ParentResultStr[] = {
00052 "Parent_Undefined",
00053 "Parent_Direct",
00054 "Parent_Specified",
00055 "Parent_Failed"
00056 };
00057
00058 static const char *ParentRRStr[] = {
00059 "false",
00060 "strict",
00061 "true"
00062 };
00063
00064
00065
00066
00067 enum ParentCB_t
00068 {
00069 PARENT_FILE_CB, PARENT_DEFAULT_CB,
00070 PARENT_RETRY_CB, PARENT_ENABLE_CB,
00071 PARENT_THRESHOLD_CB, PARENT_DNS_ONLY_CB
00072 };
00073
00074
00075
00076
00077
00078 ParentRecord *const extApiRecord = (ParentRecord *) 0xeeeeffff;
00079
00080 ParentConfigParams::ParentConfigParams()
00081 : ParentTable(NULL), DefaultParent(NULL), ParentRetryTime(30), ParentEnable(0), FailThreshold(10), DNS_ParentOnly(0)
00082 { }
00083
00084 ParentConfigParams::~ParentConfigParams()
00085 {
00086 if (ParentTable) {
00087 delete ParentTable;
00088 }
00089
00090 if (DefaultParent) {
00091 delete DefaultParent;
00092 }
00093 }
00094
00095 int ParentConfig::m_id = 0;
00096
00097
00098
00099
00100 void
00101 ParentConfig::startup()
00102 {
00103 parentConfigUpdate = new ConfigUpdateHandler<ParentConfig>();
00104
00105
00106 reconfigure();
00107
00108
00109
00110 parentConfigUpdate->attach(file_var);
00111
00112 parentConfigUpdate->attach(default_var);
00113
00114 parentConfigUpdate->attach(retry_var);
00115
00116 parentConfigUpdate->attach(enable_var);
00117
00118
00119 parentConfigUpdate->attach(threshold_var);
00120
00121
00122 parentConfigUpdate->attach(dns_parent_only_var);
00123 }
00124
00125 void
00126 ParentConfig::reconfigure()
00127 {
00128 char *default_val = NULL;
00129 int retry_time = 30;
00130 int enable = 0;
00131 int fail_threshold;
00132 int dns_parent_only;
00133
00134 ParentConfigParams *params;
00135 params = new ParentConfigParams;
00136
00137
00138 params->ParentTable = new P_table(file_var, modulePrefix, &http_dest_tags);
00139
00140
00141 PARENT_ReadConfigStringAlloc(default_val, default_var);
00142 params->DefaultParent = createDefaultParent(default_val);
00143 ats_free(default_val);
00144
00145
00146 PARENT_ReadConfigInteger(retry_time, retry_var);
00147 params->ParentRetryTime = retry_time;
00148
00149
00150 PARENT_ReadConfigInteger(enable, enable_var);
00151 params->ParentEnable = enable;
00152
00153
00154 PARENT_ReadConfigInteger(fail_threshold, threshold_var);
00155 params->FailThreshold = fail_threshold;
00156
00157
00158 PARENT_ReadConfigInteger(dns_parent_only, dns_parent_only_var);
00159 params->DNS_ParentOnly = dns_parent_only;
00160
00161 m_id = configProcessor.set(m_id, params);
00162
00163 if (is_debug_tag_set("parent_config")) {
00164 ParentConfig::print();
00165 }
00166 }
00167
00168
00169
00170
00171
00172 void
00173 ParentConfig::print()
00174 {
00175 ParentConfigParams *params = ParentConfig::acquire();
00176
00177 printf("Parent Selection Config\n");
00178 printf("\tEnabled %d\tRetryTime %d\tParent DNS Only %d\n",
00179 params->ParentEnable, params->ParentRetryTime, params->DNS_ParentOnly);
00180 if (params->DefaultParent == NULL) {
00181 printf("\tNo Default Parent\n");
00182 } else {
00183 printf("\tDefault Parent:\n");
00184 params->DefaultParent->Print();
00185 }
00186 printf(" ");
00187 params->ParentTable->Print();
00188
00189 ParentConfig::release(params);
00190 }
00191
00192 bool
00193 ParentConfigParams::apiParentExists(HttpRequestData * rdata)
00194 {
00195 return (rdata->api_info && rdata->api_info->parent_proxy_name != NULL && rdata->api_info->parent_proxy_port > 0);
00196 }
00197
00198 bool
00199 ParentConfigParams::parentExists(HttpRequestData * rdata)
00200 {
00201 ParentResult junk;
00202
00203 findParent(rdata, &junk);
00204
00205 if (junk.r == PARENT_SPECIFIED) {
00206 return true;
00207 } else {
00208 return false;
00209 }
00210 }
00211
00212 void
00213 ParentConfigParams::findParent(HttpRequestData * rdata, ParentResult * result)
00214 {
00215 P_table *tablePtr = ParentTable;
00216 ParentRecord *defaultPtr = DefaultParent;
00217 ParentRecord *rec;
00218
00219 ink_assert(result->r == PARENT_UNDEFINED);
00220
00221
00222 if (ParentEnable == 0) {
00223 result->r = PARENT_DIRECT;
00224 return;
00225 }
00226
00227 result->rec = NULL;
00228 result->epoch = tablePtr;
00229 result->line_number = 0xffffffff;
00230 result->wrap_around = false;
00231
00232
00233
00234 result->start_parent = 0;
00235 result->last_parent = 0;
00236
00237
00238
00239 if (apiParentExists(rdata)) {
00240 result->r = PARENT_SPECIFIED;
00241 result->hostname = rdata->api_info->parent_proxy_name;
00242 result->port = rdata->api_info->parent_proxy_port;
00243 result->rec = extApiRecord;
00244 result->epoch = NULL;
00245 result->start_parent = 0;
00246 result->last_parent = 0;
00247
00248 Debug("parent_select", "Result for %s was API set parent %s:%d", rdata->get_host(), result->hostname, result->port);
00249 }
00250
00251 tablePtr->Match(rdata, result);
00252 rec = result->rec;
00253
00254 if (rec == NULL) {
00255
00256
00257
00258 if (defaultPtr != NULL) {
00259 rec = result->rec = defaultPtr;
00260 } else {
00261 result->r = PARENT_DIRECT;
00262 Debug("cdn", "Returning PARENT_DIRECT (no parents were found)");
00263 return;
00264 }
00265 }
00266
00267
00268 Debug("cdn", "Calling FindParent from findParent");
00269
00270
00271
00272
00273 if (rec != extApiRecord)
00274 rec->FindParent(true, result, rdata, this);
00275
00276 if (is_debug_tag_set("parent_select") || is_debug_tag_set("cdn")) {
00277 switch (result->r) {
00278 case PARENT_UNDEFINED:
00279 Debug("cdn", "PARENT_UNDEFINED");
00280 break;
00281 case PARENT_FAIL:
00282 Debug("cdn", "PARENT_FAIL");
00283 break;
00284 case PARENT_DIRECT:
00285 Debug("cdn", "PARENT_DIRECT");
00286 break;
00287 case PARENT_SPECIFIED:
00288 Debug("cdn", "PARENT_SPECIFIED");
00289 break;
00290 default:
00291
00292
00293 break;
00294 }
00295
00296 const char *host = rdata->get_host();
00297
00298 switch (result->r) {
00299 case PARENT_UNDEFINED:
00300 case PARENT_FAIL:
00301 case PARENT_DIRECT:
00302 Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->r]);
00303 break;
00304 case PARENT_SPECIFIED:
00305 Debug("parent_select", "sizeof ParentResult = %zu", sizeof(ParentResult));
00306 Debug("parent_select", "Result for %s was parent %s:%d", host, result->hostname, result->port);
00307 break;
00308 default:
00309
00310
00311 break;
00312 }
00313 }
00314 }
00315
00316
00317 void
00318 ParentConfigParams::recordRetrySuccess(ParentResult * result)
00319 {
00320 pRecord *pRec;
00321
00322
00323
00324 ink_release_assert(result->retry == true);
00325 ink_assert(result->r == PARENT_SPECIFIED);
00326 if (result->r != PARENT_SPECIFIED) {
00327 return;
00328 }
00329
00330
00331 if (result->rec == extApiRecord) {
00332 ink_assert(0);
00333 return;
00334 }
00335
00336 ink_assert((int) (result->last_parent) < result->rec->num_parents);
00337 pRec = result->rec->parents + result->last_parent;
00338
00339 pRec->available = true;
00340
00341 ink_atomic_swap(&pRec->failedAt, (time_t)0);
00342 int old_count = ink_atomic_swap(&pRec->failCount, 0);
00343
00344 if (old_count > 0) {
00345 Note("http parent proxy %s:%d restored", pRec->hostname, pRec->port);
00346 }
00347 }
00348
00349 void
00350 ParentConfigParams::markParentDown(ParentResult * result)
00351 {
00352 time_t now;
00353 pRecord *pRec;
00354 int new_fail_count = 0;
00355
00356
00357
00358 ink_assert(result->r == PARENT_SPECIFIED);
00359 if (result->r != PARENT_SPECIFIED) {
00360 return;
00361 }
00362
00363
00364 if (result->rec == extApiRecord) {
00365 return;
00366 }
00367
00368 ink_assert((int) (result->last_parent) < result->rec->num_parents);
00369 pRec = result->rec->parents + result->last_parent;
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379 if (pRec->failedAt == 0 || result->retry == true) {
00380
00381
00382 now = time(NULL);
00383
00384
00385 ink_atomic_swap(&pRec->failedAt, now);
00386
00387
00388
00389 if (result->retry == false) {
00390 new_fail_count = pRec->failCount = 1;
00391 }
00392
00393 Debug("parent_select", "Parent %s marked as down %s:%d", (result->retry) ? "retry" : "initially", pRec->hostname, pRec->port);
00394
00395 } else {
00396 int old_count = ink_atomic_increment(&pRec->failCount, 1);
00397
00398 Debug("parent_select", "Parent fail count increased to %d for %s:%d", old_count + 1, pRec->hostname, pRec->port);
00399 new_fail_count = old_count + 1;
00400 }
00401
00402 if (new_fail_count > 0 && new_fail_count == FailThreshold) {
00403 Note("http parent proxy %s:%d marked down", pRec->hostname, pRec->port);
00404 pRec->available = false;
00405 }
00406 }
00407
00408 void
00409 ParentConfigParams::nextParent(HttpRequestData * rdata, ParentResult * result)
00410 {
00411 P_table *tablePtr = ParentTable;
00412
00413
00414
00415 ink_assert(result->r == PARENT_SPECIFIED);
00416 if (result->r != PARENT_SPECIFIED) {
00417 result->r = PARENT_FAIL;
00418 return;
00419 }
00420
00421
00422 if (result->rec == extApiRecord) {
00423 Debug("parent_select", "Retry result for %s was %s", rdata->get_host(), ParentResultStr[result->r]);
00424 result->r = PARENT_FAIL;
00425 return;
00426 }
00427
00428
00429
00430
00431
00432 ink_release_assert(tablePtr == result->epoch);
00433
00434
00435 Debug("cdn", "Calling FindParent from nextParent");
00436 result->rec->FindParent(false, result, rdata, this);
00437
00438 switch (result->r) {
00439 case PARENT_UNDEFINED:
00440 Debug("cdn", "PARENT_UNDEFINED");
00441 break;
00442 case PARENT_FAIL:
00443 Debug("cdn", "PARENT_FAIL");
00444 break;
00445 case PARENT_DIRECT:
00446 Debug("cdn", "PARENT_DIRECT");
00447 break;
00448 case PARENT_SPECIFIED:
00449 Debug("cdn", "PARENT_SPECIFIED");
00450 break;
00451 default:
00452
00453
00454 break;
00455 }
00456
00457 if (is_debug_tag_set("parent_select")) {
00458 const char *host = rdata->get_host();
00459
00460 switch (result->r) {
00461 case PARENT_UNDEFINED:
00462 case PARENT_FAIL:
00463 case PARENT_DIRECT:
00464 Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->r]);
00465 break;
00466 case PARENT_SPECIFIED:
00467 Debug("parent_select", "Retry result for %s was parent %s:%d", host, result->hostname, result->port);
00468 break;
00469 default:
00470
00471
00472 break;
00473 }
00474 }
00475 }
00476
00477
00478
00479
00480
00481 void
00482 ParentRecord::FindParent(bool first_call, ParentResult * result, RequestData * rdata, ParentConfigParams * config)
00483 {
00484 Debug("cdn", "Entering FindParent (the inner loop)");
00485 int cur_index = 0;
00486 bool parentUp = false;
00487 bool parentRetry = false;
00488 bool bypass_ok = (go_direct == true && config->DNS_ParentOnly == 0);
00489 char *url, *path = NULL;
00490 ATSHash64Sip24 hash;
00491 pRecord *prtmp = NULL;
00492
00493 HttpRequestData *request_info = (HttpRequestData *) rdata;
00494
00495 ink_assert(num_parents > 0 || go_direct == true);
00496
00497 if (first_call == true) {
00498 if (parents == NULL) {
00499
00500
00501 ink_assert(go_direct == true);
00502 goto NO_PARENTS;
00503 } else if (round_robin == true) {
00504 cur_index = ink_atomic_increment((int32_t *) & rr_next, 1);
00505 cur_index = result->start_parent = cur_index % num_parents;
00506 } else {
00507 switch (round_robin) {
00508 case P_STRICT_ROUND_ROBIN:
00509 cur_index = ink_atomic_increment((int32_t *) & rr_next, 1);
00510 cur_index = cur_index % num_parents;
00511 break;
00512 case P_HASH_ROUND_ROBIN:
00513
00514
00515
00516
00517
00518 if (rdata->get_client_ip() != NULL) {
00519 cur_index = ntohl(ats_ip_hash(rdata->get_client_ip())) % num_parents;
00520 } else {
00521 cur_index = 0;
00522 }
00523 break;
00524 case P_CONSISTENT_HASH:
00525 url = rdata->get_string();
00526 path = strstr(url + 7, "/");
00527 if (path) {
00528 prtmp = (pRecord *) chash->lookup(path, &(result->chashIter), NULL, (ATSHash64 *) &hash);
00529 if (prtmp) {
00530 cur_index = prtmp->idx;
00531 result->foundParents[cur_index] = true;
00532 result->start_parent++;
00533 } else {
00534 Error("Consistent Hash loopup returned NULL");
00535 cur_index = ink_atomic_increment((int32_t *) & rr_next, 1);
00536 cur_index = cur_index % num_parents;
00537 }
00538 } else {
00539 Error("Could not find path in URL: %s",url);
00540 cur_index = ink_atomic_increment((int32_t *) & rr_next, 1);
00541 cur_index = cur_index % num_parents;
00542 }
00543 break;
00544 case P_NO_ROUND_ROBIN:
00545 cur_index = result->start_parent = 0;
00546 break;
00547 default:
00548 ink_release_assert(0);
00549 }
00550 }
00551 } else {
00552 if (round_robin == P_CONSISTENT_HASH) {
00553 if (result->start_parent == (unsigned int) num_parents) {
00554 result->wrap_around = true;
00555 result->start_parent = 0;
00556 memset(result->foundParents, 0, sizeof(result->foundParents));
00557 url = rdata->get_string();
00558 path = strstr(url + 7, "/");
00559 }
00560
00561 do {
00562 prtmp = (pRecord *) chash->lookup(path, &(result->chashIter), NULL, (ATSHash64 *) &hash);
00563 path = NULL;
00564 } while (result->foundParents[prtmp->idx]);
00565
00566 cur_index = prtmp->idx;
00567 result->foundParents[cur_index] = true;
00568 result->start_parent++;
00569 } else {
00570
00571 cur_index = (result->last_parent + 1) % num_parents;
00572
00573
00574 if ((unsigned int) cur_index == result->start_parent) {
00575
00576 if (bypass_ok == true) {
00577 goto NO_PARENTS;
00578 } else {
00579
00580
00581 FORCE_WRAP_AROUND:
00582 result->wrap_around = true;
00583 }
00584 }
00585 }
00586 }
00587
00588
00589
00590 do {
00591
00592 if ((parents[cur_index].failedAt == 0) || (parents[cur_index].failCount < config->FailThreshold)) {
00593 Debug("parent_select", "config->FailThreshold = %d", config->FailThreshold);
00594 Debug("parent_select", "Selecting a down parent due to little failCount"
00595 "(faileAt: %u failCount: %d)", (unsigned)parents[cur_index].failedAt, parents[cur_index].failCount);
00596 parentUp = true;
00597 } else {
00598 if ((result->wrap_around) || ((parents[cur_index].failedAt + config->ParentRetryTime) < request_info->xact_start)) {
00599 Debug("parent_select", "Parent[%d].failedAt = %u, retry = %u,xact_start = %" PRId64 " but wrap = %d", cur_index,
00600 (unsigned)parents[cur_index].failedAt, config->ParentRetryTime, (int64_t)request_info->xact_start, result->wrap_around);
00601
00602 parentUp = true;
00603 parentRetry = true;
00604 Debug("parent_select", "Parent marked for retry %s:%d", parents[cur_index].hostname, parents[cur_index].port);
00605
00606 } else {
00607 parentUp = false;
00608 }
00609 }
00610
00611 if (parentUp == true) {
00612 result->r = PARENT_SPECIFIED;
00613 result->hostname = parents[cur_index].hostname;
00614 result->port = parents[cur_index].port;
00615 result->last_parent = cur_index;
00616 result->retry = parentRetry;
00617 ink_assert(result->hostname != NULL);
00618 ink_assert(result->port != 0);
00619 Debug("parent_select", "Chosen parent = %s.%d", result->hostname, result->port);
00620 return;
00621 }
00622
00623 if (round_robin == P_CONSISTENT_HASH) {
00624 if (result->start_parent == (unsigned int) num_parents) {
00625 result->wrap_around = false;
00626 result->start_parent = 0;
00627 memset(result->foundParents, 0, sizeof(result->foundParents));
00628 url = rdata->get_string();
00629 path = strstr(url + 7, "/");
00630 }
00631
00632 do {
00633 prtmp = (pRecord *) chash->lookup(path, &(result->chashIter), NULL, (ATSHash64 *) &hash);
00634 path = NULL;
00635 } while (result->foundParents[prtmp->idx]);
00636
00637 cur_index = prtmp->idx;
00638 result->foundParents[cur_index] = true;
00639 result->start_parent++;
00640 } else {
00641 cur_index = (cur_index + 1) % num_parents;
00642 }
00643
00644 } while ((round_robin == P_CONSISTENT_HASH ? result->wrap_around : ((unsigned int) cur_index != result->start_parent)));
00645
00646
00647 if (bypass_ok == false) {
00648 goto FORCE_WRAP_AROUND;
00649 }
00650
00651 NO_PARENTS:
00652
00653
00654 if (this->go_direct == true) {
00655 result->r = PARENT_DIRECT;
00656 } else {
00657 result->r = PARENT_FAIL;
00658 }
00659
00660 result->hostname = NULL;
00661 result->port = 0;
00662 }
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673 const char *
00674 ParentRecord::ProcessParents(char *val)
00675 {
00676 Tokenizer pTok(",; \t\r");
00677 int numTok;
00678 const char *current;
00679 int port;
00680 char *tmp, *tmp2;
00681 const char *errPtr;
00682 float weight = 1.0;
00683
00684 if (parents != NULL) {
00685 return "Can not specify more than one set of parents";
00686 }
00687
00688 numTok = pTok.Initialize(val, SHARE_TOKS);
00689
00690 if (numTok == 0) {
00691 return "No parents specified";
00692 }
00693
00694 this->parents = (pRecord *)ats_malloc(sizeof(pRecord) * numTok);
00695
00696
00697
00698 for (int i = 0; i < numTok; i++) {
00699 current = pTok[i];
00700
00701
00702 tmp = (char *) strchr(current, ':');
00703
00704 if (tmp == NULL) {
00705 errPtr = "No parent port specified";
00706 goto MERROR;
00707 }
00708
00709
00710 if (sscanf(tmp + 1, "%d", &port) != 1) {
00711 errPtr = "Malformed parent port";
00712 goto MERROR;
00713 }
00714
00715
00716 tmp2 = (char *) strchr(current, '|');
00717
00718 if (tmp2) {
00719 if (sscanf(tmp2 + 1, "%f", &weight) != 1) {
00720 errPtr = "Malformed parent weight";
00721 goto MERROR;
00722 }
00723 }
00724
00725
00726
00727 char *scan;
00728 if (tmp2) {
00729 scan = tmp2 + 1;
00730 } else {
00731 scan = tmp + 1;
00732 }
00733 for (; *scan != '\0' && (ParseRules::is_digit(*scan) || *scan == '.'); scan++);
00734 for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++);
00735 if (*scan != '\0') {
00736 errPtr = "Garbage trailing entry or invalid separator";
00737 goto MERROR;
00738 }
00739
00740
00741 if (tmp - current > MAXDNAME) {
00742 errPtr = "Parent hostname is too long";
00743 goto MERROR;
00744 } else if (tmp - current == 0) {
00745 errPtr = "Parent string is emtpy";
00746 goto MERROR;
00747 }
00748
00749 memcpy(this->parents[i].hostname, current, tmp - current);
00750 this->parents[i].hostname[tmp - current] = '\0';
00751 this->parents[i].port = port;
00752 this->parents[i].failedAt = 0;
00753 this->parents[i].scheme = scheme;
00754 this->parents[i].idx = i;
00755 this->parents[i].name = this->parents[i].hostname;
00756 this->parents[i].available = true;
00757 this->parents[i].weight = weight;
00758 }
00759
00760 num_parents = numTok;
00761 return NULL;
00762
00763 MERROR:
00764 ats_free(parents);
00765 parents = NULL;
00766
00767 return errPtr;
00768 }
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780 bool
00781 ParentRecord::DefaultInit(char *val)
00782 {
00783 const char *errPtr;
00784 char *errBuf;
00785 bool alarmAlready = false;
00786
00787 this->go_direct = true;
00788 this->round_robin = P_NO_ROUND_ROBIN;
00789 this->scheme = NULL;
00790 errPtr = ProcessParents(val);
00791
00792 if (errPtr != NULL) {
00793 errBuf = (char *)ats_malloc(1024);
00794 snprintf(errBuf, 1024, "%s %s for default parent proxy", modulePrefix, errPtr);
00795 SignalError(errBuf, alarmAlready);
00796 ats_free(errBuf);
00797 return false;
00798 } else {
00799 return true;
00800 }
00801 }
00802
00803 void
00804 ParentRecord::buildConsistentHash(void) {
00805 ATSHash64Sip24 hash;
00806 int i;
00807
00808 if (chash) {
00809 return;
00810 }
00811
00812 chash = new ATSConsistentHash();
00813
00814 for (i = 0; i < num_parents; i++) {
00815 chash->insert(&(this->parents[i]), this->parents[i].weight, (ATSHash64 *) &hash);
00816 }
00817 }
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828 char *
00829 ParentRecord::Init(matcher_line * line_info)
00830 {
00831 const char *errPtr = NULL;
00832 char *errBuf;
00833 const int errBufLen = 1024;
00834 const char *tmp;
00835 char *label;
00836 char *val;
00837 bool used = false;
00838
00839 this->line_num = line_info->line_num;
00840 this->scheme = NULL;
00841
00842 for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
00843 used = false;
00844 label = line_info->line[0][i];
00845 val = line_info->line[1][i];
00846
00847 if (label == NULL) {
00848 continue;
00849 }
00850
00851 if (strcasecmp(label, "round_robin") == 0) {
00852 if (strcasecmp(val, "true") == 0) {
00853 round_robin = P_HASH_ROUND_ROBIN;
00854 } else if (strcasecmp(val, "strict") == 0) {
00855 round_robin = P_STRICT_ROUND_ROBIN;
00856 } else if (strcasecmp(val, "false") == 0) {
00857 round_robin = P_NO_ROUND_ROBIN;
00858 } else if (strcasecmp(val, "consistent_hash") == 0) {
00859 round_robin = P_CONSISTENT_HASH;
00860 if (this->parents != NULL) {
00861 buildConsistentHash();
00862 }
00863 } else {
00864 round_robin = P_NO_ROUND_ROBIN;
00865 errPtr = "invalid argument to round_robin directive";
00866 }
00867 used = true;
00868 } else if (strcasecmp(label, "parent") == 0) {
00869 errPtr = ProcessParents(val);
00870 used = true;
00871 if (round_robin == P_CONSISTENT_HASH) {
00872 buildConsistentHash();
00873 }
00874 } else if (strcasecmp(label, "go_direct") == 0) {
00875 if (strcasecmp(val, "false") == 0) {
00876 go_direct = false;
00877 } else if (strcasecmp(val, "true") != 0) {
00878 errPtr = "invalid argument to go_direct directive";
00879 } else {
00880 go_direct = true;
00881 }
00882 used = true;
00883 }
00884
00885 if (errPtr != NULL) {
00886 errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00887 snprintf(errBuf, errBufLen, "%s %s at line %d", modulePrefix, errPtr, line_num);
00888 return errBuf;
00889 }
00890
00891 if (used == true) {
00892
00893 line_info->line[0][i] = NULL;
00894 line_info->num_el--;
00895 }
00896 }
00897
00898 if (this->parents == NULL && go_direct == false) {
00899 errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00900 snprintf(errBuf, errBufLen, "%s No parent specified in parent.config at line %d", modulePrefix, line_num);
00901 return errBuf;
00902 }
00903
00904 if (line_info->num_el > 0) {
00905 tmp = ProcessModifiers(line_info);
00906
00907 if (tmp != NULL) {
00908 errBuf = (char *)ats_malloc(errBufLen * sizeof(char));
00909 snprintf(errBuf, errBufLen, "%s %s at line %d in parent.config", modulePrefix, tmp, line_num);
00910 return errBuf;
00911 }
00912
00913
00914 this->scheme = this->getSchemeModText();
00915 if (this->scheme != NULL) {
00916
00917 for (int j = 0; j < num_parents; j++) {
00918 this->parents[j].scheme = this->scheme;
00919 }
00920 }
00921 }
00922
00923 return NULL;
00924 }
00925
00926
00927
00928
00929
00930
00931 void
00932 ParentRecord::UpdateMatch(ParentResult * result, RequestData * rdata)
00933 {
00934 if (this->CheckForMatch((HttpRequestData *) rdata, result->line_number) == true) {
00935 result->rec = this;
00936 result->line_number = this->line_num;
00937
00938 Debug("parent_select", "Matched with %p parent node from line %d", this, this->line_num);
00939 }
00940 }
00941
00942 ParentRecord::~ParentRecord()
00943 {
00944 if (chash) {
00945 delete chash;
00946 }
00947 ats_free(parents);
00948 }
00949
00950 void
00951 ParentRecord::Print()
00952 {
00953 printf("\t\t");
00954 for (int i = 0; i < num_parents; i++) {
00955 printf(" %s:%d ", parents[i].hostname, parents[i].port);
00956 }
00957 printf(" rr=%s direct=%s\n", ParentRRStr[round_robin], (go_direct == true) ? "true" : "false");
00958 }
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968 ParentRecord *
00969 createDefaultParent(char *val)
00970 {
00971 ParentRecord *newRec;
00972
00973 if (val == NULL || *val == '\0') {
00974 return NULL;
00975 }
00976
00977 newRec = new ParentRecord;
00978 if (newRec->DefaultInit(val) == true) {
00979 return newRec;
00980 } else {
00981 delete newRec;
00982 return NULL;
00983 }
00984 }
00985
00986
00987
00988
00989
00990 int SocksServerConfig::m_id = 0;
00991 static Ptr<ProxyMutex> socks_server_reconfig_mutex;
00992 void
00993 SocksServerConfig::startup()
00994 {
00995 socks_server_reconfig_mutex = new_ProxyMutex();
00996
00997
00998 reconfigure();
00999
01000
01001 }
01002
01003 static int
01004 setup_socks_servers(ParentRecord * rec_arr, int len)
01005 {
01006
01007 for (int j = 0; j < len; j++) {
01008 rec_arr[j].go_direct = false;
01009
01010 pRecord *pr = rec_arr[j].parents;
01011 int n_parents = rec_arr[j].num_parents;
01012
01013 for (int i = 0; i < n_parents; i++) {
01014 IpEndpoint ip4, ip6;
01015 if (0 == ats_ip_getbestaddrinfo(pr[i].hostname, &ip4, &ip6)) {
01016 IpEndpoint* ip = ats_is_ip6(&ip6) ? &ip6 : &ip4;
01017 ats_ip_ntop(ip, pr[i].hostname, MAXDNAME+1);
01018 } else {
01019 Warning("Could not resolve socks server name \"%s\". " "Please correct it", pr[i].hostname);
01020 snprintf(pr[i].hostname, MAXDNAME+1, "255.255.255.255");
01021 }
01022 }
01023 }
01024
01025 return 0;
01026 }
01027
01028
01029 void
01030 SocksServerConfig::reconfigure()
01031 {
01032 char *default_val = NULL;
01033 int retry_time = 30;
01034 int fail_threshold;
01035
01036 ParentConfigParams *params;
01037 params = new ParentConfigParams;
01038
01039
01040 params->ParentTable = new P_table("proxy.config.socks.socks_config_file", "[Socks Server Selection]",
01041 &socks_server_tags);
01042
01043
01044 PARENT_ReadConfigStringAlloc(default_val, "proxy.config.socks.default_servers");
01045 params->DefaultParent = createDefaultParent(default_val);
01046 ats_free(default_val);
01047
01048 if (params->DefaultParent)
01049 setup_socks_servers(params->DefaultParent, 1);
01050 if (params->ParentTable->ipMatch)
01051 setup_socks_servers(params->ParentTable->ipMatch->data_array, params->ParentTable->ipMatch->array_len);
01052
01053
01054 PARENT_ReadConfigInteger(retry_time, "proxy.config.socks.server_retry_time");
01055 params->ParentRetryTime = retry_time;
01056
01057
01058
01059 params->ParentEnable = 1;
01060
01061
01062 PARENT_ReadConfigInteger(fail_threshold, "proxy.config.socks.server_fail_threshold");
01063 params->FailThreshold = fail_threshold;
01064
01065
01066
01067 params->DNS_ParentOnly = 0;
01068
01069 m_id = configProcessor.set(m_id, params);
01070
01071 if (is_debug_tag_set("parent_config")) {
01072 SocksServerConfig::print();
01073 }
01074 }
01075
01076 void
01077 SocksServerConfig::print()
01078 {
01079 ParentConfigParams *params = SocksServerConfig::acquire();
01080
01081 printf("Parent Selection Config for Socks Server\n");
01082 printf("\tEnabled %d\tRetryTime %d\tParent DNS Only %d\n",
01083 params->ParentEnable, params->ParentRetryTime, params->DNS_ParentOnly);
01084 if (params->DefaultParent == NULL) {
01085 printf("\tNo Default Parent\n");
01086 } else {
01087 printf("\tDefault Parent:\n");
01088 params->DefaultParent->Print();
01089 }
01090 printf(" ");
01091 params->ParentTable->Print();
01092
01093 SocksServerConfig::release(params);
01094 }
01095
01096 #define TEST_FAIL(str) { \
01097 printf("%d: %s\n", test_id,str);\
01098 err= REGRESSION_TEST_FAILED;\
01099 }
01100
01101 void
01102 request_to_data(HttpRequestData * req, sockaddr const* srcip, sockaddr const* dstip, const char *str)
01103 {
01104 HTTPParser parser;
01105
01106 ink_zero(req->src_ip);
01107 ats_ip_copy(&req->src_ip.sa, srcip);
01108 ink_zero(req->dest_ip);
01109 ats_ip_copy(&req->dest_ip.sa, dstip);
01110
01111 req->hdr = new HTTPHdr;
01112
01113 http_parser_init(&parser);
01114
01115 req->hdr->parse_req(&parser, &str, str + strlen(str), true);
01116
01117 http_parser_clear(&parser);
01118 }
01119
01120
01121 static int passes;
01122 static int fails;
01123
01124
01125 EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION) (RegressionTest * ,
01126 int , int *pstatus)
01127 {
01128
01129 *pstatus = REGRESSION_TEST_INPROGRESS;
01130 ParentConfig config;
01131 ParentConfigParams *params = new ParentConfigParams();
01132 params->FailThreshold = 1;
01133 params->ParentRetryTime = 5;
01134 passes = fails = 0;
01135 config.startup();
01136 params->ParentEnable = true;
01137 char tbl[2048];
01138 #define T(x) ink_strlcat(tbl,x, sizeof(tbl));
01139 #define REBUILD params->ParentTable = new P_table("", "ParentSelection Unit Test Table", &http_dest_tags, ALLOW_HOST_TABLE | ALLOW_REGEX_TABLE | ALLOW_URL_TABLE | ALLOW_IP_TABLE | DONT_BUILD_TABLE); params->ParentTable->BuildTableFromString(tbl);
01140 HttpRequestData *request = NULL;
01141 ParentResult *result = NULL;
01142 #define REINIT delete request; delete result; request = new HttpRequestData(); result = new ParentResult(); if (!result || !request) { (void)printf("Allocation failed\n"); return; }
01143 #define ST(x) printf ("*** TEST %d *** STARTING ***\n", x);
01144 #define RE(x,y) if (x) { printf("*** TEST %d *** PASSED ***\n", y); passes ++; } else { printf("*** TEST %d *** FAILED *** FAILED *** FAILED ***\n", y); fails++; }
01145 #define FP params->findParent(request, result);
01146
01147
01148 tbl[0] = '\0';
01149 ST(1)
01150 T("dest_domain=. parent=red:37412,orange:37412,yellow:37412 round_robin=strict\n")
01151 REBUILD int c, red = 0, orange = 0, yellow = 0;
01152 for (c = 0; c < 21; c++) {
01153 REINIT br(request, "fruit_basket.net");
01154 FP red += verify(result, PARENT_SPECIFIED, "red", 37412);
01155 orange += verify(result, PARENT_SPECIFIED, "orange", 37412);
01156 yellow += verify(result, PARENT_SPECIFIED, "yellow", 37412);
01157 }
01158 RE(((red == 7) && (orange == 7) && (yellow == 7)), 1)
01159
01160 ST(2)
01161 tbl[0] = '\0';
01162 T("dest_domain=. parent=green:4325,blue:4325,indigo:4325,violet:4325 round_robin=false\n")
01163 REBUILD int g = 0, b = 0, i = 0, v = 0;
01164 for (c = 0; c < 17; c++) {
01165 REINIT br(request, "fruit_basket.net");
01166 FP g += verify(result, PARENT_SPECIFIED, "green", 4325);
01167 b += verify(result, PARENT_SPECIFIED, "blue", 4325);
01168 i += verify(result, PARENT_SPECIFIED, "indigo", 4325);
01169 v += verify(result, PARENT_SPECIFIED, "violet", 4325);
01170 }
01171 RE((((g == 17) && !b && !i && !v) || (!g && (b == 17) && !i && !v) || (!g && !b && (i == 17) && !v) ||
01172 (!g && !b && !i && (v == 17))), 2)
01173
01174 tbl[0] = '\0';
01175 # define TEST_IP4_ADDR "209.131.62.14"
01176 # define TEST_IP6_ADDR "BEEF:DEAD:ABBA:CAFE:1337:1E1F:5EED:C0FF"
01177 T("dest_ip=" TEST_IP4_ADDR " parent=cat:37,dog:24 round_robin=strict\n")
01178 T("dest_ip=" TEST_IP6_ADDR " parent=zwoop:37,jMCg:24 round_robin=strict\n")
01179 T("dest_host=www.pilot.net parent=pilot_net:80\n")
01180 T("url_regex=snoopy parent=odie:80,garfield:80 round_robin=true\n")
01181 T("dest_domain=i.am parent=amy:80,katie:80,carissa:771 round_robin=false\n")
01182 T("dest_domain=microsoft.net time=03:00-22:10 parent=zoo.net:341\n")
01183 T("dest_domain=microsoft.net time=0:00-02:59 parent=zoo.net:347\n")
01184 T("dest_domain=microsoft.net time=22:11-23:59 parent=zoo.edu:111\n")
01185 T("dest_domain=imac.net port=819 parent=genie:80 round_robin=strict\n")
01186 T("dest_ip=172.34.61.211 port=3142 parent=orangina:80 go_direct=false\n")
01187 T("url_regex=miffy prefix=furry/rabbit parent=nintje:80 go_direct=false\n")
01188 T("url_regex=kitty suffix=tif parent=hello:80 round_robin=strict go_direct=false\n")
01189 T("url_regex=cyclops method=get parent=turkey:80\n")
01190 T("url_regex=cyclops method=post parent=club:80\n")
01191 T("url_regex=cyclops method=put parent=sandwich:80\n")
01192 T("url_regex=cyclops method=trace parent=mayo:80\n")
01193 T("dest_host=pluto scheme=HTTP parent=strategy:80\n")
01194 REBUILD
01195
01196 IpEndpoint ip;
01197 ats_ip_pton(TEST_IP4_ADDR, &ip.sa);
01198 ST(3) REINIT br(request, "numeric_host", &ip.sa);
01199 FP RE(verify(result, PARENT_SPECIFIED, "cat", 37) + verify(result, PARENT_SPECIFIED, "dog", 24), 3)
01200 ats_ip_pton(TEST_IP6_ADDR, &ip.sa);
01201 ST(4) REINIT br(request, "numeric_host", &ip.sa);
01202 FP RE(verify(result, PARENT_SPECIFIED, "zwoop", 37) + verify(result, PARENT_SPECIFIED, "jMCg", 24), 4)
01203
01204 ST(5) REINIT br(request, "www.pilot.net");
01205 FP RE(verify(result, PARENT_SPECIFIED, "pilot_net", 80), 5)
01206
01207 ST(6) REINIT br(request, "www.snoopy.net");
01208 const char *snoopy_dog = "http://www.snoopy.com/";
01209 request->hdr->url_set(snoopy_dog, strlen(snoopy_dog));
01210 FP RE(verify(result, PARENT_SPECIFIED, "odie", 80) + verify(result, PARENT_SPECIFIED, "garfield", 80), 5)
01211
01212 ST(7) REINIT br(request, "a.rabbit.i.am");
01213 FP RE(verify(result, PARENT_SPECIFIED, "amy", 80) +
01214 verify(result, PARENT_SPECIFIED, "katie", 80) + verify(result, PARENT_SPECIFIED, "carissa", 771), 6)
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238 tbl[0] = '\0';
01239 T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=strict go_direct=true\n")
01240 REBUILD
01241
01242 ST(8) REINIT br(request, "i.am.rabbit.net");
01243 FP RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 7)
01244 params->markParentDown(result);
01245
01246
01247 ST(9) REINIT br(request, "i.am.rabbit.net");
01248 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 8)
01249
01250 ST(10) REINIT br(request, "i.am.rabbit.net");
01251 FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 9)
01252
01253 ST(11) REINIT br(request, "i.am.rabbit.net");
01254 FP RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 10)
01255
01256
01257 ST(12) REINIT br(request, "i.am.rabbit.net");
01258 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 11)
01259
01260 ST(13) REINIT br(request, "i.am.rabbit.net");
01261 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 12)
01262
01263 ST(14) REINIT br(request, "i.am.rabbit.net");
01264 FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 13)
01265
01266 ST(15) REINIT br(request, "i.am.rabbit.net");
01267 FP RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 14)
01268 params->markParentDown(result);
01269
01270
01271
01272
01273 ST(16) REINIT br(request, "i.am.rabbit.net");
01274 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 15)
01275
01276 ST(17) REINIT br(request, "i.am.rabbit.net");
01277 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 16)
01278
01279 ST(18) REINIT br(request, "i.am.rabbit.net");
01280 FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 17)
01281
01282 ST(19) REINIT br(request, "i.am.rabbit.net");
01283 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 18)
01284
01285
01286 ST(20) REINIT br(request, "i.am.rabbit.net");
01287 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 19)
01288
01289 ST(21) REINIT br(request, "i.am.rabbit.net");
01290 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 20)
01291
01292 ST(22) REINIT br(request, "i.am.rabbit.net");
01293 FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 21)
01294 params->markParentDown(result);
01295
01296
01297 for (i = 23; i < 33; i++) {
01298 ST(i) REINIT br(request, "i.am.rabbit.net");
01299 FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i)
01300 }
01301
01302 params->markParentDown(result);
01303
01304
01305 for (i = 33; i < 133; i++) {
01306 ST(i) REINIT br(request, "i.am.rabbit.net");
01307 FP RE(verify(result, PARENT_DIRECT, 0, 0), i)
01308 }
01309
01310
01311 sleep(params->ParentRetryTime + 1);
01312
01313
01314
01315
01316
01317 for (i = 133; i < 173; i++) {
01318 ST(i) REINIT br(request, "i.am.rabbit.net");
01319 FP sleep(1);
01320 switch (i % 4) {
01321 case 0:
01322 RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), i) break;
01323 case 1:
01324 RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i) break;
01325 case 2:
01326 RE(verify(result, PARENT_SPECIFIED, "furry", 80), i) break;
01327 case 3:
01328 RE(verify(result, PARENT_SPECIFIED, "frisky", 80), i) break;
01329 default:
01330 ink_assert(0);
01331 }
01332 }
01333 delete request;
01334 delete result;
01335 delete params;
01336
01337 printf("Tests Passed: %d\nTests Failed: %d\n", passes, fails);
01338 *pstatus = (!fails ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
01339 }
01340
01341
01342 int
01343 verify(ParentResult * r, ParentResultType e, const char *h, int p)
01344 {
01345 if (is_debug_tag_set("parent_select"))
01346 show_result(r);
01347 return (r->r != e) ? 0 : ((e != PARENT_SPECIFIED) ? 1 : (strcmp(r->hostname, h) ? 0 : ((r->port == p) ? 1 : 0)));
01348 }
01349
01350
01351 void
01352 br(HttpRequestData * h, const char *os_hostname, sockaddr const* dest_ip)
01353 {
01354 h->hdr = new HTTPHdr();
01355 h->hdr->create(HTTP_TYPE_REQUEST);
01356 h->hostname_str = (char *)ats_strdup(os_hostname);
01357 h->xact_start = time(NULL);
01358 ink_zero(h->src_ip);
01359 ink_zero(h->dest_ip);
01360 ats_ip_copy(&h->dest_ip.sa, dest_ip);
01361 h->incoming_port = 80;
01362 h->api_info = new _HttpApiInfo();
01363 }
01364
01365
01366 void
01367 show_result(ParentResult * p)
01368 {
01369 switch (p->r) {
01370 case PARENT_UNDEFINED:
01371 printf("result is PARENT_UNDEFINED\n");
01372 break;
01373 case PARENT_DIRECT:
01374 printf("result is PARENT_DIRECT\n");
01375 break;
01376 case PARENT_SPECIFIED:
01377 printf("result is PARENT_SPECIFIED\n");
01378 printf("hostname is %s\n", p->hostname);
01379 printf("port is %d\n", p->port);
01380 break;
01381 case PARENT_FAIL:
01382 printf("result is PARENT_FAIL\n");
01383 break;
01384 default:
01385
01386
01387 break;
01388 }
01389 }