[Http]: Clear the 'SendChunked' flag when redirecting.
[mono.git] / mcs / class / System / System.Net.Sockets / SocketAsyncEventArgs.cs
index 3e488f24c382969cd870e0d412e7e621f74f087c..d8e1c5125e44721237bd87b68263174d8fe21a2d 100644 (file)
@@ -2,8 +2,10 @@
 //
 // Authors:
 //     Marek Habersack (mhabersack@novell.com)
+//     Gonzalo Paniagua Javier (gonzalo@xamarin.com)
 //
-// Copyright (c) 2008 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2008,2010 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2011 Xamarin, Inc. (http://xamarin.com)
 //
 
 //
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
 using System;
 using System.Collections.Generic;
+using System.Reflection;
+using System.Security;
 using System.Threading;
 
 namespace System.Net.Sockets
 {
        public class SocketAsyncEventArgs : EventArgs, IDisposable
        {
+               bool disposed;
+               int in_progress;
+               internal Socket.Worker Worker;
+               EndPoint remote_ep;
+#if NET_4_0
+               public Exception ConnectByNameError { get; internal set; }
+#endif
+
                public event EventHandler<SocketAsyncEventArgs> Completed;
 
                IList <ArraySegment <byte>> _bufferList;
@@ -42,31 +53,35 @@ namespace System.Net.Sockets
                public Socket AcceptSocket { get; set; }
                public byte[] Buffer { get; private set; }
 
-               [MonoTODO ("not supported in all cases")]
                public IList<ArraySegment<byte>> BufferList {
                        get { return _bufferList; }
                        set {
-                               if (Buffer != null)
+                               if (Buffer != null && value != null)
                                        throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
                                _bufferList = value;
                        }
                }
 
-               public int BytesTransferred { get; private set; }
-               public int Count { get; private set; }
+               public int BytesTransferred { get; internal set; }
+               public int Count { get; internal set; }
                public bool DisconnectReuseSocket { get; set; }
                public SocketAsyncOperation LastOperation { get; private set; }
                public int Offset { get; private set; }
+               public EndPoint RemoteEndPoint {
+                       get { return remote_ep; }
+                       set { remote_ep = value; }
+               }
+#if !NET_2_1
                public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; }
-               public EndPoint RemoteEndPoint { get; set; }
                public SendPacketsElement[] SendPacketsElements { get; set; }
                public TransmitFileOptions SendPacketsFlags { get; set; }
+#endif
+               [MonoTODO ("unused property")]
                public int SendPacketsSendSize { get; set; }
                public SocketError SocketError { get; set; }
                public SocketFlags SocketFlags { get; set; }
                public object UserToken { get; set; }
-
-               Socket curSocket;
+               internal Socket curSocket;
 #if NET_2_1
                public Socket ConnectSocket {
                        get {
@@ -78,10 +93,19 @@ namespace System.Net.Sockets
                                }
                        }
                }
+
+               internal bool PolicyRestricted { get; private set; }
+
+               internal SocketAsyncEventArgs (bool policy) : 
+                       this ()
+               {
+                       PolicyRestricted = policy;
+               }
 #endif
                
                public SocketAsyncEventArgs ()
                {
+                       Worker = new Socket.Worker (this);
                        AcceptSocket = null;
                        Buffer = null;
                        BufferList = null;
@@ -91,9 +115,11 @@ namespace System.Net.Sockets
                        LastOperation = SocketAsyncOperation.None;
                        Offset = 0;
                        RemoteEndPoint = null;
+#if !NET_2_1
                        SendPacketsElements = null;
                        SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread;
-                       SendPacketsSendSize = 0;
+#endif
+                       SendPacketsSendSize = -1;
                        SocketError = SocketError.Success;
                        SocketFlags = SocketFlags.None;
                        UserToken = null;
@@ -106,26 +132,49 @@ namespace System.Net.Sockets
 
                void Dispose (bool disposing)
                {
-                       Socket acceptSocket = AcceptSocket;
-                       if (acceptSocket != null)
-                               acceptSocket.Close ();
-
-                       if (disposing)
-                               GC.SuppressFinalize (this);
+                       disposed = true;
+
+                       if (disposing) {
+                               if (disposed || Interlocked.CompareExchange (ref in_progress, 0, 0) != 0)
+                                       return;
+                               if (Worker != null) {
+                                       Worker.Dispose ();
+                                       Worker = null;
+                               }
+                       }
+                       AcceptSocket = null;
+                       Buffer = null;
+                       BufferList = null;
+                       RemoteEndPoint = null;
+                       UserToken = null;
+#if !NET_2_1
+                       SendPacketsElements = null;
+#endif
                }               
 
                public void Dispose ()
                {
                        Dispose (true);
+                       GC.SuppressFinalize (this);
                }
-               
+
+               internal void SetLastOperation (SocketAsyncOperation op)
+               {
+                       if (disposed)
+                               throw new ObjectDisposedException ("System.Net.Sockets.SocketAsyncEventArgs");
+                       if (Interlocked.Exchange (ref in_progress, 1) != 0)
+                               throw new InvalidOperationException ("Operation already in progress");
+                       LastOperation = op;
+               }
+
                protected virtual void OnCompleted (SocketAsyncEventArgs e)
                {
                        if (e == null)
                                return;
                        
-                       if (e.Completed != null)
-                               e.Completed (e.curSocket, e);
+                       EventHandler<SocketAsyncEventArgs> handler = e.Completed;
+                       if (handler != null)
+                               handler (e.curSocket, e);
                }
 
                public void SetBuffer (int offset, int count)
@@ -145,212 +194,141 @@ namespace System.Net.Sockets
                                        throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
                                
                                int buflen = buffer.Length;
-                               if (offset < 0 || offset >= buflen)
+                               if (offset < 0 || (offset != 0 && offset >= buflen))
                                        throw new ArgumentOutOfRangeException ("offset");
 
-                               if (count < 0 || count + offset > buflen)
+                               if (count < 0 || count > buflen - offset)
                                        throw new ArgumentOutOfRangeException ("count");
-                       }
 
