HttpConnection.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. #region License
  2. /*
  3. * HttpConnection.cs
  4. *
  5. * This code is derived from HttpConnection.cs (System.Net) of Mono
  6. * (http://www.mono-project.com).
  7. *
  8. * The MIT License
  9. *
  10. * Copyright (c) 2005 Novell, Inc. (http://www.novell.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@novell.com>
  36. */
  37. #endregion
  38. #region Contributors
  39. /*
  40. * Contributors:
  41. * - Liryna <liryna.stark@gmail.com>
  42. * - Rohan Singh <rohan-singh@hotmail.com>
  43. */
  44. #endregion
  45. using System;
  46. using System.Collections.Generic;
  47. using System.IO;
  48. using System.Net;
  49. using System.Net.Security;
  50. using System.Net.Sockets;
  51. using System.Text;
  52. using System.Threading;
  53. namespace WebSocketSharp.Net
  54. {
  55. internal sealed class HttpConnection
  56. {
  57. #region Private Fields
  58. private int _attempts;
  59. private byte[] _buffer;
  60. private static readonly int _bufferLength;
  61. private HttpListenerContext _context;
  62. private StringBuilder _currentLine;
  63. private EndPointListener _endPointListener;
  64. private InputState _inputState;
  65. private RequestStream _inputStream;
  66. private LineState _lineState;
  67. private EndPoint _localEndPoint;
  68. private static readonly int _maxInputLength;
  69. private ResponseStream _outputStream;
  70. private int _position;
  71. private EndPoint _remoteEndPoint;
  72. private MemoryStream _requestBuffer;
  73. private int _reuses;
  74. private bool _secure;
  75. private Socket _socket;
  76. private Stream _stream;
  77. private object _sync;
  78. private int _timeout;
  79. private Dictionary<int, bool> _timeoutCanceled;
  80. private Timer _timer;
  81. #endregion
  82. #region Static Constructor
  83. static HttpConnection ()
  84. {
  85. _bufferLength = 8192;
  86. _maxInputLength = 32768;
  87. }
  88. #endregion
  89. #region Internal Constructors
  90. internal HttpConnection (Socket socket, EndPointListener listener)
  91. {
  92. _socket = socket;
  93. _endPointListener = listener;
  94. var netStream = new NetworkStream (socket, false);
  95. if (listener.IsSecure) {
  96. var sslConf = listener.SslConfiguration;
  97. var sslStream = new SslStream (
  98. netStream,
  99. false,
  100. sslConf.ClientCertificateValidationCallback
  101. );
  102. sslStream.AuthenticateAsServer (
  103. sslConf.ServerCertificate,
  104. sslConf.ClientCertificateRequired,
  105. sslConf.EnabledSslProtocols,
  106. sslConf.CheckCertificateRevocation
  107. );
  108. _secure = true;
  109. _stream = sslStream;
  110. }
  111. else {
  112. _stream = netStream;
  113. }
  114. _buffer = new byte[_bufferLength];
  115. _localEndPoint = socket.LocalEndPoint;
  116. _remoteEndPoint = socket.RemoteEndPoint;
  117. _sync = new object ();
  118. _timeoutCanceled = new Dictionary<int, bool> ();
  119. _timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite);
  120. // 90k ms for first request, 15k ms from then on.
  121. init (new MemoryStream (), 90000);
  122. }
  123. #endregion
  124. #region Public Properties
  125. public bool IsClosed {
  126. get {
  127. return _socket == null;
  128. }
  129. }
  130. public bool IsLocal {
  131. get {
  132. return ((IPEndPoint) _remoteEndPoint).Address.IsLocal ();
  133. }
  134. }
  135. public bool IsSecure {
  136. get {
  137. return _secure;
  138. }
  139. }
  140. public IPEndPoint LocalEndPoint {
  141. get {
  142. return (IPEndPoint) _localEndPoint;
  143. }
  144. }
  145. public IPEndPoint RemoteEndPoint {
  146. get {
  147. return (IPEndPoint) _remoteEndPoint;
  148. }
  149. }
  150. public int Reuses {
  151. get {
  152. return _reuses;
  153. }
  154. }
  155. public Stream Stream {
  156. get {
  157. return _stream;
  158. }
  159. }
  160. #endregion
  161. #region Private Methods
  162. private void close ()
  163. {
  164. lock (_sync) {
  165. if (_socket == null)
  166. return;
  167. disposeTimer ();
  168. disposeRequestBuffer ();
  169. disposeStream ();
  170. closeSocket ();
  171. }
  172. _context.Unregister ();
  173. _endPointListener.RemoveConnection (this);
  174. }
  175. private void closeSocket ()
  176. {
  177. try {
  178. _socket.Shutdown (SocketShutdown.Both);
  179. }
  180. catch {
  181. }
  182. _socket.Close ();
  183. _socket = null;
  184. }
  185. private static MemoryStream createRequestBuffer (
  186. RequestStream inputStream
  187. )
  188. {
  189. var ret = new MemoryStream ();
  190. if (inputStream is ChunkedRequestStream) {
  191. var crs = (ChunkedRequestStream) inputStream;
  192. if (crs.HasRemainingBuffer) {
  193. var buff = crs.RemainingBuffer;
  194. ret.Write (buff, 0, buff.Length);
  195. }
  196. return ret;
  197. }
  198. var cnt = inputStream.Count;
  199. if (cnt > 0)
  200. ret.Write (inputStream.InitialBuffer, inputStream.Offset, cnt);
  201. return ret;
  202. }
  203. private void disposeRequestBuffer ()
  204. {
  205. if (_requestBuffer == null)
  206. return;
  207. _requestBuffer.Dispose ();
  208. _requestBuffer = null;
  209. }
  210. private void disposeStream ()
  211. {
  212. if (_stream == null)
  213. return;
  214. _stream.Dispose ();
  215. _stream = null;
  216. }
  217. private void disposeTimer ()
  218. {
  219. if (_timer == null)
  220. return;
  221. try {
  222. _timer.Change (Timeout.Infinite, Timeout.Infinite);
  223. }
  224. catch {
  225. }
  226. _timer.Dispose ();
  227. _timer = null;
  228. }
  229. private void init (MemoryStream requestBuffer, int timeout)
  230. {
  231. _requestBuffer = requestBuffer;
  232. _timeout = timeout;
  233. _context = new HttpListenerContext (this);
  234. _currentLine = new StringBuilder (64);
  235. _inputState = InputState.RequestLine;
  236. _inputStream = null;
  237. _lineState = LineState.None;
  238. _outputStream = null;
  239. _position = 0;
  240. }
  241. private static void onRead (IAsyncResult asyncResult)
  242. {
  243. var conn = (HttpConnection) asyncResult.AsyncState;
  244. var current = conn._attempts;
  245. if (conn._socket == null)
  246. return;
  247. lock (conn._sync) {
  248. if (conn._socket == null)
  249. return;
  250. conn._timer.Change (Timeout.Infinite, Timeout.Infinite);
  251. conn._timeoutCanceled[current] = true;
  252. var nread = 0;
  253. try {
  254. nread = conn._stream.EndRead (asyncResult);
  255. }
  256. catch (Exception) {
  257. // TODO: Logging.
  258. conn.close ();
  259. return;
  260. }
  261. if (nread <= 0) {
  262. conn.close ();
  263. return;
  264. }
  265. conn._requestBuffer.Write (conn._buffer, 0, nread);
  266. if (conn.processRequestBuffer ())
  267. return;
  268. conn.BeginReadRequest ();
  269. }
  270. }
  271. private static void onTimeout (object state)
  272. {
  273. var conn = (HttpConnection) state;
  274. var current = conn._attempts;
  275. if (conn._socket == null)
  276. return;
  277. lock (conn._sync) {
  278. if (conn._socket == null)
  279. return;
  280. if (conn._timeoutCanceled[current])
  281. return;
  282. conn._context.SendError (408);
  283. }
  284. }
  285. private bool processInput (byte[] data, int length)
  286. {
  287. // This method returns a bool:
  288. // - true Done processing
  289. // - false Need more input
  290. var req = _context.Request;
  291. try {
  292. while (true) {
  293. int nread;
  294. var line = readLineFrom (data, _position, length, out nread);
  295. _position += nread;
  296. if (line == null)
  297. break;
  298. if (line.Length == 0) {
  299. if (_inputState == InputState.RequestLine)
  300. continue;
  301. if (_position > _maxInputLength)
  302. _context.ErrorMessage = "Headers too long";
  303. return true;
  304. }
  305. if (_inputState == InputState.RequestLine) {
  306. req.SetRequestLine (line);
  307. _inputState = InputState.Headers;
  308. }
  309. else {
  310. req.AddHeader (line);
  311. }
  312. if (_context.HasErrorMessage)
  313. return true;
  314. }
  315. }
  316. catch (Exception) {
  317. // TODO: Logging.
  318. _context.ErrorMessage = "Processing failure";
  319. return true;
  320. }
  321. if (_position >= _maxInputLength) {
  322. _context.ErrorMessage = "Headers too long";
  323. return true;
  324. }
  325. return false;
  326. }
  327. private bool processRequestBuffer ()
  328. {
  329. // This method returns a bool:
  330. // - true Done processing
  331. // - false Need more write
  332. var data = _requestBuffer.GetBuffer ();
  333. var len = (int) _requestBuffer.Length;
  334. if (!processInput (data, len))
  335. return false;
  336. var req = _context.Request;
  337. if (!_context.HasErrorMessage)
  338. req.FinishInitialization ();
  339. if (_context.HasErrorMessage) {
  340. _context.SendError ();
  341. return true;
  342. }
  343. var uri = req.Url;
  344. HttpListener httplsnr;
  345. if (!_endPointListener.TrySearchHttpListener (uri, out httplsnr)) {
  346. _context.SendError (404);
  347. return true;
  348. }
  349. httplsnr.RegisterContext (_context);
  350. return true;
  351. }
  352. private string readLineFrom (
  353. byte[] buffer, int offset, int length, out int nread
  354. )
  355. {
  356. nread = 0;
  357. for (var i = offset; i < length; i++) {
  358. nread++;
  359. var b = buffer[i];
  360. if (b == 13) {
  361. _lineState = LineState.Cr;
  362. continue;
  363. }
  364. if (b == 10) {
  365. _lineState = LineState.Lf;
  366. break;
  367. }
  368. _currentLine.Append ((char) b);
  369. }
  370. if (_lineState != LineState.Lf)
  371. return null;
  372. var ret = _currentLine.ToString ();
  373. _currentLine.Length = 0;
  374. _lineState = LineState.None;
  375. return ret;
  376. }
  377. private MemoryStream takeOverRequestBuffer ()
  378. {
  379. if (_inputStream != null)
  380. return createRequestBuffer (_inputStream);
  381. var ret = new MemoryStream ();
  382. var buff = _requestBuffer.GetBuffer ();
  383. var len = (int) _requestBuffer.Length;
  384. var cnt = len - _position;
  385. if (cnt > 0)
  386. ret.Write (buff, _position, cnt);
  387. disposeRequestBuffer ();
  388. return ret;
  389. }
  390. #endregion
  391. #region Internal Methods
  392. internal void BeginReadRequest ()
  393. {
  394. _attempts++;
  395. _timeoutCanceled.Add (_attempts, false);
  396. _timer.Change (_timeout, Timeout.Infinite);
  397. try {
  398. _stream.BeginRead (_buffer, 0, _bufferLength, onRead, this);
  399. }
  400. catch (Exception) {
  401. // TODO: Logging.
  402. close ();
  403. }
  404. }
  405. internal void Close (bool force)
  406. {
  407. if (_socket == null)
  408. return;
  409. lock (_sync) {
  410. if (_socket == null)
  411. return;
  412. if (force) {
  413. if (_outputStream != null)
  414. _outputStream.Close (true);
  415. close ();
  416. return;
  417. }
  418. GetResponseStream ().Close (false);
  419. if (_context.Response.CloseConnection) {
  420. close ();
  421. return;
  422. }
  423. if (!_context.Request.FlushInput ()) {
  424. close ();
  425. return;
  426. }
  427. _context.Unregister ();
  428. _reuses++;
  429. var buff = takeOverRequestBuffer ();
  430. var len = buff.Length;
  431. init (buff, 15000);
  432. if (len > 0) {
  433. if (processRequestBuffer ())
  434. return;
  435. }
  436. BeginReadRequest ();
  437. }
  438. }
  439. #endregion
  440. #region Public Methods
  441. public void Close ()
  442. {
  443. Close (false);
  444. }
  445. public RequestStream GetRequestStream (long contentLength, bool chunked)
  446. {
  447. lock (_sync) {
  448. if (_socket == null)
  449. return null;
  450. if (_inputStream != null)
  451. return _inputStream;
  452. var buff = _requestBuffer.GetBuffer ();
  453. var len = (int) _requestBuffer.Length;
  454. var cnt = len - _position;
  455. _inputStream = chunked
  456. ? new ChunkedRequestStream (
  457. _stream, buff, _position, cnt, _context
  458. )
  459. : new RequestStream (
  460. _stream, buff, _position, cnt, contentLength
  461. );
  462. disposeRequestBuffer ();
  463. return _inputStream;
  464. }
  465. }
  466. public ResponseStream GetResponseStream ()
  467. {
  468. lock (_sync) {
  469. if (_socket == null)
  470. return null;
  471. if (_outputStream != null)
  472. return _outputStream;
  473. var lsnr = _context.Listener;
  474. var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true;
  475. _outputStream = new ResponseStream (_stream, _context.Response, ignore);
  476. return _outputStream;
  477. }
  478. }
  479. #endregion
  480. }
  481. }