HttpClientContext.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using OSHttpServer.Exceptions;
  9. using OSHttpServer.Parser;
  10. using System.Net.Security;
  11. using System.Security.Cryptography.X509Certificates;
  12. namespace OSHttpServer
  13. {
  14. /// <summary>
  15. /// Contains a connection to a browser/client.
  16. /// </summary>
  17. /// <remarks>
  18. /// Remember to <see cref="Start"/> after you have hooked the <see cref="RequestReceived"/> event.
  19. /// </remarks>
  20. public class HttpClientContext : IHttpClientContext, IDisposable
  21. {
  22. const int MAXREQUESTS = 20;
  23. const int MAXKEEPALIVE = 60000;
  24. static private int basecontextID;
  25. private readonly byte[] m_ReceiveBuffer;
  26. private int m_ReceiveBytesLeft;
  27. private ILogWriter _log;
  28. private readonly IHttpRequestParser m_parser;
  29. private readonly int m_bufferSize;
  30. private HashSet<uint> requestsInServiceIDs;
  31. private Socket m_sock;
  32. public bool Available = true;
  33. public bool StreamPassedOff = false;
  34. public int MonitorStartMS = 0;
  35. public int MonitorKeepaliveMS = 0;
  36. public bool TriggerKeepalive = false;
  37. public int TimeoutFirstLine = 70000; // 70 seconds
  38. public int TimeoutRequestReceived = 180000; // 180 seconds
  39. // The difference between this and request received is on POST more time is needed before we get the full request.
  40. public int TimeoutFullRequestProcessed = 600000; // 10 minutes
  41. public int m_TimeoutKeepAlive = MAXKEEPALIVE; // 400 seconds before keepalive timeout
  42. // public int TimeoutKeepAlive = 120000; // 400 seconds before keepalive timeout
  43. public int m_maxRequests = MAXREQUESTS;
  44. public bool FirstRequestLineReceived;
  45. public bool FullRequestReceived;
  46. public bool FullRequestProcessed;
  47. private bool isSendingResponse = false;
  48. private HttpRequest m_currentRequest;
  49. private HttpResponse m_currentResponse;
  50. public int contextID { get; private set; }
  51. public int TimeoutKeepAlive
  52. {
  53. get { return m_TimeoutKeepAlive; }
  54. set
  55. {
  56. m_TimeoutKeepAlive = (value > MAXKEEPALIVE) ? MAXKEEPALIVE : value;
  57. }
  58. }
  59. public int MAXRequests
  60. {
  61. get { return m_maxRequests; }
  62. set
  63. {
  64. m_maxRequests = value > MAXREQUESTS ? MAXREQUESTS : value;
  65. }
  66. }
  67. public bool IsSending()
  68. {
  69. return isSendingResponse;
  70. }
  71. public bool StopMonitoring;
  72. /// <summary>
  73. /// Context have been started (a new client have connected)
  74. /// </summary>
  75. public event EventHandler Started;
  76. /// <summary>
  77. /// Initializes a new instance of the <see cref="HttpClientContext"/> class.
  78. /// </summary>
  79. /// <param name="secured">true if the connection is secured (SSL/TLS)</param>
  80. /// <param name="remoteEndPoint">client that connected.</param>
  81. /// <param name="stream">Stream used for communication</param>
  82. /// <param name="parserFactory">Used to create a <see cref="IHttpRequestParser"/>.</param>
  83. /// <param name="bufferSize">Size of buffer to use when reading data. Must be at least 4096 bytes.</param>
  84. /// <exception cref="SocketException">If <see cref="Socket.BeginReceive(byte[],int,int,SocketFlags,AsyncCallback,object)"/> fails</exception>
  85. /// <exception cref="ArgumentException">Stream must be writable and readable.</exception>
  86. public HttpClientContext(bool secured, IPEndPoint remoteEndPoint,
  87. Stream stream, IRequestParserFactory parserFactory, Socket sock)
  88. {
  89. if (!stream.CanWrite || !stream.CanRead)
  90. throw new ArgumentException("Stream must be writable and readable.");
  91. RemoteAddress = remoteEndPoint.Address.ToString();
  92. RemotePort = remoteEndPoint.Port.ToString();
  93. _log = NullLogWriter.Instance;
  94. m_parser = parserFactory.CreateParser(_log);
  95. m_parser.RequestCompleted += OnRequestCompleted;
  96. m_parser.RequestLineReceived += OnRequestLine;
  97. m_parser.HeaderReceived += OnHeaderReceived;
  98. m_parser.BodyBytesReceived += OnBodyBytesReceived;
  99. m_currentRequest = new HttpRequest(this);
  100. IsSecured = secured;
  101. _stream = stream;
  102. m_sock = sock;
  103. m_bufferSize = 8196;
  104. m_ReceiveBuffer = new byte[m_bufferSize];
  105. requestsInServiceIDs = new HashSet<uint>();
  106. SSLCommonName = "";
  107. if (secured)
  108. {
  109. SslStream _ssl = (SslStream)_stream;
  110. X509Certificate _cert1 = _ssl.RemoteCertificate;
  111. if (_cert1 != null)
  112. {
  113. X509Certificate2 _cert2 = new X509Certificate2(_cert1);
  114. if (_cert2 != null)
  115. SSLCommonName = _cert2.GetNameInfo(X509NameType.SimpleName, false);
  116. }
  117. }
  118. ++basecontextID;
  119. if (basecontextID <= 0)
  120. basecontextID = 1;
  121. contextID = basecontextID;
  122. }
  123. public bool CanSend()
  124. {
  125. if (contextID < 0)
  126. return false;
  127. if (Stream == null || m_sock == null || !m_sock.Connected)
  128. return false;
  129. return true;
  130. }
  131. /// <summary>
  132. /// Process incoming body bytes.
  133. /// </summary>
  134. /// <param name="sender"><see cref="IHttpRequestParser"/></param>
  135. /// <param name="e">Bytes</param>
  136. protected virtual void OnBodyBytesReceived(object sender, BodyEventArgs e)
  137. {
  138. m_currentRequest.AddToBody(e.Buffer, e.Offset, e.Count);
  139. }
  140. /// <summary>
  141. ///
  142. /// </summary>
  143. /// <param name="sender"></param>
  144. /// <param name="e"></param>
  145. protected virtual void OnHeaderReceived(object sender, HeaderEventArgs e)
  146. {
  147. if (string.Compare(e.Name, "expect", true) == 0 && e.Value.Contains("100-continue"))
  148. {
  149. lock (requestsInServiceIDs)
  150. {
  151. if (requestsInServiceIDs.Count == 0)
  152. Respond("HTTP/1.1", HttpStatusCode.Continue, "Please continue.");
  153. }
  154. }
  155. m_currentRequest.AddHeader(e.Name, e.Value);
  156. }
  157. private void OnRequestLine(object sender, RequestLineEventArgs e)
  158. {
  159. m_currentRequest.Method = e.HttpMethod;
  160. m_currentRequest.HttpVersion = e.HttpVersion;
  161. m_currentRequest.UriPath = e.UriPath;
  162. m_currentRequest.AddHeader("remote_addr", RemoteAddress);
  163. m_currentRequest.AddHeader("remote_port", RemotePort);
  164. FirstRequestLineReceived = true;
  165. TriggerKeepalive = false;
  166. MonitorKeepaliveMS = 0;
  167. }
  168. /// <summary>
  169. /// Start reading content.
  170. /// </summary>
  171. /// <remarks>
  172. /// Make sure to call base.Start() if you override this method.
  173. /// </remarks>
  174. public virtual void Start()
  175. {
  176. ReceiveLoop();
  177. Started?.Invoke(this, EventArgs.Empty);
  178. }
  179. /// <summary>
  180. /// Clean up context.
  181. /// </summary>
  182. /// <remarks>
  183. /// </remarks>
  184. public virtual void Cleanup()
  185. {
  186. if (StreamPassedOff)
  187. return;
  188. if (Stream != null)
  189. {
  190. Stream.Close();
  191. Stream = null;
  192. m_sock = null;
  193. }
  194. m_currentRequest?.Clear();
  195. m_currentRequest = null;
  196. m_currentResponse?.Clear();
  197. m_currentResponse = null;
  198. requestsInServiceIDs.Clear();
  199. FirstRequestLineReceived = false;
  200. FullRequestReceived = false;
  201. FullRequestProcessed = false;
  202. MonitorStartMS = 0;
  203. StopMonitoring = true;
  204. MonitorKeepaliveMS = 0;
  205. TriggerKeepalive = false;
  206. isSendingResponse = false;
  207. m_ReceiveBytesLeft = 0;
  208. contextID = -100;
  209. m_parser.Clear();
  210. }
  211. public void Close()
  212. {
  213. Dispose();
  214. }
  215. /// <summary>
  216. /// Using SSL or other encryption method.
  217. /// </summary>
  218. [Obsolete("Use IsSecured instead.")]
  219. public bool Secured
  220. {
  221. get { return IsSecured; }
  222. }
  223. /// <summary>
  224. /// Using SSL or other encryption method.
  225. /// </summary>
  226. public bool IsSecured { get; internal set; }
  227. // returns the SSL commonName of remote Certificate
  228. public string SSLCommonName { get; internal set; }
  229. /// <summary>
  230. /// Specify which logger to use.
  231. /// </summary>
  232. public ILogWriter LogWriter
  233. {
  234. get { return _log; }
  235. set
  236. {
  237. _log = value ?? NullLogWriter.Instance;
  238. m_parser.LogWriter = _log;
  239. }
  240. }
  241. private Stream _stream;
  242. /// <summary>
  243. /// Gets or sets the network stream.
  244. /// </summary>
  245. internal Stream Stream
  246. {
  247. get { return _stream; }
  248. set { _stream = value; }
  249. }
  250. /// <summary>
  251. /// Gets or sets IP address that the client connected from.
  252. /// </summary>
  253. internal string RemoteAddress { get; set; }
  254. /// <summary>
  255. /// Gets or sets port that the client connected from.
  256. /// </summary>
  257. internal string RemotePort { get; set; }
  258. /// <summary>
  259. /// Disconnect from client
  260. /// </summary>
  261. /// <param name="error">error to report in the <see cref="Disconnected"/> event.</param>
  262. public void Disconnect(SocketError error)
  263. {
  264. // disconnect may not throw any exceptions
  265. try
  266. {
  267. try
  268. {
  269. if (Stream != null)
  270. {
  271. if (error == SocketError.Success)
  272. Stream.Flush();
  273. Stream.Close();
  274. Stream = null;
  275. }
  276. m_sock = null;
  277. }
  278. catch { }
  279. Disconnected?.Invoke(this, new DisconnectedEventArgs(error));
  280. }
  281. catch (Exception err)
  282. {
  283. LogWriter.Write(this, LogPrio.Error, "Disconnect threw an exception: " + err);
  284. }
  285. }
  286. private void OnReceive(IAsyncResult ar)
  287. {
  288. try
  289. {
  290. int bytesRead = 0;
  291. if (Stream == null)
  292. return;
  293. try
  294. {
  295. bytesRead = Stream.EndRead(ar);
  296. }
  297. catch (NullReferenceException)
  298. {
  299. Disconnect(SocketError.ConnectionReset);
  300. return;
  301. }
  302. if (bytesRead == 0)
  303. {
  304. Disconnect(SocketError.ConnectionReset);
  305. return;
  306. }
  307. m_ReceiveBytesLeft += bytesRead;
  308. if (m_ReceiveBytesLeft > m_ReceiveBuffer.Length)
  309. {
  310. throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
  311. }
  312. int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
  313. if (Stream == null)
  314. return; // "Connection: Close" in effect.
  315. // try again to see if we can parse another message (check parser to see if it is looking for a new message)
  316. int nextOffset;
  317. int nextBytesleft = m_ReceiveBytesLeft - offset;
  318. while (offset != 0 && nextBytesleft > 0)
  319. {
  320. nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
  321. if (Stream == null)
  322. return; // "Connection: Close" in effect.
  323. if (nextOffset == 0)
  324. break;
  325. offset = nextOffset;
  326. nextBytesleft = m_ReceiveBytesLeft - offset;
  327. }
  328. // copy unused bytes to the beginning of the array
  329. if (offset > 0 && m_ReceiveBytesLeft > offset)
  330. Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
  331. m_ReceiveBytesLeft -= offset;
  332. if (Stream != null && Stream.CanRead)
  333. {
  334. if (!StreamPassedOff)
  335. Stream.BeginRead(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft, OnReceive, null);
  336. else
  337. {
  338. _log.Write(this, LogPrio.Warning, "Could not read any more from the socket.");
  339. Disconnect(SocketError.Success);
  340. }
  341. }
  342. }
  343. catch (BadRequestException err)
  344. {
  345. LogWriter.Write(this, LogPrio.Warning, "Bad request, responding with it. Error: " + err);
  346. try
  347. {
  348. Respond("HTTP/1.0", HttpStatusCode.BadRequest, err.Message);
  349. }
  350. catch (Exception err2)
  351. {
  352. LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
  353. }
  354. Disconnect(SocketError.NoRecovery);
  355. }
  356. catch (IOException err)
  357. {
  358. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
  359. if (err.InnerException is SocketException)
  360. Disconnect((SocketError)((SocketException)err.InnerException).ErrorCode);
  361. else
  362. Disconnect(SocketError.ConnectionReset);
  363. }
  364. catch (ObjectDisposedException err)
  365. {
  366. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : " + err.Message);
  367. Disconnect(SocketError.NotSocket);
  368. }
  369. catch (NullReferenceException err)
  370. {
  371. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : NullRef: " + err.Message);
  372. Disconnect(SocketError.NoRecovery);
  373. }
  374. catch (Exception err)
  375. {
  376. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
  377. Disconnect(SocketError.NoRecovery);
  378. }
  379. }
  380. private async void ReceiveLoop()
  381. {
  382. m_ReceiveBytesLeft = 0;
  383. try
  384. {
  385. while(true)
  386. {
  387. if (_stream == null || !_stream.CanRead)
  388. return;
  389. int bytesRead = await _stream.ReadAsync(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft).ConfigureAwait(false);
  390. if (bytesRead == 0)
  391. {
  392. Disconnect(SocketError.ConnectionReset);
  393. return;
  394. }
  395. m_ReceiveBytesLeft += bytesRead;
  396. if (m_ReceiveBytesLeft > m_ReceiveBuffer.Length)
  397. throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
  398. int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
  399. if (Stream == null)
  400. return; // "Connection: Close" in effect.
  401. // try again to see if we can parse another message (check parser to see if it is looking for a new message)
  402. int nextOffset;
  403. int nextBytesleft = m_ReceiveBytesLeft - offset;
  404. while (offset != 0 && nextBytesleft > 0)
  405. {
  406. nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
  407. if (Stream == null)
  408. return; // "Connection: Close" in effect.
  409. if (nextOffset == 0)
  410. break;
  411. offset = nextOffset;
  412. nextBytesleft = m_ReceiveBytesLeft - offset;
  413. }
  414. // copy unused bytes to the beginning of the array
  415. if (offset > 0 && m_ReceiveBytesLeft > offset)
  416. Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
  417. m_ReceiveBytesLeft -= offset;
  418. if (StreamPassedOff)
  419. return; //?
  420. }
  421. }
  422. catch (BadRequestException err)
  423. {
  424. LogWriter.Write(this, LogPrio.Warning, "Bad request, responding with it. Error: " + err);
  425. try
  426. {
  427. Respond("HTTP/1.0", HttpStatusCode.BadRequest, err.Message);
  428. }
  429. catch (Exception err2)
  430. {
  431. LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
  432. }
  433. Disconnect(SocketError.NoRecovery);
  434. }
  435. catch (IOException err)
  436. {
  437. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
  438. if (err.InnerException is SocketException)
  439. Disconnect((SocketError)((SocketException)err.InnerException).ErrorCode);
  440. else
  441. Disconnect(SocketError.ConnectionReset);
  442. }
  443. catch (ObjectDisposedException err)
  444. {
  445. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : " + err.Message);
  446. Disconnect(SocketError.NotSocket);
  447. }
  448. catch (NullReferenceException err)
  449. {
  450. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : NullRef: " + err.Message);
  451. Disconnect(SocketError.NoRecovery);
  452. }
  453. catch (Exception err)
  454. {
  455. LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
  456. Disconnect(SocketError.NoRecovery);
  457. }
  458. }
  459. private void OnRequestCompleted(object source, EventArgs args)
  460. {
  461. TriggerKeepalive = false;
  462. MonitorKeepaliveMS = 0;
  463. // load cookies if they exist
  464. RequestCookies cookies = m_currentRequest.Headers["cookie"] != null
  465. ? new RequestCookies(m_currentRequest.Headers["cookie"]) : new RequestCookies(String.Empty);
  466. m_currentRequest.SetCookies(cookies);
  467. m_currentRequest.Body.Seek(0, SeekOrigin.Begin);
  468. FullRequestReceived = true;
  469. int nreqs;
  470. lock (requestsInServiceIDs)
  471. {
  472. nreqs = requestsInServiceIDs.Count;
  473. requestsInServiceIDs.Add(m_currentRequest.ID);
  474. if (m_maxRequests > 0)
  475. m_maxRequests--;
  476. }
  477. // for now pipeline requests need to be serialized by opensim
  478. RequestReceived(this, new RequestEventArgs(m_currentRequest));
  479. m_currentRequest = new HttpRequest(this);
  480. int nreqsnow;
  481. lock (requestsInServiceIDs)
  482. {
  483. nreqsnow = requestsInServiceIDs.Count;
  484. }
  485. if (nreqs != nreqsnow)
  486. {
  487. // request was not done by us
  488. }
  489. }
  490. public void ReqResponseAboutToSend(uint requestID)
  491. {
  492. isSendingResponse = true;
  493. }
  494. public void StartSendResponse(HttpResponse response)
  495. {
  496. isSendingResponse = true;
  497. m_currentResponse = response;
  498. ContextTimeoutManager.EnqueueSend(this, response.Priority);
  499. }
  500. public bool TrySendResponse(int bytesLimit)
  501. {
  502. if(m_currentResponse == null)
  503. return false;
  504. if (m_currentResponse.Sent)
  505. return false;
  506. if(!CanSend())
  507. return false;
  508. m_currentResponse?.SendNextAsync(bytesLimit);
  509. return false;
  510. }
  511. public void ContinueSendResponse()
  512. {
  513. if(m_currentResponse == null)
  514. return;
  515. ContextTimeoutManager.EnqueueSend(this, m_currentResponse.Priority);
  516. }
  517. public void ReqResponseSent(uint requestID, ConnectionType ctype)
  518. {
  519. isSendingResponse = false;
  520. m_currentResponse?.Clear();
  521. m_currentResponse = null;
  522. bool doclose = ctype == ConnectionType.Close;
  523. lock (requestsInServiceIDs)
  524. {
  525. requestsInServiceIDs.Remove(requestID);
  526. // doclose = doclose && requestsInServiceIDs.Count == 0;
  527. if (requestsInServiceIDs.Count > 1)
  528. {
  529. }
  530. }
  531. if (doclose)
  532. Disconnect(SocketError.Success);
  533. else
  534. {
  535. lock (requestsInServiceIDs)
  536. {
  537. if (requestsInServiceIDs.Count == 0)
  538. TriggerKeepalive = true;
  539. }
  540. }
  541. }
  542. /// <summary>
  543. /// Send a response.
  544. /// </summary>
  545. /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
  546. /// <param name="statusCode">HTTP status code</param>
  547. /// <param name="reason">reason for the status code.</param>
  548. /// <param name="body">HTML body contents, can be null or empty.</param>
  549. /// <param name="contentType">A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty</param>
  550. /// <exception cref="ArgumentException">If <paramref name="httpVersion"/> is invalid.</exception>
  551. public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType)
  552. {
  553. if (string.IsNullOrEmpty(contentType))
  554. contentType = "text/html";
  555. if (string.IsNullOrEmpty(reason))
  556. reason = statusCode.ToString();
  557. string response = string.IsNullOrEmpty(body)
  558. ? httpVersion + " " + (int)statusCode + " " + reason + "\r\n\r\n"
  559. : string.Format("{0} {1} {2}\r\nContent-Type: {5}\r\nContent-Length: {3}\r\n\r\n{4}",
  560. httpVersion, (int)statusCode, reason ?? statusCode.ToString(),
  561. body.Length, body, contentType);
  562. byte[] buffer = Encoding.ASCII.GetBytes(response);
  563. Send(buffer);
  564. if (m_currentRequest.Connection == ConnectionType.Close)
  565. FullRequestProcessed = true;
  566. }
  567. /// <summary>
  568. /// Send a response.
  569. /// </summary>
  570. /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
  571. /// <param name="statusCode">HTTP status code</param>
  572. /// <param name="reason">reason for the status code.</param>
  573. public void Respond(string httpVersion, HttpStatusCode statusCode, string reason)
  574. {
  575. Respond(httpVersion, statusCode, reason, null, null);
  576. }
  577. /// <summary>
  578. /// send a whole buffer
  579. /// </summary>
  580. /// <param name="buffer">buffer to send</param>
  581. /// <exception cref="ArgumentNullException"></exception>
  582. public bool Send(byte[] buffer)
  583. {
  584. if (buffer == null)
  585. throw new ArgumentNullException("buffer");
  586. return Send(buffer, 0, buffer.Length);
  587. }
  588. /// <summary>
  589. /// Send data using the stream
  590. /// </summary>
  591. /// <param name="buffer">Contains data to send</param>
  592. /// <param name="offset">Start position in buffer</param>
  593. /// <param name="size">number of bytes to send</param>
  594. /// <exception cref="ArgumentNullException"></exception>
  595. /// <exception cref="ArgumentOutOfRangeException"></exception>
  596. private object sendLock = new object();
  597. public bool Send(byte[] buffer, int offset, int size)
  598. {
  599. if (Stream == null || m_sock == null || !m_sock.Connected)
  600. return false;
  601. if (offset + size > buffer.Length)
  602. throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer.");
  603. bool ok = true;
  604. lock (sendLock) // can't have overlaps here
  605. {
  606. try
  607. {
  608. Stream.Write(buffer, offset, size);
  609. }
  610. catch
  611. {
  612. ok = false;
  613. }
  614. if (!ok && Stream != null)
  615. Disconnect(SocketError.NoRecovery);
  616. return ok;
  617. }
  618. }
  619. public async Task<bool> SendAsync(byte[] buffer, int offset, int size)
  620. {
  621. if (Stream == null || m_sock == null || !m_sock.Connected)
  622. return false;
  623. if (offset + size > buffer.Length)
  624. throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer.");
  625. bool ok = true;
  626. ContextTimeoutManager.ContextEnterActiveSend();
  627. try
  628. {
  629. await Stream.WriteAsync(buffer, offset, size).ConfigureAwait(false);
  630. }
  631. catch
  632. {
  633. ok = false;
  634. }
  635. ContextTimeoutManager.ContextLeaveActiveSend();
  636. if (!ok && Stream != null)
  637. Disconnect(SocketError.NoRecovery);
  638. return ok;
  639. }
  640. /// <summary>
  641. /// The context have been disconnected.
  642. /// </summary>
  643. /// <remarks>
  644. /// Event can be used to clean up a context, or to reuse it.
  645. /// </remarks>
  646. public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
  647. /// <summary>
  648. /// A request have been received in the context.
  649. /// </summary>
  650. public event EventHandler<RequestEventArgs> RequestReceived = delegate { };
  651. public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
  652. {
  653. StreamPassedOff = true;
  654. m_parser.RequestCompleted -= OnRequestCompleted;
  655. m_parser.RequestLineReceived -= OnRequestLine;
  656. m_parser.HeaderReceived -= OnHeaderReceived;
  657. m_parser.BodyBytesReceived -= OnBodyBytesReceived;
  658. m_parser.Clear();
  659. return new HTTPNetworkContext() { Socket = m_sock, Stream = _stream as NetworkStream };
  660. }
  661. public void Dispose()
  662. {
  663. Dispose(true);
  664. GC.SuppressFinalize(this);
  665. }
  666. public void Dispose(bool disposing)
  667. {
  668. if (contextID >= 0)
  669. {
  670. StreamPassedOff = false;
  671. Cleanup();
  672. }
  673. }
  674. }
  675. }