[otlp] Add Grpc vendored code (#5981)
This commit is contained in:
parent
913bccfdfc
commit
68c79a3bfd
|
|
@ -29,3 +29,20 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
License notice for gRPC for .NET (https://github.com/grpc/grpc-dotnet)
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
Copyright 2019 The gRPC Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Copyright 2019 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#if NET462
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
|
||||
|
||||
internal static class GrpcProtocolHelpers
|
||||
{
|
||||
internal const string StatusTrailer = "grpc-status";
|
||||
internal const string MessageTrailer = "grpc-message";
|
||||
internal const string CancelledDetail = "No grpc-status found on response.";
|
||||
|
||||
public static Status? GetResponseStatus(HttpHeaders trailingHeaders, HttpResponseMessage httpResponse)
|
||||
{
|
||||
Status? status;
|
||||
try
|
||||
{
|
||||
var result = trailingHeaders.Any() ? TryGetStatusCore(trailingHeaders, out status) : TryGetStatusCore(httpResponse.Headers, out status);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
status = new Status(StatusCode.Cancelled, CancelledDetail);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle error from parsing badly formed status
|
||||
status = new Status(StatusCode.Cancelled, ex.Message, ex);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public static bool TryGetStatusCore(HttpHeaders headers, [NotNullWhen(true)] out Status? status)
|
||||
{
|
||||
var grpcStatus = GetHeaderValue(headers, StatusTrailer);
|
||||
|
||||
// grpc-status is a required trailer
|
||||
if (grpcStatus == null)
|
||||
{
|
||||
status = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
int statusValue;
|
||||
if (!int.TryParse(grpcStatus, out statusValue))
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected grpc-status value: " + grpcStatus);
|
||||
}
|
||||
|
||||
// grpc-message is optional
|
||||
// Always read the gRPC message from the same headers collection as the status
|
||||
var grpcMessage = GetHeaderValue(headers, MessageTrailer);
|
||||
|
||||
if (!string.IsNullOrEmpty(grpcMessage))
|
||||
{
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses
|
||||
// The value portion of Status-Message is conceptually a Unicode string description of the error,
|
||||
// physically encoded as UTF-8 followed by percent-encoding.
|
||||
grpcMessage = Uri.UnescapeDataString(grpcMessage);
|
||||
}
|
||||
|
||||
status = new Status((StatusCode)statusValue, grpcMessage ?? string.Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string? GetHeaderValue(HttpHeaders? headers, string name, bool first = false)
|
||||
{
|
||||
if (headers == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
if (!headers.NonValidated.TryGetValues(name, out var values))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var e = values.GetEnumerator())
|
||||
{
|
||||
if (!e.MoveNext())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = e.Current;
|
||||
if (!e.MoveNext())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Multiple {name} headers.");
|
||||
#else
|
||||
if (!headers.TryGetValues(name, out var values))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// HttpHeaders appears to always return an array, but fallback to converting values to one just in case
|
||||
var valuesArray = values as string[] ?? values.ToArray();
|
||||
|
||||
switch (valuesArray.Length)
|
||||
{
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return valuesArray[0];
|
||||
default:
|
||||
if (first)
|
||||
{
|
||||
return valuesArray[0];
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Multiple {name} headers.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
|
||||
|
||||
/// <summary>
|
||||
/// Represents RPC result, which consists of <see cref="StatusCode"/> and an optional detail string.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
||||
internal struct Status
|
||||
{
|
||||
/// <summary>
|
||||
/// Default result of a successful RPC. StatusCode=OK, empty details message.
|
||||
/// </summary>
|
||||
public static readonly Status DefaultSuccess = new Status(StatusCode.OK, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Default result of a cancelled RPC. StatusCode=Cancelled, empty details message.
|
||||
/// </summary>
|
||||
public static readonly Status DefaultCancelled = new Status(StatusCode.Cancelled, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Status"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="statusCode">Status code.</param>
|
||||
/// <param name="detail">Detail.</param>
|
||||
public Status(StatusCode statusCode, string detail)
|
||||
: this(statusCode, detail, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Status"/> struct.
|
||||
/// Users should not use this constructor, except for creating instances for testing.
|
||||
/// The debug error string should only be populated by gRPC internals.
|
||||
/// Note: experimental API that can change or be removed without any prior notice.
|
||||
/// </summary>
|
||||
/// <param name="statusCode">Status code.</param>
|
||||
/// <param name="detail">Detail.</param>
|
||||
/// <param name="debugException">Optional internal error details.</param>
|
||||
public Status(StatusCode statusCode, string detail, Exception? debugException)
|
||||
{
|
||||
this.StatusCode = statusCode;
|
||||
this.Detail = detail;
|
||||
this.DebugException = debugException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gRPC status code. OK indicates success, all other values indicate an error.
|
||||
/// </summary>
|
||||
public StatusCode StatusCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the detail.
|
||||
/// </summary>
|
||||
public string Detail { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets in case of an error, this field may contain additional error details to help with debugging.
|
||||
/// This field will be only populated on a client and its value is generated locally,
|
||||
/// based on the internal state of the gRPC client stack (i.e. the value is never sent over the wire).
|
||||
/// Note that this field is available only for debugging purposes, the application logic should
|
||||
/// never rely on values of this field (it should use <c>StatusCode</c> and <c>Detail</c> instead).
|
||||
/// Example: when a client fails to connect to a server, this field may provide additional details
|
||||
/// why the connection to the server has failed.
|
||||
/// Note: experimental API that can change or be removed without any prior notice.
|
||||
/// </summary>
|
||||
public Exception? DebugException { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.DebugException != null)
|
||||
{
|
||||
return $"Status(StatusCode=\"{this.StatusCode}\", Detail=\"{this.Detail}\"," +
|
||||
$" DebugException=\"{this.DebugException.GetType()}: {this.DebugException.Message}\")";
|
||||
}
|
||||
|
||||
return $"Status(StatusCode=\"{this.StatusCode}\", Detail=\"{this.Detail}\")";
|
||||
}
|
||||
|
||||
private string DebuggerToString()
|
||||
{
|
||||
var text = $"StatusCode = {this.StatusCode}";
|
||||
if (!string.IsNullOrEmpty(this.Detail))
|
||||
{
|
||||
text += $@", Detail = ""{this.Detail}""";
|
||||
}
|
||||
|
||||
if (this.DebugException != null)
|
||||
{
|
||||
text += $@", DebugException = ""{this.DebugException.GetType()}: {this.DebugException.Message}""";
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a remote procedure call.
|
||||
/// Based on grpc_status_code from grpc/status.h.
|
||||
/// </summary>
|
||||
internal enum StatusCode
|
||||
{
|
||||
/// <summary>Not an error; returned on success.</summary>
|
||||
OK = 0,
|
||||
|
||||
/// <summary>The operation was cancelled (typically by the caller).</summary>
|
||||
Cancelled = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown error. An example of where this error may be returned is
|
||||
/// if a Status value received from another address space belongs to
|
||||
/// an error-space that is not known in this address space. Also
|
||||
/// errors raised by APIs that do not return enough error information
|
||||
/// may be converted to this error.
|
||||
/// </summary>
|
||||
Unknown = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Client specified an invalid argument. Note that this differs
|
||||
/// from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
|
||||
/// that are problematic regardless of the state of the system
|
||||
/// (e.g., a malformed file name).
|
||||
/// </summary>
|
||||
InvalidArgument = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Deadline expired before operation could complete. For operations
|
||||
/// that change the state of the system, this error may be returned
|
||||
/// even if the operation has completed successfully. For example, a
|
||||
/// successful response from a server could have been delayed long
|
||||
/// enough for the deadline to expire.
|
||||
/// </summary>
|
||||
DeadlineExceeded = 4,
|
||||
|
||||
/// <summary>Some requested entity (e.g., file or directory) was not found.</summary>
|
||||
NotFound = 5,
|
||||
|
||||
/// <summary>Some entity that we attempted to create (e.g., file or directory) already exists.</summary>
|
||||
AlreadyExists = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The caller does not have permission to execute the specified
|
||||
/// operation. PERMISSION_DENIED must not be used for rejections
|
||||
/// caused by exhausting some resource (use RESOURCE_EXHAUSTED
|
||||
/// instead for those errors). PERMISSION_DENIED must not be
|
||||
/// used if the caller can not be identified (use UNAUTHENTICATED
|
||||
/// instead for those errors).
|
||||
/// </summary>
|
||||
PermissionDenied = 7,
|
||||
|
||||
/// <summary>The request does not have valid authentication credentials for the operation.</summary>
|
||||
Unauthenticated = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Some resource has been exhausted, perhaps a per-user quota, or
|
||||
/// perhaps the entire file system is out of space.
|
||||
/// </summary>
|
||||
ResourceExhausted = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Operation was rejected because the system is not in a state
|
||||
/// required for the operation's execution. For example, directory
|
||||
/// to be deleted may be non-empty, an rmdir operation is applied to
|
||||
/// a non-directory, etc.
|
||||
/// </summary>
|
||||
FailedPrecondition = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The operation was aborted, typically due to a concurrency issue
|
||||
/// like sequencer check failures, transaction aborts, etc.
|
||||
/// </summary>
|
||||
Aborted = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Operation was attempted past the valid range. E.g., seeking or
|
||||
/// reading past end of file.
|
||||
/// </summary>
|
||||
OutOfRange = 11,
|
||||
|
||||
/// <summary>Operation is not implemented or not supported/enabled in this service.</summary>
|
||||
Unimplemented = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Internal errors. Means some invariants expected by underlying
|
||||
/// system has been broken. If you see one of these errors,
|
||||
/// something is very broken.
|
||||
/// </summary>
|
||||
Internal = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The service is currently unavailable. This is a most likely a
|
||||
/// transient condition and may be corrected by retrying with
|
||||
/// a backoff. Note that it is not always safe to retry
|
||||
/// non-idempotent operations.
|
||||
/// </summary>
|
||||
Unavailable = 14,
|
||||
|
||||
/// <summary>Unrecoverable data loss or corruption.</summary>
|
||||
DataLoss = 15,
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Copyright 2019 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if NET462
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc;
|
||||
|
||||
internal static class TrailingHeadersHelpers
|
||||
{
|
||||
public static readonly string ResponseTrailersKey = "__ResponseTrailers";
|
||||
|
||||
public static HttpHeaders TrailingHeaders(this HttpResponseMessage responseMessage)
|
||||
{
|
||||
#if !NETSTANDARD2_0 && !NET462
|
||||
return responseMessage.TrailingHeaders;
|
||||
#else
|
||||
if (responseMessage.RequestMessage.Properties.TryGetValue(ResponseTrailersKey, out var headers) &&
|
||||
headers is HttpHeaders httpHeaders)
|
||||
{
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
// App targets .NET Standard 2.0 and the handler hasn't set trailers
|
||||
// in RequestMessage.Properties with known key. Return empty collection.
|
||||
// Client call will likely fail because it is unable to get a grpc-status.
|
||||
return ResponseTrailers.Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0 || NET462
|
||||
public static void EnsureTrailingHeaders(this HttpResponseMessage responseMessage)
|
||||
{
|
||||
if (!responseMessage.RequestMessage.Properties.ContainsKey(ResponseTrailersKey))
|
||||
{
|
||||
responseMessage.RequestMessage.Properties[ResponseTrailersKey] = new ResponseTrailers();
|
||||
}
|
||||
}
|
||||
|
||||
private class ResponseTrailers : HttpHeaders
|
||||
{
|
||||
public static readonly ResponseTrailers Empty = new ResponseTrailers();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Loading…
Reference in New Issue