HttpConnection.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. using System;
  2. using System.IO;
  3. using System.Net;
  4. using System.Net.Security;
  5. using System.Net.Sockets;
  6. using System.Security.Authentication;
  7. using System.Security.Cryptography.X509Certificates;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using MediaBrowser.Model.Cryptography;
  12. using MediaBrowser.Model.IO;
  13. using MediaBrowser.Model.System;
  14. using MediaBrowser.Model.Text;
  15. using Microsoft.Extensions.Logging;
  16. using SocketHttpListener.Primitives;
  17. namespace SocketHttpListener.Net
  18. {
  19. sealed class HttpConnection
  20. {
  21. private static AsyncCallback s_onreadCallback = new AsyncCallback(OnRead);
  22. const int BufferSize = 8192;
  23. Socket _socket;
  24. Stream _stream;
  25. HttpEndPointListener _epl;
  26. MemoryStream _memoryStream;
  27. byte[] _buffer;
  28. HttpListenerContext _context;
  29. StringBuilder _currentLine;
  30. ListenerPrefix _prefix;
  31. HttpRequestStream _requestStream;
  32. HttpResponseStream _responseStream;
  33. bool _chunked;
  34. int _reuses;
  35. bool _contextBound;
  36. bool secure;
  37. int _timeout = 90000; // 90k ms for first request, 15k ms from then on
  38. private Timer _timer;
  39. IPEndPoint local_ep;
  40. HttpListener _lastListener;
  41. X509Certificate cert;
  42. SslStream ssl_stream;
  43. private readonly ILogger _logger;
  44. private readonly ICryptoProvider _cryptoProvider;
  45. private readonly IStreamHelper _streamHelper;
  46. private readonly ITextEncoding _textEncoding;
  47. private readonly IFileSystem _fileSystem;
  48. private readonly IEnvironmentInfo _environment;
  49. public HttpConnection(ILogger logger, Socket socket, HttpEndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IStreamHelper streamHelper, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
  50. {
  51. _logger = logger;
  52. this._socket = socket;
  53. this._epl = epl;
  54. this.secure = secure;
  55. this.cert = cert;
  56. _cryptoProvider = cryptoProvider;
  57. _streamHelper = streamHelper;
  58. _textEncoding = textEncoding;
  59. _fileSystem = fileSystem;
  60. _environment = environment;
  61. if (secure == false)
  62. {
  63. _stream = new SocketStream(_socket, false);
  64. }
  65. else
  66. {
  67. ssl_stream = new SslStream(new SocketStream(_socket, false), false, (t, c, ch, e) =>
  68. {
  69. if (c == null)
  70. {
  71. return true;
  72. }
  73. //var c2 = c as X509Certificate2;
  74. //if (c2 == null)
  75. //{
  76. // c2 = new X509Certificate2(c.GetRawCertData());
  77. //}
  78. //_clientCert = c2;
  79. //_clientCertErrors = new int[] { (int)e };
  80. return true;
  81. });
  82. _stream = ssl_stream;
  83. }
  84. }
  85. public Stream Stream => _stream;
  86. public async Task Init()
  87. {
  88. _timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
  89. if (ssl_stream != null)
  90. {
  91. var enableAsync = true;
  92. if (enableAsync)
  93. {
  94. await ssl_stream.AuthenticateAsServerAsync(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false).ConfigureAwait(false);
  95. }
  96. else
  97. {
  98. ssl_stream.AuthenticateAsServer(cert, false, (SslProtocols)ServicePointManager.SecurityProtocol, false);
  99. }
  100. }
  101. InitInternal();
  102. }
  103. private void InitInternal()
  104. {
  105. _contextBound = false;
  106. _requestStream = null;
  107. _responseStream = null;
  108. _prefix = null;
  109. _chunked = false;
  110. _memoryStream = new MemoryStream();
  111. _position = 0;
  112. _inputState = InputState.RequestLine;
  113. _lineState = LineState.None;
  114. _context = new HttpListenerContext(this, _textEncoding);
  115. }
  116. public bool IsClosed => (_socket == null);
  117. public int Reuses => _reuses;
  118. public IPEndPoint LocalEndPoint
  119. {
  120. get
  121. {
  122. if (local_ep != null)
  123. return local_ep;
  124. local_ep = (IPEndPoint)_socket.LocalEndPoint;
  125. return local_ep;
  126. }
  127. }
  128. public IPEndPoint RemoteEndPoint => _socket.RemoteEndPoint as IPEndPoint;
  129. public bool IsSecure => secure;
  130. public ListenerPrefix Prefix
  131. {
  132. get => _prefix;
  133. set => _prefix = value;
  134. }
  135. private void OnTimeout(object unused)
  136. {
  137. //_logger.LogInformation("HttpConnection timer fired");
  138. CloseSocket();
  139. Unbind();
  140. }
  141. public void BeginReadRequest()
  142. {
  143. if (_buffer == null)
  144. _buffer = new byte[BufferSize];
  145. try
  146. {
  147. if (_reuses == 1)
  148. _timeout = 15000;
  149. //_timer.Change(_timeout, Timeout.Infinite);
  150. _stream.BeginRead(_buffer, 0, BufferSize, s_onreadCallback, this);
  151. }
  152. catch
  153. {
  154. //_timer.Change(Timeout.Infinite, Timeout.Infinite);
  155. CloseSocket();
  156. Unbind();
  157. }
  158. }
  159. public HttpRequestStream GetRequestStream(bool chunked, long contentlength)
  160. {
  161. if (_requestStream == null)
  162. {
  163. byte[] buffer = _memoryStream.GetBuffer();
  164. int length = (int)_memoryStream.Length;
  165. _memoryStream = null;
  166. if (chunked)
  167. {
  168. _chunked = true;
  169. //_context.Response.SendChunked = true;
  170. _requestStream = new ChunkedInputStream(_context, _stream, buffer, _position, length - _position);
  171. }
  172. else
  173. {
  174. _requestStream = new HttpRequestStream(_stream, buffer, _position, length - _position, contentlength);
  175. }
  176. }
  177. return _requestStream;
  178. }
  179. public HttpResponseStream GetResponseStream(bool isExpect100Continue = false)
  180. {
  181. // TODO: can we get this _stream before reading the input?
  182. if (_responseStream == null)
  183. {
  184. var supportsDirectSocketAccess = !_context.Response.SendChunked && !isExpect100Continue && !secure;
  185. _responseStream = new HttpResponseStream(_stream, _context.Response, false, _streamHelper, _socket, supportsDirectSocketAccess, _environment, _fileSystem, _logger);
  186. }
  187. return _responseStream;
  188. }
  189. private static void OnRead(IAsyncResult ares)
  190. {
  191. var cnc = (HttpConnection)ares.AsyncState;
  192. cnc.OnReadInternal(ares);
  193. }
  194. private void OnReadInternal(IAsyncResult ares)
  195. {
  196. //_timer.Change(Timeout.Infinite, Timeout.Infinite);
  197. int nread = -1;
  198. try
  199. {
  200. nread = _stream.EndRead(ares);
  201. _memoryStream.Write(_buffer, 0, nread);
  202. if (_memoryStream.Length > 32768)
  203. {
  204. SendError("Bad Request", 400);
  205. Close(true);
  206. return;
  207. }
  208. }
  209. catch
  210. {
  211. if (_memoryStream != null && _memoryStream.Length > 0)
  212. SendError();
  213. if (_socket != null)
  214. {
  215. CloseSocket();
  216. Unbind();
  217. }
  218. return;
  219. }
  220. if (nread == 0)
  221. {
  222. CloseSocket();
  223. Unbind();
  224. return;
  225. }
  226. if (ProcessInput(_memoryStream))
  227. {
  228. if (!_context.HaveError)
  229. _context.Request.FinishInitialization();
  230. if (_context.HaveError)
  231. {
  232. SendError();
  233. Close(true);
  234. return;
  235. }
  236. if (!_epl.BindContext(_context))
  237. {
  238. const int NotFoundErrorCode = 404;
  239. SendError(HttpStatusDescription.Get(NotFoundErrorCode), NotFoundErrorCode);
  240. Close(true);
  241. return;
  242. }
  243. HttpListener listener = _epl.Listener;
  244. if (_lastListener != listener)
  245. {
  246. RemoveConnection();
  247. listener.AddConnection(this);
  248. _lastListener = listener;
  249. }
  250. _contextBound = true;
  251. listener.RegisterContext(_context);
  252. return;
  253. }
  254. _stream.BeginRead(_buffer, 0, BufferSize, s_onreadCallback, this);
  255. }
  256. private void RemoveConnection()
  257. {
  258. if (_lastListener == null)
  259. _epl.RemoveConnection(this);
  260. else
  261. _lastListener.RemoveConnection(this);
  262. }
  263. private enum InputState
  264. {
  265. RequestLine,
  266. Headers
  267. }
  268. private enum LineState
  269. {
  270. None,
  271. CR,
  272. LF
  273. }
  274. InputState _inputState = InputState.RequestLine;
  275. LineState _lineState = LineState.None;
  276. int _position;
  277. // true -> done processing
  278. // false -> need more input
  279. private bool ProcessInput(MemoryStream ms)
  280. {
  281. byte[] buffer = ms.GetBuffer();
  282. int len = (int)ms.Length;
  283. int used = 0;
  284. string line;
  285. while (true)
  286. {
  287. if (_context.HaveError)
  288. return true;
  289. if (_position >= len)
  290. break;
  291. try
  292. {
  293. line = ReadLine(buffer, _position, len - _position, ref used);
  294. _position += used;
  295. }
  296. catch
  297. {
  298. _context.ErrorMessage = "Bad request";
  299. _context.ErrorStatus = 400;
  300. return true;
  301. }
  302. if (line == null)
  303. break;
  304. if (line == "")
  305. {
  306. if (_inputState == InputState.RequestLine)
  307. continue;
  308. _currentLine = null;
  309. ms = null;
  310. return true;
  311. }
  312. if (_inputState == InputState.RequestLine)
  313. {
  314. _context.Request.SetRequestLine(line);
  315. _inputState = InputState.Headers;
  316. }
  317. else
  318. {
  319. try
  320. {
  321. _context.Request.AddHeader(line);
  322. }
  323. catch (Exception e)
  324. {
  325. _context.ErrorMessage = e.Message;
  326. _context.ErrorStatus = 400;
  327. return true;
  328. }
  329. }
  330. }
  331. if (used == len)
  332. {
  333. ms.SetLength(0);
  334. _position = 0;
  335. }
  336. return false;
  337. }
  338. private string ReadLine(byte[] buffer, int offset, int len, ref int used)
  339. {
  340. if (_currentLine == null)
  341. _currentLine = new StringBuilder(128);
  342. int last = offset + len;
  343. used = 0;
  344. for (int i = offset; i < last && _lineState != LineState.LF; i++)
  345. {
  346. used++;
  347. byte b = buffer[i];
  348. if (b == 13)
  349. {
  350. _lineState = LineState.CR;
  351. }
  352. else if (b == 10)
  353. {
  354. _lineState = LineState.LF;
  355. }
  356. else
  357. {
  358. _currentLine.Append((char)b);
  359. }
  360. }
  361. string result = null;
  362. if (_lineState == LineState.LF)
  363. {
  364. _lineState = LineState.None;
  365. result = _currentLine.ToString();
  366. _currentLine.Length = 0;
  367. }
  368. return result;
  369. }
  370. public void SendError(string msg, int status)
  371. {
  372. try
  373. {
  374. HttpListenerResponse response = _context.Response;
  375. response.StatusCode = status;
  376. response.ContentType = "text/html";
  377. string description = HttpStatusDescription.Get(status);
  378. string str;
  379. if (msg != null)
  380. str = string.Format("<h1>{0} ({1})</h1>", description, msg);
  381. else
  382. str = string.Format("<h1>{0}</h1>", description);
  383. byte[] error = _textEncoding.GetDefaultEncoding().GetBytes(str);
  384. response.Close(error, false);
  385. }
  386. catch
  387. {
  388. // response was already closed
  389. }
  390. }
  391. public void SendError()
  392. {
  393. SendError(_context.ErrorMessage, _context.ErrorStatus);
  394. }
  395. private void Unbind()
  396. {
  397. if (_contextBound)
  398. {
  399. _epl.UnbindContext(_context);
  400. _contextBound = false;
  401. }
  402. }
  403. public void Close()
  404. {
  405. Close(false);
  406. }
  407. private void CloseSocket()
  408. {
  409. if (_socket == null)
  410. return;
  411. try
  412. {
  413. _socket.Close();
  414. }
  415. catch { }
  416. finally
  417. {
  418. _socket = null;
  419. }
  420. RemoveConnection();
  421. }
  422. internal void Close(bool force)
  423. {
  424. if (_socket != null)
  425. {
  426. Stream st = GetResponseStream();
  427. if (st != null)
  428. st.Close();
  429. _responseStream = null;
  430. }
  431. if (_socket != null)
  432. {
  433. force |= !_context.Request.KeepAlive;
  434. if (!force)
  435. force = (string.Equals(_context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase));
  436. if (!force && _context.Request.FlushInput())
  437. {
  438. if (_chunked && _context.Response.ForceCloseChunked == false)
  439. {
  440. // Don't close. Keep working.
  441. _reuses++;
  442. Unbind();
  443. InitInternal();
  444. BeginReadRequest();
  445. return;
  446. }
  447. _reuses++;
  448. Unbind();
  449. InitInternal();
  450. BeginReadRequest();
  451. return;
  452. }
  453. Socket s = _socket;
  454. _socket = null;
  455. try
  456. {
  457. if (s != null)
  458. s.Shutdown(SocketShutdown.Both);
  459. }
  460. catch
  461. {
  462. }
  463. finally
  464. {
  465. if (s != null)
  466. {
  467. try
  468. {
  469. s.Close();
  470. }
  471. catch { }
  472. }
  473. }
  474. Unbind();
  475. RemoveConnection();
  476. return;
  477. }
  478. }
  479. }
  480. }