kernel: fq_codel: dont reinit flow state
[openwrt/staging/mkresin.git] / package / uhttpd / src / uhttpd-lua.c
index a140dc2f2de36eb8a2e88d35e3bed86be184b72f..e113937399424cbbfaae3e41d3d4ad0dcf6bb126 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)) )
+       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)
+static int uh_lua_send_common(lua_State *L, bool 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);
 
-       if( chunked )
+       if (chunked)
        {
-               if( length > 0 )
+               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;
 }
 
 static int uh_lua_send(lua_State *L)
 {
-       return uh_lua_send_common(L, 0);
+       return uh_lua_send_common(L, false);
 }
 
 static int uh_lua_sendc(lua_State *L)
 {
-       return uh_lua_send_common(L, 1);
+       return uh_lua_send_common(L, true);
 }
 
 static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const char *, int))
@@ -117,9 +129,9 @@ 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" );
+       if (outlen < 0)
+               luaL_error(L, "%s on URL-encode codec",
+                                  (outlen==-1) ? "buffer overflow" : "malformed string");
 
        lua_pushlstring(L, outbuf, outlen);
        return 1;
@@ -137,7 +149,7 @@ static int uh_lua_urlencode(lua_State *L)
 }
 
 
-lua_State * uh_lua_init(const char *handler)
+lua_State * uh_lua_init(const struct config *conf)
 {
        lua_State *L = lua_open();
        const char *err_str = NULL;
@@ -164,56 +176,65 @@ lua_State * uh_lua_init(const char *handler)
        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, handler) )
+       switch (luaL_loadfile(L, conf->lua_handler))
        {
                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:
                        /* compile Lua handler */
-                       switch( lua_pcall(L, 0, 0, 0) )
+                       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
-                                       );
+                                                       "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:
                                        /* test handler function */
                                        lua_getglobal(L, UH_LUA_CALLBACK);
 
-                                       if( ! lua_isfunction(L, -1) )
+                                       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);
                                        }
 
@@ -227,12 +248,100 @@ lua_State * uh_lua_init(const char *handler)
        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)
+{
+       free(state);
+}
+
+static bool uh_lua_socket_cb(struct client *cl)
 {
-       int i, data_sent;
-       int content_length = 0;
-       int buflen = 0;
-       int fd_max = 0;
+       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;
@@ -240,324 +349,228 @@ 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) )
+       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]);
 
-               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;
+               return false;
        }
 
 
-       switch( (child = fork()) )
+       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);
+               /* do not leak parent epoll descriptor */
+               uloop_done();
 
-                       /* put handler callback on stack */
-                       lua_getglobal(L, UH_LUA_CALLBACK);
+               /* close loose pipe ends */
+               close(rfd[0]);
+               close(wfd[1]);
 
-                       /* build env table */
-                       lua_newtable(L);
+               /* patch stdout and stdin to pipes */
+               dup2(rfd[1], 1);
+               dup2(wfd[0], 0);
 
-                       /* request method */
-                       switch(req->method)
-                       {
-                               case UH_HTTP_MSG_GET:
-                                       lua_pushstring(L, "GET");
-                                       break;
+               /* avoid leaking our pipe into child-child processes */
+               fd_cloexec(rfd[1]);
+               fd_cloexec(wfd[0]);
 
-                               case UH_HTTP_MSG_HEAD:
-                                       lua_pushstring(L, "HEAD");
-                                       break;
+               /* put handler callback on stack */
+               lua_getglobal(L, UH_LUA_CALLBACK);
 
-                               case UH_HTTP_MSG_POST:
-                                       lua_pushstring(L, "POST");
-                                       break;
-                       }
+               /* build env table */
+               lua_newtable(L);
 
