blob: bb4b1c81bebe5814ee22f4f83313826548114c52 [file]
/*
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_lua_util.h"
#define TS_LUA_FUNCTION_HTTP_INTERCEPT "do_intercept"
#define TS_LUA_FUNCTION_HTTP_SERVER_INTERCEPT "do_server_intercept"
typedef enum {
TS_LUA_TYPE_HTTP_INTERCEPT = 0,
TS_LUA_TYPE_HTTP_SERVER_INTERCEPT = 1
} TSInterceptType;
static int ts_lua_http_intercept(lua_State *L);
static int ts_lua_http_server_intercept(lua_State *L);
static int ts_lua_http_intercept_entry(TSCont contp, TSEvent event, void *edata);
static void ts_lua_http_intercept_process(ts_lua_http_ctx *http_ctx, TSVConn conn);
static void ts_lua_http_intercept_setup_read(ts_lua_http_intercept_ctx *ictx);
static void ts_lua_http_intercept_setup_write(ts_lua_http_intercept_ctx *ictx);
static int ts_lua_http_intercept_handler(TSCont contp, TSEvent event, void *edata);
static int ts_lua_http_intercept_run_coroutine(ts_lua_http_intercept_ctx *ictx);
static int ts_lua_http_intercept_process_read(TSEvent event, ts_lua_http_intercept_ctx *ictx);
static int ts_lua_http_intercept_process_write(TSEvent event, ts_lua_http_intercept_ctx *ictx);
void
ts_lua_inject_http_intercept_api(lua_State *L)
{
lua_pushcfunction(L, ts_lua_http_intercept);
lua_setfield(L, -2, "intercept");
lua_pushcfunction(L, ts_lua_http_server_intercept);
lua_setfield(L, -2, "server_intercept");
}
static int
ts_lua_http_intercept(lua_State *L)
{
TSCont contp;
int type;
ts_lua_http_ctx *http_ctx;
http_ctx = ts_lua_get_http_ctx(L);
type = lua_type(L, 1);
if (type != LUA_TFUNCTION) {
fprintf(stderr, "[%s] param in ts.http.intercept should be a function\n", __FUNCTION__);
return 0;
}
lua_pushvalue(L, 1);
lua_setglobal(L, TS_LUA_FUNCTION_HTTP_INTERCEPT);
http_ctx->intercept_type = TS_LUA_TYPE_HTTP_INTERCEPT;
contp = TSContCreate(ts_lua_http_intercept_entry, TSMutexCreate());
TSContDataSet(contp, http_ctx);
TSHttpTxnIntercept(contp, http_ctx->txnp);
return 0;
}
static int
ts_lua_http_server_intercept(lua_State *L)
{
TSCont contp;
int type;
ts_lua_http_ctx *http_ctx;
http_ctx = ts_lua_get_http_ctx(L);
type = lua_type(L, 1);
if (type != LUA_TFUNCTION) {
fprintf(stderr, "[%s] param in ts.http.server_intercept should be a function\n", __FUNCTION__);
return 0;
}
lua_pushvalue(L, 1);
lua_setglobal(L, TS_LUA_FUNCTION_HTTP_SERVER_INTERCEPT);
http_ctx->intercept_type = TS_LUA_TYPE_HTTP_SERVER_INTERCEPT;
contp = TSContCreate(ts_lua_http_intercept_entry, TSMutexCreate());
TSContDataSet(contp, http_ctx);
TSHttpTxnServerIntercept(contp, http_ctx->txnp);
return 0;
}
static int
ts_lua_http_intercept_entry(TSCont contp, TSEvent event, void *edata)
{
switch (event) {
case TS_EVENT_NET_ACCEPT_FAILED:
if (edata)
TSVConnClose((TSVConn)edata);
break;
case TS_EVENT_NET_ACCEPT:
ts_lua_http_intercept_process((ts_lua_http_ctx*)TSContDataGet(contp), (TSVConn)edata);
break;
default:
break;
}
TSContDestroy(contp);
return 0;
}
static void
ts_lua_http_intercept_process(ts_lua_http_ctx *http_ctx, TSVConn conn)
{
TSCont contp;
lua_State *l;
TSMutex mtxp;
ts_lua_http_intercept_ctx *ictx;
mtxp = http_ctx->mctx->mutexp;
TSMutexLock(mtxp);
ictx = ts_lua_create_http_intercept_ctx(http_ctx);
contp = TSContCreate(ts_lua_http_intercept_handler, TSMutexCreate());
TSContDataSet(contp, ictx);
ictx->contp = contp;
ictx->net_vc = conn;
l = ictx->lua;
// set up read.
ts_lua_http_intercept_setup_read(ictx);
// invoke function here
if (http_ctx->intercept_type == TS_LUA_TYPE_HTTP_INTERCEPT) {
lua_getglobal(l, TS_LUA_FUNCTION_HTTP_INTERCEPT);
} else {
lua_getglobal(l, TS_LUA_FUNCTION_HTTP_SERVER_INTERCEPT);
}
ts_lua_http_intercept_run_coroutine(ictx);
TSMutexUnlock(mtxp);
}
static void
ts_lua_http_intercept_setup_read(ts_lua_http_intercept_ctx *ictx)
{
ictx->input.buffer = TSIOBufferCreate();
ictx->input.reader = TSIOBufferReaderAlloc(ictx->input.buffer);
ictx->input.vio = TSVConnRead(ictx->net_vc, ictx->contp, ictx->input.buffer, INT64_MAX);
}
static void
ts_lua_http_intercept_setup_write(ts_lua_http_intercept_ctx *ictx)
{
ictx->output.buffer = TSIOBufferCreate();
ictx->output.reader = TSIOBufferReaderAlloc(ictx->output.buffer);
ictx->output.vio = TSVConnWrite(ictx->net_vc, ictx->contp, ictx->output.reader, INT64_MAX);
}
static int
ts_lua_http_intercept_handler(TSCont contp, TSEvent event, void *edata)
{
int ret;
TSMutex mtxp;
ts_lua_http_intercept_ctx *ictx;
ictx = (ts_lua_http_intercept_ctx*)TSContDataGet(contp);
mtxp = NULL;
if (edata == ictx->input.vio) {
ret = ts_lua_http_intercept_process_read(event, ictx);
} else if (edata == ictx->output.vio) {
ret = ts_lua_http_intercept_process_write(event, ictx);
} else {
mtxp = ictx->hctx->mctx->mutexp;
TSMutexLock(mtxp);
ret = ts_lua_http_intercept_run_coroutine(ictx);
}
if (ret || (ictx->send_complete && ictx->recv_complete)) {
TSContDestroy(contp);
if (!mtxp) {
mtxp = ictx->hctx->mctx->mutexp;
TSMutexLock(mtxp);
}
ts_lua_destroy_http_intercept_ctx(ictx);
}
if (mtxp)
TSMutexUnlock(mtxp);
return 0;
}
static int
ts_lua_http_intercept_run_coroutine(ts_lua_http_intercept_ctx *ictx)
{
int ret;
const char *res;
size_t res_len;
lua_State *L;
L = ictx->lua;
ret = lua_resume(L, 0);
switch (ret) {
case 0: // finished
res = lua_tolstring(L, -1, &res_len);
ts_lua_http_intercept_setup_write(ictx);
TSIOBufferWrite(ictx->output.buffer, res, res_len);
TSVIONBytesSet(ictx->output.vio, res_len);
break;
case 1: // yield
break;
default: // error
fprintf(stderr, "lua_resume failed: %s\n", lua_tostring(L, -1));
return -1;
}
return 0;
}
static int
ts_lua_http_intercept_process_read(TSEvent event, ts_lua_http_intercept_ctx *ictx)
{
int64_t avail = TSIOBufferReaderAvail(ictx->input.reader);
TSIOBufferReaderConsume(ictx->input.reader, avail);
switch (event) {
case TS_EVENT_VCONN_READ_READY:
TSVConnShutdown(ictx->net_vc, 1, 0);
case TS_EVENT_VCONN_READ_COMPLETE:
case TS_EVENT_VCONN_EOS:
ictx->recv_complete = 1;
break;
default:
return -1;
}
return 0;
}
static int
ts_lua_http_intercept_process_write(TSEvent event, ts_lua_http_intercept_ctx *ictx)
{
switch (event) {
case TS_EVENT_VCONN_WRITE_READY:
if (TSIOBufferReaderAvail(ictx->output.reader))
TSVIOReenable(ictx->output.vio);
break;
case TS_EVENT_VCONN_WRITE_COMPLETE:
ictx->send_complete = 1;
break;
case TS_EVENT_ERROR:
default:
return -1;
}
return 0;
}