login

<     >

2023-08-15 19:37:07 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

accept response object are callback return value

this will allow more flexibility, like adding headers

diff --git a/README.md b/README.md
index 1c50a73..07a2ad6 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,8 @@ which looks like this:
 
 = responses
 
+== simple responses
+
 As can be seen in the hello world example above, most callbacks will return just
 one value: the payload string, which typically contains some HTML in web apps or
 some JSON in APIs. However, callbacks can return up to four values:
@@ -80,6 +82,15 @@ local function private_page(req)
 end
 ```
 
+== advanced response
+
+If the first value returned by a callback is a table (instead of string),
+then all response parameters are read from this table, the response object.
+
+In addition to "data" (i.e. payload), "status", "reason" and "cookies",
+the response object can also optionally include the "headers" value.
+This should be a table with additional response headers.
+
 = templates
 
 ```

diff --git a/lib/ludweb/http.lua b/lib/ludweb/http.lua
index caa15aa..e267921 100644
--- a/lib/ludweb/http.lua
+++ b/lib/ludweb/http.lua
@@ -92,28 +92,41 @@ local function build_cookie_data(cookies)
     return data
 end
 
-local function build_response(data, status, reason, cookies, keep_alive)
+local function build_header(headers)
     local header = ""
-    if status == nil then
-        status = 200
-        reason = "OK"
-    elseif status == 303 then
-        reason = reason or "See Other"
-        header = "Location: " .. data .. "\r\n"
+    for key, val in pairs(headers) do
+        header = header .. key .. ": " .. val .. "\r\n"
+    end
+    return header
+end
+
+local function build_response(resp, keep_alive)
+    local headers = {}
+    if resp.status == nil then
+        resp.status = 200
+        resp.reason = "OK"
+    elseif resp.status == 303 then
+        resp.reason = resp.reason or "See Other"
+        headers["Location"] = resp.data
     end
     -- it's nice to provide a minimal error message by default
-    if status ~= 200 and data == ""  then
-        data = status.." "..reason.."\n"
+    if resp.status ~= 200 and resp.data == ""  then
+        resp.data = resp.status.." "..resp.reason.."\n"
     end
     if keep_alive then
-        header = header .. "Connection: keep-alive\r\n"
+        headers["Connection"] = "keep-alive"
     else
-        header = header .. "Connection: close\r\n"
+        headers["Connection"] = "close"
+    end
+    headers["Content-Length"] = #resp.data
+    resp.headers = resp.headers or {}
+    if resp.headers["Content-Type"] == nil then
+        headers["Content-Type"] = "text/html; charset=utf-8"
     end
-    header = header .. "Content-Length: " .. #data .. "\r\n"
-    header = header .. build_cookie_data(cookies)
+    local header = build_header(headers) .. build_header(resp.headers)
+    header = header .. build_cookie_data(resp.cookies)
     local fmt = "HTTP/1.1 %03d %s\r\n%s\r\n%s"
-    return fmt:format(status, reason, header, data)
+    return fmt:format(resp.status, resp.reason, header, resp.data)
 end
 
 local HTTP = {}
@@ -144,17 +157,18 @@ local function new_http()
             return "", true
         end
         local req = parse_request(datain)
-        local dataout, status, reason, cookies = obj:process(req)
-        if dataout == nil then
-            return nil
+        local resp, status, reason, cookies = obj:process(req)
+        if resp == nil then return nil end
+        if type(resp) == "string" then
+            resp = {data=resp, status=status, reason=reason, cookies=cookies}
         end
         local keep_alive = req.headers.connection ~= "close"
         -- randomly close some connections to save resources
         if keep_alive and math.random(32) == 1 then
             keep_alive = false
         end
-        local resp = build_response(dataout, status, reason, cookies, keep_alive)
-        return resp, keep_alive
+        local dataout = build_response(resp, keep_alive)
+        return dataout, keep_alive
     end
     return obj
 end