4c57d2129fb7969e1f257e5b123525078ff5efff
[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                         SendMessageStream (networkStream, data, requestHeaders, buffer, false);
101                 }
102
103                 public static void SendMessageStream (Stream networkStream, Stream data, ITransportHeaders requestHeaders, byte[] buffer, bool isOneWay)
104                 {
105                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
106
107                         // Writes the message start header
108                         byte[] dotnetHeader = _msgHeaders[(int) MessageStatus.MethodMessage];
109                         networkStream.Write(dotnetHeader, 0, dotnetHeader.Length);
110
111                         // Writes the header tag
112                         // 0x0000 - request stream
113                         // 0x0001 - OneWay request stream
114                         // 0x0002 - response stream
115                         if(requestHeaders[CommonTransportKeys.RequestUri]!=null) {
116                                 buffer [0] = isOneWay ? (byte) 1 : (byte) 0;
117                         }
118                         else {
119                                 buffer[0] = (byte) 2;
120                         }
121                         buffer [1] = (byte) 0 ;
122
123                         // Writes ID
124                         buffer [2] = (byte) 0;
125
126                         // Writes assemblyID????
127                         buffer [3] = (byte) 0;
128
129                         // Writes the length of the stream being sent (not including the headers)
130                         int num = (int)data.Length;
131                         buffer [4] = (byte) num;
132                         buffer [5] = (byte) (num >> 8);
133                         buffer [6] = (byte) (num >> 16);
134                         buffer [7] = (byte) (num >> 24);
135                         networkStream.Write(buffer, 0, 8);
136         
137                         // Writes the message headers
138                         SendHeaders (networkStream, requestHeaders, buffer);
139
140                         if (data.Length == 0)
141                                 return;
142
143                         // Writes the stream
144                         if (data is MemoryStream)
145                         {
146                                 // The copy of the stream can be optimized. The internal
147                                 // buffer of MemoryStream can be used.
148                                 MemoryStream memStream = (MemoryStream)data;
149                                 networkStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
150                         }
151                         else
152                         {
153                                 int nread = data.Read (buffer, 0, buffer.Length);
154                                 while (nread > 0)
155                                 {
156                                         networkStream.Write (buffer, 0, nread);
157                                         nread = data.Read (buffer, 0, buffer.Length);
158                                 }
159                         }
160                 }
161                 
162                 static byte[] msgUriTransportKey = new byte[] { 4, 0, 1, 1 };
163                 static byte[] msgContentTypeTransportKey = new byte[] { 6, 0, 1, 1 };
164                 static byte[] msgDefaultTransportKey = new byte[] { 1, 0, 1 };
165                 static byte[] msgHeaderTerminator = new byte[] { 0, 0 };
166
167                 private static void SendHeaders(Stream networkStream, ITransportHeaders requestHeaders, byte[] buffer)
168                 {
169                         // Writes the headers as a sequence of strings
170                         if (networkStream != null)
171                         {
172                                 IEnumerator e = requestHeaders.GetEnumerator();
173                                 while (e.MoveNext())
174                                 {
175                                         DictionaryEntry hdr = (DictionaryEntry)e.Current;
176                                         switch (hdr.Key.ToString())
177                                         {
178                                                 case CommonTransportKeys.RequestUri: 
179                                                         networkStream.Write (msgUriTransportKey, 0, 4);
180                                                         break;
181                                                 case "Content-Type": 
182                                                         networkStream.Write (msgContentTypeTransportKey, 0, 4);
183                                                         break;
184                                                 default: 
185                                                         networkStream.Write (msgDefaultTransportKey, 0, 3);
186                                                         SendString (networkStream, hdr.Key.ToString(), buffer);
187                                                         networkStream.WriteByte (1);
188                                                         break;
189                                         }
190                                         SendString (networkStream, hdr.Value.ToString(), buffer);
191                                 }
192                         }
193                         networkStream.Write (msgHeaderTerminator, 0, 2);        // End of headers
194                 }
195                 
196                 public static ITransportHeaders ReceiveHeaders (Stream networkStream, byte[] buffer)
197                 {
198                         StreamRead (networkStream, buffer, 2);
199                         
200                         byte headerType = buffer [0];
201                         TransportHeaders headers = new TransportHeaders ();
202
203                         while (headerType != 0)
204                         {
205                                 string key;
206                                 StreamRead (networkStream, buffer, 1);  // byte 1
207                                 switch (headerType)
208                                 {
209                                         case 4: key = CommonTransportKeys.RequestUri; break;
210                                         case 6: key = "Content-Type"; break;
211                                         case 1: key = ReceiveString (networkStream, buffer); break;
212                                         default: throw new NotSupportedException ("Unknown header code: " + headerType);
213                                 }
214                                 StreamRead (networkStream, buffer, 1);  // byte 1
215                                 headers[key] = ReceiveString (networkStream, buffer);
216
217                                 StreamRead (networkStream, buffer, 2);
218                                 headerType = buffer [0];
219                         }
220
221                         return headers;
222                 }
223                 
224                 public static Stream ReceiveMessageStream (Stream networkStream, out ITransportHeaders headers, byte[] buffer)
225                 {
226                         headers = null;
227
228                         if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
229
230                         // Reads header tag:  0 -> Stream with headers or 2 -> Response Stream
231                         // +
232                         // Gets the length of the data stream
233                         StreamRead (networkStream, buffer, 8);
234
235                         int byteCount = (buffer [4] | (buffer [5] << 8) |
236                                 (buffer [6] << 16) | (buffer [7] << 24));
237
238                         // Reads the headers
239                         headers = ReceiveHeaders (networkStream, buffer);
240
241                         if (byteCount > 0) {
242                                 byte[] resultBuffer = new byte[byteCount];
243                                 StreamRead (networkStream, resultBuffer, byteCount);
244                                 return new MemoryStream (resultBuffer);
245                         } else {
246                                 return new MemoryStream ();
247                         }
248                 }               
249
250                 private static void SendString (Stream networkStream, string str, byte[] buffer)
251                 {
252                         // Allocates a buffer. Use the internal buffer if it is 
253                         // big enough. If not, create a new one.
254
255                         int maxBytes = Encoding.UTF8.GetMaxByteCount(str.Length)+4;     //+4 bytes for storing the string length
256                         if (maxBytes > buffer.Length)
257                                 buffer = new byte[maxBytes];
258
259                         int num = Encoding.UTF8.GetBytes (str, 0, str.Length, buffer, 4);
260
261                         // store number of bytes (not number of chars!)
262
263                         buffer [0] = (byte) num;
264                         buffer [1] = (byte) (num >> 8);
265                         buffer [2] = (byte) (num >> 16);
266                         buffer [3] = (byte) (num >> 24);
267
268                         // Write the string bytes
269                         networkStream.Write (buffer, 0, num + 4);
270                 }
271
272                 private static string ReceiveString (Stream networkStream, byte[] buffer)
273                 {
274                         StreamRead (networkStream, buffer, 4);
275
276                         // Reads the number of bytes (not chars!)
277
278                         int byteCount = (buffer [0] | (buffer [1] << 8) |
279                                 (buffer [2] << 16) | (buffer [3] << 24));
280
281                         if (byteCount == 0) return string.Empty;
282
283                         // Allocates a buffer of the correct size. Use the
284                         // internal buffer if it is big enough
285
286                         if (byteCount > buffer.Length)
287                                 buffer = new byte[byteCount];
288
289                         // Reads the string
290
291                         StreamRead (networkStream, buffer, byteCount);
292                         char[] chars = Encoding.UTF8.GetChars (buffer, 0, byteCount);
293         
294                         return new string (chars);
295                 }
296                 
297         }
298 }