Thanks to visit codestin.com
Credit goes to github.com

Skip to content

SSE Implementation in Fullmoon #44

@stet

Description

@stet

I was testing Fullmoon and when I focused on SSE I had some issues.

Issue 1: fullmoon.streamContent("sse", {...}) Causes Type Error

When attempting to use the fullmoon.streamContent("sse", {...}) pattern for Server-Sent
Events, an error occurs in fullmoon.lua:1463:

./.lua/fullmoon.lua:1463: bad argument #1 to ? (string expected)

The problematic code in fullmoon.lua around line 1463 appears to be:

local function streamWrap(func)
return function(...) return coroutine.yield(func(...)()) or true end

This fails when passing "sse" as the first argument to streamContent().

Issue 2: Returning Empty String from Route Handler Closes SSE Connection

When implementing a manual workaround using coroutines, returning an empty string from the
route handler causes Redbean to add a Content-Length: 0 header, which makes browsers
immediately close the SSE connection.

Reproduction Steps

  1. Set up a Fullmoon route that attempts to use SSE:

fullmoon.setRoute("/api/sse", function(r)
-- Set appropriate headers
SetHeader("Content-Type", "text/event-stream")
SetHeader("Cache-Control", "no-cache")

  -- Method 1: Using streamContent - causes error in fullmoon.lua:1463
  return fullmoon.streamContent("sse", {
      event = "ping",
      data = "test"
  })

  -- Method 2: Manual coroutine - adds Content-Length and closes connection
  local co = coroutine.create(function()
      Write("data: test\n\n")
      coroutine.yield()
  end)
  coroutine.resume(co)
  return ""

Current Workaround

I implemented a working solution, but it requires several non-obvious steps:

fullmoon.setRoute("/api/sse", function(r)
-- Set headers
SetHeader("Content-Type", "text/event-stream")
SetHeader("Cache-Control", "no-cache")

  -- Create coroutine
  local co = coroutine.create(function()
      Write("data: test\n\n")
      coroutine.yield()
      -- More SSE code...
  

  -- CRITICAL: These two lines prevent Content-Length from being set
  SetStatus(200)
  SetHeader("Transfer-Encoding", "chunked")

  -- Start coroutine
  coroutine.resume(co)

  -- CRITICAL: Must return true, not a string
  return true

Expected Behavior

  1. fullmoon.streamContent("sse", {...}) should work correctly for SSE
  2. There should be a simpler way to maintain an SSE connection in Fullmoon

Suggested Solution

Add a dedicated SSE helper to Fullmoon that handles these details, something like:

fullmoon.serveSSE(function()
-- Simple SSE implementation that handles coroutine, chunked encoding
-- and proper response handling automatically
Write("data: test\n\n")
coroutine.yield()

Environment

  • Redbean version: 3.0.0
  • Fullmoon version: 0.384
  • Test browsers: Safari, Chrome

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions