* xbuild/Microsoft.Common.targets (_RecordCleanFile): Append list of
[mcs.git] / class / System.ServiceModel / System.ServiceModel.Channels / TcpBinaryFrameManager.cs
blob466719e25afbcab04b3586f95f6bdbd5f93eaad9
1 //
2 // TcpBinaryFrameManager.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2009 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.Collections.Generic;
31 using System.IO;
32 using System.Net;
33 using System.Net.Sockets;
34 using System.Runtime.Serialization;
35 using System.Runtime.Serialization.Formatters.Binary;
36 using System.ServiceModel.Channels;
37 using System.Text;
38 using System.Threading;
39 using System.Xml;
41 namespace System.ServiceModel.Channels
43 // seealso: [MC-NMF] Windows Protocol document.
44 class TcpBinaryFrameManager
46 class MyBinaryReader : BinaryReader
48 public MyBinaryReader (Stream s)
49 : base (s)
53 public int ReadVariableInt ()
55 return Read7BitEncodedInt ();
59 class MyBinaryWriter : BinaryWriter
61 public MyBinaryWriter (Stream s)
62 : base (s)
66 public void WriteVariableInt (int value)
68 Write7BitEncodedInt (value);
71 public int GetSizeOfLength (int value)
73 int x = 0;
74 do {
75 value /= 0x100;
76 x++;
77 } while (value != 0);
78 return x;
82 class MyXmlBinaryWriterSession : XmlBinaryWriterSession
84 public override bool TryAdd (XmlDictionaryString value, out int key)
86 if (!base.TryAdd (value, out key))
87 return false;
88 List.Add (value);
89 return true;
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)
127 this.mode = mode;
128 this.s = s;
129 this.is_service_side = isServiceSide;
130 reader = new MyBinaryReader (s);
131 ResetWriteBuffer ();
133 EncodingRecord = EncodingBinaryWithDictionary; // FIXME: it should depend on mode.
136 Stream s;
137 MemoryStream buffer;
138 bool is_service_side;
140 int mode;
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 ();
159 if (length == 0)
160 return empty_bytes;
162 if (length > 65536)
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);
168 return buffer;
171 public void WriteSizedChunk (byte [] data, int index, int length)
173 writer.WriteVariableInt (length);
174 writer.Write (data, index, length);
177 public void ProcessPreambleInitiator ()
179 ResetWriteBuffer ();
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);
191 buffer.Flush ();
192 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
193 s.Flush ();
196 public void ProcessPreambleAckInitiator ()
198 int b = s.ReadByte ();
199 switch (b) {
200 case PreambleAckRecord:
201 return; // success
202 case FaultRecord:
203 throw new FaultException (reader.ReadString ());
204 default:
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;
223 if (b < 0)
224 return false;
225 switch (b) {
226 case VersionRecord:
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");
231 break;
232 case ModeRecord:
233 if (s.ReadByte () != mode)
234 throw new ProtocolException (String.Format ("Duplex mode is expected to be {0:X}", mode));
235 break;
236 case ViaRecord:
237 Via = new Uri (reader.ReadString ());
238 break;
239 case KnownEncodingRecord:
240 EncodingRecord = (byte) s.ReadByte ();
241 break;
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:
249 preambleEnd = true;
250 break;
251 default:
252 throw new ProtocolException (String.Format ("Unexpected record type {0:X2}", b));
255 return true;
258 XmlBinaryReaderSession reader_session;
259 int reader_session_items;
261 public Message ReadSizedMessage ()
263 // FIXME: implement full [MC-NMF].
265 int packetType;
266 try {
267 packetType = s.ReadByte ();
268 } catch (IOException) {
269 // it is already disconnected
270 return null;
271 } catch (SocketException) {
272 // it is already disconnected
273 return null;
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)
277 return null;
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)
280 return null;
281 if (packetType != SizedEnvelopeRecord) {
282 if (is_service_side) {
283 // reconnect
284 ProcessPreambleRecipient (packetType);
285 ProcessPreambleAckRecipient ();
287 else
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));
299 // Encoding type 8:
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;
312 if (benc != null)
313 benc.CurrentReaderSession = session;
315 // FIXME: supply maxSizeOfHeaders.
316 Message msg = Encoder.ReadMessage (ms, 0x10000);
317 if (benc != null)
318 benc.CurrentReaderSession = null;
320 return msg;
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)
333 return null;
334 if (packetType != UnsizedEnvelopeRecord)
335 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
337 var ms = new MemoryStream ();
338 while (true) {
339 byte [] buffer = ReadSizedChunk ();
340 if (buffer.Length == 0) // i.e. it is UnsizedMessageTerminator (which is '0')
341 break;
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);
349 return msg;
352 byte [] eof_buffer = new byte [1];
353 MyXmlBinaryWriterSession writer_session;
355 public void WriteSizedMessage (Message message)
357 ResetWriteBuffer ();
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;
369 try {
370 if (benc != null)
371 benc.CurrentWriterSession = session;
372 Encoder.WriteMessage (message, ms);
373 } finally {
374 benc.CurrentWriterSession = null;
377 // dictionary
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);
382 dw.Flush ();
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);
390 // message body
391 var arr = ms.GetBuffer ();
392 writer.Write (arr, 0, (int) ms.Position);
394 writer.Flush ();
396 s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
397 s.Flush ();
400 // FIXME: support timeout
401 public void WriteUnsizedMessage (Message message, TimeSpan timeout)
403 ResetWriteBuffer ();
405 if (EncodingRecord != EncodingBinary)
406 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
408 s.WriteByte (UnsizedEnvelopeRecord);
409 s.Flush ();
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
416 s.Flush ();
419 public void WriteEndRecord ()
421 s.WriteByte (EndRecord); // it is required
422 s.Flush ();
425 public void ReadEndRecord ()
427 int b;
428 if ((b = s.ReadByte ()) != EndRecord)
429 throw new ProtocolException (String.Format ("EndRecord message was expected, got {0:X}", b));