Wed Feb 24 15:47:16 CET 2010 Paolo Molaro <lupus@ximian.com>
[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.Reflection;
33 using System.Security;
34 using System.Threading;
35
36 namespace System.Net.Sockets
37 {
38         public class SocketAsyncEventArgs : EventArgs, IDisposable
39         {
40 #if NET_2_1 && !MONOTOUCH
41                 static MethodInfo check_socket_policy;
42
43                 static SocketAsyncEventArgs ()
44                 {
45                         Type type = Type.GetType ("System.Windows.Browser.Net.CrossDomainPolicyManager, System.Windows.Browser, Version=2.0.5.0, Culture=Neutral, PublicKeyToken=7cec85d7bea7798e");
46                         check_socket_policy = type.GetMethod ("CheckEndPoint");
47                 }
48
49                 static internal bool CheckEndPoint (EndPoint endpoint)
50                 {
51                         if (check_socket_policy == null)
52                                 throw new SecurityException ();
53                         return ((bool) check_socket_policy.Invoke (null, new object [1] { endpoint }));
54                 }
55 #endif
56 #if (NET_2_1 || NET_4_0) && !MONOTOUCH
57                 public Exception ConnectByNameError { get; internal set; }
58 #endif
59
60                 public event EventHandler<SocketAsyncEventArgs> Completed;
61
62                 IList <ArraySegment <byte>> _bufferList;
63                 
64                 public Socket AcceptSocket { get; set; }
65                 public byte[] Buffer { get; private set; }
66
67                 [MonoTODO ("not supported in all cases")]
68                 public IList<ArraySegment<byte>> BufferList {
69                         get { return _bufferList; }
70                         set {
71                                 if (Buffer != null && value != null)
72                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
73                                 _bufferList = value;
74                         }
75                 }
76
77                 public int BytesTransferred { get; private set; }
78                 public int Count { get; private set; }
79                 public bool DisconnectReuseSocket { get; set; }
80                 public SocketAsyncOperation LastOperation { get; private set; }
81                 public int Offset { get; private set; }
82                 public EndPoint RemoteEndPoint { get; set; }
83 #if !NET_2_1
84                 public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; }
85                 public SendPacketsElement[] SendPacketsElements { get; set; }
86                 public TransmitFileOptions SendPacketsFlags { get; set; }
87 #endif
88                 [MonoTODO ("unused property")]
89                 public int SendPacketsSendSize { get; set; }
90                 public SocketError SocketError { get; set; }
91                 public SocketFlags SocketFlags { get; set; }
92                 public object UserToken { get; set; }
93
94                 Socket curSocket;
95 #if NET_2_1
96                 public Socket ConnectSocket {
97                         get {
98                                 switch (SocketError) {
99                                 case SocketError.AccessDenied:
100                                         return null;
101                                 default:
102                                         return curSocket;
103                                 }
104                         }
105                 }
106
107                 internal bool PolicyRestricted { get; private set; }
108
109                 internal SocketAsyncEventArgs (bool policy) : 
110                         this ()
111                 {
112                         PolicyRestricted = policy;
113                 }
114 #endif
115                 
116                 public SocketAsyncEventArgs ()
117                 {
118                         AcceptSocket = null;
119                         Buffer = null;
120                         BufferList = null;
121                         BytesTransferred = 0;
122                         Count = 0;
123                         DisconnectReuseSocket = false;
124                         LastOperation = SocketAsyncOperation.None;
125                         Offset = 0;
126                         RemoteEndPoint = null;
127 #if !NET_2_1
128                         SendPacketsElements = null;
129                         SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread;
130 #endif
131                         SendPacketsSendSize = -1;
132                         SocketError = SocketError.Success;
133                         SocketFlags = SocketFlags.None;
134                         UserToken = null;
135                 }
136
137                 ~SocketAsyncEventArgs ()
138                 {
139                         Dispose (false);
140                 }
141
142                 void Dispose (bool disposing)
143                 {
144                         Socket acceptSocket = AcceptSocket;
145                         if (acceptSocket != null)
146                                 acceptSocket.Close ();
147
148                         if (disposing)
149                                 GC.SuppressFinalize (this);
150                 }               
151
152                 public void Dispose ()
153                 {
154                         Dispose (true);
155                 }
156                 
157                 protected virtual void OnCompleted (SocketAsyncEventArgs e)
158                 {
159                         if (e == null)
160                                 return;
161                         
162                         EventHandler<SocketAsyncEventArgs> handler = e.Completed;
163                         if (handler != null)
164                                 handler (e.curSocket, e);
165                 }
166
167                 public void SetBuffer (int offset, int count)
168                 {
169                         SetBufferInternal (Buffer, offset, count);
170                 }
171
172                 public void SetBuffer (byte[] buffer, int offset, int count)
173                 {
174                         SetBufferInternal (buffer, offset, count);
175                 }
176
177                 void SetBufferInternal (byte[] buffer, int offset, int count)
178                 {
179                         if (buffer != null) {
180                                 if (BufferList != null)
181                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
182                                 
183                                 int buflen = buffer.Length;
184                                 if (offset < 0 || (offset != 0 && offset >= buflen))
185                                         throw new ArgumentOutOfRangeException ("offset");
186
187                                 if (count < 0 || count > buflen - offset)
188                                         throw new ArgumentOutOfRangeException ("count");
189
190                                 Count = count;
191                                 Offset = offset;
192                         }
193                         Buffer = buffer;
194                 }
195
196 #region Internals
197                 void ReceiveCallback ()
198                 {
199                         SocketError = SocketError.Success;
200                         LastOperation = SocketAsyncOperation.Receive;
201                         SocketError error = SocketError.Success;
202
203                         if (!curSocket.Connected) {
204                                 SocketError = SocketError.NotConnected;
205                                 return;
206                         }
207                         
208                         try {
209                                 // FIXME: this does not support using BufferList
210                                 BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error);
211                         } finally {
212                                 SocketError = error;
213                                 OnCompleted (this);
214                         }
215                 }
216
217                 void ConnectCallback ()
218                 {
219                         LastOperation = SocketAsyncOperation.Connect;
220                         SocketError error = SocketError.AccessDenied;
221                         try {
222 #if (NET_2_1 || NET_4_0) && !MONOTOUCH
223                                 // Connect to the first address that match the host name, like:
224                                 // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx
225                                 // while skipping entries that do not match the address family
226                                 DnsEndPoint dep = (RemoteEndPoint as DnsEndPoint);
227                                 if (dep != null) {
228                                         IPAddress[] addresses = Dns.GetHostAddresses (dep.Host);
229                                         foreach (IPAddress addr in addresses) {
230                                                 try {
231                                                         if (curSocket.AddressFamily == addr.AddressFamily) {
232                                                                 error = TryConnect (new IPEndPoint (addr, dep.Port));
233                                                                 if (error == SocketError.Success) {
234                                                                         ConnectByNameError = null;
235                                                                         break;
236                                                                 }
237                                                         }
238                                                 }
239                                                 catch (SocketException se) {
240                                                         ConnectByNameError = se;
241                                                         error = SocketError.AccessDenied;
242                                                 }
243                                         }
244                                 } else {
245                                         ConnectByNameError = null;
246                                         error = TryConnect (RemoteEndPoint);
247                                 }
248 #else
249                                 error = TryConnect (RemoteEndPoint);
250 #endif
251                         } finally {
252                                 SocketError = error;
253                                 OnCompleted (this);
254                         }
255                 }
256
257                 SocketError TryConnect (EndPoint endpoint)
258                 {
259                         curSocket.Connected = false;
260                         SocketError error = SocketError.Success;
261 #if NET_2_1 && !MONOTOUCH
262                         // if we're not downloading a socket policy then check the policy
263                         if (!PolicyRestricted) {
264                                 error = SocketError.AccessDenied;
265                                 if (!CheckEndPoint (endpoint)) {
266                                         return error;
267                                 }
268                                 error = SocketError.Success;
269                         }
270 #endif
271                         try {
272 #if !NET_2_1
273                                 if (!curSocket.Blocking) {
274                                         int success;
275                                         curSocket.Poll (-1, SelectMode.SelectWrite, out success);
276                                         error = (SocketError)success;
277                                         if (success == 0)
278                                                 curSocket.Connected = true;
279                                         else
280                                                 return error;
281                                 } else
282 #endif
283                                 {
284                                         curSocket.seed_endpoint = endpoint;
285                                         curSocket.Connect (endpoint);
286                                         curSocket.Connected = true;
287                                 }
288                         } catch (SocketException se){
289                                 error = se.SocketErrorCode;
290                         }
291                         return error;
292                 }
293
294                 void SendCallback ()
295                 {
296                         SocketError = SocketError.Success;
297                         LastOperation = SocketAsyncOperation.Send;
298                         SocketError error = SocketError.Success;
299
300                         if (!curSocket.Connected) {
301                                 SocketError = SocketError.NotConnected;
302                                 return;
303                         }
304
305                         try {
306                                 if (Buffer != null) {
307                                         BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error);
308                                 } else if (BufferList != null) {
309                                         BytesTransferred = 0;
310                                         foreach (ArraySegment<byte> asb in BufferList) {
311                                                 BytesTransferred += curSocket.Send_nochecks (asb.Array, asb.Offset, asb.Count, 
312                                                         SocketFlags.None, out error);
313                                                 if (error != SocketError.Success)
314                                                         break;
315                                         }
316                                 }
317                         } finally {
318                                 SocketError = error;
319                                 OnCompleted (this);
320                         }
321                 }
322 #if !NET_2_1
323                 void AcceptCallback ()
324                 {
325                         SocketError = SocketError.Success;
326                         LastOperation = SocketAsyncOperation.Accept;
327                         try {
328                                 curSocket.Accept (AcceptSocket);
329                         } catch (SocketException ex) {
330                                 SocketError = ex.SocketErrorCode;
331                                 throw;
332                         } finally {
333                                 OnCompleted (this);
334                         }
335                 }
336
337                 void DisconnectCallback ()
338                 {
339                         SocketError = SocketError.Success;
340                         LastOperation = SocketAsyncOperation.Disconnect;
341
342                         try {
343                                 curSocket.Disconnect (DisconnectReuseSocket);
344                         } catch (SocketException ex) {
345                                 SocketError = ex.SocketErrorCode;
346                                 throw;
347                         } finally {
348                                 OnCompleted (this);
349                         }
350                 }
351
352                 void ReceiveFromCallback ()
353                 {
354                         SocketError = SocketError.Success;
355                         LastOperation = SocketAsyncOperation.ReceiveFrom;
356
357                         try {
358                                 EndPoint ep = RemoteEndPoint;
359                                 if (Buffer != null) {
360                                         BytesTransferred = curSocket.ReceiveFrom_nochecks (Buffer, Offset, Count, SocketFlags, ref ep);
361                                 } else if (BufferList != null) {
362                                         throw new NotImplementedException ();
363                                 }
364                         } catch (SocketException ex) {
365                                 SocketError = ex.SocketErrorCode;
366                                 throw;
367                         } finally {
368                                 OnCompleted (this);
369                         }
370                 }
371
372                 void SendToCallback ()
373                 {
374                         SocketError = SocketError.Success;
375                         LastOperation = SocketAsyncOperation.SendTo;
376                         int total = 0;
377                         
378                         try {
379                                 int count = Count;
380
381                                 while (total < count)
382                                         total += curSocket.SendTo_nochecks (Buffer, Offset, count, SocketFlags, RemoteEndPoint);
383                                 BytesTransferred = total;
384                         } catch (SocketException ex) {
385                                 SocketError = ex.SocketErrorCode;
386                                 throw;
387                         } finally {
388                                 OnCompleted (this);
389                         }
390                 }
391 #endif
392                 internal void DoOperation (SocketAsyncOperation operation, Socket socket)
393                 {
394                         ThreadStart callback;
395                         curSocket = socket;
396                         
397                         switch (operation) {
398 #if !NET_2_1
399                                 case SocketAsyncOperation.Accept:
400                                         callback = new ThreadStart (AcceptCallback);
401                                         break;
402
403                                 case SocketAsyncOperation.Disconnect:
404                                         callback = new ThreadStart (DisconnectCallback);
405                                         break;
406
407                                 case SocketAsyncOperation.ReceiveFrom:
408                                         callback = new ThreadStart (ReceiveFromCallback);
409                                         break;
410
411                                 case SocketAsyncOperation.SendTo:
412                                         callback = new ThreadStart (SendToCallback);
413                                         break;
414 #endif
415                                 case SocketAsyncOperation.Receive:
416                                         callback = new ThreadStart (ReceiveCallback);
417                                         break;
418
419                                 case SocketAsyncOperation.Connect:
420                                         callback = new ThreadStart (ConnectCallback);
421                                         break;
422
423                                 case SocketAsyncOperation.Send:
424                                         callback = new ThreadStart (SendCallback);
425                                         break;
426                                 
427                                 default:
428                                         throw new NotSupportedException ();
429                         }
430
431                         Thread t = new Thread (callback);
432                         t.IsBackground = true;
433                         t.Start ();
434                 }
435 #endregion
436         }
437 }
438 #endif