Skip to content
This repository was archived by the owner on Sep 3, 2024. It is now read-only.

Commit 04d4fdf

Browse files
netcore devserver
1 parent a381983 commit 04d4fdf

17 files changed

Lines changed: 728 additions & 154 deletions

src/KestrelPureOwin/Constants.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
namespace KestrelPureOwin
2+
{
3+
internal static class Constants
4+
{
5+
public static class Owin
6+
{
7+
public const string CallCancelled = "owin.CallCancelled";
8+
public const string OwinVersion = "owin.Version";
9+
10+
public static class Request
11+
{
12+
public const string Body = "owin.RequestBody";
13+
public const string Headers = "owin.RequestHeaders";
14+
public const string Method = "owin.RequestMethod";
15+
public const string Path = "owin.RequestPath";
16+
public const string PathBase = "owin.RequestPathBase";
17+
public const string Protocol = "owin.RequestProtocol";
18+
public const string QueryString = "owin.RequestQueryString";
19+
public const string Scheme = "owin.RequestScheme";
20+
public const string User = "owin.RequestUser";
21+
public const string Id = "owin.RequestId";
22+
}
23+
24+
public static class Response
25+
{
26+
public const string Body = "owin.ResponseBody";
27+
public const string Headers = "owin.ResponseHeaders";
28+
public const string StatusCode = "owin.ResponseStatusCode";
29+
public const string ReasonPhrase = "owin.ResponseReasonPhrase";
30+
public const string Protocol = "owin.ResponseProtocol";
31+
}
32+
}
33+
34+
public static class Server
35+
{
36+
public const string RemoteIpAddress = "server.RemoteIpAddress";
37+
public const string RemotePort = "server.RemotePort";
38+
public const string LocalIpAddress = "server.LocalIpAddress";
39+
public const string LocalPort = "server.LocalPort";
40+
public const string User = "server.User";
41+
public const string Features = "server.Features";
42+
public const string Capabilities = "server.Capabilities";
43+
public const string Name = "server.Name";
44+
public const string OnSendingHeaders = "server.OnSendingHeaders";
45+
}
46+
47+
public static class Host
48+
{
49+
public const string Addresses = "host.Addresses";
50+
}
51+
}
52+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Hosting.Internal;
7+
using Microsoft.AspNetCore.Hosting.Server.Features;
8+
using Microsoft.AspNetCore.Server.Kestrel;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.Extensions.Options;
11+
using static KestrelPureOwin.Constants;
12+
13+
namespace KestrelPureOwin
14+
{
15+
using AppFunc = Func<IDictionary<string, object>, Task>;
16+
17+
using MidFunc = Func<
18+
Func<IDictionary<string, object>, Task>,
19+
Func<IDictionary<string, object>, Task>>;
20+
21+
using BuildFunc = Action<Func<
22+
Func<IDictionary<string, object>, Task>,
23+
Func<IDictionary<string, object>, Task>>>;
24+
25+
public class KestrelOwinServer : IDisposable
26+
{
27+
public KestrelOwinServer() : this(new Dictionary<string, object>())
28+
{
29+
}
30+
31+
public KestrelOwinServer(IDictionary<string, object> properties) : this(GetOptions(properties))
32+
{
33+
var addresses = properties
34+
.Get<IList<IDictionary<string, object>>>(Host.Addresses)
35+
?? new List<IDictionary<string, object>>();
36+
37+
foreach (var address in addresses)
38+
{
39+
40+
}
41+
}
42+
43+
public KestrelOwinServer(KestrelServerOptions options)
44+
{
45+
if (options == null)
46+
{
47+
throw new ArgumentNullException(nameof(options));
48+
}
49+
50+
var wrappedOptions = Options.Create(options);
51+
52+
var lifetime = new ApplicationLifetime();
53+
54+
var loggerFactory = new LoggerFactory();
55+
56+
Server = new KestrelServer(wrappedOptions, lifetime, loggerFactory);
57+
}
58+
59+
private static KestrelServerOptions GetOptions(IDictionary<string, object> properties)
60+
=> new KestrelServerOptions();
61+
62+
public KestrelServer Server { get; }
63+
64+
public void Run(string url, Action<BuildFunc> configure)
65+
{
66+
var done = new ManualResetEventSlim(false);
67+
68+
Console.CancelKeyPress += (s, e) =>
69+
{
70+
Console.WriteLine("Application is shutting down...");
71+
72+
done.Set();
73+
74+
e.Cancel = true;
75+
};
76+
77+
Start(url, configure);
78+
79+
done.Wait();
80+
}
81+
82+
public void Start(string url, Action<BuildFunc> configure)
83+
{
84+
var application = ConfigureApplication(configure);
85+
86+
var addresses = Server.Features.Get<IServerAddressesFeature>();
87+
88+
if (addresses == null)
89+
{
90+
Server.Features.Set(addresses = new ServerAddressesFeature());
91+
}
92+
93+
addresses.Addresses.Add(url);
94+
95+
foreach (var address in addresses.Addresses)
96+
{
97+
Console.WriteLine($"Now listening on: {address}");
98+
}
99+
100+
Server.Start(application);
101+
}
102+
103+
public void Dispose() => Server.Dispose();
104+
105+
private static OwinApplication ConfigureApplication(Action<BuildFunc> configure)
106+
{
107+
var middleware = new List<MidFunc>();
108+
109+
var builder = new BuildFunc(middleware.Add);
110+
111+
configure(builder);
112+
113+
return BuildApplication(middleware);
114+
}
115+
116+
private static OwinApplication BuildApplication(IEnumerable<MidFunc> middleware)
117+
{
118+
var end = new AppFunc(env => Tasks.Completed);
119+
120+
var app = middleware.Reverse().Aggregate(end, (current, next) => next(current));
121+
122+
return new OwinApplication(app);
123+
}
124+
}
125+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net451;netstandard1.3</TargetFrameworks>
5+
<AssemblyName>KestrelPureOwin</AssemblyName>
6+
<PackageId>KestrelPureOwin</PackageId>
7+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
8+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
9+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
14+
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
15+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="1.1.0" />
16+
</ItemGroup>
17+
18+
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
19+
<Reference Include="System" />
20+
<Reference Include="Microsoft.CSharp" />
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Hosting.Server;
7+
using Microsoft.AspNetCore.Http.Features;
8+
using Microsoft.AspNetCore.Http.Features.Authentication;
9+
using Microsoft.Extensions.ObjectPool;
10+
using static KestrelPureOwin.Constants;
11+
12+
namespace KestrelPureOwin
13+
{
14+
using AppFunc = Func<IDictionary<string, object>, Task>;
15+
16+
internal class OwinApplication : IHttpApplication<Dictionary<string, object>>
17+
{
18+
private static readonly ObjectPoolProvider PoolProvider = new DefaultObjectPoolProvider();
19+
20+
public OwinApplication(AppFunc application)
21+
{
22+
Application = application;
23+
EnvironmentPool = PoolProvider.Create<Dictionary<string, object>>();
24+
}
25+
26+
private AppFunc Application { get; }
27+
28+
private ObjectPool<Dictionary<string, object>> EnvironmentPool { get; }
29+
30+
public Dictionary<string, object> CreateContext(IFeatureCollection features)
31+
{
32+
var environment = EnvironmentPool.Get();
33+
34+
var request = features.Get<IHttpRequestFeature>();
35+
var response = features.Get<IHttpResponseFeature>();
36+
var connection = features.Get<IHttpConnectionFeature>();
37+
var lifetime = features.Get<IHttpRequestLifetimeFeature>();
38+
var identifier = features.Get<IHttpRequestIdentifierFeature>();
39+
var authentication = features.Get<IHttpAuthenticationFeature>();
40+
41+
environment.Clear();
42+
43+
environment.Set(Owin.Request.Body, request.Body);
44+
environment.Set(Owin.Request.Headers, new OwinHeaderDictionary(request.Headers));
45+
environment.Set(Owin.Request.Method, request.Method);
46+
environment.Set(Owin.Request.Path, request.Path);
47+
environment.Set(Owin.Request.PathBase, request.PathBase);
48+
environment.Set(Owin.Request.Protocol, request.Protocol);
49+
environment.Set(Owin.Request.QueryString, request.QueryString.TrimStart('?'));
50+
environment.Set(Owin.Request.Scheme, request.Scheme);
51+
environment.Set(Owin.Request.User, authentication?.User);
52+
environment.Set(Owin.Request.Id, identifier?.TraceIdentifier);
53+
54+
environment.Set(Owin.Response.Body, response.Body);
55+
environment.Set(Owin.Response.Headers, new OwinHeaderDictionary(response.Headers));
56+
environment.Set(Owin.Response.StatusCode, response.StatusCode);
57+
environment.Set(Owin.Response.ReasonPhrase, response.ReasonPhrase);
58+
environment.Set(Owin.Response.Protocol, request.Protocol);
59+
60+
environment.Set(Owin.CallCancelled, lifetime?.RequestAborted ?? CancellationToken.None);
61+
environment.Set(Owin.OwinVersion, "1.1");
62+
63+
environment.Set(Server.OnSendingHeaders, CreateCallback(response.OnStarting));
64+
environment.Set(Server.RemoteIpAddress, connection?.RemoteIpAddress.ToString());
65+
environment.Set(Server.RemotePort, connection?.RemotePort.ToString());
66+
environment.Set(Server.LocalIpAddress, connection?.LocalIpAddress.ToString());
67+
environment.Set(Server.LocalPort, connection?.LocalPort.ToString());
68+
environment.Set(Server.User, authentication?.User);
69+
environment.Set(Server.Features, features);
70+
71+
response.OnStarting(SetStatus, (response, environment));
72+
73+
return environment;
74+
}
75+
76+
private static Task SetStatus(object state)
77+
{
78+
var (response, environment) = ((IHttpResponseFeature, Dictionary<string, object>)) state;
79+
80+
response.StatusCode = environment.Get<int>(Owin.Response.StatusCode);
81+
response.ReasonPhrase = environment.Get<string>(Owin.Response.ReasonPhrase);
82+
83+
return Tasks.Completed;
84+
}
85+
86+
public async Task ProcessRequestAsync(Dictionary<string, object> environment)
87+
{
88+
await Application.Invoke(environment).ConfigureAwait(false);
89+
90+
var features = environment.Get<IFeatureCollection>(Server.Features);
91+
92+
var response = features.Get<IHttpResponseFeature>();
93+
94+
var owinHeaders = environment.Get<IDictionary<string, string[]>>(Owin.Response.Headers);
95+
96+
response.Body = environment.Get<Stream>(Owin.Response.Body);
97+
response.Headers = new ReverseOwinHeaderDictionary(owinHeaders);
98+
}
99+
100+
public void DisposeContext(Dictionary<string, object> environment, Exception exception)
101+
{
102+
EnvironmentPool.Return(environment);
103+
}
104+
105+
private static Action<Action<object>, object> CreateCallback(Action<Func<object, Task>, object> onStarting)
106+
{
107+
return (callback, state) => onStarting(WrapCallback(callback), state);
108+
}
109+
110+
private static Func<object, Task> WrapCallback(Action<object> callback)
111+
{
112+
return state =>
113+
{
114+
callback(state);
115+
return Tasks.Completed;
116+
};
117+
}
118+
}
119+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
3+
namespace KestrelPureOwin
4+
{
5+
internal static class OwinEnvironmentExtensions
6+
{
7+
public static T Get<T>(this IDictionary<string, object> environment, string key)
8+
{
9+
object value;
10+
if (environment.TryGetValue(key, out value) && value is T)
11+
{
12+
return (T) value;
13+
}
14+
15+
return default(T);
16+
}
17+
18+
public static void Set<T>(this IDictionary<string, object> environment, string key, T value)
19+
{
20+
environment[key] = value;
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)