using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.IdentityModel.Claims; using System.IO; using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Web; using System.Text; using System.Threading.Tasks; using Nancy; using Nancy.Bootstrapper; using Nancy.Cookies; using Nancy.Extensions; using Nancy.IO; using NLog; using Sony.Filtr.API.Nancy.Config; namespace Sony.Filtr.API.WCF { /// /// Host for running Nancy ontop of WCF. /// /// [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceContract] public class MyNancyWcfGenericService : IDisposable { private readonly INancyEngine engine; private readonly INancyBootstrapper bootstrapper; /// /// Initializes a new instance of the class with a default bootstrapper. /// /// public MyNancyWcfGenericService() : this(NancyBootstrapperLocator.Bootstrapper) { } /// /// Initializes a new instance of the class, with the provided . /// /// /// An instance, that should be used to handle the requests. public MyNancyWcfGenericService(INancyBootstrapper bootstrapper) { this.bootstrapper = bootstrapper; bootstrapper.Initialise(); this.engine = bootstrapper.GetEngine(); } /// /// Handle an incoming request with Nancy. /// /// /// The body of the incoming request. /// /// A instance. /// [WebInvoke(Method = "*", UriTemplate = "*")] public Message HandleRequests(Stream requestBody) { _logger.Debug("MyNancyWcfGenericService handleRequests"); WebOperationContext current = WebOperationContext.Current; NancyContext nancyContext = NancyEngineExtensions.HandleRequest(this.engine, CreateNancyRequestFromIncomingWebRequest(current.IncomingRequest, requestBody, OperationContext.Current)); SetNancyResponseToOutgoingWebResponse(current.OutgoingResponse, nancyContext.Response); _logger.Debug("Kinda done with MyNancyWcfGenericService handleRequests"); return current.CreateStreamResponse((Action)(stream => { nancyContext.Response.Contents(stream); nancyContext.Dispose(); }), nancyContext.Response.ContentType ?? string.Empty); } public void Dispose() { this.bootstrapper.Dispose(); } private static Request CreateNancyRequestFromIncomingWebRequest(IncomingWebRequestContext webRequest, Stream requestBody, OperationContext context) { var logger = NLog.LogManager.GetLogger("Argh"); string ip = (string)null; object obj; if (OperationContext.Current.IncomingMessageProperties.TryGetValue(RemoteEndpointMessageProperty.Name, out obj)) ip = ((RemoteEndpointMessageProperty)obj).Address; var relativeUri = GetNancyRelativeUrlFromBaseUrl(webRequest, logger); long expectedRequestLength = GetExpectedRequestLength(CollectionExtensions.ToDictionary((NameValueCollection)webRequest.Headers)); Url url = new Url() { BasePath = webRequest.UriTemplateMatch.BaseUri.AbsolutePath, Scheme = webRequest.UriTemplateMatch.RequestUri.Scheme, HostName = webRequest.UriTemplateMatch.BaseUri.Host, Port = webRequest.UriTemplateMatch.RequestUri.IsDefaultPort ? new int?() : new int?(webRequest.UriTemplateMatch.RequestUri.Port), Path = "/" + relativeUri, Query = webRequest.UriTemplateMatch.RequestUri.Query }; logger.Debug("Url.BasePath" + url.BasePath); logger.Debug("Url.Scheme" + url.Scheme); logger.Debug("Url.HostName" + url.HostName); logger.Debug("Url.Port" + url.Port); logger.Debug("Url.Path" + url.Path); logger.Debug("Url.Query" + url.Query); byte[] certificate = (byte[])null; if (context.ServiceSecurityContext != null && context.ServiceSecurityContext.AuthorizationContext.ClaimSets.Count > 0) { X509CertificateClaimSet certificateClaimSet = Enumerable.FirstOrDefault((IEnumerable)context.ServiceSecurityContext.AuthorizationContext.ClaimSets, (Func)(c => c is X509CertificateClaimSet)) as X509CertificateClaimSet; if (certificateClaimSet != null) certificate = certificateClaimSet.X509Certificate.RawData; } logger.Debug("Kinda done with CreateNancyRequestFromIncomingWebRequest"); logger.Debug("CreateNancyRequestFromIncomingWebRequest says the url is" + url); return new Request(webRequest.Method, url, RequestStream.FromStream(requestBody, expectedRequestLength, false), CollectionExtensions.ToDictionary((NameValueCollection)webRequest.Headers), ip, certificate); } private static Uri GetNancyRelativeUrlFromBaseUrl(IncomingWebRequestContext webRequest, Logger logger) { Uri uri1 = GetUrlAndPathComponents(webRequest.UriTemplateMatch.BaseUri); if (!uri1.OriginalString.EndsWith("/")) uri1 = new Uri(uri1.OriginalString + "/"); logger.Debug("CreateNancyRequestFromIncomingWebRequest uri1 " + uri1); var uriBuilder = new UriBuilder(uri1); uriBuilder.Host = webRequest.UriTemplateMatch.RequestUri.Host; Uri uri2 = uriBuilder.Uri.MakeRelativeUri(GetUrlAndPathComponents(webRequest.UriTemplateMatch.RequestUri)); logger.Debug("CreateNancyRequestFromIncomingWebRequest uri2 " + uri2); return uri2; } private static void PrintLogValues(Logger logger, IncomingWebRequestContext webRequest) { logger.Debug("PrintLogValues:BasePath: " + webRequest.UriTemplateMatch.BaseUri.AbsolutePath); logger.Debug("PrintLogValues:Scheme: " + webRequest.UriTemplateMatch.RequestUri.Scheme); logger.Debug("PrintLogValues:HostName: " + webRequest.UriTemplateMatch.BaseUri.Host); logger.Debug("PrintLogValues:Port: " + webRequest.UriTemplateMatch.RequestUri.Port); logger.Debug("PrintLogValues:Path: " + webRequest.UriTemplateMatch.BaseUri.AbsolutePath); logger.Debug("PrintLogValues:Query: " + webRequest.UriTemplateMatch.RequestUri.Query); } private static long GetExpectedRequestLength(IDictionary> incomingHeaders) { if (incomingHeaders == null || !incomingHeaders.ContainsKey("Content-Length")) return 0; string s = Enumerable.SingleOrDefault(incomingHeaders["Content-Length"]); long result; if (s == null || !long.TryParse(s, NumberStyles.Any, (IFormatProvider)CultureInfo.InvariantCulture, out result)) return 0; return result; } private static Uri GetUrlAndPathComponents(Uri uri) { return new Uri(uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped)); } private static void SetNancyResponseToOutgoingWebResponse(OutgoingWebResponseContext webResponse, Response nancyResponse) { SetHttpResponseHeaders(webResponse, nancyResponse); if (nancyResponse.ContentType != null) webResponse.ContentType = nancyResponse.ContentType; if (nancyResponse.ReasonPhrase != null) webResponse.StatusDescription = nancyResponse.ReasonPhrase; webResponse.StatusCode = (System.Net.HttpStatusCode)nancyResponse.StatusCode; } private static void SetHttpResponseHeaders(OutgoingWebResponseContext context, Response response) { foreach (KeyValuePair keyValuePair in (IEnumerable>)response.Headers) context.Headers.Add(keyValuePair.Key, keyValuePair.Value); foreach (INancyCookie nancyCookie in (IEnumerable)response.Cookies) context.Headers.Add("Set-Cookie", nancyCookie.ToString()); } } }