HttpConnection.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. //
  2. // HttpConnection.cs
  3. // Copied from System.Net.HttpConnection
  4. //
  5. // Author:
  6. // Gonzalo Paniagua Javier (gonzalo@novell.com)
  7. //
  8. // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  9. // Copyright (c) 2012 sta.blockhead (sta.blockhead@gmail.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.IO;
  32. using System.Net;
  33. using System.Net.Sockets;
  34. using System.Reflection;
  35. using System.Security.Cryptography;
  36. using System.Security.Cryptography.X509Certificates;
  37. using System.Text;
  38. using System.Threading;
  39. using WebSocketSharp.Net.Security;
  40. namespace WebSocketSharp.Net
  41. {
  42. sealed class HttpConnection
  43. {
  44. #region Enums
  45. enum InputState
  46. {
  47. RequestLine,
  48. Headers
  49. }
  50. enum LineState
  51. {
  52. None,
  53. CR,
  54. LF
  55. }
  56. #endregion
  57. #region Private Const Field
  58. const int BufferSize = 8192;
  59. #endregion
  60. #region Private Static Field
  61. static AsyncCallback onread_cb = new AsyncCallback(OnRead);
  62. #endregion
  63. #region Private Fields
  64. byte[] buffer;
  65. bool chunked;
  66. HttpListenerContext context;
  67. bool context_bound;
  68. StringBuilder current_line;
  69. EndPointListener epl;
  70. InputState input_state;
  71. RequestStream i_stream;
  72. AsymmetricAlgorithm key;
  73. HttpListener last_listener;
  74. LineState line_state;
  75. // IPEndPoint local_ep; // never used
  76. MemoryStream ms;
  77. ResponseStream o_stream;
  78. int position;
  79. ListenerPrefix prefix;
  80. int reuses;
  81. bool secure;
  82. Socket sock;
  83. Stream stream;
  84. int s_timeout;
  85. Timer timer;
  86. #endregion
  87. #region Constructor
  88. public HttpConnection(
  89. Socket sock,
  90. EndPointListener epl,
  91. bool secure,
  92. X509Certificate2 cert,
  93. AsymmetricAlgorithm key
  94. )
  95. {
  96. this.sock = sock;
  97. this.epl = epl;
  98. this.secure = secure;
  99. this.key = key;
  100. // if (secure == false) {
  101. // stream = new NetworkStream (sock, false);
  102. // } else {
  103. // var ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
  104. // ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
  105. // stream = ssl_stream;
  106. // }
  107. var net_stream = new NetworkStream(sock, false);
  108. if (!secure)
  109. {
  110. stream = net_stream;
  111. }
  112. else
  113. {
  114. var ssl_stream = new SslStream(net_stream, false);
  115. ssl_stream.AuthenticateAsServer(cert);
  116. stream = ssl_stream;
  117. }
  118. timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
  119. Init();
  120. }
  121. #endregion
  122. #region Properties
  123. public bool IsClosed
  124. {
  125. get { return (sock == null); }
  126. }
  127. public bool IsSecure
  128. {
  129. get { return secure; }
  130. }
  131. public IPEndPoint LocalEndPoint
  132. {
  133. get { return (IPEndPoint)sock.LocalEndPoint; }
  134. }
  135. public ListenerPrefix Prefix
  136. {
  137. get { return prefix; }
  138. set { prefix = value; }
  139. }
  140. public IPEndPoint RemoteEndPoint
  141. {
  142. get { return (IPEndPoint)sock.RemoteEndPoint; }
  143. }
  144. public int Reuses
  145. {
  146. get { return reuses; }
  147. }
  148. public Stream Stream
  149. {
  150. get { return stream; }
  151. }
  152. #endregion
  153. #region Private Methods
  154. void CloseSocket()
  155. {
  156. if (sock == null)
  157. return;
  158. try
  159. {
  160. sock.Close();
  161. }
  162. catch
  163. {
  164. }
  165. finally
  166. {
  167. sock = null;
  168. }
  169. RemoveConnection();
  170. }
  171. void Init()
  172. {
  173. context_bound = false;
  174. i_stream = null;
  175. o_stream = null;
  176. prefix = null;
  177. chunked = false;
  178. ms = new MemoryStream();
  179. position = 0;
  180. input_state = InputState.RequestLine;
  181. line_state = LineState.None;
  182. context = new HttpListenerContext(this);
  183. s_timeout = 90000; // 90k ms for first request, 15k ms from then on
  184. }
  185. AsymmetricAlgorithm OnPVKSelection(X509Certificate certificate, string targetHost)
  186. {
  187. return key;
  188. }
  189. static void OnRead(IAsyncResult ares)
  190. {
  191. HttpConnection cnc = (HttpConnection)ares.AsyncState;
  192. cnc.OnReadInternal(ares);
  193. }
  194. void OnReadInternal(IAsyncResult ares)
  195. {
  196. timer.Change(Timeout.Infinite, Timeout.Infinite);
  197. int nread = -1;
  198. try
  199. {
  200. nread = stream.EndRead(ares);
  201. ms.Write(buffer, 0, nread);
  202. if (ms.Length > 32768)
  203. {
  204. SendError("Bad request", 400);
  205. Close(true);
  206. return;
  207. }
  208. }
  209. catch
  210. {
  211. if (ms != null && ms.Length > 0)
  212. SendError();
  213. if (sock != null)
  214. {
  215. CloseSocket();
  216. Unbind();
  217. }
  218. return;
  219. }
  220. if (nread == 0)
  221. {
  222. //if (ms.Length > 0)
  223. // SendError (); // Why bother?
  224. CloseSocket();
  225. Unbind();
  226. return;
  227. }
  228. if (ProcessInput(ms))
  229. {
  230. if (!context.HaveError)
  231. context.Request.FinishInitialization();
  232. if (context.HaveError)
  233. {
  234. SendError();
  235. Close(true);
  236. return;
  237. }
  238. if (!epl.BindContext(context))
  239. {
  240. SendError("Invalid host", 400);
  241. Close(true);
  242. return;
  243. }
  244. HttpListener listener = context.Listener;
  245. if (last_listener != listener)
  246. {
  247. RemoveConnection();
  248. listener.AddConnection(this);
  249. last_listener = listener;
  250. }
  251. context_bound = true;
  252. listener.RegisterContext(context);
  253. return;
  254. }
  255. stream.BeginRead(buffer, 0, BufferSize, onread_cb, this);
  256. }
  257. void OnTimeout(object unused)
  258. {
  259. CloseSocket();
  260. Unbind();
  261. }
  262. // true -> done processing
  263. // false -> need more input
  264. bool ProcessInput(MemoryStream ms)
  265. {
  266. byte[] buffer = ms.GetBuffer();
  267. int len = (int)ms.Length;
  268. int used = 0;
  269. string line;
  270. try
  271. {
  272. line = ReadLine(buffer, position, len - position, ref used);
  273. position += used;
  274. }
  275. catch
  276. {
  277. context.ErrorMessage = "Bad request";
  278. context.ErrorStatus = 400;
  279. return true;
  280. }
  281. do
  282. {
  283. if (line == null)
  284. break;
  285. if (line == "")
  286. {
  287. if (input_state == InputState.RequestLine)
  288. continue;
  289. current_line = null;
  290. ms = null;
  291. return true;
  292. }
  293. if (input_state == InputState.RequestLine)
  294. {
  295. context.Request.SetRequestLine(line);
  296. input_state = InputState.Headers;
  297. }
  298. else
  299. {
  300. try
  301. {
  302. context.Request.AddHeader(line);
  303. }
  304. catch (Exception e)
  305. {
  306. context.ErrorMessage = e.Message;
  307. context.ErrorStatus = 400;
  308. return true;
  309. }
  310. }
  311. if (context.HaveError)
  312. return true;
  313. if (position >= len)
  314. break;
  315. try
  316. {
  317. line = ReadLine(buffer, position, len - position, ref used);
  318. position += used;
  319. }
  320. catch
  321. {
  322. context.ErrorMessage = "Bad request";
  323. context.ErrorStatus = 400;
  324. return true;
  325. }
  326. } while (line != null);
  327. if (used == len)
  328. {
  329. ms.SetLength(0);
  330. position = 0;
  331. }
  332. return false;
  333. }
  334. string ReadLine(byte[] buffer, int offset, int len, ref int used)
  335. {
  336. if (current_line == null)
  337. current_line = new StringBuilder();
  338. int last = offset + len;
  339. used = 0;
  340. for (int i = offset; i < last && line_state != LineState.LF; i++)
  341. {
  342. used++;
  343. byte b = buffer[i];
  344. if (b == 13)
  345. {
  346. line_state = LineState.CR;
  347. }
  348. else if (b == 10)
  349. {
  350. line_state = LineState.LF;
  351. }
  352. else
  353. {
  354. current_line.Append((char)b);
  355. }
  356. }
  357. string result = null;
  358. if (line_state == LineState.LF)
  359. {
  360. line_state = LineState.None;
  361. result = current_line.ToString();
  362. current_line.Length = 0;
  363. }
  364. return result;
  365. }
  366. void RemoveConnection()
  367. {
  368. if (last_listener == null)
  369. epl.RemoveConnection(this);
  370. else
  371. last_listener.RemoveConnection(this);
  372. }
  373. void Unbind()
  374. {
  375. if (context_bound)
  376. {
  377. epl.UnbindContext(context);
  378. context_bound = false;
  379. }
  380. }
  381. #endregion
  382. #region Internal Method
  383. internal void Close(bool force_close)
  384. {
  385. if (sock != null)
  386. {
  387. Stream st = GetResponseStream();
  388. st.Close();
  389. o_stream = null;
  390. }
  391. if (sock != null)
  392. {
  393. force_close |= !context.Request.KeepAlive;
  394. if (!force_close)
  395. force_close = (context.Response.Headers["connection"] == "close");
  396. /*
  397. if (!force_close) {
  398. // bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
  399. // status_code == 413 || status_code == 414 || status_code == 500 ||
  400. // status_code == 503);
  401. force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
  402. }
  403. */
  404. if (!force_close && context.Request.FlushInput())
  405. {
  406. if (chunked && context.Response.ForceCloseChunked == false)
  407. {
  408. // Don't close. Keep working.
  409. reuses++;
  410. Unbind();
  411. Init();
  412. BeginReadRequest();
  413. return;
  414. }
  415. reuses++;
  416. Unbind();
  417. Init();
  418. BeginReadRequest();
  419. return;
  420. }
  421. Socket s = sock;
  422. sock = null;
  423. try
  424. {
  425. if (s != null)
  426. s.Shutdown(SocketShutdown.Both);
  427. }
  428. catch
  429. {
  430. }
  431. finally
  432. {
  433. if (s != null)
  434. s.Close();
  435. }
  436. Unbind();
  437. RemoveConnection();
  438. return;
  439. }
  440. }
  441. #endregion
  442. #region Public Methods
  443. public void BeginReadRequest()
  444. {
  445. if (buffer == null)
  446. buffer = new byte[BufferSize];
  447. try
  448. {
  449. if (reuses == 1)
  450. s_timeout = 15000;
  451. timer.Change(s_timeout, Timeout.Infinite);
  452. stream.BeginRead(buffer, 0, BufferSize, onread_cb, this);
  453. }
  454. catch
  455. {
  456. timer.Change(Timeout.Infinite, Timeout.Infinite);
  457. CloseSocket();
  458. Unbind();
  459. }
  460. }
  461. public void Close()
  462. {
  463. Close(false);
  464. }
  465. public RequestStream GetRequestStream(bool chunked, long contentlength)
  466. {
  467. if (i_stream == null)
  468. {
  469. byte[] buffer = ms.GetBuffer();
  470. int length = (int)ms.Length;
  471. ms = null;
  472. if (chunked)
  473. {
  474. this.chunked = true;
  475. context.Response.SendChunked = true;
  476. i_stream = new ChunkedInputStream(context, stream, buffer, position, length - position);
  477. }
  478. else
  479. {
  480. i_stream = new RequestStream(stream, buffer, position, length - position, contentlength);
  481. }
  482. }
  483. return i_stream;
  484. }
  485. public ResponseStream GetResponseStream()
  486. {
  487. // TODO: can we get this stream before reading the input?
  488. if (o_stream == null)
  489. {
  490. HttpListener listener = context.Listener;
  491. bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
  492. o_stream = new ResponseStream(stream, context.Response, ign);
  493. }
  494. return o_stream;
  495. }
  496. public void SendError()
  497. {
  498. SendError(context.ErrorMessage, context.ErrorStatus);
  499. }
  500. public void SendError(string msg, int status)
  501. {
  502. try
  503. {
  504. HttpListenerResponse response = context.Response;
  505. response.StatusCode = status;
  506. response.ContentType = "text/html";
  507. string description = Ext.GetStatusDescription(status);
  508. string str;
  509. if (msg != null)
  510. str = String.Format("<h1>{0} ({1})</h1>", description, msg);
  511. else
  512. str = String.Format("<h1>{0}</h1>", description);
  513. byte[] error = context.Response.ContentEncoding.GetBytes(str);
  514. response.Close(error, false);
  515. }
  516. catch
  517. {
  518. // response was already closed
  519. }
  520. }
  521. #endregion
  522. }
  523. }