Skip to content

Commit 5f424c6

Browse files
authored
Merge pull request #315 from basho/fixes/lrb/smarter-ttb-decoding
Add tests for various rpberrorresp TTB responses.
2 parents 02b588c + a8f11b4 commit 5f424c6

File tree

6 files changed

+204
-36
lines changed

6 files changed

+204
-36
lines changed

src/RiakClient/Erlang/OtpInputStream.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,25 @@ public double ReadDouble()
295295
return d;
296296
}
297297

298+
/// <summary>
299+
/// Can the tag be parsed as a long
300+
/// </summary>
301+
/// <param name="tag">the tag to check</param>
302+
/// <returns>boolean indicating if tag can be parsed as a long.</returns>
303+
public bool IsLongTag(byte tag)
304+
{
305+
switch (tag)
306+
{
307+
case OtpExternal.SmallIntTag:
308+
case OtpExternal.IntTag:
309+
case OtpExternal.SmallBigTag:
310+
case OtpExternal.LargeBigTag:
311+
return true;
312+
default:
313+
return false;
314+
}
315+
}
316+
298317
/// <summary>
299318
/// Read an array of bytes
300319
/// </summary>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
namespace RiakClient.Erlang
2+
{
3+
using Exceptions;
4+
using Util;
5+
6+
internal static class TtbErrorDecoder
7+
{
8+
public const string RpbErrorRespAtom = "rpberrorresp";
9+
public const string RpbErrorRespEmpty = "No Riak error message or code returned.";
10+
11+
public static RiakException MaybeRiakError(byte[] response)
12+
{
13+
RiakException rv = null;
14+
15+
if (EnumerableUtil.IsNullOrEmpty(response))
16+
{
17+
string errMsg = "TTB request returned null or zero-length data buffer.";
18+
rv = new RiakException(0, errMsg, false);
19+
}
20+
21+
using (var s = new OtpInputStream(response))
22+
{
23+
string atom;
24+
byte tag = s.Peek();
25+
switch (tag)
26+
{
27+
case OtpExternal.AtomTag:
28+
atom = s.ReadAtom();
29+
if (atom.Equals(RpbErrorRespAtom))
30+
{
31+
throw new RiakException(0, RpbErrorRespEmpty, false);
32+
}
33+
34+
break;
35+
case OtpExternal.SmallTupleTag:
36+
case OtpExternal.LargeTupleTag:
37+
int arity = s.ReadTupleHead();
38+
if (arity >= 1)
39+
{
40+
tag = s.Peek();
41+
if (tag == OtpExternal.AtomTag)
42+
{
43+
atom = s.ReadAtom();
44+
if (atom.Equals(RpbErrorRespAtom))
45+
{
46+
arity--; // We've read one item in the tuple
47+
string errMsg = RpbErrorRespEmpty;
48+
int errCode = 0;
49+
50+
for (int i = 0; i < arity; ++i)
51+
{
52+
tag = s.Peek();
53+
if (tag == OtpExternal.BinTag)
54+
{
55+
errMsg = s.ReadBinaryAsString();
56+
}
57+
else if (s.IsLongTag(tag))
58+
{
59+
errCode = (int)s.ReadLong();
60+
}
61+
else
62+
{
63+
errMsg = string.Format("Unexpected tag {0} in {1}", tag, RpbErrorRespAtom);
64+
errCode = 0;
65+
break;
66+
}
67+
}
68+
69+
rv = new RiakException(errCode, errMsg, false);
70+
}
71+
}
72+
}
73+
74+
break;
75+
}
76+
}
77+
78+
return rv;
79+
}
80+
}
81+
}

src/RiakClient/Messages/RpbClasses.cs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ public override void WriteTo(Stream s)
282282

283283
public sealed class TsTtbResp : RiakResp
284284
{
285-
private static readonly string RpbErrorRespAtom = "rpberrorresp";
286285
private readonly byte[] response;
287286

288287
public TsTtbResp(byte[] response)
@@ -293,41 +292,10 @@ public TsTtbResp(byte[] response)
293292
}
294293

295294
this.response = response;
296-
maybeRiakError(response);
297-
}
298-
299-
private static void maybeRiakError(byte[] response)
300-
{
301-
if (EnumerableUtil.IsNullOrEmpty(response))
302-
{
303-
string errMsg = "TTB request returned null or zero-length data buffer.";
304-
throw new RiakException(0, errMsg, false);
305-
}
306-
307-
using (var s = new OtpInputStream(response))
295+
RiakException ex = TtbErrorDecoder.MaybeRiakError(response);
296+
if (ex != null)
308297
{
309-
byte tag = s.Peek();
310-
switch (tag)
311-
{
312-
case OtpExternal.SmallTupleTag:
313-
case OtpExternal.LargeTupleTag:
314-
int arity = s.ReadTupleHead();
315-
if (arity == 3)
316-
{
317-
tag = s.Peek();
318-
if (tag == OtpExternal.AtomTag)
319-
{
320-
string atom = s.ReadAtom();
321-
if (atom.Equals(RpbErrorRespAtom))
322-
{
323-
string errMsg = s.ReadBinaryAsString();
324-
int errCode = (int)s.ReadLong();
325-
throw new RiakException(errCode, errMsg, false);
326-
}
327-
}
328-
}
329-
break;
330-
}
298+
throw ex;
331299
}
332300
}
333301

