{
internal class AspNetReplyChannel : HttpReplyChannel
{
- HttpContext http_context;
AspNetChannelListener<IReplyChannel> listener;
+ List<HttpContext> waiting = new List<HttpContext> ();
+ HttpContext http_context;
+ AutoResetEvent wait;
public AspNetReplyChannel (AspNetChannelListener<IReplyChannel> listener)
: base (listener)
}
}
+ void ShutdownPendingRequests ()
+ {
+ lock (waiting)
+ foreach (HttpContext ctx in waiting)
+ try {
+ listener.HttpHandler.EndRequest (listener, ctx);
+ } catch {
+ }
+ }
+
protected override void OnAbort ()
{
- if (http_context == null)
- return;
- try {
- listener.HttpHandler.EndRequest (listener, http_context);
- } finally {
- http_context = null;
- }
+ ShutdownPendingRequests ();
+ CloseContext ();
}
protected override void OnClose (TimeSpan timeout)
{
base.OnClose (timeout);
- if (http_context == null)
- return;
- try {
- listener.HttpHandler.EndRequest (listener, http_context);
- } finally {
- http_context = null;
- }
+ ShutdownPendingRequests ();
+ CloseContext ();
}
public override bool TryReceiveRequest (TimeSpan timeout, out RequestContext context)
bool TryReceiveRequestCore (TimeSpan timeout, out RequestContext context)
{
context = null;
- if (!WaitForRequest (timeout))
+ if (waiting.Count == 0 && !WaitForRequest (timeout))
+ return false;
+ lock (waiting) {
+ if (waiting.Count > 0) {
+ http_context = waiting [0];
+ waiting.RemoveAt (0);
+ }
+ }
+ if (http_context == null)
+ // Though as long as this instance is used
+ // synchronously, it should not happen.
return false;
Message msg;
var req = http_context.Request;
if (req.HttpMethod == "GET") {
msg = Message.CreateMessage (Encoder.MessageVersion, null);
- msg.Headers.To = req.Url;
} else {
//FIXME: Do above stuff for HttpContext ?
int maxSizeOfHeaders = 0x10000;
}
// FIXME: prop.SuppressEntityBody
+ msg.Headers.To = req.Url;
msg.Properties.Add ("Via", LocalAddress.Uri);
msg.Properties.Add (HttpRequestMessageProperty.Name, CreateRequestProperty (req.HttpMethod, req.Url.Query, req.Headers));
return true;
}
+ /*
public override bool WaitForRequest (TimeSpan timeout)
{
if (http_context == null)
http_context = listener.HttpHandler.WaitForRequest (listener, timeout);
return http_context != null;
}
+ */
+
+ public override bool WaitForRequest (TimeSpan timeout)
+ {
+Console.WriteLine ("Enter AspNetReplyChannel.WaitForRequest");
+ if (wait != null)
+ throw new InvalidOperationException ("Another wait operation is in progress");
+ try {
+ wait = new AutoResetEvent (false);
+ listener.ListenerManager.GetHttpContextAsync (timeout, HttpContextAcquired);
+ if (wait != null) // in case callback is done before WaitOne() here.
+ return wait.WaitOne (timeout, false);
+Console.WriteLine ("Exit AspNetReplyChannel.WaitForRequest");
+ return waiting.Count > 0;
+ } finally {
+ wait = null;
+ }
+ }
+
+ void HttpContextAcquired (HttpContextInfo ctx)
+ {
+Console.WriteLine ("Enter HttpContextAcquired: {0}", ctx != null ? ctx.RequestUrl : null);
+ if (wait == null)
+ throw new InvalidOperationException ("WaitForRequest operation has not started");
+ var sctx = (AspNetHttpContextInfo) ctx;
+ if (State == CommunicationState.Opened && ctx != null)
+ waiting.Add (sctx.Source);
+ var wait_ = wait;
+ wait = null;
+ wait_.Set ();
+Console.WriteLine ("Exit HttpContextAcquired: {0}", ctx != null ? ctx.RequestUrl : null);
+ }
}
}
internal class HttpSimpleChannelListener<TChannel> : HttpChannelListenerBase<TChannel>
where TChannel : class, IChannel
{
- HttpListenerManager<TChannel> httpChannelManager;
-
public HttpSimpleChannelListener (HttpTransportBindingElement source,
BindingContext context)
: base (source, context)
{
}
- public HttpListenerManager<TChannel> ListenerManager {
- get { return httpChannelManager; }
- }
-
object creator_lock = new object ();
protected override TChannel CreateChannel (TimeSpan timeout)
throw new NotSupportedException (String.Format ("Channel type {0} is not supported", typeof (TChannel)));
}
- protected override void OnOpen (TimeSpan timeout)
- {
- base.OnOpen (timeout);
- StartListening (timeout);
- }
-
- protected override void OnAbort ()
- {
- httpChannelManager.Stop (true);
- }
-
- protected override void OnClose (TimeSpan timeout)
+ protected override HttpListenerManager CreateListenerManager ()
{
- if (State == CommunicationState.Closed)
- return;
- base.OnClose (timeout);
- // The channels are kept open when the creator channel listener is closed.
- // http://blogs.msdn.com/drnick/archive/2006/03/22/557642.aspx
- httpChannelManager.Stop (false);
- }
-
- void StartListening (TimeSpan timeout)
- {
- httpChannelManager = new HttpListenerManager<TChannel> (this);
- httpChannelManager.Open (timeout);
+ return new HttpSimpleListenerManager (this);
}
}
{
}
- SvcHttpHandler http_handler;
internal SvcHttpHandler HttpHandler {
- get {
- if (http_handler == null)
- http_handler = SvcHttpHandlerFactory.GetHandlerForListener (this);
- return http_handler;
- }
+ get { return ((AspNetListenerManager) ListenerManager).Source; }
}
protected override TChannel CreateChannel (TimeSpan timeout)
throw new NotSupportedException (String.Format ("Channel type {0} is not supported", typeof (TChannel)));
}
- protected override void OnAbort ()
+ protected override HttpListenerManager CreateListenerManager ()
{
- HttpHandler.UnregisterListener (this);
- }
-
- protected override void OnOpen (TimeSpan timeout)
- {
- base.OnOpen (timeout);
- HttpHandler.RegisterListener (this);
- }
-
- protected override void OnClose (TimeSpan timeout)
- {
- HttpHandler.UnregisterListener (this);
- base.OnClose (timeout);
+ return new AspNetListenerManager (this);
}
}
BindingContext context;
List<TChannel> channels = new List<TChannel> ();
MessageEncoder encoder;
+ HttpListenerManager httpChannelManager;
public HttpChannelListenerBase (HttpTransportBindingElement source,
BindingContext context)
encoder = new TextMessageEncoder (MessageVersion.Default, Encoding.UTF8);
}
+ public HttpListenerManager ListenerManager {
+ get { return httpChannelManager; }
+ }
+
public MessageEncoder MessageEncoder {
get { return encoder; }
}
throw new NotImplementedException ();
}
- protected override void OnAbort ()
+ protected abstract HttpListenerManager CreateListenerManager ();
+
+ protected override void OnOpen (TimeSpan timeout)
{
- OnClose (TimeSpan.Zero);
+ httpChannelManager = CreateListenerManager ();
+ Properties.Add (httpChannelManager);
+ httpChannelManager.Open (timeout);
}
- protected override void OnOpen (TimeSpan timeout)
+ protected override void OnAbort ()
{
+ httpChannelManager.Stop (true);
}
protected override void OnClose (TimeSpan timeout)
{
- DateTime start = DateTime.Now;
- base.OnClose (timeout - (DateTime.Now - start));
+ if (State == CommunicationState.Closed)
+ return;
+ base.OnClose (timeout);
+ // The channels are kept open when the creator channel listener is closed.
+ // http://blogs.msdn.com/drnick/archive/2006/03/22/557642.aspx
+ httpChannelManager.Stop (false);
}
}
}
//
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;
using System.Net;
+using System.Web;
namespace System.ServiceModel.Channels
{
- internal class HttpListenerManager<TChannel> where TChannel : class, IChannel
+ abstract class HttpContextInfo
+ {
+ public abstract NameValueCollection QueryString { get; }
+ public abstract Uri RequestUrl { get; }
+ public abstract string HttpMethod { get; }
+ public abstract void Abort ();
+ }
+
+ class HttpListenerContextInfo : HttpContextInfo
+ {
+ public HttpListenerContextInfo (HttpListenerContext ctx)
+ {
+ this.ctx = ctx;
+ }
+
+ HttpListenerContext ctx;
+
+ public HttpListenerContext Source {
+ get { return ctx; }
+ }
+
+ public override NameValueCollection QueryString {
+ get { return ctx.Request.QueryString; }
+ }
+ public override Uri RequestUrl {
+ get { return ctx.Request.Url; }
+ }
+ public override string HttpMethod {
+ get { return ctx.Request.HttpMethod; }
+ }
+ public override void Abort ()
+ {
+ ctx.Response.Abort ();
+ }
+ }
+
+ class AspNetHttpContextInfo : HttpContextInfo
+ {
+ public AspNetHttpContextInfo (HttpContext ctx)
+ {
+ this.ctx = ctx;
+ }
+
+ HttpContext ctx;
+
+ public HttpContext Source {
+ get { return ctx; }
+ }
+
+ public override NameValueCollection QueryString {
+ get { return ctx.Request.QueryString; }
+ }
+ public override Uri RequestUrl {
+ get { return ctx.Request.Url; }
+ }
+ public override string HttpMethod {
+ get { return ctx.Request.HttpMethod; }
+ }
+
+ public override void Abort ()
+ {
+ ctx.Response.Close ();
+ }
+ }
+
+ internal class HttpSimpleListenerManager : HttpListenerManager
{
static Dictionary<Uri, HttpListener> opened_listeners;
- static Dictionary<Uri, List<HttpSimpleChannelListener<TChannel>>> registered_channels;
- HttpSimpleChannelListener<TChannel> channel_listener;
HttpListener http_listener;
- MetadataPublishingInfo mex_info;
- HttpGetWsdl wsdl_instance;
- AutoResetEvent wait_http_ctx = new AutoResetEvent (false);
- List<HttpListenerContext> pending = new List<HttpListenerContext> ();
- static HttpListenerManager ()
+ static HttpSimpleListenerManager ()
{
opened_listeners = new Dictionary<Uri, HttpListener> ();
- registered_channels = new Dictionary<Uri, List<HttpSimpleChannelListener<TChannel>>> ();
}
- public HttpListenerManager (HttpSimpleChannelListener<TChannel> channelListener)
+ public HttpSimpleListenerManager (IChannelListener channelListener)
+ : base (channelListener)
{
- this.channel_listener = channelListener;
- // FIXME: this cast should not be required, but current JIT somehow causes an internal error.
- mex_info = ((IChannelListener) channelListener).GetProperty<MetadataPublishingInfo> ();
- wsdl_instance = mex_info != null ? mex_info.Instance : null;
}
- public void Open (TimeSpan timeout)
+ protected override void OnRegister (IChannelListener channelListener, TimeSpan timeout)
{
lock (opened_listeners) {
- if (!opened_listeners.ContainsKey (channel_listener.Uri)) {
+ if (!opened_listeners.ContainsKey (channelListener.Uri)) {
HttpListener listener = new HttpListener ();
- string uriString = channel_listener.Uri.ToString ();
+ string uriString = channelListener.Uri.ToString ();
if (!uriString.EndsWith ("/", StringComparison.Ordinal))
uriString += "/";
listener.Prefixes.Add (uriString);
listener.Start ();
- opened_listeners [channel_listener.Uri] = listener;
- List<HttpSimpleChannelListener<TChannel>> registeredList = new List<HttpSimpleChannelListener<TChannel>> ();
- registered_channels [channel_listener.Uri] = registeredList;
+ opened_listeners [channelListener.Uri] = listener;
}
- http_listener = opened_listeners [channel_listener.Uri];
- registered_channels [channel_listener.Uri].Add (channel_listener);
-
- // make sure to fill wsdl_instance among other
- // listeners. It is somewhat hacky way, but
- // otherwise there is no assured way to do it.
- if (wsdl_instance != null) {
- foreach (var l in registered_channels [channel_listener.Uri])
- l.ListenerManager.wsdl_instance = wsdl_instance;
- }
+ http_listener = opened_listeners [channelListener.Uri];
}
}
- public void Stop (bool abort)
+ protected override void OnUnregister (IChannelListener listener, bool abort)
{
lock (opened_listeners) {
if (http_listener == null)
return;
- List<HttpSimpleChannelListener<TChannel>> channelsList = registered_channels [channel_listener.Uri];
- channelsList.Remove (channel_listener);
- if (channelsList.Count == 0) {
- if (http_listener.IsListening) {
- if (abort)
- http_listener.Abort ();
- else
- http_listener.Close ();
- }
- ((IDisposable) http_listener).Dispose ();
-
- opened_listeners.Remove (channel_listener.Uri);
- try {
- foreach (var ctx in pending)
- ctx.Response.Abort ();
- } catch (Exception ex) {
- // FIXME: log it
- Console.WriteLine ("error during HTTP channel listener shutdown: " + ex);
- }
- http_listener = null;
+ if (http_listener.IsListening) {
+ if (abort)
+ http_listener.Abort ();
+ else
+ http_listener.Close ();
}
+ ((IDisposable) http_listener).Dispose ();
+
+ opened_listeners.Remove (listener.Uri);
+ }
+
+ http_listener = null;
+ }
+
+ public override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
+ {
+ http_listener.BeginGetContext (delegate (IAsyncResult result) {
+ var hctx = http_listener.EndGetContext (result);
+ contextReceivedCallback (new HttpListenerContextInfo (hctx));
+ }, null);
+ }
+ }
+
+ internal class AspNetListenerManager : HttpListenerManager
+ {
+ SvcHttpHandler http_handler;
+
+ public AspNetListenerManager (IChannelListener channelListener)
+ : base (channelListener)
+ {
+ http_handler = SvcHttpHandlerFactory.GetHandlerForListener (channelListener);
+ }
+
+ public SvcHttpHandler Source {
+ get { return http_handler; }
+ }
+
+ protected override void OnRegister (IChannelListener channelListener, TimeSpan timeout)
+ {
+ http_handler.RegisterListener (channelListener);
+ }
+
+ protected override void OnUnregister (IChannelListener listener, bool abort)
+ {
+ http_handler.UnregisterListener (listener);
+ }
+
+ Func<IChannelListener,HttpContext> wait_delegate;
+
+ public override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
+ {
+ if (wait_delegate == null)
+ wait_delegate = new Func<IChannelListener,HttpContext> (http_handler.WaitForRequest);
+ wait_delegate.BeginInvoke (listener, delegate (IAsyncResult result) {
+ var ctx = wait_delegate.EndInvoke (result);
+ contextReceivedCallback (ctx != null ? new AspNetHttpContextInfo (ctx) : null);
+ }, null);
+ }
+ }
+
+ internal abstract class HttpListenerManager
+ {
+ static Dictionary<Uri, List<IChannelListener>> registered_channels;
+ IChannelListener channel_listener;
+ MetadataPublishingInfo mex_info;
+ HttpGetWsdl wsdl_instance;
+ AutoResetEvent wait_http_ctx = new AutoResetEvent (false);
+ List<HttpContextInfo> pending = new List<HttpContextInfo> ();
+
+public MetadataPublishingInfo MexInfo { get { return mex_info; } }
+
+ static HttpListenerManager ()
+ {
+ registered_channels = new Dictionary<Uri, List<IChannelListener>> ();
+ }
+
+ protected HttpListenerManager (IChannelListener channelListener)
+ {
+ this.channel_listener = channelListener;
+ // FIXME: this cast should not be required, but current JIT somehow causes an internal error.
+ mex_info = ((IChannelListener) channelListener).GetProperty<MetadataPublishingInfo> ();
+ wsdl_instance = mex_info != null ? mex_info.Instance : null;
+ }
+
+ public void Open (TimeSpan timeout)
+ {
+ if (!registered_channels.ContainsKey (channel_listener.Uri))
+ registered_channels [channel_listener.Uri] = new List<IChannelListener> ();
+ OnRegister (channel_listener, timeout);
+ registered_channels [channel_listener.Uri].Add (channel_listener);
+
+ // make sure to fill wsdl_instance among other
+ // listeners. It is somewhat hacky way, but
+ // otherwise there is no assured way to do it.
+ if (wsdl_instance != null) {
+ foreach (var l in registered_channels [channel_listener.Uri])
+ l.GetProperty<HttpListenerManager> ().wsdl_instance = wsdl_instance;
}
}
+ public void Stop (bool abort)
+ {
+ List<IChannelListener> channelsList = registered_channels [channel_listener.Uri];
+ channelsList.Remove (channel_listener);
+
+ try {
+ foreach (var ctx in pending)
+ ctx.Abort ();
+ } catch (Exception ex) {
+ // FIXME: log it
+ Console.WriteLine ("error during HTTP channel listener shutdown: " + ex);
+ }
+
+ if (channelsList.Count == 0)
+ OnUnregister (channel_listener, abort);
+ }
+
+ protected abstract void OnRegister (IChannelListener listener, TimeSpan timeout);
+ protected abstract void OnUnregister (IChannelListener listener, bool abort);
+
// Do not directly handle retrieved HttpListenerContexts when
// the listener received ones.
// Instead, iterate every listeners to find the most-likely-
// If the listener is not requesting a context right now, then
// store it in *each* listener's queue.
- public void GetHttpContextAsync (Action<HttpListenerContext> callback)
+ public void GetHttpContextAsync (TimeSpan timeout, Action<HttpContextInfo> callback)
{
lock (pending) {
foreach (var pctx in pending) {
}
}
}
- HttpListenerContext ctx;
- http_listener.BeginGetContext (delegate (IAsyncResult result) {
- ctx = http_listener.EndGetContext (result);
- DispatchHttpListenerContext (ctx);
- }, null);
- wait_http_ctx.WaitOne ();
+ KickContextReceiver (channel_listener, DispatchHttpListenerContext);
+ wait_http_ctx.WaitOne (timeout);
lock (pending) {
- ctx = pending.Count > 0 ? pending [0] : null;
+ HttpContextInfo ctx = pending.Count > 0 ? pending [0] : null;
if (ctx != null)
pending.Remove (ctx);
+ callback (ctx);
}
- callback (ctx);
}
- void DispatchHttpListenerContext (HttpListenerContext ctx)
+ public abstract void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceiverCallback);
+
+ void DispatchHttpListenerContext (HttpContextInfo ctx)
{
if (wsdl_instance == null) {
pending.Add (ctx);
return;
}
foreach (var l in registered_channels [channel_listener.Uri]) {
- var lm = l.ListenerManager;
+ var lm = l.GetProperty<HttpListenerManager> ();
if (lm.FilterHttpContext (ctx)) {
lm.pending.Add (ctx);
lm.wait_http_ctx.Set ();
wait_http_ctx.Set ();
}
- bool FilterHttpContext (HttpListenerContext ctx)
+ internal bool FilterHttpContext (HttpContextInfo ctx)
{
- if (ctx.Request.HttpMethod.ToUpper () != "GET")
+ if (ctx.HttpMethod.ToUpper () != "GET")
return mex_info == null;
if (wsdl_instance == null)
var cmpflag = UriComponents.HttpRequestUrl ^ UriComponents.Query;
var fmtflag = UriFormat.SafeUnescaped;
- if (Uri.Compare (ctx.Request.Url, wsdl_instance.WsdlUrl, cmpflag, fmtflag, StringComparison.Ordinal) == 0) {
+ if (wsdl_instance.WsdlUrl != null && Uri.Compare (ctx.RequestUrl, wsdl_instance.WsdlUrl, cmpflag, fmtflag, StringComparison.Ordinal) == 0) {
if (mex_info == null)
return false; // Do not handle this at normal dispatcher.
- if (ctx.Request.QueryString [null] == "wsdl")
+ if (ctx.QueryString [null] == "wsdl")
return mex_info.IsMex; // wsdl dispatcher should handle this.
if (!wsdl_instance.HelpUrl.Equals (wsdl_instance.WsdlUrl))
return true; // in case help URL is not equivalent to WSDL URL, it anyways returns WSDL regardless of ?wsdl existence.
}
- if (Uri.Compare (ctx.Request.Url, wsdl_instance.HelpUrl, cmpflag, fmtflag, StringComparison.Ordinal) == 0) {
+ if (wsdl_instance.HelpUrl != null && Uri.Compare (ctx.RequestUrl, wsdl_instance.HelpUrl, cmpflag, fmtflag, StringComparison.Ordinal) == 0) {
// Do not handle this at normal dispatcher.
// Do return true otherwise, even if it is with "?wsdl".
// (It must be handled above if applicable.)