Skip to content

Commit 219307d

Browse files
authored
Merge pull request #89 from launchdarkly/eb/sc-165127/only-utf8
remove support for non-UTF8 encodings since SSE doesn't allow them
2 parents 4ae0a83 + bdbce68 commit 219307d

File tree

7 files changed

+86
-87
lines changed

7 files changed

+86
-87
lines changed

src/LaunchDarkly.EventSource/Configuration.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,6 @@ public sealed class Configuration
7474
[Obsolete("Use ResponseStartTimeout")]
7575
public TimeSpan ConnectionTimeout => ResponseStartTimeout;
7676

77-
/// <summary>
78-
/// The character encoding to use when reading the stream if the server did not specify
79-
/// an encoding in a <c>Content-Type</c> header.
80-
/// </summary>
81-
/// <seealso cref="ConfigurationBuilder.DefaultEncoding(Encoding)"/>
82-
public Encoding DefaultEncoding { get; }
83-
8477
/// <summary>
8578
/// The HttpClient that will be used as the HTTP client, or null for a new HttpClient.
8679
/// </summary>
@@ -183,7 +176,6 @@ internal Configuration(ConfigurationBuilder builder)
183176
(builder._logAdapter is null ? null : builder._logAdapter.Logger(Configuration.DefaultLoggerName));
184177

185178
BackoffResetThreshold = builder._backoffResetThreshold;
186-
DefaultEncoding = builder._defaultEncoding ?? Encoding.UTF8;
187179
HttpClient = builder._httpClient;
188180
HttpMessageHandler = (builder._httpClient != null) ? null : builder._httpMessageHandler;
189181
InitialRetryDelay = builder._initialRetryDelay;

src/LaunchDarkly.EventSource/ConfigurationBuilder.cs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public class ConfigurationBuilder
2828
#region Private Fields
2929

3030
internal readonly Uri _uri;
31-
internal Encoding _defaultEncoding = Encoding.UTF8;
3231
internal TimeSpan _initialRetryDelay = Configuration.DefaultInitialRetryDelay;
3332
internal TimeSpan _backoffResetThreshold = Configuration.DefaultBackoffResetThreshold;
3433
internal string _lastEventId;
@@ -89,21 +88,6 @@ public ConfigurationBuilder HttpRequestModifier(Action<HttpRequestMessage> httpR
8988
return this;
9089
}
9190

92-
/// <summary>
93-
/// Sets the character encoding to use when reading the stream if the server did not specify
94-
/// an encoding in a <c>Content-Type</c> header.
95-
/// </summary>
96-
/// <param name="defaultEncoding">A <c>System.Text.Encoding</c>; if null, the default
97-
/// is <see cref="Encoding.UTF8"/></param>
98-
/// <returns>the builder</returns>
99-
/// <seealso cref="MessageEvent"/>
100-
/// <seealso cref="PreferDataAsUtf8Bytes"/>
101-
public ConfigurationBuilder DefaultEncoding(Encoding defaultEncoding)
102-
{
103-
_defaultEncoding = defaultEncoding ?? Encoding.UTF8;
104-
return this;
105-
}
106-
10791
/// <summary>
10892
/// Sets the initial amount of time to wait before attempting to reconnect to the EventSource API.
10993
/// </summary>
@@ -320,17 +304,14 @@ public ConfigurationBuilder Logger(Logger logger)
320304
/// preferable to store and process event data as UTF-8 byte arrays rather than
321305
/// strings. By default, <c>EventSource</c> will use the <c>string</c> type when
322306
/// processing the event stream; if you then use <see cref="MessageEvent.DataUtf8Bytes"/>
323-
/// to get the data, it will be converted to a byte array as needed. It will also
324-
/// always use the <c>string</c> type internally if the stream's encoding is not
325-
/// UTF-8. However, if the stream's encoding is UTF-8 <c>and</c> you have set
326-
/// <c>PreferDataAsUtf8Bytes</c> to <see langword="true"/>, the event data will
327-
/// be stored internally as a UTF-8 byte array so that if you read
307+
/// to get the data, it will be converted to a byte array as needed. However, if
308+
/// you set <c>PreferDataAsUtf8Bytes</c> to <see langword="true"/>, the event data
309+
/// will be stored internally as a UTF-8 byte array so that if you read
328310
/// <see cref="MessageEvent.DataUtf8Bytes"/>, you will get the same array with no
329311
/// extra copying or conversion. Therefore, for greatest efficiency you should set
330-
/// this to <see langword="true"/> if you intend to process the data as UTF-8 and
331-
/// if you expect that the server will provide it in that encoding. If the server
332-
/// turns out not to use that encoding, everything will still work the same except
333-
/// that there will be more overhead from string conversion.
312+
/// this to <see langword="true"/> if you intend to process the data as UTF-8. Note
313+
/// that Server-Sent Event streams always use UTF-8 encoding, as required by the
314+
/// SSE specification.
334315
/// </remarks>
335316
/// <param name="preferDataAsUtf8Bytes">true if you intend to request the event
336317
/// data as UTF-8 bytes</param>

