+2010-07-05 Atsushi Enomoto <atsushi@ximian.com>
+
+ * HttpStandaloneReplyChannel.cs
+ HttpContextInfo.cs
+ HttpListenerManager.cs
+ HttpStandaloneRequestContext.cs
+ HttpListenerManagerTable.cs : revert previous changes, regression
+ on wsdl support.
+
2010-07-02 Atsushi Enomoto <atsushi@ximian.com>
* HttpStandaloneReplyChannel.cs, HttpStandaloneRequestContext.cs:
using System.Collections.Specialized;
using System.IO;
using System.Net;
-using System.Security.Principal;
using System.ServiceModel;
using System.Text;
using System.Threading;
-using System.Web;
namespace System.ServiceModel.Channels.Http
{
ctx.Response.StatusCode = 401;
}
}
-
- 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 ();
- }
-
- public override string User {
- get { return ctx.User != null ? ((GenericIdentity) ctx.User.Identity).Name : null; }
- }
-
- // FIXME: how to acquire this?
- public override string Password {
- get { return null; }
- }
-
- public override void ReturnUnauthorized ()
- {
- ctx.Response.StatusCode = 401;
- }
- }
}
namespace System.ServiceModel.Channels.Http
{
- internal abstract class HttpListenerManager
+ internal class HttpListenerManager
{
- protected HttpListenerManager ()
- {
- Entries = new List<HttpChannelListenerEntry> ();
- }
-
- public List<HttpChannelListenerEntry> Entries { get; private set; }
-
- public abstract void RegisterListener (ChannelDispatcher channel, TimeSpan timeout);
- public abstract void UnregisterListener (ChannelDispatcher channel, TimeSpan timeout);
-
- protected void RegisterListenerCommon (ChannelDispatcher channel, TimeSpan timeout)
- {
- Entries.Add (new HttpChannelListenerEntry (channel, new AutoResetEvent (false)));
-
- Entries.Sort (HttpChannelListenerEntry.CompareEntries);
- }
-
- protected void UnregisterListenerCommon (ChannelDispatcher channel, TimeSpan timeout)
- {
- var entry = Entries.First (e => e.ChannelDispatcher == channel);
- Entries.Remove (entry);
-
- entry.WaitHandle.Set (); // make sure to finish pending requests.
- }
-
- public void ProcessNewContext (HttpContextInfo ctxi)
- {
- var ce = SelectChannel (ctxi);
- if (ce == null)
- throw new InvalidOperationException ("HttpListenerContext does not match any of the registered channels");
- ce.ContextQueue.Enqueue (ctxi);
- ce.WaitHandle.Set ();
- }
-
- HttpChannelListenerEntry SelectChannel (HttpContextInfo ctx)
- {
- foreach (var e in Entries)
- if (e.FilterHttpContext (ctx))
- return e;
- return null;
- }
-
- public bool TryDequeueRequest (ChannelDispatcher channel, TimeSpan timeout, out HttpContextInfo context)
- {
- DateTime start = DateTime.Now;
-
- context = null;
- var ce = Entries.First (e => e.ChannelDispatcher == channel);
- lock (ce.RetrieverLock) {
- var q = ce.ContextQueue;
- if (q.Count == 0) {
- bool ret = ce.WaitHandle.WaitOne (timeout);
- return ret && TryDequeueRequest (channel, timeout - (DateTime.Now - start), out context); // recurse, am lazy :/
- }
- context = q.Dequeue ();
- return true;
- }
- }
- }
-
- internal class HttpStandaloneListenerManager : HttpListenerManager
- {
- public HttpStandaloneListenerManager (Uri uri)
+ public HttpListenerManager (Uri uri)
{
var l = new HttpListener ();
}
HttpListener listener;
+ List<HttpChannelListenerEntry> entries = new List<HttpChannelListenerEntry> ();
Thread loop;
// FIXME: use timeout
- public override void RegisterListener (ChannelDispatcher channel, TimeSpan timeout)
+ public void RegisterListener (ChannelDispatcher channel, TimeSpan timeout)
{
- RegisterListenerCommon (channel, timeout);
+ entries.Add (new HttpChannelListenerEntry (channel, new AutoResetEvent (false)));
+
+ entries.Sort (HttpChannelListenerEntry.CompareEntries);
- if (Entries.Count != 1)
+ if (entries.Count != 1)
return;
// Start here. It is shared between channel listeners
}
// FIXME: use timeout
- public override void UnregisterListener (ChannelDispatcher channel, TimeSpan timeout)
+ public void UnregisterListener (ChannelDispatcher channel, TimeSpan timeout)
{
- UnregisterListenerCommon (channel, timeout);
+ var entry = entries.First (e => e.ChannelDispatcher == channel);
+ entries.Remove (entry);
+
+ entry.WaitHandle.Set (); // make sure to finish pending requests.
// stop the server if there is no more registered listener.
- if (Entries.Count > 0)
+ if (entries.Count > 0)
return;
#if true
{
if (ctx == null)
return;
- ProcessNewContext (new HttpStandaloneContextInfo (ctx));
- }
- }
- internal class AspNetHttpListenerManager : HttpListenerManager
- {
- public AspNetHttpListenerManager (Uri uri)
- {
+ var ctxi = new HttpStandaloneContextInfo (ctx);
+ var ce = SelectChannel (ctxi);
+ if (ce == null)
+ throw new InvalidOperationException ("HttpListenerContext does not match any of the registered channels");
+ ce.ContextQueue.Enqueue (ctxi);
+ ce.WaitHandle.Set ();
}
- public override void RegisterListener (ChannelDispatcher channel, TimeSpan timeout)
+ HttpChannelListenerEntry SelectChannel (HttpContextInfo ctx)
{
- RegisterListenerCommon (channel, timeout);
+ foreach (var e in entries)
+ if (e.FilterHttpContext (ctx))
+ return e;
+ return null;
}
- public override void UnregisterListener (ChannelDispatcher channel, TimeSpan timeout)
+ public bool TryDequeueRequest (ChannelDispatcher channel, TimeSpan timeout, out HttpContextInfo context)
{
- UnregisterListenerCommon (channel, timeout);
+ DateTime start = DateTime.Now;
+
+ context = null;
+ var ce = entries.First (e => e.ChannelDispatcher == channel);
+ lock (ce.RetrieverLock) {
+ var q = ce.ContextQueue;
+ if (q.Count == 0) {
+ bool ret = ce.WaitHandle.WaitOne (timeout);
+ return ret && TryDequeueRequest (channel, timeout - (DateTime.Now - start), out context); // recurse, am lazy :/
+ }
+ context = q.Dequeue ();
+ return true;
+ }
}
}
}
{
var m = listeners.FirstOrDefault (p => p.Key.Equals (uri)).Value;
if (m == null) {
- if (ServiceHostingEnvironment.InAspNet)
- m = new AspNetHttpListenerManager (uri);
- else
- m = new HttpStandaloneListenerManager (uri);
+ m = new HttpListenerManager (uri);
listeners [uri] = m;
}
return m;
internal class HttpStandaloneReplyChannel : HttpReplyChannel
{
HttpStandaloneChannelListener<IReplyChannel> source;
+ RequestContext reqctx;
public HttpStandaloneReplyChannel (HttpStandaloneChannelListener<IReplyChannel> listener)
: base (listener)
this.source = listener;
}
- protected override RequestContext CreateRequestContext (HttpContextInfo ctxi, Message msg)
+ protected override void OnAbort ()
{
- var ctx = ((HttpStandaloneContextInfo) ctxi).Source;
- return new HttpStandaloneRequestContext (this, msg, ctx);
+ AbortConnections (TimeSpan.Zero);
+ base.OnAbort (); // FIXME: remove it. The base is wrong. But it is somehow required to not block some tests.
}
- protected override Message CreatePostMessage (HttpContextInfo ctxi)
+ public override bool CancelAsync (TimeSpan timeout)
{
- var ctx = ((HttpStandaloneContextInfo) ctxi).Source;
+ AbortConnections (timeout);
+ // FIXME: this wait is sort of hack (because it should not be required), but without it some tests are blocked.
+ // This hack even had better be moved to base.CancelAsync().
+ if (CurrentAsyncResult != null)
+ CurrentAsyncResult.AsyncWaitHandle.WaitOne (TimeSpan.FromMilliseconds (300));
+ return base.CancelAsync (timeout);
+ }
- if (ctx.Response.StatusCode != 200) { // it's already invalid.
- ctx.Response.Close ();
- return null;
- }
+ void AbortConnections (TimeSpan timeout)
+ {
+ if (reqctx != null)
+ reqctx.Close (timeout);
+ }
- if (!Encoder.IsContentTypeSupported (ctx.Request.ContentType)) {
- ctx.Response.StatusCode = (int) HttpStatusCode.UnsupportedMediaType;
- ctx.Response.StatusDescription = String.Format (
- "Expected content-type '{0}' but got '{1}'", Encoder.ContentType, ctx.Request.ContentType);
- ctx.Response.Close ();
+ bool close_started;
+
+ protected override void OnClose (TimeSpan timeout)
+ {
+ if (close_started)
+ return;
+ close_started = true;
+ DateTime start = DateTime.Now;
+
+ // FIXME: consider timeout
+ AbortConnections (timeout - (DateTime.Now - start));
- return null;
+ base.OnClose (timeout - (DateTime.Now - start));
+ }
+
+ public override bool TryReceiveRequest (TimeSpan timeout, out RequestContext context)
+ {
+ context = null;
+ HttpContextInfo ctxi;
+ if (!source.ListenerManager.TryDequeueRequest (source.ChannelDispatcher, timeout, out ctxi))
+ return false;
+ if (ctxi == null)
+ return true; // returning true, yet context is null. This happens at closing phase.
+
+ var ctx = ((HttpStandaloneContextInfo) ctxi).Source;
+ if (ctx.Response.StatusCode != 200) { // it's already invalid.
+ ctx.Response.Close ();
+ return false;
}
// FIXME: supply maxSizeOfHeaders.
int maxSizeOfHeaders = 0x10000;
- var msg = Encoder.ReadMessage (
- ctx.Request.InputStream, maxSizeOfHeaders);
+ Message msg = null;
+
+ if (ctx.Request.HttpMethod == "POST") {
+ if (!Encoder.IsContentTypeSupported (ctx.Request.ContentType)) {
+ ctx.Response.StatusCode = (int) HttpStatusCode.UnsupportedMediaType;
+ ctx.Response.StatusDescription = String.Format (
+ "Expected content-type '{0}' but got '{1}'", Encoder.ContentType, ctx.Request.ContentType);
+ ctx.Response.Close ();
- if (MessageVersion.Envelope.Equals (EnvelopeVersion.Soap11) ||
- MessageVersion.Addressing.Equals (AddressingVersion.None)) {
- string action = GetHeaderItem (ctx.Request.Headers ["SOAPAction"]);
- if (action != null) {
- if (action.Length > 2 && action [0] == '"' && action [action.Length] == '"')
- action = action.Substring (1, action.Length - 2);
- msg.Headers.Action = action;
+ return false;
}
- }
+ msg = Encoder.ReadMessage (
+ ctx.Request.InputStream, maxSizeOfHeaders);
+
+ if (MessageVersion.Envelope.Equals (EnvelopeVersion.Soap11) ||
+ MessageVersion.Addressing.Equals (AddressingVersion.None)) {
+ string action = GetHeaderItem (ctx.Request.Headers ["SOAPAction"]);
+ if (action != null) {
+ if (action.Length > 2 && action [0] == '"' && action [action.Length] == '"')
+ action = action.Substring (1, action.Length - 2);
+ msg.Headers.Action = action;
+ }
+ }
+ } else if (ctx.Request.HttpMethod == "GET") {
+ msg = Message.CreateMessage (MessageVersion.None, null); // HTTP GET-based request
+ }
if (msg.Headers.To == null)
msg.Headers.To = ctx.Request.Url;
msg.Properties.Add ("Via", LocalAddress.Uri);
- msg.Properties.Add (HttpRequestMessageProperty.Name, CreateRequestProperty (ctxi.HttpMethod, ctx.Request.Url.Query, ctx.Request.Headers));
-
- return msg;
- }
-
- public override bool WaitForRequest (TimeSpan timeout)
- {
- throw new NotImplementedException ();
- }
- }
-
- internal class AspNetReplyChannel : HttpReplyChannel
- {
- public AspNetReplyChannel (HttpStandaloneChannelListener<IReplyChannel> listener)
- : base (listener)
- {
- }
-
- protected override RequestContext CreateRequestContext (HttpContextInfo ctxi, Message msg)
- {
- return new AspNetRequestContext (this, ctxi, msg);
- }
-
- protected override Message CreatePostMessage (HttpContextInfo ctxi)
- {
- throw new NotImplementedException ();
+ msg.Properties.Add (HttpRequestMessageProperty.Name, CreateRequestProperty (ctx.Request.HttpMethod, ctx.Request.Url.Query, ctx.Request.Headers));
+ context = new HttpStandaloneRequestContext (this, msg, ctx);
+ reqctx = context;
+ return true;
}
public override bool WaitForRequest (TimeSpan timeout)
internal abstract class HttpReplyChannel : InternalReplyChannelBase
{
HttpStandaloneChannelListener<IReplyChannel> source;
- RequestContext reqctx;
protected HttpReplyChannel (HttpStandaloneChannelListener<IReplyChannel> listener)
: base (listener)
{
}
- protected override void OnAbort ()
- {
- AbortConnections (TimeSpan.Zero);
- base.OnAbort (); // FIXME: remove it. The base is wrong. But it is somehow required to not block some tests.
- }
-
- public override bool CancelAsync (TimeSpan timeout)
- {
- AbortConnections (timeout);
- // FIXME: this wait is sort of hack (because it should not be required), but without it some tests are blocked.
- // This hack even had better be moved to base.CancelAsync().
- if (CurrentAsyncResult != null)
- CurrentAsyncResult.AsyncWaitHandle.WaitOne (TimeSpan.FromMilliseconds (300));
- return base.CancelAsync (timeout);
- }
-
- void AbortConnections (TimeSpan timeout)
- {
- if (reqctx != null)
- reqctx.Close (timeout);
- }
-
- bool close_started;
-
- protected override void OnClose (TimeSpan timeout)
- {
- if (close_started)
- return;
- close_started = true;
- DateTime start = DateTime.Now;
-
- // FIXME: consider timeout
- AbortConnections (timeout - (DateTime.Now - start));
-
- base.OnClose (timeout - (DateTime.Now - start));
- }
-
protected string GetHeaderItem (string raw)
{
if (raw == null || raw.Length == 0)
prop.Headers.Add (headers);
return prop;
}
-
- protected abstract RequestContext CreateRequestContext (HttpContextInfo ctxi, Message msg);
- protected abstract Message CreatePostMessage (HttpContextInfo ctxi);
-
- public override bool TryReceiveRequest (TimeSpan timeout, out RequestContext context)
- {
- context = null;
- HttpContextInfo ctxi;
- if (!source.ListenerManager.TryDequeueRequest (source.ChannelDispatcher, timeout, out ctxi))
- return false;
- if (ctxi == null)
- return true; // returning true, yet context is null. This happens at closing phase.
-
- Message msg = null;
-
- if (ctxi.HttpMethod == "POST") {
- msg = CreatePostMessage (ctxi);
- if (msg == null)
- return false;
- } else if (ctxi.HttpMethod == "GET")
- msg = Message.CreateMessage (MessageVersion.None, null); // HTTP GET-based request
-
- context = CreateRequestContext (ctxi, msg);
- reqctx = context;
- return true;
- }
}
}
namespace System.ServiceModel.Channels.Http
{
- internal class HttpStandaloneRequestContext : HttpRequestContextBase
+ internal class HttpStandaloneRequestContext : HttpStandaloneRequestContextBase
{
HttpListenerContext ctx;
- public HttpStandaloneRequestContext (HttpReplyChannel channel, Message msg, HttpListenerContext ctx)
+ public HttpStandaloneRequestContext (
+ HttpStandaloneReplyChannel channel,
+ Message msg, HttpListenerContext ctx)
: base (channel, msg)
{
if (ctx == null)
}
}
- internal class AspNetRequestContext : HttpRequestContextBase
- {
- public AspNetRequestContext (AspNetReplyChannel channel, HttpContextInfo ctxi, Message request)
- : base (channel, request)
- {
- throw new NotImplementedException ();
- }
-
- public override void Abort ()
- {
- throw new NotImplementedException ();
- }
-
- protected override void ProcessReply (Message msg, TimeSpan timeout)
- {
- throw new NotImplementedException ();
- }
-
- public override void Close (TimeSpan timeout)
- {
- throw new NotImplementedException ();
- }
- }
-
- internal abstract class HttpRequestContextBase : RequestContext
+ internal abstract class HttpStandaloneRequestContextBase : RequestContext
{
Message request;
- HttpReplyChannel channel;
+ HttpStandaloneReplyChannel channel;
- public HttpRequestContextBase (
- HttpReplyChannel channel,
+ public HttpStandaloneRequestContextBase (
+ HttpStandaloneReplyChannel channel,
Message request)
{
if (channel == null)
get { return request; }
}
- public HttpReplyChannel Channel {
+ public HttpStandaloneReplyChannel Channel {
get { return channel; }
}