├── uhttpsharp ├── .gitignore ├── Controllers │ ├── IController.cs │ ├── IRequestParameter.cs │ ├── IErrorContainer.cs │ ├── IPipeline.cs │ ├── ErrorContainer.cs │ └── IControllerResponse.cs ├── IHttpMethodProvider.cs ├── packages.config ├── Listeners │ ├── IHttpListener.cs │ ├── TcpListenerAdapter.cs │ └── SslListenerDecorator.cs ├── Handlers │ ├── IResponseProvider.cs │ ├── Compression │ │ ├── GZipCompressor.cs │ │ ├── DeflateCompressor.cs │ │ ├── ICompressor.cs │ │ ├── CompressionHandler.cs │ │ └── CompressedResponse.cs │ ├── JsonResponseProvider.cs │ ├── IView.cs │ ├── IRestController.cs │ ├── HttpRouter.cs │ ├── FileHandler.cs │ ├── RestHandler.cs │ ├── GZipHandler.cs~RF51dd35.TMP │ ├── ClassRouter.cs │ └── ControllerHandler.cs ├── Headers │ ├── IHttpHeaders.cs │ ├── EmptyHttpHeaders.cs │ ├── HttpHeadersDebuggerProxy.cs │ ├── HttpHeadersExtensions.cs │ ├── QueryStringHttpHeaders.cs │ ├── CompositeHttpHeaders.cs │ └── HttpHeaders.cs ├── Clients │ ├── IClient.cs │ ├── TcpClientAdapter.cs │ └── ClientSslDecoerator.cs ├── TaskFactoryExtensions.cs ├── Attributes │ ├── NullableAttribute.cs │ ├── HttpMethodAttribute.cs │ └── IModelBinding.cs ├── RequestProviders │ ├── IHttpRequestProvider.cs │ ├── HttpRequestProviderMethodOverrideDecorator.cs │ ├── HttpRequestMethodDecorator.cs │ └── HttpRequestProvider.cs ├── HttpMethodProvider.cs ├── HttpMethodProviderCache.cs ├── HttpServerExtensions.cs ├── HttpRequestHandler.cs ├── ModelBinders │ ├── IModelBinder.cs │ ├── JsonModelBinder.cs │ └── ModelBinder.cs ├── HttpContext.cs ├── Properties │ └── AssemblyInfo.cs ├── HttpMethods.cs ├── HttpResponseCode.cs ├── IHttpContext.cs ├── HttpServer.cs ├── LimitedStream.cs ├── HttpRequest.cs ├── uhttpsharp.csproj ├── HttpClient.cs └── HttpResponse.cs ├── uhttpsharp-demo ├── .gitignore ├── packages.config ├── HttpException.cs ├── Handlers │ ├── TimingHandler.cs │ ├── ExceptionHandler.cs │ ├── ErrorHandler.cs │ ├── AboutHandler.cs │ └── IndexHandler.cs ├── app.config ├── Properties │ └── AssemblyInfo.cs ├── SomeRestController.cs ├── StringsRestController.cs ├── Controllers │ └── DemoController.cs ├── uhttpsharp.Demo.csproj └── Program.cs ├── renovate.json ├── .nuget ├── NuGet.exe ├── NuGet.Config └── NuGet.targets ├── AssemblyCommon.cs ├── .gitignore ├── uhttpsharp.Tests ├── packages.config ├── HttpMethodProviderTests.cs ├── Properties │ └── AssemblyInfo.cs ├── HttpMethodProviderCacheTests.cs └── uhttpsharp.Tests.csproj ├── .ci └── RunTests.sh ├── .gitattributes ├── .travis.yml ├── uhttpsharp.dll.nuspec ├── LICENSE ├── uhttpsharp.sln ├── README.md └── uhttpsharp.6.0.ReSharper /uhttpsharp/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /obj/ 3 | -------------------------------------------------------------------------------- /uhttpsharp-demo/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /obj/ 3 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bonesoul/uhttpsharp/HEAD/.nuget/NuGet.exe -------------------------------------------------------------------------------- /AssemblyCommon.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bonesoul/uhttpsharp/HEAD/AssemblyCommon.cs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_ReSharper.uhttpsharp/ 2 | /*.6.0.ReSharper.user 3 | /*.suo 4 | /packages/* 5 | */bin/* 6 | */obj/* 7 | *.psess 8 | *.vsp 9 | *.nupkg -------------------------------------------------------------------------------- /uhttpsharp/Controllers/IController.cs: -------------------------------------------------------------------------------- 1 | namespace uhttpsharp.Controllers 2 | { 3 | public interface IController 4 | { 5 | IPipeline Pipeline { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /uhttpsharp/IHttpMethodProvider.cs: -------------------------------------------------------------------------------- 1 | namespace uhttpsharp 2 | { 3 | public interface IHttpMethodProvider 4 | { 5 | HttpMethods Provide(string name); 6 | } 7 | } -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /uhttpsharp/Controllers/IRequestParameter.cs: -------------------------------------------------------------------------------- 1 | namespace uhttpsharp.Controllers 2 | { 3 | public interface IValidate 4 | { 5 | 6 | void Validate(IErrorContainer container); 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /uhttpsharp.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uhttpsharp/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uhttpsharp-demo/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /uhttpsharp/Listeners/IHttpListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Clients; 4 | 5 | namespace uhttpsharp.Listeners 6 | { 7 | public interface IHttpListener : IDisposable 8 | { 9 | 10 | Task GetClient(); 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/IResponseProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace uhttpsharp.Handlers 4 | { 5 | public interface IResponseProvider 6 | { 7 | 8 | Task Provide(object value, HttpResponseCode responseCode = HttpResponseCode.Ok); 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /uhttpsharp/Headers/IHttpHeaders.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace uhttpsharp.Headers 4 | { 5 | public interface IHttpHeaders : IEnumerable> 6 | { 7 | 8 | string GetByName(string name); 9 | 10 | bool TryGetByName(string name, out string value); 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /uhttpsharp/Clients/IClient.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | 4 | namespace uhttpsharp.Clients 5 | { 6 | public interface IClient 7 | { 8 | 9 | Stream Stream { get; } 10 | 11 | bool Connected { get; } 12 | 13 | void Close(); 14 | 15 | EndPoint RemoteEndPoint { get; } 16 | 17 | 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /uhttpsharp/TaskFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace uhttpsharp 4 | { 5 | public static class TaskFactoryExtensions 6 | { 7 | private static readonly Task CompletedTask = Task.FromResult(null); 8 | 9 | public static Task GetCompleted(this TaskFactory factory) 10 | { 11 | return CompletedTask; 12 | } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.ci/RunTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | mono --runtime=v4.0 .nuget/NuGet.exe install NUnit.Runners -Version 2.6.1 -o packages 4 | 5 | runTest(){ 6 | mono --runtime=v4.0 packages/NUnit.Runners.2.6.1/tools/nunit-console.exe -noxml -nodots -labels $@ 7 | if [ $? -ne 0 ] 8 | then 9 | exit 1 10 | fi 11 | } 12 | 13 | runTest uhttpsharp.Tests/bin/Debug/uhttpsharp.Tests.dll -exclude=Performance 14 | 15 | exit $? -------------------------------------------------------------------------------- /uhttpsharp/Attributes/NullableAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace uhttpsharp.Attributes 4 | { 5 | /// 6 | /// Marks a controller method argmuent 7 | /// as an argument that may be null. 8 | /// 9 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 10 | public class NullableAttribute : Attribute 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /uhttpsharp/Controllers/IErrorContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Handlers; 4 | 5 | namespace uhttpsharp.Controllers 6 | { 7 | public interface IErrorContainer 8 | { 9 | 10 | void Log(string description); 11 | 12 | IEnumerable Errors { get; } 13 | 14 | bool Any { get; } 15 | 16 | Task GetResponse(); 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/Compression/GZipCompressor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace uhttpsharp.Handlers.Compression 4 | { 5 | public class GZipCompressor : ICompressor 6 | { 7 | public static readonly ICompressor Default = new GZipCompressor(); 8 | 9 | public string Name 10 | { 11 | get { return "gzip"; } 12 | } 13 | public Task Compress(IHttpResponse response) 14 | { 15 | return CompressedResponse.CreateGZip(response); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/Compression/DeflateCompressor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace uhttpsharp.Handlers.Compression 4 | { 5 | public class DeflateCompressor : ICompressor 6 | { 7 | public static readonly ICompressor Default = new DeflateCompressor(); 8 | 9 | public string Name 10 | { 11 | get { return "deflate"; } 12 | } 13 | public Task Compress(IHttpResponse response) 14 | { 15 | return CompressedResponse.CreateDeflate(response); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /uhttpsharp/Attributes/HttpMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace uhttpsharp.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] 6 | public class HttpMethodAttribute : Attribute 7 | { 8 | private readonly HttpMethods _httpMethod; 9 | public HttpMethodAttribute(HttpMethods httpMethod) 10 | { 11 | _httpMethod = httpMethod; 12 | } 13 | 14 | public HttpMethods HttpMethod 15 | { 16 | get { return _httpMethod; } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /uhttpsharp/RequestProviders/IHttpRequestProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | 4 | namespace uhttpsharp.RequestProviders 5 | { 6 | public interface IHttpRequestProvider 7 | { 8 | /// 9 | /// Provides an based on the context of the stream, 10 | /// May return null / throw exceptions on invalid requests. 11 | /// 12 | /// 13 | /// 14 | Task Provide(StreamReader streamReader); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpMethodProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace uhttpsharp 4 | { 5 | public class HttpMethodProvider : IHttpMethodProvider 6 | { 7 | public static readonly IHttpMethodProvider Default = new HttpMethodProviderCache(new HttpMethodProvider()); 8 | 9 | internal HttpMethodProvider() 10 | { 11 | 12 | } 13 | 14 | public HttpMethods Provide(string name) 15 | { 16 | var capitalName = name.Substring(0, 1).ToUpper() + name.Substring(1).ToLower(); 17 | return (HttpMethods)Enum.Parse(typeof(HttpMethods), capitalName); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/HttpException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using uhttpsharp; 3 | 4 | namespace uhttpsharpdemo 5 | { 6 | public class HttpException : Exception 7 | { 8 | private readonly HttpResponseCode _responseCode; 9 | 10 | public HttpResponseCode ResponseCode 11 | { 12 | get { return _responseCode; } 13 | } 14 | 15 | public HttpException(HttpResponseCode responseCode) 16 | { 17 | _responseCode = responseCode; 18 | } 19 | public HttpException(HttpResponseCode responseCode, string message) : base(message) 20 | { 21 | _responseCode = responseCode; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis-CI Build for WampSharp 2 | # Copied from keyczar-dotnet 3 | # see travis-ci.org for details 4 | 5 | language: objective-c 6 | 7 | env: 8 | global: 9 | - EnableNuGetPackageRestore=true 10 | matrix: 11 | - MONO_VER="3.0.12" 12 | 13 | before_install: 14 | - wget "http://download.mono-project.com/archive/${MONO_VER}/macos-10-x86/MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.dmg" 15 | - hdid "MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.dmg" 16 | - sudo installer -pkg "/Volumes/Mono Framework MDK ${MONO_VER}/MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.pkg" -target / 17 | 18 | script: 19 | - xbuild 20 | - .ci/RunTests.sh 21 | 22 | branches: 23 | only: 24 | - master -------------------------------------------------------------------------------- /uhttpsharp/HttpMethodProviderCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace uhttpsharp 5 | { 6 | public class HttpMethodProviderCache : IHttpMethodProvider 7 | { 8 | private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); 9 | 10 | private readonly Func _childProvide; 11 | public HttpMethodProviderCache(IHttpMethodProvider child) 12 | { 13 | _childProvide = child.Provide; 14 | } 15 | public HttpMethods Provide(string name) 16 | { 17 | return _cache.GetOrAdd(name, _childProvide); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/Handlers/TimingHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | using log4net; 6 | using uhttpsharp; 7 | 8 | namespace uhttpsharpdemo.Handlers 9 | { 10 | public class TimingHandler : IHttpRequestHandler 11 | { 12 | private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 13 | 14 | public async Task Handle(IHttpContext context, Func next) 15 | { 16 | var stopWatch = Stopwatch.StartNew(); 17 | await next(); 18 | 19 | Logger.InfoFormat("request {0} took {1}", context.Request.Uri, stopWatch.Elapsed); 20 | 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /uhttpsharp/Listeners/TcpListenerAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Clients; 4 | 5 | namespace uhttpsharp.Listeners 6 | { 7 | public class TcpListenerAdapter : IHttpListener 8 | { 9 | private readonly TcpListener _listener; 10 | 11 | public TcpListenerAdapter(TcpListener listener) 12 | { 13 | _listener = listener; 14 | _listener.Start(); 15 | } 16 | public async Task GetClient() 17 | { 18 | return new TcpClientAdapter(await _listener.AcceptTcpClientAsync().ConfigureAwait(false)); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _listener.Stop(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/Compression/ICompressor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace uhttpsharp.Handlers.Compression 4 | { 5 | 6 | /// 7 | /// Represents an object that can compress s. 8 | /// 9 | public interface ICompressor 10 | { 11 | 12 | /// 13 | /// The name of the compression algorithm 14 | /// 15 | string Name { get; } 16 | 17 | /// 18 | /// Compresses the given 19 | /// 20 | /// 21 | /// The compressed response 22 | Task Compress(IHttpResponse response); 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /uhttpsharp/Clients/TcpClientAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | 5 | namespace uhttpsharp.Clients 6 | { 7 | public class TcpClientAdapter : IClient 8 | { 9 | private readonly TcpClient _client; 10 | 11 | public TcpClientAdapter(TcpClient client) 12 | { 13 | _client = client; 14 | } 15 | 16 | public Stream Stream 17 | { 18 | get { return _client.GetStream(); } 19 | } 20 | 21 | public bool Connected 22 | { 23 | get { return _client.Connected; } 24 | } 25 | 26 | public void Close() 27 | { 28 | _client.Close(); 29 | } 30 | 31 | 32 | public EndPoint RemoteEndPoint 33 | { 34 | get { return _client.Client.RemoteEndPoint; } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/Handlers/ExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using uhttpsharp; 4 | 5 | namespace uhttpsharpdemo.Handlers 6 | { 7 | public class ExceptionHandler : IHttpRequestHandler 8 | { 9 | public async Task Handle(IHttpContext context, Func next) 10 | { 11 | try 12 | { 13 | await next().ConfigureAwait(false); 14 | } 15 | catch (HttpException e) 16 | { 17 | context.Response = new HttpResponse(e.ResponseCode, "Error while handling your request. " + e.Message, false); 18 | } 19 | catch (Exception e) 20 | { 21 | context.Response = new HttpResponse(HttpResponseCode.InternalServerError, "Error while handling your request. " + e, false); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /uhttpsharp/Listeners/SslListenerDecorator.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography.X509Certificates; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Clients; 4 | 5 | namespace uhttpsharp.Listeners 6 | { 7 | public class ListenerSslDecorator : IHttpListener 8 | { 9 | private readonly IHttpListener _child; 10 | private readonly X509Certificate _certificate; 11 | 12 | public ListenerSslDecorator(IHttpListener child, X509Certificate certificate) 13 | { 14 | _child = child; 15 | _certificate = certificate; 16 | } 17 | 18 | public async Task GetClient() 19 | { 20 | return new ClientSslDecorator(await _child.GetClient().ConfigureAwait(false), _certificate); 21 | } 22 | 23 | public void Dispose() 24 | { 25 | _child.Dispose(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpServerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace uhttpsharp 5 | { 6 | public static class HttpServerExtensions 7 | { 8 | 9 | public static void Use(this HttpServer server, Func, Task> method) 10 | { 11 | server.Use(new AnonymousHttpRequestHandler(method)); 12 | } 13 | 14 | } 15 | 16 | public class AnonymousHttpRequestHandler : IHttpRequestHandler 17 | { 18 | private readonly Func, Task> _method; 19 | 20 | public AnonymousHttpRequestHandler(Func, Task> method) 21 | { 22 | _method = method; 23 | } 24 | 25 | 26 | public Task Handle(IHttpContext context, Func next) 27 | { 28 | return _method(context, next); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /uhttpsharp/Controllers/IPipeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Handlers; 4 | 5 | namespace uhttpsharp.Controllers 6 | { 7 | /// 8 | /// Represents a series of functions and an entry point in which a function can be called at. 9 | /// 10 | 11 | public interface IPipeline 12 | { 13 | /// 14 | /// Starts the pipeline. 15 | /// An empty pipeline implementation will just return the task of the injected task function. 16 | /// 17 | /// The function to be called at the appropriate stage. 18 | /// The context in which this task will run (in case you need to check something before the actual task runs). 19 | /// 20 | Task Go(Func> injectedTask, IHttpContext context); 21 | } 22 | } -------------------------------------------------------------------------------- /uhttpsharp/Controllers/ErrorContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using uhttpsharp.Handlers; 4 | 5 | namespace uhttpsharp.Controllers 6 | { 7 | public class ErrorContainer : IErrorContainer 8 | { 9 | private readonly IList _errors = new List(); 10 | 11 | public void Log(string description) 12 | { 13 | _errors.Add(description); 14 | } 15 | 16 | public IEnumerable Errors 17 | { 18 | get { return _errors; } 19 | } 20 | public bool Any 21 | { 22 | get { return _errors.Count != 0; } 23 | } 24 | public Task GetResponse() 25 | { 26 | return 27 | Task.FromResult(new RenderResponse(HttpResponseCode.MethodNotAllowed, 28 | new {Message = string.Join(", ", _errors)})); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /uhttpsharp.Tests/HttpMethodProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | 5 | namespace uhttpsharp.Tests 6 | { 7 | [TestFixture] 8 | public class HttpMethodProviderTests 9 | { 10 | 11 | private static IHttpMethodProvider GetTarget() 12 | { 13 | return new HttpMethodProvider(); 14 | } 15 | 16 | 17 | private IEnumerable Methods 18 | { 19 | get 20 | { 21 | return Enum.GetNames(typeof(HttpMethods)); 22 | } 23 | } 24 | 25 | [TestCaseSource("Methods")] 26 | public void Should_Get_Right_Method(string methodName) 27 | { 28 | // Arrange 29 | var target = GetTarget(); 30 | 31 | // Act 32 | var actual = target.Provide(methodName); 33 | 34 | // Assert 35 | Assert.IsTrue(StringComparer.InvariantCultureIgnoreCase.Equals(actual.ToString(), methodName)); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/JsonResponseProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using Newtonsoft.Json; 4 | 5 | namespace uhttpsharp.Handlers 6 | { 7 | public class JsonResponseProvider : IResponseProvider 8 | { 9 | public static readonly IResponseProvider Default = new JsonResponseProvider(); 10 | 11 | private JsonResponseProvider() 12 | { 13 | 14 | } 15 | 16 | public Task Provide(object value, HttpResponseCode responseCode = HttpResponseCode.Ok) 17 | { 18 | var memoryStream = new MemoryStream(); 19 | var writer = new JsonTextWriter(new StreamWriter(memoryStream)); 20 | var serializer = new JsonSerializer() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented}; 21 | serializer.Serialize(writer, value); 22 | writer.Flush(); 23 | return Task.FromResult(new HttpResponse(responseCode, "application/json; charset=utf-8", memoryStream, true)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /uhttpsharp.dll.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | uHttpSharp 5 | 0.1.6.11 6 | uHttpSharp 7 | Shani Elharrar, Joe White, Hüseyin Uslu 8 | Shani Elharrar, Joe White, Hüseyin Uslu 9 | https://raw.github.com/shanielh/uHttpSharp/master/LICENSE.txt 10 | https://github.com/shanielh/uHttpSharp 11 | false 12 | A very lightweight & simple embedded http server for c# 13 | Initial NuGet Release. 14 | Copyright 2014 15 | http server microframeworks 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /uhttpsharp/RequestProviders/HttpRequestProviderMethodOverrideDecorator.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | 4 | namespace uhttpsharp.RequestProviders 5 | { 6 | public class HttpRequestProviderMethodOverrideDecorator : IHttpRequestProvider 7 | { 8 | private readonly IHttpRequestProvider _child; 9 | 10 | public HttpRequestProviderMethodOverrideDecorator(IHttpRequestProvider child) 11 | { 12 | _child = child; 13 | } 14 | 15 | public async Task Provide(StreamReader streamReader) 16 | { 17 | var childValue = await _child.Provide(streamReader).ConfigureAwait(false); 18 | 19 | if (childValue == null) 20 | { 21 | return null; 22 | } 23 | 24 | string methodName; 25 | if (!childValue.Headers.TryGetByName("X-HTTP-Method-Override", out methodName)) 26 | { 27 | return childValue; 28 | } 29 | 30 | return new HttpRequestMethodDecorator(childValue, HttpMethodProvider.Default.Provide(methodName)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpRequestHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Threading.Tasks; 21 | 22 | namespace uhttpsharp 23 | { 24 | public interface IHttpRequestHandler 25 | { 26 | Task Handle(IHttpContext context, Func next); 27 | } 28 | } -------------------------------------------------------------------------------- /uhttpsharp/Clients/ClientSslDecoerator.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | using System.Net.Security; 4 | using System.Security.Authentication; 5 | using System.Security.Cryptography.X509Certificates; 6 | 7 | namespace uhttpsharp.Clients 8 | { 9 | public class ClientSslDecorator : IClient 10 | { 11 | private readonly IClient _child; 12 | private readonly SslStream _sslStream; 13 | 14 | public ClientSslDecorator(IClient child, X509Certificate certificate) 15 | { 16 | _child = child; 17 | _sslStream = new SslStream(_child.Stream); 18 | _sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls, true); 19 | } 20 | 21 | public Stream Stream 22 | { 23 | get { return _sslStream; } 24 | } 25 | 26 | public bool Connected 27 | { 28 | get { return _child.Connected; } 29 | } 30 | 31 | public void Close() 32 | { 33 | _child.Close(); 34 | } 35 | 36 | public EndPoint RemoteEndPoint 37 | { 38 | get { return _child.RemoteEndPoint; } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /uhttpsharp/ModelBinders/IModelBinder.cs: -------------------------------------------------------------------------------- 1 | using uhttpsharp.Headers; 2 | 3 | namespace uhttpsharp.ModelBinders 4 | { 5 | public interface IModelBinder 6 | { 7 | /// 8 | /// Gets the object from the unparsed body 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | T Get(byte[] raw, string prefix); 15 | 16 | /// 17 | /// Gets the object from the body of the given headers 18 | /// 19 | /// 20 | /// 21 | /// 22 | T Get(IHttpHeaders headers); 23 | 24 | /// 25 | /// Gets the object using the prefix from the given headers 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | T Get(IHttpHeaders headers, string prefix); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011 - 2016 Intware.com - Hüseyin Uslu, shalafiraistlin at gmail dot com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/IView.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Newtonsoft.Json; 3 | 4 | namespace uhttpsharp.Handlers 5 | { 6 | public interface IViewResponse 7 | { 8 | 9 | string Body { get; } 10 | 11 | string ContentType { get; } 12 | 13 | } 14 | 15 | public interface IView 16 | { 17 | Task Render (IHttpContext context, object state); 18 | } 19 | 20 | public class JsonView : IView 21 | { 22 | public Task Render (IHttpContext context, object state) 23 | { 24 | return Task.FromResult(new JsonViewResponse(JsonConvert.SerializeObject(state))); 25 | } 26 | 27 | class JsonViewResponse : IViewResponse 28 | { 29 | private readonly string _body; 30 | public JsonViewResponse(string body) 31 | { 32 | _body = body; 33 | } 34 | public string Body 35 | { 36 | get { return _body; } 37 | } 38 | public string ContentType 39 | { 40 | get { return "application/json; charset=utf-8"; } 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /uhttpsharp/Headers/EmptyHttpHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | 6 | namespace uhttpsharp.Headers 7 | { 8 | [DebuggerDisplay("Empty Headers")] 9 | public class EmptyHttpHeaders : IHttpHeaders 10 | { 11 | public static readonly IHttpHeaders Empty = new EmptyHttpHeaders(); 12 | 13 | private static readonly IEnumerable> EmptyKeyValuePairs = new KeyValuePair[0]; 14 | 15 | private EmptyHttpHeaders() 16 | { 17 | 18 | } 19 | 20 | public IEnumerator> GetEnumerator() 21 | { 22 | return EmptyKeyValuePairs.GetEnumerator(); 23 | } 24 | IEnumerator IEnumerable.GetEnumerator() 25 | { 26 | return EmptyKeyValuePairs.GetEnumerator(); 27 | } 28 | public string GetByName(string name) 29 | { 30 | throw new ArgumentException("EmptyHttpHeaders does not contain any header"); 31 | } 32 | public bool TryGetByName(string name, out string value) 33 | { 34 | value = null; 35 | return false; 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpContext.cs: -------------------------------------------------------------------------------- 1 | using System.Dynamic; 2 | using System.Net; 3 | using uhttpsharp.Headers; 4 | 5 | namespace uhttpsharp 6 | { 7 | internal class HttpContext : IHttpContext 8 | { 9 | private readonly IHttpRequest _request; 10 | private readonly EndPoint _remoteEndPoint; 11 | private readonly ICookiesStorage _cookies; 12 | private readonly ExpandoObject _state = new ExpandoObject(); 13 | public HttpContext(IHttpRequest request, EndPoint remoteEndPoint) 14 | { 15 | _request = request; 16 | _remoteEndPoint = remoteEndPoint; 17 | _cookies = new CookiesStorage(_request.Headers.GetByNameOrDefault("cookie", string.Empty)); 18 | } 19 | 20 | public IHttpRequest Request 21 | { 22 | get { return _request; } 23 | } 24 | 25 | public IHttpResponse Response { get; set; } 26 | 27 | public ICookiesStorage Cookies 28 | { 29 | get { return _cookies; } 30 | } 31 | 32 | 33 | public dynamic State 34 | { 35 | get { return _state; } 36 | } 37 | public EndPoint RemoteEndPoint 38 | { 39 | get { return _remoteEndPoint; } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /uhttpsharp/RequestProviders/HttpRequestMethodDecorator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using uhttpsharp.Headers; 3 | 4 | namespace uhttpsharp.RequestProviders 5 | { 6 | internal class HttpRequestMethodDecorator : IHttpRequest 7 | { 8 | private readonly IHttpRequest _child; 9 | private readonly HttpMethods _method; 10 | 11 | public HttpRequestMethodDecorator(IHttpRequest child, HttpMethods method) 12 | { 13 | _child = child; 14 | _method = method; 15 | } 16 | 17 | public IHttpHeaders Headers 18 | { 19 | get { return _child.Headers; } 20 | } 21 | 22 | public HttpMethods Method 23 | { 24 | get { return _method; } 25 | } 26 | 27 | public string Protocol 28 | { 29 | get { return _child.Protocol; } 30 | } 31 | 32 | public Uri Uri 33 | { 34 | get { return _child.Uri; } 35 | } 36 | 37 | public string[] RequestParameters 38 | { 39 | get { return _child.RequestParameters; } 40 | } 41 | 42 | public IHttpPost Post 43 | { 44 | get { return _child.Post; } 45 | } 46 | 47 | public IHttpHeaders QueryString 48 | { 49 | get { return _child.QueryString; } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /uhttpsharp-demo/Handlers/ErrorHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System.Threading.Tasks; 20 | using uhttpsharp; 21 | 22 | namespace uhttpsharpdemo.Handlers 23 | { 24 | public class ErrorHandler : IHttpRequestHandler 25 | { 26 | public Task Handle(IHttpContext context, System.Func next) 27 | { 28 | context.Response = new HttpResponse(HttpResponseCode.NotFound, "These are not the droids you are looking for.", true); 29 | return Task.Factory.GetCompleted(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System.Reflection; 20 | using System.Runtime.InteropServices; 21 | // General Information about an assembly is controlled through the following 22 | // set of attributes. Change these attribute values to modify the information 23 | // associated with an assembly. 24 | 25 | [assembly: AssemblyTitle("µHttpSharp Demo")] 26 | 27 | // The following GUID is for the ID of the typelib if this project is exposed to COM 28 | 29 | [assembly: Guid("5a2d8552-ed37-43cb-8744-026174645de4")] -------------------------------------------------------------------------------- /uhttpsharp/Headers/HttpHeadersDebuggerProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | namespace uhttpsharp.Headers 6 | { 7 | internal class HttpHeadersDebuggerProxy 8 | { 9 | private readonly IHttpHeaders _real; 10 | 11 | [DebuggerDisplay("{Value,nq}", Name = "{Key,nq}")] 12 | internal class HttpHeader 13 | { 14 | private readonly KeyValuePair _header; 15 | public HttpHeader(KeyValuePair header) 16 | { 17 | _header = header; 18 | } 19 | 20 | public string Value 21 | { 22 | get 23 | { 24 | return _header.Value; 25 | } 26 | } 27 | 28 | public string Key 29 | { 30 | get 31 | { 32 | return _header.Key; 33 | } 34 | } 35 | } 36 | 37 | public HttpHeadersDebuggerProxy(IHttpHeaders real) 38 | { 39 | _real = real; 40 | } 41 | 42 | [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] 43 | public HttpHeader[] Headers 44 | { 45 | get 46 | { 47 | return _real.Select(kvp => new HttpHeader(kvp)).ToArray(); 48 | } 49 | } 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/IRestController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace uhttpsharp.Handlers 5 | { 6 | public interface IRestController 7 | { 8 | /// 9 | /// Returns a list of object that found in the collection 10 | /// 11 | /// 12 | /// 13 | Task> Get(IHttpRequest request); 14 | 15 | /// 16 | /// Returns an item from the collection 17 | /// 18 | /// 19 | /// 20 | Task GetItem(IHttpRequest request); 21 | 22 | /// 23 | /// Creates a new entry in the collection - new uri is returned 24 | /// 25 | /// 26 | /// 27 | Task Create(IHttpRequest request); 28 | 29 | /// 30 | /// Updates an entry in the collection 31 | /// 32 | /// 33 | /// 34 | Task Upsert(IHttpRequest request); 35 | 36 | /// 37 | /// Removes an entry from the collection 38 | /// 39 | /// 40 | /// 41 | Task Delete(IHttpRequest request); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /uhttpsharp-demo/Handlers/AboutHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Threading.Tasks; 21 | using uhttpsharp; 22 | using uhttpsharp.Headers; 23 | 24 | namespace uhttpsharpdemo.Handlers 25 | { 26 | public class AboutHandler : IHttpRequestHandler 27 | { 28 | 29 | public Task Handle(IHttpContext context, Func next) 30 | { 31 | context.Response = HttpResponse.CreateWithMessage(HttpResponseCode.Ok, "Sample http-request-handler", context.Request.Headers.KeepAliveConnection()); 32 | 33 | return Task.Factory.GetCompleted(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /uhttpsharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System.Reflection; 20 | using System.Runtime.CompilerServices; 21 | using System.Runtime.InteropServices; 22 | 23 | // General Information about an assembly is controlled through the following 24 | // set of attributes. Change these attribute values to modify the information 25 | // associated with an assembly. 26 | 27 | [assembly: AssemblyTitle("µHttpSharp")] 28 | 29 | // The following GUID is for the ID of the typelib if this project is exposed to COM 30 | 31 | [assembly: Guid("2a229a48-3435-4e8d-bfda-9e2535940a81")] 32 | 33 | [assembly:InternalsVisibleTo("uhttpsharp.Tests")] 34 | -------------------------------------------------------------------------------- /uhttpsharp.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("µHttpSharp Tests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Microsoft")] 11 | [assembly: AssemblyProduct("µHttpSharp Tests")] 12 | [assembly: AssemblyCopyright("Copyright © Microsoft 2013")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("431b01f5-152f-4859-a201-fde2db3d72cd")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /uhttpsharp/ModelBinders/JsonModelBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using uhttpsharp.Headers; 6 | 7 | namespace uhttpsharp.ModelBinders 8 | { 9 | public class JsonModelBinder : IModelBinder 10 | { 11 | private readonly JsonSerializer _serializer; 12 | 13 | public JsonModelBinder(JsonSerializer serializer) 14 | { 15 | _serializer = serializer; 16 | } 17 | 18 | public JsonModelBinder() : this(JsonSerializer.CreateDefault()) 19 | { 20 | 21 | } 22 | public T Get(byte[] raw, string prefix) 23 | { 24 | var rawDecoded = Encoding.UTF8.GetString(raw); 25 | 26 | if (raw.Length == 0) 27 | { 28 | return default(T); 29 | } 30 | 31 | if (prefix == null && typeof(T) == typeof(string)) 32 | { 33 | return (T)(object)rawDecoded; 34 | } 35 | 36 | var jToken = JToken.Parse(rawDecoded); 37 | 38 | if (prefix != null) 39 | { 40 | jToken = jToken.SelectToken(prefix); 41 | } 42 | 43 | return jToken.ToObject(_serializer); 44 | } 45 | public T Get(IHttpHeaders headers) 46 | { 47 | throw new NotSupportedException(); 48 | } 49 | public T Get(IHttpHeaders headers, string prefix) 50 | { 51 | throw new NotSupportedException(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /uhttpsharp-demo/SomeRestController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using uhttpsharp; 7 | 8 | namespace uhttpsharpdemo 9 | { 10 | class SomeRestController 11 | { 12 | readonly IDictionary _strings = new Dictionary() { { 1 , "Hahaha"}}; 13 | 14 | public Task Get(uhttpsharp.IHttpRequest request) 15 | { 16 | var memoryStream = new MemoryStream(); 17 | Newtonsoft.Json.JsonWriter writer = new JsonTextWriter(new StreamWriter( memoryStream)); 18 | 19 | JsonSerializer.Create().Serialize(writer, _strings); 20 | writer.Flush(); 21 | return Task.FromResult(new HttpResponse(HttpResponseCode.Ok, "application/json; charset=utf-8", memoryStream, true)); 22 | } 23 | 24 | public Task GetItem(uhttpsharp.IHttpRequest request) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | public Task Create(uhttpsharp.IHttpRequest request) 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | 34 | public Task Upsert(uhttpsharp.IHttpRequest request) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | public Task Delete(uhttpsharp.IHttpRequest request) 40 | { 41 | throw new NotImplementedException(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /uhttpsharp/HttpMethods.cs: -------------------------------------------------------------------------------- 1 | namespace uhttpsharp 2 | { 3 | /// 4 | /// Specifies the methods of an http request. 5 | /// 6 | public enum HttpMethods 7 | { 8 | /// 9 | /// Requests a representation of the specified resource. 10 | /// 11 | Get, 12 | 13 | /// 14 | /// Asks for the response identical to the one that would correspond to a GET request, but without the response body 15 | /// 16 | Head, 17 | 18 | /// 19 | /// Requests that the server accept the entity enclosed in the request as a new subordinate of the web resource identified by the URI 20 | /// 21 | Post, 22 | 23 | /// 24 | /// Requests that the enclosed entity be stored under the supplied URI 25 | /// 26 | Put, 27 | 28 | /// 29 | /// Deletes the specified resource. 30 | /// 31 | Delete, 32 | 33 | /// 34 | /// Echoes back the received request so that a client can see what (if any) changes or additions have been made by intermediate servers 35 | /// 36 | Trace, 37 | 38 | /// 39 | /// Returns the HTTP methods that the server supports for the specified URL 40 | /// 41 | Options, 42 | 43 | /// 44 | /// Converts the request connection to a transparent TCP/IP tunnel 45 | /// 46 | Connect, 47 | 48 | /// 49 | /// Is used to apply partial modifications to a resource 50 | /// 51 | Patch 52 | } 53 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/Handlers/IndexHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Text; 21 | using System.Threading.Tasks; 22 | using uhttpsharp; 23 | using uhttpsharp.Headers; 24 | 25 | namespace uhttpsharpdemo.Handlers 26 | { 27 | public class IndexHandler : IHttpRequestHandler 28 | { 29 | private readonly HttpResponse _response; 30 | private readonly HttpResponse _keepAliveResponse; 31 | 32 | public IndexHandler() 33 | { 34 | byte[] contents = Encoding.UTF8.GetBytes("Welcome to the Index."); 35 | _keepAliveResponse = new HttpResponse(HttpResponseCode.Ok, contents, true); 36 | _response = new HttpResponse(HttpResponseCode.Ok, contents, false); 37 | } 38 | 39 | public Task Handle(IHttpContext context, Func next) 40 | { 41 | context.Response = context.Request.Headers.KeepAliveConnection() ? _keepAliveResponse : _response; 42 | return Task.Factory.GetCompleted(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/StringsRestController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using uhttpsharp; 5 | using uhttpsharp.Handlers; 6 | 7 | namespace uhttpsharpdemo 8 | { 9 | class StringsRestController : IRestController 10 | { 11 | private readonly ICollection _collection = new HashSet(); 12 | 13 | public Task> Get(IHttpRequest request) 14 | { 15 | return Task.FromResult>(_collection); 16 | } 17 | public Task GetItem(IHttpRequest request) 18 | { 19 | var id = GetId(request); 20 | 21 | if (_collection.Contains(id)) 22 | { 23 | return Task.FromResult(id); 24 | } 25 | 26 | throw GetNotFoundException(); 27 | } 28 | private static string GetId(IHttpRequest request) 29 | { 30 | var id = request.RequestParameters[1]; 31 | 32 | return id; 33 | } 34 | public Task Create(IHttpRequest request) 35 | { 36 | var id = GetId(request); 37 | 38 | _collection.Add(id); 39 | 40 | return Task.FromResult(id); 41 | } 42 | public Task Upsert(IHttpRequest request) 43 | { 44 | return Create(request); 45 | } 46 | public Task Delete(IHttpRequest request) 47 | { 48 | var id = GetId(request); 49 | 50 | if (_collection.Remove(id)) 51 | { 52 | return Task.FromResult(id); 53 | } 54 | 55 | throw GetNotFoundException(); 56 | } 57 | private static Exception GetNotFoundException() 58 | { 59 | return new HttpException(HttpResponseCode.NotFound, "The resource you've looked for is not found"); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /uhttpsharp/Headers/HttpHeadersExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace uhttpsharp.Headers 5 | { 6 | public static class HttpHeadersExtensions 7 | { 8 | public static bool KeepAliveConnection(this IHttpHeaders headers) 9 | { 10 | string value; 11 | return headers.TryGetByName("connection", out value) 12 | && value.Equals("Keep-Alive", StringComparison.InvariantCultureIgnoreCase); 13 | } 14 | 15 | public static bool TryGetByName(this IHttpHeaders headers, string name, out T value) 16 | { 17 | string stringValue; 18 | 19 | if (headers.TryGetByName(name, out stringValue)) 20 | { 21 | value = (T) Convert.ChangeType(stringValue, typeof(T)); 22 | return true; 23 | } 24 | 25 | value = default(T); 26 | return false; 27 | } 28 | 29 | public static T GetByName(this IHttpHeaders headers, string name) 30 | { 31 | T value; 32 | headers.TryGetByName(name, out value); 33 | return value; 34 | } 35 | 36 | public static T GetByNameOrDefault(this IHttpHeaders headers, string name, T defaultValue) 37 | { 38 | T value; 39 | if (headers.TryGetByName(name, out value)) 40 | { 41 | return value; 42 | } 43 | 44 | return defaultValue; 45 | } 46 | 47 | public static string ToUriData(this IHttpHeaders headers) 48 | { 49 | var builder = new StringBuilder(); 50 | 51 | foreach (var header in headers) 52 | { 53 | builder.AppendFormat("{0}={1}&", Uri.EscapeDataString(header.Key), Uri.EscapeDataString(header.Value)); 54 | } 55 | 56 | return builder.ToString(0, builder.Length - 1); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /uhttpsharp.Tests/HttpMethodProviderCacheTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using NUnit.Framework; 3 | 4 | namespace uhttpsharp.Tests 5 | { 6 | [TestFixture] 7 | public class HttpMethodProviderCacheTests 8 | { 9 | private const string MethodName = "Hello World"; 10 | 11 | private static IHttpMethodProvider GetTarget(IHttpMethodProvider child) 12 | { 13 | return new HttpMethodProviderCache(child); 14 | } 15 | 16 | [Test] 17 | public void Should_Call_Child_With_Right_Parameters() 18 | { 19 | // Arrange 20 | var mock = new Mock(); 21 | var target = GetTarget(mock.Object); 22 | 23 | // Act 24 | target.Provide(MethodName); 25 | 26 | // Assert 27 | mock.Verify(m => m.Provide(MethodName), Times.Once); 28 | } 29 | 30 | [Test] 31 | public void Should_Return_Same_Child_Value() 32 | { 33 | // Arrange 34 | const HttpMethods expectedMethod = HttpMethods.Post; 35 | 36 | var mock = new Mock(); 37 | var target = GetTarget(mock.Object); 38 | 39 | mock.Setup(m => m.Provide(MethodName)).Returns(expectedMethod); 40 | 41 | // Act 42 | var actual = target.Provide(MethodName); 43 | 44 | // Assert 45 | Assert.That(actual, Is.EqualTo(expectedMethod)); 46 | } 47 | 48 | [Test] 49 | public void Should_Cache_The_Value() 50 | { 51 | // Arrange 52 | var mock = new Mock(); 53 | var target = GetTarget(mock.Object); 54 | 55 | // Act 56 | target.Provide(MethodName); 57 | target.Provide(MethodName); 58 | target.Provide(MethodName); 59 | 60 | // Assert 61 | mock.Verify(m => m.Provide(MethodName), Times.Once); 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/HttpRouter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Threading.Tasks; 22 | 23 | namespace uhttpsharp.Handlers 24 | { 25 | public class HttpRouter : IHttpRequestHandler 26 | { 27 | private readonly IDictionary _handlers = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 28 | 29 | public HttpRouter With(string function, IHttpRequestHandler handler) 30 | { 31 | _handlers.Add(function, handler); 32 | 33 | return this; 34 | } 35 | 36 | public Task Handle(IHttpContext context, Func nextHandler) 37 | { 38 | string function = string.Empty; 39 | 40 | if (context.Request.RequestParameters.Length > 0) 41 | { 42 | function = context.Request.RequestParameters[0]; 43 | } 44 | 45 | IHttpRequestHandler value; 46 | if (_handlers.TryGetValue(function, out value)) 47 | { 48 | return value.Handle(context, nextHandler); 49 | } 50 | 51 | 52 | // Route not found, Call next. 53 | return nextHandler(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /uhttpsharp/Headers/QueryStringHttpHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | 6 | namespace uhttpsharp.Headers 7 | { 8 | [DebuggerDisplay("{Count} Query String Headers")] 9 | [DebuggerTypeProxy(typeof(HttpHeadersDebuggerProxy))] 10 | internal class QueryStringHttpHeaders : IHttpHeaders 11 | { 12 | private readonly HttpHeaders _child; 13 | private static readonly char[] Seperators = {'&', '='}; 14 | 15 | private readonly int _count; 16 | 17 | public QueryStringHttpHeaders(string query) 18 | { 19 | var splittedKeyValues = query.Split(Seperators, StringSplitOptions.RemoveEmptyEntries); 20 | var values = new Dictionary(splittedKeyValues.Length / 2, StringComparer.InvariantCultureIgnoreCase); 21 | 22 | for (int i = 0; i < splittedKeyValues.Length; i += 2) 23 | { 24 | var key = Uri.UnescapeDataString(splittedKeyValues[i]); 25 | string value = null; 26 | if (splittedKeyValues.Length > i + 1) 27 | { 28 | value = Uri.UnescapeDataString(splittedKeyValues[i + 1]).Replace('+', ' '); 29 | } 30 | 31 | values[key] = value; 32 | } 33 | 34 | _count = values.Count; 35 | _child = new HttpHeaders(values); 36 | } 37 | 38 | public string GetByName(string name) 39 | { 40 | return _child.GetByName(name); 41 | } 42 | public bool TryGetByName(string name, out string value) 43 | { 44 | return _child.TryGetByName(name, out value); 45 | } 46 | public IEnumerator> GetEnumerator() 47 | { 48 | return _child.GetEnumerator(); 49 | } 50 | IEnumerator IEnumerable.GetEnumerator() 51 | { 52 | return GetEnumerator(); 53 | } 54 | 55 | internal int Count 56 | { 57 | get 58 | { 59 | return _count; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/Compression/CompressionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace uhttpsharp.Handlers.Compression 7 | { 8 | /// 9 | /// An 10 | /// 11 | /// That lets the following s in the chain to run 12 | /// and afterwards tries to compress the returned response by the "Accept-Encoding" header that 13 | /// given from the client. 14 | /// 15 | /// The compressors given in the constructor are prefered by the order that they are given. 16 | /// 17 | public class CompressionHandler : IHttpRequestHandler 18 | { 19 | private readonly IEnumerable _compressors; 20 | private static readonly char[] Seperator = { ',' }; 21 | 22 | /// 23 | /// Creates an instance of 24 | /// 25 | /// The compressors to use, Ordered by preference 26 | public CompressionHandler(params ICompressor[] compressors) 27 | { 28 | _compressors = compressors; 29 | } 30 | 31 | public async Task Handle(IHttpContext context, Func next) 32 | { 33 | await next().ConfigureAwait(false); 34 | 35 | if (context.Response == null) 36 | { 37 | return; 38 | } 39 | 40 | string encodingNames; 41 | if (!context.Request.Headers.TryGetByName("Accept-Encoding", out encodingNames)) 42 | { 43 | return; 44 | } 45 | 46 | var encodings = encodingNames.Split(Seperator, StringSplitOptions.RemoveEmptyEntries); 47 | 48 | var compressor = 49 | _compressors.FirstOrDefault(c => encodings.Contains(c.Name, StringComparer.InvariantCultureIgnoreCase)); 50 | 51 | if (compressor == null) 52 | { 53 | return; 54 | } 55 | 56 | context.Response = await compressor.Compress(context.Response).ConfigureAwait(false); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpResponseCode.cs: -------------------------------------------------------------------------------- 1 | namespace uhttpsharp 2 | { 3 | public enum HttpResponseCode 4 | { 5 | // Informational 6 | Continue = 100, 7 | SwitchingProtocols = 101, 8 | Processing = 102, 9 | 10 | // Success 11 | Ok = 200, 12 | Created = 201, 13 | Accepted =202, 14 | NonAuthorativeInformation = 203, 15 | NoContent = 204, 16 | ResetContent = 205, 17 | PartialContent = 206, 18 | MultiStatus = 207, 19 | AlreadyReported = 208, 20 | IMUsed = 226, 21 | 22 | // Redirection 23 | MovedPermanently=301, 24 | Found = 302, 25 | SeeOther = 303, 26 | NotModified=304, 27 | UseProxy=305, 28 | SwitchProxy=306, 29 | TemporaryRedirect=307, 30 | PermanentRedirect=308, 31 | 32 | // Client Error 33 | BadRequest = 400, 34 | Unauthorized=401, 35 | PaymentRequired=402, 36 | Forbidden=403, 37 | NotFound = 404, 38 | MethodNotAllowed=405, 39 | NotAcceptable=406, 40 | ProxyAuthenticationRequired=407, 41 | RequestTimeout=408, 42 | Conflict=409, 43 | Gone=410, 44 | LengthRequired=411, 45 | PreconditionFailed=412, 46 | RequestEntityTooLarge=413, 47 | RequestUriTooLong=414, 48 | UnsupportedMediaType=415, 49 | RequestedRangeNotSatisfiable=416, 50 | ExpectationFailed=417, 51 | ImATeapot=418, 52 | AuthenticationTimeout=419, 53 | MethodFailure=420, 54 | UnprocessableEntity=422, 55 | Locked=423, 56 | FailedDependency=424, 57 | UnorderedCollection=425, 58 | UpgradeRequired=426, 59 | PrecondittionRequired=428, 60 | TooManyRequests=429, 61 | RequestHeaderFieldsTooLarge=431, 62 | LoginTimeout=440, 63 | NoResponse=444, 64 | RetryWith=449, 65 | BlockedByWindowsParentalControls=450, 66 | UnavailableForLegalReasons=451, 67 | RequestHeaderTooLarge=494, 68 | CertError=495, 69 | NoCert=496, 70 | HttpToHttps=497, 71 | ClientClosedRequest=499, 72 | 73 | // Server Errors 74 | InternalServerError = 500, 75 | NotImplemented=501, 76 | BadGateway=502, 77 | ServiceUnavailable=503, 78 | GatewayTimeout=504, 79 | HttpVersionNotSupported=505, 80 | VariantAlsoNegotiates=506, 81 | InsufficientStorage=507, 82 | LoopDetected=508, 83 | BandwidthLimitExceeded=509, 84 | NotExtended=510, 85 | NetworkAuthenticationRequired=511, 86 | OriginError=520, 87 | WebServerIsDown=521, 88 | ConnectionTimedOut=522, 89 | ProxyDeclinedRequest=523, 90 | ATimeoutOccured=524, 91 | NetworkReadTimeoutError=598, 92 | NetworkRConnectTimeoutError=599, 93 | } 94 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/FileHandler.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System.Collections.Generic; 20 | using System.IO; 21 | using System.Threading.Tasks; 22 | using uhttpsharp.Headers; 23 | 24 | namespace uhttpsharp.Handlers 25 | { 26 | public class FileHandler : IHttpRequestHandler 27 | { 28 | public static string DefaultMimeType { get; set; } 29 | public static string HttpRootDirectory { get; set; } 30 | public static IDictionary MimeTypes { get; private set; } 31 | 32 | static FileHandler() 33 | { 34 | 35 | DefaultMimeType = "text/plain"; 36 | MimeTypes = new Dictionary 37 | { 38 | {".css", "text/css"}, 39 | {".gif", "image/gif"}, 40 | {".htm", "text/html"}, 41 | {".html", "text/html"}, 42 | {".jpg", "image/jpeg"}, 43 | {".js", "application/javascript"}, 44 | {".png", "image/png"}, 45 | {".xml", "application/xml"}, 46 | }; 47 | } 48 | 49 | private string GetContentType(string path) 50 | { 51 | var extension = Path.GetExtension(path) ?? string.Empty; 52 | if (MimeTypes.ContainsKey(extension)) 53 | return MimeTypes[extension]; 54 | return DefaultMimeType; 55 | } 56 | public async Task Handle(IHttpContext context, System.Func next) 57 | { 58 | var requestPath = context.Request.Uri.OriginalString.TrimStart('/'); 59 | 60 | var httpRoot = Path.GetFullPath(HttpRootDirectory ?? "."); 61 | var path = Path.GetFullPath(Path.Combine(httpRoot, requestPath)); 62 | 63 | if (!File.Exists(path)) 64 | { 65 | await next().ConfigureAwait(false); 66 | 67 | return; 68 | } 69 | 70 | context.Response = new HttpResponse(GetContentType(path), File.OpenRead(path), context.Request.Headers.KeepAliveConnection()); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /uhttpsharp/IHttpContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Text; 5 | using uhttpsharp.Headers; 6 | 7 | namespace uhttpsharp 8 | { 9 | public interface IHttpContext 10 | { 11 | IHttpRequest Request { get; } 12 | 13 | IHttpResponse Response { get; set; } 14 | 15 | ICookiesStorage Cookies { get; } 16 | 17 | dynamic State { get; } 18 | 19 | EndPoint RemoteEndPoint { get; } 20 | } 21 | 22 | public interface ICookiesStorage : IHttpHeaders 23 | { 24 | void Upsert(string key, string value); 25 | 26 | void Remove(string key); 27 | 28 | bool Touched { get; } 29 | 30 | string ToCookieData(); 31 | } 32 | 33 | public class CookiesStorage : ICookiesStorage 34 | { 35 | private static readonly string[] CookieSeparators = { "; ", "=" }; 36 | 37 | private readonly Dictionary _values; 38 | 39 | private bool _touched; 40 | 41 | public bool Touched 42 | { 43 | get { return _touched; } 44 | } 45 | 46 | public string ToCookieData() 47 | { 48 | StringBuilder builder = new StringBuilder(); 49 | 50 | foreach (var kvp in _values) 51 | { 52 | builder.AppendFormat("Set-Cookie: {0}={1}{2}", kvp.Key, kvp.Value, Environment.NewLine); 53 | } 54 | 55 | return builder.ToString(); 56 | } 57 | 58 | public CookiesStorage(string cookie) 59 | { 60 | var keyValues = cookie.Split(CookieSeparators, StringSplitOptions.RemoveEmptyEntries); 61 | _values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 62 | 63 | for (int i = 0; i < keyValues.Length; i += 2) 64 | { 65 | var key = keyValues[i]; 66 | var value = keyValues[i + 1]; 67 | 68 | _values[key] = value; 69 | } 70 | } 71 | 72 | 73 | 74 | public void Upsert(string key, string value) 75 | { 76 | _values[key] = value; 77 | 78 | _touched = true; 79 | } 80 | 81 | public void Remove(string key) 82 | { 83 | if (_values.Remove(key)) 84 | { 85 | _touched = true; 86 | } 87 | } 88 | 89 | public IEnumerator> GetEnumerator() 90 | { 91 | return _values.GetEnumerator(); 92 | } 93 | 94 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 95 | { 96 | return GetEnumerator(); 97 | } 98 | 99 | public string GetByName(string name) 100 | { 101 | return _values[name]; 102 | } 103 | 104 | public bool TryGetByName(string name, out string value) 105 | { 106 | return _values.TryGetValue(name, out value); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/Compression/CompressedResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.IO.Compression; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using uhttpsharp.Headers; 9 | 10 | namespace uhttpsharp.Handlers.Compression 11 | { 12 | public class CompressedResponse : IHttpResponse 13 | { 14 | private readonly HttpResponseCode _responseCode; 15 | private readonly IHttpHeaders _headers; 16 | private readonly MemoryStream _memoryStream; 17 | private readonly bool _closeConnection; 18 | 19 | public CompressedResponse(IHttpResponse child, MemoryStream memoryStream, string encoding) 20 | { 21 | _memoryStream = memoryStream; 22 | 23 | _responseCode = child.ResponseCode; 24 | _closeConnection = child.CloseConnection; 25 | _headers = 26 | new ListHttpHeaders( 27 | child.Headers.Where(h => !h.Key.Equals("content-length", StringComparison.InvariantCultureIgnoreCase)) 28 | .Concat(new[] 29 | { 30 | new KeyValuePair("content-length", memoryStream.Length.ToString(CultureInfo.InvariantCulture)), 31 | new KeyValuePair("content-encoding", encoding), 32 | }) 33 | .ToList()); 34 | 35 | 36 | } 37 | 38 | 39 | public static async Task Create(string name, IHttpResponse child, Func streamFactory) 40 | { 41 | var memoryStream = new MemoryStream(); 42 | using (var deflateStream = streamFactory(memoryStream)) 43 | using (var deflateWriter = new StreamWriter(deflateStream)) 44 | { 45 | await child.WriteBody(deflateWriter).ConfigureAwait(false); 46 | await deflateWriter.FlushAsync().ConfigureAwait(false); 47 | } 48 | 49 | return new CompressedResponse(child, memoryStream, name); 50 | } 51 | 52 | public static Task CreateDeflate(IHttpResponse child) 53 | { 54 | return Create("deflate",child, s => new DeflateStream(s, CompressionMode.Compress, true)); 55 | } 56 | 57 | public static Task CreateGZip(IHttpResponse child) 58 | { 59 | return Create("gzip",child, s => new GZipStream(s, CompressionMode.Compress, true)); 60 | } 61 | 62 | public async Task WriteBody(StreamWriter writer) 63 | { 64 | _memoryStream.Position = 0; 65 | 66 | await writer.FlushAsync().ConfigureAwait(false); 67 | await _memoryStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false); 68 | } 69 | public HttpResponseCode ResponseCode 70 | { 71 | get { return _responseCode; } 72 | } 73 | public IHttpHeaders Headers 74 | { 75 | get { return _headers; } 76 | } 77 | public bool CloseConnection 78 | { 79 | get { return _closeConnection; } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /uhttpsharp/HttpServer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using log4net; 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Reflection; 23 | using System.Threading.Tasks; 24 | using uhttpsharp.Listeners; 25 | using uhttpsharp.RequestProviders; 26 | 27 | namespace uhttpsharp 28 | { 29 | public sealed class HttpServer : IDisposable 30 | { 31 | private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 32 | 33 | private bool _isActive; 34 | 35 | private readonly IList _handlers = new List(); 36 | private readonly IList _listeners = new List(); 37 | private readonly IHttpRequestProvider _requestProvider; 38 | 39 | 40 | public HttpServer(IHttpRequestProvider requestProvider) 41 | { 42 | _requestProvider = requestProvider; 43 | } 44 | 45 | public void Use(IHttpRequestHandler handler) 46 | { 47 | _handlers.Add(handler); 48 | } 49 | 50 | public void Use(IHttpListener listener) 51 | { 52 | _listeners.Add(listener); 53 | } 54 | 55 | public void Start() 56 | { 57 | _isActive = true; 58 | 59 | foreach (var listener in _listeners) 60 | { 61 | IHttpListener tempListener = listener; 62 | 63 | Task.Factory.StartNew(() => Listen(tempListener)); 64 | } 65 | 66 | Logger.InfoFormat("Embedded uhttpserver started."); 67 | } 68 | 69 | private async void Listen(IHttpListener listener) 70 | { 71 | var aggregatedHandler = _handlers.Aggregate(); 72 | 73 | while (_isActive) 74 | { 75 | try 76 | { 77 | new HttpClientHandler(await listener.GetClient().ConfigureAwait(false), aggregatedHandler, _requestProvider); 78 | } 79 | catch (Exception e) 80 | { 81 | Logger.Warn("Error while getting client", e); 82 | } 83 | } 84 | 85 | Logger.InfoFormat("Embedded uhttpserver stopped."); 86 | } 87 | 88 | public void Dispose() 89 | { 90 | _isActive = false; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /uhttpsharp/Attributes/IModelBinding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using uhttpsharp.ModelBinders; 4 | 5 | namespace uhttpsharp.Attributes 6 | { 7 | internal interface IModelBinding 8 | { 9 | T Get(IHttpContext context, IModelBinder binder); 10 | } 11 | 12 | public class FromStateAttribute : Attribute, IModelBinding 13 | { 14 | private readonly string _propertyName; 15 | public FromStateAttribute(string propertyName) 16 | { 17 | _propertyName = propertyName; 18 | } 19 | public T Get(IHttpContext context, IModelBinder binder) 20 | { 21 | // Expando object 22 | var state = (context.State as IDictionary); 23 | object real; 24 | if (state != null && state.TryGetValue(_propertyName, out real) && real is T) 25 | { 26 | return (T)real; 27 | } 28 | 29 | return default(T); 30 | } 31 | } 32 | 33 | public class FromBodyAttribute : PrefixAttribute 34 | { 35 | public FromBodyAttribute(string prefix = null) : base(prefix) 36 | { 37 | 38 | } 39 | 40 | public override T Get(IHttpContext context, IModelBinder binder) 41 | { 42 | return binder.Get(context.Request.Post.Raw, Prefix); 43 | } 44 | } 45 | 46 | public class FromPostAttribute : PrefixAttribute 47 | { 48 | public FromPostAttribute(string prefix = null) 49 | : base(prefix) 50 | { 51 | } 52 | public override T Get(IHttpContext context, IModelBinder binder) 53 | { 54 | return binder.Get(context.Request.Post.Parsed, Prefix); 55 | } 56 | } 57 | 58 | public class FromQueryAttribute : PrefixAttribute 59 | { 60 | public FromQueryAttribute(string prefix) 61 | : base(prefix) 62 | { 63 | } 64 | public override T Get(IHttpContext context, IModelBinder binder) 65 | { 66 | return binder.Get(context.Request.QueryString, Prefix); 67 | } 68 | } 69 | 70 | public class FromHeadersAttribute : PrefixAttribute 71 | { 72 | public FromHeadersAttribute(string prefix) 73 | : base(prefix) 74 | { 75 | } 76 | 77 | public override T Get(IHttpContext context, IModelBinder binder) 78 | { 79 | return binder.Get(context.Request.Headers, Prefix); 80 | } 81 | } 82 | 83 | public abstract class PrefixAttribute : Attribute, IModelBinding 84 | { 85 | private readonly string _prefix; 86 | 87 | public PrefixAttribute(string prefix) 88 | { 89 | _prefix = prefix; 90 | } 91 | 92 | public bool HasPrefix 93 | { 94 | get { return !string.IsNullOrEmpty(_prefix); } 95 | } 96 | 97 | public string Prefix 98 | { 99 | get { return _prefix; } 100 | } 101 | 102 | public abstract T Get(IHttpContext context, IModelBinder binder); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /uhttpsharp-demo/Controllers/DemoController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using uhttpsharp; 5 | using uhttpsharp.Attributes; 6 | using uhttpsharp.Controllers; 7 | using uhttpsharp.Handlers; 8 | 9 | namespace uhttpsharpdemo.Controllers 10 | { 11 | public class EmptyPipeline : IPipeline 12 | { 13 | public Task Go(Func> injectedTask, IHttpContext context) 14 | { 15 | return injectedTask(); 16 | } 17 | } 18 | 19 | public class JsonController : IController 20 | { 21 | public class Question 22 | { 23 | public string TheQuestion { get; set; } 24 | } 25 | public JsonController(int id) 26 | { 27 | } 28 | 29 | [HttpMethod(HttpMethods.Post)] 30 | public Task Post([FromBody] Question question) 31 | { 32 | return Response.Render(HttpResponseCode.Ok, question); 33 | } 34 | public IPipeline Pipeline 35 | { 36 | get { return new EmptyPipeline(); } 37 | } 38 | } 39 | public class MyController 40 | { 41 | private readonly int _id; 42 | public MyController(int id) 43 | { 44 | _id = id; 45 | } 46 | public MyController() 47 | { 48 | } 49 | 50 | [HttpMethod(HttpMethods.Post)] 51 | public Task Post([FromPost("a")] MyRequest request, [FromHeaders("header")]string hello, [FromQuery("query")]string world) 52 | { 53 | return Response.Render(HttpResponseCode.Ok, null); 54 | } 55 | 56 | [Indexer] 57 | public async Task Get(IHttpContext context, int id) 58 | { 59 | return new MyController(id); 60 | } 61 | } 62 | public class MyRequest : IValidate 63 | { 64 | public int A { get; set; } 65 | public void Validate(IErrorContainer container) 66 | { 67 | if (A == 0) 68 | { 69 | container.Log("A cannot be zero"); 70 | } 71 | } 72 | } 73 | class BaseController : IController 74 | { 75 | [HttpMethod(HttpMethods.Get)] 76 | public Task Get() 77 | { 78 | return Response.Render(HttpResponseCode.Ok, new {Hello="Base!", Kaki = Enumerable.Range(0, 10000)}); 79 | } 80 | 81 | [HttpMethod(HttpMethods.Post)] 82 | public Task Post([FromBody] MyRequest a) 83 | { 84 | return Response.Render(HttpResponseCode.Ok, a); 85 | } 86 | 87 | public virtual IPipeline Pipeline 88 | { 89 | get { return new EmptyPipeline(); } 90 | } 91 | 92 | public IController Derived { 93 | get { return new DerivedController(); } 94 | } 95 | } 96 | 97 | class DerivedController : BaseController 98 | { 99 | [HttpMethod(HttpMethods.Get)] 100 | public new Task Get() 101 | { 102 | return Response.Render(HttpResponseCode.Ok, new { Hello = "Derived!" }); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /uhttpsharp/Headers/CompositeHttpHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace uhttpsharp.Headers 6 | { 7 | /// 8 | /// A trivial implementation of 9 | /// that is composed from multiple . 10 | /// 11 | /// If value is found in more then one header, 12 | /// Gets the first available value from by the order of the headers 13 | /// given in the c'tor. 14 | /// 15 | public class CompositeHttpHeaders : IHttpHeaders 16 | { 17 | private static readonly IEqualityComparer> HeaderComparer = 18 | new KeyValueComparer(k => k.Key, StringComparer.InvariantCultureIgnoreCase); 19 | 20 | private readonly IEnumerable _children; 21 | 22 | public CompositeHttpHeaders(IEnumerable children) 23 | { 24 | _children = children; 25 | } 26 | 27 | public CompositeHttpHeaders(params IHttpHeaders[] children) 28 | { 29 | _children = children; 30 | } 31 | 32 | public string GetByName(string name) 33 | { 34 | foreach (var child in _children) 35 | { 36 | string value; 37 | if (child.TryGetByName(name, out value)) 38 | { 39 | return value; 40 | } 41 | } 42 | 43 | throw new KeyNotFoundException(string.Format("Header {0} was not found in any of the children headers.", name)); 44 | } 45 | 46 | public bool TryGetByName(string name, out string value) 47 | { 48 | foreach (var child in _children) 49 | { 50 | if (child.TryGetByName(name, out value)) 51 | { 52 | return true; 53 | } 54 | } 55 | 56 | value = null; 57 | return false; 58 | } 59 | 60 | public IEnumerator> GetEnumerator() 61 | { 62 | return _children.SelectMany(c => c).Distinct(HeaderComparer).GetEnumerator(); 63 | } 64 | 65 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 66 | { 67 | return GetEnumerator(); 68 | } 69 | } 70 | 71 | public class KeyValueComparer : IEqualityComparer> 72 | { 73 | private readonly Func, TOutput> _outputFunc; 74 | private readonly IEqualityComparer _outputComparer; 75 | public KeyValueComparer(Func, TOutput> outputFunc, IEqualityComparer outputComparer) 76 | { 77 | _outputFunc = outputFunc; 78 | _outputComparer = outputComparer; 79 | } 80 | 81 | public bool Equals(KeyValuePair x, KeyValuePair y) 82 | { 83 | return _outputComparer.Equals(_outputFunc(x), _outputFunc(y)); 84 | } 85 | 86 | public int GetHashCode(KeyValuePair obj) 87 | { 88 | return _outputComparer.GetHashCode(_outputFunc(obj)); 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/RestHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace uhttpsharp.Handlers 6 | { 7 | public class RestHandler : IHttpRequestHandler 8 | { 9 | 10 | private struct RestCall 11 | { 12 | private readonly HttpMethods _method; 13 | private readonly bool _entryFull; 14 | 15 | public RestCall(HttpMethods method, bool entryFull) 16 | { 17 | _method = method; 18 | _entryFull = entryFull; 19 | } 20 | 21 | public static RestCall Create(HttpMethods method, bool entryFull) 22 | { 23 | return new RestCall(method, entryFull); 24 | } 25 | 26 | private bool Equals(RestCall other) 27 | { 28 | return _method == other._method && _entryFull.Equals(other._entryFull); 29 | } 30 | public override bool Equals(object obj) 31 | { 32 | if (ReferenceEquals(null, obj)) return false; 33 | return obj is RestCall && Equals((RestCall)obj); 34 | } 35 | public override int GetHashCode() 36 | { 37 | unchecked 38 | { 39 | return ((int)_method*397) ^ _entryFull.GetHashCode(); 40 | } 41 | } 42 | } 43 | 44 | private static readonly IDictionary, IHttpRequest, Task>> RestCallHandlers = new Dictionary, IHttpRequest, Task>>(); 45 | 46 | static RestHandler() 47 | { 48 | RestCallHandlers.Add(RestCall.Create(HttpMethods.Get, false), async (c, r) => (object) (await c.Get(r))); 49 | RestCallHandlers.Add(RestCall.Create(HttpMethods.Get, true), async (c, r) => (object) (await c.GetItem(r))); 50 | RestCallHandlers.Add(RestCall.Create(HttpMethods.Post, false), async (c, r) => (object) (await c.Create(r))); 51 | RestCallHandlers.Add(RestCall.Create(HttpMethods.Put, true), async (c, r) => (object) (await c.Upsert(r))); 52 | RestCallHandlers.Add(RestCall.Create(HttpMethods.Delete, true), async (c, r) => (object) (await c.Delete(r))); 53 | } 54 | 55 | private readonly IRestController _controller; 56 | private readonly IResponseProvider _responseProvider; 57 | public RestHandler(IRestController controller, IResponseProvider responseProvider) 58 | { 59 | _controller = controller; 60 | _responseProvider = responseProvider; 61 | } 62 | 63 | public async Task Handle(IHttpContext httpContext, Func next) 64 | { 65 | IHttpRequest httpRequest = httpContext.Request; 66 | 67 | var call = new RestCall(httpRequest.Method, httpRequest.RequestParameters.Length > 1); 68 | 69 | Func, IHttpRequest, Task> handler; 70 | if (RestCallHandlers.TryGetValue(call, out handler)) 71 | { 72 | var value = await handler(_controller, httpRequest).ConfigureAwait(false); 73 | httpContext.Response = await _responseProvider.Provide(value); 74 | 75 | return; 76 | } 77 | 78 | await next().ConfigureAwait(false); 79 | } 80 | } 81 | 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /uhttpsharp.Tests/uhttpsharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {41C9BDAC-21BE-4C50-933D-9E047F767E63} 8 | Library 9 | Properties 10 | uhttpsharp.Tests 11 | uhttpsharp.Tests 12 | v4.5 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Moq.4.2.1312.1622\lib\net40\Moq.dll 37 | 38 | 39 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {3D681959-4DA3-4A71-A68B-704D6411D5EA} 60 | uhttpsharp 61 | 62 | 63 | 64 | 65 | 72 | -------------------------------------------------------------------------------- /uhttpsharp/LimitedStream.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace uhttpsharp 4 | { 5 | class LimitedStream : Stream 6 | { 7 | private const string _exceptionMessageFormat = "The Stream has exceeded the {0} limit specified."; 8 | private readonly Stream _child; 9 | private long _readLimit; 10 | private long _writeLimit; 11 | 12 | public LimitedStream(Stream child, long readLimit = -1, long writeLimit = -1) 13 | { 14 | _child = child; 15 | _readLimit = readLimit; 16 | _writeLimit = writeLimit; 17 | } 18 | public override void Flush() 19 | { 20 | _child.Flush(); 21 | } 22 | 23 | public override long Seek(long offset, SeekOrigin origin) 24 | { 25 | return _child.Seek(offset, origin); 26 | } 27 | public override void SetLength(long value) 28 | { 29 | _child.SetLength(value); 30 | } 31 | public override int Read(byte[] buffer, int offset, int count) 32 | { 33 | var retVal = _child.Read(buffer, offset, count); 34 | 35 | AssertReadLimit(retVal); 36 | 37 | return retVal; 38 | } 39 | private void AssertReadLimit(int coefficient) 40 | { 41 | if (_readLimit == -1) 42 | { 43 | return; 44 | } 45 | 46 | _readLimit -= coefficient; 47 | 48 | if (_readLimit < 0) 49 | { 50 | throw new IOException(string.Format(_exceptionMessageFormat, "read")); 51 | } 52 | } 53 | 54 | private void AssertWriteLimit(int coefficient) 55 | { 56 | if (_writeLimit == -1) 57 | { 58 | return; 59 | } 60 | 61 | _writeLimit -= coefficient; 62 | 63 | if (_writeLimit < 0) 64 | { 65 | throw new IOException(string.Format(_exceptionMessageFormat, "write")); 66 | } 67 | } 68 | 69 | public override int ReadByte() 70 | { 71 | var retVal = _child.ReadByte(); 72 | 73 | AssertReadLimit(1); 74 | 75 | return retVal; 76 | } 77 | public override void Write(byte[] buffer, int offset, int count) 78 | { 79 | _child.Write(buffer, offset, count); 80 | 81 | AssertWriteLimit(count); 82 | } 83 | public override void WriteByte(byte value) 84 | { 85 | _child.WriteByte(value); 86 | 87 | AssertWriteLimit(1); 88 | } 89 | public override bool CanRead 90 | { 91 | get { return _child.CanRead; } 92 | } 93 | public override bool CanSeek 94 | { 95 | get { return _child.CanSeek; } 96 | } 97 | 98 | public override bool CanWrite 99 | { 100 | get { return _child.CanWrite; } 101 | } 102 | public override long Length 103 | { 104 | get { return _child.Length; } 105 | } 106 | public override long Position 107 | { 108 | get { return _child.Position; } 109 | set { _child.Position = value; } 110 | } 111 | public override int ReadTimeout 112 | { 113 | get { return _child.ReadTimeout; } 114 | set { _child.ReadTimeout = value; } 115 | } 116 | public override int WriteTimeout 117 | { 118 | get { return _child.WriteTimeout; } 119 | set { _child.WriteTimeout = value; } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /uhttpsharp/Controllers/IControllerResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using uhttpsharp.Controllers; 5 | 6 | namespace uhttpsharp.Handlers 7 | { 8 | public interface IControllerResponse 9 | { 10 | Task Respond(IHttpContext context, IView view); 11 | } 12 | 13 | public class CustomResponse : IControllerResponse 14 | { 15 | private readonly IHttpResponse _httpResponse; 16 | public CustomResponse(IHttpResponse httpResponse) 17 | { 18 | _httpResponse = httpResponse; 19 | } 20 | public Task Respond(IHttpContext context, IView view) 21 | { 22 | return Task.FromResult(_httpResponse); 23 | } 24 | } 25 | 26 | public class RenderResponse : IControllerResponse 27 | { 28 | private readonly HttpResponseCode _code; 29 | private readonly object _state; 30 | public RenderResponse(HttpResponseCode code, object state) 31 | { 32 | _code = code; 33 | _state = state; 34 | } 35 | public object State 36 | { 37 | get { return _state; } 38 | } 39 | public HttpResponseCode Code 40 | { 41 | get { return _code; } 42 | } 43 | public async Task Respond(IHttpContext context, IView view) 44 | { 45 | var output = await view.Render(context, _state).ConfigureAwait(false); 46 | return StringHttpResponse.Create(output.Body, _code, output.ContentType); 47 | } 48 | } 49 | 50 | public class RedirectResponse : IControllerResponse 51 | { 52 | private readonly Uri _newLocation; 53 | public RedirectResponse(Uri newLocation) 54 | { 55 | _newLocation = newLocation; 56 | } 57 | public Task Respond(IHttpContext context, IView view) 58 | { 59 | var headers = 60 | new[] 61 | { 62 | new KeyValuePair("Location", _newLocation.ToString()) 63 | }; 64 | return Task.FromResult( 65 | new HttpResponse(HttpResponseCode.Found, String.Empty, headers,false)); 66 | } 67 | } 68 | 69 | public static class Response 70 | { 71 | public static Task Create(IControllerResponse response) 72 | { 73 | return Task.FromResult(response); 74 | } 75 | 76 | public static Task Custom(IHttpResponse httpResponse) 77 | { 78 | return Create(new CustomResponse(httpResponse)); 79 | } 80 | public static Task Render(HttpResponseCode code, object state) 81 | { 82 | return Create(new RenderResponse(code, state)); 83 | } 84 | public static Task Render(HttpResponseCode code) 85 | { 86 | return Create(new RenderResponse(code, null)); 87 | } 88 | public static Task Redirect(Uri newLocation) 89 | { 90 | return Create(new RedirectResponse(newLocation)); 91 | } 92 | } 93 | 94 | public static class Pipeline 95 | { 96 | private class EmptyPipeline : IPipeline 97 | { 98 | public Task Go(Func> injectedTask, IHttpContext context) 99 | { 100 | return injectedTask(); 101 | } 102 | } 103 | 104 | public static IPipeline Empty = new EmptyPipeline(); 105 | } 106 | } -------------------------------------------------------------------------------- /uhttpsharp/Handlers/GZipHandler.cs~RF51dd35.TMP: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.IO.Compression; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using uhttpsharp.Headers; 9 | 10 | namespace uhttpsharp.Handlers 11 | { 12 | public class CompressionHandler : IHttpRequestHandler 13 | { 14 | private readonly IEnumerable _compressors; 15 | private static readonly char[] Seperator = new [] {','}; 16 | public CompressionHandler(IEnumerable compressors) 17 | { 18 | _compressors = compressors.ToList(); 19 | } 20 | 21 | public async Task Handle(IHttpContext context, Func next) 22 | { 23 | await next(); 24 | 25 | if (context.Response == null) 26 | { 27 | return; 28 | } 29 | 30 | var encodings = context.Request.Headers.GetByName("Accept-Encoding") 31 | .Split(Seperator, StringSplitOptions.RemoveEmptyEntries); 32 | 33 | 34 | } 35 | } 36 | 37 | public interface ICompressor 38 | { 39 | 40 | string Name { get; } 41 | 42 | IHttpResponse Compress(IHttpResponse response); 43 | 44 | } 45 | 46 | public class DeflateHandler : IHttpRequestHandler 47 | { 48 | public async Task Handle(IHttpContext context, Func next) 49 | { 50 | await next(); 51 | 52 | if (context.Response != null) 53 | { 54 | context.Response = await DeflateResponse.Create(context.Response).ConfigureAwait(false); 55 | } 56 | } 57 | } 58 | 59 | public class DeflateResponse : IHttpResponse 60 | { 61 | private readonly HttpResponseCode _responseCode; 62 | private readonly IHttpHeaders _headers; 63 | private readonly MemoryStream _memoryStream; 64 | private readonly bool _closeConnection; 65 | 66 | public DeflateResponse(IHttpResponse child, MemoryStream memoryStream) 67 | { 68 | _memoryStream = memoryStream; 69 | 70 | _responseCode = child.ResponseCode; 71 | _closeConnection = child.CloseConnection; 72 | _headers = 73 | new ListHttpHeaders( 74 | child.Headers.Where(h => !h.Key.Equals("content-length", StringComparison.InvariantCultureIgnoreCase)) 75 | .Concat(new[] 76 | { 77 | new KeyValuePair("content-length", memoryStream.Length.ToString(CultureInfo.InvariantCulture)), 78 | new KeyValuePair("content-encoding", "deflate"), 79 | }) 80 | .ToList()); 81 | 82 | 83 | } 84 | 85 | public static async Task Create(IHttpResponse child) 86 | { 87 | var memoryStream = new MemoryStream(); 88 | using (var deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress, true)) 89 | using (var deflateWriter = new StreamWriter(deflateStream)) 90 | { 91 | await child.WriteBody(deflateWriter).ConfigureAwait(false); 92 | await deflateWriter.FlushAsync(); 93 | } 94 | 95 | return new DeflateResponse(child, memoryStream); 96 | } 97 | 98 | public async Task WriteBody(StreamWriter writer) 99 | { 100 | _memoryStream.Position = 0; 101 | 102 | await _memoryStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false); 103 | } 104 | public HttpResponseCode ResponseCode 105 | { 106 | get { return _responseCode; } 107 | } 108 | public IHttpHeaders Headers 109 | { 110 | get { return _headers; } 111 | } 112 | public bool CloseConnection 113 | { 114 | get { return _closeConnection; } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /uhttpsharp/ModelBinders/ModelBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using uhttpsharp.Headers; 5 | 6 | namespace uhttpsharp.ModelBinders 7 | { 8 | public class ModelBinder : IModelBinder 9 | { 10 | private readonly IObjectActivator _activator; 11 | public ModelBinder(IObjectActivator activator) 12 | { 13 | _activator = activator; 14 | } 15 | 16 | 17 | public T Get(byte[] raw, string prefix) 18 | { 19 | throw new NotSupportedException(); 20 | } 21 | public T Get(IHttpHeaders headers) 22 | { 23 | var retVal = _activator.Activate(null); 24 | 25 | foreach (var prop in retVal.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) 26 | { 27 | if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string)) 28 | { 29 | string stringValue; 30 | if (headers.TryGetByName(prop.Name, out stringValue)) 31 | { 32 | var value = Convert.ChangeType(stringValue, prop.PropertyType); 33 | prop.SetValue(retVal, value); 34 | } 35 | } 36 | else 37 | { 38 | var value = Get(prop.PropertyType, headers, prop.Name); 39 | prop.SetValue(retVal, value); 40 | } 41 | 42 | } 43 | 44 | return retVal; 45 | } 46 | 47 | private object Get(Type type, IHttpHeaders headers, string prefix) 48 | { 49 | if (type.IsPrimitive || type == typeof(string)) 50 | { 51 | string value; 52 | if (headers.TryGetByName(prefix, out value)) 53 | { 54 | return Convert.ChangeType(value, type); 55 | } 56 | 57 | return null; 58 | } 59 | 60 | var retVal = _activator.Activate(type, null); 61 | 62 | string val; 63 | var settedValues = 64 | retVal.GetType() 65 | .GetProperties(BindingFlags.Instance | BindingFlags.Public) 66 | .Where(p => headers.TryGetByName(prefix + "[" + p.Name + "]", out val)).ToList(); 67 | 68 | if (settedValues.Count == 0) 69 | { 70 | return null; 71 | } 72 | 73 | 74 | foreach (var prop in settedValues) 75 | { 76 | string stringValue; 77 | if (headers.TryGetByName(prefix + "[" + prop.Name + "]", out stringValue)) 78 | { 79 | object value = prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string) 80 | ? Convert.ChangeType(stringValue, prop.PropertyType) 81 | : Get(prop.PropertyType, headers, prefix + "[" + prop.Name + "]"); 82 | 83 | prop.SetValue(retVal, value); 84 | } 85 | } 86 | 87 | return retVal; 88 | } 89 | 90 | public T Get(IHttpHeaders headers, string prefix) 91 | { 92 | return (T)Get(typeof(T), headers, prefix); 93 | } 94 | } 95 | 96 | public class ObjectActivator : IObjectActivator 97 | { 98 | 99 | public object Activate(Type type, Func argumentGetter) 100 | { 101 | return Activator.CreateInstance(type); 102 | } 103 | } 104 | 105 | public interface IObjectActivator 106 | { 107 | 108 | object Activate(Type type, Func argumentGetter); 109 | 110 | } 111 | 112 | public static class ObjectActivatorExtensions 113 | { 114 | 115 | public static T Activate(this IObjectActivator activator, Func argumentGetter) 116 | { 117 | return (T)activator.Activate(typeof(T), argumentGetter); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /uhttpsharp/RequestProviders/HttpRequestProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using uhttpsharp.Headers; 7 | 8 | namespace uhttpsharp.RequestProviders 9 | { 10 | public class HttpRequestProvider : IHttpRequestProvider 11 | { 12 | private static readonly char[] Separators = { '/' }; 13 | 14 | public async Task Provide(StreamReader streamReader) 15 | { 16 | // parse the http request 17 | var request = await streamReader.ReadLineAsync().ConfigureAwait(false); 18 | 19 | if (request == null) 20 | return null; 21 | 22 | var firstSpace = request.IndexOf(' '); 23 | var lastSpace = request.LastIndexOf(' '); 24 | 25 | var tokens = new [] 26 | { 27 | request.Substring(0, firstSpace), 28 | request.Substring(firstSpace + 1, lastSpace - firstSpace - 1), 29 | request.Substring(lastSpace + 1) 30 | }; 31 | 32 | if (tokens.Length != 3) 33 | { 34 | return null; 35 | } 36 | 37 | 38 | var httpProtocol = tokens[2]; 39 | 40 | var url = tokens[1]; 41 | var queryString = GetQueryStringData(ref url); 42 | var uri = new Uri(url, UriKind.Relative); 43 | 44 | var headersRaw = new List>(); 45 | 46 | // get the headers 47 | string line; 48 | 49 | while (!string.IsNullOrEmpty((line = await streamReader.ReadLineAsync().ConfigureAwait(false)))) 50 | { 51 | string currentLine = line; 52 | 53 | var headerKvp = SplitHeader(currentLine); 54 | headersRaw.Add(headerKvp); 55 | } 56 | 57 | IHttpHeaders headers = new HttpHeaders(headersRaw.ToDictionary(k => k.Key, k => k.Value, StringComparer.InvariantCultureIgnoreCase)); 58 | IHttpPost post = await GetPostData(streamReader, headers).ConfigureAwait(false); 59 | 60 | string verb; 61 | if (!headers.TryGetByName("_method", out verb)) 62 | { 63 | verb = tokens[0]; 64 | } 65 | var httpMethod = HttpMethodProvider.Default.Provide(verb); 66 | return new HttpRequest(headers, httpMethod, httpProtocol, uri, 67 | uri.OriginalString.Split(Separators, StringSplitOptions.RemoveEmptyEntries), queryString, post); 68 | } 69 | private static IHttpHeaders GetQueryStringData(ref string url) 70 | { 71 | var queryStringIndex = url.IndexOf('?'); 72 | IHttpHeaders queryString; 73 | if (queryStringIndex != -1) 74 | { 75 | queryString = new QueryStringHttpHeaders(url.Substring(queryStringIndex + 1)); 76 | url = url.Substring(0, queryStringIndex); 77 | } 78 | else 79 | { 80 | queryString = EmptyHttpHeaders.Empty; 81 | } 82 | return queryString; 83 | } 84 | 85 | private static async Task GetPostData(StreamReader streamReader, IHttpHeaders headers) 86 | { 87 | int postContentLength; 88 | IHttpPost post; 89 | if (headers.TryGetByName("content-length", out postContentLength) && postContentLength > 0) 90 | { 91 | post = await HttpPost.Create(streamReader, postContentLength).ConfigureAwait(false); 92 | } 93 | else 94 | { 95 | post = EmptyHttpPost.Empty; 96 | } 97 | return post; 98 | } 99 | 100 | private KeyValuePair SplitHeader(string header) 101 | { 102 | var index = header.IndexOf(": ", StringComparison.InvariantCultureIgnoreCase); 103 | return new KeyValuePair(header.Substring(0, index), header.Substring(index + 2)); 104 | } 105 | 106 | } 107 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Diagnostics; 22 | using uhttpsharp.Headers; 23 | 24 | namespace uhttpsharp 25 | { 26 | [DebuggerDisplay("{Method} {OriginalUri,nq}")] 27 | internal class HttpRequest : IHttpRequest 28 | { 29 | private readonly IHttpHeaders _headers; 30 | private readonly HttpMethods _method; 31 | private readonly string _protocol; 32 | private readonly Uri _uri; 33 | private readonly string[] _requestParameters; 34 | private readonly IHttpHeaders _queryString; 35 | private readonly IHttpPost _post; 36 | 37 | public HttpRequest(IHttpHeaders headers, HttpMethods method, string protocol, Uri uri, string[] requestParameters, IHttpHeaders queryString, IHttpPost post) 38 | { 39 | _headers = headers; 40 | _method = method; 41 | _protocol = protocol; 42 | _uri = uri; 43 | _requestParameters = requestParameters; 44 | _queryString = queryString; 45 | _post = post; 46 | } 47 | 48 | public IHttpHeaders Headers 49 | { 50 | get { return _headers; } 51 | } 52 | 53 | public HttpMethods Method 54 | { 55 | get { return _method; } 56 | } 57 | 58 | public string Protocol 59 | { 60 | get { return _protocol; } 61 | } 62 | 63 | public Uri Uri 64 | { 65 | get { return _uri; } 66 | } 67 | 68 | public string[] RequestParameters 69 | { 70 | get { return _requestParameters; } 71 | } 72 | 73 | public IHttpPost Post 74 | { 75 | get { return _post; } 76 | } 77 | 78 | public IHttpHeaders QueryString 79 | { 80 | get { return _queryString; } 81 | } 82 | 83 | internal string OriginalUri 84 | { 85 | get 86 | { 87 | if (QueryString == null) 88 | { 89 | return Uri.OriginalString; 90 | } 91 | 92 | return Uri.OriginalString + "?" + QueryString.ToUriData(); 93 | 94 | } 95 | } 96 | } 97 | 98 | public interface IHttpRequest 99 | { 100 | IHttpHeaders Headers { get; } 101 | 102 | HttpMethods Method { get; } 103 | 104 | string Protocol { get; } 105 | 106 | Uri Uri { get; } 107 | 108 | string[] RequestParameters { get; } 109 | 110 | IHttpPost Post {get;} 111 | 112 | IHttpHeaders QueryString { get; } 113 | 114 | } 115 | 116 | public interface IHttpPost 117 | { 118 | 119 | byte[] Raw {get;} 120 | 121 | IHttpHeaders Parsed {get;} 122 | 123 | } 124 | 125 | public sealed class HttpRequestParameters 126 | { 127 | private readonly string[] _params; 128 | 129 | private static readonly char[] Separators = { '/' }; 130 | 131 | public HttpRequestParameters(Uri uri) 132 | { 133 | var url = uri.OriginalString; 134 | _params = url.Split(Separators, StringSplitOptions.RemoveEmptyEntries); 135 | } 136 | 137 | public IList Params 138 | { 139 | get { return _params; } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /uhttpsharp-demo/uhttpsharp.Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07} 9 | Exe 10 | Properties 11 | uhttpsharpdemo 12 | uhttpsharp-demo 13 | v4.5 14 | 15 | 16 | 512 17 | ..\ 18 | true 19 | 20 | 21 | x86 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | false 30 | 31 | 32 | x86 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | false 40 | 41 | 42 | 43 | ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll 44 | 45 | 46 | False 47 | ..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Properties\AssemblyCommon.cs 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {3D681959-4DA3-4A71-A68B-704D6411D5EA} 77 | uhttpsharp 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 93 | -------------------------------------------------------------------------------- /uhttpsharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30110.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uhttpsharp.Demo", "uhttpsharp-demo\uhttpsharp.Demo.csproj", "{55C5E8D2-F55E-4B9F-B96F-FCE201739D07}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uhttpsharp", "uhttpsharp\uhttpsharp.csproj", "{3D681959-4DA3-4A71-A68B-704D6411D5EA}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EA15AFF9-2029-4D23-8F0C-FD0476394B0B}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | uhttpsharp.dll.nuspec = uhttpsharp.dll.nuspec 14 | EndProjectSection 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "uhttpsharp.Tests", "uhttpsharp.Tests\uhttpsharp.Tests.csproj", "{41C9BDAC-21BE-4C50-933D-9E047F767E63}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{EEE3A8EB-CBA2-4590-B856-F6EF95FD6113}" 19 | ProjectSection(SolutionItems) = preProject 20 | .nuget\NuGet.Config = .nuget\NuGet.Config 21 | .nuget\NuGet.exe = .nuget\NuGet.exe 22 | .nuget\NuGet.targets = .nuget\NuGet.targets 23 | EndProjectSection 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Debug|Mixed Platforms = Debug|Mixed Platforms 29 | Debug|x86 = Debug|x86 30 | Release|Any CPU = Release|Any CPU 31 | Release|Mixed Platforms = Release|Mixed Platforms 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|Any CPU.ActiveCfg = Debug|x86 36 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|Any CPU.Build.0 = Debug|x86 37 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 38 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|Mixed Platforms.Build.0 = Debug|x86 39 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|x86.ActiveCfg = Debug|x86 40 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Debug|x86.Build.0 = Debug|x86 41 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Release|Any CPU.ActiveCfg = Release|x86 42 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Release|Mixed Platforms.ActiveCfg = Release|x86 43 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Release|Mixed Platforms.Build.0 = Release|x86 44 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Release|x86.ActiveCfg = Release|x86 45 | {55C5E8D2-F55E-4B9F-B96F-FCE201739D07}.Release|x86.Build.0 = Release|x86 46 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 49 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 50 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Debug|x86.ActiveCfg = Debug|Any CPU 51 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 54 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Release|Mixed Platforms.Build.0 = Release|Any CPU 55 | {3D681959-4DA3-4A71-A68B-704D6411D5EA}.Release|x86.ActiveCfg = Release|Any CPU 56 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 59 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 60 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Debug|x86.ActiveCfg = Debug|Any CPU 61 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 64 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Release|Mixed Platforms.Build.0 = Release|Any CPU 65 | {41C9BDAC-21BE-4C50-933D-9E047F767E63}.Release|x86.ActiveCfg = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(SolutionProperties) = preSolution 68 | HideSolutionNode = FALSE 69 | EndGlobalSection 70 | GlobalSection(MonoDevelopProperties) = preSolution 71 | StartupItem = uhttpsharp-demo\uhttpsharp.Demo.csproj 72 | EndGlobalSection 73 | EndGlobal 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # µHttpSharp 2 | 3 | A very lightweight & simple embedded http server for c# 4 | 5 | Master | Provider 6 | ------ | -------- 7 | [![Build Status][TeamCityImgMaster]][TeamCityLinkMaster] | Windows CI Provided By [JetBrains][] and [CodeBetter][] 8 | [![Build status](https://ci.appveyor.com/api/projects/status/1schhjbpx7oomrx7)](https://ci.appveyor.com/project/shanielh/uHttpSharp) | Windows CI Provided By [AppVeyor][] 9 | [![Build Status][MonoImgMaster]][MonoLinkMaster] | Mono CI Provided by [travis-ci][] 10 | 11 | [TeamCityImgMaster]:http://teamcity.codebetter.com/app/rest/builds/buildType:\(id:bt1191\)/statusIcon 12 | [TeamCityLinkMaster]:http://teamcity.codebetter.com/viewLog.html?buildTypeId=bt1191&buildId=lastFinished&guest=1 13 | 14 | [MonoImgMaster]:https://travis-ci.org/Code-Sharp/uHttpSharp.png?branch=master 15 | [MonoLinkMaster]:https://travis-ci.org/Code-Sharp/uHttpSharp 16 | 17 | [travis-ci]:https://travis-ci.org/ 18 | [AppVeyor]:http://www.appveyor.com/ 19 | [JetBrains]:http://www.jetbrains.com/ 20 | [CodeBetter]:http://codebetter.com/ 21 | 22 | ## Usage 23 | 24 | A [NuGet Package](https://www.nuget.org/packages/uHttpSharp/ "Go to µHttpSharp NuGet Package page") is available, Install via NuGet Package Manager : 25 | 26 | install-package uHttpSharp 27 | 28 | A sample for usage : 29 | 30 | using (var httpServer = new HttpServer(new HttpRequestProvider())) 31 | { 32 | // Normal port 80 : 33 | httpServer.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 80))); 34 | 35 | // Ssl Support : 36 | var serverCertificate = X509Certificate.CreateFromCertFile(@"TempCert.cer"); 37 | httpServer.Use(new ListenerSslDecorator(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 443)), serverCertificate)); 38 | 39 | // Request handling : 40 | httpServer.Use((context, next) => { 41 | Console.WriteLine("Got Request!"); 42 | return next(); 43 | }); 44 | 45 | // Handler classes : 46 | httpServer.Use(new TimingHandler()); 47 | httpServer.Use(new HttpRouter().With(string.Empty, new IndexHandler()) 48 | .With("about", new AboutHandler())); 49 | httpServer.Use(new FileHandler()); 50 | httpServer.Use(new ErrorHandler()); 51 | 52 | httpServer.Start(); 53 | 54 | Console.ReadLine(); 55 | } 56 | 57 | ## Features 58 | 59 | µHttpSharp is a simple http server inspired by [koa](http://koajs.com), and has the following features : 60 | 61 | * [RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) controllers 62 | * Ssl Support 63 | * Easy Chain-Of-Responsibility architecture 64 | 65 | 66 | ## Performance 67 | 68 | µHttpSharp manages to handle **13000 requests a sec** (With Keep-Alive support) on core i5 machine, cpu goes to 27%, memory consumption and number of threads is stable. 69 | 70 | ab -n 10000 -c 50 -k -s 2 http://localhost:8000/ 71 | 72 | This is ApacheBench, Version 2.3 <$Revision: 1528965 $> 73 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 74 | Licensed to The Apache Software Foundation, http://www.apache.org/ 75 | 76 | Benchmarking localhost (be patient) 77 | Completed 1000 requests 78 | Completed 2000 requests 79 | Completed 3000 requests 80 | Completed 4000 requests 81 | Completed 5000 requests 82 | Completed 6000 requests 83 | Completed 7000 requests 84 | Completed 8000 requests 85 | Completed 9000 requests 86 | 87 | 88 | Server Software: 89 | Server Hostname: localhost 90 | Server Port: 8000 91 | 92 | Document Path: / 93 | Document Length: 21 bytes 94 | 95 | Concurrency Level: 50 96 | Time taken for tests: 0.707 seconds 97 | Complete requests: 9357 98 | Failed requests: 0 99 | Keep-Alive requests: 9363 100 | Total transferred: 1507527 bytes 101 | HTML transferred: 196644 bytes 102 | Requests per second: 13245.36 [#/sec] (mean) 103 | Time per request: 3.775 [ms] (mean) 104 | Time per request: 0.075 [ms] (mean, across all concurrent requests) 105 | Transfer rate: 2083.53 [Kbytes/sec] received 106 | 107 | Connection Times (ms) 108 | min mean[+/-sd] median max 109 | Connect: 0 0 0.0 0 1 110 | Processing: 1 4 0.7 4 13 111 | Waiting: 1 4 0.7 4 13 112 | Total: 1 4 0.7 4 13 113 | 114 | Percentage of the requests served within a certain time (ms) 115 | 50% 4 116 | 66% 4 117 | 75% 4 118 | 80% 4 119 | 90% 4 120 | 95% 4 121 | 98% 5 122 | 99% 9 123 | 100% 4 (longest request) 124 | 125 | ## How To Contribute? 126 | 127 | * Use it 128 | * Open Issues 129 | * Fork and Push requests 130 | -------------------------------------------------------------------------------- /uhttpsharp/Headers/HttpHeaders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace uhttpsharp.Headers 11 | { 12 | internal class EmptyHttpPost : IHttpPost 13 | { 14 | private static readonly byte[] _emptyBytes = new byte[0]; 15 | 16 | public static readonly IHttpPost Empty = new EmptyHttpPost(); 17 | 18 | private EmptyHttpPost() 19 | { 20 | 21 | } 22 | 23 | #region IHttpPost implementation 24 | 25 | public byte[] Raw 26 | { 27 | get 28 | { 29 | return _emptyBytes; 30 | } 31 | } 32 | 33 | public IHttpHeaders Parsed 34 | { 35 | get 36 | { 37 | return EmptyHttpHeaders.Empty; 38 | } 39 | } 40 | 41 | #endregion 42 | } 43 | 44 | internal class HttpPost : IHttpPost 45 | { 46 | public static async Task Create(StreamReader reader, int postContentLength) 47 | { 48 | char[] rawEncoded = new char[postContentLength]; 49 | 50 | int readBytes = await reader.ReadAsync(rawEncoded, 0, rawEncoded.Length).ConfigureAwait(false); 51 | 52 | byte[] raw = Encoding.UTF8.GetBytes(rawEncoded, 0, readBytes); 53 | 54 | return new HttpPost(raw, readBytes); 55 | } 56 | 57 | private readonly int _readBytes; 58 | 59 | private readonly byte[] _raw; 60 | 61 | private readonly Lazy _parsed; 62 | 63 | public HttpPost(byte[] raw, int readBytes) 64 | { 65 | _raw = raw; 66 | _readBytes = readBytes; 67 | _parsed = new Lazy(Parse); 68 | } 69 | 70 | private IHttpHeaders Parse() 71 | { 72 | string body = Encoding.UTF8.GetString(_raw, 0, _readBytes); 73 | var parsed = new QueryStringHttpHeaders(body); 74 | 75 | return parsed; 76 | } 77 | 78 | #region IHttpPost implementation 79 | 80 | public byte[] Raw 81 | { 82 | get 83 | { 84 | return _raw; 85 | } 86 | } 87 | 88 | public IHttpHeaders Parsed 89 | { 90 | get 91 | { 92 | return _parsed.Value; 93 | } 94 | } 95 | 96 | #endregion 97 | 98 | 99 | } 100 | 101 | [DebuggerDisplay("{Count} Headers")] 102 | [DebuggerTypeProxy(typeof(HttpHeadersDebuggerProxy))] 103 | public class ListHttpHeaders : IHttpHeaders 104 | { 105 | private readonly IList> _values; 106 | public ListHttpHeaders(IList> values) 107 | { 108 | _values = values; 109 | } 110 | 111 | public string GetByName(string name) 112 | { 113 | return _values.Where(kvp => kvp.Key.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Select(kvp => kvp.Value).First(); 114 | } 115 | 116 | public bool TryGetByName(string name, out string value) 117 | { 118 | value = _values.Where(kvp => kvp.Key.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Select(kvp => kvp.Value).FirstOrDefault(); 119 | 120 | return (value != default(string)); 121 | } 122 | 123 | public IEnumerator> GetEnumerator() 124 | { 125 | return _values.GetEnumerator(); 126 | } 127 | 128 | IEnumerator IEnumerable.GetEnumerator() 129 | { 130 | return GetEnumerator(); 131 | } 132 | 133 | internal int Count 134 | { 135 | get 136 | { 137 | return _values.Count; 138 | } 139 | } 140 | } 141 | 142 | 143 | [DebuggerDisplay("{Count} Headers")] 144 | [DebuggerTypeProxy(typeof(HttpHeadersDebuggerProxy))] 145 | public class HttpHeaders : IHttpHeaders 146 | { 147 | private readonly IDictionary _values; 148 | 149 | public HttpHeaders(IDictionary values) 150 | { 151 | _values = values; 152 | } 153 | 154 | public string GetByName(string name) 155 | { 156 | return _values[name]; 157 | } 158 | public bool TryGetByName(string name, out string value) 159 | { 160 | return _values.TryGetValue(name, out value); 161 | } 162 | 163 | public IEnumerator> GetEnumerator() 164 | { 165 | return _values.GetEnumerator(); 166 | } 167 | 168 | IEnumerator IEnumerable.GetEnumerator() 169 | { 170 | return GetEnumerator(); 171 | } 172 | 173 | 174 | internal int Count 175 | { 176 | get 177 | { 178 | return _values.Count; 179 | } 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /uhttpsharp/uhttpsharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {3D681959-4DA3-4A71-A68B-704D6411D5EA} 9 | Library 10 | Properties 11 | uhttpsharp 12 | uhttpsharp 13 | v4.5 14 | 512 15 | 16 | ..\ 17 | true 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | false 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | false 37 | 38 | 39 | 40 | ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll 41 | 42 | 43 | ..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Properties\AssemblyCommon.cs 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 130 | -------------------------------------------------------------------------------- /uhttpsharp.6.0.ReSharper: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SOLUTION 6 | 7 | 8 | 9 | 10 | 11 | 12 | 0 13 | 0 14 | 0 15 | 1 16 | 1 17 | 18 | public 19 | protected 20 | internal 21 | private 22 | new 23 | abstract 24 | virtual 25 | override 26 | sealed 27 | static 28 | readonly 29 | extern 30 | unsafe 31 | volatile 32 | 33 | False 34 | False 35 | 128 36 | 37 | 38 | 39 | $object$_On$event$ 40 | $event$Handler 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | $object$_On$event$ 81 | $event$Handler 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | $object$_On$event$ 96 | $event$Handler 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /uhttpsharp-demo/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Collections.Concurrent; 21 | using System.Globalization; 22 | using System.Net; 23 | using System.Net.Sockets; 24 | using System.Threading.Tasks; 25 | using uhttpsharp; 26 | using uhttpsharp.Handlers; 27 | using uhttpsharp.Handlers.Compression; 28 | using uhttpsharp.Listeners; 29 | using uhttpsharp.ModelBinders; 30 | using uhttpsharp.RequestProviders; 31 | using uhttpsharpdemo.Controllers; 32 | using uhttpsharpdemo.Handlers; 33 | 34 | namespace uhttpsharpdemo 35 | { 36 | internal static class Program 37 | { 38 | private static void Main() 39 | { 40 | log4net.Config.XmlConfigurator.Configure(); 41 | 42 | //var serverCertificate = X509Certificate.CreateFromCertFile(@"TempCert.cer"); 43 | 44 | using (var httpServer = new HttpServer(new HttpRequestProvider())) 45 | { 46 | httpServer.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Any, 82))); 47 | //httpServer.Use(new ListenerSslDecorator(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 443)), serverCertificate)); 48 | 49 | //httpServer.Use(new SessionHandler(() => DateTime.Now)); 50 | httpServer.Use(new ExceptionHandler()); 51 | httpServer.Use(new CompressionHandler(DeflateCompressor.Default, GZipCompressor.Default)); 52 | httpServer.Use(new ControllerHandler(new BaseController(), new JsonModelBinder(), new JsonView())); 53 | httpServer.Use(new HttpRouter().With(string.Empty, new IndexHandler()) 54 | .With("about", new AboutHandler()) 55 | .With("Assets", new AboutHandler()) 56 | .With("strings", new RestHandler(new StringsRestController(), JsonResponseProvider.Default))); 57 | 58 | httpServer.Use(new ClassRouter(new MySuperHandler())); 59 | httpServer.Use(new TimingHandler()); 60 | 61 | httpServer.Use(new MyHandler()); 62 | httpServer.Use(new FileHandler()); 63 | httpServer.Use(new ErrorHandler()); 64 | httpServer.Use((context, next) => 65 | { 66 | Console.WriteLine("Got Request!"); 67 | return next(); 68 | }); 69 | 70 | httpServer.Start(); 71 | Console.ReadLine(); 72 | } 73 | 74 | } 75 | } 76 | 77 | public class MySuperHandler : IHttpRequestHandler 78 | { 79 | private int _index; 80 | 81 | public MySuperHandler Child 82 | { 83 | get 84 | { 85 | _index++; return this; 86 | } 87 | } 88 | public Task Handle(IHttpContext context, Func next) 89 | { 90 | context.Response = uhttpsharp.HttpResponse.CreateWithMessage(HttpResponseCode.Ok, "Hello!" + _index, true); 91 | return Task.Factory.GetCompleted(); 92 | } 93 | 94 | 95 | [Indexer] 96 | public Task GetChild(IHttpContext context, int index) 97 | { 98 | _index += index; 99 | return Task.FromResult(this); 100 | } 101 | 102 | } 103 | 104 | class MyModel 105 | { 106 | public int MyProperty 107 | { 108 | get; 109 | set; 110 | } 111 | 112 | public MyModel Model 113 | { 114 | get; 115 | set; 116 | } 117 | } 118 | 119 | internal class MyHandler : IHttpRequestHandler 120 | { 121 | public System.Threading.Tasks.Task Handle(IHttpContext context, Func next) 122 | { 123 | var model = new ModelBinder(new ObjectActivator()).Get(context.Request.QueryString); 124 | 125 | return Task.Factory.GetCompleted(); 126 | } 127 | } 128 | 129 | internal class SessionHandler : IHttpRequestHandler 130 | { 131 | private readonly Func _sessionFactory; 132 | 133 | private static readonly Random RandomGenerator = new Random(); 134 | 135 | private class SessionHolder 136 | { 137 | private readonly TSession _session; 138 | private DateTime _lastAccessTime = DateTime.Now; 139 | 140 | public TSession Session 141 | { 142 | get 143 | { 144 | _lastAccessTime = DateTime.Now; 145 | return _session; 146 | } 147 | } 148 | 149 | public DateTime LastAccessTime 150 | { 151 | get 152 | { 153 | return _lastAccessTime; 154 | } 155 | } 156 | 157 | public SessionHolder(TSession session) 158 | { 159 | _session = session; 160 | } 161 | } 162 | 163 | private readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(); 164 | 165 | public SessionHandler(Func sessionFactory) 166 | { 167 | _sessionFactory = sessionFactory; 168 | } 169 | 170 | public System.Threading.Tasks.Task Handle(IHttpContext context, Func next) 171 | { 172 | 173 | string sessId; 174 | if (!context.Cookies.TryGetByName("SESSID", out sessId)) 175 | { 176 | sessId = RandomGenerator.Next().ToString(CultureInfo.InvariantCulture); 177 | context.Cookies.Upsert("SESSID", sessId); 178 | } 179 | 180 | var session = _sessions.GetOrAdd(sessId, CreateSession); 181 | 182 | context.State.Session = session; 183 | 184 | return next(); 185 | } 186 | private TSession CreateSession(string sessionId) 187 | { 188 | return _sessionFactory(); 189 | } 190 | } 191 | } -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | "$(SolutionDir) " 54 | "$(SolutionDir)" 55 | 56 | 57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 58 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 59 | 60 | 61 | 62 | RestorePackages; 63 | $(BuildDependsOn); 64 | 65 | 66 | 67 | 68 | $(BuildDependsOn); 69 | BuildPackage; 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/ClassRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Threading.Tasks; 8 | 9 | namespace uhttpsharp.Handlers 10 | { 11 | public class ClassRouter : IHttpRequestHandler 12 | { 13 | private static readonly object SyncRoot = new object(); 14 | 15 | private static readonly HashSet LoadedRoutes = new HashSet(); 16 | 17 | private static readonly IDictionary, Func> 18 | Routers = new Dictionary, Func>(); 19 | 20 | private static readonly ConcurrentDictionary>> 21 | IndexerRouters = new ConcurrentDictionary>>(); 22 | 23 | private readonly IHttpRequestHandler _root; 24 | 25 | public ClassRouter(IHttpRequestHandler root) 26 | { 27 | _root = root; 28 | LoadRoute(_root); 29 | } 30 | 31 | private void LoadRoute(IHttpRequestHandler root) 32 | { 33 | var rootType = root.GetType(); 34 | 35 | if (LoadedRoutes.Contains(rootType)) 36 | { 37 | return; 38 | } 39 | 40 | lock (SyncRoot) 41 | { 42 | if (LoadedRoutes.Add(rootType)) 43 | { 44 | var routes = GetRoutesOfHandler(rootType); 45 | foreach (var route in routes) 46 | { 47 | var tuple = Tuple.Create(rootType, route.Name); 48 | var value = CreateRoute(tuple); 49 | Routers.Add(tuple, value); 50 | } 51 | } 52 | } 53 | } 54 | private IEnumerable GetRoutesOfHandler(Type type) 55 | { 56 | return type 57 | .GetProperties() 58 | .Where(p => typeof(IHttpRequestHandler).IsAssignableFrom(p.PropertyType)); 59 | } 60 | 61 | public async Task Handle(IHttpContext context, Func next) 62 | { 63 | var handler = _root; 64 | 65 | foreach (var parameter in context.Request.RequestParameters) 66 | { 67 | Func getNextHandler; 68 | 69 | LoadRoute(handler); 70 | 71 | if (Routers.TryGetValue(Tuple.Create(handler.GetType(), parameter), out getNextHandler)) 72 | { 73 | handler = getNextHandler(handler); 74 | } 75 | else 76 | { 77 | var getNextByIndex = IndexerRouters.GetOrAdd(handler.GetType(), GetIndexerRouter); 78 | 79 | if (getNextByIndex == null) //Indexer is not found 80 | { 81 | 82 | await next().ConfigureAwait(false); 83 | return; 84 | } 85 | 86 | var returnedTask = getNextByIndex(context, handler, parameter); 87 | 88 | if (returnedTask == null) //Indexer found, but returned null (for whatever reason) 89 | { 90 | await next().ConfigureAwait(false); 91 | return; 92 | } 93 | 94 | handler = await returnedTask.ConfigureAwait(false); 95 | } 96 | 97 | // Incase that one of the methods returned null (Indexer / Getter) 98 | if (handler == null) 99 | { 100 | await next().ConfigureAwait(false); 101 | return; 102 | } 103 | } 104 | 105 | await handler.Handle(context, next).ConfigureAwait(false); 106 | } 107 | 108 | private Func> GetIndexerRouter(Type arg) 109 | { 110 | var indexer = GetIndexer(arg); 111 | 112 | if (indexer == null) 113 | { 114 | return null; 115 | } 116 | return CreateIndexerFunction(arg, indexer); 117 | } 118 | internal static Func> CreateIndexerFunction(Type arg, MethodInfo indexer) 119 | { 120 | var parameterType = indexer.GetParameters()[1].ParameterType; 121 | 122 | var httpContext = Expression.Parameter(typeof(IHttpContext), "context"); 123 | var inputHandler = Expression.Parameter(typeof(T), "instance"); 124 | var inputObject = Expression.Parameter(typeof(string), "input"); 125 | 126 | var tryParseMethod = parameterType.GetMethod("TryParse", new[] {typeof(string), parameterType.MakeByRefType()}); 127 | 128 | Expression body; 129 | 130 | if (tryParseMethod == null) 131 | { 132 | var handlerConverted = Expression.Convert(inputHandler, arg); 133 | var objectConverted = 134 | Expression.Convert( 135 | Expression.Call(typeof(Convert).GetMethod("ChangeType", new[] {typeof(object), typeof(Type)}), inputObject, 136 | Expression.Constant(parameterType)), parameterType); 137 | 138 | var indexerExpression = Expression.Call (handlerConverted, indexer, httpContext, objectConverted); 139 | var returnValue = Expression.Convert (indexerExpression, typeof(T)); 140 | 141 | body = returnValue; 142 | } 143 | else 144 | { 145 | var inputConvertedVar = Expression.Variable(parameterType, "inputObjectConverted"); 146 | 147 | var handlerConverted = Expression.Convert(inputHandler, arg); 148 | var objectConverted = inputConvertedVar; 149 | 150 | var indexerExpression = Expression.Call(handlerConverted, indexer, httpContext, objectConverted); 151 | var returnValue = Expression.Convert(indexerExpression, typeof(Task)); 152 | var returnTarget = Expression.Label(typeof(Task)); 153 | var returnLabel = Expression.Label(returnTarget, 154 | Expression.Convert(Expression.Constant(null), typeof(Task))); 155 | body = 156 | Expression.Block( 157 | new[] {inputConvertedVar}, 158 | Expression.IfThen( 159 | Expression.Call(tryParseMethod, inputObject, 160 | inputConvertedVar), 161 | Expression.Return(returnTarget, returnValue) 162 | ), 163 | returnLabel); 164 | } 165 | 166 | return 167 | Expression.Lambda>>(body, httpContext, 168 | inputHandler, 169 | inputObject).Compile(); 170 | } 171 | private MethodInfo GetIndexer(Type arg) 172 | { 173 | var indexer = 174 | arg.GetMethods().SingleOrDefault(m => Attribute.IsDefined(m, typeof(IndexerAttribute)) 175 | && m.GetParameters().Length == 2 176 | && typeof(Task).IsAssignableFrom(m.ReturnType)); 177 | 178 | return indexer; 179 | } 180 | private Func CreateRoute(Tuple arg) 181 | { 182 | var parameter = Expression.Parameter(typeof(IHttpRequestHandler), "input"); 183 | var converted = Expression.Convert(parameter, arg.Item1); 184 | 185 | var propertyInfo = arg.Item1.GetProperty(arg.Item2); 186 | 187 | if (propertyInfo == null) 188 | { 189 | return null; 190 | } 191 | 192 | var property = Expression.Property(converted, propertyInfo); 193 | var propertyConverted = Expression.Convert(property, typeof(IHttpRequestHandler)); 194 | 195 | return Expression.Lambda>(propertyConverted, new[] { parameter }).Compile(); 196 | } 197 | } 198 | 199 | public class IndexerAttribute : Attribute 200 | { 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /uhttpsharp/HttpClient.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System.Text; 20 | using log4net; 21 | using System.Net; 22 | using System.Reflection; 23 | using System; 24 | using System.Collections.Generic; 25 | using System.IO; 26 | using System.Threading.Tasks; 27 | using uhttpsharp.Clients; 28 | using uhttpsharp.Headers; 29 | using uhttpsharp.RequestProviders; 30 | 31 | namespace uhttpsharp 32 | { 33 | internal sealed class HttpClientHandler 34 | { 35 | private const string CrLf = "\r\n"; 36 | private static readonly byte[] CrLfBuffer = Encoding.UTF8.GetBytes(CrLf); 37 | 38 | private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 39 | 40 | private readonly IClient _client; 41 | private readonly Func _requestHandler; 42 | private readonly IHttpRequestProvider _requestProvider; 43 | private readonly EndPoint _remoteEndPoint; 44 | private DateTime _lastOperationTime; 45 | private readonly Stream _stream; 46 | 47 | public HttpClientHandler(IClient client, Func requestHandler, IHttpRequestProvider requestProvider) 48 | { 49 | _remoteEndPoint = client.RemoteEndPoint; 50 | _client = client; 51 | _requestHandler = requestHandler; 52 | _requestProvider = requestProvider; 53 | 54 | _stream = new BufferedStream(_client.Stream, 8192); 55 | 56 | Logger.InfoFormat("Got Client {0}", _remoteEndPoint); 57 | 58 | Task.Factory.StartNew(Process); 59 | 60 | UpdateLastOperationTime(); 61 | } 62 | 63 | private async void Process() 64 | { 65 | try 66 | { 67 | while (_client.Connected) 68 | { 69 | // TODO : Configuration. 70 | var limitedStream = new NotFlushingStream(new LimitedStream(_stream, readLimit: 1024*1024, writeLimit: 1024*1024)); 71 | var streamReader = new StreamReader(limitedStream); 72 | 73 | var request = await _requestProvider.Provide(streamReader).ConfigureAwait(false); 74 | 75 | if (request != null) 76 | { 77 | UpdateLastOperationTime(); 78 | 79 | var context = new HttpContext(request, _client.RemoteEndPoint); 80 | 81 | Logger.InfoFormat("{1} : Got request {0}", request.Uri, _client.RemoteEndPoint); 82 | 83 | await _requestHandler(context).ConfigureAwait(false); 84 | 85 | if (context.Response != null) 86 | { 87 | var streamWriter = new StreamWriter(limitedStream) { AutoFlush = false }; 88 | 89 | await WriteResponse(context, streamWriter).ConfigureAwait(false); 90 | await limitedStream.ExplicitFlushAsync().ConfigureAwait(false); 91 | 92 | if (!request.Headers.KeepAliveConnection() || context.Response.CloseConnection) 93 | { 94 | _client.Close(); 95 | } 96 | } 97 | 98 | UpdateLastOperationTime(); 99 | } 100 | else 101 | { 102 | _client.Close(); 103 | } 104 | } 105 | } 106 | catch (Exception e) 107 | { 108 | // Hate people who make bad calls. 109 | Logger.Warn(string.Format("Error while serving : {0}", _remoteEndPoint), e); 110 | _client.Close(); 111 | } 112 | 113 | Logger.InfoFormat("Lost Client {0}", _remoteEndPoint); 114 | } 115 | private async Task WriteResponse(HttpContext context, StreamWriter writer) 116 | { 117 | IHttpResponse response = context.Response; 118 | IHttpRequest request = context.Request; 119 | 120 | // Headers 121 | await writer.WriteLineAsync(string.Format("HTTP/1.1 {0} {1}", 122 | (int)response.ResponseCode, 123 | response.ResponseCode)) 124 | .ConfigureAwait(false); 125 | 126 | foreach (var header in response.Headers) 127 | { 128 | await writer.WriteLineAsync(string.Format("{0}: {1}", header.Key, header.Value)).ConfigureAwait(false); 129 | } 130 | 131 | // Cookies 132 | if (context.Cookies.Touched) 133 | { 134 | await writer.WriteAsync(context.Cookies.ToCookieData()) 135 | .ConfigureAwait(false); 136 | } 137 | 138 | // Empty Line 139 | await writer.WriteLineAsync().ConfigureAwait(false); 140 | 141 | // Body 142 | await response.WriteBody(writer).ConfigureAwait(false); 143 | await writer.FlushAsync().ConfigureAwait(false); 144 | 145 | } 146 | 147 | public IClient Client 148 | { 149 | get { return _client; } 150 | } 151 | 152 | public void ForceClose() 153 | { 154 | _client.Close(); 155 | } 156 | 157 | public DateTime LastOperationTime 158 | { 159 | get 160 | { 161 | return _lastOperationTime; 162 | } 163 | } 164 | 165 | private void UpdateLastOperationTime() 166 | { 167 | // _lastOperationTime = DateTime.Now; 168 | } 169 | 170 | } 171 | 172 | internal class NotFlushingStream : Stream 173 | { 174 | private readonly Stream _child; 175 | public NotFlushingStream(Stream child) 176 | { 177 | _child = child; 178 | } 179 | 180 | 181 | public void ExplicitFlush() 182 | { 183 | _child.Flush(); 184 | } 185 | 186 | public Task ExplicitFlushAsync() 187 | { 188 | return _child.FlushAsync(); 189 | } 190 | 191 | public override void Flush() 192 | { 193 | // _child.Flush(); 194 | } 195 | 196 | public override long Seek(long offset, SeekOrigin origin) 197 | { 198 | return _child.Seek(offset, origin); 199 | } 200 | public override void SetLength(long value) 201 | { 202 | _child.SetLength(value); 203 | } 204 | public override int Read(byte[] buffer, int offset, int count) 205 | { 206 | return _child.Read(buffer, offset, count); 207 | } 208 | 209 | public override int ReadByte() 210 | { 211 | return _child.ReadByte(); 212 | } 213 | public override void Write(byte[] buffer, int offset, int count) 214 | { 215 | _child.Write(buffer, offset, count); 216 | } 217 | public override void WriteByte(byte value) 218 | { 219 | _child.WriteByte(value); 220 | } 221 | public override bool CanRead 222 | { 223 | get { return _child.CanRead; } 224 | } 225 | public override bool CanSeek 226 | { 227 | get { return _child.CanSeek; } 228 | } 229 | 230 | public override bool CanWrite 231 | { 232 | get { return _child.CanWrite; } 233 | } 234 | public override long Length 235 | { 236 | get { return _child.Length; } 237 | } 238 | public override long Position 239 | { 240 | get { return _child.Position; } 241 | set { _child.Position = value; } 242 | } 243 | public override int ReadTimeout 244 | { 245 | get { return _child.ReadTimeout; } 246 | set { _child.ReadTimeout = value; } 247 | } 248 | public override int WriteTimeout 249 | { 250 | get { return _child.WriteTimeout; } 251 | set { _child.WriteTimeout = value; } 252 | } 253 | } 254 | 255 | public static class RequestHandlersAggregateExtensions 256 | { 257 | 258 | public static Func Aggregate(this IList handlers) 259 | { 260 | return handlers.Aggregate(0); 261 | } 262 | 263 | private static Func Aggregate(this IList handlers, int index) 264 | { 265 | if (index == handlers.Count) 266 | { 267 | return null; 268 | } 269 | 270 | var currentHandler = handlers[index]; 271 | var nextHandler = handlers.Aggregate(index + 1); 272 | 273 | return context => currentHandler.Handle(context, () => nextHandler(context)); 274 | } 275 | 276 | 277 | } 278 | } -------------------------------------------------------------------------------- /uhttpsharp/HttpResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Globalization; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | using System.Threading.Tasks; 26 | using uhttpsharp.Headers; 27 | 28 | namespace uhttpsharp 29 | { 30 | public interface IHttpResponse 31 | { 32 | Task WriteBody(StreamWriter writer); 33 | 34 | /// 35 | /// Gets the status line of this http response, 36 | /// The first line that will be sent to the client. 37 | /// 38 | HttpResponseCode ResponseCode { get; } 39 | 40 | IHttpHeaders Headers { get; } 41 | 42 | bool CloseConnection { get; } 43 | } 44 | 45 | public abstract class HttpResponseBase : IHttpResponse 46 | { 47 | private readonly HttpResponseCode _code; 48 | private readonly IHttpHeaders _headers; 49 | protected HttpResponseBase(HttpResponseCode code, IHttpHeaders headers) 50 | { 51 | _code = code; 52 | _headers = headers; 53 | } 54 | 55 | public abstract Task WriteBody(StreamWriter writer); 56 | public HttpResponseCode ResponseCode 57 | { 58 | get { return _code; } 59 | } 60 | public IHttpHeaders Headers 61 | { 62 | get { return _headers; } 63 | } 64 | public bool CloseConnection 65 | { 66 | get 67 | { 68 | string value; 69 | return !(_headers.TryGetByName("Connection", out value) && 70 | value.Equals("Keep-Alive", StringComparison.InvariantCultureIgnoreCase)); 71 | } 72 | } 73 | } 74 | 75 | public sealed class StreamHttpResponse : HttpResponseBase 76 | { 77 | private readonly Stream _body; 78 | public StreamHttpResponse(Stream body, HttpResponseCode code, IHttpHeaders headers) : base(code, headers) 79 | { 80 | _body = body; 81 | } 82 | 83 | public static IHttpResponse Create(Stream body, HttpResponseCode code = HttpResponseCode.Ok, string contentType = "text/html; charset=utf-8", bool keepAlive = true) 84 | { 85 | return new StreamHttpResponse(body, code, new ListHttpHeaders(new[] 86 | { 87 | new KeyValuePair("Date", DateTime.UtcNow.ToString("R")), 88 | new KeyValuePair("content-type", contentType), 89 | new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"), 90 | new KeyValuePair("content-length", body.Length.ToString(CultureInfo.InvariantCulture)), 91 | })); 92 | } 93 | 94 | public async override Task WriteBody(StreamWriter writer) 95 | { 96 | await writer.FlushAsync().ConfigureAwait(false); 97 | await _body.CopyToAsync(writer.BaseStream).ConfigureAwait(false); 98 | await writer.BaseStream.FlushAsync().ConfigureAwait(false); 99 | } 100 | } 101 | 102 | public sealed class EmptyHttpResponse : HttpResponseBase 103 | { 104 | 105 | public EmptyHttpResponse(HttpResponseCode code, IHttpHeaders headers) : base(code, headers) 106 | { 107 | 108 | } 109 | 110 | public static IHttpResponse Create(HttpResponseCode code = HttpResponseCode.Ok, bool keepAlive = true) 111 | { 112 | return new EmptyHttpResponse(code, new ListHttpHeaders(new[] 113 | { 114 | new KeyValuePair("Date", DateTime.UtcNow.ToString("R")), 115 | new KeyValuePair("content-type", "text/html"), 116 | new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"), 117 | new KeyValuePair("content-length", "0"), 118 | })); 119 | } 120 | 121 | public override Task WriteBody(StreamWriter writer) 122 | { 123 | return Task.Factory.GetCompleted(); 124 | } 125 | } 126 | 127 | public sealed class StringHttpResponse : HttpResponseBase 128 | { 129 | private readonly string _body; 130 | 131 | public StringHttpResponse(string body, HttpResponseCode code, IHttpHeaders headers) : base(code, headers) 132 | { 133 | _body = body; 134 | } 135 | 136 | public static IHttpResponse Create(string body, HttpResponseCode code = HttpResponseCode.Ok, string contentType = "text/html; charset=utf-8", bool keepAlive = true) 137 | { 138 | return new StringHttpResponse(body, code, new ListHttpHeaders(new[] 139 | { 140 | new KeyValuePair("Date", DateTime.UtcNow.ToString("R")), 141 | new KeyValuePair("content-type", contentType), 142 | new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"), 143 | new KeyValuePair("content-length", Encoding.UTF8.GetByteCount(body).ToString(CultureInfo.InvariantCulture)), 144 | })); 145 | } 146 | 147 | public async override Task WriteBody(StreamWriter writer) 148 | { 149 | await writer.WriteAsync(_body).ConfigureAwait(false); 150 | } 151 | 152 | } 153 | 154 | public sealed class HttpResponse : IHttpResponse 155 | { 156 | private Stream ContentStream { get; set; } 157 | 158 | private readonly Stream _headerStream = new MemoryStream(); 159 | private readonly bool _closeConnection; 160 | private readonly IHttpHeaders _headers; 161 | private readonly HttpResponseCode _responseCode; 162 | 163 | public HttpResponse(HttpResponseCode code, string content, bool keepAliveConnection) 164 | : this(code, "text/html; charset=utf-8", StringToStream(content), keepAliveConnection) 165 | { 166 | } 167 | 168 | public HttpResponse(HttpResponseCode code, string content, IEnumerable> headers, bool keepAliveConnection) 169 | : this(code, "text/html; charset=utf-8", StringToStream(content), keepAliveConnection, headers) 170 | { 171 | } 172 | 173 | public HttpResponse(string contentType, Stream contentStream, bool keepAliveConnection) 174 | : this(HttpResponseCode.Ok, contentType, contentStream, keepAliveConnection) 175 | { 176 | } 177 | 178 | public HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection, IEnumerable> headers) 179 | { 180 | ContentStream = contentStream; 181 | 182 | _closeConnection = !keepAliveConnection; 183 | 184 | _responseCode = code; 185 | _headers = new ListHttpHeaders(new[] 186 | { 187 | new KeyValuePair("Date", DateTime.UtcNow.ToString("R")), 188 | new KeyValuePair("Connection", _closeConnection ? "Close" : "Keep-Alive"), 189 | new KeyValuePair("Content-Type", contentType), 190 | new KeyValuePair("Content-Length", ContentStream.Length.ToString(CultureInfo.InvariantCulture)), 191 | }.Concat(headers).ToList()); 192 | 193 | } 194 | public HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection) : 195 | this(code, contentType, contentStream, keepAliveConnection, Enumerable.Empty>()) 196 | { 197 | } 198 | 199 | 200 | public HttpResponse(HttpResponseCode code, byte[] contentStream, bool keepAliveConnection) 201 | : this (code, "text/html; charset=utf-8", new MemoryStream(contentStream), keepAliveConnection) 202 | { 203 | } 204 | 205 | public HttpResponse(HttpResponseCode code, string contentType, string content, bool closeConnection) 206 | : this(code, contentType, StringToStream(content), closeConnection) 207 | { 208 | } 209 | 210 | public static HttpResponse CreateWithMessage(HttpResponseCode code, string message, bool keepAliveConnection, string body = "") 211 | { 212 | return new HttpResponse( 213 | code, 214 | string.Format( 215 | "{0}

{0}


{1}", 216 | message, body), keepAliveConnection); 217 | } 218 | private static MemoryStream StringToStream(string content) 219 | { 220 | var stream = new MemoryStream(); 221 | var writer = new StreamWriter(stream); 222 | writer.Write(content); 223 | writer.Flush(); 224 | return stream; 225 | } 226 | public async Task WriteBody(StreamWriter writer) 227 | { 228 | ContentStream.Position = 0; 229 | await ContentStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false); 230 | } 231 | public HttpResponseCode ResponseCode 232 | { 233 | get { return _responseCode; } 234 | } 235 | public IHttpHeaders Headers 236 | { 237 | get { return _headers; } 238 | } 239 | 240 | public bool CloseConnection 241 | { 242 | get { return _closeConnection; } 243 | } 244 | 245 | public async Task WriteHeaders(StreamWriter writer) 246 | { 247 | _headerStream.Position = 0; 248 | await _headerStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false); 249 | 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /uhttpsharp/Handlers/ControllerHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | using uhttpsharp.Attributes; 8 | using uhttpsharp.Controllers; 9 | using uhttpsharp.ModelBinders; 10 | 11 | namespace uhttpsharp.Handlers 12 | { 13 | 14 | /// 15 | /// Need some kind of way to prevent default behavior of controller that inherits a base controller... 16 | /// since we are not using virtual methods 17 | /// 18 | public class ControllerHandler : IHttpRequestHandler 19 | { 20 | sealed class ControllerMethod 21 | { 22 | private readonly Type _controllerType; 23 | private readonly HttpMethods _method; 24 | 25 | public ControllerMethod(Type controllerType, HttpMethods method) 26 | { 27 | _controllerType = controllerType; 28 | _method = method; 29 | } 30 | 31 | public Type ControllerType 32 | { 33 | get { return _controllerType; } 34 | } 35 | public HttpMethods Method 36 | { 37 | get { return _method; } 38 | } 39 | 40 | private bool Equals(ControllerMethod other) 41 | { 42 | return _controllerType == other._controllerType && _method == other._method; 43 | } 44 | public override bool Equals(object obj) 45 | { 46 | if (ReferenceEquals(null, obj)) return false; 47 | if (ReferenceEquals(this, obj)) return true; 48 | return obj is ControllerMethod && Equals((ControllerMethod)obj); 49 | } 50 | public override int GetHashCode() 51 | { 52 | unchecked 53 | { 54 | return (_controllerType.GetHashCode() * 397) ^ (int)_method; 55 | } 56 | } 57 | } 58 | sealed class ControllerRoute 59 | { 60 | private readonly Type _controllerType; 61 | private readonly string _propertyName; 62 | public ControllerRoute(Type controllerType, string propertyName) 63 | { 64 | _controllerType = controllerType; 65 | _propertyName = propertyName; 66 | } 67 | private bool Equals(ControllerRoute other) 68 | { 69 | return _controllerType == other._controllerType && string.Equals(_propertyName, other._propertyName, StringComparison.InvariantCultureIgnoreCase); 70 | } 71 | public override bool Equals(object obj) 72 | { 73 | if (ReferenceEquals(null, obj)) return false; 74 | if (ReferenceEquals(this, obj)) return true; 75 | return obj is ControllerRoute && Equals((ControllerRoute)obj); 76 | } 77 | public override int GetHashCode() 78 | { 79 | unchecked 80 | { 81 | return ((_controllerType != null ? _controllerType.GetHashCode() : 0) * 397) ^ (_propertyName != null ? _propertyName.ToLowerInvariant().GetHashCode() : 0); 82 | } 83 | } 84 | } 85 | 86 | private static readonly IDictionary ControllerFunctions = new Dictionary(); 87 | 88 | private static readonly IDictionary> Routes = new Dictionary>(); 89 | 90 | private static readonly IDictionary>> IndexerRoutes = new Dictionary>>(); 91 | 92 | private static readonly ICollection LoadedControllerRoutes = new HashSet(); 93 | 94 | private static readonly object SyncRoot = new object(); 95 | 96 | public delegate Task ControllerFunction(IHttpContext context, IModelBinder binder, IController controller); 97 | 98 | private readonly IController _controller; 99 | private readonly IModelBinder _modelBinder; 100 | private readonly IView _view; 101 | 102 | public ControllerHandler(IController controller, IModelBinder modelBinder, IView view) 103 | { 104 | _controller = controller; 105 | _modelBinder = modelBinder; 106 | _view = view; 107 | } 108 | protected virtual IModelBinder ModelBinder 109 | { 110 | get { return _modelBinder; } 111 | } 112 | 113 | private static Func GenerateRouteFunction(MethodInfo getter) 114 | { 115 | var instance = Expression.Parameter(typeof(IController), "instance"); 116 | return Expression.Lambda>(Expression.Call(Expression.Convert(instance, getter.DeclaringType), getter), instance).Compile(); 117 | } 118 | private static void LoadRoutes(Type controllerType) 119 | { 120 | if (!LoadedControllerRoutes.Contains(controllerType)) 121 | { 122 | lock (SyncRoot) 123 | { 124 | if (!LoadedControllerRoutes.Contains(controllerType)) 125 | { 126 | foreach (var prop in controllerType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.PropertyType == typeof(IController))) 127 | { 128 | Routes.Add(new ControllerRoute(controllerType, prop.Name), 129 | GenerateRouteFunction(prop.GetMethod)); 130 | } 131 | // Indexers 132 | var method = controllerType.GetMethods().SingleOrDefault(m => Attribute.IsDefined(m, typeof(IndexerAttribute))); 133 | if (method != null) 134 | { 135 | IndexerRoutes.Add(controllerType, ClassRouter.CreateIndexerFunction(controllerType, method)); 136 | } 137 | 138 | LoadedControllerRoutes.Add(controllerType); 139 | } 140 | } 141 | } 142 | } 143 | 144 | public async Task Handle(IHttpContext context, Func next) 145 | { 146 | // I/O Bound? 147 | var controller = await GetController(context.Request.RequestParameters, context).ConfigureAwait(false); 148 | 149 | if (controller == null) 150 | { 151 | await next().ConfigureAwait(false); 152 | return; 153 | } 154 | 155 | var response = await controller.Pipeline.Go(() => CallMethod(context, controller), context).ConfigureAwait(false); 156 | context.Response = await response.Respond(context, _view).ConfigureAwait(false); 157 | 158 | 159 | } 160 | private async Task GetController(IEnumerable requestParameters, IHttpContext context) 161 | { 162 | var current = _controller; 163 | foreach (var parameter in requestParameters) 164 | { 165 | var controllerType = current.GetType(); 166 | 167 | LoadRoutes(controllerType); 168 | 169 | var route = new ControllerRoute(controllerType, parameter); 170 | 171 | Func routeFunction; 172 | if (Routes.TryGetValue(route, out routeFunction)) 173 | { 174 | current = routeFunction(current); 175 | continue; 176 | } 177 | 178 | // Try find indexer. 179 | Func> indexerFunction; 180 | if (IndexerRoutes.TryGetValue(controllerType, out indexerFunction)) 181 | { 182 | current = await indexerFunction(context, current, parameter).ConfigureAwait(false); 183 | continue; 184 | } 185 | 186 | return null; 187 | } 188 | 189 | return current; 190 | } 191 | 192 | private Task CallMethod(IHttpContext context, IController controller) 193 | { 194 | var controllerMethod = new ControllerMethod(controller.GetType(), context.Request.Method); 195 | 196 | ControllerFunction controllerFunction; 197 | if (!ControllerFunctions.TryGetValue(controllerMethod, out controllerFunction)) 198 | { 199 | lock (SyncRoot) 200 | { 201 | if (!ControllerFunctions.TryGetValue(controllerMethod, out controllerFunction)) 202 | { 203 | ControllerFunctions[controllerMethod] = controllerFunction = CreateControllerFunction(controllerMethod); 204 | } 205 | } 206 | } 207 | 208 | return controllerFunction(context, this.ModelBinder, controller); 209 | //context.Response = await controllerResponse.Respond(context, _view).ConfigureAwait(false); 210 | } 211 | 212 | private ControllerFunction CreateControllerFunction(ControllerMethod controllerMethod) 213 | { 214 | var httpContextArgument = Expression.Parameter(typeof(IHttpContext), "httpContext"); 215 | var modelBinderArgument = Expression.Parameter(typeof(IModelBinder), "modelBinder"); 216 | var controllerArgument = Expression.Parameter(typeof(object), "controller"); 217 | 218 | var errorContainerVariable = Expression.Variable(typeof(IErrorContainer)); 219 | 220 | var foundMethod = 221 | (from method in controllerMethod.ControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public) 222 | let attribute = method.GetCustomAttribute() 223 | where attribute != null && attribute.HttpMethod == controllerMethod.Method 224 | select method).FirstOrDefault(); 225 | 226 | if (foundMethod == null) 227 | { 228 | return MethodNotFoundControllerFunction; 229 | } 230 | 231 | var parameters = foundMethod.GetParameters(); 232 | 233 | IList variables = new List(parameters.Length); 234 | 235 | IList body = new List(parameters.Length); 236 | 237 | var modelBindingGetMethod = typeof(IModelBinding).GetMethods()[0]; 238 | 239 | foreach (var parameter in parameters) 240 | { 241 | var variable = Expression.Variable(parameter.ParameterType, parameter.Name); 242 | variables.Add(variable); 243 | 244 | var attributes = parameter.GetCustomAttributes().ToList(); 245 | 246 | var modelBindingAttribute = attributes.OfType().Single(); 247 | 248 | body.Add( 249 | Expression.Assign(variable, 250 | Expression.Call(Expression.Constant(modelBindingAttribute), 251 | modelBindingGetMethod.MakeGenericMethod(parameter.ParameterType), 252 | httpContextArgument, modelBinderArgument 253 | ))); 254 | 255 | if (!attributes.OfType().Any()) 256 | { 257 | body.Add(Expression.IfThen(Expression.Equal(variable, Expression.Constant(null)), 258 | Expression.Call(errorContainerVariable, "Log", Type.EmptyTypes, 259 | Expression.Constant(parameter.Name + " Is not found (null) and not marked as nullable.")))); 260 | } 261 | 262 | if (parameter.ParameterType.GetInterfaces().Contains(typeof(IValidate))) 263 | { 264 | body.Add(Expression.IfThen(Expression.NotEqual(variable, Expression.Constant(null)), 265 | Expression.Call(variable, "Validate", Type.EmptyTypes, errorContainerVariable))); 266 | } 267 | 268 | } 269 | 270 | var methodCallExp = Expression.Call(Expression.Convert(controllerArgument, controllerMethod.ControllerType), foundMethod, variables); 271 | 272 | var labelTarget = Expression.Label(typeof(Task)); 273 | 274 | var parameterBindingExpression = body.Count > 0 ? (Expression)Expression.Block(body) : Expression.Empty(); 275 | 276 | var methodBody = Expression.Block( 277 | variables.Concat(new[] { errorContainerVariable }), 278 | Expression.Assign(errorContainerVariable, Expression.New(typeof(ErrorContainer))), 279 | parameterBindingExpression, 280 | Expression.IfThen(Expression.Not(Expression.Property(errorContainerVariable, "Any")), 281 | Expression.Return(labelTarget, methodCallExp)), 282 | 283 | Expression.Label(labelTarget, Expression.Call(errorContainerVariable, "GetResponse", Type.EmptyTypes)) 284 | ); 285 | 286 | var parameterExpressions = new[] { httpContextArgument, modelBinderArgument, controllerArgument }; 287 | var lambda = Expression.Lambda(methodBody, parameterExpressions); 288 | 289 | return lambda.Compile(); 290 | } 291 | private Task MethodNotFoundControllerFunction(IHttpContext context, IModelBinder binder, object controller) 292 | { 293 | // TODO : MethodNotFound. 294 | return Task.FromResult(new RenderResponse(HttpResponseCode.MethodNotAllowed, new { Message = "Not Allowed" })); 295 | } 296 | } 297 | 298 | 299 | } 300 | --------------------------------------------------------------------------------