ea6f26cc9bbf50ed3c0cce4d22cff0041f11e670
[openwrt/svn-archive/archive.git] / package / uhttpd / src / uhttpd-lua.c
1 /*
2 * uhttpd - Tiny single-threaded 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
32 length = luaL_checknumber(L, 1);
33
34 if ((length > 0) && (length <= sizeof(buffer)))
35 {
36 FD_ZERO(&reader);
37 FD_SET(fileno(stdin), &reader);
38
39 /* fail after 0.1s */
40 timeout.tv_sec = 0;
41 timeout.tv_usec = 100000;
42
43 /* check whether fd is readable */
44 if (select(fileno(stdin) + 1, &reader, NULL, NULL, &timeout) > 0)
45 {
46 /* receive data */
47 rlen = read(fileno(stdin), buffer, length);
48 lua_pushnumber(L, rlen);
49
50 if (rlen > 0)
51 {
52 lua_pushlstring(L, buffer, rlen);
53 return 2;
54 }
55
56 return 1;
57 }
58
59 /* no, timeout and actually no data */
60 lua_pushnumber(L, -2);
61 return 1;
62 }
63
64 /* parameter error */
65 lua_pushnumber(L, -3);
66 return 1;
67 }
68
69 static int uh_lua_send_common(lua_State *L, int chunked)
70 {
71 size_t length;
72 const char *buffer;
73 char chunk[16];
74 ssize_t slen = 0;
75
76 buffer = luaL_checklstring(L, 1, &length);
77
78 if (chunked)
79 {
80 if (length > 0)
81 {
82 snprintf(chunk, sizeof(chunk), "%X\r\n", length);
83 slen = write(fileno(stdout), chunk, strlen(chunk));
84 slen += write(fileno(stdout), buffer, length);
85 slen += write(fileno(stdout), "\r\n", 2);
86 }
87 else
88 {
89 slen = write(fileno(stdout), "0\r\n\r\n", 5);
90 }
91 }
92 else
93 {
94 slen = write(fileno(stdout), buffer, length);
95 }
96
97 lua_pushnumber(L, slen);
98 return 1;
99 }
100
101 static int uh_lua_send(lua_State *L)
102 {
103 return uh_lua_send_common(L, 0);
104 }
105
106 static int uh_lua_sendc(lua_State *L)
107 {
108 return uh_lua_send_common(L, 1);
109 }
110
111 static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const char *, int))
112 {
113 size_t inlen;
114 int outlen;
115 const char *inbuf;
116 char outbuf[UH_LIMIT_MSGHEAD];
117
118 inbuf = luaL_checklstring(L, 1, &inlen);
119 outlen = (* xlate_func)(outbuf, sizeof(outbuf), inbuf, inlen);
120 if (outlen < 0)
121 luaL_error( L, "%s on URL-encode codec",
122 (outlen==-1) ? "buffer overflow" : "malformed string" );
123
124 lua_pushlstring(L, outbuf, outlen);
125 return 1;
126 }
127
128 static int uh_lua_urldecode(lua_State *L)
129 {
130 return uh_lua_str2str( L, uh_urldecode );
131 }
132
133
134 static int uh_lua_urlencode(lua_State *L)
135 {
136 return uh_lua_str2str( L, uh_urlencode );
137 }
138
139
140 lua_State * uh_lua_init(const struct config *conf)
141 {
142 lua_State *L = lua_open();
143 const char *err_str = NULL;
144
145 /* Load standard libaries */
146 luaL_openlibs(L);
147
148 /* build uhttpd api table */
149 lua_newtable(L);
150
151 /* register global send and receive functions */
152 lua_pushcfunction(L, uh_lua_recv);
153 lua_setfield(L, -2, "recv");
154
155 lua_pushcfunction(L, uh_lua_send);
156 lua_setfield(L, -2, "send");
157
158 lua_pushcfunction(L, uh_lua_sendc);
159 lua_setfield(L, -2, "sendc");
160
161 lua_pushcfunction(L, uh_lua_urldecode);
162 lua_setfield(L, -2, "urldecode");
163
164 lua_pushcfunction(L, uh_lua_urlencode);
165 lua_setfield(L, -2, "urlencode");
166
167 /* Pass the document-root to the Lua handler by placing it in
168 ** uhttpd.docroot. It could alternatively be placed in env.DOCUMENT_ROOT
169 ** which would more closely resemble the CGI protocol; but would mean that
170 ** it is not available at the time when the handler-chunk is loaded but
171 ** rather not until the handler is called, without any code savings. */
172 lua_pushstring(L, conf->docroot);
173 lua_setfield(L, -2, "docroot");
174
175 /* _G.uhttpd = { ... } */
176 lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd");
177
178
179 /* load Lua handler */
180 switch (luaL_loadfile(L, conf->lua_handler))
181 {
182 case LUA_ERRSYNTAX:
183 fprintf(stderr,
184 "Lua handler contains syntax errors, unable to continue\n");
185 exit(1);
186
187 case LUA_ERRMEM:
188 fprintf(stderr,
189 "Lua handler ran out of memory, unable to continue\n");
190 exit(1);
191
192 case LUA_ERRFILE:
193 fprintf(stderr,
194 "Lua cannot open the handler script, unable to continue\n");
195 exit(1);
196
197 default:
198 /* compile Lua handler */
199 switch (lua_pcall(L, 0, 0, 0))
200 {
201 case LUA_ERRRUN:
202 err_str = luaL_checkstring(L, -1);
203 fprintf(stderr,
204 "Lua handler had runtime error, unable to continue\n"
205 "Error: %s\n", err_str
206 );
207 exit(1);
208
209 case LUA_ERRMEM:
210 err_str = luaL_checkstring(L, -1);
211 fprintf(stderr,
212 "Lua handler ran out of memory, unable to continue\n"
213 "Error: %s\n", err_str
214 );
215 exit(1);
216
217 default:
218 /* test handler function */
219 lua_getglobal(L, UH_LUA_CALLBACK);
220
221 if (! lua_isfunction(L, -1))
222 {
223 fprintf(stderr,
224 "Lua handler provides no " UH_LUA_CALLBACK "(), unable to continue\n");
225 exit(1);
226 }
227
228 lua_pop(L, 1);
229 break;
230 }
231
232 break;
233 }
234
235 return L;
236 }
237
238 void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
239 {
240 int i, data_sent;
241 int content_length = 0;
242 int buflen = 0;
243 int fd_max = 0;
244 char *query_string;
245 const char *prefix = cl->server->conf->lua_prefix;
246 const char *err_str = NULL;
247
248 int rfd[2] = { 0, 0 };
249 int wfd[2] = { 0, 0 };
250
251 char buf[UH_LIMIT_MSGHEAD];
252
253 pid_t child;
254
255 fd_set reader;
256 fd_set writer;
257
258 struct sigaction sa;
259 struct timeval timeout;
260
261
262 /* spawn pipes for me->child, child->me */
263 if ((pipe(rfd) < 0) || (pipe(wfd) < 0))
264 {
265 uh_http_sendhf(cl, 500, "Internal Server Error",
266 "Failed to create pipe: %s", strerror(errno));
267
268 if (rfd[0] > 0) close(rfd[0]);
269 if (rfd[1] > 0) close(rfd[1]);
270 if (wfd[0] > 0) close(wfd[0]);
271 if (wfd[1] > 0) close(wfd[1]);
272
273 return;
274 }
275
276
277 switch ((child = fork()))
278 {
279 case -1:
280 uh_http_sendhf(cl, 500, "Internal Server Error",
281 "Failed to fork child: %s", strerror(errno));
282 break;
283
284 case 0:
285 /* restore SIGTERM */
286 sa.sa_flags = 0;
287 sa.sa_handler = SIG_DFL;
288 sigemptyset(&sa.sa_mask);
289 sigaction(SIGTERM, &sa, NULL);
290
291 /* close loose pipe ends */
292 close(rfd[0]);
293 close(wfd[1]);
294
295 /* patch stdout and stdin to pipes */
296 dup2(rfd[1], 1);
297 dup2(wfd[0], 0);
298
299 /* put handler callback on stack */
300 lua_getglobal(L, UH_LUA_CALLBACK);
301
302 /* build env table */
303 lua_newtable(L);
304
305 /* request method */
306 switch(req->method)
307 {
308 case UH_HTTP_MSG_GET:
309 lua_pushstring(L, "GET");
310 break;
311
312 case UH_HTTP_MSG_HEAD:
313 lua_pushstring(L, "HEAD");
314 break;
315
316 case UH_HTTP_MSG_POST:
317 lua_pushstring(L, "POST");
318 break;
319 }
320
321 lua_setfield(L, -2, "REQUEST_METHOD");
322
323 /* request url */
324 lua_pushstring(L, req->url);
325 lua_setfield(L, -2, "REQUEST_URI");
326
327 /* script name */
328 lua_pushstring(L, cl->server->conf->lua_prefix);
329 lua_setfield(L, -2, "SCRIPT_NAME");
330
331 /* query string, path info */
332 if ((query_string = strchr(req->url, '?')) != NULL)
333 {
334 lua_pushstring(L, query_string + 1);
335 lua_setfield(L, -2, "QUERY_STRING");
336
337 if ((int)(query_string - req->url) > strlen(prefix))
338 {
339 lua_pushlstring(L,
340 &req->url[strlen(prefix)],
341 (int)(query_string - req->url) - strlen(prefix)
342 );
343
344 lua_setfield(L, -2, "PATH_INFO");
345 }
346 }
347 else if (strlen(req->url) > strlen(prefix))
348 {
349 lua_pushstring(L, &req->url[strlen(prefix)]);
350 lua_setfield(L, -2, "PATH_INFO");
351 }
352
353 /* http protcol version */
354 lua_pushnumber(L, floor(req->version * 10) / 10);
355 lua_setfield(L, -2, "HTTP_VERSION");
356
357 if (req->version > 1.0)
358 lua_pushstring(L, "HTTP/1.1");
359 else
360 lua_pushstring(L, "HTTP/1.0");
361
362 lua_setfield(L, -2, "SERVER_PROTOCOL");
363
364
365 /* address information */
366 lua_pushstring(L, sa_straddr(&cl->peeraddr));
367 lua_setfield(L, -2, "REMOTE_ADDR");
368
369 lua_pushinteger(L, sa_port(&cl->peeraddr));
370 lua_setfield(L, -2, "REMOTE_PORT");
371
372 lua_pushstring(L, sa_straddr(&cl->servaddr));
373 lua_setfield(L, -2, "SERVER_ADDR");
374
375 lua_pushinteger(L, sa_port(&cl->servaddr));
376 lua_setfield(L, -2, "SERVER_PORT");
377
378 /* essential env vars */
379 foreach_header(i, req->headers)
380 {
381 if (!strcasecmp(req->headers[i], "Content-Length"))
382 {
383 lua_pushnumber(L, atoi(req->headers[i+1]));
384 lua_setfield(L, -2, "CONTENT_LENGTH");
385 }
386 else if (!strcasecmp(req->headers[i], "Content-Type"))
387 {
388 lua_pushstring(L, req->headers[i+1]);
389 lua_setfield(L, -2, "CONTENT_TYPE");
390 }
391 }
392
393 /* misc. headers */
394 lua_newtable(L);
395
396 foreach_header(i, req->headers)
397 {
398 if( strcasecmp(req->headers[i], "Content-Length") &&
399 strcasecmp(req->headers[i], "Content-Type")
400 ) {
401 lua_pushstring(L, req->headers[i+1]);
402 lua_setfield(L, -2, req->headers[i]);
403 }
404 }
405
406 lua_setfield(L, -2, "headers");
407
408
409 /* call */
410 switch (lua_pcall(L, 1, 0, 0))
411 {
412 case LUA_ERRMEM:
413 case LUA_ERRRUN:
414 err_str = luaL_checkstring(L, -1);
415
416 if (! err_str)
417 err_str = "Unknown error";
418
419 printf(
420 "HTTP/%.1f 500 Internal Server Error\r\n"
421 "Connection: close\r\n"
422 "Content-Type: text/plain\r\n"
423 "Content-Length: %i\r\n\r\n"
424 "Lua raised a runtime error:\n %s\n",
425 req->version, 31 + strlen(err_str), err_str
426 );
427
428 break;
429
430 default:
431 break;
432 }
433
434 close(wfd[0]);
435 close(rfd[1]);
436 exit(0);
437
438 break;
439
440 /* parent; handle I/O relaying */
441 default:
442 /* close unneeded pipe ends */
443 close(rfd[1]);
444 close(wfd[0]);
445
446 /* max watch fd */
447 fd_max = max(rfd[0], wfd[1]) + 1;
448
449 /* find content length */
450 if (req->method == UH_HTTP_MSG_POST)
451 {
452 foreach_header(i, req->headers)
453 {
454 if (! strcasecmp(req->headers[i], "Content-Length"))
455 {
456 content_length = atoi(req->headers[i+1]);
457 break;
458 }
459 }
460 }
461
462
463 #define ensure(x) \
464 do { if (x < 0) goto out; } while(0)
465
466 data_sent = 0;
467
468 timeout.tv_sec = cl->server->conf->script_timeout;
469 timeout.tv_usec = 0;
470
471 /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
472 while (1)
473 {
474 FD_ZERO(&reader);
475 FD_ZERO(&writer);
476
477 FD_SET(rfd[0], &reader);
478 FD_SET(wfd[1], &writer);
479
480 /* wait until we can read or write or both */
481 if (select_intr(fd_max, &reader,
482 (content_length > -1) ? &writer : NULL,
483 NULL,
484 (data_sent < 1) ? &timeout : NULL) > 0)
485 {
486 /* ready to write to Lua child */
487 if (FD_ISSET(wfd[1], &writer))
488 {
489 /* there is unread post data waiting */
490 if (content_length > 0)
491 {
492 /* read it from socket ... */
493 if ((buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0)
494 {
495 /* ... and write it to child's stdin */
496 if (write(wfd[1], buf, buflen) < 0)
497 perror("write()");
498
499 content_length -= buflen;
500 }
501
502 /* unexpected eof! */
503 else
504 {
505 if (write(wfd[1], "", 0) < 0)
506 perror("write()");
507
508 content_length = 0;
509 }
510 }
511
512 /* there is no more post data, close pipe to child's stdin */
513 else if (content_length > -1)
514 {
515 close(wfd[1]);
516 content_length = -1;
517 }
518 }
519
520 /* ready to read from Lua child */
521 if (FD_ISSET(rfd[0], &reader))
522 {
523 /* read data from child ... */
524 if ((buflen = read(rfd[0], buf, sizeof(buf))) > 0)
525 {
526 /* pass through buffer to socket */
527 ensure(uh_tcp_send(cl, buf, buflen));
528 data_sent = 1;
529 }
530
531 /* looks like eof from child */
532 else
533 {
534 /* error? */
535 if (!data_sent)
536 uh_http_sendhf(cl, 500, "Internal Server Error",
537 "The Lua child did not produce any response");
538
539 break;
540 }
541 }
542 }
543
544 /* timeout exceeded or interrupted by SIGCHLD */
545 else
546 {
547 if ((errno != EINTR) && ! data_sent)
548 {
549 ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
550 "The Lua script took too long to produce "
551 "a response"));
552 }
553
554 break;
555 }
556 }
557
558 out:
559 close(rfd[0]);
560 close(wfd[1]);
561
562 if (!kill(child, 0))
563 {
564 kill(child, SIGTERM);
565 waitpid(child, NULL, 0);
566 }
567
568 break;
569 }
570 }
571
572 void uh_lua_close(lua_State *L)
573 {
574 lua_close(L);
575 }