diff --git a/src/NATS.Client/Conn.cs b/src/NATS.Client/Conn.cs index 00ba028c3..f299f40bc 100644 --- a/src/NATS.Client/Conn.cs +++ b/src/NATS.Client/Conn.cs @@ -18,6 +18,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Net; using System.Net.Sockets; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -987,6 +988,27 @@ public string ConnectedUrl } } + /// + /// Gets the IP of client as known by the NATS server, otherwise null. + /// + /// + /// Supported in the NATS server version 2.1.6 and above. If the client is connected to + /// an older server or is in the process of connecting, null will be returned. + /// + public IPAddress ClientIP + { + get + { + string clientIp; + lock (mu) + { + clientIp = info.client_ip; + } + + return !String.IsNullOrEmpty(clientIp) ? IPAddress.Parse(clientIp) : null; + } + } + /// /// Gets the server ID of the NATS server to which this instance /// is connected, otherwise null. diff --git a/src/NATS.Client/IConnection.cs b/src/NATS.Client/IConnection.cs index 22ef52da1..41b34d3c0 100644 --- a/src/NATS.Client/IConnection.cs +++ b/src/NATS.Client/IConnection.cs @@ -12,6 +12,7 @@ // limitations under the License. using System; +using System.Net; using System.Threading; using System.Threading.Tasks; @@ -27,6 +28,15 @@ public interface IConnection : IDisposable /// Options Opts { get; } + /// + /// Gets the IP of client as known by the NATS server, otherwise null. + /// + /// + /// Supported in the NATS server version 2.1.6 and above. If the client is connected to + /// an older server or is in the process of connecting, null will be returned. + /// + IPAddress ClientIP { get; } + /// /// Gets the URL of the NATS server to which this instance /// is connected, otherwise null. diff --git a/src/NATS.Client/Info.cs b/src/NATS.Client/Info.cs index 5ec4a7a84..fe43e1624 100644 --- a/src/NATS.Client/Info.cs +++ b/src/NATS.Client/Info.cs @@ -21,6 +21,8 @@ internal class ServerInfo { public string server_id { get; private set; } + public string client_ip { get; private set; } + public string host { get; private set; } public int port { get; private set; } @@ -46,6 +48,7 @@ public static ServerInfo CreateFromJson(string json) return new ServerInfo { server_id = x["server_id"].Value, + client_ip = x["client_ip"].Value, host = x["host"].Value, port = x["port"].AsInt, version = x["version"].Value, diff --git a/src/Tests/IntegrationTests/TestConnection.cs b/src/Tests/IntegrationTests/TestConnection.cs index f154951c7..6c9dc0b51 100644 --- a/src/Tests/IntegrationTests/TestConnection.cs +++ b/src/Tests/IntegrationTests/TestConnection.cs @@ -116,14 +116,14 @@ public void TestErrorHandlerWhenNotAllowingReconnectErrorShouldBeProvided() opts.AllowReconnect = false; opts.ClosedEventHandler = (sender, args) => { - if(args.Error != null) + if (args.Error != null) errors.Enqueue(args.Error); closedEv.Set(); }; opts.DisconnectedEventHandler = (sender, args) => { - if(args.Error != null) + if (args.Error != null) errors.Enqueue(args.Error); disconEv.Set(); @@ -154,21 +154,21 @@ public void TestErrorHandlerWhenAllowingReconnectErrorShouldNotBeProvided() opts.MaxReconnect = 1; opts.ClosedEventHandler = (sender, args) => { - if(args.Error != null) + if (args.Error != null) errors.Enqueue(args.Error); closedEv.Set(); }; opts.DisconnectedEventHandler = (sender, args) => { - if(args.Error != null) + if (args.Error != null) errors.Enqueue(args.Error); disconEv.Set(); }; opts.ReconnectedEventHandler = (sender, args) => { - if(args.Error != null) + if (args.Error != null) errors.Enqueue(args.Error); reconEv.Set(); @@ -220,7 +220,7 @@ public void TestClosedConnections() { using (var c = Context.OpenConnection(Context.Server1.Port)) { - using(var s = c.SubscribeSync("foo")) + using (var s = c.SubscribeSync("foo")) { c.Close(); @@ -260,7 +260,7 @@ public void TestConnectVerbose() var o = Context.GetTestOptions(Context.Server1.Port); o.Verbose = true; - using(var c = Context.ConnectionFactory.CreateConnection(o)) + using (var c = Context.ConnectionFactory.CreateConnection(o)) c.Close(); } } @@ -279,7 +279,7 @@ public void TestServerDiscoveredHandlerNotCalledOnConnect() serverDiscoveredCalled = true; }; - using(var c = Context.ConnectionFactory.CreateConnection(o)) + using (var c = Context.ConnectionFactory.CreateConnection(o)) c.Close(); Assert.False(serverDiscoveredCalled); @@ -303,12 +303,12 @@ public void TestCallbacksOrder() long ctime = orig; AutoResetEvent reconnected = new AutoResetEvent(false); - AutoResetEvent closed = new AutoResetEvent(false); - AutoResetEvent asyncErr1 = new AutoResetEvent(false); - AutoResetEvent asyncErr2 = new AutoResetEvent(false); - AutoResetEvent recvCh = new AutoResetEvent(false); - AutoResetEvent recvCh1 = new AutoResetEvent(false); - AutoResetEvent recvCh2 = new AutoResetEvent(false); + AutoResetEvent closed = new AutoResetEvent(false); + AutoResetEvent asyncErr1 = new AutoResetEvent(false); + AutoResetEvent asyncErr2 = new AutoResetEvent(false); + AutoResetEvent recvCh = new AutoResetEvent(false); + AutoResetEvent recvCh1 = new AutoResetEvent(false); + AutoResetEvent recvCh2 = new AutoResetEvent(false); using (NATSServer serverAuth = NATSServer.CreateWithConfig(Context.Server1.Port, "auth.conf"), @@ -360,7 +360,7 @@ public void TestCallbacksOrder() o.ReconnectWait = 500; o.NoRandomize = true; - o.Servers = new [] { Context.Server2.Url, Context.Server1.Url }; + o.Servers = new[] { Context.Server2.Url, Context.Server1.Url }; o.SubChannelLength = 1; using (IConnection @@ -450,7 +450,7 @@ public void TestCallbacksOrder() } } } - + [Fact] public void TestConnectionCloseAndDispose() { @@ -506,7 +506,7 @@ public void TestInfineReconnect() { var reconnectEv = new AutoResetEvent(false); var closedEv = new AutoResetEvent(false); - + var opts = Context.GetTestOptionsWithDefaultTimeout(Context.Server1.Port); opts.Timeout = 500; @@ -571,8 +571,18 @@ public void CanConnectWhenHandshakeTimeoutIsSpecified() } } - /// NOT IMPLEMENTED: - /// TestErrOnMaxPayloadLimit + [Fact] + [Trait("Category", "NATS 2.1.6+")] + public void TestClientIP() + { + IConnection conn; + using (NATSServer.CreateFastAndVerify(Context.Server1.Port)) + { + conn = Context.ConnectionFactory.CreateConnection("nats://127.0.0.1:" + Context.Server1.Port); + Assert.Equal(conn.ClientIP.MapToIPv4(), System.Net.IPAddress.Parse("127.0.0.1")); + conn.Close(); + } + } } public class TestConnectionSecurity : TestSuite @@ -779,12 +789,6 @@ public void TestUserPassTokenOptions() Assert.Throws(() => Context.ConnectionFactory.CreateConnection(opts)); } } - - [Fact(Skip = "WorkInProgress")] - public void TestJwtFunctionality() - { - //using (NATSServer.CreateFastAndVerify(Context.Server1.Port)) { } - } } public class TestConnectionDrain : TestSuite