diff --git a/NEventSocket.Tests/Sockets/MessageParsingTests.cs b/NEventSocket.Tests/Sockets/MessageParsingTests.cs
index 13cd8ed..3abcb0b 100644
--- a/NEventSocket.Tests/Sockets/MessageParsingTests.cs
+++ b/NEventSocket.Tests/Sockets/MessageParsingTests.cs
@@ -1,24 +1,41 @@
using System;
+using System.Text;
using System.Collections.Generic;
using System.Reactive.Linq;
using NEventSocket.FreeSwitch;
+using NEventSocket.Logging;
using NEventSocket.Sockets;
using NEventSocket.Tests.Properties;
using NEventSocket.Tests.TestSupport;
using NEventSocket.Util;
using Xunit;
+using Microsoft.Extensions.Logging;
+
namespace NEventSocket.Tests.Sockets
{
public class MessageParsingTests
{
- [Theory, MemberData(nameof(ExampleMessages))]
+ public MessageParsingTests()
+ {
+ PreventThreadPoolStarvation.Init();
+ Logger.Configure(LoggerFactory.Create(builder =>
+ {
+ builder
+ .AddFilter("Microsoft", LogLevel.Warning)
+ .AddFilter("System", LogLevel.Warning)
+ .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
+ .AddConsole();
+ }));
+ }
+
+[Theory, MemberData(nameof(ExampleMessages))]
public void it_should_parse_the_expected_messages_from_a_stream(int expectedMessageCount, string exampleInput)
{
int parsedMessageCount = 0;
-
- exampleInput.ToObservable()
- .AggregateUntil(() => new Parser(), (builder, ch) => builder.Append(ch), builder => builder.Completed)
+ byte[] exampleByteInput = Encoding.UTF8.GetBytes(exampleInput);
+ exampleByteInput.ToObservable()
+ .AggregateUntil(() => new Parser(), (builder, b) => builder.Append(b), builder => builder.Completed)
.Select(parser => parser.ExtractMessage())
.Subscribe(_ => parsedMessageCount++);
@@ -31,14 +48,17 @@ public void it_should_parse_the_expected_messages_from_a_stream(int expectedMess
[InlineData(TestMessages.ConnectEvent)]
[InlineData(TestMessages.DisconnectEvent)]
[InlineData(TestMessages.PlaybackComplete)]
+ [InlineData(TestMessages.DetectedSpeech)]
+ [InlineData(TestMessages.DetectedSpeechEnglish)]
public void can_parse_test_messages(string input)
{
var parser = new Parser();
var rawInput = input.Replace("\r\n", "\n") + "\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -51,13 +71,17 @@ public void can_parse_test_messages(string input)
[Theory]
[InlineData(TestMessages.BackgroundJob)]
[InlineData(TestMessages.CallState)]
+ [InlineData(TestMessages.DetectedSpeech)]
+ [InlineData(TestMessages.DetectedSpeechEnglish)]
public void it_should_extract_the_body_from_a_message(string input)
{
var parser = new Parser();
var rawInput = input.Replace("\r\n", "\n") + "\n\n";
- foreach (char c in rawInput)
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
+
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -65,7 +89,7 @@ public void it_should_extract_the_body_from_a_message(string input)
BasicMessage payload = parser.ExtractMessage();
Assert.Equal(ContentTypes.EventPlain, payload.ContentType);
Assert.NotNull(payload.BodyText);
- Assert.Equal(payload.ContentLength, payload.BodyText.Length);
+ Assert.Equal(payload.ContentLength, payload.BodyBytes.Length);
Console.WriteLine(payload.ToString());
}
@@ -73,13 +97,17 @@ public void it_should_extract_the_body_from_a_message(string input)
[Theory]
[InlineData(TestMessages.BackgroundJob, EventName.BackgroundJob)]
[InlineData(TestMessages.CallState, EventName.ChannelCallstate)]
+ [InlineData(TestMessages.DetectedSpeech, EventName.DetectedSpeech)]
+ [InlineData(TestMessages.DetectedSpeechEnglish, EventName.DetectedSpeech)]
public void it_should_parse_event_messages(string input, EventName eventName)
{
var parser = new Parser();
var rawInput = input.Replace("\r\n", "\n") + "\n\n";
- foreach (char c in rawInput)
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
+
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -91,16 +119,44 @@ public void it_should_parse_event_messages(string input, EventName eventName)
Console.WriteLine(eventMessage.ToString());
}
+ [Theory]
+ [InlineData(TestMessages.DetectedSpeech, EventName.DetectedSpeech)]
+ [InlineData(TestMessages.DetectedSpeechEnglish, EventName.DetectedSpeech)]
+ public void it_should_parse_event_messages_and_extract_body_payload(string input, EventName eventName)
+ {
+ var parser = new Parser();
+ var rawInput = input.Replace("\r\n", "\n") + "\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
+
+ foreach (byte b in byteInput)
+ {
+ parser.Append(b);
+ }
+
+ Assert.True(parser.Completed);
+
+ var eventMessage = new EventMessage(parser.ExtractMessage());
+ Assert.NotNull(eventMessage);
+ Assert.Equal(eventName, eventMessage.EventName);
+
+ var contentLength = int.Parse(eventMessage.Headers[HeaderNames.ContentLength]);
+
+ Assert.Equal(contentLength, Encoding.UTF8.GetByteCount(eventMessage.BodyText));
+
+ Console.WriteLine(eventMessage.ToString());
+ }
+
[Fact]
public void it_should_parse_BackgroundJobResult_OK()
{
var input = TestMessages.BackgroundJob;
var parser = new Parser();
var rawInput = input.Replace("\r\n", "\n") + "\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -118,10 +174,11 @@ public void it_should_parse_BackgroundJobResult_ERR()
var input = TestMessages.BackgroundJobError;
var parser = new Parser();
var rawInput = input.Replace("\r\n", "\n") + "\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -139,10 +196,11 @@ public void it_should_parse_Command_Reply_OK()
{
var parser = new Parser();
var rawInput = "Content-Type: command/reply\nReply-Text: +OK\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -159,10 +217,11 @@ public void it_should_parse_Command_Reply_ERR()
{
var parser = new Parser();
var rawInput = "Content-Type: command/reply\nReply-Text: -ERR Error\n\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -180,10 +239,11 @@ public void it_should_parse_Api_Response_OK()
{
var parser = new Parser();
var rawInput = "Content-Type: api/response\nContent-Length: 3\n\n+OK";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -200,10 +260,11 @@ public void it_should_parse_Api_Response_ERR()
{
var parser = new Parser();
var rawInput = "Content-Type: api/response\nContent-Length: 10\n\n-ERR Error";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -221,10 +282,11 @@ public void it_should_treat_Api_Response_ERR_no_reply_as_Success()
{
var parser = new Parser();
var rawInput = "Content-Type: api/response\nContent-Length: 13\n\n-ERR no reply";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -242,10 +304,11 @@ public void it_should_trim_new_lines_from__the_end_of_ApiResponse_Body_text()
{
var parser = new Parser();
var rawInput = "Content-Type: api/response\nContent-Length: 14\n\n-ERR no reply\n";
+ byte[] byteInput = Encoding.UTF8.GetBytes(rawInput);
- foreach (char c in rawInput)
+ foreach (byte b in byteInput)
{
- parser.Append(c);
+ parser.Append(b);
}
Assert.True(parser.Completed);
@@ -269,9 +332,10 @@ public void Can_parse_example_sessions_to_completion(string input)
}
bool gotDisconnectNotice = false;
+ byte[] byteInput = Encoding.UTF8.GetBytes(input);
- input.ToObservable()
- .AggregateUntil(() => new Parser(), (builder, ch) => builder.Append(ch), builder => builder.Completed)
+ byteInput.ToObservable()
+ .AggregateUntil(() => new Parser(), (builder, b) => builder.Append(b), builder => builder.Completed)
.Select(parser => parser.ExtractMessage())
.Subscribe(
m =>
@@ -297,8 +361,9 @@ public void Can_parse_disconnect_notice()
Disconnected, goodbye.
See you at ClueCon! http://www.cluecon.com/
";
- msg.ToObservable()
- .AggregateUntil(() => new Parser(), (builder, ch) => builder.Append(ch), builder => builder.Completed)
+ byte[] byteMsg = Encoding.UTF8.GetBytes(msg);
+ byteMsg.ToObservable()
+ .AggregateUntil(() => new Parser(), (builder, b) => builder.Append(b), builder => builder.Completed)
.Select(parser => parser.ExtractMessage())
.Subscribe(
Console.WriteLine);
diff --git a/NEventSocket.Tests/TestSupport/TestMessages.cs b/NEventSocket.Tests/TestSupport/TestMessages.cs
index 4d7981a..1c07f18 100644
--- a/NEventSocket.Tests/TestSupport/TestMessages.cs
+++ b/NEventSocket.Tests/TestSupport/TestMessages.cs
@@ -278,6 +278,155 @@ public class TestMessages
Disconnected, goodbye.
See you at ClueCon! http://www.cluecon.com/";
+ public const string DetectedSpeech = @"Content-Length: 2471
+Content-Type: text/event-plain
+
+Event-Name: DETECTED_SPEECH
+Core-UUID: ec530c4e-484d-473e-9bd0-073863f752eb
+FreeSWITCH-Hostname: ser
+FreeSWITCH-Switchname: ser
+FreeSWITCH-IPv4: 192.168.1.104
+FreeSWITCH-IPv6: %3A%3A1
+Event-Date-Local: 2021-06-28%2023%3A20%3A38
+Event-Date-GMT: Mon,%2028%20Jun%202021%2021%3A20%3A38%20GMT
+Event-Date-Timestamp: 1624915238513391
+Event-Calling-File: switch_ivr_async.c
+Event-Calling-Function: speech_thread
+Event-Calling-Line-Number: 4947
+Event-Sequence: 251969
+Speech-Type: detected-speech
+ASR-Completion-Cause: 0
+Channel-State: CS_EXECUTE
+Channel-Call-State: ACTIVE
+Channel-State-Number: 4
+Channel-Name: sofia/internal/%2B491734064561%40172.16.50.128
+Unique-ID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Call-Direction: inbound
+Presence-Call-Direction: inbound
+Channel-HIT-Dialplan: true
+Channel-Presence-ID: %2B491734064561%40172.16.50.128
+Channel-Call-UUID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Answer-State: answered
+Channel-Read-Codec-Name: G722
+Channel-Read-Codec-Rate: 16000
+Channel-Read-Codec-Bit-Rate: 64000
+Channel-Write-Codec-Name: G722
+Channel-Write-Codec-Rate: 16000
+Channel-Write-Codec-Bit-Rate: 64000
+Caller-Direction: inbound
+Caller-Logical-Direction: inbound
+Caller-Username: %2B491734000000
+Caller-Dialplan: XML
+Caller-Caller-ID-Name: %2B491734000000
+Caller-Caller-ID-Number: %2B491734000000
+Caller-Orig-Caller-ID-Name: %2B491734000000
+Caller-Orig-Caller-ID-Number: %2B491734000000
+Caller-Network-Addr: 10.1.10.100
+Caller-ANI: %2B491734000000
+Caller-Destination-Number: 493000000000000
+Caller-Unique-ID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Caller-Source: mod_sofia
+Caller-Context: public
+Caller-Channel-Name: sofia/internal/%2B491734064561%40172.16.50.128
+Caller-Profile-Index: 1
+Caller-Profile-Created-Time: 1624915212053386
+Caller-Channel-Created-Time: 1624915212053386
+Caller-Channel-Answered-Time: 1624915222293356
+Caller-Channel-Progress-Time: 0
+Caller-Channel-Progress-Media-Time: 1624915216013390
+Caller-Channel-Hangup-Time: 0
+Caller-Channel-Transfer-Time: 0
+Caller-Channel-Resurrect-Time: 0
+Caller-Channel-Bridged-Time: 0
+Caller-Channel-Last-Hold: 0
+Caller-Channel-Hold-Accum: 0
+Caller-Screen-Bit: true
+Caller-Privacy-Hide-Name: false
+Caller-Privacy-Hide-Number: false
+Content-Length: 261
+
+
+
+
+ Verlängerung Störung Bestätigung
+ Verlängerung Störung Bestätigung
+
+";
+
+ public const string DetectedSpeechEnglish = @"Content-Length: 2465
+Content-Type: text/event-plain
+
+Event-Name: DETECTED_SPEECH
+Core-UUID: ec530c4e-484d-473e-9bd0-073863f752eb
+FreeSWITCH-Hostname: ser
+FreeSWITCH-Switchname: ser
+FreeSWITCH-IPv4: 192.168.1.104
+FreeSWITCH-IPv6: %3A%3A1
+Event-Date-Local: 2021-06-28%2023%3A20%3A38
+Event-Date-GMT: Mon,%2028%20Jun%202021%2021%3A20%3A38%20GMT
+Event-Date-Timestamp: 1624915238513391
+Event-Calling-File: switch_ivr_async.c
+Event-Calling-Function: speech_thread
+Event-Calling-Line-Number: 4947
+Event-Sequence: 251969
+Speech-Type: detected-speech
+ASR-Completion-Cause: 0
+Channel-State: CS_EXECUTE
+Channel-Call-State: ACTIVE
+Channel-State-Number: 4
+Channel-Name: sofia/internal/%2B491734064561%40172.16.50.128
+Unique-ID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Call-Direction: inbound
+Presence-Call-Direction: inbound
+Channel-HIT-Dialplan: true
+Channel-Presence-ID: %2B491734064561%40172.16.50.128
+Channel-Call-UUID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Answer-State: answered
+Channel-Read-Codec-Name: G722
+Channel-Read-Codec-Rate: 16000
+Channel-Read-Codec-Bit-Rate: 64000
+Channel-Write-Codec-Name: G722
+Channel-Write-Codec-Rate: 16000
+Channel-Write-Codec-Bit-Rate: 64000
+Caller-Direction: inbound
+Caller-Logical-Direction: inbound
+Caller-Username: %2B491734000000
+Caller-Dialplan: XML
+Caller-Caller-ID-Name: %2B491734000000
+Caller-Caller-ID-Number: %2B491734000000
+Caller-Orig-Caller-ID-Name: %2B491734000000
+Caller-Orig-Caller-ID-Number: %2B491734000000
+Caller-Network-Addr: 10.1.10.100
+Caller-ANI: %2B491734000000
+Caller-Destination-Number: 493000000000000
+Caller-Unique-ID: f5228692-9715-4b6f-b54b-9e35be1a7335
+Caller-Source: mod_sofia
+Caller-Context: public
+Caller-Channel-Name: sofia/internal/%2B491734064561%40172.16.50.128
+Caller-Profile-Index: 1
+Caller-Profile-Created-Time: 1624915212053386
+Caller-Channel-Created-Time: 1624915212053386
+Caller-Channel-Answered-Time: 1624915222293356
+Caller-Channel-Progress-Time: 0
+Caller-Channel-Progress-Media-Time: 1624915216013390
+Caller-Channel-Hangup-Time: 0
+Caller-Channel-Transfer-Time: 0
+Caller-Channel-Resurrect-Time: 0
+Caller-Channel-Bridged-Time: 0
+Caller-Channel-Last-Hold: 0
+Caller-Channel-Hold-Accum: 0
+Caller-Screen-Bit: true
+Caller-Privacy-Hide-Name: false
+Caller-Privacy-Hide-Number: false
+Content-Length: 255
+
+
+
+
+ Extensions Problems Confirmation
+ Extensions Problems Confirmation
+
+";
public const string PlaybackComplete = @"Content-Length: 7209
Content-Type: text/event-plain
diff --git a/NEventSocket/FreeSwitch/BasicMessage.cs b/NEventSocket/FreeSwitch/BasicMessage.cs
index b985200..afa0e53 100644
--- a/NEventSocket/FreeSwitch/BasicMessage.cs
+++ b/NEventSocket/FreeSwitch/BasicMessage.cs
@@ -9,7 +9,7 @@ namespace NEventSocket.FreeSwitch
using System;
using System.Collections.Generic;
using System.Linq;
-
+ using System.Text;
using NEventSocket.Util;
using NEventSocket.Util.ObjectPooling;
@@ -25,10 +25,11 @@ internal BasicMessage(IDictionary headers)
Headers = new Dictionary(headers, StringComparer.OrdinalIgnoreCase);
}
- internal BasicMessage(IDictionary headers, string body)
+ internal BasicMessage(IDictionary headers, byte[] bodyBytes)
{
Headers = new Dictionary(headers, StringComparer.OrdinalIgnoreCase);
- BodyText = body;
+ BodyText = Encoding.UTF8.GetString(bodyBytes);
+ BodyBytes = bodyBytes;
}
///
@@ -48,6 +49,8 @@ protected BasicMessage()
///
public string BodyText { get; protected set; }
+ public byte[] BodyBytes { get; protected set; }
+
///
/// Gets the Content Type header.
///
diff --git a/NEventSocket/FreeSwitch/EventMessage.cs b/NEventSocket/FreeSwitch/EventMessage.cs
index 8ce0d0c..86d1b53 100644
--- a/NEventSocket/FreeSwitch/EventMessage.cs
+++ b/NEventSocket/FreeSwitch/EventMessage.cs
@@ -7,9 +7,10 @@
namespace NEventSocket.FreeSwitch
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-
+ using System.Text;
using Microsoft.Extensions.Logging;
using NEventSocket.Logging;
@@ -22,6 +23,7 @@ namespace NEventSocket.FreeSwitch
[Serializable]
public class EventMessage : BasicMessage
{
+ public static readonly byte[] delimiterBytes = Encoding.UTF8.GetBytes("\n\n");
private static readonly ILogger log = Logger.Get();
internal EventMessage(BasicMessage basicMessage)
@@ -60,8 +62,8 @@ internal EventMessage(BasicMessage basicMessage)
try
{
- var delimiterIndex = basicMessage.BodyText.IndexOf("\n\n", StringComparison.Ordinal);
- if (delimiterIndex == -1 || delimiterIndex == basicMessage.BodyText.Length - 2)
+ var delimiterIndex = PatternAt(basicMessage.BodyBytes, delimiterBytes);
+ if (delimiterIndex == -1 || delimiterIndex == basicMessage.BodyBytes.Length - 2)
{
// body text consists of key-value-pair event headers, no body
Headers = basicMessage.BodyText.ParseKeyValuePairs(": ");
@@ -71,13 +73,16 @@ internal EventMessage(BasicMessage basicMessage)
{
// ...but some Event Messages also carry a body payload, eg. a BACKGROUND_JOB event
// which is a message carried inside an EventMessage carried inside a BasicMessage..
- Headers = basicMessage.BodyText.Substring(0, delimiterIndex).ParseKeyValuePairs(": ");
+ string headersSection = Encoding.UTF8.GetString(basicMessage.BodyBytes, 0, delimiterIndex);
+ Headers = headersSection.ParseKeyValuePairs(": ");
Debug.Assert(Headers.ContainsKey(HeaderNames.ContentLength));
var contentLength = int.Parse(Headers[HeaderNames.ContentLength]);
- Debug.Assert(delimiterIndex + 2 + contentLength <= basicMessage.BodyText.Length, "Message cut off mid-transmission");
- var body = basicMessage.BodyText.Substring(delimiterIndex + 2, contentLength);
+ Debug.Assert(Headers.ContainsKey(HeaderNames.EventName));
+
+ Debug.Assert(delimiterIndex + 2 + contentLength <= basicMessage.BodyBytes.Length, "Message cut off mid-transmission");
+ var body = Encoding.UTF8.GetString(basicMessage.BodyBytes, delimiterIndex + 2, contentLength);
//remove any \n\n if any
var index = body.IndexOf("\n\n", StringComparison.Ordinal);
@@ -99,6 +104,18 @@ protected EventMessage()
{
}
+ private static int PatternAt(byte[] source, byte[] pattern)
+ {
+ for (int i = 0; i < source.Length; i++)
+ {
+ if (source.Skip(i).Take(pattern.Length).SequenceEqual(pattern))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
///
/// Gets the of this instance.
///
diff --git a/NEventSocket/NEventSocket.csproj b/NEventSocket/NEventSocket.csproj
index b850a7f..9e81c14 100644
--- a/NEventSocket/NEventSocket.csproj
+++ b/NEventSocket/NEventSocket.csproj
@@ -7,7 +7,7 @@
iamkinetic
NEventSocket.DotNetCore
.Net Core port of NEventSocket
- 2.0.2
+ 2.0.3
https://github.com/iamkinetic/NEventSocket
FreeSwitch NEventSocket DotNetCore
- .Net Standard 2.0 support.
diff --git a/NEventSocket/Sockets/EventSocket.cs b/NEventSocket/Sockets/EventSocket.cs
index 29c664a..47e5f6c 100644
--- a/NEventSocket/Sockets/EventSocket.cs
+++ b/NEventSocket/Sockets/EventSocket.cs
@@ -50,8 +50,8 @@ protected EventSocket(TcpClient tcpClient, TimeSpan? responseTimeOut = null) : b
ResponseTimeOut = responseTimeOut ?? TimeSpan.FromSeconds(5);
messages =
- Receiver.SelectMany(x => Encoding.UTF8.GetString(x))
- .AggregateUntil(() => new Parser(), (builder, ch) => builder.Append(ch), builder => builder.Completed)
+ Receiver.SelectMany(x => x)
+ .AggregateUntil(() => new Parser(), (builder, b) => builder.Append(b), builder => builder.Completed)
.Select(builder => builder.ExtractMessage())
.Do(
x => Log.LogTrace("Messages Received [{0}].".Fmt(x.ContentType)),
diff --git a/NEventSocket/Sockets/Parser.cs b/NEventSocket/Sockets/Parser.cs
index df85376..9584351 100644
--- a/NEventSocket/Sockets/Parser.cs
+++ b/NEventSocket/Sockets/Parser.cs
@@ -8,27 +8,28 @@ namespace NEventSocket.Sockets
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.IO;
using System.Text;
-
using NEventSocket.FreeSwitch;
using NEventSocket.Util;
- using NEventSocket.Util.ObjectPooling;
///
/// A parser for converting a stream of strings or chars into a stream of s from FreeSwitch.
///
public class Parser : IDisposable
{
- private StringBuilder buffer = StringBuilderPool.Allocate();
-
- private char previous;
+ private byte previous;
private int? contentLength;
private IDictionary headers;
+ private MemoryStream headerBytes = new MemoryStream();
+ private MemoryStream bodyBytes;
+
private readonly InterlockedBoolean disposed = new InterlockedBoolean();
+ private const byte headerEndDelimiter = (byte)'\n';
~Parser()
{
Dispose(false);
@@ -45,26 +46,25 @@ public class Parser : IDisposable
public bool HasBody => contentLength.HasValue && contentLength > 0;
///
- /// Appends the given to the message.
+ /// Appends the given to the message.
///
- /// The next of the message.
+ /// The next of the message.
/// The same instance of the .
- public Parser Append(char next)
+ public Parser Append(byte next)
{
if (Completed)
{
return new Parser().Append(next);
}
- buffer.Append(next);
-
if (!HasBody)
{
+ headerBytes.WriteByte(next);
// we're parsing the headers
- if (previous == '\n' && next == '\n')
+ if (previous == headerEndDelimiter && next == headerEndDelimiter)
{
// \n\n denotes the end of the Headers
- var headerString = buffer.ToString();
+ var headerString = Encoding.UTF8.GetString(headerBytes.ToArray());
headers = headerString.ParseKeyValuePairs(": ");
@@ -79,10 +79,7 @@ public Parser Append(char next)
else
{
// start parsing the body content
- buffer.Clear();
-
- // allocate the buffer up front given that we now know the expected size
- buffer.EnsureCapacity(contentLength.Value);
+ bodyBytes = new MemoryStream(contentLength.Value);
}
}
else
@@ -98,8 +95,11 @@ public Parser Append(char next)
}
else
{
+ Debug.Assert(bodyBytes != null);
+ bodyBytes.WriteByte(next);
// if we've read the Content-Length amount of bytes then we're done
- Completed = buffer.Length == contentLength.GetValueOrDefault() || contentLength == 0;
+ Debug.Assert(contentLength > 0);
+ Completed = bodyBytes.Length == contentLength.GetValueOrDefault();
}
return this;
@@ -108,11 +108,11 @@ public Parser Append(char next)
///
/// Appends the provided string to the internal buffer.
///
- public Parser Append(string next)
+ public Parser Append(byte[] next)
{
var parser = this;
- foreach (var c in next)
+ foreach (var b in next)
{
parser = parser.Append(next);
}
@@ -141,17 +141,17 @@ public BasicMessage ExtractMessage()
if (HasBody)
{
- errorMessage += "expected a body with length {0}, got {1} instead.".Fmt(contentLength, buffer.Length);
+ errorMessage += "expected a body with length {0}, got {1} instead.".Fmt(contentLength, bodyBytes.Length);
}
throw new InvalidOperationException(errorMessage);
}
- var result = HasBody ? new BasicMessage(headers, buffer.ToString()) : new BasicMessage(headers);
+ var result = HasBody ? new BasicMessage(headers, bodyBytes.ToArray()) : new BasicMessage(headers);
if (HasBody)
{
- Debug.Assert(result.BodyText.Length == result.ContentLength);
+ Debug.Assert(bodyBytes.Length == result.ContentLength);
}
Dispose();
@@ -168,11 +168,7 @@ protected virtual void Dispose(bool disposing)
{
if (disposed != null && !disposed.EnsureCalledOnce())
{
- if (buffer != null)
- {
- StringBuilderPool.Free(buffer);
- buffer = null;
- }
+ bodyBytes = null;
}
}
}