### Handle SSE Validation Errors in Go Source: https://context7.com/jetify-com/sse/llms.txt Illustrates how to handle validation errors returned by the SSE package in Go. It shows how to use `errors.Is()` to check for `sse.ErrValidation` and provides examples of invalid event configurations, such as forbidden characters in IDs or negative retry values. ```go package main import ( "errors" "fmt" "go.jetify.com/sse" ) func main() { // Invalid event: ID contains forbidden characters badEvent := &sse.Event{ ID: "id\nwith\nnewlines", Data: "test", } if err := badEvent.Validate(); err != nil { if errors.Is(err, sse.ErrValidation) { fmt.Printf("Validation error: %v\n", err) // Output: Validation error: sse: id contains forbidden char } } // Invalid event: negative retry badRetry := &sse.Event{ Data: "test", Retry: -1, } if err := badRetry.Validate(); err != nil { fmt.Printf("Validation error: %v\n", err) // Output: Validation error: sse: retry must be >=0 } // Invalid raw data: contains newlines without Split badRaw := &sse.Event{ Data: sse.Raw("line1\nline2"), Split: false, } if err := badRaw.Validate(); err != nil { fmt.Printf("Validation error: %v\n", err) // Output: Validation error: sse: raw data contains newlines but Split is false } } ``` -------------------------------- ### Send Keep-Alive Comments with SendComment in Go Source: https://context7.com/jetify-com/sse/llms.txt The SendComment method sends SSE comments (lines starting with ':') which are useful as keepalives to prevent connection timeouts. This function is part of the sse package and requires a context and the comment string. ```go package main import ( "log" "net/http" "time" "go.jetify.com/sse" ) func main() { http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { // Disable automatic heartbeats to handle manually conn, err := sse.Upgrade(r.Context(), w, sse.WithHeartbeatInterval(0), ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: // Send manual keep-alive comment if err := conn.SendComment(r.Context(), "ping"); err != nil { return } case <-r.Context().Done(): return } } }) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Configure SSE Connection with Upgrade Options Source: https://context7.com/jetify-com/sse/llms.txt Demonstrates configuring an SSE connection using the `sse.Upgrade` function with various options. This includes setting heartbeat intervals and comments, client retry delays, write timeouts, custom HTTP headers, status codes, and a close message. ```go package main import ( "log" "net/http" "time" "go.jetify.com/sse" ) func main() { http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { // Custom headers for CORS or other purposes headers := http.Header{} headers.Set("X-Custom-Header", "value") // Close message sent when connection terminates closeMsg := &sse.Event{ Event: "close", Data: "Connection closed by server", } conn, err := sse.Upgrade(r.Context(), w, sse.WithHeartbeatInterval(15*time.Second), // Send heartbeat every 15s sse.WithHeartbeatComment("ping"), // Custom heartbeat comment sse.WithRetryDelay(5*time.Second), // Client reconnect delay sse.WithWriteTimeout(10*time.Second), // Write operation timeout sse.WithHeaders(headers), // Custom HTTP headers sse.WithStatus(http.StatusOK), // HTTP status code sse.WithCloseMessage(closeMsg), // Message sent on close ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // Connection will auto-send heartbeats and close message <-r.Context().Done() }) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Integrate SSE with HTTP Middleware in Go Source: https://context7.com/jetify-com/sse/llms.txt Demonstrates how to integrate the SSE package with HTTP middleware in Go. It shows how to wrap the `http.ResponseWriter` to allow the SSE package to function correctly with middleware. This is crucial for applications that use middleware for logging, authentication, or other cross-cutting concerns. ```go package main import ( "log" "net/http" "time" "go.jetify.com/sse" ) // customResponseWriter wraps http.ResponseWriter with additional functionality type customResponseWriter struct { http.ResponseWriter statusCode int } func (w *customResponseWriter) WriteHeader(code int) { w.statusCode = code w.ResponseWriter.WriteHeader(code) } // Unwrap allows SSE to access the underlying ResponseWriter func (w *customResponseWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter } func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() wrapped := &customResponseWriter{ResponseWriter: w, statusCode: http.StatusOK} next(wrapped, r) log.Printf("Path: %s, Status: %d, Duration: %v", r.URL.Path, wrapped.statusCode, time.Since(start)) } } func eventsHandler(w http.ResponseWriter, r *http.Request) { // Works correctly even with wrapped ResponseWriter conn, err := sse.Upgrade(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() ticker := time.NewTicker(time.Second) defer ticker.Stop() for count := 0; ; count++ { select { case <-ticker.C: if err := conn.SendData(r.Context(), count); err != nil { return } case <-r.Context().Done(): return } } } func main() { http.HandleFunc("/events", loggingMiddleware(eventsHandler)) log.Println("SSE server with middleware starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Upgrade HTTP Connection to SSE in Go Source: https://context7.com/jetify-com/sse/llms.txt Demonstrates how to upgrade an HTTP connection to an SSE stream using the `sse.Upgrade` function. It configures optional parameters like heartbeat intervals, retry delays, and write timeouts. The function returns a `*Conn` for sending events and handles SSE protocol details. This is suitable for establishing real-time communication channels. ```go package main import ( "log" "net/http" "time" "go.jetify.com/sse" ) func main() { http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { // Upgrade with options: 5s heartbeat, 2s retry delay for client reconnection conn, err := sse.Upgrade(r.Context(), w, sse.WithHeartbeatInterval(5*time.Second), sse.WithRetryDelay(2*time.Second), sse.WithWriteTimeout(10*time.Second), ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // Send events until client disconnects ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ticker.C: if err := conn.SendData(r.Context(), "Hello SSE!"); err != nil { return } case <-r.Context().Done(): return } } }) log.Println("SSE server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } // Test with: curl -N http://localhost:8080/events // Output: // data: "Hello SSE!" // // data: "Hello SSE!" ``` -------------------------------- ### Encode SSE Events and Comments with Encoder Source: https://context7.com/jetify-com/sse/llms.txt Demonstrates using `sse.NewEncoder` to write SSE events and comments directly to an `io.Writer`. It shows how to encode events with ID, event type, data (including JSON and raw text), and retry delay. Also includes encoding a comment line. ```go package main import ( "bytes" "fmt" "time" "go.jetify.com/sse" ) func main() { var buf bytes.Buffer encoder := sse.NewEncoder(&buf) // Encode event with all fields event := &sse.Event{ ID: "msg-001", Event: "notification", Data: map[string]string{ "title": "New Message", "body": "You have a new message", }, Retry: 5 * time.Second, } if err := encoder.EncodeEvent(event); err != nil { fmt.Printf("Encode error: %v\n", err) return } // Encode a comment encoder.EncodeComment("keep-alive") // Encode raw text data rawEvent := &sse.Event{ Data: sse.Raw("Plain text message"), } encoder.EncodeEvent(rawEvent) fmt.Println(buf.String()) } // Output: // id: msg-001 // retry: 5000 // event: notification // data: {"body":"You have a new message","title":"New Message"} // // : keep-alive // data: Plain text message ``` -------------------------------- ### Client: Receive SSE Events with Go Source: https://github.com/jetify-com/sse/blob/main/README.md This Go code snippet illustrates how to create an SSE client that connects to a server and receives events. It uses `http.Get` to establish a connection and then `sse.NewDecoder` to process the incoming event stream. The loop continues decoding events until the connection is closed or an error occurs. ```go package main import ( "context" "fmt" "log" "net/http" "go.jetify.com/sse" ) func main() { // Make request to SSE endpoint resp, err := http.Get("http://localhost:8080/events") if err != nil { log.Fatalf("Failed to connect: %v", err) } defer resp.Body.Close() // Create decoder for the SSE stream decoder := sse.NewDecoder(resp.Body) // Process events as they arrive for { var event sse.Event err := decoder.Decode(&event) if err != nil { log.Printf("Stream ended: %v", err) break } fmt.Printf("Event ID: %s, Data: %v\n", event.ID, event.Data) } } ``` -------------------------------- ### SSE Event Struct and Data Handling Source: https://context7.com/jetify-com/sse/llms.txt Illustrates the `sse.Event` struct for defining SSE messages, including ID, event type, data, and retry delay. It shows how to use `sse.Raw` for non-JSON data and the `Split` field for multiline data. Includes event validation and marshaling to wire format. ```go package main import ( "fmt" "time" "go.jetify.com/sse" ) func main() { // Event with JSON data (automatically marshaled) jsonEvent := &sse.Event{ ID: "evt-123", Event: "user_joined", Data: map[string]any{ "user_id": 42, "name": "Alice", }, Retry: 3 * time.Second, } // Event with raw text data (not JSON encoded) textEvent := &sse.Event{ ID: "evt-124", Event: "log", Data: sse.Raw("Server started successfully"), } // Multiline raw data with Split enabled multilineEvent := &sse.Event{ Event: "code", Data: sse.Raw("line 1\nline 2\nline 3"), Split: true, // Each line becomes separate data: field } // Validate events before sending if err := jsonEvent.Validate(); err != nil { fmt.Printf("Invalid event: %v\n", err) } // Marshal to SSE wire format bytes, _ := multilineEvent.MarshalText() fmt.Println(string(bytes)) } // Output: // event: code // data: line 1 // data: line 2 // data: line 3 ``` -------------------------------- ### Send Custom SSE Events in Go Source: https://context7.com/jetify-com/sse/llms.txt Illustrates sending custom SSE events using the `SendEvent` method. This method allows for detailed control over event properties such as ID, event type, data payload, and retry delay. The data is automatically JSON-encoded unless it is of type `sse.Raw`. This is useful for sending structured or pre-formatted data streams. ```go package main import ( "fmt" "log" "net/http" "time" "go.jetify.com/sse" ) type StockUpdate struct { Symbol string `json:"symbol"` Price float64 `json:"price"` Time string `json:"time"` } func main() { http.HandleFunc("/stocks", func(w http.ResponseWriter, r *http.Request) { conn, err := sse.Upgrade(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() count := 0 ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: count++ event := &sse.Event{ ID: fmt.Sprintf("%d", count), Event: "stock_update", // Custom event type Data: StockUpdate{ Symbol: "AAPL", Price: 150.25 + float64(count), Time: time.Now().Format(time.RFC3339), }, } if err := conn.SendEvent(r.Context(), event); err != nil { log.Printf("Error sending event: %v", err) return } case <-r.Context().Done(): return } } }) log.Fatal(http.ListenAndServe(":8080", nil)) } // Test with: curl -N http://localhost:8080/stocks // Output: // id: 1 // event: stock_update // data: {"symbol":"AAPL","price":151.25,"time":"2024-01-15T10:30:00Z"} // // id: 2 // event: stock_update // data: {"symbol":"AAPL","price":152.25,"time":"2024-01-15T10:30:02Z"} ``` -------------------------------- ### Server: Send SSE Events with Go Source: https://github.com/jetify-com/sse/blob/main/README.md This Go code snippet demonstrates how to set up an HTTP handler to send Server-Sent Events. It uses the `sse.Upgrade` function to establish an SSE connection and then sends events periodically using a ticker. The events contain JSON data, and the handler gracefully handles client disconnections. ```go package main import ( "fmt" "log" "net/http" "time" "go.jetify.com/sse" ) func eventsHandler(w http.ResponseWriter, r *http.Request) { // Upgrade HTTP connection to SSE conn, err := sse.Upgrade(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // Send events until client disconnects count := 0 ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ticker.C: // Send a simple event with JSON data event := &sse.Event{ ID: fmt.Sprintf("%d", count), Data: map[string]any{"count": count, "time": time.Now().Format(time.RFC3339)}, } if err := conn.SendEvent(r.Context(), event); err != nil { log.Printf("Error: %v", err) return } count++ case <-r.Context().Done(): return // Client disconnected } } } func main() { http.HandleFunc("/events", eventsHandler) log.Println("SSE server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Send Simple Data Messages with SendData in Go Source: https://context7.com/jetify-com/sse/llms.txt The SendData method sends simple data-only messages with the default 'message' event type. It automatically JSON-encodes the data. This function is part of the sse package and requires a context and the data to be sent. ```go package main import ( "log" "net/http" "time" "go.jetify.com/sse" ) func main() { http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { conn, err := sse.Upgrade(r.Context(), w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // Send simple string data conn.SendData(r.Context(), "Hello World") // Send JSON object (automatically marshaled) conn.SendData(r.Context(), map[string]any{ "message": "Welcome", "count": 42, }) // Send slice data conn.SendData(r.Context(), []string{"apple", "banana", "cherry"}) // Keep connection alive with periodic messages ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case t := <-ticker.C: if err := conn.SendData(r.Context(), t.Format(time.RFC3339)); err != nil { return } case <-r.Context().Done(): return } } }) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` -------------------------------- ### Client-Side Event Parsing with NewDecoder and Decode in Go Source: https://context7.com/jetify-com/sse/llms.txt The Decoder type parses SSE events from an io.Reader, handling UTF-8 BOM, line endings, and JSON decoding. Use LastEventID() and RetryDelay() for reconnection logic. This is used on the client-side to process the SSE stream. ```go package main import ( "context" "errors" "fmt" "io" "log" "net/http" "time" "go.jetify.com/sse" ) func main() { ctx := context.Background() lastEventID := "" retryDelay := 3 * time.Second for { err := connectAndProcess(ctx, &lastEventID, &retryDelay) if errors.Is(err, io.EOF) { log.Println("Stream ended normally") break } if err != nil { log.Printf("Connection error: %v, reconnecting in %v...", err, retryDelay) time.Sleep(retryDelay) continue } } } func connectAndProcess(ctx context.Context, lastEventID *string, retryDelay *time.Duration) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8080/events", nil) if err != nil { return err } // Set Last-Event-ID header for reconnection if *lastEventID != "" { req.Header.Set("Last-Event-ID", *lastEventID) } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // Create decoder and process events decoder := sse.NewDecoder(resp.Body) for { var event sse.Event if err := decoder.Decode(&event); err != nil { return err } // Track state for reconnection if event.ID != "" { *lastEventID = event.ID } if serverDelay := decoder.RetryDelay(); serverDelay > 0 { *retryDelay = serverDelay } // Handle event by type switch event.Event { case "stock_update": fmt.Printf("Stock Update - ID: %s, Data: %v\n", event.ID, event.Data) case "": fmt.Printf("Message: %v\n", event.Data) default: fmt.Printf("Event [%s]: %v\n", event.Event, event.Data) } } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.