2009-07-23 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / TcpBinaryFrameManager.cs
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.
27 //
28
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;
40
41 namespace System.ServiceModel.Channels
42 {
43         // seealso: [MC-NMF] Windows Protocol document.
44         class TcpBinaryFrameManager
45         {
46                 class MyBinaryReader : BinaryReader
47                 {
48                         public MyBinaryReader (Stream s)
49                                 : base (s)
50                         {
51                         }
52
53                         public int ReadVariableInt ()
54                         {
55                                 return Read7BitEncodedInt ();
56                         }
57                 }
58
59                 class MyBinaryWriter : BinaryWriter
60                 {
61                         public MyBinaryWriter (Stream s)
62                                 : base (s)
63                         {
64                         }
65
66                         public void WriteVariableInt (int value)
67                         {
68                                 Write7BitEncodedInt (value);
69                         }
70
71                         public int GetSizeOfLength (int value)
72                         {
73                                 int x = 0;
74                                 do {
75                                         value /= 0x100;
76                                         x++;
77                                 } while (value != 0);
78                                 return x;
79                         }
80                 }
81
82                 class MyXmlBinaryWriterSession : XmlBinaryWriterSession
83                 {
84                         public override bool TryAdd (XmlDictionaryString value, out int key)
85                         {
86                                 if (!base.TryAdd (value, out key))
87                                         return false;
88                                 List.Add (value);
89                                 return true;
90                         }
91
92                         public List<XmlDictionaryString> List = new List<XmlDictionaryString> ();
93                 }
94
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;
108
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;
114
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;
121
122                 MyBinaryReader reader;
123                 MyBinaryWriter writer;
124
125                 public TcpBinaryFrameManager (int mode, Stream s, bool isServiceSide)
126                 {
127                         this.mode = mode;
128                         this.s = s;
129                         this.is_service_side = isServiceSide;
130                         reader = new MyBinaryReader (s);
131                         ResetWriteBuffer ();
132
133                         EncodingRecord = EncodingBinaryWithDictionary; // FIXME: it should depend on mode.
134                 }
135
136                 Stream s;
137                 MemoryStream buffer;
138                 bool is_service_side;
139
140                 int mode;
141
142                 public byte EncodingRecord { get; set; }
143
144                 public Uri Via { get; set; }
145
146                 public MessageEncoder Encoder { get; set; }
147
148                 void ResetWriteBuffer ()
149                 {
150                         this.buffer = new MemoryStream ();
151                         writer = new MyBinaryWriter (buffer);
152                 }
153
154                 public byte [] ReadSizedChunk ()
155                 {
156                         int length = reader.ReadVariableInt ();
157                         
158                         if (length > 65536)
159                                 throw new InvalidOperationException ("The message is too large.");
160
161                         byte [] buffer = new byte [length];
162                         for (int readSize = 0; readSize < length; )
163                                 readSize += reader.Read (buffer, readSize, length - readSize);
164                         return buffer;
165                 }
166
167                 public void WriteSizedChunk (byte [] data, int index, int length)
168                 {
169                         writer.WriteVariableInt (length);
170                         writer.Write (data, index, length);
171                 }
172
173                 public void ProcessPreambleInitiator ()
174                 {
175                         ResetWriteBuffer ();
176
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);
187                         buffer.Flush ();
188                         s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
189                         s.Flush ();
190                 }
191
192                 public void ProcessPreambleAckInitiator ()
193                 {
194                         int b = s.ReadByte ();
195                         switch (b) {
196                         case PreambleAckRecord:
197                                 return; // success
198                         case FaultRecord:
199                                 throw new FaultException (reader.ReadString ());
200                         default:
201                                 throw new ProtocolException (String.Format ("Preamble Ack Record is expected, got {0:X}", b));
202                         }
203                 }
204
205                 public void ProcessPreambleAckRecipient ()
206                 {
207                         s.WriteByte (PreambleAckRecord);
208                 }
209
210                 public void ProcessPreambleRecipient ()
211                 {
212                         bool preambleEnd = false;
213                         while (!preambleEnd) {
214                                 int b = s.ReadByte ();
215                                 switch (b) {
216                                 case VersionRecord:
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");
221                                         break;
222                                 case ModeRecord:
223                                         if (s.ReadByte () != mode)
224                                                 throw new ProtocolException (String.Format ("Duplex mode is expected to be {0:X}", mode));
225                                         break;
226                                 case ViaRecord:
227                                         Via = new Uri (reader.ReadString ());
228                                         break;
229                                 case KnownEncodingRecord:
230                                         EncodingRecord = (byte) s.ReadByte ();
231                                         break;
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:
239                                         preambleEnd = true;
240                                         break;
241                                 default:
242                                         throw new ProtocolException (String.Format ("Unexpected record type {0:X2}", b));
243                                 }
244                         }
245                 }
246
247                 XmlBinaryReaderSession reader_session;
248                 int reader_session_items;
249
250                 public Message ReadSizedMessage ()
251                 {
252                         // FIXME: implement full [MC-NMF].
253
254                         var packetType = s.ReadByte ();
255                         if (packetType == EndRecord)
256                                 return null;
257                         if (packetType != SizedEnvelopeRecord)
258                                 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
259
260                         byte [] buffer = ReadSizedChunk ();
261
262                         var ms = new MemoryStream (buffer, 0, buffer.Length);
263
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));
267
268                         // Encoding type 8:
269                         // the returned buffer consists of a serialized reader 
270                         // session and the binary xml body. 
271
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 ());
279                         }
280                         var benc = Encoder as BinaryMessageEncoder;
281                         if (benc != null)
282                                 benc.CurrentReaderSession = session;
283
284                         // FIXME: supply maxSizeOfHeaders.
285                         Message msg = Encoder.ReadMessage (ms, 0x10000);
286                         if (benc != null)
287                                 benc.CurrentReaderSession = null;
288
289                         return msg;
290                 }
291
292                 // FIXME: support timeout
293                 public Message ReadUnsizedMessage (TimeSpan timeout)
294                 {
295                         var packetType = s.ReadByte ();
296
297                         if (packetType == EndRecord)
298                                 return null;
299                         if (packetType != UnsizedEnvelopeRecord)
300                                 throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
301
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));
305
306                         byte [] buffer = ReadSizedChunk ();
307                         var ms = new MemoryStream (buffer, 0, buffer.Length);
308
309                         // FIXME: supply maxSizeOfHeaders.
310                         Message msg = Encoder.ReadMessage (ms, 0x10000);
311
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));
315
316                         return msg;
317                 }
318
319                 byte [] eof_buffer = new byte [1];
320                 MyXmlBinaryWriterSession writer_session;
321
322                 public void WriteSizedMessage (Message message)
323                 {
324                         ResetWriteBuffer ();
325
326                         if (EncodingRecord != 8)
327                                 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
328
329                         buffer.WriteByte (SizedEnvelopeRecord);
330
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;
336                         try {
337                                 if (benc != null)
338                                         benc.CurrentWriterSession = session;
339                                 Encoder.WriteMessage (message, ms);
340                         } finally {
341                                 benc.CurrentWriterSession = null;
342                         }
343
344                         // dictionary
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);
349                         dw.Flush ();
350
351                         int length = (int) (msd.Position + ms.Position);
352                         var msda = msd.ToArray ();
353                         int sizeOfLength = writer.GetSizeOfLength (msda.Length);
354
355                         writer.WriteVariableInt (length + sizeOfLength); // dictionary array also involves the size of itself.
356                         WriteSizedChunk (msda, 0, msda.Length);
357                         // message body
358                         var arr = ms.GetBuffer ();
359                         writer.Write (arr, 0, (int) ms.Position);
360
361                         writer.Flush ();
362
363                         s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
364                         s.Flush ();
365                 }
366
367                 // FIXME: support timeout
368                 public void WriteUnsizedMessage (Message message, TimeSpan timeout)
369                 {
370                         ResetWriteBuffer ();
371
372                         if (EncodingRecord != EncodingBinary)
373                                 throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
374
375                         s.WriteByte (UnsizedEnvelopeRecord);
376                         s.Flush ();
377
378                         Encoder.WriteMessage (message, buffer);
379                         new MyBinaryWriter (s).WriteVariableInt ((int) buffer.Position);
380                         s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
381
382                         s.WriteByte (UnsizedMessageTerminator); // terminator
383                         s.Flush ();
384                 }
385
386                 public void ProcessEndRecordInitiator ()
387                 {
388                         s.WriteByte (EndRecord); // it is required
389                         s.Flush ();
390                 }
391
392                 public void ProcessEndRecordRecipient ()
393                 {
394                         int b;
395                         if ((b = s.ReadByte ()) != EndRecord)
396                                 throw new ProtocolException (String.Format ("EndRecord message was expected, got {0:X}", b));
397                 }
398         }
399 }