Skip to content

Commit

Permalink
[WIP] (GH-300) Port Wyam.Hosting to netstandard2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
daveaglick committed Oct 15, 2018
2 parents fad4f9d + c76c2cd commit feca1d8
Show file tree
Hide file tree
Showing 25 changed files with 689 additions and 446 deletions.
2 changes: 1 addition & 1 deletion src/clients/Wyam/Wyam.csproj
Expand Up @@ -13,7 +13,7 @@
<ProjectReference Include="..\..\core\Wyam.Hosting\Wyam.Hosting.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="1.1.0" />
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
<PackageReference Include="NETStandard.Library" Version="2.0.1" />
Expand Down
36 changes: 17 additions & 19 deletions src/core/Wyam.Hosting/ApplicationBuilderExtensions.cs
Expand Up @@ -4,32 +4,30 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Owin.Builder;
using Owin;

namespace Wyam.Hosting
{
internal static class ApplicationBuilderExtensions
{
// http://stackoverflow.com/a/30742029/2001966
public static void UseOwinBuilder(this IApplicationBuilder app, Action<IAppBuilder> owinConfiguration)
{
app.UseOwin(
addToPipeline =>
{
addToPipeline(
next =>
{
AppBuilder builder = new AppBuilder();
owinConfiguration(builder);
builder.Run(ctx => next(ctx.Environment));
//public static void UseOwinBuilder(this IApplicationBuilder app, Action<IAppBuilder> owinConfiguration)
//{
// app.UseOwin(
// addToPipeline =>
// {
// addToPipeline(
// next =>
// {
// AppBuilder builder = new AppBuilder();
// owinConfiguration(builder);
// builder.Run(ctx => next(ctx.Environment));

// ReSharper disable once SuggestVarOrType_Elsewhere
var appFunc = (Func<IDictionary<string, object>, Task>)builder.Build(typeof(Func<IDictionary<string, object>, Task>));
// // ReSharper disable once SuggestVarOrType_Elsewhere
// var appFunc = (Func<IDictionary<string, object>, Task>)builder.Build(typeof(Func<IDictionary<string, object>, Task>));

return appFunc;
});
});
}
// return appFunc;
// });
// });
//}
}
}
29 changes: 29 additions & 0 deletions src/core/Wyam.Hosting/DefaultExtensionOptions.cs
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Wyam.Hosting
{
/// <summary>
/// Extenions to use when no extension is provided in the URL.
/// </summary>
public class DefaultExtensionsOptions
{
/// <summary>
/// Gets or sets the extensions.
/// </summary>
/// <value>
/// The extensions.
/// </value>
public IList<string> Extensions { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="DefaultExtensionsOptions"/> class.
/// </summary>
public DefaultExtensionsOptions() => Extensions = new List<string>
{
".htm",
".html"
};
}
}
26 changes: 26 additions & 0 deletions src/core/Wyam.Hosting/DefaultExtensions.cs
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Wyam.Hosting
{
public static class DefaultExtensionsExtensions
{
public static IApplicationBuilder UseDefaultExtensions(this IApplicationBuilder app, DefaultExtensionsOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}

return app.UseMiddleware<DefaultExtensionsMiddleware>(Options.Create(options));
}
}
}
75 changes: 75 additions & 0 deletions src/core/Wyam.Hosting/DefaultExtensionsMiddleware.cs
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Wyam.Hosting
{
public class DefaultExtensionsMiddleware
{
private readonly RequestDelegate _next;
private readonly IFileProvider _fileProvider;
private readonly DefaultExtensionsOptions _options;
private readonly ILogger _logger;

public DefaultExtensionsMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, IOptions<DefaultExtensionsOptions> options, ILoggerFactory loggerFactory)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}

if (hostingEnv == null)
{
throw new ArgumentNullException(nameof(hostingEnv));
}

if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_next = next;
_fileProvider = hostingEnv.WebRootFileProvider;
_options = options.Value;
_logger = loggerFactory.CreateLogger<DefaultExtensionsMiddleware>();
}

public async Task Invoke(HttpContext context)
{
if (IsGetOrHeadMethod(context.Request.Method)
&& !PathEndsInSlash(context.Request.Path))
{
// Check if there's a file with a matched extension, and rewrite the request if found
foreach (string extension in _options.Extensions)
{
string filePath = context.Request.Path.ToString() + extension;
IFileInfo fileInfo = _fileProvider.GetFileInfo(filePath);
if (fileInfo != null && fileInfo.Exists)
{
_logger.LogInformation($"Rewriting extensionless path to {filePath}");
context.Request.Path = new PathString(filePath);
break;
}
}
}
await _next(context);
}

private static bool IsGetOrHeadMethod(string method) =>
HttpMethods.IsGet(method) || HttpMethods.IsHead(method);

private static bool PathEndsInSlash(PathString path) =>
path.Value.EndsWith("/", StringComparison.Ordinal);
}
}
64 changes: 39 additions & 25 deletions src/core/Wyam.Hosting/LiveReload/ReloadClient.cs
Expand Up @@ -5,19 +5,17 @@
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using Fleck;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

using Owin.WebSocket;

using Wyam.Hosting.LiveReload.Messages;

