5f90ffe2b82241f4390b9c112c0c36c0e0abaeb9
[mono.git] / mcs / class / System / Mono.Net.Security / AsyncProtocolRequest.cs
1 #if SECURITY_DEP
2 //
3 // AsyncProtocolRequest.cs
4 //
5 // Author:
6 //       Martin Baulig <martin.baulig@xamarin.com>
7 //
8 // Copyright (c) 2015 Xamarin, Inc.
9 //
10 using System;
11 using System.IO;
12 using System.Net;
13 using System.Net.Security;
14 using SD = System.Diagnostics;
15 using System.Threading;
16 using System.Threading.Tasks;
17
18 namespace Mono.Net.Security
19 {
20         delegate AsyncOperationStatus AsyncOperation (AsyncProtocolRequest asyncRequest, AsyncOperationStatus status);
21
22         class BufferOffsetSize
23         {
24                 public byte[] Buffer;
25                 public int Offset;
26                 public int Size;
27                 public int TotalBytes;
28                 public bool Complete;
29
30                 public int EndOffset {
31                         get { return Offset + Size; }
32                 }
33
34                 public int Remaining {
35                         get { return Buffer.Length - Offset - Size; }
36                 }
37
38                 public BufferOffsetSize (byte[] buffer, int offset, int size)
39                 {
40                         Buffer = buffer;
41                         Offset = offset;
42                         Size = size;
43                         Complete = false;
44                 }
45
46                 public override string ToString ()
47                 {
48                         return string.Format ("[BufferOffsetSize: {0} {1}]", Offset, Size);
49                 }
50         }
51
52         class BufferOffsetSize2 : BufferOffsetSize
53         {
54                 public readonly int InitialSize;
55
56                 public BufferOffsetSize2 (int size)
57                         : base (new byte [size], 0, 0)
58                 {
59                         InitialSize = size;
60                 }
61
62                 public void Reset ()
63                 {
64                         Offset = Size = 0;
65                         TotalBytes = 0;
66                         Buffer = new byte [InitialSize];
67                         Complete = false;
68                 }
69
70                 public void MakeRoom (int size)
71                 {
72                         if (Remaining >= size)
73                                 return;
74
75                         int missing = size - Remaining;
76                         if (Offset == 0 && Size == 0) {
77                                 Buffer = new byte [size];
78                                 return;
79                         }
80
81                         var buffer = new byte [Buffer.Length + missing];
82                         Buffer.CopyTo (buffer, 0);
83                         Buffer = buffer;
84                 }
85
86                 public void AppendData (byte[] buffer, int offset, int size)
87                 {
88                         MakeRoom (size);
89                         System.Buffer.BlockCopy (buffer, offset, Buffer, EndOffset, size);
90                         Size += size;
91                 }
92         }
93
94         enum AsyncOperationStatus {
95                 NotStarted,
96                 Initialize,
97                 Continue,
98                 Running,
99                 Complete,
100                 WantRead,
101                 WantWrite,
102                 ReadDone,
103                 FinishWrite
104         }
105
106         class AsyncProtocolRequest
107         {
108                 public readonly MobileAuthenticatedStream Parent;
109                 public readonly BufferOffsetSize UserBuffer;
110
111                 int RequestedSize;
112                 public int CurrentSize;
113                 public int UserResult;
114
115                 AsyncOperation Operation;
116                 int Status;
117
118                 public readonly int ID = ++next_id;
119                 static int next_id;
120
121                 public readonly LazyAsyncResult UserAsyncResult;
122
123                 public AsyncProtocolRequest (MobileAuthenticatedStream parent, LazyAsyncResult lazyResult, BufferOffsetSize userBuffer = null)
124                 {
125                         Parent = parent;
126                         UserAsyncResult = lazyResult;
127                         UserBuffer = userBuffer;
128                 }
129
130                 public bool CompleteWithError (Exception ex)
131                 {
132                         Status = (int)AsyncOperationStatus.Complete;
133                         if (UserAsyncResult == null)
134                                 return true;
135                         if (!UserAsyncResult.InternalPeekCompleted)
136                                 UserAsyncResult.InvokeCallback (ex);
137                         return false;
138                 }
139
140                 [SD.Conditional ("MARTIN_DEBUG")]
141                 protected void Debug (string message, params object[] args)
142                 {
143                         Parent.Debug ("AsyncProtocolRequest({0}:{1}): {2}", Parent.ID, ID, string.Format (message, args));
144                 }
145
146                 internal void RequestRead (int size)
147                 {
148                         var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantRead, (int)AsyncOperationStatus.Running);
149                         Debug ("RequestRead: {0} {1}", oldStatus, size);
150                         if (oldStatus == AsyncOperationStatus.Running)
151                                 RequestedSize = size;
152                         else if (oldStatus == AsyncOperationStatus.WantRead)
153                                 RequestedSize += size;
154                         else if (oldStatus != AsyncOperationStatus.WantWrite)
155                                 throw new InvalidOperationException ();
156                 }
157
158                 internal void ResetRead ()
159                 {
160                         var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantRead);
161                         Debug ("ResetRead: {0} {1}", oldStatus, Status);
162                 }
163
164                 internal void ResetWrite ()
165                 {
166                         var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Complete, (int)AsyncOperationStatus.WantWrite);
167                         Debug ("ResetWrite: {0} {1}", oldStatus, Status);
168                 }
169
170                 internal void RequestWrite ()
171                 {
172                         var oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.WantWrite, (int)AsyncOperationStatus.Running);
173                         Debug ("RequestWrite: {0} {1}", oldStatus, Status);
174                         if (oldStatus == AsyncOperationStatus.Running)
175                                 return;
176                         else if (oldStatus != AsyncOperationStatus.WantRead && oldStatus != AsyncOperationStatus.WantWrite)
177                                 throw new InvalidOperationException ();
178                 }
179
180                 internal void StartOperation (AsyncOperation operation)
181                 {
182                         Debug ("Start Operation: {0} {1}", Status, operation);
183                         if (Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.Initialize, (int)AsyncOperationStatus.NotStarted) != (int)AsyncOperationStatus.NotStarted)
184                                 throw new InvalidOperationException ();
185
186                         Operation = operation;
187
188                         if (UserAsyncResult == null) {
189                                 StartOperation ();
190                                 return;
191                         }
192
193                         ThreadPool.QueueUserWorkItem (_ => StartOperation ());
194                 }
195
196                 void StartOperation ()
197                 {
198                         try {
199                                 ProcessOperation ();
200                                 if (UserAsyncResult != null && !UserAsyncResult.InternalPeekCompleted)
201                                         UserAsyncResult.InvokeCallback (UserResult);
202                         } catch (Exception ex) {
203                                 if (UserAsyncResult == null)
204                                         throw;
205                                 if (!UserAsyncResult.InternalPeekCompleted)
206                                         UserAsyncResult.InvokeCallback (ex);
207                         }
208                 }
209
210                 void ProcessOperation ()
211                 {
212                         AsyncOperationStatus status;
213                         do {
214                                 status = (AsyncOperationStatus)Interlocked.Exchange (ref Status, (int)AsyncOperationStatus.Running);
215
216                                 Debug ("ProcessOperation: {0}", status);
217
218                                 status = ProcessOperation (status);
219
220                                 Debug ("ProcessOperation done: {0}", status);
221
222                                 AsyncOperationStatus oldStatus;
223                                 if (status == AsyncOperationStatus.Complete) {
224                                         oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)AsyncOperationStatus.FinishWrite, (int)AsyncOperationStatus.WantWrite);
225                                         if (oldStatus == AsyncOperationStatus.WantWrite) {
226                                                 // We are done, but still need to flush the write queue.
227                                                 status = AsyncOperationStatus.FinishWrite;
228                                                 continue;
229                                         }
230                                 }
231
232                                 oldStatus = (AsyncOperationStatus)Interlocked.CompareExchange (ref Status, (int)status, (int)AsyncOperationStatus.Running);
233                                 Debug ("ProcessOperation done: {0} -> {1}", oldStatus, status);
234
235                                 if (oldStatus != AsyncOperationStatus.Running) {
236                                         if (status == oldStatus || status == AsyncOperationStatus.Continue || status == AsyncOperationStatus.Complete)
237                                                 status = oldStatus;
238                                         else
239                                                 throw new InvalidOperationException ();
240                                 }
241                         } while (status != AsyncOperationStatus.Complete);
242                 }
243
244                 AsyncOperationStatus ProcessOperation (AsyncOperationStatus status)
245                 {
246                         if (status == AsyncOperationStatus.WantRead) {
247                                 if (RequestedSize < 0)
248                                         throw new InvalidOperationException ();
249                                 else if (RequestedSize == 0)
250                                         return AsyncOperationStatus.Continue;
251
252                                 Debug ("ProcessOperation - read inner: {0}", RequestedSize);
253                                 var ret = Parent.InnerRead (RequestedSize);
254                                 Debug ("ProcessOperation - read inner done: {0} - {1}", RequestedSize, ret);
255
256                                 if (ret < 0)
257                                         return AsyncOperationStatus.ReadDone;
258
259                                 RequestedSize -= ret;
260
261                                 if (ret == 0 || RequestedSize == 0)
262                                         return AsyncOperationStatus.Continue;
263                                 else
264                                         return AsyncOperationStatus.WantRead;
265                         } else if (status == AsyncOperationStatus.WantWrite) {
266                                 Debug ("ProcessOperation - want write");
267                                 Parent.InnerWrite ();
268                                 Debug ("ProcessOperation - want write done");
269                                 return AsyncOperationStatus.Continue;
270                         } else if (status == AsyncOperationStatus.Initialize || status == AsyncOperationStatus.Continue) {
271                                 Debug ("ProcessOperation - continue");
272                                 status = Operation (this, status);
273                                 Debug ("ProcessOperation - continue done: {0}", status);
274                                 return status;
275                         } else if (status == AsyncOperationStatus.ReadDone) {
276                                 Debug ("ProcessOperation - read done");
277                                 status = Operation (this, status);
278                                 Debug ("ProcessOperation - read done: {0}", status);
279                                 return status;
280                         } else if (status == AsyncOperationStatus.FinishWrite) {
281                                 Debug ("ProcessOperation - finish write");
282                                 Parent.InnerWrite ();
283                                 Debug ("ProcessOperation - finish write done");
284                                 return AsyncOperationStatus.Complete;
285                         }
286
287                         throw new InvalidOperationException ();
288                 }
289         }
290 }
291 #endif