Created
January 24, 2012 23:12
-
-
Save anonymous/1673393 to your computer and use it in GitHub Desktop.
Illustrates implementing OWIN in Kayak without referencing Gate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using Kayak; | |
| using Kayak.Http; | |
| namespace Example.Owin | |
| { | |
| using ResultAction = Action< | |
| string, | |
| IDictionary<string, string>, | |
| Func< //body | |
| Func< //next | |
| ArraySegment<byte>, // data | |
| Action, // continuation | |
| bool>, // continuation was or will be invoked | |
| Action<Exception>, //error | |
| Action, //complete | |
| Action>>; //cancel | |
| using AppAction = Action< // app | |
| IDictionary<string, object>, // env | |
| Action< // result | |
| string, // status | |
| IDictionary<string, string>, // headers | |
| Func< // body | |
| Func< // next | |
| ArraySegment<byte>, // data | |
| Action, // continuation | |
| bool>, // async | |
| Action<Exception>, // error | |
| Action, // complete | |
| Action>>, // cancel | |
| Action<Exception>>; // error | |
| using BodyAction = Func< //body | |
| Func< //next | |
| ArraySegment<byte>, // data | |
| Action, // continuation | |
| bool>, // continuation was or will be invoked | |
| Action<Exception>, //error | |
| Action, //complete | |
| Action>; // cancel | |
| internal class OwinHttpRequestDelegate : IHttpRequestDelegate | |
| { | |
| private AppAction owin; | |
| internal OwinHttpRequestDelegate(AppAction owin) | |
| { | |
| this.owin = owin; | |
| } | |
| private static IDictionary<string, object> ToOwinEnvironment(HttpRequestHead head) | |
| { | |
| var env = new Dictionary<string, object>(); | |
| env.Add("owin.RequestMethod", head.Method ?? ""); | |
| env.Add("owin.RequestPath", head.Path); | |
| env.Add("owin.RequestPathBase", ""); | |
| env.Add("owin.RequestQueryString", head.QueryString ?? ""); | |
| env.Add("owin.RequestScheme", "http"); | |
| env.Add("owin.RequestHeaders", head.Headers ?? new Dictionary<string, string>()); | |
| env.Add("owin.Version", "1.0"); | |
| return env; | |
| } | |
| public void OnRequest( | |
| HttpRequestHead head, | |
| IDataProducer body, | |
| IHttpResponseDelegate response) | |
| { | |
| var env = ToOwinEnvironment(head); | |
| if (body == null) | |
| env["owin.RequestBody"] = null; | |
| else | |
| { | |
| BodyAction bodyFunc = (onData, onError, onEnd) => | |
| { | |
| var d = body.Connect(new DataConsumer(onData, onError, onEnd)); | |
| return () => { if (d != null) d.Dispose(); }; | |
| }; | |
| env["owin.RequestBody"] = bodyFunc; | |
| } | |
| owin(env, HandleResponse(response), HandleError(response)); | |
| } | |
| ResultAction HandleResponse(IHttpResponseDelegate response) | |
| { | |
| return (status, headers, body) => | |
| { | |
| if (headers == null) | |
| headers = new Dictionary<string, string>(); | |
| if (body != null && | |
| !headers.Keys.Contains("content-length", StringComparer.OrdinalIgnoreCase) && | |
| !headers.Keys.Contains("transfer-encoding", StringComparer.OrdinalIgnoreCase)) | |
| { | |
| // consume body and calculate Content-Length | |
| BufferBody(response)(status, headers, body); | |
| } | |
| else | |
| { | |
| response.OnResponse(new HttpResponseHead() | |
| { | |
| Status = status, | |
| Headers = headers.ToDictionary(kv => kv.Key, kv => string.Join("\r\n", kv.Value.ToArray()), StringComparer.OrdinalIgnoreCase), | |
| }, body == null ? null : new DataProducer(body)); | |
| } | |
| }; | |
| } | |
| ResultAction BufferBody(IHttpResponseDelegate response) | |
| { | |
| return (status, headers, body) => | |
| { | |
| var buffer = new LinkedList<ArraySegment<byte>>(); | |
| body((data, continuation) => | |
| { | |
| var copy = new byte[data.Count]; | |
| Buffer.BlockCopy(data.Array, data.Offset, copy, 0, data.Count); | |
| buffer.AddLast(new ArraySegment<byte>(copy)); | |
| return false; | |
| }, | |
| HandleError(response), | |
| () => | |
| { | |
| var contentLength = buffer.Aggregate(0, (r, i) => r + i.Count); | |
| IDataProducer responseBody = null; | |
| if (contentLength > 0) | |
| { | |
| headers["Content-Length"] = contentLength.ToString(); | |
| responseBody = new DataProducer((onData, onError, onComplete) => | |
| { | |
| bool cancelled = false; | |
| while (!cancelled && buffer.Count > 0) | |
| { | |
| var next = buffer.First; | |
| buffer.RemoveFirst(); | |
| onData(next.Value, null); | |
| } | |
| onComplete(); | |
| buffer = null; | |
| return () => cancelled = true; | |
| }); | |
| } | |
| response.OnResponse(new HttpResponseHead() | |
| { | |
| Status = status, | |
| Headers = headers | |
| }, responseBody); | |
| }); | |
| }; | |
| } | |
| Action<Exception> HandleError(IHttpResponseDelegate response) | |
| { | |
| return error => | |
| { | |
| Console.Error.WriteLine("Error from Owin application."); | |
| Console.Error.WriteStackTrace(error); | |
| response.OnResponse(new HttpResponseHead() | |
| { | |
| Status = "503 Internal Server Error", | |
| Headers = new Dictionary<string, string>() | |
| { | |
| { "Connection", "close" } | |
| } | |
| }, null); | |
| }; | |
| } | |
| } | |
| internal class DataConsumer : IDataConsumer | |
| { | |
| readonly Func<ArraySegment<byte>, Action, bool> onData; | |
| readonly Action<Exception> onError; | |
| readonly Action onEnd; | |
| public DataConsumer( | |
| Func<ArraySegment<byte>, Action, bool> onData, | |
| Action<Exception> onError, | |
| Action onEnd) | |
| { | |
| this.onData = onData; | |
| this.onError = onError; | |
| this.onEnd = onEnd; | |
| } | |
| public bool OnData(ArraySegment<byte> data, Action continuation) | |
| { | |
| return onData(data, continuation); | |
| } | |
| public void OnEnd() | |
| { | |
| onEnd(); | |
| } | |
| public void OnError(Exception e) | |
| { | |
| onError(e); | |
| } | |
| } | |
| internal class DataProducer : IDataProducer | |
| { | |
| readonly BodyAction body; | |
| public DataProducer(BodyAction body) | |
| { | |
| this.body = body; | |
| } | |
| public IDisposable Connect(IDataConsumer channel) | |
| { | |
| return new Disposable(body( | |
| (data, continuation) => channel.OnData(data, continuation), | |
| error => channel.OnError(error), | |
| () => channel.OnEnd())); | |
| } | |
| } | |
| internal class Disposable : IDisposable | |
| { | |
| readonly Action dispose; | |
| public Disposable(Action dispose) | |
| { | |
| this.dispose = dispose; | |
| } | |
| public void Dispose() | |
| { | |
| dispose(); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Net; | |
| using Kayak; | |
| using Kayak.Http; | |
| using System.Collections.Generic; | |
| namespace Example.Owin | |
| { | |
| // Give the owin app Action a shorthand: | |
| using AppAction = Action< // app | |
| IDictionary<string, object>, // env | |
| Action< // result | |
| string, // status | |
| IDictionary<string, string>, // headers | |
| Func< // body | |
| Func< // next | |
| ArraySegment<byte>, // data | |
| Action, // continuation | |
| bool>, // async | |
| Action<Exception>, // error | |
| Action, // complete | |
| Action>>, // cancel | |
| Action<Exception>>; // error | |
| public class OwinServer | |
| { | |
| private IScheduler scheduler; | |
| private IServer server; | |
| public OwinServer(AppAction owin) | |
| : this(owin, null, null) | |
| { | |
| } | |
| public OwinServer( | |
| AppAction owin, | |
| Action<IScheduler, Exception> onException, | |
| Action<IScheduler> onStop) | |
| { | |
| scheduler = Kayak.KayakScheduler.Factory.Create(new DefaultSchedulerDelegate(onException, onStop)); | |
| // The OwinServer uses an internal implementation of IHttpRequestDelegate: | |
| server = Kayak.KayakServer.Factory.CreateHttp( | |
| new OwinHttpRequestDelegate(owin), | |
| scheduler); | |
| } | |
| public void Start(IPAddress address, int port) | |
| { | |
| var endPoint = new IPEndPoint(address, port); | |
| using (server.Listen(endPoint)) | |
| scheduler.Start(); | |
| } | |
| class DefaultSchedulerDelegate : ISchedulerDelegate | |
| { | |
| private Action<IScheduler, Exception> onException; | |
| private Action<IScheduler> onStop; | |
| public DefaultSchedulerDelegate( | |
| Action<IScheduler, Exception> onException, | |
| Action<IScheduler> onStop) | |
| { | |
| this.onException = onException; | |
| this.onStop = onStop; | |
| } | |
| public void OnException(IScheduler scheduler, Exception e) | |
| { | |
| if (onException != null) | |
| onException(scheduler, e); | |
| } | |
| public void OnStop(IScheduler scheduler) | |
| { | |
| if (onStop != null) | |
| onStop(scheduler); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Net; | |
| using Example.Owin; | |
| using Nancy; | |
| namespace Example | |
| { | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| // Create a new Nancy instance | |
| var nancy = new Nancy.Hosting.Owin.NancyOwinHost(); | |
| // Create a new OwinServer (based on Kayak) | |
| var server = new OwinServer(nancy.ProcessRequest); | |
| // Start the OwinServer | |
| server.Start(System.Net.IPAddress.Any, 8080); | |
| } | |
| } | |
| public class MainModule : NancyModule | |
| { | |
| public MainModule() | |
| { | |
| Get["/"] = _ => "Hello, World!"; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment