Releases: tmaxmax/go-sse
v0.11.0
The sse.Server logging and session handling were revamped to have more familiar, more flexible and less error prone interfaces for users. Some bugs were also fixed.
Removed
LoggerandLogLevelenum have been removed.Server.Loggerhas transitioned to the standardsloglibrary for better compatibility with the ecosystem
Changed
Server.Loggeris now of typefunc(r *http.Request) *slog.Loggerinstead ofsse.Logger– it is possible to customize the logger on a per-request basis, by for example retrieving it from the context.Server.OnSessionsignature changed fromfunc(s *Session) (Subscription, bool)tofunc(w http.ResponseWriter, r *http.Request) (topics []string, accepted bool)– its initial role was to essentially just provide the topics, so the need to fiddle withSessionandSubscriptionwas redundant anywayJoe.Subscribenow always returnsErrProviderClosedwhen aJoeinstance is closed while subscriptions are active. Previously it would return it only ifJoewas already shut down before subscribing.Joewill print a stack trace forReplayerpanics.
Fixed
sse.Sessiondoesn't write the header explicitly anymore. This would cause ahttp: superfluous response.WriteHeader callwarning being logged whensse.Server.OnSessionwrites a response code itself when accepting a session. The change was initially introduced to remove the warning for users of certain external libraries (see #41) but this is the issue of the external library, not ofgo-sse. If you encounter this warning when using an external library, write the response code yourself in the HTTP handler before subscribing thesse.Session, as described in the linked discussion.- An insidious synchronization issue in
Joecausing a channel double close in an edge case scenario (see #50, see code for details)
New Contributors
- @brnsampson in #48, thank you for bringing up
slogand adding it to the library! - @NathanBaulch in #45 and @ZackarySantana in #46. I normally don't merge typo fixes from others but these deserved it. Thank you!
v0.10.0
If you're working with LLMs in Go this update will make you happy! sse.Read is now a thing – it just parses all events from an io.Reader. Use it with your response bodies and forget about any sse.Client configuration. It also makes use of the new Go 1.23 iterators to keep your code neat and tidy.
Added
ReadandReadConfig
v0.9.0
This is the replayer update. Oh, what is a "replayer"? It's how we call replay providers starting with this version! Anyway, besides renaming, this update removes many replaying bugs, improves performance, robustness and error handling and better defines expected behavior for ReplayProviders... err, Replayers.
More such overhauls are planned. I'm leaving it up to you to guess which comes next – the server or the client? ;)
Removed
FiniteReplayer.{Count, AutoIDs}– use the constructor instead.ValidReplayer.{TTL, AutoIDs}– use the constructor instead.
Changed
- The
ReplayProviderand related entities are renamed to justReplayer.go-ssestrives to have a minimal and expressive API, and minimal and expressive names are an important step in that direction. The changelog will use the new names onwards. - Due to a change in the internal implementation, the
FiniteReplayeris now able to replay events only if the event with the LastEventID provided by the client is still buffered. Previously if the LastEventID was that of the latest removed event, events would still be replayed. This detail added complexity to the implementation without an apparent significant win, so it was dropped. FiniteReplayer.GCIntervalshould be set to0now in order to disable GC.- Automatic ID generation for both replayers does not overwrite already existing message IDs and errors instead. Ensure that your events do not have IDs when using replayers configured to generate IDs.
Replayer.Putnow returns an error instead of being required to panic. Read the method documentation for more info.Joealso propagates this error throughJoe.Publish.- Replayers are now required to not overwrite message IDs and return errors instead. Sending unsupported messages to replayers is a bug which should not go unnoticed. Both replayers in this library now implement this behavior.
Joedoes not log replayer panics to the console anymore. Handle these panics inside the replay provider itself.
Added
NewFiniteReplayerconstructorNewValidReplayerconstructorConnection.Buffer
Fixed
FiniteReplayerdoesn't leak memory anymore and respects the stored messages count it was given. Previously when a new message was put after the messages count was reached and some other messages were removed, the total messages count would grow unexpectedly andFiniteReplayerwould store and replay more events than it was configured to.ValidReplayerwas also susceptible to a similar memory leak, which is also fixed now.- #41 –
sse.Sessionnow writes the header explicitly when upgrading.
v0.8.0
This version removes all external dependencies of go-sse. All our bugs are belong to us! It also does some API and documentation cleanups.
Removed
Client.DefautReconnectionTime,Client.MaxRetrieshave been replaced with the newClient.Backoffconfiguration field. See the Added section for more info.ErrReplayFailedis removed from the public API.ReplayProviderWithGCandJoe.ReplayGCIntervalare no more. The responsibility for garbage collection is assigned to the replay providers.
Changed
Server.Loggeris now of a new type: theLoggerinterface. The dependency on x/exp/slog is removed. This opens up the possibility to adapt any existing logger to be usable withServer.- The default backoff behavior has changed. The previous defaults map to the new
Backoffconfiguration as follows:
sse.Backoff{
InitialInterval: 5 * time.Second, // currently 500ms
Multiplier: 1.5, // currently the same
Jitter: 0.5, // currently the same
MaxInterval: 60 * time.Second, // currently unbounded
MaxElapsedDuration: 15 * time.Minute, // currently unbounded
MaxRetries: -1, // previously no retries by default, currently unbounded
}Joenow accepts new subscriptions even if replay providers panic (previouslyErrReplayFailedwould be returned).Server.ServeHTTPpanics if a customOnSessionhandler returns aSubscriptionwith 0 topics
Added
- The
Loggerinterface,LogLeveltype, andLogLevel(Info|Warn|Error)values. BackoffandClient.Backoff– the backoff strategy is now fully configurable. See the code documentation for info.ValidReplayProvider.GCInterval, to configure at which interval expired events should be cleaned up.
v0.7.0
This version overhauls connection retry and fixes the connection event dispatch order issue. Some internal changes to Joe were also made, which makes it faster and more resilient.
Removed
ConnectionError.TemporaryConnectionError.Timeout
Changed
- Go's
TimeoutandTemporaryinterfaces are not used anymore – the client makes no assumptions and retries on every network or response read error. The only cases whenConnection.Connectreturns now are either when there are no more retries left (when the number is not infinite), or when the request context was cancelled. *url.Errors that occur on the HTTP request are now unwrapped and their cause is put inside aConnectionError.Connection.Connectdoesn't suppress any errors anymore: the request context errors are returned as is, all other errors are wrapped insideConnectionError.- On reconnection attempt, the response reset error is now wrapped inside
ConnectionError. With this change, all errors other than the context errors are wrapped insideConnectionError. - Subscription callbacks are no longer called in individual goroutines. This caused messages to be received in an indereminate order. Make sure that your callbacks do not block for too long!
- If a
ReplayProvidermethod panics when called byJoe, instead of closing itself completely it just stops replaying, putting or GC-ing messages to upcoming clients.Joecontinues to function as if no replay provider was given. A stack trace is printed to stderr when such a panic occurs.
v0.6.0
This version brings a number of refactors to the server-side tooling the library offers. Constructors and construction related types are removed, for ease of use and reduced API size, concerns regarding topics and expiry were separated from Message, logging of the Server is upgraded to structured logging and messages can be now published to multiple topics at once. Request upgrading has also been refactored to provide a more functional API, and the Server logic can now be customized without having to create a distinct handler.
Removed
Message.ExpiresAtis no more.Message.Topicis no more. See the changes toServer,ProviderandReplayProviderfor handling topics – you can now publish a message to multiple topics at once.Message.Writeris no more. The API was redundant – one can achieve the same usingstrings.BuilderandMessage.AppendData. See theMessageWriterexample for more.NewValidReplayProvideris no more.NewFiniteReplayProvideris no more.NewJoeis no more.JoeConfigis no more.Server.Subscribeis no more – it never made sense.Server.Provideris no more.NewServer,ServerOptionand friends are no more.- The
Loggerinterface and the capability of theServerto use types that implementLoggeras logging systems is removed. SubscriptionCallbackis no more (see the change to theSubscriptiontype in the "Changed" section).
Added
- Because the
ValidReplayProviderconstructor was removed, the fieldsValidReplayProvider.{TTL,AutoIDs}were added for configuration. - Because the
FiniteReplayProviderconstructor was removed, the fieldsFiniteReplayProvider.{Count,AutoIDs}were added for configuration. - Because the
Joeconstructor was removed, the fieldsJoe.{ReplayProvider,ReplayGCInterval}were added for configuration. - Because the
Serverconstructor was removed, the fieldServer.Providerwas added for configuration. - New
MessageWriterinterface; used by providers to send messages and implemented bySession(previously namedRequest). - New
ResponseWriterinterface, which is ahttp.ResponseWriteraugmented with aFlushmethod. ValidReplayProviderhas a new fieldNowwhich allows providing a custom current time getter, liketime.Now, to the provider. Enables deterministic testing of dependents onValidReplayProvider.- New
Server.OnSessionfield, which enables customization ofServer's response and subscriptions. - New
Server.Loggerfield, which enables structured logging with logger retrieved from the request and customizable config of logged information.
Changed
ReplayProvider.Puttakes a simple*Messageand returns a*Message, instead of changing the*Messageto which the**Messageparameter points.
It also takes a slice of topics, given that theMessagedoesn't hold the topic itself anymore. If the Message cannot be put, the method must now panic – see documentation for info.- Because
Message.ExpiresAtis removed, theValidReplayProvidersets the expiry itself. Server.Publishnow takes a list of topics.Provider.Publishnow takes a non-empty slice of topics.ReplayProvider.Putnow takes a non-empty slice of topics.Provider.Stopis nowProvider.Shutdownand takes now acontext.Contextas a parameter.Server.Shutdowntakes now acontext.Contextas a parameter.Requestis now namedSessionand exposes the HTTP request, response writer, and the last event ID of the request.- A new method
Flushis added toSession; messages are no longer flushed by default, which allows providers, replay providers to batch send messages. Upgradenow takes an*http.Requestas its second parameter.Subscriptionnow has aClientfield of typeMessageWriterinstead of aCallback.- Given the
Subscriptionchange,Provider.SubscribeandReplayProvider.Replaynow report message sending errors.
If you have any suggestions, things are breaking, need some help or want to show appreciation, please and feel free to open an issue, say somthing in the Discussion of this release, or text me in private using the socials on my profile!
v0.5.2
Added
- The new
Message.Writer– write to theMessageas if it is anio.Writer.
Fixed
Message.UnmarshalTextnow strips the leading Unicode BOM, if it exists, as per the specification.- When parsing events client-side, BOM removal was attempted on each event input. Now the BOM is correctly removed only when parsing is started.
v0.5.1
Fixed
Message.WriteTonow writes nothing ifMessageis empty.Message.WriteTodoes not attempt to write theretryfield ifMessage.Retryis not at least 1ms.NewTypeerror message is updated to say "event type", not "event name".
If you have any suggestions, things are breaking, need some help or want to show appreciation, please and feel free to open an issue or text me in private using the socials on my profile!
v0.5.0
This version comes with a series of internal refactorings that improve code readability and performance. It also replaces usage of []byte for event data with string – SSE is a UTF-8 encoded text-based protocol, so raw bytes never made sense. This migration improves code safety (less unsafe usage and less worry about ownership) and reduces the memory footprint of some objects.
Creating events on the server is also revised – fields that required getters and setters, apart from data and comments, are now simple public fields on the sse.Message struct.
Across the codebase, to refer to the value of the event field the name "event type" is used, which is the nomenclature used in the SSE specification.
Documentation and examples were also fixed and improved.
Added
- New
sse.EventNametype, which holds valid values for theeventfield, together with constructors (sse.Nameandsse.NewName).
Removed
sse.Message:AppendTextwas removed, as part of the migration from byte slices to strings. SSE is a UTF-8 encoded text-based protocol – raw bytes never made sense.
Changed
- Minimum supported Go version was bumped from 1.16 to 1.19. From now on, the latest two major Go versions will be supported.
sse.Message:AppendDatatakesstrings instead of[]byte.sse.Message:Commentis now namedAppendComment, for consistency withAppendData.sse.Message: The message's expiration is not reset anymore byUnmarshalText.sse.Message:UnmarshalTextnow unmarshals comments aswell.sse.Message:WriteTo(andMarshalTextandStringas a result) replaces all newline sequences in data with LF.sse.Message: TheExpirygetter andSetExpiresAt,SetTTLsetters are replaced by the public fieldExpiresAt.sse.Message: Event ID getter and setter are replaced by the publicIDfield.sse.Message: Event type (previously namedName) getter and setter are replaced by the publicTypefield.sse.Message: Theretryfield value is now a public field on the struct. As a byproduct,WriteTowill now make 1 allocation when writing events with theretryfield set.sse.NewEventIDis nowsse.NewID, andsse.MustEventIDissse.ID.sse.Event: TheDatafield is now of typestring, not[]byte.sse.Event: TheNamefield is now namedType.
Fixed
sse.Message:Clonenow copies the topic of the message to the new value.sse.Message: ID fields that contain NUL characters are now ignored, as required by the spec, inUnmarshalText.
If you have any suggestions, things are breaking, need some help or want to show appreciation, please and feel free to open an issue or text me in private using the socials on my profile!