2c2821d065580941cb75aaa44997c8ed0505b486
[project/luci.git] / contrib / package / uhttpd / src / uhttpd-lua.c
1 /*
2  * uhttpd - Tiny non-forking httpd - Lua handler
3  *
4  *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  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 "uhttpd.h"
20 #include "uhttpd-utils.h"
21 #include "uhttpd-lua.h"
22
23
24 static int uh_lua_recv(lua_State *L)
25 {
26         size_t length;
27         char buffer[UH_LIMIT_MSGHEAD];
28         ssize_t rlen = 0;
29         fd_set reader;
30         struct timeval timeout;
31         struct client *cl;
32
33         luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
34         cl = (struct client *) lua_topointer(L, 1);
35         length = luaL_checknumber(L, 2);
36
37         if( (cl != NULL) && (length > 0) && (length <= sizeof(buffer)) )
38         {
39                 FD_ZERO(&reader);
40                 FD_SET(cl->socket, &reader);
41
42                 /* fail after 0.1s */
43                 timeout.tv_sec  = 0;
44                 timeout.tv_usec = 100000;
45
46                 /* first return stuff from peek buffer */
47                 if( cl->peeklen > 0 )
48                 {
49                         /* receive data */
50                         rlen = uh_tcp_recv(cl, buffer, min(cl->peeklen, length));
51                         lua_pushnumber(L, rlen);
52                         lua_pushlstring(L, buffer, rlen);
53
54                         return 2;
55                 }
56
57                 /* check whether fd is readable */
58                 else if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
59                 {
60                         /* receive data */
61                         rlen = uh_tcp_recv(cl, buffer, length);
62                         lua_pushnumber(L, rlen);
63
64                         if( rlen > 0 )
65                         {
66                                 lua_pushlstring(L, buffer, rlen);
67                                 return 2;
68                         }
69
70                         return 1;
71                 }
72
73                 /* no, timeout and actually no data */
74                 lua_pushnumber(L, -2);
75                 return 1;
76         }
77
78         /* parameter error */
79         lua_pushnumber(L, -3);
80         return 1;
81 }
82
83 static int uh_lua_send_common(lua_State *L, int chunked)
84 {
85         size_t length;
86         const char *buffer;
87         ssize_t slen = 0;
88         struct client *cl;
89
90         luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
91         cl = (struct client *) lua_topointer(L, 1);
92         buffer = luaL_checklstring(L, 2, &length);
93
94         if( (cl != NULL) && (length > 0) )
95         {
96                 if( chunked )
97                         slen = uh_http_sendc(cl, buffer, length);
98                 else
99                         slen = uh_tcp_send(cl, buffer, length);
100
101                 lua_pushnumber(L, slen);
102                 return 1;
103         }
104
105         lua_pushnumber(L, -1);
106         return 1;
107 }
108
109 static int uh_lua_send(lua_State *L)
110 {
111         return uh_lua_send_common(L, 0);
112 }
113
114 static int uh_lua_sendc(lua_State *L)
115 {
116         return uh_lua_send_common(L, 1);
117 }
118
119 static int uh_lua_urldecode(lua_State *L)
120 {
121         size_t inlen, outlen;
122         const char *inbuf;
123         char outbuf[UH_LIMIT_MSGHEAD];
124
125         inbuf = luaL_checklstring(L, 1, &inlen);
126         outlen = uh_urldecode(outbuf, sizeof(outbuf), inbuf, inlen);
127
128         lua_pushlstring(L, outbuf, outlen);
129         return 1;
130 }
131
132
133 lua_State * uh_lua_init(const char *handler)
134 {
135         lua_State *L = lua_open();
136         const luaL_reg *lib;
137         const char *err_str = NULL;
138
139         /* Declare the Lua libraries we wish to use. */
140         /* Note: If you are opening and running a file containing Lua code */
141         /* using 'lua_dofile(l, "myfile.lua") - you must delcare all the libraries */
142         /* used in that file here also. */
143         static const luaL_reg lualibs[] =
144         {
145                 { "base",       luaopen_base },
146                         { "string",             luaopen_string },
147                 { NULL,         NULL }
148         };
149
150         /* preload libraries */
151         for (lib = lualibs; lib->func != NULL; lib++)
152         {
153                 lib->func(L);
154                 lua_settop(L, 0);
155         }
156
157         /* build uhttpd api table */
158         lua_newtable(L);
159
160         /* register global send and receive functions */
161         lua_pushcfunction(L, uh_lua_recv);
162         lua_setfield(L, -2, "recv");
163
164         lua_pushcfunction(L, uh_lua_send);
165         lua_setfield(L, -2, "send");
166
167         lua_pushcfunction(L, uh_lua_sendc);
168         lua_setfield(L, -2, "sendc");
169
170         lua_pushcfunction(L, uh_lua_urldecode);
171         lua_setfield(L, -2, "urldecode");
172
173         /* _G.uhttpd = { ... } */
174         lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd");
175
176
177         /* load Lua handler */
178         switch( luaL_loadfile(L, handler) )
179         {
180                 case LUA_ERRSYNTAX:
181                         fprintf(stderr,
182                                 "Lua handler contains syntax errors, unable to continue\n");
183                         exit(1);
184
185                 case LUA_ERRMEM:
186                         fprintf(stderr,
187                                 "Lua handler ran out of memory, unable to continue\n");
188                         exit(1);
189
190                 case LUA_ERRFILE:
191                         fprintf(stderr,
192                                 "Lua cannot open the handler script, unable to continue\n");
193                         exit(1);
194
195                 default:
196                         /* compile Lua handler */
197                         switch( lua_pcall(L, 0, 0, 0) )
198                         {
199                                 case LUA_ERRRUN:
200                                         err_str = luaL_checkstring(L, -1);
201                                         fprintf(stderr,
202                                                 "Lua handler had runtime error, unable to continue\n"
203                                                 "Error: %s\n", err_str
204                                         );
205                                         exit(1);
206
207                                 case LUA_ERRMEM:
208                                         err_str = luaL_checkstring(L, -1);
209                                         fprintf(stderr,
210                                                 "Lua handler ran out of memory, unable to continue\n"
211                                                 "Error: %s\n", err_str
212                                         );
213                                         exit(1);
214
215                                 default:
216                                         /* test handler function */
217                                         lua_getglobal(L, UH_LUA_CALLBACK);
218
219                                         if( ! lua_isfunction(L, -1) )
220                                         {
221                                                 fprintf(stderr,
222                                                         "Lua handler provides no " UH_LUA_CALLBACK "(), unable to continue\n");
223                                                 exit(1);
224                                         }
225
226                                         lua_pop(L, 1);
227                                         break;
228                         }
229
230                         break;
231         }
232
233         return L;
234 }
235
236 void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
237 {
238         int i;
239         char *query_string;
240         const char *prefix = cl->server->conf->lua_prefix;
241         const char *err_str = NULL;
242
243         /* put handler callback on stack */
244         lua_getglobal(L, UH_LUA_CALLBACK);
245
246
247         /* build env table */
248         lua_newtable(L);
249
250         /* client object */
251         lua_pushlightuserdata(L, (void *)cl);
252         lua_setfield(L, -2, "client");
253
254         /* request method */
255         switch(req->method)
256         {
257                 case UH_HTTP_MSG_GET:
258                         lua_pushstring(L, "get");
259                         break;
260
261                 case UH_HTTP_MSG_HEAD:
262                         lua_pushstring(L, "head");
263                         break;
264
265                 case UH_HTTP_MSG_POST:
266                         lua_pushstring(L, "post");
267                         break;
268         }
269
270         lua_setfield(L, -2, "request_method");
271
272         /* request url */
273         lua_pushstring(L, req->url);
274         lua_setfield(L, -2, "request_url");
275
276         /* query string, path info */
277         if( (query_string = strchr(req->url, '?')) != NULL )
278         {
279                 lua_pushstring(L, query_string + 1);
280                 lua_setfield(L, -2, "query_string");
281
282                 if( (int)(query_string - req->url) > strlen(prefix) )
283                 {
284                         lua_pushlstring(L,
285                                 &req->url[strlen(prefix)],
286                                 (int)(query_string - req->url) - strlen(prefix)
287                         );
288
289                         lua_setfield(L, -2, "path_info");
290                 }
291         }
292         else if( strlen(req->url) > strlen(prefix) )
293         {
294                 lua_pushstring(L, &req->url[strlen(prefix)]);
295                 lua_setfield(L, -2, "path_info");
296         }
297
298         /* http protcol version */
299         lua_pushnumber(L, floor(req->version * 10) / 10);
300         lua_setfield(L, -2, "http_version");
301
302
303         /* address information */
304         lua_pushstring(L, sa_straddr(&cl->peeraddr));
305         lua_setfield(L, -2, "remote_addr");
306
307         lua_pushinteger(L, sa_port(&cl->peeraddr));
308         lua_setfield(L, -2, "remote_port");
309
310         lua_pushstring(L, sa_straddr(&cl->servaddr));
311         lua_setfield(L, -2, "server_addr");
312
313         lua_pushinteger(L, sa_port(&cl->servaddr));
314         lua_setfield(L, -2, "server_port");
315
316
317         /* headers */
318         lua_newtable(L);
319
320         foreach_header(i, req->headers)
321         {
322                 lua_pushstring(L, req->headers[i+1]);
323                 lua_setfield(L, -2, req->headers[i]);
324         }
325
326         lua_setfield(L, -2, "headers");
327
328
329         /* call */
330         switch( lua_pcall(L, 1, 0, 0) )
331         {
332                 case LUA_ERRRUN:
333                         err_str = luaL_checkstring(L, -1);
334                         uh_http_sendhf(cl, 500, "Lua runtime error",
335                                 "Lua raised an error:\n%s\n", err_str);
336                         break;
337
338                 case LUA_ERRMEM:
339                         err_str = luaL_checkstring(L, -1);
340                         uh_http_sendhf(cl, 500, "Lua out of memory",
341                                 "Lua raised an error:\n%s\n", err_str);
342                         break;
343
344                 default:
345                         break;
346         }
347 }
348