login

<     >

2023-07-16 09:57:52 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

only process request when it's complete

some clients will send requests in parts
e.g. one send() for header, another send() for payload
with this change, we check Content-Length before processing

diff --git a/lib/ludweb/http.lua b/lib/ludweb/http.lua
index a90dcc9..1e8d4b4 100644
--- a/lib/ludweb/http.lua
+++ b/lib/ludweb/http.lua
@@ -31,7 +31,7 @@ end
 local function parse_request(data)
     local req = {payload="", headers={}}
     local stage = "status"
-    for line in (data.."\n"):gmatch("(.-)\r?\n") do
+    for line, line_end in (data.."\n"):gmatch("(.-)(\r?\n)") do
         if stage == "status" then
             local target, protocol
             req.method, target, protocol = line:match("(%S*) (%S*) (%S*)")
@@ -46,7 +46,7 @@ local function parse_request(data)
                 req.headers[key:lower()] = value
             end
         else -- payload
-            req.payload = req.payload..line.."\n"
+            req.payload = req.payload..line..line_end
         end
     end
     if req.headers["content-type"] == "application/x-www-form-urlencoded" then
@@ -112,7 +112,22 @@ end
 local function new_http()
     local obj = setmetatable({}, HTTP)
     obj.tcp = tcp.new_tcp(1000, 200)
+    function obj.tcp:request_ready(datain)
+        if datain == "" then
+            return false
+        end
+        local req = parse_request(datain)
+        local length = req.headers["content-length"]
+        if length == nil then
+            return true
+        else
+            return #req.payload >= tonumber(length)
+        end
+    end
     function obj.tcp:process(datain)
+        if datain == "" then
+            return "", true
+        end
         local req = parse_request(datain)
         local dataout, status, reason, cookies = obj:process(req)
         if dataout == nil then

diff --git a/lib/ludweb/tcp.lua b/lib/ludweb/tcp.lua
index 7f48cfe..6d10675 100644
--- a/lib/ludweb/tcp.lua
+++ b/lib/ludweb/tcp.lua
@@ -117,6 +117,7 @@ function TCP:run()
     local addr_size = ffi.new("socklen_t[1]", ffi.sizeof(client_addr[0]))
     local buflen = 4096
     local buffer = ffi.new("char ["..buflen.."]")
+    local datain = {}
     local running = true
     while running do
         nfds = C.epoll_wait(efd, evs, curfds, -1)
@@ -133,18 +134,23 @@ function TCP:run()
                 assert(C.epoll_ctl(efd, C.EPOLL_CTL_ADD, newfd, ev) >= 0, "epoll_ctl error")
                 curfds = curfds + 1
             else
-                C.recv(evs[n].data.fd, buffer, buflen, 0)
-                local data, keep_alive = self:process(ffi.string(buffer))
-                if data == nil then
-                    running = false
-                else
-                    C.send(evs[n].data.fd, data, #data, 0)
-                end
-                if not keep_alive then
-                    C.epoll_ctl(efd, C.EPOLL_CTL_DEL, evs[n].data.fd, ev)
-                    C.shutdown(evs[n].data.fd, C.SHUT_RDWR)
-                    curfds = curfds - 1
-                    C.close(evs[n].data.fd)
+                local size = C.recv(evs[n].data.fd, buffer, buflen, 0)
+                buffer[size] = 0
+                datain[n] = (datain[n] or "") .. ffi.string(buffer)
+                if self:request_ready(datain[n]) then
+                    local dataout, keep_alive = self:process(datain[n])
+                    if dataout == nil then
+                        running = false
+                    else
+                        C.send(evs[n].data.fd, dataout, #dataout, 0)
+                    end
+                    if not keep_alive then
+                        C.epoll_ctl(efd, C.EPOLL_CTL_DEL, evs[n].data.fd, ev)
+                        C.shutdown(evs[n].data.fd, C.SHUT_RDWR)
+                        curfds = curfds - 1
+                        C.close(evs[n].data.fd)
+                    end
+                    datain[n] = ""
                 end
             end
         end