RequestContextBase.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Channels
  5. {
  6. using System;
  7. using System.Diagnostics;
  8. using System.Runtime;
  9. using System.ServiceModel;
  10. using System.ServiceModel.Diagnostics;
  11. using System.ServiceModel.Diagnostics.Application;
  12. abstract class RequestContextBase : RequestContext
  13. {
  14. TimeSpan defaultSendTimeout;
  15. TimeSpan defaultCloseTimeout;
  16. CommunicationState state = CommunicationState.Opened;
  17. Message requestMessage;
  18. Exception requestMessageException;
  19. bool replySent;
  20. bool replyInitiated;
  21. bool aborted;
  22. object thisLock = new object();
  23. protected RequestContextBase(Message requestMessage, TimeSpan defaultCloseTimeout, TimeSpan defaultSendTimeout)
  24. {
  25. this.defaultSendTimeout = defaultSendTimeout;
  26. this.defaultCloseTimeout = defaultCloseTimeout;
  27. this.requestMessage = requestMessage;
  28. }
  29. public void ReInitialize(Message requestMessage)
  30. {
  31. this.state = CommunicationState.Opened;
  32. this.requestMessageException = null;
  33. this.replySent = false;
  34. this.replyInitiated = false;
  35. this.aborted = false;
  36. this.requestMessage = requestMessage;
  37. }
  38. public override Message RequestMessage
  39. {
  40. get
  41. {
  42. if (this.requestMessageException != null)
  43. {
  44. #pragma warning suppress 56503 // [....], see outcome of DCR 50092
  45. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(this.requestMessageException);
  46. }
  47. return requestMessage;
  48. }
  49. }
  50. protected void SetRequestMessage(Message requestMessage)
  51. {
  52. Fx.Assert(this.requestMessageException == null, "Cannot have both a requestMessage and a requestException.");
  53. this.requestMessage = requestMessage;
  54. }
  55. protected void SetRequestMessage(Exception requestMessageException)
  56. {
  57. Fx.Assert(this.requestMessage == null, "Cannot have both a requestMessage and a requestException.");
  58. this.requestMessageException = requestMessageException;
  59. }
  60. protected bool ReplyInitiated
  61. {
  62. get { return this.replyInitiated; }
  63. }
  64. protected object ThisLock
  65. {
  66. get
  67. {
  68. return thisLock;
  69. }
  70. }
  71. public bool Aborted
  72. {
  73. get
  74. {
  75. return this.aborted;
  76. }
  77. }
  78. public TimeSpan DefaultCloseTimeout
  79. {
  80. get { return this.defaultCloseTimeout; }
  81. }
  82. public TimeSpan DefaultSendTimeout
  83. {
  84. get { return this.defaultSendTimeout; }
  85. }
  86. public override void Abort()
  87. {
  88. lock (ThisLock)
  89. {
  90. if (state == CommunicationState.Closed)
  91. return;
  92. state = CommunicationState.Closing;
  93. this.aborted = true;
  94. }
  95. if (DiagnosticUtility.ShouldTraceWarning)
  96. {
  97. TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.RequestContextAbort,
  98. SR.GetString(SR.TraceCodeRequestContextAbort), this);
  99. }
  100. try
  101. {
  102. this.OnAbort();
  103. }
  104. finally
  105. {
  106. state = CommunicationState.Closed;
  107. }
  108. }
  109. public override void Close()
  110. {
  111. this.Close(this.defaultCloseTimeout);
  112. }
  113. public override void Close(TimeSpan timeout)
  114. {
  115. if (timeout < TimeSpan.Zero)
  116. {
  117. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("timeout", timeout,
  118. SR.GetString(SR.ValueMustBeNonNegative)));
  119. }
  120. bool sendAck = false;
  121. lock (ThisLock)
  122. {
  123. if (state != CommunicationState.Opened)
  124. return;
  125. if (TryInitiateReply())
  126. {
  127. sendAck = true;
  128. }
  129. state = CommunicationState.Closing;
  130. }
  131. TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
  132. bool throwing = true;
  133. try
  134. {
  135. if (sendAck)
  136. {
  137. OnReply(null, timeoutHelper.RemainingTime());
  138. }
  139. OnClose(timeoutHelper.RemainingTime());
  140. state = CommunicationState.Closed;
  141. throwing = false;
  142. }
  143. finally
  144. {
  145. if (throwing)
  146. this.Abort();
  147. }
  148. }
  149. protected override void Dispose(bool disposing)
  150. {
  151. base.Dispose(disposing);
  152. if (!disposing)
  153. return;
  154. if (this.replySent)
  155. {
  156. this.Close();
  157. }
  158. else
  159. {
  160. this.Abort();
  161. }
  162. }
  163. protected abstract void OnAbort();
  164. protected abstract void OnClose(TimeSpan timeout);
  165. protected abstract void OnReply(Message message, TimeSpan timeout);
  166. protected abstract IAsyncResult OnBeginReply(Message message, TimeSpan timeout, AsyncCallback callback, object state);
  167. protected abstract void OnEndReply(IAsyncResult result);
  168. protected void ThrowIfInvalidReply()
  169. {
  170. if (state == CommunicationState.Closed || state == CommunicationState.Closing)
  171. {
  172. if (aborted)
  173. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationObjectAbortedException(SR.GetString(SR.RequestContextAborted)));
  174. else
  175. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().FullName));
  176. }
  177. if (this.replyInitiated)
  178. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReplyAlreadySent)));
  179. }
  180. /// <summary>
  181. /// Attempts to initiate the reply. If a reply is not initiated already (and the object is opened),
  182. /// then it initiates the reply and returns true. Otherwise, it returns false.
  183. /// </summary>
  184. protected bool TryInitiateReply()
  185. {
  186. lock (this.thisLock)
  187. {
  188. if ((this.state != CommunicationState.Opened) || this.replyInitiated)
  189. {
  190. return false;
  191. }
  192. else
  193. {
  194. this.replyInitiated = true;
  195. return true;
  196. }
  197. }
  198. }
  199. public override IAsyncResult BeginReply(Message message, AsyncCallback callback, object state)
  200. {
  201. return this.BeginReply(message, this.defaultSendTimeout, callback, state);
  202. }
  203. public override IAsyncResult BeginReply(Message message, TimeSpan timeout, AsyncCallback callback, object state)
  204. {
  205. // "null" is a valid reply (signals a 202-style "ack"), so we don't have a null-check here
  206. lock (this.thisLock)
  207. {
  208. this.ThrowIfInvalidReply();
  209. this.replyInitiated = true;
  210. }
  211. return OnBeginReply(message, timeout, callback, state);
  212. }
  213. public override void EndReply(IAsyncResult result)
  214. {
  215. OnEndReply(result);
  216. this.replySent = true;
  217. }
  218. public override void Reply(Message message)
  219. {
  220. this.Reply(message, this.defaultSendTimeout);
  221. }
  222. public override void Reply(Message message, TimeSpan timeout)
  223. {
  224. // "null" is a valid reply (signals a 202-style "ack"), so we don't have a null-check here
  225. lock (this.thisLock)
  226. {
  227. this.ThrowIfInvalidReply();
  228. this.replyInitiated = true;
  229. }
  230. this.OnReply(message, timeout);
  231. this.replySent = true;
  232. }
  233. // This method is designed for WebSocket only, and will only be used once the WebSocket response was sent.
  234. // For WebSocket, we never call HttpRequestContext.Reply to send the response back.
  235. // Instead we call AcceptWebSocket directly. So we need to set the replyInitiated and
  236. // replySent boolean to be true once the response was sent successfully. Otherwise when we
  237. // are disposing the HttpRequestContext, we will see a bunch of warnings in trace log.
  238. protected void SetReplySent()
  239. {
  240. lock (this.thisLock)
  241. {
  242. this.ThrowIfInvalidReply();
  243. this.replyInitiated = true;
  244. }
  245. this.replySent = true;
  246. }
  247. }
  248. class RequestContextMessageProperty : IDisposable
  249. {
  250. RequestContext context;
  251. object thisLock = new object();
  252. public RequestContextMessageProperty(RequestContext context)
  253. {
  254. this.context = context;
  255. }
  256. public static string Name
  257. {
  258. get { return "requestContext"; }
  259. }
  260. void IDisposable.Dispose()
  261. {
  262. bool success = false;
  263. RequestContext thisContext;
  264. lock (this.thisLock)
  265. {
  266. if (this.context == null)
  267. return;
  268. thisContext = this.context;
  269. this.context = null;
  270. }
  271. try
  272. {
  273. thisContext.Close();
  274. success = true;
  275. }
  276. catch (CommunicationException e)
  277. {
  278. DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
  279. }
  280. catch (TimeoutException e)
  281. {
  282. if (TD.CloseTimeoutIsEnabled())
  283. {
  284. TD.CloseTimeout(e.Message);
  285. }
  286. DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
  287. }
  288. finally
  289. {
  290. if (!success)
  291. {
  292. thisContext.Abort();
  293. }
  294. }
  295. }
  296. }
  297. }