Context handling in modules

Hi,

I wanted to inquire what the best way is to propagate cancel down to my module.

For context (no pun intended), I am working on a plugin/module that uses SSE. I think my SSE handler currently blocks the shutdown procedure of the Caddy process, and I’d like to clean it up before I release it. So far, I tried to work around it with another channel to make it respond to stop, but it feels dirty.

func (cs *CaddyScope) serveAPIStream(w http.ResponseWriter, r *http.Request) {
  w.Header().Set(“Content-Type”, “text/event-stream”)
  w.Header().Set(“Cache-Control”, “no-cache”)
  w.Header().Set(“X-Accel-Buffering”, “no”)

  rc := http.NewResponseController(w)

  writeAndFlush := func(data []byte) error {
	if _, err := fmt.Fprintf(w, "event: stats\ndata: %s\n\n", data);     err != nil {
	  return err
	}
	return rc.Flush()
  }

  if data := cs.snap.get(); data != nil {
	if err := writeAndFlush(data); err != nil {
	  return
	}
  }

  ticker := time.NewTicker(time.Duration(cs.Refresh))
  defer ticker.Stop()

  for {
	select {
	case <-r.Context().Done():
	  return
	case <-cs.cancelCtx.Done():
	  return
	case <-ticker.C:
	  data := cs.snap.get()
	  if data == nil {
		continue
	  }
	  if err := writeAndFlush(data); err != nil {
		return
	  }
	}
  }
}

I think there’s two things/questions here.

  • Blocking Caddy shutdown
  • Cleaning up (shutting down “cleanly”)

Why do you think your plugin is blocking the Caddy shutdown? Do you have a link to the code somewhere?

1 Like