• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

ParentSelection.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   Implementation of Parent Proxy routing
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
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 // Global Vars for Parent Selection
00040 static const char modulePrefix[] = "[ParentSelection]";
00041 static ConfigUpdateHandler<ParentConfig> * parentConfigUpdate = NULL;
00042 
00043 // Config var names
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 //  Config Callback Prototypes
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 // If the parent was set by the external customer api,
00075 //   our HttpRequestData structure told us what parent to
00076 //   use and we are only called to preserve clean interface
00077 //   between HttpTransact & the parent selection code.  The following
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 //   Begin API functions
00099 //
00100 void
00101 ParentConfig::startup()
00102 {
00103   parentConfigUpdate = new ConfigUpdateHandler<ParentConfig>();
00104 
00105   // Load the initial configuration
00106   reconfigure();
00107 
00108   // Setup the callbacks for reconfiuration
00109   //   parent table
00110   parentConfigUpdate->attach(file_var);
00111   //   default parent
00112   parentConfigUpdate->attach(default_var);
00113   //   Retry time
00114   parentConfigUpdate->attach(retry_var);
00115   //   Enable
00116   parentConfigUpdate->attach(enable_var);
00117 
00118   //   Fail Threshold
00119   parentConfigUpdate->attach(threshold_var);
00120 
00121   //   DNS Parent Only
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   // Allocate parent table
00138   params->ParentTable = new P_table(file_var, modulePrefix, &http_dest_tags);
00139 
00140   // Handle default parent
00141   PARENT_ReadConfigStringAlloc(default_val, default_var);
00142   params->DefaultParent = createDefaultParent(default_val);
00143   ats_free(default_val);
00144 
00145   // Handle parent timeout
00146   PARENT_ReadConfigInteger(retry_time, retry_var);
00147   params->ParentRetryTime = retry_time;
00148 
00149   // Handle parent enable
00150   PARENT_ReadConfigInteger(enable, enable_var);
00151   params->ParentEnable = enable;
00152 
00153   // Handle the fail threshold
00154   PARENT_ReadConfigInteger(fail_threshold, threshold_var);
00155   params->FailThreshold = fail_threshold;
00156 
00157   // Handle dns parent only
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 // void ParentConfig::print
00169 //
00170 //   Debugging function
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   // Check to see if we are enabled
00222   if (ParentEnable == 0) {
00223     result->r = PARENT_DIRECT;
00224     return;
00225   }
00226   // Initialize the result structure
00227   result->rec = NULL;
00228   result->epoch = tablePtr;
00229   result->line_number = 0xffffffff;
00230   result->wrap_around = false;
00231   // if this variabel is not set, we have problems: the code in
00232   // FindParent relies on the value of start_parent and when it is not
00233   // initialized, the code in FindParent can get into an infinite loop!
00234   result->start_parent = 0;
00235   result->last_parent = 0;
00236 
00237   // Check to see if the parent was set through the
00238   //   api
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     // No parents were found
00256     //
00257     // If there is a default parent, use it
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   // Loop through the set of parents to see if any are
00267   //   available
00268   Debug("cdn", "Calling FindParent from findParent");
00269 
00270   // Bug INKqa08251:
00271   // If a parent proxy is set by the API,
00272   // no need to call FindParent()
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       // Handled here:
00292       // PARENT_AGENT
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       // Handled here:
00310       // PARENT_AGENT
00311       break;
00312     }
00313   }
00314 }
00315 
00316 
00317 void
00318 ParentConfigParams::recordRetrySuccess(ParentResult * result)
00319 {
00320   pRecord *pRec;
00321 
00322   //  Make sure that we are being called back with with a
00323   //   result structure with a parent that is being retried
00324   ink_release_assert(result->retry == true);
00325   ink_assert(result->r == PARENT_SPECIFIED);
00326   if (result->r != PARENT_SPECIFIED) {
00327     return;
00328   }
00329   // If we were set through the API we currently have not failover
00330   //   so just return fail
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   //  Make sure that we are being called back with with a
00357   //   result structure with a parent
00358   ink_assert(result->r == PARENT_SPECIFIED);
00359   if (result->r != PARENT_SPECIFIED) {
00360     return;
00361   }
00362   // If we were set through the API we currently have not failover
00363   //   so just return fail
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   // If the parent has already been marked down, just increment
00372   //   the failure count.  If this is the first mark down on a
00373   //   parent we need to both set the failure time and set
00374   //   count to one.  It's possible for the count and time get out
00375   //   sync due there being no locks.  Therefore the code should
00376   //   handle this condition.  If this was the result of a retry, we
00377   //   must update move the failedAt timestamp to now so that we continue
00378   //   negative cache the parent
00379   if (pRec->failedAt == 0 || result->retry == true) {
00380     // Reread the current time.  We want this to be accurate since
00381     //   it relates to how long the parent has been down.
00382     now = time(NULL);
00383 
00384     // Mark the parent as down
00385     ink_atomic_swap(&pRec->failedAt, now);
00386 
00387     // If this is clean mark down and not a failed retry, we
00388     //   must set the count to reflect this
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   //  Make sure that we are being called back with a
00414   //   result structure with a parent
00415   ink_assert(result->r == PARENT_SPECIFIED);
00416   if (result->r != PARENT_SPECIFIED) {
00417     result->r = PARENT_FAIL;
00418     return;
00419   }
00420   // If we were set through the API we currently have not failover
00421   //   so just return fail
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   // The epoch pointer is a legacy from the time when the tables
00428   //  would be swapped and deleted in the future.  I'm using the
00429   //  pointer now to ensure that the ParentConfigParams structure
00430   //  is properly used.  The table should never change out from
00431   //  under the a http transaction
00432   ink_release_assert(tablePtr == result->epoch);
00433 
00434   // Find the next parent in the array
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     // Handled here:
00453     // PARENT_AGENT
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       // Handled here:
00471       // PARENT_AGENT
00472       break;
00473     }
00474   }
00475 }
00476 
00477 //
00478 //   End API functions
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       // We should only get into this state if
00500       //   if we are supposed to go dirrect
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         // INKqa12817 - make sure to convert to host byte order
00514         // Why was it important to do host order here?  And does this have any
00515         // impact with the transition to IPv6?  The IPv4 functionality is
00516         // preserved for now anyway as ats_ip_hash returns the 32-bit address in
00517         // that case.
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       // Move to next parent due to failure
00571       cur_index = (result->last_parent + 1) % num_parents;
00572 
00573       // Check to see if we have wrapped around
00574       if ((unsigned int) cur_index == result->start_parent) {
00575         // We've wrapped around so bypass if we can
00576         if (bypass_ok == true) {
00577           goto NO_PARENTS;
00578         } else {
00579           // Bypass disabled so keep trying, ignoring whether we think
00580           //   a parent is down or not
00581         FORCE_WRAP_AROUND:
00582           result->wrap_around = true;
00583         }
00584       }
00585     }
00586   }
00587 
00588   // Loop through the array of parent seeing if any are up or
00589   //   should be retried
00590   do {
00591     // DNS ParentOnly inhibits bypassing the parent so always return that t
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         // Reuse the parent
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   // We can't bypass so retry, taking any parent that we can
00647   if (bypass_ok == false) {
00648     goto FORCE_WRAP_AROUND;
00649   }
00650 
00651 NO_PARENTS:
00652 
00653   // Could not find a parent
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 // const char* ParentRecord::ProcessParents(char* val)
00665 //
00666 //   Reads in the value of a "round-robin" or "order"
00667 //     directive and parses out the individual parents
00668 //     allocates and builds the this->parents array
00669 //
00670 //   Returns NULL on success and a static error string
00671 //     on failure
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   // Allocate the parents array
00694   this->parents = (pRecord *)ats_malloc(sizeof(pRecord) * numTok);
00695 
00696   // Loop through the set of parents specified
00697   //
00698   for (int i = 0; i < numTok; i++) {
00699     current = pTok[i];
00700 
00701     // Find the parent port
00702     tmp = (char *) strchr(current, ':');
00703 
00704     if (tmp == NULL) {
00705       errPtr = "No parent port specified";
00706       goto MERROR;
00707     }
00708     // Read the parent port
00709     //coverity[secure_coding]
00710     if (sscanf(tmp + 1, "%d", &port) != 1) {
00711       errPtr = "Malformed parent port";
00712       goto MERROR;
00713     }
00714 
00715     // See if there is an optional parent weight
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     // Make sure that is no garbage beyond the parent
00726     //   port or weight
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     // Check to make sure that the string will fit in the
00740     //  pRecord
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     // Update the pRecords
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 // bool ParentRecord::DefaultInit(char* val)
00771 //
00772 //    Creates the record for a default parent proxy rule
00773 ///     established by a config variable
00774 //
00775 //    matcher_line* line_info - contains the value of
00776 //      proxy.config.http.parent_proxies
00777 //
00778 //    Returns true on success and false on failure
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 // char* ParentRecord::Init(matcher_line* line_info)
00820 //
00821 //    matcher_line* line_info - contains parsed label/value
00822 //      pairs of the current cache.config line
00823 //
00824 //    Returns NULL if everything is OK
00825 //      Otherwise, returns an error string that the caller MUST
00826 //        DEALLOCATE with ats_free()
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     // Report errors generated by ProcessParents();
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       // Consume the label/value pair we used
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   // Process any modifiers to the directive, if they exist
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     // record SCHEME modifier if present.
00913     // NULL if not present
00914     this->scheme = this->getSchemeModText();
00915     if (this->scheme != NULL) {
00916       // update parent entries' schemes
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 // void ParentRecord::UpdateMatch(ParentResult* result, RequestData* rdata);
00927 //
00928 //    Updates the record ptr in result if the this element
00929 //     appears later in the file
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 // ParentRecord* createDefaultParent(char* val)
00961 //
00962 //  Atttemtps to allocate and init new ParentRecord
00963 //    for a default parent
00964 //
00965 //  Returns a pointer to the new record on success
00966 //   and NULL on failure
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 //ParentConfig equivalent functions for SocksServerConfig
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   // Load the initial configuration
00998   reconfigure();
00999 
01000   /* Handle update functions later. Socks does not yet support config update */
01001 }
01002 
01003 static int
01004 setup_socks_servers(ParentRecord * rec_arr, int len)
01005 {
01006   /* This changes hostnames into ip addresses and sets go_direct to false */
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   // Allocate parent table
01040   params->ParentTable = new P_table("proxy.config.socks.socks_config_file", "[Socks Server Selection]",
01041                                     &socks_server_tags);
01042 
01043   // Handle default parent
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   // Handle parent timeout
01054   PARENT_ReadConfigInteger(retry_time, "proxy.config.socks.server_retry_time");
01055   params->ParentRetryTime = retry_time;
01056 
01057   // Handle parent enable
01058   // enable is always true for use. We will come here only if socks is enabled
01059   params->ParentEnable = 1;
01060 
01061   // Handle the fail threshold
01062   PARENT_ReadConfigInteger(fail_threshold, "proxy.config.socks.server_fail_threshold");
01063   params->FailThreshold = fail_threshold;
01064 
01065   // Handle dns parent only
01066   //PARENT_ReadConfigInteger(dns_parent_only, dns_parent_only_var);
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 // Parenting Tests
01125 EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION) (RegressionTest * /* t ATS_UNUSED */,
01126                                             int /* intensity_level ATS_UNUSED */, int *pstatus)
01127 {
01128   // first, set everything up
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   // Test 1
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     // Test 2
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     // Test 3 - 6 Parenting Table
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")  /* L1 */
01178   T("dest_ip=" TEST_IP6_ADDR " parent=zwoop:37,jMCg:24 round_robin=strict\n")  /* L1 */
01179     T("dest_host=www.pilot.net parent=pilot_net:80\n")  /* L2 */
01180     T("url_regex=snoopy parent=odie:80,garfield:80 round_robin=true\n") /* L3 */
01181     T("dest_domain=i.am parent=amy:80,katie:80,carissa:771 round_robin=false\n")        /* L4 */
01182     T("dest_domain=microsoft.net time=03:00-22:10 parent=zoo.net:341\n")        /* L5 */
01183     T("dest_domain=microsoft.net time=0:00-02:59 parent=zoo.net:347\n") /* L6 */
01184     T("dest_domain=microsoft.net time=22:11-23:59 parent=zoo.edu:111\n")        /* L7 */
01185     T("dest_domain=imac.net port=819 parent=genie:80 round_robin=strict\n")     /* L8 */
01186     T("dest_ip=172.34.61.211 port=3142 parent=orangina:80 go_direct=false\n")   /* L9 */
01187     T("url_regex=miffy prefix=furry/rabbit parent=nintje:80 go_direct=false\n") /* L10 */
01188     T("url_regex=kitty suffix=tif parent=hello:80 round_robin=strict go_direct=false\n")        /* L11 */
01189     T("url_regex=cyclops method=get parent=turkey:80\n")        /* L12 */
01190     T("url_regex=cyclops method=post parent=club:80\n") /* L13 */
01191     T("url_regex=cyclops method=put parent=sandwich:80\n")      /* L14 */
01192     T("url_regex=cyclops method=trace parent=mayo:80\n")        /* L15 */
01193     T("dest_host=pluto scheme=HTTP parent=strategy:80\n")       /* L16 */
01194     REBUILD
01195     // Test 3
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     // Test 5
01204     ST(5) REINIT br(request, "www.pilot.net");
01205   FP RE(verify(result, PARENT_SPECIFIED, "pilot_net", 80), 5)
01206     // Test 6
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     // Test 7
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     // Test 6+ BUGBUG needs to be fixed
01216 //   ST(7) REINIT
01217 //   br(request, "www.microsoft.net");
01218 //   FP RE( verify(result,PARENT_SPECIFIED,"zoo.net",341) +
01219 //       verify(result,PARENT_SPECIFIED,"zoo.net",347) +
01220 //       verify(result,PARENT_SPECIFIED,"zoo.edu",111) ,7)
01221     // Test 6++ BUGBUG needs to be fixed
01222 //   ST(7) REINIT
01223 //   br(request, "snow.imac.net:2020");
01224 //   FP RE(verify(result,PARENT_DIRECT,0,0),7)
01225     // Test 6+++ BUGBUG needs to be fixed
01226 //   ST(8) REINIT
01227 //   br(request, "snow.imac.net:819");
01228 //   URL* u = new URL();
01229 //   char* r = "http://snow.imac.net:819/";
01230 //   u->create(0);
01231 //   u->parse(r,strlen(r));
01232 //   u->port_set(819);
01233 //   request->hdr->url_set(u);
01234 //   ink_assert(request->hdr->url_get()->port_get() == 819);
01235 //   printf("url: %s\n",request->hdr->url_get()->string_get(0));
01236 //   FP RE(verify(result,PARENT_SPECIFIED,"genie",80),8)
01237     // Test 7 - N Parent Table
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     // Test 8
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   // Test 9
01247   ST(9) REINIT br(request, "i.am.rabbit.net");
01248   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 8)
01249     // Test 10
01250     ST(10) REINIT br(request, "i.am.rabbit.net");
01251   FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 9)
01252     // Test 11
01253     ST(11) REINIT br(request, "i.am.rabbit.net");
01254   FP RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 10)
01255     // restart the loop
01256     // Test 12
01257     ST(12) REINIT br(request, "i.am.rabbit.net");
01258   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 11)
01259     // Test 13
01260     ST(13) REINIT br(request, "i.am.rabbit.net");
01261   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 12)
01262     // Test 14
01263     ST(14) REINIT br(request, "i.am.rabbit.net");
01264   FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 13)
01265     // Test 15
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   // restart the loop
01271 
01272   // Test 16
01273   ST(16) REINIT br(request, "i.am.rabbit.net");
01274   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 15)
01275     // Test 17
01276     ST(17) REINIT br(request, "i.am.rabbit.net");
01277   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 16)
01278     // Test 18
01279     ST(18) REINIT br(request, "i.am.rabbit.net");
01280   FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 17)
01281     // Test 19
01282     ST(19) REINIT br(request, "i.am.rabbit.net");
01283   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 18)
01284     // restart the loop
01285     // Test 20
01286     ST(20) REINIT br(request, "i.am.rabbit.net");
01287   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 19)
01288     // Test 21
01289     ST(21) REINIT br(request, "i.am.rabbit.net");
01290   FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 20)
01291     // Test 22
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   // Test 23 - 32
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);       // now they're all down
01303 
01304   // Test 33 - 132
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   // sleep(5); // parents should come back up; they don't
01311   sleep(params->ParentRetryTime + 1);
01312 
01313   // Fix: The following tests failed because
01314   // br() should set xact_start correctly instead of 0.
01315 
01316   // Test 133 - 172
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 // verify returns 1 iff the test passes
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 // br creates an HttpRequestData object
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 // show_result prints out the ParentResult information
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     // Handled here:
01386     // PARENT_AGENT
01387     break;
01388   }
01389 }

Generated by  doxygen 1.7.1