Commit 0409ed24 authored by Shani Elharrar's avatar Shani Elharrar

Merge pull request #2 from shanielh/rest_controllers

Merged Rest controllers into master
parents 3a824bdc 6a83fd7a
......@@ -4,3 +4,5 @@
/packages/*
*/bin/*
*/obj/*
*.psess
*.vsp
\ No newline at end of file
......@@ -53,7 +53,7 @@ Usage :
More modifications will be made to make it more "user friendly" out of the box :
* [RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) controllers
* ~~[RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) controllers~~ (Done)
* Caching support
* NuGet package
* ~~Ssl Support~~ (Done!)
......
using System;
using System.Threading.Tasks;
using uhttpsharp;
namespace uhttpsharpdemo.Handlers
{
public class ExceptionHandler : IHttpRequestHandler
{
public async Task Handle(IHttpContext context, Func<Task> next)
{
try
{
await next();
}
catch (HttpException e)
{
context.Response = new HttpResponse(e.ResponseCode, "Error while handling your request. " + e.Message, false);
}
catch (Exception e)
{
context.Response = new HttpResponse(HttpResponseCode.InternalServerError, "Error while handling your request. " + e, false);
}
}
}
}
\ No newline at end of file
using System;
using uhttpsharp;
namespace uhttpsharpdemo
{
public class HttpException : Exception
{
private readonly HttpResponseCode _responseCode;
public HttpResponseCode ResponseCode
{
get { return _responseCode; }
}
public HttpException(HttpResponseCode responseCode)
{
_responseCode = responseCode;
}
public HttpException(HttpResponseCode responseCode, string message) : base(message)
{
_responseCode = responseCode;
}
}
}
......@@ -34,17 +34,19 @@ namespace uhttpsharpdemo
{
log4net.Config.XmlConfigurator.Configure();
var serverCertificate = X509Certificate.CreateFromCertFile(@"TempCert.cer");
//var serverCertificate = X509Certificate.CreateFromCertFile(@"TempCert.cer");
using (var httpServer = new HttpServer(new HttpRequestProvider()))
{
httpServer.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 80)));
httpServer.Use(new ListenerSslDecorator(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 443)), serverCertificate));
//httpServer.Use(new ListenerSslDecorator(new TcpListenerAdapter(new TcpListener(IPAddress.Loopback, 443)), serverCertificate));
httpServer.Use(new ExceptionHandler());
httpServer.Use(new TimingHandler());
httpServer.Use(new HttpRouter().With(string.Empty, new IndexHandler())
.With("about", new AboutHandler()));
.With("about", new AboutHandler())
.With("strings", new RestHandler<string>(new StringsRestController(), new JsonResponseProvider())));
httpServer.Use(new FileHandler());
httpServer.Use(new ErrorHandler());
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using uhttpsharp;
namespace uhttpsharpdemo
{
class SomeRestController
{
IDictionary<int, string> _strings = new Dictionary<int, string>() { { 1 , "Hahaha"}};
public Task<uhttpsharp.HttpResponse> Get(uhttpsharp.IHttpRequest request)
{
var memoryStream = new MemoryStream();
Newtonsoft.Json.JsonWriter writer = new JsonTextWriter(new StreamWriter( memoryStream));
JsonSerializer.Create().Serialize(writer, _strings);
writer.Flush();
return Task.FromResult(new HttpResponse(HttpResponseCode.Ok, "application/json; charset=utf-8", memoryStream, true));
}
public Task<uhttpsharp.HttpResponse> GetItem(uhttpsharp.IHttpRequest request)
{
throw new NotImplementedException();
}
public Task<uhttpsharp.HttpResponse> Create(uhttpsharp.IHttpRequest request)
{
throw new NotImplementedException();
}
public Task<uhttpsharp.HttpResponse> Upsert(uhttpsharp.IHttpRequest request)
{
throw new NotImplementedException();
}
public Task<uhttpsharp.HttpResponse> Delete(uhttpsharp.IHttpRequest request)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using uhttpsharp;
using uhttpsharp.Handlers;
namespace uhttpsharpdemo
{
class StringsRestController : IRestController<string>
{
private readonly ICollection<string> _collection = new HashSet<string>();
public Task<IEnumerable<string>> Get(IHttpRequest request)
{
return Task.FromResult<IEnumerable<string>>(_collection);
}
public Task<string> GetItem(IHttpRequest request)
{
var id = GetId(request);
if (_collection.Contains(id))
{
return Task.FromResult(id);
}
throw GetNotFoundException();
}
private static string GetId(IHttpRequest request)
{
var id = request.RequestParameters[1];
return id;
}
public Task<string> Create(IHttpRequest request)
{
var id = GetId(request);
_collection.Add(id);
return Task.FromResult(id);
}
public Task<string> Upsert(IHttpRequest request)
{
return Create(request);
}
public Task<string> Delete(IHttpRequest request)
{
var id = GetId(request);
if (_collection.Remove(id))
{
return Task.FromResult(id);
}
throw GetNotFoundException();
}
private static Exception GetNotFoundException()
{
return new HttpException(HttpResponseCode.NotFound, "The resource you've looked for is not found");
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -42,8 +42,12 @@
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
......@@ -56,10 +60,14 @@
</Compile>
<Compile Include="Handlers\AboutHandler.cs" />
<Compile Include="Handlers\ErrorHandler.cs" />
<Compile Include="Handlers\ExceptionHandler.cs" />
<Compile Include="Handlers\IndexHandler.cs" />
<Compile Include="HttpException.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Handlers\TimingHandler.cs" />
<Compile Include="SomeRestController.cs" />
<Compile Include="StringsRestController.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\uhttpsharp\uhttpsharp.csproj">
......
......@@ -60,12 +60,11 @@ namespace uhttpsharp.Handlers
if (!File.Exists(path))
{
await next();
await next().ConfigureAwait(false);
return;
}
context.Response = new HttpResponse(GetContentType(path), File.OpenRead(path), context.Request.Headers.KeepAliveConnection());
}
}
......
using System.Threading.Tasks;
namespace uhttpsharp.Handlers
{
public interface IResponseProvider
{
Task<IHttpResponse> Provide(object value);
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Threading.Tasks;
namespace uhttpsharp.Handlers
{
public interface IRestController<T>
{
/// <summary>
/// Returns a list of object that found in the collection
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<IEnumerable<T>> Get(IHttpRequest request);
/// <summary>
/// Returns an item from the collection
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<T> GetItem(IHttpRequest request);
/// <summary>
/// Creates a new entry in the collection - new uri is returned
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<T> Create(IHttpRequest request);
/// <summary>
/// Updates an entry in the collection
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<T> Upsert(IHttpRequest request);
/// <summary>
/// Removes an entry from the collection
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<T> Delete(IHttpRequest request);
}
}
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace uhttpsharp.Handlers
{
public class JsonResponseProvider : IResponseProvider
{
public Task<IHttpResponse> Provide(object value)
{
var memoryStream = new MemoryStream();
var writer = new JsonTextWriter(new StreamWriter(memoryStream));
var serializer = new JsonSerializer();
serializer.Serialize(writer, value);
writer.Flush();
return Task.FromResult<IHttpResponse>(new HttpResponse(HttpResponseCode.Ok, "application/json; charset=utf-8", memoryStream, true));
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace uhttpsharp.Handlers
{
public class RestHandler<T> : IHttpRequestHandler
{
private struct RestCall
{
private readonly HttpMethods _method;
private readonly bool _entryFull;
public RestCall(HttpMethods method, bool entryFull)
{
_method = method;
_entryFull = entryFull;
}
public static RestCall Create(HttpMethods method, bool entryFull)
{
return new RestCall(method, entryFull);
}
private bool Equals(RestCall other)
{
return _method == other._method && _entryFull.Equals(other._entryFull);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is RestCall && Equals((RestCall)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((int)_method*397) ^ _entryFull.GetHashCode();
}
}
}
private static readonly IDictionary<RestCall, Func<IRestController<T>, IHttpRequest, Task<object>>> RestCallHandlers = new Dictionary<RestCall, Func<IRestController<T>, IHttpRequest, Task<object>>>();
static RestHandler()
{
RestCallHandlers.Add(RestCall.Create(HttpMethods.Get, false), async (c, r) => (object) (await c.Get(r)));
RestCallHandlers.Add(RestCall.Create(HttpMethods.Get, true), async (c, r) => (object) (await c.GetItem(r)));
RestCallHandlers.Add(RestCall.Create(HttpMethods.Post, false), async (c, r) => (object) (await c.Create(r)));
RestCallHandlers.Add(RestCall.Create(HttpMethods.Put, true), async (c, r) => (object) (await c.Upsert(r)));
RestCallHandlers.Add(RestCall.Create(HttpMethods.Delete, true), async (c, r) => (object) (await c.Delete(r)));
}
private readonly IRestController<T> _controller;
private readonly IResponseProvider _responseProvider;
public RestHandler(IRestController<T> controller, IResponseProvider responseProvider)
{
_controller = controller;
_responseProvider = responseProvider;
}
public async Task Handle(IHttpContext httpContext, Func<Task> next)
{
IHttpRequest httpRequest = httpContext.Request;
var call = new RestCall(httpRequest.Method, httpRequest.RequestParameters.Length > 1);
Func<IRestController<T>, IHttpRequest, Task<object>> handler;
if (RestCallHandlers.TryGetValue(call, out handler))
{
var value = await handler(_controller, httpRequest).ConfigureAwait(false);
httpContext.Response = await _responseProvider.Provide(value);
return;
}
await next().ConfigureAwait(false);
}
}
}
......@@ -101,6 +101,12 @@ namespace uhttpsharp
{
// Socket exceptions on read will be re-thrown as IOException by BufferedStream
}
catch (Exception e)
{
// Hate people who make bad calls.
Logger.Warn(string.Format("Error while serving : {0} : {1}{2}", _remoteEndPoint, Environment.NewLine, e));
_client.Close();
}
Logger.InfoFormat("Lost Client {0}", _remoteEndPoint);
}
......
......@@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Threading.Tasks;
using uhttpsharp;
namespace uhttpsharp
{
......@@ -61,7 +62,7 @@ namespace uhttpsharp
: this(HttpResponseCode.Ok, contentType, contentStream, closeConnection)
{
}
private HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection)
public HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection)
{
Protocol = "HTTP/1.1";
ContentType = contentType;
......@@ -85,7 +86,7 @@ namespace uhttpsharp
"<html><head><title>{0}</title></head><body><h1>{0}</h1><hr>{1}</body></html>",
message, body), keepAliveConnection);
}
private static Stream StringToStream(string content)
private static MemoryStream StringToStream(string content)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
......@@ -95,6 +96,7 @@ namespace uhttpsharp
}
public async Task WriteResponse(StreamWriter writer)
{
_headerStream.Position = 0;
await _headerStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false);
......
......@@ -78,7 +78,6 @@ namespace uhttpsharp
{
Logger.Warn("Error while getting client", e);
}
}
Logger.InfoFormat("Embedded uhttpserver stopped.");
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -39,6 +39,9 @@
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
......@@ -54,6 +57,10 @@
<Compile Include="Clients\ClientSslDecoerator.cs" />
<Compile Include="Clients\IClient.cs" />
<Compile Include="Handlers\FileHandler.cs" />
<Compile Include="Handlers\IResponseProvider.cs" />
<Compile Include="Handlers\IRestController.cs" />
<Compile Include="Handlers\JsonResponseProvider.cs" />
<Compile Include="Handlers\RestHandler.cs" />
<Compile Include="HttpClient.cs" />
<Compile Include="HttpMethodProvider.cs" />
<Compile Include="HttpMethodProviderCache.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment