1 /** @file 2 3 A brief file description 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 24 /***************************************************************************** 25 * 26 * ParentSelection.h - Interface to Parent Selection System 27 * 28 * 29 ****************************************************************************/ 30 31 #pragma once 32 33 #include "ProxyConfig.h" 34 #include "ControlBase.h" 35 #include "ControlMatcher.h" 36 #include "records/P_RecProcess.h" 37 #include "tscore/ConsistentHash.h" 38 #include "tscore/Tokenizer.h" 39 #include "tscore/ink_apidefs.h" 40 #include "HostStatus.h" 41 42 #include <algorithm> 43 #include <vector> 44 45 #define MAX_PARENTS 64 46 47 struct RequestData; 48 struct matcher_line; 49 struct ParentResult; 50 struct OverridableHttpConfigParams; 51 class ParentRecord; 52 class ParentSelectionStrategy; 53 54 enum ParentResultType { 55 PARENT_UNDEFINED, 56 PARENT_DIRECT, 57 PARENT_SPECIFIED, 58 PARENT_AGENT, 59 PARENT_FAIL, 60 }; 61 62 static const char *ParentResultStr[] = {"PARENT_UNDEFINED", "PARENT_DIRECT", "PARENT_SPECIFIED", "PARENT_AGENT", "PARENT_FAIL"}; 63 64 enum ParentRR_t { 65 P_NO_ROUND_ROBIN = 0, 66 P_STRICT_ROUND_ROBIN, 67 P_HASH_ROUND_ROBIN, 68 P_CONSISTENT_HASH, 69 P_LATCHED_ROUND_ROBIN, 70 P_UNDEFINED 71 }; 72 73 enum ParentRetry_t { 74 PARENT_RETRY_NONE = 0, 75 PARENT_RETRY_SIMPLE = 1, 76 PARENT_RETRY_UNAVAILABLE_SERVER = 2, 77 // both simple and unavailable server retry 78 PARENT_RETRY_BOTH = 3 79 }; 80 81 struct UnavailableServerResponseCodes { 82 UnavailableServerResponseCodes(char *val); ~UnavailableServerResponseCodesUnavailableServerResponseCodes83 ~UnavailableServerResponseCodes(){}; 84 85 bool containsUnavailableServerResponseCodes86 contains(int code) 87 { 88 return binary_search(codes.begin(), codes.end(), code); 89 } 90 91 private: 92 std::vector<int> codes; 93 }; 94 95 struct SimpleRetryResponseCodes { 96 SimpleRetryResponseCodes(char *val); ~SimpleRetryResponseCodesSimpleRetryResponseCodes97 ~SimpleRetryResponseCodes(){}; 98 99 bool containsSimpleRetryResponseCodes100 contains(int code) 101 { 102 return binary_search(codes.begin(), codes.end(), code); 103 } 104 105 private: 106 std::vector<int> codes; 107 }; 108 // struct pRecord 109 // 110 // A record for an individual parent 111 // 112 struct pRecord : ATSConsistentHashNode { 113 char hostname[MAXDNAME + 1]; 114 int port; 115 std::atomic<time_t> failedAt = 0; 116 std::atomic<int> failCount = 0; 117 int32_t upAt; 118 const char *scheme; // for which parent matches (if any) 119 int idx; 120 float weight; 121 char hash_string[MAXDNAME + 1]; 122 std::atomic<int> retriers = 0; 123 }; 124 125 typedef ControlMatcher<ParentRecord, ParentResult> P_table; 126 127 // class ParentRecord : public ControlBase 128 // 129 // A record for a configuration line in the parent.config 130 // file 131 // 132 class ParentRecord : public ControlBase 133 { 134 public: 135 ~ParentRecord(); 136 137 Result Init(matcher_line *line_info); 138 bool DefaultInit(char *val); 139 void UpdateMatch(ParentResult *result, RequestData *rdata); 140 141 void Print() const; 142 143 pRecord *parents = nullptr; 144 pRecord *secondary_parents = nullptr; 145 int num_parents = 0; 146 int num_secondary_parents = 0; 147 148 bool bypass_ok()149 bypass_ok() const 150 { 151 return go_direct; 152 } 153 154 const char *scheme = nullptr; 155 // private: 156 void PreProcessParents(const char *val, const int line_num, char *buf, size_t len); 157 const char *ProcessParents(char *val, bool isPrimary); 158 bool ignore_query = false; 159 uint32_t rr_next = 0; 160 bool go_direct = true; 161 bool parent_is_proxy = true; 162 ParentSelectionStrategy *selection_strategy = nullptr; 163 UnavailableServerResponseCodes *unavailable_server_retry_responses = nullptr; 164 SimpleRetryResponseCodes *simple_server_retry_responses = nullptr; 165 ParentRetry_t parent_retry = PARENT_RETRY_NONE; 166 int max_simple_retries = 1; 167 int max_unavailable_server_retries = 1; 168 int secondary_mode = 1; 169 bool ignore_self_detect = false; 170 }; 171 172 // If the parent was set by the external customer api, 173 // our HttpRequestData structure told us what parent to 174 // use and we are only called to preserve clean interface 175 // between HttpTransact & the parent selection code. The following 176 ParentRecord *const extApiRecord = (ParentRecord *)0xeeeeffff; 177 178 // used here to to set the number of ATSConsistentHashIter's 179 // used in NextHopSelectionStrategy to limit the host group 180 // size as well, group size is one to one with the number of rings 181 constexpr const uint32_t MAX_GROUP_RINGS = 5; 182 183 struct ParentResult { ParentResultParentResult184 ParentResult() { reset(); } 185 // For outside consumption 186 ParentResultType result; 187 const char *hostname; 188 int port; 189 bool retry; 190 bool chash_init[MAX_GROUP_RINGS] = {false}; 191 HostStatus_t first_choice_status = HostStatus_t::HOST_STATUS_INIT; 192 193 void resetParentResult194 reset() 195 { 196 ink_zero(*this); 197 line_number = -1; 198 result = PARENT_UNDEFINED; 199 mapWrapped[0] = false; 200 mapWrapped[1] = false; 201 } 202 203 bool is_api_resultParentResult204 is_api_result() const 205 { 206 return rec == extApiRecord; 207 } 208 209 // Do we have some result? 210 bool is_someParentResult211 is_some() const 212 { 213 if (rec == nullptr) { 214 // If we don't have a result, we either haven't done a parent 215 // lookup yet (PARENT_UNDEFINED), or the lookup didn't match 216 // anything (PARENT_DIRECT). 217 ink_assert(result == PARENT_UNDEFINED || result == PARENT_DIRECT); 218 return false; 219 } 220 221 return true; 222 } 223 224 bool parent_is_proxyParentResult225 parent_is_proxy() const 226 { 227 // Parents set by the TSHttpTxnParentProxySet API are always considered proxies rather than origins. 228 return is_api_result() ? true : rec->parent_is_proxy; 229 } 230 231 unsigned retry_typeParentResult232 retry_type() const 233 { 234 return is_api_result() ? PARENT_RETRY_NONE : rec->parent_retry; 235 } 236 237 unsigned max_retriesParentResult238 max_retries(ParentRetry_t method) const 239 { 240 // There's no API for specifying the retries, so you get 0. 241 if (is_api_result()) { 242 return 0; 243 } 244 245 switch (method) { 246 case PARENT_RETRY_NONE: 247 return 0; 248 case PARENT_RETRY_SIMPLE: 249 return rec->max_simple_retries; 250 case PARENT_RETRY_UNAVAILABLE_SERVER: 251 return rec->max_unavailable_server_retries; 252 case PARENT_RETRY_BOTH: 253 return std::max(rec->max_unavailable_server_retries, rec->max_simple_retries); 254 } 255 256 return 0; 257 } 258 259 bool response_is_retryableParentResult260 response_is_retryable(HTTPStatus response_code) const 261 { 262 Debug("parent_select", "In response_is_retryable, code: %d", response_code); 263 if (retry_type() == PARENT_RETRY_BOTH) { 264 Debug("parent_select", "Saw retry both"); 265 return (rec->unavailable_server_retry_responses->contains(response_code) || 266 rec->simple_server_retry_responses->contains(response_code)); 267 } else if (retry_type() == PARENT_RETRY_UNAVAILABLE_SERVER) { 268 Debug("parent_select", "Saw retry unavailable server"); 269 return rec->unavailable_server_retry_responses->contains(response_code); 270 } else if (retry_type() == PARENT_RETRY_SIMPLE) { 271 Debug("parent_select", "Saw retry simple retry"); 272 return rec->simple_server_retry_responses->contains(response_code); 273 } else { 274 return false; 275 } 276 } 277 278 bool bypass_okParentResult279 bypass_ok() const 280 { 281 if (is_api_result()) { 282 return false; 283 } else { 284 // Caller should check for a valid result beforehand. 285 ink_assert(result != PARENT_UNDEFINED); 286 ink_assert(is_some()); 287 return rec->bypass_ok(); 288 } 289 } 290 291 void printParentResult292 print() 293 { 294 printf("ParentResult - hostname: %s, port: %d, retry: %s, line_number: %d, last_parent: %d, start_parent: %d, wrap_around: %s, " 295 "last_lookup: %d, result: %s\n", 296 hostname, port, (retry) ? "true" : "false", line_number, last_parent, start_parent, (wrap_around) ? "true" : "false", 297 last_lookup, ParentResultStr[result]); 298 } 299 300 private: 301 // Internal use only 302 // Not to be modified by HTTP 303 int line_number; 304 ParentRecord *rec; 305 uint32_t last_parent; 306 uint32_t start_parent; 307 uint32_t last_group; 308 bool wrap_around; 309 bool mapWrapped[2]; 310 // state for consistent hash. 311 int last_lookup; 312 ATSConsistentHashIter chashIter[MAX_GROUP_RINGS]; 313 314 friend class NextHopSelectionStrategy; 315 friend class NextHopRoundRobin; 316 friend class NextHopConsistentHash; 317 friend class ParentConsistentHash; 318 friend class ParentRoundRobin; 319 friend class ParentConfigParams; 320 friend class ParentRecord; 321 friend class ParentSelectionStrategy; 322 }; 323 324 struct ParentSelectionPolicy { 325 int32_t ParentRetryTime; 326 int32_t ParentEnable; 327 int32_t FailThreshold; 328 ParentSelectionPolicy(); 329 }; 330 331 // 332 // API definition. 333 class ParentSelectionStrategy 334 { 335 public: 336 int max_retriers = 0; 337 ParentSelectionStrategy()338 ParentSelectionStrategy() { REC_ReadConfigInteger(max_retriers, "proxy.config.http.parent_proxy.max_trans_retries"); } 339 // 340 // Return the pRecord. 341 virtual pRecord *getParents(ParentResult *result) = 0; 342 // void selectParent(bool firstCall, ParentResult *result, RequestData *rdata, unsigned int fail_threshold, unsigned int 343 // retry_time) 344 // 345 // The implementation parent lookup. 346 // 347 virtual void selectParent(bool firstCall, ParentResult *result, RequestData *rdata, unsigned int fail_threshold, 348 unsigned int retry_time) = 0; 349 350 // uint32_t numParents(ParentResult *result); 351 // 352 // Returns the number of parent records in a strategy. 353 // 354 virtual uint32_t numParents(ParentResult *result) const = 0; 355 void markParentDown(ParentResult *result, unsigned int fail_threshold, unsigned int retry_time); 356 void markParentUp(ParentResult *result); 357 358 // virtual destructor. ~ParentSelectionStrategy()359 virtual ~ParentSelectionStrategy(){}; 360 }; 361 362 class ParentConfigParams : public ConfigInfo 363 { 364 public: 365 explicit ParentConfigParams(P_table *_parent_table); 366 ~ParentConfigParams() override; 367 368 bool apiParentExists(HttpRequestData *rdata); 369 void findParent(HttpRequestData *rdata, ParentResult *result, unsigned int fail_threshold, unsigned int retry_time); 370 void nextParent(HttpRequestData *rdata, ParentResult *result, unsigned int fail_threshold, unsigned int retry_time); 371 bool parentExists(HttpRequestData *rdata); 372 373 // implementation of functions from ParentSelectionStrategy. 374 void selectParent(bool firstCall,ParentResult * result,RequestData * rdata,unsigned int fail_threshold,unsigned int retry_time)375 selectParent(bool firstCall, ParentResult *result, RequestData *rdata, unsigned int fail_threshold, unsigned int retry_time) 376 { 377 if (!result->is_api_result()) { 378 ink_release_assert(result->rec->selection_strategy != nullptr); 379 return result->rec->selection_strategy->selectParent(firstCall, result, rdata, fail_threshold, retry_time); 380 } 381 } 382 383 void markParentDown(ParentResult * result,unsigned int fail_threshold,unsigned int retry_time)384 markParentDown(ParentResult *result, unsigned int fail_threshold, unsigned int retry_time) 385 { 386 if (!result->is_api_result()) { 387 ink_release_assert(result->rec->selection_strategy != nullptr); 388 result->rec->selection_strategy->markParentDown(result, fail_threshold, retry_time); 389 } 390 } 391 392 void markParentUp(ParentResult * result)393 markParentUp(ParentResult *result) 394 { 395 if (!result->is_api_result()) { 396 ink_release_assert(result != nullptr); 397 result->rec->selection_strategy->markParentUp(result); 398 } 399 } 400 401 uint32_t numParents(ParentResult * result)402 numParents(ParentResult *result) 403 { 404 if (result->is_api_result()) { 405 return 1; 406 } else { 407 ink_release_assert(result->rec->selection_strategy != nullptr); 408 return result->rec->selection_strategy->numParents(result); 409 } 410 } 411 412 P_table *parent_table; 413 ParentRecord *DefaultParent; 414 ParentSelectionPolicy policy; 415 }; 416 417 class HttpRequestData; 418 419 struct ParentConfig { 420 public: 421 static void startup(); 422 static void reconfigure(); 423 static void print(); 424 static void set_parent_table(P_table *pTable, ParentRecord *rec, int num_elements); 425 426 static ParentConfigParams * acquireParentConfig427 acquire() 428 { 429 return (ParentConfigParams *)configProcessor.get(ParentConfig::m_id); 430 } 431 432 static void releaseParentConfig433 release(ParentConfigParams *strategy) 434 { 435 configProcessor.release(ParentConfig::m_id, strategy); 436 } 437 438 static int m_id; 439 }; 440 441 // Helper Functions 442 ParentRecord *createDefaultParent(char *val); 443 444 // Unit Test Functions 445 void show_result(ParentResult *aParentResult); 446 void br(HttpRequestData *h, const char *os_hostname, sockaddr const *dest_ip = nullptr); // short for build request 447 int verify(ParentResult *r, ParentResultType e, const char *h, int p); 448 449 /* 450 For supporting multiple Socks servers, we essentially use the 451 ParentSelection infrastructure. Only the initialization is different. 452 If needed, we will have to implement most of the functions in 453 ParentSection.cc for Socks as well. For right now we will just use 454 ParentSelection 455 456 All the members in ParentConfig are static. Right now 457 we will duplicate the code for these static functions. 458 */ 459 struct SocksServerConfig { 460 static void startup(); 461 static void reconfigure(); 462 static void print(); 463 464 static ParentConfigParams * acquireSocksServerConfig465 acquire() 466 { 467 return (ParentConfigParams *)configProcessor.get(SocksServerConfig::m_id); 468 } 469 static void releaseSocksServerConfig470 release(ParentConfigParams *params) 471 { 472 configProcessor.release(SocksServerConfig::m_id, params); 473 } 474 475 static int m_id; 476 }; 477