From 6ac6419f4f5beacae5c2d560d3b22c56c545c5ff Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Fri, 13 Jun 2014 13:10:46 -0700 Subject: [PATCH] Adding support for 'k web --server kestrel' --- KestrelHttpServer.sln | 6 + src/Kestrel/Kestrel.kproj | 36 +++ src/Kestrel/ServerFactory.cs | 35 +++ src/Kestrel/ServerInformation.cs | 21 ++ src/Kestrel/ServerRequest.cs | 180 +++++++++++++++ src/Kestrel/project.json | 17 ++ .../Http/CallContext.cs | 51 ----- .../Http/Frame.cs | 209 +++++++----------- .../Http/FrameDuplexStream.cs | 6 +- .../Http/Listener.cs | 6 +- .../KestrelEngine.cs | 8 +- .../Microsoft.AspNet.Server.Kestrel.kproj | 1 - .../project.json | 10 +- src/SampleApp/Program.cs | 34 +-- src/SampleApp/SampleApp.kproj | 6 +- src/SampleApp/Startup.cs | 23 ++ src/SampleApp/project.json | 12 +- .../EngineTests.cs | 29 +-- ...Microsoft.AspNet.Server.KestralTests.kproj | 4 +- .../Program.cs | 15 ++ .../TestServer.cs | 7 +- .../project.json | 4 +- 22 files changed, 475 insertions(+), 245 deletions(-) create mode 100644 src/Kestrel/Kestrel.kproj create mode 100644 src/Kestrel/ServerFactory.cs create mode 100644 src/Kestrel/ServerInformation.cs create mode 100644 src/Kestrel/ServerRequest.cs create mode 100644 src/Kestrel/project.json delete mode 100644 src/Microsoft.AspNet.Server.Kestrel/Http/CallContext.cs create mode 100644 src/SampleApp/Startup.cs create mode 100644 test/Microsoft.AspNet.Server.KestralTests/Program.cs diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index fbfe94da08..9f7f3f0bc5 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -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 diff --git a/src/Kestrel/Kestrel.kproj b/src/Kestrel/Kestrel.kproj new file mode 100644 index 0000000000..63934a9baa --- /dev/null +++ b/src/Kestrel/Kestrel.kproj @@ -0,0 +1,36 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + Debug + AnyCPU + + + + 30b7617e-58ef-4382-b3ea-5b2e718cf1a6 + Library + + + ConsoleDebugger + + + WebDebugger + + + + + 2.0 + + + + + + + + + + + \ No newline at end of file diff --git a/src/Kestrel/ServerFactory.cs b/src/Kestrel/ServerFactory.cs new file mode 100644 index 0000000000..c4f3bf770e --- /dev/null +++ b/src/Kestrel/ServerFactory.cs @@ -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 description for ServerFactory + /// + public class ServerFactory : IServerFactory + { + public IServerInformation Initialize(IConfiguration configuration) + { + var information = new ServerInformation(); + information.Initialize(configuration); + return information; + } + + public IDisposable Start(IServerInformation serverInformation, Func 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; + } + } +} diff --git a/src/Kestrel/ServerInformation.cs b/src/Kestrel/ServerInformation.cs new file mode 100644 index 0000000000..ac8377a8f1 --- /dev/null +++ b/src/Kestrel/ServerInformation.cs @@ -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"; + } + } + } +} \ No newline at end of file diff --git a/src/Kestrel/ServerRequest.cs b/src/Kestrel/ServerRequest.cs new file mode 100644 index 0000000000..643e464d5f --- /dev/null +++ b/src/Kestrel/ServerRequest.cs @@ -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 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 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 callback, object state) + { + _frame.OnSendingHeaders(callback, state); + } + } +} diff --git a/src/Kestrel/project.json b/src/Kestrel/project.json new file mode 100644 index 0000000000..75592a1e20 --- /dev/null +++ b/src/Kestrel/project.json @@ -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" + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/CallContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/CallContext.cs deleted file mode 100644 index 39b11b7ac3..0000000000 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/CallContext.cs +++ /dev/null @@ -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 description for CallContext - /// - public class CallContext : - IHttpRequestFeature, - IHttpResponseFeature - { - public CallContext() - { - ((IHttpResponseFeature)this).StatusCode = 200; - } - - Stream IHttpResponseFeature.Body { get; set; } - - Stream IHttpRequestFeature.Body { get; set; } - - IDictionary IHttpResponseFeature.Headers { get; set; } - - IDictionary 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 callback, object state) - { - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 2104952722..428b0ca6b4 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -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 _requestHeaders = - new Dictionary(StringComparer.OrdinalIgnoreCase); - - readonly IDictionary _responseHeaders = - new Dictionary(StringComparer.OrdinalIgnoreCase); - - private MessageBody _messageBody; + Mode _mode; private bool _resultStarted; private bool _keepAlive; - private CallContext _callContext; /* //IDictionary _environment; CancellationTokenSource _cts = new CancellationTokenSource(); */ - FrameResponseStream _outputStream; - FrameRequestStream _inputStream; - FrameDuplexStream _duplexStream; - Task _upgradeTask = _completedTask; - static readonly Task _completedTask = Task.FromResult(0); + List, object>> _onSendingHeaders; + object _onSendingHeadersSync = new Object(); + Stream _duplexStream; public Frame(ConnectionContext context) : base(context) { FrameControl = this; + StatusCode = 200; + RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + ResponseHeaders = new Dictionary(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 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 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 callback, object state) + { + lock (_onSendingHeadersSync) + { + if (_onSendingHeaders == null) + { + _onSendingHeaders = new List, object>>(); + } + _onSendingHeaders.Add(new KeyValuePair, object>(callback, state)); + } + } + + private void FireOnSendingHeaders() + { + List, 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(); - //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, Func, 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 data, Action 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(_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, IDisposable> CreateResponseHeader( string status, IEnumerable> 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(); } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameDuplexStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameDuplexStream.cs index f129004b51..6d4276f8eb 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameDuplexStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameDuplexStream.cs @@ -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; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs index 9bf1413579..1b8a71b25e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public KestrelThread Thread { get; set; } - public Func Application { get; set; } + public Func 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 app) + public Task StartAsync(KestrelThread thread, Func application) { Thread = thread; - Application = app; + Application = application; var tcs = new TaskCompletionSource(); Thread.Post(OnStart, tcs); diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs index 6bebfd1239..6375b45fc6 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs @@ -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 app) + public IDisposable CreateServer(Func application) { var listeners = new List(); 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(() => diff --git a/src/Microsoft.AspNet.Server.Kestrel/Microsoft.AspNet.Server.Kestrel.kproj b/src/Microsoft.AspNet.Server.Kestrel/Microsoft.AspNet.Server.Kestrel.kproj index 9ba02d48db..943ae5c366 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Microsoft.AspNet.Server.Kestrel.kproj +++ b/src/Microsoft.AspNet.Server.Kestrel/Microsoft.AspNet.Server.Kestrel.kproj @@ -26,7 +26,6 @@ - diff --git a/src/Microsoft.AspNet.Server.Kestrel/project.json b/src/Microsoft.AspNet.Server.Kestrel/project.json index 6dba34e730..c20f6afdc4 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/project.json +++ b/src/Microsoft.AspNet.Server.Kestrel/project.json @@ -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" } } }, diff --git a/src/SampleApp/Program.cs b/src/SampleApp/Program.cs index 21673112e6..b32fd7577a 100644 --- a/src/SampleApp/Program.cs +++ b/src/SampleApp/Program.cs @@ -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" + }); } } } diff --git a/src/SampleApp/SampleApp.kproj b/src/SampleApp/SampleApp.kproj index fe5828cdf7..c1ca9168bf 100644 --- a/src/SampleApp/SampleApp.kproj +++ b/src/SampleApp/SampleApp.kproj @@ -24,12 +24,16 @@ 2.0 + + web + + - + \ No newline at end of file diff --git a/src/SampleApp/Startup.cs b/src/SampleApp/Startup.cs new file mode 100644 index 0000000000..acafabf447 --- /dev/null +++ b/src/SampleApp/Startup.cs @@ -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"); + }); + } + } +} \ No newline at end of file diff --git a/src/SampleApp/project.json b/src/SampleApp/project.json index c1110915de..1d23fd79d2 100644 --- a/src/SampleApp/project.json +++ b/src/SampleApp/project.json @@ -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" } } diff --git a/test/Microsoft.AspNet.Server.KestralTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestralTests/EngineTests.cs index 414e825277..a40dcaaf70 100644 --- a/test/Microsoft.AspNet.Server.KestralTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestralTests/EngineTests.cs @@ -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 /// 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] diff --git a/test/Microsoft.AspNet.Server.KestralTests/Microsoft.AspNet.Server.KestralTests.kproj b/test/Microsoft.AspNet.Server.KestralTests/Microsoft.AspNet.Server.KestralTests.kproj index fab0735264..aecd9b6963 100644 --- a/test/Microsoft.AspNet.Server.KestralTests/Microsoft.AspNet.Server.KestralTests.kproj +++ b/test/Microsoft.AspNet.Server.KestralTests/Microsoft.AspNet.Server.KestralTests.kproj @@ -31,8 +31,10 @@ + + - + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Server.KestralTests/Program.cs b/test/Microsoft.AspNet.Server.KestralTests/Program.cs new file mode 100644 index 0000000000..86ae9b71d7 --- /dev/null +++ b/test/Microsoft.AspNet.Server.KestralTests/Program.cs @@ -0,0 +1,15 @@ +using System; + +namespace Microsoft.AspNet.Server.KestralTests +{ + /// + /// Summary description for Program + /// + public class Program + { + public void Main() + { + new EngineTests().Http11().Wait(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Server.KestralTests/TestServer.cs b/test/Microsoft.AspNet.Server.KestralTests/TestServer.cs index 4291532317..1efd5d95f5 100644 --- a/test/Microsoft.AspNet.Server.KestralTests/TestServer.cs +++ b/test/Microsoft.AspNet.Server.KestralTests/TestServer.cs @@ -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 app) + public TestServer(Func app) { Create(app); } - public void Create(Func app) + public void Create(Func 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(); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Server.KestralTests/project.json b/test/Microsoft.AspNet.Server.KestralTests/project.json index de7c2275d3..8cb87eaf48 100644 --- a/test/Microsoft.AspNet.Server.KestralTests/project.json +++ b/test/Microsoft.AspNet.Server.KestralTests/project.json @@ -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" } }