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 ()
158 int length
= reader
.ReadVariableInt ();
163 throw new InvalidOperationException ("The message is too large.");
165 byte [] buffer
= new byte [length
];
166 for (int readSize
= 0; readSize
< length
; )
167 readSize
+= reader
.Read (buffer
, readSize
, length
- readSize
);
171 public void WriteSizedChunk (byte [] data
, int index
, int length
)
173 writer
.WriteVariableInt (length
);
174 writer
.Write (data
, index
, length
);
177 public void ProcessPreambleInitiator ()
181 buffer
.WriteByte (VersionRecord
);
182 buffer
.WriteByte (1);
183 buffer
.WriteByte (0);
184 buffer
.WriteByte (ModeRecord
);
185 buffer
.WriteByte ((byte) mode
);
186 buffer
.WriteByte (ViaRecord
);
187 writer
.Write (Via
.ToString ());
188 buffer
.WriteByte (KnownEncodingRecord
); // FIXME
189 buffer
.WriteByte ((byte) EncodingRecord
);
190 buffer
.WriteByte (PreambleEndRecord
);
192 s
.Write (buffer
.GetBuffer (), 0, (int) buffer
.Position
);
196 public void ProcessPreambleAckInitiator ()
198 int b
= s
.ReadByte ();
200 case PreambleAckRecord
:
203 throw new FaultException (reader
.ReadString ());
205 throw new ProtocolException (String
.Format ("Preamble Ack Record is expected, got {0:X}", b
));
209 public void ProcessPreambleAckRecipient ()
211 s
.WriteByte (PreambleAckRecord
);
214 public bool ProcessPreambleRecipient ()
216 return ProcessPreambleRecipient (-1);
218 bool ProcessPreambleRecipient (int initialByte
)
220 bool preambleEnd
= false;
221 while (!preambleEnd
) {
222 int b
= initialByte
< 0 ? s
.ReadByte () : initialByte
;
227 if (s
.ReadByte () != 1)
228 throw new ProtocolException ("Major version must be 1");
229 if (s
.ReadByte () != 0)
230 throw new ProtocolException ("Minor version must be 0");
233 if (s
.ReadByte () != mode
)
234 throw new ProtocolException (String
.Format ("Duplex mode is expected to be {0:X}", mode
));
237 Via
= new Uri (reader
.ReadString ());
239 case KnownEncodingRecord
:
240 EncodingRecord
= (byte) s
.ReadByte ();
242 case ExtendingEncodingRecord
:
243 throw new NotImplementedException ("ExtendingEncodingRecord");
244 case UpgradeRequestRecord
:
245 throw new NotImplementedException ("UpgradeRequetRecord");
246 case UpgradeResponseRecord
:
247 throw new NotImplementedException ("UpgradeResponseRecord");
248 case PreambleEndRecord
:
252 throw new ProtocolException (String
.Format ("Unexpected record type {0:X2}", b
));
258 XmlBinaryReaderSession reader_session
;
259 int reader_session_items
;
261 public Message
ReadSizedMessage ()
263 // FIXME: implement full [MC-NMF].
267 packetType
= s
.ReadByte ();
268 } catch (IOException
) {
269 // it is already disconnected
271 } catch (SocketException
) {
272 // it is already disconnected
275 // FIXME: .NET never results in -1, so there may be implementation mismatch in Socket (but might be in other places)
276 if (packetType
== -1)
278 // FIXME: The client should wait for EndRecord, but if we try to send it, the socket blocks and becomes unable to work anymore.
279 if (packetType
== EndRecord
)
281 if (packetType
!= SizedEnvelopeRecord
) {
282 if (is_service_side
) {
284 ProcessPreambleRecipient (packetType
);
285 ProcessPreambleAckRecipient ();
288 throw new NotImplementedException (String
.Format ("Packet type {0:X} is not implemented", packetType
));
291 byte [] buffer
= ReadSizedChunk ();
293 var ms
= new MemoryStream (buffer
, 0, buffer
.Length
);
295 // FIXME: turned out that it could be either in-band dictionary ([MC-NBFSE]), or a mere xml body ([MC-NBFS]).
296 if (EncodingRecord
!= EncodingBinaryWithDictionary
)
297 throw new NotImplementedException (String
.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord
));
300 // the returned buffer consists of a serialized reader
301 // session and the binary xml body.
303 var session
= reader_session
?? new XmlBinaryReaderSession ();
304 reader_session
= session
;
305 byte [] rsbuf
= new TcpBinaryFrameManager (0, ms
, is_service_side
).ReadSizedChunk ();
306 using (var rms
= new MemoryStream (rsbuf
, 0, rsbuf
.Length
)) {
307 var rbr
= new BinaryReader (rms
, Encoding
.UTF8
);
308 while (rms
.Position
< rms
.Length
)
309 session
.Add (reader_session_items
++, rbr
.ReadString ());
311 var benc
= Encoder
as BinaryMessageEncoder
;
313 benc
.CurrentReaderSession
= session
;
315 // FIXME: supply maxSizeOfHeaders.
316 Message msg
= Encoder
.ReadMessage (ms
, 0x10000);
318 benc
.CurrentReaderSession
= null;
323 // FIXME: support timeout
324 public Message
ReadUnsizedMessage (TimeSpan timeout
)
326 // Encoding type 7 is expected
327 if (EncodingRecord
!= EncodingBinary
)
328 throw new NotImplementedException (String
.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord
));
330 var packetType
= s
.ReadByte ();
332 if (packetType
== EndRecord
)
334 if (packetType
!= UnsizedEnvelopeRecord
)
335 throw new NotImplementedException (String
.Format ("Packet type {0:X} is not implemented", packetType
));
337 var ms
= new MemoryStream ();
339 byte [] buffer
= ReadSizedChunk ();
340 if (buffer
.Length
== 0) // i.e. it is UnsizedMessageTerminator (which is '0')
342 ms
.Write (buffer
, 0, buffer
.Length
);
344 ms
.Seek (0, SeekOrigin
.Begin
);
346 // FIXME: supply correct maxSizeOfHeaders.
347 Message msg
= Encoder
.ReadMessage (ms
, (int) ms
.Length
);
352 byte [] eof_buffer
= new byte [1];
353 MyXmlBinaryWriterSession writer_session
;
355 public void WriteSizedMessage (Message message
)
359 if (EncodingRecord
!= 8)
360 throw new NotImplementedException (String
.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord
));
362 buffer
.WriteByte (SizedEnvelopeRecord
);
364 MemoryStream ms
= new MemoryStream ();
365 var session
= writer_session
?? new MyXmlBinaryWriterSession ();
366 writer_session
= session
;
367 int writer_session_count
= session
.List
.Count
;
368 var benc
= Encoder
as BinaryMessageEncoder
;
371 benc
.CurrentWriterSession
= session
;
372 Encoder
.WriteMessage (message
, ms
);
374 benc
.CurrentWriterSession
= null;
378 MemoryStream msd
= new MemoryStream ();
379 BinaryWriter dw
= new BinaryWriter (msd
);
380 for (int i
= writer_session_count
; i
< session
.List
.Count
; i
++)
381 dw
.Write (session
.List
[i
].Value
);
384 int length
= (int) (msd
.Position
+ ms
.Position
);
385 var msda
= msd
.ToArray ();
386 int sizeOfLength
= writer
.GetSizeOfLength (msda
.Length
);
388 writer
.WriteVariableInt (length
+ sizeOfLength
); // dictionary array also involves the size of itself.
389 WriteSizedChunk (msda
, 0, msda
.Length
);
391 var arr
= ms
.GetBuffer ();
392 writer
.Write (arr
, 0, (int) ms
.Position
);
396 s
.Write (buffer
.GetBuffer (), 0, (int) buffer
.Position
);
400 // FIXME: support timeout
401 public void WriteUnsizedMessage (Message message
, TimeSpan timeout
)
405 if (EncodingRecord
!= EncodingBinary
)
406 throw new NotImplementedException (String
.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord
));
408 s
.WriteByte (UnsizedEnvelopeRecord
);
411 Encoder
.WriteMessage (message
, buffer
);
412 new MyBinaryWriter (s
).WriteVariableInt ((int) buffer
.Position
);
413 s
.Write (buffer
.GetBuffer (), 0, (int) buffer
.Position
);
415 s
.WriteByte (UnsizedMessageTerminator
); // terminator
419 public void WriteEndRecord ()
421 s
.WriteByte (EndRecord
); // it is required
425 public void ReadEndRecord ()
428 if ((b
= s
.ReadByte ()) != EndRecord
)
429 throw new ProtocolException (String
.Format ("EndRecord message was expected, got {0:X}", b
));