Skip to content

[API Proposal]: High-level EventSource-style client for Server-Sent Events #122834

@YZahringer

Description

@YZahringer

Background and motivation

.NET 10 introduces System.Net.ServerSentEvents with SseParser, which provides solid low-level support for parsing Server-Sent Events streams. This is a welcome addition and fills an important gap on the protocol side.

However, consuming SSE in real-world applications requires more than parsing. In practice, every client must also handle:

  • connection lifecycle (open, closed, faulted)
  • transient network failures
  • automatic reconnection
  • honoring retry: values sent by the server
  • propagating Last-Event-ID on reconnect
  • distinguishing cancellation from transport errors

At the moment, all of this logic is left to application code. As a result, each consumer re-implements roughly the same reconnection and lifecycle machinery on top of HttpClient + SseParser, with varying quality and subtle differences.

In contrast, the JavaScript EventSource API provides a well-understood, widely adopted abstraction that addresses these concerns consistently. The absence of an equivalent high-level client in .NET makes SSE consumption significantly more complex than necessary and discourages adoption for otherwise straightforward streaming scenarios.

The goal of this proposal is not to replace SseParser, but to build a small, opinionated client abstraction on top of it.

API Proposal

Introduce a high-level SSE client in System.Net.ServerSentEvents that manages connection lifecycle and reconnection semantics, while delegating parsing to SseParser.

The API should:

  • Use HttpClient internally
  • Automatically reconnect on transient failures
  • Honor retry: values provided by the server
  • Automatically send Last-Event-ID on reconnect
  • Expose explicit lifecycle signals (connected, disconnected, error)

Conceptually, this is a client-side equivalent of EventSource, adapted to .NET conventions.

Proposal:

public sealed class EventSourceClient : IAsyncDisposable
{
    public event EventHandler Connected;
    public event EventHandler Disconnected;
    public event EventHandler<Exception> Error;
    public event EventHandler<SseItem<string>> Message;

    public Task StartAsync(CancellationToken cancellationToken);
    public Task StopAsync(CancellationToken cancellationToken);
}

This client would internally:

  • manage the HttpClient request lifecycle
  • consume the stream via SseParser
  • track LastEventId
  • apply reconnection delays based on retry: or defaults

The existing SseParser API remains unchanged and continues to serve advanced or custom scenarios.

API Usage

Example usage from an application perspective:

var client = new EventSourceClient(
    new Uri("https://example.com/events"),
    httpClient);

client.Connected += (_, _) =>
{
    logger.LogInformation("SSE connected");
};

client.Message += (_, evt) =>
{
    Process(evt.Data);
};

client.Error += (_, ex) =>
{
    logger.LogWarning(ex, "SSE error");
};

await client.StartAsync(ct);

Alternative Designs

  1. Status quo (manual composition)
    Continue requiring users to compose HttpClient + SseParser themselves.
    This works, but leads to duplicated, inconsistent implementations and subtle bugs, especially around reconnection and lifecycle signaling.

  2. Third-party libraries
    Some existing libraries implement EventSource-like behavior, but most predate SseParser, use older streaming patterns, or are no longer actively maintained.

  3. Parser-only helpers
    Adding small helpers around SseParser (e.g., exposing more metadata) would improve ergonomics but would still leave reconnection and lifecycle management to application code.

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Net

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions