HttpWebRequest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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. // TODO:
  179. // HTTP-date = rfc1123-date | rfc850-date | asctime-date
  180. // for now, assume rfc1123 only
  181. try {
  182. DateTime dt = DateTime.ParseExact (str, "r", null);
  183. return dt;
  184. } catch (Exception) {
  185. return DateTime.Now;
  186. }
  187. }
  188. set {
  189. CheckRequestStarted ();
  190. // rfc-1123 pattern
  191. webHeaders.SetInternal ("If-Modified-Since",
  192. value.ToUniversalTime ().ToString ("r", null));
  193. // TODO: check last param when using different locale
  194. }
  195. }
  196. public bool KeepAlive {
  197. get {
  198. CheckRequestStarted ();
  199. return keepAlive;
  200. }
  201. set {
  202. CheckRequestStarted ();
  203. keepAlive = value;
  204. }
  205. }
  206. public int MaximumAutomaticRedirections {
  207. get { return maxAutoRedirect; }
  208. set {
  209. if (value < 0)
  210. throw new ArgumentException ("value");
  211. maxAutoRedirect = value;
  212. }
  213. }
  214. public string MediaType {
  215. get { return mediaType; }
  216. set {
  217. CheckRequestStarted ();
  218. mediaType = value;
  219. }
  220. }
  221. public override string Method {
  222. get { return this.method; }
  223. set {
  224. CheckRequestStarted ();
  225. if (value == null ||
  226. (value != "GET" &&
  227. value != "HEAD" &&
  228. value != "POST" &&
  229. value != "PUT" &&
  230. value != "DELETE" &&
  231. value != "TRACE" &&
  232. value != "OPTIONS"))
  233. throw new ArgumentException ("not a valid method");
  234. if (contentLength != -1 &&
  235. value != "POST" &&
  236. value != "PUT")
  237. throw new ArgumentException ("method must be PUT or POST");
  238. method = value;
  239. }
  240. }
  241. public bool Pipelined {
  242. get { return pipelined; }
  243. set { this.pipelined = value; }
  244. }
  245. public override bool PreAuthenticate {
  246. get { return preAuthenticate; }
  247. set { preAuthenticate = value; }
  248. }
  249. public Version ProtocolVersion {
  250. get { return version; }
  251. set {
  252. if (value != HttpVersion.Version10 && value != HttpVersion.Version11)
  253. throw new ArgumentException ("value");
  254. version = (Version) value;
  255. }
  256. }
  257. public override IWebProxy Proxy {
  258. get { return proxy; }
  259. set {
  260. if (value == null)
  261. throw new ArgumentNullException ("value");
  262. proxy = value;
  263. }
  264. }
  265. public string Referer {
  266. get { return webHeaders ["Referer" ]; }
  267. set {
  268. CheckRequestStarted ();
  269. if (value == null || value.Trim().Length == 0) {
  270. webHeaders.RemoveInternal ("Referer");
  271. return;
  272. }
  273. webHeaders.SetInternal ("Referer", value);
  274. }
  275. }
  276. public override Uri RequestUri {
  277. get { return requestUri; }
  278. }
  279. public bool SendChunked {
  280. get { return sendChunked; }
  281. set {
  282. CheckRequestStarted ();
  283. sendChunked = value;
  284. }
  285. }
  286. public ServicePoint ServicePoint {
  287. get { return servicePoint; }
  288. }
  289. public override int Timeout {
  290. get { return timeout; }
  291. set { timeout = value; }
  292. }
  293. public string TransferEncoding {
  294. get { return webHeaders ["Transfer-Encoding"]; }
  295. set {
  296. CheckRequestStarted ();
  297. if (!sendChunked)
  298. throw new InvalidOperationException ("SendChunked must be True");
  299. string val = value;
  300. if (val != null)
  301. val = val.Trim ().ToLower ();
  302. if (val == null || val.Length == 0) {
  303. webHeaders.RemoveInternal ("Transfer-Encoding");
  304. return;
  305. }
  306. if (val == "chunked")
  307. throw new ArgumentException ("Cannot set value to Chunked");
  308. webHeaders.SetInternal ("Transfer-Encoding", value);
  309. }
  310. }
  311. public string UserAgent {
  312. get { return webHeaders ["User-Agent"]; }
  313. set { webHeaders.SetInternal ("User-Agent", value); }
  314. }
  315. // Methods
  316. public void AddRange (int range)
  317. {
  318. AddRange ("bytes", range);
  319. }
  320. public void AddRange (int from, int to)
  321. {
  322. AddRange ("bytes", from, to);
  323. }
  324. public void AddRange (string rangeSpecifier, int range)
  325. {
  326. if (rangeSpecifier == null)
  327. throw new ArgumentNullException ("rangeSpecifier");
  328. string value = webHeaders ["Range"];
  329. if (value == null || value.Length == 0)
  330. value = rangeSpecifier + "=";
  331. else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
  332. value += ",";
  333. else
  334. throw new InvalidOperationException ("rangeSpecifier");
  335. webHeaders.SetInternal ("Range", value + range + "-");
  336. }
  337. public void AddRange (string rangeSpecifier, int from, int to)
  338. {
  339. if (rangeSpecifier == null)
  340. throw new ArgumentNullException ("rangeSpecifier");
  341. if (from < 0 || to < 0 || from > to)
  342. throw new ArgumentOutOfRangeException ();
  343. string value = webHeaders ["Range"];
  344. if (value == null || value.Length == 0)
  345. value = rangeSpecifier + "=";
  346. else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
  347. value += ",";
  348. else
  349. throw new InvalidOperationException ("rangeSpecifier");
  350. webHeaders.SetInternal ("Range", value + from + "-" + to);
  351. }
  352. public override int GetHashCode ()
  353. {
  354. return base.GetHashCode ();
  355. }
  356. private delegate Stream GetRequestStreamCallback ();
  357. private delegate WebResponse GetResponseCallback ();
  358. public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state)
  359. {
  360. if (method == null || (!method.Equals ("PUT") && !method.Equals ("POST")))
  361. throw new ProtocolViolationException ("Cannot send file when method is: " + this.method + ". Method must be PUT.");
  362. // workaround for bug 24943
  363. Exception e = null;
  364. lock (this) {
  365. if (asyncResponding || webResponse != null)
  366. e = new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");
  367. else if (requesting)
  368. e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  369. else
  370. requesting = true;
  371. }
  372. if (e != null)
  373. throw e;
  374. /*
  375. lock (this) {
  376. if (asyncResponding || webResponse != null)
  377. throw new InvalidOperationException ("This operation cannot be performed after the request has been submitted.");
  378. if (requesting)
  379. throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  380. requesting = true;
  381. }
  382. */
  383. GetRequestStreamCallback c = new GetRequestStreamCallback (this.GetRequestStreamInternal);
  384. return c.BeginInvoke (callback, state);
  385. }
  386. public override Stream EndGetRequestStream (IAsyncResult asyncResult)
  387. {
  388. if (asyncResult == null)
  389. throw new ArgumentNullException ("asyncResult");
  390. if (!asyncResult.IsCompleted)
  391. asyncResult.AsyncWaitHandle.WaitOne ();
  392. AsyncResult async = (AsyncResult) asyncResult;
  393. GetRequestStreamCallback cb = (GetRequestStreamCallback) async.AsyncDelegate;
  394. return cb.EndInvoke (asyncResult);
  395. }
  396. public override Stream GetRequestStream()
  397. {
  398. IAsyncResult asyncResult = BeginGetRequestStream (null, null);
  399. if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {
  400. throw new WebException("The request timed out", WebExceptionStatus.Timeout);
  401. }
  402. return EndGetRequestStream (asyncResult);
  403. }
  404. internal Stream GetRequestStreamInternal ()
  405. {
  406. this.requestStream = null; // TODO: new HttpWebStream (this);
  407. return this.requestStream;
  408. }
  409. public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
  410. {
  411. // workaround for bug 24943
  412. Exception e = null;
  413. lock (this) {
  414. if (asyncResponding)
  415. e = new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  416. else
  417. asyncResponding = true;
  418. }
  419. if (e != null)
  420. throw e;
  421. /*
  422. lock (this) {
  423. if (asyncResponding)
  424. throw new InvalidOperationException ("Cannot re-call start of asynchronous method while a previous call is still in progress.");
  425. asyncResponding = true;
  426. }
  427. */
  428. GetResponseCallback c = new GetResponseCallback (this.GetResponseInternal);
  429. return c.BeginInvoke (callback, state);
  430. }
  431. public override WebResponse EndGetResponse (IAsyncResult asyncResult)
  432. {
  433. if (asyncResult == null)
  434. throw new ArgumentNullException ("asyncResult");
  435. if (!asyncResult.IsCompleted)
  436. asyncResult.AsyncWaitHandle.WaitOne ();
  437. AsyncResult async = (AsyncResult) asyncResult;
  438. GetResponseCallback cb = (GetResponseCallback) async.AsyncDelegate;
  439. WebResponse webResponse = cb.EndInvoke(asyncResult);
  440. asyncResponding = false;
  441. return webResponse;
  442. }
  443. public override WebResponse GetResponse()
  444. {
  445. IAsyncResult asyncResult = BeginGetResponse (null, null);
  446. if (!(asyncResult.AsyncWaitHandle.WaitOne (timeout, false))) {
  447. throw new WebException("The request timed out", WebExceptionStatus.Timeout);
  448. }
  449. return EndGetResponse (asyncResult);
  450. }
  451. public WebResponse GetResponseInternal ()
  452. {
  453. if (webResponse != null)
  454. return webResponse;
  455. lock (this) {
  456. if (requesting) {
  457. requestEndEvent = new AutoResetEvent (false);
  458. }
  459. }
  460. if (requestEndEvent != null) {
  461. requestEndEvent.WaitOne ();
  462. }
  463. Stream responseStream = null; // TODO: new HttpWebStream (this);
  464. this.webResponse = new HttpWebResponse (this.actualUri, method, responseStream);
  465. return (WebResponse) this.webResponse;
  466. }
  467. [MonoTODO]
  468. public override void Abort()
  469. {
  470. this.haveResponse = true;
  471. throw new NotImplementedException ();
  472. }
  473. [MonoTODO]
  474. void ISerializable.GetObjectData (SerializationInfo serializationInfo,
  475. StreamingContext streamingContext)
  476. {
  477. throw new NotImplementedException ();
  478. }
  479. // Private Methods
  480. private void CheckRequestStarted ()
  481. {
  482. if (requesting)
  483. throw new InvalidOperationException ("request started");
  484. }
  485. internal void Close ()
  486. {
  487. // already done in class below
  488. // if (requestStream != null) {
  489. // requestStream.Close ();
  490. // }
  491. lock (this) {
  492. requesting = false;
  493. if (requestEndEvent != null)
  494. requestEndEvent.Set ();
  495. // requestEndEvent = null;
  496. }
  497. }
  498. // Private Classes
  499. // to catch the Close called on the NetworkStream
  500. /*
  501. internal class HttpWebStream : Stream
  502. {
  503. HttpWebRequest webRequest;
  504. internal HttpWebStream (HttpWebRequest webRequest)
  505. : base (webRequest.RequestUri)
  506. {
  507. this.webRequest = webRequest;
  508. }
  509. public override void Close()
  510. {
  511. base.Close ();
  512. webRequest.Close ();
  513. }
  514. }
  515. */
  516. }
  517. }