Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
//
// System.Runtime.Remoting.Channels.AggregateDictionary.cs
//
// Author: Lluis Sanchez Gual (lluis@ximian.com)
//
// 2002 (C) Copyright, Novell, Inc.
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections;
namespace System.Runtime.Remoting.Channels.Http
{
internal class AggregateDictionary : IDictionary
{
IDictionary[] dictionaries;
ArrayList _values;
ArrayList _keys;
public AggregateDictionary (IDictionary[] dics)
{
dictionaries = dics;
}
public bool IsFixedSize
{
get { return true; }
}
public bool IsReadOnly
{
get { return true; }
}
public object this[object key]
{
get
{
foreach (IDictionary dic in dictionaries)
if (dic.Contains (key)) return dic[key];
return null;
}
set
{
foreach (IDictionary dic in dictionaries)
if (dic.Contains (key))
dic[key] = value;
}
}
public ICollection Keys
{
get
{
if (_keys != null) return _keys;
_keys = new ArrayList ();
foreach (IDictionary dic in dictionaries)
_keys.AddRange (dic.Keys);
return _keys;
}
}
public ICollection Values
{
get
{
if (_values != null) return _values;
_values = new ArrayList ();
foreach (IDictionary dic in dictionaries)
_values.AddRange (dic.Values);
return _values;
}
}
public void Add (object key, object value)
{
throw new NotSupportedException ();
}
public void Clear ()
{
throw new NotSupportedException ();
}
public bool Contains (object ob)
{
foreach (IDictionary dic in dictionaries)
if (dic.Contains (ob)) return true;
return false;
}
public IDictionaryEnumerator GetEnumerator ()
{
return new AggregateEnumerator (dictionaries);
}
IEnumerator IEnumerable.GetEnumerator ()
{
return new AggregateEnumerator (dictionaries);
}
public void Remove (object ob)
{
throw new NotSupportedException ();
}
public void CopyTo (Array array, int index)
{
foreach (object ob in this)
array.SetValue (ob, index++);
}
public int Count
{
get
{
int c = 0;
foreach (IDictionary dic in dictionaries)
c += dic.Count;
return c;
}
}
public bool IsSynchronized
{
get { return false; }
}
public object SyncRoot
{
get { return this; }
}
}
internal class AggregateEnumerator : IDictionaryEnumerator
{
IDictionary[] dictionaries;
int pos = 0;
IDictionaryEnumerator currente;
public AggregateEnumerator (IDictionary[] dics)
{
dictionaries = dics;
Reset ();
}
public DictionaryEntry Entry
{
get { return currente.Entry; }
}
public object Key
{
get { return currente.Key; }
}
public object Value
{
get { return currente.Value; }
}
public object Current
{
get { return currente.Current; }
}
public bool MoveNext ()
{
if (pos >= dictionaries.Length) return false;
if (!currente.MoveNext ()) {
pos++;
if (pos >= dictionaries.Length) return false;
currente = dictionaries[pos].GetEnumerator ();
return MoveNext ();
}
return true;
}
public void Reset ()
{
pos = 0;
if (dictionaries.Length > 0)
currente = dictionaries[0].GetEnumerator ();
}
}
}

View File

