Giter Club home page Giter Club logo

dotnet-eventsource's Introduction

LaunchDarkly EventSource SSE Client for .NET

NuGet CircleCI Documentation

Overview

The LaunchDarkly.EventSource package allows .NET developers to consume Server-Sent-Events (SSE) from a remote API. The SSE specification is defined here: https://html.spec.whatwg.org/multipage/server-sent-events.html

Supported .NET versions

This version of the library is built for the following targets:

  • .NET Framework 4.6.2: runs on .NET Framework 4.6.2 and above.
  • .NET Standard 2.0: runs on .NET Core 3.x or .NET 6.0+, or within a library that is targeted to .NET Standard 2.x.

The .NET build tools should automatically load the most appropriate build of the library for whatever platform your application or library is targeted to.

Signing

The published version of this assembly is digitally signed with Authenticode and strong-named. Building the code locally in the default Debug configuration does not use strong-naming and does not require a key file. The public key file is in this repository at LaunchDarkly.EventSource.pk as well as here:

Public key (hash algorithm: sha1):
002400000480000094000000060200000024000052534131000400000100010015ba095c5a95ac
efa557867cec3f488906ec0ef6fe6728a7cfdeef861fcce49ea79357ba825d95d56d67597bc9cc
9a473438f5607908186fc477fdeafc68f387552061ebf57d6e585317d5047a57bd496034ff854a
417236776003bcba328fa8bf4a024c4d212ba4fb4033ebfb14116c12cde63d16551b9f48c20ee5
4a417deb

Public key token is 18e8c36453e3060f

Contributing

We encourage pull requests and other contributions from the community. Check out our contributing guidelines for instructions on how to contribute to this project.

About LaunchDarkly

  • LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
    • Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    • Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    • Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    • Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
  • LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Check out our documentation for a complete list.
  • Explore LaunchDarkly

dotnet-eventsource's People

Contributors

acquleo avatar arun251 avatar atrakh avatar eli-darkly avatar eplusminus avatar fracek avatar jeffashton avatar jkodumal avatar kinyoklion avatar launchdarklyci avatar ryanfollmer avatar vslee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dotnet-eventsource's Issues

System.Net.WebException: The operation has timed out.

We are seeing the following Stack Trace in our logs. We are currently using LaunchDarkly.EventSource version 3.2.3

Class: System.Net.WebException Message: The operation has timed out. at System.Net.HttpWebRequest+<RunWithTimeoutWorker>d__241'1[T].MoveNext () <0x101b68a10 + 0x00498> in <8294fc839f2d4b799a08e766e2dfa68e#9da8dd95652572baf7b138c1bdc40608>:0 --- End of stack trace from previous location where exception was thrown --- at System.Net.WebResponseStream+<ReadAsync>d__48.MoveNext () <0x101b13b90 + 0x00a2c> in <8294fc839f2d4b799a08e766e2dfa68e#9da8dd95652572baf7b138c1bdc40608>:0 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) <0x1016cc650 + 0x000d3> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) <0x1016cc5a0 + 0x0008b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) <0x1016cc530 + 0x00053> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.ConfiguredTaskAwaitable'1+ConfiguredTaskAwaiter[TResult].GetResult () <0x1017a5c40 + 0x0001b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.IO.StreamReader+<ReadBufferAsync>d__98.MoveNext () <0x101619770 + 0x0046f> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) <0x1016cc650 + 0x000d3> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) <0x1016cc5a0 + 0x0008b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) <0x1016cc530 + 0x00053> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.ConfiguredTaskAwaitable'1+ConfiguredTaskAwaiter[TResult].GetResult () <0x1017a5c40 + 0x0001b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.IO.StreamReader+<ReadLineAsyncInternal>d__61.MoveNext () <0x101618060 + 0x001eb> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) <0x1016cc650 + 0x000d3> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) <0x1016cc5a0 + 0x0008b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) <0x1016cc530 + 0x00053> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at System.Runtime.CompilerServices.TaskAwaiter'1[TResult].GetResult () <0x1016cc910 + 0x0001b> in <3c7b99a36820490fb2cbc5a6fc6b06d8#9da8dd95652572baf7b138c1bdc40608>:0 at LaunchDarkly.EventSource.EventSourceStreamReader+<ReadLineAsync>d__2.MoveNext () <0x1041d44c0 + 0x00243> in <641c2bc30eae4c1d8d3db4b93ea5a14f#9da8dd95652572baf7b138c1bdc40608>:0

