diff options
Diffstat (limited to 'package/uhttpd/src/uhttpd-lua.c')
-rw-r--r-- | package/uhttpd/src/uhttpd-lua.c | 579 |
1 files changed, 0 insertions, 579 deletions
diff --git a/package/uhttpd/src/uhttpd-lua.c b/package/uhttpd/src/uhttpd-lua.c deleted file mode 100644 index e113937399..0000000000 --- a/package/uhttpd/src/uhttpd-lua.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * uhttpd - Tiny single-threaded httpd - Lua handler - * - * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "uhttpd.h" -#include "uhttpd-utils.h" -#include "uhttpd-lua.h" - - -static int uh_lua_recv(lua_State *L) -{ - size_t length; - - char buffer[UH_LIMIT_MSGHEAD]; - - int to = 1; - int fd = fileno(stdin); - int rlen = 0; - - length = luaL_checknumber(L, 1); - - if ((length > 0) && (length <= sizeof(buffer))) - { - /* receive data */ - rlen = uh_raw_recv(fd, buffer, length, to); - - /* data read */ - if (rlen > 0) - { - lua_pushnumber(L, rlen); - lua_pushlstring(L, buffer, rlen); - return 2; - } - - /* eof */ - else if (rlen == 0) - { - lua_pushnumber(L, 0); - return 1; - } - - /* no, timeout and actually no data */ - else - { - lua_pushnumber(L, -1); - return 1; - } - } - - /* parameter error */ - lua_pushnumber(L, -2); - return 1; -} - -static int uh_lua_send_common(lua_State *L, bool chunked) -{ - size_t length; - - char chunk[16]; - const char *buffer; - - int rv; - int to = 1; - int fd = fileno(stdout); - int slen = 0; - - buffer = luaL_checklstring(L, 1, &length); - - if (chunked) - { - if (length > 0) - { - snprintf(chunk, sizeof(chunk), "%X\r\n", length); - - ensure_out(rv = uh_raw_send(fd, chunk, strlen(chunk), to)); - slen += rv; - - ensure_out(rv = uh_raw_send(fd, buffer, length, to)); - slen += rv; - - ensure_out(rv = uh_raw_send(fd, "\r\n", 2, to)); - slen += rv; - } - else - { - slen = uh_raw_send(fd, "0\r\n\r\n", 5, to); - } - } - else - { - slen = uh_raw_send(fd, buffer, length, to); - } - -out: - lua_pushnumber(L, slen); - return 1; -} - -static int uh_lua_send(lua_State *L) -{ - return uh_lua_send_common(L, false); -} - -static int uh_lua_sendc(lua_State *L) -{ - return uh_lua_send_common(L, true); -} - -static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const char *, int)) -{ - size_t inlen; - int outlen; - const char *inbuf; - char outbuf[UH_LIMIT_MSGHEAD]; - - inbuf = luaL_checklstring(L, 1, &inlen); - outlen = (* xlate_func)(outbuf, sizeof(outbuf), inbuf, inlen); - if (outlen < 0) - luaL_error(L, "%s on URL-encode codec", - (outlen==-1) ? "buffer overflow" : "malformed string"); - - lua_pushlstring(L, outbuf, outlen); - return 1; -} - -static int uh_lua_urldecode(lua_State *L) -{ - return uh_lua_str2str( L, uh_urldecode ); -} - - -static int uh_lua_urlencode(lua_State *L) -{ - return uh_lua_str2str( L, uh_urlencode ); -} - - -lua_State * uh_lua_init(const struct config *conf) -{ - lua_State *L = lua_open(); - const char *err_str = NULL; - - /* Load standard libaries */ - luaL_openlibs(L); - - /* build uhttpd api table */ - lua_newtable(L); - - /* register global send and receive functions */ - lua_pushcfunction(L, uh_lua_recv); - lua_setfield(L, -2, "recv"); - - lua_pushcfunction(L, uh_lua_send); - lua_setfield(L, -2, "send"); - - lua_pushcfunction(L, uh_lua_sendc); - lua_setfield(L, -2, "sendc"); - - lua_pushcfunction(L, uh_lua_urldecode); - lua_setfield(L, -2, "urldecode"); - - lua_pushcfunction(L, uh_lua_urlencode); - lua_setfield(L, -2, "urlencode"); - - /* Pass the document-root to the Lua handler by placing it in - ** uhttpd.docroot. It could alternatively be placed in env.DOCUMENT_ROOT - ** which would more closely resemble the CGI protocol; but would mean that - ** it is not available at the time when the handler-chunk is loaded but - ** rather not until the handler is called, without any code savings. */ - lua_pushstring(L, conf->docroot); - lua_setfield(L, -2, "docroot"); - - /* _G.uhttpd = { ... } */ - lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd"); - - - /* load Lua handler */ - switch (luaL_loadfile(L, conf->lua_handler)) - { - case LUA_ERRSYNTAX: - fprintf(stderr, - "Lua handler contains syntax errors, unable to continue\n"); - exit(1); - - case LUA_ERRMEM: - fprintf(stderr, - "Lua handler ran out of memory, unable to continue\n"); - exit(1); - - case LUA_ERRFILE: - fprintf(stderr, - "Lua cannot open the handler script, unable to continue\n"); - exit(1); - - default: - /* compile Lua handler */ - switch (lua_pcall(L, 0, 0, 0)) - { - case LUA_ERRRUN: - err_str = luaL_checkstring(L, -1); - fprintf(stderr, - "Lua handler had runtime error, " - "unable to continue\n" - "Error: %s\n", err_str); - exit(1); - - case LUA_ERRMEM: - err_str = luaL_checkstring(L, -1); - fprintf(stderr, - "Lua handler ran out of memory, " - "unable to continue\n" - "Error: %s\n", err_str); - exit(1); - - default: - /* test handler function */ - lua_getglobal(L, UH_LUA_CALLBACK); - - if (! lua_isfunction(L, -1)) - { - fprintf(stderr, - "Lua handler provides no "UH_LUA_CALLBACK"(), " - "unable to continue\n"); - exit(1); - } - - lua_pop(L, 1); - break; - } - - break; - } - - return L; -} - -static void uh_lua_shutdown(struct uh_lua_state *state) -{ - free(state); -} - -static bool uh_lua_socket_cb(struct client *cl) -{ - int len; - char buf[UH_LIMIT_MSGHEAD]; - - struct uh_lua_state *state = (struct uh_lua_state *)cl->priv; - - /* there is unread post data waiting */ - while (state->content_length > 0) - { - /* remaining data in http head buffer ... */ - if (cl->httpbuf.len > 0) - { - len = min(state->content_length, cl->httpbuf.len); - - D("Lua: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len); - - memcpy(buf, cl->httpbuf.ptr, len); - - cl->httpbuf.len -= len; - cl->httpbuf.ptr += len; - } - - /* read it from socket ... */ - else - { - len = uh_tcp_recv(cl, buf, min(state->content_length, sizeof(buf))); - - if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - break; - - D("Lua: Child(%d) feed %d/%d TCP socket bytes\n", - cl->proc.pid, len, min(state->content_length, sizeof(buf))); - } - - if (len) - state->content_length -= len; - else - state->content_length = 0; - - /* ... write to Lua process */ - len = uh_raw_send(cl->wpipe.fd, buf, len, - cl->server->conf->script_timeout); - - /* explicit EOF notification for the child */ - if (state->content_length <= 0) - uh_ufd_remove(&cl->wpipe); - } - - /* try to read data from child */ - while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0) - { - /* pass through buffer to socket */ - D("Lua: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len); - ensure_out(uh_tcp_send(cl, buf, len)); - state->data_sent = true; - } - - /* got EOF or read error from child */ - if ((len == 0) || - ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1))) - { - D("Lua: Child(%d) presumed dead [%s]\n", - cl->proc.pid, strerror(errno)); - - goto out; - } - - return true; - -out: - if (!state->data_sent) - { - if (cl->timeout.pending) - uh_http_sendhf(cl, 502, "Bad Gateway", - "The Lua process did not produce any response\n"); - else - uh_http_sendhf(cl, 504, "Gateway Timeout", - "The Lua process took too long to produce a " - "response\n"); - } - - uh_lua_shutdown(state); - return false; -} - -bool uh_lua_request(struct client *cl, lua_State *L) -{ - int i; - char *query_string; - const char *prefix = cl->server->conf->lua_prefix; - const char *err_str = NULL; - - int rfd[2] = { 0, 0 }; - int wfd[2] = { 0, 0 }; - - pid_t child; - - struct uh_lua_state *state; - struct http_request *req = &cl->request; - - int content_length = cl->httpbuf.len; - - - /* allocate state */ - if (!(state = malloc(sizeof(*state)))) - { - uh_client_error(cl, 500, "Internal Server Error", "Out of memory"); - return false; - } - - /* spawn pipes for me->child, child->me */ - if ((pipe(rfd) < 0) || (pipe(wfd) < 0)) - { - if (rfd[0] > 0) close(rfd[0]); - if (rfd[1] > 0) close(rfd[1]); - if (wfd[0] > 0) close(wfd[0]); - if (wfd[1] > 0) close(wfd[1]); - - uh_client_error(cl, 500, "Internal Server Error", - "Failed to create pipe: %s", strerror(errno)); - - return false; - } - - - switch ((child = fork())) - { - case -1: - uh_client_error(cl, 500, "Internal Server Error", - "Failed to fork child: %s", strerror(errno)); - - return false; - - case 0: -#ifdef DEBUG - sleep(atoi(getenv("UHTTPD_SLEEP_ON_FORK") ?: "0")); -#endif - - /* do not leak parent epoll descriptor */ - uloop_done(); - - /* close loose pipe ends */ - close(rfd[0]); - close(wfd[1]); - - /* patch stdout and stdin to pipes */ - dup2(rfd[1], 1); - dup2(wfd[0], 0); - - /* avoid leaking our pipe into child-child processes */ - fd_cloexec(rfd[1]); - fd_cloexec(wfd[0]); - - /* put handler callback on stack */ - lua_getglobal(L, UH_LUA_CALLBACK); - - /* build env table */ - lua_newtable(L); - - /* request method */ - lua_pushstring(L, http_methods[req->method]); - lua_setfield(L, -2, "REQUEST_METHOD"); - - /* request url */ - lua_pushstring(L, req->url); - lua_setfield(L, -2, "REQUEST_URI"); - - /* script name */ - lua_pushstring(L, cl->server->conf->lua_prefix); - lua_setfield(L, -2, "SCRIPT_NAME"); - - /* query string, path info */ - if ((query_string = strchr(req->url, '?')) != NULL) - { - lua_pushstring(L, query_string + 1); - lua_setfield(L, -2, "QUERY_STRING"); - - if ((int)(query_string - req->url) > strlen(prefix)) - { - lua_pushlstring(L, - &req->url[strlen(prefix)], - (int)(query_string - req->url) - strlen(prefix) - ); - - lua_setfield(L, -2, "PATH_INFO"); - } - } - else if (strlen(req->url) > strlen(prefix)) - { - lua_pushstring(L, &req->url[strlen(prefix)]); - lua_setfield(L, -2, "PATH_INFO"); - } - - /* http protcol version */ - lua_pushnumber(L, 0.9 + (req->version / 10.0)); - lua_setfield(L, -2, "HTTP_VERSION"); - - lua_pushstring(L, http_versions[req->version]); - lua_setfield(L, -2, "SERVER_PROTOCOL"); - - - /* address information */ - lua_pushstring(L, sa_straddr(&cl->peeraddr)); - lua_setfield(L, -2, "REMOTE_ADDR"); - - lua_pushinteger(L, sa_port(&cl->peeraddr)); - lua_setfield(L, -2, "REMOTE_PORT"); - - lua_pushstring(L, sa_straddr(&cl->servaddr)); - lua_setfield(L, -2, "SERVER_ADDR"); - - lua_pushinteger(L, sa_port(&cl->servaddr)); - lua_setfield(L, -2, "SERVER_PORT"); - - /* essential env vars */ - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Content-Length")) - { - content_length = atoi(req->headers[i+1]); - } - else if (!strcasecmp(req->headers[i], "Content-Type")) - { - lua_pushstring(L, req->headers[i+1]); - lua_setfield(L, -2, "CONTENT_TYPE"); - } - } - - lua_pushnumber(L, content_length); - lua_setfield(L, -2, "CONTENT_LENGTH"); - - /* misc. headers */ - lua_newtable(L); - - foreach_header(i, req->headers) - { - if( strcasecmp(req->headers[i], "Content-Length") && - strcasecmp(req->headers[i], "Content-Type")) - { - lua_pushstring(L, req->headers[i+1]); - lua_setfield(L, -2, req->headers[i]); - } - } - - lua_setfield(L, -2, "headers"); - - - /* call */ - switch (lua_pcall(L, 1, 0, 0)) - { - case LUA_ERRMEM: - case LUA_ERRRUN: - err_str = luaL_checkstring(L, -1); - - if (! err_str) - err_str = "Unknown error"; - - printf("%s 500 Internal Server Error\r\n" - "Connection: close\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %i\r\n\r\n" - "Lua raised a runtime error:\n %s\n", - http_versions[req->version], - 31 + strlen(err_str), err_str); - - break; - - default: - break; - } - - close(wfd[0]); - close(rfd[1]); - exit(0); - - break; - - /* parent; handle I/O relaying */ - default: - memset(state, 0, sizeof(*state)); - - cl->rpipe.fd = rfd[0]; - cl->wpipe.fd = wfd[1]; - cl->proc.pid = child; - - /* make pipe non-blocking */ - fd_nonblock(cl->rpipe.fd); - fd_nonblock(cl->wpipe.fd); - - /* close unneeded pipe ends */ - close(rfd[1]); - close(wfd[0]); - - D("Lua: Child(%d) created: rfd(%d) wfd(%d)\n", child, rfd[0], wfd[1]); - - state->content_length = cl->httpbuf.len; - - /* find content length */ - if (req->method == UH_HTTP_MSG_POST) - { - foreach_header(i, req->headers) - { - if (!strcasecmp(req->headers[i], "Content-Length")) - { - state->content_length = atoi(req->headers[i+1]); - break; - } - } - } - - cl->cb = uh_lua_socket_cb; - cl->priv = state; - - break; - } - - return true; -} - -void uh_lua_close(lua_State *L) -{ - lua_close(L); -} |