Root/package/network/services/uhttpd/src/uhttpd-lua.c

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

Archive Download this file



interactive