Updated Branches:
refs/heads/master 2568c6f86 -> 55f83a3a3
TS-1324: lua plugin hooks and enhancements
Enhancements:
- Extend the lua remap plugin to be both a remap and full plugin.
- Add LuaConfigApiInit for ts.config library to expose the configuration
override APIs.
- First pass at Lua hook registration and demuxing using ts.hook.register().
- Remove init hook from lua remap. There's no need for an init hook when you
can just run code at global scope.
Various cleanups:
- Minor lutil.h cleanups.
- Use lua.hpp.
- Fix --enable-luajit help string.
- Use TSmalloc to allocate C++ objects.
- Make a single Lua plugin that does remap and general stuff.
- Move LuaPluginNewState() to general util code.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/55f83a3a
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/55f83a3a
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/55f83a3a
Branch: refs/heads/master
Commit: 55f83a3a3dc32fb50ce34bfe0c23ad816a7336a6
Parents: 2568c6f
Author: James Peach <[email protected]>
Authored: Mon May 21 21:01:33 2012 -0700
Committer: James Peach <[email protected]>
Committed: Tue Jul 10 13:08:00 2012 -0700
----------------------------------------------------------------------
CHANGES | 2 +
build/lua.m4 | 2 +-
plugins/experimental/lua/Makefile.am | 3 +-
plugins/experimental/lua/example.lua | 6 -
plugins/experimental/lua/hook.cc | 195 ++++++++++++++++++++++++++
plugins/experimental/lua/hook.h | 22 +++
plugins/experimental/lua/lapi.cc | 191 +++++++++++++++++++++-----
plugins/experimental/lua/lapi.h | 27 +++-
plugins/experimental/lua/lconfig.cc | 148 +++++++++++++++++++
plugins/experimental/lua/lua.cc | 218 -----------------------------
plugins/experimental/lua/lutil.cc | 103 +++++++++++++-
plugins/experimental/lua/lutil.h | 101 +++++++++++++-
plugins/experimental/lua/plugin.cc | 50 +++++++
plugins/experimental/lua/remap.cc | 131 +++++++++++++++++
14 files changed, 928 insertions(+), 271 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index c7b3c3b..82382e7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,7 @@
-*- coding: utf-8 -*-
Changes with Apache Traffic Server 3.3.0
+ *) [TS-1342] Lua plugin initial hook support
+
*) [TS-1314] Remove TS_ARG_MAX usage so that platforms with
unlimited ARG_MAX can build correctly.
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/build/lua.m4
----------------------------------------------------------------------
diff --git a/build/lua.m4 b/build/lua.m4
index 71e4e29..187c230 100644
--- a/build/lua.m4
+++ b/build/lua.m4
@@ -132,7 +132,7 @@ if test -z "${LUA_LIBS}"; then
else
AC_MSG_NOTICE([using '${LUA_LIBS}' for Lua Library])
AC_ARG_ENABLE(luajit,
- APACHE_HELP_STRING(--enable-luajit,Enable LuaJit Support),
+ AC_HELP_STRING(--enable-luajit,Enable LuaJit Support),
TS_ADDTO(CPPFLAGS, ["-DTS_ENABLE_LUAJIT"]))
ifelse([$1], , , $1)
fi
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/Makefile.am
b/plugins/experimental/lua/Makefile.am
index 81fb201..7d0851a 100644
--- a/plugins/experimental/lua/Makefile.am
+++ b/plugins/experimental/lua/Makefile.am
@@ -23,11 +23,10 @@ AM_CXXFLAGS = \
-I$(top_builddir)/proxy/api \
-I$(top_srcdir)/proxy/api
-
pkglib_LTLIBRARIES = lua.la
lua_la_LIBADD = ${LUA_LIBS}
-lua_la_SOURCES = lua.cc lapi.cc lutil.cc
+lua_la_SOURCES = remap.cc plugin.cc lapi.cc lutil.cc lconfig.cc hook.cc
lua_la_LDFLAGS = -module -avoid-version -shared
endif
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/example.lua
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/example.lua
b/plugins/experimental/lua/example.lua
index a57c601..dabbe55 100644
--- a/plugins/experimental/lua/example.lua
+++ b/plugins/experimental/lua/example.lua
@@ -55,9 +55,3 @@ function remap(request)
end
end
-
--- Optional module initialization hook.
-function init()
- TS.debug("example", string.format('init called by Traffic Server %s',
TS.VERSION));
- return true
-end
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/hook.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/hook.cc b/plugins/experimental/lua/hook.cc
new file mode 100644
index 0000000..8b5e16b
--- /dev/null
+++ b/plugins/experimental/lua/hook.cc
@@ -0,0 +1,195 @@
+/*
+ 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 <ts/ts.h>
+#include <ts/remap.h>
+#include <string.h>
+#include "lapi.h"
+#include "lutil.h"
+#include "hook.h"
+
+#include <memory> // placement new
+
+struct HookDemuxEntry
+{
+ const char * name;
+ int hookid;
+ TSCont cont;
+};
+
+#define HOOK_DEMUX_ENTRY(HOOKID) { #HOOKID, TS_ ## HOOKID, NULL }
+
+static HookDemuxEntry
+HttpHookDemuxTable[TS_HTTP_LAST_HOOK] = {
+ HOOK_DEMUX_ENTRY(HTTP_READ_REQUEST_HDR_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_OS_DNS_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_SEND_REQUEST_HDR_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_READ_CACHE_HDR_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_READ_RESPONSE_HDR_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_SEND_RESPONSE_HDR_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_REQUEST_TRANSFORM_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_RESPONSE_TRANSFORM_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_SELECT_ALT_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_TXN_START_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_TXN_CLOSE_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_SSN_START_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_SSN_CLOSE_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_CACHE_LOOKUP_COMPLETE_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_PRE_REMAP_HOOK),
+ HOOK_DEMUX_ENTRY(HTTP_POST_REMAP_HOOK),
+};
+
+static void
+LuaPushEventData(lua_State * lua, TSEvent event, void * edata)
+{
+ switch (event) {
+ case TS_EVENT_HTTP_READ_REQUEST_HDR:
+ case TS_EVENT_HTTP_OS_DNS:
+ case TS_EVENT_HTTP_SEND_REQUEST_HDR:
+ case TS_EVENT_HTTP_READ_CACHE_HDR:
+ case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+ case TS_EVENT_HTTP_SELECT_ALT:
+ case TS_EVENT_HTTP_TXN_START:
+ case TS_EVENT_HTTP_TXN_CLOSE:
+ case TS_EVENT_CACHE_LOOKUP_COMPLETE:
+ LuaPushHttpTransaction(lua, (TSHttpTxn)edata);
+ break;
+ case TS_EVENT_HTTP_SSN_START:
+ case TS_EVENT_HTTP_SSN_CLOSE:
+ LuaPushHttpSession(lua, (TSHttpSsn)edata);
+ break;
+ default:
+ lua_pushnil(lua);
+ }
+}
+
+// XXX this is bollocks ... need to keep the hook ID in the continuation. The
demuxer has to be per-thread
+// because it holds references to the per-thread Lua state. If we keep the
demuxer on the continuation,
+// then it's per hook which is not gonna work.
+
+static int
+LuaHookDemux(TSCont cont, TSEvent event, void * edata)
+{
+ TSHttpHookID hookid = (TSHttpHookID)(intptr_t)TSContDataGet(cont);
+ LuaThreadInstance * lthread;
+
+ lthread = LuaGetThreadInstance();
+
+ TSDebug("lua", "%s(%s) lthread=%p event=%d edata=%p",
+ __func__, HttpHookDemuxTable[hookid].name, lthread, event, edata);
+
+ if (lthread == NULL) {
+ lthread = tsnew<LuaThreadInstance>();
+ lthread->lua = LuaPluginNewState();
+ LuaSetThreadInstance(lthread);
+ LuaPluginLoad(lthread->lua, LuaPlugin);
+ }
+
+ TSAssert(hookid >= 0);
+ TSAssert(hookid < TS_HTTP_LAST_HOOK);
+
+ if (lthread->hooks[hookid] != LUA_NOREF) {
+ lua_rawgeti(lthread->lua, LUA_REGISTRYINDEX, lthread->hooks[hookid]);
+ lua_pushinteger(lthread->lua, event);
+ LuaPushEventData(lthread->lua, event, edata);
+ if (lua_pcall(lthread->lua, 2 /* nargs */, 0, 0) != 0) {
+ TSDebug("lua", "hook callback failed: %s", lua_tostring(lthread->lua,
-1));
+ lua_pop(lthread->lua, 1); // pop the error message
+ }
+ } else {
+ TSDebug("lua", "no demuxer for %s", HttpHookDemuxTable[hookid].name);
+ }
+
+ return 0;
+}
+
+static int
+TSLuaHttpHookRegister(lua_State * lua)
+{
+ TSHttpHookID hookid;
+ LuaThreadInstance * lthread;
+
+ hookid = (TSHttpHookID)luaL_checkint(lua, 1);
+ luaL_checktype(lua, 2, LUA_TFUNCTION);
+
+ if (hookid < 0 || hookid >= TS_HTTP_LAST_HOOK) {
+ TSDebug("lua", "hook ID %d out of range", hookid);
+ return -1;
+ }
+
+ lthread = LuaGetThreadInstance();
+ if (lthread == NULL) {
+ lthread = tsnew<LuaThreadInstance>();
+ lthread->lua = LuaPluginNewState(LuaPlugin);
+ LuaSetThreadInstance(lthread);
+ }
+
+ // Global hooks can only be registered once, but we load the Lua scripts in
every thread. Check whether the hook has
+ // already been registered and ignore any double-registrations.
+ if (lthread->hooks[hookid] != LUA_NOREF) {
+ TSReleaseAssert(HttpHookDemuxTable[hookid].cont != NULL);
+ return 0;
+ }
+
+ lthread->hooks[hookid] = luaL_ref(lua, LUA_REGISTRYINDEX);
+
+ if (HttpHookDemuxTable[hookid].cont == NULL) {
+ TSCont cont;
+
+ cont = TSContCreate(LuaHookDemux, TSMutexCreate());
+ if (__sync_bool_compare_and_swap(&HttpHookDemuxTable[hookid].cont, NULL,
cont)) {
+ TSDebug("lua", "installed hook for %s", HttpHookDemuxTable[hookid].name);
+ TSContDataSet(cont, (void *)hookid);
+ TSHttpHookAdd(hookid, cont);
+ } else {
+ TSDebug("lua", "lost hook creation race for %s",
HttpHookDemuxTable[hookid].name);
+ TSContDestroy(cont);
+ }
+ }
+
+ return 0;
+}
+
+static const luaL_Reg LUAEXPORTS[] =
+{
+ { "register", TSLuaHttpHookRegister },
+ { NULL, NULL}
+};
+
+int
+LuaHookApiInit(lua_State * lua)
+{
+ TSDebug("lua", "initializing TS Hook API");
+
+ lua_newtable(lua);
+
+ // Register functions in the "ts.hook" module.
+ luaL_register(lua, NULL, LUAEXPORTS);
+
+ for (unsigned i = 0; i < arraysz(HttpHookDemuxTable); ++i) {
+ if (HttpHookDemuxTable[i].name == NULL) {
+ continue;
+ }
+
+ TSAssert(i == HttpHookDemuxTable[i].hookid);
+ LuaSetConstantField(lua, HttpHookDemuxTable[i].name,
HttpHookDemuxTable[i].hookid);
+ }
+
+ return 1;
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/hook.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/hook.h b/plugins/experimental/lua/hook.h
new file mode 100644
index 0000000..3522779
--- /dev/null
+++ b/plugins/experimental/lua/hook.h
@@ -0,0 +1,22 @@
+/*
+ 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.
+*/
+
+#ifndef LUA_HOOK_H_
+#define LUA_HOOK_H_
+
+#endif // LUA_HOOK_H_
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lapi.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lapi.cc b/plugins/experimental/lua/lapi.cc
index 18d1bb6..d9695b4 100644
--- a/plugins/experimental/lua/lapi.cc
+++ b/plugins/experimental/lua/lapi.cc
@@ -22,8 +22,23 @@
#include "lapi.h"
#include "lutil.h"
-// Return the type name string for the given index.
-#define LTYPEOF(L, index) lua_typename(L, lua_type(L, index))
+template <typename LuaType, typename Param1> LuaType *
+push_userdata_object(lua_State * lua, Param1 p1)
+{
+ LuaType * ltype;
+ ltype = LuaType::alloc(lua, p1);
+ TSReleaseAssert(lua_isuserdata(lua, -1) == 1);
+ return ltype;
+}
+
+template <typename LuaType, typename Param1, typename Param2> LuaType *
+push_userdata_object(lua_State * lua, Param1 p1, Param2 p2)
+{
+ LuaType * ltype;
+ ltype = LuaType::alloc(lua, p1, p2);
+ TSReleaseAssert(lua_isuserdata(lua, -1) == 1);
+ return ltype;
+}
struct LuaRemapHeaders
{
@@ -37,7 +52,7 @@ struct LuaRemapHeaders
static LuaRemapHeaders * alloc(lua_State * lua) {
LuaRemapHeaders * hdrs;
- hdrs = (LuaRemapHeaders *)lua_newuserdata(lua, sizeof(LuaRemapHeaders));
+ hdrs = LuaNewUserData<LuaRemapHeaders>(lua);
luaL_getmetatable(lua, "ts.meta.rri.headers");
lua_setmetatable(lua, -2);
@@ -45,6 +60,46 @@ struct LuaRemapHeaders
}
};
+struct LuaHttpTransaction
+{
+ TSHttpTxn txn;
+
+ static LuaHttpTransaction * get(lua_State * lua, int index) {
+ return (LuaHttpTransaction *)luaL_checkudata(lua, index,
"ts.meta.http.txn");
+ }
+
+ static LuaHttpTransaction * alloc(lua_State * lua, TSHttpTxn ptr) {
+ LuaHttpTransaction * txn;
+
+ txn = LuaNewUserData<LuaHttpTransaction>(lua);
+ txn->txn = ptr;
+ luaL_getmetatable(lua, "ts.meta.http.txn");
+ lua_setmetatable(lua, -2);
+
+ return txn;
+ }
+};
+
+struct LuaHttpSession
+{
+ TSHttpSsn ssn;
+
+ static LuaHttpSession * get(lua_State * lua, int index) {
+ return (LuaHttpSession *)luaL_checkudata(lua, index, "ts.meta.http.ssn");
+ }
+
+ static LuaHttpSession * alloc(lua_State * lua, TSHttpSsn ptr) {
+ LuaHttpSession * ssn;
+
+ ssn = LuaNewUserData<LuaHttpSession>(lua);
+ ssn->ssn = ptr;
+ luaL_getmetatable(lua, "ts.meta.http.ssn");
+ lua_setmetatable(lua, -2);
+
+ return ssn;
+ }
+};
+
LuaRemapRequest *
LuaRemapRequest::get(lua_State * lua, int index)
{
@@ -52,11 +107,11 @@ LuaRemapRequest::get(lua_State * lua, int index)
}
LuaRemapRequest *
-LuaRemapRequest::alloc(lua_State * lua)
+LuaRemapRequest::alloc(lua_State * lua, TSRemapRequestInfo * rri, TSHttpTxn
txn)
{
LuaRemapRequest * rq;
- rq = (LuaRemapRequest *)lua_newuserdata(lua, sizeof(LuaRemapRequest));
+ rq = new(lua_newuserdata(lua, sizeof(LuaRemapRequest))) LuaRemapRequest(rri,
txn);
luaL_getmetatable(lua, "ts.meta.rri");
lua_setmetatable(lua, -2);
@@ -391,21 +446,78 @@ static const luaL_Reg HEADERS[] =
{ NULL, NULL }
};
-LuaRemapRequest *
-LuaPushRemapRequestInfo(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo *
rri)
+static int
+LuaHttpTxnAbort(lua_State * lua)
{
- LuaRemapRequest * rq;
+ LuaHttpTransaction * txn;
- rq = LuaRemapRequest::alloc(lua);
- rq->rri = rri;
- rq->txn = txn;
- rq->status = TSREMAP_NO_REMAP;
+ txn = LuaHttpTransaction::get(lua, 1);
+ TSHttpTxnReenable(txn->txn, TS_EVENT_HTTP_ERROR);
- TSReleaseAssert(lua_isuserdata(lua, -1) == 1);
- return rq;
+ return 1;
+}
+
+static int
+LuaHttpTxnContinue(lua_State * lua)
+{
+ LuaHttpTransaction * txn;
+
+ txn = LuaHttpTransaction::get(lua, 1);
+ TSHttpTxnReenable(txn->txn, TS_EVENT_HTTP_CONTINUE);
+
+ return 1;
+}
+
+static const luaL_Reg HTTPTXN[] =
+{
+ { "abort", LuaHttpTxnAbort },
+ { "continue", LuaHttpTxnContinue },
+ { NULL, NULL }
+};
+
+static int
+LuaHttpSsnAbort(lua_State * lua)
+{
+ LuaHttpSession * ssn;
+
+ ssn = LuaHttpSession::get(lua, 1);
+ TSHttpSsnReenable(ssn->ssn, TS_EVENT_HTTP_ERROR);
+
+ return 1;
}
static int
+LuaHttpSsnContinue(lua_State * lua)
+{
+ LuaHttpSession * ssn;
+
+ ssn = LuaHttpSession::get(lua, 1);
+ TSHttpSsnReenable(ssn->ssn, TS_EVENT_HTTP_CONTINUE);
+
+ return 1;
+}
+
+static int
+LuaHttpSsnRegister(lua_State * lua)
+{
+ LuaHttpSession * ssn;
+ int hookid;
+
+ ssn = LuaHttpSession::get(lua, 1);
+ hookid = luaL_checkint(lua, 2);
+
+ return 1;
+}
+
+static const luaL_Reg HTTPSSN[] =
+{
+ { "register", LuaHttpSsnRegister },
+ { "abort", LuaHttpSsnAbort },
+ { "continue", LuaHttpSsnContinue },
+ { NULL, NULL }
+};
+
+static int
TSLuaDebug(lua_State * lua)
{
const char * tag = luaL_checkstring(lua, 1);
@@ -421,10 +533,28 @@ static const luaL_Reg LUAEXPORTS[] =
{ NULL, NULL}
};
+LuaRemapRequest *
+LuaPushRemapRequestInfo(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo *
rri)
+{
+ return push_userdata_object<LuaRemapRequest>(lua, rri, txn);
+}
+
+LuaHttpTransaction *
+LuaPushHttpTransaction(lua_State * lua, TSHttpTxn txn)
+{
+ return push_userdata_object<LuaHttpTransaction>(lua, txn);
+}
+
+LuaHttpSession *
+LuaPushHttpSession(lua_State * lua, TSHttpSsn ssn)
+{
+ return push_userdata_object<LuaHttpSession>(lua, ssn);
+}
+
int
LuaApiInit(lua_State * lua)
{
- TSDebug("lua", "initializing Lua API");
+ TSDebug("lua", "initializing TS API");
lua_newtable(lua);
@@ -432,23 +562,10 @@ LuaApiInit(lua_State * lua)
luaL_register(lua, NULL, LUAEXPORTS);
// Push constants into the "ts" module.
- lua_pushstring(lua, TSTrafficServerVersionGet());
- lua_setfield(lua, -2, "VERSION");
-
- lua_pushinteger(lua, TSTrafficServerVersionGetMajor());
- lua_setfield(lua, -2, "MAJOR_VERSION");
-
- lua_pushinteger(lua, TSTrafficServerVersionGetMinor());
- lua_setfield(lua, -2, "MINOR_VERSION");
-
- lua_pushinteger(lua, TSTrafficServerVersionGetPatch());
- lua_setfield(lua, -2, "PATCH_VERSION");
-
- lua_pushinteger(lua, TSREMAP_DID_REMAP_STOP);
- lua_setfield(lua, -2, "REMAP_COMPLETE");
-
- lua_pushinteger(lua, TSREMAP_DID_REMAP);
- lua_setfield(lua, -2, "REMAP_CONTINUE");
+ LuaSetConstantField(lua, "VERSION", TSTrafficServerVersionGet());
+ LuaSetConstantField(lua, "MAJOR_VERSION", TSTrafficServerVersionGetMajor());
+ LuaSetConstantField(lua, "MINOR_VERSION", TSTrafficServerVersionGetMinor());
+ LuaSetConstantField(lua, "PATCH_VERSION", TSTrafficServerVersionGetPatch());
// Register TSRemapRequestInfo metatable.
LuaPushMetatable(lua, "ts.meta.rri", RRI);
@@ -460,6 +577,16 @@ LuaApiInit(lua_State * lua)
// Pop the metatable.
lua_pop(lua, 1);
+ // Register TSHttpTxn metatable.
+ LuaPushMetatable(lua, "ts.meta.http.txn", HTTPTXN);
+ // Pop the metatable.
+ lua_pop(lua, 1);
+
+ // Register TSHttpSsn metatable.
+ LuaPushMetatable(lua, "ts.meta.http.ssn", HTTPSSN);
+ // Pop the metatable.
+ lua_pop(lua, 1);
+
TSReleaseAssert(lua_istable(lua, -1) == 1);
return 1;
}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lapi.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lapi.h b/plugins/experimental/lua/lapi.h
index 9dc8534..2218330 100644
--- a/plugins/experimental/lua/lapi.h
+++ b/plugins/experimental/lua/lapi.h
@@ -19,11 +19,10 @@
#ifndef LUA_LAPI_H_
#define LUA_LAPI_H_
-extern "C" {
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-}
+#include <lua.hpp>
+
+struct LuaHttpTransaction;
+struct LuaHttpSession;
struct LuaRemapRequest
{
@@ -31,12 +30,20 @@ struct LuaRemapRequest
TSHttpTxn txn;
TSRemapStatus status;
+ LuaRemapRequest(TSRemapRequestInfo * r, TSHttpTxn t) : rri(r), txn(t),
status(TSREMAP_NO_REMAP) {}
+ LuaRemapRequest() : rri(NULL), txn(NULL), status(TSREMAP_NO_REMAP) {}
+ ~LuaRemapRequest() {}
+
static LuaRemapRequest * get(lua_State * lua, int index);
- static LuaRemapRequest * alloc(lua_State * lua);
+ static LuaRemapRequest * alloc(lua_State *, TSRemapRequestInfo *, TSHttpTxn);
};
// Initialize the 'ts' module.
int LuaApiInit(lua_State * lua);
+// Initialize the 'ts.config' module.
+int LuaConfigApiInit(lua_State * lua);
+// Initialize the 'ts.hook' module.
+int LuaHookApiInit(lua_State * lua);
// Push a copy of the given URL.
bool LuaPushUrl(lua_State * lua, TSMBuffer buffer, TSMLoc url);
@@ -45,4 +52,12 @@ bool LuaPushUrl(lua_State * lua, TSMBuffer buffer, TSMLoc
url);
LuaRemapRequest *
LuaPushRemapRequestInfo(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo *
rri);
+// Push a TSHttpTxn userdata object.
+LuaHttpTransaction *
+LuaPushHttpTransaction(lua_State * lua, TSHttpTxn txn);
+
+// Push a TSHttpSsn userdata object.
+LuaHttpSession *
+LuaPushHttpSession(lua_State * lua, TSHttpSsn ssn);
+
#endif // LUA_LAPI_H_
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lconfig.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lconfig.cc
b/plugins/experimental/lua/lconfig.cc
new file mode 100644
index 0000000..a36c4df
--- /dev/null
+++ b/plugins/experimental/lua/lconfig.cc
@@ -0,0 +1,148 @@
+/*
+ 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 <ts/ts.h>
+#include <ts/remap.h>
+#include "lapi.h"
+#include "lutil.h"
+
+// ts.config.override(txn, key, value)
+//
+// Override a configuration entry for this transaction.
+static int
+TSLuaConfigOverride(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+ TSOverridableConfigKey key;
+ union {
+ lua_Number number;
+ lua_Integer integer;
+ struct { const char * str; size_t len; } lstring;
+ } value;
+
+
+ // XXX For now, this only works on remap request objects. When we expose a
TSHttpTxn object in Lua, we should
+ // dynamically support passing one of those in as well.
+ rq = LuaRemapRequest::get(lua, 1);
+
+ key = (TSOverridableConfigKey)luaL_checkint(lua, 2);
+
+ switch(lua_type(lua, 3)) {
+ case LUA_TBOOLEAN:
+ TSHttpTxnConfigIntSet(rq->txn, key, lua_toboolean(lua, 3) ? 1 : 0);
+ break;
+ case LUA_TNUMBER:
+ // There's no API that will tell us the correct type to use for numberic
override options. Let's try int first,
+ // since that's the common case. If that fails we can try float.
+ value.integer = luaL_checkinteger(lua, 3);
+ if (TSHttpTxnConfigIntSet(rq->txn, key, value.integer) == TS_ERROR) {
+ value.number = luaL_checknumber(lua, 3);
+ TSHttpTxnConfigFloatSet(rq->txn, key, value.number);
+ }
+
+ break;
+ case LUA_TSTRING:
+ value.lstring.str = lua_tolstring(lua, 3, &value.lstring.len);
+ TSHttpTxnConfigStringSet(rq->txn, key, value.lstring.str,
value.lstring.len);
+ break;
+ }
+
+ return 0;
+}
+
+static const luaL_Reg LUAEXPORTS[] =
+{
+ { "override", TSLuaConfigOverride },
+ { NULL, NULL}
+};
+
+int
+LuaConfigApiInit(lua_State * lua)
+{
+ TSDebug("lua", "initializing TS Config API");
+
+ lua_newtable(lua);
+
+ // Register functions in the "ts.config" module.
+ luaL_register(lua, NULL, LUAEXPORTS);
+
+#define DEFINE_CONFIG_KEY(NAME) LuaSetConstantField(lua, #NAME, TS_CONFIG_ ##
NAME)
+ DEFINE_CONFIG_KEY(URL_REMAP_PRISTINE_HOST_HDR);
+ DEFINE_CONFIG_KEY(HTTP_CHUNKING_ENABLED);
+ DEFINE_CONFIG_KEY(HTTP_NEGATIVE_CACHING_ENABLED);
+ DEFINE_CONFIG_KEY(HTTP_NEGATIVE_CACHING_LIFETIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_WHEN_TO_REVALIDATE);
+ DEFINE_CONFIG_KEY(HTTP_KEEP_ALIVE_ENABLED_IN);
+ DEFINE_CONFIG_KEY(HTTP_KEEP_ALIVE_ENABLED_OUT);
+ DEFINE_CONFIG_KEY(HTTP_KEEP_ALIVE_POST_OUT);
+ DEFINE_CONFIG_KEY(HTTP_SHARE_SERVER_SESSIONS);
+ DEFINE_CONFIG_KEY(NET_SOCK_RECV_BUFFER_SIZE_OUT);
+ DEFINE_CONFIG_KEY(NET_SOCK_SEND_BUFFER_SIZE_OUT);
+ DEFINE_CONFIG_KEY(NET_SOCK_OPTION_FLAG_OUT);
+ DEFINE_CONFIG_KEY(HTTP_FORWARD_PROXY_AUTH_TO_PARENT);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_REMOVE_FROM);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_REMOVE_REFERER);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_REMOVE_USER_AGENT);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_REMOVE_COOKIE);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_REMOVE_CLIENT_IP);
+ DEFINE_CONFIG_KEY(HTTP_ANONYMIZE_INSERT_CLIENT_IP);
+ DEFINE_CONFIG_KEY(HTTP_RESPONSE_SERVER_ENABLED);
+ DEFINE_CONFIG_KEY(HTTP_INSERT_SQUID_X_FORWARDED_FOR);
+ DEFINE_CONFIG_KEY(HTTP_SERVER_TCP_INIT_CWND);
+ DEFINE_CONFIG_KEY(HTTP_SEND_HTTP11_REQUESTS);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_HTTP);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_IGNORE_CLIENT_NO_CACHE);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_IGNORE_CLIENT_CC_MAX_AGE);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_IMS_ON_CLIENT_NO_CACHE);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_IGNORE_SERVER_NO_CACHE);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_CACHE_RESPONSES_TO_COOKIES);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_IGNORE_AUTHENTICATION);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_CACHE_URLS_THAT_LOOK_DYNAMIC);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_REQUIRED_HEADERS);
+ DEFINE_CONFIG_KEY(HTTP_INSERT_REQUEST_VIA_STR);
+ DEFINE_CONFIG_KEY(HTTP_INSERT_RESPONSE_VIA_STR);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_HEURISTIC_MIN_LIFETIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_HEURISTIC_MAX_LIFETIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_GUARANTEED_MIN_LIFETIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_GUARANTEED_MAX_LIFETIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_MAX_STALE_AGE);
+ DEFINE_CONFIG_KEY(HTTP_KEEP_ALIVE_NO_ACTIVITY_TIMEOUT_IN);
+ DEFINE_CONFIG_KEY(HTTP_KEEP_ALIVE_NO_ACTIVITY_TIMEOUT_OUT);
+ DEFINE_CONFIG_KEY(HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_IN);
+ DEFINE_CONFIG_KEY(HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_OUT);
+ DEFINE_CONFIG_KEY(HTTP_TRANSACTION_ACTIVE_TIMEOUT_OUT);
+ DEFINE_CONFIG_KEY(HTTP_ORIGIN_MAX_CONNECTIONS);
+ DEFINE_CONFIG_KEY(HTTP_CONNECT_ATTEMPTS_MAX_RETRIES);
+ DEFINE_CONFIG_KEY(HTTP_CONNECT_ATTEMPTS_MAX_RETRIES_DEAD_SERVER);
+ DEFINE_CONFIG_KEY(HTTP_CONNECT_ATTEMPTS_RR_RETRIES);
+ DEFINE_CONFIG_KEY(HTTP_CONNECT_ATTEMPTS_TIMEOUT);
+ DEFINE_CONFIG_KEY(HTTP_POST_CONNECT_ATTEMPTS_TIMEOUT);
+ DEFINE_CONFIG_KEY(HTTP_DOWN_SERVER_CACHE_TIME);
+ DEFINE_CONFIG_KEY(HTTP_DOWN_SERVER_ABORT_THRESHOLD);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_FUZZ_TIME);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_FUZZ_MIN_TIME);
+ DEFINE_CONFIG_KEY(HTTP_DOC_IN_CACHE_SKIP_DNS);
+ DEFINE_CONFIG_KEY(HTTP_RESPONSE_SERVER_STR);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_HEURISTIC_LM_FACTOR);
+ DEFINE_CONFIG_KEY(HTTP_CACHE_FUZZ_PROBABILITY);
+ DEFINE_CONFIG_KEY(NET_SOCK_PACKET_MARK_OUT);
+ DEFINE_CONFIG_KEY(NET_SOCK_PACKET_TOS_OUT);
+#undef DEFINE_CONFIG_KEY
+
+ return 1;
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lua.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lua.cc b/plugins/experimental/lua/lua.cc
deleted file mode 100644
index 9a74571..0000000
--- a/plugins/experimental/lua/lua.cc
+++ /dev/null
@@ -1,218 +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.
-*/
-
-#include <ts/ts.h>
-#include <ts/remap.h>
-#include <unistd.h>
-#include <string.h>
-#include <pthread.h>
-#include <string>
-#include <vector>
-#include "lapi.h"
-#include "lutil.h"
-
-static pthread_key_t LuaStateKey;
-
-struct LuaPluginState
-{
- typedef std::vector<std::string> pathlist;
-
- LuaPluginState(unsigned argc, const char ** argv) {
- for (unsigned i = 0; i < argc; ++i) {
- paths.push_back(argv[i]);
- }
- }
-
- pathlist paths;
-};
-
-static TSReturnCode
-LuaPluginInit(lua_State * lua)
-{
- TSReturnCode status = TS_ERROR;
-
- lua_getglobal(lua, "init");
- if (lua_isnil(lua, -1)) {
- // No "init" callback.
- return TS_SUCCESS;
- }
-
- if (lua_pcall(lua, 0, 1, 0) != 0) {
- TSDebug("lua", "init failed: %s", lua_tostring(lua, -1));
- lua_pop(lua, 1);
- }
-
- // Return type is bool; check it and pop it.
- if (lua_isboolean(lua, 1) && lua_toboolean(lua, 1)) {
- status = TS_SUCCESS;
- }
-
- lua_pop(lua, 1);
- return status;
-}
-
-static TSReturnCode
-LuaPluginRelease(lua_State * lua)
-{
- lua_getglobal(lua, "release");
- if (lua_isnil(lua, -1)) {
- // No "release" callback.
- return TS_SUCCESS;
- }
-
- if (lua_pcall(lua, 0, 0, 0) != 0) {
- TSDebug("lua", "release failed: %s", lua_tostring(lua, -1));
- lua_pop(lua, 1);
- }
-
- lua_close(lua);
- return TS_SUCCESS;
-}
-
-static TSRemapStatus
-LuaPluginRemap(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
-{
- LuaRemapRequest * rq;
-
- lua_getglobal(lua, "remap");
- if (lua_isnil(lua, -1)) {
- // No "remap" callback, better continue.
- return TSREMAP_NO_REMAP;
- }
-
- TSDebug("lua", "handling request %p on thread 0x%llx", rri, (unsigned long
long)pthread_self());
-
- // XXX We can also cache the RemapRequestInfo in the Lua state. We we just
need to reset
- // the rri pointer and status.
- rq = LuaPushRemapRequestInfo(lua, txn, rri);
-
- if (lua_pcall(lua, 1, 0, 0) != 0) {
- TSDebug("lua", "remap failed: %s", lua_tostring(lua, -1));
- lua_pop(lua, 1);
- return TSREMAP_ERROR;
- }
-
- // XXX can we guarantee that rq has not been garbage collected?
- return rq->status;
-}
-
-static lua_State *
-LuaPluginNewState(void)
-{
- lua_State * lua;
-
- lua = lua_newstate(LuaAllocate, NULL);
- if (lua == NULL) {
- return NULL;
- }
-
- LuaLoadLibraries(lua);
- LuaRegisterLibrary(lua, "ts", LuaApiInit);
-
- return lua;
-}
-
-static lua_State *
-LuaPluginNewState(LuaPluginState * remap)
-{
- lua_State * lua;
-
- lua = LuaPluginNewState();
- if (lua == NULL) {
- return NULL;
- }
-
- for (LuaPluginState::pathlist::const_iterator p = remap->paths.begin(); p <
remap->paths.end(); ++p) {
- if (access(p->c_str(), F_OK) != 0) {
- continue;
- }
-
- if (luaL_dofile(lua, p->c_str()) != 0) {
- // If the load failed, it should have pushed an error message.
- TSDebug("lua", "failed to load Lua file %s: %s", p->c_str(),
lua_tostring(lua, -1));
- lua_close(lua);
- return NULL;
- }
- }
-
- if (LuaPluginInit(lua) == TS_SUCCESS) {
- return lua;
- } else {
- lua_close(lua);
- return NULL;
- }
-}
-
-void
-TSRemapDeleteInstance(void * ih)
-{
- lua_State * lua = (lua_State *)ih;
-
- if (lua) {
- LuaPluginRelease(lua);
- lua_close(lua);
- }
-}
-
-TSReturnCode
-TSRemapInit(TSRemapInterface * api_info, char * errbuf, int errbuf_size)
-{
- TSDebug("lua", "loading lua plugin");
- TSReleaseAssert(pthread_key_create(&LuaStateKey, TSRemapDeleteInstance) ==
0);
- return TS_SUCCESS;
-}
-
-TSReturnCode
-TSRemapNewInstance(int argc, char * argv[], void ** ih, char * errbuf, int
errsz)
-{
- LuaPluginState * remap;
- lua_State * lua;
-
- // Copy the plugin arguments so that we can use them to allocate a
per-thread Lua state. It would be cleaner
- // to clone a Lua state, but there's no built-in way to do that, and to
implement that ourselves would require
- // locking the template state (we need to manipulate the stack to copy
values out).
- remap = new LuaPluginState((unsigned)argc, (const char **)argv);
-
- // Test whether we can successfully load the Lua program.
- lua = LuaPluginNewState(remap);
- if (!lua) {
- delete remap;
- return TS_ERROR;
- }
-
- *ih = remap;
- return TS_SUCCESS;
-}
-
-TSRemapStatus
-TSRemapDoRemap(void * ih, TSHttpTxn txn, TSRemapRequestInfo * rri)
-{
- lua_State * lua;
-
- // Find or clone the per-thread Lua state.
- lua = (lua_State *)pthread_getspecific(LuaStateKey);
- if (!lua) {
- LuaPluginState * remap = (LuaPluginState *)ih;
-
- TSDebug("lua", "allocating new Lua state on thread 0x%llx", (unsigned long
long)pthread_self());
- lua = LuaPluginNewState(remap);
- pthread_setspecific(LuaStateKey, lua);
- }
-
- return LuaPluginRemap(lua, txn, rri);
-}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lutil.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lutil.cc
b/plugins/experimental/lua/lutil.cc
index b8241f1..99e1f77 100644
--- a/plugins/experimental/lua/lutil.cc
+++ b/plugins/experimental/lua/lutil.cc
@@ -17,14 +17,27 @@
*/
#include <ts/ts.h>
+#include <ts/remap.h>
+#include "lapi.h"
+#include "lutil.h"
+#include <pthread.h>
-extern "C" {
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
+static thread_local_pointer<LuaThreadInstance> LuaThread;
+LuaPluginState * LuaPlugin;
+
+LuaThreadInstance::LuaThreadInstance()
+ : lua(NULL)
+{
+ for (unsigned i = 0; i < arraysz(this->hooks); ++i) {
+ this->hooks[i] = LUA_NOREF;
+ }
+}
+
+LuaThreadInstance::~LuaThreadInstance()
+{
}
-void *
+static void *
LuaAllocate(void * ud, void * ptr, size_t osize, size_t nsize)
{
TSReleaseAssert(ud == NULL);
@@ -37,6 +50,60 @@ LuaAllocate(void * ud, void * ptr, size_t osize, size_t
nsize)
return TSrealloc(ptr, nsize);
}
+lua_State *
+LuaPluginNewState(void)
+{
+ lua_State * lua;
+
+ lua = lua_newstate(LuaAllocate, NULL);
+ if (lua == NULL) {
+ return NULL;
+ }
+
+ LuaLoadLibraries(lua);
+ LuaRegisterLibrary(lua, "ts", LuaApiInit);
+ LuaRegisterLibrary(lua, "ts.config", LuaConfigApiInit);
+ LuaRegisterLibrary(lua, "ts.hook", LuaHookApiInit);
+
+ return lua;
+}
+
+lua_State *
+LuaPluginNewState(LuaPluginState * plugin)
+{
+ lua_State * lua;
+
+ lua = LuaPluginNewState();
+ if (lua == NULL) {
+ return NULL;
+ }
+
+ if (!LuaPluginLoad(lua, plugin)) {
+ lua_close(lua);
+ return NULL;
+ }
+
+ return lua;
+}
+
+bool
+LuaPluginLoad(lua_State * lua, LuaPluginState * plugin)
+{
+ for (LuaPluginState::pathlist::const_iterator p = plugin->paths.begin(); p <
plugin->paths.end(); ++p) {
+ if (access(p->c_str(), F_OK) != 0) {
+ continue;
+ }
+
+ if (luaL_dofile(lua, p->c_str()) != 0) {
+ // If the load failed, it should have pushed an error message.
+ TSError("failed to load Lua file %s: %s", p->c_str(), lua_tostring(lua,
-1));
+ return false;
+ }
+ }
+
+ return true;
+}
+
void
LuaPushMetatable(lua_State * lua, const char * name, const luaL_Reg * exports)
{
@@ -78,3 +145,29 @@ LuaLoadLibraries(lua_State * lua)
#undef REGISTER_LIBRARY
}
+void
+LuaSetConstantField(lua_State * lua, const char * name, int value)
+{
+ lua_pushinteger(lua, value);
+ lua_setfield(lua, -2, name);
+}
+
+void
+LuaSetConstantField(lua_State * lua, const char * name, const char * value)
+{
+ lua_pushstring(lua, value);
+ lua_setfield(lua, -2, name);
+}
+
+LuaThreadInstance *
+LuaGetThreadInstance()
+{
+ return LuaThread.get();
+}
+
+void
+LuaSetThreadInstance(LuaThreadInstance * lthread)
+{
+ LuaThread.set(lthread);
+}
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/lutil.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lutil.h b/plugins/experimental/lua/lutil.h
index d6e24a6..2ede5bc 100644
--- a/plugins/experimental/lua/lutil.h
+++ b/plugins/experimental/lua/lutil.h
@@ -16,10 +16,109 @@
limitations under the License.
*/
+#ifndef LUA_LUTIL_H_
+#define LUA_LUTIL_H_
+
+#include <lua.hpp>
+#include <vector>
+#include <string>
+#include <memory>
+#include <pthread.h>
+
+
+struct LuaPluginState;
+struct LuaThreadInstance;
+
+extern LuaPluginState * LuaPlugin;
+
+template <typename T> T * tsnew() {
+ void * ptr = TSmalloc(sizeof(T));
+ return new(ptr) T();
+}
+
+template <typename T> void tsdelete(T * ptr) {
+ if (ptr) {
+ ptr->~T();
+ TSfree(ptr);
+ }
+}
+
+// Allocate an object with lua_newuserdata() and call the default constructor.
+template <typename T> T * LuaNewUserData(lua_State * lua) {
+ void * ptr = lua_newuserdata(lua, sizeof(T));
+ return new(ptr) T();
+}
+
// Return the type name string for the given index.
#define LTYPEOF(L, index) lua_typename(L, lua_type(L, index))
-void * LuaAllocate(void * ud, void * ptr, size_t osize, size_t nsize);
void LuaPushMetatable(lua_State * lua, const char * name, const luaL_Reg *
exports);
void LuaLoadLibraries(lua_State * lua);
void LuaRegisterLibrary(lua_State * lua, const char * name, lua_CFunction
loader);
+
+// Set the named field in the table on the top of the stack.
+void LuaSetConstantField(lua_State * lua, const char * name, int value);
+void LuaSetConstantField(lua_State * lua, const char * name, const char *
value);
+
+// Get and set the per-thread lua_State.
+LuaThreadInstance * LuaGetThreadInstance();
+void LuaSetThreadInstance(LuaThreadInstance * lua);
+
+// Allocate a new lua_State.
+lua_State * LuaPluginNewState(void);
+lua_State * LuaPluginNewState(LuaPluginState * plugin);
+bool LuaPluginLoad(lua_State * lua, LuaPluginState * plugin);
+
+// Global Lua plugin state. Used to reconstruct new lua_States.
+struct LuaPluginState
+{
+ typedef std::vector<std::string> pathlist;
+
+ void init(unsigned argc, const char ** argv) {
+ for (unsigned i = 0; i < argc; ++i) {
+ paths.push_back(argv[i]);
+ }
+ }
+
+ pathlist paths;
+};
+
+// Per-thread lua_State. Used to execute Lua-side code in ethreads.
+struct LuaThreadInstance
+{
+ lua_State * lua;
+ int hooks[TS_HTTP_LAST_HOOK];
+
+ LuaThreadInstance();
+ ~LuaThreadInstance();
+};
+
+template <typename T, unsigned N> unsigned
+arraysz(const T (&)[N]) {
+ return N;
+}
+
+template <typename T>
+struct thread_local_pointer
+{
+ thread_local_pointer() {
+ TSReleaseAssert(pthread_key_create(&key, NULL) != -1);
+ }
+
+ ~thread_local_pointer() {
+ pthread_key_delete(key);
+ }
+
+ T * get() const {
+ return (T *)pthread_getspecific(key);
+ }
+
+ void set(T * t) const {
+ pthread_setspecific(key, (void *)t);
+ }
+
+private:
+ pthread_key_t key;
+};
+
+#endif // LUA_LUTIL_H_
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/plugin.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/plugin.cc
b/plugins/experimental/lua/plugin.cc
new file mode 100644
index 0000000..52f5f25
--- /dev/null
+++ b/plugins/experimental/lua/plugin.cc
@@ -0,0 +1,50 @@
+/*
+ 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 <ts/ts.h>
+#include "lutil.h"
+#include "hook.h"
+
+extern "C" void
+TSPluginInit(int argc, const char * argv[])
+{
+ LuaThreadInstance * lthread;
+ TSPluginRegistrationInfo info;
+
+ info.plugin_name = (char *)"lua";
+ info.vendor_name = (char *)"Apache Traffic Server";
+ info.support_email = (char *)"[email protected]";
+
+ if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
+ TSError("Plugin registration failed");
+ }
+
+ TSAssert(LuaPlugin == NULL);
+
+ LuaPlugin = tsnew<LuaPluginState>();
+ LuaPlugin->init((unsigned)argc, (const char **)argv);
+
+ // Careful! We need to initialize the per-thread Lua state before we inject
+ // any user code. User code will probably call TSAPI functions, which will
+ // fetch or create the per-thread instance, which had better be available.
+ lthread = tsnew<LuaThreadInstance>();
+ lthread->lua = LuaPluginNewState();
+ LuaSetThreadInstance(lthread);
+ LuaPluginLoad(lthread->lua, LuaPlugin);
+}
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55f83a3a/plugins/experimental/lua/remap.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/remap.cc
b/plugins/experimental/lua/remap.cc
new file mode 100644
index 0000000..1dd573f
--- /dev/null
+++ b/plugins/experimental/lua/remap.cc
@@ -0,0 +1,131 @@
+/*
+ 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 <ts/ts.h>
+#include <ts/remap.h>
+#include <unistd.h>
+#include <string.h>
+#include "lapi.h"
+#include "lutil.h"
+
+static TSReturnCode
+LuaPluginRelease(lua_State * lua)
+{
+ lua_getglobal(lua, "release");
+ if (lua_isnil(lua, -1)) {
+ // No "release" callback.
+ return TS_SUCCESS;
+ }
+
+ if (lua_pcall(lua, 0, 0, 0) != 0) {
+ TSDebug("lua", "release failed: %s", lua_tostring(lua, -1));
+ lua_pop(lua, 1);
+ }
+
+ lua_close(lua);
+ return TS_SUCCESS;
+}
+
+static TSRemapStatus
+LuaPluginRemap(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
+{
+ LuaRemapRequest * rq;
+
+ lua_getglobal(lua, "remap");
+ if (lua_isnil(lua, -1)) {
+ // No "remap" callback, better continue.
+ return TSREMAP_NO_REMAP;
+ }
+
+ TSDebug("lua", "handling request %p on thread 0x%llx", rri, (unsigned long
long)pthread_self());
+
+ // XXX We can also cache the RemapRequestInfo in the Lua state. We we just
need to reset
+ // the rri pointer and status.
+ rq = LuaPushRemapRequestInfo(lua, txn, rri);
+
+ if (lua_pcall(lua, 1, 0, 0) != 0) {
+ TSDebug("lua", "remap failed: %s", lua_tostring(lua, -1));
+ lua_pop(lua, 1);
+ return TSREMAP_ERROR;
+ }
+
+ // XXX can we guarantee that rq has not been garbage collected?
+ return rq->status;
+}
+
+void
+TSRemapDeleteInstance(void * ih)
+{
+ lua_State * lua = (lua_State *)ih;
+
+ if (lua) {
+ LuaPluginRelease(lua);
+ lua_close(lua);
+ }
+}
+
+TSReturnCode
+TSRemapInit(TSRemapInterface * api_info, char * errbuf, int errbuf_size)
+{
+ TSDebug("lua", "loading lua plugin");
+ return TS_SUCCESS;
+}
+
+TSReturnCode
+TSRemapNewInstance(int argc, char * argv[], void ** ih, char * errbuf, int
errsz)
+{
+ LuaPluginState * remap;
+ lua_State * lua;
+
+ // Copy the plugin arguments so that we can use them to allocate a
per-thread Lua state. It would be cleaner
+ // to clone a Lua state, but there's no built-in way to do that, and to
implement that ourselves would require
+ // locking the template state (we need to manipulate the stack to copy
values out).
+ remap = tsnew<LuaPluginState>();
+ remap->init((unsigned)argc, (const char **)argv);
+
+ // Test whether we can successfully load the Lua program.
+ lua = LuaPluginNewState(remap);
+ if (!lua) {
+ tsdelete(remap);
+ return TS_ERROR;
+ }
+
+ *ih = remap;
+ return TS_SUCCESS;
+}
+
+TSRemapStatus
+TSRemapDoRemap(void * ih, TSHttpTxn txn, TSRemapRequestInfo * rri)
+{
+ LuaThreadInstance * lthread;
+
+ // Find or clone the per-thread Lua state.
+ lthread = LuaGetThreadInstance();
+ if (!lthread) {
+ LuaPluginState * lps;
+
+ lps = (LuaPluginState *)ih;
+ lthread = tsnew<LuaThreadInstance>();
+
+ TSDebug("lua", "allocating new Lua state on thread 0x%llx", (unsigned long
long)pthread_self());
+ lthread->lua = LuaPluginNewState(lps);
+ LuaSetThreadInstance(lthread);
+ }
+
+ return LuaPluginRemap(lthread->lua, txn, rri);
+}