Created
July 23, 2013 20:42
-
-
Save Porges/6066000 to your computer and use it in GitHub Desktop.
sketch of C# version of example program
This file contains 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.Runtime.InteropServices; | |
namespace ExampleApplication | |
{ | |
public class Haywire | |
{ | |
private readonly List<RouteCallback> _routeCallbacks = new List<RouteCallback>(); // store these in here to prevent them being GC'd | |
private ResponseCompleteCallback _responseCompleteCallback; // ditto, but I've assumed only one can be active at any one time | |
[StructLayout(LayoutKind.Sequential)] | |
unsafe struct NativeHaywireConfig | |
{ | |
IntPtr http_listen_address; | |
int http_listen_port; | |
public NativeHaywireConfig(HaywireConfig config) | |
{ | |
http_listen_address = Marshal.StringToHGlobalAnsi(config.ListenAddress.ToString()); | |
http_listen_port = config.ListenPort; | |
} | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private unsafe static extern void hw_init_with_config(NativeHaywireConfig* config); | |
public Haywire(HaywireConfig haywireConfig) | |
{ | |
// a dummy handler always needs to be registered to stop UserData from leaking | |
RegisterHttpResponseCompleteCallback(_ => { }); | |
var nativeHaywireConfig = new NativeHaywireConfig(haywireConfig); | |
unsafe | |
{ | |
hw_init_with_config(&nativeHaywireConfig); | |
} | |
} | |
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] | |
private delegate void RouteCallback(IntPtr request, IntPtr response); | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_http_add_route(string route, RouteCallback callback); | |
public void AddRoute(string route, Action<HttpRequest, HttpResponse> callback) | |
{ | |
var nativeCallback = new RouteCallback((req, resp) => | |
{ | |
var httpReq = new HttpRequest(req); | |
var httpResp = new HttpResponse(resp); | |
try | |
{ | |
callback(httpReq, httpResp); | |
} | |
finally | |
{ | |
httpReq.Invalidate(); | |
httpResp.Invalidate(); | |
} | |
}); | |
hw_http_add_route(route, nativeCallback); | |
_routeCallbacks.Add(nativeCallback); // TODO: need to evict things from here when no longer needed? | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_register_http_response_complete_callback(ResponseCompleteCallback callback); | |
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] | |
private delegate void ResponseCompleteCallback(IntPtr userData); | |
public void RegisterHttpResponseCompleteCallback(Action<object> callback) | |
{ | |
var nativeCallback = new ResponseCompleteCallback(ptr => | |
{ | |
var handle = GCHandle.FromIntPtr(ptr); | |
var obj = handle.Target; | |
handle.Free(); | |
callback(obj); | |
}); | |
hw_register_http_response_complete_callback(nativeCallback); | |
_responseCompleteCallback = nativeCallback; | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_http_open(); | |
public void Open() | |
{ | |
hw_http_open(); | |
} | |
} | |
} |
This file contains 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; | |
namespace ExampleApplication | |
{ | |
public class HaywireConfig | |
{ | |
public IPAddress ListenAddress { get; set; } | |
public int ListenPort { get; set; } | |
} | |
} |
This file contains 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.Runtime.InteropServices; | |
namespace ExampleApplication | |
{ | |
public unsafe class HttpRequest | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
struct HttpRequestNative | |
{ | |
// flesh out if needed - TODO make sure sizeofs are the same here as in C | |
ushort http_major; | |
ushort http_minor; | |
byte method; | |
public int keep_alive; | |
} | |
private HttpRequestNative* _me; | |
internal HttpRequest(IntPtr pointer) | |
{ | |
_me = (HttpRequestNative*)pointer; | |
} | |
internal void Invalidate() | |
{ | |
_me = null; | |
} | |
private void CheckIfInvalid() | |
{ | |
if (_me == null) throw new InvalidOperationException("Cannot use HttpRequest outside a route handler."); | |
} | |
public bool KeepAlive | |
{ | |
get | |
{ | |
CheckIfInvalid(); | |
return _me->keep_alive != 0; | |
} | |
} | |
} | |
} |
This file contains 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.Runtime.InteropServices; | |
using System.Threading; | |
namespace ExampleApplication | |
{ | |
public unsafe class HttpResponse | |
{ | |
struct HttpResponseNative | |
{ | |
// flesh out if needed | |
} | |
private HttpResponseNative* _me; | |
private int _sent; | |
internal HttpResponse(IntPtr pointer) | |
{ | |
_me = (HttpResponseNative*) pointer; | |
} | |
internal void Invalidate() | |
{ | |
_me = null; | |
} | |
private void CheckIfInvalid() | |
{ | |
if (_me == null) throw new InvalidOperationException("Cannot use HttpResponse outside a route handler."); | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_set_response_status_code(HttpResponseNative* response, HwString* statusCode); | |
public string StatusCode | |
{ | |
set | |
{ | |
CheckIfInvalid(); | |
hw_set_response_status_code(_me, HwString.AllocateFromString(value)); | |
} | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] | |
private static extern void hw_set_response_header(HttpResponseNative* response, HwString* key, HwString* value); | |
public void AddHeader(string key, string value) | |
{ | |
CheckIfInvalid(); | |
hw_set_response_header(_me, HwString.AllocateFromString(key), HwString.AllocateFromString(value)); | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_set_body(HttpResponseNative* response, HwString* body); | |
public string Body | |
{ | |
set | |
{ | |
CheckIfInvalid(); | |
hw_set_body(_me, HwString.AllocateFromString(value)); | |
} | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_set_http_version(HttpResponseNative* response, int major, int minor); | |
public Version HttpVersion | |
{ | |
set | |
{ | |
CheckIfInvalid(); | |
hw_set_http_version(_me, value.Major, value.Minor); | |
} | |
} | |
[DllImport("haywire.dll", CallingConvention = CallingConvention.Cdecl)] | |
private static extern void hw_http_response_send(HttpResponseNative* response, IntPtr userData); | |
public void Send(object userData) | |
{ | |
CheckIfInvalid(); | |
if (Interlocked.Exchange(ref _sent, 1) == 1) | |
throw new InvalidOperationException("Already sent a response."); | |
var handle = GCHandle.Alloc(userData); | |
hw_http_response_send(_me, (IntPtr)handle); | |
} | |
} | |
} |
This file contains 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.Runtime.InteropServices; | |
namespace ExampleApplication | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
unsafe internal struct HwString | |
{ | |
public IntPtr value; | |
public UIntPtr length; | |
public static HwString* AllocateFromString(string value) | |
{ | |
var hwstring = (HwString*)Marshal.AllocHGlobal(sizeof(HwString)); | |
hwstring->value = Marshal.StringToHGlobalAnsi(value); | |
hwstring->length = (UIntPtr)value.Length; | |
return hwstring; | |
} | |
} | |
} |
This file contains 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; | |
namespace ExampleApplication | |
{ | |
class Program | |
{ | |
private static void GetRoot(HttpRequest request, HttpResponse response) | |
{ | |
var responseString = "hello world"; | |
response.StatusCode = "200 OK"; | |
response.AddHeader("Content-Type", "text/html"); | |
response.Body = responseString; | |
if (request.KeepAlive) | |
{ | |
response.AddHeader("Connection", "Keep-Alive"); | |
} | |
else | |
{ | |
response.HttpVersion = new Version(1, 0); // hah | |
} | |
response.Send(responseString); | |
} | |
static void ResponseComplete(object userData) | |
{ | |
} | |
private static void Main(string[] args) | |
{ | |
var route = "/"; | |
var config = new HaywireConfig | |
{ | |
ListenAddress = IPAddress.Any, | |
ListenPort = 8000, | |
}; | |
if (args.Length > 0) | |
{ | |
config.ListenPort = int.Parse(args[0]); | |
} | |
var haywire = new Haywire(config); | |
haywire.AddRoute(route, GetRoot); | |
haywire.RegisterHttpResponseCompleteCallback(ResponseComplete); | |
haywire.Open(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment