2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Mono.Posix / Mono.Remoting.Channels.Unix / UnixMessageIO.cs
blob833ff6e4e2b7399b8adfb82bb16a43b34e399164
1 //
2 // Mono.Remoting.Channels.Unix.UnixMessageIO.cs
3 //
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
5 //
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Runtime.Serialization;
31 using System.Runtime.Serialization.Formatters.Binary;
32 using System.Collections;
33 using System.IO;
34 using System.Text;
35 using System.Net.Sockets;
36 using System.Runtime.Remoting.Channels;
37 using System.Runtime.Remoting;
39 namespace Mono.Remoting.Channels.Unix
41 enum MessageStatus { MethodMessage = 0, CancelSignal = 1, Unknown = 10}
43 internal class UnixMessageIO
45 static byte[][] _msgHeaders =
47 new byte[] { (byte)'.', (byte)'N', (byte)'E', (byte)'T', 1, 0 },
48 new byte[] { 255, 255, 255, 255, 255, 255 }
51 public static int DefaultStreamBufferSize = 1000;
53 // Identifies an incoming message
54 public static MessageStatus ReceiveMessageStatus (Stream networkStream, byte[] buffer)
56 try {
57 StreamRead (networkStream, buffer, 6);
58 } catch (Exception ex) {
59 throw new RemotingException ("Unix transport error.", ex);
62 try
64 bool[] isOnTrack = new bool[_msgHeaders.Length];
65 bool atLeastOneOnTrack = true;
66 int i = 0;
68 while (atLeastOneOnTrack)
70 atLeastOneOnTrack = false;
71 byte c = buffer [i];
72 for (int n = 0; n<_msgHeaders.Length; n++)
74 if (i > 0 && !isOnTrack[n]) continue;
76 isOnTrack[n] = (c == _msgHeaders[n][i]);
77 if (isOnTrack[n] && (i == _msgHeaders[n].Length-1)) return (MessageStatus) n;
78 atLeastOneOnTrack = atLeastOneOnTrack || isOnTrack[n];
80 i++;
82 return MessageStatus.Unknown;
84 catch (Exception ex) {
85 throw new RemotingException ("Unix transport error.", ex);
89 static bool StreamRead (Stream networkStream, byte[] buffer, int count)
91 int nr = 0;
92 do {
93 int pr = networkStream.Read (buffer, nr, count - nr);
94 if (pr == 0)
95 throw new RemotingException ("Connection closed");
96 nr += pr;
97 } while (nr < count);
98 return true;
101 public static void SendMessageStream (Stream networkStream, Stream data, ITransportHeaders requestHeaders, byte[] buffer)
103 if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
105 // Writes the message start header
106 byte[] dotnetHeader = _msgHeaders[(int) MessageStatus.MethodMessage];
107 networkStream.Write(dotnetHeader, 0, dotnetHeader.Length);
109 // Writes header tag (0x0000 if request stream, 0x0002 if response stream)
110 if(requestHeaders["__RequestUri"]!=null) buffer [0] = (byte) 0;
111 else buffer[0] = (byte) 2;
112 buffer [1] = (byte) 0 ;
114 // Writes ID
115 buffer [2] = (byte) 0;
117 // Writes assemblyID????
118 buffer [3] = (byte) 0;
120 // Writes the length of the stream being sent (not including the headers)
121 int num = (int)data.Length;
122 buffer [4] = (byte) num;
123 buffer [5] = (byte) (num >> 8);
124 buffer [6] = (byte) (num >> 16);
125 buffer [7] = (byte) (num >> 24);
126 networkStream.Write(buffer, 0, 8);
128 // Writes the message headers
129 SendHeaders (networkStream, requestHeaders, buffer);
131 // Writes the stream
132 if (data is MemoryStream)
134 // The copy of the stream can be optimized. The internal
135 // buffer of MemoryStream can be used.
136 MemoryStream memStream = (MemoryStream)data;
137 networkStream.Write (memStream.GetBuffer(), 0, (int)memStream.Length);
139 else
141 int nread = data.Read (buffer, 0, buffer.Length);
142 while (nread > 0)
144 networkStream.Write (buffer, 0, nread);
145 nread = data.Read (buffer, 0, buffer.Length);
150 static byte[] msgUriTransportKey = new byte[] { 4, 0, 1, 1 };
151 static byte[] msgContentTypeTransportKey = new byte[] { 6, 0, 1, 1 };
152 static byte[] msgDefaultTransportKey = new byte[] { 1, 0, 1 };
153 static byte[] msgHeaderTerminator = new byte[] { 0, 0 };
155 private static void SendHeaders(Stream networkStream, ITransportHeaders requestHeaders, byte[] buffer)
157 // Writes the headers as a sequence of strings
158 if (networkStream != null)
160 IEnumerator e = requestHeaders.GetEnumerator();
161 while (e.MoveNext())
163 DictionaryEntry hdr = (DictionaryEntry)e.Current;
164 switch (hdr.Key.ToString())
166 case "__RequestUri":
167 networkStream.Write (msgUriTransportKey, 0, 4);
168 break;
169 case "Content-Type":
170 networkStream.Write (msgContentTypeTransportKey, 0, 4);
171 break;
172 default:
173 networkStream.Write (msgDefaultTransportKey, 0, 3);
174 SendString (networkStream, hdr.Key.ToString(), buffer);
175 networkStream.WriteByte (1);
176 break;
178 SendString (networkStream, hdr.Value.ToString(), buffer);
181 networkStream.Write (msgHeaderTerminator, 0, 2); // End of headers
184 public static ITransportHeaders ReceiveHeaders (Stream networkStream, byte[] buffer)
186 StreamRead (networkStream, buffer, 2);
188 byte headerType = buffer [0];
189 TransportHeaders headers = new TransportHeaders ();
191 while (headerType != 0)
193 string key;
194 StreamRead (networkStream, buffer, 1); // byte 1
195 switch (headerType)
197 case 4: key = "__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);
202 StreamRead (networkStream, buffer, 1); // byte 1
203 headers[key] = ReceiveString (networkStream, buffer);
205 StreamRead (networkStream, buffer, 2);
206 headerType = buffer [0];
209 return headers;
212 public static Stream ReceiveMessageStream (Stream networkStream, out ITransportHeaders headers, byte[] buffer)
214 headers = null;
216 if (buffer == null) buffer = new byte[DefaultStreamBufferSize];
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);
223 int byteCount = (buffer [4] | (buffer [5] << 8) |
224 (buffer [6] << 16) | (buffer [7] << 24));
226 // Reads the headers
227 headers = ReceiveHeaders (networkStream, buffer);
229 byte[] resultBuffer = new byte[byteCount];
230 StreamRead (networkStream, resultBuffer, byteCount);
232 return new MemoryStream (resultBuffer);
235 private static void SendString (Stream networkStream, string str, byte[] buffer)
237 // Allocates a buffer. Use the internal buffer if it is
238 // big enough. If not, create a new one.
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];
244 int num = Encoding.UTF8.GetBytes (str, 0, str.Length, buffer, 4);
246 // store number of bytes (not number of chars!)
248 buffer [0] = (byte) num;
249 buffer [1] = (byte) (num >> 8);
250 buffer [2] = (byte) (num >> 16);
251 buffer [3] = (byte) (num >> 24);
253 // Write the string bytes
254 networkStream.Write (buffer, 0, num + 4);
257 private static string ReceiveString (Stream networkStream, byte[] buffer)
259 StreamRead (networkStream, buffer, 4);
261 // Reads the number of bytes (not chars!)
263 int byteCount = (buffer [0] | (buffer [1] << 8) |
264 (buffer [2] << 16) | (buffer [3] << 24));
266 if (byteCount == 0) return string.Empty;
268 // Allocates a buffer of the correct size. Use the
269 // internal buffer if it is big enough
271 if (byteCount > buffer.Length)
272 buffer = new byte[byteCount];
274 // Reads the string
276 StreamRead (networkStream, buffer, byteCount);
277 char[] chars = Encoding.UTF8.GetChars (buffer, 0, byteCount);
279 return new string (chars);