//
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Web;
using System.Threading;
namespace System.ServiceModel.Channels {
+ internal class WcfListenerInfo
+ {
+ public WcfListenerInfo ()
+ {
+ Pending = new List<HttpContext> ();
+ ProcessRequestHandles = new List<ManualResetEvent> ();
+ }
+
+ public IChannelListener Listener { get; set; }
+ public List<ManualResetEvent> ProcessRequestHandles { get; private set; }
+ public List<HttpContext> Pending { get; private set; }
+ }
+
+ internal class WcfListenerInfoCollection : KeyedCollection<IChannelListener,WcfListenerInfo>
+ {
+ protected override IChannelListener GetKeyForItem (WcfListenerInfo info)
+ {
+ return info.Listener;
+ }
+ }
+
internal class SvcHttpHandler : IHttpHandler
{
+ internal static SvcHttpHandler Current;
+
+ static object type_lock = new object ();
+
Type type;
Type factory_type;
string path;
- Uri request_url;
ServiceHostBase host;
- Queue<HttpContext> pending = new Queue<HttpContext> ();
- bool closing;
-
- AutoResetEvent wait = new AutoResetEvent (false);
- AutoResetEvent listening = new AutoResetEvent (false);
+ WcfListenerInfoCollection listeners = new WcfListenerInfoCollection ();
+ Dictionary<HttpContext,AutoResetEvent> wcf_wait_handles = new Dictionary<HttpContext,AutoResetEvent> ();
+ AutoResetEvent wait_for_request_handle = new AutoResetEvent (false);
+ int close_state;
public SvcHttpHandler (Type type, Type factoryType, string path)
{
get { return host; }
}
- public HttpContext WaitForRequest (TimeSpan timeout)
+ public HttpContext WaitForRequest (IChannelListener listener)
{
- DateTime start = DateTime.Now;
- lock (pending) {
- if (pending.Count > 0) {
- var ctx = pending.Dequeue ();
- if (ctx.AllErrors != null && ctx.AllErrors.Length > 0)
- return WaitForRequest (timeout - (DateTime.Now - start));
- return ctx;
- }
+ if (close_state > 0)
+ return null;
+
+ var wait = new ManualResetEvent (false);
+ var info = listeners [listener];
+ if (info.Pending.Count == 0) {
+ info.ProcessRequestHandles.Add (wait);
+ wait_for_request_handle.Set ();
+ wait.WaitOne ();
+ info.ProcessRequestHandles.Remove (wait);
}
- return wait.WaitOne (timeout - (DateTime.Now - start), false) && !closing ?
- WaitForRequest (timeout - (DateTime.Now - start)) : null;
+ var ctx = listeners [listener].Pending [0];
+ listeners [listener].Pending.RemoveAt (0);
+ return ctx;
+ }
+
+ IChannelListener FindBestMatchListener (HttpContext ctx)
+ {
+ var actx = new AspNetHttpContextInfo (ctx);
+
+ // Select the best-match listener.
+ IChannelListener best = null;
+ string rel = null;
+ foreach (var li in listeners) {
+ var l = li.Listener;
+ if (!l.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
+ continue;
+ if (l.Uri.Equals (ctx.Request.Url)) {
+ best = l;
+ break;
+ }
+ }
+ // FIXME: the matching must be better-considered.
+ foreach (var li in listeners) {
+ var l = li.Listener;
+ if (!l.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
+ continue;
+ if (!ctx.Request.Url.ToString ().StartsWith (l.Uri.ToString (), StringComparison.Ordinal))
+ continue;
+ if (best == null)
+ best = l;
+ }
+ if (best != null)
+ return best;
+ throw new InvalidOperationException (String.Format ("The argument HTTP context did not match any of the registered listener manager (could be mismatch in URL, method etc.) {0}", ctx.Request.Url));
+/*
+ var actx = new AspNetHttpContextInfo (ctx);
+ foreach (var i in listeners)
+ if (i.Listener.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
+ return i.Listener;
+ throw new InvalidOperationException ();
+*/
}
public void ProcessRequest (HttpContext context)
{
- request_url = context.Request.Url;
EnsureServiceHost ();
- pending.Enqueue (context);
- wait.Set ();
+ var wait = new AutoResetEvent (false);
+ var l = FindBestMatchListener (context);
+ var i = listeners [l];
+ lock (i) {
+ i.Pending.Add (context);
+ wcf_wait_handles [context] = wait;
+ if (i.ProcessRequestHandles.Count > 0)
+ i.ProcessRequestHandles [0].Set ();
+ }
- listening.WaitOne ();
+ wait.WaitOne ();
}
- public void EndRequest (HttpContext context)
+ public void EndRequest (IChannelListener listener, HttpContext context)
{
- listening.Set ();
+ var wait = wcf_wait_handles [context];
+ wcf_wait_handles.Remove (context);
+ wait.Set ();
}
+ // called from SvcHttpHandlerFactory's remove callback (i.e.
+ // unloading asp.net). It closes ServiceHost, then the host
+ // in turn closes the listener and the channels it opened.
+ // The channel listener calls CloseServiceChannel() to stop
+ // accepting further requests on its shutdown.
public void Close ()
{
- closing = true;
- listening.Set ();
- wait.Set ();
host.Close ();
host = null;
- closing = false;
+ }
+
+ public void RegisterListener (IChannelListener listener)
+ {
+ lock (type_lock)
+ listeners.Add (new WcfListenerInfo () {Listener = listener});
+ }
+
+ public void UnregisterListener (IChannelListener listener)
+ {
+ listeners.Remove (listener);
}
void EnsureServiceHost ()
+ {
+ lock (type_lock) {
+ Current = this;
+ try {
+ EnsureServiceHostCore ();
+ } finally {
+ Current = null;
+ }
+ }
+ }
+
+ void EnsureServiceHostCore ()
{
if (host != null)
return;
//ServiceHost for this not created yet
- var baseUri = new Uri (HttpContext.Current.Request.Url.GetLeftPart (UriPartial.Path));
+ var baseUri = new Uri (new Uri (HttpContext.Current.Request.Url.GetLeftPart (UriPartial.Authority)), path);
if (factory_type != null) {
host = ((ServiceHostFactory) Activator.CreateInstance (factory_type)).CreateServiceHost (type, new Uri [] {baseUri});
}
else
host = new ServiceHost (type, baseUri);
-
- /*
- if (host.Description.Endpoints.Count == 0)
- //FIXME: Binding: Get from web.config.
- host.AddServiceEndpoint (ContractDescription.GetContract (type).Name,
- new BasicHttpBinding (), new Uri (path, UriKind.Relative));
-
- var c = host.BaseAddresses;
- */
+ host.Extensions.Add (new VirtualPathExtension (baseUri.AbsolutePath));
host.Open ();
- //listening.WaitOne ();
+ // Not precise, but it needs some wait time to have all channels start requesting. And it is somehow required.
+ Thread.Sleep (500);
}
}
}