blob: 77ad876bdf64beedc8de79b4e887a6845bae557f [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 Tattermusche6d1de62016-05-25 19:32:15 -0400303 handle.RequestCall((success, ctx) => HandleNewServerRpc(success, ctx, cq), cq);
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800304 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800305 }
306
Jan Tattermusch9d567172017-05-26 14:19:23 -0700307 /// <summary>
308 /// Checks that all ports have been bound successfully.
309 /// </summary>
310 private void CheckPortsBoundSuccessfully()
311 {
312 lock (myLock)
313 {
Jan Tattermuschc4096462017-06-01 21:56:24 +0200314 var unboundPort = ports.FirstOrDefault(port => port.BoundPort == 0);
315 if (unboundPort != null)
Jan Tattermusch9d567172017-05-26 14:19:23 -0700316 {
Jan Tattermuschc4096462017-06-01 21:56:24 +0200317 throw new IOException(
318 string.Format("Failed to bind port \"{0}:{1}\"", unboundPort.Host, unboundPort.Port));
Jan Tattermusch9d567172017-05-26 14:19:23 -0700319 }
320 }
321 }
322
Jan Tattermusch2b357952015-08-20 14:54:33 -0700323 private void DisposeHandle()
324 {
325 var activeCallCount = activeCallCounter.Count;
326 if (activeCallCount > 0)
327 {
328 Logger.Warning("Server shutdown has finished but there are still {0} active calls for that server.", activeCallCount);
329 }
330 handle.Dispose();
331 }
332
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700333 /// <summary>
334 /// Selects corresponding handler for given call and handles the call.
335 /// </summary>
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200336 private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq, Action continuation)
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800337 {
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700338 try
339 {
340 IServerCallHandler callHandler;
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700341 if (!callHandlers.TryGetValue(newRpc.Method, out callHandler))
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700342 {
Jan Tattermuschf72ead32016-10-18 19:02:46 +0200343 callHandler = UnimplementedMethodCallHandler.Instance;
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700344 }
Jan Tattermuschb0893202016-05-25 19:20:39 -0400345 await callHandler.HandleCall(newRpc, cq).ConfigureAwait(false);
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700346 }
347 catch (Exception e)
348 {
Jan Tattermusch05261612015-07-24 00:28:16 -0700349 Logger.Warning(e, "Exception while handling RPC.");
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700350 }
Jan Tattermuschba098842016-10-24 11:19:41 +0200351 finally
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200352 {
353 continuation();
354 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800355 }
356
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700357 /// <summary>
358 /// Handles the native callback.
359 /// </summary>
Jan Tattermusch6e901712016-10-21 00:42:37 +0200360 private void HandleNewServerRpc(bool success, RequestCallContextSafeHandle ctx, CompletionQueueSafeHandle cq)
Jan Tattermusch075dde42015-03-11 18:21:00 -0700361 {
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200362 bool nextRpcRequested = false;
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700363 if (success)
Jan Tattermusch075dde42015-03-11 18:21:00 -0700364 {
Jan Tattermusch6e901712016-10-21 00:42:37 +0200365 var newRpc = ctx.GetServerRpcNew(this);
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700366
367 // after server shutdown, the callback returns with null call
368 if (!newRpc.Call.IsInvalid)
369 {
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200370 nextRpcRequested = true;
371
372 // Start asynchronous handler for the call.
373 // Don't await, the continuations will run on gRPC thread pool once triggered
374 // by cq.Next().
375 #pragma warning disable 4014
376 HandleCallAsync(newRpc, cq, () => AllowOneRpc(cq));
377 #pragma warning restore 4014
Jan Tattermusch5bbd8182015-07-20 20:48:40 -0700378 }
Jan Tattermusch075dde42015-03-11 18:21:00 -0700379 }
Jan Tattermuscha00698f2016-10-17 14:53:48 +0200380
381 if (!nextRpcRequested)
382 {
383 AllowOneRpc(cq);
384 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800385 }
386
Jan Tattermusch97e294a2015-04-23 14:30:59 -0700387 /// <summary>
388 /// Handles native callback.
389 /// </summary>
Jan Tattermuschd3677482015-06-01 19:27:40 -0700390 private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx)
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800391 {
Jan Tattermuschd3677482015-06-01 19:27:40 -0700392 shutdownTcs.SetResult(null);
Jan Tattermusch8ce5e8b2015-02-05 10:56:49 -0800393 }
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700394
395 /// <summary>
396 /// Collection of service definitions.
397 /// </summary>
398 public class ServiceDefinitionCollection : IEnumerable<ServerServiceDefinition>
399 {
400 readonly Server server;
401
402 internal ServiceDefinitionCollection(Server server)
403 {
404 this.server = server;
405 }
406
407 /// <summary>
408 /// Adds a service definition to the server. This is how you register
409 /// handlers for a service with the server. Only call this before Start().
410 /// </summary>
411 public void Add(ServerServiceDefinition serviceDefinition)
412 {
413 server.AddServiceDefinitionInternal(serviceDefinition);
414 }
415
Jan Tattermusch12855fc2015-08-24 16:43:23 -0700416 /// <summary>
417 /// Gets enumerator for this collection.
418 /// </summary>
Jan Tattermusch021df8a2015-08-04 20:31:11 -0700419 public IEnumerator<ServerServiceDefinition> GetEnumerator()
420 {
421 return server.serviceDefinitionsList.GetEnumerator();
422 }
423
424 IEnumerator IEnumerable.GetEnumerator()
425 {
426 return server.serviceDefinitionsList.GetEnumerator();
427 }
428 }
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700429
430 /// <summary>
431 /// Collection of server ports.
432 /// </summary>
433 public class ServerPortCollection : IEnumerable<ServerPort>
434 {
435 readonly Server server;
436
437 internal ServerPortCollection(Server server)
438 {
439 this.server = server;
440 }
441
442 /// <summary>
443 /// Adds a new port on which server should listen.
444 /// Only call this before Start().
445 /// <returns>The port on which server will be listening.</returns>
446 /// </summary>
447 public int Add(ServerPort serverPort)
448 {
449 return server.AddPortInternal(serverPort);
450 }
451
452 /// <summary>
453 /// Adds a new port on which server should listen.
454 /// <returns>The port on which server will be listening.</returns>
455 /// </summary>
456 /// <param name="host">the host</param>
457 /// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
458 /// <param name="credentials">credentials to use to secure this port.</param>
459 public int Add(string host, int port, ServerCredentials credentials)
460 {
461 return Add(new ServerPort(host, port, credentials));
462 }
463
Jan Tattermusch12855fc2015-08-24 16:43:23 -0700464 /// <summary>
465 /// Gets enumerator for this collection.
466 /// </summary>
Jan Tattermusch31ba0632015-08-04 22:02:55 -0700467 public IEnumerator<ServerPort> GetEnumerator()
468 {
469 return server.serverPortList.GetEnumerator();
470 }
471
472 IEnumerator IEnumerable.GetEnumerator()
473 {
474 return server.serverPortList.GetEnumerator();
475 }
476 }
Jan Tattermuscha7608b02015-02-03 17:54:38 -0800477 }
Craig Tiller190d3602015-02-18 09:23:38 -0800478}