@@ -0,0 +1,278 @@
2009-09-15 Gonzalo Paniagua Javier <gonzalo@novell.com>
* HttpClientTransportSink.cs: ensure the response stream is disposed.
* HttpServerTransportSink.cs: use the public API of HttpConnection.
* RemotingHttpListener.cs: use the public API of HttpConnection
instead of hooking into an internal method, possibly cause exception
and leave the Connection in an unstable state.
2009-02-08 Gonzalo Paniagua Javier <gonzalo@novell.com>
* HttpClientTransportSink.cs: if we get an WebException that does not
have a response, dispatch it to the sink.
2008-11-10 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerTransportSink.cs: Seek to beginning of server response
stream. Fixes Bug 436995 - Xml-Rpc.net does not work since
Mono 1.9.x.
2008-09-30 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerChannel.cs: make sure the channel data object is set
when the port is auto-assigned.
* HttpClientTransportSink.cs: Use the __RequestUri header for the
request's URI. Includes workaround for bad __RequestUri values.
Fixes Bug 378713 - [regression] Remoting - HttpChannel.
2008-09-24 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerTransportSink.cs: Disable chunking on the server, as
for some reason it was breaking the tests. This reduces efficiency
but right now is unavoidable.
* HttpServerChannel.cs: Implement AddHookChannelUri. Thanks to
Lluis for the patch.
2008-09-23 Michael Hutchinson <mhutchinson@novell.com>
* HttpClientChannel.cs: Fix logic error from ==/!= typo. Stops
channel from trying to handle IPC URLs, which was breaking the
IPC tests.
2008-09-19 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerChannel.cs:
* HttpClientChannel.cs:
* HttpChannel.cs: Fixes from corcompare, and some cleanup.
2008-09-19 Jeffrey Stedfast <fejj@novell.com>
* HttpChannel.cs: Throw ArgumentNullException if url is null.
2008-09-18 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerTransportSink.cs:
* HttpServerChannel.cs: Cleanup.
* HttpClientTransportSink.cs: Use username/password/domain parameters,
fix some spelling, add FIXME comment regarding stream copying.
* HttpClientChannel.cs:
* HttpChannel.cs: Fix 1.1 API slip.
2008-09-18 Michael Hutchinson <mhutchinson@novell.com>
* HttpServerChannel.cs: Don't try to resolve IPAddress.Any, so that
by default we bind to localhost/lo as well as the host address.
Also remove dead code for binding to multiple IP addresses; I don't
think we need to handle that.
2008-09-18 Michael Hutchinson <mhutchinson@novell.com>
* HttpChannel.cs: Fix default channel name.
2008-09-18 Michael Hutchinson <mhutchinson@novell.com>
* HttpClientTransportSink: Fix logic error that broke async client
functionality. Fixes HttpAsyncCallTest tests.
2008-06-14 Sebastien Pouliot <sebastien@ximian.com>
* HttpRemotingHandler.cs: Remove double assignment to bodyBuffer.
[Found using Gendarme]
2008-02-12 Roei Erez (roeie@mainsoft.com)
* HttpServerChannel.cs: Always initialize channel uri.
2007-10-30 Robert Jordan <robertj@gmx.net>
* HttpHelper.cs: Support for the "https" scheme. Make scheme parser
case invariant. Fixes bug #81701.
* HttpServerChannel.cs (GetChannelUri): Take channel data into account.
* HttpServerChannel.cs (SetupChannel): Initialize channel data
with "null", because at this stage we don't necessary have a valid
channel URI.
2007-08-23 Robert Jordan <robertj@gmx.net>
* HttpRemotingHandlerFactory.cs: configure Remoting because
System.Web doesn't anymore. Fixes #81831.
2006-12-18 Lluis Sanchez Gual <lluis@novell.com>
* HttpServerChannel.cs: The remoting infrastructure does not call
StartListening() anymore, so it has to be called by the channel.
2006-03-05 Andrew Skiba <andrews@mainsoft.com>
* HttpClientChannel.cs, HttpServerChannel.cs: exceptions propagating
incompatible with dotnet. Patch by roeie@mainsoft.com
2005-11-06 Svetlana Zholkovsky <svetlanaz@mainsoft.com>
* Add HttpHandlerFactory.jvm.cs
* HttpHandlerFactory.cs: Create and register HttpChannel in case it
was not registered before.
* HttpHandler.cs: Send response headers.
* HttpServerChannel.cs:
- Change default port to -1.
- Don't start a listener if the port == -1
- Send response to the client also in case of DispatchRequest fails
2005-10-10 Lluis Sanchez Gual <lluis@novell.com>
* HttpServer.cs: Removed all non-sense regular expressions for
parsing files. Makes the channel faster and fixes bug #75886.
2005-07-06 Lluis Sanchez Gual <lluis@novell.com>
* HttpServer.cs: Close the connection after processing the request.
Based on a patch by Brion Vibber. Fixes bug #75467.
2005-05-18 Lluis Sanchez Gual <lluis@novell.com>
* HttpServerChannel.cs: Catch exceptions thrown in the server thread.
In StopListening, wait for the server thread to stop before returning.
2005-01-25 Lluis Sanchez Gual <lluis@novell.com>
* HttpServer.cs: Use a single stream for in an out stream, since they
where the same. Set IPAddress and ConnectionId transport headers.
2005-01-14 Lluis Sanchez Gual <lluis@novell.com>
* HttpServerChannel.cs: Use the new RemotingThreadPool to manage threads.
Fixed some warnings.
* HttpServer.cs: Minor fix.
2004-12-17 Lluis Sanchez Gual <lluis@ximian.com>
* HttpHelper.cs: Removed unused method. Optimized CopyStream method.
* HttpServerChannel.cs: SendResponse does not return a bool any
more, it throws an exception when it fails.
* HttpServer.cs: Improved handling of errors.
2004-10-22 Lluis Sanchez Gual <lluis@ximian.com>
* HttpClientChannel.cs: In CreateMessageSink, process the remote channel
data if the provided url does not have the expected format. This fixes
a regression from the fix for bug #66768 and fixes #68669.
2004-09-27 Lluis Sanchez Gual <lluis@ximian.com>
* HttpClientChannel.cs: Throw an http exception if the response is a 500,
don't try to deserialize the exception.
2004-05-26 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServerChannel.cs: Default port is 0.
2004-05-25 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServerChannel.cs: Take unused port if the provided prot is 0.
2004-05-13 Lluis Sanchez Gual <lluis@ximian.com>
* HttpChannel.cs, HttpClientChannel.cs: Added missing IDictionary
properties.
* HttpRemotingHandler.cs: Added missing constructor.
* HttpRemotingHandlerFactory.cs: Made ConfigureHttpChannel private.
* HttpServerChannel.cs: Fixed IDictionary properties.
2004-04-30 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServer.cs: Removed dead code.
2004-02-27 Lluis Sanchez Gual <lluis@ximian.com>
* HttpClientChannel.cs: Set the RequestUri transport header before sending
the request.
2004-02-04 Lluis Sanchez Gual <lluis@ximian.com>
* HttpRemotingHandlerFactory.cs: Loading of remoting configuration moved
to System.Web.Configuraiton.
2003-12-12 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServer.cs: Removed debug code.
2003-12-10 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServer.cs: Allow the use of GET method (SdlServerSink supports it).
Added some text constans for transport keys.
* HttpServerChannel.cs: Add SdlChannelSinkProvider to the default
sink provider chain. Removed unused ErrorMessage class.
2003-11-16 Lluis Sanchez Gual <lluis@ximian.com>
* HttpHelper.cs: Removed usnused method GetMachineIp.
* HttpServerChannel.cs: Added support for priority, bindTo, useIpAddress and
machineName properties.
2003-11-13 Lluis Sanchez Gual <lluis@ximian.com>
* HttpChannel.cs: Create empty property dictionary in the default
constructor.
2003-11-13 Lluis Sanchez Gual <lluis@ximian.com>
* HttpChannel.cs: Moved initialization code in SetupChannel to the
respective client and server channels. Added implementatoin of the
interface IChannelReceiverHook.
* HttpRemotingHandler.cs: Implemented.
* HttpRemotingHandlerFactory.cs: Implemented.
* HttpServer.cs: In general, use Stream instances instead of Socket. It is
more reusable in this way. Also improved formatting.
* HttpServerChannel.cs: Implemented support for IChannelReceiverHook.
Added new method DispatchRequest in HttpServerTransportSink that can
be reused by HttpRemotingHandler.
2003-11-12 Lluis Sanchez Gual <lluis@ximian.com>
* HttpServerChannel.cs: Removed StartListening call from constructor. It
is called by the remoting framework.
* HttpClientChannel.cs, HttpHelper.cs: Fixed some formatting.
2003-09-17 Lluis Sanchez Gual <lluis@ximian.com>
* HttpHelper.cs: Fixed bug #48468. Patch by Jean-Marc Andre.
* HttpClientChannel.cs HttpServer.cs HttpServerChannel.cs: Fixed some warnings.
2003-08-22 Lluis Sanchez Gual <lluis@ximian.com>
* HttpClientChannel.cs: Changed text for user-agent header (removed references
to MS.NET).
Removed try/catch from AsyncProcessRequest. If there is an exception it must
flow to the caller.
in AsyncRequestHandler, improved management of exceptions. HttpWebRequest
throws an exception if the result code is 400, 500. Is is not a communication
error, but an application or server error. The content of the body must be
deserialized like in normal responses.
In CreateWebRequest, if the stream being sent is a MemoryStream, use a more
efficient way of writing the content.
In SendAndRecieve, same as in AsyncRequestHandler. Also moved some code to a
new method named ReceiveResponse, so it can be reused from AsyncRequestHandler.
* HttpHelper.cs: Removed some debugging code that is not needed.
* HttpServer.cs: Improved formatting of some code.
In CheckRequest method, send a 100-continue response if the request has
the header: expect:100-continue.
Method SendResponse: the remoting formatter may include the result code and
reason phrase to use in the transport headers. Used them if provided.
* HttpServerChannel.cs: Use ThreadPool to create the thread that will answer
a request.
2003-08-18 Lluis Sanchez Gual <lluis@ximian.com>
* HttpClientChannel.cs, HttpServerChannel.cs: Fixed bug #47703
2003-06-21 Lluis Sanchez Gual <lluis@ximian.com>
* HttpChannel.cs, HttpClientChannel.cs, HttpHelper.cs, HttpServer.cs,
HttpServerChannel.cs, HttpThread.cs: added new implementation of the HttpChannel
by Ahmad Tantawy, Ahmad Kadry and Hussein Mehanna.
* unix.args: added HttpHelper.cs,HttpServer.cs,HttpThread.cs.

View File

@@ -0,0 +1,238 @@
//
// HttpChannel.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpChannel : BaseChannelWithProperties,
IChannel, IChannelReceiver, IChannelReceiverHook, IChannelSender
#if NET_2_0
, ISecurableChannel
#endif
{
HttpClientChannel client;
HttpServerChannel server;
string name = "http";
#region Constructors
public HttpChannel ()
{
client = new HttpClientChannel ();
server = new HttpServerChannel ();
}
public HttpChannel (int port)
{
client = new HttpClientChannel ();
server = new HttpServerChannel (port);
}
public HttpChannel (IDictionary properties,
IClientChannelSinkProvider clientSinkProvider,
IServerChannelSinkProvider serverSinkProvider)
{
if (properties != null && properties.Contains ("name")) {
this.name = (string)properties["name"];
}
client = new HttpClientChannel (properties, clientSinkProvider);
server = new HttpServerChannel (properties, serverSinkProvider);
}
#endregion
#region BaseChannelWithProperties overrides
public override object this[object key]
{
get { return Properties[key]; }
set { Properties[key] = value; }
}
public override ICollection Keys
{
get { return Properties.Keys; }
}
public override IDictionary Properties
{
get
{
return new AggregateDictionary (new IDictionary[] {
client.Properties,
server.Properties
});
}
}
#endregion
#region IChannel
public string ChannelName
{
get { return name; }
}
public int ChannelPriority
{
get { return server.ChannelPriority; }
}
public string Parse (string url, out string objectURI)
{
return ParseInternal (url, out objectURI);
}
internal static string ParseInternal (string url, out string objectURI)
{
if (url == null)
throw new ArgumentNullException ("url");
// format: "http://host:port/path/to/object"
objectURI = null;
// url needs to be at least "http:" or "https:"
if (url.Length < 5 ||
(url[0] != 'H' && url[0] != 'h') ||
(url[1] != 'T' && url[1] != 't') ||
(url[2] != 'T' && url[2] != 't') ||
(url[3] != 'P' && url[3] != 'p'))
return null;
int protolen;
if (url[4] == 'S' || url[4] == 's') {
if (url.Length < 6)
return null;
protolen = 5;
} else {
protolen = 4;
}
if (url[protolen] != ':')
return null;
// "http:" and "https:" are acceptable inputs
if (url.Length == protolen + 1)
return url;
// protocol must be followed by "//"
if (url.Length < protolen + 3 || url[protolen + 1] != '/' || url[protolen + 2] != '/')
return null;
// "http://" and "https://" are acceptable inputs
if (url.Length == protolen + 3)
return url;
int slash = url.IndexOf ('/', protolen + 3);
if (slash == -1)
return url;
objectURI = url.Substring (slash);
return url.Substring (0, slash);
}
#endregion
#region IChannelReceiver (: IChannel)
public object ChannelData
{
get { return server.ChannelData; }
}
public string[] GetUrlsForUri (string objectURI)
{
return server.GetUrlsForUri (objectURI);
}
public void StartListening (object data)
{
server.StartListening (data);
}
public void StopListening (object data)
{
server.StopListening (data);
}
#endregion
#region IChannelReceiverHook
public void AddHookChannelUri (string channelUri)
{
server.AddHookChannelUri (channelUri);
}
public string ChannelScheme
{
get { return server.ChannelScheme; }
}
public IServerChannelSink ChannelSinkChain
{
get { return server.ChannelSinkChain; }
}
public bool WantsToListen
{
get { return server.WantsToListen; }
set { server.WantsToListen = value; }
}
#endregion
#region IChannelSender (: IChannel)
public IMessageSink CreateMessageSink (string url, object remoteChannelData, out string objectURI)
{
return client.CreateMessageSink (url, remoteChannelData, out objectURI);
}
#endregion
#if NET_2_0
#region ISecurableChannel
public bool IsSecured
{
get { return client.IsSecured; }
set { client.IsSecured = value; }
}
#endregion
#endif
}
}

View File

