renaming stub to client and refactoring metadata class

pull/2459/head
Jan Tattermusch 10 years ago
parent 5a9674c0e7
commit e5c9b80566
  1. 8
      src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs
  2. 2
      src/csharp/Grpc.Core/Calls.cs
  3. 34
      src/csharp/Grpc.Core/ClientBase.cs
  4. 3
      src/csharp/Grpc.Core/Grpc.Core.csproj
  5. 8
      src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
  6. 179
      src/csharp/Grpc.Core/Metadata.cs
  7. 64
      src/csharp/Grpc.Core/Stub/StubConfiguration.cs
  8. 1
      src/csharp/Grpc.Core/Version.cs
  9. 14
      src/csharp/Grpc.Examples.MathClient/MathClient.cs
  10. 26
      src/csharp/Grpc.Examples/MathExamples.cs
  11. 2
      src/csharp/Grpc.Examples/MathGrpc.cs
  12. 2
      src/csharp/Grpc.HealthCheck/HealthGrpc.cs
  13. 2
      src/csharp/Grpc.IntegrationTesting/TestGrpc.cs

@ -52,10 +52,10 @@ namespace Grpc.Auth
/// <summary>
/// Creates OAuth2 interceptor.
/// </summary>
public static HeaderInterceptorDelegate Create(GoogleCredential googleCredential)
public static MetadataInterceptorDelegate Create(GoogleCredential googleCredential)
{
var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default);
return new HeaderInterceptorDelegate(interceptor.InterceptHeaders);
return new MetadataInterceptorDelegate(interceptor.InterceptHeaders);
}
/// <summary>
@ -94,10 +94,10 @@ namespace Grpc.Auth
return credential.Token.AccessToken;
}
public void InterceptHeaders(Metadata.Builder headerBuilder)
public void InterceptHeaders(Metadata metadata)
{
var accessToken = GetAccessToken(CancellationToken.None);
headerBuilder.Add(new Metadata.MetadataEntry(AuthorizationHeader, Schema + " " + accessToken));
metadata.Add(new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken));
}
}
}

@ -39,7 +39,7 @@ using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>
/// Helper methods for generated client stubs to make RPC calls.
/// Helper methods for generated clients to make RPC calls.
/// </summary>
public static class Calls
{

@ -32,26 +32,39 @@
#endregion
using System;
using System.Collections.Generic;
using Grpc.Core.Internal;
namespace Grpc.Core
{
// TODO: support adding timeout to methods.
public delegate void MetadataInterceptorDelegate(Metadata metadata);
/// <summary>
/// Base for client-side stubs.
/// Base class for client-side stubs.
/// </summary>
public abstract class AbstractStub<TStub, TConfig>
where TConfig : StubConfiguration
public abstract class ClientBase
{
readonly Channel channel;
readonly TConfig config;
public AbstractStub(Channel channel, TConfig config)
public ClientBase(Channel channel)
{
this.channel = channel;
this.config = config;
}
/// <summary>
/// Can be used to register a custom header (initial metadata) interceptor.
/// The delegate each time before a new call on this client is started.
/// </summary>
public MetadataInterceptorDelegate HeaderInterceptor
{
get;
set;
}
/// <summary>
/// Channel associated with this client.
/// </summary>
public Channel Channel
{
get
@ -67,9 +80,10 @@ namespace Grpc.Core
where TRequest : class
where TResponse : class
{
var headerBuilder = Metadata.CreateBuilder();
config.HeaderInterceptor(headerBuilder);
return new Call<TRequest, TResponse>(serviceName, method, channel, headerBuilder.Build());
var metadata = new Metadata();
HeaderInterceptor(metadata);
metadata.Freeze();
return new Call<TRequest, TResponse>(serviceName, method, channel, metadata);
}
}
}

@ -88,8 +88,7 @@
<Compile Include="ServerCredentials.cs" />
<Compile Include="Metadata.cs" />
<Compile Include="Internal\MetadataArraySafeHandle.cs" />
<Compile Include="Stub\AbstractStub.cs" />
<Compile Include="Stub\StubConfiguration.cs" />
<Compile Include="ClientBase.cs" />
<Compile Include="Internal\ServerCalls.cs" />
<Compile Include="ServerMethods.cs" />
<Compile Include="Internal\ClientRequestStream.cs" />

@ -54,11 +54,11 @@ namespace Grpc.Core.Internal
public static MetadataArraySafeHandle Create(Metadata metadata)
{
var entries = metadata.Entries;
var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)entries.Count));
for (int i = 0; i < entries.Count; i++)
// TODO(jtattermusch): we might wanna check that the metadata is readonly
var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
for (int i = 0; i < metadata.Count; i++)
{
grpcsharp_metadata_array_add(metadataArray, entries[i].Key, entries[i].ValueBytes, new UIntPtr((ulong)entries[i].ValueBytes.Length));
grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, metadata[i].ValueBytes, new UIntPtr((ulong)metadata[i].ValueBytes.Length));
}
return metadataArray;
}

