[package] uhttpd:
[openwrt/svn-archive/archive.git] / package / uhttpd / src / uhttpd-lua.c
index ea6f26cc9bbf50ed3c0cce4d22cff0041f11e670..7d602f7c585efce0ca6b99e8619003512368894e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * uhttpd - Tiny single-threaded httpd - Lua handler
  *
- *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
+ *   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.
 static int uh_lua_recv(lua_State *L)
 {
        size_t length;
+
        char buffer[UH_LIMIT_MSGHEAD];
-       ssize_t rlen = 0;
-       fd_set reader;
-       struct timeval timeout;
+
+       int to = 1;
+       int fd = fileno(stdin);
+       int rlen = 0;
 
        length = luaL_checknumber(L, 1);
 
        if ((length > 0) && (length <= sizeof(buffer)))
        {
-               FD_ZERO(&reader);
-               FD_SET(fileno(stdin), &reader);
-
-               /* fail after 0.1s */
-               timeout.tv_sec  = 0;
-               timeout.tv_usec = 100000;
+               /* receive data */
+               rlen = uh_raw_recv(fd, buffer, length, to);
 
-               /* check whether fd is readable */
-               if (select(fileno(stdin) + 1, &reader, NULL, NULL, &timeout) > 0)
+               /* data read */
+               if (rlen > 0)
                {
-                       /* receive data */
-                       rlen = read(fileno(stdin), buffer, length);
                        lua_pushnumber(L, rlen);
+                       lua_pushlstring(L, buffer, rlen);
+                       return 2;
+               }
 
-                       if (rlen > 0)
-                       {
-                               lua_pushlstring(L, buffer, rlen);
-                               return 2;
-                       }
-
+               /* eof */
+               else if (rlen == 0)
+               {
+                       lua_pushnumber(L, 0);
                        return 1;
                }
 
                /* no, timeout and actually no data */
-               lua_pushnumber(L, -2);
-               return 1;
+               else
+               {
+                       lua_pushnumber(L, -1);
+                       return 1;
+               }
        }
 
        /* parameter error */
-       lua_pushnumber(L, -3);
+       lua_pushnumber(L, -2);
        return 1;
 }
 
 static int uh_lua_send_common(lua_State *L, int chunked)
 {
        size_t length;
-       const char *buffer;
+
        char chunk[16];
-       ssize_t slen = 0;
+       const char *buffer;
+
+       int rv;
+       int to = 1;
+       int fd = fileno(stdout);
+       int slen = 0;
 
        buffer = luaL_checklstring(L, 1, &length);
 
@@ -80,20 +85,27 @@ static int uh_lua_send_common(lua_State *L, int chunked)
                if (length > 0)
                {
                        snprintf(chunk, sizeof(chunk), "%X\r\n", length);
-                       slen =  write(fileno(stdout), chunk, strlen(chunk));
-                       slen += write(fileno(stdout), buffer, length);
-                       slen += write(fileno(stdout), "\r\n", 2);
+
+                       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 = write(fileno(stdout), "0\r\n\r\n", 5);
+                       slen = uh_raw_send(fd, "0\r\n\r\n", 5, to);
                }
        }
        else
        {
-               slen = write(fileno(stdout), buffer, length);
+               slen = uh_raw_send(fd, buffer, length, to);
        }
 
+out:
        lua_pushnumber(L, slen);
        return 1;
 }
@@ -118,8 +130,8 @@ static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const ch
        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" );
+               luaL_error(L, "%s on URL-encode codec",
+                                  (outlen==-1) ? "buffer overflow" : "malformed string");
 
        lua_pushlstring(L, outbuf, outlen);
        return 1;
@@ -181,17 +193,17 @@ lua_State * uh_lua_init(const struct config *conf)
        {
                case LUA_ERRSYNTAX:
                        fprintf(stderr,
-                               "Lua handler contains syntax errors, unable to continue\n");
+                                       "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");
+                                       "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");
+                                       "Lua cannot open the handler script, unable to continue\n");
                        exit(1);
 
                default:
@@ -201,17 +213,17 @@ lua_State * uh_lua_init(const struct config *conf)
                                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