src/LaunchDarkly.EventSource/EventSourceService.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,25 @@ CancellationToken cancellationToken
100100
_logger.Debug("Response status: {0}", (int)response.StatusCode);
101101
ValidateResponse(response);
102102

103-
OnConnectionOpened();
104-
105103
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
106104
{
107105
var encoding = DetectEncoding(response);
108-
if (encoding == Encoding.UTF8 && _configuration.PreferDataAsUtf8Bytes)
106+
if (encoding != Encoding.UTF8)
107+
{
108+
throw new EventSourceServiceCancelledException(
109+
string.Format(Resources.ErrorWrongEncoding, encoding.HeaderName));
110+
}
111+
OnConnectionOpened();
112+
113+
if (_configuration.PreferDataAsUtf8Bytes)
109114
{
110115
_logger.Debug("Reading UTF-8 stream without string conversion");
111116
await ProcessResponseFromUtf8StreamAsync(processResponseLineUTF8, stream, cancellationToken);
112117
}
113118
else
114119
{
115-
_logger.Debug("Reading stream with {0} encoding and string conversion", encoding.EncodingName);
116-
using (var reader = new StreamReader(stream, encoding))
120+
_logger.Debug("Reading stream with string conversion");
121+
using (var reader = new StreamReader(stream, Encoding.UTF8))
117122
{
118123
await ProcessResponseFromReaderAsync(processResponseLineString, reader, cancellationToken);
119124
}
@@ -135,7 +140,7 @@ private Encoding DetectEncoding(HttpResponseMessage response)
135140
}
136141
catch (ArgumentException) { }
137142
}
138-
return _configuration.DefaultEncoding ?? Encoding.UTF8;
143+
return Encoding.UTF8;
139144
}
140145