@ -30,55 +30,163 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Text;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// gRPC call metadata.
/// Provides access to read and write metadata values to be exchanged during a call.
/// </summary>
public class Metadata
public sealed class Metadata : IList<Metadata.Entry>
{
public static readonly Metadata Empty = new Metadata(ImmutableList<MetadataEntry>.Empty);
/// <summary>
/// An read-only instance of metadata containing no entries.
/// </summary>
public static readonly Metadata Empty = new Metadata().Freeze();
readonly List<Entry> entries;
bool readOnly;
public Metadata()
{
this.entries = new List<Entry>();
}
public Metadata(ICollection<Entry> entries)
{
this.entries = new List<Entry>(entries);
}
/// <summary>
/// Makes this object read-only.
/// </summary>
/// <returns>this object</returns>
public Metadata Freeze()
{
this.readOnly = true;
return this;
}
// TODO: add support for access by key
#region IList members
public int IndexOf(Metadata.Entry item)
{
return entries.IndexOf(item);
}
readonly ImmutableList<MetadataEntry> entries;
public void Insert(int index, Metadata.Entry item)
{
CheckWriteable();
entries.Insert(index, item);
}
public Metadata(ImmutableList<MetadataEntry> entries)
public void RemoveAt(int index)
{
this.entries = entries;
CheckWriteable();
entries.RemoveAt(index);
}
public ImmutableList<MetadataEntry> Entries
public Metadata.Entry this[int index]
{
get
{
return this.entries;
return entries[index];
}
set
{
CheckWriteable();
entries[index] = value;
}
}
public static Builder CreateBuilder()
public void Add(Metadata.Entry item)
{
CheckWriteable();
entries.Add(item);
}
public void Clear()
{
CheckWriteable();
entries.Clear();
}
public bool Contains(Metadata.Entry item)
{
return entries.Contains(item);
}
public void CopyTo(Metadata.Entry[] array, int arrayIndex)
{
return new Builder();
entries.CopyTo(array, arrayIndex);
}
public struct MetadataEntry
public int Count
{
get { return entries.Count; }
}
public bool IsReadOnly
{
get { return readOnly; }
}
public bool Remove(Metadata.Entry item)
{
CheckWriteable();
return entries.Remove(item);
}
public IEnumerator<Metadata.Entry> GetEnumerator()
{
return entries.GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return entries.GetEnumerator();
}
private void CheckWriteable()
{
Preconditions.CheckState(!readOnly, "Object is read only");
}
#endregion
/// <summary>
/// Metadata entry
/// </summary>
public struct Entry
{
private static readonly Encoding Encoding = Encoding.ASCII;
readonly string key;
readonly byte[] valueBytes;
string value;
byte[] valueBytes;
public MetadataEntry(string key, byte[] valueBytes)
public Entry(string key, byte[] valueBytes)
{
this.key = key;
this.valueBytes = valueBytes;
this.key = Preconditions.CheckNotNull(key);
this.value = null;
this.valueBytes = Preconditions.CheckNotNull(valueBytes);
}
public MetadataEntry(string key, string value)
public Entry(string key, string value)
{
this.key = key;
this.valueBytes = Encoding.ASCII.GetBytes(value);
this.key = Preconditions.CheckNotNull(key);
this.value = Preconditions.CheckNotNull(value);
this.valueBytes = null;
}
public string Key
@ -89,38 +197,29 @@ namespace Grpc.Core
}
}
// TODO: using ByteString would guarantee immutability.
public byte[] ValueBytes
{
get
{
return this.valueBytes;
if (valueBytes == null)
{
valueBytes = Encoding.GetBytes(value);
}
return valueBytes;
}
}
}
public class Builder
{
readonly List<Metadata.MetadataEntry> entries = new List<Metadata.MetadataEntry>();
public List<MetadataEntry> Entries
public string Value
{
get
{
return entries;
if (value == null)
{
value = Encoding.GetString(valueBytes);
}
return value;
}
}
public Builder Add(MetadataEntry entry)
{
entries.Add(entry);
return this;
}
public Metadata Build()
{
return new Metadata(entries.ToImmutableList());
}
}
}
}
}

@ -1,64 +0,0 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
public delegate void HeaderInterceptorDelegate(Metadata.Builder headerBuilder);
public class StubConfiguration
{
/// <summary>
/// The default stub configuration.
/// </summary>
public static readonly StubConfiguration Default = new StubConfiguration((headerBuilder) => { });
readonly HeaderInterceptorDelegate headerInterceptor;
public StubConfiguration(HeaderInterceptorDelegate headerInterceptor)
{
this.headerInterceptor = Preconditions.CheckNotNull(headerInterceptor);
}
public HeaderInterceptorDelegate HeaderInterceptor
{
get
{
return headerInterceptor;
}
}
}
}

