HttpConnection.cs 16 KB

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