Skip to content

Instantly share code, notes, and snippets.

@jasonsirota
Created August 4, 2011 17:51
Show Gist options
  • Save jasonsirota/1125753 to your computer and use it in GitHub Desktop.
Save jasonsirota/1125753 to your computer and use it in GitHub Desktop.
WCF OWIN
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Gate.Startup;
namespace Gate.Wcf
{
using AppDelegate = 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>>; // fault
public class GateMessageHandler : DelegatingChannel
{
readonly AppDelegate _app;
public GateMessageHandler(HttpMessageChannel innerChannel)
: base(innerChannel)
{
var configurationString = ConfigurationManager.AppSettings["Gate.Startup"];
_app = new AppBuilder()
.Configure(configurationString)
.Build();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var env = GetOwinEnvironment(request);
return InvokeOwinAppAsync(env, request, cancellationToken);
}
Task<HttpResponseMessage> InvokeOwinAppAsync(IDictionary<string, object> env, HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request,cancellationToken).ContinueWith(
(task) =>
{
var httpResponse = task.Result;
_app.Invoke(env,
(status, headers, body) =>
{
try
{
httpResponse.StatusCode = ConvertStringToHttpStatusCode(status);
foreach (var header in headers.SelectMany(kv => kv.Value.Split("\r\n".ToArray(), StringSplitOptions.RemoveEmptyEntries).Select(v => new { kv.Key, Value = v })))
{
//httpResponse.Headers.Add(header.Key, header.Value);
}
if (body == null)
{
//TODO: Figure out how to tell WCF Task that I am done
return;
}
var stream = new MemoryStream();
httpResponse.Content = new StreamContent(stream);
body(
(data, continuation) =>
{
try
{
if (continuation == null)
{
stream.Write(data.Array, data.Offset, data.Count);
return false;
}
var sr = stream.BeginWrite(data.Array, data.Offset, data.Count, ar =>
{
if (ar.CompletedSynchronously) return;
try
{
stream.EndWrite(ar);
}
catch (Exception ex)
{
//TODO: How do you tell the task there's an exception?
throw ex;
}
continuation();
}, null);
if (sr.CompletedSynchronously)
{
stream.EndWrite(sr);
return false;
}
return true;
}
catch (Exception ex)
{
//TODO: How do you tell the task there's an exception?
throw ex;
return false;
}
},
//TODO: How do you tell the task there's an exception?
(ex) => { throw ex; },
//TODO: Figure out how to end response
() => { return; }
//end body
);
}
catch (Exception ex)
{
//TODO: How do I pass an error back to WCF Async
throw ex;
}
},
ex =>
{
//TODO: How do I pass an error back to WCF Async
throw ex;
});
return httpResponse;
});
}
static Dictionary<string, object> GetOwinEnvironment(HttpRequestMessage request)
{
//TODO: figure out how to get application path here
var pathBase = "";
var path = request.RequestUri.AbsolutePath;
//environment
var env = new Dictionary<string, object>();
new Owin(env)
{
Version = "1.0",
Method = request.Method.ToString(),
Scheme = request.RequestUri.Scheme,
PathBase = pathBase,
Path = path,
QueryString = request.RequestUri.Query,
Headers = ConvertHeadersToDictionary(request.Headers),
Body = (next, error, complete) =>
{
var stream = request.Content.ContentReadStream;
var buffer = new byte[4096];
var continuation = new AsyncCallback[1];
bool[] stopped = { false };
continuation[0] = result =>
{
if (result != null && result.CompletedSynchronously) return;
try
{
for (; ; )
{
if (result != null)
{
var count = stream.EndRead(result);
if (stopped[0]) return;
if (count <= 0)
{
complete();
return;
}
var data = new ArraySegment<byte>(buffer, 0, count);
if (next(data, () => continuation[0](null))) return;
}
if (stopped[0]) return;
result = stream.BeginRead(buffer, 0, buffer.Length, continuation[0], null);
}
}
catch (Exception ex)
{
error(ex);
}
};
continuation[0](null);
return () => { stopped[0] = true; };
},
};
return env;
}
public static HttpStatusCode ConvertStringToHttpStatusCode(string statusCode)
{
var status = 0;
var truncatedStatusCode = statusCode.Length >= 3 ? statusCode.Substring(0, 3) : "";
if (!int.TryParse(truncatedStatusCode, out status)) throw new InvalidCastException("Status code returned by Application was not a valid HTTP status integer");
return (HttpStatusCode)status;
}
private static IDictionary<string, string> ConvertHeadersToDictionary(IEnumerable<KeyValuePair<string, IEnumerable<string>>> httpRequestHeaders)
{
var headers = httpRequestHeaders.ToDictionary(
item => item.Key,
item => string.Join("/r/n", item.Value)
);
return headers;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment