Skip to content

Server Sent Events are buffered instead of sent incrementally #250

@pechersky

Description

@pechersky

Consider a little server that models how SSE get sent. When proxying to this, there is a lag on output for 9 seconds (the sleeps) instead of getting an incremental response. This is relevant to lambda streaming, as mentioned in #150.

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"
)

func sseHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	clientGone := r.Context().Done()

	rc := http.NewResponseController(w)

	body, err := io.ReadAll(r.Body)
	if err != nil {
		body = []byte("Error reading body")
	}
	defer r.Body.Close()

	_, err = fmt.Fprintf(w, "data: === Initial Request Details ===\n")
	if err != nil {
		return
	}
	_, err = fmt.Fprintf(w, "data: Method: %s\n", r.Method)
	if err != nil {
		return
	}
	_, err = fmt.Fprintf(w, "data: URL: %s\n", r.URL.String())
	if err != nil {
		return
	}
	_, err = fmt.Fprintf(w, "data: Headers:\n")
	if err != nil {
		return
	}
	for key, values := range r.Header {
		for _, value := range values {
			_, err = fmt.Fprintf(w, "data:   %s: %s\n", key, value)
			if err != nil {
				return
			}
		}
	}
	_, err = fmt.Fprintf(w, "data: Body: %s\n\n", string(body))
	if err != nil {
		return
	}
	err = rc.Flush()
	if err != nil {
		return
	}

	for i := 1; i <= 3; i++ {
		select {
		case <-clientGone:
			fmt.Println("Client disconnected")
			return
		case <-time.After(time.Second * 3):
			_, err := fmt.Fprintf(w, "data: Chunk %d at %s\n\n", i, time.Now().Format(time.UnixDate))
			if err != nil {
				return
			}
			err = rc.Flush()
			if err != nil {
				return
			}
		}
	}
}

func main() {
	var (
		port     = flag.String("port", "8081", "Port to listen on")
		useHTTPS = flag.Bool("https", false, "Enable HTTPS")
		certFile = flag.String("cert", "", "Path to TLS certificate file (required for HTTPS)")
		keyFile  = flag.String("key", "", "Path to TLS key file (required for HTTPS)")
	)
	flag.Parse()

	http.HandleFunc("/events", sseHandler)

	addr := ":" + *port

	if *useHTTPS {
		if *certFile == "" || *keyFile == "" {
			log.Fatal("Both -cert and -key are required when using HTTPS")
		}
		fmt.Printf("Server is running on https://localhost:%s\n", *port)
		if err := http.ListenAndServeTLS(addr, *certFile, *keyFile, nil); err != nil {
			log.Fatal(err)
		}
	} else {
		fmt.Printf("Server is running on http://localhost:%s\n", *port)
		if err := http.ListenAndServe(addr, nil); err != nil {
			log.Fatal(err)
		}
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions