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