JsonEncodingStreamWrapper.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. #pragma warning disable 1634 // Stops compiler from warning about unknown warnings (for Presharp)
  5. namespace System.Runtime.Serialization.Json
  6. {
  7. using System.IO;
  8. using System.ServiceModel;
  9. using System.Text;
  10. using System.Xml;
  11. using System.Security;
  12. // This wrapper does not support seek.
  13. // Supports: UTF-8, Unicode, BigEndianUnicode
  14. // ASSUMPTION ([....]): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers.
  15. class JsonEncodingStreamWrapper : Stream
  16. {
  17. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  18. + " data from being modified or leaked to other components in appdomain.")]
  19. static readonly UnicodeEncoding SafeBEUTF16 = new UnicodeEncoding(true, false, false);
  20. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  21. + " data from being modified or leaked to other components in appdomain.")]
  22. static readonly UnicodeEncoding SafeUTF16 = new UnicodeEncoding(false, false, false);
  23. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  24. + " data from being modified or leaked to other components in appdomain.")]
  25. static readonly UTF8Encoding SafeUTF8 = new UTF8Encoding(false, false);
  26. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  27. + " data from being modified or leaked to other components in appdomain.")]
  28. static readonly UnicodeEncoding ValidatingBEUTF16 = new UnicodeEncoding(true, false, true);
  29. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  30. + " data from being modified or leaked to other components in appdomain.")]
  31. static readonly UnicodeEncoding ValidatingUTF16 = new UnicodeEncoding(false, false, true);
  32. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  33. + " data from being modified or leaked to other components in appdomain.")]
  34. static readonly UTF8Encoding ValidatingUTF8 = new UTF8Encoding(false, true);
  35. const int BufferLength = 128;
  36. byte[] byteBuffer = new byte[1];
  37. int byteCount;
  38. int byteOffset;
  39. byte[] bytes;
  40. char[] chars;
  41. Decoder dec;
  42. Encoder enc;
  43. Encoding encoding;
  44. SupportedEncoding encodingCode;
  45. bool isReading;
  46. Stream stream;
  47. public JsonEncodingStreamWrapper(Stream stream, Encoding encoding, bool isReader)
  48. {
  49. this.isReading = isReader;
  50. if (isReader)
  51. {
  52. InitForReading(stream, encoding);
  53. }
  54. else
  55. {
  56. if (encoding == null)
  57. {
  58. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
  59. }
  60. InitForWriting(stream, encoding);
  61. }
  62. }
  63. enum SupportedEncoding
  64. {
  65. UTF8,
  66. UTF16LE,
  67. UTF16BE,
  68. None
  69. }
  70. // This stream wrapper does not support duplex
  71. public override bool CanRead
  72. {
  73. get
  74. {
  75. if (!isReading)
  76. {
  77. return false;
  78. }
  79. return this.stream.CanRead;
  80. }
  81. }
  82. // The encoding conversion and buffering breaks seeking.
  83. public override bool CanSeek
  84. {
  85. get { return false; }
  86. }
  87. // Delegate properties
  88. public override bool CanTimeout
  89. {
  90. get { return this.stream.CanTimeout; }
  91. }
  92. // This stream wrapper does not support duplex
  93. public override bool CanWrite
  94. {
  95. get
  96. {
  97. if (isReading)
  98. {
  99. return false;
  100. }
  101. return this.stream.CanWrite;
  102. }
  103. }
  104. public override long Length
  105. {
  106. get { return this.stream.Length; }
  107. }
  108. // The encoding conversion and buffering breaks seeking.
  109. public override long Position
  110. {
  111. get
  112. {
  113. #pragma warning suppress 56503 // The contract for non seekable stream is to throw exception
  114. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
  115. }
  116. set { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); }
  117. }
  118. public override int ReadTimeout
  119. {
  120. get { return this.stream.ReadTimeout; }
  121. set { this.stream.ReadTimeout = value; }
  122. }
  123. public override int WriteTimeout
  124. {
  125. get { return this.stream.WriteTimeout; }
  126. set { this.stream.WriteTimeout = value; }
  127. }
  128. public static ArraySegment<byte> ProcessBuffer(byte[] buffer, int offset, int count, Encoding encoding)
  129. {
  130. try
  131. {
  132. SupportedEncoding expectedEnc = GetSupportedEncoding(encoding);
  133. SupportedEncoding dataEnc;
  134. if (count < 2)
  135. {
  136. dataEnc = SupportedEncoding.UTF8;
  137. }
  138. else
  139. {
  140. dataEnc = ReadEncoding(buffer[offset], buffer[offset + 1]);
  141. }
  142. if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
  143. {
  144. ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
  145. }
  146. // Fastpath: UTF-8
  147. if (dataEnc == SupportedEncoding.UTF8)
  148. {
  149. return new ArraySegment<byte>(buffer, offset, count);
  150. }
  151. // Convert to UTF-8
  152. return
  153. new ArraySegment<byte>(ValidatingUTF8.GetBytes(GetEncoding(dataEnc).GetChars(buffer, offset, count)));
  154. }
  155. catch (DecoderFallbackException e)
  156. {
  157. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  158. new XmlException(SR.GetString(SR.JsonInvalidBytes), e));
  159. }
  160. }
  161. public override void Close()
  162. {
  163. Flush();
  164. base.Close();
  165. this.stream.Close();
  166. }
  167. public override void Flush()
  168. {
  169. this.stream.Flush();
  170. }
  171. public override int Read(byte[] buffer, int offset, int count)
  172. {
  173. try
  174. {
  175. if (byteCount == 0)
  176. {
  177. if (encodingCode == SupportedEncoding.UTF8)
  178. {
  179. return this.stream.Read(buffer, offset, count);
  180. }
  181. // No more bytes than can be turned into characters
  182. byteOffset = 0;
  183. byteCount = this.stream.Read(bytes, byteCount, (chars.Length - 1) * 2);
  184. // Check for end of stream
  185. if (byteCount == 0)
  186. {
  187. return 0;
  188. }
  189. // Fix up incomplete chars
  190. CleanupCharBreak();
  191. // Change encoding
  192. int charCount = this.encoding.GetChars(bytes, 0, byteCount, chars, 0);
  193. byteCount = Encoding.UTF8.GetBytes(chars, 0, charCount, bytes, 0);
  194. }
  195. // Give them bytes
  196. if (byteCount < count)
  197. {
  198. count = byteCount;
  199. }
  200. Buffer.BlockCopy(bytes, byteOffset, buffer, offset, count);
  201. byteOffset += count;
  202. byteCount -= count;
  203. return count;
  204. }
  205. catch (DecoderFallbackException ex)
  206. {
  207. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  208. new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
  209. }
  210. }
  211. public override int ReadByte()
  212. {
  213. if (byteCount == 0 && encodingCode == SupportedEncoding.UTF8)
  214. {
  215. return this.stream.ReadByte();
  216. }
  217. if (Read(byteBuffer, 0, 1) == 0)
  218. {
  219. return -1;
  220. }
  221. return byteBuffer[0];
  222. }
  223. public override long Seek(long offset, SeekOrigin origin)
  224. {
  225. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
  226. }
  227. // Delegate methods
  228. public override void SetLength(long value)
  229. {
  230. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
  231. }
  232. public override void Write(byte[] buffer, int offset, int count)
  233. {
  234. // Optimize UTF-8 case
  235. if (encodingCode == SupportedEncoding.UTF8)
  236. {
  237. this.stream.Write(buffer, offset, count);
  238. return;
  239. }
  240. while (count > 0)
  241. {
  242. int size = chars.Length < count ? chars.Length : count;
  243. int charCount = dec.GetChars(buffer, offset, size, chars, 0, false);
  244. byteCount = enc.GetBytes(chars, 0, charCount, bytes, 0, false);
  245. this.stream.Write(bytes, 0, byteCount);
  246. offset += size;
  247. count -= size;
  248. }
  249. }
  250. public override void WriteByte(byte b)
  251. {
  252. if (encodingCode == SupportedEncoding.UTF8)
  253. {
  254. this.stream.WriteByte(b);
  255. return;
  256. }
  257. byteBuffer[0] = b;
  258. Write(byteBuffer, 0, 1);
  259. }
  260. static Encoding GetEncoding(SupportedEncoding e)
  261. {
  262. switch (e)
  263. {
  264. case SupportedEncoding.UTF8:
  265. return ValidatingUTF8;
  266. case SupportedEncoding.UTF16LE:
  267. return ValidatingUTF16;
  268. case SupportedEncoding.UTF16BE:
  269. return ValidatingBEUTF16;
  270. default:
  271. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  272. new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
  273. }
  274. }
  275. static string GetEncodingName(SupportedEncoding enc)
  276. {
  277. switch (enc)
  278. {
  279. case SupportedEncoding.UTF8:
  280. return "utf-8";
  281. case SupportedEncoding.UTF16LE:
  282. return "utf-16LE";
  283. case SupportedEncoding.UTF16BE:
  284. return "utf-16BE";
  285. default:
  286. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  287. new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
  288. }
  289. }
  290. static SupportedEncoding GetSupportedEncoding(Encoding encoding)
  291. {
  292. if (encoding == null)
  293. {
  294. return SupportedEncoding.None;
  295. }
  296. if (encoding.WebName == ValidatingUTF8.WebName)
  297. {
  298. return SupportedEncoding.UTF8;
  299. }
  300. else if (encoding.WebName == ValidatingUTF16.WebName)
  301. {
  302. return SupportedEncoding.UTF16LE;
  303. }
  304. else if (encoding.WebName == ValidatingBEUTF16.WebName)
  305. {
  306. return SupportedEncoding.UTF16BE;
  307. }
  308. else
  309. {
  310. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  311. new XmlException(SR.GetString(SR.JsonEncodingNotSupported)));
  312. }
  313. }
  314. static SupportedEncoding ReadEncoding(byte b1, byte b2)
  315. {
  316. if (b1 == 0x00 && b2 != 0x00)
  317. {
  318. return SupportedEncoding.UTF16BE;
  319. }
  320. else if (b1 != 0x00 && b2 == 0x00)
  321. {
  322. // 857 It's possible to misdetect UTF-32LE as UTF-16LE, but that's OK.
  323. return SupportedEncoding.UTF16LE;
  324. }
  325. else if (b1 == 0x00 && b2 == 0x00)
  326. {
  327. // UTF-32BE not supported
  328. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonInvalidBytes)));
  329. }
  330. else
  331. {
  332. return SupportedEncoding.UTF8;
  333. }
  334. }
  335. static void ThrowExpectedEncodingMismatch(SupportedEncoding expEnc, SupportedEncoding actualEnc)
  336. {
  337. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.JsonExpectedEncoding, GetEncodingName(expEnc), GetEncodingName(actualEnc))));
  338. }
  339. void CleanupCharBreak()
  340. {
  341. int max = byteOffset + byteCount;
  342. // Read on 2 byte boundaries
  343. if ((byteCount % 2) != 0)
  344. {
  345. int b = this.stream.ReadByte();
  346. if (b < 0)
  347. {
  348. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  349. new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
  350. }
  351. bytes[max++] = (byte)b;
  352. byteCount++;
  353. }
  354. // Don't cut off a surrogate character
  355. int w;
  356. if (encodingCode == SupportedEncoding.UTF16LE)
  357. {
  358. w = bytes[max - 2] + (bytes[max - 1] << 8);
  359. }
  360. else
  361. {
  362. w = bytes[max - 1] + (bytes[max - 2] << 8);
  363. }
  364. if ((w & 0xDC00) != 0xDC00 && w >= 0xD800 && w <= 0xDBFF) // First 16-bit number of surrogate pair
  365. {
  366. int b1 = this.stream.ReadByte();
  367. int b2 = this.stream.ReadByte();
  368. if (b2 < 0)
  369. {
  370. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  371. new XmlException(SR.GetString(SR.JsonUnexpectedEndOfFile)));
  372. }
  373. bytes[max++] = (byte)b1;
  374. bytes[max++] = (byte)b2;
  375. byteCount += 2;
  376. }
  377. }
  378. void EnsureBuffers()
  379. {
  380. EnsureByteBuffer();
  381. if (chars == null)
  382. {
  383. chars = new char[BufferLength];
  384. }
  385. }
  386. void EnsureByteBuffer()
  387. {
  388. if (bytes != null)
  389. {
  390. return;
  391. }
  392. bytes = new byte[BufferLength * 4];
  393. byteOffset = 0;
  394. byteCount = 0;
  395. }
  396. void FillBuffer(int count)
  397. {
  398. count -= byteCount;
  399. while (count > 0)
  400. {
  401. int read = stream.Read(bytes, byteOffset + byteCount, count);
  402. if (read == 0)
  403. {
  404. break;
  405. }
  406. byteCount += read;
  407. count -= read;
  408. }
  409. }
  410. void InitForReading(Stream inputStream, Encoding expectedEncoding)
  411. {
  412. try
  413. {
  414. this.stream = new BufferedStream(inputStream);
  415. SupportedEncoding expectedEnc = GetSupportedEncoding(expectedEncoding);
  416. SupportedEncoding dataEnc = ReadEncoding();
  417. if ((expectedEnc != SupportedEncoding.None) && (expectedEnc != dataEnc))
  418. {
  419. ThrowExpectedEncodingMismatch(expectedEnc, dataEnc);
  420. }
  421. // Fastpath: UTF-8 (do nothing)
  422. if (dataEnc != SupportedEncoding.UTF8)
  423. {
  424. // Convert to UTF-8
  425. EnsureBuffers();
  426. FillBuffer((BufferLength - 1) * 2);
  427. this.encodingCode = dataEnc;
  428. this.encoding = GetEncoding(dataEnc);
  429. CleanupCharBreak();
  430. int count = this.encoding.GetChars(bytes, byteOffset, byteCount, chars, 0);
  431. byteOffset = 0;
  432. byteCount = ValidatingUTF8.GetBytes(chars, 0, count, bytes, 0);
  433. }
  434. }
  435. catch (DecoderFallbackException ex)
  436. {
  437. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  438. new XmlException(SR.GetString(SR.JsonInvalidBytes), ex));
  439. }
  440. }
  441. void InitForWriting(Stream outputStream, Encoding writeEncoding)
  442. {
  443. this.encoding = writeEncoding;
  444. this.stream = new BufferedStream(outputStream);
  445. // Set the encoding code
  446. this.encodingCode = GetSupportedEncoding(writeEncoding);
  447. if (this.encodingCode != SupportedEncoding.UTF8)
  448. {
  449. EnsureBuffers();
  450. dec = ValidatingUTF8.GetDecoder();
  451. enc = this.encoding.GetEncoder();
  452. }
  453. }
  454. SupportedEncoding ReadEncoding()
  455. {
  456. int b1 = this.stream.ReadByte();
  457. int b2 = this.stream.ReadByte();
  458. EnsureByteBuffer();
  459. SupportedEncoding e;
  460. if (b1 == -1)
  461. {
  462. e = SupportedEncoding.UTF8;
  463. byteCount = 0;
  464. }
  465. else if (b2 == -1)
  466. {
  467. e = SupportedEncoding.UTF8;
  468. bytes[0] = (byte)b1;
  469. byteCount = 1;
  470. }
  471. else
  472. {
  473. e = ReadEncoding((byte)b1, (byte)b2);
  474. bytes[0] = (byte)b1;
  475. bytes[1] = (byte)b2;
  476. byteCount = 2;
  477. }
  478. return e;
  479. }
  480. }
  481. }