ChunkedReadStream.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. namespace WinSCP
  5. {
  6. internal class ChunkedReadStream : Stream
  7. {
  8. public ChunkedReadStream(Stream baseStream, Action onDispose)
  9. {
  10. _baseStream = baseStream;
  11. _onDispose = onDispose;
  12. _remaining = 0;
  13. _eof = false;
  14. }
  15. public override bool CanRead => !_eof;
  16. public override bool CanSeek => false;
  17. public override bool CanWrite => false;
  18. public override long Length => throw new NotImplementedException();
  19. public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
  20. public override void Flush()
  21. {
  22. throw new NotImplementedException();
  23. }
  24. public override int Read(byte[] buffer, int offset, int count)
  25. {
  26. int result;
  27. if (_eof)
  28. {
  29. result = 0;
  30. }
  31. else
  32. {
  33. if (_remaining == 0)
  34. {
  35. string lenStr = string.Empty;
  36. while (!lenStr.EndsWith("\r\n"))
  37. {
  38. if (lenStr.Length > 64)
  39. {
  40. throw new Exception("Too long chunk length line");
  41. }
  42. int b = _baseStream.ReadByte();
  43. if (b < 0)
  44. {
  45. throw new Exception("End of stream reached while reading chunk length line");
  46. }
  47. lenStr += (char)b;
  48. }
  49. lenStr = lenStr.Trim();
  50. _remaining = int.Parse(lenStr, NumberStyles.HexNumber);
  51. if (_remaining == 0)
  52. {
  53. _eof = true;
  54. }
  55. }
  56. // Not sure if it is ok to call Read with 0
  57. if (_remaining > 0)
  58. {
  59. int read = Math.Min(count, _remaining);
  60. result = _baseStream.Read(buffer, offset, read);
  61. _remaining -= result;
  62. }
  63. else
  64. {
  65. result = 0;
  66. }
  67. if (_remaining == 0)
  68. {
  69. int cr = _baseStream.ReadByte();
  70. if (cr != '\r')
  71. {
  72. throw new Exception("Expected CR");
  73. }
  74. int lf = _baseStream.ReadByte();
  75. if (lf != '\n')
  76. {
  77. throw new Exception("Expected LF");
  78. }
  79. }
  80. if (_eof)
  81. {
  82. // Throw any pending exception asap, not only once the stream is closed.
  83. // Also releases the lock.
  84. Closed();
  85. }
  86. }
  87. return result;
  88. }
  89. public override long Seek(long offset, SeekOrigin origin)
  90. {
  91. throw new NotImplementedException();
  92. }
  93. public override void SetLength(long value)
  94. {
  95. throw new NotImplementedException();
  96. }
  97. public override void Write(byte[] buffer, int offset, int count)
  98. {
  99. throw new NotImplementedException();
  100. }
  101. protected override void Dispose(bool disposing)
  102. {
  103. try
  104. {
  105. // Have to consume the rest of the buffered download data, otherwise we could not continue with other downloads
  106. while (!_eof)
  107. {
  108. byte[] buf = new byte[10240];
  109. Read(buf, 0, buf.Length);
  110. }
  111. base.Dispose(disposing);
  112. }
  113. finally
  114. {
  115. Closed();
  116. }
  117. }
  118. private void Closed()
  119. {
  120. Action onDispose = _onDispose;
  121. _onDispose = null;
  122. onDispose?.Invoke();
  123. }
  124. private Stream _baseStream;
  125. private Action _onDispose;
  126. private int _remaining;
  127. private bool _eof;
  128. }
  129. }