@ -3,4 +3,3 @@ using System.Runtime.CompilerServices;
// The current version of gRPC C#.
[assembly: AssemblyVersion("0.6.0.*")]

@ -41,18 +41,18 @@ namespace math
{
using (Channel channel = new Channel("127.0.0.1", 23456))
{
Math.IMathClient stub = new Math.MathClient(channel);
MathExamples.DivExample(stub);
Math.IMathClient client = new Math.MathClient(channel);
MathExamples.DivExample(client);
MathExamples.DivAsyncExample(stub).Wait();
MathExamples.DivAsyncExample(client).Wait();
MathExamples.FibExample(stub).Wait();
MathExamples.FibExample(client).Wait();
MathExamples.SumExample(stub).Wait();
MathExamples.SumExample(client).Wait();
MathExamples.DivManyExample(stub).Wait();
MathExamples.DivManyExample(client).Wait();
MathExamples.DependendRequestsExample(stub).Wait();
MathExamples.DependendRequestsExample(client).Wait();
}
GrpcEnvironment.Shutdown();

@ -38,29 +38,29 @@ namespace math
{
public static class MathExamples
{
public static void DivExample(Math.IMathClient stub)
public static void DivExample(Math.IMathClient client)
{
DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
DivReply result = client.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
Console.WriteLine("Div Result: " + result);
}
public static async Task DivAsyncExample(Math.IMathClient stub)
public static async Task DivAsyncExample(Math.IMathClient client)
{
Task<DivReply> resultTask = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
Task<DivReply> resultTask = client.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
DivReply result = await resultTask;
Console.WriteLine("DivAsync Result: " + result);
}
public static async Task FibExample(Math.IMathClient stub)
public static async Task FibExample(Math.IMathClient client)
{
using (var call = stub.Fib(new FibArgs.Builder { Limit = 5 }.Build()))
using (var call = client.Fib(new FibArgs.Builder { Limit = 5 }.Build()))
{
List<Num> result = await call.ResponseStream.ToList();
Console.WriteLine("Fib Result: " + string.Join("|", result));
}
}
public static async Task SumExample(Math.IMathClient stub)
public static async Task SumExample(Math.IMathClient client)
{
var numbers = new List<Num>
{
@ -69,14 +69,14 @@ namespace math
new Num.Builder { Num_ = 3 }.Build()
};
using (var call = stub.Sum())
using (var call = client.Sum())
{
await call.RequestStream.WriteAll(numbers);
Console.WriteLine("Sum Result: " + await call.Result);
}
}
public static async Task DivManyExample(Math.IMathClient stub)
public static async Task DivManyExample(Math.IMathClient client)
{
var divArgsList = new List<DivArgs>
{
@ -84,14 +84,14 @@ namespace math
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
};
using (var call = stub.DivMany())
using (var call = client.DivMany())
{
await call.RequestStream.WriteAll(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList()));
}
}
public static async Task DependendRequestsExample(Math.IMathClient stub)
public static async Task DependendRequestsExample(Math.IMathClient client)
{
var numbers = new List<Num>
{
@ -101,13 +101,13 @@ namespace math
};
Num sum;
using (var sumCall = stub.Sum())
using (var sumCall = client.Sum())
{
await sumCall.RequestStream.WriteAll(numbers);
sum = await sumCall.Result;
}
DivReply result = await stub.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build());
DivReply result = await client.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build());
Console.WriteLine("Avg Result: " + result);
}
}

@ -61,7 +61,7 @@ namespace math {
}
// client stub
public class MathClient : AbstractStub<MathClient, StubConfiguration>, IMathClient
public class MathClient : ClientBase<MathClient, StubConfiguration>, IMathClient
{
public MathClient(Channel channel) : this(channel, StubConfiguration.Default)
{

@ -35,7 +35,7 @@ namespace Grpc.Health.V1Alpha {
}
// client stub
public class HealthClient : AbstractStub<HealthClient, StubConfiguration>, IHealthClient
public class HealthClient : ClientBase<HealthClient, StubConfiguration>, IHealthClient
{
public HealthClient(Channel channel) : this(channel, StubConfiguration.Default)
{

@ -81,7 +81,7 @@ namespace grpc.testing {
}
// client stub
public class TestServiceClient : AbstractStub<TestServiceClient, StubConfiguration>, ITestServiceClient
public class TestServiceClient : ClientBase<TestServiceClient, StubConfiguration>, ITestServiceClient
{
public TestServiceClient(Channel channel) : this(channel, StubConfiguration.Default)
{

Loading…
Cancel
Save