HttpClientChannel.cs 19 KB

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