-                       lua_setfield(L, -2, "REQUEST_METHOD");
+               /* 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");
+               /* 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");
+               /* 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)
-                                       );
+               /* query string, path info */
+               if ((query_string = strchr(req->url, '?')) != NULL)
+               {
+                       lua_pushstring(L, query_string + 1);
+                       lua_setfield(L, -2, "QUERY_STRING");
 
-                                       lua_setfield(L, -2, "PATH_INFO");
-                               }
-                       }
-                       else if( strlen(req->url) > strlen(prefix) )
+                       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");
-
-                       if( req->version > 1.0 )
-                               lua_pushstring(L, "HTTP/1.1");
-                       else
-                               lua_pushstring(L, "HTTP/1.0");
+               /* http protcol version */
+               lua_pushnumber(L, 0.9 + (req->version / 10.0));
+               lua_setfield(L, -2, "HTTP_VERSION");
 
-                       lua_setfield(L, -2, "SERVER_PROTOCOL");
+               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");
+               /* 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);
+                               lua_pushstring(L, req->headers[i+1]);
+                               lua_setfield(L, -2, req->headers[i]);
+                       }
+               }
 
-                                       if( ! err_str )
-                                               err_str = "Unknown error";
+               lua_setfield(L, -2, "headers");
 
-                                       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;
+               /* call */
+               switch (lua_pcall(L, 1, 0, 0))
+               {
+                       case LUA_ERRMEM:
+                       case LUA_ERRRUN:
+                               err_str = luaL_checkstring(L, -1);
 
-                               default:
-                                       break;
-                       }
+                               if (! err_str)
+                                       err_str = "Unknown error";
 
-                       close(wfd[0]);
-                       close(rfd[1]);
-                       exit(0);
+                               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;
+                               break;
 
-               /* parent; handle I/O relaying */
-               default:
-                       /* close unneeded pipe ends */
-                       close(rfd[1]);
-                       close(wfd[0]);
+                       default:
+                               break;
+               }
 
-                       /* max watch fd */
-                       fd_max = max(rfd[0], wfd[1]) + 1;
+               close(wfd[0]);
+               close(rfd[1]);
+               exit(0);
 
-                       /* 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;
-                                       }
-                               }
-                       }
+               break;
 
+       /* parent; handle I/O relaying */
+       default:
+               memset(state, 0, sizeof(*state));
 
-#define ensure(x) \
-       do { if( x < 0 ) goto out; } while(0)
+               cl->rpipe.fd = rfd[0];
+               cl->wpipe.fd = wfd[1];
+               cl->proc.pid = child;
 
-                       data_sent = 0;
+               /* make pipe non-blocking */
+               fd_nonblock(cl->rpipe.fd);
+               fd_nonblock(cl->wpipe.fd);
 
-                       timeout.tv_sec = cl->server->conf->script_timeout;
-                       timeout.tv_usec = 0;
+               /* close unneeded pipe ends */
+               close(rfd[1]);
+               close(wfd[0]);
 
-                       /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
-                       while( 1 )
-                       {
-                               FD_ZERO(&reader);
-                               FD_ZERO(&writer);
-
-                               FD_SET(rfd[0], &reader);
-                               FD_SET(wfd[1], &writer);
-
-                               /* 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;
-                                               }
-                                       }
+               D("Lua: Child(%d) created: rfd(%d) wfd(%d)\n", child, rfd[0], wfd[1]);
 
-                                       /* 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;
-                                               }
-                                       }
-                               }
+               state->content_length = cl->httpbuf.len;
 
-                               /* timeout exceeded or interrupted by SIGCHLD */
-                               else
+               /* find content length */
+               if (req->method == UH_HTTP_MSG_POST)
+               {
+                       foreach_header(i, req->headers)
+                       {
+                               if (!strcasecmp(req->headers[i], "Content-Length"))
                                {
-                                       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 = atoi(req->headers[i+1]);
                                        break;
                                }
                        }
+               }
 
-               out:
-                       close(rfd[0]);
-                       close(wfd[1]);
-
-                       if( !kill(child, 0) )
-                       {
-                               kill(child, SIGTERM);
-                               waitpid(child, NULL, 0);
-                       }
+               cl->cb = uh_lua_socket_cb;
+               cl->priv = state;
 
-                       break;
+               break;
        }
+
+       return true;
 }
 
 void uh_lua_close(lua_State *L)