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);
+}

Reply via email to