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 }