trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jpe...@apache.org
Subject [1/2] git commit: TS-1440: Lua transaction and session hook support
Date Thu, 06 Sep 2012 03:41:44 GMT
Updated Branches:
  refs/heads/master 20443732b -> 5c03b0b0e


TS-1440: Lua transaction and session hook support

Implement Lua support for transaction and session hooks. We now
keep continuation tables for transaction, session and global hooks.
Session and transaction hooks are handled generically by using the
plugin argument index to pass a reference to the hooks table.

Remove transformation and alternate selection hooks since we don't
have support for tose wired up yet.

  - Add more Lua plugin examples.
  - Add LuaLogDebug and LuaLogError.
  - Bug fixes and stuff.
  - Add TODO list.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/5c03b0b0
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/5c03b0b0
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/5c03b0b0

Branch: refs/heads/master
Commit: 5c03b0b0e2a1e3d5d4cbae26caf39d440b301e96
Parents: 7f3402a
Author: James Peach <jpeach@apache.org>
Authored: Tue Jul 17 20:11:24 2012 -0700
Committer: James Peach <jpeach@apache.org>
Committed: Wed Sep 5 20:40:15 2012 -0700

----------------------------------------------------------------------
 CHANGES                                     |    2 +
 plugins/experimental/lua/TODO               |   51 +++
 plugins/experimental/lua/example.lua        |   57 ---
 plugins/experimental/lua/examples/hooks.lua |   90 +++++
 plugins/experimental/lua/examples/remap.lua |   72 ++++
 plugins/experimental/lua/examples/test.lua  |   14 +
 plugins/experimental/lua/hook.cc            |  443 +++++++++++++++++++---
 plugins/experimental/lua/hook.h             |   11 +
 plugins/experimental/lua/lapi.cc            |   60 +++-
 plugins/experimental/lua/lconfig.cc         |    2 +-
 plugins/experimental/lua/lutil.cc           |    6 +-
 plugins/experimental/lua/lutil.h            |   31 ++-
 plugins/experimental/lua/plugin.cc          |    6 +-
 plugins/experimental/lua/remap.cc           |   10 +-
 14 files changed, 711 insertions(+), 144 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index b53bcaf..a2bafee 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 3.3.0
 
