| /* |
| * 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 test code for TS API HTTP hooks. The code assumes there will only be one active transaction at a time. It |
| verifies the event data parameter to the continuations triggered by the hooks is correct. |
| */ |
| |
| #include <fstream> |
| #include <cstdlib> |
| |
| #include <ts/ts.h> |
| |
| // 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_hooks" |
| char PIName[] = PINAME; |
| |
| DbgCtl dbg_ctl{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; |
| |
| TSVConn activeVConn; |
| |
| TSHttpSsn activeSsn; |
| |
| TSHttpTxn activeTxn; |
| |
| int |
| transactionContFunc(TSCont, TSEvent event, void *eventData) |
| { |
| logFile << "Transaction: event=" << TSHttpEventNameLookup(event) << std::endl; |
| |
| Dbg(dbg_ctl, "Transaction: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); |
| |
| switch (event) { |
| case TS_EVENT_HTTP_TXN_CLOSE: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Transaction: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| // Don't assume any order of continuation execution on the same hook. |
| ALWAYS_ASSERT((txn == activeTxn) or !activeTxn) |
| |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_READ_REQUEST_HDR: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Transaction: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| ALWAYS_ASSERT(txn == activeTxn) |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| default: { |
| ALWAYS_ASSERT(false) |
| } break; |
| |
| } // end switch |
| |
| return 0; |
| } |
| |
| TSCont tCont; |
| |
| int |
| sessionContFunc(TSCont, TSEvent event, void *eventData) |
| { |
| logFile << "Session: event=" << TSHttpEventNameLookup(event) << std::endl; |
| |
| Dbg(dbg_ctl, "Session: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); |
| |
| switch (event) { |
| case TS_EVENT_HTTP_SSN_CLOSE: { |
| auto ssn = static_cast<TSHttpSsn>(eventData); |
| |
| // Don't assume any order of continuation execution on the same hook. |
| ALWAYS_ASSERT((ssn == activeSsn) or !activeSsn) |
| |
| TSHttpSsnReenable(ssn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_TXN_START: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| // Don't assume any order of continuation execution on the same hook. |
| ALWAYS_ASSERT((txn == activeTxn) or !activeTxn) |
| |
| Dbg(dbg_ctl, "Session: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnHookAdd(txn, TS_HTTP_READ_REQUEST_HDR_HOOK, tCont); |
| TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, tCont); |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_TXN_CLOSE: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Session: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| // Don't assume any order of continuation execution on the same hook. |
| ALWAYS_ASSERT((txn == activeTxn) or !activeTxn) |
| |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_READ_REQUEST_HDR: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Session: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| ALWAYS_ASSERT(txn == activeTxn) |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| default: { |
| ALWAYS_ASSERT(false) |
| } break; |
| |
| } // end switch |
| |
| return 0; |
| } |
| |
| TSCont sCont; |
| |
| int |
| globalContFunc(TSCont, TSEvent event, void *eventData) |
| { |
| logFile << "Global: event=" << TSHttpEventNameLookup(event) << std::endl; |
| |
| Dbg(dbg_ctl, "Global: event=%s(%d) eventData=%p", TSHttpEventNameLookup(event), event, eventData); |
| |
| switch (event) { |
| case TS_EVENT_VCONN_START: { |
| ALWAYS_ASSERT(!activeVConn) |
| |
| auto vConn = static_cast<TSVConn>(eventData); |
| |
| activeVConn = vConn; |
| |
| logFile << "Global: ssl flag=" << TSVConnIsSsl(vConn) << std::endl; |
| |
| TSVConnReenable(vConn); |
| } break; |
| case TS_EVENT_SSL_CERT: |
| case TS_EVENT_SSL_SERVERNAME: { |
| auto vConn = static_cast<TSVConn>(eventData); |
| |
| ALWAYS_ASSERT(vConn == activeVConn) |
| |
| logFile << "Global: ssl flag=" << TSVConnIsSsl(vConn) << std::endl; |
| |
| TSVConnReenable(vConn); |
| } break; |
| |
| case TS_EVENT_VCONN_CLOSE: { |
| auto vConn = static_cast<TSVConn>(eventData); |
| |
| ALWAYS_ASSERT(vConn == activeVConn) |
| |
| logFile << "Global: ssl flag=" << TSVConnIsSsl(vConn) << std::endl; |
| |
| TSVConnReenable(vConn); |
| |
| activeVConn = nullptr; |
| } break; |
| |
| case TS_EVENT_HTTP_SSN_START: { |
| ALWAYS_ASSERT(!activeSsn) |
| |
| auto ssn = static_cast<TSHttpSsn>(eventData); |
| |
| activeSsn = ssn; |
| |
| TSHttpSsnHookAdd(ssn, TS_HTTP_READ_REQUEST_HDR_HOOK, sCont); |
| TSHttpSsnHookAdd(ssn, TS_HTTP_SSN_CLOSE_HOOK, sCont); |
| TSHttpSsnHookAdd(ssn, TS_HTTP_TXN_START_HOOK, sCont); |
| TSHttpSsnHookAdd(ssn, TS_HTTP_TXN_CLOSE_HOOK, sCont); |
| |
| TSHttpSsnReenable(ssn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_SSN_CLOSE: { |
| auto ssn = static_cast<TSHttpSsn>(eventData); |
| |
| ALWAYS_ASSERT(ssn == activeSsn) |
| |
| activeSsn = nullptr; |
| |
| TSHttpSsnReenable(ssn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_TXN_START: { |
| ALWAYS_ASSERT(!activeTxn) |
| |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Global: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| activeTxn = txn; |
| |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_TXN_CLOSE: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Global: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| ALWAYS_ASSERT(txn == activeTxn) |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| activeTxn = nullptr; |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| case TS_EVENT_HTTP_READ_REQUEST_HDR: { |
| auto txn = static_cast<TSHttpTxn>(eventData); |
| |
| Dbg(dbg_ctl, "Global: ssn=%p", TSHttpTxnSsnGet(txn)); |
| |
| ALWAYS_ASSERT(txn == activeTxn) |
| ALWAYS_ASSERT(TSHttpTxnSsnGet(txn) == activeSsn) |
| |
| TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE); |
| } break; |
| |
| default: { |
| ALWAYS_ASSERT(false) |
| } break; |
| |
| } // end switch |
| |
| return 0; |
| } |
| |
| TSCont gCont; |
| |
| } // end anonymous namespace |
| |
| void |
| TSPluginInit(int /* argc ATS_UNUSED */, const char ** /* argv ATS_UNUSED */) |
| { |
| 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 protect the logFile object. |
| // |
| TSMutex mtx = TSMutexCreate(); |
| |
| gCont = TSContCreate(globalContFunc, mtx); |
| |
| // Setup the global hook |
| TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, gCont); |
| TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, gCont); |
| TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, gCont); |
| TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, gCont); |
| TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, gCont); |
| TSHttpHookAdd(TS_SSL_CERT_HOOK, gCont); |
| TSHttpHookAdd(TS_SSL_SERVERNAME_HOOK, gCont); |
| // NOTE: as of January 2019 these two hooks are only triggered for TLS connections. It seems that, at trafficserver |
| // startup, spurious data on the TLS TCP port may cause trafficserver to attempt (and fail) to create a TLS |
| // connection. If this happens, it will result in TS_VCONN_START_HOOK being triggered, and then TS_VCONN_CLOSE_HOOK |
| // will be triggered when the connection closes due to failure. |
| // |
| TSHttpHookAdd(TS_VCONN_START_HOOK, gCont); |
| TSHttpHookAdd(TS_VCONN_CLOSE_HOOK, gCont); |
| |
| // TSHttpHookAdd(TS_SSL_SESSION_HOOK, gCont); -- Event is TS_EVENT_SSL_SESSION_NEW -- Event data is TSHttpSsn |
| // TSHttpHookAdd(TS_SSL_SERVER_VERIFY_HOOK, gCont); |
| // TSHttpHookAdd(TS_SSL_VERIFY_CLIENT_HOOK, gCont); |
| // TSHttpHookAdd(TS_VCONN_OUTBOUND_START_HOOK, gCont); |
| // TSHttpHookAdd(TS_VCONN_OUTBOUND_CLOSE_HOOK, gCont); |
| |
| sCont = TSContCreate(sessionContFunc, mtx); |
| |
| tCont = TSContCreate(transactionContFunc, mtx); |
| } |