2 // TcpBinaryFrameManager.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections.Generic;
33 using System.Net.Sockets;
34 using System.Runtime.Serialization;
35 using System.Runtime.Serialization.Formatters.Binary;
36 using System.ServiceModel.Channels;
38 using System.Threading;
41 namespace System.ServiceModel.Channels
43 // seealso: [MC-NMF] Windows Protocol document.
44 class TcpBinaryFrameManager
46 class MyBinaryReader : BinaryReader
48 public MyBinaryReader (Stream s)
53 public int ReadVariableInt ()
55 return Read7BitEncodedInt ();
59 class MyBinaryWriter : BinaryWriter
61 public MyBinaryWriter (Stream s)
66 public void WriteVariableInt (int value)
68 Write7BitEncodedInt (value);
71 public int GetSizeOfLength (int value)
82 class MyXmlBinaryWriterSession : XmlBinaryWriterSession
84 public override bool TryAdd (XmlDictionaryString value, out int key)
86 if (!base.TryAdd (value, out key))
92 public List<XmlDictionaryString> List = new List<XmlDictionaryString> ();
95 public const byte VersionRecord = 0;
96 public const byte ModeRecord = 1;
97 public const byte ViaRecord = 2;
98 public const byte KnownEncodingRecord = 3;
99 public const byte ExtendingEncodingRecord = 4;
100 public const byte UnsizedEnvelopeRecord = 5;
101 public const byte SizedEnvelopeRecord = 6;
102 public const byte EndRecord = 7;
103 public const byte FaultRecord = 8;
104 public const byte UpgradeRequestRecord = 9;
105 public const byte UpgradeResponseRecord = 0xA;
106 public const byte PreambleAckRecord = 0xB;
107 public const byte PreambleEndRecord = 0xC;
109 public const byte UnsizedMessageTerminator = 0;
110 public const byte SingletonUnsizedMode = 1;
111 public const byte DuplexMode = 2;
112 public const byte SimplexMode = 3;
113 public const byte SingletonSizedMode = 4;
115 public const byte EncodingUtf8 = 3;
116 public const byte EncodingUtf16 = 4;
117 public const byte EncodingUtf16LE = 5;
118 public const byte EncodingMtom = 6;
119 public const byte EncodingBinary = 7;
120 public const byte EncodingBinaryWithDictionary = 8;
122 MyBinaryReader reader;
123 MyBinaryWriter writer;
125 public TcpBinaryFrameManager (int mode, Stream s, bool isServiceSide)
129 this.is_service_side = isServiceSide;
130 reader = new MyBinaryReader (s);
133 EncodingRecord = EncodingBinaryWithDictionary; // FIXME: it should depend on mode.
138 bool is_service_side;
142 public byte EncodingRecord { get; set; }
144 public Uri Via { get; set; }
146 public MessageEncoder Encoder { get; set; }
148 void ResetWriteBuffer ()
150 this.buffer = new MemoryStream ();
151 writer = new MyBinaryWriter (buffer);
154 static readonly byte [] empty_bytes = new byte [0];
156 public byte [] ReadSizedChunk ()
160 int length = reader.ReadVariableInt ();
165 throw new InvalidOperationException ("The message is too large.");
167 byte [] buffer = new byte [length];
168 for (int readSize = 0; readSize < length; )
169 readSize += reader.Read (buffer, readSize, length - readSize);
175 void WriteSizedChunk (byte [] data, int index, int length)
177 writer.WriteVariableInt (length);
178 writer.Write (data, index, length);
181 public void ProcessPreambleInitiator ()
185 buffer.WriteByte (VersionRecord);
186 buffer.WriteByte (1);
187 buffer.WriteByte (0);
188 buffer.WriteByte (ModeRecord);
189 buffer.WriteByte ((byte) mode);
190 buffer.WriteByte (ViaRecord);
191 writer.Write (Via.ToString ());
192 buffer.WriteByte (KnownEncodingRecord); // FIXME
193 buffer.WriteByte ((byte) EncodingRecord);
194 buffer.WriteByte (PreambleEndRecord);
196 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
200 public void ProcessPreambleAckInitiator ()
202 int b = s.ReadByte ();
204 case PreambleAckRecord:
207 throw new FaultException (reader.ReadString ());
209 throw new ProtocolException (String.Format ("Preamble Ack Record is expected, got {0:X}", b));
213 public void ProcessPreambleAckRecipient ()
215 s.WriteByte (PreambleAckRecord);
218 public bool ProcessPreambleRecipient ()
220 return ProcessPreambleRecipient (-1);
222 bool ProcessPreambleRecipient (int initialByte)
224 bool preambleEnd = false;
225 while (!preambleEnd) {
226 int b = initialByte < 0 ? s.ReadByte () : initialByte;
231 if (s.ReadByte () != 1)
232 throw new ProtocolException ("Major version must be 1");
233 if (s.ReadByte () != 0)
234 throw new ProtocolException ("Minor version must be 0");
237 if (s.ReadByte () != mode)
238 throw new ProtocolException (String.Format ("Duplex mode is expected to be {0:X}", mode));
241 Via = new Uri (reader.ReadString ());
243 case KnownEncodingRecord:
244 EncodingRecord = (byte) s.ReadByte ();
246 case ExtendingEncodingRecord:
247 throw new NotImplementedException ("ExtendingEncodingRecord");
248 case UpgradeRequestRecord:
249 throw new NotImplementedException ("UpgradeRequetRecord");
250 case UpgradeResponseRecord:
251 throw new NotImplementedException ("UpgradeResponseRecord");
252 case PreambleEndRecord:
256 throw new ProtocolException (String.Format ("Unexpected record type {0:X2}", b));
262 XmlBinaryReaderSession reader_session;
263 int reader_session_items;
265 object read_lock = new object ();
266 object write_lock = new object ();
268 public Message ReadSizedMessage ()
272 // FIXME: implement full [MC-NMF].
276 packetType = s.ReadByte ();
277 } catch (IOException) {
278 // it is already disconnected
280 } catch (SocketException) {
281 // it is already disconnected
284 // FIXME: .NET never results in -1, so there may be implementation mismatch in Socket (but might be in other places)
285 if (packetType == -1)
287 // FIXME: The client should wait for EndRecord, but if we try to send it, the socket blocks and becomes unable to work anymore.
288 if (packetType == EndRecord)
290 if (packetType != SizedEnvelopeRecord) {
291 if (is_service_side) {
293 ProcessPreambleRecipient (packetType);
294 ProcessPreambleAckRecipient ();
297 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
300 byte [] buffer = ReadSizedChunk ();
302 var ms = new MemoryStream (buffer, 0, buffer.Length);
304 // FIXME: turned out that it could be either in-band dictionary ([MC-NBFSE]), or a mere xml body ([MC-NBFS]).
305 if (EncodingRecord != EncodingBinaryWithDictionary)
306 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
309 // the returned buffer consists of a serialized reader
310 // session and the binary xml body.
312 var session = reader_session ?? new XmlBinaryReaderSession ();
313 reader_session = session;
314 byte [] rsbuf = new TcpBinaryFrameManager (0, ms, is_service_side).ReadSizedChunk ();
315 using (var rms = new MemoryStream (rsbuf, 0, rsbuf.Length)) {
316 var rbr = new BinaryReader (rms, Encoding.UTF8);
317 while (rms.Position < rms.Length)
318 session.Add (reader_session_items++, rbr.ReadString ());
320 var benc = Encoder as BinaryMessageEncoder;
322 benc.CurrentReaderSession = session;
324 // FIXME: supply maxSizeOfHeaders.
325 Message msg = Encoder.ReadMessage (ms, 0x10000);
327 benc.CurrentReaderSession = null;
334 // FIXME: support timeout
335 public Message ReadUnsizedMessage (TimeSpan timeout)
339 // Encoding type 7 is expected
340 if (EncodingRecord != EncodingBinary)
341 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
343 var packetType = s.ReadByte ();
345 if (packetType == EndRecord)
347 if (packetType != UnsizedEnvelopeRecord)
348 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
350 var ms = new MemoryStream ();
352 byte [] buffer = ReadSizedChunk ();
353 if (buffer.Length == 0) // i.e. it is UnsizedMessageTerminator (which is '0')
355 ms.Write (buffer, 0, buffer.Length);
357 ms.Seek (0, SeekOrigin.Begin);
359 // FIXME: supply correct maxSizeOfHeaders.
360 Message msg = Encoder.ReadMessage (ms, (int) ms.Length);
367 byte [] eof_buffer = new byte [1];
368 MyXmlBinaryWriterSession writer_session;
370 public void WriteSizedMessage (Message message)
376 if (EncodingRecord != 8)
377 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
379 buffer.WriteByte (SizedEnvelopeRecord);
381 MemoryStream ms = new MemoryStream ();
382 var session = writer_session ?? new MyXmlBinaryWriterSession ();
383 writer_session = session;
384 int writer_session_count = session.List.Count;
385 var benc = Encoder as BinaryMessageEncoder;
388 benc.CurrentWriterSession = session;
389 Encoder.WriteMessage (message, ms);
391 benc.CurrentWriterSession = null;
395 MemoryStream msd = new MemoryStream ();
396 BinaryWriter dw = new BinaryWriter (msd);
397 for (int i = writer_session_count; i < session.List.Count; i++)
398 dw.Write (session.List [i].Value);
401 int length = (int) (msd.Position + ms.Position);
402 var msda = msd.ToArray ();
403 int sizeOfLength = writer.GetSizeOfLength (msda.Length);
405 writer.WriteVariableInt (length + sizeOfLength); // dictionary array also involves the size of itself.
406 WriteSizedChunk (msda, 0, msda.Length);
408 var arr = ms.GetBuffer ();
409 writer.Write (arr, 0, (int) ms.Position);
413 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
419 // FIXME: support timeout
420 public void WriteUnsizedMessage (Message message, TimeSpan timeout)
426 if (EncodingRecord != EncodingBinary)
427 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
429 s.WriteByte (UnsizedEnvelopeRecord);
432 Encoder.WriteMessage (message, buffer);
433 new MyBinaryWriter (s).WriteVariableInt ((int) buffer.Position);
434 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
436 s.WriteByte (UnsizedMessageTerminator); // terminator
442 public void WriteEndRecord ()
446 s.WriteByte (EndRecord); // it is required
452 public void ReadEndRecord ()
457 if ((b = s.ReadByte ()) != EndRecord)
458 throw new ProtocolException (String.Format ("EndRecord message was expected, got {0:X}", b));