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

changed the way ports are added to the server

parent 021df8a7
No related branches found
No related tags found
No related merge requests found
Showing
with 277 additions and 68 deletions
......@@ -77,13 +77,13 @@ namespace Grpc.Core.Tests
[SetUp]
public void Init()
{
server = new Server()
server = new Server
{
Services = { ServiceDefinition }
Services = { ServiceDefinition },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure);
server.Start();
channel = new Channel(Host, port, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
}
[TearDown]
......
......@@ -32,6 +32,7 @@
#endregion
using System;
using System.Linq;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
......@@ -44,11 +45,45 @@ namespace Grpc.Core.Tests
[Test]
public void StartAndShutdownServer()
{
Server server = new Server();
server.AddPort("localhost", Server.PickUnusedPort, ServerCredentials.Insecure);
Server server = new Server
{
Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) }
};
server.Start();
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
[Test]
public void PickUnusedPort()
{
Server server = new Server
{
Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) }
};
var boundPort = server.Ports.Single();
Assert.AreEqual(0, boundPort.Port);
Assert.Greater(boundPort.BoundPort, 0);
server.Start();
server.ShutdownAsync();
GrpcEnvironment.Shutdown();
}
[Test]
public void CannotModifyAfterStarted()
{
Server server = new Server
{
Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) }
};
server.Start();
Assert.Throws(typeof(InvalidOperationException), () => server.Ports.Add("localhost", 9999, ServerCredentials.Insecure));
Assert.Throws(typeof(InvalidOperationException), () => server.Services.Add(ServerServiceDefinition.CreateBuilder("serviceName").Build()));
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
}
}
......@@ -70,13 +70,13 @@ namespace Grpc.Core.Tests
[SetUp]
public void Init()
{
server = new Server()
server = new Server
{
Services = { ServiceDefinition }
Services = { ServiceDefinition },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure);
server.Start();
channel = new Channel(Host, port, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
stringFromServerHandlerTcs = new TaskCompletionSource<string>();
}
......
......@@ -52,6 +52,7 @@
<Compile Include="IServerStreamWriter.cs" />
<Compile Include="IAsyncStreamWriter.cs" />
<Compile Include="IAsyncStreamReader.cs" />
<Compile Include="ServerPort.cs" />
<Compile Include="Version.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcException.cs" />
......
......@@ -48,20 +48,17 @@ namespace Grpc.Core
/// </summary>
public class Server
{
/// <summary>
/// Pass this value as port to have the server choose an unused listening port for you.
/// </summary>
public const int PickUnusedPort = 0;
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>();
readonly ServiceDefinitionCollection serviceDefinitions;
readonly ServerPortCollection ports;
readonly GrpcEnvironment environment;
readonly List<ChannelOption> options;
readonly ServerSafeHandle handle;
readonly object myLock = new object();
readonly List<ServerServiceDefinition> serviceDefinitionsList = new List<ServerServiceDefinition>();
readonly List<ServerPort> serverPortList = new List<ServerPort>();
readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>();
readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>();
......@@ -75,6 +72,7 @@ namespace Grpc.Core
public Server(IEnumerable<ChannelOption> options = null)
{
this.serviceDefinitions = new ServiceDefinitionCollection(this);
this.ports = new ServerPortCollection(this);
this.environment = GrpcEnvironment.GetInstance();
this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>();
using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options))
......@@ -96,30 +94,14 @@ namespace Grpc.Core
}
/// <summary>
/// Add a port on which server should listen.
/// Only call this before Start().
/// Ports on which the server will listen once started. Register a port with this
/// server by adding its definition to this collection.
/// </summary>
/// <returns>The port on which server will be listening.</returns>
/// <param name="host">the host</param>
/// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
public int AddPort(string host, int port, ServerCredentials credentials)
public ServerPortCollection Ports
{
lock (myLock)
get
{
Preconditions.CheckNotNull(credentials);
Preconditions.CheckState(!startRequested);
var address = string.Format("{0}:{1}", host, port);
using (var nativeCredentials = credentials.ToNativeCredentials())
{
if (nativeCredentials != null)
{
return handle.AddSecurePort(address, nativeCredentials);
}
else
{
return handle.AddInsecurePort(address);
}
}
return ports;
}
}
......@@ -203,6 +185,34 @@ namespace Grpc.Core
}
}
/// <summary>
/// Adds a listening port.
/// </summary>
private int AddPortInternal(ServerPort serverPort)
{
lock (myLock)
{
Preconditions.CheckNotNull(serverPort.Credentials);
Preconditions.CheckState(!startRequested);
var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port);
int boundPort;
using (var nativeCredentials = serverPort.Credentials.ToNativeCredentials())
{
if (nativeCredentials != null)
{
boundPort = handle.AddSecurePort(address, nativeCredentials);
}
else
{
boundPort = handle.AddInsecurePort(address);
}
}
var newServerPort = new ServerPort(serverPort, boundPort);
this.serverPortList.Add(newServerPort);
return boundPort;
}
}
/// <summary>
/// Allows one new RPC call to be received by server.
/// </summary>
......@@ -295,5 +305,50 @@ namespace Grpc.Core
return server.serviceDefinitionsList.GetEnumerator();
}
}
/// <summary>
/// Collection of server ports.
/// </summary>
public class ServerPortCollection : IEnumerable<ServerPort>
{
readonly Server server;
internal ServerPortCollection(Server server)
{
this.server = server;
}
/// <summary>
/// Adds a new port on which server should listen.
/// Only call this before Start().
/// <returns>The port on which server will be listening.</returns>
/// </summary>
public int Add(ServerPort serverPort)
{
return server.AddPortInternal(serverPort);
}
/// <summary>
/// Adds a new port on which server should listen.
/// <returns>The port on which server will be listening.</returns>
/// </summary>
/// <param name="host">the host</param>
/// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
/// <param name="credentials">credentials to use to secure this port.</param>
public int Add(string host, int port, ServerCredentials credentials)
{
return Add(new ServerPort(host, port, credentials));
}
public IEnumerator<ServerPort> GetEnumerator()
{
return server.serverPortList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return server.serverPortList.GetEnumerator();
}
}
}
}
#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.Utils;
namespace Grpc.Core
{
/// <summary>
/// A port exposed by a server.
/// </summary>
public class ServerPort
{
/// <summary>
/// Pass this value as port to have the server choose an unused listening port for you.
/// Ports added to a server will contain the bound port in their <see cref="BoundPort"/> property.
/// </summary>
public const int PickUnused = 0;
readonly string host;
readonly int port;
readonly ServerCredentials credentials;
readonly int boundPort;
/// <summary>
/// Creates a new port on which server should listen.
/// </summary>
/// <returns>The port on which server will be listening.</returns>
/// <param name="host">the host</param>
/// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
/// <param name="credentials">credentials to use to secure this port.</param>
public ServerPort(string host, int port, ServerCredentials credentials)
{
this.host = Preconditions.CheckNotNull(host);
this.port = port;
this.credentials = Preconditions.CheckNotNull(credentials);
}
/// <summary>
/// Creates a port from an existing <c>ServerPort</c> instance and boundPort value.
/// </summary>
internal ServerPort(ServerPort serverPort, int boundPort)
{
this.host = serverPort.host;
this.port = serverPort.port;
this.credentials = serverPort.credentials;
this.boundPort = boundPort;
}
/// <value>The host.</value>
public string Host
{
get
{
return host;
}
}
/// <value>The port.</value>
public int Port
{
get
{
return port;
}
}
/// <value>The server credentials.</value>
public ServerCredentials Credentials
{
get
{
return credentials;
}
}
/// <value>
/// The port actually bound by the server. This is useful if you let server
/// pick port automatically. <see cref="PickUnused"/>
/// </value>
public int BoundPort
{
get
{
return boundPort;
}
}
}
}
......@@ -38,18 +38,19 @@ namespace math
{
class MainClass
{
const string Host = "0.0.0.0";
const int Port = 23456;
public static void Main(string[] args)
{
string host = "0.0.0.0";
Server server = new Server()
Server server = new Server
{
Services = { Math.BindService(new MathServiceImpl()) },
Ports = { { Host, Port, ServerCredentials.Insecure } }
};
int port = server.AddPort(host, 23456, ServerCredentials.Insecure);
server.Start();
Console.WriteLine("MathServer listening on port " + port);
Console.WriteLine("MathServer listening on port " + Port);
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();
......
......@@ -33,6 +33,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
......@@ -46,7 +47,7 @@ namespace math.Tests
/// </summary>
public class MathClientServerTest
{
string host = "localhost";
const string Host = "localhost";
Server server;
Channel channel;
Math.MathClient client;
......@@ -54,21 +55,14 @@ namespace math.Tests
[TestFixtureSetUp]
public void Init()
{
server = new Server()
server = new Server
{
Services = { Math.BindService(new MathServiceImpl()) }
Services = { Math.BindService(new MathServiceImpl()) },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
int port = server.AddPort(host, Server.PickUnusedPort, ServerCredentials.Insecure);
server.Start();
channel = new Channel(host, port, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
client = Math.NewClient(channel);
// TODO(jtattermusch): get rid of the custom header here once we have dedicated tests
// for header support.
client.HeaderInterceptor = (metadata) =>
{
metadata.Add(new Metadata.Entry("custom-header", "abcdef"));
};
}
[TestFixtureTearDown]
......
......@@ -57,13 +57,13 @@ namespace Grpc.HealthCheck.Tests
{
serviceImpl = new HealthServiceImpl();
server = new Server()
server = new Server
{
Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) }
Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) },
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure);
server.Start();
channel = new Channel(Host, port, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
client = Grpc.Health.V1Alpha.Health.NewClient(channel);
}
......
......@@ -33,6 +33,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using grpc.testing;
......@@ -47,7 +48,7 @@ namespace Grpc.IntegrationTesting
/// </summary>
public class InteropClientServerTest
{
string host = "localhost";
const string Host = "localhost";
Server server;
Channel channel;
TestService.ITestServiceClient client;
......@@ -55,18 +56,19 @@ namespace Grpc.IntegrationTesting
[TestFixtureSetUp]
public void Init()
{
server = new Server()
server = new Server
{
Services = { TestService.BindService(new TestServiceImpl()) }
Services = { TestService.BindService(new TestServiceImpl()) },
Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } }
};
int port = server.AddPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials());
server.Start();
var options = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options);
int port = server.Ports.Single().BoundPort;
channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options);
client = TestService.NewClient(channel);
}
......
......@@ -97,11 +97,11 @@ namespace Grpc.IntegrationTesting
int port = options.port.Value;
if (options.useTls)
{
server.AddPort(host, port, TestCredentials.CreateTestServerCredentials());
server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials());
}
else
{
server.AddPort(host, options.port.Value, ServerCredentials.Insecure);
server.Ports.Add(host, options.port.Value, ServerCredentials.Insecure);
}
Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port));
server.Start();
......
......@@ -34,6 +34,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using grpc.testing;
......@@ -49,7 +50,7 @@ namespace Grpc.IntegrationTesting
/// </summary>
public class SslCredentialsTest
{
string host = "localhost";
const string Host = "localhost";
Server server;
Channel channel;
TestService.ITestServiceClient client;
......@@ -67,9 +68,9 @@ namespace Grpc.IntegrationTesting
server = new Server
{
Services = { TestService.BindService(new TestServiceImpl()) }
Services = { TestService.BindService(new TestServiceImpl()) },
Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
};
int port = server.AddPort(host, Server.PickUnusedPort, serverCredentials);
server.Start();
var options = new List<ChannelOption>
......@@ -77,7 +78,7 @@ namespace Grpc.IntegrationTesting
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
channel = new Channel(host, port, clientCredentials, options);
channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
client = TestService.NewClient(channel);
}
......
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