HttpClientChannel.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. //
  2. // System.Runtime.Remoting.Channels.Http.HttpClientChannel
  3. //
  4. // Summary: Implements a client channel that transmits method calls over HTTP.
  5. //
  6. // Classes: public HttpClientChannel
  7. // internal HttpClientTransportSink
  8. // Authors:
  9. // Martin Willemoes Hansen ([email protected])
  10. // Ahmad Tantawy ([email protected])
  11. // Ahmad Kadry ([email protected])
  12. // Hussein Mehanna ([email protected])
  13. //
  14. // (C) 2003 Martin Willemoes Hansen
  15. //
  16. using System;
  17. using System.Collections;
  18. using System.IO;
  19. using System.Net;
  20. using System.Runtime.Remoting;
  21. using System.Runtime.Remoting.Channels;
  22. using System.Runtime.Remoting.Messaging;
  23. using System.Threading;
  24. using System.Text;
  25. namespace System.Runtime.Remoting.Channels.Http
  26. {
  27. public class HttpClientChannel : BaseChannelWithProperties, IChannelSender,IChannel
  28. {
  29. // Property Keys (purposely all lower-case)
  30. private const String ProxyNameKey = "proxyname";
  31. private const String ProxyPortKey = "proxyport";
  32. // Settings
  33. private int _channelPriority = 1; // channel priority
  34. private String _channelName = "http"; // channel name
  35. // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
  36. //private IWebProxy _proxyObject = WebProxy.GetDefaultProxy(); // proxy object for request, can be overridden in transport sink
  37. private IWebProxy _proxyObject = null;
  38. private String _proxyName = null;
  39. private int _proxyPort = -1;
  40. private int _clientConnectionLimit = 0; // bump connection limit to at least this number (only meaningful if > 0)
  41. private bool _bUseDefaultCredentials = false; // should default credentials be used?
  42. private IClientChannelSinkProvider _sinkProvider = null; // sink chain provider
  43. public HttpClientChannel()
  44. {
  45. SetupProvider (null,null);
  46. }
  47. public HttpClientChannel(String name, IClientChannelSinkProvider sinkProvider)
  48. {
  49. if(name != null)
  50. _channelName = name;
  51. SetupProvider (sinkProvider, null);
  52. }
  53. // constructor used by config file
  54. public HttpClientChannel(IDictionary properties, IClientChannelSinkProvider sinkProvider)
  55. {
  56. if (properties != null)
  57. {
  58. foreach(DictionaryEntry Dict in properties)
  59. {
  60. switch(Dict.Key.ToString())
  61. {
  62. case "name":
  63. _channelName = Dict.Value.ToString();
  64. break;
  65. case "priority":
  66. _channelPriority = Convert.ToInt32(Dict.Value);
  67. break;
  68. case "clientConnectionLimit":
  69. _clientConnectionLimit = Convert.ToInt32(Dict.Value);
  70. break;
  71. case "proxyName":
  72. _proxyName = Dict.Value.ToString();
  73. break;
  74. case "proxyPort":
  75. _proxyPort = Convert.ToInt32(Dict.Value);
  76. break;
  77. case "useDefaultCredentials":
  78. _bUseDefaultCredentials = Convert.ToBoolean(Dict.Value);
  79. break;
  80. }
  81. }
  82. }
  83. SetupProvider (sinkProvider, properties);
  84. }
  85. public int ChannelPriority
  86. {
  87. get { return _channelPriority; }
  88. }
  89. public String ChannelName
  90. {
  91. get { return _channelName; }
  92. }
  93. // returns channelURI and places object uri into out parameter
  94. public String Parse(String url, out String objectURI)
  95. {
  96. return HttpHelper.Parse(url,out objectURI);
  97. }
  98. //
  99. // end of IChannel implementation
  100. //
  101. //
  102. // IChannelSender implementation
  103. //
  104. public virtual IMessageSink CreateMessageSink(String url, Object remoteChannelData, out String objectURI)
  105. {
  106. if (url == null && remoteChannelData != null && remoteChannelData as IChannelDataStore != null )
  107. {
  108. IChannelDataStore ds = (IChannelDataStore) remoteChannelData;
  109. url = ds.ChannelUris[0];
  110. }
  111. if(url != null && HttpHelper.StartsWithHttp(url))
  112. {
  113. HttpHelper.Parse(url, out objectURI);
  114. IMessageSink msgSink = (IMessageSink) _sinkProvider.CreateSink(this,url,remoteChannelData);
  115. if(msgSink !=null )
  116. SetServicePoint(url);
  117. return msgSink;
  118. }
  119. else
  120. {
  121. objectURI = null;
  122. return null;
  123. }
  124. }
  125. private void UpdateProxy()
  126. {
  127. // If the user values for the proxy object are valid , then the proxy
  128. // object will be created based on these values , if not it'll have the
  129. // value given when declared , as a default proxy object
  130. if(_proxyName!=null && _proxyPort !=-1)
  131. _proxyObject = new WebProxy(_proxyName,_proxyPort);
  132. // Either it's default or not it'll have this property
  133. ((WebProxy)_proxyObject).BypassProxyOnLocal = true;
  134. }
  135. private void SetServicePoint(string channelURI)
  136. {
  137. // Find a ServicePoint for the given url and assign the connection limit
  138. // to the user given value only if it valid
  139. ServicePoint sp = ServicePointManager.FindServicePoint(channelURI,ProxyObject);
  140. if(_clientConnectionLimit> 0)
  141. sp.ConnectionLimit = _clientConnectionLimit;
  142. }
  143. internal IWebProxy ProxyObject { get { return _proxyObject; } }
  144. internal bool UseDefaultCredentials { get { return _bUseDefaultCredentials; } }
  145. private void SetupProvider (IClientChannelSinkProvider sinkProvider, IDictionary properties)
  146. {
  147. if (properties == null) properties = new Hashtable ();
  148. HttpClientTransportSinkProvider httpSink = new HttpClientTransportSinkProvider (properties);
  149. SinksWithProperties = httpSink;
  150. if(sinkProvider == null)
  151. {
  152. _sinkProvider = new SoapClientFormatterSinkProvider();
  153. _sinkProvider.Next = httpSink;
  154. }
  155. else
  156. {
  157. IClientChannelSinkProvider dummySinkProvider;
  158. dummySinkProvider = sinkProvider;
  159. _sinkProvider = sinkProvider;
  160. while(dummySinkProvider.Next != null)
  161. {
  162. dummySinkProvider = dummySinkProvider.Next;
  163. }
  164. dummySinkProvider.Next = httpSink;
  165. }
  166. }
  167. public override object this [object key]
  168. {
  169. get { return Properties[key]; }
  170. set { Properties[key] = value; }
  171. }
  172. public override ICollection Keys
  173. {
  174. get { return Properties.Keys; }
  175. }
  176. }
  177. internal class HttpClientTransportSinkProvider : IClientChannelSinkProvider, IChannelSinkBase
  178. {
  179. IDictionary _properties;
  180. internal HttpClientTransportSinkProvider (IDictionary properties)
  181. {
  182. _properties = properties;
  183. }
  184. public IClientChannelSink CreateSink(IChannelSender channel, String url,
  185. Object remoteChannelData)
  186. {
  187. // url is set to the channel uri in CreateMessageSink
  188. return new HttpClientTransportSink((HttpClientChannel)channel, url);
  189. }
  190. public IClientChannelSinkProvider Next
  191. {
  192. get { return null; }
  193. set { throw new NotSupportedException(); }
  194. }
  195. public IDictionary Properties
  196. {
  197. get { return _properties; }
  198. }
  199. } // class HttpClientTransportSinkProvider
  200. // transport sender sink used by HttpClientChannel
  201. internal class HttpClientTransportSink : BaseChannelSinkWithProperties, IClientChannelSink
  202. {
  203. private const String s_defaultVerb = "POST";
  204. private static String s_userAgent =
  205. "Mono Remoting Client (Mono CLR " + System.Environment.Version.ToString() + ")";
  206. // Property keys (purposely all lower-case)
  207. private const String UserNameKey = "username";
  208. private const String PasswordKey = "password";
  209. private const String DomainKey = "domain";
  210. private const String PreAuthenticateKey = "preauthenticate";
  211. private const String CredentialsKey = "credentials";
  212. private const String ClientCertificatesKey = "clientcertificates";
  213. private const String ProxyNameKey = "proxyname";
  214. private const String ProxyPortKey = "proxyport";
  215. private const String TimeoutKey = "timeout";
  216. private const String AllowAutoRedirectKey = "allowautoredirect";
  217. // If above keys get modified be sure to modify, the KeySet property on this
  218. // class.
  219. private static ICollection s_keySet = null;
  220. // Property values
  221. private String _securityUserName = null;
  222. private String _securityPassword = null;
  223. private String _securityDomain = null;
  224. private bool _bSecurityPreAuthenticate = false;
  225. private ICredentials _credentials = null; // this overrides all of the other security settings
  226. private int _timeout = System.Threading.Timeout.Infinite; // timeout value in milliseconds (only used if greater than 0)
  227. private bool _bAllowAutoRedirect = false;
  228. // Proxy settings (_proxyObject gets recreated when _proxyName and _proxyPort are updated)
  229. private IWebProxy _proxyObject = null; // overrides channel proxy object if non-null
  230. private String _proxyName = null;
  231. private int _proxyPort = -1;
  232. // Other members
  233. private HttpClientChannel _channel; // channel that created this sink
  234. private String _channelURI; // complete url to remote object
  235. // settings
  236. private bool _useChunked = false;
  237. // private bool _useKeepAlive = true;
  238. internal HttpClientTransportSink(HttpClientChannel channel, String channelURI) : base()
  239. {
  240. string dummy;
  241. _channel = channel;
  242. _channelURI = HttpHelper.Parse(channelURI,out dummy);
  243. }
  244. public void ProcessMessage(IMessage msg,
  245. ITransportHeaders requestHeaders, Stream requestStream,
  246. out ITransportHeaders responseHeaders, out Stream responseStream)
  247. {
  248. string url = null;
  249. string uri = ((IMethodCallMessage)msg).Uri;
  250. requestHeaders [CommonTransportKeys.RequestUri] = uri;
  251. CreateUrl(uri,out url);
  252. HttpWebRequest httpWebRequest = CreateWebRequest(url,requestHeaders,requestStream);
  253. SendAndRecieve(httpWebRequest,out responseHeaders,out responseStream);
  254. }
  255. public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg,
  256. ITransportHeaders headers, Stream stream)
  257. {
  258. string url = null;
  259. string uri = ((IMethodCallMessage)msg).Uri;
  260. headers [CommonTransportKeys.RequestUri] = uri;
  261. CreateUrl(uri,out url);
  262. HttpWebRequest httpWebRequest = CreateWebRequest(url,headers,stream);
  263. RequestState reqState = new RequestState(httpWebRequest,sinkStack);
  264. httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncRequestHandler),reqState);
  265. }
  266. private void AsyncRequestHandler(IAsyncResult ar)
  267. {
  268. HttpWebResponse httpWebResponse = null;
  269. RequestState reqState = (RequestState) ar.AsyncState;
  270. HttpWebRequest httpWebRequest = reqState.webRquest;
  271. IClientChannelSinkStack sinkStack = reqState.sinkStack;
  272. try
  273. {
  274. httpWebResponse = (HttpWebResponse) httpWebRequest.EndGetResponse(ar);
  275. }
  276. catch (WebException ex)
  277. {
  278. httpWebResponse = ex.Response as HttpWebResponse;
  279. if (httpWebResponse == null) sinkStack.DispatchException (ex);
  280. }
  281. Stream responseStream;
  282. ITransportHeaders responseHeaders;
  283. try
  284. {
  285. ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
  286. sinkStack.AsyncProcessResponse(responseHeaders,responseStream);
  287. }
  288. catch (Exception ex)
  289. {
  290. sinkStack.DispatchException (ex);
  291. }
  292. }
  293. public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, Object state,
  294. ITransportHeaders headers, Stream stream)
  295. {
  296. // We don't have to implement this since we are always last in the chain.
  297. } // AsyncProcessRequest
  298. public Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
  299. {
  300. return null;
  301. } // GetRequestStream
  302. public IClientChannelSink NextChannelSink
  303. {
  304. get { return null; }
  305. }
  306. public override Object this[Object key]
  307. {
  308. get
  309. {
  310. String keyStr = key as String;
  311. if (keyStr == null)
  312. return null;
  313. switch (keyStr.ToLower())
  314. {
  315. case UserNameKey: return _securityUserName;
  316. case PasswordKey: return null; // Intentionally refuse to return password.
  317. case DomainKey: return _securityDomain;
  318. case PreAuthenticateKey: return _bSecurityPreAuthenticate;
  319. case CredentialsKey: return _credentials;
  320. case ClientCertificatesKey: return null; // Intentionally refuse to return certificates
  321. case ProxyNameKey: return _proxyName;
  322. case ProxyPortKey: return _proxyPort;
  323. case TimeoutKey: return _timeout;
  324. case AllowAutoRedirectKey: return _bAllowAutoRedirect;
  325. } // switch (keyStr.ToLower())
  326. return null;
  327. }
  328. set
  329. {
  330. String keyStr = key as String;
  331. if (keyStr == null)
  332. return;
  333. switch (keyStr.ToLower())
  334. {
  335. case UserNameKey: _securityUserName = (String)value; break;
  336. case PasswordKey: _securityPassword = (String)value; break;
  337. case DomainKey: _securityDomain = (String)value; break;
  338. case PreAuthenticateKey: _bSecurityPreAuthenticate = Convert.ToBoolean(value); break;
  339. case CredentialsKey: _credentials = (ICredentials)value; break;
  340. case ProxyNameKey: _proxyName = (String)value; UpdateProxy(); break;
  341. case ProxyPortKey: _proxyPort = Convert.ToInt32(value); UpdateProxy(); break;
  342. case TimeoutKey:
  343. {
  344. if (value is TimeSpan)
  345. _timeout = (int)((TimeSpan)value).TotalMilliseconds;
  346. else
  347. _timeout = Convert.ToInt32(value);
  348. break;
  349. } // case TimeoutKey
  350. case AllowAutoRedirectKey: _bAllowAutoRedirect = Convert.ToBoolean(value); break;
  351. } // switch (keyStr.ToLower())
  352. }
  353. } // this[]
  354. public override ICollection Keys
  355. {
  356. get
  357. {
  358. if (s_keySet == null)
  359. {
  360. // No need for synchronization
  361. ArrayList keys = new ArrayList(6);
  362. keys.Add(UserNameKey);
  363. keys.Add(PasswordKey);
  364. keys.Add(DomainKey);
  365. keys.Add(PreAuthenticateKey);
  366. keys.Add(CredentialsKey);
  367. keys.Add(ClientCertificatesKey);
  368. keys.Add(ProxyNameKey);
  369. keys.Add(ProxyPortKey);
  370. keys.Add(TimeoutKey);
  371. keys.Add(AllowAutoRedirectKey);
  372. s_keySet = keys;
  373. }
  374. return s_keySet;
  375. }
  376. }
  377. private void UpdateProxy()
  378. {
  379. // If the user values for the proxy object are valid , then the proxy
  380. // object will be created based on these values , if not it'll have the
  381. // value given when declared , as a default proxy object
  382. if(_proxyName!=null && _proxyPort !=-1)
  383. _proxyObject = new WebProxy(_proxyName,_proxyPort);
  384. // Either it's default or not it'll have this property
  385. ((WebProxy)_proxyObject).BypassProxyOnLocal = true;
  386. }
  387. internal static String UserAgent
  388. {
  389. get { return s_userAgent; }
  390. }
  391. private void CreateUrl(string uri, out string fullURL)
  392. {
  393. if(HttpHelper.StartsWithHttp(uri)) //this is a full url
  394. {
  395. fullURL = uri;
  396. return;
  397. }
  398. if(_channelURI.EndsWith("/") && uri.StartsWith("/"))
  399. {
  400. fullURL = _channelURI + uri.Substring(1);
  401. return;
  402. }
  403. else
  404. if(_channelURI.EndsWith("/") && !uri.StartsWith("/") ||
  405. !_channelURI.EndsWith("/") && uri.StartsWith("/") )
  406. {
  407. fullURL = _channelURI +uri;
  408. return;
  409. }
  410. else
  411. {
  412. fullURL = _channelURI +'/'+ uri;
  413. return;
  414. }
  415. }
  416. private HttpWebRequest CreateWebRequest(string url, ITransportHeaders requestHeaders, Stream requestStream)
  417. {
  418. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);;
  419. request.AllowAutoRedirect = _bAllowAutoRedirect;
  420. request.ContentLength = requestStream.Length;
  421. request.Credentials = GetCredenentials();
  422. //request.Expect = "100-Continue";
  423. //This caused us some troubles with the HttpWebResponse class
  424. //maybe its fixed now. TODO
  425. //request.KeepAlive = _useKeepAlive;
  426. request.KeepAlive = false;;
  427. request.Method = s_defaultVerb;
  428. request.Pipelined = false;
  429. request.SendChunked = _useChunked;
  430. request.UserAgent = s_userAgent;
  431. // write the remoting headers
  432. IEnumerator headerenum = requestHeaders.GetEnumerator();
  433. while (headerenum.MoveNext())
  434. {
  435. DictionaryEntry entry = (DictionaryEntry) headerenum.Current;
  436. String key = entry.Key as String;
  437. if(key == "Content-Type")
  438. {
  439. request.ContentType = entry.Value.ToString();
  440. continue;
  441. }
  442. if (key == null || key.StartsWith("__"))
  443. {
  444. continue;
  445. }
  446. request.Headers.Add(entry.Key.ToString(),entry.Value.ToString());
  447. }
  448. Stream reqStream = request.GetRequestStream();
  449. if (requestStream is MemoryStream)
  450. {
  451. MemoryStream memStream = (MemoryStream)requestStream;
  452. reqStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
  453. }
  454. else
  455. HttpHelper.CopyStream(requestStream, reqStream);
  456. reqStream.Close();
  457. return request;
  458. }
  459. private void SendAndRecieve(HttpWebRequest httpRequest,out ITransportHeaders responseHeaders,out Stream responseStream)
  460. {
  461. responseStream = null;
  462. responseHeaders = null;
  463. HttpWebResponse httpWebResponse = null;
  464. try
  465. {
  466. httpWebResponse = (HttpWebResponse)httpRequest.GetResponse();
  467. }
  468. catch (WebException ex)
  469. {
  470. httpWebResponse = ex.Response as HttpWebResponse;
  471. if (httpWebResponse == null) throw ex;
  472. }
  473. ReceiveResponse (httpWebResponse, out responseHeaders, out responseStream);
  474. }
  475. private void ReceiveResponse (HttpWebResponse httpWebResponse, out ITransportHeaders responseHeaders, out Stream responseStream)
  476. {
  477. responseHeaders = new TransportHeaders();
  478. try
  479. {
  480. Stream webStream = httpWebResponse.GetResponseStream();
  481. if (httpWebResponse.ContentLength != -1)
  482. {
  483. byte[] buffer = new byte [httpWebResponse.ContentLength];
  484. int nr = 0;
  485. while (nr < buffer.Length)
  486. nr += webStream.Read (buffer, nr, buffer.Length - nr);
  487. responseStream = new MemoryStream (buffer);
  488. }
  489. else
  490. {
  491. responseStream = new MemoryStream();
  492. HttpHelper.CopyStream(webStream, responseStream);
  493. }
  494. //Use the two commented lines below instead of the 3 below lines when HttpWebResponse
  495. //class is fully implemented in order to support custom headers
  496. //for(int i=0; i < httpWebResponse.Headers.Count; ++i)
  497. // responseHeaders[httpWebResponse.Headers.Keys[i].ToString()] = httpWebResponse.Headers[i].ToString();
  498. responseHeaders["Content-Type"] = httpWebResponse.ContentType;
  499. responseHeaders["Server"] = httpWebResponse.Server;
  500. responseHeaders["Content-Length"] = httpWebResponse.ContentLength;
  501. }
  502. finally
  503. {
  504. if(httpWebResponse!=null)
  505. httpWebResponse.Close();
  506. }
  507. }
  508. private void ProcessErrorCode()
  509. {
  510. }
  511. private ICredentials GetCredenentials()
  512. {
  513. if(_credentials!=null)
  514. return _credentials;
  515. //Now use the username , password and domain if provided
  516. if(_securityUserName==null ||_securityUserName=="")
  517. if(_channel.UseDefaultCredentials)
  518. return CredentialCache.DefaultCredentials;
  519. else
  520. return null;
  521. return new NetworkCredential(_securityUserName,_securityPassword,_securityDomain);
  522. }
  523. } // class HttpClientTransportSink
  524. internal class RequestState
  525. {
  526. public HttpWebRequest webRquest;
  527. public IClientChannelSinkStack sinkStack;
  528. public RequestState(HttpWebRequest wr,IClientChannelSinkStack ss)
  529. {
  530. webRquest = wr;
  531. sinkStack = ss;
  532. }
  533. }
  534. } // namespace System.Runtime.Remoting.Channels.Http