Skip to content

Commit 02cbfac

Browse files
authored
Merge pull request #136 from jacqueskang/history/v2.3.0
History/v2.3.0
2 parents e72ff9a + 94b888d commit 02cbfac

File tree

16 files changed

+527
-68
lines changed

16 files changed

+527
-68
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
language: csharp
22
mono: none
33
solution: IpcServiceFramework.sln
4-
dotnet: 2.1.3
4+
dotnet: 2.1.502
55
dist: trusty
66
sudo: required
77
script:
88
- cd ./src
99
- dotnet restore
1010
- dotnet build --configuration Release
11-
- dotnet test JKang.IpcServiceFramework.Core.Tests
11+
- dotnet test JKang.IpcServiceFramework.Core.Tests

src/JKang.IpcServiceFramework.Client/IpcServiceClient.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using JKang.IpcServiceFramework.Services;
44
using System;
55
using System.IO;
6+
using System.Linq;
67
using System.Linq.Expressions;
78
using System.Threading;
89
using System.Threading.Tasks;
@@ -36,7 +37,7 @@ public async Task InvokeAsync(Expression<Action<TInterface>> exp,
3637
}
3738
else
3839
{
39-
throw new InvalidOperationException(response.Failure);
40+
throw response.GetException();
4041
}
4142
}
4243

@@ -59,7 +60,7 @@ public async Task<TResult> InvokeAsync<TResult>(Expression<Func<TInterface, TRes
5960
}
6061
else
6162
{
62-
throw new InvalidOperationException(response.Failure);
63+
throw response.GetException();
6364
}
6465
}
6566

@@ -75,7 +76,7 @@ public async Task InvokeAsync(Expression<Func<TInterface, Task>> exp,
7576
}
7677
else
7778
{
78-
throw new InvalidOperationException(response.Failure);
79+
throw response.GetException();
7980
}
8081
}
8182

@@ -98,31 +99,37 @@ public async Task<TResult> InvokeAsync<TResult>(Expression<Func<TInterface, Task
9899
}
99100
else
100101
{
101-
throw new InvalidOperationException(response.Failure);
102+
throw response.GetException();
102103
}
103104
}
104105

105106

106107
private static IpcRequest GetRequest(Expression exp, MyInterceptor interceptor)
107108
{
108-
if (!(exp is LambdaExpression lamdaExp))
109+
if (!(exp is LambdaExpression lambdaExp))
109110
{
110-
throw new ArgumentException("Only support lamda expresion, ex: x => x.GetData(a, b)");
111+
throw new ArgumentException("Only support lambda expression, ex: x => x.GetData(a, b)");
111112
}
112113

113-
if (!(lamdaExp.Body is MethodCallExpression methodCallExp))
114+
if (!(lambdaExp.Body is MethodCallExpression methodCallExp))
114115
{
115116
throw new ArgumentException("Only support calling method, ex: x => x.GetData(a, b)");
116117
}
117118

118119
TInterface proxy = _proxyGenerator.CreateInterfaceProxyWithoutTarget<TInterface>(interceptor);
119-
Delegate @delegate = lamdaExp.Compile();
120+
Delegate @delegate = lambdaExp.Compile();
120121
@delegate.DynamicInvoke(proxy);
121122

122123
return new IpcRequest
123124
{
124125
MethodName = interceptor.LastInvocation.Method.Name,
125126
Parameters = interceptor.LastInvocation.Arguments,
127+
128+
ParameterTypes = interceptor.LastInvocation.Method.GetParameters()
129+
.Select(p => p.ParameterType)
130+
.ToArray(),
131+
132+
126133
GenericArguments = interceptor.LastInvocation.GenericArguments,
127134
};
128135
}

src/JKang.IpcServiceFramework.Core/IpcRequest.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ public class IpcRequest
88

99
public string MethodName { get; set; }
1010
public object[] Parameters { get; set; }
11+
12+
/// <summary>
13+
/// Gets or sets the types of parameter of the IPC method to call
14+
/// </summary>
15+
public Type[] ParameterTypes { get; set; } = new Type[0];
16+
1117
public Type[] GenericArguments
1218
{
1319
get => _genericArguments ?? new Type[0];
1420
set => _genericArguments = value;
1521
}
1622
}
17-
}
23+
}
Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,82 @@
11
using System;
2+
using System.Reflection;
23
using Newtonsoft.Json;
34