@@ -0,0 +1,341 @@
//
// HttpClientChannel.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Net;
using System.Collections;
using System.Globalization;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpClientChannel : BaseChannelWithProperties,
IChannel, IChannelSender
#if NET_2_0
, ISecurableChannel
#endif
{
string name = "http client";
int priority = 1;
// names and (some) default values fromhttp://msdn.microsoft.com/en-us/library/bb187435(VS.85).aspx
// other values guessed from defaults of HttpWebResponse
string machineName = null;
bool allowAutoRedirect = true; //FIXME: what's the default? true/false?
int clientConnectionLimit = 2;
string connectionGroupName = null;
ICredentials credentials = null;
string domain = null;
string password = null;
string proxyName = null;
int proxyPort = -1;
Uri proxyUri = null;
string servicePrincipalName = null;
int timeout = -1;
bool unsafeAuthenticatedConnectionSharing = false;
// according to docs, should be true if useDefaultCredentials true or
// credentials is CredentialCache.DefaultCredentials
bool useAuthenticatedConnectionSharing = false;
bool useDefaultCredentials = false;
string username = null;
bool isSecured = false;
IClientChannelSinkProvider sinkProvider;
#region Constructors
public HttpClientChannel ()
{
BuildSink (null);
}
[MonoTODO ("Handle the machineName, proxyName, proxyPort, servicePrincipalName, " +
"useAuthenticatedConnectionSharing properties")]
public HttpClientChannel (IDictionary properties, IClientChannelSinkProvider sinkProvider)
{
if (properties != null) {
foreach (DictionaryEntry property in properties) {
switch ((string)property.Key) {
case "name":
//NOTE: matching MS behaviour: throws InvalidCastException, allows null
this.name = (string)property.Value;
break;
case "priority":
this.priority = Convert.ToInt32 (property.Value);
break;
case "machineName":
this.machineName = (string)property.Value;
break;
case "allowAutoRedirect":
this.allowAutoRedirect = Convert.ToBoolean (property.Value);
break;
case "clientConnectionLimit":
this.clientConnectionLimit = Convert.ToInt32 (property.Value);
break;
case "connectionGroupName":
this.connectionGroupName = (string)property.Value;
break;
case "credentials":
this.credentials = (ICredentials)property.Value;
if (this.credentials == CredentialCache.DefaultCredentials)
useAuthenticatedConnectionSharing = true;
break;
case "domain":
this.domain = (string)property.Value;
break;
case "password":
this.password = (string)property.Value;
break;
case "proxyName":
this.proxyName = (string)property.Value;
break;
case "proxyPort":
this.proxyPort = Convert.ToInt32 (property.Value);
break;
case "servicePrincipalName":
this.servicePrincipalName = (string)property.Value;
break;
case "timeout":
this.timeout = Convert.ToInt32 (property.Value);
break;
case "unsafeAuthenticatedConnectionSharing":
this.unsafeAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
break;
case "useAuthenticatedConnectionSharing":
this.useAuthenticatedConnectionSharing = Convert.ToBoolean (property.Value);
break;
case "useDefaultCredentials":
this.useDefaultCredentials = Convert.ToBoolean (property.Value);
if (useDefaultCredentials)
useAuthenticatedConnectionSharing = true;
break;
case "username":
this.username = (string)property.Value;
break;
}
}
}
BuildSink (sinkProvider);
}
public HttpClientChannel (string name, IClientChannelSinkProvider sinkProvider)
{
this.name = name;
BuildSink (sinkProvider);
}
void BuildSink (IClientChannelSinkProvider sinkProvider)
{
if (sinkProvider == null) {
//according to docs, defaults to SOAP if no other sink provided
sinkProvider = new SoapClientFormatterSinkProvider ();
}
this.sinkProvider = sinkProvider;
//add HTTP sink at the end of the chain
while (sinkProvider.Next != null) sinkProvider = sinkProvider.Next;
sinkProvider.Next = new HttpClientTransportSinkProvider ();
// LAMESPEC: BaseChannelWithProperties wants SinksWithProperties to be set with the sink chain
// BUT MS' HttpClientChannel does not set it (inspected from HttpClientChannel subclass)
}
#endregion
#region BaseChannelWithProperties overrides
public override object this[object key]
{
get
{
switch (key as string) {
case "proxyport":
return proxyPort;
case "proxyname":
return proxyName;
}
return null;
}
set
{
switch (key as string) {
case "proxyport":
proxyPort = Convert.ToInt32 (value);
ConstructProxy ();
return;
case "proxyname":
proxyName = (string)value;
ConstructProxy ();
return;
}
//ignore other values, MS does so
}
}
public override ICollection Keys
{
get
{
return new string[] {
"proxyname",
"proxyport"
};
}
}
void ConstructProxy ()
{
if (proxyName != null && proxyPort > 0)
proxyUri = new Uri (proxyName + ":" + proxyPort);
}
#endregion
#region IChannel
public string ChannelName
{
get { return name; }
}
public int ChannelPriority
{
get { return priority; }
}
public string Parse (string url, out string objectURI)
{
return HttpChannel.ParseInternal (url, out objectURI);
}
#endregion
#region IChannelSender (: IChannel)
public virtual IMessageSink CreateMessageSink (string url, object remoteChannelData, out string objectURI)
{
//Mostly copied from TcpClientChannel
if (url == null || Parse (url, out objectURI) == null) {
if (remoteChannelData != null) {
IChannelDataStore ds = remoteChannelData as IChannelDataStore;
if (ds != null && ds.ChannelUris.Length > 0)
url = ds.ChannelUris[0];
else {
objectURI = null;
return null;
}
}
if (Parse (url, out objectURI) == null)
return null;
}
object newSink = sinkProvider.CreateSink (this, url, remoteChannelData);
if (newSink is IMessageSink) {
return (IMessageSink) newSink;
} else {
throw new RemotingException ("First channel sink must implement IMessageSink");
}
}
#endregion
#if NET_2_0
#region ISecurableChannel
public bool IsSecured
{
get { return isSecured; }
set {
throw new NotImplementedException ("Unable to determine expected behaviour yet.");
}
}
#endregion
#endif
#region Internal properties
internal string MachineName {
get { return machineName; }
}
internal bool AllowAutoRedirect {
get { return allowAutoRedirect; }
}
internal int ClientConnectionLimit {
get { return clientConnectionLimit; }
}
internal string ConnectionGroupName {
get { return connectionGroupName; }
}
internal ICredentials Credentials {
get { return credentials; }
}
internal string Domain {
get { return domain; }
}
internal string Password {
get { return password; }
}
internal string Username {
get { return username; }
}
internal string ProxyName {
get { return proxyName; }
}
internal int ProxyPort {
get { return proxyPort; }
}
internal Uri ProxyUri {
get { return proxyUri; }
}
internal string ServicePrincipalName {
get { return servicePrincipalName; }
}
internal int Timeout {
get { return timeout; }
}
internal bool UnsafeAuthenticatedConnectionSharing {
get { return unsafeAuthenticatedConnectionSharing; }
}
internal bool UseAuthenticatedConnectionSharing {
get { return useAuthenticatedConnectionSharing; }
}
internal bool UseDefaultCredentials {
get { return useDefaultCredentials; }
}
#endregion
}
}

View File

