New test.
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpMessageIO.cs
1 // System.Runtime.Remoting.Channels.Tcp.TcpMessageIO.cs
2 //
3 // Author: Lluis Sanchez Gual (lluis@ideary.com)
4 //
5 // (C) 2002 Lluis Sanchez Gual
6
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27
28 using System;
29 using System.Runtime.Serialization;
30 using System.Runtime.Serialization.Formatters.Binary;
31 using System.Collections;
32 using System.IO;
33 using System.Text;
34 using System.Net.Sockets;
35
36 namespace System.Runtime.Remoting.Channels.Tcp 
37 {
38         enum MessageStatus { MethodMessage = 0, CancelSignal = 1, Unknown = 10}
39
40         internal class TcpMessageIO
41         {
42                 static byte[][] _msgHeaders = 
43                           {
44                                   new byte[] { (byte)'.', (byte)'N', (byte)'E', (byte)'T', 1, 0 },
45                                   new byte[] { 255, 255, 255, 255, 255, 255 }
46                           };
47                                                         
48                 public static int DefaultStreamBufferSize = 1000;
49
50                 // Identifies an incoming message
51                 public static MessageStatus ReceiveMessageStatus (Stream networkStream, byte[] buffer)
52                 {
53                         try {
54                                 StreamRead (networkStream, buffer, 6);
55                         } catch (Exception ex) {
56                                 throw new RemotingException ("Tcp transport error.", ex);
57                         }
58
59                         try
60                         {
61                                 bool[] isOnTrack = new bool[_msgHeaders.Length];
62                                 bool atLeastOneOnTrack = true;
63                                 int i = 0;
64
65                                 while (atLeastOneOnTrack)
66                                 {
67                                         atLeastOneOnTrack = false;
68                                         byte c = buffer [i];
69                                         for (int n = 0; n<_msgHeaders.Length; n++)
70                                         {
71                                                 if (i > 0 && !isOnTrack[n]) continue;
72
73                                                 isOnTrack[n] = (c == _msgHeaders[n][i]);
74                                                 if (isOnTrack[n] && (i == _msgHeaders[n].Length-1)) return (MessageStatus) n;
75                                                 atLeastOneOnTrack = atLeastOneOnTrack || isOnTrack[n];
76                                         }
77                                         i++;
78                                 }
79                                 return MessageStatus.Unknown;
80                         }
81                         catch (Exception ex) {
82                                 throw new RemotingException ("Tcp transport error.", ex);
83                         }
84                 }
85                 
86                 static bool StreamRead (Stream networkStream, byte[] buffer, int count)
87                 {
88                         int nr = 0;
89                         do {
90                                 int pr = networkStream.Read (buffer, nr, count - nr);
91                                 if (pr == 0)
92                                         throw new RemotingException ("Connection closed");
93                                 nr += pr;
94                         } while (nr < count);
95                         return true;
96                 }
97
98                 public static void SendMessageStream (Stream networkStream, Stream data, ITransportHeaders requestHeaders, byte[] buffer)
99                 {
100                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
101
102                         // Writes the message start header
103                         byte[] dotnetHeader = _msgHeaders[(int) MessageStatus.MethodMessage];
104                         networkStream.Write(dotnetHeader, 0, dotnetHeader.Length);
105
106                         // Writes header tag (0x0000 if request stream, 0x0002 if response stream)
107                         if(requestHeaders[CommonTransportKeys.RequestUri]!=null) buffer [0] = (byte) 0;
108                         else buffer[0] = (byte) 2;
109                         buffer [1] = (byte) 0 ;
110
111                         // Writes ID
112                         buffer [2] = (byte) 0;
113
114                         // Writes assemblyID????
115                         buffer [3] = (byte) 0;
116
117                         // Writes the length of the stream being sent (not including the headers)
118                         int num = (int)data.Length;
119                         buffer [4] = (byte) num;
120                         buffer [5] = (byte) (num >> 8);
121                         buffer [6] = (byte) (num >> 16);
122                         buffer [7] = (byte) (num >> 24);
123                         networkStream.Write(buffer, 0, 8);
124         
125                         // Writes the message headers
126                         SendHeaders (networkStream, requestHeaders, buffer);
127
128                         // Writes the stream
129                         if (data is MemoryStream)
130                         {
131                                 // The copy of the stream can be optimized. The internal
132                                 // buffer of MemoryStream can be used.
133                                 MemoryStream memStream = (MemoryStream)data;
134                                 networkStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
135                         }
136                         else
137                         {
138                                 int nread = data.Read (buffer, 0, buffer.Length);
139                                 while (nread > 0)
140                                 {
141                                         networkStream.Write (buffer, 0, nread);
142                                         nread = data.Read (buffer, 0, buffer.Length);
143                                 }
144                         }
145                 }
146                 
147                 static byte[] msgUriTransportKey = new byte[] { 4, 0, 1, 1 };
148                 static byte[] msgContentTypeTransportKey = new byte[] { 6, 0, 1, 1 };
149                 static byte[] msgDefaultTransportKey = new byte[] { 1, 0, 1 };
150                 static byte[] msgHeaderTerminator = new byte[] { 0, 0 };
151
152                 private static void SendHeaders(Stream networkStream, ITransportHeaders requestHeaders, byte[] buffer)
153                 {
154                         // Writes the headers as a sequence of strings
155                         if (networkStream != null)
156                         {
157                                 IEnumerator e = requestHeaders.GetEnumerator();
158                                 while (e.MoveNext())
159                                 {
160                                         DictionaryEntry hdr = (DictionaryEntry)e.Current;
161                                         switch (hdr.Key.ToString())
162                                         {
163                                                 case CommonTransportKeys.RequestUri: 
164                                                         networkStream.Write (msgUriTransportKey, 0, 4);
165                                                         break;
166                                                 case "Content-Type": 
167                                                         networkStream.Write (msgContentTypeTransportKey, 0, 4);
168                                                         break;
169                                                 default: 
170                                                         networkStream.Write (msgDefaultTransportKey, 0, 3);
171                                                         SendString (networkStream, hdr.Key.ToString(), buffer);
172                                                         networkStream.WriteByte (1);
173                                                         break;
174                                         }
175                                         SendString (networkStream, hdr.Value.ToString(), buffer);
176                                 }
177                         }
178                         networkStream.Write (msgHeaderTerminator, 0, 2);        // End of headers
179                 }
180                 
181                 public static ITransportHeaders ReceiveHeaders (Stream networkStream, byte[] buffer)
182                 {
183                         StreamRead (networkStream, buffer, 2);
184                         
185                         byte headerType = buffer [0];
186                         TransportHeaders headers = new TransportHeaders ();
187
188                         while (headerType != 0)
189                         {
190                                 string key;
191                                 StreamRead (networkStream, buffer, 1);  // byte 1
192                                 switch (headerType)
193                                 {
194                                         case 4: key = CommonTransportKeys.RequestUri; break;
195                                         case 6: key = "Content-Type"; break;
196                                         case 1: key = ReceiveString (networkStream, buffer); break;
197                                         default: throw new NotSupportedException ("Unknown header code: " + headerType);
198                                 }
199                                 StreamRead (networkStream, buffer, 1);  // byte 1
200                                 headers[key] = ReceiveString (networkStream, buffer);
201
202                                 StreamRead (networkStream, buffer, 2);
203                                 headerType = buffer [0];
204                         }
205
206                         return headers;
207                 }
208                 
209                 public static Stream ReceiveMessageStream (Stream networkStream, out ITransportHeaders headers, byte[] buffer)
210                 {
211                         headers = null;
212
213                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
214
215                         // Reads header tag:  0 -> Stream with headers or 2 -> Response Stream
216                         // +
217                         // Gets the length of the data stream
218                         StreamRead (networkStream, buffer, 8);
219
220                         int byteCount = (buffer [4] | (buffer [5] << 8) |
221                                 (buffer [6] << 16) | (buffer [7] << 24));
222
223                         // Reads the headers
224                         headers = ReceiveHeaders (networkStream, buffer);
225
226                         byte[] resultBuffer = new byte[byteCount];
227                         StreamRead (networkStream, resultBuffer, byteCount);
228
229                         return new MemoryStream (resultBuffer);
230                 }               
231
232                 private static void SendString (Stream networkStream, string str, byte[] buffer)
233                 {
234                         // Allocates a buffer. Use the internal buffer if it is 
235                         // big enough. If not, create a new one.
236
237                         int maxBytes = Encoding.UTF8.GetMaxByteCount(str.Length)+4;     //+4 bytes for storing the string length
238                         if (maxBytes > buffer.Length)
239                                 buffer = new byte[maxBytes];
240
241                         int num = Encoding.UTF8.GetBytes (str, 0, str.Length, buffer, 4);
242
243                         // store number of bytes (not number of chars!)
244
245                         buffer [0] = (byte) num;
246                         buffer [1] = (byte) (num >> 8);
247                         buffer [2] = (byte) (num >> 16);
248                         buffer [3] = (byte) (num >> 24);
249
250                         // Write the string bytes
251                         networkStream.Write (buffer, 0, num + 4);
252                 }
253
254                 private static string ReceiveString (Stream networkStream, byte[] buffer)
255                 {
256                         StreamRead (networkStream, buffer, 4);
257
258                         // Reads the number of bytes (not chars!)
259
260                         int byteCount = (buffer [0] | (buffer [1] << 8) |
261                                 (buffer [2] << 16) | (buffer [3] << 24));
262
263                         if (byteCount == 0) return string.Empty;
264
265                         // Allocates a buffer of the correct size. Use the
266                         // internal buffer if it is big enough
267
268                         if (byteCount > buffer.Length)
269                                 buffer = new byte[byteCount];
270
271                         // Reads the string
272
273                         StreamRead (networkStream, buffer, byteCount);
274                         char[] chars = Encoding.UTF8.GetChars (buffer, 0, byteCount);
275         
276                         return new string (chars);
277                 }
278                 
279         }
280 }