Merge pull request #409 from Alkarex/patch-1
[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_1 || MOBILE
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                 public override bool CanTimeout
97                 {
98                         get {
99                                 return(true);
100                         }
101                 }
102
103                 public override bool CanWrite {
104                         get {
105                                 return access == FileAccess.ReadWrite || access == FileAccess.Write;
106                         }
107                 }
108
109                 public virtual bool DataAvailable {
110                         get {
111                                 CheckDisposed ();
112                                 return socket.Available > 0;
113                         }
114                 }
115
116                 public override long Length {
117                         get {
118                                 // Network sockets always throw an exception
119                                 throw new NotSupportedException ();
120                         }
121                 }
122
123                 public override long Position {
124                         get {
125                                 // Network sockets always throw an exception
126                                 throw new NotSupportedException ();
127                         }
128                         
129                         set {
130                                 // Network sockets always throw an exception
131                                 throw new NotSupportedException ();
132                         }
133                 }
134
135                 protected bool Readable {
136                         get {
137                                 return readable;
138                         }
139
140                         set {
141                                 readable = value;
142                         }
143                 }
144
145 #if !NET_2_1 || MOBILE
146 #if TARGET_JVM
147                 [MonoNotSupported ("Not supported since Socket.ReceiveTimeout is not supported")]
148 #endif
149                 public override int ReadTimeout
150                 {
151                         get {
152                                 int r = socket.ReceiveTimeout;
153                                 return (r <= 0) ? Timeout.Infinite : r;
154                         }
155                         set {
156                                 if (value <= 0 && value != Timeout.Infinite) {
157                                         throw new ArgumentOutOfRangeException ("value", "The value specified is less than or equal to zero and is not Infinite.");
158                                 }
159                                 
160                                 socket.ReceiveTimeout = value;
161                         }
162                 }
163 #endif
164
165                 protected Socket Socket {
166                         get {
167                                 return socket;
168                         }
169                 }
170
171                 protected bool Writeable {
172                         get {
173                                 return writeable;
174                         }
175
176                         set {
177                                 writeable = value;
178                         }
179                 }
180
181 #if !NET_2_1 || MOBILE
182 #if TARGET_JVM
183                 [MonoNotSupported ("Not supported since Socket.SendTimeout is not supported")]
184 #endif
185                 public override int WriteTimeout
186                 {
187                         get {
188                                 int r = socket.SendTimeout;
189                                 return (r <= 0) ? Timeout.Infinite : r;
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
270 #if !NET_2_1 || MOBILE
271                 public void Close (int timeout)
272                 {
273                         if (timeout < -1) {
274                                 throw new ArgumentOutOfRangeException ("timeout", "timeout is less than -1");
275                         }
276                         
277                         System.Timers.Timer close_timer = new System.Timers.Timer ();
278                         close_timer.Elapsed += new ElapsedEventHandler (OnTimeoutClose);
279                         /* NB timeout is in milliseconds here, cf
280                          * seconds in Socket.Close(int)
281                          */
282                         close_timer.Interval = timeout;
283                         close_timer.AutoReset = false;
284                         close_timer.Enabled = true;
285                 }
286                 
287                 private void OnTimeoutClose (object source, ElapsedEventArgs e)
288                 {
289                         this.Close ();
290                 }
291 #endif
292
293                 protected override void Dispose (bool disposing)
294                 {
295                         if (disposed) 
296                                 return;
297                         disposed = true;
298                         
299                         if (owns_socket) {
300                                 Socket s = socket;
301                                 if (s != null)
302                                         s.Close ();
303                         }
304                         socket = null;
305                         access = 0;
306
307                         if (disposing)
308                                 GC.SuppressFinalize (this);
309                 }
310
311                 public override int EndRead (IAsyncResult ar)
312                 {
313                         CheckDisposed ();
314                         int res;
315
316                         if (ar == null)
317                                 throw new ArgumentNullException ("async result is null");
318
319                         Socket s = socket;
320
321                         if (s == null) {
322                                 throw new IOException("Connection closed");
323                         }
324
325                         try {
326                                 res = s.EndReceive (ar);
327                         } catch (Exception e) {
328                                 throw new IOException ("EndRead failure", e);
329                         }
330                         return res;
331                 }
332
333                 public override void EndWrite (IAsyncResult ar)
334                 {
335                         CheckDisposed ();
336                         if (ar == null)
337                                 throw new ArgumentNullException ("async result is null");
338
339                         Socket s = socket;
340
341                         if (s == null) {
342                                 throw new IOException("Connection closed");
343                         }
344
345                         try {
346                                 s.EndSend (ar);
347                         } catch (Exception e) {
348                                 throw new IOException ("EndWrite failure", e);
349                         }
350                 }
351
352                 public override void Flush ()
353                 {
354                         // network streams are non-buffered, this is a no-op
355                 }
356
357                 public override int Read ([In,Out] byte [] buffer, int offset, int size)
358                 {
359                         CheckDisposed ();
360                         int res;
361
362                         if (buffer == null)
363                                 throw new ArgumentNullException ("buffer is null");
364                         if(offset<0 || offset>buffer.Length) {
365                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
366                         }
367                         if(size < 0 || offset+size>buffer.Length) {
368                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
369                         }
370
371                         Socket s = socket;
372
373                         if (s == null) {
374                                 throw new IOException("Connection closed");
375                         }
376
377                         try {
378                                 res = s.Receive (buffer, offset, size, 0);
379                         } catch (Exception e) {
380                                 throw new IOException ("Read failure", e);
381                         }
382                         
383                         return res;
384                 }
385
386                 public override long Seek (long offset, SeekOrigin origin)
387                 {
388                         // NetworkStream objects do not support seeking.
389                         
390                         throw new NotSupportedException ();
391                 }
392
393                 public override void SetLength (long value)
394                 {
395                         // NetworkStream objects do not support SetLength
396                         
397                         throw new NotSupportedException ();
398                 }
399
400                 public override void Write (byte [] buffer, int offset, int size)
401                 {
402                         CheckDisposed ();
403                         if (buffer == null)
404                                 throw new ArgumentNullException ("buffer");
405
406                         if (offset < 0 || offset > buffer.Length)
407                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
408
409                         if (size < 0 || size > buffer.Length - offset)
410                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
411
412                         Socket s = socket;
413
414                         if (s == null) {
415                                 throw new IOException("Connection closed");
416                         }
417
418                         try {
419                                 int count = 0;
420                                 while (size - count > 0) {
421                                         count += s.Send (buffer, offset + count, size - count, 0);
422                                 }
423                         } catch (Exception e) {
424                                 throw new IOException ("Write failure", e); 
425                         }
426                 }
427                 
428                 private void CheckDisposed ()
429                 {
430                         if (disposed)
431                                 throw new ObjectDisposedException (GetType().FullName);
432                 }
433
434 #if TARGET_JVM
435                 public void ChangeToSSLSocket()
436                 {
437                         socket.ChangeToSSL();
438                 }
439 #endif
440
441         }
442 }