Hello,
im trying to implement simple POC for Server Send Events using middleware in asp.net core... this is what i have so far:
Server:
using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; namespace cshdd.Middlewares { public class ServerSendEventsMiddleware { private readonly RequestDelegate _Next; private readonly string _Url; public ServerSendEventsMiddleware(RequestDelegate next, string url) { _Next = next; _Url = url; } public async Task Invoke(HttpContext httpContext) { if (httpContext.Request.Path.Value == _Url) { Initialize(httpContext);
//await InitializeAsync(httpContext); return; } await _Next(httpContext); } private void Initialize(HttpContext httpContext) { httpContext.Response.Clear(); httpContext.Response.ContentType = "text/event-stream"; httpContext.Response.Headers.Add("Cache-Control", "no-cache"); httpContext.Response.Headers.Add("Connection", "keep-alive"); httpContext.Response.Headers.Add("Keep-Alive", "timeout=15, max=100"); var stream = httpContext.Response.Body; // block 1 using (var writer = new StreamWriter(stream, Encoding.UTF8, 4096, true)) { writer.Write("data:initialize\n\n"); writer.Flush(); } // block 2 Task.Run(() => { using (var writer = new StreamWriter(stream, Encoding.UTF8, 4096, true)) { for (int i = 0; i < 10; i++) { writer.Write("data:test + " + i + " \n\n"); writer.Flush(); Thread.Sleep(1000); } } }); } private async Task InitializeAsync(HttpContext httpContext) { httpContext.Response.Clear(); httpContext.Response.ContentType = "text/event-stream"; httpContext.Response.Headers.Add("Cache-Control", "no-cache"); httpContext.Response.Headers.Add("Connection", "keep-alive"); httpContext.Response.Headers.Add("Keep-Alive", "timeout=15, max=100"); var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 4096, true); writer.Write("data:initialize\n\n"); await writer.FlushAsync(); for (int i = 0; i < 10; i++) { writer.Write("data:test + " + i + " \n\n"); writer.FlushAsync(); Thread.Sleep(1000); } } } }
Client: (jQuery is not needed but for simplicity ...)
$(function () { var source = new EventSource(url); // url is same value as in ServerSendEventsMiddleware ctor on server console.log("init"); source.addEventListener("message", function (e) { console.log("message: " + e.data); }, false); source.addEventListener("open", function(e) { console.log("open"); }, false); source.addEventListener("error", function(e) { if (e.target.readyState === EventSource.CLOSED) { console.log("error: CLOSED"); } else if (e.target.readyState === EventSource.CONNECTING) { console.log("error: CONNECTING"); } }, false); });
- block 1 works fine
- block 2 throws null references exception on writer.Flush() - probably because by that time httpContext.Response.Body is set to null
- async method will return result to client after it is completed and not on first await writer.FlushAsync();
one of recommendations i found on the internet is to set Response.BufferOutput to false but i cannot find any property or method with similar name on httpContext / httpResponse used in asp.net core ...
And my question at last:
How to create open connection with client without closing it? Eg: client send request, server sends back empty response and stores response stream somewhere, then some time later server writes message into stored stream and client react on it.
Cheers.
PS: not sure if this is usefull but my project.json looks like this:
{"userSecretsId": "cshdd-329afda3-c19b-499b-98bb-8ed98fe6fd18","version": "1.0.0-*","compilationOptions": {"emitEntryPoint": true },"dependencies": {"AspNet.Hosting.Katana.Extensions": "1.0.0-alpha1","AspNet.Identity3.MongoDB": "2.1.0-*","Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final","Microsoft.AspNet.Authentication.Facebook": "1.0.0-rc1-final","Microsoft.AspNet.Diagnostics.Entity": "7.0.0-rc1-final","Microsoft.AspNet.Identity": "3.0.0-rc1-final","Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final","Microsoft.AspNet.Mvc": "6.0.0-rc1-final","Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final","Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final","Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final","Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final","Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final","Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final","Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final","Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final","Microsoft.Extensions.Logging": "1.0.0-rc1-final","Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final","Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final","Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc1-final","Facebook": "7.0.10-beta","SharpZipLib": "0.86.0" },"commands": {"web": "Microsoft.AspNet.Server.Kestrel" },"frameworks": {"dnx451": { } },"exclude": ["wwwroot","node_modules" ],"publishExclude": ["**.user","**.vspscc" ],"scripts": {"prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ] } }