Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lua httpserver duplicates status line #3616

Open
athompson673 opened this issue Oct 23, 2023 · 1 comment
Open

Lua httpserver duplicates status line #3616

athompson673 opened this issue Oct 23, 2023 · 1 comment

Comments

@athompson673
Copy link

athompson673 commented Oct 23, 2023

Expected behavior

http status line is only sent once at the beginning of a response

Actual behavior

calling res:send(nil, status) then res:send(body_data, nil) results in duplicate status line being inserted at the end of the headers before the body. This caused me errors in certain http clients (particularly NodeRed).

Test code

Provide a Minimal, Complete, and Verifiable example which will reproduce the problem.

-- run http-example.lua

from a terminal call curl -v node_addr/:

> HTTP/1.1 200 OK
> Transfer-Encoding: chunked
> Connection: close
HTTP/1.1 200 OK* <- error on extra status line (missing colon (bad header line format))

Proposed solution

While this could be fixed in-place with a flag on whether the status has been already sent, I believe the most appropriate solution is to make a breaking change to httpserver.lua by separating the function to send the status line and body data. I don't see it as substantially more complicated to the user to replace the first call with res:send_status(200) and eliminate the status argument from res:send other than that it is a breaking change.

NodeMCU startup banner

recent build of release branch from cloud build service

Hardware

adafruit feather 8266

@athompson673
Copy link
Author

athompson673 commented Oct 23, 2023

This would probably do the trick, though I haven't had a chance to test it yet:

    ------------------------------------------------------------------------------
    -- response methods
    ------------------------------------------------------------------------------
    local make_res = function(csend, cfini)
        local send_status = function(self, status, reason)
            csend("HTTP/1.1 ")
            if status then
                csend(" " .. tostring(status))
            else
                csend(" 200")
            end
            if reason then
                csend((" %s\r\n"):format(reason))
            else
                csend(" OK\r\n")
            end
            csend("Transfer-Encoding: chunked\r\n") --always use chunked transfer
            self.send_status = nil --only send status once
        end

        local send_header = function(self, name, value)
            if self.send_status then
                self:send_status()
            end
            csend(("%s: %s\r\n"):format(name, value))
        end

        local send = function(self, data)
            if self.send_status then
                self:send_status()
            end
            if self.send_header then
                csend("\r\n") --blank line indicates end of headers
                self.send_header = nil
            end
            -- chunked transfer encoding
            csend(("%X\r\n%s\r\n"):format(#data,data))
        end

        -- finalize request
        local finish = function(self)
            if self.send_status then
                self:send_status()
            end
            if self.send_header then
                csend("\r\n") --blank line indicates end of headers
                self.send_header = nil
            end
            -- finalize chunked transfer encoding
            csend("0\r\n\r\n")
            -- close connection
            cfini()
        end
        --
        local res = { }
        res.send_status = send_status
        res.send_header = send_header
        res.send = send
        res.finish = finish
        return res
    end

The example file would then be changed as such:

    -- reply
    res:send_status(200, "OK")
    res:send_header("Connection", "close")
    res:send("Hello, world!\n")
    res:finish()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant