[tests] Avoid "Address already in use"
[mono.git] / mcs / class / System.ServiceModel / Test / System.ServiceModel / ServiceHostTest.cs
1 //
2 // ServiceHostTest.cs
3 //
4 // Author:
5 //      Ankit Jain  <jankit@novell.com>
6 //      Atsushi Enomoto  <atsushi@ximian.com>
7 //
8 // Copyright (C) 2005-2006 Novell, Inc.  http://www.novell.com
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 using System;
30 using System.Collections.Generic;
31 using System.ServiceModel;
32 using System.ServiceModel.Channels;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using NUnit.Framework;
36
37 using MonoTests.Helpers;
38
39 namespace MonoTests.System.ServiceModel
40 {
41         [TestFixture]
42         public class ServiceHostTest
43         {
44                 class MyHost : ServiceHost
45                 {
46                         public MyHost (Type type, Uri uri)
47                                 : base (type, uri)
48                         {
49                         }
50
51                         public IDictionary<string,ContractDescription> ExposedContracts {
52                                 get { return ImplementedContracts; }
53                         }
54                 }
55
56                 [Test]
57                 public void Ctor ()
58                 {
59                         MyHost host = new MyHost (typeof (Foo), new Uri ("http://localhost"));
60                         Assert.IsNotNull (host.Description, "#1");
61                         Assert.AreEqual (typeof (Foo), host.Description.ServiceType, "#1-2");
62                         Assert.IsNotNull (host.BaseAddresses, "#2");
63                         Assert.AreEqual (1, host.BaseAddresses.Count, "#3");
64
65                         Assert.IsNotNull (host.ChannelDispatchers, "#4");
66                         Assert.AreEqual (0, host.ChannelDispatchers.Count, "#5");
67                         Assert.IsNotNull (host.Authorization, "#6");
68                         Assert.IsNotNull (host.ExposedContracts, "#7");
69                         // Foo is already in the contracts.
70                         Assert.AreEqual (1, host.ExposedContracts.Count, "#8");
71                         // this loop iterates only once.
72                         foreach (KeyValuePair<string,ContractDescription> e in host.ExposedContracts) {
73                                 // hmm... so, seems like the key is just the full name of the contract type.
74                                 Assert.AreEqual ("MonoTests.System.ServiceModel.ServiceHostTest+Foo", e.Key, "#9");
75                                 ContractDescription cd = e.Value;
76                                 Assert.AreEqual ("Foo", cd.Name, "#10");
77                                 Assert.AreEqual ("http://tempuri.org/", cd.Namespace, "#11");
78                         }
79                 }
80
81                 [Test]
82                 [ExpectedException (typeof (ArgumentNullException))]
83                 public void CtorNull ()
84                 {
85                         new ServiceHost (typeof (Foo), null);
86                 }
87
88                 [Test]
89                 [ExpectedException (typeof (ArgumentException))]
90                 public void CtorServiceTypeNotClass ()
91                 {
92                         new ServiceHost (typeof (IBar), new Uri ("http://localhost"));
93                 }
94
95                 [Test]
96                 [ExpectedException (typeof (ArgumentException))]
97                 public void CtorRelativeBaseAddress ()
98                 {
99                         new ServiceHost (typeof (Foo), new Uri ("test", UriKind.Relative));
100                 }
101                 
102                 [Test]
103                 [ExpectedException (typeof (ArgumentException))]
104                 public void CtorMultipleAddressPerScheme ()
105                 {
106                         new ServiceHost ( typeof (Foo), 
107                                         new Uri ("http://localhost", UriKind.Absolute),
108                                         new Uri ("http://someotherhost", UriKind.Absolute));
109                 }
110
111                 [Test]
112                 public void AddServiceEndpoint ()
113                 {
114                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
115                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
116                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "svc");
117
118                         Assert.IsNotNull (host.Description, "#6");
119                         Assert.IsNotNull (host.Description.Endpoints, "#7");
120                         Assert.AreEqual (host.Description.Endpoints.Count, 2, "#8");
121                         Assert.AreEqual ("http://localhost/echo/rel", host.Description.Endpoints [0].Address.Uri.AbsoluteUri,  "#9");
122                 }
123
124                 [Test]
125                 [ExpectedException (typeof (InvalidOperationException))]
126                 public void AddServiceEndpoint1 ()
127                 {
128                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("ftp://localhost/echo"));
129                         // ftp does not match BasicHttpBinding
130                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
131                 }
132
133                 [Test]
134                 [ExpectedException (typeof (InvalidOperationException))]
135                 public void AddServiceEndpoint2 ()
136                 {
137                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
138                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
139                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel"); // duplicate URI
140
141                         host.Open ();
142                         host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
143                 }
144
145                 [Test]
146                 [ExpectedException (typeof (InvalidOperationException))]
147                 public void AddServiceEndpoint2_2 ()
148                 {
149                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
150                         // same as above, but through Endpoints.Add()
151                         host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (Foo)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo/rel")));
152                         host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (Foo)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo/rel")));
153
154                         host.Open ();
155                         host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
156                 }
157
158                 [Test]
159                 [ExpectedException (typeof (InvalidOperationException))]
160                 public void AddServiceEndpoint2_3 ()
161                 {
162                         ServiceHost host = new ServiceHost (typeof (HogeFuga), new Uri ("http://localhost/echo"));
163                         host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (IHoge)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo")));
164                         host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (IFuga)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo")));
165
166                         // Different contracts unlike previous two cases.
167                         // If two or more endpoints are bound to the same listen
168                         // URI, then they must share the same instance.
169
170                         host.Open ();
171                         host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
172                 }
173
174                 [Test]
175                 public void AddServiceEndpoint2_4 ()
176                 {
177                         var ep = "http://" + NetworkHelpers.LocalEphemeralEndPoint().ToString();
178                         ServiceHost host = new ServiceHost (typeof (HogeFuga), new Uri (ep));
179                         var binding = new BasicHttpBinding ();
180                         host.AddServiceEndpoint (typeof (IHoge), binding, new Uri (ep));
181                         host.AddServiceEndpoint (typeof (IFuga), binding, new Uri (ep));
182
183                         // Use the same binding, results in one ChannelDispatcher (actually two, for metadata/debug behavior).
184                         host.Open ();
185                         try {
186                                 Assert.AreEqual (2, host.ChannelDispatchers.Count, "#1");
187                                 foreach (ChannelDispatcher cd in host.ChannelDispatchers) {
188                                         if (cd.BindingName != binding.Name)
189                                                 continue; // mex
190                                         Assert.AreEqual (2, cd.Endpoints.Count, "#2");
191                                 }
192                         } finally {
193                                 host.Close ();
194                         }
195                 }
196
197                 [Test]
198                 [ExpectedException (typeof (InvalidOperationException))]
199                 public void AddServiceEndpoint3 ()
200                 {
201                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
202                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
203                         host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "http://localhost/echo/rel"); // duplicate URI when resolved
204
205                         host.Open ();
206                         host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
207                 }
208
209                 [Test]
210                 public void Open ()
211                 {
212                         ServiceHost host = new ServiceHost (typeof (ZeroOperationsImpl));
213                         host.AddServiceEndpoint (typeof (IHaveZeroOperarationsContract), new BasicHttpBinding (), "http://localhost/echo");
214
215                         try {
216                                 host.Open ();
217                                 Assert.Fail ("InvalidOperationException expected");
218                         } 
219                         catch (InvalidOperationException e) {
220                                 //"ContractDescription 'IHaveZeroOperarationsContract' has zero operations; a contract must have at least one operation."
221                                 StringAssert.Contains ("IHaveZeroOperarationsContract", e.Message);
222                         }
223                         finally {
224                                 if (host.State == CommunicationState.Opened)
225                                         host.Close (); // It is to make sure to close unexpectedly opened host if the test fail.
226                         }
227                 }
228
229                 [Test]
230                 public void AddServiceEndpoint4 ()
231                 {
232                         ServiceHost host = new ServiceHost (typeof (Baz), new Uri ("http://localhost/echo"));
233                         host.AddServiceEndpoint ("MonoTests.System.ServiceModel.ServiceHostTest+IBaz", new BasicHttpBinding (), "rel");
234                 }
235
236                 [Test]
237                 [ExpectedException (typeof (InvalidOperationException))]
238                 public void AddServiceEndpoint5 ()
239                 {
240                         ServiceHost host = new ServiceHost (typeof (Baz), new Uri ("http://localhost/echo"));
241
242                         // Full type name is expected here (see AddServiceEndpoint4).
243                         host.AddServiceEndpoint ("IBaz", new BasicHttpBinding (), "rel");
244                 }
245
246                 [Test]
247                 [ExpectedException (typeof (InvalidOperationException))]
248                 public void AddServiceEndpoint6 ()
249                 {
250                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
251                         host.AddServiceEndpoint ("ISuchTypeDoesNotExist", new BasicHttpBinding (), "rel");
252                 }
253
254                 [Test]
255                 public void AddServiceEndpoint7 ()
256                 {
257                         ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
258                         var a = host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "a");
259                         Console.WriteLine (a.Address);
260                         Assert.AreEqual ("http", a.Address.Uri.Scheme, "#1");
261                         Assert.AreEqual ("http://localhost/echo/a", a.Address.Uri.AbsoluteUri, "#2");
262
263                         var b = host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "/b");
264                         Console.WriteLine (b.Address);
265                         Assert.AreEqual ("http", b.Address.Uri.Scheme, "#3");
266                         Assert.AreEqual ("http://localhost/echo/b", b.Address.Uri.AbsoluteUri, "#4");
267                 }
268                 
269                 [Test]
270                 [ExpectedException (typeof (InvalidOperationException))]
271                 public void AddServiceEndpointMexWithNoImpl ()
272                 {
273                         using (ServiceHost h = new ServiceHost (typeof (Foo), new Uri ("http://localhost:8080"))) {
274                                 // it expects ServiceMetadataBehavior
275                                 h.AddServiceEndpoint (ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding (), "mex");
276                         }
277                 }
278
279                 [Test]
280                 public void AddServiceEndpointMetadataExchange ()
281                 {
282                         // MyMetadataExchange implements IMetadataExchange
283                         ServiceHost host = new ServiceHost (typeof (MyMetadataExchange));
284                         host.AddServiceEndpoint ("IMetadataExchange",
285                                                  new BasicHttpBinding (),
286                                                  "http://localhost:8080/");
287                 }
288
289                 [Test]
290                 [ExpectedException (typeof (InvalidOperationException))]
291                 public void AddServiceEndpointMetadataExchangeFullNameFails ()
292                 {
293                         ServiceHost host = new ServiceHost (typeof (MyMetadataExchange));
294                         host.AddServiceEndpoint ("System.ServiceModel.Description.IMetadataExchange",
295                                                  new BasicHttpBinding (),
296                                                  "http://localhost:8080");
297                 }
298
299                 [Test]
300                 public void InstanceWithNonSingletonMode ()
301                 {
302                         var ep = NetworkHelpers.LocalEphemeralEndPoint().ToString();
303                         ServiceHost host = new ServiceHost (
304                                 new NonSingletonService ());
305                         Assert.IsNotNull (host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().GetWellKnownSingleton (), "premise1");
306                         host.AddServiceEndpoint (
307                                 typeof (NonSingletonService),
308                                 new BasicHttpBinding (),
309                                 new Uri ("http://" + ep + "/s1"));
310
311                         // in case Open() didn't fail, we need to close the host.
312                         // And even if Close() caused the expected exception,
313                         // the test should still fail.
314                         try {
315                                 host.Open ();
316                                 try {
317                                         if (host.State == CommunicationState.Opened)
318                                                 host.Close ();
319                                 } catch (InvalidOperationException) {
320                                 }
321                                 Assert.Fail ("InstanceContextMode was not checked");
322                         } catch (InvalidOperationException) {
323                         }
324                 }
325
326
327                 [Test]
328                 public void InstanceWithSingletonMode ()
329                 {
330             var ep = NetworkHelpers.LocalEphemeralEndPoint().ToString();
331                         SingletonService instance = new SingletonService ();
332                         ServiceHost host = new ServiceHost (instance);
333                         Assert.IsNotNull (host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().GetWellKnownSingleton (), "#1");
334                         host.AddServiceEndpoint (
335                                 typeof (SingletonService),
336                                 new BasicHttpBinding (),
337                                 new Uri ("http://" + ep + "/s2"));
338
339                         // in case Open() didn't fail, we need to close the host.
340                         // And even if Close() caused the expected exception,
341                         // the test should still fail.
342                         try {
343                                 host.Open ();
344                                 ChannelDispatcher cd = (ChannelDispatcher) host.ChannelDispatchers [0];
345                                 DispatchRuntime dr = cd.Endpoints [0].DispatchRuntime;
346                                 Assert.IsNotNull (dr.InstanceContextProvider, "#2");
347                                 InstanceContext ctx = dr.InstanceContextProvider.GetExistingInstanceContext (null, null);
348                                 Assert.IsNotNull (ctx, "#3");
349                                 Assert.AreEqual (instance, ctx.GetServiceInstance (), "#4");
350                         } finally {
351                                 if (host.State == CommunicationState.Opened)
352                                         host.Close ();
353                         }
354                 }
355
356                 [ServiceContract]
357                 interface IBar
358                 {
359                 }
360
361                 [ServiceContract]
362                 class Foo
363                 {
364                         [OperationContract]
365                         public void SayWhat () { }
366                 }
367
368                 [ServiceContract]
369                 interface IBaz
370                 {
371                         [OperationContract]
372                         string Echo (string source);
373                 }
374                 
375                 [ServiceContract]
376                 interface IHoge
377                 {
378                         [OperationContract]
379                         void DoX ();
380                 }
381
382                 [ServiceContract]
383                 interface IFuga
384                 {
385                         [OperationContract]
386                         void DoY ();
387                 }
388
389                 [ServiceContract]
390                 interface IHaveZeroOperarationsContract
391                 {
392                         string Echo (string source);
393                 }
394
395                 class ZeroOperationsImpl : IHaveZeroOperarationsContract
396                 {
397                         public string Echo(string source)
398                         {
399                                 return null;
400                         }
401                 }
402
403                 class HogeFuga : IHoge, IFuga
404                 {
405                         public void DoX () {}
406                         public void DoY () {}
407                 }
408
409                 class Baz : IBaz
410                 {
411                         public string Echo (string source)
412                         {
413                                 return source;
414                         }
415                 }
416
417                 class MyMetadataExchange : IMetadataExchange
418                 {
419                         public Message Get (Message req)
420                         {
421                                 throw new NotImplementedException ();
422                         }
423
424                         public IAsyncResult BeginGet (Message request, AsyncCallback cb, object state)
425                         {
426                                 throw new NotImplementedException ();
427                         }
428
429                         public Message EndGet (IAsyncResult result)
430                         {
431                                 throw new NotImplementedException ();
432                         }
433                 }
434
435                 [ServiceContract]
436                 public class NonSingletonService
437                 {
438                         [OperationContract]
439                         public void Process (string input)
440                         {
441                         }
442                 }
443
444                 [ServiceContract]
445                 [ServiceBehavior (InstanceContextMode = InstanceContextMode.Single)]
446                 public class SingletonService
447                 {
448                         [OperationContract]
449                         public void Process (string input)
450                         {
451                         }
452                 }
453         }
454 }