+  *) [TS-1440] Lua transaction and session hook support
+
   *) [TS-1266] RAM cache stats are wrong with CLFUS.
 
   *) [TS-1437] Make the Lua plugin compile on RedHat based distros.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/TODO
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/TODO b/plugins/experimental/lua/TODO
new file mode 100644
index 0000000..e926771
--- /dev/null
+++ b/plugins/experimental/lua/TODO
@@ -0,0 +1,51 @@
+- Hook registration consistency
+
+  Per-object hooks register a hook table, but global hooks register
+  an individual function. We should make the register function
+  accept both calling conventions.
+
+- Transaction object parity
+
+  The transaction object that remap callbacks get is richer than
+  the one that hook callbacks get. They should both use the same
+  object and get the same API.
+
+- Richer TS API support
+
+  Flesh out API mappings. Humbedooh has a script that can generate
+  a lot of Lua binding. We should use that to make as much API as
+  we reasonably can.
+
+- HTTP client support
+
+  Wrap TSFetchURL (or its successor), such that Lua can fetch single
+  resources or multiple resources in parallel. For single resources,
+  use yield to make the call look synchronous; for multiple resources
+  consider a callback table.
+
+- HTTP server support
+
+  Reference stats_over_http to enabLe us server responses directly
+  from a Lua plugin. This would be useful for serving status, log
+  builder pages, admin tools, etc.
+
+- HTTP Alternate selection support
+
+  Enable support for HTTP_SELECT_ALT_HOOK. This probably just
+  requires plumbing an object for the TSHttpAltInfo edata argument.
+
+- Documentation
+
+  Document the Lua API in LuaDoc.
+
+- More examples
+
+  Add more Lua plugin examples. Take some of the header manipulation
+  C plugins and rewrite them in Lua. More examples will help us
+  figure out which Lua API is missing.
+
+- Transformation plugins
+
+  We don't support transforms yet. This probably warrants some
+  special purpose API rather than just using the existing hook
+  registration API.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/example.lua
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/example.lua b/plugins/experimental/lua/example.lua
deleted file mode 100644
index dabbe55..0000000
--- a/plugins/experimental/lua/example.lua
+++ /dev/null
@@ -1,57 +0,0 @@
--- Example Lua remap plugin. Load this with the following remap.comfig line:
---
--- map http://test.foo.com http://foo.foo.com @plugin=lua.so @pparam=/path/to/example.lua
-
--- Pull in the Traffic Server API.
-local TS = require 'ts'
-
-require 'string'
-require 'math'
-
--- Compulsory remap hook. We are given a request object that we can modify if necessary.
-function remap(request)
-  -- Get a copy of the current URL.
-  url = request:url()
-
-  TS.debug('example', string.format('remapping %s://%s', url.scheme, url.host))
-
-  -- Do some header manipulation, just to mess with the origin.
-  request.headers['added-bool'] = true
-  request.headers['added-int'] = 1
-  request.headers['added-string'] = 'ttt'
-  request.headers['added-table'] = {}
-  request.headers['deleted'] = nil
-
-  -- We can also print to stdout using Lua standard library.
-  print(string.format('request URL is %s://%s:%d/%s',
-        url.scheme, url.host, url.port, url.path and url.path or ''))
-
-  -- Modify components of the URL ... everybody loves slashdot.
-  url.host = 'www.slashdot.org'
-  url.port = 80
-  url.method = 'POST'
-
-  -- Plugin chain evaluation rules:
-  --    redirect: plugin chain terminates
-  --    reject: plugin chain terminates
-  --    rewrite: plugin chain continues
-
-  chance = math.random(4)
-  if chance == 1 then
-    -- Send a 301 redirect to the new URL.
-    request:redirect(url)
-  elseif chance == 2 then
-    -- Reject the request with an optional message.
-    request:reject(400, "Denied")
-  elseif chance == 3 then
-    -- Reject the request with a response body. We sniff the body to set the content type.
-    request:reject(500, [[
-      <HEAD></TITLE></HEAD>
-      <BODY>Internal error, sorry</BODY>
-      ]])
-  else
-    -- Rewrite the request URL. The remap plugin chain continues and other plugins
-    request:rewrite(url)
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/examples/hooks.lua
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/examples/hooks.lua b/plugins/experimental/lua/examples/hooks.lua
new file mode 100644
index 0000000..931cd22
--- /dev/null
+++ b/plugins/experimental/lua/examples/hooks.lua
@@ -0,0 +1,90 @@
+--  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.
+
+-- Example Lua hooks plugin to demonstrate the use of global, per-session and per-transaction
hooks.
+
+require 'string'
+require 'debug'
+
+ts = require 'ts'
+ts.hook = require 'ts.hook'
+
+function OnSession(event, ssn)
+    -- NOTE: the 'ssn' argument is either a session or a transaction object, depending on
the event.
+    ts.debug('hooks', string.format('handling session event %d', event))
+    ssn:continue()
+end
+
+-- Callback function for per-transaction hooks.
+function OnTransaction(event, txn)
+    ts.debug('hooks', string.format('handling transaction event %d', event))
+   txn:continue()
+end
+
+events = {
+    ssn = {},
+    txn = {}
+}
+
+events.txn[ts.hook.HTTP_OS_DNS_HOOK]                = OnTransaction
+events.txn[ts.hook.HTTP_READ_CACHE_HDR_HOOK]        = OnTransaction
+events.txn[ts.hook.HTTP_READ_REQUEST_HDR_HOOK]      = OnTransaction
+events.txn[ts.hook.HTTP_SEND_REQUEST_HDR_HOOK]      = OnTransaction
+events.txn[ts.hook.HTTP_READ_RESPONSE_HDR_HOOK]     = OnTransaction
+events.txn[ts.hook.HTTP_SEND_RESPONSE_HDR_HOOK]     = OnTransaction
+events.txn[ts.hook.HTTP_TXN_START_HOOK]             = OnTransaction
+events.txn[ts.hook.HTTP_TXN_CLOSE_HOOK]             = OnTransaction
+events.txn[ts.hook.HTTP_SSN_START_HOOK]             = OnTransaction
+events.txn[ts.hook.HTTP_SSN_CLOSE_HOOK]             = OnTransaction
+events.txn[ts.hook.HTTP_CACHE_LOOKUP_COMPLETE_HOOK] = OnTransaction
+events.txn[ts.hook.HTTP_PRE_REMAP_HOOK]             = OnTransaction
+events.txn[ts.hook.HTTP_POST_REMAP_HOOK]            = OnTransaction
+
+events.ssn[ts.hook.HTTP_READ_REQUEST_HDR_HOOK]      = OnSession
+events.ssn[ts.hook.HTTP_OS_DNS_HOOK]                = OnSession
+events.ssn[ts.hook.HTTP_SEND_REQUEST_HDR_HOOK]      = OnSession
+events.ssn[ts.hook.HTTP_READ_CACHE_HDR_HOOK]        = OnSession
+events.ssn[ts.hook.HTTP_READ_RESPONSE_HDR_HOOK]     = OnSession
+events.ssn[ts.hook.HTTP_SEND_RESPONSE_HDR_HOOK]     = OnSession
+events.ssn[ts.hook.HTTP_TXN_START_HOOK]             = OnSession
+events.ssn[ts.hook.HTTP_TXN_CLOSE_HOOK]             = OnSession
+events.ssn[ts.hook.HTTP_SSN_START_HOOK]             = OnSession
+events.ssn[ts.hook.HTTP_SSN_CLOSE_HOOK]             = OnSession
+events.ssn[ts.hook.HTTP_CACHE_LOOKUP_COMPLETE_HOOK] = OnSession
+events.ssn[ts.hook.HTTP_PRE_REMAP_HOOK]             = OnSession
+events.ssn[ts.hook.HTTP_POST_REMAP_HOOK]            = OnSession
+
+ts.debug('hooks', string.format('loaded %s', debug.getinfo(1).source))
+
+-- Hook the global session start so we can register the per-session events.
+ts.hook.register(ts.hook.HTTP_SSN_START_HOOK,
+    function(event, ssn)
+        ts.debug('hooks', string.format('callback for HTTP_SSN_START event=%d', event))
+        ssn:register(events.ssn)
+        ssn:continue()
+    end
+)
+
+-- Hook the global transaction start so we can register the per-transaction events.
+ts.hook.register(ts.hook.HTTP_TXN_START_HOOK,
+    function(event, txn)
+        ts.debug('hooks', string.format('callback for HTTP_TXN_START event=%d', event))
+        txn:register(events.txn)
+        txn:continue()
+    end
+)
+
+-- vim: set sw=4 ts=4 et :

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/examples/remap.lua
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/examples/remap.lua b/plugins/experimental/lua/examples/remap.lua
new file mode 100644
index 0000000..135b55c
--- /dev/null
+++ b/plugins/experimental/lua/examples/remap.lua
@@ -0,0 +1,72 @@
+--  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.
+
+-- Example Lua remap plugin. Load this with the following remap.comfig line:
+--
+-- map http://test.foo.com http://foo.foo.com @plugin=lua.so @pparam=/path/to/example.lua
+
+-- Pull in the Traffic Server API.
+local TS = require 'ts'
+
+require 'string'
+require 'math'
+
+-- Compulsory remap hook. We are given a request object that we can modify if necessary.
+function remap(request)
+  -- Get a copy of the current URL.
+  url = request:url()
+
+  TS.debug('example', string.format('remapping %s://%s', url.scheme, url.host))
+
+  -- Do some header manipulation, just to mess with the origin.
+  request.headers['added-bool'] = true
+  request.headers['added-int'] = 1
+  request.headers['added-string'] = 'ttt'
+  request.headers['added-table'] = {}
+  request.headers['deleted'] = nil
+
+  -- We can also print to stdout using Lua standard library.
+  print(string.format('request URL is %s://%s:%d/%s',
+        url.scheme, url.host, url.port, url.path and url.path or ''))
+
+  -- Modify components of the URL ... everybody loves slashdot.
+  url.host = 'www.slashdot.org'
+  url.port = 80
+  url.method = 'POST'
+
+  -- Plugin chain evaluation rules:
+  --    redirect: plugin chain terminates
+  --    reject: plugin chain terminates
+  --    rewrite: plugin chain continues
+
+  chance = math.random(4)
+  if chance == 1 then
+    -- Send a 301 redirect to the new URL.
+    request:redirect(url)
+  elseif chance == 2 then
+    -- Reject the request with an optional message.
+    request:reject(400, "Denied")
+  elseif chance == 3 then
+    -- Reject the request with a response body. We sniff the body to set the content type.
+    request:reject(500, [[
+      <HEAD></TITLE></HEAD>
+      <BODY>Internal error, sorry</BODY>
+      ]])
+  else
+    -- Rewrite the request URL. The remap plugin chain continues and other plugins
+    request:rewrite(url)
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/examples/test.lua
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/examples/test.lua b/plugins/experimental/lua/examples/test.lua
new file mode 100644
index 0000000..8693e98
--- /dev/null
+++ b/plugins/experimental/lua/examples/test.lua
@@ -0,0 +1,14 @@
+require 'string'
+require 'debug'
+ts = require 'ts'
+ts.hook = require 'ts.hook'
+
+ts.debug('lua', string.format('loaded %s', debug.getinfo(1).source))
+
+ts.hook.register(ts.hook.OS_DNS_HOOK,
+    function(event, txn)
+        ts.debug('lua', string.format('callback for event=%d', event))
+        txn:continue()
+    end
+)
+-- vim: set sw=4 ts=4 et :

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/hook.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/hook.cc b/plugins/experimental/lua/hook.cc
index 0794acc..464d901 100644
--- a/plugins/experimental/lua/hook.cc
+++ b/plugins/experimental/lua/hook.cc
@@ -24,35 +24,57 @@
 #include "hook.h"
 
 #include <memory> // placement new
