diff --git a/src/Fleck/QueuedStream.cs b/src/Fleck/QueuedStream.cs index 0c8240d..70ac2e5 100644 --- a/src/Fleck/QueuedStream.cs +++ b/src/Fleck/QueuedStream.cs @@ -137,47 +137,89 @@ protected override void Dispose(bool disposing) IAsyncResult BeginWriteInternal(byte[] buffer, int offset, int count, AsyncCallback callback, object state, WriteData queued) { _pendingWrite++; + var result = _stream.BeginWrite(buffer, offset, count, ar => { - // callback can be executed even before return value of BeginWriteInternal is set to this property - queued.AsyncResult.ActualResult = ar; - try + // if the operation completes in async mode, the callback will manage it + if (!ar.CompletedSynchronously) { - // so that we can call BeginWrite again - _stream.EndWrite(ar); - } - catch (Exception exc) - { - queued.AsyncResult.Exception = exc; + ProcessComplete(ar, queued, callback); } + }, state); + + queued.AsyncResult.ActualResult = result; + + // if it completes in not async mode, we could manage it on the current thread + if (result.CompletedSynchronously) + { + ProcessComplete(result, queued, callback); + } + + return queued.AsyncResult; + } + + // this Method avoid the recursion of the stack + private void ProcessComplete(IAsyncResult ar, WriteData queued, AsyncCallback callback) + { + queued.AsyncResult.ActualResult = ar; + try + { + _stream.EndWrite(ar); + } + catch (Exception exc) + { + queued.AsyncResult.Exception = exc; + } - // one down, another is good to go - lock (_queue) + lock (_queue) + { + _pendingWrite--; + + while (_queue.Count > 0) { - _pendingWrite--; - while (_queue.Count > 0) + var nextData = _queue.Dequeue(); + _pendingWrite++; + + try { - var data = _queue.Dequeue(); - try + var nextResult = _stream.BeginWrite(nextData.Buffer, nextData.Offset, nextData.Count, nextAr => + { + if (!nextAr.CompletedSynchronously) + { + ProcessComplete(nextAr, nextData, nextData.Callback); + } + }, nextData.State); + + nextData.AsyncResult.ActualResult = nextResult; + + // if it is async, the runtine will exit from the loop + if (!nextResult.CompletedSynchronously) { - data.AsyncResult.ActualResult = BeginWriteInternal(data.Buffer, data.Offset, data.Count, data.Callback, data.State, data); break; } + + // if it is not async, not recursion + try + { + _stream.EndWrite(nextResult); + } catch (Exception exc) { - _pendingWrite--; - data.AsyncResult.Exception = exc; - data.Callback(data.AsyncResult); + nextData.AsyncResult.Exception = exc; } + _pendingWrite--; + nextData.Callback(nextData.AsyncResult); + } + catch (Exception exc) + { + _pendingWrite--; + nextData.AsyncResult.Exception = exc; + nextData.Callback(nextData.AsyncResult); } - callback(queued.AsyncResult); } - }, state); - - // always return the wrapped async result. - // this is especially important if the underlying stream completed the operation synchronously (hence "result.CompletedSynchronously" is true!) - queued.AsyncResult.ActualResult = result; - return queued.AsyncResult; + + callback(queued.AsyncResult); + } } #region Nested type: WriteData @@ -242,4 +284,4 @@ public bool IsCompleted #endregion } -} \ No newline at end of file +} diff --git a/src/Fleck/RequestParser.cs b/src/Fleck/RequestParser.cs index b614c0d..210957e 100644 --- a/src/Fleck/RequestParser.cs +++ b/src/Fleck/RequestParser.cs @@ -1,3 +1,4 @@ +using System; using System.Text; using System.Text.RegularExpressions; @@ -5,14 +6,18 @@ namespace Fleck { public class RequestParser { - const string pattern = @"^(?[^\s]+)\s(?[^\s]+)\sHTTP\/1\.1\r\n" + // request line - @"((?[^:\r\n]+):(?([^\r\n])\s)*(?[^\r\n]*)\r\n)+" + //headers - @"\r\n" + //newline - @"(?.+)?"; - const string FlashSocketPolicyRequestPattern = @"^[<]policy-file-request\s*[/][>]"; + // Pattern without body + const string pattern = @"^(?[^\s]+)\s(?[^\s]+)\sHTTP\/1\.1\r\n" + + @"((?[^:\r\n]+):\s*(?[^\r\n]*)\r\n)+"; - private static readonly Regex _regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex _FlashSocketPolicyRequestRegex = new Regex(FlashSocketPolicyRequestPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + // Timeout setted + private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(100); + + // Added timeout + private static readonly Regex _regex = new Regex( + pattern, + RegexOptions.IgnoreCase | RegexOptions.Compiled, + RegexTimeout); public static WebSocketHttpRequest Parse(byte[] bytes) { @@ -21,49 +26,43 @@ public static WebSocketHttpRequest Parse(byte[] bytes) public static WebSocketHttpRequest Parse(byte[] bytes, string scheme) { - // Check for websocket request header - var body = Encoding.UTF8.GetString(bytes); - Match match = _regex.Match(body); - - if (!match.Success) + // Try Catch for timeout + try { - // No websocket request header found, check for a flash socket policy request - match = _FlashSocketPolicyRequestRegex.Match(body); - if (match.Success) + var text = Encoding.UTF8.GetString(bytes); + Match match = _regex.Match(text); + + if (!match.Success) + return null; + + var request = new WebSocketHttpRequest { - // It's a flash socket policy request, so return - return new WebSocketHttpRequest - { - Body = body, - Bytes = bytes - }; - } - else + Method = match.Groups["method"].Value, + Path = match.Groups["path"].Value, + Bytes = bytes, + Scheme = scheme + }; + + var fields = match.Groups["field_name"].Captures; + var values = match.Groups["field_value"].Captures; + for (var i = 0; i < fields.Count; i++) { - return null; + request.Headers[fields[i].Value] = values[i].Value; } - } - var request = new WebSocketHttpRequest - { - Method = match.Groups["method"].Value, - Path = match.Groups["path"].Value, - Body = match.Groups["body"].Value, - Bytes = bytes, - Scheme = scheme - }; + // Added the Body extraction + int headerEndIndex = text.IndexOf("\r\n\r\n", StringComparison.Ordinal); + if (headerEndIndex != -1 && text.Length > headerEndIndex + 4) + { + request.Body = text.Substring(headerEndIndex + 4); + } - var fields = match.Groups["field_name"].Captures; - var values = match.Groups["field_value"].Captures; - for (var i = 0; i < fields.Count; i++) + return request; + } + catch (RegexMatchTimeoutException) { - var name = fields[i].ToString(); - var value = values[i].ToString(); - request.Headers[name] = value; + return null; } - - return request; } } } -