HttpWebRequest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. //
  2. // System.Net.HttpWebRequest
  3. //
  4. // Author:
  5. // Lawrence Pit ([email protected])
  6. //
  7. using System;
  8. using System.Collections;
  9. using System.IO;
  10. using System.Net.Sockets;
  11. using System.Runtime.Remoting.Messaging;
  12. using System.Runtime.Serialization;
  13. using System.Security.Cryptography.X509Certificates;
  14. using System.Threading;
  15. namespace System.Net
  16. {
  17. [Serializable]
  18. public class HttpWebRequest : WebRequest, ISerializable
  19. {
  20. private Uri requestUri;
  21. private Uri actualUri = null;
  22. private bool allowAutoRedirect = true;
  23. private bool allowBuffering = true;
  24. private X509CertificateCollection certificate = null;
  25. private string connectionGroup = null;
  26. private long contentLength = -1;
  27. private HttpContinueDelegate continueDelegate = null;
  28. private CookieContainer cookieContainer = null;
  29. private ICredentials credentials = null;
  30. private bool haveResponse = false;
  31. private WebHeaderCollection webHeaders;
  32. private bool keepAlive = true;
  33. private int maxAutoRedirect = 50;
  34. private string mediaType = String.Empty;
  35. private string method;
  36. private bool pipelined = true;
  37. private bool preAuthenticate = false;
  38. private Version version;
  39. private IWebProxy proxy;
  40. private bool sendChunked = false;
  41. private ServicePoint servicePoint = null;
  42. private int timeout = System.Threading.Timeout.Infinite;
  43. private Stream requestStream = null;
  44. private HttpWebResponse webResponse = null;
  45. private AutoResetEvent requestEndEvent = null;
  46. private bool requesting = false;
  47. private bool asyncResponding = false;
  48. // Constructors
  49. internal HttpWebRequest (Uri uri)
  50. {
  51. this.requestUri = uri;
  52. this.actualUri = uri;
  53. this.webHeaders = new WebHeaderCollection (true);
  54. // this.webHeaders.SetInternal ("Host", uri.Authority);
  55. // this.webHeaders.SetInternal ("Date", DateTime.Now.ToUniversalTime ().ToString ("r", null));
  56. // this.webHeaders.SetInternal ("Expect", "100-continue");
  57. this.method = "GET";
  58. this.version = HttpVersion.Version11;
  59. this.proxy = GlobalProxySelection.Select;
  60. }
  61. [MonoTODO]
  62. protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext)
  63. {
  64. throw new NotImplementedException ();
  65. }
  66. // Properties
  67. public string Accept {
  68. get { return webHeaders ["Accept"]; }
  69. set {
  70. CheckRequestStarted ();
  71. webHeaders.SetInternal ("Accept", value);
  72. }
  73. }
  74. public Uri Address {
  75. get { return actualUri; }
  76. }
  77. public bool AllowAutoRedirect {
  78. get { return allowAutoRedirect; }
  79. set { this.allowAutoRedirect = value; }
  80. }
  81. public bool AllowWriteStreamBuffering {
  82. get { return allowBuffering; }
  83. set { this.allowBuffering = value; }
  84. }
  85. public X509CertificateCollection ClientCertificates {
  86. get { return certificate; }
  87. }
  88. public string Connection {
  89. get { return webHeaders ["Connection"]; }
  90. set {
  91. CheckRequestStarted ();
  92. string val = value;
  93. if (val != null)
  94. val = val.Trim ().ToLower ();
  95. if (val == null || val.Length == 0) {
  96. webHeaders.RemoveInternal ("Connection");
  97. return;
  98. }
  99. if (val == "keep-alive" || val == "close")
  100. throw new ArgumentException ("value");
  101. if (KeepAlive && val.IndexOf ("keep-alive") == -1)
  102. value = value + ", Keep-Alive";
  103. webHeaders.SetInternal ("Connection", value);
  104. }
  105. }
  106. public override string ConnectionGroupName {
  107. get { return connectionGroup; }
  108. set { connectionGroup = value; }
  109. }
  110. public override long ContentLength {
  111. get { return contentLength; }
  112. set {
  113. CheckRequestStarted ();
  114. if (value < 0)
  115. throw new ArgumentException ("value");
  116. contentLength = value;
  117. webHeaders.SetInternal ("Content-Length", Convert.ToString (value));
  118. }
  119. }
  120. public override string ContentType {
  121. get { return webHeaders ["Content-Type"]; }
  122. set {
  123. CheckRequestStarted ();
  124. if (value == null || value.Trim().Length == 0) {
  125. webHeaders.RemoveInternal ("Content-Type");
  126. return;
  127. }
  128. webHeaders.SetInternal ("Content-Type", value);
  129. }
  130. }
  131. public HttpContinueDelegate ContinueDelegate {
  132. get { return continueDelegate; }
  133. set { continueDelegate = value; }
  134. }
  135. public CookieContainer CookieContainer {
  136. get { return cookieContainer; }
  137. set { cookieContainer = value; }
  138. }
  139. public override ICredentials Credentials {
  140. get { return credentials; }
  141. set { credentials = value; }
  142. }
  143. public string Expect {
  144. get { return webHeaders ["Expect"]; }
  145. set {
  146. CheckRequestStarted ();
  147. string val = value;
  148. if (val != null)
  149. val = val.Trim ().ToLower ();
  150. if (val == null || val.Length == 0) {
  151. webHeaders.RemoveInternal ("Expect");
  152. return;
  153. }
  154. if (val == "100-continue")
  155. throw new ArgumentException ("value");
  156. webHeaders.SetInternal ("Expect", value);
  157. }
  158. }
  159. public bool HaveResponse {
  160. get { return haveResponse; }
  161. }
  162. public override WebHeaderCollection Headers {
  163. get { return webHeaders; }
  164. set {
  165. CheckRequestStarted ();
  166. WebHeaderCollection newHeaders = new WebHeaderCollection (true);
  167. int count = value.Count;
  168. for (int i = 0; i < count; i++)
  169. newHeaders.Add (value.GetKey (i), value.Get (i));
  170. webHeaders = newHeaders;
  171. }
  172. }
  173. public DateTime IfModifiedSince {
  174. get {
  175. string str = webHeaders ["If-Modified-Since"];
  176. if (str == null)
  177. return DateTime.Now;
  178. try {
  179. return MonoHttpDate.Parse (str);
  180. } catch (Exception) {
  181. return DateTime.Now;
  182. }
  183. }
  184. set {
  185. CheckRequestStarted ();
  186. // rfc-1123 pattern
  187. webHeaders.SetInternal ("If-Modified-Since",
  188. value.ToUniversalTime ().ToString ("r", null));
  189. // TODO: check last param when using different locale
  190. }
  191. }
  192. public bool KeepAlive {
  193. get {
  194. CheckRequestStarted ();
  195. return keepAlive;
  196. }
  197. set {
  198. CheckRequestStarted ();
  199. keepAlive = value;
  200. }
  201. }
  202. public int MaximumAutomaticRedirections {
  203. get { return maxAutoRedirect; }
  204. set {
  205. if (value < 0)
  206. throw new ArgumentException ("value");
  207. maxAutoRedirect = value;
  208. }
  209. }
  210. public string MediaType {
  211. get { return mediaType; }
  212. set {
  213. CheckRequestStarted ();
  214. mediaType = value;
  215. }
  216. }
  217. public override string Method {
  218. get { return this.method; }
  219. set {
  220. CheckRequestStarted ();
  221. if (value == null ||
  222. (value != "GET" &&
  223. value != "HEAD" &&
  224. value != "POST" &&
  225. value != "PUT" &&
  226. value != "DELETE" &&
  227. value != "TRACE" &&
  228. value != "OPTIONS"))
  229. throw new ArgumentException ("not a valid method");
  230. if (contentLength != -1 &&
  231. value != "POST" &&
  232. value != "PUT")
  233. throw new ArgumentException ("method must be PUT or POST");
  234. method = value;
  235. }
  236. }
  237. public bool Pipelined {
  238. get { return pipelined; }
  239. set { this.pipelined = value; }
  240. }
  241. public override bool PreAuthenticate {
  242. get { return preAuthenticate; }
  243. set { preAuthenticate = value; }
  244. }
  245. public Version ProtocolVersion {
  246. get { return version; }
  247. set {
  248. if (value != HttpVersion.Version10 && value != HttpVersion.Version11)
  249. throw new ArgumentException ("value");
  250. version = (Version) value;
  251. }
  252. }
  253. public override IWebProxy Proxy {
  254. get { return proxy; }
  255. set {
  256. if (value == null)
  257. throw new ArgumentNullException ("value");
  258. proxy = value;
  259. }
  260. }
  261. public string Referer {
  262. get { return webHeaders ["Referer" ]; }
  263. set {
  264. CheckRequestStarted ();
  265. if (value == null || value.Trim().Length == 0) {
  266. webHeaders.RemoveInternal ("Referer");
  267. return;
  268. }
  269. webHeaders.SetInternal ("Referer", value);
  270. }
  271. }
  272. public override Uri RequestUri {
  273. get { return requestUri; }
  274. }
  275. public bool SendChunked {
  276. get { return sendChunked; }
  277. set {
  278. CheckRequestStarted ();
  279. sendChunked = value;
  280. }
  281. }
  282. public ServicePoint ServicePoint {
  283. get { return servicePoint; }
  284. }
  285. public override int Timeout {
  286. get { return timeout; }
  287. set { timeout = value; }
  288. }
  289. public string TransferEncoding {
  290. get { return webHeaders ["Transfer-Encoding"]; }
  291. set {
  292. CheckRequestStarted ();
  293. if (!sendChunked)
  294. throw new InvalidOperationException ("SendChunked must be True");
  295. string val = value;
  296. if (val != null)
  297. val = val.Trim ().ToLower ();
  298. if (val == null || val.Length == 0) {
  299. webHeaders.RemoveInternal ("Transfer-Encoding");
  300. return;
  301. }
  302. if (val == "chunked")
  303. throw new ArgumentException ("Cannot set value to Chunked");
  304. webHeaders.SetInternal ("Transfer-Encoding", value);
  305. }
  306. }
  307. public string UserAgent {
  308. get { return webHeaders ["User-Agent"]; }
  309. set { webHeaders.SetInternal ("User-Agent", value); }
  310. }
  311. // Methods
  312. public void AddRange (int range)
  313. {
  314. AddRange ("bytes", range);
  315. }
  316. public void AddRange (int from, int to)
  317. {
  318. AddRange ("bytes", from, to);
  319. }
  320. public void AddRange (string rangeSpecifier, int range)
  321. {
  322. if (rangeSpecifier == null)
  323. throw new ArgumentNullException ("rangeSpecifier");
  324. string value = webHeaders ["Range"];
  325. if (value == null || value.Length == 0)
  326. value = rangeSpecifier + "=";
  327. else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
  328. value += ",";
  329. else
  330. throw new InvalidOperationException ("rangeSpecifier");
  331. webHeaders.SetInternal ("Range", value + range + "-");
  332. }
  333. public void AddRange (string rangeSpecifier, int from, int to)
  334. {
  335. if (rangeSpecifier == null)
  336. throw new ArgumentNullException ("rangeSpecifier");
  337. if (from < 0 || to < 0 || from > to)
  338. throw new ArgumentOutOfRangeException ();
  339. string value = webHeaders ["Range"];
  340. if (value == null || value.Length == 0)
  341. value = rangeSpecifier + "=";
  342. else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
  343. value += ",";
  344. else
  345. throw new InvalidOperationException ("rangeSpecifier");
  346. webHeaders.SetInternal ("Range", value + from + "-" + to);
  347. }
  348. public override int GetHashCode ()
  349. {
  350. return base.GetHashCode ();
  351. }
  352. private delegate Stream GetRequestStreamCallback ();
  353. private delegate WebResponse GetResponseCallback ();
  354. public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state)
  355. {
  356. if (method == null || (!method.Equals ("PUT") && !method.Equals ("POST")))
  357. throw new ProtocolViolationException ("Cannot send file when method is: " + this.method + ". Method must be PUT.");
  358. // workaround for bug 24943
  359. Exception e = null;
  360. lock (this) {
  361. if (asyncResponding || webResponse != null)
  362. e = new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");
  363. else if (requesting)
  364. e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  365. else
  366. requesting = true;
  367. }
  368. if (e != null)
  369. throw e;
  370. /*
  371. lock (this) {
  372. if (asyncResponding || webResponse != null)
  373. throw new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");
  374. if (requesting)
  375. throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  376. requesting = true;
  377. }
  378. */
  379. GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStreamInternal);
  380. return c.BeginInvoke (callback, state);
  381. }
  382. public override Stream EndGetRequestStream (IAsyncResult asyncResult)
  383. {
  384. if (asyncResult == null)
  385. throw new ArgumentNullException ("asyncResult");
  386. if (!asyncResult.IsCompleted)
  387. asyncResult.AsyncWaitHandle.WaitOne ();
  388. AsyncResult async = (AsyncResult) asyncResult;
  389. GetRequestStreamCallback cb = (GetRequestStreamCallback) async.AsyncDelegate;
  390. return cb.EndInvoke (asyncResult);
  391. }
  392. public override Stream GetRequestStream()
  393. {
  394. IAsyncResult asyncResult = BeginGetRequestStream (null, null);
  395. if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {
  396. throw new WebException("The request timed out", WebExceptionStatus.Timeout);
  397. }
  398. return EndGetRequestStream (asyncResult);
  399. }
  400. internal Stream GetRequestStreamInternal ()
  401. {
  402. this.requestStream = null; // TODO: new HttpWebStream (this);
  403. return this.requestStream;
  404. }
  405. public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
  406. {
  407. // workaround for bug 24943
  408. Exception e = null;
  409. lock (this) {
  410. if (asyncResponding)
  411. e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  412. else
  413. asyncResponding = true;
  414. }
  415. if (e != null)
  416. throw e;
  417. /*
  418. lock (this) {
  419. if (asyncResponding)
  420. throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  421. asyncResponding = true;
  422. }
  423. */
  424. GetResponseCallback c = new GetResponseCallback (this.GetResponseInternal);
  425. return c.BeginInvoke (callback, state);
  426. }
  427. public override WebResponse EndGetResponse (IAsyncResult asyncResult)
  428. {
  429. if (asyncResult == null)
  430. throw new ArgumentNullException ("asyncResult");
  431. if (!asyncResult.IsCompleted)
  432. asyncResult.AsyncWaitHandle.WaitOne ();
  433. AsyncResult async = (AsyncResult) asyncResult;
  434. GetResponseCallback cb = (GetResponseCallback) async.AsyncDelegate;
  435. WebResponse webResponse = cb.EndInvoke(asyncResult);
  436. asyncResponding = false;
  437. return webResponse;
  438. }
  439. public override WebResponse GetResponse()
  440. {
  441. IAsyncResult asyncResult = BeginGetResponse (null, null);
  442. if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {
  443. throw new WebException("The request timed out", WebExceptionStatus.Timeout);
  444. }
  445. return EndGetResponse (asyncResult);
  446. }
  447. public WebResponse GetResponseInternal ()
  448. {
  449. if (webResponse != null)
  450. return webResponse;
  451. lock (this) {
  452. if (requesting) {
  453. requestEndEvent = new AutoResetEvent (false);
  454. }
  455. }
  456. if (requestEndEvent != null) {
  457. requestEndEvent.WaitOne ();
  458. }
  459. Stream responseStream = null; // TODO: new HttpWebStream (this);
  460. this.webResponse = new HttpWebResponse (this.actualUri, method, responseStream);
  461. return (WebResponse) this.webResponse;
  462. }
  463. [MonoTODO]
  464. public override void Abort()
  465. {
  466. this.haveResponse = true;
  467. throw new NotImplementedException ();
  468. }
  469. [MonoTODO]
  470. void ISerializable.GetObjectData (SerializationInfo serializationInfo,
  471. StreamingContext streamingContext)
  472. {
  473. throw new NotImplementedException ();
  474. }
  475. // Private Methods
  476. private void CheckRequestStarted ()
  477. {
  478. if (requesting)
  479. throw new InvalidOperationException ("request started");
  480. }
  481. internal void Close ()
  482. {
  483. // already done in class below
  484. // if (requestStream != null) {
  485. // requestStream.Close ();
  486. // }
  487. lock (this) {
  488. requesting = false;
  489. if (requestEndEvent != null)
  490. requestEndEvent.Set ();
  491. // requestEndEvent = null;
  492. }
  493. }
  494. // Private Classes
  495. // to catch the Close called on the NetworkStream
  496. /*
  497. internal class HttpWebStream : Stream
  498. {
  499. HttpWebRequest webRequest;
  500. internal HttpWebStream (HttpWebRequest webRequest)
  501. : base (webRequest.RequestUri)
  502. {
  503. this.webRequest = webRequest;
  504. }
  505. public override void Close()
  506. {
  507. base.Close ();
  508. webRequest.Close ();
  509. }
  510. }
  511. */
  512. }
  513. }