Skip to content

Commit df90d3b

Browse files
authored
Merge branch 'main' into users/dulikvor/markinstrumentation
2 parents 2b1abe4 + f9a0b4c commit df90d3b

19 files changed

+1286
-62
lines changed

THIRD-PARTY-NOTICES.TXT

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,20 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2929
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3030
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3131
SOFTWARE.
32+
33+
License notice for gRPC for .NET (https://github.com/grpc/grpc-dotnet)
34+
----------------------------------------------------------------------------------------------
35+
36+
Copyright 2019 The gRPC Authors
37+
38+
Licensed under the Apache License, Version 2.0 (the "License");
39+
you may not use this file except in compliance with the License.
40+
You may obtain a copy of the License at
41+
42+
http://www.apache.org/licenses/LICENSE-2.0
43+
44+
Unless required by applicable law or agreed to in writing, software
45+
distributed under the License is distributed on an "AS IS" BASIS,
46+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47+
See the License for the specific language governing permissions and
48+
limitations under the License.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Copyright 2019 The gRPC Authors
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
using System.Diagnostics.CodeAnalysis;
19+
#if NET462
20+
using System.Net.Http;
21+
#endif
22+
using System.Net.Http.Headers;
23+
24+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
25+
26+
internal static class GrpcProtocolHelpers
27+
{
28+
internal const string StatusTrailer = "grpc-status";
29+
internal const string MessageTrailer = "grpc-message";
30+
internal const string CancelledDetail = "No grpc-status found on response.";
31+
32+
public static Status? GetResponseStatus(HttpHeaders trailingHeaders, HttpResponseMessage httpResponse)
33+
{
34+
Status? status;
35+
try
36+
{
37+
var result = trailingHeaders.Any() ? TryGetStatusCore(trailingHeaders, out status) : TryGetStatusCore(httpResponse.Headers, out status);
38+
39+
if (!result)
40+
{
41+
status = new Status(StatusCode.Cancelled, CancelledDetail);
42+
}
43+
}
44+
catch (Exception ex)
45+
{
46+
// Handle error from parsing badly formed status
47+
status = new Status(StatusCode.Cancelled, ex.Message, ex);
48+
}
49+
50+
return status;
51+
}
52+
53+
public static bool TryGetStatusCore(HttpHeaders headers, [NotNullWhen(true)] out Status? status)
54+
{
55+
var grpcStatus = GetHeaderValue(headers, StatusTrailer);
56+
57+
// grpc-status is a required trailer
58+
if (grpcStatus == null)
59+
{
60+
status = null;
61+
return false;
62+
}
63+
64+
int statusValue;
65+
if (!int.TryParse(grpcStatus, out statusValue))
66+
{
67+
throw new InvalidOperationException("Unexpected grpc-status value: " + grpcStatus);
68+
}
69+
70+
// grpc-message is optional
71+
// Always read the gRPC message from the same headers collection as the status
72+
var grpcMessage = GetHeaderValue(headers, MessageTrailer);
73+
74+
if (!string.IsNullOrEmpty(grpcMessage))
75+
{
76+
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses
77+
// The value portion of Status-Message is conceptually a Unicode string description of the error,
78+
// physically encoded as UTF-8 followed by percent-encoding.
79+
grpcMessage = Uri.UnescapeDataString(grpcMessage);
80+
}
81+
82+
status = new Status((StatusCode)statusValue, grpcMessage ?? string.Empty);
83+
return true;
84+
}
85+
86+
public static string? GetHeaderValue(HttpHeaders? headers, string name, bool first = false)
87+
{
88+
if (headers == null)
89+
{
90+
return null;
91+
}
92+
93+
#if NET6_0_OR_GREATER
94+
if (!headers.NonValidated.TryGetValues(name, out var values))
95+
{
96+
return null;
97+
}
98+
99+
using (var e = values.GetEnumerator())
100+
{
101+
if (!e.MoveNext())
102+
{
103+
return null;
104+
}
105+
106+
var result = e.Current;
107+
if (!e.MoveNext())
108+
{
109+
return result;
110+
}
111+
112+
if (first)
113+
{
114+
return result;
115+
}
116+
}
117+
118+
throw new InvalidOperationException($"Multiple {name} headers.");
119+
#else
120+
if (!headers.TryGetValues(name, out var values))
121+
{
122+
return null;
123+
}
124+
125+
// HttpHeaders appears to always return an array, but fallback to converting values to one just in case
126+
var valuesArray = values as string[] ?? values.ToArray();
127+
128+
switch (valuesArray.Length)
129+
{
130+
case 0:
131+
return null;
132+
case 1:
133+
return valuesArray[0];
134+
default:
135+
if (first)
136+
{
137+
return valuesArray[0];
138+
}
139+
140+
throw new InvalidOperationException($"Multiple {name} headers.");
141+
}
142+
#endif
143+
}
144+
}

0 commit comments

Comments
 (0)