uhttpd: terminate I/O loops if socket writes fail
[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(lua_State *L)
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                 slen = uh_tcp_send(cl, buffer, length);
97                 lua_pushnumber(L, slen);
98                 return 1;
99         }
100
101         lua_pushnumber(L, -1);
102         return 1;
103 }
104
105 static int uh_lua_urldecode(lua_State *L)
106 {
107         size_t inlen, outlen;
108         const char *inbuf;
109         char outbuf[UH_LIMIT_MSGHEAD];
110
111         inbuf = luaL_checklstring(L, 1, &inlen);
112         outlen = uh_urldecode(outbuf, sizeof(outbuf), inbuf, inlen);
113
114         lua_pushlstring(L, outbuf, outlen);
115         return 1;
116 }
117
118
119 lua_State * uh_lua_init(const char *handler)
120 {
121         lua_State *L = lua_open();
122         const luaL_reg *lib;
123         const char *err_str = NULL;
124
125         /* Declare the Lua libraries we wish to use. */
126         /* Note: If you are opening and running a file containing Lua code */
127         /* using 'lua_dofile(l, "myfile.lua") - you must delcare all the libraries */
128         /* used in that file here also. */
129         static const luaL_reg lualibs[] =
130         {
131                 { "base",       luaopen_base },
132                         { "string",             luaopen_string },
133                 { NULL,         NULL }
134         };
135
136         /* preload libraries */
137         for (lib = lualibs; lib->func != NULL; lib++)
138         {
139                 lib->func(L);
140                 lua_settop(L, 0);
141         }
142
143         /* register global send and receive functions */
144         lua_pushcfunction(L, uh_lua_recv);
145         lua_setfield(L, LUA_GLOBALSINDEX, "recv");
146
147         lua_pushcfunction(L, uh_lua_send);
148         lua_setfield(L, LUA_GLOBALSINDEX, "send");
149
150         lua_pushcfunction(L, uh_lua_urldecode);
151         lua_setfield(L, LUA_GLOBALSINDEX, "urldecode");
152
153
154         /* load Lua handler */
155         switch( luaL_loadfile(L, handler) )
156         {
157                 case LUA_ERRSYNTAX:
158                         fprintf(stderr,
159                                 "Lua handler contains syntax errors, unable to continue\n");
160                         exit(1);
161
162                 case LUA_ERRMEM:
163                         fprintf(stderr,
164                                 "Lua handler ran out of memory, unable to continue\n");
165                         exit(1);
166
167                 case LUA_ERRFILE:
168                         fprintf(stderr,
169                                 "Lua cannot open the handler script, unable to continue\n");
170                         exit(1);
171
172                 default:
173                         /* compile Lua handler */
174                         switch( lua_pcall(L, 0, 0, 0) )
175                         {
176                                 case LUA_ERRRUN:
177                                         err_str = luaL_checkstring(L, -1);
178                                         fprintf(stderr,
179                                                 "Lua handler had runtime error, unable to continue\n"
180                                                 "Error: %s\n", err_str
181                                         );
182                                         exit(1);
183
184                                 case LUA_ERRMEM:
185                                         err_str = luaL_checkstring(L, -1);
186                                         fprintf(stderr,
187                                                 "Lua handler ran out of memory, unable to continue\n"
188                                                 "Error: %s\n", err_str
189                                         );
190                                         exit(1);
191
192                                 default:
193                                         /* test handler function */
194                                         lua_getglobal(L, UH_LUA_CALLBACK);
195
196                                         if( ! lua_isfunction(L, -1) )
197                                         {
198                                                 fprintf(stderr,
199                                                         "Lua handler provides no " UH_LUA_CALLBACK "(), unable to continue\n");
200                                                 exit(1);
201                                         }
202
203                                         lua_pop(L, 1);
204                                         break;
205                         }
206
207                         break;
208         }
209
210         return L;
211 }
212
213 void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
214 {
215         int i;
216         char *query_string;
217         const char *prefix = cl->server->conf->lua_prefix;
218         const char *err_str = NULL;
219
220         /* put handler callback on stack */
221         lua_getglobal(L, UH_LUA_CALLBACK);
222
223
224         /* build env table */
225         lua_newtable(L);
226
227         /* client object */
228         lua_pushlightuserdata(L, (void *)cl);
229         lua_setfield(L, -2, "client");
230
231         /* request method */
232         switch(req->method)
233         {
234                 case UH_HTTP_MSG_GET:
235                         lua_pushstring(L, "get");
236                         break;
237
238                 case UH_HTTP_MSG_HEAD:
239                         lua_pushstring(L, "head");
240                         break;
241
242                 case UH_HTTP_MSG_POST:
243                         lua_pushstring(L, "post");
244                         break;
245         }
246
247         lua_setfield(L, -2, "request_method");
248
249         /* request url */
250         lua_pushstring(L, req->url);
251         lua_setfield(L, -2, "request_url");
252
253         /* query string, path info */
254         if( (query_string = strchr(req->url, '?')) != NULL )
255         {
256                 lua_pushstring(L, query_string + 1);
257                 lua_setfield(L, -2, "query_string");
258
259                 if( (int)(query_string - req->url) > strlen(prefix) )
260                 {
261                         lua_pushlstring(L,
262                                 &req->url[strlen(prefix)],
263                                 (int)(query_string - req->url) - strlen(prefix)
264                         );
265
266                         lua_setfield(L, -2, "path_info");
267                 }
268         }
269         else if( strlen(req->url) > strlen(prefix) )
270         {
271                 lua_pushstring(L, &req->url[strlen(prefix)]);
272                 lua_setfield(L, -2, "path_info");
273         }
274
275         /* http protcol version */
276         lua_pushnumber(L, floor(req->version * 10) / 10);
277         lua_setfield(L, -2, "http_version");
278
279
280         /* address information */
281         lua_pushstring(L, sa_straddr(&cl->peeraddr));
282         lua_setfield(L, -2, "remote_addr");
283
284         lua_pushinteger(L, sa_port(&cl->peeraddr));
285         lua_setfield(L, -2, "remote_port");
286
287         lua_pushstring(L, sa_straddr(&cl->servaddr));
288         lua_setfield(L, -2, "server_addr");
289
290         lua_pushinteger(L, sa_port(&cl->servaddr));
291         lua_setfield(L, -2, "server_port");
292
293
294         /* headers */
295         lua_newtable(L);
296
297         foreach_header(i, req->headers)
298         {
299                 lua_pushstring(L, req->headers[i+1]);
300                 lua_setfield(L, -2, req->headers[i]);
301         }
302
303         lua_setfield(L, -2, "headers");
304
305
306         /* call */
307         switch( lua_pcall(L, 1, 0, 0) )
308         {
309                 case LUA_ERRRUN:
310                         err_str = luaL_checkstring(L, -1);
311                         uh_http_sendhf(cl, 500, "Lua runtime error",
312                                 "Lua raised an error:\n%s\n", err_str);
313                         break;
314
315                 case LUA_ERRMEM:
316                         err_str = luaL_checkstring(L, -1);
317                         uh_http_sendhf(cl, 500, "Lua out of memory",
318                                 "Lua raised an error:\n%s\n", err_str);
319                         break;
320
321                 default:
322                         break;
323         }
324 }
325