Skip to content

Commit

Permalink
Improve grpc-dotnet debugging (#2192)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK authored Jul 16, 2023
1 parent 68caeb7 commit b57e35d
Show file tree
Hide file tree
Showing 20 changed files with 394 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextServerCallContextDebugView))]
internal sealed partial class HttpContextServerCallContext : ServerCallContext, IServerCallContextFeature
{
private static readonly AuthContext UnauthenticatedContext = new AuthContext(null, new Dictionary<string, List<AuthProperty>>());
Expand Down Expand Up @@ -572,4 +574,30 @@ internal void ValidateAcceptEncodingContainsResponseEncoding()
GrpcServerLog.EncodingNotInAcceptEncoding(Logger, resolvedResponseGrpcEncoding);
}
}

private string DebuggerToString() => $"Host = {Host}, Method = {Method}";

private sealed class HttpContextServerCallContextDebugView
{
private readonly HttpContextServerCallContext _context;

public HttpContextServerCallContextDebugView(HttpContextServerCallContext context)
{
_context = context;
}

public AuthContext AuthContext => _context.AuthContext;
public CancellationToken CancellationToken => _context.CancellationToken;
public DateTime Deadline => _context.Deadline;
public string Host => _context.Host;
public string Method => _context.Method;
public string Peer => _context.Peer;
public Metadata RequestHeaders => _context.RequestHeaders;
public Metadata ResponseTrailers => _context.ResponseTrailers;
public Status Status => _context.Status;
public IDictionary<object, object> UserState => _context.UserState;
public WriteOptions? WriteOptions => _context.WriteOptions;

public HttpContext HttpContext => _context.HttpContext;
}
}
23 changes: 22 additions & 1 deletion src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#region Copyright notice and license
#region Copyright notice and license

// Copyright 2019 The gRPC Authors
//
Expand All @@ -16,16 +16,20 @@

#endregion

using System.Diagnostics;
using Grpc.Core;
using Grpc.Shared;

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextStreamReader<>.HttpContextStreamReaderDebugView))]
internal class HttpContextStreamReader<TRequest> : IAsyncStreamReader<TRequest> where TRequest : class
{
private readonly HttpContextServerCallContext _serverCallContext;
private readonly Func<DeserializationContext, TRequest> _deserializer;
private bool _completed;
private long _readCount;

public HttpContextStreamReader(HttpContextServerCallContext serverCallContext, Func<DeserializationContext, TRequest> deserializer)
{
Expand Down Expand Up @@ -75,11 +79,28 @@ private bool ProcessPayload(TRequest? request)
}

Current = request;
Interlocked.Increment(ref _readCount);
return true;
}

public void Complete()
{
_completed = true;
}

private string DebuggerToString() => $"ReadCount = {_readCount}, CallCompleted = {(_completed ? "true" : "false")}";

private sealed class HttpContextStreamReaderDebugView
{
private readonly HttpContextStreamReader<TRequest> _reader;

public HttpContextStreamReaderDebugView(HttpContextStreamReader<TRequest> reader)
{
_reader = reader;
}

public bool ReaderCompleted => _reader._completed;
public long ReadCount => _reader._readCount;
public TRequest Current => _reader.Current;
}
}
22 changes: 22 additions & 0 deletions src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

#endregion

using System.Diagnostics;
using Grpc.Core;

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextStreamWriter<>.HttpContextStreamWriterDebugView))]
internal class HttpContextStreamWriter<TResponse> : IServerStreamWriter<TResponse>
where TResponse : class
{
Expand All @@ -28,6 +31,7 @@ internal class HttpContextStreamWriter<TResponse> : IServerStreamWriter<TRespons
private readonly object _writeLock;
private Task? _writeTask;
private bool _completed;
private long _writeCount;

public HttpContextStreamWriter(HttpContextServerCallContext context, Action<TResponse, SerializationContext> serializer)
{
Expand Down Expand Up @@ -93,6 +97,7 @@ private async Task WriteCoreAsync(TResponse message, CancellationToken cancellat
}

await _writeTask;
Interlocked.Increment(ref _writeCount);
}
finally
{
Expand All @@ -117,4 +122,21 @@ private bool IsWriteInProgressUnsynchronized
return writeTask != null && !writeTask.IsCompleted;
}
}

private string DebuggerToString() => $"WriteCount = {_writeCount}, CallCompleted = {(_completed ? "true" : "false")}";

private sealed class HttpContextStreamWriterDebugView
{
private readonly HttpContextStreamWriter<TResponse> _writer;

public HttpContextStreamWriterDebugView(HttpContextStreamWriter<TResponse> writer)
{
_writer = writer;
}

public bool WriterCompleted => _writer._completed;
public bool IsWriteInProgress => _writer.IsWriteInProgressUnsynchronized;
public long WriteCount => _writer._writeCount;
public WriteOptions? WriteOptions => _writer.WriteOptions;
}
}
1 change: 0 additions & 1 deletion src/Grpc.Core.Api/AsyncCallState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#endregion


using System;
using System.Threading.Tasks;

Expand Down
23 changes: 23 additions & 0 deletions src/Grpc.Core.Api/AsyncClientStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#endregion

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

Expand All @@ -27,6 +29,8 @@ namespace Grpc.Core;
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncClientStreamingCall<,>.AsyncClientStreamingCallDebugView))]
public sealed class AsyncClientStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
Expand Down Expand Up @@ -164,4 +168,23 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncClientStreamingCallDebugView
{
private readonly AsyncClientStreamingCall<TRequest, TResponse> _call;

public AsyncClientStreamingCallDebugView(AsyncClientStreamingCall<TRequest, TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.GetAwaiter().GetResult() : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IClientStreamWriter<TRequest> RequestStream => _call.RequestStream;
public TResponse? Response => _call.ResponseAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseAsync.Result : default;
}
}
23 changes: 23 additions & 0 deletions src/Grpc.Core.Api/AsyncDuplexStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

Expand All @@ -26,6 +28,8 @@ namespace Grpc.Core;
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncDuplexStreamingCall<,>.AsyncDuplexStreamingCallDebugView))]
public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
Expand Down Expand Up @@ -141,4 +145,23 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncDuplexStreamingCallDebugView
{
private readonly AsyncDuplexStreamingCall<TRequest, TResponse> _call;

public AsyncDuplexStreamingCallDebugView(AsyncDuplexStreamingCall<TRequest, TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IAsyncStreamReader<TResponse> ResponseStream => _call.ResponseStream;
public IClientStreamWriter<TRequest> RequestStream => _call.RequestStream;
}
}
22 changes: 22 additions & 0 deletions src/Grpc.Core.Api/AsyncServerStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

/// <summary>
/// Return type for server streaming calls.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncServerStreamingCall<>.AsyncServerStreamingCallDebugView))]
public sealed class AsyncServerStreamingCall<TResponse> : IDisposable
{
readonly IAsyncStreamReader<TResponse> responseStream;
Expand Down Expand Up @@ -122,4 +126,22 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncServerStreamingCallDebugView
{
private readonly AsyncServerStreamingCall<TResponse> _call;

public AsyncServerStreamingCallDebugView(AsyncServerStreamingCall<TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IAsyncStreamReader<TResponse> ResponseStream => _call.ResponseStream;
}
}
23 changes: 22 additions & 1 deletion src/Grpc.Core.Api/AsyncUnaryCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@
#endregion

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

/// <summary>
/// Return type for single request - single response call.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncUnaryCall<>.AsyncUnaryCallDebugView))]
public sealed class AsyncUnaryCall<TResponse> : IDisposable
{
readonly Task<TResponse> responseAsync;
readonly AsyncCallState callState;


/// <summary>
/// Creates a new AsyncUnaryCall object with the specified properties.
/// </summary>
Expand Down Expand Up @@ -146,4 +149,22 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncUnaryCallDebugView
{
private readonly AsyncUnaryCall<TResponse> _call;

public AsyncUnaryCallDebugView(AsyncUnaryCall<TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public TResponse? Response => _call.ResponseAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseAsync.Result : default;
}
}
2 changes: 2 additions & 0 deletions src/Grpc.Core.Api/AuthContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Grpc.Core.Utils;

Expand All @@ -28,6 +29,7 @@ namespace Grpc.Core;
/// Using any other call/context properties for authentication purposes is wrong and inherently unsafe.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
[DebuggerDisplay("IsPeerAuthenticated = {IsPeerAuthenticated}")]
public class AuthContext
{
private readonly string? peerIdentityPropertyName;
Expand Down
Loading

0 comments on commit b57e35d

Please sign in to comment.