blob: 715673ccb050170895add42ab5399b9ed5e80721 [file]
/*
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.
*/
#pragma once
#include "ts/ts.h"
#include "ts/remap.h"
#include "cripts/Bundle.hpp"
#include "cripts/Context.hpp"
// Case hierarchy, this is from ATS ts_meta.h.
template <unsigned N> struct CaseTag : public CaseTag<N - 1> {
constexpr CaseTag() = default;
static constexpr unsigned value = N;
};
template <> struct CaseTag<0> {
constexpr CaseTag() = default;
static constexpr unsigned value = 0;
};
static constexpr CaseTag<9> CaseArg{};
inline void
CaseVoidFunc()
{
}
// See if we should setup the send_response hook.
// ToDo: The execute flag was weird, but necessary for clang++ (at least) to not
// complain about the _do_send_response symbol not being defined for the case
// when no do_send_response() is used.
// ToDo: With ATS 10.x, we have support in the remap plugin init structure to
// probe available plugin symbols through the dlopen/dlsym APIs. We should use
// that instead of this.
// do-remap pseudo hook caller
template <typename T>
auto
wrap_do_remap(T *context, bool execute, CaseTag<1>) -> decltype(_do_remap(context), bool())
{
if (execute) {
_do_remap(context);
}
return true;
}
template <typename T>
auto
wrap_do_remap(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// post-remap-header hook caller
template <typename T>
auto
wrap_post_remap(T *context, bool execute, CaseTag<1>) -> decltype(_do_post_remap(context), bool())
{
if (execute) {
_do_post_remap(context);
}
return true;
}
template <typename T>
auto
wrap_post_remap(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// send-response-header hook caller
template <typename T>
auto
wrap_send_response(T *context, bool execute, CaseTag<1>) -> decltype(_do_send_response(context), bool())
{
if (execute) {
_do_send_response(context);
}
return true;
}
template <typename T>
auto
wrap_send_response(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// send-request-header hook caller
template <typename T>
auto
wrap_send_request(T *context, bool execute, CaseTag<1>) -> decltype(_do_send_request(context), bool())
{
if (execute) {
_do_send_request(context);
}
return true;
}
template <typename T>
auto
wrap_send_request(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// read-response-header hook caller
template <typename T>
auto
wrap_read_response(T *context, bool execute, CaseTag<1>) -> decltype(_do_read_response(context), bool())
{
if (execute) {
_do_read_response(context);
}
return true;
}
template <typename T>
auto
wrap_read_response(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// cache-lookup-complete hook caller
template <typename T>
auto
wrap_cache_lookup(T *context, bool execute, CaseTag<1>) -> decltype(_do_cache_lookup(context), bool())
{
if (execute) {
_do_cache_lookup(context);
}
return true;
}
template <typename T>
auto
wrap_cache_lookup(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// txn-close hook caller
template <typename T>
auto
wrap_txn_close(T *context, bool execute, CaseTag<1>) -> decltype(_do_txn_close(context), bool())
{
if (execute) {
_do_txn_close(context);
}
return true;
}
template <typename T>
auto
wrap_txn_close(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// do_init caller (not a hook, but called when a Cript is loaded.
// Note that the "context" here is a different context, it's the remap plugin
// data.
template <typename T>
auto
wrap_plugin_init(T *context, bool execute, CaseTag<1>) -> decltype(_do_init(context), bool())
{
if (execute) {
_do_init(context);
}
return true;
}
template <typename T>
auto
wrap_plugin_init(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// create/delete instance caller (not a hook, but called when a Cript is used in a
// remap rule). Note that the "context" here is a different context, it's the
// instance data.
template <typename T>
auto
wrap_create_instance(T *context, bool execute, CaseTag<1>) -> decltype(_do_create_instance(context), bool())
{
if (execute) {
_do_create_instance(context);
}
return true;
}
template <typename T>
auto
wrap_create_instance(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
template <typename T>
auto
wrap_delete_instance(T *context, bool execute, CaseTag<1>) -> decltype(_do_delete_instance(context), bool())
{
if (execute) {
_do_delete_instance(context);
}
return true;
}
template <typename T>
auto
wrap_delete_instance(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// Next are all the wrappers for the global hooks. We intentionally name and handle these different,
// to allow a Cript to define both global and per remap hooks.
// global txn-start hook caller
template <typename T>
auto
wrap_glb_txn_start(T *context, bool execute, CaseTag<1>) -> decltype(_glb_txn_start(context), bool())
{
if (execute) {
_glb_txn_start(context);
}
return true;
}
template <typename T>
auto
wrap_glb_txn_start(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global read-request hook caller
template <typename T>
auto
wrap_glb_read_request(T *context, bool execute, CaseTag<1>) -> decltype(_glb_read_request(context), bool())
{
if (execute) {
_glb_read_request(context);
}
return true;
}
template <typename T>
auto
wrap_glb_read_request(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global pre-remap hook caller
template <typename T>
auto
wrap_glb_pre_remap(T *context, bool execute, CaseTag<1>) -> decltype(_glb_pre_remap(context), bool())
{
if (execute) {
_glb_pre_remap(context);
}
return true;
}
template <typename T>
auto
wrap_glb_pre_remap(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global post-remap hook caller
template <typename T>
auto
wrap_glb_post_remap(T *context, bool execute, CaseTag<1>) -> decltype(_glb_post_remap(context), bool())
{
if (execute) {
_glb_post_remap(context);
}
return true;
}
template <typename T>
auto
wrap_glb_post_remap(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global cache-lookup hook caller
template <typename T>
auto
wrap_glb_cache_lookup(T *context, bool execute, CaseTag<1>) -> decltype(_glb_cache_lookup(context), bool())
{
if (execute) {
_glb_cache_lookup(context);
}
return true;
}
template <typename T>
auto
wrap_glb_cache_lookup(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global send-request hook caller
template <typename T>
auto
wrap_glb_send_request(T *context, bool execute, CaseTag<1>) -> decltype(_glb_send_request(context), bool())
{
if (execute) {
_glb_send_request(context);
}
return true;
}
template <typename T>
auto
wrap_glb_send_request(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global read-response hook caller
template <typename T>
auto
wrap_glb_read_response(T *context, bool execute, CaseTag<1>) -> decltype(_glb_read_response(context), bool())
{
if (execute) {
_glb_read_response(context);
}
return true;
}
template <typename T>
auto
wrap_glb_read_response(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global send-response hook caller
template <typename T>
auto
wrap_glb_send_response(T *context, bool execute, CaseTag<1>) -> decltype(_glb_send_response(context), bool())
{
if (execute) {
_glb_send_response(context);
}
return true;
}
template <typename T>
auto
wrap_glb_send_response(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// global txn-close hook caller
template <typename T>
auto
wrap_glb_txn_close(T *context, bool execute, CaseTag<1>) -> decltype(_glb_txn_close(context), bool())
{
if (execute) {
_glb_txn_close(context);
}
return true;
}
template <typename T>
auto
wrap_glb_txn_close(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// glb_init caller (not a hook, called when the global plugin is initialized).
template <typename T>
auto
wrap_glb_init(T *context, bool execute, CaseTag<1>) -> decltype(_glb_init(context), bool())
{
if (execute) {
_glb_init(context);
}
return true;
}
template <typename T>
auto
wrap_glb_init(T * /* context ATS_UNUSED */, bool /* execute ATS_UNUSED */, CaseTag<0>) -> bool
{
return false;
}
// This is the HTTP transaction continuation, which is used for all the HTTP hooks.
int
http_txn_cont(TSCont contp, TSEvent event, void *edata)
{
auto txnp = static_cast<TSHttpTxn>(edata);
auto *context = static_cast<cripts::Context *>(TSContDataGet(contp));
// ToDo: We can optimize this once we have header heap generation IDs in place.
context->reset(); // Clears the cached handles to internal ATS data (mloc's etc.)
switch (event) {
// This is only used for global plugin, sine DoRemap() is handled without a continuation
case TS_EVENT_HTTP_READ_REQUEST_HDR: // 60002
context->state.hook = TS_HTTP_READ_REQUEST_HDR_HOOK;
if (!context->state.error.Failed()) {
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() && context->p_instance.Callbacks() & cripts::Callbacks::GLB_READ_REQUEST) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & cripts::Callbacks::GLB_READ_REQUEST) {
bundle->doSendRequest(context);
}
}
}
CDebug("Entering glb_read_request()");
wrap_glb_read_request(context, true, CaseArg);
if (context->_urls.request.Modified()) {
context->_urls.request._update(); // Make sure any changes to the request URL is updated
}
}
break;
case TS_EVENT_HTTP_SEND_REQUEST_HDR: // 60004
context->state.hook = TS_HTTP_SEND_REQUEST_HDR_HOOK;
if (!context->state.error.Failed()) {
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_SEND_REQUEST | cripts::Callbacks::GLB_SEND_REQUEST)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_SEND_REQUEST | cripts::Callbacks::GLB_SEND_REQUEST)) {
bundle->doSendRequest(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_TXN_CLOSE) {
CDebug("Entering do_send_request()");
wrap_send_request(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_SEND_REQUEST) {
CDebug("Entering glb_send_request()");
wrap_glb_send_request(context, true, CaseArg);
}
if (context->_urls.request.Modified()) {
context->_urls.request._update(); // Make sure any changes to the request URL is updated
}
}
break;
case TS_EVENT_HTTP_READ_RESPONSE_HDR: // 60006
context->state.hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
if (!context->state.error.Failed()) {
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_READ_RESPONSE | cripts::Callbacks::GLB_READ_RESPONSE)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_READ_RESPONSE | cripts::Callbacks::GLB_READ_RESPONSE)) {
bundle->doReadResponse(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_READ_RESPONSE) {
CDebug("Entering do_read_response()");
wrap_read_response(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_READ_RESPONSE) {
CDebug("Entering glb_read_response()");
wrap_glb_read_response(context, true, CaseArg);
}
}
break;
case TS_EVENT_HTTP_SEND_RESPONSE_HDR: // 60007
context->state.hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK;
if (!context->state.error.Failed()) {
CDebug("Entering do_send_response()");
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_SEND_RESPONSE | cripts::Callbacks::GLB_SEND_RESPONSE)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_SEND_RESPONSE | cripts::Callbacks::GLB_SEND_RESPONSE)) {
bundle->doSendResponse(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_SEND_RESPONSE) {
wrap_send_response(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_SEND_RESPONSE) {
wrap_glb_send_response(context, true, CaseArg);
}
}
break;
case TS_EVENT_HTTP_TXN_CLOSE: // 60012
context->state.hook = TS_HTTP_TXN_CLOSE_HOOK;
if (context->state.enabled_hooks & (cripts::Callbacks::DO_TXN_CLOSE | cripts::Callbacks::GLB_TXN_CLOSE)) {
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_TXN_CLOSE | cripts::Callbacks::GLB_TXN_CLOSE)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_TXN_CLOSE | cripts::Callbacks::GLB_TXN_CLOSE)) {
bundle->doTxnClose(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_TXN_CLOSE) {
CDebug("Entering do_txn_close()");
wrap_txn_close(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_TXN_CLOSE) {
CDebug("Entering glb_txn_close()");
wrap_glb_txn_close(context, true, CaseArg);
}
}
TSContDestroy(contp);
context->Release();
break;
case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: // 60015
context->state.hook = TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK;
if (!context->state.error.Failed()) {
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_CACHE_LOOKUP | cripts::Callbacks::GLB_CACHE_LOOKUP)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_CACHE_LOOKUP | cripts::Callbacks::GLB_CACHE_LOOKUP)) {
bundle->doCacheLookup(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_CACHE_LOOKUP) {
CDebug("Entering do_cache_lookup()");
wrap_cache_lookup(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_CACHE_LOOKUP) {
CDebug("Entering glb_cache_lookup()");
wrap_cache_lookup(context, true, CaseArg);
}
}
break;
case TS_EVENT_HTTP_PRE_REMAP: // 60016, this is never usable in a remap plugin
context->state.hook = TS_HTTP_PRE_REMAP_HOOK;
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() && context->p_instance.Callbacks() & cripts::Callbacks::GLB_PRE_REMAP) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::GLB_PRE_REMAP)) {
bundle->doPostRemap(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::GLB_PRE_REMAP) {
CDebug("Entering glb_pre_remap()");
wrap_glb_pre_remap(context, true, CaseArg);
}
if (!context->state.error.Failed()) {
if (context->_cache.url.Modified()) {
context->_cache.url._update(); // Make sure the cache-key gets updated, if modified
}
if (context->_urls.request.Modified()) {
context->_urls.request._update(); // Make sure any changes to the request URL is updated
}
}
break;
case TS_EVENT_HTTP_POST_REMAP: // 60017
context->state.hook = TS_HTTP_POST_REMAP_HOOK;
// Call any bundle callbacks that are registered for this hook
if (!context->state.error.Failed() &&
context->p_instance.Callbacks() & (cripts::Callbacks::DO_POST_REMAP | cripts::Callbacks::GLB_POST_REMAP)) {
for (auto &bundle : context->p_instance.bundles) {
if (bundle->Callbacks() & (cripts::Callbacks::DO_POST_REMAP | cripts::Callbacks::GLB_POST_REMAP)) {
bundle->doPostRemap(context);
}
}
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_POST_REMAP) {
CDebug("Entering do_post_remap()");
wrap_post_remap(context, true, CaseArg);
} else if (context->state.enabled_hooks & cripts::Callbacks::GLB_POST_REMAP) {
CDebug("Entering glb_post_remap()");
wrap_glb_post_remap(context, true, CaseArg);
}
if (!context->state.error.Failed()) {
if (context->_cache.url.Modified()) {
context->_cache.url._update(); // Make sure the cache-key gets updated, if modified
}
if (context->_urls.request.Modified()) {
context->_urls.request._update(); // Make sure any changes to the request URL is updated
}
}
break;
default:
CFatal("Cripts continuation: Unknown event %d", event);
break;
}
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
return 0;
}
// This is the global continuation, used to deal with various hooks as well as setting up
// a per TXN continuation if needed. ToDo: Other non-TXN hooks should be added here.
int
global_cont(TSCont contp, TSEvent event, void *edata)
{
// Duplicated, but this is cheap and we don't need it in the instance.
static bool has_glb_txn_start = wrap_glb_txn_start(static_cast<cripts::Context *>(nullptr), false, CaseArg);
auto glb_ctx = static_cast<cripts::InstanceContext *>(TSContDataGet(contp));
auto txnp = static_cast<TSHttpTxn>(edata);
auto ssnp = TSHttpTxnSsnGet(txnp);
auto enabled_hooks = glb_ctx->p_instance.Callbacks();
switch (event) {
case TS_EVENT_HTTP_TXN_START: {
auto *context = cripts::Context::Factory(txnp, ssnp, nullptr, glb_ctx->p_instance);
context->state.hook = TS_HTTP_TXN_START_HOOK;
context->state.enabled_hooks = enabled_hooks;
if (has_glb_txn_start) {
wrap_glb_txn_start(context, true, CaseArg);
}
if (enabled_hooks) {
context->contp = TSContCreate(http_txn_cont, nullptr);
TSContDataSet(context->contp, context);
if (enabled_hooks & cripts::Callbacks::GLB_READ_REQUEST) {
TSHttpTxnHookAdd(txnp, TS_HTTP_READ_REQUEST_HDR_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_PRE_REMAP) {
TSHttpTxnHookAdd(txnp, TS_HTTP_PRE_REMAP_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_POST_REMAP) {
TSHttpTxnHookAdd(txnp, TS_HTTP_POST_REMAP_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_CACHE_LOOKUP) {
TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_SEND_REQUEST) {
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_READ_RESPONSE) {
TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, context->contp);
}
if (enabled_hooks & cripts::Callbacks::GLB_SEND_RESPONSE) {
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, context->contp);
}
TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, context->contp); // Release the context later
} else {
context->Release();
}
} break;
// ToDo: Add handlers for other non-HTTP hooks here.
default:
CFatal("Cripts continuation: Unknown event %d", event);
break;
}
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
return 0;
}
extern void global_initialization();
extern pthread_once_t init_once_control;
// This sets up this Cript as a global plugin. This uses the glb_ prefix for all
// the callbacks. This would only be called if the cript is added to plugins.config.
void
TSPluginInit(int argc, const char *argv[])
{
static bool has_glb_txn_start = wrap_glb_txn_start(static_cast<cripts::Context *>(nullptr), false, CaseArg);
static unsigned enabled_txn_hooks =
(wrap_glb_read_request(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_READ_REQUEST :
cripts::Callbacks::NONE) |
(wrap_glb_pre_remap(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_PRE_REMAP :
cripts::Callbacks::NONE) |
(wrap_glb_post_remap(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_POST_REMAP :
cripts::Callbacks::NONE) |
(wrap_glb_cache_lookup(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_CACHE_LOOKUP :
cripts::Callbacks::NONE) |
(wrap_glb_send_request(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_SEND_REQUEST :
cripts::Callbacks::NONE) |
(wrap_glb_read_response(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_READ_RESPONSE :
cripts::Callbacks::NONE) |
(wrap_glb_send_response(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_SEND_RESPONSE :
cripts::Callbacks::NONE) |
(wrap_glb_txn_close(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::GLB_TXN_CLOSE :
cripts::Callbacks::NONE);
// ToDo: Add more global hooks here in enabled_other_hooks
TSPluginRegistrationInfo info;
auto *inst = new cripts::Instance(argc, argv, false);
info.plugin_name = (char *)inst->plugin_debug_tag.c_str();
info.vendor_name = (char *)"Apache Software Foundation";
info.support_email = (char *)"dev@trafficserver.apache.org";
if (TS_SUCCESS != TSPluginRegister(&info)) {
TSError("[%s] plugin registration failed", info.plugin_name);
delete inst;
return;
}
// ToDo: This InstanceContext should also be usabled / used by other non-HTTP hooks.
auto *context = new cripts::InstanceContext(*inst);
pthread_once(&init_once_control, global_initialization);
bool needs_glb_init = wrap_glb_init(context, false, CaseArg);
if (needs_glb_init) {
wrap_glb_init(context, true, CaseArg);
}
if (has_glb_txn_start || enabled_txn_hooks > 0) {
auto contp = TSContCreate(global_cont, nullptr);
inst->NeedCallback(enabled_txn_hooks);
TSContDataSet(contp, context);
TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, contp); // This acts similarly to the DoRemap callback
} else {
if (needs_glb_init) {
CDebug("[%s] - No global hooks, but there is a global init callback", info.plugin_name);
} else {
TSError("[%s] - No global hooks, no global init callback", info.plugin_name);
}
delete context;
delete inst;
}
}
// This section is for Cripts as a remap plugin. They all use the do_ prefix for all
// callbacks.
TSReturnCode
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
{
if (!api_info) {
strncpy(errbuf, "[TSRemapInit] - Invalid TSRemapInterface argument", errbuf_size - 1);
return TS_ERROR;
}
if (api_info->size < sizeof(TSRemapInterface)) {
strncpy(errbuf, "[TSRemapInit] - Incorrect size of TSRemapInterface structure", errbuf_size - 1);
return TS_ERROR;
}
if (api_info->tsremap_version < TSREMAP_VERSION) {
snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
(api_info->tsremap_version & 0xffff));
return TS_ERROR;
}
pthread_once(&init_once_control, global_initialization);
// This is to check, and call, the Cript's do_init() if provided.
// Note that the context here is not a Cript context, but rather tha API info
bool needs_plugin_init = wrap_plugin_init(api_info, false, CaseArg);
if (needs_plugin_init) {
wrap_plugin_init(api_info, true, CaseArg);
}
return TS_SUCCESS;
}
TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSED */
,
int /* errbuf_size ATS_UNUSED */)
{
auto *inst = new cripts::Instance(argc, const_cast<const char **>(argv));
cripts::InstanceContext context(*inst);
bool needs_create_instance = wrap_create_instance(&context, false, CaseArg);
if (needs_create_instance) {
wrap_create_instance(&context, true, CaseArg);
}
if (!inst->Failed()) {
std::vector<cripts::Bundle::Error> errors;
for (auto &bundle : inst->bundles) {
// Collect all the callbacks needed from all bundles, and validate them
if (bundle->Validate(errors)) {
inst->NeedCallback(bundle->Callbacks());
}
}
if (!errors.empty()) {
TSError("[Cript: %s] - Failed to validate callbacks for the following bundles:", inst->plugin_debug_tag.c_str());
for (auto &err : errors) {
TSError("[Cript: %s] \tIn Bundle %.*s, option %.*s()", inst->plugin_debug_tag.c_str(),
static_cast<int>(err.Bundle().size()), err.Bundle().data(), static_cast<int>(err.Option().size()),
err.Option().data());
TSError("[Cript: %s] \t\t-> %.*s", inst->plugin_debug_tag.c_str(), static_cast<int>(err.Message().size()),
err.Message().data());
}
delete inst;
return TS_ERROR;
}
*ih = static_cast<void *>(inst);
inst->debug("Created a new instance for Cript = {}", inst->plugin_debug_tag);
inst->debug("The context data size = {}", sizeof(cripts::Context));
return TS_SUCCESS;
} else {
delete inst; // Cleanup the instance data, which can't be used now
return TS_ERROR;
}
}
void
TSRemapDeleteInstance(void *ih)
{
auto inst = static_cast<cripts::Instance *>(ih);
cripts::InstanceContext context(*inst);
bool needs_delete_instance = wrap_delete_instance(&context, false, CaseArg);
if (needs_delete_instance) {
wrap_delete_instance(&context, true, CaseArg);
}
inst->debug("Deleted an instance for Cript = {}", inst->plugin_debug_tag);
delete inst;
}
TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
{
// Check to see if we need to setup future hooks with a continuation. This
// should only happen once.
static bool needs_do_remap = wrap_do_remap(static_cast<cripts::Context *>(nullptr), false, CaseArg);
static uint32_t enabled_hooks =
(wrap_post_remap(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_POST_REMAP :
cripts::Callbacks::NONE) |
(wrap_cache_lookup(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_CACHE_LOOKUP :
cripts::Callbacks::NONE) |
(wrap_send_request(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_SEND_REQUEST :
cripts::Callbacks::NONE) |
(wrap_read_response(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_READ_RESPONSE :
cripts::Callbacks::NONE) |
(wrap_send_response(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_SEND_RESPONSE :
cripts::Callbacks::NONE) |
(wrap_txn_close(static_cast<cripts::Context *>(nullptr), false, CaseArg) ? cripts::Callbacks::DO_TXN_CLOSE :
cripts::Callbacks::NONE);
auto ssnp = TSHttpTxnSsnGet(txnp);
auto *inst = static_cast<cripts::Instance *>(ih);
auto bundle_cbs = inst->Callbacks();
auto *context = cripts::Context::Factory(txnp, ssnp, rri, *inst);
bool keep_context = false;
context->state.hook = TS_HTTP_READ_REQUEST_HDR_HOOK; // Not quite true
context->state.enabled_hooks = (enabled_hooks | bundle_cbs);
if (needs_do_remap || bundle_cbs & cripts::Callbacks::DO_REMAP) {
CDebug("Entering do_remap()");
for (auto &bundle : context->p_instance.bundles) {
bundle->doRemap(context);
}
if (!context->state.error.Failed()) {
wrap_do_remap(context, true, CaseArg); // This can fail the context
}
}
// Don't do the callbacks when we are in a failure state.
if (!context->state.error.Failed()) {
if (context->_cache.url.Modified()) {
context->_cache.url._update(); // Make sure the cache-key gets updated, if modified
}
if (context->_urls.request.Modified()) {
context->_urls.request._update(); // Make sure any changes to the request URL is updated
}
if (context->state.enabled_hooks >= cripts::Callbacks::DO_POST_REMAP) {
context->contp = TSContCreate(http_txn_cont, nullptr);
TSContDataSet(context->contp, context);
if (context->state.enabled_hooks & cripts::Callbacks::DO_POST_REMAP) {
TSHttpTxnHookAdd(txnp, TS_HTTP_POST_REMAP_HOOK, context->contp);
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_SEND_RESPONSE) {
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, context->contp);
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_SEND_REQUEST) {
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, context->contp);
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_READ_RESPONSE) {
TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, context->contp);
}
if (context->state.enabled_hooks & cripts::Callbacks::DO_CACHE_LOOKUP) {
TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, context->contp);
}
// This iremaps needed when we have at least one Txn Hook. It will also
// call the Cript's callback, if it exists.
TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, context->contp);
// Make sure we keep the context
keep_context = true;
}
}
// Check and deal with any failures here. Failures here are considered
// catastrophic, and should give an error. ToDo: Do we want to have different
// levels of failure here? Non-fatal vs fatal?
context->state.error.Execute(context);
// For now, we always allocate the context, but a possible future optimization
// could be to use stack allocation when there is only a do_remap() callback.
if (!keep_context) {
context->Release();
}
// See if the Client URL was modified, which dicates the return code here.
if (context->_urls.request.Modified()) {
context->p_instance.debug("Client::URL was modified, returning TSREMAP_DID_REMAP");
return TSREMAP_DID_REMAP;
} else {
context->p_instance.debug("Client::URL was NOT modified, returning TSREMAP_NO_REMAP");
return TSREMAP_NO_REMAP;
}
}