@@ -0,0 +1,245 @@
//
// HttpClientTransportSink.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
class HttpClientTransportSink : IClientChannelSink
{
string url;
HttpClientChannel channel;
public HttpClientTransportSink (HttpClientChannel channel, string url)
{
this.channel = channel;
this.url = url;
}
//always the last sink in the chain
public IClientChannelSink NextChannelSink
{
get { return null; }
}
public void AsyncProcessRequest (IClientChannelSinkStack sinkStack, IMessage msg,
ITransportHeaders headers, Stream requestStream)
{
bool isOneWay = RemotingServices.IsOneWay (((IMethodMessage)msg).MethodBase);
HttpWebRequest request = CreateRequest (headers);
using (Stream targetStream = request.GetRequestStream ()) {
CopyStream (requestStream, targetStream, 1024);
}
if (!isOneWay) {
sinkStack.Push (this, request);
request.BeginGetResponse (new AsyncCallback (AsyncProcessResponseCallback), sinkStack);
}
}
void AsyncProcessResponseCallback (IAsyncResult ar)
{
IClientChannelSinkStack sinkStack = (IClientChannelSinkStack)ar.AsyncState;
HttpWebRequest request = (HttpWebRequest)sinkStack.Pop(this);
WebResponse response;
try {
response = request.EndGetResponse (ar);
} catch (WebException ex) {
response = ex.Response;
//only error 500 is handled by the remoting stack
HttpWebResponse httpResponse = response as HttpWebResponse;
if (httpResponse == null || httpResponse.StatusCode != HttpStatusCode.InternalServerError) {
sinkStack.DispatchException (ex);
return;
}
}
//this is only valid after the response is fetched
SetConnectionLimit (request);
using (response) {
Stream responseStream = response.GetResponseStream ();
ITransportHeaders responseHeaders = GetHeaders (response);
sinkStack.AsyncProcessResponse (responseHeaders, responseStream);
}
}
public void AsyncProcessResponse (IClientResponseChannelSinkStack sinkStack, object state,
ITransportHeaders headers, Stream stream)
{
// Should never be called
throw new NotSupportedException ();
}
public Stream GetRequestStream (IMessage msg, ITransportHeaders headers)
{
return null;
}
HttpWebRequest CreateRequest (ITransportHeaders requestHeaders)
{
string url = this.url;
//FIXME: requestUri should contain the URL-less URI only when it's a CAO call;
// at all other times it should be null. On Mono, whenever it should be null, it contains the full
// URL+URI, so we have a broken mixure of path types and we need to hack around it
string requestUri = requestHeaders[CommonTransportKeys.RequestUri] as string;
string objectURI;
if (requestUri != null && HttpChannel.ParseInternal (requestUri, out objectURI) == null) {
url = HttpChannel.ParseInternal (url, out objectURI);
if (!url.EndsWith ("/"))
url = url + "/";
url = url + requestUri;
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url);
request.UserAgent = string.Format ("Mozilla/4.0+(compatible; Mono Remoting; Mono {0})",
System.Environment.Version);
//Only set these if they deviate from the defaults, as some map to
//properties that throw NotImplementedExceptions
if (channel.Timeout != -1)
request.Timeout = channel.Timeout;
if (channel.AllowAutoRedirect == false)
request.AllowAutoRedirect = false;
if (channel.Credentials != null)
request.Credentials = channel.Credentials;
#if NET_2_0
else if (channel.UseDefaultCredentials == true)
request.UseDefaultCredentials = true;
#endif
else if (channel.Username != null && channel.Username.Length > 0) {
if (channel.Domain != null && channel.Domain.Length > 0) {
request.Credentials = new NetworkCredential (channel.Username, channel.Password,
channel.Domain);
} else {
request.Credentials = new NetworkCredential (channel.Username, channel.Password);
}
}
if (channel.UnsafeAuthenticatedConnectionSharing == true)
request.UnsafeAuthenticatedConnectionSharing = true;
if (channel.ConnectionGroupName != null)
request.ConnectionGroupName = channel.ConnectionGroupName;
/*
FIXME: implement these
MachineName
ProxyName
ProxyPort
ProxyUri
ServicePrincipalName
UseAuthenticatedConnectionSharing
*/
//build the headers
request.ContentType = (string)requestHeaders["Content-Type"];
//BUG: Mono formatters/dispatcher don't set this. Something in the MS stack does.
string method = (string)requestHeaders["__RequestVerb"];
if (method == null)
method = "POST";
request.Method = method;
foreach (DictionaryEntry entry in requestHeaders) {
string key = entry.Key.ToString ();
if (key != "__RequestVerb" && key != "Content-Type" && key != CommonTransportKeys.RequestUri) {
request.Headers.Add (key, entry.Value.ToString ());
}
}
return request;
}
void SetConnectionLimit (HttpWebRequest request)
{
if (channel.ClientConnectionLimit != 2) {
request.ServicePoint.ConnectionLimit = channel.ClientConnectionLimit;
}
}
static TransportHeaders GetHeaders (WebResponse response)
{
TransportHeaders headers = new TransportHeaders ();
foreach (string key in response.Headers) {
headers[key] = response.Headers[key];
}
return headers;
}
internal static void CopyStream (Stream source, Stream target, int bufferSize)
{
byte[] buffer = new byte[bufferSize];
int readLen = source.Read (buffer, 0, buffer.Length);
while (readLen > 0) {
target.Write (buffer, 0, readLen);
readLen = source.Read (buffer, 0, buffer.Length);
}
}
public void ProcessMessage (IMessage msg, ITransportHeaders requestHeaders, Stream requestStream,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
HttpWebRequest request = CreateRequest (requestHeaders);
Stream targetStream = request.GetRequestStream ();
CopyStream (requestStream, targetStream, 1024);
targetStream.Close ();
WebResponse response;
try {
response = request.GetResponse ();
} catch (WebException ex) {
response = ex.Response;
//only error 500 is handled by the remoting stack
HttpWebResponse httpResponse = response as HttpWebResponse;
if (httpResponse == null || httpResponse.StatusCode != HttpStatusCode.InternalServerError)
throw;
}
//this is only valid after the response is fetched
SetConnectionLimit (request);
//FIXME: can we assume that the formatters will close the stream? Or do we need to make
// a copy and close it ourselves?
responseHeaders = GetHeaders (response);
responseStream = response.GetResponseStream ();
}
public IDictionary Properties
{
get { return null; }
}
}
}

View File

@@ -0,0 +1,56 @@
//
// HttpClientTransportSinkProvider.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
class HttpClientTransportSinkProvider : IClientChannelSinkProvider
{
public HttpClientTransportSinkProvider ()
{
}
// empty impl, because always last in chain
public IClientChannelSinkProvider Next
{
get { return null; }
set { }
}
public IClientChannelSink CreateSink (IChannelSender channel, string url, object remoteChannelData)
{
return new HttpClientTransportSink ((HttpClientChannel)channel, url);
}
}
}

View File