+#include <ink_config.h>
 
-struct HookDemuxEntry
+typedef TSCont HookDemuxTable[TS_HTTP_LAST_HOOK];
+
+// Continuation tables for global, txn and ssn hooks. These are all indexed by the TSHttpHookID
and
+// are used to select which callback to invoke during event demuxing.
+static struct
 {
-  const char *  name;
-  int           hookid;
-  TSCont        cont;
-};
+  HookDemuxTable global;
+  HookDemuxTable txn;
+  HookDemuxTable ssn;
+} HttpHookDemuxTable;
 
-#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),
-};
+const char *
+HttpHookName(TSHttpHookID hookid)
+{
+  static const char * names[TS_HTTP_LAST_HOOK] = {
+    "HTTP_READ_REQUEST_HDR_HOOK",
+    "HTTP_OS_DNS_HOOK",
+    "HTTP_SEND_REQUEST_HDR_HOOK",
+    "HTTP_READ_CACHE_HDR_HOOK",
+    "HTTP_READ_RESPONSE_HDR_HOOK",
+    "HTTP_SEND_RESPONSE_HDR_HOOK",
+    NULL, // XXX TS_HTTP_REQUEST_TRANSFORM_HOOK
+    NULL, // XXX TS_HTTP_RESPONSE_TRANSFORM_HOOK
+    NULL, // XXX HTTP_SELECT_ALT_HOOK
+    "HTTP_TXN_START_HOOK",
+    "HTTP_TXN_CLOSE_HOOK",
+    "HTTP_SSN_START_HOOK",
+    "HTTP_SSN_CLOSE_HOOK",
+    "HTTP_CACHE_LOOKUP_COMPLETE_HOOK",
+    "HTTP_PRE_REMAP_HOOK",
+    "HTTP_POST_REMAP_HOOK",
+  };
+
+  if (hookid >= 0 && hookid < countof(names)) {
+    return names[hookid];
+  }
+
+  return NULL;
+}
+
+static bool
+HookIsValid(int hookid)
+{
+  if (hookid == TS_HTTP_REQUEST_TRANSFORM_HOOK || hookid == TS_HTTP_RESPONSE_TRANSFORM_HOOK)
{
+    return false;
+  }
+
+  return hookid >= 0 && hookid < TS_HTTP_LAST_HOOK;
+}
 
 static void
 LuaPushEventData(lua_State * lua, TSEvent event, void * edata)
@@ -68,6 +90,9 @@ LuaPushEventData(lua_State * lua, TSEvent event, void * edata)
   case TS_EVENT_HTTP_TXN_START:
   case TS_EVENT_HTTP_TXN_CLOSE:
   case TS_EVENT_CACHE_LOOKUP_COMPLETE:
+  case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
+  case TS_EVENT_HTTP_PRE_REMAP:
+  case TS_EVENT_HTTP_POST_REMAP:
     LuaPushHttpTransaction(lua, (TSHttpTxn)edata);
     break;
   case TS_EVENT_HTTP_SSN_START:
@@ -79,21 +104,115 @@ LuaPushEventData(lua_State * lua, TSEvent event, void * edata)
   }
 }
 
-// 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.
+// The per-ssn and per-txn argument mechanism stores a pointer, so it's NULL when not set.
Unfortunately, 0 is a
+// legitimate Lua reference value (all valies except LUA_NOREF are legitimate), so we can't
distinguish NULL from a 0
+// reference. In 64-bit mode we have some extra bits and we can maintain the state, but in
32-bit mode, we need to
+// allocate the LuaHookReference to have enough space to store the state.
 
