blob: e9a53abd19364ef8417ccf1f9ae9aeed2f25de64 [file] [log] [blame]
/*
* 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);
}