Copied remotely
[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)
52                 {
53                         byte[] buffer = new byte[6];
54
55                         try {
56                                 StreamRead (networkStream, buffer, 6);
57                         } catch {
58                                 return MessageStatus.Unknown;
59                         }
60
61                         try
62                         {
63                                 bool[] isOnTrack = new bool[_msgHeaders.Length];
64                                 bool atLeastOneOnTrack = true;
65                                 int i = 0;
66
67                                 while (atLeastOneOnTrack)
68                                 {
69                                         atLeastOneOnTrack = false;
70                                         byte c = buffer [i];
71                                         for (int n = 0; n<_msgHeaders.Length; n++)
72                                         {
73                                                 if (i > 0 && !isOnTrack[n]) continue;
74
75                                                 isOnTrack[n] = (c == _msgHeaders[n][i]);
76                                                 if (isOnTrack[n] && (i == _msgHeaders[n].Length-1)) return (MessageStatus) n;
77                                                 atLeastOneOnTrack = atLeastOneOnTrack || isOnTrack[n];
78                                         }
79                                         i++;
80                                 }
81                                 return MessageStatus.Unknown;
82                         }
83                         catch (IOException)
84                         {
85                                 // Stream closed
86                                 return MessageStatus.CancelSignal;
87                         }
88                 }
89                 
90                 static bool StreamRead (Stream networkStream, byte[] buffer, int count)
91                 {
92                         int nr = 0;
93                         do {
94                                 int pr = networkStream.Read (buffer, nr, count - nr);
95                                 if (pr == 0)
96                                         throw new RemotingException ("Connection closed");
97                                 nr += pr;
98                         } while (nr < count);
99                         return true;
100                 }
101
102                 public static void SendMessageStream (Stream networkStream, Stream data, ITransportHeaders requestHeaders, byte[] buffer)
103                 {
104                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
105
106                         // Writes the message start header
107                         byte[] dotnetHeader = _msgHeaders[(int) MessageStatus.MethodMessage];
108                         networkStream.Write(dotnetHeader, 0, dotnetHeader.Length);
109
110                         // Writes header tag (0x0000 if request stream, 0x0002 if response stream)
111                         if(requestHeaders[CommonTransportKeys.RequestUri]!=null) buffer [0] = (byte) 0;
112                         else buffer[0] = (byte) 2;
113                         buffer [1] = (byte) 0 ;
114
115                         // Writes ID
116                         buffer [2] = (byte) 0;
117
118                         // Writes assemblyID????
119                         buffer [3] = (byte) 0;
120
121                         // Writes the length of the stream being sent (not including the headers)
122                         int num = (int)data.Length;
123                         buffer [4] = (byte) num;
124                         buffer [5] = (byte) (num >> 8);
125                         buffer [6] = (byte) (num >> 16);
126                         buffer [7] = (byte) (num >> 24);
127                         networkStream.Write(buffer, 0, 8);
128         
129                         // Writes the message headers
130                         SendHeaders (networkStream, requestHeaders, buffer);
131
132                         // Writes the stream
133                         if (data is MemoryStream)
134                         {
135                                 // The copy of the stream can be optimized. The internal
136                                 // buffer of MemoryStream can be used.
137                                 MemoryStream memStream = (MemoryStream)data;
138                                 networkStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
139                         }
140                         else
141                         {
142                                 int nread = data.Read (buffer, 0, buffer.Length);
143                                 while (nread > 0)
144                                 {
145                                         networkStream.Write (buffer, 0, nread);
146                                         nread = data.Read (buffer, 0, buffer.Length);
147                                 }
148                         }
149                 }
150
151                 private static void SendHeaders(Stream networkStream, ITransportHeaders requestHeaders, byte[] buffer)
152                 {
153                         // Writes the headers as a sequence of strings
154                         if (networkStream != null)
155                         {
156                                 IEnumerator e = requestHeaders.GetEnumerator();
157                                 while (e.MoveNext())
158                                 {
159                                         DictionaryEntry hdr = (DictionaryEntry)e.Current;
160                                         switch (hdr.Key.ToString())
161                                         {
162                                                 case CommonTransportKeys.RequestUri: 
163                                                         buffer[0] = 4; buffer[1] = 0; buffer[2] = 1;
164                                                         networkStream.Write(buffer, 0, 3);
165                                                         break;
166                                                 case "Content-Type": 
167                                                         buffer[0] = 6; buffer[1] = 0; buffer[2] = 1;
168                                                         networkStream.Write(buffer, 0, 3);
169                                                         break;
170                                                 default: 
171                                                         buffer[0] = 1; buffer[1] = 0; buffer[2] = 1;
172                                                         networkStream.Write(buffer, 0, 3);
173                                                         SendString (networkStream, hdr.Key.ToString(), buffer);
174                                                         break;
175                                         }
176                                         networkStream.WriteByte (1);
177                                         SendString (networkStream, hdr.Value.ToString(), buffer);
178                                 }
179                         }
180                         networkStream.WriteByte (0);    // End of headers
181                         networkStream.WriteByte (0);
182                 }
183                 
184                 public static ITransportHeaders ReceiveHeaders (Stream networkStream, byte[] buffer)
185                 {
186                         StreamRead (networkStream, buffer, 2);
187                         
188                         byte headerType = buffer [0];
189                         TransportHeaders headers = new TransportHeaders ();
190
191                         while (headerType != 0)
192                         {
193                                 string key;
194                                 StreamRead (networkStream, buffer, 1);  // byte 1
195                                 switch (headerType)
196                                 {
197                                         case 4: key = CommonTransportKeys.RequestUri; break;
198                                         case 6: key = "Content-Type"; break;
199                                         case 1: key = ReceiveString (networkStream, buffer); break;
200                                         default: throw new NotSupportedException ("Unknown header code: " + headerType);
201                                 }
202                                 StreamRead (networkStream, buffer, 1);  // byte 1
203                                 headers[key] = ReceiveString (networkStream, buffer);
204
205                                 StreamRead (networkStream, buffer, 2);
206                                 headerType = buffer [0];
207                         }
208
209                         return headers;
210                 }
211                 
212                 public static Stream ReceiveMessageStream (Stream networkStream, out ITransportHeaders headers, byte[] buffer)
213                 {
214                         headers = null;
215
216                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
217
218                         // Reads header tag:  0 -> Stream with headers or 2 -> Response Stream
219                         // +
220                         // Gets the length of the data stream
221                         StreamRead (networkStream, buffer, 8);
222
223                         int byteCount = (buffer [4] | (buffer [5] << 8) |
224                                 (buffer [6] << 16) | (buffer [7] << 24));
225
226                         // Reads the headers
227                         headers = ReceiveHeaders (networkStream, buffer);
228
229                         byte[] resultBuffer = new byte[byteCount];
230                         StreamRead (networkStream, resultBuffer, byteCount);
231
232                         return new MemoryStream (resultBuffer);
233                 }               
234
235                 private static void SendString (Stream networkStream, string str, byte[] buffer)
236                 {
237                         // Allocates a buffer. Use the internal buffer if it is 
238                         // big enough. If not, create a new one.
239
240                         int maxBytes = Encoding.UTF8.GetMaxByteCount(str.Length)+4;     //+4 bytes for storing the string length
241                         if (maxBytes > buffer.Length)
242                                 buffer = new byte[maxBytes];
243
244                         int num = Encoding.UTF8.GetBytes (str, 0, str.Length, buffer, 4);
245
246                         // store number of bytes (not number of chars!)
247
248                         buffer [0] = (byte) num;
249                         buffer [1] = (byte) (num >> 8);
250                         buffer [2] = (byte) (num >> 16);
251                         buffer [3] = (byte) (num >> 24);
252
253                         // Write the string bytes
254                         networkStream.Write (buffer, 0, num + 4);
255                 }
256
257                 private static string ReceiveString (Stream networkStream, byte[] buffer)
258                 {
259                         StreamRead (networkStream, buffer, 4);
260
261                         // Reads the number of bytes (not chars!)
262
263                         int byteCount = (buffer [0] | (buffer [1] << 8) |
264                                 (buffer [2] << 16) | (buffer [3] << 24));
265
266                         if (byteCount == 0) return string.Empty;
267
268                         // Allocates a buffer of the correct size. Use the
269                         // internal buffer if it is big enough
270
271                         if (byteCount > buffer.Length)
272                                 buffer = new byte[byteCount];
273
274                         // Reads the string
275
276                         StreamRead (networkStream, buffer, byteCount);
277                         char[] chars = Encoding.UTF8.GetChars (buffer, 0, byteCount);
278         
279                         return new string (chars);
280                 }
281                 
282         }
283 }