-static int
-LuaHookDemux(TSCont cont, TSEvent event, void * edata)
+union LuaHookReference
+{
+  struct ref {
+    bool  set;
+    int   value;
+  } ref;
+  void * storage;
+};
+
+// For 64-bit pointers, we can inline the LuaHookReference, otherwise we need an extra malloc.
+#if SIZEOF_VOID_POINTER >= 8
+#define INLINE_LUA_HOOK_REFERENCE 1
+#else
+#undef INLINE_LUA_HOOK_REFERENCE
+#endif
+
+// Verify that LuaHookReference fits in sizeof(void *).
+#if INLINE_LUA_HOOK_REFERENCE
+extern char __LuaHookReferenceSizeCheck[sizeof(LuaHookReference) == SIZEOF_VOID_POINTER ?
0 : -1];
+#endif
+
+static void *
+LuaHttpObjectArgGet(TSHttpSsn ssn)
+{
+  return TSHttpSsnArgGet(ssn, LuaHttpArgIndex);
+}
+
+static void *
+LuaHttpObjectArgGet(TSHttpTxn txn)
+{
+  return TSHttpTxnArgGet(txn, LuaHttpArgIndex);
+}
+
+static void
+LuaHttpObjectArgSet(TSHttpSsn ssn, void * ptr)
+{
+  return TSHttpSsnArgSet(ssn, LuaHttpArgIndex, ptr);
+}
+
+static void
+LuaHttpObjectArgSet(TSHttpTxn txn, void * ptr)
+{
+  return TSHttpTxnArgSet(txn, LuaHttpArgIndex, ptr);
+}
+
+template<typename T> static int
+LuaGetArgReference(T ptr)
+{
+  LuaHookReference href;
+
+  href.storage = LuaHttpObjectArgGet(ptr);
+
+#if !defined(INLINE_LUA_HOOK_REFERENCE)
+  if (href.storage) {
+    href  = *(LuaHookReference *)href.storage;
+  }
+#endif
+
+  return (href.ref.set) ? href.ref.value : LUA_NOREF;
+}
+
+template <typename T> void
+LuaSetArgReference(T ptr, int ref)
+{
+  LuaHookReference href;
+
+  href.storage = NULL;
+  href.ref.value = ref;
+  href.ref.set = true;
+
+#if defined(INLINE_LUA_HOOK_REFERENCE)
+  LuaHttpObjectArgSet(ptr, href.storage);
+#else
+  LuaHookReference * tmp = (LuaHookReference *)LuaHttpObjectArgGet(ptr);
+  if (tmp) {
+    *tmp = href;
+  } else {
+    tmp = (LuaHookReference *)TSmalloc(sizeof(LuaHookReference));
+    *tmp = href;
+    LuaHttpObjectArgSet(ptr, tmp);
+  }
+#endif
+}
+
+template <typename T> static void
+LuaClearArgReference(T ptr)
+{
+#if !defined(INLINE_LUA_HOOK_REFERENCE)
+  TSfree(LuaHttpObjectArgGet(ptr));
+#endif
+  LuaHttpObjectArgSet(ptr, NULL);
+}
+
+// Force template instantiation of LuaSetArgReference().
+template void LuaSetArgReference<TSHttpSsn>(TSHttpSsn ssn, int ref);
+template void LuaSetArgReference<TSHttpTxn>(TSHttpTxn txn, int ref);
+
+static LuaThreadInstance *
+LuaDemuxThreadInstance()
 {
-  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();
@@ -101,22 +220,217 @@ LuaHookDemux(TSCont cont, TSEvent event, void * edata)
     LuaPluginLoad(lthread->lua, LuaPlugin);
   }
 
-  TSAssert(hookid >= 0);
-  TSAssert(hookid < TS_HTTP_LAST_HOOK);
+  return lthread;
+}
 
-  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
+static TSHttpHookID
+LuaDemuxHookID(TSCont cont)
+{
+  TSHttpHookID hookid = (TSHttpHookID)(intptr_t)TSContDataGet(cont);
+  TSAssert(HookIsValid(hookid));
+  return hookid;
+}
+
+static void
+LuaDemuxInvokeCallback(lua_State * lua, TSHttpHookID hookid, TSEvent event, void * edata,
int ref)
+{
+  int nitems = lua_gettop(lua);
+
+  // Push the callback table onto the top of the stack.
+  lua_rawgeti(lua, LUA_REGISTRYINDEX, ref);
+
+  // XXX If this is a global hook, we have a function reference. If it's a ssn or txn hook
then we
+  // have a callback table reference. We need to make these the same, but not rught now ...
+
+  switch (lua_type(lua, -1)) {
+    case LUA_TFUNCTION:
+      // Nothing to do, the function we want to invoke is already on top of the stack.
+      break;
+    case LUA_TTABLE:
+      // Push the hookid onto the stack so we can use it to index the table (that is now
at -2).
+      lua_pushinteger(lua, hookid);
+
+      TSAssert(lua_isnumber(lua, -1));
+      TSAssert(lua_istable(lua, -2));
+
+      // Index the callback table with the hookid to get the callback function for this hook.
+      lua_gettable(lua, -2);
+
+      break;
+    default:
+      LuaLogError("invalid callback reference type %s", ltypeof(lua, -1));
+      TSReleaseAssert(0);
+  }
+
+  // The item on the top of the stack *ought* to be the callback function. However when we
register a cleanup function
+  // to release the callback reference (because the ssn ot txn closes), then we won't have
a function because there's
+  // nothing to do here.
+  if (!lua_isnil(lua, -1)) {
+
+    TSAssert(lua_isfunction(lua, -1));
+
+    lua_pushinteger(lua, event);
+    LuaPushEventData(lua, event, edata);
+
+    if (lua_pcall(lua, 2 /* nargs */, 0, 0) != 0) {
+      LuaLogDebug("hook callback failed: %s", lua_tostring(lua, -1));
+      lua_pop(lua, 1); // pop the error message
     }
-  } else {
-    TSDebug("lua", "no demuxer for %s", HttpHookDemuxTable[hookid].name);
   }
 
