Fix bug #311: On LinkedList.Clear, detach each node instead of dropping them en masse.
[mono.git] / mcs / class / System / System.Net.Sockets / NetworkStream.cs
1 //
2 // System.Net.Sockets.NetworkStream.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Sridhar Kulkarni <sridharkulkarni@gmail.com>
7 //
8 // (C) 2002 Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2002-2006 Novell, Inc.  http://www.novell.com
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.IO;
34 using System.Runtime.InteropServices;
35 #if NET_2_0 && !NET_2_1
36 using System.Timers;
37 using System.Threading;
38 #endif
39
40 namespace System.Net.Sockets
41 {
42         public class NetworkStream : Stream, IDisposable {
43                 FileAccess access;
44                 Socket socket;
45                 bool owns_socket;
46                 bool readable, writeable;
47                 bool disposed = false;
48                 
49                 public NetworkStream (Socket socket)
50                         : this (socket, FileAccess.ReadWrite, false)
51                 {
52                 }
53
54                 public NetworkStream (Socket socket, bool owns_socket)
55                         : this (socket, FileAccess.ReadWrite, owns_socket)
56                 {
57                 }
58
59                 public NetworkStream (Socket socket, FileAccess access)
60                         : this (socket, access, false)
61                 {
62                 }
63                 
64                 public NetworkStream (Socket socket, FileAccess access, bool owns_socket)
65                 {
66                         if (socket == null)
67                                 throw new ArgumentNullException ("socket is null");
68                         if (socket.SocketType != SocketType.Stream)
69                                 throw new ArgumentException ("Socket is not of type Stream", "socket");
70                         if (!socket.Connected)
71                                 throw new IOException ("Not connected");
72                         if (!socket.Blocking)
73                                 throw new IOException ("Operation not allowed on a non-blocking socket.");
74                         
75                         this.socket = socket;
76                         this.owns_socket = owns_socket;
77                         this.access = access;
78
79                         readable = CanRead;
80                         writeable = CanWrite;
81                 }
82
83                 public override bool CanRead {
84                         get {
85                                 return access == FileAccess.ReadWrite || access == FileAccess.Read;
86                         }
87                 }
88
89                 public override bool CanSeek {
90                         get {
91                                 // network sockets cant seek.
92                                 return false;
93                         }
94                 }
95
96 #if NET_2_0
97                 public override bool CanTimeout
98                 {
99                         get {
100                                 return(true);
101                         }
102                 }
103 #endif
104
105                 public override bool CanWrite {
106                         get {
107                                 return access == FileAccess.ReadWrite || access == FileAccess.Write;
108                         }
109                 }
110
111                 public virtual bool DataAvailable {
112                         get {
113                                 CheckDisposed ();
114                                 return socket.Available > 0;
115                         }
116                 }
117
118                 public override long Length {
119                         get {
120                                 // Network sockets always throw an exception
121                                 throw new NotSupportedException ();
122                         }
123                 }
124
125                 public override long Position {
126                         get {
127                                 // Network sockets always throw an exception
128                                 throw new NotSupportedException ();
129                         }
130                         
131                         set {
132                                 // Network sockets always throw an exception
133                                 throw new NotSupportedException ();
134                         }
135                 }
136
137                 protected bool Readable {
138                         get {
139                                 return readable;
140                         }
141
142                         set {
143                                 readable = value;
144                         }
145                 }
146
147 #if NET_2_0 && !NET_2_1
148 #if TARGET_JVM
149                 [MonoNotSupported ("Not supported since Socket.ReceiveTimeout is not supported")]
150 #endif
151                 public override int ReadTimeout
152                 {
153                         get {
154                                 return(socket.ReceiveTimeout);
155                         }
156                         set {
157                                 if (value <= 0 && value != Timeout.Infinite) {
158                                         throw new ArgumentOutOfRangeException ("value", "The value specified is less than or equal to zero and is not Infinite.");
159                                 }
160                                 
161                                 socket.ReceiveTimeout = value;
162                         }
163                 }
164 #endif
165
166                 protected Socket Socket {
167                         get {
168                                 return socket;
169                         }
170                 }
171
172                 protected bool Writeable {
173                         get {
174                                 return writeable;
175                         }
176
177                         set {
178                                 writeable = value;
179                         }
180                 }
181
182 #if NET_2_0 && !NET_2_1
183 #if TARGET_JVM
184                 [MonoNotSupported ("Not supported since Socket.SendTimeout is not supported")]
185 #endif
186                 public override int WriteTimeout
187                 {
188                         get {
189                                 return(socket.SendTimeout);
190                         }
191                         set {
192                                 if (value <= 0 && value != Timeout.Infinite) {
193                                         throw new ArgumentOutOfRangeException ("value", "The value specified is less than or equal to zero and is not Infinite");
194                                 }
195                                 
196                                 socket.SendTimeout = value;
197                         }
198                 }
199 #endif
200
201                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,
202                                                         AsyncCallback callback, object state)
203                 {
204                         CheckDisposed ();                               
205                         IAsyncResult retval;
206
207                         if (buffer == null)
208                                 throw new ArgumentNullException ("buffer is null");
209                         int len = buffer.Length;
210                         if(offset<0 || offset>len) {
211                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
212                         }
213                         if(size<0 || offset+size>len) {
214                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
215                         }
216
217                         Socket s = socket;
218
219                         if (s == null) {
220                                 throw new IOException("Connection closed");
221                         }
222
223                         try {
224                                 retval = s.BeginReceive (buffer, offset, size, 0, callback, state);
225                         } catch (Exception e) {
226                                 throw new IOException ("BeginReceive failure", e);
227                         }
228
229                         return retval;
230                 }
231
232                 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,
233                                                         AsyncCallback callback, object state)
234                 {
235                         CheckDisposed ();
236                         IAsyncResult retval;
237
238                         if (buffer == null)
239                                 throw new ArgumentNullException ("buffer is null");
240
241                         int len = buffer.Length;
242                         if(offset<0 || offset>len) {
243                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
244                         }
245                         if(size<0 || offset+size>len) {
246                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
247                         }
248
249                         Socket s = socket;
250
251                         if (s == null) {
252                                 throw new IOException("Connection closed");
253                         }
254
255                         try {
256                                 retval = s.BeginSend (buffer, offset, size, 0, callback, state);
257                         } catch {
258                                 throw new IOException ("BeginWrite failure");
259                         }
260
261                         return retval;
262                 }
263
264                 ~NetworkStream ()
265                 {
266                         Dispose (false);
267                 }
268                 
269 #if !NET_2_0
270                 public override void Close ()
271                 {
272                         ((IDisposable) this).Dispose ();
273                 }
274 #endif
275
276 #if NET_2_0 && !NET_2_1
277                 public void Close (int timeout)
278                 {
279                         if (timeout < -1) {
280                                 throw new ArgumentOutOfRangeException ("timeout", "timeout is less than -1");
281                         }
282                         
283                         System.Timers.Timer close_timer = new System.Timers.Timer ();
284                         close_timer.Elapsed += new ElapsedEventHandler (OnTimeoutClose);
285                         /* NB timeout is in milliseconds here, cf
286                          * seconds in Socket.Close(int)
287                          */
288                         close_timer.Interval = timeout;
289                         close_timer.AutoReset = false;
290                         close_timer.Enabled = true;
291                 }
292                 
293                 private void OnTimeoutClose (object source, ElapsedEventArgs e)
294                 {
295                         this.Close ();
296                 }
297 #endif
298
299                 protected
300 #if NET_2_0
301                 override
302 #else
303                 virtual
304 #endif
305                 void Dispose (bool disposing)
306                 {
307                         if (disposed) 
308                                 return;
309                         disposed = true;
310                         
311                         if (owns_socket) {
312                                 Socket s = socket;
313                                 if (s != null)
314                                         s.Close ();
315                         }
316                         socket = null;
317                         access = 0;
318
319                         if (disposing)
320                                 GC.SuppressFinalize (this);
321                 }
322
323                 public override int EndRead (IAsyncResult ar)
324                 {
325                         CheckDisposed ();
326                         int res;
327
328                         if (ar == null)
329                                 throw new ArgumentNullException ("async result is null");
330
331                         Socket s = socket;
332
333                         if (s == null) {
334                                 throw new IOException("Connection closed");
335                         }
336
337                         try {
338                                 res = s.EndReceive (ar);
339                         } catch (Exception e) {
340                                 throw new IOException ("EndRead failure", e);
341                         }
342                         return res;
343                 }
344
345                 public override void EndWrite (IAsyncResult ar)
346                 {
347                         CheckDisposed ();
348                         if (ar == null)
349                                 throw new ArgumentNullException ("async result is null");
350
351                         Socket s = socket;
352
353                         if (s == null) {
354                                 throw new IOException("Connection closed");
355                         }
356
357                         try {
358                                 s.EndSend (ar);
359                         } catch (Exception e) {
360                                 throw new IOException ("EndWrite failure", e);
361                         }
362                 }
363
364                 public override void Flush ()
365                 {
366                         // network streams are non-buffered, this is a no-op
367                 }
368
369 #if !NET_2_0
370                 void IDisposable.Dispose ()
371                 {
372                         Dispose (true);
373                 }
374 #endif
375
376                 public override int Read ([In,Out] byte [] buffer, int offset, int size)
377                 {
378                         CheckDisposed ();
379                         int res;
380
381                         if (buffer == null)
382                                 throw new ArgumentNullException ("buffer is null");
383                         if(offset<0 || offset>buffer.Length) {
384                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
385                         }
386                         if(size < 0 || offset+size>buffer.Length) {
387                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
388                         }
389
390                         Socket s = socket;
391
392                         if (s == null) {
393                                 throw new IOException("Connection closed");
394                         }
395
396                         try {
397                                 res = s.Receive (buffer, offset, size, 0);
398                         } catch (Exception e) {
399                                 throw new IOException ("Read failure", e);
400                         }
401                         
402                         return res;
403                 }
404
405                 public override long Seek (long offset, SeekOrigin origin)
406                 {
407                         // NetworkStream objects do not support seeking.
408                         
409                         throw new NotSupportedException ();
410                 }
411
412                 public override void SetLength (long value)
413                 {
414                         // NetworkStream objects do not support SetLength
415                         
416                         throw new NotSupportedException ();
417                 }
418
419                 public override void Write (byte [] buffer, int offset, int size)
420                 {
421                         CheckDisposed ();
422                         if (buffer == null)
423                                 throw new ArgumentNullException ("buffer");
424
425                         if (offset < 0 || offset > buffer.Length)
426                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
427
428                         if (size < 0 || size > buffer.Length - offset)
429                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
430
431                         Socket s = socket;
432
433                         if (s == null) {
434                                 throw new IOException("Connection closed");
435                         }
436
437                         try {
438                                 int count = 0;
439                                 while (size - count > 0) {
440                                         count += s.Send (buffer, offset + count, size - count, 0);
441                                 }
442                         } catch (Exception e) {
443                                 throw new IOException ("Write failure", e); 
444                         }
445                 }
446                 
447                 private void CheckDisposed ()
448                 {
449                         if (disposed)
450                                 throw new ObjectDisposedException (GetType().FullName);
451                 }
452
453 #if TARGET_JVM
454                 public void ChangeToSSLSocket()
455                 {
456                         socket.ChangeToSSL();
457                 }
458 #endif
459
460         }
461 }