1 /*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18
19 #include "ts_lua_util.h"
20
21 #define TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx) \
22 do { \
23 if (!http_ctx->client_response_hdrp) { \
24 if (TSHttpTxnClientRespGet(http_ctx->txnp, &http_ctx->client_response_bufp, &http_ctx->client_response_hdrp) != \
25 TS_SUCCESS) { \
26 return 0; \
27 } \
28 } \
29 } while (0)
30
31 static int ts_lua_client_response_header_get(lua_State *L);
32 static int ts_lua_client_response_header_set(lua_State *L);
33
34 static int ts_lua_client_response_get_headers(lua_State *L);
35
36 static int ts_lua_client_response_get_status(lua_State *L);
37 static int ts_lua_client_response_set_status(lua_State *L);
38
39 static int ts_lua_client_response_set_error_resp(lua_State *L);
40
41 static int ts_lua_client_response_get_version(lua_State *L);
42 static int ts_lua_client_response_set_version(lua_State *L);
43
44 static void ts_lua_inject_client_response_header_api(lua_State *L);
45 static void ts_lua_inject_client_response_headers_api(lua_State *L);
46 static void ts_lua_inject_client_response_misc_api(lua_State *L);
47
48 void
ts_lua_inject_client_response_api(lua_State * L)49 ts_lua_inject_client_response_api(lua_State *L)
50 {
51 lua_newtable(L);
52
53 ts_lua_inject_client_response_header_api(L);
54 ts_lua_inject_client_response_headers_api(L);
55 ts_lua_inject_client_response_misc_api(L);
56
57 lua_setfield(L, -2, "client_response");
58 }
59
60 static void
ts_lua_inject_client_response_header_api(lua_State * L)61 ts_lua_inject_client_response_header_api(lua_State *L)
62 {
63 lua_newtable(L); /* .header */
64
65 lua_createtable(L, 0, 2); /* metatable for .header */
66
67 lua_pushcfunction(L, ts_lua_client_response_header_get);
68 lua_setfield(L, -2, "__index");
69 lua_pushcfunction(L, ts_lua_client_response_header_set);
70 lua_setfield(L, -2, "__newindex");
71
72 lua_setmetatable(L, -2);
73
74 lua_setfield(L, -2, "header");
75 }
76
77 static int
ts_lua_client_response_header_get(lua_State * L)78 ts_lua_client_response_header_get(lua_State *L)
79 {
80 const char *key;
81 const char *val;
82 int val_len;
83 size_t key_len;
84 int count;
85
86 TSMLoc field_loc, next_field_loc;
87
88 ts_lua_http_ctx *http_ctx;
89
90 GET_HTTP_CONTEXT(http_ctx, L);
91
92 /* we skip the first argument that is the table */
93 key = luaL_checklstring(L, 2, &key_len);
94
95 if (!http_ctx->client_response_hdrp) {
96 if (TSHttpTxnClientRespGet(http_ctx->txnp, &http_ctx->client_response_bufp, &http_ctx->client_response_hdrp) != TS_SUCCESS) {
97 lua_pushnil(L);
98 return 1;
99 }
100 }
101
102 if (key && key_len) {
103 field_loc = TSMimeHdrFieldFind(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, key, key_len);
104 if (field_loc != TS_NULL_MLOC) {
105 count = 0;
106 while (field_loc != TS_NULL_MLOC) {
107 val = TSMimeHdrFieldValueStringGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, -1, &val_len);
108 next_field_loc = TSMimeHdrFieldNextDup(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
109 lua_pushlstring(L, val, val_len);
110 count++;
111 // multiple headers with the same name must be semantically the same as one value which is comma separated
112 if (next_field_loc != TS_NULL_MLOC) {
113 lua_pushlstring(L, ",", 1);
114 count++;
115 }
116 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
117 field_loc = next_field_loc;
118 }
119 lua_concat(L, count);
120 } else {
121 lua_pushnil(L);
122 }
123
124 } else {
125 lua_pushnil(L);
126 }
127
128 return 1;
129 }
130
131 static int
ts_lua_client_response_header_set(lua_State * L)132 ts_lua_client_response_header_set(lua_State *L)
133 {
134 const char *key;
135 const char *val;
136 size_t val_len;
137 size_t key_len;
138 int remove;
139 int first;
140
141 TSMLoc field_loc, tmp;
142
143 ts_lua_http_ctx *http_ctx;
144
145 GET_HTTP_CONTEXT(http_ctx, L);
146
147 remove = 0;
148 val = NULL;
149
150 /* we skip the first argument that is the table */
151 key = luaL_checklstring(L, 2, &key_len);
152 if (lua_isnil(L, 3)) {
153 remove = 1;
154 } else {
155 val = luaL_checklstring(L, 3, &val_len);
156 }
157
158 if (!http_ctx->client_response_hdrp) {
159 if (TSHttpTxnClientRespGet(http_ctx->txnp, &http_ctx->client_response_bufp, &http_ctx->client_response_hdrp) != TS_SUCCESS) {
160 return 0;
161 }
162 }
163
164 field_loc = TSMimeHdrFieldFind(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, key, key_len);
165
166 if (remove) {
167 while (field_loc != TS_NULL_MLOC) {
168 tmp = TSMimeHdrFieldNextDup(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
169 TSMimeHdrFieldDestroy(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
170 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
171 field_loc = tmp;
172 }
173
174 } else if (field_loc != TS_NULL_MLOC) {
175 first = 1;
176 while (field_loc != TS_NULL_MLOC) {
177 tmp = TSMimeHdrFieldNextDup(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
178 if (first) {
179 first = 0;
180 TSMimeHdrFieldValueStringSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, -1, val, val_len);
181 } else {
182 TSMimeHdrFieldDestroy(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
183 }
184 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
185 field_loc = tmp;
186 }
187 } else if (TSMimeHdrFieldCreateNamed(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, key, key_len, &field_loc) !=
188 TS_SUCCESS) {
189 TSError("[ts_lua][%s] TSMimeHdrFieldCreateNamed error", __FUNCTION__);
190 return 0;
191
192 } else {
193 TSMimeHdrFieldValueStringSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, -1, val, val_len);
194 TSMimeHdrFieldAppend(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
195 }
196
197 if (field_loc != TS_NULL_MLOC) {
198 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
199 }
200
201 return 0;
202 }
203
204 static void
ts_lua_inject_client_response_headers_api(lua_State * L)205 ts_lua_inject_client_response_headers_api(lua_State *L)
206 {
207 lua_pushcfunction(L, ts_lua_client_response_get_headers);
208 lua_setfield(L, -2, "get_headers");
209 }
210
211 static int
ts_lua_client_response_get_headers(lua_State * L)212 ts_lua_client_response_get_headers(lua_State *L)
213 {
214 const char *name;
215 const char *value;
216 int name_len;
217 int value_len;
218 TSMLoc field_loc;
219 TSMLoc next_field_loc;
220 const char *tvalue;
221 size_t tvalue_len;
222
223 ts_lua_http_ctx *http_ctx;
224
225 GET_HTTP_CONTEXT(http_ctx, L);
226
227 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
228
229 lua_newtable(L);
230
231 field_loc = TSMimeHdrFieldGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, 0);
232
233 while (field_loc != TS_NULL_MLOC) {
234 name = TSMimeHdrFieldNameGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, &name_len);
235 if (name && name_len) {
236 // retrieve the header name from table
237 lua_pushlstring(L, name, name_len);
238 lua_gettable(L, -2);
239 if (lua_isnil(L, -1)) {
240 // if header name does not exist in the table, insert it
241 lua_pop(L, 1);
242 value =
243 TSMimeHdrFieldValueStringGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, -1, &value_len);
244 lua_pushlstring(L, name, name_len);
245 lua_pushlstring(L, value, value_len);
246 lua_rawset(L, -3);
247 } else {
248 // if header name exists in the table, append a command and the new value to the end of the existing value
249 tvalue = lua_tolstring(L, -1, &tvalue_len);
250 lua_pop(L, 1);
251 value =
252 TSMimeHdrFieldValueStringGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc, -1, &value_len);
253 lua_pushlstring(L, name, name_len);
254 lua_pushlstring(L, tvalue, tvalue_len);
255 lua_pushlstring(L, ",", 1);
256 lua_pushlstring(L, value, value_len);
257 lua_concat(L, 3);
258 lua_rawset(L, -3);
259 }
260 }
261
262 next_field_loc = TSMimeHdrFieldNext(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
263 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
264 field_loc = next_field_loc;
265 }
266
267 return 1;
268 }
269
270 static void
ts_lua_inject_client_response_misc_api(lua_State * L)271 ts_lua_inject_client_response_misc_api(lua_State *L)
272 {
273 lua_pushcfunction(L, ts_lua_client_response_get_status);
274 lua_setfield(L, -2, "get_status");
275 lua_pushcfunction(L, ts_lua_client_response_set_status);
276 lua_setfield(L, -2, "set_status");
277
278 lua_pushcfunction(L, ts_lua_client_response_get_version);
279 lua_setfield(L, -2, "get_version");
280 lua_pushcfunction(L, ts_lua_client_response_set_version);
281 lua_setfield(L, -2, "set_version");
282
283 lua_pushcfunction(L, ts_lua_client_response_set_error_resp);
284 lua_setfield(L, -2, "set_error_resp");
285
286 return;
287 }
288
289 static int
ts_lua_client_response_get_status(lua_State * L)290 ts_lua_client_response_get_status(lua_State *L)
291 {
292 int status;
293 ts_lua_http_ctx *http_ctx;
294
295 GET_HTTP_CONTEXT(http_ctx, L);
296
297 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
298
299 status = TSHttpHdrStatusGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp);
300
301 lua_pushinteger(L, status);
302
303 return 1;
304 }
305
306 static int
ts_lua_client_response_set_status(lua_State * L)307 ts_lua_client_response_set_status(lua_State *L)
308 {
309 int status;
310 const char *reason;
311 int reason_len;
312
313 ts_lua_http_ctx *http_ctx;
314
315 GET_HTTP_CONTEXT(http_ctx, L);
316
317 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
318
319 // NOLINTNEXTLINE
320 status = luaL_checkint(L, 1);
321
322 reason = TSHttpHdrReasonLookup(status);
323 reason_len = strlen(reason);
324
325 TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, status);
326 TSHttpHdrReasonSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, reason, reason_len);
327
328 return 0;
329 }
330
331 static int
ts_lua_client_response_get_version(lua_State * L)332 ts_lua_client_response_get_version(lua_State *L)
333 {
334 int version;
335 char buf[32];
336 int n;
337
338 ts_lua_http_ctx *http_ctx;
339
340 GET_HTTP_CONTEXT(http_ctx, L);
341
342 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
343
344 version = TSHttpHdrVersionGet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp);
345
346 n = snprintf(buf, sizeof(buf), "%d.%d", TS_HTTP_MAJOR(version), TS_HTTP_MINOR(version));
347 if (n >= (int)sizeof(buf)) {
348 lua_pushlstring(L, buf, sizeof(buf) - 1);
349 } else if (n > 0) {
350 lua_pushlstring(L, buf, n);
351 }
352
353 return 1;
354 }
355
356 static int
ts_lua_client_response_set_version(lua_State * L)357 ts_lua_client_response_set_version(lua_State *L)
358 {
359 const char *version;
360 size_t len;
361 unsigned int major, minor;
362
363 ts_lua_http_ctx *http_ctx;
364
365 GET_HTTP_CONTEXT(http_ctx, L);
366
367 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
368
369 version = luaL_checklstring(L, 1, &len);
370
371 if (sscanf(version, "%2u.%2u", &major, &minor) != 2) {
372 return luaL_error(L, "failed to set version. Format must be X.Y");
373 }
374
375 TSHttpHdrVersionSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TS_HTTP_VERSION(major, minor));
376
377 return 0;
378 }
379
380 static int
ts_lua_client_response_set_error_resp(lua_State * L)381 ts_lua_client_response_set_error_resp(lua_State *L)
382 {
383 int n, status;
384 const char *body = NULL;
385 size_t body_len = 0;
386 const char *reason;
387 int reason_len;
388 int resp_len;
389 char *resp_buf;
390 TSMLoc field_loc;
391
392 ts_lua_http_ctx *http_ctx;
393
394 GET_HTTP_CONTEXT(http_ctx, L);
395
396 TS_LUA_CHECK_CLIENT_RESPONSE_HDR(http_ctx);
397
398 n = lua_gettop(L);
399
400 status = luaL_checkinteger(L, 1);
401
402 reason = TSHttpHdrReasonLookup(status);
403 reason_len = strlen(reason);
404
405 TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, status);
406 TSHttpHdrReasonSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, reason, reason_len);
407
408 body_len = 0;
409
410 if (n == 2) {
411 body = luaL_checklstring(L, 2, &body_len);
412 }
413
414 if (body_len && body) {
415 resp_buf = TSmalloc(body_len);
416 memcpy(resp_buf, body, body_len);
417 resp_len = body_len;
418
419 } else {
420 resp_buf = TSmalloc(reason_len);
421 memcpy(resp_buf, reason, reason_len);
422 resp_len = reason_len;
423 }
424
425 field_loc = TSMimeHdrFieldFind(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TS_MIME_FIELD_TRANSFER_ENCODING,
426 TS_MIME_LEN_TRANSFER_ENCODING);
427
428 if (field_loc) {
429 TSMimeHdrFieldDestroy(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
430 TSHandleMLocRelease(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, field_loc);
431 }
432
433 TSHttpTxnErrorBodySet(http_ctx->txnp, resp_buf, resp_len, NULL);
434
435 return 0;
436 }
437