-  return 0;
+  // If we left anything on the stack, pop it.
+  lua_pop(lua, lua_gettop(lua) - nitems);
+}
+
+static int
+LuaDemuxGlobalHook(TSCont cont, TSEvent event, void * edata)
+{
+  TSHttpHookID        hookid = LuaDemuxHookID(cont);
+  LuaThreadInstance * lthread = LuaDemuxThreadInstance();
+  int                 ref = lthread->hooks[hookid];
+
+  LuaLogDebug("%s lthread=%p event=%d edata=%p, ref=%d",
+      HttpHookName(hookid), lthread, event, edata, ref);
+
+  if (ref == LUA_NOREF) {
+    LuaLogError("no Lua callback for hook %s", HttpHookName(hookid));
+    return TS_EVENT_ERROR;
+  }
+
+  LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+  return TS_EVENT_NONE;
+}
+
+static int
+LuaDemuxTxnHook(TSCont cont, TSEvent event, void * edata)
+{
+  TSHttpHookID        hookid = LuaDemuxHookID(cont);
+  LuaThreadInstance * lthread = LuaDemuxThreadInstance();
+  int                 ref = LuaGetArgReference((TSHttpTxn)edata);
+
+  LuaLogDebug("%s(%s) lthread=%p event=%d edata=%p",
+      __func__, HttpHookName(hookid), lthread, event, edata);
+
+  if (ref == LUA_NOREF) {
+    LuaLogError("no Lua callback for hook %s", HttpHookName(hookid));
+    return TS_EVENT_ERROR;
+  }
+
+  LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+
+  if (event == TS_EVENT_HTTP_TXN_CLOSE) {
+    LuaLogDebug("unref event handler %d", ref);
+    luaL_unref(lthread->lua, LUA_REGISTRYINDEX, ref);
+    LuaClearArgReference((TSHttpTxn)edata);
+  }
+
+  return TS_EVENT_NONE;
+}
+
+static int
+LuaDemuxSsnHook(TSCont cont, TSEvent event, void * edata)
+{
+  TSHttpHookID        hookid = LuaDemuxHookID(cont);
+  LuaThreadInstance * lthread = LuaDemuxThreadInstance();
+  TSHttpSsn           ssn;
+  int                 ref;
+
+  // The edata might be a Txn or a Ssn, depending on the event type. If we get here, it's
because we registered a
+  // callback on the Ssn, so we need to get back to the Ssn object in order to the the callback
table reference ...
+  switch (event) {
+    case TS_EVENT_HTTP_SSN_START:
+    case TS_EVENT_HTTP_SSN_CLOSE:
+      ssn = (TSHttpSsn)edata;
+      break;
+    default:
+      ssn = TSHttpTxnSsnGet((TSHttpTxn)edata);
+  }
+
+  LuaLogDebug("%s(%s) lthread=%p event=%d edata=%p",
+      __func__, HttpHookName(hookid), lthread, event, edata);
+
+  ref = LuaGetArgReference(ssn);
+  if (ref == LUA_NOREF) {
+    LuaLogError("no Lua callback for hook %s", HttpHookName(hookid));
+    return TS_EVENT_ERROR;
+  }
+
+  LuaDemuxInvokeCallback(lthread->lua, hookid, event, edata, ref);
+
+  if (event == TS_EVENT_HTTP_SSN_CLOSE) {
+    LuaLogDebug("unref event handler %d", ref);
+    luaL_unref(lthread->lua, LUA_REGISTRYINDEX, ref);
+    LuaClearArgReference((TSHttpSsn)edata);
+  }
+
+  return TS_EVENT_NONE;
+}
+
+bool
+LuaRegisterHttpHooks(lua_State * lua, void * obj, LuaHookAddFunction add, int hooks)
+{
+  bool hooked_close = false;
+  const TSHttpHookID closehook = (add == LuaHttpSsnHookAdd ? TS_HTTP_SSN_CLOSE_HOOK : TS_HTTP_TXN_CLOSE_HOOK);
+
+  TSAssert(add == LuaHttpSsnHookAdd || add == LuaHttpTxnHookAdd);
+
+  // Push the hooks reference back onto the stack.
+  lua_rawgeti(lua, LUA_REGISTRYINDEX, hooks);
+
+  // The value on the top of the stack (index -1) MUST be the callback table.
+  TSAssert(lua_istable(lua, lua_gettop(lua)));
+
+  // Walk the table and register the hook for each entry.
+  lua_pushnil(lua);  // Push the first key, makes the callback table index -2.
+  while (lua_next(lua, -2) != 0) {
+    TSHttpHookID hookid;
+
+    // uses 'key' (at index -2) and 'value' (at index -1).
+    // LuaLogDebug("key=%s value=%s\n", ltypeof(lua, -2), ltypeof(lua, -1));
+
+    // Now the key (index -2) and value (index -1) got pushed onto the stack. The key must
be a hook ID and the value
+    // must be a callback function.
+    luaL_checktype(lua, -1, LUA_TFUNCTION);
+    hookid = (TSHttpHookID)luaL_checkint(lua, -2);
+
+    if (!HookIsValid(hookid)) {
+      LuaLogError("invalid Hook ID %d", hookid);
+      goto next;
+    }
+
+    if (hookid == closehook) {
+      hooked_close = true;
+    }
+
+    // At demux time, we need the hook ID and the table (or function) ref.
+    add(obj, hookid);
+    LuaLogDebug("registered callback table %d for event %s on object %p",
+        hooks, HttpHookName(hookid), obj);
+
+next:
+    // Pop the value (index -1), leaving key as the new top (index -1).
+    lua_pop(lua, 1);
+  }
+
+  // we always need to hook the close because we keep a reference to the callback table and
we need to release that
+  // reference when the object's lifetime ends.
+  if (!hooked_close) {
+    add(obj, closehook);
+  }
+
+  return true;
+}
+
+void
+LuaHttpSsnHookAdd(void * ssn, TSHttpHookID hookid)
+{
+  TSHttpSsnHookAdd((TSHttpSsn)ssn, hookid, HttpHookDemuxTable.ssn[hookid]);
+}
+
+void
+LuaHttpTxnHookAdd(void * txn, TSHttpHookID hookid)
+{
+  TSHttpTxnHookAdd((TSHttpTxn)txn, hookid, HttpHookDemuxTable.txn[hookid]);
 }
 
 static int
