Skip to content

Commit 671c360

Browse files
authored
Add support for TS_HTTP_REQUEST_CLIENT/TS_HTTP_RESPONSE_CLIENT for lua plugin (#9682)
* Add support for TS_HTTP_REQUEST_CLIENT/TS_HTTP_RESPONSE_CLIENT * fix clang format error * fix unused variables problem * add examples and fix unused variables errors * add documentation changes and fix clang format errors * add gold tests
1 parent 8044490 commit 671c360

File tree

8 files changed

+399
-0
lines changed

8 files changed

+399
-0
lines changed

doc/admin-guide/plugins/lua.en.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,8 @@ Hook point constants
647647
TS_LUA_HOOK_SEND_RESPONSE_HDR
648648
TS_LUA_REQUEST_TRANSFORM
649649
TS_LUA_RESPONSE_TRANSFORM
650+
TS_LUA_REQUEST_CLIENT
651+
TS_LUA_RESPONSE_CLIENT
650652

651653
These constants are usually used in ts.hook method call.
652654

@@ -694,6 +696,12 @@ Additional Information:
694696
| TS_HTTP_RESPONSE | TS_LUA_RESPONSE_TRANSFORM | YES | YES | YES |
695697
| _TRANSFORM_HOOK | | | | |
696698
+-----------------------+---------------------------+----------------------+--------------------+----------------------+
699+
| TS_HTTP_REQUEST | TS_LUA_REQUEST_CLIENT | YES | NO | YES |
700+
| _CLIENT_HOOK | | | | |
701+
+-----------------------+---------------------------+----------------------+--------------------+----------------------+
702+
| TS_HTTP_RESPONSE | TS_LUA_RESPONSE_CLIENT | YES | YES | YES |
703+
| _CLIENT_HOOK | | | | |
704+
+-----------------------+---------------------------+----------------------+--------------------+----------------------+
697705
| TS_HTTP_TXN | TS_LUA_HOOK_TXN_CLOSE | YES | YES | YES |
698706
| _CLOSE_HOOK | | | | |
699707
+-----------------------+---------------------------+----------------------+--------------------+----------------------+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
-- Licensed to the Apache Software Foundation (ASF) under one
2+
-- or more contributor license agreements. See the NOTICE file
3+
-- distributed with this work for additional information
4+
-- regarding copyright ownership. The ASF licenses this file
5+
-- to you under the Apache License, Version 2.0 (the
6+
-- "License"); you may not use this file except in compliance
7+
-- with the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing, software
12+
-- distributed under the License is distributed on an "AS IS" BASIS,
13+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
-- See the License for the specific language governing permissions and
15+
-- limitations under the License.
16+
17+
18+
function request_client(data, eos)
19+
ts.debug('request_client')
20+
ts.ctx['reqbody'] = ts.ctx['reqbody'] .. data
21+
ts.debug("req transform got " .. string.len(data) .. "bytes, eos=" .. eos)
22+
if (eos == 1) then
23+
ts.debug('End of Stream and the reqbody is ... ')
24+
ts.debug(ts.ctx['reqbody'])
25+
end
26+
end
27+
28+
function do_global_read_request()
29+
ts.debug('do_global_read_request')
30+
if (ts.client_request.get_method() == 'POST') then
31+
ts.debug('post')
32+
ts.ctx['reqbody'] = ''
33+
ts.hook(TS_LUA_REQUEST_CLIENT, request_client)
34+
end
35+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-- Licensed to the Apache Software Foundation (ASF) under one
2+
-- or more contributor license agreements. See the NOTICE file
3+
-- distributed with this work for additional information
4+
-- regarding copyright ownership. The ASF licenses this file
5+
-- to you under the Apache License, Version 2.0 (the
6+
-- "License"); you may not use this file except in compliance
7+
-- with the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing, software
12+
-- distributed under the License is distributed on an "AS IS" BASIS,
13+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
-- See the License for the specific language governing permissions and
15+
-- limitations under the License.
16+
17+
18+
function response_client(data, eos)
19+
ts.debug("testing")
20+
ts.debug(data)
21+
end
22+
23+
function do_global_read_request()
24+
ts.hook(TS_LUA_RESPONSE_CLIENT, response_client)
25+
end

plugins/lua/ts_lua_hook.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ typedef enum {
3535
TS_LUA_HOOK_TXN_CLOSE,
3636
TS_LUA_REQUEST_TRANSFORM,
3737
TS_LUA_RESPONSE_TRANSFORM,
38+
TS_LUA_REQUEST_CLIENT,
39+
TS_LUA_RESPONSE_CLIENT,
3840
TS_LUA_HOOK_LAST
3941
} TSLuaHookID;
4042

@@ -52,6 +54,8 @@ char *ts_lua_hook_id_string[] = {"TS_LUA_HOOK_DUMMY",
5254
"TS_LUA_HOOK_TXN_CLOSE",
5355
"TS_LUA_REQUEST_TRANSFORM",
5456
"TS_LUA_RESPONSE_TRANSFORM",
57+
"TS_LUA_REQUEST_CLIENT",
58+
"TS_LUA_RESPONSE_CLIENT",
5559
"TS_LUA_HOOK_LAST"};
5660

5761
static int ts_lua_add_hook(lua_State *L);
@@ -240,6 +244,20 @@ ts_lua_add_hook(lua_State *L)
240244
}
241245
break;
242246