141146
protected virtual async Task ProcessResponseFromReaderAsync(

src/LaunchDarkly.EventSource/MessageEvent.cs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,12 @@ namespace LaunchDarkly.EventSource
1919
/// <para>
2020
/// Since strings in .NET use two-byte UTF-16 characters, if you have a large block of
2121
/// UTF-8 data it is considerably more efficient to process it in its original form
22-
/// rather than converting it to or from a string. <c>MessageEvent</c> converts
23-
/// transparently between these types depending on the original character encoding of
24-
/// the stream; the <see cref="Configuration"/> properties <see cref="Configuration.DefaultEncoding"/>
25-
/// and <see cref="Configuration.PreferDataAsUtf8Bytes"/>; and whether the caller reads
26-
/// from the property <see cref="MessageEvent.Data"/> or <see cref="MessageEvent.DataUtf8Bytes"/>.
27-
/// If you intend to process the data as UTF-8 bytes, and if you expect that the server
28-
/// will provide UTF-8, you should set
22+
/// rather than converting it to or from a string. <see cref="EventSource"/> stores
23+
/// data as strings by default, but you set <see cref="ConfigurationBuilder.PreferDataAsUtf8Bytes(bool)"/>
24+
/// it can store the raw UTF-8 data instead. In either case, <c>MessageEvent</c> will
25+
/// convert types transparently so that you can read either <see cref="MessageEvent.Data"/>
26+
/// or <see cref="MessageEvent.DataUtf8Bytes"/>.
2927
/// </para>
30-
/// <list type="bullet">
31-
/// <item> If the stream encoding is UTF-8, and you read the event data with the
32-
/// <see cref="MessageEvent.DataUtf8Bytes"/> property, the event data is stored as a
33-
/// UTF-8 byte array when it is first read from the stream and it returns the same
34-
/// array, without any further copying and without creating a <c>string</c>. </item>
35-
/// <item> If the stream encoding is UTF-8, but you read the event data with the
36-
/// <see cref="MessageEvent.Data"/> property, the event data is originally read from
37-
/// the stream as a UTF-8 byte array but is then converted to a <c>string</c>. </item>
38-
/// <item> If the stream encoding is not UTF-8, the event data is originally read from
39-
/// the stream as a <c>string</c>. <see cref="MessageEvent.Data"/> will return the
40-
/// same <c>string</c>; <see cref="MessageEvent.DataUtf8Bytes"/> will create a new
41-
/// UTF-8 byte array from it.</item>
42-
/// </list>
4328
/// </remarks>
4429
public struct MessageEvent
4530
{

src/LaunchDarkly.EventSource/Resources.Designer.cs

Lines changed: 55 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LaunchDarkly.EventSource/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@
129129
<data name="ErrorWrongContentType" xml:space="preserve">
130130
<value>Unexpected HTTP content type "{0}"; should be "text/event-stream"</value>
131131
</data>
132+
<data name="ErrorWrongEncoding" xml:space="preserve">
133+
<value>Unexpected character encoding {0}; should be UTF-8</value>
134+
</data>
132135
<data name="ErrorEmptyResponse" xml:space="preserve">
133136
<value>HTTP response had no body</value>
134137
</data>

test/LaunchDarkly.EventSource.Tests/EventSourceEncodingTest.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text;
22
using System.Threading.Tasks;
3+
using LaunchDarkly.TestHelpers;
34
using LaunchDarkly.TestHelpers.HttpTest;
45
using Xunit;
56
using Xunit.Abstractions;
@@ -97,24 +98,22 @@ public void CanReceiveUtf8EventDataAsBytes(bool setExplicitEncoding)
9798
}
9899
}
99100

100-
[Theory]
101-
[InlineData(true)]
102-
[InlineData(false)]
103-
public void NonUtf8EncodingIsReadAsStrings(bool preferUtf8Data)
101+
[Fact]
102+
public void NonUtf8EncodingIsRejected()
104103
{
105104
using (var server = HttpServer.Start(MakeStreamHandler(Encoding.GetEncoding("iso-8859-1"))))
106105
{
107106
var config = Configuration.Builder(server.Uri)
108107
.LogAdapter(_testLogging)
109-
.PreferDataAsUtf8Bytes(preferUtf8Data)
110108
.Build();
111109
using (var es = new EventSource(config))
112110
{
113111
var sink = new EventSink(es, _testLogging) { ExpectUtf8Data = false };
114-
115112
_ = Task.Run(es.StartAsync);
116113

117-
sink.ExpectActions(expectedEventActions);
114+
var errorAction = sink.ExpectAction();
115+
var ex = Assert.IsType<EventSourceServiceCancelledException>(errorAction.Exception);
116+
Assert.Matches(".*encoding.*8859.*", ex.Message);
118117
}
119118
}
120119
}

0 commit comments

Comments
 (0)