-                                       );
+                                                       "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
-                                       );
+                                                       "Lua handler ran out of memory, "
+                                                       "unable to continue\n"
+                                                       "Error: %s\n", err_str);
                                        exit(1);
 
                                default:
@@ -221,7 +233,8 @@ lua_State * uh_lua_init(const struct config *conf)
                                        if (! lua_isfunction(L, -1))
                                        {
                                                fprintf(stderr,
-                                                       "Lua handler provides no " UH_LUA_CALLBACK "(), unable to continue\n");
+                                                               "Lua handler provides no "UH_LUA_CALLBACK"(), "
+                                                               "unable to continue\n");
                                                exit(1);
                                        }
 
@@ -235,12 +248,107 @@ lua_State * uh_lua_init(const struct config *conf)
        return L;
 }
 
-void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
+static void uh_lua_shutdown(struct uh_lua_state *state)
 {
-       int i, data_sent;
-       int content_length = 0;
-       int buflen = 0;
-       int fd_max = 0;
+       close(state->rfd);
+       close(state->wfd);
+       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 (state->cl->httpbuf.len > 0)
+               {
+                       len = min(state->content_length, state->cl->httpbuf.len);
+
+                       D("Lua: Child(%d) feed %d HTTP buffer bytes\n",
+                         state->cl->proc.pid, len);
+
+                       memcpy(buf, state->cl->httpbuf.ptr, len);
+
+                       state->cl->httpbuf.len -= len;
+                       state->cl->httpbuf.ptr += len;
+               }
+
+               /* read it from socket ... */
+               else
+               {
+                       len = uh_tcp_recv(state->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",
+                         state->cl->proc.pid, len,
+                         min(state->content_length, sizeof(buf)));
+               }
+
+               if (len)
+                       state->content_length -= len;
+               else
+                       state->content_length = 0;
+
+               /* ... write to CGI process */
+               len = uh_raw_send(state->wfd, buf, len,
+                                                 cl->server->conf->script_timeout);
+       }
+
+       /* try to read data from child */
+       while ((len = uh_raw_recv(state->rfd, buf, sizeof(buf), -1)) > 0)
+       {
+               /* pass through buffer to socket */
+               D("Lua: Child(%d) relaying %d normal bytes\n", state->cl->proc.pid, len);
+               ensure_out(uh_tcp_send(state->cl, buf, len));
+               state->data_sent = true;
+       }
+
+       /* child has been marked dead by timeout or child handler, bail out */
+       if (false && cl->dead)
+       {
+               D("Lua: Child(%d) is marked dead, returning\n", state->cl->proc.pid);
+               goto out;
+       }
+
+       if ((len == 0) ||
+               ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1)))
+       {
+               D("Lua: Child(%d) presumed dead [%s]\n",
+                 state->cl->proc.pid, strerror(errno));
+
+               goto out;
+       }
+
+       return true;
+
+out:
+       if (!state->data_sent)
+       {
+               if (state->cl->timeout.pending)
+                       uh_http_sendhf(state->cl, 502, "Bad Gateway",
+                                                  "The Lua process did not produce any response\n");
+               else
+                       uh_http_sendhf(state->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;
@@ -248,325 +356,243 @@ void uh_lua_request(struct client *cl, struct http_request *req, lua_State *L)
        int rfd[2] = { 0, 0 };
        int wfd[2] = { 0, 0 };
 
-       char buf[UH_LIMIT_MSGHEAD];
-
        pid_t child;
 
-       fd_set reader;
-       fd_set writer;
+       struct uh_lua_state *state;
+       struct http_request *req = &cl->request;
 
-       struct sigaction sa;
-       struct timeval timeout;
+       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))
        {
-               uh_http_sendhf(cl, 500, "Internal Server Error",
-                       "Failed to create pipe: %s", strerror(errno));
-
                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]);
 