@@ -125,11 +439,13 @@ TSLuaHttpHookRegister(lua_State * lua)
   TSHttpHookID hookid;
   LuaThreadInstance * lthread;
 
+  LuaLogDebug("[1]=%s [2]=%s", ltypeof(lua, 1), ltypeof(lua, 2));
   hookid = (TSHttpHookID)luaL_checkint(lua, 1);
   luaL_checktype(lua, 2, LUA_TFUNCTION);
 
+  LuaLogDebug("registering hook %s (%d)", HttpHookName(hookid), (int)hookid);
   if (hookid < 0 || hookid >= TS_HTTP_LAST_HOOK) {
-    TSDebug("lua", "hook ID %d out of range", hookid);
+    LuaLogDebug("hook ID %d out of range", hookid);
     return -1;
   }
 
@@ -143,22 +459,22 @@ TSLuaHttpHookRegister(lua_State * lua)
   // 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);
+    TSReleaseAssert(HttpHookDemuxTable.global[hookid] != NULL);
     return 0;
   }
 
   lthread->hooks[hookid] = luaL_ref(lua, LUA_REGISTRYINDEX);
 
-  if (HttpHookDemuxTable[hookid].cont == NULL) {
+  if (HttpHookDemuxTable.global[hookid] == 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);
+    cont = TSContCreate(LuaDemuxGlobalHook, TSMutexCreate());
+    if (__sync_bool_compare_and_swap(&HttpHookDemuxTable.global[hookid], NULL, cont))
{
+      LuaLogDebug("installed continuation for %s", HttpHookName(hookid));
       TSContDataSet(cont, (void *)hookid);
       TSHttpHookAdd(hookid, cont);
     } else {
-      TSDebug("lua", "lost hook creation race for %s", HttpHookDemuxTable[hookid].name);
+      LuaLogDebug("lost hook creation race for %s", HttpHookName(hookid));
       TSContDestroy(cont);
     }
   }
