TcpBinaryFrameManager.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. //
  2. // TcpBinaryFrameManager.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
  8. // Copyright 2011 Xamarin Inc (http://xamarin.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  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. namespace System.ServiceModel.Channels.NetTcp
  41. {
  42. // seealso: [MC-NMF] Windows Protocol document.
  43. class TcpBinaryFrameManager
  44. {
  45. class MyBinaryReader : BinaryReader
  46. {
  47. public MyBinaryReader (Stream s)
  48. : base (s)
  49. {
  50. }
  51. public int ReadVariableInt ()
  52. {
  53. return Read7BitEncodedInt ();
  54. }
  55. }
  56. class MyBinaryWriter : BinaryWriter
  57. {
  58. public MyBinaryWriter (Stream s)
  59. : base (s)
  60. {
  61. }
  62. public void WriteVariableInt (int value)
  63. {
  64. Write7BitEncodedInt (value);
  65. }
  66. public int GetSizeOfLength (int value)
  67. {
  68. int x = 0;
  69. do {
  70. value /= 0x100;
  71. x++;
  72. } while (value != 0);
  73. return x;
  74. }
  75. }
  76. class MyXmlBinaryWriterSession : XmlBinaryWriterSession
  77. {
  78. public override bool TryAdd (XmlDictionaryString value, out int key)
  79. {
  80. if (!base.TryAdd (value, out key))
  81. return false;
  82. List.Add (value);
  83. return true;
  84. }
  85. public List<XmlDictionaryString> List = new List<XmlDictionaryString> ();
  86. }
  87. public const byte VersionRecord = 0;
  88. public const byte ModeRecord = 1;
  89. public const byte ViaRecord = 2;
  90. public const byte KnownEncodingRecord = 3;
  91. public const byte ExtendingEncodingRecord = 4;
  92. public const byte UnsizedEnvelopeRecord = 5;
  93. public const byte SizedEnvelopeRecord = 6;
  94. public const byte EndRecord = 7;
  95. public const byte FaultRecord = 8;
  96. public const byte UpgradeRequestRecord = 9;
  97. public const byte UpgradeResponseRecord = 0xA;
  98. public const byte PreambleAckRecord = 0xB;
  99. public const byte PreambleEndRecord = 0xC;
  100. public const byte UnsizedMessageTerminator = 0;
  101. public const byte SingletonUnsizedMode = 1;
  102. public const byte DuplexMode = 2;
  103. public const byte SimplexMode = 3;
  104. public const byte SingletonSizedMode = 4;
  105. public const byte Soap11EncodingUtf8 = 0;
  106. public const byte Soap11EncodingUtf16 = 1;
  107. public const byte Soap11EncodingUtf16LE = 2;
  108. public const byte Soap12EncodingUtf8 = 3;
  109. public const byte Soap12EncodingUtf16 = 4;
  110. public const byte Soap12EncodingUtf16LE = 5;
  111. public const byte Soap12EncodingMtom = 6;
  112. public const byte Soap12EncodingBinary = 7;
  113. public const byte Soap12EncodingBinaryWithDictionary = 8;
  114. public const byte UseExtendedEncodingRecord = 0xFF;
  115. MyBinaryReader reader;
  116. MyBinaryWriter writer;
  117. public TcpBinaryFrameManager (int mode, Stream s, bool isServiceSide)
  118. {
  119. this.mode = mode;
  120. this.s = s;
  121. this.is_service_side = isServiceSide;
  122. reader = new MyBinaryReader (s);
  123. ResetWriteBuffer ();
  124. EncodingRecord = Soap12EncodingBinaryWithDictionary; // FIXME: it should depend on mode.
  125. }
  126. Stream s;
  127. MemoryStream buffer;
  128. bool is_service_side;
  129. int mode;
  130. public byte EncodingRecord { get; private set; }
  131. public string ExtendedEncodingRecord { get; private set; }
  132. public Uri Via { get; set; }
  133. static readonly char [] convtest = new char [1] {'A'};
  134. MessageEncoder encoder;
  135. public MessageEncoder Encoder {
  136. get { return encoder; }
  137. set {
  138. encoder = value;
  139. EncodingRecord = UseExtendedEncodingRecord;
  140. var be = encoder as BinaryMessageEncoder;
  141. if (be != null)
  142. EncodingRecord = be.UseSession ? Soap12EncodingBinaryWithDictionary : Soap12EncodingBinary;
  143. var te = encoder as TextMessageEncoder;
  144. if (te != null) {
  145. var u16 = te.Encoding as UnicodeEncoding;
  146. bool u16be = u16 != null && u16.GetBytes (convtest) [0] == 0;
  147. if (encoder.MessageVersion.Envelope.Equals (EnvelopeVersion.Soap11)) {
  148. if (u16 != null)
  149. EncodingRecord = u16be ? Soap11EncodingUtf16 : Soap11EncodingUtf16LE;
  150. else
  151. EncodingRecord = Soap11EncodingUtf8;
  152. } else {
  153. if (u16 != null)
  154. EncodingRecord = u16be ? Soap12EncodingUtf16 : Soap12EncodingUtf16LE;
  155. else
  156. EncodingRecord = Soap12EncodingUtf8;
  157. }
  158. }
  159. if (value is MtomMessageEncoder)
  160. EncodingRecord = Soap12EncodingMtom;
  161. if (EncodingRecord == UseExtendedEncodingRecord)
  162. ExtendedEncodingRecord = encoder.ContentType;
  163. }
  164. }
  165. void ResetWriteBuffer ()
  166. {
  167. this.buffer = new MemoryStream ();
  168. writer = new MyBinaryWriter (buffer);
  169. }
  170. static readonly byte [] empty_bytes = new byte [0];
  171. public byte [] ReadSizedChunk ()
  172. {
  173. lock (read_lock) {
  174. int length = reader.ReadVariableInt ();
  175. if (length == 0)
  176. return empty_bytes;
  177. byte [] buffer = new byte [length];
  178. for (int readSize = 0; readSize < length; )
  179. readSize += reader.Read (buffer, readSize, length - readSize);
  180. return buffer;
  181. }
  182. }
  183. void WriteSizedChunk (byte [] data, int index, int length)
  184. {
  185. writer.WriteVariableInt (length);
  186. writer.Write (data, index, length);
  187. }
  188. public void ProcessPreambleInitiator ()
  189. {
  190. ResetWriteBuffer ();
  191. buffer.WriteByte (VersionRecord);
  192. buffer.WriteByte (1);
  193. buffer.WriteByte (0);
  194. buffer.WriteByte (ModeRecord);
  195. buffer.WriteByte ((byte) mode);
  196. buffer.WriteByte (ViaRecord);
  197. writer.Write (Via.ToString ());
  198. buffer.WriteByte (KnownEncodingRecord); // FIXME
  199. buffer.WriteByte ((byte) EncodingRecord);
  200. buffer.WriteByte (PreambleEndRecord);
  201. buffer.Flush ();
  202. s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
  203. s.Flush ();
  204. }
  205. public void ProcessPreambleAckInitiator ()
  206. {
  207. int b = s.ReadByte ();
  208. switch (b) {
  209. case PreambleAckRecord:
  210. return; // success
  211. case FaultRecord:
  212. throw new FaultException (reader.ReadString ());
  213. default:
  214. throw new ProtocolException (String.Format ("Preamble Ack Record is expected, got {0:X}", b));
  215. }
  216. }
  217. public void ProcessPreambleAckRecipient ()
  218. {
  219. s.WriteByte (PreambleAckRecord);
  220. }
  221. public bool ProcessPreambleRecipient ()
  222. {
  223. return ProcessPreambleRecipient (-1);
  224. }
  225. bool ProcessPreambleRecipient (int initialByte)
  226. {
  227. bool preambleEnd = false;
  228. while (!preambleEnd) {
  229. int b = initialByte < 0 ? s.ReadByte () : initialByte;
  230. if (b < 0)
  231. return false;
  232. switch (b) {
  233. case VersionRecord:
  234. if (s.ReadByte () != 1)
  235. throw new ProtocolException ("Major version must be 1");
  236. if (s.ReadByte () != 0)
  237. throw new ProtocolException ("Minor version must be 0");
  238. break;
  239. case ModeRecord:
  240. if (s.ReadByte () != mode)
  241. throw new ProtocolException (String.Format ("Duplex mode is expected to be {0:X}", mode));
  242. break;
  243. case ViaRecord:
  244. Via = new Uri (reader.ReadString ());
  245. break;
  246. case KnownEncodingRecord:
  247. EncodingRecord = (byte) s.ReadByte ();
  248. break;
  249. case ExtendingEncodingRecord:
  250. throw new NotImplementedException ("ExtendingEncodingRecord");
  251. case UpgradeRequestRecord:
  252. throw new NotImplementedException ("UpgradeRequetRecord");
  253. case UpgradeResponseRecord:
  254. throw new NotImplementedException ("UpgradeResponseRecord");
  255. case PreambleEndRecord:
  256. preambleEnd = true;
  257. break;
  258. default:
  259. throw new ProtocolException (String.Format ("Unexpected record type {0:X2}", b));
  260. }
  261. }
  262. return true;
  263. }
  264. XmlBinaryReaderSession reader_session;
  265. int reader_session_items;
  266. object read_lock = new object ();
  267. object write_lock = new object ();
  268. public Message ReadSizedMessage ()
  269. {
  270. lock (read_lock) {
  271. // FIXME: implement full [MC-NMF].
  272. int packetType;
  273. try {
  274. packetType = s.ReadByte ();
  275. } catch (IOException) {
  276. // it is already disconnected
  277. return null;
  278. } catch (SocketException) {
  279. // it is already disconnected
  280. return null;
  281. }
  282. // FIXME: .NET never results in -1, so there may be implementation mismatch in Socket (but might be in other places)
  283. if (packetType == -1)
  284. return null;
  285. // FIXME: The client should wait for EndRecord, but if we try to send it, the socket blocks and becomes unable to work anymore.
  286. if (packetType == EndRecord)
  287. return null;
  288. if (packetType != SizedEnvelopeRecord) {
  289. if (is_service_side) {
  290. // reconnect
  291. ProcessPreambleRecipient (packetType);
  292. ProcessPreambleAckRecipient ();
  293. }
  294. else
  295. throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
  296. }
  297. byte [] buffer = ReadSizedChunk ();
  298. var ms = new MemoryStream (buffer, 0, buffer.Length);
  299. // FIXME: turned out that it could be either in-band dictionary ([MC-NBFSE]), or a mere xml body ([MC-NBFS]).
  300. bool inBandDic = false;
  301. XmlBinaryReaderSession session = null;
  302. switch (EncodingRecord) {
  303. case Soap11EncodingUtf8:
  304. case Soap11EncodingUtf16:
  305. case Soap11EncodingUtf16LE:
  306. case Soap12EncodingUtf8:
  307. case Soap12EncodingUtf16:
  308. case Soap12EncodingUtf16LE:
  309. if (!(Encoder is TextMessageEncoder))
  310. throw new InvalidOperationException (String.Format ("Unexpected message encoding value in the received message: {0:X}", EncodingRecord));
  311. break;
  312. case Soap12EncodingMtom:
  313. if (!(Encoder is MtomMessageEncoder))
  314. throw new InvalidOperationException (String.Format ("Unexpected message encoding value in the received message: {0:X}", EncodingRecord));
  315. break;
  316. default:
  317. throw new InvalidOperationException (String.Format ("Unexpected message encoding value in the received message: {0:X}", EncodingRecord));
  318. case Soap12EncodingBinaryWithDictionary:
  319. inBandDic = true;
  320. goto case Soap12EncodingBinary;
  321. case Soap12EncodingBinary:
  322. session = inBandDic ? (reader_session ?? new XmlBinaryReaderSession ()) : null;
  323. reader_session = session;
  324. if (inBandDic) {
  325. byte [] rsbuf = new TcpBinaryFrameManager (0, ms, is_service_side).ReadSizedChunk ();
  326. using (var rms = new MemoryStream (rsbuf, 0, rsbuf.Length)) {
  327. var rbr = new BinaryReader (rms, Encoding.UTF8);
  328. while (rms.Position < rms.Length)
  329. session.Add (reader_session_items++, rbr.ReadString ());
  330. }
  331. }
  332. break;
  333. }
  334. var benc = Encoder as BinaryMessageEncoder;
  335. lock (Encoder) {
  336. if (benc != null)
  337. benc.CurrentReaderSession = session;
  338. // FIXME: supply maxSizeOfHeaders.
  339. Message msg = Encoder.ReadMessage (ms, 0x10000);
  340. if (benc != null)
  341. benc.CurrentReaderSession = null;
  342. return msg;
  343. }
  344. }
  345. }
  346. // FIXME: support timeout
  347. public Message ReadUnsizedMessage (TimeSpan timeout)
  348. {
  349. lock (read_lock) {
  350. // Encoding type 7 is expected
  351. if (EncodingRecord != Soap12EncodingBinary)
  352. throw new NotImplementedException (String.Format ("Message encoding {0:X} is not implemented yet", EncodingRecord));
  353. var packetType = s.ReadByte ();
  354. if (packetType == EndRecord)
  355. return null;
  356. if (packetType != UnsizedEnvelopeRecord)
  357. throw new NotImplementedException (String.Format ("Packet type {0:X} is not implemented", packetType));
  358. var ms = new MemoryStream ();
  359. while (true) {
  360. byte [] buffer = ReadSizedChunk ();
  361. if (buffer.Length == 0) // i.e. it is UnsizedMessageTerminator (which is '0')
  362. break;
  363. ms.Write (buffer, 0, buffer.Length);
  364. }
  365. ms.Seek (0, SeekOrigin.Begin);
  366. // FIXME: supply correct maxSizeOfHeaders.
  367. Message msg = Encoder.ReadMessage (ms, (int) ms.Length);
  368. return msg;
  369. }
  370. }
  371. byte [] eof_buffer = new byte [1];
  372. MyXmlBinaryWriterSession writer_session;
  373. public void WriteSizedMessage (Message message)
  374. {
  375. lock (write_lock) {
  376. ResetWriteBuffer ();
  377. buffer.WriteByte (SizedEnvelopeRecord);
  378. MemoryStream ms = new MemoryStream ();
  379. var session = writer_session ?? new MyXmlBinaryWriterSession ();
  380. writer_session = session;
  381. int writer_session_count = session.List.Count;
  382. var benc = Encoder as BinaryMessageEncoder;
  383. try {
  384. if (benc != null)
  385. benc.CurrentWriterSession = session;
  386. Encoder.WriteMessage (message, ms);
  387. } finally {
  388. if (benc != null)
  389. benc.CurrentWriterSession = null;
  390. }
  391. // dictionary
  392. if (EncodingRecord == Soap12EncodingBinaryWithDictionary) {
  393. MemoryStream msd = new MemoryStream ();
  394. BinaryWriter dw = new BinaryWriter (msd);
  395. for (int i = writer_session_count; i < session.List.Count; i++)
  396. dw.Write (session.List [i].Value);
  397. dw.Flush ();
  398. int length = (int) (msd.Position + ms.Position);
  399. var msda = msd.ToArray ();
  400. int sizeOfLength = writer.GetSizeOfLength (msda.Length);
  401. writer.WriteVariableInt (length + sizeOfLength); // dictionary array also involves the size of itself.
  402. WriteSizedChunk (msda, 0, msda.Length);
  403. }
  404. else
  405. writer.WriteVariableInt ((int) ms.Position);
  406. // message body
  407. var arr = ms.GetBuffer ();
  408. writer.Write (arr, 0, (int) ms.Position);
  409. writer.Flush ();
  410. s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
  411. s.Flush ();
  412. }
  413. }
  414. // FIXME: support timeout
  415. public void WriteUnsizedMessage (Message message, TimeSpan timeout)
  416. {
  417. lock (write_lock) {
  418. ResetWriteBuffer ();
  419. s.WriteByte (UnsizedEnvelopeRecord);
  420. s.Flush ();
  421. Encoder.WriteMessage (message, buffer);
  422. new MyBinaryWriter (s).WriteVariableInt ((int) buffer.Position);
  423. s.Write (buffer.GetBuffer (), 0, (int) buffer.Position);
  424. s.WriteByte (UnsizedMessageTerminator); // terminator
  425. s.Flush ();
  426. }
  427. }
  428. public void WriteEndRecord ()
  429. {
  430. lock (write_lock) {
  431. s.WriteByte (EndRecord); // it is required
  432. s.Flush ();
  433. }
  434. }
  435. public void ReadEndRecord ()
  436. {
  437. lock (read_lock) {
  438. int b;
  439. if ((b = s.ReadByte ()) != EndRecord)
  440. throw new ProtocolException (String.Format ("EndRecord message was expected, got {0:X}", b));
  441. }
  442. }
  443. }
  444. }