From 835dfe37b90b45942ca65fa90aa10231f94ab986 Mon Sep 17 00:00:00 2001 From: Mads Tordal Date: Tue, 11 Apr 2017 06:04:30 -0700 Subject: [PATCH 1/3] Added support for request headers --- .../WebRequesterFactoryTest.cs | 3 ++- EventSource4Net/ConnectedState.cs | 9 ++++++--- EventSource4Net/ConnectingState.cs | 16 +++++++-------- EventSource4Net/DisconnectedState.cs | 11 ++++++---- EventSource4Net/EventSource.cs | 20 ++++++++++++------- EventSource4Net/IConnectionState.cs | 2 +- EventSource4Net/IWebRequester.cs | 3 +-- EventSource4Net/WebRequester.cs | 12 ++++++++--- 8 files changed, 47 insertions(+), 29 deletions(-) diff --git a/EventSource4Net.Test/WebRequesterFactoryTest.cs b/EventSource4Net.Test/WebRequesterFactoryTest.cs index ab61e7e..ecd86b8 100644 --- a/EventSource4Net.Test/WebRequesterFactoryTest.cs +++ b/EventSource4Net.Test/WebRequesterFactoryTest.cs @@ -36,7 +36,7 @@ public WebRequesterMock(ServiceResponseMock response) this.Response = response; } - public System.Threading.Tasks.Task Get(Uri url) + public System.Threading.Tasks.Task Get(Uri url, Dictionary headers) { return Task.Factory.StartNew(() => { @@ -44,6 +44,7 @@ public System.Threading.Tasks.Task Get(Uri url) return Response; }); } + } class ServiceResponseMock : IServerResponse diff --git a/EventSource4Net/ConnectedState.cs b/EventSource4Net/ConnectedState.cs index 7669993..9bb4067 100644 --- a/EventSource4Net/ConnectedState.cs +++ b/EventSource4Net/ConnectedState.cs @@ -17,15 +17,18 @@ class ConnectedState : IConnectionState private ServerSentEvent mSse = null; private string mRemainingText = string.Empty; // the text that is not ended with a lineending char is saved for next call. private IServerResponse mResponse; + private Dictionary headers; + public EventSourceState State { get { return EventSourceState.OPEN; } } - public ConnectedState(IServerResponse response, IWebRequesterFactory webRequesterFactory) + public ConnectedState(IServerResponse response, IWebRequesterFactory webRequesterFactory, Dictionary headers) { mResponse = response; mWebRequesterFactory = webRequesterFactory; + this.headers = headers; } - public Task Run(Action msgReceived, CancellationToken cancelToken) + public Task Run(Action msgReceived, CancellationToken cancelToken, Dictionary headers) { int i = 0; @@ -130,7 +133,7 @@ public Task Run(Action msgReceived, Cancellat //stream.Close(); //mResponse.Close(); //mResponse.Dispose(); - return new DisconnectedState(mResponse.ResponseUri, mWebRequesterFactory); + return new DisconnectedState(mResponse.ResponseUri, mWebRequesterFactory, headers); } } }); diff --git a/EventSource4Net/ConnectingState.cs b/EventSource4Net/ConnectingState.cs index d22c9f9..08da09b 100644 --- a/EventSource4Net/ConnectingState.cs +++ b/EventSource4Net/ConnectingState.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Net; -using System.IO; using System.Threading; namespace EventSource4Net @@ -15,20 +12,23 @@ class ConnectingState : IConnectionState private Uri mUrl; private IWebRequesterFactory mWebRequesterFactory; + private Dictionary headers; + public EventSourceState State { get { return EventSourceState.CONNECTING; } } - public ConnectingState(Uri url, IWebRequesterFactory webRequesterFactory) + public ConnectingState(Uri url, IWebRequesterFactory webRequesterFactory, Dictionary headers) { if (url == null) throw new ArgumentNullException("Url cant be null"); if (webRequesterFactory == null) throw new ArgumentNullException("Factory cant be null"); mUrl = url; mWebRequesterFactory = webRequesterFactory; + this.headers = headers; } - public Task Run(Action donothing, CancellationToken cancelToken) + public Task Run(Action donothing, CancellationToken cancelToken, Dictionary headers) { IWebRequester requester = mWebRequesterFactory.Create(); - var taskResp = requester.Get(mUrl); + var taskResp = requester.Get(mUrl, headers); return taskResp.ContinueWith(tsk => { @@ -37,7 +37,7 @@ public Task Run(Action donothing, Cancellatio IServerResponse response = tsk.Result; if (response.StatusCode == HttpStatusCode.OK) { - return new ConnectedState(response, mWebRequesterFactory); + return new ConnectedState(response, mWebRequesterFactory, headers); } else { @@ -45,7 +45,7 @@ public Task Run(Action donothing, Cancellatio } } - return new DisconnectedState(mUrl, mWebRequesterFactory); + return new DisconnectedState(mUrl, mWebRequesterFactory, headers); }); } } diff --git a/EventSource4Net/DisconnectedState.cs b/EventSource4Net/DisconnectedState.cs index da347cb..ec3c955 100644 --- a/EventSource4Net/DisconnectedState.cs +++ b/EventSource4Net/DisconnectedState.cs @@ -11,24 +11,27 @@ class DisconnectedState : IConnectionState { private Uri mUrl; private IWebRequesterFactory mWebRequesterFactory; + private Dictionary headers; + public EventSourceState State { get { return EventSourceState.CLOSED; } } - public DisconnectedState(Uri url, IWebRequesterFactory webRequesterFactory) + public DisconnectedState(Uri url, IWebRequesterFactory webRequesterFactory, Dictionary headers) { if (url == null) throw new ArgumentNullException("Url cant be null"); mUrl = url; mWebRequesterFactory = webRequesterFactory; + this.headers = headers; } - public Task Run(Action donothing, CancellationToken cancelToken) + public Task Run(Action donothing, CancellationToken cancelToken, Dictionary headers) { if(cancelToken.IsCancellationRequested) - return Task.Factory.StartNew(() => { return new DisconnectedState(mUrl, mWebRequesterFactory); }); + return Task.Factory.StartNew(() => { return new DisconnectedState(mUrl, mWebRequesterFactory, headers); }); else - return Task.Factory.StartNew(() => { return new ConnectingState(mUrl, mWebRequesterFactory); }); + return Task.Factory.StartNew(() => { return new ConnectingState(mUrl, mWebRequesterFactory, headers); }); } } } diff --git a/EventSource4Net/EventSource.cs b/EventSource4Net/EventSource.cs index 7785155..7455dbd 100644 --- a/EventSource4Net/EventSource.cs +++ b/EventSource4Net/EventSource.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Net; -using System.IO; -using System.Threading.Tasks; using System.Threading; namespace EventSource4Net @@ -16,6 +12,8 @@ public class EventSource public event EventHandler StateChanged; public event EventHandler EventReceived; + public CancellationTokenSource CancellationToken { get; set; } + private IWebRequesterFactory _webRequesterFactory = new WebRequesterFactory(); private int _timeout = 0; public Uri Url { get; private set; } @@ -24,6 +22,8 @@ public class EventSource private IConnectionState mCurrentState = null; private CancellationToken mStopToken; private CancellationTokenSource mTokenSource = new CancellationTokenSource(); + private Dictionary _headers; + private IConnectionState CurrentState { get { return mCurrentState; } @@ -47,11 +47,17 @@ public EventSource(Uri url, int timeout) Initialize(url, timeout); } + public EventSource(Uri url, Dictionary headers, int timeout) + { + _headers = headers; + Initialize(url, timeout); + } + /// /// Constructor for testing purposes /// /// The factory that generates the WebRequester to use. - protected EventSource(Uri url, IWebRequesterFactory factory) + public EventSource(Uri url, IWebRequesterFactory factory) { _webRequesterFactory = factory; Initialize(url, 0); @@ -61,7 +67,7 @@ private void Initialize(Uri url, int timeout) { _timeout = timeout; Url = url; - CurrentState = new DisconnectedState(Url,_webRequesterFactory); + CurrentState = new DisconnectedState(Url, _webRequesterFactory, _headers); _logger.Info("EventSource created for " + url.ToString()); } @@ -85,7 +91,7 @@ protected void Run() if (mTokenSource.IsCancellationRequested && CurrentState.State == EventSourceState.CLOSED) return; - mCurrentState.Run(this.OnEventReceived, mTokenSource.Token).ContinueWith(cs => + mCurrentState.Run(this.OnEventReceived, mTokenSource.Token, _headers).ContinueWith(cs => { CurrentState = cs.Result; Run(); diff --git a/EventSource4Net/IConnectionState.cs b/EventSource4Net/IConnectionState.cs index 5654023..b3d008b 100644 --- a/EventSource4Net/IConnectionState.cs +++ b/EventSource4Net/IConnectionState.cs @@ -10,6 +10,6 @@ namespace EventSource4Net interface IConnectionState { EventSourceState State { get; } - Task Run(Action MsgReceivedCallback, CancellationToken cancelToken); + Task Run(Action MsgReceivedCallback, CancellationToken cancelToken, Dictionary headers); } } diff --git a/EventSource4Net/IWebRequester.cs b/EventSource4Net/IWebRequester.cs index 4a3367a..9e21b21 100644 --- a/EventSource4Net/IWebRequester.cs +++ b/EventSource4Net/IWebRequester.cs @@ -9,7 +9,6 @@ namespace EventSource4Net { public interface IWebRequester { - Task Get(Uri url); - + Task Get(Uri url, Dictionary headers = null); } } diff --git a/EventSource4Net/WebRequester.cs b/EventSource4Net/WebRequester.cs index 092ca1d..d1ffff5 100644 --- a/EventSource4Net/WebRequester.cs +++ b/EventSource4Net/WebRequester.cs @@ -1,20 +1,26 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Text; using System.Threading.Tasks; namespace EventSource4Net { class WebRequester : IWebRequester { - public Task Get(Uri url) + public Task Get(Uri url, Dictionary headers = null) { var wreq = (HttpWebRequest)WebRequest.Create(url); wreq.Method = "GET"; wreq.Proxy = null; + if (headers != null) + { + foreach (var header in headers) + { + wreq.Headers.Add(header.Key, header.Value); + } + } + var taskResp = Task.Factory.FromAsync(wreq.BeginGetResponse, wreq.EndGetResponse, null).ContinueWith(t => new ServerResponse(t.Result)); From 2c7d370e29f5de51c85f0e65fcbcb053231e84a3 Mon Sep 17 00:00:00 2001 From: Mads Tordal Date: Thu, 4 May 2017 08:43:55 +0000 Subject: [PATCH 2/3] Added test for connection with header and cleaned up the implementation. --- EventSource4Net.Test/EventSourceTest.cs | 39 +++++++++++++++++++ EventSource4Net.Test/TestableEventSource.cs | 4 ++ .../WebRequesterFactoryTest.cs | 17 +++++++- EventSource4Net/ConnectedState.cs | 10 ++--- EventSource4Net/ConnectingState.cs | 12 +++--- EventSource4Net/DisconnectedState.cs | 10 ++--- EventSource4Net/EventSource.cs | 12 +++++- EventSource4Net/IConnectionState.cs | 5 +-- 8 files changed, 86 insertions(+), 23 deletions(-) diff --git a/EventSource4Net.Test/EventSourceTest.cs b/EventSource4Net.Test/EventSourceTest.cs index 43cd90e..0148e75 100644 --- a/EventSource4Net.Test/EventSourceTest.cs +++ b/EventSource4Net.Test/EventSourceTest.cs @@ -81,5 +81,44 @@ public void TestSuccesfulConnection() } + [TestMethod] + public void TestSuccesfulConnectionWithHeaders() + { + // setup + Uri url = new Uri("http://test.com"); + CancellationTokenSource cts = new CancellationTokenSource(); + List states = new List(); + ServiceResponseMock response = new ServiceResponseMock(url, System.Net.HttpStatusCode.OK); + WebRequesterFactoryMock factory = new WebRequesterFactoryMock(response); + ManualResetEvent stateIsOpen = new ManualResetEvent(false); + + var headers = new Dictionary + { + { "x-key", "headerValue" } + }; + + TestableEventSource es = new TestableEventSource(url, factory, headers); + es.StateChanged += (o, e) => + { + states.Add(e.State); + if (e.State == EventSourceState.OPEN) + { + stateIsOpen.Set(); + cts.Cancel(); + } + }; + + + // act + stateIsOpen.Reset(); + + es.Start(cts.Token); + + stateIsOpen.WaitOne(); + + // assert + Assert.AreEqual(1, factory.WebRequesterMock.Response.Headers.Count); + Assert.AreEqual("headerValue", factory.WebRequesterMock.Response.Headers["x-key"]); + } } } diff --git a/EventSource4Net.Test/TestableEventSource.cs b/EventSource4Net.Test/TestableEventSource.cs index e12c41d..e87f158 100644 --- a/EventSource4Net.Test/TestableEventSource.cs +++ b/EventSource4Net.Test/TestableEventSource.cs @@ -11,6 +11,10 @@ class TestableEventSource : EventSource public TestableEventSource(Uri url,IWebRequesterFactory factory) : base(url,factory) { + } + public TestableEventSource(Uri url, IWebRequesterFactory factory, Dictionary headers) : base(url, factory, headers) + { + } } } diff --git a/EventSource4Net.Test/WebRequesterFactoryTest.cs b/EventSource4Net.Test/WebRequesterFactoryTest.cs index ecd86b8..00c71b0 100644 --- a/EventSource4Net.Test/WebRequesterFactoryTest.cs +++ b/EventSource4Net.Test/WebRequesterFactoryTest.cs @@ -18,7 +18,7 @@ public WebRequesterMock WebRequesterMock } public WebRequesterFactoryMock(ServiceResponseMock response) { - this.WebRequesterMock = new WebRequesterMock(response); + this.WebRequesterMock = new WebRequesterMock(response); } public IWebRequester Create() { @@ -41,6 +41,7 @@ public System.Threading.Tasks.Task Get(Uri url, Dictionary(() => { GetCalled.Set(); + Response.Headers = headers; return Response; }); } @@ -52,6 +53,7 @@ class ServiceResponseMock : IServerResponse private Stream mStream; private StreamWriter mStreamWriter; private Uri mUrl; + private Dictionary mHeaders; private HttpStatusCode mStatusCode; public ManualResetEvent StatusCodeCalled = new ManualResetEvent(false); @@ -62,6 +64,7 @@ public ServiceResponseMock(Uri url, HttpStatusCode statusCode) mStatusCode = statusCode; mStream = new TestableStream(); mStreamWriter = new StreamWriter(mStream); + mHeaders = new Dictionary(); } public System.Net.HttpStatusCode StatusCode @@ -78,6 +81,18 @@ public System.IO.Stream GetResponseStream() return mStream; } + public Dictionary Headers + { + get + { + return mHeaders; + } + set + { + mHeaders = value; + } + } + public Uri ResponseUri { get { return mUrl; } diff --git a/EventSource4Net/ConnectedState.cs b/EventSource4Net/ConnectedState.cs index 9bb4067..602a0c3 100644 --- a/EventSource4Net/ConnectedState.cs +++ b/EventSource4Net/ConnectedState.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Net; -using System.IO; using System.Threading.Tasks; using System.Threading; @@ -17,7 +15,7 @@ class ConnectedState : IConnectionState private ServerSentEvent mSse = null; private string mRemainingText = string.Empty; // the text that is not ended with a lineending char is saved for next call. private IServerResponse mResponse; - private Dictionary headers; + private Dictionary mHeaders; public EventSourceState State { get { return EventSourceState.OPEN; } } @@ -25,10 +23,10 @@ public ConnectedState(IServerResponse response, IWebRequesterFactory webRequeste { mResponse = response; mWebRequesterFactory = webRequesterFactory; - this.headers = headers; + mHeaders = headers; } - public Task Run(Action msgReceived, CancellationToken cancelToken, Dictionary headers) + public Task Run(Action msgReceived, CancellationToken cancelToken) { int i = 0; @@ -133,7 +131,7 @@ public Task Run(Action msgReceived, Cancellat //stream.Close(); //mResponse.Close(); //mResponse.Dispose(); - return new DisconnectedState(mResponse.ResponseUri, mWebRequesterFactory, headers); + return new DisconnectedState(mResponse.ResponseUri, mWebRequesterFactory, mHeaders); } } }); diff --git a/EventSource4Net/ConnectingState.cs b/EventSource4Net/ConnectingState.cs index 08da09b..e2c9f53 100644 --- a/EventSource4Net/ConnectingState.cs +++ b/EventSource4Net/ConnectingState.cs @@ -12,7 +12,7 @@ class ConnectingState : IConnectionState private Uri mUrl; private IWebRequesterFactory mWebRequesterFactory; - private Dictionary headers; + private Dictionary mHeaders; public EventSourceState State { get { return EventSourceState.CONNECTING; } } @@ -22,13 +22,13 @@ public ConnectingState(Uri url, IWebRequesterFactory webRequesterFactory, Dictio if (webRequesterFactory == null) throw new ArgumentNullException("Factory cant be null"); mUrl = url; mWebRequesterFactory = webRequesterFactory; - this.headers = headers; + mHeaders = headers; } - public Task Run(Action donothing, CancellationToken cancelToken, Dictionary headers) + public Task Run(Action donothing, CancellationToken cancelToken) { IWebRequester requester = mWebRequesterFactory.Create(); - var taskResp = requester.Get(mUrl, headers); + var taskResp = requester.Get(mUrl, mHeaders); return taskResp.ContinueWith(tsk => { @@ -37,7 +37,7 @@ public Task Run(Action donothing, Cancellatio IServerResponse response = tsk.Result; if (response.StatusCode == HttpStatusCode.OK) { - return new ConnectedState(response, mWebRequesterFactory, headers); + return new ConnectedState(response, mWebRequesterFactory, mHeaders); } else { @@ -45,7 +45,7 @@ public Task Run(Action donothing, Cancellatio } } - return new DisconnectedState(mUrl, mWebRequesterFactory, headers); + return new DisconnectedState(mUrl, mWebRequesterFactory, mHeaders); }); } } diff --git a/EventSource4Net/DisconnectedState.cs b/EventSource4Net/DisconnectedState.cs index ec3c955..289eb23 100644 --- a/EventSource4Net/DisconnectedState.cs +++ b/EventSource4Net/DisconnectedState.cs @@ -11,7 +11,7 @@ class DisconnectedState : IConnectionState { private Uri mUrl; private IWebRequesterFactory mWebRequesterFactory; - private Dictionary headers; + private Dictionary mHeaders; public EventSourceState State { @@ -23,15 +23,15 @@ public DisconnectedState(Uri url, IWebRequesterFactory webRequesterFactory, Dict if (url == null) throw new ArgumentNullException("Url cant be null"); mUrl = url; mWebRequesterFactory = webRequesterFactory; - this.headers = headers; + mHeaders = headers; } - public Task Run(Action donothing, CancellationToken cancelToken, Dictionary headers) + public Task Run(Action donothing, CancellationToken cancelToken) { if(cancelToken.IsCancellationRequested) - return Task.Factory.StartNew(() => { return new DisconnectedState(mUrl, mWebRequesterFactory, headers); }); + return Task.Factory.StartNew(() => { return new DisconnectedState(mUrl, mWebRequesterFactory, mHeaders); }); else - return Task.Factory.StartNew(() => { return new ConnectingState(mUrl, mWebRequesterFactory, headers); }); + return Task.Factory.StartNew(() => { return new ConnectingState(mUrl, mWebRequesterFactory, mHeaders); }); } } } diff --git a/EventSource4Net/EventSource.cs b/EventSource4Net/EventSource.cs index 7455dbd..0110acd 100644 --- a/EventSource4Net/EventSource.cs +++ b/EventSource4Net/EventSource.cs @@ -23,6 +23,9 @@ public class EventSource private CancellationToken mStopToken; private CancellationTokenSource mTokenSource = new CancellationTokenSource(); private Dictionary _headers; + private Uri url; + private IWebRequesterFactory factory; + private Dictionary headers; private IConnectionState CurrentState { @@ -63,6 +66,13 @@ public EventSource(Uri url, IWebRequesterFactory factory) Initialize(url, 0); } + public EventSource(Uri url, IWebRequesterFactory factory, Dictionary headers) + { + _webRequesterFactory = factory; + _headers = headers; + Initialize(url, 0); + } + private void Initialize(Uri url, int timeout) { _timeout = timeout; @@ -91,7 +101,7 @@ protected void Run() if (mTokenSource.IsCancellationRequested && CurrentState.State == EventSourceState.CLOSED) return; - mCurrentState.Run(this.OnEventReceived, mTokenSource.Token, _headers).ContinueWith(cs => + mCurrentState.Run(this.OnEventReceived, mTokenSource.Token).ContinueWith(cs => { CurrentState = cs.Result; Run(); diff --git a/EventSource4Net/IConnectionState.cs b/EventSource4Net/IConnectionState.cs index b3d008b..a17e36c 100644 --- a/EventSource4Net/IConnectionState.cs +++ b/EventSource4Net/IConnectionState.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -10,6 +7,6 @@ namespace EventSource4Net interface IConnectionState { EventSourceState State { get; } - Task Run(Action MsgReceivedCallback, CancellationToken cancelToken, Dictionary headers); + Task Run(Action MsgReceivedCallback, CancellationToken cancelToken); } } From c900594dd813ddbd46c1d7391fea9fcfe827dce0 Mon Sep 17 00:00:00 2001 From: Mads Tordal Date: Thu, 4 May 2017 08:50:21 +0000 Subject: [PATCH 3/3] Reverted accessibility level on EventSource constructor. Was changed by mistake. --- EventSource4Net/EventSource.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EventSource4Net/EventSource.cs b/EventSource4Net/EventSource.cs index 0110acd..389679f 100644 --- a/EventSource4Net/EventSource.cs +++ b/EventSource4Net/EventSource.cs @@ -60,13 +60,13 @@ public EventSource(Uri url, Dictionary headers, int timeout) /// Constructor for testing purposes /// /// The factory that generates the WebRequester to use. - public EventSource(Uri url, IWebRequesterFactory factory) + protected EventSource(Uri url, IWebRequesterFactory factory) { _webRequesterFactory = factory; Initialize(url, 0); } - public EventSource(Uri url, IWebRequesterFactory factory, Dictionary headers) + protected EventSource(Uri url, IWebRequesterFactory factory, Dictionary headers) { _webRequesterFactory = factory; _headers = headers;