Skip to content
Snippets Groups Projects
Commit e5c9b805 authored by Jan Tattermusch's avatar Jan Tattermusch
Browse files

renaming stub to client and refactoring metadata class

parent 5a9674c0
No related branches found
No related tags found
No related merge requests found
Showing
with 196 additions and 149 deletions
...@@ -52,10 +52,10 @@ namespace Grpc.Auth ...@@ -52,10 +52,10 @@ namespace Grpc.Auth
/// <summary> /// <summary>
/// Creates OAuth2 interceptor. /// Creates OAuth2 interceptor.
/// </summary> /// </summary>
public static HeaderInterceptorDelegate Create(GoogleCredential googleCredential) public static MetadataInterceptorDelegate Create(GoogleCredential googleCredential)
{ {
var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default); var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default);
return new HeaderInterceptorDelegate(interceptor.InterceptHeaders); return new MetadataInterceptorDelegate(interceptor.InterceptHeaders);
} }
/// <summary> /// <summary>
...@@ -94,10 +94,10 @@ namespace Grpc.Auth ...@@ -94,10 +94,10 @@ namespace Grpc.Auth
return credential.Token.AccessToken; return credential.Token.AccessToken;
} }
public void InterceptHeaders(Metadata.Builder headerBuilder) public void InterceptHeaders(Metadata metadata)
{ {
var accessToken = GetAccessToken(CancellationToken.None); 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; ...@@ -39,7 +39,7 @@ using Grpc.Core.Internal;
namespace Grpc.Core namespace Grpc.Core
{ {
/// <summary> /// <summary>
/// Helper methods for generated client stubs to make RPC calls. /// Helper methods for generated clients to make RPC calls.
/// </summary> /// </summary>
public static class Calls public static class Calls
{ {
......
...@@ -32,26 +32,39 @@ ...@@ -32,26 +32,39 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using Grpc.Core.Internal; using Grpc.Core.Internal;
namespace Grpc.Core namespace Grpc.Core
{ {
// TODO: support adding timeout to methods. public delegate void MetadataInterceptorDelegate(Metadata metadata);
/// <summary> /// <summary>
/// Base for client-side stubs. /// Base class for client-side stubs.
/// </summary> /// </summary>
public abstract class AbstractStub<TStub, TConfig> public abstract class ClientBase
where TConfig : StubConfiguration
{ {
readonly Channel channel; readonly Channel channel;
readonly TConfig config;
public AbstractStub(Channel channel, TConfig config) public ClientBase(Channel channel)
{ {
this.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 public Channel Channel
{ {
get get
...@@ -67,9 +80,10 @@ namespace Grpc.Core ...@@ -67,9 +80,10 @@ namespace Grpc.Core
where TRequest : class where TRequest : class
where TResponse : class where TResponse : class
{ {
var headerBuilder = Metadata.CreateBuilder(); var metadata = new Metadata();
config.HeaderInterceptor(headerBuilder); HeaderInterceptor(metadata);
return new Call<TRequest, TResponse>(serviceName, method, channel, headerBuilder.Build()); metadata.Freeze();
return new Call<TRequest, TResponse>(serviceName, method, channel, metadata);
} }
} }
} }
...@@ -88,8 +88,7 @@ ...@@ -88,8 +88,7 @@
<Compile Include="ServerCredentials.cs" /> <Compile Include="ServerCredentials.cs" />
<Compile Include="Metadata.cs" /> <Compile Include="Metadata.cs" />
<Compile Include="Internal\MetadataArraySafeHandle.cs" /> <Compile Include="Internal\MetadataArraySafeHandle.cs" />
<Compile Include="Stub\AbstractStub.cs" /> <Compile Include="ClientBase.cs" />
<Compile Include="Stub\StubConfiguration.cs" />
<Compile Include="Internal\ServerCalls.cs" /> <Compile Include="Internal\ServerCalls.cs" />
<Compile Include="ServerMethods.cs" /> <Compile Include="ServerMethods.cs" />
<Compile Include="Internal\ClientRequestStream.cs" /> <Compile Include="Internal\ClientRequestStream.cs" />
......
...@@ -54,11 +54,11 @@ namespace Grpc.Core.Internal ...@@ -54,11 +54,11 @@ namespace Grpc.Core.Internal
public static MetadataArraySafeHandle Create(Metadata metadata) public static MetadataArraySafeHandle Create(Metadata metadata)
{ {
var entries = metadata.Entries; // TODO(jtattermusch): we might wanna check that the metadata is readonly
var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)entries.Count)); var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
for (int i = 0; i < entries.Count; i++) 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; return metadataArray;
} }
......
...@@ -30,55 +30,163 @@ ...@@ -30,55 +30,163 @@
#endregion #endregion
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Grpc.Core.Utils;
namespace Grpc.Core namespace Grpc.Core
{ {
/// <summary> /// <summary>
/// gRPC call metadata. /// Provides access to read and write metadata values to be exchanged during a call.
/// </summary> /// </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 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 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.key = Preconditions.CheckNotNull(key);
this.valueBytes = valueBytes; this.value = null;
this.valueBytes = Preconditions.CheckNotNull(valueBytes);
} }
public MetadataEntry(string key, string value) public Entry(string key, string value)
{ {
this.key = key; this.key = Preconditions.CheckNotNull(key);
this.valueBytes = Encoding.ASCII.GetBytes(value); this.value = Preconditions.CheckNotNull(value);
this.valueBytes = null;
} }
public string Key public string Key
...@@ -89,38 +197,29 @@ namespace Grpc.Core ...@@ -89,38 +197,29 @@ namespace Grpc.Core
} }
} }
// TODO: using ByteString would guarantee immutability.
public byte[] ValueBytes public byte[] ValueBytes
{ {
get get
{ {
return this.valueBytes; if (valueBytes == null)
{
valueBytes = Encoding.GetBytes(value);
}
return valueBytes;
} }
} }
}
public class Builder public string Value
{
readonly List<Metadata.MetadataEntry> entries = new List<Metadata.MetadataEntry>();
public List<MetadataEntry> Entries
{ {
get 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());
}
} }
} }
} }
#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; ...@@ -3,4 +3,3 @@ using System.Runtime.CompilerServices;
// The current version of gRPC C#. // The current version of gRPC C#.
[assembly: AssemblyVersion("0.6.0.*")] [assembly: AssemblyVersion("0.6.0.*")]
...@@ -41,18 +41,18 @@ namespace math ...@@ -41,18 +41,18 @@ namespace math
{ {
using (Channel channel = new Channel("127.0.0.1", 23456)) using (Channel channel = new Channel("127.0.0.1", 23456))
{ {
Math.IMathClient stub = new Math.MathClient(channel); Math.IMathClient client = new Math.MathClient(channel);
MathExamples.DivExample(stub); 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(); GrpcEnvironment.Shutdown();
......
...@@ -38,29 +38,29 @@ namespace math ...@@ -38,29 +38,29 @@ namespace math
{ {
public static class MathExamples 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); 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; DivReply result = await resultTask;
Console.WriteLine("DivAsync Result: " + result); 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(); List<Num> result = await call.ResponseStream.ToList();
Console.WriteLine("Fib Result: " + string.Join("|", result)); 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> var numbers = new List<Num>
{ {
...@@ -69,14 +69,14 @@ namespace math ...@@ -69,14 +69,14 @@ namespace math
new Num.Builder { Num_ = 3 }.Build() new Num.Builder { Num_ = 3 }.Build()
}; };
using (var call = stub.Sum()) using (var call = client.Sum())
{ {
await call.RequestStream.WriteAll(numbers); await call.RequestStream.WriteAll(numbers);
Console.WriteLine("Sum Result: " + await call.Result); 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> var divArgsList = new List<DivArgs>
{ {
...@@ -84,14 +84,14 @@ namespace math ...@@ -84,14 +84,14 @@ namespace math
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
}; };
using (var call = stub.DivMany()) using (var call = client.DivMany())
{ {
await call.RequestStream.WriteAll(divArgsList); await call.RequestStream.WriteAll(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList())); 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> var numbers = new List<Num>
{ {
...@@ -101,13 +101,13 @@ namespace math ...@@ -101,13 +101,13 @@ namespace math
}; };
Num sum; Num sum;
using (var sumCall = stub.Sum()) using (var sumCall = client.Sum())
{ {
await sumCall.RequestStream.WriteAll(numbers); await sumCall.RequestStream.WriteAll(numbers);
sum = await sumCall.Result; 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); Console.WriteLine("Avg Result: " + result);
} }
} }
......
...@@ -61,7 +61,7 @@ namespace math { ...@@ -61,7 +61,7 @@ namespace math {
} }
// client stub // client stub
public class MathClient : AbstractStub<MathClient, StubConfiguration>, IMathClient public class MathClient : ClientBase<MathClient, StubConfiguration>, IMathClient
{ {
public MathClient(Channel channel) : this(channel, StubConfiguration.Default) public MathClient(Channel channel) : this(channel, StubConfiguration.Default)
{ {
......
...@@ -35,7 +35,7 @@ namespace Grpc.Health.V1Alpha { ...@@ -35,7 +35,7 @@ namespace Grpc.Health.V1Alpha {
} }
// client stub // client stub
public class HealthClient : AbstractStub<HealthClient, StubConfiguration>, IHealthClient public class HealthClient : ClientBase<HealthClient, StubConfiguration>, IHealthClient
{ {
public HealthClient(Channel channel) : this(channel, StubConfiguration.Default) public HealthClient(Channel channel) : this(channel, StubConfiguration.Default)
{ {
......
...@@ -81,7 +81,7 @@ namespace grpc.testing { ...@@ -81,7 +81,7 @@ namespace grpc.testing {
} }
// client stub // client stub
public class TestServiceClient : AbstractStub<TestServiceClient, StubConfiguration>, ITestServiceClient public class TestServiceClient : ClientBase<TestServiceClient, StubConfiguration>, ITestServiceClient
{ {
public TestServiceClient(Channel channel) : this(channel, StubConfiguration.Default) public TestServiceClient(Channel channel) : this(channel, StubConfiguration.Default)
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment