Adding support for 'k web --server kestrel'

This commit is contained in:
Louis DeJardin 2014-06-13 13:10:46 -07:00
parent 9c7cb6a958
commit 6ac6419f4f
22 changed files with 475 additions and 245 deletions

View File

@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleApp", "src\SampleApp\SampleApp.kproj", "{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Kestrel", "src\Kestrel\Kestrel.kproj", "{30B7617E-58EF-4382-B3EA-5B2E718CF1A6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -32,6 +34,10 @@ Global
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}.Release|Any CPU.Build.0 = Release|Any CPU
{30B7617E-58EF-4382-B3EA-5B2E718CF1A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30B7617E-58EF-4382-B3EA-5B2E718CF1A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30B7617E-58EF-4382-B3EA-5B2E718CF1A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30B7617E-58EF-4382-B3EA-5B2E718CF1A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

36
src/Kestrel/Kestrel.kproj Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>30b7617e-58ef-4382-b3ea-5b2e718cf1a6</ProjectGuid>
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup Condition="$(OutputType) == 'Console'">
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="$(OutputType) == 'Web'">
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="ServerFactory.cs" />
<Compile Include="ServerInformation.cs" />
<Compile Include="ServerRequest.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="project.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,35 @@
using Microsoft.AspNet.Hosting.Server;
using System;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.ConfigurationModel;
using System.Threading.Tasks;
using Microsoft.AspNet.Server.Kestrel;
namespace Kestrel
{
/// <summary>
/// Summary description for ServerFactory
/// </summary>
public class ServerFactory : IServerFactory
{
public IServerInformation Initialize(IConfiguration configuration)
{
var information = new ServerInformation();
information.Initialize(configuration);
return information;
}
public IDisposable Start(IServerInformation serverInformation, Func<object, Task> application)
{
var information = (ServerInformation)serverInformation;
var engine = new KestrelEngine();
engine.Start(1);
engine.CreateServer(async frame =>
{
var request = new ServerRequest(frame);
await application.Invoke(request);
});
return engine;
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.ConfigurationModel;
namespace Kestrel
{
public class ServerInformation : IServerInformation
{
public void Initialize(IConfiguration configuration)
{
}
public string Name
{
get
{
return "Kestrel";
}
}
}
}

View File

@ -0,0 +1,180 @@
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.Server.Kestrel.Http;
using System;
using System.Collections.Generic;
using System.IO;
namespace Kestrel
{
public class ServerRequest : IHttpRequestFeature, IHttpResponseFeature
{
Frame _frame;
string _scheme;
string _pathBase;
public ServerRequest(Frame frame)
{
_frame = frame;
}
string IHttpRequestFeature.Protocol
{
get
{
return _frame.HttpVersion;
}
set
{
_frame.HttpVersion = value;
}
}
string IHttpRequestFeature.Scheme
{
get
{
return _scheme ?? "http";
}
set
{
_scheme = value;
}
}
string IHttpRequestFeature.Method
{
get
{
return _frame.Method;
}
set
{
_frame.Method = value;
}
}
string IHttpRequestFeature.PathBase
{
get
{
return _pathBase ?? "";
}
set
{
_pathBase = value;
}
}
string IHttpRequestFeature.Path
{
get
{
return _frame.Path;
}
set
{
_frame.Path = value;
}
}
string IHttpRequestFeature.QueryString
{
get
{
return _frame.QueryString;
}
set
{
_frame.QueryString = value;
}
}
IDictionary<string, string[]> IHttpRequestFeature.Headers
{
get
{
return _frame.RequestHeaders;
}
set
{
_frame.RequestHeaders = value;
}
}
Stream IHttpRequestFeature.Body
{
get
{
return _frame.RequestBody;
}
set
{
_frame.RequestBody = value;
}
}
int IHttpResponseFeature.StatusCode
{
get
{
return _frame.StatusCode;
}
set
{
_frame.StatusCode = value;
}
}
string IHttpResponseFeature.ReasonPhrase
{
get
{
return _frame.ReasonPhrase;
}
set
{
_frame.ReasonPhrase = value;
}
}
IDictionary<string, string[]> IHttpResponseFeature.Headers
{
get
{
return _frame.ResponseHeaders;
}
set
{
_frame.ResponseHeaders = value;
}
}
Stream IHttpResponseFeature.Body
{
get
{
return _frame.ResponseBody;
}
set
{
_frame.ResponseBody = value;
}
}
void IHttpResponseFeature.OnSendingHeaders(Action<object> callback, object state)
{
_frame.OnSendingHeaders(callback, state);
}
}
}

17
src/Kestrel/project.json Normal file
View File

@ -0,0 +1,17 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Hosting": "0.1-*",
"Microsoft.AspNet.Server.Kestrel": "0.1-*"
},
"configurations": {
"net45": {
"dependencies": { }
},
"k10": {
"dependencies": {
"System.Runtime": "4.0.20.0"
}
}
}
}

View File

@ -1,51 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.HttpFeature;
using System;
using System.Collections.Generic;
using System.IO;
namespace Microsoft.AspNet.Server.Kestrel
{
/// <summary>
/// Summary description for CallContext
/// </summary>
public class CallContext :
IHttpRequestFeature,
IHttpResponseFeature
{
public CallContext()
{
((IHttpResponseFeature)this).StatusCode = 200;
}
Stream IHttpResponseFeature.Body { get; set; }
Stream IHttpRequestFeature.Body { get; set; }
IDictionary<string, string[]> IHttpResponseFeature.Headers { get; set; }
IDictionary<string, string[]> IHttpRequestFeature.Headers { get; set; }
string IHttpRequestFeature.Method { get; set; }
string IHttpRequestFeature.Path { get; set; }
string IHttpRequestFeature.PathBase { get; set; }
string IHttpRequestFeature.Protocol { get; set; }
string IHttpRequestFeature.QueryString { get; set; }
string IHttpResponseFeature.ReasonPhrase { get; set; }
string IHttpRequestFeature.Scheme { get; set; }
int IHttpResponseFeature.StatusCode { get; set; }
void IHttpResponseFeature.OnSendingHeaders(Action<object> callback, object state)
{
}
}
}

View File

@ -1,9 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.HttpFeature;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -43,8 +43,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public class Frame : FrameContext, IFrameControl
{
Mode _mode;
enum Mode
{
StartLine,
@ -53,41 +51,43 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
Terminated,
}
private string _method;
private string _requestUri;
private string _path;
private string _queryString;
private string _httpVersion;
private readonly IDictionary<string, string[]> _requestHeaders =
new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
readonly IDictionary<string, string[]> _responseHeaders =
new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
private MessageBody _messageBody;
Mode _mode;
private bool _resultStarted;
private bool _keepAlive;
private CallContext _callContext;
/*
//IDictionary<string, object> _environment;
CancellationTokenSource _cts = new CancellationTokenSource();
*/
FrameResponseStream _outputStream;
FrameRequestStream _inputStream;
FrameDuplexStream _duplexStream;
Task _upgradeTask = _completedTask;
static readonly Task _completedTask = Task.FromResult(0);
List<KeyValuePair<Action<object>, object>> _onSendingHeaders;
object _onSendingHeadersSync = new Object();
Stream _duplexStream;
public Frame(ConnectionContext context) : base(context)
{
FrameControl = this;
StatusCode = 200;
RequestHeaders = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
ResponseHeaders = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
}
public string Method { get; set; }
public string RequestUri { get; set; }
public string Path { get; set; }
public string QueryString { get; set; }
public string HttpVersion { get; set; }
public IDictionary<string, string[]> RequestHeaders { get; set; }
public MessageBody MessageBody { get; set; }
public Stream RequestBody { get; set; }
public int StatusCode { get; set; }
public string ReasonPhrase { get; set; }
public IDictionary<string, string[]> ResponseHeaders { get; set; }
public Stream ResponseBody { get; set; }
/*
public bool LocalIntakeFin
{
@ -151,12 +151,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
break;
case Mode.MessageBody:
if (_messageBody.LocalIntakeFin)
if (MessageBody.LocalIntakeFin)
{
// NOTE: stop reading and resume on keepalive?
return;
}
_messageBody.Consume();
MessageBody.Consume();
// NOTE: keep looping?
return;
@ -168,23 +168,53 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private void Execute()
{
_messageBody = MessageBody.For(
_httpVersion,
_requestHeaders,
MessageBody = MessageBody.For(
HttpVersion,
RequestHeaders,
this);
_keepAlive = _messageBody.RequestKeepAlive;
_callContext = CreateCallContext();
_keepAlive = MessageBody.RequestKeepAlive;
RequestBody = new FrameRequestStream(MessageBody);
ResponseBody = new FrameResponseStream(this);
_duplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
SocketInput.Free();
Task.Run(ExecuteAsync);
}
public void OnSendingHeaders(Action<object> callback, object state)
{
lock (_onSendingHeadersSync)
{
if (_onSendingHeaders == null)
{
_onSendingHeaders = new List<KeyValuePair<Action<object>, object>>();
}
_onSendingHeaders.Add(new KeyValuePair<Action<object>, object>(callback, state));
}
}
private void FireOnSendingHeaders()
{
List<KeyValuePair<Action<object>, object>> onSendingHeaders = null;
lock (_onSendingHeadersSync)
{
onSendingHeaders = _onSendingHeaders;
_onSendingHeaders = null;
}
if (onSendingHeaders != null)
{
foreach (var entry in onSendingHeaders)
{
entry.Key.Invoke(entry.Value);
}
}
}
private async Task ExecuteAsync()
{
Exception error = null;
try
{
await Application.Invoke(_callContext);
await _upgradeTask;
await Application.Invoke(this);
}
catch (Exception ex)
{
@ -196,77 +226,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
}
}
private CallContext CreateCallContext()
{
_inputStream = new FrameRequestStream(_messageBody);
_outputStream = new FrameResponseStream(this);
_duplexStream = new FrameDuplexStream(_inputStream, _outputStream);
var remoteIpAddress = "127.0.0.1";
var remotePort = "0";
var localIpAddress = "127.0.0.1";
var localPort = "80";
var isLocal = false;
//if (_context.Socket != null)
//{
// var remoteEndPoint = _context.Socket.RemoteEndPoint as IPEndPoint;
// if (remoteEndPoint != null)
// {
// remoteIpAddress = remoteEndPoint.Address.ToString();
// remotePort = remoteEndPoint.Port.ToString(CultureInfo.InvariantCulture);
// }
// var localEndPoint = _context.Socket.LocalEndPoint as IPEndPoint;
// if (localEndPoint != null)
// {
// localIpAddress = localEndPoint.Address.ToString();
// localPort = localEndPoint.Port.ToString(CultureInfo.InvariantCulture);
// }
// if (remoteEndPoint != null && localEndPoint != null)
// {
// isLocal = Equals(remoteEndPoint.Address, localEndPoint.Address);
// }
//}
var callContext = new CallContext();
var request = (IHttpRequestFeature)callContext;
var response = (IHttpResponseFeature)callContext;
//var lifetime = (IHttpRequestLifetimeFeature)callContext;
request.Protocol = _httpVersion;
request.Scheme = "http";
request.Method = _method;
request.Path = _path;
request.PathBase = "";
request.QueryString = _queryString;
request.Headers = _requestHeaders;
request.Body = _inputStream;
response.Headers = _responseHeaders;
response.Body = _outputStream;
//var env = new Dictionary<string, object>();
//env["owin.Version"] = "1.0";
//env["owin.RequestProtocol"] = _httpVersion;
//env["owin.RequestScheme"] = "http";
//env["owin.RequestMethod"] = _method;
//env["owin.RequestPath"] = _path;
//env["owin.RequestPathBase"] = "";
//env["owin.RequestQueryString"] = _queryString;
//env["owin.RequestHeaders"] = _requestHeaders;
//env["owin.RequestBody"] = _inputStream;
//env["owin.ResponseHeaders"] = _responseHeaders;
//env["owin.ResponseBody"] = _outputStream;
//env["owin.CallCancelled"] = _cts.Token;
//env["opaque.Upgrade"] = (Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>)Upgrade;
//env["opaque.Stream"] = _duplexStream;
//env["server.RemoteIpAddress"] = remoteIpAddress;
//env["server.RemotePort"] = remotePort;
//env["server.LocalIpAddress"] = localIpAddress;
//env["server.LocalPort"] = localPort;
//env["server.IsLocal"] = isLocal;
return callContext;
}
public void Write(ArraySegment<byte> data, Action<object> callback, object state)
{
@ -279,7 +238,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
_keepAlive = false;
ProduceStart();
_upgradeTask = callback(_callContext);
// NOTE: needs changes
//_upgradeTask = callback(_callContext);
}
byte[] _continueBytes = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
@ -289,8 +249,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
if (_resultStarted) return;
string[] expect;
if (_httpVersion.Equals("HTTP/1.1") &&
_requestHeaders.TryGetValue("Expect", out expect) &&
if (HttpVersion.Equals("HTTP/1.1") &&
RequestHeaders.TryGetValue("Expect", out expect) &&
(expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase))
{
SocketOutput.Write(new ArraySegment<byte>(_continueBytes, 0, _continueBytes.Length), _ => { }, null);
@ -300,15 +260,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public void ProduceStart()
{
if (_resultStarted) return;
_resultStarted = true;
var response = (IHttpResponseFeature)_callContext;
var status = ReasonPhrases.ToStatus(
response.StatusCode,
response.ReasonPhrase);
FireOnSendingHeaders();
var responseHeader = CreateResponseHeader(status, _responseHeaders);
var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase);
var responseHeader = CreateResponseHeader(status, ResponseHeaders);
SocketOutput.Write(responseHeader.Item1, x => ((IDisposable)x).Dispose(), responseHeader.Item2);
}
@ -325,12 +283,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
ConnectionControl.End(_keepAlive ? ProduceEndType.ConnectionKeepAlive : ProduceEndType.SocketDisconnect);
}
private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
string status, IEnumerable<KeyValuePair<string, string[]>> headers)
{
var writer = new MemoryPoolTextWriter(Memory);
writer.Write(_httpVersion);
writer.Write(HttpVersion);
writer.Write(' ');
writer.Write(status);
writer.Write('\r');
@ -381,11 +338,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
_keepAlive = false;
}
if (_keepAlive == false && hasConnection == false && _httpVersion == "HTTP/1.1")
if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1")
{
writer.Write("Connection: close\r\n\r\n");
}
else if (_keepAlive && hasConnection == false && _httpVersion == "HTTP/1.0")
else if (_keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0")
{
writer.Write("Connection: keep-alive\r\n\r\n");
}
@ -418,19 +375,19 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
throw new InvalidOperationException("INVALID REQUEST FORMAT");
}
_method = GetString(remaining, 0, firstSpace);
_requestUri = GetString(remaining, firstSpace + 1, secondSpace);
Method = GetString(remaining, 0, firstSpace);
RequestUri = GetString(remaining, firstSpace + 1, secondSpace);
if (questionMark == -1)
{
_path = _requestUri;
_queryString = string.Empty;
Path = RequestUri;
QueryString = string.Empty;
}
else
{
_path = GetString(remaining, firstSpace + 1, questionMark);
_queryString = GetString(remaining, questionMark, secondSpace);
Path = GetString(remaining, firstSpace + 1, questionMark);
QueryString = GetString(remaining, questionMark, secondSpace);
}
_httpVersion = GetString(remaining, secondSpace + 1, index);
HttpVersion = GetString(remaining, secondSpace + 1, index);
baton.Skip(index + 2);
return true;
}
@ -540,15 +497,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private void AddRequestHeader(string name, string value)
{
string[] existing;
if (!_requestHeaders.TryGetValue(name, out existing) ||
if (!RequestHeaders.TryGetValue(name, out existing) ||
existing == null ||
existing.Length == 0)
{
_requestHeaders[name] = new[] { value };
RequestHeaders[name] = new[] { value };
}
else
{
_requestHeaders[name] = existing.Concat(new[] { value }).ToArray();
RequestHeaders[name] = existing.Concat(new[] { value }).ToArray();
}
}
}

View File

@ -10,10 +10,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
class FrameDuplexStream : Stream
{
readonly FrameRequestStream _requestStream;
readonly FrameResponseStream _responseStream;
readonly Stream _requestStream;
readonly Stream _responseStream;
public FrameDuplexStream(FrameRequestStream requestStream, FrameResponseStream responseStream)
public FrameDuplexStream(Stream requestStream, Stream responseStream)
{
_requestStream = requestStream;
_responseStream = responseStream;

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public KestrelThread Thread { get; set; }
public Func<object, Task> Application { get; set; }
public Func<Frame, Task> Application { get; set; }
public IMemoryPool Memory { get; set; }
}
@ -45,10 +45,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
Memory = memory;
}
public Task StartAsync(KestrelThread thread, Func<object, Task> app)
public Task StartAsync(KestrelThread thread, Func<Frame, Task> application)
{
Thread = thread;
Application = app;
Application = application;
var tcs = new TaskCompletionSource<int>();
Thread.Post(OnStart, tcs);

View File

@ -9,7 +9,7 @@ using Microsoft.AspNet.Server.Kestrel.Http;
namespace Microsoft.AspNet.Server.Kestrel
{
public class KestrelEngine
public class KestrelEngine : IDisposable
{
public KestrelEngine()
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Server.Kestrel
}
}
public void Stop()
public void Dispose()
{
foreach (var thread in Threads)
{
@ -48,13 +48,13 @@ namespace Microsoft.AspNet.Server.Kestrel
Threads.Clear();
}
public IDisposable CreateServer(Func<object, Task> app)
public IDisposable CreateServer(Func<Frame, Task> application)
{
var listeners = new List<Listener>();
foreach (var thread in Threads)
{
var listener = new Listener(Memory);
listener.StartAsync(thread, app).Wait();
listener.StartAsync(thread, application).Wait();
listeners.Add(listener);
}
return new Disposable(() =>

View File

@ -26,7 +26,6 @@
<Content Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Http\CallContext.cs" />
<Compile Include="Http\FrameDuplexStream.cs" />
<Compile Include="Http\FrameRequestStream.cs" />
<Compile Include="Http\FrameResponseStream.cs" />

View File

@ -1,7 +1,6 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Hosting": "0.1-*"
},
"configurations": {
"net45": { },
@ -13,7 +12,14 @@
"System.Diagnostics.TraceSource": "4.0.0.0",
"System.Text.Encoding": "4.0.20.0",
"System.Threading.Tasks": "4.0.10.0",
"System.Diagnostics.Tracing": "4.0.10.0"
"System.Diagnostics.Tracing": "4.0.10.0",
"System.Collections": "4.0.0.0",
"System.IO": "4.0.0.0",
"System.Linq": "4.0.0.0",
"System.Net.Primitives": "4.0.10.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Threading": "4.0.0.0",
"System.Globalization": "4.0.10.0"
}
}
},

View File

@ -1,39 +1,21 @@
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace SampleApp
{
public class Program
{
public static void Main(string[] args)
private IServiceProvider _services;
public Program(IServiceProvider services)
{
var engine = new Microsoft.AspNet.Server.Kestrel.KestrelEngine();
engine.Start(1);
using (var server = engine.CreateServer(App))
{
Console.WriteLine("Hello World");
Console.ReadLine();
}
engine.Stop();
_services = services;
}
private static async Task App(object arg)
public void Main(string[] args)
{
var httpContext = new Microsoft.AspNet.PipelineCore.DefaultHttpContext(
new Microsoft.AspNet.FeatureModel.FeatureCollection(
new Microsoft.AspNet.FeatureModel.FeatureObject(arg)));
Console.WriteLine("{0} {1}{2}{3}",
httpContext.Request.Method,
httpContext.Request.PathBase,
httpContext.Request.Path,
httpContext.Request.QueryString);
httpContext.Response.ContentType = "text/plain";
await httpContext.Response.WriteAsync("Hello world");
new Microsoft.AspNet.Hosting.Program(_services).Main(new[] {
"--server","kestrel"
});
}
}
}

View File

@ -24,12 +24,16 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<CommandLineArguments>web</CommandLineArguments>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Startup.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="libuv.dll" />
<Content Include="project.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

23
src/SampleApp/Startup.cs Normal file
View File

@ -0,0 +1,23 @@
using Microsoft.AspNet.Builder;
using System;
namespace SampleApp
{
public class Startup
{
public void Configure(IBuilder app)
{
app.Run(async context =>
{
Console.WriteLine("{0} {1}{2}{3}",
context.Request.Method,
context.Request.PathBase,
context.Request.Path,
context.Request.QueryString);
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello world");
});
}
}
}

View File

@ -1,14 +1,16 @@
{
"dependencies": {
"Microsoft.AspNet.Server.Kestrel": "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore": "0.1-alpha-*"
"Kestrel": "0.1-alpha-*"
},
"configurations" : {
"net45" : { },
"k10" : {
"configurations": {
"net45": { },
"k10": {
"dependencies": {
"System.Console": "4.0.0.0"
}
}
},
"commands": {
"web": "Microsoft.AspNet.Hosting --server kestrel"
}
}

View File

@ -1,5 +1,5 @@
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.Server.Kestrel;
using Microsoft.AspNet.Server.Kestrel;
using Microsoft.AspNet.Server.Kestrel.Http;
using System;
using System.IO;
using System.Net;
@ -15,31 +15,26 @@ namespace Microsoft.AspNet.Server.KestralTests
/// </summary>
public class EngineTests
{
private async Task App(object callContext)
private async Task App(Frame frame)
{
var request = callContext as IHttpRequestFeature;
var response = callContext as IHttpResponseFeature;
for (; ;)
{
var buffer = new byte[8192];
var count = await request.Body.ReadAsync(buffer, 0, buffer.Length);
var count = await frame.RequestBody.ReadAsync(buffer, 0, buffer.Length);
if (count == 0)
{
break;
}
await response.Body.WriteAsync(buffer, 0, count);
await frame.ResponseBody.WriteAsync(buffer, 0, count);
}
}
private async Task AppChunked(object callContext)
private async Task AppChunked(Frame frame)
{
var request = callContext as IHttpRequestFeature;
var response = callContext as IHttpResponseFeature;
var data = new MemoryStream();
for (; ;)
{
var buffer = new byte[8192];
var count = await request.Body.ReadAsync(buffer, 0, buffer.Length);
var count = await frame.RequestBody.ReadAsync(buffer, 0, buffer.Length);
if (count == 0)
{
break;
@ -47,8 +42,8 @@ namespace Microsoft.AspNet.Server.KestralTests
data.Write(buffer, 0, count);
}
var bytes = data.ToArray();
response.Headers["Content-Length"] = new[] { bytes.Length.ToString() };
await response.Body.WriteAsync(bytes, 0, bytes.Length);
frame.ResponseHeaders["Content-Length"] = new[] { bytes.Length.ToString() };
await frame.ResponseBody.WriteAsync(bytes, 0, bytes.Length);
}
[Fact]
@ -56,7 +51,7 @@ namespace Microsoft.AspNet.Server.KestralTests
{
var engine = new KestrelEngine();
engine.Start(1);
engine.Stop();
engine.Dispose();
}
[Fact]
@ -66,7 +61,7 @@ namespace Microsoft.AspNet.Server.KestralTests
engine.Start(1);
var started = engine.CreateServer(App);
started.Dispose();
engine.Stop();
engine.Dispose();
}
@ -89,7 +84,7 @@ namespace Microsoft.AspNet.Server.KestralTests
var text = Encoding.ASCII.GetString(buffer, 0, length);
}
started.Dispose();
engine.Stop();
engine.Dispose();
}
[Fact]

View File

@ -31,8 +31,10 @@
<Compile Include="MessageBodyExchangerTests.cs" />
<Compile Include="MessageBodyTests.cs" />
<Compile Include="NetworkingTests.cs" />
<Compile Include="Program.cs" />
<Compile Include="TestConnection.cs" />
<Compile Include="TestInput.cs" />
<Compile Include="TestServer.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -0,0 +1,15 @@
using System;
namespace Microsoft.AspNet.Server.KestralTests
{
/// <summary>
/// Summary description for Program
/// </summary>
public class Program
{
public void Main()
{
new EngineTests().Http11().Wait();
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNet.Server.Kestrel;
using Microsoft.AspNet.Server.Kestrel.Http;
using System;
using System.Threading.Tasks;
@ -12,12 +13,12 @@ namespace Microsoft.AspNet.Server.KestralTests
private KestrelEngine _engine;
private IDisposable _server;
public TestServer(Func<object, Task> app)
public TestServer(Func<Frame, Task> app)
{
Create(app);
}
public void Create(Func<object,Task> app)
public void Create(Func<Frame, Task> app)
{
_engine = new KestrelEngine();
_engine.Start(1);
@ -28,7 +29,7 @@ namespace Microsoft.AspNet.Server.KestralTests
public void Dispose()
{
_server.Dispose();
_engine.Stop();
_engine.Dispose();
}
}
}

View File

@ -14,12 +14,12 @@
"compilationOptions": { "define": [ "TRACE" ] },
"dependencies": {
"System.Net.Sockets": "4.0.0.0",
"System.Runtime.Handles": "4.0.0.0"
"System.Runtime.Handles": "4.0.0.0",
"System.IO": "4.0.0.0"
}
}
},
"commands": {
"run": "Xunit.KRunner -trait x=vs",
"test": "Xunit.KRunner"
}
}