2010-03-24 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.ServiceModel / System.ServiceModel.Channels / TcpDuplexSessionChannel.cs
blob380dad2e6208e0fa0af05972bcb3f5c0dbaa968f
1 //
2 // TcpDuplexSessionChannel.cs
3 //
4 // Author:
5 // Marcos Cobena (marcoscobena@gmail.com)
6 // Atsushi Enomoto <atsushi@ximian.com>
7 //
8 // Copyright 2007 Marcos Cobena (http://www.youcannoteatbits.org/)
9 //
10 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Net;
36 using System.Net.Sockets;
37 using System.Runtime.Serialization;
38 using System.Runtime.Serialization.Formatters.Binary;
39 using System.ServiceModel.Channels;
40 using System.Text;
41 using System.Threading;
42 using System.Xml;
44 namespace System.ServiceModel.Channels
46 internal class TcpDuplexSessionChannel : DuplexChannelBase, IDuplexSessionChannel
48 class TcpDuplexSession : DuplexSessionBase
50 TcpDuplexSessionChannel owner;
52 internal TcpDuplexSession (TcpDuplexSessionChannel owner)
54 this.owner = owner;
57 public override TimeSpan DefaultCloseTimeout {
58 get { return owner.DefaultCloseTimeout; }
61 public override void Close (TimeSpan timeout)
63 owner.DiscardSession ();
67 TcpChannelInfo info;
68 TcpClient client;
69 bool is_service_side;
70 TcpBinaryFrameManager frame;
71 TcpDuplexSession session; // do not use this directly. Use Session instead.
72 EndpointAddress counterpart_address;
74 public TcpDuplexSessionChannel (ChannelFactoryBase factory, TcpChannelInfo info, EndpointAddress address, Uri via)
75 : base (factory, address, via)
77 is_service_side = false;
78 this.info = info;
80 // make sure to acquire TcpClient here.
81 int explicitPort = Via.Port;
82 client = new TcpClient (Via.Host, explicitPort <= 0 ? TcpTransportBindingElement.DefaultPort : explicitPort);
83 counterpart_address = GetEndpointAddressFromTcpClient (client);
86 public TcpDuplexSessionChannel (ChannelListenerBase listener, TcpChannelInfo info, TcpClient client)
87 : base (listener)
89 is_service_side = true;
90 this.client = client;
91 this.info = info;
92 counterpart_address = GetEndpointAddressFromTcpClient (client);
95 EndpointAddress GetEndpointAddressFromTcpClient (TcpClient client)
97 IPEndPoint ep = (IPEndPoint) client.Client.RemoteEndPoint;
98 return new EndpointAddress (new Uri ("net.tcp://" + ep));
101 public MessageEncoder Encoder {
102 get { return info.MessageEncoder; }
105 public override EndpointAddress RemoteAddress {
106 get { return base.RemoteAddress ?? counterpart_address; }
109 public override EndpointAddress LocalAddress {
110 get { return base.LocalAddress ?? counterpart_address; }
113 public IDuplexSession Session {
114 get {
115 if (session == null)
116 session = new TcpDuplexSession (this);
117 return session;
121 internal TcpClient TcpClient {
122 get { return client; }
125 void DiscardSession ()
127 if (client.Connected)
128 frame.WriteEndRecord ();
129 session = null;
132 public override void Send (Message message)
134 Send (message, DefaultSendTimeout);
137 public override void Send (Message message, TimeSpan timeout)
139 ThrowIfDisposedOrNotOpen ();
141 if (timeout <= TimeSpan.Zero)
142 throw new ArgumentException (String.Format ("Timeout value must be positive value. It was {0}", timeout));
144 if (!is_service_side) {
145 if (message.Headers.To == null)
146 message.Headers.To = RemoteAddress.Uri;
147 } else {
148 if (message.Headers.RelatesTo == null && OperationContext.Current.IncomingMessageHeaders != null)
149 message.Headers.RelatesTo = OperationContext.Current.IncomingMessageHeaders.MessageId;
152 client.SendTimeout = (int) timeout.TotalMilliseconds;
153 frame.WriteSizedMessage (message);
156 public override bool TryReceive (TimeSpan timeout, out Message message)
158 ThrowIfDisposedOrNotOpen ();
160 // FIXME: there seems to be some pipeline or channel-
161 // recycling issues, which could be mostly workarounded
162 // by delaying input receiver.
163 // This place is not ideal, but it covers both loops in
164 // ChannelDispatcher and DuplexClientRuntimeChannel.
165 Thread.Sleep (50);
167 if (timeout <= TimeSpan.Zero)
168 throw new ArgumentException (String.Format ("Timeout value must be positive value. It was {0}", timeout));
169 client.ReceiveTimeout = (int) timeout.TotalMilliseconds;
170 message = frame.ReadSizedMessage ();
171 // FIXME: this may not be precise, but connection might be reused for some weird socket state transition (that's what happens). So as a workaround, avoid closing the session by sending EndRecord from this channel at OnClose().
172 if (message == null) {
173 session = null;
174 return false;
176 return true;
179 public override bool WaitForMessage (TimeSpan timeout)
181 ThrowIfDisposedOrNotOpen ();
183 if (client.Available > 0)
184 return true;
186 DateTime start = DateTime.Now;
187 do {
188 Thread.Sleep (50);
189 if (client.Available > 0)
190 return true;
191 } while (DateTime.Now - start < timeout);
192 return false;
195 // CommunicationObject
197 [MonoTODO]
198 protected override void OnAbort ()
200 if (session != null)
201 session.Close (TimeSpan.FromTicks (0));
203 if (client != null)
204 client.Close ();
207 protected override void OnClose (TimeSpan timeout)
209 if (session != null)
210 session.Close (timeout);
212 if (client != null)
213 client.Close ();
216 protected override void OnOpen (TimeSpan timeout)
218 if (! is_service_side) {
219 NetworkStream ns = client.GetStream ();
220 frame = new TcpBinaryFrameManager (TcpBinaryFrameManager.DuplexMode, ns, is_service_side) {
221 Encoder = this.Encoder,
222 Via = this.Via };
223 frame.ProcessPreambleInitiator ();
224 frame.ProcessPreambleAckInitiator ();
225 } else {
226 // server side
227 Stream s = client.GetStream ();
229 frame = new TcpBinaryFrameManager (TcpBinaryFrameManager.DuplexMode, s, is_service_side) { Encoder = this.Encoder };
231 // FIXME: use retrieved record properties in the request processing.
233 frame.ProcessPreambleRecipient ();
234 frame.ProcessPreambleAckRecipient ();