HttpConnection.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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. }
  196. return o_stream;
  197. }
  198. void OnReadInternal(int nread)
  199. {
  200. ms.Write(buffer, 0, nread);
  201. if (ms.Length > 32768)
  202. {
  203. SendError("Bad request", 400);
  204. Close(true);
  205. return;
  206. }
  207. if (nread == 0)
  208. {
  209. //if (ms.Length > 0)
  210. // SendError (); // Why bother?
  211. CloseSocket();
  212. Unbind();
  213. return;
  214. }
  215. if (ProcessInput(ms))
  216. {
  217. if (!context.HaveError)
  218. context.Request.FinishInitialization();
  219. if (context.HaveError)
  220. {
  221. SendError();
  222. Close(true);
  223. return;
  224. }
  225. if (!epl.BindContext(context))
  226. {
  227. SendError("Invalid host", 400);
  228. Close(true);
  229. return;
  230. }
  231. HttpListener listener = epl.Listener;
  232. if (last_listener != listener)
  233. {
  234. RemoveConnection();
  235. listener.AddConnection(this);
  236. last_listener = listener;
  237. }
  238. context_bound = true;
  239. listener.RegisterContext(context);
  240. return;
  241. }
  242. BeginReadRequest();
  243. }
  244. private void OnReadInternalException(MemoryStream ms, Exception ex)
  245. {
  246. //_logger.ErrorException("Error in HttpConnection.OnReadInternal", ex);
  247. if (ms != null && ms.Length > 0)
  248. SendError();
  249. if (sock != null)
  250. {
  251. CloseSocket();
  252. Unbind();
  253. }
  254. }
  255. void RemoveConnection()
  256. {
  257. if (last_listener == null)
  258. epl.RemoveConnection(this);
  259. else
  260. last_listener.RemoveConnection(this);
  261. }
  262. enum InputState
  263. {
  264. RequestLine,
  265. Headers
  266. }
  267. enum LineState
  268. {
  269. None,
  270. CR,
  271. LF
  272. }
  273. InputState input_state = InputState.RequestLine;
  274. LineState line_state = LineState.None;
  275. int position;
  276. // true -> done processing
  277. // false -> need more input
  278. bool ProcessInput(MemoryStream ms)
  279. {
  280. byte[] buffer;
  281. _memoryStreamFactory.TryGetBuffer(ms, out buffer);
  282. int len = (int)ms.Length;
  283. int used = 0;
  284. string line;
  285. while (true)
  286. {
  287. if (context.HaveError)
  288. return true;
  289. if (position >= len)
  290. break;
  291. try
  292. {
  293. line = ReadLine(buffer, position, len - position, ref used);
  294. position += used;
  295. }
  296. catch
  297. {
  298. context.ErrorMessage = "Bad request";
  299. context.ErrorStatus = 400;
  300. return true;
  301. }
  302. if (line == null)
  303. break;
  304. if (line == "")
  305. {
  306. if (input_state == InputState.RequestLine)
  307. continue;
  308. current_line = null;
  309. ms = null;
  310. return true;
  311. }
  312. if (input_state == InputState.RequestLine)
  313. {
  314. context.Request.SetRequestLine(line);
  315. input_state = InputState.Headers;
  316. }
  317. else
  318. {
  319. try
  320. {
  321. context.Request.AddHeader(line);
  322. }
  323. catch (Exception e)
  324. {
  325. context.ErrorMessage = e.Message;
  326. context.ErrorStatus = 400;
  327. return true;
  328. }
  329. }
  330. }
  331. if (used == len)
  332. {
  333. ms.SetLength(0);
  334. position = 0;
  335. }
  336. return false;
  337. }
  338. string ReadLine(byte[] buffer, int offset, int len, ref int used)
  339. {
  340. if (current_line == null)
  341. current_line = new StringBuilder(128);
  342. int last = offset + len;
  343. used = 0;
  344. for (int i = offset; i < last && line_state != LineState.LF; i++)
  345. {
  346. used++;
  347. byte b = buffer[i];
  348. if (b == 13)
  349. {
  350. line_state = LineState.CR;
  351. }
  352. else if (b == 10)
  353. {
  354. line_state = LineState.LF;
  355. }
  356. else
  357. {
  358. current_line.Append((char)b);
  359. }
  360. }
  361. string result = null;
  362. if (line_state == LineState.LF)
  363. {
  364. line_state = LineState.None;
  365. result = current_line.ToString();
  366. current_line.Length = 0;
  367. }
  368. return result;
  369. }
  370. public void SendError(string msg, int status)
  371. {
  372. try
  373. {
  374. HttpListenerResponse response = context.Response;
  375. response.StatusCode = status;
  376. response.ContentType = "text/html";
  377. string description = HttpListenerResponse.GetStatusDescription(status);
  378. string str;
  379. if (msg != null)
  380. str = String.Format("<h1>{0} ({1})</h1>", description, msg);
  381. else
  382. str = String.Format("<h1>{0}</h1>", description);
  383. byte[] error = context.Response.ContentEncoding.GetBytes(str);
  384. response.ContentLength64 = error.Length;
  385. response.OutputStream.Write(error, 0, (int)error.Length);
  386. response.Close();
  387. }
  388. catch
  389. {
  390. // response was already closed
  391. }
  392. }
  393. public void SendError()
  394. {
  395. SendError(context.ErrorMessage, context.ErrorStatus);
  396. }
  397. void Unbind()
  398. {
  399. if (context_bound)
  400. {
  401. epl.UnbindContext(context);
  402. context_bound = false;
  403. }
  404. }
  405. public void Close()
  406. {
  407. Close(false);
  408. }
  409. private void CloseSocket()
  410. {
  411. if (sock == null)
  412. return;
  413. try
  414. {
  415. sock.Close();
  416. }
  417. catch
  418. {
  419. }
  420. finally
  421. {
  422. sock = null;
  423. }
  424. RemoveConnection();
  425. }
  426. internal void Close(bool force_close)
  427. {
  428. if (sock != null)
  429. {
  430. if (!context.Request.IsWebSocketRequest || force_close)
  431. {
  432. Stream st = GetResponseStream();
  433. if (st != null)
  434. {
  435. st.Dispose();
  436. }
  437. o_stream = null;
  438. }
  439. }
  440. if (sock != null)
  441. {
  442. force_close |= !context.Request.KeepAlive;
  443. if (!force_close)
  444. force_close = (string.Equals(context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase));
  445. /*
  446. if (!force_close) {
  447. // bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
  448. // status_code == 413 || status_code == 414 || status_code == 500 ||
  449. // status_code == 503);
  450. force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
  451. }
  452. */
  453. if (!force_close && context.Request.FlushInput())
  454. {
  455. reuses++;
  456. Unbind();
  457. Init();
  458. BeginReadRequest();
  459. return;
  460. }
  461. IAcceptSocket s = sock;
  462. sock = null;
  463. try
  464. {
  465. if (s != null)
  466. s.Shutdown(true);
  467. }
  468. catch
  469. {
  470. }
  471. finally
  472. {
  473. if (s != null)
  474. s.Close();
  475. }
  476. Unbind();
  477. RemoveConnection();
  478. return;
  479. }
  480. }
  481. }
  482. }