Skip to content

Commit

Permalink
Merge pull request #74 from lanfeust69/master
Browse files Browse the repository at this point in the history
Fix connection closed on authorization timeout
  • Loading branch information
Colin Sullivan committed May 26, 2016
2 parents e1d0c60 + 98be796 commit 1d3794c
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 34 deletions.
17 changes: 12 additions & 5 deletions NATS/Conn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,11 +1117,11 @@ private void sendConnect()
else if (result.StartsWith(IC._ERR_OP_))
{
throw new NATSConnectionException(
result.TrimStart(IC._ERR_OP_.ToCharArray()));
result.Substring(IC._ERR_OP_.Length));
}
else
{
throw new NATSException(result);
throw new NATSException("Error from sendConnect(): " + result);
}
}
}
Expand Down Expand Up @@ -1745,8 +1745,7 @@ internal void processErr(MemoryStream errorStream)
bool invokeDelegates = false;
Exception ex = null;

String s = System.Text.Encoding.UTF8.GetString(
errorStream.ToArray(), 0, (int)errorStream.Position);
string s = getNormalizedError(errorStream);

if (IC.STALE_CONNECTION.Equals(s))
{
Expand All @@ -1761,7 +1760,7 @@ internal void processErr(MemoryStream errorStream)
}
else
{
ex = new NATSException(s);
ex = new NATSException("Error from processErr(): " + s);
lock (mu)
{
lastEx = ex;
Expand All @@ -1776,6 +1775,14 @@ internal void processErr(MemoryStream errorStream)
}
}

// getNormalizedError extracts a string from a MemoryStream, then normalizes it
// by removing leading and trailing spaces and quotes, and converting to lowercase
private string getNormalizedError(MemoryStream errorStream)
{
string s = Encoding.UTF8.GetString(errorStream.ToArray(), 0, (int)errorStream.Position);
return s.Trim('\'', '"', ' ', '\t', '\r', '\n').ToLower();
}

// Use low level primitives to build the protocol for the publish
// message.
private int writePublishProto(byte[] dst, string subject, string reply, int msgSize)
Expand Down
4 changes: 2 additions & 2 deletions NATS/NATS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ internal class IC
internal const string pongProtoNoCRLF = "PONG";
internal const string okProtoNoCRLF = "+OK";

internal const string STALE_CONNECTION = "Stale Connection";
internal const string AUTH_TIMEOUT = "Authorization Timeout";
internal const string STALE_CONNECTION = "stale connection";
internal const string AUTH_TIMEOUT = "authorization timeout";
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions NATS/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ internal void parse(byte[] buffer, int len)
break;
default:
state = MINUS_ERR_ARG;
i--;
break;
}
break;
Expand Down
2 changes: 1 addition & 1 deletion NATSUnitTests/NATSUnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>c:\tmp\bin\Debug\</OutputPath>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
Expand Down
27 changes: 14 additions & 13 deletions NATSUnitTests/UnitTestAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Reflection;
using System.IO;
using System.Linq;

namespace NATSUnitTests
{
Expand Down Expand Up @@ -184,19 +185,19 @@ public void TestReconnectAuthTimeoutLateClose()

IConnection c = new ConnectionFactory().CreateConnection(opts);

// inject an authorization timeout, as if it were processed by an incoming
// server message.
using (MemoryStream ms = new MemoryStream())
{
byte[] value = System.Text.Encoding.UTF8.GetBytes("Authorization Timeout");
ms.Write(value, 0, value.Length);

MethodInfo processErr = typeof(Connection).GetMethod(
"processErr",
BindingFlags.NonPublic | BindingFlags.Instance);

processErr.Invoke(c, new object[] { ms });
}
// inject an authorization timeout, as if it were processed by an incoming server message.
// this is done at the parser level so that parsing is also tested,
// therefore it needs reflection since Parser is an internal type.
Type parserType = typeof(Connection).Assembly.GetType("NATS.Client.Parser");
Assert.IsNotNull(parserType, "Failed to find NATS.Client.Parser");
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
object parser = Activator.CreateInstance(parserType, flags, null, new object[] { c }, null);
Assert.IsNotNull(parser, "Failed to instanciate a NATS.Client.Parser");
MethodInfo parseMethod = parserType.GetMethod("parse", flags);
Assert.IsNotNull(parseMethod, "Failed to find method parse in NATS.Client.Parser");

byte[] bytes = "-ERR 'Authorization Timeout'\r\n".ToCharArray().Select(ch => (byte)ch).ToArray();
parseMethod.Invoke(parser, new object[] { bytes, bytes.Length });

// sleep to allow the client to process the error, then shutdown the server.
Thread.Sleep(250);
Expand Down
27 changes: 14 additions & 13 deletions NATSUnitTests/UnitTestTLS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using NATS.Client;
using System.Reflection;
using System.IO;
using System.Linq;
using System.Threading;

namespace NATSUnitTests
Expand Down Expand Up @@ -332,19 +333,19 @@ public void TestTlsReconnectAuthTimeoutLateClose()

IConnection c = new ConnectionFactory().CreateConnection(opts);

// inject an authorization error, as if it were processed by an incoming
// server message.
using (MemoryStream ms = new MemoryStream())
{
byte[] value = System.Text.Encoding.UTF8.GetBytes("Authorization Timeout");
ms.Write(value, 0, value.Length);

MethodInfo processErr = typeof(Connection).GetMethod(
"processErr",
BindingFlags.NonPublic | BindingFlags.Instance);

processErr.Invoke(c, new object[] { ms });
}
// inject an authorization timeout, as if it were processed by an incoming server message.
// this is done at the parser level so that parsing is also tested,
// therefore it needs reflection since Parser is an internal type.
Type parserType = typeof(Connection).Assembly.GetType("NATS.Client.Parser");
Assert.IsNotNull(parserType, "Failed to find NATS.Client.Parser");
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
object parser = Activator.CreateInstance(parserType, flags, null, new object[] { c }, null);
Assert.IsNotNull(parser, "Failed to instanciate a NATS.Client.Parser");
MethodInfo parseMethod = parserType.GetMethod("parse", flags);
Assert.IsNotNull(parseMethod, "Failed to find method parse in NATS.Client.Parser");

byte[] bytes = "-ERR 'Authorization Timeout'\r\n".ToCharArray().Select(ch => (byte)ch).ToArray();
parseMethod.Invoke(parser, new object[] { bytes, bytes.Length });

// sleep to allow the client to process the error, then shutdown the server.
Thread.Sleep(250);
Expand Down

0 comments on commit 1d3794c

Please sign in to comment.