Skip to content

Instantly share code, notes, and snippets.

Created January 24, 2012 23:12
Show Gist options
  • Select an option

  • Save anonymous/1673393 to your computer and use it in GitHub Desktop.

Select an option

Save anonymous/1673393 to your computer and use it in GitHub Desktop.
Illustrates implementing OWIN in Kayak without referencing Gate
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();
}
}
}
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);
}
}
}
}
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