ObjectDisposedException

HI,

Our .NET client application has started throwing the following error all of a sudden (in the last few days).

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'SslStream'.
at System.Net.ConnectStream.EndRead(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory1.FromAsyncTrimPromise1.Complete(TInstance thisRef, Func3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.<ReadAsync>d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.IO.StreamReader.<ReadBufferAsync>d__97.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.IO.StreamReader.<ReadLineAsyncInternal>d__60.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at LaunchDarkly.EventSource.EventSourceStreamReader.d__2.MoveNext()

EventSource doesn't use Proxy configuration

I'm configuring an LdClient as described in https://docs.launchdarkly.com/sdk/server-side/dotnet/migration-5-to-6#understanding-changes-to-networking:

var config =  Configuration.Builder("my-sdk-key")
    .Http(
        Components.HttpConfiguration()
            .Proxy(new System.Net.WebProxy(new Uri("http://my-proxy:8080")))
    )
    .Build();

However, when EventSource creates its HttpClient, it doesn't use the Http/Proxy configuration, resulting in errors. From the log:

INFO  LaunchDarkly.Sdk.DataSource Connecting to LaunchDarkly stream
INFO  LaunchDarkly.Sdk Waiting up to 5000 milliseconds for LaunchDarkly client to start...
DEBUG LaunchDarkly.Sdk.DataSource Making GET request to EventSource URI https://stream.launchdarkly.com/all
WARN  LaunchDarkly.Sdk Timeout encountered waiting for LaunchDarkly client initialization

The only workaround we've found is to set the global proxy as per instructions here: https://docs.microsoft.com/en-us/dotnet/framework/network-programming/proxy-configuration

.NET 4.7.2
LaunchDarkly.ServerSdk 6.1.0
LaunchDarkly.EventSource 4.1.2
LaunchDarkly.CommonSdk 5.1.0

Error when importing because of dependencies of LaunchDarkly.Logging

image

Assembly 'Assets/netstandard2.0/LaunchDarkly.EventSource.dll' will not be loaded due to errors:
Unable to resolve reference 'LaunchDarkly.Logging'. Is the assembly missing or incompatible with the current platform?
Reference validation can be disabled in the Plugin Inspector.

ReadTimeout not working as intended

To reproduce, initialize .net-client 3.4.0-alpha1 with a low read timeout (less than 3 minutes). Debug output does not show the client reconnecting to the stream after every read timeout interval is reached, and a heartbeat is received from the flag stream after 3 minutes.

Remove dependency on Polly-Signed

The dependency on Polly-Signed has been problematic in our project, which uses Polly (the not-signed version). We've had to change from Polly to Polly-Signed in each of our projects, which is a bit of a hassle.

It looks like there's just one retry policy used here; can I suggest that the retry logic be written manually and the dependency on Polly-Signed removed? That way this library would have less of an impact on client projects.

SSE Server, Disconnection detection

Hi,

this is not an issue, rather a question. Also, it's not related to the .net client. I will have to excuse in advance for not finding the right repository to ask.
The topic is related to your server side SSE technology, specifically how your server handles disconnections.

When a client close, it should terminate the TPC connection using a FIN. It's not uncommon for a client connection to drop without closing correctly the connection (this is especially true on mobile devices)
How do you handle "broken" connections on your server streaming architecture? Are you using TPC keepAlive to detect unresponsive clients?

Thanks in advance.

eventsource in blazor

I've tried open a SSE stream from a blazor client.
the call ReadAsStreamAsync on the http request don't exit creating a deadlock.

I've found that the solution is calling this extension on the http request object:
request.SetBrowserResponseStreamingEnabled(true);
which is a method extension on the Microsoft.AspNetCore.Components.WebAssembly assembly.

I believe this assembly cannot be added as reference to the source code.
A solution could be to expose a method hook from the library before execute the http request to allow the blazor code to invoke the extension method.

ASP.NET app needs binding redirect to run

After adding LaunchDarkly.ServerSdk version 7.0.3 (which transitively pulled in LaunchDarkly.EventSource 5.0.1) to an ASP.NET MVC app targeting .NET Framework 4.7.2, requests to the app returned this error:

Could not load file or assembly 'LaunchDarkly.Logging, Version=1.0.1.0, Culture=neutral, PublicKeyToken=d9182e4b0afd33e7' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.

It seems that this is because LaunchDarkly.ServerSdk depends on LaunchDarkly.Logging 2.0.0, while LaunchDarkly.EventSource depends on LaunchDarkly.Logging 1.0.1. To resolve this, I had to add a binding redirect to the Web.config:

<dependentAssembly>
  <assemblyIdentity name="LaunchDarkly.Logging" publicKeyToken="d9182e4b0afd33e7" culture="neutral"/>
  <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>

Is this expected behavior for this package? As far as I can tell, EventSource is the only installed package that references Logging version 1.0.1. We will be adding LaunchDarkly to more apps in the future, and it would be nice if we did not have to include a binding redirect every time.

Exception when trying to startup LdClient with Event Source

Error trying to initialize LDClient with SDK key.
Error:

System.MethodAccessException: Attempt by method 'LaunchDarkly.Common.StreamManager..ctor(LaunchDarkly.Common.IStreamProcessor, LaunchDarkly.Common.StreamProperties, LaunchDarkly.Common.IBaseConfiguration, LaunchDarkly.Common.ClientEnvironment, EventSourceCreator)' to access method 'LaunchDarkly.EventSource.ExponentialBackoffWithDecorrelation..ctor(System.TimeSpan, System.TimeSpan)' failed..

Using packages:

  • LaunchDarkly.Client v5.6.3
  • LaunchDarkly.EventSource v4.0.0

Retry field not used correctly

Even though the retry field is read it seems like it is not really used for the reconnection time.

Here the field is read to _retryDelay:

else if (result.IsRetryField)
{
if (long.TryParse(result.GetValueAsString(), out var retry))
{
_retryDelay = TimeSpan.FromMilliseconds(retry);
}
}

But _retryDelay is not really used for the reconnection time:

private async Task MaybeWaitWithBackOff() {
if (_retryDelay.TotalMilliseconds > 0)
{
TimeSpan sleepTime = _backOff.GetNextBackOff();
if (sleepTime.TotalMilliseconds > 0) {
_logger.Info("Waiting {0} milliseconds before reconnecting...", sleepTime.TotalMilliseconds);
BackOffDelay = sleepTime;
await Task.Delay(sleepTime);
}
}
}

Instead it is only passed initially for construction of ExponentialBackoffWithDecorrelation:

_backOff = new ExponentialBackoffWithDecorrelation(_retryDelay, _configuration.MaxRetryDelay);

The _minimumDelay in ExponentialBackoffWithDecorrelation is never updated

Make the EventSource-class extensible

It would be great if the EventSource.OnMessageReceived-method, EventSource.DispatchEvent (or an intermediary) could be made virtual so that custom message processing could be implemented by inheriting from the EventSource-class and overriding this method.

As a concrete example, I'd like to extend the class with a method for adding and removing listeners for specific events (which may, in itself, be a nice additional feature), for which I'd need to hook into the message handling loop. However, at present, I can only do so by subscribing to the MessageReceived-event.

Null Reference Exceptions in EventSource

Message: Object reference not set to an instance of an object.
Type: System.NullReferenceException

   at LaunchDarkly.EventSource.EventSource.ProcessResponseContent(String content)
   at LaunchDarkly.EventSource.EventSourceService.<ProcessResponseFromReaderAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at LaunchDarkly.EventSource.EventSourceService.<ConnectToEventSourceApi>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at LaunchDarkly.EventSource.EventSourceService.<GetDataAsync>d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at LaunchDarkly.EventSource.EventSource.<ConnectToEventSourceAsync>d__37.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at LaunchDarkly.EventSource.EventSource.<StartAsync>d__32.MoveNext()

Occurs around the same time tasks are cancelled:

image

.NET Standard 1 CVE

Due to the support of .NET Standard 1, there is a security vulnerability in the LaunchDarkly dependency chain that is rooted in EventSource. This is being picked up by SAST scanners and we are currently compelled to use rule exceptions as there is no workaround to force a package version that does not contain the vulnerability.

Ideally there would be a version of EventSource that did not support .NET Standard 1 that would either be referenced in the chain by LaunchDarkly.Common.StrongName, or simply available to override.

The result of scanning, which I believe uses dotnet retire:

* Microsoft Security Advisory CVE-2018-8416: .NET Core Tampering Vulnerability in System.IO.Compression.ZipFile/4.3.0
https://github.com/dotnet/announcements/issues/95
LaunchDarkly.EventSource/3.3.2
╚ NETStandard.Library/1.6.1
 ╚ System.IO.Compression.ZipFile/4.3.0

* Microsoft Security Advisory CVE-2018-8292: .NET Core Information Disclosure Vulnerability in System.Net.Http/4.3.0
https://github.com/dotnet/announcements/issues/88
LaunchDarkly.EventSource/3.3.2
╚ NETStandard.Library/1.6.1
 ╚ System.IO.Compression.ZipFile/4.3.0

CancellationTokenSource memory leak

Hello,

In using your library to process SSE events, I'm finding that as millions of events are received there can be a significant buildup in RAM. I ran it through dotMemory to profile and found that the leak appears to be due to the combined token in DoWithTimeout (AsyncHelpers.cs). The combined token is used but never disposed. Adding a combinedCancellation.Dispose() after the await task seems to cure the problem.

Thanks!

Error when trying to send a request with headers

Hello.
I followed your tutorial here : #91

But I have an error

"Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."

image
public class testSEE : MonoBehaviour
{
    // found here : https://github.com/launchdarkly/dotnet-eventsource/issues/91
    private void Start()
    {
        string JSON = @"{'model':'gpt-3.5-turbo','stream':true,'messages': [{""role"": ""user"", ""content"": ""Say this is a test!""}]}";

        Configuration config = Configuration.Builder(new Uri("https://api.openai.com/v1/chat/completions"))
            .RequestHeader("Content-Type", "application/json")
            .RequestHeader("Authorization", "Bearer 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")
            .RequestBody(JSON, "application/json")
            .Build();
        EventSource eventSource = new EventSource(config);

        Debug.Log("Event source is configurated");

        eventSource.MessageReceived += EventSource_MessageReceived;
        eventSource.Closed += EventSource_Closed;
        eventSource.Opened += EventSource_Opened;
        eventSource.Error += EventSource_Error;
        Task.Run(() => eventSource.StartAsync());
    }

    private void EventSource_Error(object sender, ExceptionEventArgs e)
    {
        Debug.LogException(e.Exception);
        Debug.LogError($"error : {e.Exception.Message}");
    }

    private void EventSource_Opened(object sender, StateChangedEventArgs e)
    {
        Debug.Log($"Opened: {sender}, {e}");
    }

    private void EventSource_MessageReceived(object sender, MessageReceivedEventArgs eventArgs)
    {
        var eventName = eventArgs.Message.Name;
        var eventData = eventArgs.Message.Data;
        Debug.Log(eventData);
        Debug.Log(eventName);
        // do whatever you want to do with the message
    }

    private void EventSource_Closed(object sender, StateChangedEventArgs e)
    {
        Debug.Log($"close: {sender}, {e}");
    }
}

Do you know how I can fix this ?

DelayRetryDuration not working as intended

Steps to reproduce:

  1. run the server-side php script which will close a connection every time after it sends the data:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.

/**
 * Constructs the SSE data format and flushes that data to the client.
 *
 * @param string $id Timestamp/id of this connection.
 * @param string $msg Line of text that should be transmitted.
 */
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

$serverTime = time();

sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
  1. run the console app:
        static async Task Main(string[] args)
        {
            var config = Configuration.Builder(new Uri("http://localhost:8000/your_script.php"))
                .ConnectionTimeout(TimeSpan.FromSeconds(1))
                .DelayRetryDuration(TimeSpan.FromSeconds(3))
                .ReadTimeout(TimeSpan.FromMinutes(4))
                .Build();

            var evt = new EventSource(config);

            evt.Opened += (sender, eventArgs) => { Console.WriteLine("Opened"); };
            evt.Error += (sender, eventArgs) => { Console.WriteLine("Error"); };
            evt.CommentReceived += (sender, eventArgs) => { Console.WriteLine("CommentReceived"); };
            evt.MessageReceived += (sender, eventArgs) => { Console.WriteLine($"MessageReceived: {eventArgs.Message.Data}"); };
            evt.Closed += (sender, eventArgs) => { Console.WriteLine("Closed"); };

            try
            {
                await evt.StartAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Closed");
            }

            Console.ReadLine();
        }
  1. There should be a delay = 3 seconds between reconnects, but EventSource reconnects immediately. It looks like it ignores DelayRetryDuration(...) setting.

My workaround is to change some code:

  • in ExponentialBackoffWithDecorrelation.cs:
        public TimeSpan GetNextBackOff(TimeSpan retryDelay)
        {
            _reconnectAttempts++;
            return retryDelay;
        }
  • in EventSource.cs:
        public async Task StartAsync()
        {
            var cancellationToken = _pendingRequest.Token;
            while (!cancellationToken.IsCancellationRequested)
            {
                await MaybeWaitWithBackOff();
                try
                {
                    await ConnectToEventSourceAsync(cancellationToken);
                    _backOff.ResetReconnectAttemptCount();
                }
                catch (Exception e)
                {
                    _logger.ErrorFormat("Encountered an error connecting to EventSource: {0}", e, e.Message);
                    _logger.Debug("", e);
                }
            }
        }

        private async Task MaybeWaitWithBackOff()  {
            if (_backOff.GetReconnectAttemptCount() > 0 && _retryDelay > TimeSpan.FromMilliseconds(0))
            {
                TimeSpan sleepTime = _backOff.GetNextBackOff(_retryDelay);
                _logger.InfoFormat("Waiting {0} milliseconds before reconnecting...", sleepTime.TotalMilliseconds);
                BackOffDelay = sleepTime;
                await Task.Delay(sleepTime);
            }
            else {
                _backOff.IncrementReconnectAttemptCount();
            }
        }

(I think the main problem is calling MaybeWaitWithBackOff(); without await.)

close is not closing the stream which is leaving the socket alive

I noticed that even though the cancellation token is being set the stream itself is not actually closing when cancelled which is causing the socket and the stream to stay open. To resolve the issue I made the following code changes so that it will close the stream properly.

EventSourceServices.cs line 101
FROM
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
TO
await using (var stream = await response.Content.ReadAsStreamAsync(CancellationToken.None).ConfigureAwait(false))
await using (cancellationToken.Register(stream.Close)) // Calls stream.close when token has been cancelled


and the above code then caused an exception with the StreamReader. to resolve that issue modify lines 147-151
FROM
if (line == null)
{
// this means the stream is done, i.e. the connection was closed
return;
}
TO
if (line == null)
{
reader.Dispose(); // <----- this will prevent a premature end of stream exception
// this means the stream is done, i.e. the connection was closed
return;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.