HttpConnection.cs 16 KB

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