-               return;
+               uh_client_error(cl, 500, "Internal Server Error",
+                                               "Failed to create pipe: %s", strerror(errno));
+
+               return false;
        }
 
 
        switch ((child = fork()))
        {
-               case -1:
-                       uh_http_sendhf(cl, 500, "Internal Server Error",
-                               "Failed to fork child: %s", strerror(errno));
-                       break;
+       case -1:
+               uh_client_error(cl, 500, "Internal Server Error",
+                                               "Failed to fork child: %s", strerror(errno));
 
-               case 0:
-                       /* restore SIGTERM */
-                       sa.sa_flags = 0;
-                       sa.sa_handler = SIG_DFL;
-                       sigemptyset(&sa.sa_mask);
-                       sigaction(SIGTERM, &sa, NULL);
+               return false;
 
-                       /* close loose pipe ends */
-                       close(rfd[0]);
-                       close(wfd[1]);
+       case 0:
+#ifdef DEBUG
+               sleep(atoi(getenv("UHTTPD_SLEEP_ON_FORK") ?: "0"));
+#endif
 
-                       /* patch stdout and stdin to pipes */
-                       dup2(rfd[1], 1);
-                       dup2(wfd[0], 0);
+               /* close loose pipe ends */
+               close(rfd[0]);
+               close(wfd[1]);
 
-                       /* put handler callback on stack */
-                       lua_getglobal(L, UH_LUA_CALLBACK);
+               /* patch stdout and stdin to pipes */
+               dup2(rfd[1], 1);
+               dup2(wfd[0], 0);
 
-                       /* build env table */
-                       lua_newtable(L);
+               /* avoid leaking our pipe into child-child processes */
+               fd_cloexec(rfd[1]);
+               fd_cloexec(wfd[0]);
 
-                       /* request method */
-                       switch(req->method)
-                       {
-                               case UH_HTTP_MSG_GET:
-                                       lua_pushstring(L, "GET");
-                                       break;
+               /* put handler callback on stack */
+               lua_getglobal(L, UH_LUA_CALLBACK);
 
-                               case UH_HTTP_MSG_HEAD:
-                                       lua_pushstring(L, "HEAD");
-                                       break;
+               /* build env table */
+               lua_newtable(L);
 
-                               case UH_HTTP_MSG_POST:
-                                       lua_pushstring(L, "POST");
-                                       break;
-                       }
+               /* request method */
+               switch(req->method)
+               {
+                       case UH_HTTP_MSG_GET:
+                               lua_pushstring(L, "GET");
+                               break;
 
-                       lua_setfield(L, -2, "REQUEST_METHOD");
+                       case UH_HTTP_MSG_HEAD:
+                               lua_pushstring(L, "HEAD");
+                               break;
 
-                       /* request url */
-                       lua_pushstring(L, req->url);
-                       lua_setfield(L, -2, "REQUEST_URI");
+                       case UH_HTTP_MSG_POST:
+                               lua_pushstring(L, "POST");
+                               break;
+               }
 
-                       /* script name */
-                       lua_pushstring(L, cl->server->conf->lua_prefix);
-                       lua_setfield(L, -2, "SCRIPT_NAME");
+               lua_setfield(L, -2, "REQUEST_METHOD");
 
-                       /* query string, path info */
-                       if ((query_string = strchr(req->url, '?')) != NULL)
-                       {
-                               lua_pushstring(L, query_string + 1);
-                               lua_setfield(L, -2, "QUERY_STRING");
+               /* request url */
+               lua_pushstring(L, req->url);
+               lua_setfield(L, -2, "REQUEST_URI");
 
-                               if ((int)(query_string - req->url) > strlen(prefix))
-                               {
-                                       lua_pushlstring(L,
-                                               &req->url[strlen(prefix)],
-                                               (int)(query_string - req->url) - strlen(prefix)
-                                       );
+               /* script name */
+               lua_pushstring(L, cl->server->conf->lua_prefix);
+               lua_setfield(L, -2, "SCRIPT_NAME");
 
-                                       lua_setfield(L, -2, "PATH_INFO");
-                               }
-                       }
-                       else if (strlen(req->url) > strlen(prefix))
+               /* 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_pushstring(L, &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, floor(req->version * 10) / 10);
-                       lua_setfield(L, -2, "HTTP_VERSION");
+               /* http protcol version */
+               lua_pushnumber(L, floor(req->version * 10) / 10);
+               lua_setfield(L, -2, "HTTP_VERSION");
 
-                       if (req->version > 1.0)
-                               lua_pushstring(L, "HTTP/1.1");
-                       else
-                               lua_pushstring(L, "HTTP/1.0");
+               if (req->version > 1.0)
+                       lua_pushstring(L, "HTTP/1.1");
+               else
+                       lua_pushstring(L, "HTTP/1.0");
 
-                       lua_setfield(L, -2, "SERVER_PROTOCOL");
+               lua_setfield(L, -2, "SERVER_PROTOCOL");
 
 
-                       /* address information */
-                       lua_pushstring(L, sa_straddr(&cl->peeraddr));
-                       lua_setfield(L, -2, "REMOTE_ADDR");
+               /* 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_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_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");
+               lua_pushinteger(L, sa_port(&cl->servaddr));
+               lua_setfield(L, -2, "SERVER_PORT");
 
-                       /* essential env vars */
-                       foreach_header(i, req->headers)
+               /* essential env vars */
+               foreach_header(i, req->headers)
+               {
+                       if (!strcasecmp(req->headers[i], "Content-Length"))
                        {
-                               if (!strcasecmp(req->headers[i], "Content-Length"))
-                               {
-                                       lua_pushnumber(L, atoi(req->headers[i+1]));
-                                       lua_setfield(L, -2, "CONTENT_LENGTH");
-                               }
-                               else if (!strcasecmp(req->headers[i], "Content-Type"))
-                               {
-                                       lua_pushstring(L, req->headers[i+1]);
-                                       lua_setfield(L, -2, "CONTENT_TYPE");
-                               }
+                               content_length = atoi(req->headers[i+1]);
                        }
-
-                       /* misc. headers */
-                       lua_newtable(L);
-
-                       foreach_header(i, req->headers)
+                       else if (!strcasecmp(req->headers[i], "Content-Type"))
                        {
-                               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_pushstring(L, req->headers[i+1]);
+                               lua_setfield(L, -2, "CONTENT_TYPE");
                        }
+               }
 
-                       lua_setfield(L, -2, "headers");
+               lua_pushnumber(L, content_length);
+               lua_setfield(L, -2, "CONTENT_LENGTH");
 
+               /* misc. headers */
+               lua_newtable(L);
 
-                       /* call */
-                       switch (lua_pcall(L, 1, 0, 0))
+               foreach_header(i, req->headers)
+               {
+                       if( strcasecmp(req->headers[i], "Content-Length") &&
+                               strcasecmp(req->headers[i], "Content-Type"))
                        {
-                               case LUA_ERRMEM:
-                               case LUA_ERRRUN:
-                                       err_str = luaL_checkstring(L, -1);
-
-                                       if (! err_str)
-                                               err_str = "Unknown error";
-
-                                       printf(
-                                               "HTTP/%.1f 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",
-                                                       req->version, 31 + strlen(err_str), err_str
-                                       );
-
-                                       break;
-
-                               default:
-                                       break;
+                               lua_pushstring(L, req->headers[i+1]);
+                               lua_setfield(L, -2, req->headers[i]);
                        }
+               }
 
-                       close(wfd[0]);
-                       close(rfd[1]);
-                       exit(0);
+               lua_setfield(L, -2, "headers");
 
-                       break;
 
-               /* parent; handle I/O relaying */
-               default:
-                       /* close unneeded pipe ends */
-                       close(rfd[1]);
-                       close(wfd[0]);
+               /* call */
+               switch (lua_pcall(L, 1, 0, 0))
+               {
+                       case LUA_ERRMEM:
+                       case LUA_ERRRUN:
+                               err_str = luaL_checkstring(L, -1);
 
-                       /* max watch fd */
-                       fd_max = max(rfd[0], wfd[1]) + 1;
+                               if (! err_str)
+                                       err_str = "Unknown error";
 
-                       /* find content length */
-                       if (req->method == UH_HTTP_MSG_POST)
-                       {
-                               foreach_header(i, req->headers)
-                               {
-                                       if (! strcasecmp(req->headers[i], "Content-Length"))
-                                       {
-                                               content_length = atoi(req->headers[i+1]);
-                                               break;
-                                       }
-                               }
-                       }
+                               printf("HTTP/%.1f 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",
+                                          req->version, 31 + strlen(err_str), err_str);
 
+                               break;
 
-#define ensure(x) \
-       do { if (x < 0) goto out; } while(0)
+                       default:
+                               break;
+               }
 
-                       data_sent = 0;
+               close(wfd[0]);
+               close(rfd[1]);
+               exit(0);
 
-                       timeout.tv_sec = cl->server->conf->script_timeout;
-                       timeout.tv_usec = 0;
+               break;
 
-                       /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
-                       while (1)
-                       {
-                               FD_ZERO(&reader);
-                               FD_ZERO(&writer);
+       /* parent; handle I/O relaying */
+       default:
+               memset(state, 0, sizeof(*state));
 
-                               FD_SET(rfd[0], &reader);
-                               FD_SET(wfd[1], &writer);
+               state->cl = cl;
+               state->cl->proc.pid = child;
 
-                               /* wait until we can read or write or both */
-                               if (select_intr(fd_max, &reader,
-                                                               (content_length > -1) ? &writer : NULL,
-                                                               NULL,
-                                                               (data_sent < 1) ? &timeout : NULL) > 0)
-                               {
-                                       /* ready to write to Lua child */
-                                       if (FD_ISSET(wfd[1], &writer))
-                                       {
-                                               /* there is unread post data waiting */
-                                               if (content_length > 0)
-                                               {
-                                                       /* read it from socket ... */
-                                                       if ((buflen = uh_tcp_recv(cl, buf, min(content_length, sizeof(buf)))) > 0)
-                                                       {
-                                                               /* ... and write it to child's stdin */
-                                                               if (write(wfd[1], buf, buflen) < 0)
-                                                                       perror("write()");
-
-                                                               content_length -= buflen;
-                                                       }
-
-                                                       /* unexpected eof! */
-                                                       else
-                                                       {
-                                                               if (write(wfd[1], "", 0) < 0)
-                                                                       perror("write()");
-
-                                                               content_length = 0;
-                                                       }
-                                               }
-
-                                               /* there is no more post data, close pipe to child's stdin */
-                                               else if (content_length > -1)
-                                               {
-                                                       close(wfd[1]);
-                                                       content_length = -1;
-                                               }
-                                       }
+               /* close unneeded pipe ends */
+               close(rfd[1]);
+               close(wfd[0]);
 
-                                       /* ready to read from Lua child */
-                                       if (FD_ISSET(rfd[0], &reader))
-                                       {
-                                               /* read data from child ... */
-                                               if ((buflen = read(rfd[0], buf, sizeof(buf))) > 0)
-                                               {
-                                                       /* pass through buffer to socket */
-                                                       ensure(uh_tcp_send(cl, buf, buflen));
-                                                       data_sent = 1;
-                                               }
-
-                                               /* looks like eof from child */
-                                               else
-                                               {
-                                                       /* error? */
-                                                       if (!data_sent)
-                                                               uh_http_sendhf(cl, 500, "Internal Server Error",
-                                                                       "The Lua child did not produce any response");
-
-                                                       break;
-                                               }
-                                       }
-                               }
+               D("Lua: Child(%d) created: rfd(%d) wfd(%d)\n", child, rfd[0], wfd[1]);
 
-                               /* timeout exceeded or interrupted by SIGCHLD */
-                               else
-                               {
-                                       if ((errno != EINTR) && ! data_sent)
-                                       {
-                                               ensure(uh_http_sendhf(cl, 504, "Gateway Timeout",
-                                                       "The Lua script took too long to produce "
-                                                       "a response"));
-                                       }
+               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;
                                }
                        }
+               }
 
-               out:
-                       close(rfd[0]);
-                       close(wfd[1]);
+               state->rfd = rfd[0];
+               fd_nonblock(state->rfd);
 
-                       if (!kill(child, 0))
-                       {
-                               kill(child, SIGTERM);
-                               waitpid(child, NULL, 0);
-                       }
+               state->wfd = wfd[1];
+               fd_nonblock(state->wfd);
 
-                       break;
+               cl->cb = uh_lua_socket_cb;
+               cl->priv = state;
+
+               break;
        }
+
+       return true;
 }
 
 void uh_lua_close(lua_State *L)