Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 69 additions & 27 deletions src/Fleck/QueuedStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -242,4 +284,4 @@ public bool IsCompleted

#endregion
}
}
}
83 changes: 41 additions & 42 deletions src/Fleck/RequestParser.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
using System;
using System.Text;
using System.Text.RegularExpressions;

namespace Fleck
{
public class RequestParser
{
const string pattern = @"^(?<method>[^\s]+)\s(?<path>[^\s]+)\sHTTP\/1\.1\r\n" + // request line
@"((?<field_name>[^:\r\n]+):(?([^\r\n])\s)*(?<field_value>[^\r\n]*)\r\n)+" + //headers
@"\r\n" + //newline
@"(?<body>.+)?";
const string FlashSocketPolicyRequestPattern = @"^[<]policy-file-request\s*[/][>]";
// Pattern without body
const string pattern = @"^(?<method>[^\s]+)\s(?<path>[^\s]+)\sHTTP\/1\.1\r\n" +
@"((?<field_name>[^:\r\n]+):\s*(?<field_value>[^\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)
{
Expand All @@ -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;
}
}
}