@@ -0,0 +1,120 @@
//
// System.Runtime.Remoting.Channels.Http.HttpRemotingHandler
//
// Authors:
// Martin Willemoes Hansen (mwh@sysrq.dk)
// Lluis Sanchez Gual (lluis@ximian.com)
//
// (C) 2003 Martin Willemoes Hansen
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Web;
using System.Collections;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpRemotingHandler : IHttpHandler
{
HttpServerTransportSink transportSink;
public HttpRemotingHandler ()
{
}
[MonoTODO]
public HttpRemotingHandler (Type type, object srvID)
{
throw new NotImplementedException ();
}
internal HttpRemotingHandler (HttpServerTransportSink sink)
{
transportSink = sink;
}
public bool IsReusable {
get { return true; }
}
public void ProcessRequest (HttpContext context)
{
HttpRequest request = context.Request;
HttpResponse response = context.Response;
// Create transport headers for the request
TransportHeaders theaders = new TransportHeaders();
string objectUri = request.RawUrl;
objectUri = objectUri.Substring (request.ApplicationPath.Length); // application path is not part of the uri
if (request.ApplicationPath.Length > 0 &&
(objectUri.StartsWith("/") || objectUri.StartsWith(@"\")) )
{
objectUri = objectUri.Substring(1);
}
theaders ["__RequestUri"] = objectUri;
theaders ["Content-Type"] = request.ContentType;
theaders ["__RequestVerb"]= request.HttpMethod;
theaders ["__HttpVersion"] = request.Headers ["http-version"];
theaders ["User-Agent"] = request.UserAgent;
theaders ["Host"] = request.Headers ["host"];
ITransportHeaders responseHeaders;
Stream responseStream;
// Dispatch the request
ServerProcessing proc = transportSink.SynchronousDispatch
(theaders, request.InputStream, out responseHeaders, out responseStream);
if (proc == ServerProcessing.Async) {
throw new NotSupportedException ("HttpRemotingHandler does not support async processing in " +
"the synchronous HTTP pipeline" );
}
// Write the response
if (responseHeaders != null && responseHeaders["__HttpStatusCode"] != null)
{
// The formatter can set the status code
response.StatusCode = int.Parse ((string) responseHeaders["__HttpStatusCode"]);
response.StatusDescription = (string) responseHeaders["__HttpReasonPhrase"];
}
if (responseHeaders != null)
{
foreach (DictionaryEntry entry in responseHeaders)
{
string key = entry.Key.ToString();
if (key != CommonTransportKeys.HttpStatusCode && key != CommonTransportKeys.HttpReasonPhrase)
response.AppendHeader(key, entry.Value.ToString());
}
}
if (responseStream != null) {
HttpClientTransportSink.CopyStream (responseStream, response.OutputStream, 1024);
}
}
}
}

View File

@@ -0,0 +1,112 @@
//
// System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory
//
// Authors:
// Martin Willemoes Hansen (mwh@sysrq.dk)
// Lluis Sanchez Gual (lluis@ximian.com)
//
// (C) 2003 Martin Willemoes Hansen
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Web;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpRemotingHandlerFactory : IHttpHandlerFactory
{
static bool webConfigLoaded = false;
static HttpServerTransportSink transportSink = null;
public HttpRemotingHandlerFactory ()
{
}
public IHttpHandler GetHandler (HttpContext context,
string verb,
string url,
string filePath)
{
if (!webConfigLoaded)
ConfigureHttpChannel (context);
return new HttpRemotingHandler (transportSink);
}
void ConfigureHttpChannel (HttpContext context)
{
lock (GetType())
{
if (webConfigLoaded) return;
string appConfig = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
if (File.Exists (appConfig))
RemotingConfiguration.Configure (appConfig);
// Look for a channel that wants to receive http request
IChannelReceiverHook chook = null;
foreach (IChannel channel in ChannelServices.RegisteredChannels)
{
chook = channel as IChannelReceiverHook;
if (chook == null) continue;
if (chook.ChannelScheme != "http")
throw new RemotingException ("Only http channels are allowed when hosting remoting objects in a web server");
if (!chook.WantsToListen)
{
chook = null;
continue;
}
//found chook
break;
}
if (chook == null)
{
HttpChannel chan = new HttpChannel();
ChannelServices.RegisterChannel(chan);
chook = chan;
}
// Register the uri for the channel. The channel uri includes the scheme, the
// host and the application path
string channelUrl = context.Request.Url.GetLeftPart(UriPartial.Authority);
channelUrl += context.Request.ApplicationPath;
chook.AddHookChannelUri (channelUrl);
transportSink = new HttpServerTransportSink (chook.ChannelSinkChain);
webConfigLoaded = true;
}
}
public void ReleaseHandler (IHttpHandler handler)
{
}
}
}

View File

@@ -0,0 +1,128 @@
//
// System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory
//
// Authors:
// Martin Willemoes Hansen (mwh@sysrq.dk)
// Lluis Sanchez Gual (lluis@ximian.com)
//
// (C) 2003 Martin Willemoes Hansen
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Web;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpRemotingHandlerFactory : IHttpHandlerFactory
{
public HttpRemotingHandlerFactory ()
{
}
private static HttpRemotingHandlerFactoryData CurrentHttpRemotingHandlerFactoryData
{
get
{
HttpRemotingHandlerFactoryData res = (HttpRemotingHandlerFactoryData)AppDomain.CurrentDomain.GetData("HttpRemotingHandlerFactory");
if (res == null)
{
res = new HttpRemotingHandlerFactoryData();
AppDomain.CurrentDomain.SetData("HttpRemotingHandlerFactory", res);
}
return res;
}
}
public IHttpHandler GetHandler (HttpContext context,
string verb,
string url,
string filePath)
{
if (!CurrentHttpRemotingHandlerFactoryData.webConfigLoaded)
ConfigureHttpChannel (context);
return new HttpRemotingHandler (CurrentHttpRemotingHandlerFactoryData.transportSink);
}
void ConfigureHttpChannel (HttpContext context)
{
lock (GetType())
{
if (CurrentHttpRemotingHandlerFactoryData.webConfigLoaded) return;
// Look for a channel that wants to receive http request
IChannelReceiverHook chook = null;
foreach (IChannel channel in ChannelServices.RegisteredChannels)
{
chook = channel as IChannelReceiverHook;
if (chook == null) continue;
if (chook.ChannelScheme != "http")
throw new RemotingException ("Only http channels are allowed when hosting remoting objects in a web server");
if (!chook.WantsToListen)
{
chook = null;
continue;
}
//found chook
break;
}
if (chook == null)
{
HttpChannel chan = new HttpChannel();
ChannelServices.RegisterChannel(chan);
chook = chan;
}
// Register the uri for the channel. The channel uri includes the scheme, the
// host and the application path
string channelUrl = context.Request.Url.GetLeftPart(UriPartial.Authority);
channelUrl += context.Request.ApplicationPath;
chook.AddHookChannelUri (channelUrl);
CurrentHttpRemotingHandlerFactoryData.transportSink = new HttpServerTransportSink (chook.ChannelSinkChain, null);
CurrentHttpRemotingHandlerFactoryData.webConfigLoaded = true;
}
}
public void ReleaseHandler (IHttpHandler handler)
{
}
}
internal class HttpRemotingHandlerFactoryData
{
internal bool webConfigLoaded = false;
internal HttpServerTransportSink transportSink = null;
}
}

View File

