diff --git a/src/NATS.Client.Services/NatsSvcMsg.cs b/src/NATS.Client.Services/NatsSvcMsg.cs index b39da560f..d3646b821 100644 --- a/src/NATS.Client.Services/NatsSvcMsg.cs +++ b/src/NATS.Client.Services/NatsSvcMsg.cs @@ -47,6 +47,11 @@ public NatsSvcMsg(NatsMsg msg, NatsSvcEndpointBase? endPoint, Exception? exce /// public string? ReplyTo => _msg.ReplyTo; + /// + /// Pass additional information using name-value pairs. + /// + public NatsHeaders? Headers => _msg.Headers; + /// /// Send a reply with an empty message body. /// diff --git a/tests/NATS.Client.Services.Tests/ServicesTests.cs b/tests/NATS.Client.Services.Tests/ServicesTests.cs index 36c43569b..4920c9518 100644 --- a/tests/NATS.Client.Services.Tests/ServicesTests.cs +++ b/tests/NATS.Client.Services.Tests/ServicesTests.cs @@ -196,10 +196,7 @@ await grp2.AddEndpointAsync( await using var s2 = await svc.AddServiceAsync( new NatsSvcConfig("s2", "2.0.0") { - Description = "es-two", - QueueGroup = "q2", - Metadata = new Dictionary { { "k1", "v1" }, { "k2", "v2" }, }, - StatsHandler = ep => JsonNode.Parse($"{{\"stat-k1\":\"stat-v1\",\"stat-k2\":\"stat-v2\",\"ep_name\": \"{ep.Name}\"}}")!, + Description = "es-two", QueueGroup = "q2", Metadata = new Dictionary { { "k1", "v1" }, { "k2", "v2" }, }, StatsHandler = ep => JsonNode.Parse($"{{\"stat-k1\":\"stat-v1\",\"stat-k2\":\"stat-v2\",\"ep_name\": \"{ep.Name}\"}}")!, }, cancellationToken: cancellationToken); @@ -264,4 +261,62 @@ public async Task Add_multiple_service_listeners_ping_info_and_stats() Assert.Equal("1.0.0", stats.First(s => s.Name == "s1").Version); Assert.Equal("2.0.0", stats.First(s => s.Name == "s2").Version); } + + [Fact] + public async Task Pass_headers_to_request_and_in_response() + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var cancellationToken = cts.Token; + + await using var server = NatsServer.Start(); + await using var nats = server.CreateClientConnection(); + var svc = new NatsSvcContext(nats); + + await using var s1 = await svc.AddServiceAsync("s1", "1.0.0", cancellationToken: cancellationToken); + + await s1.AddEndpointAsync( + name: "e1", + handler: async m => + { + if (m.Headers != null) + { + var headers = m.Headers; + if (headers.TryGetValue("foo", out var foo)) + { + if (foo != "bar") + { + await m.ReplyErrorAsync(m.Data, "Expected 'foo' = 'bar' header", cancellationToken: cancellationToken); + return; + } + + await m.ReplyAsync(m.Data, headers: new NatsHeaders { { "bar", "baz" } }, cancellationToken: cancellationToken); + return; + } + } + + await m.ReplyErrorAsync(m.Data, "Missing 'foo' header", cancellationToken: cancellationToken); + }, + cancellationToken: cancellationToken); + + // With headers + var headers = new NatsHeaders { { "foo", "bar" } }; + var response = await nats.RequestAsync("e1", 999, headers, cancellationToken: cancellationToken); + Assert.Equal(999, response.Data); + Assert.Equal("baz", response.Headers?["bar"]); + + // With headers, but not the expected one. + headers = new NatsHeaders + { + { "not-the-expected", "4711" }, + { "also-not-the-expected", "4242" }, + }; + response = await nats.RequestAsync("e1", 999, headers, cancellationToken: cancellationToken); + Assert.Equal("999", response.Headers?["Nats-Service-Error-Code"]); + Assert.Equal("Missing 'foo' header", response.Headers?["Nats-Service-Error"]); + + // No headers. + response = await nats.RequestAsync("e1", 999, cancellationToken: cancellationToken); + Assert.Equal("999", response.Headers?["Nats-Service-Error-Code"]); + Assert.Equal("Missing 'foo' header", response.Headers?["Nats-Service-Error"]); + } }