diff --git a/Ooui/UI.cs b/Ooui/UI.cs index 5f63b86..e93ea7b 100644 --- a/Ooui/UI.cs +++ b/Ooui/UI.cs @@ -12,6 +12,8 @@ namespace Ooui { public static class UI { + static readonly ManualResetEvent started = new ManualResetEvent (false); + static CancellationTokenSource serverCts; static readonly Dictionary publishedPaths = @@ -77,27 +79,60 @@ namespace Ooui } } - public static void Publish (string path, Func elementCtor) + static void Publish (string path, RequestHandler handler) { - Console.WriteLine ($"PUBLISH {path}"); - var handler = new ElementHandler (elementCtor); + Console.WriteLine ($"PUBLISH {path} {handler}"); lock (publishedPaths) publishedPaths[path] = handler; Start (); } + public static void Publish (string path, Func elementCtor) + { + Publish (path, new ElementHandler (elementCtor)); + } + public static void Publish (string path, Element element) { Publish (path, () => element); } + public static void PublishFile (string filePath) + { + var path = "/" + System.IO.Path.GetFileName (filePath); + PublishFile (path, filePath); + } + + public static void PublishFile (string path, string filePath, string contentType = null, string contentEncoding = null) + { + var data = System.IO.File.ReadAllBytes (filePath); + if (contentType == null) { + contentType = GuessContentType (path, filePath); + } + Publish (path, new DataHandler (data, contentType, contentEncoding)); + } + + static string GuessContentType (string path, string filePath) + { + return null; + } + public static void Present (string path, object presenter = null) { - var localHost = host == "*" ? "localhost" : host; - var url = $"http://{localHost}:{port}{path}"; + WaitUntilStarted (); + var url = GetUrl (path); Console.WriteLine ($"PRESENT {url}"); Platform.OpenBrowser (url, presenter); } + public static string GetUrl (string path) + { + var localhost = host == "*" ? "localhost" : host; + var url = $"http://{localhost}:{port}{path}"; + return url; + } + + public static void WaitUntilStarted () => started.WaitOne (); + static void Start () { if (serverCts != null) return; @@ -112,6 +147,7 @@ namespace Ooui var scts = serverCts; if (scts == null) return; serverCts = null; + started.Reset (); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine ($"Stopping..."); @@ -131,13 +167,13 @@ namespace Ooui { HttpListener listener = null; - var started = false; - while (!started && !token.IsCancellationRequested) { + started.Reset (); + while (!started.WaitOne(0) && !token.IsCancellationRequested) { try { listener = new HttpListener (); listener.Prefixes.Add (listenerPrefix); listener.Start (); - started = true; + started.Set (); } catch (System.Net.Sockets.SocketException ex) when (ex.SocketErrorCode == System.Net.Sockets.SocketError.AddressAlreadyInUse) { @@ -244,6 +280,39 @@ namespace Ooui } } + class DataHandler : RequestHandler + { + readonly byte[] data; + readonly string contentType; + readonly string contentEncoding; + + public DataHandler (byte[] data, string contentType = null, string contentEncoding = null) + { + this.data = data; + this.contentType = contentType; + this.contentEncoding = contentEncoding; + } + + public override void Respond (HttpListenerContext listenerContext, CancellationToken token) + { + var url = listenerContext.Request.Url; + var path = url.LocalPath; + var response = listenerContext.Response; + + response.StatusCode = 200; + if (!string.IsNullOrEmpty (contentType)) + response.ContentType = contentType; + if (!string.IsNullOrEmpty (contentEncoding)) + response.ContentType = contentEncoding; + response.ContentLength64 = data.LongLength; + + using (var s = response.OutputStream) { + s.Write (data, 0, data.Length); + } + response.Close (); + } + } + static async void ProcessWebSocketRequest (HttpListenerContext listenerContext, CancellationToken serverToken) { // diff --git a/Tests/ButtonTests.cs b/Tests/ButtonTests.cs index 74a6409..e0e3eed 100644 --- a/Tests/ButtonTests.cs +++ b/Tests/ButtonTests.cs @@ -5,7 +5,7 @@ using NUnit.Framework; using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; using TestMethodAttribute = NUnit.Framework.TestCaseAttribute; #else -using MicrosofVisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; #endif using Ooui; diff --git a/Tests/UITests.cs b/Tests/UITests.cs index b99965d..11fffb5 100644 --- a/Tests/UITests.cs +++ b/Tests/UITests.cs @@ -1,4 +1,6 @@ using System; +using System.Net.Http; +using System.Threading.Tasks; #if NUNIT using NUnit.Framework; @@ -60,5 +62,51 @@ namespace Tests Assert.AreEqual ("inherit", UI.Styles[".t3"].BackgroundColor); Assert.AreEqual ("", UI.Styles.ToString ()); } + + [TestMethod] + public void PublishMissingFileFails () + { + try { + UI.PublishFile ("/file", "a file that doesn't exist"); + Assert.Fail ("Publishing not existing file should fail"); + } + catch (System.IO.FileNotFoundException) { + } + } + + [TestMethod] + public void PublishEmptyFile () + { + var f = System.IO.Path.GetTempFileName (); + UI.PublishFile ("/file", f); + UI.WaitUntilStarted (); + var c = new System.Net.WebClient (); + var r = c.DownloadString (UI.GetUrl ("/file")); + Assert.AreEqual ("", r); + } + + [TestMethod] + public void PublishTextFile () + { + var f = System.IO.Path.GetTempFileName (); + System.IO.File.WriteAllText (f, "Test Ooui Text File", System.Text.Encoding.UTF8); + UI.PublishFile ("/text-file", f, "text/plain", "utf-8"); + UI.WaitUntilStarted (); + var c = new System.Net.WebClient (); + var r = c.DownloadString (UI.GetUrl ("/text-file")); + Assert.AreEqual ("Test Ooui Text File", r); + } + + [TestMethod] + public void PublishFileWithoutPath () + { + var f = System.IO.Path.GetTempFileName (); + System.IO.File.WriteAllText (f, "Test Ooui Text File 2", System.Text.Encoding.UTF8); + UI.PublishFile (f); + UI.WaitUntilStarted (); + var c = new System.Net.WebClient (); + var r = c.DownloadString (UI.GetUrl ("/" + System.IO.Path.GetFileName (f))); + Assert.AreEqual ("Test Ooui Text File 2", r); + } } }