[LEDE-DEV] uhttpd embedded lua read() always returns entirety of STDIN

Bryan Mayland bmayland+lede at capnbry.net
Wed Jan 24 07:52:09 PST 2018


I use uhttpd with the embedded lua engine and uploading files with
standard POST / multipart/form-data / type=file has never been speedy.
However, I've reached a point that the file upload completes but
uhttpd times out (in 60 seconds) before it can transfer the file to
the lua script that is using the luci.http.setfilehandler() method. To
be clear, uhttpd times out before the first call to the filehander
function-- it times out in the CGI boundary between the server and
sgi/uhttp.lua.

I tracked this down to uhttpd/lua.c:uh_lua_recv(). A single
uhttpd.read(4096) from lua reads *the entirety of stdin* up until it
is exhausted instead of the requested size. Note that the underlying
reads are LUAL_BUFFERSIZE in size, which is 1024 bytes. With a large
file upload (or any upload really), the entire content will be
contained in a single lua string which is contatenated together 1KB at
a time, and contatentated together in 10x gathered chunks off the lua
stack in the luaL_buffer implementation.

Example, bcm2708 target, 8876KB upload:
-- 18 seconds between browser data transfer complete and CGI boundary
crossing into user lua code
-- 89MB RAM usage in uhttpd process as the giant string is built

Example, bcm2708 target, 28992KB upload:
-- 68 seconds between browser data transfer complete and CGI boundary
crossing into user lua code
-- 320MB RAM usage in uhttpd process as the giant string is built

The solution is pretty simple, to only return as much data as is
requested by the lua method uhttpd.read():
--- uhttpd-2017-08-19-3fd58e9b/lua.c.orig       2018-01-24
10:24:34.288327793 -0500
+++ uhttpd-2017-08-19-3fd58e9b/lua.c    2018-01-24 10:28:31.817731660 -0500
@@ -72,6 +72,7 @@

                luaL_addsize(&B, r);
                data_len += r;
+               len -= r;
                if (r != LUAL_BUFFERSIZE)
                        break;
        }

For my 8876KB example, this reduces the execution time from 18 seconds
to 1 second and the RAM usage from 89MB to 3.7MB. The 28992KB example
drops from 68 seconds to 4 seconds, and 320MB RAM to 3.7MB as well.
The size of the process working set is really just the size of the
running lua interpreter reusing the same 4K over and over again,
instead of trying to hold an entire 29MB file in a string,
concatenating it over and over. The sha256sum and md5sum of the
uploaded files are identical.



More information about the Lede-dev mailing list