uhttpd: added uhttpd.docroot
[openwrt.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, NULL,
483                                         (data_sent < 1) ? &timeout : NULL) > 0
484                                 ) {
485                                         /* ready to write to Lua child */
486                                         if( FD_ISSET(wfd[1], &writer) )
487                                         {
488                                                 /* there is unread post data waiting */
489                                                 if( content_length > 0 )
490                                                 {
491                                                         /* read it from socket ... */
492                                                         if( (buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0 )
493                                                         {
494                                                                 /* ... and write it to child's stdin */
495                                                                 if( write(wfd[1], buf, buflen) < 0 )
496                                                                         perror("write()");
497
498                                                                 content_length -= buflen;
499                                                         }
500
501                                                         /* unexpected eof! */
502                                                         else
503                                                         {
504                                                                 if( write(wfd[1], "", 0) < 0 )
505                                                                         perror("write()");
506
507                                                                 content_length = 0;
508                                                         }
509                                                 }
510
511                                                 /* there is no more post data, close pipe to child's stdin */
512                                                 else if( content_length > -1 )
513                                                 {
514                                                         close(wfd[1]);
515                                                         content_length = -1;
516                                                 }
517                                         }
518
519                                         /* ready to read from Lua child */
520                                         if( FD_ISSET(rfd[0], &reader) )
521                                         {
522                                                 /* read data from child ... */
523                                                 if( (buflen = read(rfd[0], buf, sizeof(buf))) > 0 )
524                                                 {
525                                                         /* pass through buffer to socket */
526                                                         ensure(uh_tcp_send(cl, buf, buflen));
527                                                         data_sent = 1;
528                                                 }
529
530                                                 /* looks like eof from child */
531                                                 else
532                                                 {
533                                                         /* error? */
534                                                         if( ! data_sent )
535                                                                 uh_http_sendhf(cl, 500, "Internal Server Error",
536                                                                         "The Lua child did not produce any response");
537
538                                                         break;
539                                                 }
540                                         }
541                                 }
542
543                                 /* timeout exceeded or interrupted by SIGCHLD */
544                                 else
545                                 {
546                                         if( (errno != EINTR) && ! data_sent )
547                                         {
548                                                 ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
549                                                         "The Lua script took too long to produce "
550                                                         "a response"));
551                                         }
552
553                                         break;
554                                 }
555                         }
556
557                 out:
558                         close(rfd[0]);
559                         close(wfd[1]);
560
561                         if( !kill(child, 0) )
562                         {
563                                 kill(child, SIGTERM);
564                                 waitpid(child, NULL, 0);
565                         }
566
567                         break;
568         }
569 }
570
571 void uh_lua_close(lua_State *L)
572 {
573         lua_close(L);
574 }