diff --git a/doc/developer-guide/api/functions/TSUrlHostGet.en.rst b/doc/developer-guide/api/functions/TSUrlHostGet.en.rst index bb071765054..139c04d7aaf 100644 --- a/doc/developer-guide/api/functions/TSUrlHostGet.en.rst +++ b/doc/developer-guide/api/functions/TSUrlHostGet.en.rst @@ -33,6 +33,7 @@ Synopsis .. function:: const char * TSUrlHostGet(TSMBuffer bufp, TSMLoc offset, int * length) .. function:: const char * TSUrlSchemeGet(TSMBuffer bufp, TSMLoc offset, int * length) +.. function:: const char * TSUrlRawSchemeGet(TSMBuffer bufp, TSMLoc offset, int * length) .. function:: const char * TSUrlUserGet(TSMBuffer bufp, TSMLoc offset, int * length) .. function:: const char * TSUrlPasswordGet(TSMBuffer bufp, TSMLoc offset, int* length) .. function:: int TSUrlPortGet(TSMBuffer bufp, TSMLoc offset) @@ -50,13 +51,17 @@ buffers. The URL functions can create, copy, retrieve or delete entire URLs, and retrieve or modify parts of URLs, such as their host, port or scheme information. -:func:`TSUrlSchemeGet`, :func:`TSUrlUserGet`, :func:`TSUrlPasswordGet`, +:func:`TSUrlSchemeGet`, :func:`TSUrlRawSchemeGet`, :func:`TSUrlUserGet`, :func:`TSUrlPasswordGet`, :func:`TSUrlHostGet`, :func:`TSUrlPathGet`, :func:`TSUrlHttpParamsGet`, :func:`TSUrlHttpQueryGet` and :func:`TSUrlHttpFragmentGet` each retrieve an internal pointer to the specified portion of the URL from the marshall buffer :arg:`bufp`. The length of the returned string is placed in :arg:`length` and a pointer to the URL portion is returned. +If a request URL does not have a explicit scheme, :func:`TSUrlRawSchemeGet` will return null and +set :arg:`length` to zero. :func:`TSUrlSchemeGet`, will return the scheme corresponding to the +URL type (HTTP or HTTPS) if there is no explicit scheme. + :func:`TSUrlPortGet` retrieves the port number portion of the URL located at :arg:`offset` within the marshal buffer :arg:`bufp`. If there is no explicit port number in the URL, a canonicalized valued is returned based on the URL diff --git a/doc/developer-guide/plugins/http-headers/urls.en.rst b/doc/developer-guide/plugins/http-headers/urls.en.rst index 700603b2607..ab7a2da92b2 100644 --- a/doc/developer-guide/plugins/http-headers/urls.en.rst +++ b/doc/developer-guide/plugins/http-headers/urls.en.rst @@ -121,9 +121,10 @@ one of the known values. If it is, then it stores a pointer into a global table (instead of storing the known value in the marshal buffer). The scheme values listed above are also pointers into this table. This allows simple pointer comparison of the value returned from -``TSUrlSchemeGet`` with one of the values listed above. You should use -the Traffic Server-defined values when referring to one of the known -schemes, since doing so can prevent the possibility of spelling errors. +``TSUrlSchemeGet`` or ``TSUrlRawSchemeGet`` with one of the values +listed above. You should use the Traffic Server-defined values when +referring to one of the known schemes, since doing so can prevent the +possibility of spelling errors. Traffic Server **URL functions** are listed below: diff --git a/include/ts/ts.h b/include/ts/ts.h index 082eda128c1..c805a7a9131 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -430,6 +430,22 @@ tsapi char *TSUrlStringGet(TSMBuffer bufp, TSMLoc offset, int *length); @param length length of the returned string. @return The scheme portion of the URL, as a string. + */ +tsapi const char *TSUrlRawSchemeGet(TSMBuffer bufp, TSMLoc offset, int *length); + +/** + Retrieves the scheme portion of the URL located at url_loc within + the marshal buffer bufp. TSUrlSchemeGet() places the length of + the string in the length argument. If the length is NULL then no + attempt is made to dereference it. If there is no explicit scheme, + a scheme of http is returned if the URL type is HTTP, and a scheme + of https is returned if the URL type is HTTPS. + + @param bufp marshal buffer storing the URL. + @param offset location of the URL within bufp. + @param length length of the returned string. + @return The scheme portion of the URL, as a string. + */ tsapi const char *TSUrlSchemeGet(TSMBuffer bufp, TSMLoc offset, int *length); diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 5f529c26e8f..eee68ed2bf8 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -2350,11 +2350,34 @@ URLPartSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length, URLPartSet } const char * -TSUrlSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) +TSUrlRawSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) { return URLPartGet(bufp, obj, length, &URL::scheme_get); } +const char * +TSUrlSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) +{ + char const *data = TSUrlRawSchemeGet(bufp, obj, length); + if (data && *length) { + return data; + } + switch (reinterpret_cast(obj)->m_url_type) { + case URL_TYPE_HTTP: + data = URL_SCHEME_HTTP; + *length = URL_LEN_HTTP; + break; + case URL_TYPE_HTTPS: + data = URL_SCHEME_HTTPS; + *length = URL_LEN_HTTPS; + break; + default: + *length = 0; + data = nullptr; + } + return data; +} + TSReturnCode TSUrlSchemeSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) { diff --git a/tests/Makefile.am b/tests/Makefile.am index e3d4097678a..cf03eeda7e8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,6 +32,7 @@ AM_LDFLAGS += -rpath $(abs_builddir) include gold_tests/continuations/plugins/Makefile.inc include gold_tests/chunked_encoding/Makefile.inc +include gold_tests/pluginTest/tsapi/Makefile.inc include gold_tests/timeout/Makefile.inc include gold_tests/tls/Makefile.inc include tools/plugins/Makefile.inc diff --git a/tests/gold_tests/pluginTest/tsapi/Makefile.inc b/tests/gold_tests/pluginTest/tsapi/Makefile.inc new file mode 100644 index 00000000000..c3fa39cc960 --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/Makefile.inc @@ -0,0 +1,18 @@ +# 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. + +noinst_LTLIBRARIES += gold_tests/pluginTest/tsapi/test_tsapi.la +gold_tests_pluginTest_tsapi_test_tsapi_la_SOURCES = gold_tests/pluginTest/tsapi/test_tsapi.cc diff --git a/tests/gold_tests/pluginTest/tsapi/log.gold b/tests/gold_tests/pluginTest/tsapi/log.gold index 5274ed14f14..b12f447867f 100644 --- a/tests/gold_tests/pluginTest/tsapi/log.gold +++ b/tests/gold_tests/pluginTest/tsapi/log.gold @@ -1,14 +1,135 @@ +TSRemapNewInstance(): +argv[0]=http://myhost.test/ +argv[1]=http://127.0.0.1:SERVER_PORT/ +TSRemapNewInstance(): +argv[0]=http://myhost.test/ +argv[1]=http://127.0.0.1:SERVER_PORT/ +TSRemapNewInstance(): +argv[0]=https://myhost.test:123/ +argv[1]=http://127.0.0.1:SERVER_PORT/ +TSRemapNewInstance(): +argv[0]=https://myhost.test:123/ +argv[1]=http://127.0.0.1:SERVER_PORT/ Global: event=TS_EVENT_HTTP_TXN_START Global: event=TS_EVENT_HTTP_READ_REQUEST_HDR -TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt:SERVER_PORT/ -TSHttpHdrEffectiveUrlBufGet(): http://myhost.test:SERVER_PORT/ +TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt/ +Client Request: +TSHttpHdrEffectiveUrlBufGet(): http://myhost.test/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): 80 Transaction: event=TS_EVENT_HTTP_READ_REQUEST_HDR -TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt:SERVER_PORT/ -TSHttpHdrEffectiveUrlBufGet(): http://myhost.test:SERVER_PORT/ +TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt/ +Client Request: +TSHttpHdrEffectiveUrlBufGet(): http://myhost.test/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): 80 +TSRemapDoRemap(): instance=0 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +TSRemapDoRemap(): instance=1 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +Global: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/ +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 +Transaction: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/ +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 Global: event=TS_EVENT_HTTP_TXN_START Global: event=TS_EVENT_HTTP_READ_REQUEST_HDR -TSHttpTxnEffectiveUrlStringGet(): https://myhost.test:SERVER_PORT/ -TSHttpHdrEffectiveUrlBufGet(): https://myhost.test:SERVER_PORT/ +TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt/xYz +Client Request: +TSHttpHdrEffectiveUrlBufGet(): http://myhost.test/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): 80 Transaction: event=TS_EVENT_HTTP_READ_REQUEST_HDR -TSHttpTxnEffectiveUrlStringGet(): https://myhost.test:SERVER_PORT/ -TSHttpHdrEffectiveUrlBufGet(): https://myhost.test:SERVER_PORT/ +TSHttpTxnEffectiveUrlStringGet(): http://mYhOsT.teSt/xYz +Client Request: +TSHttpHdrEffectiveUrlBufGet(): http://myhost.test/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): 80 +TSRemapDoRemap(): instance=0 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +TSRemapDoRemap(): instance=1 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +Global: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/xYz +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 +Transaction: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/xYz +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/xYz +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 +Global: event=TS_EVENT_HTTP_TXN_START +Global: event=TS_EVENT_HTTP_READ_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): https://myhost.test:123/ +Client Request: +TSHttpHdrEffectiveUrlBufGet(): https://myhost.test:123/ +TSUrlSchemeGet(): https +TSUrlRawSchemeGet(): https +TSUrlPortGet(): 123 +Transaction: event=TS_EVENT_HTTP_READ_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): https://myhost.test:123/ +Client Request: +TSHttpHdrEffectiveUrlBufGet(): https://myhost.test:123/ +TSUrlSchemeGet(): https +TSUrlRawSchemeGet(): https +TSUrlPortGet(): 123 +TSRemapDoRemap(): instance=2 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +TSRemapDoRemap(): instance=3 redirect=0 +Remap Request: +TSHttpHdrEffectiveUrlBufGet(): http://127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): http +TSUrlPortGet(): SERVER_PORT +Global: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/ +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 +Transaction: event=TS_EVENT_HTTP_SEND_REQUEST_HDR +TSHttpTxnEffectiveUrlStringGet(): http://127.0.0.1:SERVER_PORT/ +Request To Server: +TSHttpHdrEffectiveUrlBufGet(): 127.0.0.1:SERVER_PORT/ +TSUrlSchemeGet(): http +TSUrlRawSchemeGet(): failed to get raw URL scheme +TSUrlPortGet(): 80 diff --git a/tests/gold_tests/pluginTest/tsapi/test_tsapi.cc b/tests/gold_tests/pluginTest/tsapi/test_tsapi.cc new file mode 100644 index 00000000000..1d803c6f4fb --- /dev/null +++ b/tests/gold_tests/pluginTest/tsapi/test_tsapi.cc @@ -0,0 +1,356 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* +Regression testing code for TS API. Not comprehensive, hopefully will be built up over time. +*/ + +#define PINAME "test_tsapi" + +namespace +{ +char PIName[] = PINAME; + +// NOTE: It's important to flush this after writing so that a gold test using this plugin can examine the log before TS +// terminates. +// +std::fstream logFile; + +TSCont tCont, gCont; + +std::uintptr_t remap_count; +std::bitset<64> remap_mask; + +void +testsForReqHdr(char const *desc, TSMBuffer hbuf, TSMLoc hloc) +{ + logFile << desc << ':' << std::endl; + logFile << "TSHttpHdrEffectiveUrlBufGet(): "; + int64_t url_length; + + if (TSHttpHdrEffectiveUrlBufGet(hbuf, hloc, nullptr, 0, &url_length) != TS_SUCCESS) { + logFile << "sizing call failed " << std::endl; + + } else if (0 == url_length) { + logFile << "zero URL length returned" << std::endl; + + } else { + std::string s(url_length, '?'); + + s += "yada"; + + int64_t url_length2; + + if (TSHttpHdrEffectiveUrlBufGet(hbuf, hloc, s.data(), url_length + 4, &url_length2) != TS_SUCCESS) { + logFile << "data-obtaining call failed" << std::endl; + + } else if (url_length2 != url_length) { + logFile << "second size does not match first" << std::endl; + + } else if (s.substr(url_length, 4) != "yada") { + logFile << "overwrite" << std::endl; + + } else { + logFile << s.substr(0, url_length) << std::endl; + } + } + logFile << "TSUrlSchemeGet(): "; + TSMLoc url_loc; + if (TSHttpHdrUrlGet(hbuf, hloc, &url_loc) != TS_SUCCESS) { + logFile << "failed to get URL loc" << std::endl; + + } else { + ts::PostScript ps([=]() -> void { TSHandleMLocRelease(hbuf, TS_NULL_MLOC, url_loc); }); + + int scheme_len; + char const *scheme_data = TSUrlSchemeGet(hbuf, url_loc, &scheme_len); + if (!scheme_data || !scheme_len) { + logFile << "failed to get URL scheme" << std::endl; + } else { + logFile << std::string_view(scheme_data, scheme_len) << std::endl; + } + logFile << "TSUrlRawSchemeGet(): "; + scheme_data = TSUrlRawSchemeGet(hbuf, url_loc, &scheme_len); + if (!scheme_data || !scheme_len) { + logFile << "failed to get raw URL scheme" << std::endl; + } else { + logFile << std::string_view(scheme_data, scheme_len) << std::endl; + } + logFile << "TSUrlPortGet(): " << TSUrlPortGet(hbuf, url_loc) << std::endl; + } +} + +void +testsForEffectiveUrlStringGet(TSHttpTxn txn) +{ + logFile << "TSHttpTxnEffectiveUrlStringGet(): "; + int urlLength; + char *urlStr = TSHttpTxnEffectiveUrlStringGet(txn, &urlLength); + if (!urlStr) { + logFile << "URL null" << std::endl; + } else if (0 == urlLength) { + logFile << "URL length zero" << std::endl; + } else if (0 > urlLength) { + logFile << "URL length negative" << std::endl; + } else { + logFile << std::string_view(urlStr, urlLength) << std::endl; + + TSfree(urlStr); + } +} + +void +testsForReadReqHdrHook(TSHttpTxn txn) +{ + testsForEffectiveUrlStringGet(txn); + + { + TSMBuffer hbuf; + TSMLoc hloc; + + if (TSHttpTxnClientReqGet(txn, &hbuf, &hloc) != TS_SUCCESS) { + logFile << "failed to get client request" << std::endl; + + } else { + testsForReqHdr("Client Request", hbuf, hloc); + TSHandleMLocRelease(hbuf, TS_NULL_MLOC, hloc); + } + } +} + +void +testsForSendReqHdrHook(TSHttpTxn txn) +{ + testsForEffectiveUrlStringGet(txn); + + { + TSMBuffer hbuf; + TSMLoc hloc; + + if (TSHttpTxnServerReqGet(txn, &hbuf, &hloc) != TS_SUCCESS) { + logFile << "failed to get server request" << std::endl; + + } else { + testsForReqHdr("Request To Server", hbuf, hloc); + TSHandleMLocRelease(hbuf, TS_NULL_MLOC, hloc); + } + } +} + +int +transactionContFunc(TSCont, TSEvent event, void *eventData) +{ + logFile << "Transaction: event=" << TSHttpEventNameLookup(event) << std::endl; + + TSDebug(PIName, "Transaction: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); + + switch (event) { + case TS_EVENT_HTTP_READ_REQUEST_HDR: { + auto txn = static_cast(eventData); + + testsForReadReqHdrHook(txn); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + } break; + + case TS_EVENT_HTTP_SEND_REQUEST_HDR: { + auto txn = static_cast(eventData); + + testsForSendReqHdrHook(txn); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + } break; + + default: { + TSReleaseAssert(false); + } break; + + } // end switch + + return 0; +} + +int +globalContFunc(TSCont, TSEvent event, void *eventData) +{ + logFile << "Global: event=" << TSHttpEventNameLookup(event) << std::endl; + + TSDebug(PIName, "Global: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); + + switch (event) { + case TS_EVENT_HTTP_TXN_START: { + auto txn = static_cast(eventData); + + TSHttpTxnHookAdd(txn, TS_HTTP_READ_REQUEST_HDR_HOOK, tCont); + TSHttpTxnHookAdd(txn, TS_HTTP_SEND_REQUEST_HDR_HOOK, tCont); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + } break; + + case TS_EVENT_HTTP_READ_REQUEST_HDR: { + auto txn = static_cast(eventData); + + testsForReadReqHdrHook(txn); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + } break; + + case TS_EVENT_HTTP_SEND_REQUEST_HDR: { + auto txn = static_cast(eventData); + + testsForSendReqHdrHook(txn); + + TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); + } break; + + default: { + TSReleaseAssert(false); + } break; + + } // end switch + + return 0; +} + +} // end anonymous namespace + +TSReturnCode +TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) +{ + TSDebug(PIName, "TSRemapInit()"); + + TSReleaseAssert(api_info && errbuf && errbuf_size); + + if (api_info->tsremap_version < TSREMAP_VERSION) { + std::snprintf(errbuf, errbuf_size, "Incorrect API version %ld.%ld", api_info->tsremap_version >> 16, + (api_info->tsremap_version & 0xffff)); + return TS_ERROR; + } + + const char *fileSpec = std::getenv("OUTPUT_FILE"); + + if (nullptr == fileSpec) { + TSError(PINAME ": Environment variable OUTPUT_FILE not found."); + + return TS_ERROR; + } + + // Disable output buffering for logFile, so that explicit flushing is not necessary. + logFile.rdbuf()->pubsetbuf(nullptr, 0); + + logFile.open(fileSpec, std::ios::out); + if (!logFile.is_open()) { + TSError(PINAME ": could not open log file \"%s\"", fileSpec); + + return TS_ERROR; + } + + // Mutex to protext the logFile object. + // + TSMutex mtx = TSMutexCreate(); + + gCont = TSContCreate(globalContFunc, mtx); + + TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, gCont); + TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, gCont); + TSHttpHookAdd(TS_HTTP_SEND_REQUEST_HDR_HOOK, gCont); + + tCont = TSContCreate(transactionContFunc, mtx); + return TS_SUCCESS; +} + +TSReturnCode +TSRemapNewInstance(int argc, char *argv[], void **instance, char *errbuf, int errbuf_size) +{ + TSReleaseAssert(errbuf && errbuf_size); + TSReleaseAssert(remap_count < remap_mask.size()); + + remap_mask[remap_count++] = true; + *instance = reinterpret_cast(remap_count); + + logFile << "TSRemapNewInstance():" << std::endl; + for (int i = 0; i < argc; ++i) { + logFile << "argv[" << i << "]=" << argv[i] << std::endl; + } + + return TS_SUCCESS; +} + +void +TSRemapDeleteInstance(void *instance) +{ + // NOTE: Currently this is never called. + + auto inum = reinterpret_cast(instance) - 1; + logFile << "TSRemapNewInstance(): instance=" << inum << std::endl; + TSReleaseAssert(inum < remap_mask.size()); + TSReleaseAssert(remap_mask[inum]); + remap_mask[inum] = false; +} + +TSRemapStatus +TSRemapDoRemap(void *instance, TSHttpTxn txnp, TSRemapRequestInfo *rri) +{ + TSReleaseAssert(txnp && rri); + auto inum = reinterpret_cast(instance) - 1; + TSReleaseAssert(inum < remap_mask.size()); + + logFile << "TSRemapDoRemap(): instance=" << inum << " redirect=" << rri->redirect << std::endl; + + testsForReqHdr("Remap Request", rri->requestBufp, rri->requestHdrp); + + return TSREMAP_NO_REMAP; +} + +namespace +{ +class Cleanup +{ +public: + ~Cleanup() + { + // In practice it is not strictly necessary to destroy remaining continuations on program exit. + + if (tCont) { + TSContDestroy(tCont); + } + if (gCont) { + TSContDestroy(gCont); + } + } +}; + +// Do any needed cleanup for this source file at program termination time. +// +Cleanup cleanup; + +} // end anonymous namespace diff --git a/tests/gold_tests/pluginTest/tsapi/tsapi.test.py b/tests/gold_tests/pluginTest/tsapi/tsapi.test.py index 999d958b889..de9c2a8d640 100644 --- a/tests/gold_tests/pluginTest/tsapi/tsapi.test.py +++ b/tests/gold_tests/pluginTest/tsapi/tsapi.test.py @@ -36,6 +36,11 @@ response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": "112233"} server.addResponse("sessionlog.json", request_header, response_header) +request_header = { + "headers": "GET /xYz HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": "445566"} +server.addResponse("sessionlog.json", request_header, response_header) + # Disable the cache to make sure each request is forwarded to the origin # server. ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True, enable_cache=False) @@ -47,8 +52,8 @@ 'proxy.config.proxy_name': 'Poxy_Proxy', # This will be the server name. 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), - 'proxy.config.url_remap.remap_required': 0, - 'proxy.config.diags.debug.enabled': 0, + 'proxy.config.url_remap.remap_required': 1, + 'proxy.config.diags.debug.enabled': 1, 'proxy.config.diags.debug.tags': 'http|test_tsapi', }) @@ -56,36 +61,41 @@ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' ) +rp = os.path.join(Test.TestDirectory, '.libs', 'test_tsapi.so') + ts.Disk.remap_config.AddLine( - "map http://myhost.test:{0} http://127.0.0.1:{0}".format(server.Variables.Port) + "map http://myhost.test http://127.0.0.1:{0} @plugin={1} @plugin={1}".format(server.Variables.Port, rp) ) ts.Disk.remap_config.AddLine( - "map https://myhost.test:{0} http://127.0.0.1:{0}".format(server.Variables.Port) + "map https://myhost.test:123 http://127.0.0.1:{0} @plugin={1} @plugin={1}".format(server.Variables.Port, rp) ) -Test.PrepareTestPlugin(os.path.join(Test.Variables.AtsTestPluginsDir, 'test_tsapi.so'), ts) - tr = Test.AddTestRun() # Probe server port to check if ready. tr.Processes.Default.StartBefore(server, ready=When.PortOpen(server.Variables.Port)) tr.Processes.Default.StartBefore(Test.Processes.ts) # tr.Processes.Default.Command = ( - 'curl --verbose --ipv4 --header "Host: mYhOsT.teSt:{0}" hTtP://loCalhOst:{1}/'.format(server.Variables.Port, ts.Variables.port) + 'curl --verbose --ipv4 --header "Host: mYhOsT.teSt" hTtP://loCalhOst:{}/'.format(ts.Variables.port) +) +tr.Processes.Default.ReturnCode = 0 + +tr = Test.AddTestRun() +tr.Processes.Default.Command = ( + 'curl --verbose --ipv4 --proxy localhost:{} http://mYhOsT.teSt/xYz'.format(ts.Variables.port) ) tr.Processes.Default.ReturnCode = 0 tr = Test.AddTestRun() tr.Processes.Default.Command = ( 'curl --verbose --ipv4 --http2 --insecure --header ' + - '"Host: myhost.test:{0}" HttPs://LocalHost:{1}/'.format(server.Variables.Port, ts.Variables.ssl_port) + '"Host: myhost.test:123" HttPs://LocalHost:{}/'.format(ts.Variables.ssl_port) ) tr.Processes.Default.ReturnCode = 0 tr = Test.AddTestRun() # Change server port number (which can vary) to a fixed string for compare to gold file. -tr.Processes.Default.Command = "sed 's/:{0}/:SERVER_PORT/' < {1}/log.txt > {1}/log2.txt".format( - server.Variables.Port, Test.RunDirectory) +tr.Processes.Default.Command = "sed 's/{}/SERVER_PORT/' < log.txt > log2.txt".format(server.Variables.Port) tr.Processes.Default.ReturnCode = 0 f = tr.Disk.File("log2.txt") f.Content = "log.gold" diff --git a/tests/tools/plugins/Makefile.inc b/tests/tools/plugins/Makefile.inc index 3680da97217..36486dfe0bc 100644 --- a/tests/tools/plugins/Makefile.inc +++ b/tests/tools/plugins/Makefile.inc @@ -72,8 +72,5 @@ tools_plugins_test_hooks_la_SOURCES = tools/plugins/test_hooks.cc noinst_LTLIBRARIES += tools/plugins/test_log_interface.la tools_plugins_test_log_interface_la_SOURCES = tools/plugins/test_log_interface.cc -noinst_LTLIBRARIES += tools/plugins/test_tsapi.la -tools_plugins_test_tsapi_la_SOURCES = tools/plugins/test_tsapi.cc - noinst_LTLIBRARIES += tools/plugins/user_args.la tools_plugins_user_args_la_SOURCES = tools/plugins/user_args.cc diff --git a/tests/tools/plugins/test_tsapi.cc b/tests/tools/plugins/test_tsapi.cc deleted file mode 100644 index 747db32d1db..00000000000 --- a/tests/tools/plugins/test_tsapi.cc +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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. - */ - -/* -Regression testing code for TS API. Not comprehensive, hopefully will be built up over time. -*/ - -#include -#include -#include -#include - -#include -#include - -// TSReleaseAssert() doesn't seem to produce any logging output for a debug build, so do both kinds of assert. -// -#define ALWAYS_ASSERT(EXPR) \ - { \ - TSAssert(EXPR); \ - TSReleaseAssert(EXPR); \ - } - -namespace -{ -#define PINAME "test_tsapi" -char PIName[] = PINAME; - -// NOTE: It's important to flush this after writing so that a gold test using this plugin can examine the log before TS -// terminates. -// -std::fstream logFile; - -TSCont tCont, gCont; - -void -testsForReadReqHdrHook(TSHttpTxn txn) -{ - logFile << "TSHttpTxnEffectiveUrlStringGet(): "; - int urlLength; - char *urlStr = TSHttpTxnEffectiveUrlStringGet(txn, &urlLength); - if (!urlStr) { - logFile << "URL null" << std::endl; - } else if (0 == urlLength) { - logFile << "URL length zero" << std::endl; - } else if (0 > urlLength) { - logFile << "URL length negative" << std::endl; - } else { - logFile << std::string_view(urlStr, urlLength) << std::endl; - - TSfree(urlStr); - } - - logFile << "TSHttpHdrEffectiveUrlBufGet(): "; - { - TSMBuffer hbuf; - TSMLoc hloc; - - if (TSHttpTxnClientReqGet(txn, &hbuf, &hloc) != TS_SUCCESS) { - logFile << "failed to get client request" << std::endl; - - } else { - ts::PostScript ps([=]() -> void { TSHandleMLocRelease(hbuf, TS_NULL_MLOC, hloc); }); - - int64_t url_length; - - if (TSHttpHdrEffectiveUrlBufGet(hbuf, hloc, nullptr, 0, &url_length) != TS_SUCCESS) { - logFile << "sizing call failed " << std::endl; - - } else if (0 == url_length) { - logFile << "zero URL length returned" << std::endl; - - } else { - std::string s(url_length, '?'); - - s += "yada"; - - int64_t url_length2; - - if (TSHttpHdrEffectiveUrlBufGet(hbuf, hloc, s.data(), url_length + 4, &url_length2) != TS_SUCCESS) { - logFile << "data-obtaining call failed" << std::endl; - - } else if (url_length2 != url_length) { - logFile << "second size does not match first" << std::endl; - - } else if (s.substr(url_length, 4) != "yada") { - logFile << "overwrite" << std::endl; - - } else { - logFile << s.substr(0, url_length) << std::endl; - } - } - } - } -} - -int -transactionContFunc(TSCont, TSEvent event, void *eventData) -{ - logFile << "Transaction: event=" << TSHttpEventNameLookup(event) << std::endl; - - TSDebug(PIName, "Transaction: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); - - switch (event) { - case TS_EVENT_HTTP_READ_REQUEST_HDR: { - auto txn = static_cast(eventData); - - testsForReadReqHdrHook(txn); - - TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); - } break; - - default: { - ALWAYS_ASSERT(false) - } break; - - } // end switch - - return 0; -} - -int -globalContFunc(TSCont, TSEvent event, void *eventData) -{ - logFile << "Global: event=" << TSHttpEventNameLookup(event) << std::endl; - - TSDebug(PIName, "Global: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); - - switch (event) { - case TS_EVENT_HTTP_TXN_START: { - auto txn = static_cast(eventData); - - TSHttpTxnHookAdd(txn, TS_HTTP_READ_REQUEST_HDR_HOOK, tCont); - - TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); - } break; - - case TS_EVENT_HTTP_READ_REQUEST_HDR: { - auto txn = static_cast(eventData); - - testsForReadReqHdrHook(txn); - - TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); - } break; - - default: { - ALWAYS_ASSERT(false) - } break; - - } // end switch - - return 0; -} - -} // end anonymous namespace - -void -TSPluginInit(int argc, const char *argv[]) -{ - TSDebug(PIName, "TSPluginInit()"); - - TSPluginRegistrationInfo info; - - info.plugin_name = PIName; - info.vendor_name = "Apache Software Foundation"; - info.support_email = "dev@trafficserver.apache.org"; - - if (TSPluginRegister(&info) != TS_SUCCESS) { - TSError(PINAME ": Plugin registration failed"); - - return; - } - - const char *fileSpec = std::getenv("OUTPUT_FILE"); - - if (nullptr == fileSpec) { - TSError(PINAME ": Environment variable OUTPUT_FILE not found."); - - return; - } - - // Disable output buffering for logFile, so that explicit flushing is not necessary. - logFile.rdbuf()->pubsetbuf(nullptr, 0); - - logFile.open(fileSpec, std::ios::out); - if (!logFile.is_open()) { - TSError(PINAME ": could not open log file \"%s\"", fileSpec); - - return; - } - - // Mutex to protext the logFile object. - // - TSMutex mtx = TSMutexCreate(); - - gCont = TSContCreate(globalContFunc, mtx); - - TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, gCont); - TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, gCont); - - tCont = TSContCreate(transactionContFunc, mtx); -} - -namespace -{ -class Cleanup -{ -public: - ~Cleanup() - { - // In practice it is not strictly necessary to destroy remaining continuations on program exit. - - if (tCont) { - TSContDestroy(tCont); - } - if (gCont) { - TSContDestroy(gCont); - } - } -}; - -// Do any needed cleanup for this source file at program termination time. -// -Cleanup cleanup; - -} // end anonymous namespace