src/RiakClient/RiakClient.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
<Compile Include="Commands\CommandOptions.cs" />
8080
<Compile Include="Commands\Response{TValue}.cs" />
8181
<Compile Include="Commands\TS\DecodedResponse.cs" />
82+
<Compile Include="Erlang\TtbErrorDecoder.cs" />
8283
<Compile Include="Commands\TS\ListKeys.cs" />
8384
<Compile Include="Commands\TS\ListKeysOptions.cs" />
8485
<Compile Include="Commands\TS\QueryOptions.cs" />
@@ -411,4 +412,4 @@
411412
<Zip Condition="$(OS) == 'Windows_NT'" Files="@(ZipFiles)" WorkingDirectory="$(OutputPath)" ZipFileName="$(OutputPath)$(AssemblyName)-$(VersionString).zip" ZipLevel="9" />
412413
</Target>
413414
<Import Project="..\..\packages\StyleCop.MSBuild.4.7.54.0\build\StyleCop.MSBuild.Targets" Condition="'$(OS)' == 'Windows_NT' And Exists('..\..\packages\StyleCop.MSBuild.4.7.54.0\build\StyleCop.MSBuild.Targets')" />
414-
</Project>
415+
</Project>

src/Test/Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<Compile Include="Unit\TS\CellTests.cs" />
7373
<Compile Include="Unit\TS\ListKeysTests.cs" />
7474
<Compile Include="Unit\TS\QueryTests.cs" />
75+
<Compile Include="Unit\Erlang\TtbErrorDecoderTests.cs" />
7576
<Compile Include="Unit\TS\TsCellTests.cs" />
7677
<Compile Include="Unit\TS\TimeseriesTest.cs" />
7778
<Compile Include="Unit\TS\StoreTests.cs" />
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
namespace Test.Unit.Erlang
2+
{
3+
using NUnit.Framework;
4+
using RiakClient.Erlang;
5+
using RiakClient.Exceptions;
6+
using RiakClient.Messages;
7+
8+
[TestFixture, UnitTest]
9+
public class TtbErrorDecoderTests
10+
{
11+
private const string ErrMsg = "error-message";
12+
private const int ErrCode = 234;
13+
14+
[Test]
15+
public void Can_Parse_Bare_RpbErrorResp()
16+
{
17+
byte[] b = null;
18+
using (var os = new OtpOutputStream())
19+
{
20+
os.WriteAtom(TtbErrorDecoder.RpbErrorRespAtom);
21+
os.Flush();
22+
b = os.ToArray();
23+
}
24+
25+
var ex = Assert.Throws<RiakException>(() => new TsTtbResp(b));
26+
Assert.IsTrue(ex.Message.Contains(TtbErrorDecoder.RpbErrorRespEmpty));
27+
}
28+
29+
[Test]
30+
public void Can_Parse_RpbErrorResp_In_1_Tuple()
31+
{
32+
byte[] b = null;
33+
using (var os = new OtpOutputStream())
34+
{
35+
os.WriteTupleHead(1);
36+
os.WriteAtom(TtbErrorDecoder.RpbErrorRespAtom);
37+
os.Flush();
38+
b = os.ToArray();
39+
}
40+
41+
var ex = Assert.Throws<RiakException>(() => new TsTtbResp(b));
42+
Assert.IsTrue(ex.Message.Contains(TtbErrorDecoder.RpbErrorRespEmpty));
43+
}
44+
45+
[Test]
46+
public void Can_Parse_RpbErrorResp_In_2_Tuple_With_String()
47+
{
48+
byte[] b = null;
49+
using (var os = new OtpOutputStream())
50+
{
51+
os.WriteTupleHead(2);
52+
os.WriteAtom(TtbErrorDecoder.RpbErrorRespAtom);
53+
os.WriteStringAsBinary(ErrMsg);
54+
os.Flush();
55+
b = os.ToArray();
56+
}
57+
58+
var ex = Assert.Throws<RiakException>(() => new TsTtbResp(b));
59+
Assert.IsTrue(ex.Message.Contains(ErrMsg));
60+
}
61+
62+
[Test]
63+
public void Can_Parse_RpbErrorResp_In_2_Tuple_With_Code()
64+
{
65+
byte[] b = null;
66+
using (var os = new OtpOutputStream())
67+
{
68+
os.WriteTupleHead(2);
69+
os.WriteAtom(TtbErrorDecoder.RpbErrorRespAtom);
70+
os.WriteLong(ErrCode);
71+
os.Flush();
72+
b = os.ToArray();
73+
}
74+
75+
var ex = Assert.Throws<RiakException>(() => new TsTtbResp(b));
76+
Assert.IsTrue(ex.Message.Contains(ErrCode.ToString()));
77+
}
78+
79+
[Test]
80+
public void Can_Parse_RpbErrorResp_In_3_Tuple()
81+
{
82+
byte[] b = null;
83+
using (var os = new OtpOutputStream())
84+
{
85+
os.WriteTupleHead(3);
86+
os.WriteAtom(TtbErrorDecoder.RpbErrorRespAtom);
87+
os.WriteLong(ErrCode);
88+
os.WriteStringAsBinary(ErrMsg);
89+
os.Flush();
90+
b = os.ToArray();
91+
}
92+
93+
var ex = Assert.Throws<RiakException>(() => new TsTtbResp(b));
94+
Assert.IsTrue(ex.Message.Contains(ErrCode.ToString()));
95+
Assert.IsTrue(ex.Message.Contains(ErrMsg));
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)