Merge pull request #234 from kripper/master
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpClientTransportSink.cs
1 //
2 // System.Runtime.Remoting.Channels.Tcp.TcpClientTransportSink.cs
3 //
4 // Author: Dietmar Maurer (dietmar@ximian.com)
5 //         Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // 2002 (C) Copyright, Ximian, Inc.
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Runtime.Remoting.Channels;
33 using System.Runtime.Remoting.Messaging;
34 using System.Collections;
35 using System.IO;
36 using System.Threading;
37
38 namespace System.Runtime.Remoting.Channels.Tcp
39 {
40         internal class TcpClientTransportSink : IClientChannelSink
41         {
42                 string _host;
43                 int _port;
44                 
45                 public TcpClientTransportSink (string url)
46                 {
47                         string objectUri;
48                         string port;
49                         
50                         TcpChannel.ParseTcpURL (url, out _host, out port, out objectUri);
51                         
52                         try {
53                                 if (port != null)
54                                         _port = Convert.ToInt32 (port);
55                                 else
56                                         _port = 0;
57                         } catch {
58                                 _host = null;
59                                 _port = -1;
60                         }
61                 }
62
63                 public IDictionary Properties
64                 {
65                         get 
66                         {
67                                 return null;
68                         }
69                 }
70
71                 public IClientChannelSink NextChannelSink
72                 {
73                         get 
74                         {
75                                 // we are the last one
76                                 return null;
77                         }
78                 }
79
80                 public void AsyncProcessRequest (IClientChannelSinkStack sinkStack, IMessage msg,
81                         ITransportHeaders headers, Stream requestStream)
82                 {
83                         TcpConnection connection = null;
84                         bool isOneWay = RemotingServices.IsOneWay (((IMethodMessage)msg).MethodBase);
85
86                         try
87                         {
88                                 if (headers == null) headers = new TransportHeaders();
89                                 headers [CommonTransportKeys.RequestUri] = ((IMethodMessage)msg).Uri;
90                                 
91                                 // Sends the stream using a connection from the pool
92                                 // and creates a WorkItem that will wait for the
93                                 // response of the server
94
95                                 connection = TcpConnectionPool.GetConnection (_host, _port);
96                                 TcpMessageIO.SendMessageStream (connection.Stream, requestStream, headers, connection.Buffer, isOneWay);
97                                 connection.Stream.Flush ();
98
99                                 if (!isOneWay) 
100                                 {
101                                         sinkStack.Push (this, connection);
102                                         ThreadPool.QueueUserWorkItem (new WaitCallback(data => {
103                                                 try {
104                                                         ReadAsyncTcpMessage (data);
105                                                 } catch {}
106                                                 }), sinkStack);
107                                 }
108                                 else
109                                         connection.Release();
110                         }
111                         catch
112                         {
113                                 if (connection != null) connection.Release();
114                                 if (!isOneWay) throw;
115                         }
116                 }
117
118                 private void ReadAsyncTcpMessage(object data)
119                 {
120                         // This method is called by a new thread to asynchronously
121                         // read the response to a request
122
123                         // The stack was provided as state data in QueueUserWorkItem
124                         IClientChannelSinkStack stack = (IClientChannelSinkStack)data;
125
126                         // The first sink in the stack is this sink. Pop it and
127                         // get the status data, which is the TcpConnection used to send
128                         // the request
129                         TcpConnection connection = (TcpConnection)stack.Pop(this);
130
131                         try
132                         {
133                                 ITransportHeaders responseHeaders;
134
135                                 // Read the response, blocking if necessary
136                                 MessageStatus status = TcpMessageIO.ReceiveMessageStatus (connection.Stream, connection.Buffer);
137
138                                 if (status != MessageStatus.MethodMessage)
139                                         throw new RemotingException ("Unknown response message from server");
140
141                                 Stream responseStream = TcpMessageIO.ReceiveMessageStream (connection.Stream, out responseHeaders, connection.Buffer);
142
143                                 // Free the connection, so it can be reused
144                                 connection.Release();
145                                 connection = null;
146
147                                 // Ok, proceed with the other sinks
148                                 stack.AsyncProcessResponse (responseHeaders, responseStream);
149                         }
150                         catch
151                         {
152                                 if (connection != null) connection.Release();
153                                 throw;
154                         }
155                 }
156         
157                 public void AsyncProcessResponse (IClientResponseChannelSinkStack sinkStack,
158                         object state, ITransportHeaders headers,
159                         Stream stream)
160                 {
161                         // Should never be called
162                         throw new NotSupportedException();
163                 }
164
165                 public Stream GetRequestStream (IMessage msg, ITransportHeaders headers)
166                 {
167                         return null;
168                 }
169                 
170                 public void ProcessMessage (IMessage msg,
171                         ITransportHeaders requestHeaders,
172                         Stream requestStream,
173                         out ITransportHeaders responseHeaders,
174                         out Stream responseStream)
175                 {
176                         TcpConnection connection = null;
177                         try
178                         {
179                                 if (requestHeaders == null) requestHeaders = new TransportHeaders();
180                                 requestHeaders [CommonTransportKeys.RequestUri] = ((IMethodMessage)msg).Uri;
181                                 
182                                 // Sends the message
183                                 connection = TcpConnectionPool.GetConnection (_host, _port);
184                                 TcpMessageIO.SendMessageStream (connection.Stream, requestStream, requestHeaders, connection.Buffer);
185                                 connection.Stream.Flush ();
186
187                                 // Reads the response
188                                 MessageStatus status = TcpMessageIO.ReceiveMessageStatus (connection.Stream, connection.Buffer);
189
190                                 if (status != MessageStatus.MethodMessage)
191                                         throw new RemotingException ("Unknown response message from server");
192
193                                 responseStream = TcpMessageIO.ReceiveMessageStream (connection.Stream, out responseHeaders, connection.Buffer);
194                         }
195                         finally
196                         {
197                                 if (connection != null) 
198                                         connection.Release();
199                         }
200                 }
201
202         }
203
204
205 }