blob: 60dacbf126c3ed3bbd7f68578d04b6bee4b6cb2d [file] [log] [blame]
Jan Tattermuscha7fff862015-02-13 11:08:08 -08001#region Copyright notice and license
2
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003// Copyright 2015 gRPC authors.
Craig Tiller190d3602015-02-18 09:23:38 -08004//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
Craig Tiller190d3602015-02-18 09:23:38 -08008//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009// http://www.apache.org/licenses/LICENSE-2.0
Craig Tiller190d3602015-02-18 09:23:38 -080010//
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
Jan Tattermuscha7fff862015-02-13 11:08:08 -080016
17#endregion
18
Jan Tattermuscha7608b02015-02-03 17:54:38 -080019using System;
Jan Tattermusch021df8a2015-08-04 20:31:11 -070020using System.Collections;
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -080021using System.Collections.Generic;
Jan Tattermusch9d567172017-05-26 14:19:23 -070022using System.IO;
Jan Tattermusche6d1de62016-05-25 19:32:15 -040023using System.Linq;
Jan Tattermusch30868622015-02-19 09:22:33 -080024using System.Threading.Tasks;
25using Grpc.Core.Internal;
Jan Tattermusch05261612015-07-24 00:28:16 -070026using Grpc.Core.Logging;
Jan Tattermusch97e294a2015-04-23 14:30:59 -070027using Grpc.Core.Utils;
Jan Tattermuscha7608b02015-02-03 17:54:38 -080028
Jan Tattermusch30868622015-02-19 09:22:33 -080029namespace Grpc.Core
Jan Tattermuscha7608b02015-02-03 17:54:38 -080030{
31 /// <summary>
Jan Tattermusch12855fc2015-08-24 16:43:23 -070032 /// gRPC server. A single server can server arbitrary number of services and can listen on more than one ports.
Jan Tattermuscha7608b02015-02-03 17:54:38 -080033 /// </summary>
34 public class Server
35 {
Jan Tattermuschfac33ad2016-10-21 01:42:10 +020036 const int DefaultRequestCallTokensPerCq = 2000;
Jan Tattermusch05261612015-07-24 00:28:16 -070037 static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>();
38
Jan Tattermusch2b357952015-08-20 14:54:33 -070039 readonly AtomicCounter activeCallCounter = new AtomicCounter();
40
Jan Tattermusch021df8a2015-08-04 20:31:11 -070041 readonly ServiceDefinitionCollection serviceDefinitions;
Jan Tattermusch31ba0632015-08-04 22:02:55 -070042 readonly ServerPortCollection ports;
Jan Tattermusch04eb89c2015-06-12 13:03:05 -070043 readonly GrpcEnvironment environment;
Jan Tattermusch766d72b2015-07-21 20:09:25 -070044 readonly List<ChannelOption> options;
Jan Tattermuscha7608b02015-02-03 17:54:38 -080045 readonly ServerSafeHandle handle;
Jan Tattermusch97e294a2015-04-23 14:30:59 -070046 readonly object myLock = new object();
Jan Tattermuscha7608b02015-02-03 17:54:38 -080047
Jan Tattermusch021df8a2015-08-04 20:31:11 -070048 readonly List<ServerServiceDefinition> serviceDefinitionsList = new List<ServerServiceDefinition>();
Jan Tattermusch31ba0632015-08-04 22:02:55 -070049 readonly List<ServerPort> serverPortList = new List<ServerPort>();
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -080050 readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>();
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -080051 readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>();
52
Jan Tattermusch97e294a2015-04-23 14:30:59 -070053 bool startRequested;
Jan Tattermusch1e1fa082016-05-09 15:18:11 -070054 volatile bool shutdownRequested;
Jan Tattermuschfac33ad2016-10-21 01:42:10 +020055 int requestCallTokensPerCq = DefaultRequestCallTokensPerCq;
Jan Tattermusch8d829d02016-06-06 16:43:54 -070056
Jan Tattermusch6d53a5c2015-06-08 18:03:05 -070057 /// <summary>
Jan Tattermusch8d829d02016-06-06 16:43:54 -070058 /// Creates a new server.
59 /// </summary>
60 public Server() : this(null)
61 {
62 }
63
64 /// <summary>
65 /// Creates a new server.
Jan Tattermusch6d53a5c2015-06-08 18:03:05 -070066 /// </summary>
67 /// <param name="options">Channel options.</param>
Jan Tattermusch8d829d02016-06-06 16:43:54 -070068 public Server(IEnumerable<ChannelOption> options)
Jan Tattermuscha7608b02015-02-03 17:54:38 -080069 {
Jan Tattermusch021df8a2015-08-04 20:31:11 -070070 this.serviceDefinitions = new ServiceDefinitionCollection(this);
Jan Tattermusch31ba0632015-08-04 22:02:55 -070071 this.ports = new ServerPortCollection(this);
Jan Tattermusch2b357952015-08-20 14:54:33 -070072 this.environment = GrpcEnvironment.AddRef();
Jan Tattermusch766d72b2015-07-21 20:09:25 -070073 this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>();
74 using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options))
Jan Tattermusch6d53a5c2015-06-08 18:03:05 -070075 {
Jan Tattermusch5ee8e772016-05-24 16:17:10 -040076 this.handle = ServerSafeHandle.NewServer(channelArgs);
77 }
78
79 foreach (var cq in environment.CompletionQueues)
80 {
81 this.handle.RegisterCompletionQueue(cq);
Jan Tattermusch6d53a5c2015-06-08 18:03:05 -070082 }
Jan Tattermusch739ee1b2016-06-01 14:08:26 -070083 GrpcEnvironment.RegisterServer(this);
Jan Tattermuscha7608b02015-02-03 17:54:38 -080084 }
85
Jan Tattermusch97e294a2015-04-23 14:30:59 -070086 /// <summary>
Jan Tattermusch021df8a2015-08-04 20:31:11 -070087 /// Services that will be exported by the server once started. Register a service with this
88 /// server by adding its definition to this collection.
Jan Tattermusch97e294a2015-04-23 14:30:59 -070089 /// </summary>
Jan Tattermusch021df8a2015-08-04 20:31:11 -070090 public ServiceDefinitionCollection Services
Jan Tattermusch075dde42015-03-11 18:21:00 -070091 {
Jan Tattermusch021df8a2015-08-04 20:31:11 -070092 get
Jan Tattermusch15111f52015-02-05 18:15:14 -080093 {
Jan Tattermusch021df8a2015-08-04 20:31:11 -070094 return serviceDefinitions;
Jan Tattermusch15111f52015-02-05 18:15:14 -080095 }
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -080096 }
Jan Tattermusch15111f52015-02-05 18:15:14 -080097
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -080098 /// <summary>
Jan Tattermusch31ba0632015-08-04 22:02:55 -070099 /// Ports on which the server will listen once started. Register a port with this
100 /// server by adding its definition to this collection.
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800101 /// </summary>
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700102 public ServerPortCollection Ports
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800103 {
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700104 get
Jan Tattermuscha96ac052015-07-24 14:49:30 -0700105 {
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700106 return ports;
Jan Tattermuscha96ac052015-07-24 14:49:30 -0700107 }
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700108 }
109
110 /// <summary>
Jan Tattermusch2b357952015-08-20 14:54:33 -0700111 /// To allow awaiting termination of the server.
112 /// </summary>
113 public Task ShutdownTask
114 {
115 get
116 {
117 return shutdownTcs.Task;
118 }
119 }
120
121 /// <summary>
Jan Tattermuschfac33ad2016-10-21 01:42:10 +0200122 /// Experimental API. Might anytime change without prior notice.
123 /// Number or calls requested via grpc_server_request_call at any given time for each completion queue.
124 /// </summary>
125 public int RequestCallTokensPerCompletionQueue
126 {
127 get
128 {
129 return requestCallTokensPerCq;
130 }
131 set
132 {
133 lock (myLock)
134 {
135 GrpcPreconditions.CheckState(!startRequested);
136 GrpcPreconditions.CheckArgument(value > 0);
137 requestCallTokensPerCq = value;
138 }
139 }
140 }
141
142 /// <summary>
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700143 /// Starts the server.
Jan Tattermusch9d567172017-05-26 14:19:23 -0700144 /// Throws <c>IOException</c> if not successful.
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700145 /// </summary>
146 public void Start()
147 {
148 lock (myLock)
149 {
Jan Tattermusch7a3ee6a2016-02-18 10:36:02 -0800150 GrpcPreconditions.CheckState(!startRequested);
Jan Tattermusch8e935332016-06-08 19:45:54 -0700151 GrpcPreconditions.CheckState(!shutdownRequested);
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700152 startRequested = true;
Jan Tattermusch9d567172017-05-26 14:19:23 -0700153
154 CheckPortsBoundSuccessfully();
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700155 handle.Start();
Jan Tattermusch26cc1422016-05-04 17:21:17 -0700156
Jan Tattermuschfac33ad2016-10-21 01:42:10 +0200157 for (int i = 0; i < requestCallTokensPerCq; i++)
Jan Tattermusch26cc1422016-05-04 17:21:17 -0700158 {
Jan Tattermusch5ee8e772016-05-24 16:17:10 -0400159 foreach (var cq in environment.CompletionQueues)
160 {
161 AllowOneRpc(cq);
162 }
Jan Tattermusch26cc1422016-05-04 17:21:17 -0700163 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800164 }
165 }
166
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800167 /// <summary>
168 /// Requests server shutdown and when there are no more calls being serviced,
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700169 /// cleans up used resources. The returned task finishes when shutdown procedure
170 /// is complete.
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800171 /// </summary>
Jan Tattermuscha134fa72016-06-03 17:24:50 -0700172 /// <remarks>
173 /// It is strongly recommended to shutdown all previously created servers before exiting from the process.
174 /// </remarks>
Jan Tattermusch63386a12016-06-01 12:47:46 -0700175 public Task ShutdownAsync()
Jan Tattermusch075dde42015-03-11 18:21:00 -0700176 {
Jan Tattermusch63386a12016-06-01 12:47:46 -0700177 return ShutdownInternalAsync(false);
Jan Tattermusch503bbac2015-02-26 18:19:47 -0800178 }
179
Jan Tattermuschc4e81ad2015-05-29 17:39:07 -0700180 /// <summary>
181 /// Requests server shutdown while cancelling all the in-progress calls.
182 /// The returned task finishes when shutdown procedure is complete.
183 /// </summary>
Jan Tattermuscha134fa72016-06-03 17:24:50 -0700184 /// <remarks>
185 /// It is strongly recommended to shutdown all previously created servers before exiting from the process.
186 /// </remarks>
Jan Tattermusch63386a12016-06-01 12:47:46 -0700187 public Task KillAsync()
Jan Tattermusch075dde42015-03-11 18:21:00 -0700188 {
Jan Tattermusch63386a12016-06-01 12:47:46 -0700189 return ShutdownInternalAsync(true);
Jan Tattermusch2b357952015-08-20 14:54:33 -0700190 }
191
192 internal void AddCallReference(object call)
193 {
194 activeCallCounter.Increment();
195
196 bool success = false;
197 handle.DangerousAddRef(ref success);
Jan Tattermusch7a3ee6a2016-02-18 10:36:02 -0800198 GrpcPreconditions.CheckState(success);
Jan Tattermusch2b357952015-08-20 14:54:33 -0700199 }
200
201 internal void RemoveCallReference(object call)
202 {
203 handle.DangerousRelease();
204 activeCallCounter.Decrement();
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800205 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800206
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700207 /// <summary>
Jan Tattermusch63386a12016-06-01 12:47:46 -0700208 /// Shuts down the server.
209 /// </summary>
210 private async Task ShutdownInternalAsync(bool kill)
211 {
212 lock (myLock)
213 {
Jan Tattermusch63386a12016-06-01 12:47:46 -0700214 GrpcPreconditions.CheckState(!shutdownRequested);
215 shutdownRequested = true;
216 }
Jan Tattermusch739ee1b2016-06-01 14:08:26 -0700217 GrpcEnvironment.UnregisterServer(this);
Jan Tattermusch63386a12016-06-01 12:47:46 -0700218
219 var cq = environment.CompletionQueues.First(); // any cq will do
220 handle.ShutdownAndNotify(HandleServerShutdown, cq);
221 if (kill)
222 {
223 handle.CancelAllCalls();
224 }
Jan Tattermusch739ee1b2016-06-01 14:08:26 -0700225 await ShutdownCompleteOrEnvironmentDeadAsync().ConfigureAwait(false);
226
Jan Tattermusch63386a12016-06-01 12:47:46 -0700227 DisposeHandle();
228
229 await GrpcEnvironment.ReleaseAsync().ConfigureAwait(false);
230 }
231
232 /// <summary>
Jan Tattermusch739ee1b2016-06-01 14:08:26 -0700233 /// In case the environment's threadpool becomes dead, the shutdown completion will
234 /// never be delivered, but we need to release the environment's handle anyway.
235 /// </summary>
236 private async Task ShutdownCompleteOrEnvironmentDeadAsync()
237 {
238 while (true)
239 {
240 var task = await Task.WhenAny(shutdownTcs.Task, Task.Delay(20)).ConfigureAwait(false);
241 if (shutdownTcs.Task == task)
242 {
243 return;
244 }
245 if (!environment.IsAlive)
246 {
247 return;
248 }
249 }
250 }
251
252 /// <summary>
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700253 /// Adds a service definition.
254 /// </summary>
255 private void AddServiceDefinitionInternal(ServerServiceDefinition serviceDefinition)
256 {
257 lock (myLock)
258 {
Jan Tattermusch7a3ee6a2016-02-18 10:36:02 -0800259 GrpcPreconditions.CheckState(!startRequested);
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700260 foreach (var entry in serviceDefinition.CallHandlers)
261 {
262 callHandlers.Add(entry.Key, entry.Value);
263 }
264 serviceDefinitionsList.Add(serviceDefinition);
265 }
266 }
267
268 /// <summary>
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700269 /// Adds a listening port.
270 /// </summary>
271 private int AddPortInternal(ServerPort serverPort)
272 {
273 lock (myLock)
274 {
Jan Tattermusch7a3ee6a2016-02-18 10:36:02 -0800275 GrpcPreconditions.CheckNotNull(serverPort.Credentials, "serverPort");
276 GrpcPreconditions.CheckState(!startRequested);
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700277 var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port);
278 int boundPort;
279 using (var nativeCredentials = serverPort.Credentials.ToNativeCredentials())
280 {
281 if (nativeCredentials != null)
282 {
283 boundPort = handle.AddSecurePort(address, nativeCredentials);
284 }
285 else
286 {
287 boundPort = handle.AddInsecurePort(address);
288 }
289 }
290 var newServerPort = new ServerPort(serverPort, boundPort);
291 this.serverPortList.Add(newServerPort);
292 return boundPort;
293 }
294 }
295
296 /// <summary>
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700297 /// Allows one new RPC call to be received by server.
298 /// </summary>
Jan Tattermusch5ee8e772016-05-24 16:17:10 -0400299 private void AllowOneRpc(CompletionQueueSafeHandle cq)
Jan Tattermusch075dde42015-03-11 18:21:00 -0700300 {
Jan Tattermusch1e1fa082016-05-09 15:18:11 -0700301 if (!shutdownRequested)
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800302 {
Jan Tattermusche8b33132017-11-28 13:54:13 +0100303 // TODO(jtattermusch): avoid unnecessary delegate allocation
Jan Tattermusche6d1de62016-05-25 19:32:15 -0400304 handle.RequestCall((success, ctx) => HandleNewServerRpc(success, ctx, cq), cq);
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800305 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800306 }
307
Jan Tattermusch9d567172017-05-26 14:19:23 -0700308 /// <summary>
309 /// Checks that all ports have been bound successfully.
310 /// </summary>
311 private void CheckPortsBoundSuccessfully()
312 {
313 lock (myLock)
314 {
Jan Tattermuschc4096462017-06-01 21:56:24 +0200315 var unboundPort = ports.FirstOrDefault(port => port.BoundPort == 0);
316 if (unboundPort != null)
Jan Tattermusch9d567172017-05-26 14:19:23 -0700317 {
Jan Tattermuschc4096462017-06-01 21:56:24 +0200318 throw new IOException(
319 string.Format("Failed to bind port \"{0}:{1}\"", unboundPort.Host, unboundPort.Port));
Jan Tattermusch9d567172017-05-26 14:19:23 -0700320 }
321 }
322 }
323
Jan Tattermusch2b357952015-08-20 14:54:33 -0700324 private void DisposeHandle()
325 {
326 var activeCallCount = activeCallCounter.Count;
327 if (activeCallCount > 0)
328 {
329 Logger.Warning("Server shutdown has finished but there are still {0} active calls for that server.", activeCallCount);
330 }
331 handle.Dispose();
332 }
333
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700334 /// <summary>
335 /// Selects corresponding handler for given call and handles the call.
336 /// </summary>
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200337 private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq, Action continuation)
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800338 {
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700339 try
340 {
341 IServerCallHandler callHandler;
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700342 if (!callHandlers.TryGetValue(newRpc.Method, out callHandler))
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700343 {
Jan Tattermuschf72ead32016-10-18 19:02:46 +0200344 callHandler = UnimplementedMethodCallHandler.Instance;
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700345 }
Jan Tattermuschb0893202016-05-25 19:20:39 -0400346 await callHandler.HandleCall(newRpc, cq).ConfigureAwait(false);
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700347 }
348 catch (Exception e)
349 {
Jan Tattermusch05261612015-07-24 00:28:16 -0700350 Logger.Warning(e, "Exception while handling RPC.");
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700351 }
Jan Tattermuschba098842016-10-24 11:19:41 +0200352 finally
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200353 {
354 continuation();
355 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800356 }
357
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700358 /// <summary>
359 /// Handles the native callback.
360 /// </summary>
Jan Tattermusch6e901712016-10-21 00:42:37 +0200361 private void HandleNewServerRpc(bool success, RequestCallContextSafeHandle ctx, CompletionQueueSafeHandle cq)
Jan Tattermusch075dde42015-03-11 18:21:00 -0700362 {
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200363 bool nextRpcRequested = false;
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700364 if (success)
Jan Tattermusch075dde42015-03-11 18:21:00 -0700365 {
Jan Tattermusch6e901712016-10-21 00:42:37 +0200366 var newRpc = ctx.GetServerRpcNew(this);
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700367
368 // after server shutdown, the callback returns with null call
369 if (!newRpc.Call.IsInvalid)
370 {
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200371 nextRpcRequested = true;
372
373 // Start asynchronous handler for the call.
374 // Don't await, the continuations will run on gRPC thread pool once triggered
375 // by cq.Next().
376 #pragma warning disable 4014
377 HandleCallAsync(newRpc, cq, () => AllowOneRpc(cq));
378 #pragma warning restore 4014
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700379 }
Jan Tattermusch075dde42015-03-11 18:21:00 -0700380 }
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200381
382 if (!nextRpcRequested)
383 {
384 AllowOneRpc(cq);
385 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800386 }
387
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700388 /// <summary>
389 /// Handles native callback.
390 /// </summary>
Jan Tattermusch8b451ed2017-11-20 11:46:48 +0100391 private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx, object state)
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800392 {
Jan Tattermuschd3677482015-06-01 19:27:40 -0700393 shutdownTcs.SetResult(null);
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800394 }
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700395
396 /// <summary>
397 /// Collection of service definitions.
398 /// </summary>
399 public class ServiceDefinitionCollection : IEnumerable<ServerServiceDefinition>
400 {
401 readonly Server server;
402
403 internal ServiceDefinitionCollection(Server server)
404 {
405 this.server = server;
406 }
407
408 /// <summary>
409 /// Adds a service definition to the server. This is how you register
410 /// handlers for a service with the server. Only call this before Start().
411 /// </summary>
412 public void Add(ServerServiceDefinition serviceDefinition)
413 {
414 server.AddServiceDefinitionInternal(serviceDefinition);
415 }
416
Jan Tattermusch12855fc2015-08-24 16:43:23 -0700417 /// <summary>
418 /// Gets enumerator for this collection.
419 /// </summary>
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700420 public IEnumerator<ServerServiceDefinition> GetEnumerator()
421 {
422 return server.serviceDefinitionsList.GetEnumerator();
423 }
424
425 IEnumerator IEnumerable.GetEnumerator()
426 {
427 return server.serviceDefinitionsList.GetEnumerator();
428 }
429 }
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700430
431 /// <summary>
432 /// Collection of server ports.
433 /// </summary>
434 public class ServerPortCollection : IEnumerable<ServerPort>
435 {
436 readonly Server server;
437
438 internal ServerPortCollection(Server server)
439 {
440 this.server = server;
441 }
442
443 /// <summary>
444 /// Adds a new port on which server should listen.
445 /// Only call this before Start().
446 /// <returns>The port on which server will be listening.</returns>
447 /// </summary>
448 public int Add(ServerPort serverPort)
449 {
450 return server.AddPortInternal(serverPort);
451 }
452
453 /// <summary>
454 /// Adds a new port on which server should listen.
455 /// <returns>The port on which server will be listening.</returns>
456 /// </summary>
457 /// <param name="host">the host</param>
458 /// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
459 /// <param name="credentials">credentials to use to secure this port.</param>
460 public int Add(string host, int port, ServerCredentials credentials)
461 {
462 return Add(new ServerPort(host, port, credentials));
463 }
464
Jan Tattermusch12855fc2015-08-24 16:43:23 -0700465 /// <summary>
466 /// Gets enumerator for this collection.
467 /// </summary>
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700468 public IEnumerator<ServerPort> GetEnumerator()
469 {
470 return server.serverPortList.GetEnumerator();
471 }
472
473 IEnumerator IEnumerable.GetEnumerator()
474 {
475 return server.serverPortList.GetEnumerator();
476 }
477 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800478 }
Craig Tiller190d3602015-02-18 09:23:38 -0800479}