1 /** @file
2 
3   Implementation of Parent Proxy routing
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 #include "P_EventSystem.h"
24 #include "ParentSelection.h"
25 #include "ParentConsistentHash.h"
26 #include "ParentRoundRobin.h"
27 #include "ControlMatcher.h"
28 #include "ProxyConfig.h"
29 #include "HostStatus.h"
30 #include "HTTP.h"
31 #include "HttpTransact.h"
32 #include "I_Machine.h"
33 #include "tscore/Filenames.h"
34 
35 #define MAX_SIMPLE_RETRIES 5
36 #define MAX_UNAVAILABLE_SERVER_RETRIES 5
37 
38 using P_table = ControlMatcher<ParentRecord, ParentResult>;
39 
40 // Global Vars for Parent Selection
41 static const char modulePrefix[]                             = "[ParentSelection]";
42 static ConfigUpdateHandler<ParentConfig> *parentConfigUpdate = nullptr;
43 static int self_detect                                       = 2;
44 
45 // Config var names
46 static const char *file_var      = "proxy.config.http.parent_proxy.file";
47 static const char *default_var   = "proxy.config.http.parent_proxies";
48 static const char *retry_var     = "proxy.config.http.parent_proxy.retry_time";
49 static const char *threshold_var = "proxy.config.http.parent_proxy.fail_threshold";
50 
51 //
52 //  Config Callback Prototypes
53 //
54 enum ParentCB_t {
55   PARENT_FILE_CB,
56   PARENT_DEFAULT_CB,
57   PARENT_RETRY_CB,
58   PARENT_ENABLE_CB,
59   PARENT_THRESHOLD_CB,
60   PARENT_DNS_ONLY_CB,
61 };
62 
ParentSelectionPolicy()63 ParentSelectionPolicy::ParentSelectionPolicy()
64 {
65   int32_t retry_time     = 0;
66   int32_t fail_threshold = 0;
67 
68   // Handle parent timeout
69   REC_ReadConfigInteger(retry_time, retry_var);
70   ParentRetryTime = retry_time;
71 
72   // Handle the fail threshold
73   REC_ReadConfigInteger(fail_threshold, threshold_var);
74   FailThreshold = fail_threshold;
75 }
76 
ParentConfigParams(P_table * _parent_table)77 ParentConfigParams::ParentConfigParams(P_table *_parent_table) : parent_table(_parent_table), DefaultParent(nullptr), policy()
78 {
79   char *default_val = nullptr;
80 
81   // Handle default parent
82   REC_ReadConfigStringAlloc(default_val, default_var);
83   DefaultParent = createDefaultParent(default_val);
84   ats_free(default_val);
85 }
86 
~ParentConfigParams()87 ParentConfigParams::~ParentConfigParams()
88 {
89   if (parent_table) {
90     Debug("parent_select", "~ParentConfigParams(): releasing parent_table %p", parent_table);
91   }
92   delete parent_table;
93   delete DefaultParent;
94 }
95 
96 bool
apiParentExists(HttpRequestData * rdata)97 ParentConfigParams::apiParentExists(HttpRequestData *rdata)
98 {
99   return (rdata->api_info && rdata->api_info->parent_proxy_name != nullptr && rdata->api_info->parent_proxy_port > 0);
100 }
101 
102 void
findParent(HttpRequestData * rdata,ParentResult * result,unsigned int fail_threshold,unsigned int retry_time)103 ParentConfigParams::findParent(HttpRequestData *rdata, ParentResult *result, unsigned int fail_threshold, unsigned int retry_time)
104 {
105   P_table *tablePtr        = parent_table;
106   ParentRecord *defaultPtr = DefaultParent;
107   ParentRecord *rec;
108 
109   // Check to see if the parent was set through the
110   //   api
111   if (apiParentExists(rdata)) {
112     result->result       = PARENT_SPECIFIED;
113     result->hostname     = rdata->api_info->parent_proxy_name;
114     result->port         = rdata->api_info->parent_proxy_port;
115     result->rec          = extApiRecord;
116     result->start_parent = 0;
117     result->last_parent  = 0;
118 
119     Debug("parent_select", "Result for %s was API set parent %s:%d", rdata->get_host(), result->hostname, result->port);
120     return;
121   }
122 
123   // Initialize the result structure
124   result->reset();
125 
126   tablePtr->Match(rdata, result);
127   rec = result->rec;
128 
129   if (rec == nullptr) {
130     // No parents were found
131     //
132     // If there is a default parent, use it
133     if (defaultPtr != nullptr) {
134       rec = result->rec = defaultPtr;
135     } else {
136       result->result = PARENT_DIRECT;
137       Debug("parent_select", "Returning PARENT_DIRECT (no parents were found)");
138       return;
139     }
140   }
141 
142   if (rec != extApiRecord) {
143     selectParent(true, result, rdata, fail_threshold, retry_time);
144   }
145 
146   const char *host = rdata->get_host();
147 
148   switch (result->result) {
149   case PARENT_UNDEFINED:
150     Debug("parent_select", "PARENT_UNDEFINED");
151     Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->result]);
152     break;
153   case PARENT_FAIL:
154     Debug("parent_select", "PARENT_FAIL");
155     break;
156   case PARENT_DIRECT:
157     Debug("parent_select", "PARENT_DIRECT");
158     Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->result]);
159     break;
160   case PARENT_SPECIFIED:
161     Debug("parent_select", "PARENT_SPECIFIED");
162     Debug("parent_select", "Result for %s was parent %s:%d", host, result->hostname, result->port);
163     break;
164   default:
165     // Handled here:
166     // PARENT_AGENT
167     break;
168   }
169 }
170 
171 void
nextParent(HttpRequestData * rdata,ParentResult * result,unsigned int fail_threshold,unsigned int retry_time)172 ParentConfigParams::nextParent(HttpRequestData *rdata, ParentResult *result, unsigned int fail_threshold, unsigned int retry_time)
173 {
174   P_table *tablePtr = parent_table;
175 
176   Debug("parent_select", "ParentConfigParams::nextParent(): parent_table: %p, result->rec: %p", parent_table, result->rec);
177 
178   //  Make sure that we are being called back with a
179   //   result structure with a parent
180   ink_assert(result->result == PARENT_SPECIFIED);
181   if (result->result != PARENT_SPECIFIED) {
182     result->result = PARENT_FAIL;
183     return;
184   }
185   // If we were set through the API we currently have not failover
186   //   so just return fail
187   if (result->is_api_result()) {
188     Debug("parent_select", "Retry result for %s was %s", rdata->get_host(), ParentResultStr[result->result]);
189     result->result = PARENT_FAIL;
190     return;
191   }
192   Debug("parent_select", "ParentConfigParams::nextParent(): result->r: %d, tablePtr: %p", result->result, tablePtr);
193 
194   // Find the next parent in the array
195   Debug("parent_select", "Calling selectParent() from nextParent");
196   selectParent(false, result, rdata, fail_threshold, retry_time);
197 
198   const char *host = rdata->get_host();
199 
200   switch (result->result) {
201   case PARENT_UNDEFINED:
202     Debug("parent_select", "PARENT_UNDEFINED");
203     Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]);
204     break;
205   case PARENT_FAIL:
206     Debug("parent_select", "PARENT_FAIL");
207     Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]);
208     break;
209   case PARENT_DIRECT:
210     Debug("parent_select", "PARENT_DIRECT");
211     Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]);
212     break;
213   case PARENT_SPECIFIED:
214     Debug("parent_select", "Retry result for %s was parent %s:%d", host, result->hostname, result->port);
215     break;
216   default:
217     // Handled here:
218     // PARENT_AGENT
219     break;
220   }
221 }
222 
223 bool
parentExists(HttpRequestData * rdata)224 ParentConfigParams::parentExists(HttpRequestData *rdata)
225 {
226   P_table *tablePtr = parent_table;
227   ParentRecord *rec = nullptr;
228   ParentResult result;
229 
230   // Initialize the result structure;
231   result.reset();
232 
233   tablePtr->Match(rdata, &result);
234   rec = result.rec;
235 
236   if (rec == nullptr) {
237     Debug("parent_select", "No matching parent record was found for the request.");
238     return false;
239   }
240 
241   if (rec->num_parents > 0) {
242     for (int ii = 0; ii < rec->num_parents; ii++) {
243       if (rec->parents[ii].available) {
244         Debug("parent_select", "found available parent: %s", rec->parents[ii].hostname);
245         return true;
246       }
247     }
248   }
249   if (rec->secondary_parents && rec->num_secondary_parents > 0) {
250     for (int ii = 0; ii < rec->num_secondary_parents; ii++) {
251       if (rec->secondary_parents[ii].available) {
252         Debug("parent_select", "found available parent: %s", rec->secondary_parents[ii].hostname);
253         return true;
254       }
255     }
256   }
257   return false;
258 }
259 
260 int ParentConfig::m_id = 0;
261 
262 void
startup()263 ParentConfig::startup()
264 {
265   parentConfigUpdate = new ConfigUpdateHandler<ParentConfig>();
266 
267   // Load the initial configuration
268   reconfigure();
269 
270   // Setup the callbacks for reconfiuration
271   //   parent table
272   parentConfigUpdate->attach(file_var);
273   //   default parent
274   parentConfigUpdate->attach(default_var);
275   //   Retry time
276   parentConfigUpdate->attach(retry_var);
277   //   Fail Threshold
278   parentConfigUpdate->attach(threshold_var);
279 }
280 
281 void
reconfigure()282 ParentConfig::reconfigure()
283 {
284   Note("%s loading ...", ts::filename::PARENT);
285 
286   ParentConfigParams *params = nullptr;
287 
288   // Allocate parent table
289   P_table *pTable = new P_table(file_var, modulePrefix, &http_dest_tags);
290 
291   params = new ParentConfigParams(pTable);
292   ink_assert(params != nullptr);
293 
294   m_id = configProcessor.set(m_id, params);
295 
296   if (is_debug_tag_set("parent_config")) {
297     ParentConfig::print();
298   }
299 
300   Note("%s finished loading", ts::filename::PARENT);
301 }
302 
303 void
print()304 ParentConfig::print()
305 {
306   ParentConfigParams *params = ParentConfig::acquire();
307 
308   printf("Parent Selection Config\n");
309   printf("\tRetryTime %d\n", params->policy.ParentRetryTime);
310   if (params->DefaultParent == nullptr) {
311     printf("\tNo Default Parent\n");
312   } else {
313     printf("\tDefault Parent:\n");
314     params->DefaultParent->Print();
315   }
316   printf("  ");
317   params->parent_table->Print();
318 
319   ParentConfig::release(params);
320 }
321 
UnavailableServerResponseCodes(char * val)322 UnavailableServerResponseCodes::UnavailableServerResponseCodes(char *val)
323 {
324   Tokenizer pTok(", \t\r");
325   int numTok = 0, c;
326 
327   if (val == nullptr) {
328     Warning("UnavailableServerResponseCodes - unavailable_server_retry_responses is null loading default 503 code.");
329     codes.push_back(HTTP_STATUS_SERVICE_UNAVAILABLE);
330     return;
331   }
332   numTok = pTok.Initialize(val, SHARE_TOKS);
333   if (numTok == 0) {
334     c = atoi(val);
335     if (c > 499 && c < 600) {
336       codes.push_back(HTTP_STATUS_SERVICE_UNAVAILABLE);
337     }
338   }
339   for (int i = 0; i < numTok; i++) {
340     c = atoi(pTok[i]);
341     if (c > 499 && c < 600) {
342       Debug("parent_select", "loading response code: %d", c);
343       codes.push_back(c);
344     } else {
345       Warning("UnavailableServerResponseCodes received non-5xx code '%s', ignoring!", pTok[i]);
346     }
347   }
348   std::sort(codes.begin(), codes.end());
349 }
350 
SimpleRetryResponseCodes(char * val)351 SimpleRetryResponseCodes::SimpleRetryResponseCodes(char *val)
352 {
353   Tokenizer pTok(", \t\r");
354   int numTok = 0, c;
355 
356   if (val == nullptr) {
357     Warning("SimpleRetryResponseCodes - simple_server_retry_responses is null loading default 404 code.");
358     codes.push_back(HTTP_STATUS_NOT_FOUND);
359     return;
360   }
361   numTok = pTok.Initialize(val, SHARE_TOKS);
362   if (numTok == 0) {
363     c = atoi(val);
364     if (c > 399 && c < 500) {
365       codes.push_back(HTTP_STATUS_NOT_FOUND);
366     }
367   }
368   for (int i = 0; i < numTok; i++) {
369     c = atoi(pTok[i]);
370     if (c > 399 && c < 500) {
371       Debug("parent_select", "loading simple response code: %d", c);
372       codes.push_back(c);
373     } else {
374       Warning("SimpleRetryResponseCodes received non-4xx code '%s', ignoring!", pTok[i]);
375     }
376   }
377   std::sort(codes.begin(), codes.end());
378 }
379 
380 void
PreProcessParents(const char * val,const int line_num,char * buf,size_t len)381 ParentRecord::PreProcessParents(const char *val, const int line_num, char *buf, size_t len)
382 {
383   char *_val                      = ats_strndup(val, strlen(val));
384   char fqdn[TS_MAX_HOST_NAME_LEN] = {0}, *nm, *token, *savePtr;
385   std::string str;
386   Machine *machine                   = Machine::instance();
387   constexpr char PARENT_DELIMITERS[] = ";, ";
388   HostStatus &hs                     = HostStatus::instance();
389 
390   token = strtok_r(_val, PARENT_DELIMITERS, &savePtr);
391   while (token != nullptr) {
392     if ((nm = strchr(token, ':')) != nullptr) {
393       size_t length = (nm - token);
394       ink_assert(length < sizeof(fqdn));
395       memset(fqdn, 0, sizeof(fqdn));
396       strncpy(fqdn, token, length);
397       if (self_detect && machine->is_self(fqdn)) {
398         if (self_detect == 1) {
399           Debug("parent_select", "token: %s, matches this machine.  Removing self from parent list at line %d", fqdn, line_num);
400           token = strtok_r(nullptr, PARENT_DELIMITERS, &savePtr);
401           continue;
402         } else {
403           Debug("parent_select", "token: %s, matches this machine.  Marking down self from parent list at line %d", fqdn, line_num);
404           hs.setHostStatus(fqdn, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT);
405         }
406       }
407     } else {
408       if (self_detect && machine->is_self(token)) {
409         if (self_detect == 1) {
410           Debug("parent_select", "token: %s, matches this machine.  Removing self from parent list at line %d", token, line_num);
411           token = strtok_r(nullptr, PARENT_DELIMITERS, &savePtr);
412           continue;
413         } else {
414           Debug("parent_select", "token: %s, matches this machine.  Marking down self from parent list at line %d", token,
415                 line_num);
416           hs.setHostStatus(token, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT);
417         }
418       }
419     }
420 
421     str += token;
422     str += ";";
423     token = strtok_r(nullptr, PARENT_DELIMITERS, &savePtr);
424   }
425   strncpy(buf, str.c_str(), len);
426   ats_free(_val);
427 }
428 
429 // const char* ParentRecord::ProcessParents(char* val, bool isPrimary)
430 //
431 //   Reads in the value of a "round-robin" or "order"
432 //     directive and parses out the individual parents
433 //     allocates and builds the this->parents array or
434 //     this->secondary_parents based upon the isPrimary
435 //     boolean.
436 //
437 //   Returns NULL on success and a static error string
438 //     on failure
439 //
440 const char *
ProcessParents(char * val,bool isPrimary)441 ParentRecord::ProcessParents(char *val, bool isPrimary)
442 {
443   Tokenizer pTok(",; \t\r");
444   int numTok          = 0;
445   const char *current = nullptr;
446   int port            = 0;
447   char *tmp = nullptr, *tmp2 = nullptr, *tmp3 = nullptr;
448   const char *errPtr = nullptr;
449   float weight       = 1.0;
450 
451   if (parents != nullptr && isPrimary == true) {
452     return "Can not specify more than one set of parents";
453   }
454   if (secondary_parents != nullptr && isPrimary == false) {
455     return "Can not specify more than one set of secondary parents";
456   }
457 
458   numTok = pTok.Initialize(val, SHARE_TOKS);
459 
460   if (numTok == 0) {
461     return "No parents specified";
462   }
463   // Allocate the parents array
464   if (isPrimary) {
465     this->parents = static_cast<pRecord *>(ats_malloc(sizeof(pRecord) * numTok));
466   } else {
467     this->secondary_parents = static_cast<pRecord *>(ats_malloc(sizeof(pRecord) * numTok));
468   }
469 
470   // Loop through the set of parents specified
471   //
472   for (int i = 0; i < numTok; i++) {
473     current = pTok[i];
474 
475     // Find the parent port
476     tmp = const_cast<char *>(strchr(current, ':'));
477 
478     if (tmp == nullptr) {
479       errPtr = "No parent port specified";
480       goto MERROR;
481     }
482     // Read the parent port
483     // coverity[secure_coding]
484     if (sscanf(tmp + 1, "%d", &port) != 1) {
485       errPtr = "Malformed parent port";
486       goto MERROR;
487     }
488 
489     // See if there is an optional parent weight
490     tmp2 = const_cast<char *>(strchr(current, '|'));
491 
492     if (tmp2) {
493       if (sscanf(tmp2 + 1, "%f", &weight) != 1) {
494         errPtr = "Malformed parent weight";
495         goto MERROR;
496       }
497     }
498 
499     tmp3 = const_cast<char *>(strchr(current, '&'));
500 
501     // Make sure that is no garbage beyond the parent
502     //  port or weight
503     if (!tmp3) {
504       char *scan;
505       if (tmp2) {
506         scan = tmp2 + 1;
507       } else {
508         scan = tmp + 1;
509       }
510       for (; *scan != '\0' && (ParseRules::is_digit(*scan) || *scan == '.'); scan++) {
511         ;
512       }
513       for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++) {
514         ;
515       }
516       if (*scan != '\0') {
517         errPtr = "Garbage trailing entry or invalid separator";
518         goto MERROR;
519       }
520     }
521     // Check to make sure that the string will fit in the
522     //  pRecord
523     if (tmp - current > MAXDNAME) {
524       errPtr = "Parent hostname is too long";
525       goto MERROR;
526     } else if (tmp - current == 0) {
527       errPtr = "Parent string is empty";
528       goto MERROR;
529     }
530     // Update the pRecords
531     if (isPrimary) {
532       memcpy(this->parents[i].hostname, current, tmp - current);
533       this->parents[i].hostname[tmp - current] = '\0';
534       this->parents[i].port                    = port;
535       this->parents[i].failedAt                = 0;
536       this->parents[i].failCount               = 0;
537       this->parents[i].scheme                  = scheme;
538       this->parents[i].idx                     = i;
539       this->parents[i].name                    = this->parents[i].hostname;
540       this->parents[i].available               = true;
541       this->parents[i].weight                  = weight;
542       this->parents[i].retriers                = 0;
543       if (tmp3) {
544         memcpy(this->parents[i].hash_string, tmp3 + 1, strlen(tmp3));
545         this->parents[i].name = this->parents[i].hash_string;
546       }
547     } else {
548       memcpy(this->secondary_parents[i].hostname, current, tmp - current);
549       this->secondary_parents[i].hostname[tmp - current] = '\0';
550       this->secondary_parents[i].port                    = port;
551       this->secondary_parents[i].failedAt                = 0;
552       this->secondary_parents[i].failCount               = 0;
553       this->secondary_parents[i].scheme                  = scheme;
554       this->secondary_parents[i].idx                     = i;
555       this->secondary_parents[i].name                    = this->secondary_parents[i].hostname;
556       this->secondary_parents[i].available               = true;
557       this->secondary_parents[i].weight                  = weight;
558       this->secondary_parents[i].retriers                = 0;
559       if (tmp3) {
560         memcpy(this->secondary_parents[i].hash_string, tmp3 + 1, strlen(tmp3));
561         this->secondary_parents[i].name = this->secondary_parents[i].hash_string;
562       }
563     }
564     tmp3 = nullptr;
565   }
566 
567   if (isPrimary) {
568     num_parents = numTok;
569   } else {
570     num_secondary_parents = numTok;
571   }
572 
573   return nullptr;
574 
575 MERROR:
576   if (isPrimary) {
577     ats_free(parents);
578     parents = nullptr;
579   } else {
580     ats_free(secondary_parents);
581     secondary_parents = nullptr;
582   }
583 
584   return errPtr;
585 }
586 
587 // bool ParentRecord::DefaultInit(char* val)
588 //
589 //    Creates the record for a default parent proxy rule
590 ///     established by a config variable
591 //
592 //    matcher_line* line_info - contains the value of
593 //      proxy.config.http.parent_proxies
594 //
595 //    Returns true on success and false on failure
596 //
597 bool
DefaultInit(char * val)598 ParentRecord::DefaultInit(char *val)
599 {
600   const char *errPtr;
601   char *errBuf;
602   bool alarmAlready = false;
603 
604   this->go_direct       = true;
605   this->ignore_query    = false;
606   this->scheme          = nullptr;
607   this->parent_is_proxy = true;
608   errPtr                = ProcessParents(val, true);
609 
610   if (errPtr != nullptr) {
611     errBuf = static_cast<char *>(ats_malloc(1024));
612     snprintf(errBuf, 1024, "%s %s for default parent proxy", modulePrefix, errPtr);
613     SignalError(errBuf, alarmAlready);
614     ats_free(errBuf);
615     return false;
616   } else {
617     ParentRR_t round_robin = P_NO_ROUND_ROBIN;
618     Debug("parent_select", "allocating ParentRoundRobin() lookup strategy.");
619     selection_strategy = new ParentRoundRobin(this, round_robin);
620     return true;
621   }
622 }
623 
624 // Result ParentRecord::Init(matcher_line* line_info)
625 //
626 //    matcher_line* line_info - contains parsed label/value
627 //      pairs of the current cache.config line
628 //
629 //    Returns NULL if everything is OK
630 //      Otherwise, returns an error string that the caller MUST
631 //        DEALLOCATE with ats_free()
632 //
633 Result
Init(matcher_line * line_info)634 ParentRecord::Init(matcher_line *line_info)
635 {
636   const char *errPtr = nullptr;
637   const char *tmp;
638   char *label;
639   char *val;
640   char parent_buf[16384] = {0};
641   bool used              = false;
642   ParentRR_t round_robin = P_NO_ROUND_ROBIN;
643   char buf[128];
644   RecInt rec_self_detect = 2;
645 
646   this->line_num = line_info->line_num;
647   this->scheme   = nullptr;
648 
649   if (RecGetRecordInt("proxy.config.http.parent_proxy.self_detect", &rec_self_detect) == REC_ERR_OKAY) {
650     self_detect = static_cast<int>(rec_self_detect);
651   }
652 
653   for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
654     used  = false;
655     label = line_info->line[0][i];
656     val   = line_info->line[1][i];
657 
658     if (label == nullptr) {
659       continue;
660     }
661 
662     if (strcasecmp(label, "round_robin") == 0) {
663       if (strcasecmp(val, "true") == 0) {
664         round_robin = P_HASH_ROUND_ROBIN;
665       } else if (strcasecmp(val, "strict") == 0) {
666         round_robin = P_STRICT_ROUND_ROBIN;
667       } else if (strcasecmp(val, "false") == 0) {
668         round_robin = P_NO_ROUND_ROBIN;
669       } else if (strcasecmp(val, "consistent_hash") == 0) {
670         round_robin = P_CONSISTENT_HASH;
671       } else if (strcasecmp(val, "latched") == 0) {
672         round_robin = P_LATCHED_ROUND_ROBIN;
673       } else {
674         round_robin = P_NO_ROUND_ROBIN;
675         errPtr      = "invalid argument to round_robin directive";
676       }
677       used = true;
678     } else if (strcasecmp(label, "parent") == 0 || strcasecmp(label, "primary_parent") == 0) {
679       PreProcessParents(val, line_num, parent_buf, sizeof(parent_buf) - 1);
680       errPtr = ProcessParents(parent_buf, true);
681       used   = true;
682     } else if (strcasecmp(label, "secondary_parent") == 0) {
683       PreProcessParents(val, line_num, parent_buf, sizeof(parent_buf) - 1);
684       errPtr = ProcessParents(parent_buf, false);
685       used   = true;
686     } else if (strcasecmp(label, "go_direct") == 0) {
687       if (strcasecmp(val, "false") == 0) {
688         go_direct = false;
689       } else if (strcasecmp(val, "true") != 0) {
690         errPtr = "invalid argument to go_direct directive";
691       } else {
692         go_direct = true;
693       }
694       used = true;
695     } else if (strcasecmp(label, "qstring") == 0) {
696       // qstring=ignore | consider
697       if (strcasecmp(val, "ignore") == 0) {
698         this->ignore_query = true;
699       } else {
700         this->ignore_query = false;
701       }
702       used = true;
703     } else if (strcasecmp(label, "parent_is_proxy") == 0) {
704       if (strcasecmp(val, "false") == 0) {
705         parent_is_proxy = false;
706       } else if (strcasecmp(val, "true") != 0) {
707         errPtr = "invalid argument to parent_is_proxy directive";
708       } else {
709         parent_is_proxy = true;
710       }
711       used = true;
712     } else if (strcasecmp(label, "parent_retry") == 0) {
713       if (strcasecmp(val, "simple_retry") == 0) {
714         parent_retry = PARENT_RETRY_SIMPLE;
715       } else if (strcasecmp(val, "unavailable_server_retry") == 0) {
716         parent_retry = PARENT_RETRY_UNAVAILABLE_SERVER;
717       } else if (strcasecmp(val, "both") == 0) {
718         parent_retry = PARENT_RETRY_BOTH;
719       } else {
720         errPtr = "invalid argument to parent_retry directive.";
721       }
722       used = true;
723     } else if (strcasecmp(label, "unavailable_server_retry_responses") == 0 && unavailable_server_retry_responses == nullptr) {
724       unavailable_server_retry_responses = new UnavailableServerResponseCodes(val);
725       used                               = true;
726     } else if (strcasecmp(label, "simple_server_retry_responses") == 0 && simple_server_retry_responses == nullptr) {
727       simple_server_retry_responses = new SimpleRetryResponseCodes(val);
728       used                          = true;
729     } else if (strcasecmp(label, "max_simple_retries") == 0) {
730       int v = atoi(val);
731       if (v >= 1 && v < MAX_SIMPLE_RETRIES) {
732         max_simple_retries = v;
733         used               = true;
734       } else {
735         snprintf(buf, sizeof(buf), "invalid argument to max_simple_retries.  Argument must be between 1 and %d.",
736                  MAX_SIMPLE_RETRIES);
737         errPtr = buf;
738       }
739     } else if (strcasecmp(label, "max_unavailable_server_retries") == 0) {
740       int v = atoi(val);
741       if (v >= 1 && v < MAX_UNAVAILABLE_SERVER_RETRIES) {
742         max_unavailable_server_retries = v;
743         used                           = true;
744       } else {
745         snprintf(buf, sizeof(buf), "invalid argument to max_unavailable_server_retries.  Argument must be between 1 and %d.",
746                  MAX_UNAVAILABLE_SERVER_RETRIES);
747         errPtr = buf;
748       }
749     } else if (strcasecmp(label, "secondary_mode") == 0) {
750       int v          = atoi(val);
751       secondary_mode = v;
752       used           = true;
753     } else if (strcasecmp(label, "ignore_self_detect") == 0) {
754       if (strcasecmp(val, "true") == 0) {
755         ignore_self_detect = true;
756       } else {
757         ignore_self_detect = false;
758       }
759       used = true;
760     }
761     // Report errors generated by ProcessParents();
762     if (errPtr != nullptr) {
763       return Result::failure("%s %s at line %d", modulePrefix, errPtr, line_num);
764     }
765 
766     if (used == true) {
767       // Consume the label/value pair we used
768       line_info->line[0][i] = nullptr;
769       line_info->num_el--;
770     }
771   }
772 
773   // delete unavailable_server_retry_responses if unavailable_server_retry is not enabled.
774   if (unavailable_server_retry_responses != nullptr && !(parent_retry & PARENT_RETRY_UNAVAILABLE_SERVER)) {
775     Warning("%s ignoring unavailable_server_retry_responses directive on line %d, as unavailable_server_retry is not enabled.",
776             modulePrefix, line_num);
777     delete unavailable_server_retry_responses;
778     unavailable_server_retry_responses = nullptr;
779   } else if (unavailable_server_retry_responses == nullptr && (parent_retry & PARENT_RETRY_UNAVAILABLE_SERVER)) {
780     // initialize UnavailableServerResponseCodes to the default value if unavailable_server_retry is enabled.
781     Warning("%s initializing UnavailableServerResponseCodes on line %d to 503 default.", modulePrefix, line_num);
782     unavailable_server_retry_responses = new UnavailableServerResponseCodes(nullptr);
783   }
784 
785   // delete simple_server_retry_responses if simple_retry is not enabled.
786   if (simple_server_retry_responses != nullptr && !(parent_retry & PARENT_RETRY_SIMPLE)) {
787     Warning("%s ignore simple_server_Retry_responses directive on line %d, as simple_server_retry is not enabled.", modulePrefix,
788             line_num);
789     delete simple_server_retry_responses;
790     simple_server_retry_responses = nullptr;
791   } else if (simple_server_retry_responses == nullptr && (parent_retry & PARENT_RETRY_SIMPLE)) {
792     // initialize simple server respones codes to the default value if simple_retry is enabled.
793     Warning("%s initializing SimpleRetryResponseCodes on line %d to 404 default.", modulePrefix, line_num);
794     simple_server_retry_responses = new SimpleRetryResponseCodes(nullptr);
795   }
796 
797   if (this->parents == nullptr && go_direct == false) {
798     return Result::failure("%s No parent specified in %s at line %d", modulePrefix, ts::filename::PARENT, line_num);
799   }
800   // Process any modifiers to the directive, if they exist
801   if (line_info->num_el > 0) {
802     tmp = ProcessModifiers(line_info);
803 
804     if (tmp != nullptr) {
805       return Result::failure("%s %s at line %d in %s", modulePrefix, tmp, line_num, ts::filename::PARENT);
806     }
807     // record SCHEME modifier if present.
808     // NULL if not present
809     this->scheme = this->getSchemeModText();
810     if (this->scheme != nullptr) {
811       // update parent entries' schemes
812       for (int j = 0; j < num_parents; j++) {
813         this->parents[j].scheme = this->scheme;
814       }
815     }
816   }
817 
818   switch (round_robin) {
819   // ParentRecord.round_robin defaults to P_NO_ROUND_ROBIN when round_robin
820   // is not set in parent.config.  Therefore ParentRoundRobin is the default
821   // strategy.  If setting go_direct to true, there should be no parent list
822   // in parent.config and ParentRoundRobin::lookup will set parent_result->r
823   // to PARENT_DIRECT.
824   case P_NO_ROUND_ROBIN:
825   case P_STRICT_ROUND_ROBIN:
826   case P_HASH_ROUND_ROBIN:
827   case P_LATCHED_ROUND_ROBIN:
828     Debug("parent_select", "allocating ParentRoundRobin() lookup strategy.");
829     selection_strategy = new ParentRoundRobin(this, round_robin);
830     break;
831   case P_CONSISTENT_HASH:
832     Debug("parent_select", "allocating ParentConsistentHash() lookup strategy.");
833     selection_strategy = new ParentConsistentHash(this);
834     break;
835   default:
836     ink_release_assert(0);
837   }
838 
839   return Result::ok();
840 }
841 
842 // void ParentRecord::UpdateMatch(ParentResult* result, RequestData* rdata);
843 //
844 //    Updates the record ptr in result if the this element
845 //     appears later in the file
846 //
847 void
UpdateMatch(ParentResult * result,RequestData * rdata)848 ParentRecord::UpdateMatch(ParentResult *result, RequestData *rdata)
849 {
850   if (this->CheckForMatch((HttpRequestData *)rdata, result->line_number) == true) {
851     result->rec         = this;
852     result->line_number = this->line_num;
853 
854     Debug("parent_select", "Matched with %p parent node from line %d", this, this->line_num);
855   }
856 }
857 
~ParentRecord()858 ParentRecord::~ParentRecord()
859 {
860   ats_free(parents);
861   ats_free(secondary_parents);
862   delete selection_strategy;
863   delete unavailable_server_retry_responses;
864 }
865 
866 void
Print() const867 ParentRecord::Print() const
868 {
869   printf("\t\t");
870   for (int i = 0; i < num_parents; i++) {
871     printf(" %s:%d|%f&%s ", parents[i].hostname, parents[i].port, parents[i].weight, parents[i].name);
872   }
873   printf(" direct=%s\n", (go_direct == true) ? "true" : "false");
874   printf(" parent_is_proxy=%s\n", (parent_is_proxy == true) ? "true" : "false");
875 }
876 
877 // ParentRecord* createDefaultParent(char* val)
878 //
879 //  Atttemtps to allocate and init new ParentRecord
880 //    for a default parent
881 //
882 //  Returns a pointer to the new record on success
883 //   and NULL on failure
884 //
885 ParentRecord *
createDefaultParent(char * val)886 createDefaultParent(char *val)
887 {
888   ParentRecord *newRec;
889 
890   if (val == nullptr || *val == '\0') {
891     return nullptr;
892   }
893 
894   newRec = new ParentRecord;
895   if (newRec->DefaultInit(val) == true) {
896     return newRec;
897   } else {
898     delete newRec;
899     return nullptr;
900   }
901 }
902 
903 //
904 // ParentConfig equivalent functions for SocksServerConfig
905 //
906 
907 int SocksServerConfig::m_id = 0;
908 static Ptr<ProxyMutex> socks_server_reconfig_mutex;
909 void
startup()910 SocksServerConfig::startup()
911 {
912   socks_server_reconfig_mutex = new_ProxyMutex();
913 
914   // Load the initial configuration
915   reconfigure();
916 
917   /* Handle update functions later. Socks does not yet support config update */
918 }
919 
920 static int
setup_socks_servers(ParentRecord * rec_arr,int len)921 setup_socks_servers(ParentRecord *rec_arr, int len)
922 {
923   /* This changes hostnames into ip addresses and sets go_direct to false */
924   for (int j = 0; j < len; j++) {
925     rec_arr[j].go_direct = false;
926 
927     pRecord *pr   = rec_arr[j].parents;
928     int n_parents = rec_arr[j].num_parents;
929 
930     for (int i = 0; i < n_parents; i++) {
931       IpEndpoint ip4, ip6;
932       if (0 == ats_ip_getbestaddrinfo(pr[i].hostname, &ip4, &ip6)) {
933         IpEndpoint *ip = ats_is_ip6(&ip6) ? &ip6 : &ip4;
934         ats_ip_ntop(ip, pr[i].hostname, MAXDNAME + 1);
935       } else {
936         Warning("Could not resolve socks server name \"%s\". "
937                 "Please correct it",
938                 pr[i].hostname);
939         snprintf(pr[i].hostname, MAXDNAME + 1, "255.255.255.255");
940       }
941     }
942   }
943 
944   return 0;
945 }
946 
947 void
reconfigure()948 SocksServerConfig::reconfigure()
949 {
950   Note("%s loading ...", ts::filename::SOCKS);
951 
952   char *default_val = nullptr;
953   int retry_time    = 30;
954   int fail_threshold;
955 
956   ParentConfigParams *params = nullptr;
957 
958   // Allocate parent table
959   P_table *pTable = new P_table("proxy.config.socks.socks_config_file", "[Socks Server Selection]", &socks_server_tags);
960 
961   params = new ParentConfigParams(pTable);
962   ink_assert(params != nullptr);
963 
964   // Handle default parent
965   REC_ReadConfigStringAlloc(default_val, "proxy.config.socks.default_servers");
966   params->DefaultParent = createDefaultParent(default_val);
967   ats_free(default_val);
968 
969   if (params->DefaultParent) {
970     setup_socks_servers(params->DefaultParent, 1);
971   }
972   if (params->parent_table->ipMatch) {
973     setup_socks_servers(params->parent_table->ipMatch->data_array, params->parent_table->ipMatch->array_len);
974   }
975 
976   // Handle parent timeout
977   REC_ReadConfigInteger(retry_time, "proxy.config.socks.server_retry_time");
978   params->policy.ParentRetryTime = retry_time;
979 
980   // Handle the fail threshold
981   REC_ReadConfigInteger(fail_threshold, "proxy.config.socks.server_fail_threshold");
982   params->policy.FailThreshold = fail_threshold;
983 
984   m_id = configProcessor.set(m_id, params);
985 
986   if (is_debug_tag_set("parent_config")) {
987     SocksServerConfig::print();
988   }
989 
990   Note("%s finished loading", ts::filename::SOCKS);
991 }
992 
993 void
print()994 SocksServerConfig::print()
995 {
996   ParentConfigParams *params = SocksServerConfig::acquire();
997 
998   printf("Parent Selection Config for Socks Server\n");
999   printf("\tRetryTime %d\n", params->policy.ParentRetryTime);
1000   if (params->DefaultParent == nullptr) {
1001     printf("\tNo Default Parent\n");
1002   } else {
1003     printf("\tDefault Parent:\n");
1004     params->DefaultParent->Print();
1005   }
1006   printf("  ");
1007   params->parent_table->Print();
1008 
1009   SocksServerConfig::release(params);
1010 }
1011 
1012 #define TEST_FAIL(str)                \
1013   {                                   \
1014     printf("%d: %s\n", test_id, str); \
1015     err = REGRESSION_TEST_FAILED;     \
1016   }
1017 
1018 void
request_to_data(HttpRequestData * req,sockaddr const * srcip,sockaddr const * dstip,const char * str)1019 request_to_data(HttpRequestData *req, sockaddr const *srcip, sockaddr const *dstip, const char *str)
1020 {
1021   HTTPParser parser;
1022 
1023   ink_zero(req->src_ip);
1024   ats_ip_copy(&req->src_ip.sa, srcip);
1025   ink_zero(req->dest_ip);
1026   ats_ip_copy(&req->dest_ip.sa, dstip);
1027 
1028   req->hdr = new HTTPHdr;
1029 
1030   http_parser_init(&parser);
1031 
1032   req->hdr->parse_req(&parser, &str, str + strlen(str), true);
1033 
1034   http_parser_clear(&parser);
1035 }
1036 
1037 static int passes;
1038 static int fails;
1039 
1040 // Parenting Tests
EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)1041 EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, int /* intensity_level ATS_UNUSED */, int *pstatus)
1042 {
1043   // first, set everything up
1044   *pstatus = REGRESSION_TEST_INPROGRESS;
1045   ParentConfig config;
1046   P_table *ParentTable       = nullptr;
1047   ParentConfigParams *params = nullptr;
1048   passes = fails = 0;
1049   config.startup();
1050   char tbl[2048];
1051   HttpRequestData *request    = nullptr;
1052   ParentResult *result        = nullptr;
1053   unsigned int fail_threshold = 1;
1054   unsigned int retry_time     = 5;
1055 
1056 #define T(x)                          \
1057   do {                                \
1058     ink_strlcat(tbl, x, sizeof(tbl)); \
1059   } while (0)
1060 
1061 #define REBUILD                                                                                                            \
1062   do {                                                                                                                     \
1063     delete params;                                                                                                         \
1064     ParentTable = new P_table("", "ParentSelection Unit Test Table", &http_dest_tags,                                      \
1065                               ALLOW_HOST_TABLE | ALLOW_REGEX_TABLE | ALLOW_URL_TABLE | ALLOW_IP_TABLE | DONT_BUILD_TABLE); \
1066     ParentTable->BuildTableFromString(tbl);                                                                                \
1067     RecSetRecordInt("proxy.config.http.parent_proxy.fail_threshold", fail_threshold, REC_SOURCE_DEFAULT);                  \
1068     RecSetRecordInt("proxy.config.http.parent_proxy.retry_time", retry_time, REC_SOURCE_DEFAULT);                          \
1069     params = new ParentConfigParams(ParentTable);                                                                          \
1070   } while (0)
1071 
1072 #define REINIT                             \
1073   do {                                     \
1074     if (request != NULL) {                 \
1075       delete request->hdr;                 \
1076       ats_free(request->hostname_str);     \
1077       delete request->api_info;            \
1078     }                                      \
1079     delete request;                        \
1080     delete result;                         \
1081     request = new HttpRequestData();       \
1082     result  = new ParentResult();          \
1083     if (!result || !request) {             \
1084       (void)printf("Allocation failed\n"); \
1085       return;                              \
1086     }                                      \
1087   } while (0)
1088 
1089 #define ST(x)                                    \
1090   do {                                           \
1091     printf("*** TEST %d *** STARTING ***\n", x); \
1092   } while (0)
1093 
1094 #define RE(x, y)                                                       \
1095   do {                                                                 \
1096     if (x) {                                                           \
1097       printf("*** TEST %d *** PASSED ***\n", y);                       \
1098       passes++;                                                        \
1099     } else {                                                           \
1100       printf("*** TEST %d *** FAILED *** FAILED *** FAILED ***\n", y); \
1101       fails++;                                                         \
1102     }                                                                  \
1103   } while (0)
1104 
1105 #define FP                                                           \
1106   do {                                                               \
1107     params->findParent(request, result, fail_threshold, retry_time); \
1108   } while (0)
1109 
1110 #define SET_MAX_RETRIERS(x)                                                                     \
1111   do {                                                                                          \
1112     RecSetRecordInt("proxy.config.http.parent_proxy.max_trans_retries", x, REC_SOURCE_DEFAULT); \
1113   } while (0)
1114 
1115   // start tests by marking up all tests hosts that will be marked down
1116   // as part of testing.  This will insure that test hosts are not loaded
1117   // from records.snap as DOWN due to previous testing.
1118   //
1119   HostStatus &_st = HostStatus::instance();
1120   _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL);
1121   _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL);
1122   _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL);
1123   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1124   _st.setHostStatus("curly", HOST_STATUS_UP, 0, Reason::MANUAL);
1125 
1126   // Test 1
1127   SET_MAX_RETRIERS(20);
1128   tbl[0] = '\0';
1129   ST(1);
1130   T("dest_domain=. parent=red:37412,orange:37412,yellow:37412 round_robin=strict\n");
1131   REBUILD;
1132   int c, red = 0, orange = 0, yellow = 0;
1133   for (c = 0; c < 21; c++) {
1134     REINIT;
1135     br(request, "fruit_basket.net");
1136     FP;
1137     red += verify(result, PARENT_SPECIFIED, "red", 37412);
1138     orange += verify(result, PARENT_SPECIFIED, "orange", 37412);
1139     yellow += verify(result, PARENT_SPECIFIED, "yellow", 37412);
1140   }
1141   RE(((red == 7) && (orange == 7) && (yellow == 7)), 1);
1142   // Test 2
1143   ST(2);
1144   tbl[0] = '\0';
1145   T("dest_domain=. parent=green:4325,blue:4325,indigo:4325,violet:4325 round_robin=false\n");
1146   REBUILD;
1147   int g = 0, b = 0, i = 0, v = 0;
1148   for (c = 0; c < 17; c++) {
1149     REINIT;
1150     br(request, "fruit_basket.net");
1151     FP;
1152     g += verify(result, PARENT_SPECIFIED, "green", 4325);
1153     b += verify(result, PARENT_SPECIFIED, "blue", 4325);
1154     i += verify(result, PARENT_SPECIFIED, "indigo", 4325);
1155     v += verify(result, PARENT_SPECIFIED, "violet", 4325);
1156   }
1157   RE((((g == 17) && !b && !i && !v) || (!g && (b == 17) && !i && !v) || (!g && !b && (i == 17) && !v) ||
1158       (!g && !b && !i && (v == 17))),
1159      2);
1160   // Test 3 - 6 Parenting Table
1161   tbl[0] = '\0';
1162 #define TEST_IP4_ADDR "209.131.62.14"
1163 #define TEST_IP6_ADDR "BEEF:DEAD:ABBA:CAFE:1337:1E1F:5EED:C0FF"
1164   T("dest_ip=" TEST_IP4_ADDR " parent=cat:37,dog:24 round_robin=strict\n");             /* L1 */
1165   T("dest_ip=" TEST_IP6_ADDR " parent=zwoop:37,jMCg:24 round_robin=strict\n");          /* L1 */
1166   T("dest_host=www.pilot.net parent=pilot_net:80\n");                                   /* L2 */
1167   T("url_regex=snoopy parent=odie:80,garfield:80 round_robin=true\n");                  /* L3 */
1168   T("dest_domain=i.am parent=amy:80,katie:80,carissa:771 round_robin=false\n");         /* L4 */
1169   T("dest_domain=microsoft.net time=03:00-22:10 parent=zoo.net:341\n");                 /* L5 */
1170   T("dest_domain=microsoft.net time=0:00-02:59 parent=zoo.net:347\n");                  /* L6 */
1171   T("dest_domain=microsoft.net time=22:11-23:59 parent=zoo.edu:111\n");                 /* L7 */
1172   T("dest_domain=imac.net port=819 parent=genie:80 round_robin=strict\n");              /* L8 */
1173   T("dest_ip=172.34.61.211 port=3142 parent=orangina:80 go_direct=false\n");            /* L9 */
1174   T("url_regex=miffy prefix=furry/rabbit parent=nintje:80 go_direct=false\n");          /* L10 */
1175   T("url_regex=kitty suffix=tif parent=hello:80 round_robin=strict go_direct=false\n"); /* L11 */
1176   T("url_regex=cyclops method=get parent=turkey:80\n");                                 /* L12 */
1177   T("url_regex=cyclops method=post parent=club:80\n");                                  /* L13 */
1178   T("url_regex=cyclops method=put parent=sandwich:80\n");                               /* L14 */
1179   T("url_regex=cyclops method=trace parent=mayo:80\n");                                 /* L15 */
1180   T("dest_host=pluto scheme=HTTP parent=strategy:80\n");                                /* L16 */
1181   REBUILD;
1182   // Test 3
1183   IpEndpoint ip;
1184   ats_ip_pton(TEST_IP4_ADDR, &ip.sa);
1185   ST(3);
1186   REINIT;
1187   br(request, "numeric_host", &ip.sa);
1188   FP;
1189   RE(verify(result, PARENT_SPECIFIED, "cat", 37) + verify(result, PARENT_SPECIFIED, "dog", 24), 3);
1190   ats_ip_pton(TEST_IP6_ADDR, &ip.sa);
1191   ST(4);
1192   REINIT;
1193   br(request, "numeric_host", &ip.sa);
1194   FP;
1195   RE(verify(result, PARENT_SPECIFIED, "zwoop", 37) + verify(result, PARENT_SPECIFIED, "jMCg", 24), 4);
1196   // Test 5
1197   ST(5);
1198   REINIT;
1199   br(request, "www.pilot.net");
1200   FP;
1201   RE(verify(result, PARENT_SPECIFIED, "pilot_net", 80), 5);
1202   // Test 6
1203   ST(6);
1204   REINIT;
1205   br(request, "www.snoopy.net");
1206   const char *snoopy_dog = "http://www.snoopy.com/";
1207   request->hdr->url_set(snoopy_dog, strlen(snoopy_dog));
1208   FP;
1209   RE(verify(result, PARENT_SPECIFIED, "odie", 80) + verify(result, PARENT_SPECIFIED, "garfield", 80), 5);
1210   // Test 7
1211   ST(7);
1212   REINIT;
1213   br(request, "a.rabbit.i.am");
1214   FP;
1215   RE(verify(result, PARENT_SPECIFIED, "amy", 80) + verify(result, PARENT_SPECIFIED, "katie", 80) +
1216        verify(result, PARENT_SPECIFIED, "carissa", 771),
1217      6);
1218   // Test 6+ BUGBUG needs to be fixed
1219   //   ST(7); REINIT;
1220   //   br(request, "www.microsoft.net");
1221   //   FP; RE( verify(result,PARENT_SPECIFIED,"zoo.net",341) +
1222   //       verify(result,PARENT_SPECIFIED,"zoo.net",347) +
1223   //       verify(result,PARENT_SPECIFIED,"zoo.edu",111) ,7);
1224   // Test 6++ BUGBUG needs to be fixed
1225   //   ST(7); REINIT;
1226   //   br(request, "snow.imac.net:2020");
1227   //   FP; RE(verify(result,PARENT_DIRECT,0,0),7);
1228   // Test 6+++ BUGBUG needs to be fixed
1229   //   ST(8); REINIT;
1230   //   br(request, "snow.imac.net:819");
1231   //   URL* u = new URL();
1232   //   char* r = "http://snow.imac.net:819/";
1233   //   u->create(0);
1234   //   u->parse(r,strlen(r));
1235   //   u->port_set(819);
1236   //   request->hdr->url_set(u);
1237   //   ink_assert(request->hdr->url_get()->port_get() == 819);
1238   //   printf("url: %s\n",request->hdr->url_get()->string_get(0));
1239   //   FP; RE(verify(result,PARENT_SPECIFIED,"genie",80),8);
1240   // Test 7 - N Parent Table
1241   tbl[0] = '\0';
1242   T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=strict go_direct=true\n");
1243   REBUILD;
1244   // Test 8
1245   ST(8);
1246   REINIT;
1247   br(request, "i.am.rabbit.net");
1248   FP;
1249   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 7);
1250   params->markParentDown(result, fail_threshold, retry_time);
1251 
1252   // Test 9
1253   ST(9);
1254   REINIT;
1255   br(request, "i.am.rabbit.net");
1256   FP;
1257   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 8);
1258   // Test 10
1259   ST(10);
1260   REINIT;
1261   br(request, "i.am.rabbit.net");
1262   FP;
1263   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 9);
1264   // Test 11
1265   ST(11);
1266   REINIT;
1267   br(request, "i.am.rabbit.net");
1268   FP;
1269   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 10);
1270   // restart the loop
1271   // Test 12
1272   ST(12);
1273   REINIT;
1274   br(request, "i.am.rabbit.net");
1275   FP;
1276   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 11);
1277   // Test 13
1278   ST(13);
1279   REINIT;
1280   br(request, "i.am.rabbit.net");
1281   FP;
1282   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 12);
1283   // Test 14
1284   ST(14);
1285   REINIT;
1286   br(request, "i.am.rabbit.net");
1287   FP;
1288   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 13);
1289   // Test 15
1290   ST(15);
1291   REINIT;
1292   br(request, "i.am.rabbit.net");
1293   FP;
1294   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 14);
1295   params->markParentDown(result, fail_threshold, retry_time);
1296 
1297   // restart the loop
1298 
1299   // Test 16
1300   ST(16);
1301   REINIT;
1302   br(request, "i.am.rabbit.net");
1303   FP;
1304   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 15);
1305   // Test 17
1306   ST(17);
1307   REINIT;
1308   br(request, "i.am.rabbit.net");
1309   FP;
1310   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 16);
1311   // Test 18
1312   ST(18);
1313   REINIT;
1314   br(request, "i.am.rabbit.net");
1315   FP;
1316   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 17);
1317   // Test 19
1318   ST(19);
1319   REINIT;
1320   br(request, "i.am.rabbit.net");
1321   FP;
1322   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 18);
1323   // restart the loop
1324   // Test 20
1325   ST(20);
1326   REINIT;
1327   br(request, "i.am.rabbit.net");
1328   FP;
1329   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 19);
1330   // Test 21
1331   ST(21);
1332   REINIT;
1333   br(request, "i.am.rabbit.net");
1334   FP;
1335   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 20);
1336   // Test 22
1337   ST(22);
1338   REINIT;
1339   br(request, "i.am.rabbit.net");
1340   FP;
1341   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 21);
1342   params->markParentDown(result, fail_threshold, retry_time);
1343 
1344   // Test 23 - 32
1345   for (i = 23; i < 33; i++) {
1346     ST(i);
1347     REINIT;
1348     br(request, "i.am.rabbit.net");
1349     FP;
1350     RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i);
1351   }
1352 
1353   params->markParentDown(result, 1, 5); // now they're all down
1354 
1355   // Test 33 - 132
1356   for (i = 33; i < 133; i++) {
1357     ST(i);
1358     REINIT;
1359     br(request, "i.am.rabbit.net");
1360     FP;
1361     RE(verify(result, PARENT_DIRECT, nullptr, 0), i);
1362   }
1363 
1364   // sleep(5); // parents should come back up; they don't
1365   sleep(params->policy.ParentRetryTime + 1);
1366 
1367   // Fix: The following tests failed because
1368   // br() should set xact_start correctly instead of 0.
1369 
1370   // Test 133 - 172
1371   for (i = 133; i < 173; i++) {
1372     ST(i);
1373     REINIT;
1374     br(request, "i.am.rabbit.net");
1375     FP;
1376     sleep(1);
1377     switch (i % 4) {
1378     case 0:
1379       RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), i);
1380       break;
1381     case 1:
1382       RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i);
1383       break;
1384     case 2:
1385       RE(verify(result, PARENT_SPECIFIED, "furry", 80), i);
1386       break;
1387     case 3:
1388       RE(verify(result, PARENT_SPECIFIED, "frisky", 80), i);
1389       break;
1390     default:
1391       ink_assert(0);
1392     }
1393   }
1394 
1395   // Test 173
1396   tbl[0] = '\0';
1397   ST(173);
1398   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 "
1399     "round_robin=consistent_hash go_direct=false\n");
1400   REBUILD;
1401   REINIT;
1402   br(request, "i.am.rabbit.net");
1403   FP;
1404   sleep(1);
1405   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 173);
1406   params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down.
1407 
1408   // Test 174
1409   ST(174);
1410   REINIT;
1411   br(request, "i.am.rabbit.net");
1412   FP;
1413   sleep(1);
1414   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 174);
1415 
1416   params->markParentDown(result, fail_threshold, retry_time); // frisky is down.
1417 
1418   // Test 175
1419   ST(175);
1420   REINIT;
1421   br(request, "i.am.rabbit.net");
1422   FP;
1423   sleep(1);
1424   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 175);
1425 
1426   params->markParentDown(result, fail_threshold, retry_time); // frisky is down.
1427 
1428   // Test 176
1429   ST(176);
1430   REINIT;
1431   br(request, "i.am.rabbit.net");
1432   FP;
1433   sleep(1);
1434   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 176);
1435 
1436   params->markParentDown(result, fail_threshold, retry_time); // all are down now.
1437 
1438   // Test 177
1439   ST(177);
1440   REINIT;
1441   br(request, "i.am.rabbit.net");
1442   FP;
1443   sleep(1);
1444   RE(verify(result, PARENT_FAIL, nullptr, 80), 177);
1445 
1446   // Test 178
1447   tbl[0] = '\0';
1448   ST(178);
1449   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0;furry:80|1.0;frisky:80|1.0 "
1450     "round_robin=latched go_direct=false\n");
1451   REBUILD;
1452   REINIT;
1453   br(request, "i.am.rabbit.net");
1454   FP;
1455   sleep(1);
1456   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 178);
1457 
1458   params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down
1459 
1460   // Test 179
1461   ST(179);
1462   REINIT;
1463   br(request, "i.am.rabbit.net");
1464   FP;
1465   sleep(1);
1466   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 179);
1467 
1468   params->markParentDown(result, fail_threshold, retry_time); // fluffy is down
1469 
1470   // Test 180
1471   ST(180);
1472   REINIT;
1473   br(request, "i.am.rabbit.net");
1474   FP;
1475   sleep(1);
1476   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 180);
1477 
1478   params->markParentDown(result, fail_threshold, retry_time); // furry is down
1479 
1480   // Test 181
1481   ST(181);
1482   REINIT;
1483   br(request, "i.am.rabbit.net");
1484   FP;
1485   sleep(1);
1486   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 181);
1487 
1488   params->markParentDown(result, fail_threshold, retry_time); // frisky is down and we should be back on fuzzy.
1489 
1490   // Test 182
1491   ST(182);
1492   REINIT;
1493   br(request, "i.am.rabbit.net");
1494   FP;
1495   sleep(1);
1496   RE(verify(result, PARENT_FAIL, nullptr, 80), 182);
1497 
1498   // wait long enough so that fuzzy is retryable.
1499   sleep(params->policy.ParentRetryTime - 2);
1500 
1501   // Test 183
1502   ST(183);
1503   REINIT;
1504   br(request, "i.am.rabbit.net");
1505   FP;
1506   sleep(1);
1507   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 183);
1508 
1509   // Test 184
1510   // mark fuzzy down with HostStatus API.
1511   _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1512 
1513   ST(184);
1514   REINIT;
1515   br(request, "i.am.rabbit.net");
1516   FP;
1517   sleep(1);
1518   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 184);
1519 
1520   // Test 185
1521   // mark fluffy down and expect furry to be chosen
1522   _st.setHostStatus("fluffy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1523 
1524   ST(185);
1525   REINIT;
1526   br(request, "i.am.rabbit.net");
1527   FP;
1528   sleep(1);
1529   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 185);
1530 
1531   // Test 186
1532   // mark furry and frisky down, fuzzy up and expect fuzzy to be chosen
1533   _st.setHostStatus("furry", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1534   _st.setHostStatus("frisky", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1535   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1536 
1537   ST(186);
1538   REINIT;
1539   br(request, "i.am.rabbit.net");
1540   FP;
1541   sleep(1);
1542   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 186);
1543 
1544   // Test 187
1545   // test the HostStatus API with ParentConsistent Hash.
1546   tbl[0] = '\0';
1547   ST(187);
1548   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0;furry:80|1.0;frisky:80|1.0 "
1549     "round_robin=consistent_hash go_direct=false\n");
1550   REBUILD;
1551 
1552   // mark all up.
1553   _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL);
1554   _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL);
1555   _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL);
1556   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1557 
1558   REINIT;
1559   br(request, "i.am.rabbit.net");
1560   FP;
1561   sleep(1);
1562   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 187);
1563 
1564   // Test 188
1565   // mark fuzzy down and expect fluffy.
1566   _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1567 
1568   ST(188);
1569   REINIT;
1570   br(request, "i.am.rabbit.net");
1571   FP;
1572   sleep(1);
1573   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 188);
1574 
1575   // Test 189
1576   // mark fuzzy back up and expect fuzzy.
1577   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1578 
1579   ST(189);
1580   REINIT;
1581   br(request, "i.am.rabbit.net");
1582   FP;
1583   sleep(1);
1584   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 189);
1585 
1586   // Test 190
1587   // mark fuzzy back down and set the host status down
1588   // then wait for fuzzy to become available.
1589   // even though fuzzy becomes retryable we should not select it
1590   // because the host status is set to down.
1591   params->markParentDown(result, fail_threshold, retry_time);
1592   // set host status down
1593   _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1594   // sleep long enough so that fuzzy is retryable
1595   sleep(params->policy.ParentRetryTime + 1);
1596   ST(190);
1597   REINIT;
1598   br(request, "i.am.rabbit.net");
1599   FP;
1600   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 190);
1601 
1602   // now set the host status on fuzzy to up and it should now
1603   // be retried.
1604   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1605   ST(191);
1606   REINIT;
1607   br(request, "i.am.rabbit.net");
1608   FP;
1609   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 191);
1610 
1611   // Test 192
1612   tbl[0] = '\0';
1613   ST(192);
1614   T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=false go_direct=true\n");
1615   REBUILD;
1616   // mark all up.
1617   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1618   _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL);
1619   _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL);
1620   _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL);
1621   // fuzzy should be chosen.
1622   sleep(1);
1623   REINIT;
1624   br(request, "i.am.rabbit.net");
1625   FP;
1626   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 192);
1627 
1628   // Test 193
1629   // mark fuzzy down and wait for it to become retryable
1630   ST(193);
1631   params->markParentDown(result, fail_threshold, retry_time);
1632   sleep(params->policy.ParentRetryTime + 1);
1633   // since the host status is down even though fuzzy is
1634   // retryable, fluffy should be chosen
1635   _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1636   REINIT;
1637   br(request, "i.am.rabbit.net");
1638   FP;
1639   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 193);
1640 
1641   // Test 194
1642   // set the host status for fuzzy  back up and since its
1643   // retryable fuzzy should be chosen
1644   ST(194);
1645   _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
1646   REINIT;
1647   br(request, "i.am.rabbit.net");
1648   FP;
1649   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 194);
1650 
1651   // Test 195
1652   // secondary_mode=1 (default) is covered by tests cases 173-177 above
1653   // secondary_mode=2 is tested here
1654   // fuzzy { frisky furry } fluffy
1655   tbl[0] = '\0';
1656   ST(195);
1657   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 "
1658     "round_robin=consistent_hash go_direct=false secondary_mode=2\n");
1659   REBUILD;
1660   REINIT;
1661   br(request, "i.am.rabbit.net");
1662   FP;
1663   sleep(1);
1664   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 195);
1665   params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down.
1666 
1667   // Test 196
1668   ST(196);
1669   REINIT;
1670   br(request, "i.am.rabbit.net");
1671   FP;
1672   sleep(1);
1673   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 196);
1674 
1675   params->markParentDown(result, fail_threshold, retry_time); // fluffy is down.
1676 
1677   // Test 197
1678   ST(197);
1679   REINIT;
1680   br(request, "i.am.rabbit.net");
1681   FP;
1682   sleep(1);
1683   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 197);
1684 
1685   params->markParentDown(result, fail_threshold, retry_time); // frisky is down.
1686 
1687   // Test 198
1688   ST(198);
1689   REINIT;
1690   br(request, "i.am.rabbit.net");
1691   FP;
1692   sleep(1);
1693   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 198);
1694 
1695   params->markParentDown(result, fail_threshold, retry_time); // all are down now.
1696 
1697   // Test 199
1698   ST(199);
1699   REINIT;
1700   br(request, "i.am.rabbit.net");
1701   FP;
1702   sleep(1);
1703   RE(verify(result, PARENT_FAIL, nullptr, 80), 199);
1704 
1705   // Test 200
1706   // secondary_mode=3 is tested here first-choice NOT marked down
1707   // fuzzy { frisky furry } fluffy
1708   tbl[0] = '\0';
1709   ST(200);
1710   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 "
1711     "round_robin=consistent_hash go_direct=false secondary_mode=3\n");
1712   REBUILD;
1713   REINIT;
1714   br(request, "i.am.rabbit.net");
1715   FP;
1716   sleep(1);
1717   RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 200);
1718   params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down.
1719 
1720   // Test 201
1721   ST(201);
1722   REINIT;
1723   br(request, "i.am.rabbit.net");
1724   FP;
1725   sleep(1);
1726   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 201);
1727 
1728   params->markParentDown(result, fail_threshold, retry_time); // fluffy is down.
1729 
1730   // Test 202
1731   ST(202);
1732   REINIT;
1733   br(request, "i.am.rabbit.net");
1734   FP;
1735   sleep(1);
1736   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 202);
1737 
1738   params->markParentDown(result, fail_threshold, retry_time); // frisky is down.
1739 
1740   // Test 203
1741   ST(203);
1742   REINIT;
1743   br(request, "i.am.rabbit.net");
1744   FP;
1745   sleep(1);
1746   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 203);
1747 
1748   params->markParentDown(result, fail_threshold, retry_time); // all are down now.
1749 
1750   // Test 204
1751   ST(204);
1752   REINIT;
1753   br(request, "i.am.rabbit.net");
1754   FP;
1755   sleep(1);
1756   RE(verify(result, PARENT_FAIL, nullptr, 80), 204);
1757 
1758   // Test 205
1759   // secondary_mode=3 is tested here first-choice marked down
1760   // fuzzy { frisky furry } fluffy
1761   tbl[0] = '\0';
1762   ST(205);
1763   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 "
1764     "round_robin=consistent_hash go_direct=false secondary_mode=3\n");
1765   REBUILD;
1766   _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1767   REINIT;
1768   br(request, "i.am.rabbit.net");
1769   FP;
1770   sleep(1);
1771   RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 205);
1772   params->markParentDown(result, fail_threshold, retry_time); // frisky is down.
1773 
1774   // Test 206
1775   ST(206);
1776   REINIT;
1777   br(request, "i.am.rabbit.net");
1778   FP;
1779   sleep(1);
1780   RE(verify(result, PARENT_SPECIFIED, "furry", 80), 206);
1781 
1782   params->markParentDown(result, fail_threshold, retry_time); // furry is down.
1783 
1784   // Test 207
1785   ST(207);
1786   REINIT;
1787   br(request, "i.am.rabbit.net");
1788   FP;
1789   sleep(1);
1790   RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 207);
1791 
1792   params->markParentDown(result, fail_threshold, retry_time); // all are down now.
1793 
1794   // Test 208
1795   ST(208);
1796   REINIT;
1797   br(request, "i.am.rabbit.net");
1798   FP;
1799   sleep(1);
1800   RE(verify(result, PARENT_FAIL, nullptr, 80), 208);
1801 
1802   // Tests 209 through 211 test that host selection is based upon the hash_string
1803 
1804   // Test 209
1805   // fuzzy { curly larry, moe } fluffy
1806   tbl[0] = '\0';
1807   ST(209);
1808   T("dest_domain=stooges.net parent=curly:80|1.0&myhash;joe:80|1.0&hishash;larry:80|1.0&ourhash "
1809     "round_robin=consistent_hash go_direct=false\n");
1810   REBUILD;
1811   REINIT;
1812   br(request, "i.am.stooges.net");
1813   FP;
1814   RE(verify(result, PARENT_SPECIFIED, "larry", 80), 209);
1815 
1816   // Test 210
1817   // fuzzy { curly larry, moe } fluffy
1818   tbl[0] = '\0';
1819   ST(210);
1820   T("dest_domain=stooges.net parent=curly:80|1.0&ourhash;joe:80|1.0&hishash;larry:80|1.0&myhash "
1821     "round_robin=consistent_hash go_direct=false\n");
1822   REBUILD;
1823   REINIT;
1824   br(request, "i.am.stooges.net");
1825   FP;
1826   RE(verify(result, PARENT_SPECIFIED, "curly", 80), 210);
1827 
1828   // Test 211
1829   // fuzzy { curly larry, moe } fluffy
1830   tbl[0] = '\0';
1831   ST(211);
1832   T("dest_domain=stooges.net parent=curly:80|1.0&ourhash;joe:80|1.0&hishash;larry:80|1.0&myhash "
1833     "secondary_parent=carol:80|1.0&ourhash;betty:80|1.0&hishash;donna:80|1.0&myhash "
1834     "round_robin=consistent_hash go_direct=false\n");
1835   REBUILD;
1836   REINIT;
1837   _st.setHostStatus("curly", HOST_STATUS_DOWN, 0, Reason::MANUAL);
1838   br(request, "i.am.stooges.net");
1839   FP;
1840   RE(verify(result, PARENT_SPECIFIED, "carol", 80), 211);
1841 
1842   // max_retriers tests
1843   SET_MAX_RETRIERS(1);
1844 
1845   // Test 212
1846   tbl[0] = '\0';
1847   ST(212);
1848   T("dest_domain=mouse.com parent=mickey:80|0.33;minnie:80|0.33;goofy:80|0.33 "
1849     "round_robin=consistent_hash go_direct=false\n");
1850   REBUILD;
1851   REINIT;
1852   br(request, "i.am.mouse.com");
1853   FP;
1854   RE(verify(result, PARENT_SPECIFIED, "goofy", 80), 212);
1855 
1856   // Test 213
1857   // markdown goofy and minnie gets chosen.
1858   ST(213);
1859   params->markParentDown(result, fail_threshold, retry_time); // marked down goofy
1860   REINIT;
1861   br(request, "i.am.mouse.com");
1862   FP;
1863   RE(verify(result, PARENT_SPECIFIED, "minnie", 80), 213);
1864 
1865   // Test 214
1866   // goofy gets chosen because max_retriers was set to 1
1867   // and goofy becomes available.
1868   sleep(params->policy.ParentRetryTime + 3);
1869   ST(214);
1870   REINIT;
1871   br(request, "i.am.mouse.com");
1872   FP;
1873   RE(verify(result, PARENT_SPECIFIED, "goofy", 80), 214);
1874 
1875   delete request;
1876   delete result;
1877   delete params;
1878 
1879   printf("Tests Passed: %d\nTests Failed: %d\n", passes, fails);
1880   *pstatus = (!fails ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
1881 }
1882 
1883 // verify returns 1 iff the test passes
1884 int
verify(ParentResult * r,ParentResultType e,const char * h,int p)1885 verify(ParentResult *r, ParentResultType e, const char *h, int p)
1886 {
1887   if (is_debug_tag_set("parent_select")) {
1888     show_result(r);
1889   }
1890   return (r->result != e) ? 0 : ((e != PARENT_SPECIFIED) ? 1 : (strcmp(r->hostname, h) ? 0 : ((r->port == p) ? 1 : 0)));
1891 }
1892 
1893 // br creates an HttpRequestData object
1894 void
br(HttpRequestData * h,const char * os_hostname,sockaddr const * dest_ip)1895 br(HttpRequestData *h, const char *os_hostname, sockaddr const *dest_ip)
1896 {
1897   h->hdr = new HTTPHdr();
1898   h->hdr->create(HTTP_TYPE_REQUEST);
1899   h->hostname_str = ats_strdup(os_hostname);
1900   h->xact_start   = time(nullptr);
1901   ink_zero(h->src_ip);
1902   ink_zero(h->dest_ip);
1903   ats_ip_copy(&h->dest_ip.sa, dest_ip);
1904   h->incoming_port = 80;
1905   h->api_info      = new HttpApiInfo();
1906 }
1907 
1908 // show_result prints out the ParentResult information
1909 void
show_result(ParentResult * p)1910 show_result(ParentResult *p)
1911 {
1912   switch (p->result) {
1913   case PARENT_UNDEFINED:
1914     printf("result is PARENT_UNDEFINED\n");
1915     break;
1916   case PARENT_DIRECT:
1917     printf("result is PARENT_DIRECT\n");
1918     break;
1919   case PARENT_SPECIFIED:
1920     printf("result is PARENT_SPECIFIED\n");
1921     printf("hostname is %s\n", p->hostname);
1922     printf("port is %d\n", p->port);
1923     break;
1924   case PARENT_FAIL:
1925     printf("result is PARENT_FAIL\n");
1926     break;
1927   default:
1928     // Handled here:
1929     // PARENT_AGENT
1930     break;
1931   }
1932 }
1933