5 // Ankit Jain <jankit@novell.com>
6 // Atsushi Enomoto <atsushi@ximian.com>
8 // Copyright (C) 2006,2009 Novell, Inc. http://www.novell.com
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
34 using System.Threading;
36 using System.ServiceModel;
37 using System.ServiceModel.Activation;
38 using System.ServiceModel.Configuration;
39 using System.ServiceModel.Description;
41 namespace System.ServiceModel.Channels {
43 internal class WcfListenerInfo
45 public WcfListenerInfo ()
47 Pending = new List<HttpContext> ();
48 ProcessRequestHandles = new List<ManualResetEvent> ();
51 public IChannelListener Listener { get; set; }
52 public List<ManualResetEvent> ProcessRequestHandles { get; private set; }
53 public List<HttpContext> Pending { get; private set; }
56 internal class WcfListenerInfoCollection : KeyedCollection<IChannelListener,WcfListenerInfo>
58 protected override IChannelListener GetKeyForItem (WcfListenerInfo info)
64 internal class SvcHttpHandler : IHttpHandler
66 internal static SvcHttpHandler Current;
68 static object type_lock = new object ();
74 WcfListenerInfoCollection listeners = new WcfListenerInfoCollection ();
75 Dictionary<HttpContext,AutoResetEvent> wcf_wait_handles = new Dictionary<HttpContext,AutoResetEvent> ();
76 AutoResetEvent wait_for_request_handle = new AutoResetEvent (false);
79 public SvcHttpHandler (Type type, Type factoryType, string path)
82 this.factory_type = factoryType;
86 public bool IsReusable
91 public ServiceHostBase Host {
95 public HttpContext WaitForRequest (IChannelListener listener)
100 var wait = new ManualResetEvent (false);
101 var info = listeners [listener];
102 if (info.Pending.Count == 0) {
103 info.ProcessRequestHandles.Add (wait);
104 wait_for_request_handle.Set ();
106 info.ProcessRequestHandles.Remove (wait);
109 var ctx = listeners [listener].Pending [0];
110 listeners [listener].Pending.RemoveAt (0);
114 IChannelListener FindBestMatchListener (HttpContext ctx)
116 var actx = new AspNetHttpContextInfo (ctx);
118 // Select the best-match listener.
119 IChannelListener best = null;
121 foreach (var li in listeners) {
123 if (!l.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
125 if (l.Uri.Equals (ctx.Request.Url)) {
130 // FIXME: the matching must be better-considered.
131 foreach (var li in listeners) {
133 if (!l.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
135 if (!ctx.Request.Url.ToString ().StartsWith (l.Uri.ToString (), StringComparison.Ordinal))
142 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));
144 var actx = new AspNetHttpContextInfo (ctx);
145 foreach (var i in listeners)
146 if (i.Listener.GetProperty<HttpListenerManager> ().FilterHttpContext (actx))
148 throw new InvalidOperationException ();
152 public void ProcessRequest (HttpContext context)
154 EnsureServiceHost ();
156 var wait = new AutoResetEvent (false);
157 var l = FindBestMatchListener (context);
158 var i = listeners [l];
160 i.Pending.Add (context);
161 wcf_wait_handles [context] = wait;
162 if (i.ProcessRequestHandles.Count > 0)
163 i.ProcessRequestHandles [0].Set ();
169 public void EndRequest (IChannelListener listener, HttpContext context)
171 var wait = wcf_wait_handles [context];
172 wcf_wait_handles.Remove (context);
176 // called from SvcHttpHandlerFactory's remove callback (i.e.
177 // unloading asp.net). It closes ServiceHost, then the host
178 // in turn closes the listener and the channels it opened.
179 // The channel listener calls CloseServiceChannel() to stop
180 // accepting further requests on its shutdown.
187 public void RegisterListener (IChannelListener listener)
190 listeners.Add (new WcfListenerInfo () {Listener = listener});
193 public void UnregisterListener (IChannelListener listener)
195 listeners.Remove (listener);
198 void EnsureServiceHost ()
203 EnsureServiceHostCore ();
210 void EnsureServiceHostCore ()
215 //ServiceHost for this not created yet
216 var baseUri = new Uri (new Uri (HttpContext.Current.Request.Url.GetLeftPart (UriPartial.Authority)), path);
217 if (factory_type != null) {
218 host = ((ServiceHostFactory) Activator.CreateInstance (factory_type)).CreateServiceHost (type, new Uri [] {baseUri});
221 host = new ServiceHost (type, baseUri);
222 host.Extensions.Add (new VirtualPathExtension (baseUri.AbsolutePath));
226 // Not precise, but it needs some wait time to have all channels start requesting. And it is somehow required.