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 #if !MOBILE && !XAMMAC_4_5
30 using System;
31 using System.Collections.Generic;
32 using System.ServiceModel;
33 using System.ServiceModel.Channels;
34 using System.ServiceModel.Description;
35 using System.ServiceModel.Dispatcher;
36 using NUnit.Framework;
37 
38 using MonoTests.Helpers;
39 
40 namespace MonoTests.System.ServiceModel
41 {
42 	[TestFixture]
43 	public class ServiceHostTest
44 	{
45 		class MyHost : ServiceHost
46 		{
MyHost(Type type, Uri uri)47 			public MyHost (Type type, Uri uri)
48 				: base (type, uri)
49 			{
50 			}
51 
52 			public IDictionary<string,ContractDescription> ExposedContracts {
53 				get { return ImplementedContracts; }
54 			}
55 		}
56 
57 		[Test]
Ctor()58 		public void Ctor ()
59 		{
60 			MyHost host = new MyHost (typeof (Foo), new Uri ("http://localhost"));
61 			Assert.IsNotNull (host.Description, "#1");
62 			Assert.AreEqual (typeof (Foo), host.Description.ServiceType, "#1-2");
63 			Assert.IsNotNull (host.BaseAddresses, "#2");
64 			Assert.AreEqual (1, host.BaseAddresses.Count, "#3");
65 
66 			Assert.IsNotNull (host.ChannelDispatchers, "#4");
67 			Assert.AreEqual (0, host.ChannelDispatchers.Count, "#5");
68 			Assert.IsNotNull (host.Authorization, "#6");
69 			Assert.IsNotNull (host.ExposedContracts, "#7");
70 			// Foo is already in the contracts.
71 			Assert.AreEqual (1, host.ExposedContracts.Count, "#8");
72 			// this loop iterates only once.
73 			foreach (KeyValuePair<string,ContractDescription> e in host.ExposedContracts) {
74 				// hmm... so, seems like the key is just the full name of the contract type.
75 				Assert.AreEqual ("MonoTests.System.ServiceModel.ServiceHostTest+Foo", e.Key, "#9");
76 				ContractDescription cd = e.Value;
77 				Assert.AreEqual ("Foo", cd.Name, "#10");
78 				Assert.AreEqual ("http://tempuri.org/", cd.Namespace, "#11");
79 			}
80 		}
81 
82 		[Test]
83 		[ExpectedException (typeof (ArgumentNullException))]
CtorNull()84 		public void CtorNull ()
85 		{
86 			new ServiceHost (typeof (Foo), null);
87 		}
88 
89 		[Test]
90 		[ExpectedException (typeof (ArgumentException))]
CtorServiceTypeNotClass()91 		public void CtorServiceTypeNotClass ()
92 		{
93 			new ServiceHost (typeof (IBar), new Uri ("http://localhost"));
94 		}
95 
96 		[Test]
97 		[ExpectedException (typeof (ArgumentException))]
CtorRelativeBaseAddress()98 		public void CtorRelativeBaseAddress ()
99 		{
100 			new ServiceHost (typeof (Foo), new Uri ("test", UriKind.Relative));
101 		}
102 
103 		[Test]
104 		[ExpectedException (typeof (ArgumentException))]
CtorMultipleAddressPerScheme()105 		public void CtorMultipleAddressPerScheme ()
106 		{
107 			new ServiceHost ( typeof (Foo),
108 					new Uri ("http://localhost", UriKind.Absolute),
109 					new Uri ("http://someotherhost", UriKind.Absolute));
110 		}
111 
112 		[Test]
AddServiceEndpoint()113 		public void AddServiceEndpoint ()
114 		{
115 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
116 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
117 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "svc");
118 
119 			Assert.IsNotNull (host.Description, "#6");
120 			Assert.IsNotNull (host.Description.Endpoints, "#7");
121 			Assert.AreEqual (host.Description.Endpoints.Count, 2, "#8");
122 			Assert.AreEqual ("http://localhost/echo/rel", host.Description.Endpoints [0].Address.Uri.AbsoluteUri,  "#9");
123 		}
124 
125 		[Test]
126 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint1()127 		public void AddServiceEndpoint1 ()
128 		{
129 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("ftp://localhost/echo"));
130 			// ftp does not match BasicHttpBinding
131 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
132 		}
133 
134 		[Test]
135 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint2()136 		public void AddServiceEndpoint2 ()
137 		{
138 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
139 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
140 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel"); // duplicate URI
141 
142 			host.Open ();
143 			host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
144 		}
145 
146 		[Test]
147 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint2_2()148 		public void AddServiceEndpoint2_2 ()
149 		{
150 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
151 			// same as above, but through Endpoints.Add()
152 			host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (Foo)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo/rel")));
153 			host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (Foo)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo/rel")));
154 
155 			host.Open ();
156 			host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
157 		}
158 
159 		[Test]
160 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint2_3()161 		public void AddServiceEndpoint2_3 ()
162 		{
163 			ServiceHost host = new ServiceHost (typeof (HogeFuga), new Uri ("http://localhost/echo"));
164 			host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (IHoge)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo")));
165 			host.Description.Endpoints.Add (new ServiceEndpoint (ContractDescription.GetContract (typeof (IFuga)), new BasicHttpBinding (), new EndpointAddress ("http://localhost/echo")));
166 
167 			// Different contracts unlike previous two cases.
168 			// If two or more endpoints are bound to the same listen
169 			// URI, then they must share the same instance.
170 
171 			host.Open ();
172 			host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
173 		}
174 
175 		[Test]
AddServiceEndpoint2_4()176 		public void AddServiceEndpoint2_4 ()
177 		{
178 			var ep = "http://" + NetworkHelpers.LocalEphemeralEndPoint().ToString();
179 			ServiceHost host = new ServiceHost (typeof (HogeFuga), new Uri (ep));
180 			var binding = new BasicHttpBinding ();
181 			host.AddServiceEndpoint (typeof (IHoge), binding, new Uri (ep));
182 			host.AddServiceEndpoint (typeof (IFuga), binding, new Uri (ep));
183 
184 			// Use the same binding, results in one ChannelDispatcher (actually two, for metadata/debug behavior).
185 			host.Open ();
186 			try {
187 				Assert.AreEqual (2, host.ChannelDispatchers.Count, "#1");
188 				foreach (ChannelDispatcher cd in host.ChannelDispatchers) {
189 					if (cd.BindingName != binding.Name)
190 						continue; // mex
191 					Assert.AreEqual (2, cd.Endpoints.Count, "#2");
192 				}
193 			} finally {
194 				host.Close ();
195 			}
196 		}
197 
198 		[Test]
199 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint3()200 		public void AddServiceEndpoint3 ()
201 		{
202 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
203 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "rel");
204 			host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "http://localhost/echo/rel"); // duplicate URI when resolved
205 
206 			host.Open ();
207 			host.Close (); // should not reach here. It is to make sure to close unexpectedly opened host.
208 		}
209 
210 		[Test]
Open()211 		public void Open ()
212 		{
213 			ServiceHost host = new ServiceHost (typeof (ZeroOperationsImpl));
214 			host.AddServiceEndpoint (typeof (IHaveZeroOperarationsContract), new BasicHttpBinding (), "http://localhost/echo");
215 
216 			try {
217 				host.Open ();
218 				Assert.Fail ("InvalidOperationException expected");
219 			}
220 			catch (InvalidOperationException e) {
221 				//"ContractDescription 'IHaveZeroOperarationsContract' has zero operations; a contract must have at least one operation."
222 				StringAssert.Contains ("IHaveZeroOperarationsContract", e.Message);
223 			}
224 			finally {
225 				if (host.State == CommunicationState.Opened)
226 					host.Close (); // It is to make sure to close unexpectedly opened host if the test fail.
227 			}
228 		}
229 
230 		[Test]
AddServiceEndpoint4()231 		public void AddServiceEndpoint4 ()
232 		{
233 			ServiceHost host = new ServiceHost (typeof (Baz), new Uri ("http://localhost/echo"));
234 			host.AddServiceEndpoint ("MonoTests.System.ServiceModel.ServiceHostTest+IBaz", new BasicHttpBinding (), "rel");
235 		}
236 
237 		[Test]
238 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint5()239 		public void AddServiceEndpoint5 ()
240 		{
241 			ServiceHost host = new ServiceHost (typeof (Baz), new Uri ("http://localhost/echo"));
242 
243 			// Full type name is expected here (see AddServiceEndpoint4).
244 			host.AddServiceEndpoint ("IBaz", new BasicHttpBinding (), "rel");
245 		}
246 
247 		[Test]
248 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpoint6()249 		public void AddServiceEndpoint6 ()
250 		{
251 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
252 			host.AddServiceEndpoint ("ISuchTypeDoesNotExist", new BasicHttpBinding (), "rel");
253 		}
254 
255 		[Test]
AddServiceEndpoint7()256 		public void AddServiceEndpoint7 ()
257 		{
258 			ServiceHost host = new ServiceHost (typeof (Foo), new Uri ("http://localhost/echo"));
259 			var a = host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "a");
260 			Console.WriteLine (a.Address);
261 			Assert.AreEqual ("http", a.Address.Uri.Scheme, "#1");
262 			Assert.AreEqual ("http://localhost/echo/a", a.Address.Uri.AbsoluteUri, "#2");
263 
264 			var b = host.AddServiceEndpoint (typeof (Foo), new BasicHttpBinding (), "/b");
265 			Console.WriteLine (b.Address);
266 			Assert.AreEqual ("http", b.Address.Uri.Scheme, "#3");
267 			Assert.AreEqual ("http://localhost/echo/b", b.Address.Uri.AbsoluteUri, "#4");
268 		}
269 
270 		[Test]
271 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpointMexWithNoImpl()272 		public void AddServiceEndpointMexWithNoImpl ()
273 		{
274 			var port = NetworkHelpers.FindFreePort ();
275 			using (ServiceHost h = new ServiceHost (typeof (Foo), new Uri ("http://localhost:" + port))) {
276 				// it expects ServiceMetadataBehavior
277 				h.AddServiceEndpoint (ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding (), "mex");
278 			}
279 		}
280 
281 		[Test]
AddServiceEndpointMetadataExchange()282 		public void AddServiceEndpointMetadataExchange ()
283 		{
284 			var port = NetworkHelpers.FindFreePort ();
285 			// MyMetadataExchange implements IMetadataExchange
286 			ServiceHost host = new ServiceHost (typeof (MyMetadataExchange));
287 			host.AddServiceEndpoint ("IMetadataExchange",
288 						 new BasicHttpBinding (),
289 						 "http://localhost:" + port + "/");
290 		}
291 
292 		[Test]
293 		[ExpectedException (typeof (InvalidOperationException))]
AddServiceEndpointMetadataExchangeFullNameFails()294 		public void AddServiceEndpointMetadataExchangeFullNameFails ()
295 		{
296 			var port = NetworkHelpers.FindFreePort ();
297 			ServiceHost host = new ServiceHost (typeof (MyMetadataExchange));
298 			host.AddServiceEndpoint ("System.ServiceModel.Description.IMetadataExchange",
299 						 new BasicHttpBinding (),
300 						 "http://localhost:" + port);
301 		}
302 
303 		[Test]
InstanceWithNonSingletonMode()304 		public void InstanceWithNonSingletonMode ()
305 		{
306 			var ep = NetworkHelpers.LocalEphemeralEndPoint().ToString();
307 			ServiceHost host = new ServiceHost (
308 				new NonSingletonService ());
309 			Assert.IsNotNull (host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().GetWellKnownSingleton (), "premise1");
310 			host.AddServiceEndpoint (
311 				typeof (NonSingletonService),
312 				new BasicHttpBinding (),
313 				new Uri ("http://" + ep + "/s1"));
314 
315 			// in case Open() didn't fail, we need to close the host.
316 			// And even if Close() caused the expected exception,
317 			// the test should still fail.
318 			try {
319 				host.Open ();
320 				try {
321 					if (host.State == CommunicationState.Opened)
322 						host.Close ();
323 				} catch (InvalidOperationException) {
324 				}
325 				Assert.Fail ("InstanceContextMode was not checked");
326 			} catch (InvalidOperationException) {
327 			}
328 		}
329 
330 
331 		[Test]
InstanceWithSingletonMode()332 		public void InstanceWithSingletonMode ()
333 		{
334             var ep = NetworkHelpers.LocalEphemeralEndPoint().ToString();
335 			SingletonService instance = new SingletonService ();
336 			ServiceHost host = new ServiceHost (instance);
337 			Assert.IsNotNull (host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().GetWellKnownSingleton (), "#1");
338 			host.AddServiceEndpoint (
339 				typeof (SingletonService),
340 				new BasicHttpBinding (),
341 				new Uri ("http://" + ep + "/s2"));
342 
343 			// in case Open() didn't fail, we need to close the host.
344 			// And even if Close() caused the expected exception,
345 			// the test should still fail.
346 			try {
347 				host.Open ();
348 				ChannelDispatcher cd = (ChannelDispatcher) host.ChannelDispatchers [0];
349 				DispatchRuntime dr = cd.Endpoints [0].DispatchRuntime;
350 				Assert.IsNotNull (dr.InstanceContextProvider, "#2");
351 				InstanceContext ctx = dr.InstanceContextProvider.GetExistingInstanceContext (null, null);
352 				Assert.IsNotNull (ctx, "#3");
353 				Assert.AreEqual (instance, ctx.GetServiceInstance (), "#4");
354 			} finally {
355 				if (host.State == CommunicationState.Opened)
356 					host.Close ();
357 			}
358 		}
359 
360 		[Test]
InstanceWithSingletonMode_InheritServiceBehavior()361 		public void InstanceWithSingletonMode_InheritServiceBehavior ()
362 		{
363 			// # 37035
364 
365 			var ep = NetworkHelpers.LocalEphemeralEndPoint ().ToString ();
366 
367 			ChildSingletonService instance = new ChildSingletonService ();
368 			ServiceHost host = new ServiceHost (instance);
369 
370 			host.AddServiceEndpoint (typeof (SingletonService),
371 						 new BasicHttpBinding (),
372 						 new Uri ("http://" + ep + "/s3"));
373 
374 			try {
375 				host.Open ();
376 			} catch (InvalidOperationException ex) {
377 				Assert.Fail ("InstanceContextMode was not inherited from parent, exception was: {0}", ex);
378 			} finally {
379 				host.Close ();
380 			}
381 		}
382 
383 		[ServiceContract]
384 		interface IBar
385 		{
386 		}
387 
388 		[ServiceContract]
389 		class Foo
390 		{
391 			[OperationContract]
SayWhat()392 			public void SayWhat () { }
393 		}
394 
395 		[ServiceContract]
396 		interface IBaz
397 		{
398 			[OperationContract]
Echo(string source)399 			string Echo (string source);
400 		}
401 
402 		[ServiceContract]
403 		interface IHoge
404 		{
405 			[OperationContract]
DoX()406 			void DoX ();
407 		}
408 
409 		[ServiceContract]
410 		interface IFuga
411 		{
412 			[OperationContract]
DoY()413 			void DoY ();
414 		}
415 
416 		[ServiceContract]
417 		interface IHaveZeroOperarationsContract
418 		{
Echo(string source)419 			string Echo (string source);
420 		}
421 
422 		class ZeroOperationsImpl : IHaveZeroOperarationsContract
423 		{
Echo(string source)424 			public string Echo(string source)
425 			{
426 				return null;
427 			}
428 		}
429 
430 		class HogeFuga : IHoge, IFuga
431 		{
DoX()432 			public void DoX () {}
DoY()433 			public void DoY () {}
434 		}
435 
436 		class Baz : IBaz
437 		{
Echo(string source)438 			public string Echo (string source)
439 			{
440 				return source;
441 			}
442 		}
443 
444 		class MyMetadataExchange : IMetadataExchange
445 		{
Get(Message req)446 			public Message Get (Message req)
447 			{
448 				throw new NotImplementedException ();
449 			}
450 
BeginGet(Message request, AsyncCallback cb, object state)451 			public IAsyncResult BeginGet (Message request, AsyncCallback cb, object state)
452 			{
453 				throw new NotImplementedException ();
454 			}
455 
EndGet(IAsyncResult result)456 			public Message EndGet (IAsyncResult result)
457 			{
458 				throw new NotImplementedException ();
459 			}
460 		}
461 
462 		[ServiceContract]
463 		public class NonSingletonService
464 		{
465 			[OperationContract]
Process(string input)466 			public void Process (string input)
467 			{
468 			}
469 		}
470 
471 		[ServiceContract]
472 		[ServiceBehavior (InstanceContextMode = InstanceContextMode.Single)]
473 		public class SingletonService
474 		{
475 			[OperationContract]
Process(string input)476 			public virtual void Process (string input)
477 			{
478 			}
479 		}
480 
481 		public class ChildSingletonService : SingletonService
482 		{
Process(string input)483 			public override void Process (string input)
484 			{
485 			}
486 		}
487 	}
488 }
489 #endif
490