ChunkStream.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #region License
  2. /*
  3. * ChunkStream.cs
  4. *
  5. * This code is derived from ChunkStream.cs (System.Net) of Mono
  6. * (http://www.mono-project.com).
  7. *
  8. * The MIT License
  9. *
  10. * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
  11. * Copyright (c) 2012-2022 sta.blockhead
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a copy
  14. * of this software and associated documentation files (the "Software"), to deal
  15. * in the Software without restriction, including without limitation the rights
  16. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  17. * copies of the Software, and to permit persons to whom the Software is
  18. * furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included in
  21. * all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  29. * THE SOFTWARE.
  30. */
  31. #endregion
  32. #region Authors
  33. /*
  34. * Authors:
  35. * - Gonzalo Paniagua Javier <gonzalo@ximian.com>
  36. */
  37. #endregion
  38. using System;
  39. using System.Collections.Generic;
  40. using System.Globalization;
  41. using System.IO;
  42. using System.Net;
  43. using System.Text;
  44. namespace WebSocketSharp.Net
  45. {
  46. internal class ChunkStream
  47. {
  48. #region Private Fields
  49. private int _chunkRead;
  50. private int _chunkSize;
  51. private List<Chunk> _chunks;
  52. private int _count;
  53. private byte[] _endBuffer;
  54. private bool _gotIt;
  55. private WebHeaderCollection _headers;
  56. private int _offset;
  57. private StringBuilder _saved;
  58. private bool _sawCr;
  59. private InputChunkState _state;
  60. private int _trailerState;
  61. #endregion
  62. #region Public Constructors
  63. public ChunkStream (WebHeaderCollection headers)
  64. {
  65. _headers = headers;
  66. _chunkSize = -1;
  67. _chunks = new List<Chunk> ();
  68. _saved = new StringBuilder ();
  69. }
  70. #endregion
  71. #region Internal Properties
  72. internal int Count {
  73. get {
  74. return _count;
  75. }
  76. }
  77. internal byte[] EndBuffer {
  78. get {
  79. return _endBuffer;
  80. }
  81. }
  82. internal int Offset {
  83. get {
  84. return _offset;
  85. }
  86. }
  87. #endregion
  88. #region Public Properties
  89. public WebHeaderCollection Headers {
  90. get {
  91. return _headers;
  92. }
  93. }
  94. public bool WantsMore {
  95. get {
  96. return _state < InputChunkState.End;
  97. }
  98. }
  99. #endregion
  100. #region Private Methods
  101. private int read (byte[] buffer, int offset, int count)
  102. {
  103. var nread = 0;
  104. var cnt = _chunks.Count;
  105. for (var i = 0; i < cnt; i++) {
  106. var chunk = _chunks[i];
  107. if (chunk == null)
  108. continue;
  109. if (chunk.ReadLeft == 0) {
  110. _chunks[i] = null;
  111. continue;
  112. }
  113. nread += chunk.Read (buffer, offset + nread, count - nread);
  114. if (nread == count)
  115. break;
  116. }
  117. return nread;
  118. }
  119. private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
  120. {
  121. if (!_sawCr) {
  122. if (buffer[offset++] != 13)
  123. throwProtocolViolation ("CR is expected.");
  124. _sawCr = true;
  125. if (offset == length)
  126. return InputChunkState.DataEnded;
  127. }
  128. if (buffer[offset++] != 10)
  129. throwProtocolViolation ("LF is expected.");
  130. return InputChunkState.None;
  131. }
  132. private InputChunkState setChunkSize (
  133. byte[] buffer, ref int offset, int length
  134. )
  135. {
  136. byte b = 0;
  137. while (offset < length) {
  138. b = buffer[offset++];
  139. if (_sawCr) {
  140. if (b != 10)
  141. throwProtocolViolation ("LF is expected.");
  142. break;
  143. }
  144. if (b == 13) {
  145. _sawCr = true;
  146. continue;
  147. }
  148. if (b == 10)
  149. throwProtocolViolation ("LF is unexpected.");
  150. if (_gotIt)
  151. continue;
  152. if (b == 32 || b == 59) { // SP or ';'
  153. _gotIt = true;
  154. continue;
  155. }
  156. _saved.Append ((char) b);
  157. }
  158. if (_saved.Length > 20)
  159. throwProtocolViolation ("The chunk size is too big.");
  160. if (b != 10)
  161. return InputChunkState.None;
  162. var s = _saved.ToString ();
  163. try {
  164. _chunkSize = Int32.Parse (s, NumberStyles.HexNumber);
  165. }
  166. catch {
  167. throwProtocolViolation ("The chunk size cannot be parsed.");
  168. }
  169. _chunkRead = 0;
  170. if (_chunkSize == 0) {
  171. _trailerState = 2;
  172. return InputChunkState.Trailer;
  173. }
  174. return InputChunkState.Data;
  175. }
  176. private InputChunkState setTrailer (
  177. byte[] buffer, ref int offset, int length
  178. )
  179. {
  180. while (offset < length) {
  181. if (_trailerState == 4) // CR LF CR LF
  182. break;
  183. var b = buffer[offset++];
  184. _saved.Append ((char) b);
  185. if (_trailerState == 1 || _trailerState == 3) { // CR or CR LF CR
  186. if (b != 10)
  187. throwProtocolViolation ("LF is expected.");
  188. _trailerState++;
  189. continue;
  190. }
  191. if (b == 13) {
  192. _trailerState++;
  193. continue;
  194. }
  195. if (b == 10)
  196. throwProtocolViolation ("LF is unexpected.");
  197. _trailerState = 0;
  198. }
  199. var len = _saved.Length;
  200. if (len > 4196)
  201. throwProtocolViolation ("The trailer is too long.");
  202. if (_trailerState < 4)
  203. return InputChunkState.Trailer;
  204. if (len == 2)
  205. return InputChunkState.End;
  206. _saved.Length = len - 2;
  207. var val = _saved.ToString ();
  208. var reader = new StringReader (val);
  209. while (true) {
  210. var line = reader.ReadLine ();
  211. if (line == null || line.Length == 0)
  212. break;
  213. _headers.Add (line);
  214. }
  215. return InputChunkState.End;
  216. }
  217. private static void throwProtocolViolation (string message)
  218. {
  219. throw new WebException (
  220. message, null, WebExceptionStatus.ServerProtocolViolation, null
  221. );
  222. }
  223. private void write (byte[] buffer, int offset, int length)
  224. {
  225. if (_state == InputChunkState.End)
  226. throwProtocolViolation ("The chunks were ended.");
  227. if (_state == InputChunkState.None) {
  228. _state = setChunkSize (buffer, ref offset, length);
  229. if (_state == InputChunkState.None)
  230. return;
  231. _saved.Length = 0;
  232. _sawCr = false;
  233. _gotIt = false;
  234. }
  235. if (_state == InputChunkState.Data) {
  236. if (offset >= length)
  237. return;
  238. _state = writeData (buffer, ref offset, length);
  239. if (_state == InputChunkState.Data)
  240. return;
  241. }
  242. if (_state == InputChunkState.DataEnded) {
  243. if (offset >= length)
  244. return;
  245. _state = seekCrLf (buffer, ref offset, length);
  246. if (_state == InputChunkState.DataEnded)
  247. return;
  248. _sawCr = false;
  249. }
  250. if (_state == InputChunkState.Trailer) {
  251. if (offset >= length)
  252. return;
  253. _state = setTrailer (buffer, ref offset, length);
  254. if (_state == InputChunkState.Trailer)
  255. return;
  256. _saved.Length = 0;
  257. }
  258. if (_state == InputChunkState.End) {
  259. _endBuffer = buffer;
  260. _offset = offset;
  261. _count = length - offset;
  262. return;
  263. }
  264. if (offset >= length)
  265. return;
  266. write (buffer, offset, length);
  267. }
  268. private InputChunkState writeData (
  269. byte[] buffer, ref int offset, int length
  270. )
  271. {
  272. var cnt = length - offset;
  273. var left = _chunkSize - _chunkRead;
  274. if (cnt > left)
  275. cnt = left;
  276. var data = new byte[cnt];
  277. Buffer.BlockCopy (buffer, offset, data, 0, cnt);
  278. var chunk = new Chunk (data);
  279. _chunks.Add (chunk);
  280. offset += cnt;
  281. _chunkRead += cnt;
  282. return _chunkRead == _chunkSize
  283. ? InputChunkState.DataEnded
  284. : InputChunkState.Data;
  285. }
  286. #endregion
  287. #region Internal Methods
  288. internal void ResetChunkStore ()
  289. {
  290. _chunkRead = 0;
  291. _chunkSize = -1;
  292. _chunks.Clear ();
  293. }
  294. #endregion
  295. #region Public Methods
  296. public int Read (byte[] buffer, int offset, int count)
  297. {
  298. if (count <= 0)
  299. return 0;
  300. return read (buffer, offset, count);
  301. }
  302. public void Write (byte[] buffer, int offset, int count)
  303. {
  304. if (count <= 0)
  305. return;
  306. write (buffer, offset, offset + count);
  307. }
  308. #endregion
  309. }
  310. }