New tests.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / SvcHttpHandler.cs
index 73f2c073ca52ce99228b0a6f1cda402e174a0d41..161b3f476b45c37473abf35b1d2fa69844a6ac3a 100644 (file)
@@ -28,6 +28,8 @@
 //
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
 using System.Web;
 using System.Threading;
 
@@ -38,18 +40,41 @@ using System.ServiceModel.Description;
 
 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)
                {
@@ -63,105 +88,143 @@ namespace System.ServiceModel.Channels {
                        get { return true; }
                }
 
-               public Uri Uri { get; private set; }
+               public ServiceHostBase Host {
+                       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;
                }
 
-               void ApplyConfiguration (ServiceHost host)
+               public void RegisterListener (IChannelListener listener)
                {
-                       foreach (ServiceElement service in ConfigUtil.ServicesSection.Services) {
-                               foreach (ServiceEndpointElement endpoint in service.Endpoints) {
-                                       // FIXME: consider BindingName as well
-                                       ServiceEndpoint se = host.AddServiceEndpoint (
-                                               endpoint.Contract,
-                                               ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration),
-                                               new Uri (path, UriKind.Relative));
-                                       this.Uri = se.Address.Uri;
-                               }
-                               // behaviors
-                               ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors.Find (service.BehaviorConfiguration);
-                               if (behavior != null) {
-                                       foreach (BehaviorExtensionElement bxel in behavior) {
-                                               IServiceBehavior b = null;
-                                               ServiceMetadataPublishingElement meta = bxel as ServiceMetadataPublishingElement;
-                                               if (meta != null) {
-                                                       ServiceMetadataBehavior smb = meta.CreateBehavior () as ServiceMetadataBehavior;
-                                                       smb.HttpGetUrl = request_url;
-                                                       // FIXME: HTTPS as well
-                                                       b = smb;
-                                               }
-                                               if (b != null)
-                                                       host.Description.Behaviors.Add (b);
-                                       }
+                       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 EnsureServiceHost ()
+               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 true
-                       //FIXME: Binding: Get from web.config.
-                       var se = host.AddServiceEndpoint (ContractDescription.GetContract (type).Name,
-                               new BasicHttpBinding (), new Uri (path, UriKind.Relative));
-                       this.Uri = se.Address.Uri;
-#else
-                       ApplyConfiguration (host);
-#endif
+                       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);
                }
        }
 }