@@ -175,20 +491,29 @@ static const luaL_Reg LUAEXPORTS[] =
 int
 LuaHookApiInit(lua_State * lua)
 {
-  TSDebug("lua", "initializing TS Hook API");
+  LuaLogDebug("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) {
+  for (unsigned i = 0; i < TS_HTTP_LAST_HOOK; ++i) {
+    if (HttpHookName((TSHttpHookID)i) == NULL) {
+      // Unsupported hook, skip it.
       continue;
     }
 
-    TSAssert((int)i == HttpHookDemuxTable[i].hookid);
-    LuaSetConstantField(lua, HttpHookDemuxTable[i].name, HttpHookDemuxTable[i].hookid);
+    // Register named constants for each hook ID.
+    LuaSetConstantField(lua, HttpHookName((TSHttpHookID)i), i);
+    // Allocate txn and ssn continuations.
+    HttpHookDemuxTable.txn[i] = TSContCreate(LuaDemuxTxnHook, NULL);
+    HttpHookDemuxTable.ssn[i] = TSContCreate(LuaDemuxSsnHook, NULL);
+    // And keep track of which hook each continuation was allocated for.
+    TSContDataSet(HttpHookDemuxTable.txn[i], (void *)i);
+    TSContDataSet(HttpHookDemuxTable.ssn[i], (void *)i);
+
+    // Note that we allocate the global continuation table lazily so that we know when to
add the hook.
   }
 
   return 1;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/hook.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/hook.h b/plugins/experimental/lua/hook.h
index 3522779..58ea4ac 100644
--- a/plugins/experimental/lua/hook.h
+++ b/plugins/experimental/lua/hook.h
@@ -19,4 +19,15 @@
 #ifndef LUA_HOOK_H_
 #define LUA_HOOK_H_
 
+// Pointer to LuaHttpSsnHookAdd() or LuaHttpTxnsnHookAdd().
+typedef void (*LuaHookAddFunction)(void *, TSHttpHookID);
+
+void LuaHttpSsnHookAdd(void *, TSHttpHookID);
+void LuaHttpTxnHookAdd(void *, TSHttpHookID);
+
+// Set a LuaHookReference as the argument on the corresponding object. T can be either TSHttpSsn
or TSHttpTxn.
+template <typename T> void LuaSetArgReference(T ptr, int ref);
+
+bool LuaRegisterHttpHooks(lua_State *, void *, LuaHookAddFunction, int);
+
 #endif // LUA_HOOK_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/lapi.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lapi.cc b/plugins/experimental/lua/lapi.cc
index 2eafcee..0398b9f 100644
--- a/plugins/experimental/lua/lapi.cc
+++ b/plugins/experimental/lua/lapi.cc
@@ -21,6 +21,7 @@
 #include <string.h>
 #include "lapi.h"
 #include "lutil.h"
+#include "hook.h"
 
 template <typename LuaType, typename Param1> LuaType *
 push_userdata_object(lua_State * lua, Param1 p1)
@@ -64,6 +65,8 @@ struct LuaHttpTransaction
 {
   TSHttpTxn txn;
 
+  LuaHttpTransaction() : txn(NULL) {}
+
   static LuaHttpTransaction * get(lua_State * lua, int index) {
     return (LuaHttpTransaction *)luaL_checkudata(lua, index, "ts.meta.http.txn");
   }
@@ -82,7 +85,9 @@ struct LuaHttpTransaction
 
 struct LuaHttpSession
 {
-  TSHttpSsn ssn;
+  TSHttpSsn ssn;      // session pointer
+
+  LuaHttpSession() : ssn(NULL) {}
 
   static LuaHttpSession * get(lua_State * lua, int index) {
     return (LuaHttpSession *)luaL_checkudata(lua, index, "ts.meta.http.ssn");
@@ -214,7 +219,7 @@ LuaRemapRedirect(lua_State * lua)
   rq = LuaRemapRequest::get(lua, 1);
   luaL_checktype(lua, 2, LUA_TTABLE);
 
-  TSDebug("lua", "redirecting request %p", rq->rri);
+  LuaLogDebug("redirecting request %p", rq->rri);
 
   lua_pushvalue(lua, 2);
   LuaPopUrl(lua, rq->rri->requestBufp, rq->rri->requestUrl);
@@ -237,7 +242,7 @@ LuaRemapRewrite(lua_State * lua)
   rq = LuaRemapRequest::get(lua, 1);
   luaL_checktype(lua, 2, LUA_TTABLE);
 
-  TSDebug("lua", "rewriting request %p", rq->rri);
+  LuaLogDebug("rewriting request %p", rq->rri);
 
   lua_pushvalue(lua, 2);
   LuaPopUrl(lua, rq->rri->requestBufp, rq->rri->requestUrl);
@@ -264,7 +269,7 @@ LuaRemapReject(lua_State * lua)
     body = luaL_checkstring(lua, 3);
   }
 
-  TSDebug("lua", "rejecting request %p with status %d", rq->rri, status);
+  LuaLogDebug("rejecting request %p with status %d", rq->rri, status);
 
   TSHttpTxnSetHttpRetStatus(rq->txn, (TSHttpStatus)status);
   if (body && *body) {
@@ -305,7 +310,7 @@ LuaRemapIndex(lua_State * lua)
   rq = LuaRemapRequest::get(lua, 1);
   index = luaL_checkstring(lua, 2);
 
-  TSDebug("lua", "%s[%s]", __func__, index);
+  LuaLogDebug("%s[%s]", __func__, index);
 
   // Get the userdata's metatable and look up the index in it.
   lua_getmetatable(lua, 1);
@@ -374,7 +379,7 @@ LuaRemapHeaderIndex(lua_State * lua)
   hdrs = LuaRemapHeaders::get(lua, 1);;
   index = luaL_checkstring(lua, 2);
 
-  TSDebug("lua", "%s[%s]", __func__, index);
+  LuaLogDebug("%s[%s]", __func__, index);
 
   field = TSMimeHdrFieldFind(hdrs->buffer, hdrs->headers, index, -1);
   if (field == TS_NULL_MLOC) {
@@ -399,7 +404,7 @@ LuaRemapHeaderNewIndex(lua_State * lua)
   hdrs = LuaRemapHeaders::get(lua, 1);
   index = luaL_checkstring(lua, 2);
 
-  TSDebug("lua", "%s[%s] = (%s)", __func__, index, LTYPEOF(lua, 3));
+  LuaLogDebug("%s[%s] = (%s)", __func__, index, ltypeof(lua, 3));
   field = TSMimeHdrFieldFind(hdrs->buffer, hdrs->headers, index, -1);
 
   // Setting a key to nil means to delete it.
@@ -468,10 +473,32 @@ LuaHttpTxnContinue(lua_State * lua)
   return 1;
 }
 
+static int
+LuaHttpTxnRegister(lua_State * lua)
+{
+  LuaHttpTransaction * txn;
+  int tableref;
+
+  txn = LuaHttpTransaction::get(lua, 1);
+  luaL_checktype(lua, 2, LUA_TTABLE);
+
+  // Keep a reference to the hooks table in ssn->hooks.
+  tableref = luaL_ref(lua, LUA_REGISTRYINDEX);
+
+  // On the other side of the denux, we need the hook, and the table.
+  if (LuaRegisterHttpHooks(lua, txn->txn, LuaHttpTxnHookAdd, tableref)) {
+    LuaSetArgReference(txn->txn, tableref);
+    return 1;
+  }
+
+  return 0;
+}
+
 static const luaL_Reg HTTPTXN[] =
 {
   { "abort", LuaHttpTxnAbort },
   { "continue", LuaHttpTxnContinue },
+  { "register", LuaHttpTxnRegister },
   { NULL, NULL }
 };
 
@@ -501,18 +528,21 @@ static int
 LuaHttpSsnRegister(lua_State * lua)
 {
   LuaHttpSession * ssn;
-  int hookid;
+  int tableref;
 
   ssn = LuaHttpSession::get(lua, 1);
-  hookid = luaL_checkint(lua, 2);
+  luaL_checktype(lua, 2, LUA_TTABLE);
 
-  // XXX: need to actually register the hooks here ...
+  // Keep a reference to the hooks table in ssn->hooks.
+  tableref = luaL_ref(lua, LUA_REGISTRYINDEX);
 
-  // Prevent unused variable warnings.
-  (void)ssn;
-  (void)hookid;
+  // On the other side of the denux, we need the hook, and the table.
+  if (LuaRegisterHttpHooks(lua, ssn->ssn, LuaHttpSsnHookAdd, tableref)) {
+    LuaSetArgReference(ssn->ssn, tableref);
+    return 1;
+  }
 
-  return 1;
+  return 0;
 }
 
 static const luaL_Reg HTTPSSN[] =
@@ -560,7 +590,7 @@ LuaPushHttpSession(lua_State * lua, TSHttpSsn ssn)
 int
 LuaApiInit(lua_State * lua)
 {
-  TSDebug("lua", "initializing TS API");
+  LuaLogDebug("initializing TS API");
 
   lua_newtable(lua);
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/lconfig.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lconfig.cc b/plugins/experimental/lua/lconfig.cc
index c112a7e..69b1571 100644
--- a/plugins/experimental/lua/lconfig.cc
+++ b/plugins/experimental/lua/lconfig.cc
@@ -74,7 +74,7 @@ static const luaL_Reg LUAEXPORTS[] =
 int
 LuaConfigApiInit(lua_State * lua)
 {
-  TSDebug("lua", "initializing TS Config API");
+  LuaLogDebug("initializing TS Config API");
 
   lua_newtable(lua);
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/lutil.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lutil.cc b/plugins/experimental/lua/lutil.cc
index 50e1557..0fced5f 100644
--- a/plugins/experimental/lua/lutil.cc
+++ b/plugins/experimental/lua/lutil.cc
@@ -24,12 +24,14 @@
 #include <unistd.h>
 
 static thread_local_pointer<LuaThreadInstance> LuaThread;
+
 LuaPluginState * LuaPlugin;
+int LuaHttpArgIndex;
 
 LuaThreadInstance::LuaThreadInstance()
   : lua(NULL)
 {
-  for (unsigned i = 0; i < arraysz(this->hooks); ++i) {
+  for (unsigned i = 0; i < countof(this->hooks); ++i) {
     this->hooks[i] = LUA_NOREF;
   }
 }
@@ -104,7 +106,7 @@ LuaPluginLoad(lua_State * lua, LuaPluginState * plugin)
 
     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));
+      LuaLogError("failed to load Lua file %s: %s", p->c_str(), lua_tostring(lua, -1));
       return false;
     }
   }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/lutil.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/lutil.h b/plugins/experimental/lua/lutil.h
index 2ede5bc..6e386fe 100644
--- a/plugins/experimental/lua/lutil.h
+++ b/plugins/experimental/lua/lutil.h
@@ -31,6 +31,32 @@ struct LuaThreadInstance;
 
 extern LuaPluginState * LuaPlugin;
 
+// Global argument index for TSHttpSsnArgGet and TSHttpTxnArgGet.
+extern int LuaHttpArgIndex;
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define likely(x)   __builtin_expect(!!(x), 1)
+
+#define LuaLogDebug(fmt, ...) do { \
+    if (unlikely(TSIsDebugTagSet("lua"))) { \
+        TSDebug("lua", "%s: "fmt, __func__, ##__VA_ARGS__); \
+    } \
+} while (0)
+
+// In DEBUG mode, log errors to the debug channel. This is handy for making Lua runtime
+// errors show up on stdout along with the rest of the debug loggin.
+#if DEBUG
+#define LuaLogError(fmt, ...) LuaLogDebug(fmt, ##__VA_ARGS__)
+#else
+#define LuaLogError(fmt, ...) TSError(fmt, ##__VA_ARGS__)
+#endif
+
+// Return the type name string for the given index.
+static inline const char *
+ltypeof(lua_State * lua, int index) {
+  return lua_typename(lua, lua_type(lua, index));
+}
+
 template <typename T> T * tsnew() {
   void * ptr = TSmalloc(sizeof(T));
   return new(ptr) T();
@@ -49,9 +75,6 @@ template <typename T> T * LuaNewUserData(lua_State * lua) {
   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 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);
@@ -94,7 +117,7 @@ struct LuaThreadInstance
 };
 
 template <typename T, unsigned N> unsigned
-arraysz(const T (&)[N]) {
+countof(const T (&)[N]) {
   return N;
 }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/plugin.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/plugin.cc b/plugins/experimental/lua/plugin.cc
index 52f5f25..a3e1d7e 100644
--- a/plugins/experimental/lua/plugin.cc
+++ b/plugins/experimental/lua/plugin.cc
@@ -31,11 +31,15 @@ TSPluginInit(int argc, const char * argv[])
   info.support_email = (char *)"dev@trafficserver.apache.org";
 
   if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
-      TSError("Plugin registration failed");
+      LuaLogError("Plugin registration failed");
   }
 
   TSAssert(LuaPlugin == NULL);
 
+  // Allocate a TSHttpTxn argument index for handling per-transaction hooks.
+  TSReleaseAssert(TSHttpArgIndexReserve(info.plugin_name, info.plugin_name, &LuaHttpArgIndex)
== TS_SUCCESS);
+
+  // Create the initial global Lua state.
   LuaPlugin = tsnew<LuaPluginState>();
   LuaPlugin->init((unsigned)argc, (const char **)argv);
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5c03b0b0/plugins/experimental/lua/remap.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/lua/remap.cc b/plugins/experimental/lua/remap.cc
index 1dd573f..21928a4 100644
--- a/plugins/experimental/lua/remap.cc
+++ b/plugins/experimental/lua/remap.cc
@@ -33,7 +33,7 @@ LuaPluginRelease(lua_State * lua)
   }
 
   if (lua_pcall(lua, 0, 0, 0) != 0) {
-    TSDebug("lua", "release failed: %s", lua_tostring(lua, -1));
+    LuaLogDebug("release failed: %s", lua_tostring(lua, -1));
     lua_pop(lua, 1);
   }
 
@@ -52,14 +52,14 @@ LuaPluginRemap(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
     return TSREMAP_NO_REMAP;
   }
 
-  TSDebug("lua", "handling request %p on thread 0x%llx", rri, (unsigned long long)pthread_self());
+  LuaLogDebug("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));
+    LuaLogDebug("remap failed: %s", lua_tostring(lua, -1));
     lua_pop(lua, 1);
     return TSREMAP_ERROR;
   }
@@ -82,7 +82,7 @@ TSRemapDeleteInstance(void * ih)
 TSReturnCode
 TSRemapInit(TSRemapInterface * api_info, char * errbuf, int errbuf_size)
 {
-  TSDebug("lua", "loading lua plugin");
+  LuaLogDebug("loading lua plugin");
   return TS_SUCCESS;
 }
 
@@ -122,7 +122,7 @@ TSRemapDoRemap(void * ih, TSHttpTxn txn, TSRemapRequestInfo * rri)
     lps = (LuaPluginState *)ih;
     lthread = tsnew<LuaThreadInstance>();
 
-    TSDebug("lua", "allocating new Lua state on thread 0x%llx", (unsigned long long)pthread_self());
+    LuaLogDebug("allocating new Lua state on thread 0x%llx", (unsigned long long)pthread_self());
     lthread->lua = LuaPluginNewState(lps);
     LuaSetThreadInstance(lthread);
   }


Mime
View raw message