TcpBinaryFrameManager.cs 15 KB

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