@@ -0,0 +1,316 @@
//
// HttpServerChannel.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Net;
using System.Collections;
using System.Globalization;
using System.Threading;
using System.Runtime.Remoting.MetadataServices;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
public class HttpServerChannel : BaseChannelWithProperties,
IChannel, IChannelReceiver, IChannelReceiverHook
{
string name = "http server";
int priority = 1;
string machineName = null;
IPAddress bindAddress = IPAddress.Any;
int port = -1; // querying GetChannelUri () on .NET indicates this is the default value
bool suppressChannelData = false;
bool useIPAddress = true;
#if !NET_2_0
bool exclusiveAddressUse = true;
#endif
bool wantsToListen = true;
HttpServerTransportSink sink;
ChannelDataStore channelData;
RemotingHttpListener listener;
#region Constructors
public HttpServerChannel ()
{
//DONT START SERVER, EVEN THOUGH ALL OTHER CONSTRUCTORS DO
BuildSink (null);
}
public HttpServerChannel (int port)
{
this.port = port;
BuildSink (null);
}
[MonoTODO ("Handle the listen property")]
public HttpServerChannel (IDictionary properties, IServerChannelSinkProvider sinkProvider)
{
if (properties != null) {
foreach (DictionaryEntry property in properties) {
switch ((string)property.Key) {
case "name":
//NOTE: matching MS behaviour: throws InvalidCastException, allows null
this.name = (string)property.Value;
break;
case "priority":
this.priority = Convert.ToInt32 (property.Value);
break;
case "port":
this.port = Convert.ToInt32 (property.Value);
break;
case "suppressChannelData":
this.suppressChannelData = Convert.ToBoolean (property.Value);
break;
case "bindTo":
bindAddress = IPAddress.Parse ((string)property.Value);
break;
case "useIpAddress":
this.useIPAddress = Convert.ToBoolean (property.Value);
break;
case "machineName":
this.machineName = (string)property.Value;
break;
case "listen":
this.wantsToListen = Convert.ToBoolean (property.Value);
break;
#if !NET_2_0
case "exclusiveAddressUse":
this.exclusiveAddressUse = Convert.ToBoolean (property.Value);
break;
#endif
}
}
}
BuildSink (sinkProvider);
}
public HttpServerChannel (string name, int port)
: this (name, port, null)
{
}
public HttpServerChannel (string name, int port, IServerChannelSinkProvider sinkProvider)
{
this.name = name;
this.port = port;
BuildSink (sinkProvider);
}
void BuildSink (IServerChannelSinkProvider sinkProvider)
{
//resolve names (modified from TcpChannel)
if (machineName == null) {
if (useIPAddress) {
if (!bindAddress.Equals (IPAddress.Any)) {
machineName = bindAddress.ToString ();
} else {
IPHostEntry hostEntry = Dns.Resolve (Dns.GetHostName ());
if (hostEntry.AddressList.Length == 0)
throw new RemotingException ("IP address could not be determined for this host");
// We DON'T want to take the resolved address from the hostEntry, since the socket
// should still bind to IPAddress.Any, so that we get the loopback too
machineName = hostEntry.AddressList[0].ToString ();
}
} else {
IPHostEntry hostEntry = Dns.GetHostByName (Dns.GetHostName ());
bindAddress = hostEntry.AddressList[0];
machineName = hostEntry.HostName;
}
}
if (sinkProvider == null) {
//build a default chain that can handle wsdl, soap, binary
sinkProvider = new SdlChannelSinkProvider (); //for wsdl
sinkProvider.Next = new SoapServerFormatterSinkProvider ();
sinkProvider.Next.Next = new BinaryServerFormatterSinkProvider ();
}
//MS compat: channelData is null when port < 0
if (port >= 0) {
channelData = new ChannelDataStore (null);
IServerChannelSinkProvider provider = sinkProvider;
while (provider != null) {
provider.GetChannelData (channelData);
provider = provider.Next;
}
}
//create the sink chain and add an HTTP sink
IServerChannelSink nextSink = ChannelServices.CreateServerChannelSinkChain (sinkProvider, this);
sink = new HttpServerTransportSink (nextSink);
// BaseChannelWithProperties wants this to be set with the chain
base.SinksWithProperties = nextSink;
StartListening (null);
}
#endregion
#region IChannel
public string ChannelName
{
get { return name; }
}
public int ChannelPriority
{
get { return priority; }
}
public string Parse (string url, out string objectURI)
{
return HttpChannel.ParseInternal (url, out objectURI);
}
#endregion
public string GetChannelUri ()
{
return "http://" + machineName + ":" + port;
}
#region IChannelReceiver (: IChannel)
public object ChannelData
{
get
{
return suppressChannelData ? null
: channelData;
}
}
//from TcpServerChannel
public virtual string[] GetUrlsForUri (string objectUri)
{
if (!objectUri.StartsWith ("/"))
objectUri = "/" + objectUri;
if (channelData == null || channelData.ChannelUris == null || channelData.ChannelUris.Length < 1) {
return new string[] { GetChannelUri () + objectUri };
}
string[] channelUris = channelData.ChannelUris;
string[] result = new string[channelUris.Length];
for (int i = 0; i < channelUris.Length; i++)
result[i] = channelUris[i] + objectUri;
return result;
}
public void StartListening (object data)
{
if (listener != null)
return;
if (port < 0)
return;
try {
listener = new RemotingHttpListener (bindAddress, port, sink);
} catch (Exception) {
if (listener != null) {
listener.Dispose ();
listener = null;
}
throw;
}
if (port == 0)
port = listener.AssignedPort;
channelData.ChannelUris = new string [] { GetChannelUri () };
wantsToListen = false;
}
public void StopListening (object data)
{
if (listener != null) {
listener.Dispose ();
listener = null;
}
}
#endregion
#region BaseChannelWithProperties overrides
public override object this[object key]
{
get { return base[key]; }
set { base[key] = value; }
}
public override ICollection Keys
{
get { return new object[0]; }
}
#endregion
#region IChannelReceiverHook
public void AddHookChannelUri (string channelUri)
{
string [] newUris = new string[1] { channelUri };
if (channelData == null)
channelData = new ChannelDataStore (newUris);
else
channelData.ChannelUris = newUris;
wantsToListen = false;
}
public string ChannelScheme
{
get { return "http"; }
}
public IServerChannelSink ChannelSinkChain
{
get { return (IServerChannelSink)base.SinksWithProperties; }
}
public bool WantsToListen
{
get { return wantsToListen; }
set {
throw new NotImplementedException ("Behaviour not yet determined");
}
}
#endregion
}
}

View File

