HttpConnection.cs 16 KB

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