diff options
author | Daniel Gruno <humbedooh@apache.org> | 2013-09-17 10:38:12 +0000 |
---|---|---|
committer | Daniel Gruno <humbedooh@apache.org> | 2013-09-17 10:38:12 +0000 |
commit | f41d8a48ea54240407b14e303eca4117aea5e979 (patch) | |
tree | 74f0c3df7b6bc31379cb45715d5244f62c86b158 | |
parent | c64faef062ced55434cd9c9de5a80bdfea740ede (diff) | |
download | httpd-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.xml | 105 | ||||
-rw-r--r-- | modules/lua/lua_request.c | 429 | ||||
-rw-r--r-- | modules/lua/mod_lua.c | 89 |
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) = { |