45
namespace JKang.IpcServiceFramework
56
{
67
public class IpcResponse
78
{
89
[JsonConstructor]
9-
private IpcResponse(bool succeed, object data, string failure)
10+
private IpcResponse(bool succeed, object data, string failure, string failureDetails, bool userCodeFailure)
1011
{
1112
Succeed = succeed;
1213
Data = data;
1314
Failure = failure;
15+
FailureDetails = failureDetails;
16+
UserCodeFailure = userCodeFailure;
1417
}
1518

1619
public static IpcResponse Fail(string failure)
1720
{
18-
return new IpcResponse(false, null, failure);
21+
return new IpcResponse(false, null, failure, null, false);
22+
}
23+
24+
public static IpcResponse Fail(Exception ex, bool includeDetails, bool userFailure = false)
25+
{
26+
string message = null;
27+
string details = null;
28+
29+
if (!userFailure)
30+
{
31+
message = "Internal server error: ";
32+
}
33+
34+
message += GetFirstUsableMessage(ex);
35+
36+
if (includeDetails)
37+
{
38+
details = ex.ToString();
39+
}
40+
41+
return new IpcResponse(false, null, message, details, userFailure);
1942
}
2043

2144
public static IpcResponse Success(object data)
2245
{
23-
return new IpcResponse(true, data, null);
46+
return new IpcResponse(true, data, null, null, false);
2447
}
2548

2649
public bool Succeed { get; }
2750
public object Data { get; }
2851
public string Failure { get; }
52+
public string FailureDetails { get; }
53+
public bool UserCodeFailure { get; set; }
54+
55+
public Exception GetException()
56+
{
57+
if (UserCodeFailure)
58+
{
59+
throw new IpcServerUserCodeException(Failure, FailureDetails);
60+
}
61+
62+
throw new IpcServerException(Failure, FailureDetails);
63+
}
64+
65+
private static string GetFirstUsableMessage(Exception ex)
66+
{
67+
var e = ex;
68+
69+
while (e != null)
70+
{
71+
if (!(e is TargetInvocationException))
72+
{
73+
return e.Message;
74+
}
75+
76+
e = e.InnerException;
77+
}
78+
79+
return ex.Message;
80+
}
2981
}
3082
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.Serialization;
4+
using System.Text;
5+
6+
namespace JKang.IpcServiceFramework
7+
{
8+
/// <summary>
9+
/// An exception that originated at the server.
10+
/// </summary>
11+
[Serializable]
12+
public class IpcServerException : InvalidOperationException
13+
{
14+
public const string ServerFailureDetails = "Server failure details:";
15+
public string FailureDetails { get; }
16+
17+
public IpcServerException()
18+
{
19+
}
20+
21+
public IpcServerException(string message, string failureDetails)
22+
: base(message)
23+
{
24+
FailureDetails = failureDetails;
25+
}
26+
27+
public IpcServerException(string message, Exception inner)
28+
: base(message, inner)
29+
{
30+
}
31+
32+
protected IpcServerException(SerializationInfo info, StreamingContext context)
33+
: base(info, context)
34+
{
35+
if (info == null)
36+
throw new ArgumentNullException(nameof(info));
37+
38+
FailureDetails = info.GetString("FailureDetails");
39+
}
40+
41+
public override void GetObjectData(SerializationInfo info, StreamingContext context)
42+
{
43+
base.GetObjectData(info, context);
44+
45+
info.AddValue("FailureDetails", FailureDetails, typeof(string));
46+
}
47+
48+
public override string ToString()
49+
{
50+
if (!string.IsNullOrWhiteSpace(FailureDetails))
51+
{
52+
var sb = new StringBuilder();
53+
sb.AppendLine(base.ToString());
54+
sb.AppendLine();
55+
sb.AppendLine(ServerFailureDetails);
56+
sb.Append(FailureDetails);
57+
return sb.ToString();
58+
}
59+
60+
return base.ToString();
61+
}
62+
}
63+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
4+
namespace JKang.IpcServiceFramework
5+
{
6+
/// <summary>
7+
/// An exception that originated at the server, in user code.
8+
/// </summary>
9+
[Serializable]
10+
public class IpcServerUserCodeException : IpcServerException
11+
{
12+
public IpcServerUserCodeException()
13+
{
14+
}
15+
16+
public IpcServerUserCodeException(string message, string failureDetails)
17+
: base(message, failureDetails)
18+
{
19+
}
20+
21+
public IpcServerUserCodeException(string message, Exception inner)
22+
: base(message, inner)
23+
{
24+
}
25+
26+
protected IpcServerUserCodeException(SerializationInfo info, StreamingContext context)
27+
: base(info, context)
28+
{
29+
}
30+
}
31+
}

src/JKang.IpcServiceFramework.IntegrationTests/ContractTest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ public async Task AsyncOperation()
9898
Assert.True(actual >= 450);
9999
}
100100

101+
[Fact]
102+
public async Task ExplicitInterfaceOperation()
103+
{
104+
int actual = await _client.InvokeAsync(x => x.ExplicitInterfaceMember());
105+
Assert.True(actual == 0);
106+
}
107+
108+
[Fact]
109+
public async Task ThrowException()
110+
{
111+
try
112+
{
113+
await _client.InvokeAsync(x => x.ThrowException("This was forced"));
114+
}
115+
catch (IpcServerException ex)
116+
{
117+
Assert.Contains("This was forced", ex.Message);
118+
Assert.DoesNotContain(IpcServerException.ServerFailureDetails, ex.ToString());
119+
}
120+
}
121+
101122
public void Dispose()
102123
{
103124
_cancellationToken.Cancel();
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using AutoFixture.Xunit2;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Globalization;
6+
using System.Net;
7+
using System.Numerics;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using Xunit;
11+
12+
namespace JKang.IpcServiceFramework.IntegrationTests
13+
{
14+
public class FailureDetailTest : IDisposable
15+
{
16+
private readonly CancellationTokenSource _cancellationToken;
17+
private readonly int _port;
18+
private readonly IpcServiceClient<ITestService> _client;
19+
20+
public FailureDetailTest()
21+
{
22+
// configure DI
23+
IServiceCollection services = new ServiceCollection()
24+
.AddIpc(builder => builder.AddNamedPipe().AddService<ITestService, TestService>());
25+
_port = new Random().Next(10000, 50000);
26+
IIpcServiceHost host = new IpcServiceHostBuilder(services.BuildServiceProvider())
27+
.AddTcpEndpoint<ITestService>(
28+
name: Guid.NewGuid().ToString(),
29+
ipEndpoint: IPAddress.Loopback,
30+
port: _port,
31+
includeFailureDetailsInResponse: true)
32+
.Build();
33+
_cancellationToken = new CancellationTokenSource();
34+
host.RunAsync(_cancellationToken.Token);
35+
36+
_client = new IpcServiceClientBuilder<ITestService>()
37+
.UseTcp(IPAddress.Loopback, _port)
38+
.Build();
39+
}
40+
41+
[Fact]
42+
public async Task ThrowException()
43+
{
44+
try
45+
{
46+
await _client.InvokeAsync(x => x.ThrowException("This was forced"));
47+
}
48+
catch (IpcServerException ex)
49+
{
50+
Assert.Contains("This was forced", ex.Message);
51+
Assert.Contains(IpcServerException.ServerFailureDetails, ex.ToString());
52+
}
53+
}
54+
55+
public void Dispose()
56+
{
57+
_cancellationToken.Cancel();
58+
}
59+
}
60+
}

src/JKang.IpcServiceFramework.IntegrationTests/ITestService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using IpcServiceSample.ServiceContracts;
2-
using System;
1+
using System;
32
using System.Collections.Generic;
43
using System.Globalization;
54
using System.Numerics;
@@ -17,5 +16,7 @@ public interface ITestService
1716
byte[] ReverseBytes(byte[] input);
1817
T GetDefaultValue<T>();
1918
Task<long> WaitAsync(int milliseconds);
19+
int ExplicitInterfaceMember();
20+
void ThrowException(string message);
2021
}
2122
}

0 commit comments

Comments
 (0)