summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gruno <humbedooh@apache.org>2013-09-17 10:38:12 +0000
committerDaniel Gruno <humbedooh@apache.org>2013-09-17 10:38:12 +0000
commitf41d8a48ea54240407b14e303eca4117aea5e979 (patch)
tree74f0c3df7b6bc31379cb45715d5244f62c86b158
parentc64faef062ced55434cd9c9de5a80bdfea740ede (diff)
downloadhttpd-f41d8a48ea54240407b14e303eca4117aea5e979.tar.gz
Backport a few things in mod_lua:
- Fix filters - Add setcookie/getcookie - Add preliminary WebSocket support - Update documentation git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1523974 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--docs/manual/mod/mod_lua.xml105
-rw-r--r--modules/lua/lua_request.c429
-rw-r--r--modules/lua/mod_lua.c89
3 files changed, 603 insertions, 20 deletions
diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml
index 1726401faa..f357ab9031 100644
--- a/docs/manual/mod/mod_lua.xml
+++ b/docs/manual/mod/mod_lua.xml
@@ -269,9 +269,10 @@ performing access control, or setting mime types:</p>
</tr>
<tr>
<td>Logging</td>
- <td>(none)</td>
+ <td><directive module="mod_lua">LuaHookLog</directive></td>
<td>Once a request has been handled, it enters several logging phases,
- which logs the request in either the error or access log</td>
+ which logs the request in either the error or access log. Mod_lua
+ is able to hook into the start of this and control logging output.</td>
</tr>
</table>
@@ -943,7 +944,7 @@ r:rmdir(dir) -- Removes a directory.
</highlight>
<highlight language="lua">
-r:touch([mtime]) -- Sets the file modification time to current time or to optional mtime msec value.
+r:touch(file [,mtime]) -- Sets the file modification time to current time or to optional mtime msec value.
</highlight>
<highlight language="lua">
@@ -966,6 +967,48 @@ end
r.date_parse_rfc(string) -- Parses a date/time string and returns seconds since epoche.
</highlight>
+<highlight language="lua">
+r:getcookie(key) -- Gets a HTTP cookie
+</highlight>
+
+<highlight language="lua">
+r:setcookie(key, value, secure, expires) -- Sets a HTTP cookie, for instance:
+r:setcookie("foo", "bar and stuff", false, os.time() + 86400)
+</highlight>
+
+<highlight language="lua">
+r:wsupgrade() -- Upgrades a connection to WebSockets if possible (and requested):
+if r:wsupgrade() then -- if we can upgrade:
+ r:wswrite("Welcome to websockets!") -- write something to the client
+ r:wsclose() -- goodbye!
+end
+</highlight>
+
+<highlight language="lua">
+r:wsread() -- Reads a WebSocket frame from a WebSocket upgraded connection (see above):
+
+local line, isFinal = r:wsread() -- isFinal denotes whether this is the final frame.
+ -- If it isn't, then more frames can be read
+r:wswrite("You wrote: " .. line)
+</highlight>
+
+<highlight language="lua">
+r:wswrite(line) -- Writes a frame to a WebSocket client:
+r:wswrite("Hello, world!")
+</highlight>
+
+<highlight language="lua">
+r:wsclose() -- Closes a WebSocket request and terminates it for httpd:
+
+if r:wsupgrade() then
+ r:wswrite("Write something: ")
+ local line = r:wsread() or "nothing"
+ r:wswrite("You wrote: " .. line);
+ r:wswrite("Goodbye!")
+ r:wsclose()
+end
+</highlight>
+
</section>
<section id="logging"><title>Logging Functions</title>
@@ -1454,6 +1497,55 @@ processing</description>
</directivesynopsis>
<directivesynopsis>
+<name>LuaHookLog</name>
+<description>Provide a hook for the access log phase of a request
+processing</description>
+<syntax>LuaHookLog /path/to/lua/script.lua log_function_name</syntax>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>All</override>
+<usage>
+<p>
+ This simple logging hook allows you to run a function when httpd enters the
+ logging phase of a request. With it, you can append data to your own logs,
+ manipulate data before the regular log is written, or prevent a log entry
+ from being created. To prevent the usual logging from happening, simply return
+ <code>apache2.DONE</code> in your logging handler, otherwise return
+ <code>apache2.OK</code> to tell httpd to log as normal.
+</p>
+<p>Example:</p>
+<highlight language="config">
+LuaHookLog /path/to/script.lua logger
+</highlight>
+<highlight language="lua">
+-- /path/to/script.lua --
+function logger(r)
+ -- flip a coin:
+ -- If 1, then we write to our own Lua log and tell httpd not to log
+ -- in the main log.
+ -- If 2, then we just sanitize the output a bit and tell httpd to
+ -- log the sanitized bits.
+
+ if math.random(1,2) == 1 then
+ -- Log stuff ourselves and don't log in the regular log
+ local f = io.open("/foo/secret.log", "a")
+ if f then
+ f:write("Something secret happened at " .. r.uri .. "\n")
+ f:close()
+ end
+ return apache2.DONE -- Tell httpd not to use the regular logging functions
+ else
+ r.uri = r.uri:gsub("somesecretstuff", "") -- sanitize the URI
+ return apache2.OK -- tell httpd to log it.
+ end
+end
+</highlight>
+</usage>
+</directivesynopsis>
+
+
+<directivesynopsis>
<name>LuaHookMapToStorage</name>
<description>Provide a hook for the map_to_storage phase of request processing</description>
<syntax>LuaHookMapToStorage /path/to/lua/script.lua hook_function_name</syntax>
@@ -1811,10 +1903,17 @@ function output_filter(r)
... -- insert filter stuff here
end
</highlight>
+<note><title>Lua filters with <module>mod_filter</module></title>
+<p> When a Lua filter is used as the underlying provider via the
+<directive module="mod_filter">FilterProvider</directive> directive, filtering
+will only work when the <var>filter-name</var> is identical to the <var>provider-name</var>.
+</p> </note>
+
<p>
See "<a href="#modifying_buckets">Modifying contents with Lua filters</a>" for more
information.
</p>
+
</usage>
</directivesynopsis>
diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c
index eb6aef1d16..23efcc2a4e 100644
--- a/modules/lua/lua_request.c
+++ b/modules/lua/lua_request.c
@@ -26,8 +26,9 @@
#include "apr_date.h"
#include "apr_pools.h"
#include "apr_thread_mutex.h"
-
-#include <lua.h>
+#include "apr_tables.h"
+#include "util_cookies.h"
+#include "apr_want.h"
extern apr_thread_mutex_t* lua_ivm_mutex;
@@ -839,6 +840,7 @@ static int lua_apr_sha1(lua_State *L)
apr_sha1_init(&sha1);
apr_sha1_update(&sha1, buffer, len);
apr_sha1_final(digest, &sha1);
+
ap_bin2hex(digest, sizeof(digest), result);
lua_pushstring(L, result);
return 1;
@@ -1887,6 +1889,411 @@ static int lua_ivm_set(lua_State *L)
return 0;
}
+static int lua_get_cookie(lua_State *L)
+{
+ const char *key, *cookie;
+ request_rec *r = ap_lua_check_request_rec(L, 1);
+ key = luaL_checkstring(L, 2);
+ cookie = NULL;
+ ap_cookie_read(r, key, &cookie, 0);
+ if (cookie != NULL) {
+ lua_pushstring(L, cookie);
+ return 1;
+ }
+ return 0;
+}
+
+static int lua_set_cookie(lua_State *L)
+{
+ const char *key, *value, *out, *strexpires;
+ int secure, expires;
+ char cdate[APR_RFC822_DATE_LEN+1];
+ apr_status_t rv;
+ request_rec *r = ap_lua_check_request_rec(L, 1);
+ key = luaL_checkstring(L, 2);
+ value = luaL_checkstring(L, 3);
+ secure = 0;
+ if (lua_isboolean(L, 4)) {
+ secure = lua_toboolean(L, 4);
+ }
+ expires = luaL_optinteger(L, 5, 0);
+ strexpires = "";
+ if (expires > 0) {
+ rv = apr_rfc822_date(cdate, apr_time_from_sec(expires));
+ if (rv == APR_SUCCESS) {
+ strexpires = apr_psprintf(r->pool, "Expires=%s", cdate);
+ }
+ }
+ out = apr_psprintf(r->pool, "%s=%s; %s %s", key, value, secure ? "Secure;" : "", expires ? strexpires : "");
+ apr_table_set(r->headers_out, "Set-Cookie", out);
+ return 0;
+}
+
+static apr_uint64_t ap_ntoh64(const apr_uint64_t *input)
+{
+ apr_uint64_t rval;
+ unsigned char *data = (unsigned char *)&rval;
+
+ data[0] = *input >> 56;
+ data[1] = *input >> 48;
+ data[2] = *input >> 40;
+ data[3] = *input >> 32;
+ data[4] = *input >> 24;
+ data[5] = *input >> 16;
+ data[6] = *input >> 8;
+ data[7] = *input >> 0;
+
+ return rval;
+}
+
+static int lua_websocket_greet(lua_State *L)
+{
+ const char *key = NULL;
+ unsigned char digest[APR_SHA1_DIGESTSIZE];
+ apr_sha1_ctx_t sha1;
+ char *encoded;
+ int encoded_len;
+ request_rec *r = ap_lua_check_request_rec(L, 1);
+ key = apr_table_get(r->headers_in, "Sec-WebSocket-Key");
+ if (key != NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Got websocket key: %s", key);
+ key = apr_pstrcat(r->pool, key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+ NULL);
+ apr_sha1_init(&sha1);
+ apr_sha1_update(&sha1, key, strlen(key));
+ apr_sha1_final(digest, &sha1);
+ encoded_len = apr_base64_encode_len(APR_SHA1_DIGESTSIZE);
+ if (encoded_len) {
+ encoded = apr_palloc(r->pool, encoded_len);
+ encoded_len = apr_base64_encode(encoded, (char*) digest, APR_SHA1_DIGESTSIZE);
+ r->status = 101;
+ apr_table_set(r->headers_out, "Upgrade", "websocket");
+ apr_table_set(r->headers_out, "Connection", "Upgrade");
+ apr_table_set(r->headers_out, "Sec-WebSocket-Accept", encoded);
+
+ /* Trick httpd into NOT using the chunked filter, IMPORTANT!!!111*/
+ apr_table_set(r->headers_out, "Transfer-Encoding", "chunked");
+
+ r->clength = 0;
+ r->bytes_sent = 0;
+ r->read_chunked = 0;
+ ap_rflush(r);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Upgraded from HTTP to Websocket");
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
+ "Websocket: Upgrade from HTTP to Websocket failed");
+ return 0;
+}
+
+static apr_status_t lua_websocket_readbytes(conn_rec* c, char* buffer,
+ apr_off_t len)
+{
+ apr_bucket_brigade *brigade = apr_brigade_create(c->pool, c->bucket_alloc);
+ apr_status_t rv;
+ rv = ap_get_brigade(c->input_filters, brigade, AP_MODE_READBYTES,
+ APR_BLOCK_READ, len);
+ if (rv == APR_SUCCESS) {
+ if (!APR_BRIGADE_EMPTY(brigade)) {
+ apr_bucket* bucket = APR_BRIGADE_FIRST(brigade);
+ const char* data = NULL;
+ apr_size_t data_length = 0;
+ rv = apr_bucket_read(bucket, &data, &data_length, APR_BLOCK_READ);
+ if (rv == APR_SUCCESS) {
+ memcpy(buffer, data, len);
+ }
+ apr_bucket_delete(bucket);
+ }
+ }
+ apr_brigade_cleanup(brigade);
+ return rv;
+}
+
+static int lua_websocket_read(lua_State *L)
+{
+ apr_socket_t *sock;
+ apr_status_t rv;
+ int n = 0;
+ apr_size_t len = 1;
+ apr_size_t plen = 0;
+ unsigned short payload_short = 0;
+ apr_uint64_t payload_long = 0;
+ unsigned char *mask_bytes;
+ char byte;
+ int plaintext;
+
+
+ request_rec *r = (request_rec *) lua_unboxpointer(L, 1);
+ plaintext = ap_lua_ssl_is_https(r->connection) ? 0 : 1;
+
+
+ mask_bytes = apr_pcalloc(r->pool, 4);
+ sock = ap_get_conn_socket(r->connection);
+
+ /* Get opcode and FIN bit */
+ if (plaintext) {
+ rv = apr_socket_recv(sock, &byte, &len);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection, &byte, 1);
+ }
+ if (rv == APR_SUCCESS) {
+ unsigned char fin, opcode, mask, payload;
+ fin = byte >> 7;
+ opcode = (byte << 4) >> 4;
+
+ /* Get the payload length and mask bit */
+ if (plaintext) {
+ rv = apr_socket_recv(sock, &byte, &len);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection, &byte, 1);
+ }
+ if (rv == APR_SUCCESS) {
+ mask = byte >> 7;
+ payload = byte - 128;
+ plen = payload;
+
+ /* Extended payload? */
+ if (payload == 126) {
+ len = 2;
+ if (plaintext) {
+ rv = apr_socket_recv(sock, (char*) &payload_short, &len);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection,
+ (char*) &payload_short, 2);
+ }
+ payload_short = ntohs(payload_short);
+
+ if (rv == APR_SUCCESS) {
+ plen = payload_short;
+ }
+ else {
+ return 0;
+ }
+ }
+ /* Super duper extended payload? */
+ if (payload == 127) {
+ len = 8;
+ if (plaintext) {
+ rv = apr_socket_recv(sock, (char*) &payload_long, &len);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection,
+ (char*) &payload_long, 8);
+ }
+ if (rv == APR_SUCCESS) {
+ plen = ap_ntoh64(&payload_long);
+ }
+ else {
+ return 0;
+ }
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Reading %lu (%s) bytes, masking is %s. %s",
+ plen,
+ (payload >= 126) ? "extra payload" : "no extra payload",
+ mask ? "on" : "off",
+ fin ? "This is a final frame" : "more to follow");
+ if (mask) {
+ len = 4;
+ if (plaintext) {
+ rv = apr_socket_recv(sock, (char*) mask_bytes, &len);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection,
+ (char*) mask_bytes, 4);
+ }
+ if (rv != APR_SUCCESS) {
+ return 0;
+ }
+ }
+ if (plen < (HUGE_STRING_LEN*1024) && plen > 0) {
+ apr_size_t remaining = plen;
+ apr_size_t received;
+ apr_off_t at = 0;
+ char *buffer = apr_palloc(r->pool, plen+1);
+ buffer[plen] = 0;
+
+ if (plaintext) {
+ while (remaining > 0) {
+ received = remaining;
+ rv = apr_socket_recv(sock, buffer+at, &received);
+ if (received > 0 ) {
+ remaining -= received;
+ at += received;
+ }
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Websocket: Frame contained %lu bytes, pushed to Lua stack",
+ at);
+ }
+ else {
+ rv = lua_websocket_readbytes(r->connection, buffer,
+ remaining);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Websocket: SSL Frame contained %lu bytes, "\
+ "pushed to Lua stack",
+ remaining);
+ }
+ if (mask) {
+ for (n = 0; n < plen; n++) {
+ buffer[n] ^= mask_bytes[n%4];
+ }
+ }
+
+ lua_pushlstring(L, buffer, (size_t) plen); /* push to stack */
+ lua_pushboolean(L, fin); /* push FIN bit to stack as boolean */
+ return 2;
+ }
+
+
+ /* Decide if we need to react to the opcode or not */
+ if (opcode == 0x09) { /* ping */
+ char frame[2];
+ plen = 2;
+ frame[0] = 0x8A;
+ frame[1] = 0;
+ apr_socket_send(sock, frame, &plen); /* Pong! */
+ lua_websocket_read(L); /* read the next frame instead */
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int lua_websocket_write(lua_State *L)
+{
+ const char *string;
+ apr_status_t rv;
+ size_t len;
+ int raw = 0;
+ char prelude;
+ request_rec *r = (request_rec *) lua_unboxpointer(L, 1);
+
+ if (lua_isboolean(L, 3)) {
+ raw = lua_toboolean(L, 3);
+ }
+ string = lua_tolstring(L, 2, &len);
+
+ if (raw != 1) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Writing framed message to client");
+
+ prelude = 0x81; /* text frame, FIN */
+ ap_rputc(prelude, r);
+ if (len < 126) {
+ ap_rputc(len, r);
+ }
+ else if (len < 65535) {
+ apr_uint16_t slen = len;
+ ap_rputc(126, r);
+ slen = htons(slen);
+ ap_rwrite((char*) &slen, 2, r);
+ }
+ else {
+ apr_uint64_t llen = len;
+ ap_rputc(127, r);
+ llen = ap_ntoh64(&llen); /* ntoh doubles as hton */
+ ap_rwrite((char*) &llen, 8, r);
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Writing raw message to client");
+ }
+ ap_rwrite(string, len, r);
+ rv = ap_rflush(r);
+ if (rv == APR_SUCCESS) {
+ lua_pushboolean(L, 1);
+ }
+ else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+
+static int lua_websocket_close(lua_State *L)
+{
+ apr_socket_t *sock;
+ char prelude[2];
+ request_rec *r = (request_rec *) lua_unboxpointer(L, 1);
+
+ sock = ap_get_conn_socket(r->connection);
+
+ /* Send a header that says: socket is closing. */
+ prelude[0] = 0x88; /* closing socket opcode */
+ prelude[1] = 0; /* zero length frame */
+ ap_rwrite(prelude, 2, r);
+
+ /* Close up tell the MPM and filters to back off */
+ apr_socket_close(sock);
+ r->output_filters = NULL;
+ r->connection->keepalive = AP_CONN_CLOSE;
+ ap_destroy_sub_req(r);
+ return DONE;
+}
+
+
+static int lua_websocket_ping(lua_State *L)
+{
+ apr_socket_t *sock;
+ apr_size_t plen;
+ char prelude[2];
+ apr_status_t rv;
+ request_rec *r = ap_lua_check_request_rec(L, 1);
+ sock = ap_get_conn_socket(r->connection);
+
+ /* Send a header that says: PING. */
+ prelude[0] = 0x89; /* ping opcode */
+ prelude[1] = 0;
+ plen = 2;
+ apr_socket_send(sock, prelude, &plen);
+
+
+ /* Get opcode and FIN bit from pong */
+ plen = 2;
+ rv = apr_socket_recv(sock, prelude, &plen);
+ if (rv == APR_SUCCESS) {
+ unsigned char opcode = prelude[0];
+ unsigned char len = prelude[1];
+ unsigned char mask = len >> 7;
+ if (mask) len -= 128;
+ plen = len;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Websocket: Got PONG opcode: %x", opcode);
+ if (opcode == 0x8A) {
+ lua_pushboolean(L, 1);
+ }
+ else {
+ lua_pushboolean(L, 0);
+ }
+ if (plen > 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Websocket: Reading %lu bytes of PONG", plen);
+ return 1;
+ }
+ if (mask) {
+ plen = 2;
+ apr_socket_recv(sock, prelude, &plen);
+ plen = 2;
+ apr_socket_recv(sock, prelude, &plen);
+ }
+ }
+ else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+
#define APLUA_REQ_TRACE(lev) static int req_trace##lev(lua_State *L) \
{ \
return req_log_at(L, APLOG_TRACE##lev); \
@@ -1994,6 +2401,7 @@ static const char* lua_ap_get_server_name(request_rec* r)
+
static const struct luaL_Reg server_methods[] = {
{NULL, NULL}
};
@@ -2236,7 +2644,22 @@ void ap_lua_load_request_lmodule(lua_State *L, apr_pool_t *p)
makefun(&lua_ivm_get, APL_REQ_FUNTYPE_LUACFUN, p));
apr_hash_set(dispatch, "ivm_set", APR_HASH_KEY_STRING,
makefun(&lua_ivm_set, APL_REQ_FUNTYPE_LUACFUN, p));
-
+ apr_hash_set(dispatch, "getcookie", APR_HASH_KEY_STRING,
+ makefun(&lua_get_cookie, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "setcookie", APR_HASH_KEY_STRING,
+ makefun(&lua_set_cookie, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "wsupgrade", APR_HASH_KEY_STRING,
+ makefun(&lua_websocket_greet, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "wsread", APR_HASH_KEY_STRING,
+ makefun(&lua_websocket_read, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "wswrite", APR_HASH_KEY_STRING,
+ makefun(&lua_websocket_write, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "wsclose", APR_HASH_KEY_STRING,
+ makefun(&lua_websocket_close, APL_REQ_FUNTYPE_LUACFUN, p));
+ apr_hash_set(dispatch, "wsping", APR_HASH_KEY_STRING,
+ makefun(&lua_websocket_ping, APL_REQ_FUNTYPE_LUACFUN, p));
+
+
lua_pushlightuserdata(L, dispatch);
lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Request.dispatch");
diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c
index 7c35011ec1..6e3390fbd2 100644
--- a/modules/lua/mod_lua.c
+++ b/modules/lua/mod_lua.c
@@ -318,7 +318,10 @@ static apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r, lua_fil
ctx = apr_pcalloc(r->pool, sizeof(lua_filter_ctx));
ctx->broken = 0;
*c = ctx;
- /* Find the filter that was called */
+ /* Find the filter that was called.
+ * XXX: If we were wired with mod_filter, the filter (mod_filters name)
+ * and the provider (our underlying filters name) need to have matched.
+ */
for (n = 0; n < cfg->mapped_filters->nelts; n++) {
ap_lua_filter_handler_spec *hook_spec =
((ap_lua_filter_handler_spec **) cfg->mapped_filters->elts)[n];
@@ -374,6 +377,12 @@ static apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r, lua_fil
*/
rc = lua_resume(L, 1);
if (rc == LUA_YIELD) {
+ if (f->frec->providers == NULL) {
+ /* Not wired by mod_filter */
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+ apr_table_unset(r->headers_out, "ETAG");
+ }
return OK;
}
else {
@@ -407,8 +416,25 @@ static apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade
ap_remove_output_filter(f);
return ap_pass_brigade(f->next,pbbIn);
}
- f->ctx = ctx;
- ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
+ else {
+ /* We've got a willing lua filter, setup and check for a prefix */
+ size_t olen;
+ apr_bucket *pbktOut;
+ const char* output = lua_tolstring(ctx->L, 1, &olen);
+
+ f->ctx = ctx;
+ ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
+
+ if (olen > 0) {
+ pbktOut = apr_bucket_heap_create(output, olen, NULL, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
+ rv = ap_pass_brigade(f->next, ctx->tmpBucket);
+ apr_brigade_cleanup(ctx->tmpBucket);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ }
}
ctx = (lua_filter_ctx*) f->ctx;
L = ctx->L;
@@ -433,13 +459,15 @@ static apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade
if (lua_resume(L, 0) == LUA_YIELD) {
size_t olen;
const char* output = lua_tolstring(L, 1, &olen);
- pbktOut = apr_bucket_heap_create(output, olen, NULL,
- c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
- rv = ap_pass_brigade(f->next, ctx->tmpBucket);
- apr_brigade_cleanup(ctx->tmpBucket);
- if (rv != APR_SUCCESS) {
- return rv;
+ if (olen > 0) {
+ pbktOut = apr_bucket_heap_create(output, olen, NULL,
+ c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
+ rv = ap_pass_brigade(f->next, ctx->tmpBucket);
+ apr_brigade_cleanup(ctx->tmpBucket);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
}
}
else {
@@ -461,9 +489,11 @@ static apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade
apr_bucket *pbktOut;
size_t olen;
const char* output = lua_tolstring(L, 1, &olen);
- pbktOut = apr_bucket_heap_create(output, olen, NULL,
- c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
+ if (olen > 0) {
+ pbktOut = apr_bucket_heap_create(output, olen, NULL,
+ c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
+ }
}
pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktEOS);
@@ -661,6 +691,13 @@ static int lua_request_rec_hook_harness(request_rec *r, const char *name, int ap
rc = DECLINED;
if (lua_isnumber(L, -1)) {
rc = lua_tointeger(L, -1);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "Lua hook %s:%s for phase %s returned %d",
+ hook_spec->file_name, hook_spec->function_name, name, rc);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, "Lua hook %s:%s for phase %s did not return a numeric value",
+ hook_spec->file_name, hook_spec->function_name, name);
+ return HTTP_INTERNAL_SERVER_ERROR;
}
if (rc != DECLINED) {
ap_lua_release_state(L, spec, r);
@@ -1076,7 +1113,8 @@ static const char *register_filter_function_hook(const char *filter,
/* TODO: Make it work on other types than just AP_FTYPE_RESOURCE? */
if (direction == AP_LUA_FILTER_OUTPUT) {
spec->direction = AP_LUA_FILTER_OUTPUT;
- ap_register_output_filter(filter, lua_output_filter_handle, NULL, AP_FTYPE_RESOURCE);
+ ap_register_output_filter_protocol(filter, lua_output_filter_handle, NULL, AP_FTYPE_RESOURCE,
+ AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
}
else {
spec->direction = AP_LUA_FILTER_INPUT;
@@ -1155,6 +1193,11 @@ static void lua_insert_filter_harness(request_rec *r)
/* ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "LuaHookInsertFilter not yet implemented"); */
}
+static int lua_log_transaction_harness(request_rec *r)
+{
+ return lua_request_rec_hook_harness(r, "log_transaction", APR_HOOK_FIRST);
+}
+
static int lua_quick_harness(request_rec *r, int lookup)
{
if (lookup) {
@@ -1219,6 +1262,15 @@ static const char *register_map_to_storage_hook(cmd_parms *cmd, void *_cfg,
return register_named_file_function_hook("map_to_storage", cmd, _cfg,
file, function, APR_HOOK_MIDDLE);
}
+
+static const char *register_log_transaction_hook(cmd_parms *cmd, void *_cfg,
+ const char *file,
+ const char *function)
+{
+ return register_named_file_function_hook("log_transaction", cmd, _cfg,
+ file, function, APR_HOOK_FIRST);
+}
+
static const char *register_map_to_storage_block(cmd_parms *cmd, void *_cfg,
const char *line)
{
@@ -1226,6 +1278,7 @@ static const char *register_map_to_storage_block(cmd_parms *cmd, void *_cfg,
line);
}
+
static const char *register_check_user_id_hook(cmd_parms *cmd, void *_cfg,
const char *file,
const char *function,
@@ -1783,6 +1836,10 @@ command_rec lua_commands[] = {
AP_INIT_TAKE2("LuaHookInsertFilter", register_insert_filter_hook, NULL,
OR_ALL,
"Provide a hook for the insert_filter phase of request processing"),
+
+ AP_INIT_TAKE2("LuaHookLog", register_log_transaction_hook, NULL,
+ OR_ALL,
+ "Provide a hook for the logging phase of request processing"),
AP_INIT_TAKE123("LuaScope", register_lua_scope, NULL, OR_ALL,
"One of once, request, conn, server -- default is once"),
@@ -1983,6 +2040,10 @@ static void lua_register_hooks(apr_pool_t *p)
/* ivm mutex */
apr_thread_mutex_create(&lua_ivm_mutex, APR_THREAD_MUTEX_DEFAULT, p);
+
+ /* Logging catcher */
+ ap_hook_log_transaction(lua_log_transaction_harness,NULL,NULL,
+ APR_HOOK_FIRST);
}
AP_DECLARE_MODULE(lua) = {