2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / TcpChannelListener.cs
1 // 
2 // TcpChannelListener.cs
3 // 
4 // Author: 
5 //     Marcos Cobena (marcoscobena@gmail.com)
6 // 
7 // Copyright 2007 Marcos Cobena (http://www.youcannoteatbits.org/)
8 // 
9
10 using System;
11 using System.Collections.Generic;
12 using System.IO;
13 using System.Net;
14 using System.Net.Sockets;
15 using System.ServiceModel.Description;
16 using System.Text;
17 using System.Threading;
18 using System.Xml;
19
20 namespace System.ServiceModel.Channels
21 {
22         internal class TcpChannelListener<TChannel> : InternalChannelListenerBase<TChannel> 
23                 where TChannel : class, IChannel
24         {
25                 BindingContext context;
26                 TcpChannelInfo info;
27                 IDuplexSession session;
28                 Uri listen_uri;
29                 TcpListener tcp_listener;
30                 
31                 public TcpChannelListener (TcpTransportBindingElement source, 
32                                            BindingContext context) : base (context.Binding)
33                 {
34                         MessageEncoder encoder = null;
35                         XmlDictionaryReaderQuotas quotas = null;
36
37                         if (context.ListenUriMode == ListenUriMode.Explicit)
38                                 listen_uri =
39                                         context.ListenUriRelativeAddress != null ?
40                                         new Uri (context.ListenUriBaseAddress, context.ListenUriRelativeAddress) :
41                                         context.ListenUriBaseAddress;
42                         else
43                                 throw new NotImplementedException ();
44                         
45                         foreach (BindingElement be in context.RemainingBindingElements) {
46                                 MessageEncodingBindingElement mbe = be as MessageEncodingBindingElement;
47                                 if (mbe != null) {
48                                         encoder = CreateEncoder<TChannel> (mbe);
49                                         quotas = mbe.GetProperty<XmlDictionaryReaderQuotas> (context);
50                                         break;
51                                 }
52                         }
53                         
54                         if (encoder == null)
55                                 encoder = new BinaryMessageEncoder ();
56
57                         info = new TcpChannelInfo (source, encoder, quotas);
58                 }
59                 
60                 public override Uri Uri {
61                         get { return listen_uri; }
62                 }
63
64                 List<ManualResetEvent> accept_handles = new List<ManualResetEvent> ();
65
66                 protected override TChannel OnAcceptChannel (TimeSpan timeout)
67                 {
68                         TcpClient client = null;
69                         if (tcp_listener.Pending ()) {
70                                 client = tcp_listener.AcceptTcpClient ();
71                         } else {
72                                 var wait = new ManualResetEvent (false);
73                                 tcp_listener.BeginAcceptTcpClient (delegate (IAsyncResult result) {
74                                         client = tcp_listener.EndAcceptTcpClient (result);
75                                         wait.Set ();
76                                         accept_handles.Remove (wait);
77                                 }, null);
78                                 if (State == CommunicationState.Closing)
79                                         return null;
80                                 accept_handles.Add (wait);
81                                 wait.WaitOne (timeout);
82                         }
83                         if (client == null)
84                                 return null; // onclose
85
86                         if (typeof (TChannel) == typeof (IDuplexSessionChannel))
87                                 return (TChannel) (object) new TcpDuplexSessionChannel (this, info, client);
88
89                         // FIXME: To implement more.
90                         throw new NotImplementedException ();
91                 }
92
93                 [MonoTODO]
94                 protected override bool OnWaitForChannel (TimeSpan timeout)
95                 {
96                         throw new NotImplementedException ();
97                 }
98                 
99                 // CommunicationObject
100                 
101                 protected override void OnAbort ()
102                 {
103                         if (State == CommunicationState.Closed)
104                                 return;
105                         ProcessClose ();
106                 }
107
108                 protected override void OnClose (TimeSpan timeout)
109                 {
110                         if (State == CommunicationState.Closed)
111                                 return;
112                         ProcessClose ();
113                 }
114
115                 void ProcessClose ()
116                 {
117                         if (tcp_listener == null)
118                                 throw new InvalidOperationException ("Current state is " + State);
119                         lock (accept_handles) {
120                                 foreach (var wait in accept_handles)
121                                         wait.Set ();
122                         }
123                         tcp_listener.Stop ();
124                         tcp_listener = null;
125                 }
126
127                 protected override void OnOpen (TimeSpan timeout)
128                 {
129                         IPHostEntry entry = Dns.GetHostEntry (listen_uri.Host);
130                         
131                         if (entry.AddressList.Length ==0)
132                                 throw new ArgumentException (String.Format ("Invalid listen URI: {0}", listen_uri));
133                         
134                         int explicitPort = listen_uri.Port;
135                         tcp_listener = new TcpListener (entry.AddressList [0], explicitPort <= 0 ? TcpTransportBindingElement.DefaultPort : explicitPort);
136                         tcp_listener.Start ();
137                 }
138         }
139 }