-                       Count = count;
-                       Offset = offset;
+                               Count = count;
+                               Offset = offset;
+                       }
                        Buffer = buffer;
                }
 
 #region Internals
-               void ReceiveCallback ()
+               internal static AsyncCallback Dispatcher = new AsyncCallback (DispatcherCB);
+
+               static void DispatcherCB (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.Receive;
-                       SocketError error = SocketError.Success;
+                       SocketAsyncEventArgs args = (SocketAsyncEventArgs) ares.AsyncState;
+                       if (Interlocked.Exchange (ref args.in_progress, 0) != 1)
+                               throw new InvalidOperationException ("No operation in progress");
+                       SocketAsyncOperation op = args.LastOperation;
+                       // Notes;
+                       //      -SocketOperation.AcceptReceive not used in SocketAsyncEventArgs
+                       //      -SendPackets and ReceiveMessageFrom are not implemented yet
+                       if (op == SocketAsyncOperation.Receive)
+                               args.ReceiveCallback (ares);
+                       else if (op == SocketAsyncOperation.Send)
+                               args.SendCallback (ares);
+                       else if (op == SocketAsyncOperation.ReceiveFrom)
+                               args.ReceiveFromCallback (ares);
+                       else if (op == SocketAsyncOperation.SendTo)
+                               args.SendToCallback (ares);
+                       else if (op == SocketAsyncOperation.Accept)
+                               args.AcceptCallback (ares);
+                       else if (op == SocketAsyncOperation.Disconnect)
+                               args.DisconnectCallback (ares);
+                       else if (op == SocketAsyncOperation.Connect)
+                               args.ConnectCallback ();
+                       /*
+                       else if (op == Socket.SocketOperation.ReceiveMessageFrom)
+                       else if (op == Socket.SocketOperation.SendPackets)
+                       */
+                       else
+                               throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
 
-                       if (!curSocket.Connected) {
-                               SocketError = SocketError.NotConnected;
-                               return;
-                       }
-                       
+               }
+
+               internal void ReceiveCallback (IAsyncResult ares)
+               {
                        try {
-                               // FIXME: this does not support using BufferList
-                               BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error);
+                               BytesTransferred = curSocket.EndReceive (ares);
+                       } catch (SocketException se){
+                               SocketError = se.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
-                               SocketError = error;
                                OnCompleted (this);
                        }
                }
 
                void ConnectCallback ()
                {
-                       LastOperation = SocketAsyncOperation.Connect;
-#if NET_2_1
-                       if (SocketError == SocketError.AccessDenied) {
-                               curSocket.Connected = false;
-                               OnCompleted (this);
-                               return;
-                       }
-#endif
-                       SocketError = SocketError.Success;
-                       SocketError error = SocketError.Success;
-
                        try {
-                               if (!curSocket.Blocking) {
-                                       int success;
-                                       curSocket.Poll (-1, SelectMode.SelectWrite, out success);
-                                       SocketError = (SocketError)success;
-                                       if (success == 0)
-                                               curSocket.Connected = true;
-                                       else
-                                               return;
-                               } else {
-                                       curSocket.seed_endpoint = RemoteEndPoint;
-                                       curSocket.Connect (RemoteEndPoint);
-                                       curSocket.Connected = true;
-                               }
-                       } catch (SocketException se){
-                               error = se.SocketErrorCode;
+                               SocketError = (SocketError) Worker.result.error;
                        } finally {
-                               SocketError = error;
                                OnCompleted (this);
                        }
                }
 
