Skip to content

Commit 139040e

Browse files
authored
Fix client to only filter known gRPC headers (#1046)
1 parent 0a37300 commit 139040e

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

src/Grpc.Net.Client/Internal/GrpcProtocolHelpers.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,7 @@ public static Metadata BuildMetadata(HttpResponseHeaders responseHeaders)
6262

6363
foreach (var header in responseHeaders)
6464
{
65-
// ASP.NET Core includes pseudo headers in the set of request headers
66-
// whereas, they are not in gRPC implementations. We will filter them
67-
// out when we construct the list of headers on the context.
68-
if (header.Key.StartsWith(':'))
69-
{
70-
continue;
71-
}
72-
// Exclude grpc related headers
73-
if (header.Key.StartsWith("grpc-", StringComparison.OrdinalIgnoreCase))
65+
if (ShouldSkipHeader(header.Key))
7466
{
7567
continue;
7668
}
@@ -91,6 +83,32 @@ public static Metadata BuildMetadata(HttpResponseHeaders responseHeaders)
9183
return headers;
9284
}
9385

86+
internal static bool ShouldSkipHeader(string name)
87+
{
88+
if (name.Length == 0)
89+
{
90+
return false;
91+
}
92+
93+
switch (name[0])
94+
{
95+
case ':':
96+
// ASP.NET Core includes pseudo headers in the set of request headers
97+
// whereas, they are not in gRPC implementations. We will filter them
98+
// out when we construct the list of headers on the context.
99+
return true;
100+
case 'g':
101+
case 'G':
102+
// Exclude known grpc headers. This matches Grpc.Core client behavior.
103+
return string.Equals(name, GrpcProtocolConstants.StatusTrailer, StringComparison.OrdinalIgnoreCase)
104+
|| string.Equals(name, GrpcProtocolConstants.MessageTrailer, StringComparison.OrdinalIgnoreCase)
105+
|| string.Equals(name, GrpcProtocolConstants.MessageEncodingHeader, StringComparison.OrdinalIgnoreCase)
106+
|| string.Equals(name, GrpcProtocolConstants.MessageAcceptEncodingHeader, StringComparison.OrdinalIgnoreCase);
107+
default:
108+
return false;
109+
}
110+
}
111+
94112
private const int MillisecondsPerSecond = 1000;
95113

96114
/* round an integer up to the next value with three significant figures */

test/FunctionalTests/Client/TrailerMetadataTests.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#endregion
1818

1919
using System.Linq;
20+
using System.Text;
2021
using System.Threading.Tasks;
2122
using Greet;
2223
using Grpc.AspNetCore.FunctionalTests.Infrastructure;
@@ -35,6 +36,7 @@ public async Task GetTrailers_UnaryMethodSetStatusWithTrailers_TrailersAvailable
3536
Task<HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext context)
3637
{
3738
context.ResponseTrailers.Add(new Metadata.Entry("Name", "the value was empty"));
39+
context.ResponseTrailers.Add(new Metadata.Entry("grpc-status-details-bin", Encoding.UTF8.GetBytes("Hello world")));
3840
context.Status = new Status(StatusCode.InvalidArgument, "Validation failed");
3941
return Task.FromResult(new HelloReply());
4042
}
@@ -65,13 +67,15 @@ Task<HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext c
6567

6668
// Assert
6769
var trailers = call.GetTrailers();
68-
Assert.AreEqual(1, trailers.Count);
70+
Assert.AreEqual(2, trailers.Count);
6971
Assert.AreEqual("the value was empty", trailers.GetValue("name"));
72+
Assert.AreEqual("Hello world", Encoding.UTF8.GetString(trailers.GetValueBytes("grpc-status-details-bin")));
7073

7174
Assert.AreEqual(StatusCode.InvalidArgument, ex.StatusCode);
7275
Assert.AreEqual("Validation failed", ex.Status.Detail);
73-
Assert.AreEqual(1, ex.Trailers.Count);
76+
Assert.AreEqual(2, ex.Trailers.Count);
7477
Assert.AreEqual("the value was empty", ex.Trailers.GetValue("name"));
78+
Assert.AreEqual("Hello world", Encoding.UTF8.GetString(ex.Trailers.GetValueBytes("grpc-status-details-bin")));
7579
}
7680

7781
[Test]
@@ -81,6 +85,7 @@ Task<HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext c
8185
{
8286
var trailers = new Metadata();
8387
trailers.Add(new Metadata.Entry("Name", "the value was empty"));
88+
trailers.Add(new Metadata.Entry("grpc-status-details-bin", Encoding.UTF8.GetBytes("Hello world")));
8489
return Task.FromException<HelloReply>(new RpcException(new Status(StatusCode.InvalidArgument, "Validation failed"), trailers));
8590
}
8691

@@ -110,13 +115,15 @@ Task<HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext c
110115

111116
// Assert
112117
var trailers = call.GetTrailers();
113-
Assert.GreaterOrEqual(trailers.Count, 1);
118+
Assert.GreaterOrEqual(trailers.Count, 2);
114119
Assert.AreEqual("the value was empty", trailers.GetValue("name"));
120+
Assert.AreEqual("Hello world", Encoding.UTF8.GetString(trailers.GetValueBytes("grpc-status-details-bin")));
115121

116122
Assert.AreEqual(StatusCode.InvalidArgument, ex.StatusCode);
117123
Assert.AreEqual("Validation failed", ex.Status.Detail);
118-
Assert.GreaterOrEqual(ex.Trailers.Count, 1);
124+
Assert.GreaterOrEqual(ex.Trailers.Count, 2);
119125
Assert.AreEqual("the value was empty", ex.Trailers.GetValue("name"));
126+
Assert.AreEqual("Hello world", Encoding.UTF8.GetString(ex.Trailers.GetValueBytes("grpc-status-details-bin")));
120127

121128
AssertHasLogRpcConnectionError(StatusCode.InvalidArgument, "Validation failed");
122129
}

test/Grpc.Net.Client.Tests/GrpcProtocolHelpersTests.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
#endregion
1818

19-
using System.Net.Http.Headers;
2019
using Grpc.Net.Client.Internal;
21-
using Grpc.Shared;
2220
using NUnit.Framework;
2321

2422
namespace Grpc.Net.Client.Tests
@@ -54,5 +52,24 @@ public void EncodeTimeout(int milliseconds, string expected)
5452
var encoded = GrpcProtocolHelpers.EncodeTimeout(milliseconds);
5553
Assert.AreEqual(expected, encoded);
5654
}
55+
56+
[TestCase(":status", true)]
57+
[TestCase(":STATUS", true)]
58+
[TestCase("grpc-status", true)]
59+
[TestCase("GRPC-STATUS", true)]
60+
[TestCase("grpc-message", true)]
61+
[TestCase("GRPC-MESSAGE", true)]
62+
[TestCase("grpc-encoding", true)]
63+
[TestCase("GRPC-ENCODING", true)]
64+
[TestCase("grpc-accept-encoding", true)]
65+
[TestCase("GRPC-ACCEPT-ENCODING", true)]
66+
[TestCase("custom", false)]
67+
[TestCase("grpc-status-details-bin", false)]
68+
[TestCase("GRPC-STATUS-DETAILS-BIN", false)]
69+
public void ShouldSkipHeader(string header, bool skipped)
70+
{
71+
var result = GrpcProtocolHelpers.ShouldSkipHeader(header);
72+
Assert.AreEqual(skipped, result);
73+
}
5774
}
5875
}

0 commit comments

Comments
 (0)