using System; namespace LaunchDarkly.EventSource { /// /// Represents the Server-Sent Event message received from a stream. /// /// /// /// An SSE event consists of an event name (defaulting to "message" if not specified), /// a data string, and an optional "ID" string that the server may provide. /// /// /// The event name and ID properties are always stored as strings. By default, the /// data property is also stored as a string. However, in some applications, it may /// be desirable to represent the data as a UTF-8 byte array (for instance, if you are /// using the System.Text.Json API to parse JSON data). /// /// /// Since strings in .NET use two-byte UTF-16 characters, if you have a large block of /// UTF-8 data it is considerably more efficient to process it in its original form /// rather than converting it to or from a string. stores /// data as strings by default, but you set /// it can store the raw UTF-8 data instead. In either case, MessageEvent will /// convert types transparently so that you can read either /// or . /// /// public struct MessageEvent { /// /// The default value of if the SSE stream did not specify an /// event: field. /// public const string DefaultName = "message"; #region Private Fields private readonly string _name; private readonly string _dataString; private readonly Utf8ByteSpan _dataUtf8Bytes; private readonly string _lastEventId; private readonly Uri _origin; #endregion #region Public Constructors /// /// Initializes a new instance of the class. /// /// the event name /// the data received in the server-sent event /// the last event identifier, or null /// the origin URI of the stream public MessageEvent(string name, string data, string lastEventId, Uri origin) { _name = name; _dataString = data; _dataUtf8Bytes = new Utf8ByteSpan(); _lastEventId = lastEventId; _origin = origin; } /// /// Initializes a new instance of the class, /// providing the data as a UTF-8 byte span. /// /// the event name /// the data received in the server-sent event; /// the MessageEvent will store a reference to the byte array, rather than /// copying it, so it should not be modified afterward by the caller /// /// the last event identifier, or null /// the origin URI of the stream public MessageEvent(string name, Utf8ByteSpan dataUtf8Bytes, string lastEventId, Uri origin) { _name = name; _dataString = null; _dataUtf8Bytes = dataUtf8Bytes; _lastEventId = lastEventId; _origin = origin; } /// /// Initializes a new instance of the class. /// /// the event name /// the data received in the server-sent event /// the origin URI of the stream /// /// The will be initialized to null. /// public MessageEvent(string name, string data, Uri origin) : this(name, data, null, origin) { } #endregion #region Public Properties /// /// The event name. /// /// /// This can be specified by the server in the event: field in the SSE data, as in /// event: my-event-name. If there is no event: field, the default name /// is . /// public string Name => _name; /// /// Gets the data received in the event as a string. /// /// /// /// This is the value of the data: field in the SSE data; if there are multiple /// data: lines for a single event, they are concatenated with "\n". /// /// /// If the data was originally stored as a string, the same string is returned. /// If it was stored as a UTF-8 byte array, the bytes are copied to a new string. /// /// public string Data => _dataString is null ? _dataUtf8Bytes.GetString() : _dataString; /// /// Gets the data received in the event as a UTF-8 byte span. /// /// /// /// This is the value of the data: field in the SSE data; if there are multiple /// data: lines for a single event, they are concatenated with "\n". /// /// /// If the data was originally stored as UTF-8 bytes, the returned value refers to /// the same array, offset, and length (it is the caller's responsibility not to /// modify the byte array). If it was originally stored as a string, the string /// is copied to a new byte array. /// /// public Utf8ByteSpan DataUtf8Bytes => _dataString is null ? _dataUtf8Bytes : new Utf8ByteSpan(_dataString); /// /// Gets the last event identifier received in the server-sent event. /// /// /// This is the value of the id: field in the SSE data. If there is no such /// field, it is null. You can use a previously received /// value with when starting /// a new to tell the server what the last event you /// received was, although not all servers support this. /// public string LastEventId => _lastEventId; /// /// Gets the origin URI of the stream that generated the server-sent event. /// public Uri Origin => _origin; /// /// True if the event data is stored internally as UTF-8 bytes. /// /// /// The data can be accessed with either or /// regardless of the value of this property. The property only indicates the original /// format of the data, so, for instance, if it is then /// reading will have more overhead (due to copying) than /// reading . /// public bool IsDataUtf8Bytes => _dataString is null; #endregion #region Public Methods /// /// Determines whether the specified is equal to this instance. /// /// /// This method is potentially inefficient and should be used only in testing. /// /// the to compare with this instance /// if the instances are equal public override bool Equals(object obj) { if (!(obj is MessageEvent that) || !object.Equals(Name, that.Name) || !object.Equals(LastEventId, that.LastEventId) || !object.Equals(Origin, that.Origin)) { return false; } if (this._dataString != null) { return that._dataString != null ? this._dataString == that._dataString : that._dataUtf8Bytes.Equals(this._dataString); } return that._dataString != null ? this._dataUtf8Bytes.Equals(that._dataString) : this._dataUtf8Bytes.Equals(that._dataUtf8Bytes); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// This method is potentially inefficient and should be used only in testing. /// public override int GetHashCode() { int hash = 17; hash = hash * 31 + (_dataString != null ? _dataString.GetHashCode() : _dataUtf8Bytes.GetString().GetHashCode()); hash = hash * 31 + (_lastEventId != null ? _lastEventId.GetHashCode() : 0); hash = hash * 31 + (_origin != null ? _origin.GetHashCode() : 0); return hash; } #endregion } }