/websocket-sharp/Net/ChunkStream.cs

https://gitlab.com/OriumVR/websocket-sharp · C# · 360 lines · 251 code · 75 blank · 34 comment · 84 complexity · e7aa3ca5feb68ee688a654206514a59a MD5 · raw file

  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-2015 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 bool _gotIt;
  53. private WebHeaderCollection _headers;
  54. private StringBuilder _saved;
  55. private bool _sawCr;
  56. private InputChunkState _state;
  57. private int _trailerState;
  58. #endregion
  59. #region Public Constructors
  60. public ChunkStream (WebHeaderCollection headers)
  61. {
  62. _headers = headers;
  63. _chunkSize = -1;
  64. _chunks = new List<Chunk> ();
  65. _saved = new StringBuilder ();
  66. }
  67. public ChunkStream (byte[] buffer, int offset, int count, WebHeaderCollection headers)
  68. : this (headers)
  69. {
  70. Write (buffer, offset, count);
  71. }
  72. #endregion
  73. #region Internal Properties
  74. internal WebHeaderCollection Headers {
  75. get {
  76. return _headers;
  77. }
  78. }
  79. #endregion
  80. #region Public Properties
  81. public int ChunkLeft {
  82. get {
  83. return _chunkSize - _chunkRead;
  84. }
  85. }
  86. public bool WantMore {
  87. get {
  88. return _state != InputChunkState.End;
  89. }
  90. }
  91. #endregion
  92. #region Private Methods
  93. private int read (byte[] buffer, int offset, int count)
  94. {
  95. var nread = 0;
  96. var cnt = _chunks.Count;
  97. for (var i = 0; i < cnt; i++) {
  98. var chunk = _chunks[i];
  99. if (chunk == null)
  100. continue;
  101. if (chunk.ReadLeft == 0) {
  102. _chunks[i] = null;
  103. continue;
  104. }
  105. nread += chunk.Read (buffer, offset + nread, count - nread);
  106. if (nread == count)
  107. break;
  108. }
  109. return nread;
  110. }
  111. private static string removeChunkExtension (string value)
  112. {
  113. var idx = value.IndexOf (';');
  114. return idx > -1 ? value.Substring (0, idx) : value;
  115. }
  116. private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
  117. {
  118. if (!_sawCr) {
  119. if (buffer[offset++] != 13)
  120. throwProtocolViolation ("CR is expected.");
  121. _sawCr = true;
  122. if (offset == length)
  123. return InputChunkState.DataEnded;
  124. }
  125. if (buffer[offset++] != 10)
  126. throwProtocolViolation ("LF is expected.");
  127. return InputChunkState.None;
  128. }
  129. private InputChunkState setChunkSize (byte[] buffer, ref int offset, int length)
  130. {
  131. byte b = 0;
  132. while (offset < length) {
  133. b = buffer[offset++];
  134. if (_sawCr) {
  135. if (b != 10)
  136. throwProtocolViolation ("LF is expected.");
  137. break;
  138. }
  139. if (b == 13) {
  140. _sawCr = true;
  141. continue;
  142. }
  143. if (b == 10)
  144. throwProtocolViolation ("LF is unexpected.");
  145. if (b == 32) // SP
  146. _gotIt = true;
  147. if (!_gotIt)
  148. _saved.Append ((char) b);
  149. if (_saved.Length > 20)
  150. throwProtocolViolation ("The chunk size is too long.");
  151. }
  152. if (!_sawCr || b != 10)
  153. return InputChunkState.None;
  154. _chunkRead = 0;
  155. try {
  156. _chunkSize = Int32.Parse (
  157. removeChunkExtension (_saved.ToString ()), NumberStyles.HexNumber);
  158. }
  159. catch {
  160. throwProtocolViolation ("The chunk size cannot be parsed.");
  161. }
  162. if (_chunkSize == 0) {
  163. _trailerState = 2;
  164. return InputChunkState.Trailer;
  165. }
  166. return InputChunkState.Data;
  167. }
  168. private InputChunkState setTrailer (byte[] buffer, ref int offset, int length)
  169. {
  170. // Check if no trailer.
  171. if (_trailerState == 2 && buffer[offset] == 13 && _saved.Length == 0) {
  172. offset++;
  173. if (offset < length && buffer[offset] == 10) {
  174. offset++;
  175. return InputChunkState.End;
  176. }
  177. offset--;
  178. }
  179. while (offset < length && _trailerState < 4) {
  180. var b = buffer[offset++];
  181. _saved.Append ((char) b);
  182. if (_saved.Length > 4196)
  183. throwProtocolViolation ("The trailer is too long.");
  184. if (_trailerState == 1 || _trailerState == 3) {
  185. if (b != 10)
  186. throwProtocolViolation ("LF is expected.");
  187. _trailerState++;
  188. continue;
  189. }
  190. if (b == 13) {
  191. _trailerState++;
  192. continue;
  193. }
  194. if (b == 10)
  195. throwProtocolViolation ("LF is unexpected.");
  196. _trailerState = 0;
  197. }
  198. if (_trailerState < 4)
  199. return InputChunkState.Trailer;
  200. _saved.Length -= 2;
  201. var reader = new StringReader (_saved.ToString ());
  202. string line;
  203. while ((line = reader.ReadLine ()) != null && line.Length > 0)
  204. _headers.Add (line);
  205. return InputChunkState.End;
  206. }
  207. private static void throwProtocolViolation (string message)
  208. {
  209. throw new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
  210. }
  211. private void write (byte[] buffer, ref int offset, int length)
  212. {
  213. if (_state == InputChunkState.End)
  214. throwProtocolViolation ("The chunks were ended.");
  215. if (_state == InputChunkState.None) {
  216. _state = setChunkSize (buffer, ref offset, length);
  217. if (_state == InputChunkState.None)
  218. return;
  219. _saved.Length = 0;
  220. _sawCr = false;
  221. _gotIt = false;
  222. }
  223. if (_state == InputChunkState.Data && offset < length) {
  224. _state = writeData (buffer, ref offset, length);
  225. if (_state == InputChunkState.Data)
  226. return;
  227. }
  228. if (_state == InputChunkState.DataEnded && offset < length) {
  229. _state = seekCrLf (buffer, ref offset, length);
  230. if (_state == InputChunkState.DataEnded)
  231. return;
  232. _sawCr = false;
  233. }
  234. if (_state == InputChunkState.Trailer && offset < length) {
  235. _state = setTrailer (buffer, ref offset, length);
  236. if (_state == InputChunkState.Trailer)
  237. return;
  238. _saved.Length = 0;
  239. }
  240. if (offset < length)
  241. write (buffer, ref offset, length);
  242. }
  243. private InputChunkState writeData (byte[] buffer, ref int offset, int length)
  244. {
  245. var cnt = length - offset;
  246. var left = _chunkSize - _chunkRead;
  247. if (cnt > left)
  248. cnt = left;
  249. var data = new byte[cnt];
  250. Buffer.BlockCopy (buffer, offset, data, 0, cnt);
  251. _chunks.Add (new Chunk (data));
  252. offset += cnt;
  253. _chunkRead += cnt;
  254. return _chunkRead == _chunkSize ? InputChunkState.DataEnded : InputChunkState.Data;
  255. }
  256. #endregion
  257. #region Internal Methods
  258. internal void ResetBuffer ()
  259. {
  260. _chunkRead = 0;
  261. _chunkSize = -1;
  262. _chunks.Clear ();
  263. }
  264. internal int WriteAndReadBack (byte[] buffer, int offset, int writeCount, int readCount)
  265. {
  266. Write (buffer, offset, writeCount);
  267. return Read (buffer, offset, readCount);
  268. }
  269. #endregion
  270. #region Public Methods
  271. public int Read (byte[] buffer, int offset, int count)
  272. {
  273. if (count <= 0)
  274. return 0;
  275. return read (buffer, offset, count);
  276. }
  277. public void Write (byte[] buffer, int offset, int count)
  278. {
  279. if (count <= 0)
  280. return;
  281. write (buffer, ref offset, offset + count);
  282. }
  283. #endregion
  284. }
  285. }