make ServerCallContext an abstract base class
diff --git a/src/csharp/Grpc.Core.Testing/TestServerCallContext.cs b/src/csharp/Grpc.Core.Testing/TestServerCallContext.cs
index d72e98e..7a4fb15 100644
--- a/src/csharp/Grpc.Core.Testing/TestServerCallContext.cs
+++ b/src/csharp/Grpc.Core.Testing/TestServerCallContext.cs
@@ -36,12 +36,73 @@
string peer, AuthContext authContext, ContextPropagationToken contextPropagationToken,
Func<Metadata, Task> writeHeadersFunc, Func<WriteOptions> writeOptionsGetter, Action<WriteOptions> writeOptionsSetter)
{
- return new ServerCallContext(null, method, host, deadline, requestHeaders, cancellationToken,
- (ctx, extraData, headers) => writeHeadersFunc(headers),
- (ctx, extraData) => writeOptionsGetter(),
- (ctx, extraData, options) => writeOptionsSetter(options),
- (ctx, extraData) => peer, (ctx, callHandle) => authContext,
- (ctx, callHandle, options) => contextPropagationToken);
+ return new TestingServerCallContext(method, host, deadline, requestHeaders, cancellationToken, peer,
+ authContext, contextPropagationToken, writeHeadersFunc, writeOptionsGetter, writeOptionsSetter);
+ }
+
+ private class TestingServerCallContext : ServerCallContext
+ {
+ private readonly string method;
+ private readonly string host;
+ private readonly DateTime deadline;
+ private readonly Metadata requestHeaders;
+ private readonly CancellationToken cancellationToken;
+ private readonly Metadata responseTrailers = new Metadata();
+ private Status status;
+ private readonly string peer;
+ private readonly AuthContext authContext;
+ private readonly ContextPropagationToken contextPropagationToken;
+ private readonly Func<Metadata, Task> writeHeadersFunc;
+ private readonly Func<WriteOptions> writeOptionsGetter;
+ private readonly Action<WriteOptions> writeOptionsSetter;
+
+ public TestingServerCallContext(string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken,
+ string peer, AuthContext authContext, ContextPropagationToken contextPropagationToken,
+ Func<Metadata, Task> writeHeadersFunc, Func<WriteOptions> writeOptionsGetter, Action<WriteOptions> writeOptionsSetter)
+ {
+ this.method = method;
+ this.host = host;
+ this.deadline = deadline;
+ this.requestHeaders = requestHeaders;
+ this.cancellationToken = cancellationToken;
+ this.responseTrailers = new Metadata();
+ this.status = Status.DefaultSuccess;
+ this.peer = peer;
+ this.authContext = authContext;
+ this.contextPropagationToken = contextPropagationToken;
+ this.writeHeadersFunc = writeHeadersFunc;
+ this.writeOptionsGetter = writeOptionsGetter;
+ this.writeOptionsSetter = writeOptionsSetter;
+ }
+
+ protected override string MethodInternal => method;
+
+ protected override string HostInternal => host;
+
+ protected override string PeerInternal => peer;
+
+ protected override DateTime DeadlineInternal => deadline;
+
+ protected override Metadata RequestHeadersInternal => requestHeaders;
+
+ protected override CancellationToken CancellationTokenInternal => cancellationToken;
+
+ protected override Metadata ResponseTrailersInternal => responseTrailers;
+
+ protected override Status StatusInternal { get => status; set => status = value; }
+ protected override WriteOptions WriteOptionsInternal { get => writeOptionsGetter(); set => writeOptionsSetter(value); }
+
+ protected override AuthContext AuthContextInternal => authContext;
+
+ protected override ContextPropagationToken CreatePropagationTokenInternal(ContextPropagationOptions options)
+ {
+ return contextPropagationToken;
+ }
+
+ protected override Task WriteResponseHeadersInternalAsync(Metadata responseHeaders)
+ {
+ return writeHeadersFunc(responseHeaders);
+ }
}
}
}
diff --git a/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs b/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs
new file mode 100644
index 0000000..1e484bd
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs
@@ -0,0 +1,111 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+ /// <summary>
+ /// Default implementation of <c>ServerCallContext</c>.
+ /// </summary>
+ internal class DefaultServerCallContext : ServerCallContext
+ {
+ private readonly CallSafeHandle callHandle;
+ private readonly string method;
+ private readonly string host;
+ private readonly DateTime deadline;
+ private readonly Metadata requestHeaders;
+ private readonly CancellationToken cancellationToken;
+ private readonly Metadata responseTrailers;
+ private Status status;
+ private readonly IServerResponseStream serverResponseStream;
+ private readonly Lazy<AuthContext> authContext;
+
+ /// <summary>
+ /// Creates a new instance of <c>ServerCallContext</c>.
+ /// To allow reuse of ServerCallContext API by different gRPC implementations, the implementation of some members is provided externally.
+ /// To provide state, this <c>ServerCallContext</c> instance and <c>extraData</c> will be passed to the member implementations.
+ /// </summary>
+ internal DefaultServerCallContext(CallSafeHandle callHandle, string method, string host, DateTime deadline,
+ Metadata requestHeaders, CancellationToken cancellationToken, IServerResponseStream serverResponseStream)
+ {
+ this.callHandle = callHandle;
+ this.method = method;
+ this.host = host;
+ this.deadline = deadline;
+ this.requestHeaders = requestHeaders;
+ this.cancellationToken = cancellationToken;
+ this.responseTrailers = new Metadata();
+ this.status = Status.DefaultSuccess;
+ this.serverResponseStream = serverResponseStream;
+ // TODO(jtattermusch): avoid unnecessary allocation of factory function and the lazy object
+ this.authContext = new Lazy<AuthContext>(GetAuthContextEager);
+ }
+
+ protected override ContextPropagationToken CreatePropagationTokenInternal(ContextPropagationOptions options)
+ {
+ return new ContextPropagationToken(callHandle, deadline, cancellationToken, options);
+ }
+
+ protected override Task WriteResponseHeadersInternalAsync(Metadata responseHeaders)
+ {
+ return serverResponseStream.WriteResponseHeadersAsync(responseHeaders);
+ }
+
+ protected override string MethodInternal => method;
+
+ protected override string HostInternal => host;
+
+ protected override string PeerInternal => callHandle.GetPeer();
+
+ protected override DateTime DeadlineInternal => deadline;
+
+ protected override Metadata RequestHeadersInternal => requestHeaders;
+
+ protected override CancellationToken CancellationTokenInternal => cancellationToken;
+
+ protected override Metadata ResponseTrailersInternal => responseTrailers;
+
+ protected override Status StatusInternal
+ {
+ get => status;
+ set => status = value;
+ }
+
+ protected override WriteOptions WriteOptionsInternal
+ {
+ get => serverResponseStream.WriteOptions;
+ set => serverResponseStream.WriteOptions = value;
+ }
+
+ protected override AuthContext AuthContextInternal => authContext.Value;
+
+ private AuthContext GetAuthContextEager()
+ {
+ using (var authContextNative = callHandle.GetAuthContext())
+ {
+ return authContextNative.ToAuthContext();
+ }
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallContextExtraData.cs b/src/csharp/Grpc.Core/Internal/ServerCallContextExtraData.cs
deleted file mode 100644
index 97b95e6..0000000
--- a/src/csharp/Grpc.Core/Internal/ServerCallContextExtraData.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-#region Copyright notice and license
-
-// 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.
-
-#endregion
-
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Grpc.Core.Internal
-{
- /// <summary>
- /// Additional state for <c>ServerCallContext</c>.
- /// Storing the extra state outside of <c>ServerCallContext</c> allows it to be implementation-agnostic.
- /// </summary>
- internal class ServerCallContextExtraData
- {
- readonly CallSafeHandle callHandle;
- readonly IServerResponseStream serverResponseStream;
- readonly Lazy<AuthContext> cachedAuthContext;
-
- public ServerCallContextExtraData(CallSafeHandle callHandle, IServerResponseStream serverResponseStream)
- {
- this.callHandle = callHandle;
- this.serverResponseStream = serverResponseStream;
- // TODO(jtattermusch): avoid unnecessary allocation of factory function and the lazy object.
- this.cachedAuthContext = new Lazy<AuthContext>(GetAuthContextEager);
- }
-
- public ServerCallContext NewServerCallContext(ServerRpcNew newRpc, CancellationToken cancellationToken)
- {
- DateTime realtimeDeadline = newRpc.Deadline.ToClockType(ClockType.Realtime).ToDateTime();
-
- return new ServerCallContext(this, newRpc.Method, newRpc.Host, realtimeDeadline,
- newRpc.RequestMetadata, cancellationToken,
- ServerCallContext_WriteHeadersFunc, ServerCallContext_WriteOptionsGetter, ServerCallContext_WriteOptionsSetter,
- ServerCallContext_PeerGetter, ServerCallContext_AuthContextGetter, ServerCallContext_ContextPropagationTokenFactory);
- }
-
- private AuthContext GetAuthContextEager()
- {
- using (var authContextNative = callHandle.GetAuthContext())
- {
- return authContextNative.ToAuthContext();
- }
- }
-
- // Implementors of ServerCallContext's members are pre-allocated to avoid unneccessary delegate allocations.
- readonly static Func<ServerCallContext, object, Metadata, Task> ServerCallContext_WriteHeadersFunc = (ctx, extraData, headers) =>
- {
- return ((ServerCallContextExtraData)extraData).serverResponseStream.WriteResponseHeadersAsync(headers);
- };
-
- readonly static Func<ServerCallContext, object, WriteOptions> ServerCallContext_WriteOptionsGetter = (ctx, extraData) =>
- {
-
- return ((ServerCallContextExtraData)extraData).serverResponseStream.WriteOptions;
- };
-
- readonly static Action<ServerCallContext, object, WriteOptions> ServerCallContext_WriteOptionsSetter = (ctx, extraData, options) =>
- {
- ((ServerCallContextExtraData)extraData).serverResponseStream.WriteOptions = options;
- };
-
- readonly static Func<ServerCallContext, object, string> ServerCallContext_PeerGetter = (ctx, extraData) =>
- {
- // Getting the peer lazily is fine as the native call is guaranteed
- // not to be disposed before user-supplied server side handler returns.
- // Most users won't need to read this field anyway.
- return ((ServerCallContextExtraData)extraData).callHandle.GetPeer();
- };
-
- readonly static Func<ServerCallContext, object, AuthContext> ServerCallContext_AuthContextGetter = (ctx, extraData) =>
- {
- return ((ServerCallContextExtraData)extraData).cachedAuthContext.Value;
- };
-
- readonly static Func<ServerCallContext, object, ContextPropagationOptions, ContextPropagationToken> ServerCallContext_ContextPropagationTokenFactory = (ctx, extraData, options) =>
- {
- var callHandle = ((ServerCallContextExtraData)extraData).callHandle;
- return new ContextPropagationToken(callHandle, ctx.Deadline, ctx.CancellationToken, options);
- };
- }
-}
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index ae586f7..c3859f1 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -348,9 +348,7 @@
public static ServerCallContext NewContext(ServerRpcNew newRpc, IServerResponseStream serverResponseStream, CancellationToken cancellationToken)
{
DateTime realtimeDeadline = newRpc.Deadline.ToClockType(ClockType.Realtime).ToDateTime();
-
- var contextExtraData = new ServerCallContextExtraData(newRpc.Call, serverResponseStream);
- return contextExtraData.NewServerCallContext(newRpc, cancellationToken);
+ return new DefaultServerCallContext(newRpc.Call, newRpc.Method, newRpc.Host, realtimeDeadline, newRpc.RequestMetadata, cancellationToken, serverResponseStream);
}
}
}
diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs
index 05c20ca..4a2fdf3 100644
--- a/src/csharp/Grpc.Core/ServerCallContext.cs
+++ b/src/csharp/Grpc.Core/ServerCallContext.cs
@@ -28,51 +28,13 @@
/// <summary>
/// Context for a server-side call.
/// </summary>
- public class ServerCallContext
+ public abstract class ServerCallContext
{
- private readonly object extraData;
- private readonly string method;
- private readonly string host;
- private readonly DateTime deadline;
- private readonly Metadata requestHeaders;
- private readonly CancellationToken cancellationToken;
- private readonly Metadata responseTrailers = new Metadata();
- private readonly Func<ServerCallContext, object, Metadata, Task> writeHeadersFunc;
- private readonly Func<ServerCallContext, object, WriteOptions> writeOptionsGetter;
- private readonly Action<ServerCallContext, object, WriteOptions> writeOptionsSetter;
-
- private readonly Func<ServerCallContext, object, string> peerGetter;
- private readonly Func<ServerCallContext, object, AuthContext> authContextGetter;
- private readonly Func<ServerCallContext, object, ContextPropagationOptions, ContextPropagationToken> contextPropagationTokenFactory;
-
- private Status status = Status.DefaultSuccess;
-
/// <summary>
/// Creates a new instance of <c>ServerCallContext</c>.
- /// To allow reuse of ServerCallContext API by different gRPC implementations, the implementation of some members is provided externally.
- /// To provide state, this <c>ServerCallContext</c> instance and <c>extraData</c> will be passed to the member implementations.
/// </summary>
- internal ServerCallContext(object extraData,
- string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken,
- Func<ServerCallContext, object, Metadata, Task> writeHeadersFunc,
- Func<ServerCallContext, object, WriteOptions> writeOptionsGetter,
- Action<ServerCallContext, object, WriteOptions> writeOptionsSetter,
- Func<ServerCallContext, object, string> peerGetter,
- Func<ServerCallContext, object, AuthContext> authContextGetter,
- Func<ServerCallContext, object, ContextPropagationOptions, ContextPropagationToken> contextPropagationTokenFactory)
+ protected ServerCallContext()
{
- this.extraData = extraData;
- this.method = method;
- this.host = host;
- this.deadline = deadline;
- this.requestHeaders = requestHeaders;
- this.cancellationToken = cancellationToken;
- this.writeHeadersFunc = GrpcPreconditions.CheckNotNull(writeHeadersFunc);
- this.writeOptionsGetter = GrpcPreconditions.CheckNotNull(writeOptionsGetter);
- this.writeOptionsSetter = GrpcPreconditions.CheckNotNull(writeOptionsSetter);
- this.peerGetter = GrpcPreconditions.CheckNotNull(peerGetter);
- this.authContextGetter = GrpcPreconditions.CheckNotNull(authContextGetter);
- this.contextPropagationTokenFactory = GrpcPreconditions.CheckNotNull(contextPropagationTokenFactory);
}
/// <summary>
@@ -84,7 +46,7 @@
/// <returns>The task that finished once response headers have been written.</returns>
public Task WriteResponseHeadersAsync(Metadata responseHeaders)
{
- return writeHeadersFunc(this, extraData, responseHeaders);
+ return WriteResponseHeadersInternalAsync(responseHeaders);
}
/// <summary>
@@ -92,83 +54,41 @@
/// </summary>
public ContextPropagationToken CreatePropagationToken(ContextPropagationOptions options = null)
{
- return contextPropagationTokenFactory(this, extraData, options);
+ return CreatePropagationTokenInternal(options);
}
/// <summary>Name of method called in this RPC.</summary>
- public string Method
- {
- get
- {
- return this.method;
- }
- }
+ public string Method => MethodInternal;
/// <summary>Name of host called in this RPC.</summary>
- public string Host
- {
- get
- {
- return this.host;
- }
- }
+ public string Host => HostInternal;
/// <summary>Address of the remote endpoint in URI format.</summary>
- public string Peer
- {
- get
- {
- return peerGetter(this, extraData);
- }
- }
+ public string Peer => PeerInternal;
/// <summary>Deadline for this RPC.</summary>
- public DateTime Deadline
- {
- get
- {
- return this.deadline;
- }
- }
+ public DateTime Deadline => DeadlineInternal;
/// <summary>Initial metadata sent by client.</summary>
- public Metadata RequestHeaders
- {
- get
- {
- return this.requestHeaders;
- }
- }
+ public Metadata RequestHeaders => RequestHeadersInternal;
/// <summary>Cancellation token signals when call is cancelled.</summary>
- public CancellationToken CancellationToken
- {
- get
- {
- return this.cancellationToken;
- }
- }
+ public CancellationToken CancellationToken => CancellationTokenInternal;
/// <summary>Trailers to send back to client after RPC finishes.</summary>
- public Metadata ResponseTrailers
- {
- get
- {
- return this.responseTrailers;
- }
- }
+ public Metadata ResponseTrailers => ResponseTrailersInternal;
/// <summary> Status to send back to client after RPC finishes.</summary>
public Status Status
{
get
{
- return this.status;
+ return StatusInternal;
}
set
{
- status = value;
+ StatusInternal = value;
}
}
@@ -181,12 +101,12 @@
{
get
{
- return writeOptionsGetter(this, extraData);
+ return WriteOptionsInternal;
}
set
{
- writeOptionsSetter(this, extraData, value);
+ WriteOptionsInternal = value;
}
}
@@ -194,12 +114,31 @@
/// Gets the <c>AuthContext</c> associated with this call.
/// Note: Access to AuthContext is an experimental API that can change without any prior notice.
/// </summary>
- public AuthContext AuthContext
- {
- get
- {
- return authContextGetter(this, extraData);
- }
- }
+ public AuthContext AuthContext => AuthContextInternal;
+
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract Task WriteResponseHeadersInternalAsync(Metadata responseHeaders);
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract ContextPropagationToken CreatePropagationTokenInternal(ContextPropagationOptions options);
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract string MethodInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract string HostInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract string PeerInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract DateTime DeadlineInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract Metadata RequestHeadersInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract CancellationToken CancellationTokenInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract Metadata ResponseTrailersInternal { get; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract Status StatusInternal { get; set; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract WriteOptions WriteOptionsInternal { get; set; }
+ /// <summary>Provides implementation of a non-virtual public member.</summary>
+ protected abstract AuthContext AuthContextInternal { get; }
}
}