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