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