HttpWebRequest.cs 16 KB

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