HttpConnection.cs 16 KB

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