You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2012/06/15 06:08:58 UTC
[2/2] git commit: TS-1302: Initial lua remap plugin
TS-1302: Initial lua remap plugin
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/7722bb1b
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/7722bb1b
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/7722bb1b
Branch: refs/heads/master
Commit: 7722bb1b9f9431ed5a4aee75be23113e1c5241cb
Parents: 01f0e3f
Author: James Peach <jp...@apache.org>
Authored: Wed Apr 4 22:10:02 2012 -0700
Committer: James Peach <jp...@apache.org>
Committed: Thu Jun 14 21:07:44 2012 -0700
----------------------------------------------------------------------
build/lua.m4 | 140 +++++++++++++
configure.ac | 10 +
plugins/Makefile.am | 2 +-
plugins/lua/Makefile.am | 33 +++
plugins/lua/example.lua | 63 ++++++
plugins/lua/lapi.cc | 465 ++++++++++++++++++++++++++++++++++++++++++
plugins/lua/lapi.h | 48 +++++
plugins/lua/lua.cc | 218 ++++++++++++++++++++
plugins/lua/lutil.cc | 80 +++++++
plugins/lua/lutil.h | 25 +++
10 files changed, 1083 insertions(+), 1 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/build/lua.m4
----------------------------------------------------------------------
diff --git a/build/lua.m4 b/build/lua.m4
new file mode 100644
index 0000000..71e4e29
--- /dev/null
+++ b/build/lua.m4
@@ -0,0 +1,140 @@
+dnl -------------------------------------------------------- -*- autoconf -*-
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements. See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License. You may obtain a copy of the License at
+dnl
+dnl http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+
+dnl Check for Lua 5.1 Libraries
+dnl
+dnl CHECK_LUA(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND])
+dnl Sets:
+dnl LUA_CFLAGS
+dnl LUA_LIBS
+AC_DEFUN([CHECK_LUA],
+[dnl
+
+AC_ARG_WITH(
+ lua,
+ [AC_HELP_STRING([--with-lua=PATH],[Path to the Lua 5.1 prefix])],
+ lua_path="$withval",
+ :)
+
+dnl # Determine lua lib directory
+if test -z "$lua_path"; then
+ test_paths=". /usr/local /usr"
+else
+ test_paths="${lua_path}"
+fi
+
+AC_CHECK_LIB(m, pow, lib_m="-lm")
+AC_CHECK_LIB(m, sqrt, lib_m="-lm")
+for x in $test_paths ; do
+ if test "x$x" = "x."; then
+ AC_CHECK_HEADER(lua.h,[
+ save_CFLAGS=$CFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS"
+ LDFLAGS="$LDFLAGS $lib_m"
+ AC_CHECK_LIB(lua5.1, luaL_newstate, [
+ LUA_LIBS="-llua5.1 $lib_m"
+ ],[
+ AC_CHECK_LIB(lua-5.1, luaL_newstate, [
+ LUA_LIBS="-llua-5.1 $lib_m"
+ ],[
+ AC_CHECK_LIB(lua, luaL_newstate, [
+ LUA_LIBS="-llua $lib_m"
+ ])
+ ])
+ ])
+ LUA_CFLAGS=
+ CFLAGS=$save_CFLAGS
+ LDFLAGS=$save_LDFLAGS
+ break
+ ])
+ else
+ AC_MSG_CHECKING([for lua.h in ${x}/include/lua5.1])
+ if test -f ${x}/include/lua5.1/lua.h; then
+ AC_MSG_RESULT([yes])
+ save_CFLAGS=$CFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS"
+ LDFLAGS="-L$x/lib $LDFLAGS $lib_m"
+ AC_CHECK_LIB(lua5.1, luaL_newstate, [
+ LUA_LIBS="-L$x/lib -llua5.1 $lib_m"
+ LUA_CFLAGS="-I$x/include/lua5.1"
+ ])
+ CFLAGS=$save_CFLAGS
+ LDFLAGS=$save_LDFLAGS
+ break
+ else
+ AC_MSG_RESULT([no])
+ fi
+ AC_MSG_CHECKING([for lua.h in ${x}/include/lua51])
+ if test -f ${x}/include/lua51/lua.h; then
+ AC_MSG_RESULT([yes])
+ save_CFLAGS=$CFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS"
+ LDFLAGS="-L$x/lib/lua51 $LDFLAGS $lib_m"
+ AC_CHECK_LIB(lua, luaL_newstate, [
+ LUA_LIBS="-L$x/lib/lua51 -llua $lib_m"
+ LUA_CFLAGS="-I$x/include/lua51"
+ ])
+ CFLAGS=$save_CFLAGS
+ LDFLAGS=$save_LDFLAGS
+ break
+ else
+ AC_MSG_RESULT([no])
+ fi
+ AC_MSG_CHECKING([for lua.h in ${x}/include])
+ if test -f ${x}/include/lua.h; then
+ AC_MSG_RESULT([yes])
+ save_CFLAGS=$CFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS"
+ LDFLAGS="-L$x/lib $LDFLAGS $lib_m"
+ AC_CHECK_LIB(lua, luaL_newstate, [
+ LUA_LIBS="-L$x/lib -llua $lib_m"
+ LUA_CFLAGS="-I$x/include"
+ ])
+ CFLAGS=$save_CFLAGS
+ LDFLAGS=$save_LDFLAGS
+ break
+ else
+ AC_MSG_RESULT([no])
+ fi
+ fi
+done
+
+AC_SUBST(LUA_LIBS)
+AC_SUBST(LUA_CFLAGS)
+
+if test -z "${LUA_LIBS}"; then
+ AC_MSG_WARN([*** Lua 5.1 library not found.])
+ ifelse([$2], ,
+ enable_lua="no"
+ if test -z "${lua_path}"; then
+ AC_MSG_WARN([Lua 5.1 library is required])
+ else
+ AC_MSG_ERROR([Lua 5.1 library is required])
+ fi,
+ $2)
+else
+ AC_MSG_NOTICE([using '${LUA_LIBS}' for Lua Library])
+ AC_ARG_ENABLE(luajit,
+ APACHE_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/7722bb1b/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 94e93e2..ab30adf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1063,6 +1063,15 @@ AC_SUBST(arg_max)
# Check for libreadline/libedit
AX_LIB_READLINE
+#
+# Check for Lua, at least version 5.1, sets LUA_CFLAGS and LUA_LIBS.
+CHECK_LUA(
+ [ enable_lua_support="yes" ],
+ [ enable_lua_support="no" ]
+)
+
+AM_CONDITIONAL([BUILD_LUA_SUPPORT], [ test "x${enable_lua_support}" = "xyes" ])
+
# -----------------------------------------------------------------------------
# 5. CHECK FOR HEADER FILES
@@ -1459,6 +1468,7 @@ AC_CONFIG_FILES([plugins/conf_remap/Makefile])
AC_CONFIG_FILES([plugins/regex_remap/Makefile])
AC_CONFIG_FILES([plugins/header_filter/Makefile])
AC_CONFIG_FILES([plugins/stats_over_http/Makefile])
+AC_CONFIG_FILES([plugins/lua/Makefile])
# various tools
AC_CONFIG_FILES([tools/Makefile])
# example plugins
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/plugins/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 14d0bf9..6d3d433 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SUBDIRS = conf_remap regex_remap header_filter stats_over_http
+SUBDIRS = conf_remap regex_remap header_filter stats_over_http lua
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/plugins/lua/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/lua/Makefile.am b/plugins/lua/Makefile.am
new file mode 100644
index 0000000..81fb201
--- /dev/null
+++ b/plugins/lua/Makefile.am
@@ -0,0 +1,33 @@
+# 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.
+
+pkglibdir = ${pkglibexecdir}
+
+if BUILD_LUA_SUPPORT
+
+AM_CXXFLAGS = \
+ ${LUA_CFLAGS} \
+ -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_LDFLAGS = -module -avoid-version -shared
+
+endif
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/plugins/lua/example.lua
----------------------------------------------------------------------
diff --git a/plugins/lua/example.lua b/plugins/lua/example.lua
new file mode 100644
index 0000000..a57c601
--- /dev/null
+++ b/plugins/lua/example.lua
@@ -0,0 +1,63 @@
+-- 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
+
+-- 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/7722bb1b/plugins/lua/lapi.cc
----------------------------------------------------------------------
diff --git a/plugins/lua/lapi.cc b/plugins/lua/lapi.cc
new file mode 100644
index 0000000..18d1bb6
--- /dev/null
+++ b/plugins/lua/lapi.cc
@@ -0,0 +1,465 @@
+/*
+ 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"
+
+// Return the type name string for the given index.
+#define LTYPEOF(L, index) lua_typename(L, lua_type(L, index))
+
+struct LuaRemapHeaders
+{
+ TSMBuffer buffer;
+ TSMLoc headers;
+
+ static LuaRemapHeaders * get(lua_State * lua, int index) {
+ return (LuaRemapHeaders *)luaL_checkudata(lua, index, "ts.meta.rri.headers");
+ }
+
+ static LuaRemapHeaders * alloc(lua_State * lua) {
+ LuaRemapHeaders * hdrs;
+
+ hdrs = (LuaRemapHeaders *)lua_newuserdata(lua, sizeof(LuaRemapHeaders));
+ luaL_getmetatable(lua, "ts.meta.rri.headers");
+ lua_setmetatable(lua, -2);
+
+ return hdrs;
+ }
+};
+
+LuaRemapRequest *
+LuaRemapRequest::get(lua_State * lua, int index)
+{
+ return (LuaRemapRequest *)luaL_checkudata(lua, index, "ts.meta.rri");
+}
+
+LuaRemapRequest *
+LuaRemapRequest::alloc(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+
+ rq = (LuaRemapRequest *)lua_newuserdata(lua, sizeof(LuaRemapRequest));
+ luaL_getmetatable(lua, "ts.meta.rri");
+ lua_setmetatable(lua, -2);
+
+ // Stash a new table as the environment for this object. We will use it later for __index.
+ lua_newtable(lua);
+ TSReleaseAssert(lua_setfenv(lua, -2));
+
+ return rq;
+}
+
+// Given a URL table on the top of the stack, pop it's values into the URL buffer.
+bool
+LuaPopUrl(lua_State * lua, TSMBuffer buffer, TSMLoc url)
+{
+ const char * strval;
+ size_t len;
+
+#define SET_URL_COMPONENT(name, setter) do { \
+ lua_getfield(lua, -1, name); \
+ if (!lua_isnil(lua, -1)) { \
+ strval = luaL_checklstring(lua, -1, &len); \
+ if (strval) { \
+ setter(buffer, url, strval, len); \
+ } \
+ } \
+ lua_pop(lua, 1); \
+} while (0)
+
+ // We ignore the 'href' field. When constructing URL tables, it's convenient, but it doesn't seem
+ // necessary here. Callers can easily construct the URL table.
+ SET_URL_COMPONENT("scheme", TSUrlSchemeSet);
+ SET_URL_COMPONENT("user", TSUrlUserSet);
+ SET_URL_COMPONENT("password", TSUrlPasswordSet);
+ SET_URL_COMPONENT("host", TSUrlHostSet);
+ SET_URL_COMPONENT("path", TSUrlPathSet);
+ SET_URL_COMPONENT("query", TSUrlHttpQuerySet);
+ SET_URL_COMPONENT("fragment", TSUrlHttpFragmentSet);
+
+ lua_getfield(lua, -1, "port");
+ if (!lua_isnil(lua, -1)) {
+ TSUrlPortSet(buffer, url, luaL_checkint(lua, -1));
+ }
+ lua_pop(lua, 1);
+
+#undef SET_URL_COMPONENT
+ return true;
+}
+
+bool
+LuaPushUrl(lua_State * lua, TSMBuffer buffer, TSMLoc url)
+{
+ int len;
+ const char * str;
+
+#define PUSH_URL_COMPONENT(accessor, name) do { \
+ str = accessor(buffer, url, &len); \
+ if (str) { \
+ lua_pushlstring(lua, str, len); \
+ } else { \
+ lua_pushnil(lua); \
+ } \
+ lua_setfield(lua, -2, name); \
+} while (0)
+
+ lua_newtable(lua);
+
+ // Set fundamental URL fields.
+ // XXX should we be luvit-compatible with these names?
+ PUSH_URL_COMPONENT(TSUrlSchemeGet, "scheme"); // luvit: protocol
+ PUSH_URL_COMPONENT(TSUrlUserGet, "user");
+ PUSH_URL_COMPONENT(TSUrlPasswordGet, "password");
+ PUSH_URL_COMPONENT(TSUrlHostGet, "host");
+ lua_pushinteger(lua, TSUrlPortGet(buffer, url));
+ lua_setfield(lua, -2, "port");
+ PUSH_URL_COMPONENT(TSUrlPathGet, "path"); // luvit: pathname
+ PUSH_URL_COMPONENT(TSUrlHttpQueryGet, "query"); // luvit: search
+ PUSH_URL_COMPONENT(TSUrlHttpFragmentGet, "fragment");
+
+ // It would be cleaner to add a __tostring metamethod, but to do that we would have to keep the
+ // buffer and url around indefinitely. Better to make a straight copy now; use the 'href' key
+ // just like luvit does.
+ str = TSUrlStringGet(buffer, url, &len);
+ if (str) {
+ lua_pushlstring(lua, str, len);
+ lua_setfield(lua, -2, "href");
+ TSfree((void *)str);
+ }
+
+ TSReleaseAssert(lua_istable(lua, -1) == 1);
+ return true;
+
+#undef PUSH_URL_COMPONENT
+}
+
+static int
+LuaRemapRedirect(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+
+ rq = LuaRemapRequest::get(lua, 1);
+ luaL_checktype(lua, 2, LUA_TTABLE);
+
+ TSDebug("lua", "redirecting request %p", rq->rri);
+
+ lua_pushvalue(lua, 2);
+ LuaPopUrl(lua, rq->rri->requestBufp, rq->rri->requestUrl);
+ lua_pop(lua, 1);
+
+ // A redirect always terminates plugin chain evaluation.
+ rq->rri->redirect = 1;
+ rq->status = TSREMAP_DID_REMAP_STOP;
+
+ // Return true back to Lua-space.
+ lua_pushboolean(lua, 1);
+ return 1;
+}
+
+static int
+LuaRemapRewrite(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+
+ rq = LuaRemapRequest::get(lua, 1);
+ luaL_checktype(lua, 2, LUA_TTABLE);
+
+ TSDebug("lua", "rewriting request %p", rq->rri);
+
+ lua_pushvalue(lua, 2);
+ LuaPopUrl(lua, rq->rri->requestBufp, rq->rri->requestUrl);
+ lua_pop(lua, 1);
+
+ // A rewrite updates the request URL but never terminates plugin chain evaluation.
+ rq->status = TSREMAP_DID_REMAP;
+
+ // Return true back to Lua-space.
+ lua_pushboolean(lua, 1);
+ return 1;
+}
+
+static int
+LuaRemapReject(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+ int status;
+ const char * body = NULL;
+
+ rq = LuaRemapRequest::get(lua, 1);
+ status = luaL_checkint(lua, 2);
+ if (!lua_isnoneornil(lua, 3)) {
+ body = luaL_checkstring(lua, 3);
+ }
+
+ TSDebug("lua", "rejecting request %p with status %d", rq->rri, status);
+
+ TSHttpTxnSetHttpRetStatus(rq->txn, (TSHttpStatus)status);
+ if (body && *body) {
+ // XXX Dubiously guess the content type from the body. This doesn't actually seem to work
+ // so it doesn't matter that our guess is pretty bad.
+ int isplain = (*body != '<');
+ TSHttpTxnSetHttpRetBody(rq->txn, body, isplain);
+ }
+
+ // A reject terminates plugin chain evaluation but does not update the request URL.
+ rq->status = TSREMAP_NO_REMAP_STOP;
+
+ return 1;
+}
+
+static int
+LuaRemapUrl(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+
+ rq = LuaRemapRequest::get(lua, 1);
+ LuaPushUrl(lua, rq->rri->requestBufp, rq->rri->requestUrl);
+ return 1;
+}
+
+// Since we cannot add fields to userdata objects, we use the environment to store the fields. If the requested
+// field isn't in our metatable, try to find it in the environment. Populate keys in the environment on demand if
+// the request is for a key that we know about.
+//
+// XXX When we set __index in the metatable, Lua routes all method calls through here rather than checking for the
+// existing key first. That's a bit surprising and I wonder whether there's a better way to handle this.
+static int
+LuaRemapIndex(lua_State * lua)
+{
+ LuaRemapRequest * rq;
+ const char * index;
+
+ rq = LuaRemapRequest::get(lua, 1);
+ index = luaL_checkstring(lua, 2);
+
+ TSDebug("lua", "%s[%s]", __func__, index);
+
+ // Get the userdata's metatable and look up the index in it.
+ lua_getmetatable(lua, 1);
+ lua_getfield(lua, -1, index);
+ if (!lua_isnoneornil(lua, -1)) {
+ // Pop the metatable, leaving the field value on top.
+ lua_remove(lua, -2);
+ return 1;
+ }
+
+ // Pop the field value and the metatable.
+ lua_pop(lua, 2);
+
+ lua_getfenv(lua, 1);
+
+ // Get the requested field from the environment table.
+ lua_getfield(lua, -1, index);
+
+ // If we have a value for that field, pop the environment table, leaving the value on top.
+ if (!lua_isnoneornil(lua, -1)) {
+ lua_remove(lua, -2);
+ return 1;
+ }
+
+ // Pop the nil field value.
+ lua_pop(lua, 1);
+
+ if (strcmp(index, "headers") == 0) {
+ LuaRemapHeaders * hdrs;
+
+ hdrs = LuaRemapHeaders::alloc(lua);
+ hdrs->buffer = rq->rri->requestBufp;
+ hdrs->headers = rq->rri->requestHdrp;
+
+ // Set it for the 'headers' index and then push it on the stack.
+ lua_setfield(lua, -2, index);
+ lua_getfield(lua, -1, index);
+
+ // Pop the environment table, leaving the field value on top.
+ lua_remove(lua, -2);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const luaL_Reg RRI[] =
+{
+ { "redirect", LuaRemapRedirect },
+ { "rewrite", LuaRemapRewrite },
+ { "reject", LuaRemapReject },
+ { "url", LuaRemapUrl },
+ { "__index", LuaRemapIndex },
+ { NULL, NULL}
+};
+
+static int
+LuaRemapHeaderIndex(lua_State * lua)
+{
+ LuaRemapHeaders * hdrs;
+ const char * index;
+ const char * value;
+ int vlen;
+ TSMLoc field;
+
+ hdrs = LuaRemapHeaders::get(lua, 1);;
+ index = luaL_checkstring(lua, 2);
+
+ TSDebug("lua", "%s[%s]", __func__, index);
+
+ field = TSMimeHdrFieldFind(hdrs->buffer, hdrs->headers, index, -1);
+ if (field == TS_NULL_MLOC) {
+ lua_pushnil(lua);
+ return 1;
+ }
+
+ value = TSMimeHdrFieldValueStringGet(hdrs->buffer, hdrs->headers, field, 0, &vlen);
+ lua_pushlstring(lua, value, vlen);
+ return 1;
+}
+
+static int
+LuaRemapHeaderNewIndex(lua_State * lua)
+{
+ LuaRemapHeaders * hdrs;
+ const char * index;
+ const char * value;
+ size_t vlen;
+ TSMLoc field;
+
+ hdrs = LuaRemapHeaders::get(lua, 1);
+ index = luaL_checkstring(lua, 2);
+
+ TSDebug("lua", "%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.
+ if (lua_isnoneornil(lua, 3)) {
+ if (field != TS_NULL_MLOC) {
+ TSMimeHdrFieldDestroy(hdrs->buffer, hdrs->headers, field);
+ TSHandleMLocRelease(hdrs->buffer, hdrs->headers, field);
+ }
+
+ return 1;
+ }
+
+ // If the MIME field doesn't exist yet, we'd better make it.
+ if (field == TS_NULL_MLOC) {
+ TSMimeHdrFieldCreateNamed(hdrs->buffer, hdrs->headers, index, -1, &field);
+ TSMimeHdrFieldAppend(hdrs->buffer, hdrs->headers, field);
+ }
+
+ TSMimeHdrFieldValuesClear(hdrs->buffer, hdrs->headers, field);
+
+ // Finally, we can set it's value.
+ switch(lua_type(lua, 3)) {
+ case LUA_TBOOLEAN:
+ value = lua_toboolean(lua, 3) ? "1" : "0";
+ vlen = 1;
+ break;
+ default:
+ value = lua_tolstring(lua, 3, &vlen);
+ break;
+ }
+
+ if (value) {
+ TSMimeHdrFieldValueStringInsert(hdrs->buffer, hdrs->headers, field, -1, value, vlen);
+ }
+
+ TSHandleMLocRelease(hdrs->buffer, hdrs->headers, field);
+ return 1;
+}
+
+static const luaL_Reg HEADERS[] =
+{
+ { "__index", LuaRemapHeaderIndex },
+ { "__newindex", LuaRemapHeaderNewIndex },
+ { NULL, NULL }
+};
+
+LuaRemapRequest *
+LuaPushRemapRequestInfo(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri)
+{
+ LuaRemapRequest * rq;
+
+ rq = LuaRemapRequest::alloc(lua);
+ rq->rri = rri;
+ rq->txn = txn;
+ rq->status = TSREMAP_NO_REMAP;
+
+ TSReleaseAssert(lua_isuserdata(lua, -1) == 1);
+ return rq;
+}
+
+static int
+TSLuaDebug(lua_State * lua)
+{
+ const char * tag = luaL_checkstring(lua, 1);
+ const char * message = luaL_checkstring(lua, 2);
+
+ TSDebug(tag, "%s", message);
+ return 0;
+}
+
+static const luaL_Reg LUAEXPORTS[] =
+{
+ { "debug", TSLuaDebug },
+ { NULL, NULL}
+};
+
+int
+LuaApiInit(lua_State * lua)
+{
+ TSDebug("lua", "initializing Lua API");
+
+ lua_newtable(lua);
+
+ // Register functions in the "ts" module.
+ 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");
+
+ // Register TSRemapRequestInfo metatable.
+ LuaPushMetatable(lua, "ts.meta.rri", RRI);
+ // Pop the metatable.
+ lua_pop(lua, 1);
+
+ // Register the remap headers metatable.
+ LuaPushMetatable(lua, "ts.meta.rri.headers", HEADERS);
+ // 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/7722bb1b/plugins/lua/lapi.h
----------------------------------------------------------------------
diff --git a/plugins/lua/lapi.h b/plugins/lua/lapi.h
new file mode 100644
index 0000000..9dc8534
--- /dev/null
+++ b/plugins/lua/lapi.h
@@ -0,0 +1,48 @@
+/*
+ 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_LAPI_H_
+#define LUA_LAPI_H_
+
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+}
+
+struct LuaRemapRequest
+{
+ TSRemapRequestInfo * rri;
+ TSHttpTxn txn;
+ TSRemapStatus status;
+
+ static LuaRemapRequest * get(lua_State * lua, int index);
+ static LuaRemapRequest * alloc(lua_State * lua);
+};
+
+// Initialize the 'ts' module.
+int LuaApiInit(lua_State * lua);
+
+// Push a copy of the given URL.
+bool LuaPushUrl(lua_State * lua, TSMBuffer buffer, TSMLoc url);
+
+// Push a wrapper object for the given TSRemapRequestInfo.
+LuaRemapRequest *
+LuaPushRemapRequestInfo(lua_State * lua, TSHttpTxn txn, TSRemapRequestInfo * rri);
+
+#endif // LUA_LAPI_H_
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/plugins/lua/lua.cc
----------------------------------------------------------------------
diff --git a/plugins/lua/lua.cc b/plugins/lua/lua.cc
new file mode 100644
index 0000000..9a74571
--- /dev/null
+++ b/plugins/lua/lua.cc
@@ -0,0 +1,218 @@
+/*
+ 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/7722bb1b/plugins/lua/lutil.cc
----------------------------------------------------------------------
diff --git a/plugins/lua/lutil.cc b/plugins/lua/lutil.cc
new file mode 100644
index 0000000..b8241f1
--- /dev/null
+++ b/plugins/lua/lutil.cc
@@ -0,0 +1,80 @@
+/*
+ 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>
+
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+}
+
+void *
+LuaAllocate(void * ud, void * ptr, size_t osize, size_t nsize)
+{
+ TSReleaseAssert(ud == NULL);
+
+ if (nsize == 0) {
+ TSfree(ptr);
+ return NULL;
+ }
+
+ return TSrealloc(ptr, nsize);
+}
+
+void
+LuaPushMetatable(lua_State * lua, const char * name, const luaL_Reg * exports)
+{
+ luaL_newmetatable(lua, name);
+ lua_pushvalue(lua, -1);
+ lua_setfield(lua, -2, "__index");
+ luaL_register(lua, NULL, exports);
+}
+
+void
+LuaRegisterLibrary(lua_State * lua, const char * name, lua_CFunction loader)
+{
+ // Pull up the preload table.
+ lua_getglobal(lua, "package");
+ lua_getfield(lua, -1, "preload");
+
+ lua_pushcfunction(lua, loader);
+ lua_setfield(lua, -2, name);
+
+ // Pop the 'package' and 'preload' tables.
+ lua_pop(lua, 2);
+}
+
+void
+LuaLoadLibraries(lua_State * lua)
+{
+#define REGISTER_LIBRARY(name) LuaRegisterLibrary(lua, #name, luaopen_ ## name)
+
+ lua_cpcall(lua, luaopen_base, NULL);
+ lua_cpcall(lua, luaopen_package, NULL);
+
+ REGISTER_LIBRARY(io);
+ REGISTER_LIBRARY(os);
+ REGISTER_LIBRARY(table);
+ REGISTER_LIBRARY(string);
+ REGISTER_LIBRARY(math);
+ REGISTER_LIBRARY(debug);
+
+#undef REGISTER_LIBRARY
+}
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7722bb1b/plugins/lua/lutil.h
----------------------------------------------------------------------
diff --git a/plugins/lua/lutil.h b/plugins/lua/lutil.h
new file mode 100644
index 0000000..d6e24a6
--- /dev/null
+++ b/plugins/lua/lutil.h
@@ -0,0 +1,25 @@
+/*
+ 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.
+*/
+
+// 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);