namespace Wyam.Hosting.LiveReload
{
// Attempt to support the Livereload protocol v7.
// http://feedback.livereload.com/knowledgebase/articles/86174-livereload-protocol
internal class ReloadClient : FleckWebSocketConnection, IReloadClient
internal class ReloadClient : WebSocketConnection, IReloadClient
{
private readonly Guid _clientId = Guid.NewGuid();

Expand All @@ -36,24 +34,18 @@ internal class ReloadClient : FleckWebSocketConnection, IReloadClient

public ILogger Logger { private get; set; }

public override Task OnMessageReceived(ArraySegment<byte> message, WebSocketMessageType type)
{
string json = Encoding.UTF8.GetString(message.Array, message.Offset, message.Count);
HandleClientMessage(json);
return Task.CompletedTask;
}

public override void OnOpen()
{
SayHello();
base.OnOpen();
}

public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription)
public ReloadClient(ISocket socket,
Action<IWebSocketConnection> initialize,
Func<byte[], WebSocketHttpRequest> parseRequest,
Func<WebSocketHttpRequest, IHandler> handlerFactory,
Func<IEnumerable<string>, string> negotiateSubProtocol)
: base(socket,
initialize,
parseRequest,
handlerFactory,
negotiateSubProtocol)
{
IsConnected = false;
Log($"Lost connection with LiveReload client, status: {closeStatus}, description: {closeStatusDescription}");
base.OnClose(closeStatus, closeStatusDescription);
OnMessage = (str) => { };
}

public void NotifyOfChanges()
Expand All @@ -63,6 +55,26 @@ public void NotifyOfChanges()
SendObject(reloadMessage);
}

//public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription)
//{
// IsConnected = false;
// Log($"Lost connection with LiveReload client, status: {closeStatus}, description: {closeStatusDescription}");
// base.OnClose(closeStatus, closeStatusDescription);
//}

public string OnMessageReceived(ArraySegment<byte> message, WebSocketMessageType type)
{
string json = Encoding.UTF8.GetString(message.Array, message.Offset, message.Count);
HandleClientMessage(json);
return json;
}

//public override void OnOpen()
//{
// SayHello();
// base.OnOpen();
//}

private ILiveReloadMessage HandleClientMessage(string json)
{
BasicMessage parsedMessage = JsonConvert.DeserializeObject<BasicMessage>(json, _defaultSettings);
Expand All @@ -72,10 +84,12 @@ private ILiveReloadMessage HandleClientMessage(string json)
InfoMessage info = JsonConvert.DeserializeObject<InfoMessage>(json, _defaultSettings);
Log($"LiveReload client sent info: {info.Url}");
break;

case "hello":
HelloMessage hello = JsonConvert.DeserializeObject<HelloMessage>(json, _defaultSettings);
HandleHello(hello);
break;

default:
Log($"Unknown command received from LiveReload client: {parsedMessage.Command}");
break;
Expand All @@ -99,7 +113,7 @@ private void HandleHello(HelloMessage message)
$"(client: {string.Join(",", message.Protocols)}, " +
$"server: {string.Join(",", _supportedVersion)})";
Log(incompatibleMessage);
Abort();
Socket.Close(); //Abort();
}
else
{
Expand All @@ -108,6 +122,8 @@ private void HandleHello(HelloMessage message)
}
}

private void Log(string message) => Logger?.LogDebug($"{message} (LiveReload client: {_clientId})");

private void SayHello()
{
HelloMessage helloMessage = new HelloMessage
Expand All @@ -121,9 +137,7 @@ private void SendObject<T>(T obj)
{
string json = JsonConvert.SerializeObject(obj, _defaultSettings);
byte[] bytes = Encoding.UTF8.GetBytes(json); // UTF-8 by spec
SendText(bytes, true);
Socket.Send(bytes, null, null); // SendText(bytes, true);
}

private void Log(string message) => Logger?.LogDebug($"{message} (LiveReload client: {_clientId})");
}
}
21 changes: 21 additions & 0 deletions src/core/Wyam.Hosting/Middleware/DisableCacheExtensions.cs
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Wyam.Hosting.Middleware;

namespace Wyam.Hosting.Middleware
{
public static class DisableCacheExtensions
{
public static IApplicationBuilder UseDisableCache(this IApplicationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

return builder.UseMiddleware<DisableCacheMiddleware>();
}
}
}
34 changes: 34 additions & 0 deletions src/core/Wyam.Hosting/Middleware/DisableCacheMiddleware.cs
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Wyam.Hosting.Middleware
{
public class DisableCacheMiddleware
{
private readonly IHostingEnvironment _hostingEnv;
private readonly ILoggerFactory _loggerFactory;
private readonly RequestDelegate _next;

public DisableCacheMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, ILoggerFactory loggerFactory)
{
_next = next;
_hostingEnv = hostingEnv;
_loggerFactory = loggerFactory;
}

public async Task Invoke(HttpContext context)
{
await _next(context);

context.Response.Headers.Append("Cache-Control", "no-cache, no-store, must-revalidate");
context.Response.Headers.Append("Pragma", "no-cache");
context.Response.Headers.Append("Expires", "0");
}
}
}

0 comments on commit feca1d8

Please sign in to comment.