| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /***************************************************************************** |
| * |
| * ParentSelection.cc - Implementation of Parent Proxy routing |
| * |
| * |
| ****************************************************************************/ |
| |
| #include "ink_unused.h" /* MAGIC_EDITING_TAG */ |
| |
| #include "libts.h" |
| #include "P_EventSystem.h" |
| #include "ParentSelection.h" |
| #include "ControlMatcher.h" |
| #include "Main.h" |
| #include "Error.h" |
| #include "ProxyConfig.h" |
| #include "HTTP.h" |
| #include "HttpTransact.h" |
| |
| #define PARENT_RegisterConfigUpdateFunc REC_RegisterConfigUpdateFunc |
| #define PARENT_ReadConfigInteger REC_ReadConfigInteger |
| #define PARENT_ReadConfigStringAlloc REC_ReadConfigStringAlloc |
| |
| typedef ControlMatcher<ParentRecord, ParentResult> P_table; |
| |
| // Global Vars for Parent Selection |
| static const char modulePrefix[] = "[ParentSelection]"; |
| static Ptr<ProxyMutex> reconfig_mutex = NULL; |
| |
| // Config var names |
| static const char *file_var = "proxy.config.http.parent_proxy.file"; |
| static const char *default_var = "proxy.config.http.parent_proxies"; |
| static const char *retry_var = "proxy.config.http.parent_proxy.retry_time"; |
| static const char *enable_var = "proxy.config.http.parent_proxy_routing_enable"; |
| static const char *threshold_var = "proxy.config.http.parent_proxy.fail_threshold"; |
| static const char *dns_parent_only_var = "proxy.config.http.no_dns_just_forward_to_parent"; |
| |
| static const char *ParentResultStr[] = { |
| "Parent_Undefined", |
| "Parent_Direct", |
| "Parent_Specified", |
| "Parent_Failed" |
| }; |
| |
| static const char *ParentRRStr[] = { |
| "false", |
| "strict", |
| "true" |
| }; |
| |
| // |
| // Config Callback Prototypes |
| // |
| enum ParentCB_t |
| { PARENT_FILE_CB, PARENT_DEFAULT_CB, |
| PARENT_RETRY_CB, PARENT_ENABLE_CB, |
| PARENT_THRESHOLD_CB, PARENT_DNS_ONLY_CB |
| }; |
| |
| // If the parent was set by the external customer api, |
| // our HttpRequestData structure told us what parent to |
| // use and we are only called to preserve clean interface |
| // between HttpTransact & the parent selection code. The following |
| ParentRecord *const extApiRecord = (ParentRecord *) 0xeeeeffff; |
| |
| ParentConfigParams::ParentConfigParams(): |
| ParentTable(NULL), DefaultParent(NULL), ParentRetryTime(30), ParentEnable(0), FailThreshold(10), DNS_ParentOnly(0) |
| { |
| } |
| |
| ParentConfigParams::~ParentConfigParams() |
| { |
| if (ParentTable) { |
| delete ParentTable; |
| } |
| |
| if (DefaultParent) { |
| delete DefaultParent; |
| } |
| } |
| |
| int |
| ParentConfig::m_id = 0; |
| |
| // |
| // Begin API functions |
| // |
| |
| ParentConfigParams * |
| ParentConfig::acquire() |
| { |
| return (ParentConfigParams *) configProcessor.get(ParentConfig::m_id); |
| } |
| |
| void |
| ParentConfig::release(ParentConfigParams * params) |
| { |
| configProcessor.release(ParentConfig::m_id, params); |
| } |
| |
| void |
| ParentConfig::startup() |
| { |
| reconfig_mutex = new_ProxyMutex(); |
| |
| // Load the initial configuration |
| reconfigure(); |
| |
| // Setup the callbacks for reconfiuration |
| // parent table |
| PARENT_RegisterConfigUpdateFunc(file_var, parentSelection_CB, (void *) PARENT_FILE_CB); |
| // default parent |
| PARENT_RegisterConfigUpdateFunc(default_var, parentSelection_CB, (void *) PARENT_DEFAULT_CB); |
| // Retry time |
| PARENT_RegisterConfigUpdateFunc(retry_var, parentSelection_CB, (void *) PARENT_RETRY_CB); |
| // Enable |
| PARENT_RegisterConfigUpdateFunc(enable_var, parentSelection_CB, (void *) PARENT_ENABLE_CB); |
| |
| // Fail Threshold |
| PARENT_RegisterConfigUpdateFunc(threshold_var, parentSelection_CB, (void *) PARENT_THRESHOLD_CB); |
| |
| // DNS Parent Only |
| PARENT_RegisterConfigUpdateFunc(dns_parent_only_var, parentSelection_CB, (void *) PARENT_DNS_ONLY_CB); |
| } |
| |
| void |
| ParentConfig::reconfigure() |
| { |
| |
| char *default_val = NULL; |
| int retry_time = 30; |
| int enable = 0; |
| int fail_threshold; |
| int dns_parent_only; |
| |
| ParentConfigParams *params; |
| params = NEW(new ParentConfigParams); |
| |
| // Allocate parent table |
| params->ParentTable = NEW(new P_table(file_var, modulePrefix, &http_dest_tags)); |
| |
| // Handle default parent |
| PARENT_ReadConfigStringAlloc(default_val, default_var); |
| params->DefaultParent = createDefaultParent(default_val); |
| if (default_val) { |
| xfree(default_val); |
| } |
| // Handle parent timeout |
| PARENT_ReadConfigInteger(retry_time, retry_var); |
| params->ParentRetryTime = retry_time; |
| |
| // Handle parent enable |
| PARENT_ReadConfigInteger(enable, enable_var); |
| params->ParentEnable = enable; |
| |
| // Handle the fail threshold |
| PARENT_ReadConfigInteger(fail_threshold, threshold_var); |
| params->FailThreshold = fail_threshold; |
| |
| // Handle dns parent only |
| PARENT_ReadConfigInteger(dns_parent_only, dns_parent_only_var); |
| params->DNS_ParentOnly = dns_parent_only; |
| |
| m_id = configProcessor.set(m_id, params); |
| |
| if (is_debug_tag_set("parent_config")) { |
| ParentConfig::print(); |
| } |
| } |
| |
| // void ParentConfig::print |
| // |
| // Debugging function |
| // |
| void |
| ParentConfig::print() |
| { |
| ParentConfigParams *params = ParentConfig::acquire(); |
| |
| printf("Parent Selection Config\n"); |
| printf("\tEnabled %d\tRetryTime %d\tParent DNS Only %d\n", |
| params->ParentEnable, params->ParentRetryTime, params->DNS_ParentOnly); |
| if (params->DefaultParent == NULL) { |
| printf("\tNo Default Parent\n"); |
| } else { |
| printf("\tDefault Parent:\n"); |
| params->DefaultParent->Print(); |
| } |
| printf(" "); |
| params->ParentTable->Print(); |
| |
| ParentConfig::release(params); |
| } |
| |
| bool |
| ParentConfigParams::apiParentExists(HttpRequestData * rdata) |
| { |
| return (rdata->api_info && rdata->api_info->parent_proxy_name != NULL && rdata->api_info->parent_proxy_port > 0); |
| } |
| |
| bool |
| ParentConfigParams::parentExists(HttpRequestData * rdata) |
| { |
| ParentResult junk; |
| |
| findParent(rdata, &junk); |
| |
| if (junk.r == PARENT_SPECIFIED) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| void |
| ParentConfigParams::findParent(HttpRequestData * rdata, ParentResult * result, char *tag) |
| { |
| P_table *tablePtr = ParentTable; |
| ParentRecord *defaultPtr = DefaultParent; |
| ParentRecord *rec; |
| |
| ink_assert(result->r == PARENT_UNDEFINED); |
| |
| // Check to see if we are enabled |
| if (ParentEnable == 0) { |
| result->r = PARENT_DIRECT; |
| return; |
| } |
| // Initialize the result structure |
| result->rec = NULL; |
| result->epoch = tablePtr; |
| result->line_number = 0xffffffff; |
| result->wrap_around = false; |
| // if this variabel is not set, we have problems: the code in |
| // FindParent relies on the value of start_parent and when it is not |
| // initialized, the code in FindParent can get into an infinite loop! |
| result->start_parent = 0; |
| result->last_parent = 0; |
| |
| // Check to see if the parent was set through the |
| // api |
| if (apiParentExists(rdata)) { |
| |
| result->r = PARENT_SPECIFIED; |
| result->hostname = rdata->api_info->parent_proxy_name; |
| result->port = rdata->api_info->parent_proxy_port; |
| result->rec = extApiRecord; |
| result->epoch = NULL; |
| result->start_parent = 0; |
| result->last_parent = 0; |
| |
| Debug("parent_select", "Result for %s was API set parent %s:%d", rdata->get_host(), result->hostname, result->port); |
| } |
| |
| rdata->tag = tag; |
| tablePtr->Match(rdata, result); |
| rec = result->rec; |
| |
| if (rec == NULL) { |
| // No parents were found |
| // |
| // If there is a default parent, use it |
| if (defaultPtr != NULL) { |
| rec = result->rec = defaultPtr; |
| } else { |
| result->r = PARENT_DIRECT; |
| Debug("cdn", "Returning PARENT_DIRECT (no parents were found)"); |
| return; |
| } |
| } |
| // Loop through the set of parents to see if any are |
| // available |
| Debug("cdn", "Calling FindParent from findParent"); |
| |
| // Bug INKqa08251: |
| // If a parent proxy is set by the API, |
| // no need to call FindParent() |
| if (rec != extApiRecord) |
| rec->FindParent(true, result, rdata, this); |
| |
| if (is_debug_tag_set("parent_select") || is_debug_tag_set("cdn")) { |
| |
| switch (result->r) { |
| case PARENT_UNDEFINED: |
| Debug("cdn", "PARENT_UNDEFINED"); |
| break; |
| case PARENT_FAIL: |
| Debug("cdn", "PARENT_FAIL"); |
| break; |
| case PARENT_DIRECT: |
| Debug("cdn", "PARENT_DIRECT"); |
| break; |
| case PARENT_SPECIFIED: |
| Debug("cdn", "PARENT_SPECIFIED"); |
| break; |
| default: |
| // Handled here: |
| // PARENT_AGENT |
| break; |
| } |
| |
| const char *host = rdata->get_host(); |
| |
| switch (result->r) { |
| case PARENT_UNDEFINED: |
| case PARENT_FAIL: |
| case PARENT_DIRECT: |
| Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->r]); |
| break; |
| case PARENT_SPECIFIED: |
| Debug("parent_select", "sizeof ParentResult = %d", sizeof(ParentResult)); |
| Debug("parent_select", "Result for %s was parent %s:%d", host, result->hostname, result->port); |
| break; |
| default: |
| // Handled here: |
| // PARENT_AGENT |
| break; |
| } |
| } |
| } |
| |
| |
| void |
| ParentConfigParams::recordRetrySuccess(ParentResult * result) |
| { |
| |
| pRecord *pRec; |
| |
| // Make sure that we are being called back with with a |
| // result structure with a parent that is being retried |
| ink_release_assert(result->retry == true); |
| ink_assert(result->r == PARENT_SPECIFIED); |
| if (result->r != PARENT_SPECIFIED) { |
| return; |
| } |
| // If we were set through the API we currently have not failover |
| // so just return fail |
| if (result->rec == extApiRecord) { |
| ink_assert(0); |
| return; |
| } |
| |
| ink_assert((int) (result->last_parent) < result->rec->num_parents); |
| pRec = result->rec->parents + result->last_parent; |
| |
| ink_atomic_swap(&pRec->failedAt, 0); |
| int old_count = ink_atomic_swap(&pRec->failCount, 0); |
| |
| if (old_count > 0) { |
| Note("http parent proxy %s:%d restored", pRec->hostname, pRec->port); |
| } |
| } |
| |
| void |
| ParentConfigParams::markParentDown(ParentResult * result) |
| { |
| |
| time_t now; |
| pRecord *pRec; |
| int new_fail_count = 0; |
| |
| // Make sure that we are being called back with with a |
| // result structure with a parent |
| ink_assert(result->r == PARENT_SPECIFIED); |
| if (result->r != PARENT_SPECIFIED) { |
| return; |
| } |
| // If we were set through the API we currently have not failover |
| // so just return fail |
| if (result->rec == extApiRecord) { |
| return; |
| } |
| |
| ink_assert((int) (result->last_parent) < result->rec->num_parents); |
| pRec = result->rec->parents + result->last_parent; |
| |
| // If the parent has already been marked down, just increment |
| // the failure count. If this is the first mark down on a |
| // parent we need to both set the failure time and set |
| // count to one. It's possible for the count and time get out |
| // sync due there being no locks. Therefore the code should |
| // handle this condition. If this was the result of a retry, we |
| // must update move the failedAt timestamp to now so that we continue |
| // negative cache the parent |
| if (pRec->failedAt == 0 || result->retry == true) { |
| |
| // Reread the current time. We want this to be accurate since |
| // it relates to how long the parent has been down. |
| now = time(NULL); |
| |
| // Mark the parent as down |
| ink_atomic_swap(&pRec->failedAt, now); |
| |
| // If this is clean mark down and not a failed retry, we |
| // must set the count to reflect this |
| if (result->retry == false) { |
| new_fail_count = pRec->failCount = 1; |
| } |
| |
| Debug("parent_select", "Parent %s marked as down %s:%d", |
| (result->retry) ? "retry" : "initially", pRec->hostname, pRec->port); |
| |
| } else { |
| int old_count = ink_atomic_increment(&pRec->failCount, 1); |
| Debug("parent_select", "Parent fail count increased to %d for %s:%d", old_count + 1, pRec->hostname, pRec->port); |
| new_fail_count = old_count + 1; |
| } |
| |
| if (new_fail_count > 0 && new_fail_count == FailThreshold) { |
| Note("http parent proxy %s:%d marked down", pRec->hostname, pRec->port); |
| } |
| } |
| |
| void |
| ParentConfigParams::nextParent(HttpRequestData * rdata, ParentResult * result) |
| { |
| P_table *tablePtr = ParentTable; |
| |
| // Make sure that we are being called back with a |
| // result structure with a parent |
| ink_assert(result->r == PARENT_SPECIFIED); |
| if (result->r != PARENT_SPECIFIED) { |
| result->r = PARENT_FAIL; |
| return; |
| } |
| // If we were set through the API we currently have not failover |
| // so just return fail |
| if (result->rec == extApiRecord) { |
| Debug("parent_select", "Retry result for %s was %s", rdata->get_host(), ParentResultStr[result->r]); |
| result->r = PARENT_FAIL; |
| return; |
| } |
| // The epoch pointer is a legacy from the time when the tables |
| // would be swapped and deleted in the future. I'm using the |
| // pointer now to ensure that the ParentConfigParams structure |
| // is properly used. The table should never change out from |
| // under the a http transaction |
| ink_release_assert(tablePtr == result->epoch); |
| |
| // Find the next parent in the array |
| Debug("cdn", "Calling FindParent from nextParent"); |
| result->rec->FindParent(false, result, rdata, this); |
| |
| switch (result->r) { |
| case PARENT_UNDEFINED: |
| Debug("cdn", "PARENT_UNDEFINED"); |
| break; |
| case PARENT_FAIL: |
| Debug("cdn", "PARENT_FAIL"); |
| break; |
| case PARENT_DIRECT: |
| Debug("cdn", "PARENT_DIRECT"); |
| break; |
| case PARENT_SPECIFIED: |
| Debug("cdn", "PARENT_SPECIFIED"); |
| break; |
| default: |
| // Handled here: |
| // PARENT_AGENT |
| break; |
| } |
| |
| if (is_debug_tag_set("parent_select")) { |
| const char *host = rdata->get_host(); |
| |
| switch (result->r) { |
| case PARENT_UNDEFINED: |
| case PARENT_FAIL: |
| case PARENT_DIRECT: |
| Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->r]); |
| break; |
| case PARENT_SPECIFIED: |
| Debug("parent_select", "Retry result for %s was parent %s:%d", host, result->hostname, result->port); |
| break; |
| default: |
| // Handled here: |
| // PARENT_AGENT |
| break; |
| } |
| } |
| } |
| |
| // |
| // End API functions |
| // |
| |
| void |
| ParentRecord::FindParent(bool first_call, ParentResult * result, RD * rdata, ParentConfigParams * config) |
| { |
| Debug("cdn", "Entering FindParent (the inner loop)"); |
| int cur_index = 0; |
| bool parentUp = false; |
| bool parentRetry = false; |
| bool bypass_ok = (go_direct == true && config->DNS_ParentOnly == 0); |
| |
| HttpRequestData *request_info = (HttpRequestData *) rdata; |
| |
| ink_assert(num_parents > 0 || go_direct == true); |
| |
| if (first_call == true) { |
| if (parents == NULL) { |
| // We should only get into this state if |
| // if we are supposed to go dirrect |
| ink_assert(go_direct == true); |
| goto NO_PARENTS; |
| } else if (round_robin == true) { |
| cur_index = ink_atomic_increment((int32_t *) & rr_next, 1); |
| cur_index = result->start_parent = cur_index % num_parents; |
| } else { |
| switch (round_robin) { |
| case P_STRICT_ROUND_ROBIN: |
| cur_index = ink_atomic_increment((int32_t *) & rr_next, 1); |
| cur_index = cur_index % num_parents; |
| break; |
| case P_HASH_ROUND_ROBIN: |
| // INKqa12817 - make sure to convert to host byte order |
| cur_index = ntohl(rdata->get_client_ip()) % num_parents; |
| break; |
| case P_NO_ROUND_ROBIN: |
| cur_index = result->start_parent = 0; |
| break; |
| default: |
| ink_release_assert(0); |
| } |
| } |
| } else { |
| |
| // Move to next parent due to failure |
| cur_index = (result->last_parent + 1) % num_parents; |
| |
| // Check to see if we have wrapped around |
| if ((unsigned int) cur_index == result->start_parent) { |
| // We've wrapped around so bypass if we can |
| if (bypass_ok == true) { |
| goto NO_PARENTS; |
| } else { |
| // Bypass disabled so keep trying, ignoring whether we think |
| // a parent is down or not |
| FORCE_WRAP_AROUND: |
| result->wrap_around = true; |
| } |
| } |
| } |
| |
| // Loop through the array of parent seeing if any are up or |
| // should be retried |
| do { |
| // DNS ParentOnly inhibits bypassing the parent so always return that t |
| if ((parents[cur_index].failedAt == 0) || (parents[cur_index].failCount < config->FailThreshold)) { |
| Debug("parent_select", "config->FailThreshold = %d", config->FailThreshold); |
| Debug("parent_select", "Selecting a down parent due to little failCount" |
| "(faileAt: %u failCount: %d)", parents[cur_index].failedAt, parents[cur_index].failCount); |
| parentUp = true; |
| } else { |
| if ((result->wrap_around) || ((parents[cur_index].failedAt + config->ParentRetryTime) < request_info->xact_start)) { |
| Debug("parent_select", "Parent[%d].failedAt = %u, retry = %u,xact_start = %u but wrap = %d", cur_index, |
| parents[cur_index].failedAt, config->ParentRetryTime, request_info->xact_start, result->wrap_around); |
| // Reuse the parent |
| parentUp = true; |
| parentRetry = true; |
| Debug("parent_select", "Parent marked for retry %s:%d", parents[cur_index].hostname, parents[cur_index].port); |
| |
| } else { |
| parentUp = false; |
| } |
| } |
| |
| if (parentUp == true) { |
| result->r = PARENT_SPECIFIED; |
| result->hostname = parents[cur_index].hostname; |
| result->port = parents[cur_index].port; |
| result->last_parent = cur_index; |
| result->retry = parentRetry; |
| ink_assert(result->hostname != NULL); |
| ink_assert(result->port != 0); |
| Debug("parent_select", "Chosen parent = %s.%d", result->hostname, result->port); |
| return; |
| } |
| |
| cur_index = (cur_index + 1) % num_parents; |
| |
| } while ((unsigned int) cur_index != result->start_parent); |
| |
| // We can't bypass so retry, taking any parent that we can |
| if (bypass_ok == false) { |
| goto FORCE_WRAP_AROUND; |
| } |
| |
| NO_PARENTS: |
| |
| // Could not find a parent |
| if (this->go_direct == true) { |
| result->r = PARENT_DIRECT; |
| } else { |
| result->r = PARENT_FAIL; |
| } |
| |
| result->hostname = NULL; |
| result->port = 0; |
| } |
| |
| // const char* ParentRecord::ProcessParents(char* val) |
| // |
| // Reads in the value of a "round-robin" or "order" |
| // directive and parses out the individual parents |
| // allocates and builds the this->parents array |
| // |
| // Returns NULL on success and a static error string |
| // on failure |
| // |
| const char * |
| ParentRecord::ProcessParents(char *val) |
| { |
| Tokenizer pTok(",; \t\r"); |
| int numTok; |
| const char *current; |
| int port; |
| char *tmp; |
| const char *errPtr; |
| |
| if (parents != NULL) { |
| return "Can not specify more than one set of parents"; |
| } |
| |
| numTok = pTok.Initialize(val, SHARE_TOKS); |
| |
| if (numTok == 0) { |
| return "No parents specified"; |
| } |
| // Allocate the parents array |
| this->parents = (pRecord *) xmalloc(sizeof(pRecord) * numTok); |
| |
| // Loop through the set of parents specified |
| // |
| for (int i = 0; i < numTok; i++) { |
| current = pTok[i]; |
| |
| // Find the parent port |
| tmp = (char *) strchr(current, ':'); |
| |
| if (tmp == NULL) { |
| errPtr = "No parent port specified"; |
| goto MERROR; |
| } |
| // Read the parent port |
| //coverity[secure_coding] |
| if (sscanf(tmp + 1, "%d", &port) != 1) { |
| errPtr = "Malformed parent port"; |
| goto MERROR; |
| } |
| // Make sure that is no garbage beyond the parent |
| // port |
| char *scan = tmp + 1; |
| for (; *scan != '\0' && ParseRules::is_digit(*scan); scan++); |
| for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++); |
| if (*scan != '\0') { |
| errPtr = "Garbage trailing entry or invalid separator"; |
| goto MERROR; |
| } |
| // Check to make sure that the string will fit in the |
| // pRecord |
| if (tmp - current > DOMAIN_NAME_MAX) { |
| errPtr = "Parent hostname is too long"; |
| goto MERROR; |
| } else if (tmp - current == 0) { |
| errPtr = "Parent string is emtpy"; |
| goto MERROR; |
| } |
| // Update the pRecords |
| memcpy(this->parents[i].hostname, current, tmp - current); |
| this->parents[i].hostname[tmp - current] = '\0'; |
| this->parents[i].port = port; |
| this->parents[i].failedAt = 0; |
| this->parents[i].scheme = scheme; |
| } |
| |
| num_parents = numTok; |
| return NULL; |
| |
| MERROR: |
| if (parents != NULL) { |
| xfree(parents); |
| parents = NULL; |
| } |
| return errPtr; |
| } |
| |
| // bool ParentRecord::DefaultInit(char* val) |
| // |
| // Creates the record for a default parent proxy rule |
| /// established by a config variable |
| // |
| // matcher_line* line_info - contains the value of |
| // proxy.config.http.parent_proxies |
| // |
| // Returns true on success and false on failure |
| // |
| bool |
| ParentRecord::DefaultInit(char *val) |
| { |
| const char *errPtr; |
| char *errBuf; |
| bool alarmAlready = false; |
| |
| this->go_direct = true; |
| this->round_robin = P_NO_ROUND_ROBIN; |
| this->scheme = NULL; |
| errPtr = ProcessParents(val); |
| |
| if (errPtr != NULL) { |
| errBuf = (char *) xmalloc(1024); |
| snprintf(errBuf, 1024, "%s %s for default parent proxy", modulePrefix, errPtr); |
| SignalError(errBuf, alarmAlready); |
| xfree(errBuf); |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| |
| // char* ParentRecord::Init(matcher_line* line_info) |
| // |
| // matcher_line* line_info - contains parsed label/value |
| // pairs of the current cache.config line |
| // |
| // Returns NULL if everything is OK |
| // Otherwise, returns an error string that the caller MUST |
| // DEALLOCATE with free() |
| // |
| char * |
| ParentRecord::Init(matcher_line * line_info) |
| { |
| |
| const char *errPtr = NULL; |
| char *errBuf; |
| const int errBufLen = 1024; |
| const char *tmp; |
| char *label; |
| char *val; |
| bool used = false; |
| |
| this->line_num = line_info->line_num; |
| this->scheme = NULL; |
| |
| for (int i = 0; i < MATCHER_MAX_TOKENS; i++) { |
| used = false; |
| label = line_info->line[0][i]; |
| val = line_info->line[1][i]; |
| |
| if (label == NULL) { |
| continue; |
| } |
| |
| if (strcasecmp(label, "round_robin") == 0) { |
| if (strcasecmp(val, "true") == 0) { |
| round_robin = P_HASH_ROUND_ROBIN; |
| } else if (strcasecmp(val, "strict") == 0) { |
| round_robin = P_STRICT_ROUND_ROBIN; |
| } else if (strcasecmp(val, "false") == 0) { |
| round_robin = P_NO_ROUND_ROBIN; |
| } else { |
| round_robin = P_NO_ROUND_ROBIN; |
| errPtr = "invalid argument to round_robin directive"; |
| } |
| used = true; |
| } else if (strcasecmp(label, "parent") == 0) { |
| errPtr = ProcessParents(val); |
| used = true; |
| } else if (strcasecmp(label, "go_direct") == 0) { |
| if (strcasecmp(val, "false") == 0) { |
| go_direct = false; |
| } else if (strcasecmp(val, "true") != 0) { |
| errPtr = "invalid argument to go_direct directive"; |
| } else { |
| go_direct = true; |
| } |
| used = true; |
| } |
| // Report errors generated by ProcessParents(); |
| if (errPtr != NULL) { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s %s at line %d", modulePrefix, errPtr, line_num); |
| return errBuf; |
| } |
| |
| if (used == true) { |
| // Consume the label/value pair we used |
| line_info->line[0][i] = NULL; |
| line_info->num_el--; |
| } |
| } |
| |
| if (this->parents == NULL && go_direct == false) { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s No parent specified in parent.config at line %d", modulePrefix, line_num); |
| return errBuf; |
| } |
| // Process any modifiers to the directive, if they exist |
| if (line_info->num_el > 0) { |
| tmp = ProcessModifiers(line_info); |
| |
| if (tmp != NULL) { |
| errBuf = (char *) xmalloc(errBufLen * sizeof(char)); |
| snprintf(errBuf, errBufLen, "%s %s at line %d in parent.config", modulePrefix, tmp, line_num); |
| return errBuf; |
| } |
| // record SCHEME modifier if present. |
| // NULL if not present |
| this->scheme = (const char *) getModElem(MOD_SCHEME); |
| if (this->scheme != NULL) { |
| // update parent entries' schemes |
| for (int j = 0; j < num_parents; j++) { |
| this->parents[j].scheme = this->scheme; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| // void ParentRecord::UpdateMatch(ParentResult* result, RD* rdata); |
| // |
| // Updates the record ptr in result if the this element |
| // appears later in the file |
| // |
| void |
| ParentRecord::UpdateMatch(ParentResult * result, RD * rdata) |
| { |
| |
| if (this->CheckForMatch((HttpRequestData *) rdata, result->line_number) == true) { |
| result->rec = this; |
| result->line_number = this->line_num; |
| |
| Debug("parent_select", "Matched with 0x%x parent node from line %d", this, this->line_num); |
| } |
| } |
| |
| ParentRecord::~ParentRecord() |
| { |
| xfree(parents); |
| } |
| |
| void |
| ParentRecord::Print() |
| { |
| printf("\t\t"); |
| for (int i = 0; i < num_parents; i++) { |
| printf(" %s:%d ", parents[i].hostname, parents[i].port); |
| } |
| printf(" rr=%s direct=%s\n", ParentRRStr[round_robin], (go_direct == true) ? "true" : "false"); |
| } |
| |
| |
| // struct PA_UpdateContinuation |
| // |
| // Used to handle parent.conf or default parent updates after the |
| // manager signals a change |
| // |
| struct PA_UpdateContinuation: public Continuation |
| { |
| |
| int handle_event(int event, void *data) |
| { |
| NOWARN_UNUSED(event); |
| NOWARN_UNUSED(data); |
| ParentConfig::reconfigure(); |
| delete this; |
| return EVENT_DONE; |
| |
| } |
| |
| PA_UpdateContinuation(ProxyMutex * m):Continuation(m) |
| { |
| SET_HANDLER(&PA_UpdateContinuation::handle_event); |
| } |
| }; |
| |
| |
| // ParentRecord* createDefaultParent(char* val) |
| // |
| // Atttemtps to allocate and init new ParentRecord |
| // for a default parent |
| // |
| // Returns a pointer to the new record on success |
| // and NULL on failure |
| // |
| ParentRecord * |
| createDefaultParent(char *val) |
| { |
| ParentRecord *newRec; |
| |
| if (val == NULL || *val == '\0') { |
| return NULL; |
| } |
| |
| newRec = NEW(new ParentRecord); |
| if (newRec->DefaultInit(val) == true) { |
| return newRec; |
| } else { |
| delete newRec; |
| return NULL; |
| } |
| } |
| |
| // parentSelection_CB(const char *name, RecDataT data_type, |
| // RecData data, void *cookie)) |
| // |
| // Called by manager to notify of config changes |
| // |
| int |
| parentSelection_CB(const char *name, RecDataT data_type, RecData data, void *cookie) |
| { |
| NOWARN_UNUSED(name); |
| NOWARN_UNUSED(data_type); |
| NOWARN_UNUSED(data); |
| ParentCB_t type = (ParentCB_t) (long) cookie; |
| |
| switch (type) { |
| case PARENT_FILE_CB: |
| case PARENT_DEFAULT_CB: |
| case PARENT_RETRY_CB: |
| case PARENT_ENABLE_CB: |
| case PARENT_THRESHOLD_CB: |
| case PARENT_DNS_ONLY_CB: |
| eventProcessor.schedule_imm(NEW(new PA_UpdateContinuation(reconfig_mutex)), ET_CACHE); |
| break; |
| default: |
| ink_assert(0); |
| } |
| |
| return 0; |
| } |
| |
| // |
| //ParentConfig equivalent functions for SocksServerConfig |
| // |
| |
| int |
| SocksServerConfig::m_id = 0; |
| static ProxyMutexPtr |
| socks_server_reconfig_mutex = NULL; |
| |
| ParentConfigParams * |
| SocksServerConfig::acquire() |
| { |
| return (ParentConfigParams *) configProcessor.get(SocksServerConfig::m_id); |
| } |
| |
| void |
| SocksServerConfig::release(ParentConfigParams * params) |
| { |
| configProcessor.release(SocksServerConfig::m_id, params); |
| } |
| |
| void |
| SocksServerConfig::startup() |
| { |
| socks_server_reconfig_mutex = new_ProxyMutex(); |
| |
| // Load the initial configuration |
| reconfigure(); |
| |
| /* Handle update functions later. Socks does not yet support config update */ |
| } |
| |
| static int |
| setup_socks_servers(ParentRecord * rec_arr, int len) |
| { |
| /* This changes hostnames into ip addresses and sets go_direct to false */ |
| |
| for (int j = 0; j < len; j++) { |
| rec_arr[j].go_direct = false; |
| |
| pRecord *pr = rec_arr[j].parents; |
| int n_parents = rec_arr[j].num_parents; |
| |
| for (int i = 0; i < n_parents; i++) { |
| uint32_t bad_ip = (uint32_t) - 1; |
| uint8_t *ip; |
| |
| ink_gethostbyname_r_data data; |
| struct hostent *ent = ink_gethostbyname_r(pr[i].hostname, &data); |
| |
| if (ent) { |
| ip = (uint8_t *) ent->h_addr_list[0]; |
| } else { |
| Warning("Could not resolve socks server name \"%s\". " "Please correct it", pr[i].hostname); |
| ip = (uint8_t *) & bad_ip; |
| } |
| snprintf(pr[i].hostname, DOMAIN_NAME_MAX + 1, "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]); |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| void |
| SocksServerConfig::reconfigure() |
| { |
| |
| char *default_val = NULL; |
| int retry_time = 30; |
| int fail_threshold; |
| |
| ParentConfigParams *params; |
| params = NEW(new ParentConfigParams); |
| |
| // Allocate parent table |
| params->ParentTable = NEW(new P_table("proxy.config.socks.socks_config_file", "[Socks Server Selection]", |
| &socks_server_tags)); |
| |
| // Handle default parent |
| PARENT_ReadConfigStringAlloc(default_val, "proxy.config.socks.default_servers"); |
| params->DefaultParent = createDefaultParent(default_val); |
| if (default_val) { |
| xfree(default_val); |
| } |
| |
| if (params->DefaultParent) |
| setup_socks_servers(params->DefaultParent, 1); |
| if (params->ParentTable->ipMatch) |
| setup_socks_servers(params->ParentTable->ipMatch->data_array, params->ParentTable->ipMatch->array_len); |
| |
| // Handle parent timeout |
| PARENT_ReadConfigInteger(retry_time, "proxy.config.socks.server_retry_time"); |
| params->ParentRetryTime = retry_time; |
| |
| // Handle parent enable |
| // enable is always true for use. We will come here only if socks is enabled |
| params->ParentEnable = 1; |
| |
| // Handle the fail threshold |
| PARENT_ReadConfigInteger(fail_threshold, "proxy.config.socks.server_fail_threshold"); |
| params->FailThreshold = fail_threshold; |
| |
| // Handle dns parent only |
| //PARENT_ReadConfigInteger(dns_parent_only, dns_parent_only_var); |
| params->DNS_ParentOnly = 0; |
| |
| m_id = configProcessor.set(m_id, params); |
| |
| if (is_debug_tag_set("parent_config")) { |
| SocksServerConfig::print(); |
| } |
| } |
| |
| void |
| SocksServerConfig::print() |
| { |
| ParentConfigParams *params = SocksServerConfig::acquire(); |
| |
| printf("Parent Selection Config for Socks Server\n"); |
| printf("\tEnabled %d\tRetryTime %d\tParent DNS Only %d\n", |
| params->ParentEnable, params->ParentRetryTime, params->DNS_ParentOnly); |
| if (params->DefaultParent == NULL) { |
| printf("\tNo Default Parent\n"); |
| } else { |
| printf("\tDefault Parent:\n"); |
| params->DefaultParent->Print(); |
| } |
| printf(" "); |
| params->ParentTable->Print(); |
| |
| SocksServerConfig::release(params); |
| } |
| |
| #define TEST_FAIL(str) { \ |
| printf("%d: %s\n", test_id,str);\ |
| err= REGRESSION_TEST_FAILED;\ |
| } |
| |
| void |
| request_to_data(HttpRequestData * req, ip_addr_t srcip, ip_addr_t dstip, const char *str) |
| { |
| HTTPParser parser; |
| |
| req->src_ip = srcip; |
| req->dest_ip = dstip; |
| req->hdr = NEW(new HTTPHdr); |
| |
| http_parser_init(&parser); |
| |
| req->hdr->parse_req(&parser, &str, str + strlen(str), true); |
| |
| http_parser_clear(&parser); |
| } |
| |
| /* |
| REGRESSION_TEST(ParentResult) { |
| bool err = REGRESSION_TEST_PASSED; |
| int test_id = 0; |
| |
| ParentConfigParams test; |
| HttpRequestData rd; |
| ParentEnable = 1; |
| test.ParentTable = new P_table("","ParentSelection test",&http_dest_tags,ALLOW_HOST_TABLE | ALLOW_REGEX_TABLE | ALLOW_IP_TABLE | DONT_BUILD_TABLE); |
| request_to_data(&rd,IP(209,3,24,36),0, |
| "GET http://www.inktomi.com HTTP/1.0\r\n" |
| "\r\n"); |
| |
| // request for inktomi.com matches inktomi.com |
| test_id++; |
| { |
| test.ParentTable->BuildTableFromString( |
| "dest_domain=inktomi.com parent=foobar:8080\n" |
| ); |
| { |
| ParentResult r; |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_SPECIFIED) { |
| TEST_FAIL("Request for www.inktomi.com matches inktomi.com"); |
| } |
| test.nextParent(&rd,&r); |
| if (r.r != PARENT_FAIL) { |
| TEST_FAIL("next parent request should go direct."); |
| } |
| } |
| { |
| ParentResult r; |
| rd.xact_start = time(NULL); |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_FAIL) { |
| TEST_FAIL("firstParent request should return FAIL since retry timeout has not been reached."); |
| } |
| } |
| { |
| ParentResult r; |
| rd.xact_start = time(NULL) + ParentRetryTime+2; |
| //printf("third request, xact_start = %d\n",rd.xact_start); |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_SPECIFIED) { |
| TEST_FAIL("after retry timeout, firstParent request should return parent."); |
| } |
| test.nextParent(&rd,&r); |
| if (r.r != PARENT_FAIL) { |
| TEST_FAIL("next parent request should go direct(2)."); |
| } |
| } |
| } |
| |
| { |
| test.ParentTable->BuildTableFromString( |
| "dest_domain=com parent=foobar:8080\n" |
| ); |
| { |
| ParentResult r; |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_SPECIFIED) { |
| TEST_FAIL("request for www.inktomi.com matches .com"); |
| } |
| } |
| } |
| |
| { |
| test.ParentTable->BuildTableFromString( |
| "dest_domain=edu parent=foobar:8080\n" |
| ); |
| { |
| ParentResult r; |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_DIRECT) { |
| TEST_FAIL("request for www.inktomi.com doesn't match .edu"); |
| } |
| } |
| } |
| |
| { |
| test.ParentTable->BuildTableFromString( |
| "dest_host=www.inktomi.com parent=foobar:8080\n" |
| ); |
| { |
| ParentResult r; |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_SPECIFIED) { |
| TEST_FAIL("request for www.inktomi.com matches host www.inktomi.com"); |
| } |
| } |
| } |
| |
| { |
| test.ParentTable->BuildTableFromString( |
| "dest_ip=209.1.12.219 parent=foobar:8080\n" |
| ); |
| { |
| ParentResult r; |
| test.findParent(&rd,&r); |
| |
| if (r.r != PARENT_SPECIFIED) { |
| TEST_FAIL("request for www.inktomi.com matches 209.1.12.219"); |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| */ |
| |
| |
| #define IP(a,b,c,d) htonl((a) << 24 | (b) << 16 | (c) << 8 | (d)) |
| |
| static int passes; |
| static int fails; |
| |
| // Parenting Tests |
| EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION) (RegressionTest * t, int intensity_level, int *pstatus) |
| { |
| NOWARN_UNUSED(t); |
| NOWARN_UNUSED(intensity_level); |
| // first, set everything up |
| *pstatus = REGRESSION_TEST_INPROGRESS; |
| ParentConfig config; |
| ParentConfigParams *params = new ParentConfigParams(); |
| params->FailThreshold = 1; |
| params->ParentRetryTime = 5; |
| passes = fails = 0; |
| config.startup(); |
| params->ParentEnable = true; |
| char tbl[2048]; |
| #define T(x) strncat(tbl,x, sizeof(tbl)); |
| #define REBUILD params->ParentTable = new P_table("", "ParentSelection Unit Test Table", &http_dest_tags, ALLOW_HOST_TABLE | ALLOW_REGEX_TABLE | ALLOW_IP_TABLE | DONT_BUILD_TABLE); params->ParentTable->BuildTableFromString(tbl); |
| HttpRequestData *request = NULL; |
| ParentResult *result = NULL; |
| #define REINIT delete request; delete result; request = new HttpRequestData(); result = new ParentResult(); if (!result || !request) { (void)printf("Allocation failed\n"); return; } |
| #define ST(x) printf ("*** TEST %d *** STARTING ***\n", x); |
| #define RE(x,y) if (x) { printf("*** TEST %d *** PASSED ***\n", y); passes ++; } else { printf("*** TEST %d *** FAILED *** FAILED *** FAILED ***\n", y); fails++; } |
| #define FP params->findParent(request, result); |
| |
| // Test 1 |
| tbl[0] = '\0'; |
| ST(1) |
| T("dest_domain=. parent=red:37412,orange:37412,yellow:37412 round_robin=strict\n") |
| REBUILD int c, red = 0, orange = 0, yellow = 0; |
| for (c = 0; c < 21; c++) { |
| REINIT br(request, "fruit_basket.net"); |
| FP red += verify(result, PARENT_SPECIFIED, "red", 37412); |
| orange += verify(result, PARENT_SPECIFIED, "orange", 37412); |
| yellow += verify(result, PARENT_SPECIFIED, "yellow", 37412); |
| } |
| RE(((red == 7) && (orange == 7) && (yellow == 7)), 1) |
| // Test 2 |
| ST(2) |
| tbl[0] = '\0'; |
| T("dest_domain=. parent=green:4325,blue:4325,indigo:4325,violet:4325 round_robin=false\n") |
| REBUILD int g = 0, b = 0, i = 0, v = 0; |
| for (c = 0; c < 17; c++) { |
| REINIT br(request, "fruit_basket.net"); |
| FP g += verify(result, PARENT_SPECIFIED, "green", 4325); |
| b += verify(result, PARENT_SPECIFIED, "blue", 4325); |
| i += verify(result, PARENT_SPECIFIED, "indigo", 4325); |
| v += verify(result, PARENT_SPECIFIED, "violet", 4325); |
| } |
| RE((((g == 17) && !b && !i && !v) || (!g && (b == 17) && !i && !v) || (!g && !b && (i == 17) && !v) || |
| (!g && !b && !i && (v == 17))), 2) |
| // Test 3 - 6 Parenting Table |
| tbl[0] = '\0'; |
| T("dest_ip=209.131.62.14 parent=cat:37,dog:24 round_robin=strict\n") /* L1 */ |
| T("dest_host=www.pilot.net parent=pilot_net:80\n") /* L2 */ |
| T("url_regex=snoopy parent=odie:80,garfield:80 round_robin=true\n") /* L3 */ |
| T("dest_domain=i.am parent=amy:80,katie:80,carissa:771 round_robin=false\n") /* L4 */ |
| T("dest_domain=microsoft.net time=03:00-22:10 parent=zoo.net:341\n") /* L5 */ |
| T("dest_domain=microsoft.net time=0:00-02:59 parent=zoo.net:347\n") /* L6 */ |
| T("dest_domain=microsoft.net time=22:11-23:59 parent=zoo.edu:111\n") /* L7 */ |
| T("dest_domain=imac.net port=819 parent=genie:80 round_robin=strict\n") /* L8 */ |
| T("dest_ip=172.34.61.211 port=3142 parent=orangina:80 go_direct=false\n") /* L9 */ |
| T("url_regex=miffy prefix=furry/rabbit parent=nintje:80 go_direct=false\n") /* L10 */ |
| T("url_regex=kitty suffix=tif parent=hello:80 round_robin=strict go_direct=false\n") /* L11 */ |
| T("url_regex=cyclops method=get parent=turkey:80\n") /* L12 */ |
| T("url_regex=cyclops method=post parent=club:80\n") /* L13 */ |
| T("url_regex=cyclops method=put parent=sandwich:80\n") /* L14 */ |
| T("url_regex=cyclops method=trace parent=mayo:80\n") /* L15 */ |
| T("dest_host=pluto scheme=HTTP parent=strategy:80\n") /* L16 */ |
| REBUILD |
| // Test 3 |
| ST(3) REINIT br(request, "numeric_host", IP(209, 131, 62, 14)); |
| FP RE(verify(result, PARENT_SPECIFIED, "cat", 37) + verify(result, PARENT_SPECIFIED, "dog", 24), 3) |
| // Test 4 |
| ST(4) REINIT br(request, "www.pilot.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "pilot_net", 80), 4) |
| // Test 5 |
| ST(5) REINIT br(request, "www.snoopy.net"); |
| const char *snoopy_dog = "http://www.snoopy.com/"; |
| request->hdr->url_set(snoopy_dog, strlen(snoopy_dog)); |
| FP RE(verify(result, PARENT_SPECIFIED, "odie", 80) + verify(result, PARENT_SPECIFIED, "garfield", 80), 5) |
| // Test 6 |
| ST(6) REINIT br(request, "a.rabbit.i.am"); |
| FP RE(verify(result, PARENT_SPECIFIED, "amy", 80) + |
| verify(result, PARENT_SPECIFIED, "katie", 80) + verify(result, PARENT_SPECIFIED, "carissa", 771), 6) |
| // Test 6+ BUGBUG needs to be fixed |
| // ST(7) REINIT |
| // br(request, "www.microsoft.net"); |
| // FP RE( verify(result,PARENT_SPECIFIED,"zoo.net",341) + |
| // verify(result,PARENT_SPECIFIED,"zoo.net",347) + |
| // verify(result,PARENT_SPECIFIED,"zoo.edu",111) ,7) |
| // Test 6++ BUGBUG needs to be fixed |
| // ST(7) REINIT |
| // br(request, "snow.imac.net:2020"); |
| // FP RE(verify(result,PARENT_DIRECT,0,0),7) |
| // Test 6+++ BUGBUG needs to be fixed |
| // ST(8) REINIT |
| // br(request, "snow.imac.net:819"); |
| // URL* u = new URL(); |
| // char* r = "http://snow.imac.net:819/"; |
| // u->create(0); |
| // u->parse(r,strlen(r)); |
| // u->port_set(819); |
| // request->hdr->url_set(u); |
| // ink_assert(request->hdr->url_get()->port_get() == 819); |
| // printf("url: %s\n",request->hdr->url_get()->string_get(0)); |
| // FP RE(verify(result,PARENT_SPECIFIED,"genie",80),8) |
| // Test 7 - N Parent Table |
| tbl[0] = '\0'; |
| T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=strict go_direct=true\n") |
| REBUILD |
| // Test 7 |
| ST(7) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 7) |
| params->markParentDown(result); |
| |
| // Test 8 |
| ST(8) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 8) |
| // Test 9 |
| ST(9) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 9) |
| // Test 10 |
| ST(10) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 10) |
| // restart the loop |
| // Test 11 |
| ST(11) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 11) |
| // Test 12 |
| ST(12) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 12) |
| // Test 13 |
| ST(13) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 13) |
| // Test 14 |
| ST(14) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 14) |
| params->markParentDown(result); |
| |
| // restart the loop |
| |
| // Test 15 |
| ST(15) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 15) |
| // Test 16 |
| ST(16) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 16) |
| // Test 17 |
| ST(17) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 17) |
| // Test 18 |
| ST(18) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 18) |
| // restart the loop |
| // Test 19 |
| ST(19) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 19) |
| // Test 20 |
| ST(20) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 20) |
| // Test 21 |
| ST(21) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "furry", 80), 21) |
| params->markParentDown(result); |
| |
| // Test 22 - 31 |
| for (i = 0; i < 10; i++) { |
| ST(22 + i) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 22 + i) |
| } |
| |
| params->markParentDown(result); // now they're all down |
| |
| // Test 32 - 131 |
| for (i = 0; i < 100; i++) { |
| ST(32 + i) REINIT br(request, "i.am.rabbit.net"); |
| FP RE(verify(result, PARENT_DIRECT, 0, 0), 32 + i) |
| } |
| |
| // sleep(5); // parents should come back up; they don't |
| sleep(params->ParentRetryTime + 1); |
| |
| // Fix: The following tests failed because |
| // br() should set xact_start correctly instead of 0. |
| |
| // Test 132 - 631 |
| for (i = 0; i < 40; i++) { |
| ST(132 + i) REINIT br(request, "i.am.rabbit.net"); |
| FP sleep(1); |
| switch ((i + 1) % 4) { |
| case 0: |
| RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 132 + i) break; |
| case 1: |
| RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 132 + i) break; |
| case 2: |
| RE(verify(result, PARENT_SPECIFIED, "furry", 80), 132 + i) break; |
| case 3: |
| RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 132 + i) break; |
| default: |
| ink_assert(0); |
| } |
| } |
| delete request; |
| delete result; |
| |
| printf("Tests Passed: %d\nTests Failed: %d\n", passes, fails); |
| *pstatus = (!fails ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED); |
| } |
| |
| // verify returns 1 iff the test passes |
| int |
| verify(ParentResult * r, ParentResultType e, const char *h, int p) |
| { |
| if (is_debug_tag_set("parent_select")) |
| show_result(r); |
| return (r->r != e) ? 0 : ((e != PARENT_SPECIFIED) ? 1 : (strcmp(r->hostname, h) ? 0 : ((r->port == p) ? 1 : 0))); |
| } |
| |
| // br creates an HttpRequestData object |
| void |
| br(HttpRequestData * h, const char *os_hostname, int dest_ip) |
| { |
| int hostname_len = strlen(os_hostname) + 1; |
| |
| h->hdr = new HTTPHdr(); |
| h->hdr->create(HTTP_TYPE_REQUEST); |
| h->hostname_str = (char *) malloc(hostname_len); |
| ink_strncpy(h->hostname_str, os_hostname, hostname_len); |
| h->xact_start = time(NULL); |
| h->src_ip = 0; |
| h->dest_ip = dest_ip; |
| h->incoming_port = 80; |
| h->api_info = new _HttpApiInfo(); |
| } |
| |
| // show_result prints out the ParentResult information |
| void |
| show_result(ParentResult * p) |
| { |
| switch (p->r) { |
| case PARENT_UNDEFINED: |
| printf("result is PARENT_UNDEFINED\n"); |
| break; |
| case PARENT_DIRECT: |
| printf("result is PARENT_DIRECT\n"); |
| break; |
| case PARENT_SPECIFIED: |
| printf("result is PARENT_SPECIFIED\n"); |
| printf("hostname is %s\n", p->hostname); |
| printf("port is %d\n", p->port); |
| break; |
| case PARENT_FAIL: |
| printf("result is PARENT_FAIL\n"); |
| break; |
| default: |
| // Handled here: |
| // PARENT_AGENT |
| break; |
| } |
| } |