PushStreamContent and Self hosting server

Topics: ASP.NET Web API, General
Jul 20, 2013 at 4:44 PM
Edited Jul 20, 2013 at 4:46 PM
Hi,

I'm trying to make the PushStreamContent works under the HttpSelfHostServer, without any success. I made a small test (basically, took the PushContentControllerSample and converted to a console app) and I can't make it to work. Nothing was changed, only that now the controller is hosted under the HttpSelfHostServer.

Here is the code I'm using:
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Client {
    using System.Web.Http;
    using System.Web.Http.SelfHost;

    class Program {
        static readonly Uri _address = new Uri("http://localhost/ddsrpc/PushContent");

        static void RunClient() {
            HttpSelfHostServer httpServer = new HttpSelfHostServer(new HttpSelfHostConfiguration("http://localhost"));
            httpServer.Configuration.Routes.MapHttpRoute("SseTransport", "ddsrpc/{controller}");
            httpServer.OpenAsync().Wait();

            HttpClient client = new HttpClient();

            HttpRequestMessage request = new HttpRequestMessage {
                Method = HttpMethod.Get,
                RequestUri = _address,
            };

            client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ContinueWith(
                getTask => {
                    if (getTask.IsCanceled) {
                        return;
                    }
                    if (getTask.IsFaulted) {
                        throw getTask.Exception;
                    }
                    HttpResponseMessage response = getTask.Result;

                    response.Content.ReadAsStreamAsync().ContinueWith(
                        (streamTask) => {
                            if (streamTask.IsCanceled) {
                                return;
                            }
                            if (streamTask.IsFaulted) {
                                throw streamTask.Exception;
                            }

                            byte[] readBuffer = new byte[512];
                            ReadResponseStream(streamTask.Result, readBuffer);
                        });
                });
        }

        private static void ReadResponseStream(Stream rspStream, byte[] readBuffer) {
            Task.Factory.FromAsync<byte[], int, int, int>(rspStream.BeginRead, rspStream.EndRead, readBuffer, 0, readBuffer.Length, state: null).ContinueWith(
                (readTask) => {
                    if (readTask.IsCanceled) {
                        return;
                    }
                    if (readTask.IsFaulted) {
                        throw readTask.Exception;
                    }

                    int bytesRead = readTask.Result;
                    string content = Encoding.UTF8.GetString(readBuffer, 0, bytesRead);
                    Console.WriteLine("Received: {0}", content);

                    if (bytesRead != 0) {
                        ReadResponseStream(rspStream, readBuffer);
                    }
                });
        }

        static void Main(string[] args) {
            RunClient();

            Console.WriteLine("Hit ENTER to exit...");
            Console.ReadLine();
        }
    }

    public class PushContentController : ApiController {
        private static readonly Lazy<Timer> _timer = new Lazy<Timer>(() => new Timer(TimerCallback, null, 0, 1000));
        private static readonly ConcurrentDictionary<StreamWriter, StreamWriter> _outputs = new ConcurrentDictionary<StreamWriter, StreamWriter>();

        [HttpGet]
        public HttpResponseMessage GetUpdates(HttpRequestMessage request) {
            Timer t = _timer.Value;
            request.Headers.AcceptEncoding.Clear();
            HttpResponseMessage response = request.CreateResponse();
            response.Content = new PushStreamContent(OnStreamAvailable, "text/plain");
            return response;
        }

        private static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context) {
            StreamWriter sWriter = new StreamWriter(stream);
            _outputs.TryAdd(sWriter, sWriter);
        }

        private static void TimerCallback(object state) {
            foreach (var kvp in _outputs.ToArray()) {
                try {
                    kvp.Value.Write(DateTime.Now);
                    kvp.Value.Flush();
                } catch {
                    StreamWriter sWriter;
                    _outputs.TryRemove(kvp.Value, out sWriter);
                }
            }
        }
    }
}
Of course under IIS (controller under IIS...) this works fine. But under HttpSelfHostServer I never receive anything, unless I close the stream, where I then get only one notification (and of course that's not the idea).

Is this by design? It's a bug in the above code?