Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Channels / InitialServerConnectionReader.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.ServiceModel.Channels
6 {
7     using System;
8     using System.Diagnostics;
9     using System.IO;
10     using System.Runtime;
11     using System.Runtime.Diagnostics;
12     using System.ServiceModel;
13     using System.ServiceModel.Diagnostics;
14     using System.ServiceModel.Diagnostics.Application;
15
16
17     delegate IConnectionOrientedTransportFactorySettings TransportSettingsCallback(Uri via);
18     delegate void ConnectionClosedCallback(InitialServerConnectionReader connectionReader);
19
20     // Host for a connection that deals with structured close/abort and notifying the owner appropriately
21     // used for cases where no one else (channel, etc) actually owns the reader
22     abstract class InitialServerConnectionReader : IDisposable
23     {
24         int maxViaSize;
25         int maxContentTypeSize;
26         IConnection connection;
27         Action connectionDequeuedCallback;
28         ConnectionClosedCallback closedCallback;
29         bool isClosed;
30
31         protected InitialServerConnectionReader(IConnection connection, ConnectionClosedCallback closedCallback)
32             : this(connection, closedCallback,
33             ConnectionOrientedTransportDefaults.MaxViaSize, ConnectionOrientedTransportDefaults.MaxContentTypeSize)
34         {
35         }
36
37         protected InitialServerConnectionReader(IConnection connection, ConnectionClosedCallback closedCallback, int maxViaSize, int maxContentTypeSize)
38         {
39             if (connection == null)
40             {
41                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("connection");
42             }
43
44             if (closedCallback == null)
45             {
46                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("closedCallback");
47             }
48
49             this.connection = connection;
50             this.closedCallback = closedCallback;
51             this.maxContentTypeSize = maxContentTypeSize;
52             this.maxViaSize = maxViaSize;
53         }
54
55         public IConnection Connection
56         {
57             get { return connection; }
58         }
59
60         public Action ConnectionDequeuedCallback
61         {
62             get
63             {
64                 return this.connectionDequeuedCallback;
65             }
66
67             set
68             {
69                 this.connectionDequeuedCallback = value;
70             }
71         }
72
73         public Action GetConnectionDequeuedCallback()
74         {
75             Action dequeuedCallback = this.connectionDequeuedCallback;
76             this.connectionDequeuedCallback = null;
77             return dequeuedCallback;
78         }
79
80         protected bool IsClosed
81         {
82             get { return isClosed; }
83         }
84
85         protected int MaxContentTypeSize
86         {
87             get
88             {
89                 return maxContentTypeSize;
90             }
91         }
92
93         protected int MaxViaSize
94         {
95             get
96             {
97                 return maxViaSize;
98             }
99         }
100
101         object ThisLock
102         {
103             get { return this; }
104         }
105
106         // used by the listener to release the connection object so it can be closed at a later time
107         public void ReleaseConnection()
108         {
109             isClosed = true;
110             connection = null;
111         }
112
113         // for cached connections -- try to shut down gracefully if possible
114         public void CloseFromPool(TimeSpan timeout)
115         {
116             try
117             {
118                 Close(timeout);
119             }
120             catch (CommunicationException communicationException)
121             {
122                 DiagnosticUtility.TraceHandledException(communicationException, TraceEventType.Information);
123             }
124             catch (TimeoutException timeoutException)
125             {
126                 if (TD.CloseTimeoutIsEnabled())
127                 {
128                     TD.CloseTimeout(timeoutException.Message);
129                 }
130                 DiagnosticUtility.TraceHandledException(timeoutException, TraceEventType.Information);
131             }
132         }
133
134         public void Dispose()
135         {
136             lock (ThisLock)
137             {
138                 if (isClosed)
139                 {
140                     return;
141                 }
142
143                 this.isClosed = true;
144             }
145
146             IConnection connection = this.connection;
147             if (connection != null)
148             {
149                 connection.Abort();
150             }
151
152             if (this.connectionDequeuedCallback != null)
153             {
154                 this.connectionDequeuedCallback();
155             }
156         }
157
158         protected void Abort()
159         {
160             Abort(null);
161         }
162
163         internal void Abort(Exception e)
164         {
165             lock (ThisLock)
166             {
167                 if (isClosed)
168                     return;
169
170                 isClosed = true;
171             }
172
173             try
174             {
175                 if (e != null)
176                 {
177                     if (DiagnosticUtility.ShouldTraceError)
178                     {
179                         TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.ChannelConnectionDropped,
180                             SR.GetString(SR.TraceCodeChannelConnectionDropped), this, e);
181                     }
182                 }
183
184                 connection.Abort();
185             }
186             finally
187             {
188                 if (closedCallback != null)
189                 {
190                     closedCallback(this);
191                 }
192
193                 if (this.connectionDequeuedCallback != null)
194                 {
195                     this.connectionDequeuedCallback();
196                 }
197             }
198         }
199
200         protected void Close(TimeSpan timeout)
201         {
202             lock (ThisLock)
203             {
204                 if (isClosed)
205                     return;
206
207                 isClosed = true;
208             }
209
210             bool success = false;
211             try
212             {
213                 connection.Close(timeout, true);
214                 success = true;
215             }
216             finally
217             {
218                 if (!success)
219                 {
220                     connection.Abort();
221                 }
222
223                 if (closedCallback != null)
224                 {
225                     closedCallback(this);
226                 }
227
228                 if (this.connectionDequeuedCallback != null)
229                 {
230                     this.connectionDequeuedCallback();
231                 }
232             }
233         }
234
235         internal static void SendFault(IConnection connection, string faultString, byte[] drainBuffer, TimeSpan sendTimeout, int maxRead)
236         {
237
238             if (TD.ConnectionReaderSendFaultIsEnabled())
239             {
240                 TD.ConnectionReaderSendFault(faultString);
241             }
242
243             EncodedFault encodedFault = new EncodedFault(faultString);
244             TimeoutHelper timeoutHelper = new TimeoutHelper(sendTimeout);
245             try
246             {
247                 connection.Write(encodedFault.EncodedBytes, 0, encodedFault.EncodedBytes.Length, true, timeoutHelper.RemainingTime());
248                 connection.Shutdown(timeoutHelper.RemainingTime());
249             }
250             catch (CommunicationException e)
251             {
252                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
253                 connection.Abort();
254                 return;
255             }
256             catch (TimeoutException e)
257             {
258                 if (TD.SendTimeoutIsEnabled())
259                 {
260                     TD.SendTimeout(e.Message);
261                 }
262                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
263                 connection.Abort();
264                 return;
265             }
266
267             // make sure we read until EOF or a quota is hit
268             int read = 0;
269             int readTotal = 0;
270             for (;;)
271             {
272                 try
273                 {
274                     read = connection.Read(drainBuffer, 0, drainBuffer.Length, timeoutHelper.RemainingTime());
275                 }
276                 catch (CommunicationException e)
277                 {
278                     DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
279                     connection.Abort();
280                     return;
281                 }
282                 catch (TimeoutException e)
283                 {
284                     if (TD.SendTimeoutIsEnabled())
285                     {
286                         TD.SendTimeout(e.Message);
287                     }
288                     DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
289                     connection.Abort();
290                     return;
291                 }
292
293                 if (read == 0)
294                     break;
295
296                 readTotal += read;
297                 if (readTotal > maxRead || timeoutHelper.RemainingTime() <= TimeSpan.Zero)
298                 {
299                     connection.Abort();
300                     return;
301                 }
302             }
303
304             ConnectionUtilities.CloseNoThrow(connection, timeoutHelper.RemainingTime());
305         }
306
307         public static IAsyncResult BeginUpgradeConnection(IConnection connection, StreamUpgradeAcceptor upgradeAcceptor,
308             IDefaultCommunicationTimeouts defaultTimeouts, AsyncCallback callback, object state)
309         {
310             return new UpgradeConnectionAsyncResult(connection, upgradeAcceptor, defaultTimeouts, callback, state);
311         }
312
313         public static IConnection EndUpgradeConnection(IAsyncResult result)
314         {
315             // get our upgraded connection
316             return UpgradeConnectionAsyncResult.End(result);
317         }
318
319         public static IConnection UpgradeConnection(IConnection connection, StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts)
320         {
321             ConnectionStream connectionStream = new ConnectionStream(connection, defaultTimeouts);
322             Stream stream = upgradeAcceptor.AcceptUpgrade(connectionStream);
323             if (upgradeAcceptor is StreamSecurityUpgradeAcceptor)
324             {
325                 if (DiagnosticUtility.ShouldTraceInformation)
326                 {
327                     TraceUtility.TraceEvent(TraceEventType.Information,
328                         TraceCode.StreamSecurityUpgradeAccepted, SR.GetString(SR.TraceCodeStreamSecurityUpgradeAccepted),
329                         new StringTraceRecord("Type", upgradeAcceptor.GetType().ToString()), connection, null);
330                 }
331             }
332
333             return new StreamConnection(stream, connectionStream);
334         }
335
336         class UpgradeConnectionAsyncResult : AsyncResult
337         {
338             ConnectionStream connectionStream;
339             static AsyncCallback onAcceptUpgrade = Fx.ThunkCallback(new AsyncCallback(OnAcceptUpgrade));
340             IConnection connection;
341             StreamUpgradeAcceptor upgradeAcceptor;
342
343             public UpgradeConnectionAsyncResult(IConnection connection,
344                 StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts,
345                 AsyncCallback callback, object state)
346                 : base(callback, state)
347             {
348                 this.upgradeAcceptor = upgradeAcceptor;
349                 this.connectionStream = new ConnectionStream(connection, defaultTimeouts);
350                 bool completeSelf = false;
351
352                 IAsyncResult result = upgradeAcceptor.BeginAcceptUpgrade(connectionStream, onAcceptUpgrade, this);
353
354                 if (result.CompletedSynchronously)
355                 {
356                     CompleteAcceptUpgrade(result);
357                     completeSelf = true;
358                 }
359
360                 if (completeSelf)
361                 {
362                     base.Complete(true);
363                 }
364             }
365
366             public static IConnection End(IAsyncResult result)
367             {
368                 UpgradeConnectionAsyncResult thisPtr = AsyncResult.End<UpgradeConnectionAsyncResult>(result);
369                 return thisPtr.connection;
370             }
371
372             void CompleteAcceptUpgrade(IAsyncResult result)
373             {
374                 Stream stream;
375                 bool endSucceeded = false;
376                 try
377                 {
378                     stream = this.upgradeAcceptor.EndAcceptUpgrade(result);
379                     endSucceeded = true;
380                 }
381                 finally
382                 {
383                     if (upgradeAcceptor is StreamSecurityUpgradeAcceptor)
384                     {
385                         if (DiagnosticUtility.ShouldTraceInformation && endSucceeded)
386                         {
387                             TraceUtility.TraceEvent(TraceEventType.Information,
388                                 TraceCode.StreamSecurityUpgradeAccepted, SR.GetString(SR.TraceCodeStreamSecurityUpgradeAccepted),
389                                 new StringTraceRecord("Type", upgradeAcceptor.GetType().ToString()), this, null);
390                         }
391                     }
392                 }
393                 this.connection = new StreamConnection(stream, this.connectionStream);
394             }
395
396             static void OnAcceptUpgrade(IAsyncResult result)
397             {
398                 if (result.CompletedSynchronously)
399                     return;
400
401                 UpgradeConnectionAsyncResult thisPtr = (UpgradeConnectionAsyncResult)result.AsyncState;
402                 Exception completionException = null;
403                 try
404                 {
405                     thisPtr.CompleteAcceptUpgrade(result);
406                 }
407 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
408                 catch (Exception e)
409                 {
410                     if (Fx.IsFatal(e))
411                     {
412                         throw;
413                     }
414
415                     completionException = e;
416                 }
417
418                 thisPtr.Complete(false, completionException);
419             }
420         }
421     }
422 }