Browse Source

ported mod_cml to lua

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.3.x@452 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.3.15
Jan Kneschke 17 years ago
parent
commit
1408a4820d
  1. 15
      configure.in
  2. 104
      doc/cml.txt
  3. 4
      src/Makefile.am
  4. 25
      src/mod_cml.c
  5. 65
      src/mod_cml.h
  6. 302
      src/mod_cml_funcs.c

15
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)

104
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 <http://www.lua.org/>`_.
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 <http://jan.kneschke.de/projects/mysql/udf/>`_.

4
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

25
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) {

65
src/mod_cml.h

@ -6,6 +6,7 @@
#include "response.h"
#include "stream.h"
#include "plugin.h"
#if defined(HAVE_MEMCACHE_H)
#include <memcache.h>
@ -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

302
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 <openssl/md5.h>
@ -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

Loading…
Cancel
Save