PageRenderTime 25ms CodeModel.GetById 48ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System/System.Net/WebOperation.cs

https://github.com/kumpera/mono
C# | 344 lines | 265 code | 54 blank | 25 comment | 45 complexity | 31fdd867be41ccab4a767d874e284525 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, Unlicense
  1. //
  2. // WebOperation.cs
  3. //
  4. // Author:
  5. // Martin Baulig <mabaul@microsoft.com>
  6. //
  7. // Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. using System.IO;
  27. using System.Collections;
  28. using System.Net.Sockets;
  29. using System.Threading;
  30. using System.Threading.Tasks;
  31. using System.Runtime.ExceptionServices;
  32. using System.Diagnostics;
  33. namespace System.Net
  34. {
  35. class WebOperation
  36. {
  37. public HttpWebRequest Request {
  38. get;
  39. }
  40. public WebConnection Connection {
  41. get;
  42. private set;
  43. }
  44. public ServicePoint ServicePoint {
  45. get;
  46. private set;
  47. }
  48. public BufferOffsetSize WriteBuffer {
  49. get;
  50. }
  51. public bool IsNtlmChallenge {
  52. get;
  53. }
  54. #if MONO_WEB_DEBUG
  55. static int nextID;
  56. internal readonly int ID = ++nextID;
  57. #else
  58. internal readonly int ID;
  59. #endif
  60. public WebOperation (HttpWebRequest request, BufferOffsetSize writeBuffer, bool isNtlmChallenge, CancellationToken cancellationToken)
  61. {
  62. Request = request;
  63. WriteBuffer = writeBuffer;
  64. IsNtlmChallenge = isNtlmChallenge;
  65. cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken);
  66. requestTask = new TaskCompletionSource<WebRequestStream> ();
  67. requestWrittenTask = new TaskCompletionSource<WebRequestStream> ();
  68. completeResponseReadTask = new TaskCompletionSource<bool> ();
  69. responseTask = new TaskCompletionSource<WebResponseStream> ();
  70. finishedTask = new TaskCompletionSource<(bool, WebOperation)> ();
  71. }
  72. CancellationTokenSource cts;
  73. TaskCompletionSource<WebRequestStream> requestTask;
  74. TaskCompletionSource<WebRequestStream> requestWrittenTask;
  75. TaskCompletionSource<WebResponseStream> responseTask;
  76. TaskCompletionSource<bool> completeResponseReadTask;
  77. TaskCompletionSource<(bool, WebOperation)> finishedTask;
  78. WebRequestStream writeStream;
  79. WebResponseStream responseStream;
  80. ExceptionDispatchInfo disposedInfo;
  81. ExceptionDispatchInfo closedInfo;
  82. WebOperation priorityRequest;
  83. volatile bool finishedReading;
  84. int requestSent;
  85. public bool Aborted {
  86. get {
  87. if (disposedInfo != null || Request.Aborted)
  88. return true;
  89. if (cts != null && cts.IsCancellationRequested)
  90. return true;
  91. return false;
  92. }
  93. }
  94. public bool Closed {
  95. get {
  96. return Aborted || closedInfo != null;
  97. }
  98. }
  99. public void Abort ()
  100. {
  101. var (exception, disposed) = SetDisposed (ref disposedInfo);
  102. if (!disposed)
  103. return;
  104. cts?.Cancel ();
  105. SetCanceled ();
  106. Close ();
  107. }
  108. public void Close ()
  109. {
  110. var (exception, closed) = SetDisposed (ref closedInfo);
  111. if (!closed)
  112. return;
  113. var stream = Interlocked.Exchange (ref writeStream, null);
  114. if (stream != null) {
  115. try {
  116. stream.Close ();
  117. } catch { }
  118. }
  119. }
  120. void SetCanceled ()
  121. {
  122. requestTask.TrySetCanceled ();
  123. requestWrittenTask.TrySetCanceled ();
  124. responseTask.TrySetCanceled ();
  125. completeResponseReadTask.TrySetCanceled ();
  126. }
  127. void SetError (Exception error)
  128. {
  129. requestTask.TrySetException (error);
  130. requestWrittenTask.TrySetException (error);
  131. responseTask.TrySetException (error);
  132. completeResponseReadTask.TrySetException (error);
  133. }
  134. (ExceptionDispatchInfo, bool) SetDisposed (ref ExceptionDispatchInfo field)
  135. {
  136. var wexc = new WebException (SR.GetString (SR.net_webstatus_RequestCanceled), WebExceptionStatus.RequestCanceled);
  137. var exception = ExceptionDispatchInfo.Capture (wexc);
  138. var old = Interlocked.CompareExchange (ref field, exception, null);
  139. return (old ?? exception, old == null);
  140. }
  141. internal void ThrowIfDisposed ()
  142. {
  143. ThrowIfDisposed (CancellationToken.None);
  144. }
  145. internal void ThrowIfDisposed (CancellationToken cancellationToken)
  146. {
  147. if (Aborted || cancellationToken.IsCancellationRequested)
  148. ThrowDisposed (ref disposedInfo);
  149. }
  150. internal void ThrowIfClosedOrDisposed ()
  151. {
  152. ThrowIfClosedOrDisposed (CancellationToken.None);
  153. }
  154. internal void ThrowIfClosedOrDisposed (CancellationToken cancellationToken)
  155. {
  156. if (Closed || cancellationToken.IsCancellationRequested)
  157. ThrowDisposed (ref closedInfo);
  158. }
  159. void ThrowDisposed (ref ExceptionDispatchInfo field)
  160. {
  161. var (exception, disposed) = SetDisposed (ref field);
  162. if (disposed)
  163. cts?.Cancel ();
  164. exception.Throw ();
  165. }
  166. internal void RegisterRequest (ServicePoint servicePoint, WebConnection connection)
  167. {
  168. if (servicePoint == null)
  169. throw new ArgumentNullException (nameof (servicePoint));
  170. if (connection == null)
  171. throw new ArgumentNullException (nameof (connection));
  172. lock (this) {
  173. if (Interlocked.CompareExchange (ref requestSent, 1, 0) != 0)
  174. throw new InvalidOperationException ("Invalid nested call.");
  175. ServicePoint = servicePoint;
  176. Connection = connection;
  177. }
  178. cts.Token.Register (() => {
  179. Request.FinishedReading = true;
  180. SetDisposed (ref disposedInfo);
  181. });
  182. }
  183. public void SetPriorityRequest (WebOperation operation)
  184. {
  185. lock (this) {
  186. if (requestSent != 1 || ServicePoint == null || finishedReading)
  187. throw new InvalidOperationException ("Should never happen.");
  188. if (Interlocked.CompareExchange (ref priorityRequest, operation, null) != null)
  189. throw new InvalidOperationException ("Invalid nested request.");
  190. }
  191. }
  192. public Task<WebRequestStream> GetRequestStream ()
  193. {
  194. return requestTask.Task;
  195. }
  196. public Task WaitUntilRequestWritten ()
  197. {
  198. return requestWrittenTask.Task;
  199. }
  200. public WebRequestStream WriteStream {
  201. get {
  202. ThrowIfDisposed ();
  203. return writeStream;
  204. }
  205. }
  206. public Task<WebResponseStream> GetResponseStream ()
  207. {
  208. return responseTask.Task;
  209. }
  210. internal async Task<(bool, WebOperation)> WaitForCompletion (bool ignoreErrors)
  211. {
  212. try {
  213. return await finishedTask.Task.ConfigureAwait (false);
  214. } catch {
  215. if (ignoreErrors)
  216. return (false, null);
  217. throw;
  218. }
  219. }
  220. internal async void Run ()
  221. {
  222. try {
  223. FinishReading ();
  224. ThrowIfClosedOrDisposed ();
  225. var requestStream = await Connection.InitConnection (this, cts.Token).ConfigureAwait (false);
  226. ThrowIfClosedOrDisposed ();
  227. writeStream = requestStream;
  228. await requestStream.Initialize (cts.Token).ConfigureAwait (false);
  229. ThrowIfClosedOrDisposed ();
  230. requestTask.TrySetResult (requestStream);
  231. var stream = new WebResponseStream (requestStream);
  232. responseStream = stream;
  233. await stream.InitReadAsync (cts.Token).ConfigureAwait (false);
  234. responseTask.TrySetResult (stream);
  235. } catch (OperationCanceledException) {
  236. SetCanceled ();
  237. } catch (Exception e) {
  238. SetError (e);
  239. }
  240. }
  241. async void FinishReading ()
  242. {
  243. bool ok = false;
  244. Exception error = null;
  245. try {
  246. ok = await completeResponseReadTask.Task.ConfigureAwait (false);
  247. } catch (Exception e) {
  248. error = e;
  249. }
  250. WebResponseStream stream;
  251. WebOperation next;
  252. lock (this) {
  253. finishedReading = true;
  254. stream = Interlocked.Exchange (ref responseStream, null);
  255. next = Interlocked.Exchange (ref priorityRequest, null);
  256. Request.FinishedReading = true;
  257. }
  258. if (error != null) {
  259. if (next != null)
  260. next.SetError (error);
  261. finishedTask.TrySetException (error);
  262. return;
  263. }
  264. WebConnection.Debug ($"WO FINISH READING: Cnc={Connection?.ID} Op={ID} ok={ok} error={error != null} stream={stream != null} next={next != null}");
  265. var keepAlive = !Aborted && ok && (stream?.KeepAlive ?? false);
  266. if (next != null && next.Aborted) {
  267. next = null;
  268. keepAlive = false;
  269. }
  270. finishedTask.TrySetResult ((keepAlive, next));
  271. WebConnection.Debug ($"WO FINISH READING DONE: Cnc={Connection.ID} Op={ID} - {keepAlive} next={next?.ID}");
  272. }
  273. internal void CompleteRequestWritten (WebRequestStream stream, Exception error = null)
  274. {
  275. WebConnection.Debug ($"WO COMPLETE REQUEST WRITTEN: Op={ID} {error != null}");
  276. if (error != null)
  277. SetError (error);
  278. else
  279. requestWrittenTask.TrySetResult (stream);
  280. }
  281. internal void CompleteResponseRead (bool ok, Exception error = null)
  282. {
  283. WebConnection.Debug ($"WO COMPLETE RESPONSE READ: Op={ID} {ok} {error?.GetType ()}");
  284. if (error != null)
  285. completeResponseReadTask.TrySetException (error);
  286. else
  287. completeResponseReadTask.TrySetResult (ok);
  288. }
  289. }
  290. }