-               void SendCallback ()
+               internal void SendCallback (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.Send;
-                       SocketError error = SocketError.Success;
-
-                       if (!curSocket.Connected) {
-                               SocketError = SocketError.NotConnected;
-                               return;
-                       }
-
                        try {
-                               if (Buffer != null) {
-                                       BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error);
-                               } else if (BufferList != null) {
-                                       BytesTransferred = 0;
-                                       foreach (ArraySegment<byte> asb in BufferList) {
-                                               BytesTransferred += curSocket.Send_nochecks (asb.Array, asb.Offset, asb.Count, 
-                                                       SocketFlags.None, out error);
-                                               if (error != SocketError.Success)
-                                                       break;
-                                       }
-                               }
+                               BytesTransferred = curSocket.EndSend (ares);
+                       } catch (SocketException se){
+                               SocketError = se.SocketErrorCode;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
-                               SocketError = error;
                                OnCompleted (this);
                        }
                }
-#if !NET_2_1
-               void AcceptCallback ()
+
+               internal void AcceptCallback (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.Accept;
                        try {
-                               curSocket.Accept (AcceptSocket);
+                               AcceptSocket = curSocket.EndAccept (ares);
                        } catch (SocketException ex) {
                                SocketError = ex.SocketErrorCode;
-                               throw;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
+                               if (AcceptSocket == null)
+                                       AcceptSocket = new Socket (curSocket.AddressFamily, curSocket.SocketType, curSocket.ProtocolType, (IntPtr)(-1));
                                OnCompleted (this);
                        }
                }
 
-               void DisconnectCallback ()
+               internal void DisconnectCallback (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.Disconnect;
-
                        try {
-                               curSocket.Disconnect (DisconnectReuseSocket);
+                               curSocket.EndDisconnect (ares);
                        } catch (SocketException ex) {
                                SocketError = ex.SocketErrorCode;
-                               throw;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
                                OnCompleted (this);
                        }
                }
 
-               void ReceiveFromCallback ()
+               internal void ReceiveFromCallback (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.ReceiveFrom;
-
                        try {
-                               EndPoint ep = RemoteEndPoint;
-                               BytesTransferred = curSocket.ReceiveFrom_nochecks (Buffer, Offset, Count, SocketFlags, ref ep);
+                               BytesTransferred = curSocket.EndReceiveFrom (ares, ref remote_ep);
                        } catch (SocketException ex) {
                                SocketError = ex.SocketErrorCode;
-                               throw;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
                                OnCompleted (this);
                        }
                }
 
-               void SendToCallback ()
+               internal void SendToCallback (IAsyncResult ares)
                {
-                       SocketError = SocketError.Success;
-                       LastOperation = SocketAsyncOperation.SendTo;
-                       int total = 0;
-                       
                        try {
-                               int count = Count;
-
-                               while (total < count)
-                                       total += curSocket.SendTo_nochecks (Buffer, Offset, count, SocketFlags, RemoteEndPoint);
-                               BytesTransferred = total;
+                               BytesTransferred = curSocket.EndSendTo (ares);
                        } catch (SocketException ex) {
                                SocketError = ex.SocketErrorCode;
-                               throw;
+                       } catch (ObjectDisposedException) {
+                               SocketError = SocketError.OperationAborted;
                        } finally {
                                OnCompleted (this);
                        }
                }
-#endif
-               internal void DoOperation (SocketAsyncOperation operation, Socket socket)
-               {
-                       ThreadStart callback;
-                       curSocket = socket;
-                       
-                       switch (operation) {
-#if !NET_2_1
-                               case SocketAsyncOperation.Accept:
-                                       callback = new ThreadStart (AcceptCallback);
-                                       break;
-
-                               case SocketAsyncOperation.Disconnect:
-                                       callback = new ThreadStart (DisconnectCallback);
-                                       break;
-
-                               case SocketAsyncOperation.ReceiveFrom:
-                                       callback = new ThreadStart (ReceiveFromCallback);
-                                       break;
-
-                               case SocketAsyncOperation.SendTo:
-                                       callback = new ThreadStart (SendToCallback);
-                                       break;
-#endif
-                               case SocketAsyncOperation.Receive:
-                                       callback = new ThreadStart (ReceiveCallback);
-                                       break;
-
-                               case SocketAsyncOperation.Connect:
-                                       callback = new ThreadStart (ConnectCallback);
-                                       break;
-
-                               case SocketAsyncOperation.Send:
-                                       callback = new ThreadStart (SendCallback);
-                                       break;
-                               
-                               default:
-                                       throw new NotSupportedException ();
-                       }
-
-                       Thread t = new Thread (callback);
-                       t.IsBackground = true;
-                       t.Start ();
-               }
 #endregion
        }
 }
-#endif