Fixed: [PATCH 2/3] uhttpd URL-codec enhancements.
[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 char *handler)
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         /* _G.uhttpd = { ... } */
168         lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd");
169
170
171         /* load Lua handler */
172         switch( luaL_loadfile(L, handler) )
173         {
174                 case LUA_ERRSYNTAX:
175                         fprintf(stderr,
176                                 "Lua handler contains syntax errors, unable to continue\n");
177                         exit(1);
178
179                 case LUA_ERRMEM:
180                         fprintf(stderr,
181                                 "Lua handler ran out of memory, unable to continue\n");
182                         exit(1);
183
184                 case LUA_ERRFILE:
185                         fprintf(stderr,
186                                 "Lua cannot open the handler script, unable to continue\n");
187                         exit(1);
188
189                 default:
190                         /* compile Lua handler */
191                         switch( lua_pcall(L, 0, 0, 0) )
192                         {
193                                 case LUA_ERRRUN:
194                                         err_str = luaL_checkstring(L, -1);
195                                         fprintf(stderr,
196                                                 "Lua handler had runtime error, unable to continue\n"
197                                                 "Error: %s\n", err_str
198                                         );
199                                         exit(1);
200
201                                 case LUA_ERRMEM:
202                                         err_str = luaL_checkstring(L, -1);
203                                         fprintf(stderr,
204                                                 "Lua handler ran out of memory, unable to continue\n"
205                                                 "Error: %s\n", err_str
206                                         );
207                                         exit(1);
208
209                                 default:
210                                         /* test handler function */
211                                         lua_getglobal(L, UH_LUA_CALLBACK);
212
213                                         if( ! lua_isfunction(L, -1) )
214                                         {
215                                                 fprintf(stderr,
216                                                         "Lua handler provides no " UH_LUA_CALLBACK "(), unable to continue\n");
217                                                 exit(1);
218                                         }
219
220                                         lua_pop(L, 1);
221                                         break;
222                         }
223
224                         break;
225         }
226
227         return L;
228 }
229
230 void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
231 {
232         int i, data_sent;
233         int content_length = 0;
234         int buflen = 0;
235         int fd_max = 0;
236         char *query_string;
237         const char *prefix = cl->server->conf->lua_prefix;
238         const char *err_str = NULL;
239
240         int rfd[2] = { 0, 0 };
241         int wfd[2] = { 0, 0 };
242
243         char buf[UH_LIMIT_MSGHEAD];
244
245         pid_t child;
246
247         fd_set reader;
248         fd_set writer;
249
250         struct sigaction sa;
251         struct timeval timeout;
252
253
254         /* spawn pipes for me->child, child->me */
255         if( (pipe(rfd) < 0) || (pipe(wfd) < 0) )
256         {
257                 uh_http_sendhf(cl, 500, "Internal Server Error",
258                         "Failed to create pipe: %s", strerror(errno));
259
260                 if( rfd[0] > 0 ) close(rfd[0]);
261                 if( rfd[1] > 0 ) close(rfd[1]);
262                 if( wfd[0] > 0 ) close(wfd[0]);
263                 if( wfd[1] > 0 ) close(wfd[1]);
264
265                 return;
266         }
267
268
269         switch( (child = fork()) )
270         {
271                 case -1:
272                         uh_http_sendhf(cl, 500, "Internal Server Error",
273                                 "Failed to fork child: %s", strerror(errno));
274                         break;
275
276                 case 0:
277                         /* restore SIGTERM */
278                         sa.sa_flags = 0;
279                         sa.sa_handler = SIG_DFL;
280                         sigemptyset(&sa.sa_mask);
281                         sigaction(SIGTERM, &sa, NULL);
282
283                         /* close loose pipe ends */
284                         close(rfd[0]);
285                         close(wfd[1]);
286
287                         /* patch stdout and stdin to pipes */
288                         dup2(rfd[1], 1);
289                         dup2(wfd[0], 0);
290
291                         /* put handler callback on stack */
292                         lua_getglobal(L, UH_LUA_CALLBACK);
293
294                         /* build env table */
295                         lua_newtable(L);
296
297                         /* request method */
298                         switch(req->method)
299                         {
300                                 case UH_HTTP_MSG_GET:
301                                         lua_pushstring(L, "GET");
302                                         break;
303
304                                 case UH_HTTP_MSG_HEAD:
305                                         lua_pushstring(L, "HEAD");
306                                         break;
307
308                                 case UH_HTTP_MSG_POST:
309                                         lua_pushstring(L, "POST");
310                                         break;
311                         }
312
313                         lua_setfield(L, -2, "REQUEST_METHOD");
314
315                         /* request url */
316                         lua_pushstring(L, req->url);
317                         lua_setfield(L, -2, "REQUEST_URI");
318
319                         /* script name */
320                         lua_pushstring(L, cl->server->conf->lua_prefix);
321                         lua_setfield(L, -2, "SCRIPT_NAME");
322
323                         /* query string, path info */
324                         if( (query_string = strchr(req->url, '?')) != NULL )
325                         {
326                                 lua_pushstring(L, query_string + 1);
327                                 lua_setfield(L, -2, "QUERY_STRING");
328
329                                 if( (int)(query_string - req->url) > strlen(prefix) )
330                                 {
331                                         lua_pushlstring(L,
332                                                 &req->url[strlen(prefix)],
333                                                 (int)(query_string - req->url) - strlen(prefix)
334                                         );
335
336                                         lua_setfield(L, -2, "PATH_INFO");
337                                 }
338                         }
339                         else if( strlen(req->url) > strlen(prefix) )
340                         {
341                                 lua_pushstring(L, &req->url[strlen(prefix)]);
342                                 lua_setfield(L, -2, "PATH_INFO");
343                         }
344
345                         /* http protcol version */
346                         lua_pushnumber(L, floor(req->version * 10) / 10);
347                         lua_setfield(L, -2, "HTTP_VERSION");
348
349                         if( req->version > 1.0 )
350                                 lua_pushstring(L, "HTTP/1.1");
351                         else
352                                 lua_pushstring(L, "HTTP/1.0");
353
354                         lua_setfield(L, -2, "SERVER_PROTOCOL");
355
356
357                         /* address information */
358                         lua_pushstring(L, sa_straddr(&cl->peeraddr));
359                         lua_setfield(L, -2, "REMOTE_ADDR");
360
361                         lua_pushinteger(L, sa_port(&cl->peeraddr));
362                         lua_setfield(L, -2, "REMOTE_PORT");
363
364                         lua_pushstring(L, sa_straddr(&cl->servaddr));
365                         lua_setfield(L, -2, "SERVER_ADDR");
366
367                         lua_pushinteger(L, sa_port(&cl->servaddr));
368                         lua_setfield(L, -2, "SERVER_PORT");
369
370                         /* essential env vars */
371                         foreach_header(i, req->headers)
372                         {
373                                 if( !strcasecmp(req->headers[i], "Content-Length") )
374                                 {
375                                         lua_pushnumber(L, atoi(req->headers[i+1]));
376                                         lua_setfield(L, -2, "CONTENT_LENGTH");
377                                 }
378                                 else if( !strcasecmp(req->headers[i], "Content-Type") )
379                                 {
380                                         lua_pushstring(L, req->headers[i+1]);
381                                         lua_setfield(L, -2, "CONTENT_TYPE");
382                                 }
383                         }
384
385                         /* misc. headers */
386                         lua_newtable(L);
387
388                         foreach_header(i, req->headers)
389                         {
390                                 if( strcasecmp(req->headers[i], "Content-Length") &&
391                                         strcasecmp(req->headers[i], "Content-Type")
392                                 ) {
393                                         lua_pushstring(L, req->headers[i+1]);
394                                         lua_setfield(L, -2, req->headers[i]);
395                                 }
396                         }
397
398                         lua_setfield(L, -2, "headers");
399
400
401                         /* call */
402                         switch( lua_pcall(L, 1, 0, 0) )
403                         {
404                                 case LUA_ERRMEM:
405                                 case LUA_ERRRUN:
406                                         err_str = luaL_checkstring(L, -1);
407
408                                         if( ! err_str )
409                                                 err_str = "Unknown error";
410
411                                         printf(
412                                                 "HTTP/%.1f 500 Internal Server Error\r\n"
413                                                 "Connection: close\r\n"
414                                                 "Content-Type: text/plain\r\n"
415                                                 "Content-Length: %i\r\n\r\n"
416                                                 "Lua raised a runtime error:\n  %s\n",
417                                                         req->version, 31 + strlen(err_str), err_str
418                                         );
419
420                                         break;
421
422                                 default:
423                                         break;
424                         }
425
426                         close(wfd[0]);
427                         close(rfd[1]);
428                         exit(0);
429
430                         break;
431
432                 /* parent; handle I/O relaying */
433                 default:
434                         /* close unneeded pipe ends */
435                         close(rfd[1]);
436                         close(wfd[0]);
437
438                         /* max watch fd */
439                         fd_max = max(rfd[0], wfd[1]) + 1;
440
441                         /* find content length */
442                         if( req->method == UH_HTTP_MSG_POST )
443                         {
444                                 foreach_header(i, req->headers)
445                                 {
446                                         if( ! strcasecmp(req->headers[i], "Content-Length") )
447                                         {
448                                                 content_length = atoi(req->headers[i+1]);
449                                                 break;
450                                         }
451                                 }
452                         }
453
454
455 #define ensure(x) \
456         do { if( x < 0 ) goto out; } while(0)
457
458                         data_sent = 0;
459
460                         timeout.tv_sec = cl->server->conf->script_timeout;
461                         timeout.tv_usec = 0;
462
463                         /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
464                         while( 1 )
465                         {
466                                 FD_ZERO(&reader);
467                                 FD_ZERO(&writer);
468
469                                 FD_SET(rfd[0], &reader);
470                                 FD_SET(wfd[1], &writer);
471
472                                 /* wait until we can read or write or both */
473                                 if( select_intr(fd_max, &reader,
474                                     (content_length > -1) ? &writer : NULL, NULL,
475                                         (data_sent < 1) ? &timeout : NULL) > 0
476                                 ) {
477                                         /* ready to write to Lua child */
478                                         if( FD_ISSET(wfd[1], &writer) )
479                                         {
480                                                 /* there is unread post data waiting */
481                                                 if( content_length > 0 )
482                                                 {
483                                                         /* read it from socket ... */
484                                                         if( (buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0 )
485                                                         {
486                                                                 /* ... and write it to child's stdin */
487                                                                 if( write(wfd[1], buf, buflen) < 0 )
488                                                                         perror("write()");
489
490                                                                 content_length -= buflen;
491                                                         }
492
493                                                         /* unexpected eof! */
494                                                         else
495                                                         {
496                                                                 if( write(wfd[1], "", 0) < 0 )
497                                                                         perror("write()");
498
499                                                                 content_length = 0;
500                                                         }
501                                                 }
502
503                                                 /* there is no more post data, close pipe to child's stdin */
504                                                 else if( content_length > -1 )
505                                                 {
506                                                         close(wfd[1]);
507                                                         content_length = -1;
508                                                 }
509                                         }
510
511                                         /* ready to read from Lua child */
512                                         if( FD_ISSET(rfd[0], &reader) )
513                                         {
514                                                 /* read data from child ... */
515                                                 if( (buflen = read(rfd[0], buf, sizeof(buf))) > 0 )
516                                                 {
517                                                         /* pass through buffer to socket */
518                                                         ensure(uh_tcp_send(cl, buf, buflen));
519                                                         data_sent = 1;
520                                                 }
521
522                                                 /* looks like eof from child */
523                                                 else
524                                                 {
525                                                         /* error? */
526                                                         if( ! data_sent )
527                                                                 uh_http_sendhf(cl, 500, "Internal Server Error",
528                                                                         "The Lua child did not produce any response");
529
530                                                         break;
531                                                 }
532                                         }
533                                 }
534
535                                 /* timeout exceeded or interrupted by SIGCHLD */
536                                 else
537                                 {
538                                         if( (errno != EINTR) && ! data_sent )
539                                         {
540                                                 ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
541                                                         "The Lua script took too long to produce "
542                                                         "a response"));
543                                         }
544
545                                         break;
546                                 }
547                         }
548
549                 out:
550                         close(rfd[0]);
551                         close(wfd[1]);
552
553                         if( !kill(child, 0) )
554                         {
555                                 kill(child, SIGTERM);
556                                 waitpid(child, NULL, 0);
557                         }
558
559                         break;
560         }
561 }
562
563 void uh_lua_close(lua_State *L)
564 {
565         lua_close(L);
566 }