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 public byte [] ReadSizedChunk ()
156 int length = reader.ReadVariableInt ();
159 throw new InvalidOperationException ("The message is too large.");
161 byte [] buffer = new byte [length];
162 for (int readSize = 0; readSize < length; )
163 readSize += reader.Read (buffer, readSize, length - readSize);
167 public void WriteSizedChunk (byte [] data, int index, int length)
169 writer.WriteVariableInt (length);
170 writer.Write (data, index, length);
173 public void ProcessPreambleInitiator ()
177 buffer.WriteByte (VersionRecord);
178 buffer.WriteByte (1);
179 buffer.WriteByte (0);
180 buffer.WriteByte (ModeRecord);
181 buffer.WriteByte ((byte) mode);
182 buffer.WriteByte (ViaRecord);
183 writer.Write (Via.ToString ());
184 buffer.WriteByte (KnownEncodingRecord); // FIXME
185 buffer.WriteByte ((byte) EncodingRecord);
186 buffer.WriteByte (PreambleEndRecord);
188 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
192 public void ProcessPreambleAckInitiator ()
194 int b = s.ReadByte ();
196 case PreambleAckRecord:
199 throw new FaultException (reader.ReadString ());
201 throw new ProtocolException (String.Format ("Preamble Ack Record is expected, got {0:X}", b));
205 public void ProcessPreambleAckRecipient ()
207 s.WriteByte (PreambleAckRecord);
210 public void ProcessPreambleRecipient ()
212 bool preambleEnd = false;
213 while (!preambleEnd) {
214 int b = s.ReadByte ();
217 if (s.ReadByte () != 1)
218 throw new ProtocolException ("Major version must be 1");
219 if (s.ReadByte () != 0)
220 throw new ProtocolException ("Minor version must be 0");
223 if (s.ReadByte () != mode)
224 throw new ProtocolException (String.Format ("Duplex mode is expected to be {0:X}", mode));
227 Via = new Uri (reader.ReadString ());
229 case KnownEncodingRecord:
230 EncodingRecord = (byte) s.ReadByte ();
232 case ExtendingEncodingRecord:
233 throw new NotImplementedException ("ExtendingEncodingRecord");
234 case UpgradeRequestRecord:
235 throw new NotImplementedException ("UpgradeRequetRecord");
236 case UpgradeResponseRecord:
237 throw new NotImplementedException ("UpgradeResponseRecord");
238 case PreambleEndRecord:
242 throw new ProtocolException (String.Format ("Unexpected record type {0:X2}", b));
247 XmlBinaryReaderSession reader_session;
248 int reader_session_items;
250 public Message ReadSizedMessage ()
252 // FIXME: implement full [MC-NMF].
254 var packetType = s.ReadByte ();
255 if (packetType == EndRecord)
257 if (packetType != SizedEnvelopeRecord)
258 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
260 byte [] buffer = ReadSizedChunk ();
262 var ms = new MemoryStream (buffer, 0, buffer.Length);
264 // FIXME: turned out that it could be either in-band dictionary ([MC-NBFSE]), or a mere xml body ([MC-NBFS]).
265 if (EncodingRecord != EncodingBinaryWithDictionary)
266 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
269 // the returned buffer consists of a serialized reader
270 // session and the binary xml body.
272 var session = reader_session ?? new XmlBinaryReaderSession ();
273 reader_session = session;
274 byte [] rsbuf = new TcpBinaryFrameManager (0, ms, is_service_side).ReadSizedChunk ();
275 using (var rms = new MemoryStream (rsbuf, 0, rsbuf.Length)) {
276 var rbr = new BinaryReader (rms, Encoding.UTF8);
277 while (rms.Position < rms.Length)
278 session.Add (reader_session_items++, rbr.ReadString ());
280 var benc = Encoder as BinaryMessageEncoder;
282 benc.CurrentReaderSession = session;
284 // FIXME: supply maxSizeOfHeaders.
285 Message msg = Encoder.ReadMessage (ms, 0x10000);
287 benc.CurrentReaderSession = null;
292 // FIXME: support timeout
293 public Message ReadUnsizedMessage (TimeSpan timeout)
295 var packetType = s.ReadByte ();
297 if (packetType == EndRecord)
299 if (packetType != UnsizedEnvelopeRecord)
300 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
302 // Encoding type 7 is expected
303 if (EncodingRecord != EncodingBinary)
304 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
306 byte [] buffer = ReadSizedChunk ();
307 var ms = new MemoryStream (buffer, 0, buffer.Length);
309 // FIXME: supply maxSizeOfHeaders.
310 Message msg = Encoder.ReadMessage (ms, 0x10000);
312 var terminator = s.ReadByte ();
313 if (terminator != UnsizedMessageTerminator)
314 throw new InvalidOperationException (String.Format ("Unsized message terminator is expected. Got '{0}' (&#x{1:X};).", (char) terminator, terminator));
319 byte [] eof_buffer = new byte [1];
320 MyXmlBinaryWriterSession writer_session;
322 public void WriteSizedMessage (Message message)
326 if (EncodingRecord != 8)
327 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
329 buffer.WriteByte (SizedEnvelopeRecord);
331 MemoryStream ms = new MemoryStream ();
332 var session = writer_session ?? new MyXmlBinaryWriterSession ();
333 writer_session = session;
334 int writer_session_count = session.List.Count;
335 var benc = Encoder as BinaryMessageEncoder;
338 benc.CurrentWriterSession = session;
339 Encoder.WriteMessage (message, ms);
341 benc.CurrentWriterSession = null;
345 MemoryStream msd = new MemoryStream ();
346 BinaryWriter dw = new BinaryWriter (msd);
347 for (int i = writer_session_count; i < session.List.Count; i++)
348 dw.Write (session.List [i].Value);
351 int length = (int) (msd.Position + ms.Position);
352 var msda = msd.ToArray ();
353 int sizeOfLength = writer.GetSizeOfLength (msda.Length);
355 writer.WriteVariableInt (length + sizeOfLength); // dictionary array also involves the size of itself.
356 WriteSizedChunk (msda, 0, msda.Length);
358 var arr = ms.GetBuffer ();
359 writer.Write (arr, 0, (int) ms.Position);
363 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
367 // FIXME: support timeout
368 public void WriteUnsizedMessage (Message message, TimeSpan timeout)
372 if (EncodingRecord != EncodingBinary)
373 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
375 s.WriteByte (UnsizedEnvelopeRecord);
378 Encoder.WriteMessage (message, buffer);
379 new MyBinaryWriter (s).WriteVariableInt ((int) buffer.Position);
380 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
382 s.WriteByte (UnsizedMessageTerminator); // terminator
386 public void ProcessEndRecordInitiator ()
388 s.WriteByte (EndRecord); // it is required
392 public void ProcessEndRecordRecipient ()
395 if ((b = s.ReadByte ()) != EndRecord)
396 throw new ProtocolException (String.Format ("EndRecord message was expected, got {0:X}", b));