@@ -0,0 +1,212 @@
//
// HttpServerTransportSink.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Runtime.Remoting.Messaging;
namespace System.Runtime.Remoting.Channels.Http
{
class HttpServerTransportSink : IServerChannelSink
{
IServerChannelSink nextSink;
public HttpServerTransportSink (IServerChannelSink nextSink)
{
this.nextSink = nextSink;
}
public IServerChannelSink NextChannelSink
{
get { return nextSink; }
}
public void AsyncProcessResponse (IServerResponseChannelSinkStack sinkStack, object state,
IMessage msg, ITransportHeaders headers, Stream responseStream)
{
ContextWithId ctx = (ContextWithId) state;
WriteOut (ctx.Context, headers, responseStream);
ctx.Context.Response.Close ();
}
public Stream GetResponseStream (IServerResponseChannelSinkStack sinkStack, object state,
IMessage msg, ITransportHeaders headers)
{
return null;
}
public ServerProcessing ProcessMessage (IServerChannelSinkStack sinkStack, IMessage requestMsg,
ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
//we know we never call this
throw new NotSupportedException ();
}
public IDictionary Properties
{
get
{
if (nextSink != null)
return nextSink.Properties;
else return null;
}
}
internal void HandleRequest (HttpListenerContext context)
{
//build the headers
ITransportHeaders requestHeaders = new TransportHeaders ();
System.Collections.Specialized.NameValueCollection httpHeaders = context.Request.Headers;
foreach (string key in httpHeaders.Keys) {
requestHeaders[key] = httpHeaders[key];
}
//get an ID for this connection
ContextWithId identitiedContext = new ContextWithId (context);
requestHeaders[CommonTransportKeys.RequestUri] = context.Request.Url.PathAndQuery;
requestHeaders[CommonTransportKeys.IPAddress] = context.Request.RemoteEndPoint.Address;
requestHeaders[CommonTransportKeys.ConnectionId] = identitiedContext.ID;
requestHeaders["__RequestVerb"] = context.Request.HttpMethod;
requestHeaders["__HttpVersion"] = string.Format ("HTTP/{0}.{1}",
context.Request.ProtocolVersion.Major, context.Request.ProtocolVersion.Minor);
if (RemotingConfiguration.CustomErrorsEnabled (context.Request.IsLocal))
requestHeaders["__CustomErrorsEnabled"] = false;
IMessage responseMsg;
Stream responseStream;
ITransportHeaders responseHeaders;
// attach the context as state so that our async handler can use it to send the response
ServerChannelSinkStack sinkStack = new ServerChannelSinkStack ();
sinkStack.Push (this, identitiedContext);
// NOTE: if we copy the InputStream before passing it so the sinks, the .NET formatters have
// unspecified internal errors. Let's hope they don't need to seek the stream!
ServerProcessing proc = nextSink.ProcessMessage (sinkStack, null, requestHeaders, context.Request.InputStream,
out responseMsg, out responseHeaders, out responseStream);
switch (proc) {
case ServerProcessing.Complete:
WriteOut (context, responseHeaders, responseStream);
context.Response.Close ();
break;
case ServerProcessing.Async:
break;
case ServerProcessing.OneWay:
context.Response.Close ();
break;
}
}
internal ServerProcessing SynchronousDispatch (ITransportHeaders requestHeaders, Stream requestStream,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
IMessage responseMsg;
ContextWithId identitiedContext = new ContextWithId (null);
// attach the context as state so that our async handler can use it to send the response
ServerChannelSinkStack sinkStack = new ServerChannelSinkStack ();
sinkStack.Push (this, identitiedContext);
return nextSink.ProcessMessage (sinkStack, null, requestHeaders, requestStream,
out responseMsg, out responseHeaders, out responseStream);
}
static void WriteOut (HttpListenerContext context, ITransportHeaders responseHeaders, Stream responseStream)
{
//header processing taken/modified from HttpRemotingHandler
if (responseHeaders != null && responseHeaders["__HttpStatusCode"] != null) {
context.Response.StatusCode = Convert.ToInt32 (responseHeaders["__HttpStatusCode"]);
context.Response.StatusDescription = (string) responseHeaders["__HttpReasonPhrase"];
}
if (responseHeaders != null) {
foreach (DictionaryEntry entry in responseHeaders) {
string key = entry.Key.ToString ();
if (key != "__HttpStatusCode" && key != "__HttpReasonPhrase") {
context.Response.AddHeader ((string)entry.Key, responseHeaders[entry.Key].ToString ());
}
}
}
//we need a stream with a length, so if it's not a MemoryStream we copy it
MemoryStream ms;
if (responseStream is MemoryStream) {
ms = (MemoryStream) responseStream;
//this seems to be necessary for some third-party formatters
//even though my testing suggested .NET doesn't seem to seek incoming streams
ms.Position = 0;
} else {
ms = new MemoryStream ();
HttpClientTransportSink.CopyStream (responseStream, ms, 1024);
ms.Position = 0;
responseStream.Close ();
}
//FIXME: WHY DOES CHUNKING BREAK THE TESTS?
//for now, we set the content length so that the server doesn't use chunking
context.Response.ContentLength64 = ms.Length;
HttpClientTransportSink.CopyStream (ms, context.Response.OutputStream, 1024);
ms.Close ();
}
class ContextWithId
{
static object lockObj = new object ();
static int nextId = 0;
HttpListenerContext context;
int id;
public HttpListenerContext Context { get { return context; } }
public int ID { get { return id; } }
public ContextWithId (HttpListenerContext context)
{
this.context = context;
lock (lockObj) {
//FIXME: is it really valid to roll arund and reset the ID?
unchecked {
this.id = nextId++;
}
}
}
}
}
}

View File

@@ -0,0 +1,122 @@
//
// RemotingHttpListener.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.IO;
namespace System.Runtime.Remoting.Channels.Http
{
sealed class RemotingHttpListener : IDisposable
{
HttpServerTransportSink sink;
HttpListener listener;
int local_port;
public RemotingHttpListener (IPAddress addr, int port, HttpServerTransportSink sink)
{
this.sink = sink;
bool find_port = false;
if (port == 0)
find_port = true;
string address = null;
if (addr == IPAddress.Any)
address = "*";
#if NET_2_0
else if (addr == IPAddress.IPv6Any)
address = "*";
#endif
else
address = addr.ToString ();
listener = new HttpListener ();
while (true) {
Random rnd = null;
if (find_port) {
if (rnd == null)
rnd = new Random ();
port = rnd.Next (1025, 65000);
}
try {
listener.Prefixes.Add (String.Format ("http://{0}:{1}/", address, port));
listener.Start ();
local_port = port;
break;
} catch (Exception) {
if (!find_port)
throw;
listener.Prefixes.Clear ();
// Port already in use
}
}
listener.BeginGetContext (new AsyncCallback (OnGetContext), null);
}
public int AssignedPort
{
get { return local_port; }
}
void OnGetContext (IAsyncResult ares)
{
if (listener == null)
return; // already disposed
HttpListenerContext context = null;
try {
context = listener.EndGetContext (ares);
listener.BeginGetContext (new AsyncCallback (OnGetContext), null);
} catch {
// Listener was closed
}
if (context != null) {
try {
sink.HandleRequest (context);
} catch {
try {
context.Response.Close ();
} catch {}
}
}
}
public void Dispose ()
{
if (listener != null) {
listener.Close ();
listener = null;
}
}
}
}