├── .gitattributes ├── .gitignore ├── README.md ├── ServerStack.sln ├── global.json ├── samples ├── ChatSample │ ├── BasicChat.cs │ ├── ChatMessage.cs │ ├── ChatMiddleware.cs │ ├── ChatSample.xproj │ ├── Codec.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── project.json ├── EchoServerSample │ ├── EchoServer.cs │ ├── EchoServerSample.xproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── project.json └── JsonRPCSample │ ├── EchoEndpoint.cs │ ├── JsonRPCSample.xproj │ ├── JsonRPCStartup.cs │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ └── project.json └── src ├── JsonRPC ├── JsonRPC.xproj ├── JsonRPCAppBuilderExtensions.cs ├── JsonRPCHandler.cs ├── JsonRPCMethodAttribute.cs ├── JsonRPCServiceCollectionExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── RpcEndPoint.cs └── project.json └── ServerStack ├── ApplicationBuilder.cs ├── ApplicationLifetime.cs ├── ContextFactory.cs ├── Dispatch ├── Dispatcher.cs ├── DispatcherObservable.cs └── IDispatcher.cs ├── DispatcherServiceCollectionExtensions.cs ├── FeatureCollection.cs ├── FeatureCollectionFactory.cs ├── Features └── IConnectionFeature.cs ├── HostingApplication.cs ├── IApplication.cs ├── IApplicationBuilder.cs ├── IApplicationLifetime.cs ├── IContextFactory.cs ├── IFeatureCollection.cs ├── IServer.cs ├── IServerFactory.cs ├── IServerHost.cs ├── IServerHostBuilder.cs ├── Infrastructure └── TaskQueue.cs ├── Middleware ├── DispatcherMiddleware.cs ├── ExceptionHandlerMiddleware.cs ├── LoggingMiddleware.cs ├── LoggingStream.cs └── TlsMiddleware.cs ├── Properties └── AssemblyInfo.cs ├── Protocols └── Tcp │ ├── TcpContext.cs │ └── TcpContextFactory.cs ├── Serialization ├── DefaultOutputProducer.cs ├── DefaultOutputProducerFactory.cs ├── Frame.cs ├── FrameOutput.cs ├── IFrameDecoder.cs ├── IFrameEncoder.cs ├── IFrameOutput.cs ├── IOutputProducer.cs ├── IOutputProducerFactory.cs └── Json │ ├── JsonDecoder.cs │ └── JsonEncoder.cs ├── SerializerServiceCollectionExtensions.cs ├── ServerHost.cs ├── ServerHostBuilder.cs ├── ServerStack.xproj ├── Servers ├── TcpServer.cs └── TcpServerFactory.cs └── project.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.doc diff=astextplain 2 | *.DOC diff=astextplain 3 | *.docx diff=astextplain 4 | *.DOCX diff=astextplain 5 | *.dot diff=astextplain 6 | *.DOT diff=astextplain 7 | *.pdf diff=astextplain 8 | *.PDF diff=astextplain 9 | *.rtf diff=astextplain 10 | *.RTF diff=astextplain 11 | 12 | *.jpg binary 13 | *.png binary 14 | *.gif binary 15 | 16 | *.cs text=auto diff=csharp 17 | *.vb text=auto 18 | *.resx text=auto 19 | *.c text=auto 20 | *.cpp text=auto 21 | *.cxx text=auto 22 | *.h text=auto 23 | *.hxx text=auto 24 | *.py text=auto 25 | *.rb text=auto 26 | *.java text=auto 27 | *.html text=auto 28 | *.htm text=auto 29 | *.css text=auto 30 | *.scss text=auto 31 | *.sass text=auto 32 | *.less text=auto 33 | *.js text=auto 34 | *.lisp text=auto 35 | *.clj text=auto 36 | *.sql text=auto 37 | *.php text=auto 38 | *.lua text=auto 39 | *.m text=auto 40 | *.asm text=auto 41 | *.erl text=auto 42 | *.fs text=auto 43 | *.fsx text=auto 44 | *.hs text=auto 45 | 46 | *.csproj text=auto 47 | *.vbproj text=auto 48 | *.fsproj text=auto 49 | *.dbproj text=auto 50 | *.sln text=auto eol=crlf 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | *.sln.ide/ 6 | _ReSharper.*/ 7 | packages/ 8 | artifacts/ 9 | PublishProfiles/ 10 | .vs/ 11 | bower_components/ 12 | node_modules/ 13 | **/wwwroot/lib/ 14 | debugSettings.json 15 | project.lock.json 16 | *.user 17 | *.suo 18 | *.cache 19 | *.docstates 20 | _ReSharper.* 21 | nuget.exe 22 | *net45.csproj 23 | *net451.csproj 24 | *k10.csproj 25 | *.psess 26 | *.vsp 27 | *.pidb 28 | *.userprefs 29 | *DS_Store 30 | *.ncrunchsolution 31 | *.*sdf 32 | *.ipch 33 | .settings 34 | *.sln.ide 35 | node_modules 36 | **/[Cc]ompiler/[Rr]esources/**/*.js 37 | *launchSettings.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## .NET Server Stack (Prototype) 2 | 3 | Inspiried by software like netty, finagle, wcf 4 | 5 | ### Benefits 6 | 7 | - Shared cross cutting concerns and bootup pattern 8 | - Logging 9 | - Dependency injection 10 | - Configuration 11 | - Startup class 12 | - Hosting API 13 | - Middleware pipeline 14 | 15 | ### Gaps 16 | 17 | - Missing unified client stack 18 | - Context object is dependent on the server implementation. Should we expose a feature collection directly as the default? 19 | 20 | 21 | ## Architecture 22 | 23 | ``` 24 | [incoming] [outgoing] 25 | [transport] [bytes] --> [IFrameDecoder] [Dispatcher] [IFrameEncoder] --> [bytes] [transport] 26 | [IFrameHandler] 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /ServerStack.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{54FEBF83-53F8-4447-9E81-E5C3136CFA3D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E7D95457-77D8-4468-BA02-37BA333B7ED2}" 9 | ProjectSection(SolutionItems) = preProject 10 | global.json = global.json 11 | EndProjectSection 12 | EndProject 13 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ServerStack", "src\ServerStack\ServerStack.xproj", "{031A9BB4-CC2E-4284-BD79-B355F4DD39F0}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{60F90A10-557C-4A63-AFC5-A8DF156FEDFB}" 16 | EndProject 17 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JsonRPC", "src\JsonRPC\JsonRPC.xproj", "{B27DF9CA-F1FB-441A-9FC2-EF91880E636F}" 18 | EndProject 19 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EchoServerSample", "samples\EchoServerSample\EchoServerSample.xproj", "{D2D2A34D-6F23-4F71-AA5E-4050F6ED503F}" 20 | EndProject 21 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ChatSample", "samples\ChatSample\ChatSample.xproj", "{95BD991A-7697-4227-8E3A-01D71A3AFE78}" 22 | EndProject 23 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JsonRPCSample", "samples\JsonRPCSample\JsonRPCSample.xproj", "{31E7C8E3-CEB4-429F-900E-32A662AFD136}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {031A9BB4-CC2E-4284-BD79-B355F4DD39F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {031A9BB4-CC2E-4284-BD79-B355F4DD39F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {031A9BB4-CC2E-4284-BD79-B355F4DD39F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {031A9BB4-CC2E-4284-BD79-B355F4DD39F0}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {B27DF9CA-F1FB-441A-9FC2-EF91880E636F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {B27DF9CA-F1FB-441A-9FC2-EF91880E636F}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {B27DF9CA-F1FB-441A-9FC2-EF91880E636F}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {B27DF9CA-F1FB-441A-9FC2-EF91880E636F}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {D2D2A34D-6F23-4F71-AA5E-4050F6ED503F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {D2D2A34D-6F23-4F71-AA5E-4050F6ED503F}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {D2D2A34D-6F23-4F71-AA5E-4050F6ED503F}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {D2D2A34D-6F23-4F71-AA5E-4050F6ED503F}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {95BD991A-7697-4227-8E3A-01D71A3AFE78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {95BD991A-7697-4227-8E3A-01D71A3AFE78}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {95BD991A-7697-4227-8E3A-01D71A3AFE78}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {95BD991A-7697-4227-8E3A-01D71A3AFE78}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {31E7C8E3-CEB4-429F-900E-32A662AFD136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {31E7C8E3-CEB4-429F-900E-32A662AFD136}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {31E7C8E3-CEB4-429F-900E-32A662AFD136}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {31E7C8E3-CEB4-429F-900E-32A662AFD136}.Release|Any CPU.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(NestedProjects) = preSolution 56 | {031A9BB4-CC2E-4284-BD79-B355F4DD39F0} = {54FEBF83-53F8-4447-9E81-E5C3136CFA3D} 57 | {B27DF9CA-F1FB-441A-9FC2-EF91880E636F} = {54FEBF83-53F8-4447-9E81-E5C3136CFA3D} 58 | {D2D2A34D-6F23-4F71-AA5E-4050F6ED503F} = {60F90A10-557C-4A63-AFC5-A8DF156FEDFB} 59 | {95BD991A-7697-4227-8E3A-01D71A3AFE78} = {60F90A10-557C-4A63-AFC5-A8DF156FEDFB} 60 | {31E7C8E3-CEB4-429F-900E-32A662AFD136} = {60F90A10-557C-4A63-AFC5-A8DF156FEDFB} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test" ], 3 | "sdk": { 4 | "version": "1.0.0-rc1-update1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /samples/ChatSample/BasicChat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using ServerStack; 5 | using ServerStack.Dispatch; 6 | using ServerStack.Middleware; 7 | using ServerStack.Protocols.Tcp; 8 | using ServerStack.Serialization; 9 | 10 | namespace ChatSample 11 | { 12 | public class BasicChat 13 | { 14 | public void ConfigureServices(IServiceCollection services) 15 | { 16 | services.AddCodec(); 17 | 18 | services.AddObservableDispatcher(); 19 | } 20 | 21 | public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 22 | { 23 | loggerFactory.AddConsole(LogLevel.Debug); 24 | 25 | app.UseLogging(); 26 | 27 | app.UseChat(); 28 | 29 | app.UseDispatcher(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/ChatSample/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ServerStack.Serialization.Json; 6 | 7 | namespace ChatSample 8 | { 9 | public class ChatMessage 10 | { 11 | public string Message { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/ChatSample/ChatMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using ServerStack; 5 | using ServerStack.Protocols.Tcp; 6 | using ServerStack.Serialization; 7 | 8 | namespace ChatSample 9 | { 10 | public class ChatMiddleware 11 | { 12 | private readonly Func _next; 13 | private readonly IOutputProducerFactory _producerFactory; 14 | private readonly IObservable> _observable; 15 | 16 | 17 | public ChatMiddleware(Func next, 18 | IOutputProducerFactory producerFactory, 19 | IObservable> observable) 20 | { 21 | _next = next; 22 | _producerFactory = producerFactory; 23 | _observable = observable; 24 | } 25 | 26 | public async Task Invoke(TcpContext context) 27 | { 28 | var producer = _producerFactory.Create(context.Body); 29 | 30 | try 31 | { 32 | using (_observable.Subscribe(new ChatClient(producer))) 33 | { 34 | await _next(context); 35 | } 36 | } 37 | finally 38 | { 39 | (producer as IDisposable)?.Dispose(); 40 | } 41 | } 42 | 43 | private class ChatClient : IObserver> 44 | { 45 | private readonly IOutputProducer _output; 46 | 47 | public ChatClient(IOutputProducer output) 48 | { 49 | _output = output; 50 | } 51 | 52 | public void OnCompleted() 53 | { 54 | 55 | } 56 | 57 | public void OnError(Exception error) 58 | { 59 | 60 | } 61 | 62 | public void OnNext(Frame value) 63 | { 64 | _output.Produce(value.Data); 65 | } 66 | } 67 | } 68 | 69 | public static class UserTrackingMiddlewareExtensions 70 | { 71 | public static IApplicationBuilder UseChat(this IApplicationBuilder app) 72 | { 73 | var output = app.ApplicationServices.GetRequiredService(); 74 | var observable = app.ApplicationServices.GetRequiredService>>(); 75 | 76 | return app.Use(next => new ChatMiddleware(next, output, observable).Invoke); 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /samples/ChatSample/ChatSample.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 95bd991a-7697-4227-8e3a-01d71a3afe78 11 | ChatSample 12 | ..\..\artifacts\obj\$(MSBuildProjectName) 13 | ..\..\artifacts\bin\$(MSBuildProjectName)\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/ChatSample/Codec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using ServerStack.Serialization; 8 | 9 | namespace ChatSample 10 | { 11 | public class ChatMessageDecoder : IFrameDecoder 12 | { 13 | public async Task Decode(Stream input, List results) 14 | { 15 | var reader = new StreamReader(input); 16 | var line = await reader.ReadLineAsync(); 17 | 18 | if (!string.IsNullOrEmpty(line)) 19 | { 20 | results.Add(new ChatMessage 21 | { 22 | Message = line 23 | }); 24 | } 25 | } 26 | } 27 | 28 | public class ChatMessageEncoder : IFrameEncoder 29 | { 30 | public Task Encode(Stream output, ChatMessage value) 31 | { 32 | var buffer = Encoding.UTF8.GetBytes(value.Message + Environment.NewLine); 33 | 34 | return output.WriteAsync(buffer, 0, buffer.Length); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /samples/ChatSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ServerStack; 6 | using ServerStack.Protocols.Tcp; 7 | using ServerStack.Servers; 8 | 9 | namespace ChatSample 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var host = new ServerHostBuilder() 16 | .UseSetting("server.address", "tcp://127.0.0.1:1335") 17 | .UseServer() 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/ChatSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ChatSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ChatSample")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("95bd991a-7697-4227-8e3a-01d71a3afe78")] 24 | -------------------------------------------------------------------------------- /samples/ChatSample/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | 4 | "compilationOptions": { 5 | "emitEntryPoint": true 6 | }, 7 | 8 | "dependencies": { 9 | "ServerStack": { "target": "project" }, 10 | "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final" 11 | }, 12 | 13 | "commands": { 14 | "ChatSample": "ChatSample" 15 | }, 16 | 17 | "frameworks": { 18 | "dnx451": { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/EchoServerSample/EchoServer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using ServerStack; 3 | using ServerStack.Middleware; 4 | using ServerStack.Protocols.Tcp; 5 | 6 | namespace EchoServerSample 7 | { 8 | public class EchoServer 9 | { 10 | public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 11 | { 12 | loggerFactory.AddConsole(LogLevel.Debug); 13 | 14 | app.UseLogging(); 15 | 16 | app.Run(context => 17 | { 18 | return context.Body.CopyToAsync(context.Body); 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/EchoServerSample/EchoServerSample.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | d2d2a34d-6f23-4f71-aa5e-4050f6ed503f 11 | EchoServerSample 12 | ..\..\artifacts\obj\$(MSBuildProjectName) 13 | ..\..\artifacts\bin\$(MSBuildProjectName)\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/EchoServerSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ServerStack; 6 | using ServerStack.Protocols.Tcp; 7 | using ServerStack.Servers; 8 | 9 | namespace EchoServerSample 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var host = new ServerHostBuilder() 16 | .UseSetting("server.address", "tcp://127.0.0.1:1335") 17 | .UseServer() 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/EchoServerSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EchoServerSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EchoServerSample")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d2d2a34d-6f23-4f71-aa5e-4050f6ed503f")] 24 | -------------------------------------------------------------------------------- /samples/EchoServerSample/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | 4 | "compilationOptions": { 5 | "emitEntryPoint": true 6 | }, 7 | 8 | "dependencies": { 9 | "ServerStack": { "target": "project" }, 10 | "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final" 11 | }, 12 | 13 | "commands": { 14 | "EchoServerSample": "EchoServerSample" 15 | }, 16 | 17 | "frameworks": { 18 | "dnx451": { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/JsonRPCSample/EchoEndpoint.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using JsonRPC; 3 | 4 | namespace JsonRPCSample 5 | { 6 | public class EchoEndpoint : RpcEndpoint 7 | { 8 | public string Echo(string value) 9 | { 10 | return value; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/JsonRPCSample/JsonRPCSample.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 31e7c8e3-ceb4-429f-900e-32a662afd136 11 | JsonRPCSample 12 | ..\..\artifacts\obj\$(MSBuildProjectName) 13 | ..\..\artifacts\bin\$(MSBuildProjectName)\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/JsonRPCSample/JsonRPCStartup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | using Newtonsoft.Json.Linq; 4 | using ServerStack; 5 | using ServerStack.Middleware; 6 | using ServerStack.Protocols.Tcp; 7 | 8 | namespace JsonRPCSample 9 | { 10 | public class JsonRPCStartup 11 | { 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddJsonRPC(); 15 | } 16 | 17 | public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 18 | { 19 | loggerFactory.AddConsole(LogLevel.Debug); 20 | 21 | app.UseJsonRpc(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /samples/JsonRPCSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ServerStack; 6 | using ServerStack.Protocols.Tcp; 7 | using ServerStack.Servers; 8 | 9 | namespace JsonRPCSample 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var host = new ServerHostBuilder() 16 | .UseSetting("server.address", "tcp://127.0.0.1:1335") 17 | .UseServer() 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/JsonRPCSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("JsonRPCSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("JsonRPCSample")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("31e7c8e3-ceb4-429f-900e-32a662afd136")] 24 | -------------------------------------------------------------------------------- /samples/JsonRPCSample/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | 4 | "compilationOptions": { 5 | "emitEntryPoint": true 6 | }, 7 | 8 | "dependencies": { 9 | "JsonRPC": { "target": "project" }, 10 | "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final" 11 | }, 12 | 13 | "commands": { 14 | "JsonRPCSample": "JsonRPCSample" 15 | }, 16 | 17 | "frameworks": { 18 | "dnx451": { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/JsonRPC/JsonRPC.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | b27df9ca-f1fb-441a-9fc2-ef91880e636f 11 | JsonRPC 12 | ..\..\artifacts\obj\$(MSBuildProjectName) 13 | ..\..\artifacts\bin\$(MSBuildProjectName)\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/JsonRPC/JsonRPCAppBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json.Linq; 6 | using ServerStack; 7 | using ServerStack.Middleware; 8 | using ServerStack.Protocols.Tcp; 9 | 10 | namespace ServerStack 11 | { 12 | public static class JsonRPCAppBuilderExtensions 13 | { 14 | public static IApplicationBuilder UseJsonRpc(this IApplicationBuilder app) 15 | { 16 | return app.UseDispatcher(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/JsonRPC/JsonRPCHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Logging; 8 | using Newtonsoft.Json.Linq; 9 | using ServerStack.Serialization; 10 | 11 | namespace JsonRPC 12 | { 13 | public class JsonRPCHandler : IObserver> 14 | { 15 | private readonly Dictionary> _callbacks = new Dictionary>(StringComparer.OrdinalIgnoreCase); 16 | private readonly IServiceProvider _serviceProvider; 17 | private readonly ILogger _logger; 18 | 19 | private bool _isBound; 20 | 21 | public JsonRPCHandler(ILogger logger, 22 | IEnumerable endpoints, 23 | IServiceProvider serviceProvider) 24 | { 25 | _logger = logger; 26 | _serviceProvider = serviceProvider; 27 | 28 | foreach (var endpoint in endpoints) 29 | { 30 | Bind(endpoint.GetType()); 31 | } 32 | } 33 | 34 | public void OnError(Exception error) 35 | { 36 | 37 | } 38 | 39 | public void OnCompleted() 40 | { 41 | 42 | } 43 | 44 | public void OnNext(Frame value) 45 | { 46 | var request = value.Data; 47 | 48 | if (_logger.IsEnabled(LogLevel.Verbose)) 49 | { 50 | _logger.LogVerbose("Received JSON RPC request: {request}", request); 51 | } 52 | JObject response = null; 53 | 54 | Func callback; 55 | if (_callbacks.TryGetValue(request.Value("method"), out callback)) 56 | { 57 | response = callback(request); 58 | } 59 | else 60 | { 61 | // If there's no method then return a failed response for this request 62 | response = new JObject(); 63 | response["id"] = request["id"]; 64 | response["error"] = string.Format("Unknown method '{0}'", request.Value("method")); 65 | } 66 | 67 | _logger.LogVerbose("Sending JSON RPC response: {data}", response); 68 | value.Output.Produce(response); 69 | } 70 | 71 | private void Bind(Type type) 72 | { 73 | if (_isBound) 74 | { 75 | throw new NotSupportedException("Can't bind to different objects"); 76 | } 77 | 78 | _isBound = true; 79 | 80 | var methods = new List(); 81 | 82 | foreach (var m in type.GetTypeInfo().DeclaredMethods.Where(m => m.IsPublic)) 83 | { 84 | var methodName = m.GetCustomAttribute()?.MethodName ?? type.FullName + "." + m.Name; 85 | 86 | methods.Add(methodName); 87 | 88 | var parameters = m.GetParameters(); 89 | 90 | if (_callbacks.ContainsKey(methodName)) 91 | { 92 | throw new NotSupportedException(String.Format("Duplicate definitions of {0}. Overloading is not supported.", m.Name)); 93 | } 94 | 95 | if (_logger.IsEnabled(LogLevel.Verbose)) 96 | { 97 | _logger.LogVerbose("RPC method '{methodName}' is bound", methodName); 98 | } 99 | 100 | _callbacks[methodName] = request => 101 | { 102 | var response = new JObject(); 103 | response["id"] = request["id"]; 104 | 105 | var scopeFactory = _serviceProvider.GetRequiredService(); 106 | 107 | // Scope per call so that deps injected get disposed 108 | using (var scope = scopeFactory.CreateScope()) 109 | { 110 | object value = scope.ServiceProvider.GetService(type); 111 | 112 | try 113 | { 114 | var args = request.Value("params").Zip(parameters, (a, p) => a.ToObject(p.ParameterType)) 115 | .ToArray(); 116 | 117 | var result = m.Invoke(value, args); 118 | 119 | if (result != null) 120 | { 121 | response["result"] = JToken.FromObject(result); 122 | } 123 | } 124 | catch (TargetInvocationException ex) 125 | { 126 | response["error"] = ex.InnerException.Message; 127 | } 128 | catch (Exception ex) 129 | { 130 | response["error"] = ex.Message; 131 | } 132 | } 133 | 134 | return response; 135 | }; 136 | }; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/JsonRPC/JsonRPCMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace JsonRPC 7 | { 8 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] 9 | public sealed class JsonRPCMethodAttribute : Attribute 10 | { 11 | public JsonRPCMethodAttribute(string methodName) 12 | { 13 | MethodName = methodName; 14 | } 15 | 16 | public string MethodName { get; private set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/JsonRPC/JsonRPCServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using JsonRPC; 4 | using Microsoft.Extensions.PlatformAbstractions; 5 | using Newtonsoft.Json.Linq; 6 | using ServerStack.Serialization; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | public static class JsonRPCServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddJsonRPC(this IServiceCollection services) 13 | { 14 | var libraries = PlatformServices.Default.LibraryManager.GetReferencingLibraries(typeof(RpcEndpoint).Assembly.GetName().Name); 15 | 16 | foreach (var library in libraries) 17 | { 18 | foreach (var an in library.Assemblies) 19 | { 20 | var asm = Assembly.Load(an); 21 | foreach (var type in asm.GetTypes()) 22 | { 23 | if (typeof(RpcEndpoint) != type && typeof(RpcEndpoint).IsAssignableFrom(type)) 24 | { 25 | services.AddTransient(typeof(RpcEndpoint), type); 26 | services.AddTransient(type); 27 | } 28 | } 29 | } 30 | } 31 | 32 | services.AddSingleton>, JsonRPCHandler>(); 33 | services.AddObservableDispatcher(); 34 | return services; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/JsonRPC/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("JsonRPC")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("JsonRPC")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b27df9ca-f1fb-441a-9fc2-ef91880e636f")] 24 | -------------------------------------------------------------------------------- /src/JsonRPC/RpcEndPoint.cs: -------------------------------------------------------------------------------- 1 | namespace JsonRPC 2 | { 3 | public class RpcEndpoint { } 4 | } -------------------------------------------------------------------------------- /src/JsonRPC/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "description": "JsonRPC Class Library", 4 | "authors": [ "dfowler" ], 5 | "tags": [ "" ], 6 | "projectUrl": "", 7 | "licenseUrl": "", 8 | 9 | "dependencies": { 10 | "ServerStack": { "target": "project" }, 11 | "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc1-final", 12 | "Newtonsoft.Json": "8.0.2" 13 | }, 14 | 15 | "frameworks": { 16 | "net451": { } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ServerStack/ApplicationBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public class ApplicationBuilder : IApplicationBuilder 9 | { 10 | internal static string ServerFeaturesKey = "server.Features"; 11 | internal static string ApplicationServicesKey = "application.Services"; 12 | 13 | private readonly IList, Func>> _components = new List, Func>>(); 14 | 15 | public ApplicationBuilder(IServiceProvider serviceProvider) 16 | { 17 | Properties = new Dictionary(); 18 | ApplicationServices = serviceProvider; 19 | } 20 | 21 | public ApplicationBuilder(IServiceProvider serviceProvider, object server) 22 | : this(serviceProvider) 23 | { 24 | SetProperty(ServerFeaturesKey, server); 25 | } 26 | 27 | private ApplicationBuilder(ApplicationBuilder builder) 28 | { 29 | Properties = builder.Properties; 30 | } 31 | 32 | public IServiceProvider ApplicationServices 33 | { 34 | get 35 | { 36 | return GetProperty(ApplicationServicesKey); 37 | } 38 | set 39 | { 40 | SetProperty(ApplicationServicesKey, value); 41 | } 42 | } 43 | 44 | public IFeatureCollection ServerFeatures 45 | { 46 | get 47 | { 48 | return GetProperty(ServerFeaturesKey); 49 | } 50 | } 51 | 52 | public IDictionary Properties { get; } 53 | 54 | private T GetProperty(string key) 55 | { 56 | object value; 57 | return Properties.TryGetValue(key, out value) ? (T)value : default(T); 58 | } 59 | 60 | private void SetProperty(string key, T value) 61 | { 62 | Properties[key] = value; 63 | } 64 | 65 | public IApplicationBuilder Use(Func, Func> middleware) 66 | { 67 | _components.Add(middleware); 68 | return this; 69 | } 70 | 71 | public IApplicationBuilder New() 72 | { 73 | return new ApplicationBuilder(this); 74 | } 75 | 76 | public Func Build() 77 | { 78 | Func app = ctx => Task.FromResult(0); 79 | 80 | foreach (var component in _components.Reverse()) 81 | { 82 | app = component(app); 83 | } 84 | 85 | return app; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/ServerStack/ApplicationLifetime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack 8 | { 9 | public class ApplicationLifetime : IApplicationLifetime 10 | { 11 | private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); 12 | private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); 13 | private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); 14 | 15 | /// 16 | /// Triggered when the application host has fully started and is about to wait 17 | /// for a graceful shutdown. 18 | /// 19 | public CancellationToken ApplicationStarted => _startedSource.Token; 20 | 21 | /// 22 | /// Triggered when the application host is performing a graceful shutdown. 23 | /// Request may still be in flight. Shutdown will block until this event completes. 24 | /// 25 | /// 26 | public CancellationToken ApplicationStopping => _stoppingSource.Token; 27 | 28 | /// 29 | /// Triggered when the application host is performing a graceful shutdown. 30 | /// All requests should be complete at this point. Shutdown will block 31 | /// until this event completes. 32 | /// 33 | /// 34 | public CancellationToken ApplicationStopped => _stoppedSource.Token; 35 | 36 | /// 37 | /// Signals the ApplicationStopping event and blocks until it completes. 38 | /// 39 | public void StopApplication() 40 | { 41 | try 42 | { 43 | _stoppingSource.Cancel(throwOnFirstException: false); 44 | } 45 | catch (Exception) 46 | { 47 | // TODO: LOG 48 | } 49 | } 50 | 51 | /// 52 | /// Signals the ApplicationStarted event and blocks until it completes. 53 | /// 54 | public void NotifyStarted() 55 | { 56 | try 57 | { 58 | _startedSource.Cancel(throwOnFirstException: false); 59 | } 60 | catch (Exception) 61 | { 62 | // TODO: LOG 63 | } 64 | } 65 | 66 | /// 67 | /// Signals the ApplicationStopped event and blocks until it completes. 68 | /// 69 | public void NotifyStopped() 70 | { 71 | try 72 | { 73 | _stoppedSource.Cancel(throwOnFirstException: false); 74 | } 75 | catch (Exception) 76 | { 77 | // TODO: LOG 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/ServerStack/ContextFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public class ContextFactory : IContextFactory 9 | { 10 | public TContext CreateContext(IFeatureCollection features) 11 | { 12 | return features.Get(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ServerStack/Dispatch/Dispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | using ServerStack.Serialization; 8 | 9 | namespace ServerStack.Dispatch 10 | { 11 | public class Dispatcher : IDispatcher 12 | { 13 | private readonly List>> _observers = new List>>(); 14 | private readonly IFrameDecoder _decoder; 15 | private readonly ILogger> _logger; 16 | private readonly IOutputProducerFactory _producerFactory; 17 | 18 | public Dispatcher(ILogger> logger, 19 | IFrameDecoder decoder, 20 | IOutputProducerFactory producerFactory) 21 | { 22 | _logger = logger; 23 | _decoder = decoder; 24 | _producerFactory = producerFactory; 25 | } 26 | 27 | public async Task Invoke(Stream stream) 28 | { 29 | var producer = _producerFactory.Create(stream); 30 | var frames = new List(); 31 | 32 | while (true) 33 | { 34 | try 35 | { 36 | frames.Clear(); 37 | // Read some data, if the decoder doesn't consume any then stop 38 | await _decoder.Decode(stream, frames); 39 | 40 | if (frames.Count == 0) 41 | { 42 | break; 43 | } 44 | 45 | foreach (var frame in frames) 46 | { 47 | OnNext(producer, frame); 48 | } 49 | } 50 | catch (Exception ex) 51 | { 52 | OnError(ex); 53 | 54 | _logger.LogError("Failed to process frame", ex); 55 | break; 56 | } 57 | } 58 | 59 | OnCompleted(); 60 | 61 | (producer as IDisposable)?.Dispose(); 62 | } 63 | 64 | public IDisposable Subscribe(IObserver> observer) 65 | { 66 | lock (_observers) 67 | { 68 | _observers.Add(observer); 69 | } 70 | 71 | return new DisposableAction(() => 72 | { 73 | lock (_observers) 74 | { 75 | _observers.Remove(observer); 76 | } 77 | }); 78 | } 79 | 80 | private void OnCompleted() 81 | { 82 | lock (_observers) 83 | { 84 | foreach (var observer in _observers) 85 | { 86 | observer.OnCompleted(); 87 | } 88 | } 89 | } 90 | 91 | private void OnError(Exception exception) 92 | { 93 | lock (_observers) 94 | { 95 | foreach (var observer in _observers) 96 | { 97 | observer.OnError(exception); 98 | } 99 | } 100 | } 101 | 102 | private void OnNext(IOutputProducer producer, T frame) 103 | { 104 | lock (_observers) 105 | { 106 | foreach (var observer in _observers) 107 | { 108 | observer.OnNext(new Frame(producer, frame)); 109 | } 110 | } 111 | } 112 | 113 | private class DisposableAction : IDisposable 114 | { 115 | private Action _action; 116 | public DisposableAction(Action action) 117 | { 118 | _action = action; 119 | } 120 | public void Dispose() 121 | { 122 | Interlocked.Exchange(ref _action, () => { }).Invoke(); 123 | } 124 | } 125 | 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/ServerStack/Dispatch/DispatcherObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ServerStack.Serialization; 4 | 5 | namespace ServerStack.Dispatch 6 | { 7 | public class DispatcherObservable : IObservable>, IDisposable 8 | { 9 | private readonly IDispatcher _dispatcher; 10 | private readonly List _topLevelSubscriptions = new List(); 11 | 12 | public DispatcherObservable(IDispatcher dispatcher, IEnumerable>> observers) 13 | { 14 | _dispatcher = dispatcher; 15 | 16 | foreach (var observer in observers) 17 | { 18 | _topLevelSubscriptions.Add(Subscribe(observer)); 19 | } 20 | } 21 | 22 | public IDisposable Subscribe(IObserver> observer) 23 | { 24 | return _dispatcher.Subscribe(observer); 25 | } 26 | 27 | public void Dispose() 28 | { 29 | _topLevelSubscriptions.ForEach(s => s.Dispose()); 30 | _topLevelSubscriptions.Clear(); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/ServerStack/Dispatch/IDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using ServerStack.Serialization; 5 | 6 | namespace ServerStack.Dispatch 7 | { 8 | public interface IDispatcher : IObservable> 9 | { 10 | Task Invoke(Stream stream); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ServerStack/DispatcherServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ServerStack.Dispatch; 3 | using ServerStack.Serialization; 4 | 5 | namespace Microsoft.Extensions.DependencyInjection 6 | { 7 | public static class DispatcherServiceCollectionExtensions 8 | { 9 | public static IServiceCollection AddObservableDispatcher(this IServiceCollection services) 10 | { 11 | services.AddSingleton>, DispatcherObservable>(); 12 | return services; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ServerStack/FeatureCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack 8 | { 9 | public class FeatureCollection : IFeatureCollection 10 | { 11 | private static KeyComparer FeatureKeyComparer = new KeyComparer(); 12 | private readonly IFeatureCollection _defaults; 13 | private IDictionary _features; 14 | private volatile int _containerRevision; 15 | 16 | public FeatureCollection() 17 | { 18 | } 19 | 20 | public FeatureCollection(IFeatureCollection defaults) 21 | { 22 | _defaults = defaults; 23 | } 24 | 25 | public virtual int Revision 26 | { 27 | get { return _containerRevision + (_defaults?.Revision ?? 0); } 28 | } 29 | 30 | public bool IsReadOnly { get { return false; } } 31 | 32 | public object this[Type key] 33 | { 34 | get 35 | { 36 | if (key == null) 37 | { 38 | throw new ArgumentNullException(nameof(key)); 39 | } 40 | 41 | object result; 42 | return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key]; 43 | } 44 | set 45 | { 46 | if (key == null) 47 | { 48 | throw new ArgumentNullException(nameof(key)); 49 | } 50 | 51 | if (value == null) 52 | { 53 | if (_features != null && _features.Remove(key)) 54 | { 55 | _containerRevision++; 56 | } 57 | return; 58 | } 59 | 60 | if (_features == null) 61 | { 62 | _features = new Dictionary(); 63 | } 64 | _features[key] = value; 65 | _containerRevision++; 66 | } 67 | } 68 | 69 | IEnumerator IEnumerable.GetEnumerator() 70 | { 71 | return GetEnumerator(); 72 | } 73 | 74 | public IEnumerator> GetEnumerator() 75 | { 76 | if (_features != null) 77 | { 78 | foreach (var pair in _features) 79 | { 80 | yield return pair; 81 | } 82 | } 83 | 84 | if (_defaults != null) 85 | { 86 | // Don't return features masked by the wrapper. 87 | foreach (var pair in _features == null ? _defaults : _defaults.Except(_features, FeatureKeyComparer)) 88 | { 89 | yield return pair; 90 | } 91 | } 92 | } 93 | 94 | public TFeature Get() 95 | { 96 | return (TFeature)this[typeof(TFeature)]; 97 | } 98 | 99 | public void Set(TFeature instance) 100 | { 101 | this[typeof(TFeature)] = instance; 102 | } 103 | 104 | private class KeyComparer : IEqualityComparer> 105 | { 106 | public bool Equals(KeyValuePair x, KeyValuePair y) 107 | { 108 | return x.Key.Equals(y.Key); 109 | } 110 | 111 | public int GetHashCode(KeyValuePair obj) 112 | { 113 | return obj.Key.GetHashCode(); 114 | } 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/ServerStack/FeatureCollectionFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ServerStack 4 | { 5 | public class FeatureCollectionFactory : IContextFactory 6 | { 7 | public IFeatureCollection CreateContext(IFeatureCollection features) 8 | { 9 | return features; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ServerStack/Features/IConnectionFeature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | 8 | namespace ServerStack.Features 9 | { 10 | 11 | public interface IConnectionFeature 12 | { 13 | IPAddress RemoteIpAddress { get; set; } 14 | IPAddress LocalIpAddress { get; set; } 15 | int RemotePort { get; set; } 16 | int LocalPort { get; set; } 17 | 18 | Stream Body { get; set; } 19 | } 20 | 21 | public class ConnectionFeature : IConnectionFeature 22 | { 23 | public IPAddress RemoteIpAddress { get; set; } 24 | public IPAddress LocalIpAddress { get; set; } 25 | public int RemotePort { get; set; } 26 | public int LocalPort { get; set; } 27 | public Stream Body { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ServerStack/HostingApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public class HostingApplication : IApplication 9 | { 10 | private readonly Func _pipeline; 11 | private readonly IContextFactory _contextFactory; 12 | 13 | public HostingApplication(IContextFactory contextFactory, Func pipeline) 14 | { 15 | _contextFactory = contextFactory; 16 | _pipeline = pipeline; 17 | } 18 | 19 | public TContext CreateContext(IFeatureCollection features) 20 | { 21 | return _contextFactory.CreateContext(features); 22 | } 23 | 24 | public void DisposeContext(TContext context, Exception exception) 25 | { 26 | 27 | } 28 | 29 | public Task ProcessRequestAsync(TContext context) 30 | { 31 | return _pipeline(context); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ServerStack/IApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public interface IApplication 9 | { 10 | TContext CreateContext(IFeatureCollection features); 11 | 12 | Task ProcessRequestAsync(TContext context); 13 | 14 | void DisposeContext(TContext context, Exception exception); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ServerStack/IApplicationBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public static class ApplicationBuilderExtensions 9 | { 10 | public static IApplicationBuilder Run(this IApplicationBuilder app, Func callback) 11 | { 12 | return app.Use(next => ctx => callback(ctx)); 13 | } 14 | } 15 | 16 | public interface IApplicationBuilder 17 | { 18 | IServiceProvider ApplicationServices { get; set; } 19 | 20 | IFeatureCollection ServerFeatures { get; } 21 | 22 | IDictionary Properties { get; } 23 | 24 | IApplicationBuilder Use(Func, Func> middleware); 25 | 26 | IApplicationBuilder New(); 27 | 28 | Func Build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ServerStack/IApplicationLifetime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack 8 | { 9 | public interface IApplicationLifetime 10 | { 11 | /// 12 | /// Triggered when the application host has fully started and is about to wait 13 | /// for a graceful shutdown. 14 | /// 15 | CancellationToken ApplicationStarted { get; } 16 | 17 | /// 18 | /// Triggered when the application host is performing a graceful shutdown. 19 | /// Request may still be in flight. Shutdown will block until this event completes. 20 | /// 21 | /// 22 | CancellationToken ApplicationStopping { get; } 23 | 24 | /// 25 | /// Triggered when the application host is performing a graceful shutdown. 26 | /// All requests should be complete at this point. Shutdown will block 27 | /// until this event completes. 28 | /// 29 | /// 30 | CancellationToken ApplicationStopped { get; } 31 | 32 | /// 33 | /// Requests termination the current application. 34 | /// 35 | void StopApplication(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ServerStack/IContextFactory.cs: -------------------------------------------------------------------------------- 1 | namespace ServerStack 2 | { 3 | public interface IContextFactory 4 | { 5 | TContext CreateContext(IFeatureCollection features); 6 | } 7 | } -------------------------------------------------------------------------------- /src/ServerStack/IFeatureCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public interface IFeatureCollection : IEnumerable> 9 | { 10 | /// 11 | /// Indicates if the collection can be modified. 12 | /// 13 | bool IsReadOnly { get; } 14 | 15 | /// 16 | /// Incremented for each modification and can be used to verify cached results. 17 | /// 18 | int Revision { get; } 19 | 20 | /// 21 | /// Gets or sets a given feature. Setting a null value removes the feature. 22 | /// 23 | /// 24 | /// The requested feature, or null if it is not present. 25 | object this[Type key] { get; set; } 26 | 27 | /// 28 | /// Retrieves the requested feature from the collection. 29 | /// 30 | /// The feature key. 31 | /// The requested feature, or null if it is not present. 32 | TFeature Get(); 33 | 34 | /// 35 | /// Sets the given feature in the collection. 36 | /// 37 | /// The feature key. 38 | /// The feature value. 39 | void Set(TFeature instance); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/ServerStack/IServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public interface IServer : IDisposable 9 | { 10 | /// 11 | /// A collection of HTTP features of the server. 12 | /// 13 | IFeatureCollection Features { get; } 14 | 15 | /// 16 | /// Start the server with an HttpApplication. 17 | /// 18 | /// An instance of . 19 | void Start(IApplication application); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ServerStack/IServerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Configuration; 6 | 7 | namespace ServerStack 8 | { 9 | public interface IServerFactory 10 | { 11 | /// 12 | /// Creates based on the given configuration. 13 | /// 14 | /// An instance of . 15 | /// The created server. 16 | IServer CreateServer(IConfiguration configuration); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ServerStack/IServerHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace ServerStack 6 | { 7 | public static class ServerHostingExtensions 8 | { 9 | /// 10 | /// Runs a web application and block the calling thread until host shutdown. 11 | /// 12 | /// 13 | public static void Run(this IServerHost host) 14 | { 15 | using (var cts = new CancellationTokenSource()) 16 | { 17 | Console.CancelKeyPress += (sender, eventArgs) => 18 | { 19 | cts.Cancel(); 20 | 21 | // Don't terminate the process immediately, wait for the Main thread to exit gracefully. 22 | eventArgs.Cancel = true; 23 | }; 24 | 25 | host.Run(cts.Token, "Application started. Press Ctrl+C to shut down."); 26 | } 27 | } 28 | 29 | /// 30 | /// Runs a web application and block the calling thread until token is triggered or shutdown is triggered 31 | /// 32 | /// 33 | /// The token to trigger shutdown 34 | public static void Run(this IServerHost host, CancellationToken token) 35 | { 36 | host.Run(token, shutdownMessage: null); 37 | } 38 | 39 | private static void Run(this IServerHost host, CancellationToken token, string shutdownMessage) 40 | { 41 | using (host) 42 | { 43 | host.Start(); 44 | 45 | var applicationLifetime = host.Services.GetService(); 46 | 47 | /*var serverAddresses = host.ServerFeatures.Get()?.Addresses; 48 | if (serverAddresses != null) 49 | { 50 | foreach (var address in serverAddresses) 51 | { 52 | Console.WriteLine($"Now listening on: {address}"); 53 | } 54 | }*/ 55 | 56 | if (!string.IsNullOrEmpty(shutdownMessage)) 57 | { 58 | Console.WriteLine(shutdownMessage); 59 | } 60 | 61 | token.Register(state => 62 | { 63 | ((IApplicationLifetime)state).StopApplication(); 64 | }, 65 | applicationLifetime); 66 | 67 | applicationLifetime.ApplicationStopping.WaitHandle.WaitOne(); 68 | } 69 | } 70 | } 71 | 72 | public interface IServerHost : IDisposable 73 | { 74 | IFeatureCollection ServerFeatures { get; } 75 | 76 | IServiceProvider Services { get; } 77 | 78 | void Start(); 79 | } 80 | } -------------------------------------------------------------------------------- /src/ServerStack/IServerHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace ServerStack 8 | { 9 | public interface IServerHostBuilder 10 | { 11 | /// 12 | /// Builds an which hosts a web application. 13 | /// 14 | IServerHost Build(); 15 | 16 | /// 17 | /// Specify the to be used by the web host. 18 | /// 19 | /// The to be used. 20 | /// The . 21 | IServerHostBuilder UseServer(IServerFactory factory); 22 | 23 | IServerHostBuilder UseServer(); 24 | 25 | /// 26 | /// Specify the startup type to be used by the web host. 27 | /// 28 | /// The to be used. 29 | /// The . 30 | IServerHostBuilder UseStartup(Type startupType); 31 | 32 | 33 | IServerHostBuilder UseStartup(); 34 | 35 | /// 36 | /// Specify the delegate that is used to configure the services of the web application. 37 | /// 38 | /// The delegate that configures the . 39 | /// The . 40 | IServerHostBuilder ConfigureServices(Action configureServices); 41 | /// 42 | /// Specify the startup method to be used to configure the web application. 43 | /// 44 | /// The delegate that configures the . 45 | /// The . 46 | IServerHostBuilder Configure(Action> configureApplication); 47 | 48 | /// 49 | /// Add or replace a setting in the configuration. 50 | /// 51 | /// The key of the setting to add or replace. 52 | /// The value of the setting to add or replace. 53 | /// The . 54 | IServerHostBuilder UseSetting(string key, string value); 55 | 56 | /// 57 | string GetSetting(string key); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/ServerStack/Infrastructure/TaskQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Infrastructure 8 | { 9 | // Allows serial queuing of Task instances 10 | // The tasks are not called on the current synchronization context 11 | 12 | internal sealed class TaskQueue 13 | { 14 | private readonly object _lockObj = new object(); 15 | private Task _lastQueuedTask; 16 | private volatile bool _drained; 17 | private readonly int? _maxSize; 18 | private long _size; 19 | 20 | public TaskQueue(int maxSize) 21 | { 22 | _lastQueuedTask = Task.FromResult(0); 23 | _maxSize = maxSize; 24 | } 25 | 26 | public bool IsDrained 27 | { 28 | get 29 | { 30 | return _drained; 31 | } 32 | } 33 | 34 | public Task Enqueue(Func taskFunc, object state) 35 | { 36 | // Lock the object for as short amount of time as possible 37 | lock (_lockObj) 38 | { 39 | if (_drained) 40 | { 41 | return _lastQueuedTask; 42 | } 43 | 44 | if (_maxSize != null) 45 | { 46 | // Increment the size if the queue 47 | if (Interlocked.Increment(ref _size) > _maxSize) 48 | { 49 | Interlocked.Decrement(ref _size); 50 | 51 | // We failed to enqueue because the size limit was reached 52 | return null; 53 | } 54 | } 55 | 56 | var newTask = _lastQueuedTask.ContinueWith(async task => 57 | { 58 | try 59 | { 60 | await task; 61 | await taskFunc(state); 62 | } 63 | finally 64 | { 65 | Dequeue(); 66 | } 67 | }); 68 | 69 | _lastQueuedTask = newTask; 70 | return newTask; 71 | } 72 | } 73 | 74 | private void Dequeue() 75 | { 76 | if (_maxSize != null) 77 | { 78 | // Decrement the number of items left in the queue 79 | Interlocked.Decrement(ref _size); 80 | } 81 | } 82 | 83 | public Task Enqueue(Func taskFunc) 84 | { 85 | return Enqueue(state => ((Func)state).Invoke(), taskFunc); 86 | } 87 | 88 | public Task Drain() 89 | { 90 | lock (_lockObj) 91 | { 92 | _drained = true; 93 | 94 | return _lastQueuedTask; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/ServerStack/Middleware/DispatcherMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using ServerStack.Dispatch; 5 | using ServerStack.Protocols.Tcp; 6 | 7 | namespace ServerStack 8 | { 9 | public class DispatcherMiddleware 10 | { 11 | private readonly Func _next; 12 | private readonly IDispatcher _dispatcher; 13 | 14 | public DispatcherMiddleware(Func next, 15 | IDispatcher dispatcher) 16 | { 17 | _next = next; 18 | _dispatcher = dispatcher; 19 | } 20 | 21 | public async Task Invoke(TcpContext context) 22 | { 23 | await _dispatcher.Invoke(context.Body); 24 | await _next(context); 25 | } 26 | } 27 | 28 | public static class DispatcherMiddlewareExtensions 29 | { 30 | public static IApplicationBuilder UseDispatcher(this IApplicationBuilder app) 31 | { 32 | return app.Use(next => 33 | { 34 | var dispatcher = app.ApplicationServices.GetRequiredService>(); 35 | 36 | return new DispatcherMiddleware(next, dispatcher).Invoke; 37 | }); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/ServerStack/Middleware/ExceptionHandlerMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack.Middleware 7 | { 8 | public class ExceptionHandlerMiddleware 9 | { 10 | private readonly Func _next; 11 | private readonly Action _handler; 12 | public ExceptionHandlerMiddleware(Func next, Action handler) 13 | { 14 | _next = next; 15 | _handler = handler; 16 | } 17 | 18 | public async Task Invoke(TContext context) 19 | { 20 | try 21 | { 22 | await _next(context); 23 | } 24 | catch (Exception ex) 25 | { 26 | _handler(ex); 27 | } 28 | } 29 | } 30 | 31 | public static class ExceptionHandlerMiddlewareExtensions 32 | { 33 | public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, Action handler) 34 | { 35 | return app.Use(next => ctx => new ExceptionHandlerMiddleware(next, handler).Invoke(ctx)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ServerStack/Middleware/LoggingMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Logging; 7 | using ServerStack.Protocols.Tcp; 8 | 9 | namespace ServerStack.Middleware 10 | { 11 | public class LoggingMiddleware 12 | { 13 | private readonly ILogger _logger; 14 | private readonly Func _next; 15 | 16 | public LoggingMiddleware(Func next, ILoggerFactory loggerFactory) 17 | { 18 | _next = next; 19 | _logger = loggerFactory.CreateLogger(); 20 | } 21 | 22 | public Task Invoke(TcpContext context) 23 | { 24 | context.Body = new LoggingStream(context.Body, _logger); 25 | 26 | return _next(context); 27 | } 28 | } 29 | 30 | public static class LoggingMiddlewareExtensions 31 | { 32 | public static IApplicationBuilder UseLogging(this IApplicationBuilder app) 33 | { 34 | var loggerFactory = app.ApplicationServices.GetRequiredService(); 35 | 36 | return app.Use(next => 37 | { 38 | return new LoggingMiddleware(next, loggerFactory).Invoke; 39 | }); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ServerStack/Middleware/LoggingStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ServerStack.Middleware 11 | { 12 | internal class LoggingStream : Stream 13 | { 14 | private readonly Stream _inner; 15 | private readonly ILogger _logger; 16 | 17 | public LoggingStream(Stream inner, ILogger logger) 18 | { 19 | _inner = inner; 20 | _logger = logger; 21 | } 22 | 23 | public override bool CanRead 24 | { 25 | get 26 | { 27 | return _inner.CanRead; 28 | } 29 | } 30 | 31 | public override bool CanSeek 32 | { 33 | get 34 | { 35 | return _inner.CanSeek; 36 | } 37 | } 38 | 39 | public override bool CanWrite 40 | { 41 | get 42 | { 43 | return _inner.CanWrite; 44 | } 45 | } 46 | 47 | public override long Length 48 | { 49 | get 50 | { 51 | return _inner.Length; 52 | } 53 | } 54 | 55 | public override long Position 56 | { 57 | get 58 | { 59 | return _inner.Position; 60 | } 61 | 62 | set 63 | { 64 | _inner.Position = value; 65 | } 66 | } 67 | 68 | public override void Flush() 69 | { 70 | _inner.Flush(); 71 | } 72 | 73 | public override int Read(byte[] buffer, int offset, int count) 74 | { 75 | int read = _inner.Read(buffer, offset, count); 76 | Log("Read", read, buffer, offset); 77 | return read; 78 | } 79 | 80 | public async override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) 81 | { 82 | int read = await _inner.ReadAsync(buffer, offset, count, cancellationToken); 83 | Log("ReadAsync", read, buffer, offset); 84 | return read; 85 | } 86 | 87 | public override long Seek(long offset, SeekOrigin origin) 88 | { 89 | return _inner.Seek(offset, origin); 90 | } 91 | 92 | public override void SetLength(long value) 93 | { 94 | _inner.SetLength(value); 95 | } 96 | 97 | public override void Write(byte[] buffer, int offset, int count) 98 | { 99 | Log("Write", count, buffer, offset); 100 | _inner.Write(buffer, offset, count); 101 | } 102 | 103 | public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) 104 | { 105 | Log("WriteAsync", count, buffer, offset); 106 | return _inner.WriteAsync(buffer, offset, count, cancellationToken); 107 | } 108 | 109 | private void Log(string method, int count, byte[] buffer, int offset) 110 | { 111 | var builder = new StringBuilder($"{method}[{count}] "); 112 | 113 | // Write the hex 114 | for (int i = offset; i < offset + count; i++) 115 | { 116 | builder.Append(buffer[i].ToString("X2")); 117 | builder.Append(" "); 118 | } 119 | builder.AppendLine(); 120 | // Write the bytes as if they were ASCII 121 | for (int i = offset; i < offset + count; i++) 122 | { 123 | builder.Append((char)buffer[i]); 124 | } 125 | 126 | _logger.LogVerbose(builder.ToString()); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/ServerStack/Middleware/TlsMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Security; 3 | using System.Security.Cryptography.X509Certificates; 4 | using System.Threading.Tasks; 5 | using ServerStack.Features; 6 | using ServerStack.Protocols.Tcp; 7 | 8 | namespace ServerStack 9 | { 10 | public class TlsMiddleware 11 | { 12 | private readonly X509Certificate2 _cert; 13 | private readonly Func _next; 14 | 15 | public TlsMiddleware(Func next, X509Certificate2 cert) 16 | { 17 | _next = next; 18 | _cert = cert; 19 | } 20 | 21 | public async Task Invoke(TcpContext context) 22 | { 23 | var sslStream = new SslStream(context.Body); 24 | 25 | await sslStream.AuthenticateAsServerAsync(_cert); 26 | 27 | context.Body = sslStream; 28 | 29 | await _next(context); 30 | } 31 | } 32 | 33 | public static class TlsMiddlewareExtensions 34 | { 35 | public static IApplicationBuilder UseTls(this IApplicationBuilder app, X509Certificate2 cert) 36 | { 37 | return app.Use(next => ctx => new TlsMiddleware(next, cert).Invoke(ctx)); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/ServerStack/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ServerStack")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ServerStack")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("031a9bb4-cc2e-4284-bd79-b355f4dd39f0")] 24 | -------------------------------------------------------------------------------- /src/ServerStack/Protocols/Tcp/TcpContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | using ServerStack.Features; 8 | 9 | namespace ServerStack.Protocols.Tcp 10 | { 11 | public class TcpContext 12 | { 13 | private readonly IFeatureCollection _features; 14 | 15 | public TcpContext(IFeatureCollection features) 16 | { 17 | _features = features; 18 | } 19 | 20 | public IPAddress RemoteIpAddress { get; set; } 21 | public IPAddress LocalIpAddress { get; set; } 22 | public int RemotePort { get; set; } 23 | public int LocalPort { get; set; } 24 | 25 | public Stream Body 26 | { 27 | get 28 | { 29 | return _features.Get().Body; 30 | } 31 | set 32 | { 33 | { 34 | _features.Get().Body = value; 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ServerStack/Protocols/Tcp/TcpContextFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack.Protocols.Tcp 7 | { 8 | public class TcpContextFactory : IContextFactory 9 | { 10 | public TcpContext CreateContext(IFeatureCollection features) 11 | { 12 | return new TcpContext(features); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/DefaultOutputProducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using ServerStack.Infrastructure; 4 | 5 | namespace ServerStack.Serialization 6 | { 7 | public class DefaultOutputProducer : IOutputProducer, IDisposable 8 | { 9 | private readonly Stream _body; 10 | private readonly IFrameOutput _output; 11 | private readonly TaskQueue _queue = new TaskQueue(10000); 12 | 13 | public DefaultOutputProducer(IFrameOutput output, Stream body) 14 | { 15 | _output = output; 16 | _body = body; 17 | } 18 | 19 | public void Dispose() 20 | { 21 | _queue.Drain().GetAwaiter().GetResult(); 22 | } 23 | 24 | public void Produce(object value) 25 | { 26 | // Fire and forget 27 | // REVIEW: How to errors get handled? 28 | _queue.Enqueue(() => _output.WriteAsync(_body, value)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/DefaultOutputProducerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Serialization 8 | { 9 | 10 | public class DefaultOutputProducerFactory : IOutputProducerFactory 11 | { 12 | private readonly IFrameOutput _output; 13 | 14 | public DefaultOutputProducerFactory(IFrameOutput output) 15 | { 16 | _output = output; 17 | } 18 | 19 | 20 | public IOutputProducer Create(Stream stream) 21 | { 22 | return new DefaultOutputProducer(_output, stream); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/Frame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using ServerStack.Infrastructure; 4 | 5 | namespace ServerStack.Serialization 6 | { 7 | public struct Frame 8 | { 9 | public Frame(IOutputProducer output, T data) 10 | { 11 | Output = output; 12 | Data = data; 13 | } 14 | 15 | public IOutputProducer Output { get; } 16 | 17 | public T Data { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/FrameOutput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.IO; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace ServerStack.Serialization 10 | { 11 | public class FrameOutput : IFrameOutput 12 | { 13 | private readonly IServiceProvider _serviceProvider; 14 | private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); 15 | 16 | public FrameOutput(IServiceProvider serviceProvider) 17 | { 18 | _serviceProvider = serviceProvider; 19 | } 20 | 21 | public Task WriteAsync(Stream stream, object value) 22 | { 23 | if (value == null) 24 | { 25 | return Task.FromResult(0); 26 | } 27 | 28 | Type type = value.GetType(); 29 | var entry = _cache.GetOrAdd(type, t => CreateCacheEntry(t)); 30 | 31 | return entry.Encode(entry.Encoder, stream, value); 32 | } 33 | 34 | private CacheEntry CreateCacheEntry(Type type) 35 | { 36 | var encoderType = typeof(IFrameEncoder<>).MakeGenericType(type); 37 | 38 | // Func callback = (encoder, body, value) => 39 | // { 40 | // return ((IStreamEncoder)encoder).Encode(body, (T)value); 41 | // } 42 | 43 | var encoderParam = Expression.Parameter(typeof(object), "encoder"); 44 | var bodyParam = Expression.Parameter(typeof(Stream), "body"); 45 | var valueParam = Expression.Parameter(typeof(object), "value"); 46 | 47 | var encoderCast = Expression.Convert(encoderParam, encoderType); 48 | var valueCast = Expression.Convert(valueParam, type); 49 | var encode = encoderType.GetMethod("Encode", BindingFlags.Public | BindingFlags.Instance); 50 | var encodeCall = Expression.Call(encoderCast, encode, bodyParam, valueCast); 51 | var lambda = Expression.Lambda>(encodeCall, encoderParam, bodyParam, valueParam); 52 | 53 | var entry = new CacheEntry 54 | { 55 | Encode = lambda.Compile(), 56 | Encoder = _serviceProvider.GetRequiredService(encoderType) 57 | }; 58 | 59 | return entry; 60 | } 61 | 62 | private struct CacheEntry 63 | { 64 | public Func Encode; 65 | public object Encoder; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/IFrameDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Serialization 8 | { 9 | public interface IFrameDecoder 10 | { 11 | Task Decode(Stream input, List results); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/IFrameEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Serialization 8 | { 9 | public interface IFrameEncoder 10 | { 11 | Task Encode(Stream output, TOutput value); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/IFrameOutput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Serialization 8 | { 9 | public interface IFrameOutput 10 | { 11 | Task WriteAsync(Stream output, object value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/IOutputProducer.cs: -------------------------------------------------------------------------------- 1 | namespace ServerStack.Serialization 2 | { 3 | public interface IOutputProducer 4 | { 5 | void Produce(object value); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/IOutputProducerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ServerStack.Serialization 8 | { 9 | 10 | public interface IOutputProducerFactory 11 | { 12 | IOutputProducer Create(Stream stream); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/Json/JsonDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | 9 | namespace ServerStack.Serialization.Json 10 | { 11 | public class JsonDecoder : IFrameDecoder 12 | { 13 | public Task Decode(Stream input, List results) 14 | { 15 | var reader = new JsonTextReader(new StreamReader(input)); 16 | 17 | results.Add(JObject.Load(reader).ToObject()); 18 | return Task.FromResult(0); 19 | } 20 | } 21 | 22 | public class JsonDecoder : IFrameDecoder 23 | { 24 | public Task Decode(Stream input, List results) 25 | { 26 | var reader = new JsonTextReader(new StreamReader(input)); 27 | 28 | results.Add(JObject.Load(reader)); 29 | return Task.FromResult(0); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ServerStack/Serialization/Json/JsonEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace ServerStack.Serialization.Json 11 | { 12 | public class JsonEncoder : IFrameEncoder 13 | { 14 | public Task Encode(Stream output, T value) 15 | { 16 | var serializer = new JsonSerializer(); 17 | 18 | using (var writer = new StreamWriter(output) { AutoFlush = true }) 19 | { 20 | serializer.Serialize(writer, value); 21 | } 22 | 23 | return Task.FromResult(0); 24 | } 25 | } 26 | 27 | public class JsonEncoder : IFrameEncoder 28 | { 29 | public virtual Task Encode(Stream output, JObject value) 30 | { 31 | var bytes = Encoding.UTF8.GetBytes(value.ToString()); 32 | 33 | return output.WriteAsync(bytes, 0, bytes.Length); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ServerStack/SerializerServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json.Linq; 6 | using ServerStack.Serialization; 7 | using ServerStack.Serialization.Json; 8 | 9 | namespace Microsoft.Extensions.DependencyInjection 10 | { 11 | public static class SerializerServiceCollectionExtensions 12 | { 13 | public static IServiceCollection AddJsonCodec(this IServiceCollection services) 14 | { 15 | return services.AddCodec, JsonDecoder>(); 16 | } 17 | 18 | public static IServiceCollection AddCodec(this IServiceCollection services) 19 | where TEncoder : class, IFrameEncoder 20 | where TDecoder : class, IFrameDecoder 21 | { 22 | services.AddEncoder(); 23 | services.AddDecoder(); 24 | return services; 25 | } 26 | 27 | public static IServiceCollection AddEncoder(this IServiceCollection services) 28 | where TEncoder : class, IFrameEncoder 29 | { 30 | services.AddSingleton, TEncoder>(); 31 | return services; 32 | } 33 | 34 | public static IServiceCollection AddDecoder(this IServiceCollection services) 35 | where TDecoder : class, IFrameDecoder 36 | { 37 | services.AddSingleton, TDecoder>(); 38 | return services; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ServerStack/ServerHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ServerStack 7 | { 8 | public class ServerHost : IServerHost 9 | { 10 | private readonly Func _pipeline; 11 | private readonly IServer _server; 12 | private readonly IContextFactory _contextFactory; 13 | 14 | public ServerHost(IServer server, IServiceProvider services, IContextFactory contextFactory, Func pipeline) 15 | { 16 | _server = server; 17 | ServerFeatures = server.Features; 18 | Services = services; 19 | _contextFactory = contextFactory; 20 | _pipeline = pipeline; 21 | } 22 | 23 | public IFeatureCollection ServerFeatures { get; } 24 | 25 | public IServiceProvider Services { get; } 26 | 27 | public void Dispose() 28 | { 29 | _server.Dispose(); 30 | } 31 | 32 | public void Start() 33 | { 34 | _server.Start(new HostingApplication(_contextFactory, _pipeline)); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ServerStack/ServerHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Threading.Tasks; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | using Newtonsoft.Json.Linq; 12 | using ServerStack.Dispatch; 13 | using ServerStack.Protocols.Tcp; 14 | using ServerStack.Serialization; 15 | using ServerStack.Serialization.Json; 16 | 17 | namespace ServerStack 18 | { 19 | public class ServerHostBuilder : IServerHostBuilder 20 | { 21 | private Action _configureServices; 22 | private IConfiguration _config = new ConfigurationBuilder().AddInMemoryCollection().Build(); 23 | 24 | private Type _startupType; 25 | private Type _serverFactoryType; 26 | 27 | public IServerHost Build() 28 | { 29 | var services = new ServiceCollection(); 30 | services.AddInstance(new LoggerFactory()); 31 | services.AddLogging(); 32 | services.AddOptions(); 33 | services.AddSingleton(); 34 | 35 | services.AddSingleton(); 36 | services.AddSingleton(); 37 | services.AddSingleton(typeof(IDispatcher<>), typeof(Dispatcher<>)); 38 | 39 | // known encoders 40 | services.AddCodec(); 41 | 42 | // Add known protocols 43 | services.AddSingleton(typeof(IContextFactory), typeof(TcpContextFactory)); 44 | 45 | // Add the startup type 46 | services.AddSingleton(_startupType); 47 | services.AddSingleton(typeof(IServerFactory), _serverFactoryType); 48 | 49 | _configureServices?.Invoke(services); 50 | 51 | var serviceProvider = services.BuildServiceProvider(); 52 | 53 | var startup = serviceProvider.GetService(_startupType); 54 | 55 | var configure = FindConfigureDelegate(_startupType); 56 | var configureServices = FindConfigureServicesDelegate(_startupType); 57 | 58 | // ConfigureServices 59 | var applicationServices = configureServices?.Invoke(startup, new object[] { services }) as IServiceProvider ?? 60 | services.BuildServiceProvider(); 61 | 62 | // Configure (will support DI at some point) 63 | // configure.Invoke(startup, new object[] { }); 64 | var parameters = configure.GetParameters(); 65 | var args = new object[parameters.Length]; 66 | 67 | if (parameters.Length == 0) 68 | { 69 | throw new InvalidOperationException("Invalid Configure signature"); 70 | } 71 | 72 | var appBuilder = new ApplicationBuilder(applicationServices); 73 | args[0] = appBuilder; 74 | 75 | for (int i = 1; i < parameters.Length; i++) 76 | { 77 | args[i] = applicationServices.GetRequiredService(parameters[i].ParameterType); 78 | } 79 | 80 | configure.Invoke(startup, args); 81 | 82 | var pipeline = appBuilder.Build(); 83 | var serverFactory = applicationServices.GetRequiredService(); 84 | var contextFactory = applicationServices.GetRequiredService>(); 85 | var server = serverFactory.CreateServer(_config); 86 | 87 | return new ServerHost(server, applicationServices, contextFactory, pipeline); 88 | } 89 | 90 | public IServerHostBuilder Configure(Action> configureApplication) 91 | { 92 | throw new NotImplementedException(); 93 | } 94 | 95 | public IServerHostBuilder ConfigureServices(Action configureServices) 96 | { 97 | _configureServices = configureServices; 98 | return this; 99 | } 100 | 101 | public string GetSetting(string key) 102 | { 103 | return _config[key]; 104 | } 105 | 106 | public IServerHostBuilder UseServer() 107 | { 108 | _serverFactoryType = typeof(TServerFactory); 109 | return this; 110 | } 111 | 112 | public IServerHostBuilder UseServer(IServerFactory factory) 113 | { 114 | throw new NotImplementedException(); 115 | } 116 | 117 | public IServerHostBuilder UseSetting(string key, string value) 118 | { 119 | _config[key] = value; 120 | return this; 121 | } 122 | 123 | public IServerHostBuilder UseStartup(Type startupType) 124 | { 125 | _startupType = startupType; 126 | return this; 127 | } 128 | public IServerHostBuilder UseStartup() 129 | { 130 | return UseStartup(typeof(TStartup)); 131 | } 132 | 133 | private static MethodInfo FindConfigureDelegate(Type startupType) 134 | { 135 | return FindMethod(startupType, "Configure", typeof(void), required: true); 136 | } 137 | 138 | private static MethodInfo FindConfigureServicesDelegate(Type startupType) 139 | { 140 | var servicesMethod = FindMethod(startupType, "ConfigureServices", typeof(IServiceProvider), required: false) 141 | ?? FindMethod(startupType, "ConfigureServices", typeof(void), required: false); 142 | return servicesMethod; 143 | } 144 | 145 | private static MethodInfo FindMethod(Type startupType, string methodName, Type returnType = null, bool required = true) 146 | { 147 | var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, ""); 148 | 149 | var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 150 | var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv)).ToList(); 151 | 152 | if (selectedMethods.Count > 1) 153 | { 154 | throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv)); 155 | } 156 | 157 | var methodInfo = selectedMethods.FirstOrDefault(); 158 | if (methodInfo == null) 159 | { 160 | if (required) 161 | { 162 | throw new InvalidOperationException(string.Format("A public method named '{0}' could not be found in the '{2}' type.", 163 | methodNameWithNoEnv, 164 | startupType.FullName)); 165 | 166 | } 167 | return null; 168 | } 169 | 170 | if (returnType != null && methodInfo.ReturnType != returnType) 171 | { 172 | if (required) 173 | { 174 | throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.", 175 | methodInfo.Name, 176 | startupType.FullName, 177 | returnType.Name)); 178 | } 179 | return null; 180 | } 181 | return methodInfo; 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/ServerStack/ServerStack.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0.24720 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 031a9bb4-cc2e-4284-bd79-b355f4dd39f0 10 | ServerStack 11 | ..\..\artifacts\obj\$(MSBuildProjectName) 12 | ..\..\artifacts\bin\$(MSBuildProjectName)\ 13 | 14 | 15 | 2.0 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ServerStack/Servers/TcpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.Logging; 8 | using ServerStack.Features; 9 | 10 | namespace ServerStack.Servers 11 | { 12 | public class TcpServer : IServer 13 | { 14 | public IFeatureCollection Features { get; } 15 | 16 | private readonly TcpListener _listener; 17 | private readonly ILogger _logger; 18 | 19 | public TcpServer(IPEndPoint endPoint, ILoggerFactory loggerFactory) 20 | { 21 | _listener = new TcpListener(endPoint); 22 | _logger = loggerFactory.CreateLogger(); 23 | } 24 | 25 | public void Dispose() 26 | { 27 | _listener.Stop(); 28 | } 29 | 30 | public async void Start(IApplication application) 31 | { 32 | _listener.Start(); 33 | 34 | // Async void is bad 35 | while (true) 36 | { 37 | var client = await _listener.AcceptTcpClientAsync(); 38 | var fc = new FeatureCollection(); 39 | fc.Set(new ConnectionFeature 40 | { 41 | Body = client.GetStream() 42 | }); 43 | 44 | _logger.LogVerbose("Accepted connection {connection}", client.Client.RemoteEndPoint); 45 | 46 | var ignore = Task.Run(async () => 47 | { 48 | var context = application.CreateContext(fc); 49 | 50 | try 51 | { 52 | await application.ProcessRequestAsync(context); 53 | 54 | application.DisposeContext(context, null); 55 | } 56 | catch (Exception ex) 57 | { 58 | application.DisposeContext(context, ex); 59 | } 60 | finally 61 | { 62 | _logger.LogVerbose("Connection {connection} closed", client.Client.RemoteEndPoint); 63 | } 64 | }); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/ServerStack/Servers/TcpServerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace ServerStack.Servers 10 | { 11 | public class TcpServerFactory : IServerFactory 12 | { 13 | private readonly ILoggerFactory _loggerFactory; 14 | 15 | public TcpServerFactory(ILoggerFactory loggerFactory) 16 | { 17 | _loggerFactory = loggerFactory; 18 | } 19 | 20 | public IServer CreateServer(IConfiguration configuration) 21 | { 22 | var ip = new IPEndPoint(IPAddress.Loopback, 5000); 23 | var address = configuration["server.address"]; 24 | if (!string.IsNullOrEmpty(address)) 25 | { 26 | var uri = new Uri(address); 27 | if (!string.Equals(uri.Scheme, "tcp", StringComparison.OrdinalIgnoreCase)) 28 | { 29 | throw new InvalidOperationException($"Invalid scheme {uri.Scheme}"); 30 | } 31 | 32 | ip = new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port); 33 | } 34 | return new TcpServer(ip, _loggerFactory); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ServerStack/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | 4 | "dependencies": { 5 | "Microsoft.Extensions.Configuration.Abstractions": "1.0.0-rc1-final", 6 | "Microsoft.Extensions.DependencyInjection": "1.0.0-rc1-final", 7 | "Microsoft.Extensions.OptionsModel": "1.0.0-rc1-final", 8 | "Microsoft.Extensions.Logging": "1.0.0-rc1-final", 9 | "Newtonsoft.Json": "8.0.2" 10 | }, 11 | 12 | "frameworks": { 13 | "net451": { } 14 | } 15 | } 16 | --------------------------------------------------------------------------------