| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- //
- // System.Runtime.Remoting.Channels.Http.HttpClientChannel
- //
- // Summary: Implements a client channel that transmits method calls over HTTP.
- //
- // Classes: public HttpClientChannel
- // internal HttpClientTransportSink
- // Authors:
- // Martin Willemoes Hansen ([email protected])
- // Ahmad Tantawy ([email protected])
- // Ahmad Kadry ([email protected])
- // Hussein Mehanna ([email protected])
- //
- // (C) 2003 Martin Willemoes Hansen
- //
- using System;
- using System.Collections;
- using System.IO;
- using System.Net;
- using System.Runtime.Remoting;
- using System.Runtime.Remoting.Channels;
- using System.Runtime.Remoting.Messaging;
- using System.Threading;
- using System.Text;
- namespace System.Runtime.Remoting.Channels.Http
- {
- public class HttpClientChannel : IChannelSender,IChannel
- {
- // Property Keys (purposely all lower-case)
- private const String ProxyNameKey = "proxyname";
- private const String ProxyPortKey = "proxyport";
- // Settings
- private int _channelPriority = 1; // channel priority
- private String _channelName = "http"; // channel name
- // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
- //private IWebProxy _proxyObject = WebProxy.GetDefaultProxy(); // proxy object for request, can be overridden in transport sink
- private IWebProxy _proxyObject = null;
- private String _proxyName = null;
- private int _proxyPort = -1;
- private int _clientConnectionLimit = 0; // bump connection limit to at least this number (only meaningful if > 0)
- private bool _bUseDefaultCredentials = false; // should default credentials be used?
-
- private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider
-
- private IMessageSink _msgSink;
- public HttpClientChannel()
- {
-
- SetupProvider(_sinkProvider);
- }
-
- public HttpClientChannel(String name, IClientChannelSinkProvider sinkProvider)
- {
- if(name != null)
- _channelName = name;
-
- SetupProvider(sinkProvider);
- }
-
- // constructor used by config file
- public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
- {
- if (properties != null)
- {
- foreach(DictionaryEntry Dict in properties)
- {
- switch(Dict.Key.ToString())
- {
- case "name":
- _channelName = Dict.Value.ToString();
- break;
- case "priority":
- _channelPriority = Convert.ToInt32(Dict.Value);
- break;
- case "clientConnectionLimit":
- _clientConnectionLimit = Convert.ToInt32(Dict.Value);
- break;
- case "proxyName":
- _proxyName = Dict.Value.ToString();
- break;
- case "proxyPort":
- _proxyPort = Convert.ToInt32(Dict.Value);
- break;
- case "useDefaultCredentials":
- _bUseDefaultCredentials = Convert.ToBoolean(Dict.Value);
- break;
- }
- }
- }
-
- SetupProvider (sinkProvider);
- }
-
-
- public int ChannelPriority
- {
- get { return _channelPriority; }
- }
- public String ChannelName
- {
- get { return _channelName; }
- }
- // returns channelURI and places object uri into out parameter
-
- public String Parse(String url, out String objectURI)
- {
- return HttpHelper.Parse(url,out objectURI);
- }
- //
- // end of IChannel implementation
- //
- //
- // IChannelSender implementation
- //
-
- public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI)
- {
- if (url == null && remoteChannelData != null && remoteChannelData as IChannelDataStore != null )
- {
- IChannelDataStore ds = (IChannelDataStore) remoteChannelData;
- url = ds.ChannelUris[0];
- }
- if(url != null && HttpHelper.StartsWithHttp(url))
- {
- HttpHelper.Parse(url, out objectURI);
- _msgSink = (IMessageSink) _sinkProvider.CreateSink(this,url,remoteChannelData);
-
- if(_msgSink !=null )
- SetServicePoint(url);
- return _msgSink;
- }
- else
- {
- objectURI = null;
- return null;
- }
- }
- private void UpdateProxy()
- {
- // If the user values for the proxy object are valid , then the proxy
- // object will be created based on these values , if not it'll have the
- // value given when declared , as a default proxy object
- if(_proxyName!=null && _proxyPort !=-1)
- _proxyObject = new WebProxy(_proxyName,_proxyPort);
-
- // Either it's default or not it'll have this property
- ((WebProxy)_proxyObject).BypassProxyOnLocal = true;
- }
- private void SetServicePoint(string channelURI)
- {
- // Find a ServicePoint for the given url and assign the connection limit
- // to the user given value only if it valid
- ServicePoint sp = ServicePointManager.FindServicePoint(channelURI,ProxyObject);
- if(_clientConnectionLimit> 0)
- sp.ConnectionLimit = _clientConnectionLimit;
- }
- internal IWebProxy ProxyObject { get { return _proxyObject; } }
- internal bool UseDefaultCredentials { get { return _bUseDefaultCredentials; } }
- private void SetupProvider(IClientChannelSinkProvider sinkProvider)
- {
- if(sinkProvider == null)
- {
- _sinkProvider = new SoapClientFormatterSinkProvider();
- _sinkProvider.Next = new HttpClientTransportSinkProvider();
- }
- else
- {
- IClientChannelSinkProvider dummySinkProvider;
- dummySinkProvider = sinkProvider;
- _sinkProvider = sinkProvider;
- while(dummySinkProvider.Next != null)
- {
- dummySinkProvider = dummySinkProvider.Next;
- }
- dummySinkProvider.Next = new HttpClientTransportSinkProvider();
- }
-
- }
- }
- internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider
- {
- internal HttpClientTransportSinkProvider()
- {
- }
-
- public IClientChannelSink CreateSink(IChannelSender channel, String url,
- Object remoteChannelData)
- {
- // url is set to the channel uri in CreateMessageSink
- return new HttpClientTransportSink((HttpClientChannel)channel, url);
- }
- public IClientChannelSinkProvider Next
- {
- get { return null; }
- set { throw new NotSupportedException(); }
- }
- } // class HttpClientTransportSinkProvider
- // transport sender sink used by HttpClientChannel
- internal class HttpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink
- {
- private const String s_defaultVerb = "POST";
- private static String s_userAgent =
- "Mono Remoting Client (Mono CLR " + System.Environment.Version.ToString() + ")";
-
- // Property keys (purposely all lower-case)
- private const String UserNameKey = "username";
- private const String PasswordKey = "password";
- private const String DomainKey = "domain";
- private const String PreAuthenticateKey = "preauthenticate";
- private const String CredentialsKey = "credentials";
- private const String ClientCertificatesKey = "clientcertificates";
- private const String ProxyNameKey = "proxyname";
- private const String ProxyPortKey = "proxyport";
- private const String TimeoutKey = "timeout";
- private const String AllowAutoRedirectKey = "allowautoredirect";
- // If above keys get modified be sure to modify, the KeySet property on this
- // class.
- private static ICollection s_keySet = null;
- // Property values
- private String _securityUserName = null;
- private String _securityPassword = null;
- private String _securityDomain = null;
- private bool _bSecurityPreAuthenticate = false;
- private ICredentials _credentials = null; // this overrides all of the other security settings
- private int _timeout = System.Threading.Timeout.Infinite; // timeout value in milliseconds (only used if greater than 0)
- private bool _bAllowAutoRedirect = false;
- // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
- private IWebProxy _proxyObject = null; // overrides channel proxy object if non-null
- private String _proxyName = null;
- private int _proxyPort = -1;
- // Other members
- private HttpClientChannel _channel; // channel that created this sink
- private String _channelURI; // complete url to remote object
- // settings
- private bool _useChunked = false;
- // private bool _useKeepAlive = true;
- internal HttpClientTransportSink(HttpClientChannel channel, String channelURI) : base()
- {
- string dummy;
- _channel = channel;
- _channelURI = HttpHelper.Parse(channelURI,out dummy);
- }
-
- public void ProcessMessage(IMessage msg,
- ITransportHeaders requestHeaders, Stream requestStream,
- out ITransportHeaders responseHeaders, out Stream responseStream)
- {
- string url = null;
- string uri = ((IMethodCallMessage)msg).Uri;
- requestHeaders [CommonTransportKeys.RequestUri] = uri;
- CreateUrl(uri,out url);
- HttpWebRequest httpWebRequest = CreateWebRequest(url,requestHeaders,requestStream);
- SendAndRecieve(httpWebRequest,out responseHeaders,out responseStream);
- }
- public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg,
- ITransportHeaders headers, Stream stream)
- {
- string url = null;
- string uri = ((IMethodCallMessage)msg).Uri;
- headers [CommonTransportKeys.RequestUri] = uri;
- CreateUrl(uri,out url);
- HttpWebRequest httpWebRequest = CreateWebRequest(url,headers,stream);
- RequestState reqState = new RequestState(httpWebRequest,sinkStack);
- httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncRequestHandler),reqState);
- }
- private void AsyncRequestHandler(IAsyncResult ar)
- {
- HttpWebResponse httpWebResponse = null;
- RequestState reqState = (RequestState) ar.AsyncState;
- HttpWebRequest httpWebRequest = reqState.webRquest;
- IClientChannelSinkStack sinkStack = reqState.sinkStack;
- try
- {
- httpWebResponse = (HttpWebResponse) httpWebRequest.EndGetResponse(ar);
- }
- catch (WebException ex)
- {
- httpWebResponse = ex.Response as HttpWebResponse;
- if (httpWebResponse == null) sinkStack.DispatchException (ex);
- }
- Stream responseStream;
- ITransportHeaders responseHeaders;
- try
- {
- ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
- sinkStack.AsyncProcessResponse(responseHeaders,responseStream);
- }
- catch (Exception ex)
- {
- sinkStack.DispatchException (ex);
- }
- }
- public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, Object state,
- ITransportHeaders headers, Stream stream)
- {
- // We don't have to implement this since we are always last in the chain.
- } // AsyncProcessRequest
-
- public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
- {
- return null;
- } // GetRequestStream
- public IClientChannelSink NextChannelSink
- {
- get { return null; }
- }
-
- public override Object this[Object key]
- {
- get
- {
- String keyStr = key as String;
- if (keyStr == null)
- return null;
-
- switch (keyStr.ToLower())
- {
- case UserNameKey: return _securityUserName;
- case PasswordKey: return null; // Intentionally refuse to return password.
- case DomainKey: return _securityDomain;
- case PreAuthenticateKey: return _bSecurityPreAuthenticate;
- case CredentialsKey: return _credentials;
- case ClientCertificatesKey: return null; // Intentionally refuse to return certificates
- case ProxyNameKey: return _proxyName;
- case ProxyPortKey: return _proxyPort;
- case TimeoutKey: return _timeout;
- case AllowAutoRedirectKey: return _bAllowAutoRedirect;
- } // switch (keyStr.ToLower())
- return null;
- }
-
- set
- {
- String keyStr = key as String;
- if (keyStr == null)
- return;
-
- switch (keyStr.ToLower())
- {
- case UserNameKey: _securityUserName = (String)value; break;
- case PasswordKey: _securityPassword = (String)value; break;
- case DomainKey: _securityDomain = (String)value; break;
- case PreAuthenticateKey: _bSecurityPreAuthenticate = Convert.ToBoolean(value); break;
- case CredentialsKey: _credentials = (ICredentials)value; break;
- case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break;
- case ProxyPortKey: _proxyPort = Convert.ToInt32(value); UpdateProxy(); break;
- case TimeoutKey:
- {
- if (value is TimeSpan)
- _timeout = (int)((TimeSpan)value).TotalMilliseconds;
- else
- _timeout = Convert.ToInt32(value);
- break;
- } // case TimeoutKey
- case AllowAutoRedirectKey: _bAllowAutoRedirect = Convert.ToBoolean(value); break;
-
- } // switch (keyStr.ToLower())
- }
- } // this[]
-
- public override ICollection Keys
- {
- get
- {
- if (s_keySet == null)
- {
- // No need for synchronization
- ArrayList keys = new ArrayList(6);
- keys.Add(UserNameKey);
- keys.Add(PasswordKey);
- keys.Add(DomainKey);
- keys.Add(PreAuthenticateKey);
- keys.Add(CredentialsKey);
- keys.Add(ClientCertificatesKey);
- keys.Add(ProxyNameKey);
- keys.Add(ProxyPortKey);
- keys.Add(TimeoutKey);
- keys.Add(AllowAutoRedirectKey);
- s_keySet = keys;
- }
- return s_keySet;
- }
- }
- private void UpdateProxy()
- {
- // If the user values for the proxy object are valid , then the proxy
- // object will be created based on these values , if not it'll have the
- // value given when declared , as a default proxy object
- if(_proxyName!=null && _proxyPort !=-1)
- _proxyObject = new WebProxy(_proxyName,_proxyPort);
-
- // Either it's default or not it'll have this property
- ((WebProxy)_proxyObject).BypassProxyOnLocal = true;
- }
-
-
- internal static String UserAgent
- {
- get { return s_userAgent; }
- }
- private void CreateUrl(string uri, out string fullURL)
- {
- if(HttpHelper.StartsWithHttp(uri)) //this is a full url
- {
- fullURL = uri;
- return;
- }
- if(_channelURI.EndsWith("/") && uri.StartsWith("/"))
- {
- fullURL = _channelURI + uri.Substring(1);
- return;
- }
- else
- if(_channelURI.EndsWith("/") && !uri.StartsWith("/") ||
- !_channelURI.EndsWith("/") && uri.StartsWith("/") )
- {
- fullURL = _channelURI +uri;
- return;
- }
- else
- {
- fullURL = _channelURI +'/'+ uri;
- return;
- }
- }
- private HttpWebRequest CreateWebRequest(string url, ITransportHeaders requestHeaders, Stream requestStream)
- {
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);;
- request.AllowAutoRedirect = _bAllowAutoRedirect;
- request.ContentLength = requestStream.Length;
- request.Credentials = GetCredenentials();
- //request.Expect = "100-Continue";
-
- //This caused us some troubles with the HttpWebResponse class
- //maybe its fixed now. TODO
- //request.KeepAlive = _useKeepAlive;
- request.KeepAlive = false;;
-
- request.Method = s_defaultVerb;
- request.Pipelined = false;
- request.SendChunked = _useChunked;
- request.UserAgent = s_userAgent;
- // write the remoting headers
- IEnumerator headerenum = requestHeaders.GetEnumerator();
- while (headerenum.MoveNext())
- {
- DictionaryEntry entry = (DictionaryEntry) headerenum.Current;
- String key = entry.Key as String;
- if(key == "Content-Type")
- {
- request.ContentType = entry.Value.ToString();
- continue;
- }
- if (key == null || key.StartsWith("__"))
- {
- continue;
- }
- request.Headers.Add(entry.Key.ToString(),entry.Value.ToString());
- }
- Stream reqStream = request.GetRequestStream();
- if (requestStream is MemoryStream)
- {
- MemoryStream memStream = (MemoryStream)requestStream;
- reqStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
- }
- else
- HttpHelper.CopyStream(requestStream, reqStream);
- reqStream.Close();
-
- return request;
- }
-
- private void SendAndRecieve(HttpWebRequest httpRequest,out ITransportHeaders responseHeaders,out Stream responseStream)
- {
- responseStream = null;
- responseHeaders = null;
- HttpWebResponse httpWebResponse = null;
- try
- {
- httpWebResponse = (HttpWebResponse)httpRequest.GetResponse();
- }
- catch (WebException ex)
- {
- httpWebResponse = ex.Response as HttpWebResponse;
- if (httpWebResponse == null) throw ex;
- }
- ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
- }
- private void ReceiveResponse (HttpWebResponse httpWebResponse, out ITransportHeaders responseHeaders, out Stream responseStream)
- {
- responseHeaders = new TransportHeaders();
- try
- {
- Stream webStream = httpWebResponse.GetResponseStream();
- if (httpWebResponse.ContentLength != -1)
- {
- byte[] buffer = new byte [httpWebResponse.ContentLength];
- int nr = 0;
- while (nr < buffer.Length)
- nr += webStream.Read (buffer, nr, buffer.Length - nr);
- responseStream = new MemoryStream (buffer);
- }
- else
- {
- responseStream = new MemoryStream();
- HttpHelper.CopyStream(webStream, responseStream);
- }
- //Use the two commented lines below instead of the 3 below lines when HttpWebResponse
- //class is fully implemented in order to support custom headers
- //for(int i=0; i < httpWebResponse.Headers.Count; ++i)
- // responseHeaders[httpWebResponse.Headers.Keys[i].ToString()] = httpWebResponse.Headers[i].ToString();
- responseHeaders["Content-Type"] = httpWebResponse.ContentType;
- responseHeaders["Server"] = httpWebResponse.Server;
- responseHeaders["Content-Length"] = httpWebResponse.ContentLength;
- }
- finally
- {
- if(httpWebResponse!=null)
- httpWebResponse.Close();
- }
- }
- private void ProcessErrorCode()
- {
- }
- private ICredentials GetCredenentials()
- {
- if(_credentials!=null)
- return _credentials;
- //Now use the username , password and domain if provided
- if(_securityUserName==null ||_securityUserName=="")
- if(_channel.UseDefaultCredentials)
- return CredentialCache.DefaultCredentials;
- else
- return null;
- return new NetworkCredential(_securityUserName,_securityPassword,_securityDomain);
- }
-
- } // class HttpClientTransportSink
- internal class RequestState
- {
- public HttpWebRequest webRquest;
- public IClientChannelSinkStack sinkStack;
- public RequestState(HttpWebRequest wr,IClientChannelSinkStack ss)
- {
- webRquest = wr;
- sinkStack = ss;
- }
- }
- } // namespace System.Runtime.Remoting.Channels.Http
|