New tests, updates
[mono.git] / mcs / class / System / System.Net.Sockets / SocketAsyncEventArgs.cs
1 // System.Net.Sockets.SocketAsyncEventArgs.cs
2 //
3 // Authors:
4 //      Marek Habersack (mhabersack@novell.com)
5 //
6 // Copyright (c) 2008 Novell, Inc. (http://www.novell.com)
7 //
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 #if NET_2_0
30 using System;
31 using System.Collections.Generic;
32 using System.Threading;
33
34 namespace System.Net.Sockets
35 {
36         public class SocketAsyncEventArgs : EventArgs, IDisposable
37         {
38                 public event EventHandler<SocketAsyncEventArgs> Completed;
39
40                 IList <ArraySegment <byte>> _bufferList;
41                 
42                 public Socket AcceptSocket { get; set; }
43                 public byte[] Buffer { get; private set; }
44
45                 [MonoTODO ("not supported in all cases")]
46                 public IList<ArraySegment<byte>> BufferList {
47                         get { return _bufferList; }
48                         set {
49                                 if (Buffer != null)
50                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
51                                 _bufferList = value;
52                         }
53                 }
54
55                 public int BytesTransferred { get; private set; }
56                 public int Count { get; private set; }
57                 public bool DisconnectReuseSocket { get; set; }
58                 public SocketAsyncOperation LastOperation { get; private set; }
59                 public int Offset { get; private set; }
60                 public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; }
61                 public EndPoint RemoteEndPoint { get; set; }
62                 public SendPacketsElement[] SendPacketsElements { get; set; }
63                 public TransmitFileOptions SendPacketsFlags { get; set; }
64                 public int SendPacketsSendSize { get; set; }
65                 public SocketError SocketError { get; set; }
66                 public SocketFlags SocketFlags { get; set; }
67                 public object UserToken { get; set; }
68
69                 Socket curSocket;
70 #if NET_2_1
71                 public Socket ConnectSocket {
72                         get {
73                                 switch (SocketError) {
74                                 case SocketError.AccessDenied:
75                                         return null;
76                                 default:
77                                         return curSocket;
78                                 }
79                         }
80                 }
81 #endif
82                 
83                 public SocketAsyncEventArgs ()
84                 {
85                         AcceptSocket = null;
86                         Buffer = null;
87                         BufferList = null;
88                         BytesTransferred = 0;
89                         Count = 0;
90                         DisconnectReuseSocket = false;
91                         LastOperation = SocketAsyncOperation.None;
92                         Offset = 0;
93                         RemoteEndPoint = null;
94                         SendPacketsElements = null;
95                         SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread;
96                         SendPacketsSendSize = 0;
97                         SocketError = SocketError.Success;
98                         SocketFlags = SocketFlags.None;
99                         UserToken = null;
100                 }
101
102                 ~SocketAsyncEventArgs ()
103                 {
104                         Dispose (false);
105                 }
106
107                 void Dispose (bool disposing)
108                 {
109                         Socket acceptSocket = AcceptSocket;
110                         if (acceptSocket != null)
111                                 acceptSocket.Close ();
112
113                         if (disposing)
114                                 GC.SuppressFinalize (this);
115                 }               
116
117                 public void Dispose ()
118                 {
119                         Dispose (true);
120                 }
121                 
122                 protected virtual void OnCompleted (SocketAsyncEventArgs e)
123                 {
124                         if (e == null)
125                                 return;
126                         
127                         if (e.Completed != null)
128                                 e.Completed (e.curSocket, e);
129                 }
130
131                 public void SetBuffer (int offset, int count)
132                 {
133                         SetBufferInternal (Buffer, offset, count);
134                 }
135
136                 public void SetBuffer (byte[] buffer, int offset, int count)
137                 {
138                         SetBufferInternal (buffer, offset, count);
139                 }
140
141                 void SetBufferInternal (byte[] buffer, int offset, int count)
142                 {
143                         if (buffer != null) {
144                                 if (BufferList != null)
145                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
146                                 
147                                 int buflen = buffer.Length;
148                                 if (offset < 0 || offset >= buflen)
149                                         throw new ArgumentOutOfRangeException ("offset");
150
151                                 if (count < 0 || count + offset > buflen)
152                                         throw new ArgumentOutOfRangeException ("count");
153                         }
154
155                         Count = count;
156                         Offset = offset;
157                         Buffer = buffer;
158                 }
159
160 #region Internals
161                 void ReceiveCallback ()
162                 {
163                         SocketError = SocketError.Success;
164                         LastOperation = SocketAsyncOperation.Receive;
165                         SocketError error = SocketError.Success;
166
167                         if (!curSocket.Connected) {
168                                 SocketError = SocketError.NotConnected;
169                                 return;
170                         }
171                         
172                         try {
173                                 // FIXME: this does not support using BufferList
174                                 BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error);
175                         } finally {
176                                 SocketError = error;
177                                 OnCompleted (this);
178                         }
179                 }
180
181                 void ConnectCallback ()
182                 {
183                         LastOperation = SocketAsyncOperation.Connect;
184 #if NET_2_1
185                         if (SocketError == SocketError.AccessDenied) {
186                                 curSocket.Connected = false;
187                                 OnCompleted (this);
188                                 return;
189                         }
190 #endif
191                         SocketError = SocketError.Success;
192                         SocketError error = SocketError.Success;
193
194                         try {
195                                 if (!curSocket.Blocking) {
196                                         int success;
197                                         curSocket.Poll (-1, SelectMode.SelectWrite, out success);
198                                         SocketError = (SocketError)success;
199                                         if (success == 0)
200                                                 curSocket.Connected = true;
201                                         else
202                                                 return;
203                                 } else {
204                                         curSocket.seed_endpoint = RemoteEndPoint;
205                                         curSocket.Connect (RemoteEndPoint);
206                                         curSocket.Connected = true;
207                                 }
208                         } catch (SocketException se){
209                                 error = se.SocketErrorCode;
210                         } finally {
211                                 SocketError = error;
212                                 OnCompleted (this);
213                         }
214                 }
215
216                 void SendCallback ()
217                 {
218                         SocketError = SocketError.Success;
219                         LastOperation = SocketAsyncOperation.Send;
220                         SocketError error = SocketError.Success;
221
222                         if (!curSocket.Connected) {
223                                 SocketError = SocketError.NotConnected;
224                                 return;
225                         }
226
227                         try {
228                                 if (Buffer != null) {
229                                         BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error);
230                                 } else if (BufferList != null) {
231                                         BytesTransferred = 0;
232                                         foreach (ArraySegment<byte> asb in BufferList) {
233                                                 BytesTransferred += curSocket.Send_nochecks (asb.Array, asb.Offset, asb.Count, 
234                                                         SocketFlags.None, out error);
235                                                 if (error != SocketError.Success)
236                                                         break;
237                                         }
238                                 }
239                         } finally {
240                                 SocketError = error;
241                                 OnCompleted (this);
242                         }
243                 }
244 #if !NET_2_1
245                 void AcceptCallback ()
246                 {
247                         SocketError = SocketError.Success;
248                         LastOperation = SocketAsyncOperation.Accept;
249                         try {
250                                 curSocket.Accept (AcceptSocket);
251                         } catch (SocketException ex) {
252                                 SocketError = ex.SocketErrorCode;
253                                 throw;
254                         } finally {
255                                 OnCompleted (this);
256                         }
257                 }
258
259                 void DisconnectCallback ()
260                 {
261                         SocketError = SocketError.Success;
262                         LastOperation = SocketAsyncOperation.Disconnect;
263
264                         try {
265                                 curSocket.Disconnect (DisconnectReuseSocket);
266                         } catch (SocketException ex) {
267                                 SocketError = ex.SocketErrorCode;
268                                 throw;
269                         } finally {
270                                 OnCompleted (this);
271                         }
272                 }
273
274                 void ReceiveFromCallback ()
275                 {
276                         SocketError = SocketError.Success;
277                         LastOperation = SocketAsyncOperation.ReceiveFrom;
278
279                         try {
280                                 EndPoint ep = RemoteEndPoint;
281                                 BytesTransferred = curSocket.ReceiveFrom_nochecks (Buffer, Offset, Count, SocketFlags, ref ep);
282                         } catch (SocketException ex) {
283                                 SocketError = ex.SocketErrorCode;
284                                 throw;
285                         } finally {
286                                 OnCompleted (this);
287                         }
288                 }
289
290                 void SendToCallback ()
291                 {
292                         SocketError = SocketError.Success;
293                         LastOperation = SocketAsyncOperation.SendTo;
294                         int total = 0;
295                         
296                         try {
297                                 int count = Count;
298
299                                 while (total < count)
300                                         total += curSocket.SendTo_nochecks (Buffer, Offset, count, SocketFlags, RemoteEndPoint);
301                                 BytesTransferred = total;
302                         } catch (SocketException ex) {
303                                 SocketError = ex.SocketErrorCode;
304                                 throw;
305                         } finally {
306                                 OnCompleted (this);
307                         }
308                 }
309 #endif
310                 internal void DoOperation (SocketAsyncOperation operation, Socket socket)
311                 {
312                         ThreadStart callback;
313                         curSocket = socket;
314                         
315                         switch (operation) {
316 #if !NET_2_1
317                                 case SocketAsyncOperation.Accept:
318                                         callback = new ThreadStart (AcceptCallback);
319                                         break;
320
321                                 case SocketAsyncOperation.Disconnect:
322                                         callback = new ThreadStart (DisconnectCallback);
323                                         break;
324
325                                 case SocketAsyncOperation.ReceiveFrom:
326                                         callback = new ThreadStart (ReceiveFromCallback);
327                                         break;
328
329                                 case SocketAsyncOperation.SendTo:
330                                         callback = new ThreadStart (SendToCallback);
331                                         break;
332 #endif
333                                 case SocketAsyncOperation.Receive:
334                                         callback = new ThreadStart (ReceiveCallback);
335                                         break;
336
337                                 case SocketAsyncOperation.Connect:
338                                         callback = new ThreadStart (ConnectCallback);
339                                         break;
340
341                                 case SocketAsyncOperation.Send:
342                                         callback = new ThreadStart (SendCallback);
343                                         break;
344                                 
345                                 default:
346                                         throw new NotSupportedException ();
347                         }
348
349                         Thread t = new Thread (callback);
350                         t.IsBackground = true;
351                         t.Start ();
352                 }
353 #endregion
354         }
355 }
356 #endif