Github user SolidWallOfCode commented on a diff in the pull request:
https://github.com/apache/trafficserver/pull/843#discussion_r75863130
--- Diff: plugins/experimental/carp/carp.cc ---
@@ -0,0 +1,713 @@
+/** @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.
+ */
+
+#include <errno.h>
+
+#include <string>
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory.h>
+
+#include <ts/ts.h>
+
+#include "Common.h"
+#include "CarpConfig.h"
+#include "CarpConfigPool.h"
+#include "CarpHashAlgorithm.h"
+#include "UrlComponents.h"
+
+using namespace std;
+
+CarpConfigPool* g_CarpConfigPool = NULL;
+int g_carpSelectedHostArgIndex = 0;
+TSTextLogObject g_logObject = NULL;
+
+const char *logFileName = "carp";
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+/*
+ check for our carp routed header, dump status if requested
+ */
+static int
+processCarpRoutedHeader(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc)
+{
+ string value;
+ if (getHeader(bufp, hdr_loc, CARP_ROUTED_HEADER, value)) { // if found
header
+ if (value.compare("1") == 0) { // is loop prevention value
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with loop prevention value,
not forwarding again", CARP_ROUTED_HEADER.c_str());
+ return 0;
+ } else if (value.compare("dump") == 0) { // is dump status request
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with dump request",
CARP_ROUTED_HEADER.c_str());
+ string status;
+ g_CarpConfigPool->getGlobalHashAlgo()->dump(status);
+ TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_MULTI_STATUS);
+ TSHttpTxnErrorBodySet(txnp, TSstrdup(status.c_str()),
status.length(), NULL);
+ return -1;
+ }
+ TSDebug(DEBUG_TAG_HOOK, "Found %s header with unknown value of %s,
ignoring", CARP_ROUTED_HEADER.c_str(), value.c_str());
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ }
+ return 1; // all OK
+}
+
+static bool
+checkListForSelf(std::vector<HashNode *> list)
+{
+ for (size_t k = 0; k < list.size(); k++) {
+ if (list[k]->isSelf) return true;
+ }
+ return false;
+}
+
+/**
+ bIsPOSTRemap = false --- Hash request and forward to peer
+ bIsPOSTRemap = true --- hash request, extract OS sockaddr, insert
forwarding header, forward
+ */
+static int
+handleRequestProcessing(TSCont contp, TSEvent event, void *edata, bool
bIsPOSTRemap)
+{
+ TSHttpTxn txnp = (TSHttpTxn) edata;
+ TSMBuffer bufp;
+ TSMLoc hdr_loc;
+ TSMLoc url_loc;
+
+ // get the client request so we can get URL and add header
+ if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+ TSError("carp couldn't get request headers");
+ return -1;
+ }
+
+ int method_len;
+ const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
+ if (NULL == method) {
+ TSError("carp couldn't get http method");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return -1;
+ }
+ if (((method_len == TS_HTTP_LEN_DELETE) && (strncasecmp(method,
TS_HTTP_METHOD_DELETE, TS_HTTP_LEN_DELETE) == 0)) ||
+ ((method_len == TS_HTTP_LEN_PURGE) && (strncasecmp(method,
TS_HTTP_METHOD_PURGE, TS_HTTP_LEN_PURGE) == 0))) {
+ TSDebug(DEBUG_TAG_HOOK, "Request method is '%s' so not routing
request", string(method,method_len).c_str());
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+
+ if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
+ TSError("carp couldn't get url");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return -1;
+ }
+ // if has carp loop prevention header, do not remap
+ int iTemp = processCarpRoutedHeader(txnp, bufp, hdr_loc);
+ if(iTemp <= 0) { // if dump or do not remap
+ // check origin client request's scheme for premap mode
+ if (!bIsPOSTRemap) {
+ string oriScheme;
+ if (!getHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME, oriScheme)) {
+ TSDebug(DEBUG_TAG_HOOK, "couldn't get '%s' header",
CARP_PREMAP_SCHEME.c_str());
+ } else {
+ bool isHttps = (oriScheme == TS_URL_SCHEME_HTTPS);
+
+ if (isHttps) {
+ TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTPS,
TS_URL_LEN_HTTPS);
+ } else {
+ TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTP,
TS_URL_LEN_HTTP);
+ }
+
+ removeHeader(bufp, hdr_loc, CARP_STATUS_HEADER);
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ removeHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME.c_str());
+ TSDebug(DEBUG_TAG_HOOK, "Set client request's scheme to %s through
%s header",
+ isHttps?"https":"http", CARP_PREMAP_SCHEME.c_str());
+ }
+ } else {
+ removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
+ }
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return iTemp;
+ }
+
+ UrlComponents reqUrl;
+ reqUrl.populate(bufp, url_loc);
+ // the url ONLY used to determine the cache owner
+ string sUrl;
+
+ if (!bIsPOSTRemap) { // if pre-remap, then host not in URL so get from
header
+ string sHost;
+ if (!getHeader(bufp, hdr_loc, TS_MIME_FIELD_HOST, sHost)) {
+ TSDebug(DEBUG_TAG_HOOK, "Could not find host header, ignoring it");
+ }
+ reqUrl.setHost(sHost);
+
+ //[YTSATS-836] heuristically ignore the scheme and port when calculate
cache owner
+ UrlComponents normalizedUrl = reqUrl;
+ normalizedUrl.setScheme(CARP_SCHEME_FOR_HASH);
+ normalizedUrl.setPort(CARP_PORT_FOR_HASH);
+ normalizedUrl.construct(sUrl);
+ } else {
+ reqUrl.construct(sUrl);
+ }
+
+
+ if (g_CarpConfigPool->getGlobalConfig()->hasWhiteList()) {
+ string sCarpable;
+ if (!getHeader(bufp, hdr_loc, CARPABLE_HEADER, sCarpable)) { // if no
carpable header check whitelist
+ if
(!g_CarpConfigPool->getGlobalConfig()->isWhiteListed(reqUrl.getHost())) { // if
white list exists, then host must be present
+ TSDebug(DEBUG_TAG_HOOK, "Host '%s' is not whitelisted, not going
through carp", reqUrl.getHost().c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ } else {
+ TSDebug(DEBUG_TAG_HOOK, "Found host (%s) whitelisted,
routing...",reqUrl.getHost().c_str());
+ }
+ } else { // found carpable header, make sure it's 1
+ if (sCarpable.compare("1") != 0) { // carpable header present but
not 0, be strict and do not forward request
+ TSDebug(DEBUG_TAG_HOOK, "Carpable (%s) present but value not
acceptable (%s)", CARPABLE_HEADER.c_str(), sCarpable.c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ } else {
+ TSDebug(DEBUG_TAG_HOOK, "Found Carpable header, routing...");
+ }
+ }
+ } else { // no whitelist so blacklist could be used
+ if
(g_CarpConfigPool->getGlobalConfig()->isBlackListed(reqUrl.getHost())) { // if
host black listed, do not carp
+ TSDebug(DEBUG_TAG_HOOK, "Host '%s' is blacklisted, not going through
carp", reqUrl.getHost().c_str());
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+ }
+ }
+
+ TSDebug(DEBUG_TAG_HOOK, "URL to hash with=%s", sUrl.c_str());
+
+ // get nodeList and select node to forward to
+ std::vector<HashNode *> nodeList =
g_CarpConfigPool->getGlobalHashAlgo()->getRemapProxyList(sUrl);
+ bool bAddHeaderResult=false;
+ bool bAddForwardedResult = false;
+ HashNode* node = NULL;
+ bool bIsOwner = false;
+
+ if (nodeList.size() == 0) { // no hosts available to forward to
+ TSDebug(DEBUG_TAG_HOOK, "no hosts available to forward to, will handle
locally");
+ goto done;
+ } else {
+ node = nodeList[0];
+ }
+
+ bIsOwner = checkListForSelf(nodeList);
+ for (size_t k = 0; k < nodeList.size(); k++) {
+ TSDebug(DEBUG_TAG_HOOK, "nodeList host %d name is %s",
static_cast<int>(k), nodeList[k]->name.c_str());
+ }
+
+ //handle forwarding
+ TSDebug(DEBUG_TAG_HOOK, "forwarding to '%s' (isSelf=%d)",
node->name.c_str(), node->isSelf);
+ if (!node->isSelf) { // carp does not forward if we choose ourself
+ node->carpForward();
+ TSDebug(DEBUG_TAG_HOOK, "carp forwarded to %s.", node->name.c_str());
+ // insert carp loop prevention header
+ bAddHeaderResult = addHeader(bufp, hdr_loc, CARP_ROUTED_HEADER,
string("1"));
+ if (!bAddHeaderResult) {
+ TSError("Carp, error inserting '%s' header",
CARP_ROUTED_HEADER.c_str());
+ }
+ bAddForwardedResult = addHeader(bufp, hdr_loc, CARP_STATUS_HEADER,
string(CARP_FORWARDED));
+ if (!bAddForwardedResult) {
+ TSError("Carp, error inserting '%s' header",
CARP_STATUS_HEADER.c_str());
+ }
+
+ if (bIsPOSTRemap) { // if post remap, get remapped/OS Server Addr and
add as header
+ const struct sockaddr* sa = TSHttpTxnServerAddrGet(txnp);
+ // const struct sockaddr* sa = TSHttpTxnNextHopAddrGet(txnp);
+ if (sa) { // sanity check
+ struct sockaddr_storage ss;
+ memcpy(static_cast<void *> (&ss), sa, sizeof (sockaddr_storage));
+ if ((reinterpret_cast<sockaddr_in *> (&ss))->sin_port == 0) { //
set port from client request URL
+ (reinterpret_cast<sockaddr_in *> (&ss))->sin_port =
htons(reqUrl.getPort());
+ }
+
+ string sTemp;
+ getStringFromSockaddr(reinterpret_cast<const sockaddr *> (&ss),
sTemp);
+ TSDebug(DEBUG_TAG_HOOK, "Inserting forward header with
sockaddr:%s", sTemp.c_str());
+ string sSockaddr;
+ sSockaddr.reserve(32 + sizeof (sockaddr_storage) * 2);
+ for (unsigned int i = 0; i<sizeof (sockaddr_storage); i++) {
+ char val[8];
+ sprintf(val, "%02X", reinterpret_cast<const unsigned char *>
(&ss)[i]);
+ sSockaddr += val;
+ }
+ sSockaddr += "/" + reqUrl.getScheme();
+ // insert carp forwarding header
+ bool bAddFwdHeaderResult = addHeader(bufp, hdr_loc,
CARP_FORWARD_HEADER, sSockaddr);
+ if (!bAddFwdHeaderResult) {
+ TSError("Carp, error inserting '%s' header",
CARP_FORWARD_HEADER.c_str());
+ }
+ }
+ } else { // for premap mode
+ string sScheme = reqUrl.getScheme();
+
+ if (!addHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME, sScheme)) {
+ TSError("Carp, error inserting '%s' header in premap mode",
CARP_PREMAP_SCHEME.c_str());
+ } else {
+ TSDebug(DEBUG_TAG_HOOK, "Insert client request scheme %s in premap
mode", sScheme.c_str());
+ }
+ }
+ // set origin server/destination
+ // TSHttpTxnConfigIntSet(txnp,
TS_CONFIG_HTTP_SHARE_SERVER_SESSIONS, 0); // disable conn sharing due to bug
when used with TSHttpTxnServerAddrSet
+ if (TSHttpTxnServerAddrSet(txnp, reinterpret_cast<const struct
sockaddr *> (&node->forwardAddr)) != TS_SUCCESS) {
+ TSDebug(DEBUG_TAG_HOOK, "Error calling TSHttpTxnServerAddrSet");
+ } else {
+ // set scheme appropriately based on destination
+ TSDebug(DEBUG_TAG_HOOK, "Setting scheme to '%s'",
node->getSchemeString());
+ TSUrlSchemeSet(bufp, url_loc, node->getSchemeString(), -1);
+ if (!bIsPOSTRemap) { // since we are forwarding, do not remap
request and do not cache result
+ TSSkipRemappingSet(txnp, true);
+ }
+ TSHttpTxnArgSet(txnp, g_carpSelectedHostArgIndex, static_cast<void
*> (node));
+ if (!bIsOwner) {
+ TSHttpTxnServerRespNoStoreSet(txnp, 1); // do not cache the
response
+ } else {
+ TSHttpTxnServerRespNoStoreSet(txnp, 0); // we are replicate owner,
cache response
+ }
+ }
+ } else {
+ node->carpNoForward();
+ TSDebug(DEBUG_TAG_HOOK, "carp forwarded to self.");
+ }
+
+done :
+ // done w/buffers, release them
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ return 0;
+}
+
+/**
+ * Convert ASCII hex digit to value of hex digit
+ * @param ch
+ * @return
+ */
+static unsigned char
+getValueOfHex(unsigned char ch)
+{
+ ch -= '0';
+ if(ch > 9) ch -= 7; // 'A' - ':' = 7
--- End diff --
Really? Use a magic constant then explain it, instead of just letting the
compiler do the constant folding? Also, this doesn't work for lower case.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---