diff --git a/configure.in b/configure.in
index 549023ab..5b3fbe3f 100644
--- a/configure.in
+++ b/configure.in
@@ -271,7 +271,20 @@ AC_ARG_WITH(memcache, AC_HELP_STRING([--with-memcache],[memcached storage for mo
])
],[AC_MSG_RESULT(no)])
AC_SUBST(MEMCACHE_LIB)
-
+
+AC_MSG_CHECKING(for lua)
+AC_ARG_WITH(lua, AC_HELP_STRING([--with-lua],[lua engine for mod_cml]),
+[AC_MSG_RESULT(yes)
+ AC_CHECK_LIB(lua, lua_open, [
+ AC_CHECK_HEADERS([lua.h],[
+ LUA_LIB="-llua -llualib -lm"
+ AC_DEFINE([HAVE_LUA], [1], [liblua])
+ AC_DEFINE([HAVE_LUA_H], [1])
+ ])
+ ])
+],[AC_MSG_RESULT(no)])
+AC_SUBST(LUA_LIB)
+
AC_SEARCH_LIBS(socket,socket)
AC_SEARCH_LIBS(gethostbyname,nsl socket)
diff --git a/doc/cml.txt b/doc/cml.txt
index d0f9b694..fc086229 100644
--- a/doc/cml.txt
+++ b/doc/cml.txt
@@ -11,10 +11,10 @@ Module: mod_cml
:Revision: $Revision: 1.2 $
:abstract:
- CML is a Meta language to describe the dependencies of a page at one side and building a page from its fragments on the other side
+ CML is a Meta language to describe the dependencies of a page at one side and building a page from its fragments on the other side using LUA.
.. meta::
- :keywords: lighttpd, cml
+ :keywords: lighttpd, cml, lua
.. contents:: Table of Contents
@@ -97,14 +97,22 @@ to start PHP for a cache-hit.
To transform this example into a CML you need 'index.cml' in the list of indexfiles
and the following index.cml file: ::
- output.content-type text/html
+ output_contenttype = "text/html"
- output.include _cache.html
+ b = request["DOCUMENT_ROOT"]
+ cwd = request["CWD"]
- trigger.handler index.php
- trigger.if file.mtime("../lib/php/menu.csv") > file.mtime("_cache.html")
- trigger.if file.mtime("templates/jk.tmpl") > file.mtime("_cache.html")
- trigger.if file.mtime("content.html") > file.mtime("_cache.html")
+ output_include = { b + "_cache.html" }
+
+ trigger_handler = "index.php"
+
+ if file_mtime(b + "../lib/php/menu.csv") > file_mtime(cwd + "_cache.html") or
+ file_mtime(b + "templates/jk.tmpl") > file.mtime(cwd + "_cache.html")
+ file.mtime(b + "content.html") > file.mtime(cwd + "_cache.html") then
+ return 1
+ else
+ return 0
+ end
Numbers again:
@@ -132,15 +140,19 @@ Don't forget: Webserver are built to send out static content, that is what they
The index.cml for this looks like: ::
- output.content-type text/html
+ output_content_type = "text/html"
+
+ cwd = request["CWD"]
- output.include head.html
- output.include menu.html
- output.include spacer.html
- output.include db-content.html
- output.include spacer2.html
- output.include news.html
- output.include footer.html
+ output_include = { cwd + "head.html",
+ cwd + "menu.html",
+ cwd + "spacer.html",
+ cwd + "db-content.html",
+ cwd + "spacer2.html",
+ cwd + "news.html",
+ cwd + "footer.html" }
+
+ return 0
Now we get about 10000 req/s instead of 600 req/s.
@@ -149,8 +161,66 @@ Options
:cml.extension:
the file extension that is bound to the cml-module
+:cml.memcache-hosts:
+ hosts for the memcache.* functions
+:cml.memcache-namespace:
+ (not used yet)
Language
========
-... will come later ...
+The language used for CML is provided by `LUA `_.
+
+Additionally to the functions provided by lua mod_cml provides: ::
+
+ tables:
+
+ request
+ - REQUEST_URI
+ - SCRIPT_NAME
+ - SCRIPT_FILENAME
+ - DOCUMENT_ROOT
+ - PATH_INFO
+ - CWD
+ - BASEURI
+
+ get
+ - parameters from the query-string
+
+ functions:
+ string md5(string)
+ number file_mtime(string)
+ string memcache_get_string(string)
+ number memcache_get_long(string)
+ boolean memcache_exists(string)
+
+
+What ever your script does, it has to return either 0 or 1 for ``cache-hit`` or ``cache-miss``.
+It case a error occures check the error-log, the user will get a error 500. If you don't like
+the standard error-page use ``server.errorfile-prefix``.
+
+Examples
+========
+
+Using the memcache-udf for MySQL we can do: ::
+
+ output_contenttype = "text/html"
+ output_include = { "cache-hit.html" }
+
+ trigger_handler = "generate.php"
+
+ if get["page"] == memcache_get_string("123") then
+ return 0
+ else
+ return 1
+ end
+
+In MySQL you do: ::
+
+ SELECT memcache_set("127.0.0.1:11211", "123", "12");
+
+or to retrieve a value:
+
+ SELECT memcache_get("127.0.0.1:11211", "123");
+
+You can get the mysql udf at `jan's mysql page `_.
diff --git a/src/Makefile.am b/src/Makefile.am
index be69b28d..8372859b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -69,9 +69,9 @@ common_libadd =
endif
lib_LTLIBRARIES += mod_cml.la
-mod_cml_la_SOURCES = mod_cml.c mod_cml_funcs.c mod_cml_logic.c
+mod_cml_la_SOURCES = mod_cml.c mod_cml_lua.c mod_cml_funcs.c
mod_cml_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
-mod_cml_la_LIBADD = $(MEMCACHE_LIB) $(common_libadd)
+mod_cml_la_LIBADD = $(MEMCACHE_LIB) $(common_libadd) $(LUA_LIB)
lib_LTLIBRARIES += mod_trigger_b4_dl.la
mod_trigger_b4_dl_la_SOURCES = mod_trigger_b4_dl.c
diff --git a/src/mod_cml.c b/src/mod_cml.c
index a9e60169..89bfb862 100644
--- a/src/mod_cml.c
+++ b/src/mod_cml.c
@@ -28,11 +28,6 @@ INIT_FUNC(mod_cml_init) {
p->session_id = buffer_init();
p->trigger_handler = buffer_init();
- p->eval = buffer_array_init();
- p->trigger_if = buffer_array_init();
- p->output_include = buffer_array_init();
- p->params = tnode_val_array_init();
-
return p;
}
@@ -63,12 +58,6 @@ FREE_FUNC(mod_cml_free) {
free(p->config_storage);
}
- tnode_val_array_free(p->params);
-
- buffer_array_free(p->eval);
- buffer_array_free(p->trigger_if);
- buffer_array_free(p->output_include);
-
buffer_free(p->trigger_handler);
buffer_free(p->session_id);
buffer_free(p->basedir);
@@ -103,6 +92,9 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) {
s->ext = buffer_init();
s->mc_hosts = array_init();
s->mc_namespace = buffer_init();
+#if defined(HAVE_MEMCACHE_H)
+ s->mc = NULL;
+#endif
cv[0].destination = s->ext;
cv[1].destination = s->mc_hosts;
@@ -322,6 +314,7 @@ URIHANDLER_FUNC(mod_cml_is_handled) {
buffer *fn = con->physical.path;
plugin_data *p = p_d;
size_t i;
+ int ret;
if (fn->used == 0) return HANDLER_ERROR;
@@ -332,17 +325,11 @@ URIHANDLER_FUNC(mod_cml_is_handled) {
mod_cml_patch_connection(srv, con, p, CONST_BUF_LEN(patch));
}
- buffer_array_reset(p->output_include);
- buffer_array_reset(p->eval);
- buffer_array_reset(p->trigger_if);
-
buffer_reset(p->basedir);
buffer_reset(p->baseurl);
buffer_reset(p->session_id);
buffer_reset(p->trigger_handler);
- tnode_val_array_reset(p->params);
-
if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON;
ct_len = p->conf.ext->used - 1;
@@ -382,7 +369,9 @@ URIHANDLER_FUNC(mod_cml_is_handled) {
cache_get_session_id(srv, con, p);
- switch(cache_parse(srv, con, p, fn)) {
+ ret = cache_parse_lua(srv, con, p, fn);
+
+ switch(ret) {
case -1:
/* error */
if (con->conf.log_request_handling) {
diff --git a/src/mod_cml.h b/src/mod_cml.h
index 2ecd493b..9b098770 100644
--- a/src/mod_cml.h
+++ b/src/mod_cml.h
@@ -6,6 +6,7 @@
#include "response.h"
#include "stream.h"
+#include "plugin.h"
#if defined(HAVE_MEMCACHE_H)
#include
@@ -13,39 +14,6 @@
#define plugin_data mod_cache_plugin_data
-typedef enum { UNSET, PART, TIMES, MINUS, PLUS, OR, AND, GT, LT, GE, LE, EQ, NE } tnode_op_t;
-
-typedef enum { T_NODE_VALUE_UNSET, T_NODE_VALUE_LONG, T_NODE_VALUE_STRING } tnode_val_t;
-
-typedef struct {
- tnode_val_t type;
-
- union {
- buffer *str;
- long lon;
- } data;
-} tnode_val;
-
-#define VAL_LONG(x) x->value.data.lon
-#define VAL_STRING(x) x->value.data.str
-
-#define IS_LONG(x) ((x->op == UNSET) && (x->value.type == T_NODE_VALUE_LONG))
-#define IS_STRING(x) ((x->op == UNSET) && (x->value.type == T_NODE_VALUE_STRING))
-
-typedef struct tnode {
- tnode_val value;
- tnode_op_t op;
-
- struct tnode *l, *r;
-} tnode;
-
-typedef struct {
- tnode_val **ptr;
-
- size_t size;
- size_t used;
-} tnode_val_array;
-
typedef struct {
buffer *ext;
@@ -66,40 +34,11 @@ typedef struct {
buffer *session_id;
- buffer_array *eval;
- buffer_array *trigger_if;
- buffer_array *output_include;
-
- tnode_val_array *params;
-
plugin_config **config_storage;
plugin_config conf;
} plugin_data;
-typedef struct {
- char *name;
- size_t params;
- int (*func)(server *srv, connection *con, plugin_data *p, tnode *result);
-} cache_trigger_functions;
-
-int cache_parse_parameters(server *srv, connection *con, plugin_data *p, const char *params, size_t param_len, tnode_val_array *res);
-int cache_parse(server *srv, connection *con, plugin_data *p, buffer *fn);
-int tnode_prepare_long(tnode *t);
-int tnode_prepare_string(tnode *t);
-
-tnode_val_array *tnode_val_array_init();
-void tnode_val_array_free(tnode_val_array *tva);
-void tnode_val_array_reset(tnode_val_array *tva);
-
-#define CACHE_FUNC_PROTO(x) int x(server *srv, connection *con, plugin_data *p, tnode *result)
-
-CACHE_FUNC_PROTO(f_unix_time_now);
-CACHE_FUNC_PROTO(f_file_mtime);
-CACHE_FUNC_PROTO(f_memcache_exists);
-CACHE_FUNC_PROTO(f_memcache_get_string);
-CACHE_FUNC_PROTO(f_memcache_get_long);
-CACHE_FUNC_PROTO(f_http_request_get_param);
-CACHE_FUNC_PROTO(f_crypto_md5);
+int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn);
#endif
diff --git a/src/mod_cml_funcs.c b/src/mod_cml_funcs.c
index 44fa973c..d5c7f320 100644
--- a/src/mod_cml_funcs.c
+++ b/src/mod_cml_funcs.c
@@ -14,6 +14,7 @@
#include "response.h"
#include "mod_cml.h"
+#include "mod_cml_funcs.h"
#ifdef USE_OPENSSL
# include
@@ -33,259 +34,174 @@ typedef char HASHHEX[HASHHEXLEN+1];
#endif
#define OUT
-CACHE_FUNC_PROTO(f_unix_time_now) {
- UNUSED(srv);
- UNUSED(con);
- UNUSED(p);
-
- VAL_LONG(result) = srv->cur_ts;
-
- return 0;
-}
+#ifdef HAVE_LUA_H
-CACHE_FUNC_PROTO(f_file_mtime) {
- buffer *b;
- struct stat st;
+int f_crypto_md5(lua_State *L) {
+ MD5_CTX Md5Ctx;
+ HASH HA1;
+ buffer b;
+ char hex[33];
+ int n = lua_gettop(L);
- UNUSED(con);
+ b.ptr = hex;
+ b.used = 0;
+ b.size = sizeof(hex);
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_file_mtime: I need a string:",
- p->params->ptr[0]->type);
-
- return -1;
+ if (n != 1) {
+ lua_pushstring(L, "expected one argument");
+ lua_error(L);
}
- b = buffer_init();
-
- /* build filename */
- buffer_copy_string_buffer(b, p->basedir);
- buffer_append_string_buffer(b, p->params->ptr[0]->data.str);
-
- if (-1 == stat(b->ptr, &st)) {
- log_error_write(srv, __FILE__, __LINE__, "sbs",
- "trigger.if file.mtime():", b, strerror(errno));
-
- buffer_free(b);
- return -1;
+ if (!lua_isstring(L, 1)) {
+ lua_pushstring(L, "argument has to be a string");
+ lua_error(L);
}
- buffer_free(b);
- tnode_prepare_long(result);
- VAL_LONG(result) = st.st_mtime;
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1));
+ MD5_Final(HA1, &Md5Ctx);
- return 0;
-}
-
-int split_query_string(server *srv, connection *con, array *vals) {
- size_t key_start = 0, key_end = 0,
- value_start = 0;
- size_t is_key = 1;
- size_t i;
-
- for (i = 0; i < con->uri.query->used; i++) {
- switch(con->uri.query->ptr[i]) {
- case '=':
- if (is_key) {
- key_end = i - 1;
- value_start = i + 1;
-
- is_key = 0;
- }
-
- break;
- case '&':
- case '\0': /* fin symbol */
- if (!is_key) {
- data_string *ds;
-
- /* we need at least a = since the last & */
-
- if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
- ds = data_string_init();
- }
-
- buffer_copy_string_len(ds->key, con->uri.query->ptr + key_start, key_end - key_start);
- buffer_copy_string_len(ds->value, con->uri.query->ptr + value_start, i - value_start);
-
- array_insert_unique(vals, (data_unset *)ds);
- }
-
- key_start = i + 1;
- value_start = 0;
- is_key = 1;
- break;
- }
- }
+ buffer_copy_string_hex(&b, (char *)HA1, 16);
+
+ lua_pushstring(L, b.ptr);
- return 0;
+ return 1;
}
-CACHE_FUNC_PROTO(f_http_request_get_param) {
- array *qry_str;
- data_string *ds;
-
- /* fetch data from the con-> request query string */
-
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_http_request_get_param: I need a string:",
- p->params->ptr[0]->type);
-
- return -1;
- }
-
- qry_str = array_init();
-
- split_query_string(srv, con, qry_str);
-
- tnode_prepare_string(result);
+int f_file_mtime(lua_State *L) {
+ struct stat st;
+ int n = lua_gettop(L);
- if (NULL == (ds = (data_string *)array_get_element(qry_str, p->params->ptr[0]->data.str->ptr))) {
-
- buffer_copy_string(VAL_STRING(result), "");
-
- array_free(qry_str);
-
- return 0;
+ if (n != 1) {
+ lua_pushstring(L, "expected one argument");
+ lua_error(L);
}
- buffer_copy_string_buffer(VAL_STRING(result), ds->value);
-
- array_free(qry_str);
-
- return 0;
-}
-
-CACHE_FUNC_PROTO(f_crypto_md5) {
- MD5_CTX Md5Ctx;
- HASH HA1;
-
- /* fetch data from the con-> request query string */
-
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "crypto.md5: I need a string:",
- p->params->ptr[0]->type);
-
- return -1;
+ if (!lua_isstring(L, 1)) {
+ lua_pushstring(L, "argument has to be a string");
+ lua_error(L);
}
- MD5_Init(&Md5Ctx);
- MD5_Update(&Md5Ctx, (unsigned char *)p->params->ptr[0]->data.str->ptr, p->params->ptr[0]->data.str->used - 1);
- MD5_Final(HA1, &Md5Ctx);
+ if (-1 == stat(lua_tostring(L, 1), &st)) {
+ lua_pushstring(L, "stat failed");
+ lua_error(L);
+ }
- tnode_prepare_string(result);
- buffer_copy_string_hex(VAL_STRING(result), (char *)HA1, 16);
+ lua_pushnumber(L, st.st_mtime);
- return 0;
+ return 1;
}
#ifdef HAVE_MEMCACHE_H
-CACHE_FUNC_PROTO(f_memcache_exists) {
+int f_memcache_exists(lua_State *L) {
char *r;
+ int n = lua_gettop(L);
+ struct memcache *mc;
- UNUSED(con);
-
- if (!p->conf.mc) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_exists: no memcache.hosts set:",
- p->params->ptr[0]->type);
- return -1;
+ if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+ lua_pushstring(L, "where is my userdata ?");
+ lua_error(L);
}
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_exists: I need a string:",
- p->params->ptr[0]->type);
-
- return -1;
+ mc = lua_touserdata(L, lua_upvalueindex(1));
+
+ if (n != 1) {
+ lua_pushstring(L, "expected one argument");
+ lua_error(L);
}
- tnode_prepare_long(result);
+ if (!lua_isstring(L, 1)) {
+ lua_pushstring(L, "argument has to be a string");
+ lua_error(L);
+ }
- if (NULL == (r = mc_aget(p->conf.mc,
- CONST_BUF_LEN(p->params->ptr[0]->data.str)))) {
+ if (NULL == (r = mc_aget(mc,
+ lua_tostring(L, 1), lua_strlen(L, 1)))) {
- VAL_LONG(result) = 0;
- return 0;
+ lua_pushboolean(L, 0);
+ return 1;
}
free(r);
- VAL_LONG(result) = 1;
-
- return 0;
+ lua_pushboolean(L, 1);
+ return 1;
}
-CACHE_FUNC_PROTO(f_memcache_get_string) {
+int f_memcache_get_string(lua_State *L) {
char *r;
+ int n = lua_gettop(L);
+
+ struct memcache *mc;
+
+ if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+ lua_pushstring(L, "where is my userdata ?");
+ lua_error(L);
+ }
+
+ mc = lua_touserdata(L, lua_upvalueindex(1));
- UNUSED(con);
- if (!p->conf.mc) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_get_string: no memcache.hosts set:",
- p->params->ptr[0]->type);
- return -1;
+ if (n != 1) {
+ lua_pushstring(L, "expected one argument");
+ lua_error(L);
}
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_get_string: I need a string:",
- p->params->ptr[0]->type);
- return -1;
+ if (!lua_isstring(L, 1)) {
+ lua_pushstring(L, "argument has to be a string");
+ lua_error(L);
}
- if (NULL == (r = mc_aget(p->conf.mc,
- p->params->ptr[0]->data.str->ptr, p->params->ptr[0]->data.str->used - 1))) {
- log_error_write(srv, __FILE__, __LINE__, "sb",
- "f_memcache_get_string: couldn't find:",
- p->params->ptr[0]->data.str);
- return -1;
+ if (NULL == (r = mc_aget(mc,
+ lua_tostring(L, 1), lua_strlen(L, 1)))) {
+ lua_pushnil(L);
+ return 1;
}
- tnode_prepare_string(result);
- buffer_copy_string(VAL_STRING(result), r);
+
+ lua_pushstring(L, r);
free(r);
- return 0;
+ return 1;
}
-CACHE_FUNC_PROTO(f_memcache_get_long) {
+int f_memcache_get_long(lua_State *L) {
char *r;
+ int n = lua_gettop(L);
- UNUSED(con);
+ struct memcache *mc;
- if (!p->conf.mc) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_get_long: no memcache.hosts set:",
- p->params->ptr[0]->type);
- return -1;
+ if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+ lua_pushstring(L, "where is my userdata ?");
+ lua_error(L);
}
- if (p->params->ptr[0]->type != T_NODE_VALUE_STRING) {
- log_error_write(srv, __FILE__, __LINE__, "sd",
- "f_memcache_get_long: I need a string:",
- p->params->ptr[0]->type);
- return -1;
+ mc = lua_touserdata(L, lua_upvalueindex(1));
+
+
+ if (n != 1) {
+ lua_pushstring(L, "expected one argument");
+ lua_error(L);
+ }
+
+ if (!lua_isstring(L, 1)) {
+ lua_pushstring(L, "argument has to be a string");
+ lua_error(L);
}
- if (NULL == (r = mc_aget(p->conf.mc,
- CONST_BUF_LEN(p->params->ptr[0]->data.str)))) {
- log_error_write(srv, __FILE__, __LINE__, "sb",
- "f_memcache_get_long: couldn't find:",
- p->params->ptr[0]->data.str);
- return -1;
+ if (NULL == (r = mc_aget(mc,
+ lua_tostring(L, 1), lua_strlen(L, 1)))) {
+ lua_pushnil(L);
+ return 1;
}
- tnode_prepare_long(result);
- VAL_LONG(result) = strtol(r, NULL, 10);
+ lua_pushnumber(L, strtol(r, NULL, 10));
free(r);
- return 0;
+ return 1;
}
#endif
+
+#endif