247+
case TS_LUA_REQUEST_CLIENT:
248+
case TS_LUA_RESPONSE_CLIENT:
249+
if (http_ctx) {
250+
connp = TSTransformCreate(ts_lua_client_entry, http_ctx->txnp);
251+
ts_lua_create_http_transform_ctx(http_ctx, connp);
252+
253+
if (entry == TS_LUA_REQUEST_CLIENT) {
254+
TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_REQUEST_CLIENT_HOOK, connp);
255+
} else {
256+
TSHttpTxnHookAdd(http_ctx->txnp, TS_HTTP_RESPONSE_CLIENT_HOOK, connp);
257+
}
258+
}
259+
break;
260+
243261
default:
244262
break;
245263
}

plugins/lua/ts_lua_transform.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,230 @@
1818

1919
#include "ts_lua_util.h"
2020

21+
static int ts_lua_client_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n);
2122
static int ts_lua_transform_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n);
2223

24+
int
25+
ts_lua_client_entry(TSCont contp, TSEvent ev, void *edata)
26+
{
27+
int n, event;
28+
TSVIO input_vio;
29+
ts_lua_http_transform_ctx *transform_ctx;
30+
31+
event = (int)ev;
32+
transform_ctx = (ts_lua_http_transform_ctx *)TSContDataGet(contp);
33+
34+
n = 0;
35+
36+
switch (event) {
37+
case TS_EVENT_ERROR:
38+
input_vio = TSVConnWriteVIOGet(contp);
39+
TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
40+
break;
41+
42+
case TS_EVENT_VCONN_WRITE_COMPLETE:
43+
TSDebug(TS_LUA_DEBUG_TAG, "[%s] received TS_EVENT_VCONN_WRITE_COMPLETE", __FUNCTION__);
44+
break;
45+
46+
case TS_LUA_EVENT_COROUTINE_CONT:
47+
n = (intptr_t)edata;
48+
/* FALL THROUGH */
49+
case TS_EVENT_VCONN_WRITE_READY:
50+
default:
51+
ts_lua_client_handler(contp, transform_ctx, event, n);
52+
break;
53+
}
54+
55+
return 0;
56+
}
57+
58+
static int
59+
ts_lua_client_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n)
60+
{
61+
TSVIO input_vio;
62+
TSIOBufferReader input_reader;
63+
TSIOBufferBlock blk;
64+
int64_t toread, towrite, blk_len, upstream_done, input_avail, input_wm_bytes;
65+
const char *start;
66+
int ret, eos, rc, top, empty_input;
67+
ts_lua_coroutine *crt;
68+
ts_lua_cont_info *ci;
69+
70+
lua_State *L;
71+
TSMutex mtxp;
72+
73+
ci = &transform_ctx->cinfo;
74+
crt = &ci->routine;
75+
76+
mtxp = crt->mctx->mutexp;
77+
L = crt->lua;
78+
79+
input_vio = TSVConnWriteVIOGet(contp);
80+
81+
empty_input = 0;
82+
if (!TSVIOBufferGet(input_vio)) {
83+
TSDebug(TS_LUA_DEBUG_TAG, "[%s] no input VIO and output VIO", __FUNCTION__);
84+
empty_input = 1;
85+
} else { // input VIO exists
86+
input_wm_bytes = TSIOBufferWaterMarkGet(TSVIOBufferGet(input_vio));
87+
if (transform_ctx->upstream_watermark_bytes >= 0 && transform_ctx->upstream_watermark_bytes != input_wm_bytes) {
88+
TSDebug(TS_LUA_DEBUG_TAG, "[%s] Setting input_vio watermark to %" PRId64 " bytes", __FUNCTION__,
89+
transform_ctx->upstream_watermark_bytes);
90+
TSIOBufferWaterMarkSet(TSVIOBufferGet(input_vio), transform_ctx->upstream_watermark_bytes);
91+
}
92+
}
93+
94+
if (empty_input == 0) {
95+
input_reader = TSVIOReaderGet(input_vio);
96+
}
97+
98+
if (!transform_ctx->output.buffer) {
99+
transform_ctx->output.buffer = TSIOBufferCreate();
100+
transform_ctx->output.reader = TSIOBufferReaderAlloc(transform_ctx->output.buffer);
101+
102+
transform_ctx->reserved.buffer = TSIOBufferCreate();
103+
transform_ctx->reserved.reader = TSIOBufferReaderAlloc(transform_ctx->reserved.buffer);
104+
105+
if (empty_input == 0) {
106+
transform_ctx->upstream_bytes = TSVIONBytesGet(input_vio);
107+
} else {
108+
transform_ctx->upstream_bytes = 0;
109+
}
110+
111+
transform_ctx->downstream_bytes = INT64_MAX;
112+
}
113+
114+
if (empty_input == 0) {
115+
input_avail = TSIOBufferReaderAvail(input_reader);
116+
upstream_done = TSVIONDoneGet(input_vio);
117+
toread = TSVIONTodoGet(input_vio);
118+
119+
if (toread <= input_avail) { // upstream finished
120+
eos = 1;
121+
} else {
122+
eos = 0;
123+
}
124+
} else {
125+
input_avail = 0;
126+
upstream_done = 0;
127+
toread = 0;
128+
eos = 1;
129+
}
130+
131+
if (input_avail > 0) {
132+
// move to the reserved.buffer
133+
TSIOBufferCopy(transform_ctx->reserved.buffer, input_reader, input_avail, 0);
134+
135+
// reset input
136+
TSIOBufferReaderConsume(input_reader, input_avail);
137+
TSVIONDoneSet(input_vio, upstream_done + input_avail);
138+
}
139+
140+
if (empty_input == 0) {
141+
towrite = TSIOBufferReaderAvail(transform_ctx->reserved.reader);
142+
} else {
143+
towrite = 0;
144+
}
145+
146+
TSMutexLock(mtxp);
147+
ts_lua_set_cont_info(L, ci);
148+
149+
do {
150+
if (event == TS_LUA_EVENT_COROUTINE_CONT) {
151+
event = 0;
152+
goto launch;
153+
} else {
154+
n = 2;
155+
}
156+
157+
if (towrite == 0 && empty_input == 0) {
158+
break;
159+
}
160+
161+
// additional condition for client
162+
if (towrite == 0) {
163+
break;
164+
}
165+
166+
if (empty_input == 0) {
167+
blk = TSIOBufferReaderStart(transform_ctx->reserved.reader);
168+
start = TSIOBufferBlockReadStart(blk, transform_ctx->reserved.reader, &blk_len);
169+
170+
lua_pushlightuserdata(L, transform_ctx);
171+
lua_rawget(L, LUA_GLOBALSINDEX); /* push function */
172+
173+
if (towrite > blk_len) {
174+
lua_pushlstring(L, start, (size_t)blk_len);
175+
towrite -= blk_len;
176+
TSIOBufferReaderConsume(transform_ctx->reserved.reader, blk_len);
177+
} else {
178+
lua_pushlstring(L, start, (size_t)towrite);
179+
TSIOBufferReaderConsume(transform_ctx->reserved.reader, towrite);
180+
towrite = 0;
181+
}
182+
183+
if (!towrite && eos) {
184+
lua_pushinteger(L, 1); /* second param, data finished */
185+
} else {
186+
lua_pushinteger(L, 0); /* second param, data not finish */
187+
}
188+
} else {
189+
lua_pushlightuserdata(L, transform_ctx);
190+
lua_rawget(L, LUA_GLOBALSINDEX); /* push function */
191+
192+
lua_pushlstring(L, "", 0);
193+
lua_pushinteger(L, 1); /* second param, data finished */
194+
}
195+
196+
launch:
197+
rc = lua_resume(L, n);
198+
top = lua_gettop(L);
199+
200+
switch (rc) {
201+
case LUA_YIELD: // coroutine yield
202+
TSMutexUnlock(mtxp);
203+
return 0;
204+
205+
case 0: // coroutine success
206+
ret = 0;
207+
break;
208+
209+
default: // coroutine failed
210+
TSError("[ts_lua][%s] lua_resume failed: %s", __FUNCTION__, lua_tostring(L, -1));
211+
ret = 1;
212+
break;
213+
}
214+
215+
lua_pop(L, top);
216+
217+
if (ret || (eos && !towrite)) { // EOS
218+
eos = 1;
219+
break;
220+
}
221+
222+
} while (towrite > 0);
223+
224+
TSMutexUnlock(mtxp);
225+
226+
if (toread > input_avail) { // upstream not finished.
227+
if (eos) {
228+
if (empty_input == 0) {
229+
TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_EOS, input_vio);
230+
}
231+
} else {
232+
if (empty_input == 0) {
233+
TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio);
234+
}
235+
}
236+
} else { // upstream is finished.
237+
if (empty_input == 0) {
238+
TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio);
239+
}
240+
}
241+
242+
return 0;
243+
}
244+
23245
int
24246
ts_lua_transform_entry(TSCont contp, TSEvent ev, void *edata)
25247
{

plugins/lua/ts_lua_transform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020

2121
#include "ts_lua_common.h"
2222

23+
int ts_lua_client_entry(TSCont contp, TSEvent event, void *edata);
2324
int ts_lua_transform_entry(TSCont contp, TSEvent event, void *edata);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Licensed to the Apache Software Foundation (ASF) under one
2+
-- or more contributor license agreements. See the NOTICE file
3+
-- distributed with this work for additional information
4+
-- regarding copyright ownership. The ASF licenses this file
5+
-- to you under the Apache License, Version 2.0 (the
6+
-- "License"); you may not use this file except in compliance
7+
-- with the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing, software
12+
-- distributed under the License is distributed on an "AS IS" BASIS,
13+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
-- See the License for the specific language governing permissions and
15+
-- limitations under the License.
16+
17+
function response_client(data, eos)
18+
ts.debug(data)
19+
end
20+
21+
function do_remap()
22+
ts.hook(TS_LUA_RESPONSE_CLIENT, response_client)
23+
return 0
24+
end

0 commit comments

Comments
 (0)