HttpConnection.cs 16 KB

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