4 // Copyright (C) 2005 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
29 using System
.Net
.Sockets
;
30 using System
.Threading
;
31 using System
.Xml
.Serialization
;
41 internal class Client
{
43 private class EventThrowingClosure
{
45 private Client client
;
46 private ResponseMessage response
;
48 public EventThrowingClosure (Client client
, ResponseMessage response
)
51 this.response
= response
;
54 public bool ThrowEvent ()
56 if (this.client
.AsyncResponseEvent
!= null)
57 this.client
.AsyncResponseEvent (this.response
);
63 private string socket_name
;
65 private UnixClient client
;
67 private byte[] network_data
= new byte [4096];
68 private MemoryStream buffer_stream
= new MemoryStream ();
70 private bool closed
= false;
72 public delegate void AsyncResponse (ResponseMessage response
);
73 public event AsyncResponse AsyncResponseEvent
;
75 public delegate void Closed ();
76 public event Closed ClosedEvent
;
78 public Client (string client_name
)
80 // use the default socket name when passed null
81 if (client_name
== null)
82 client_name
= "socket";
84 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
85 if (storage_dir
== null)
86 throw new System
.Net
.Sockets
.SocketException ();
88 this.socket_name
= Path
.Combine (storage_dir
, client_name
);
91 public Client () : this (null)
103 bool previously_closed
= this.closed
;
105 // Important to set this before we close the
106 // UnixClient, since that will trigger the
107 // ReadCallback() method, reading 0 bytes off the
108 // wire, and we check this.closed in there.
111 if (this.client
!= null)
112 this.client
.Close ();
114 if (!previously_closed
&& this.ClosedEvent
!= null)
118 static XmlSerializer req_serializer
= new XmlSerializer (typeof (RequestWrapper
), RequestMessage
.Types
);
120 private void SendRequest (RequestMessage request
)
122 this.client
= new UnixClient (this.socket_name
);
123 NetworkStream stream
= this.client
.GetStream ();
125 // The socket may be shut down at some point here. It
126 // is the caller's responsibility to handle the error
129 MemoryStream mem_stream
= new MemoryStream ();
130 XmlFu
.SerializeUtf8 (req_serializer
, mem_stream
, new RequestWrapper (request
));
131 mem_stream
.Seek (0, SeekOrigin
.Begin
);
132 StreamReader r
= new StreamReader (mem_stream
);
133 Logger
.Log
.Debug ("Sending request:\n{0}\n", r
.ReadToEnd ());
134 mem_stream
.Seek (0, SeekOrigin
.Begin
);
135 mem_stream
.WriteTo (stream
);
138 XmlFu
.SerializeUtf8 (req_serializer
, stream
, new RequestWrapper (request
));
140 // Send end of message marker
141 stream
.WriteByte (0xff);
145 static XmlSerializer resp_serializer
= new XmlSerializer (typeof (ResponseWrapper
), ResponseMessage
.Types
);
147 // This function will be called from its own thread
148 private void ReadCallback (IAsyncResult ar
)
154 NetworkStream stream
= this.client
.GetStream ();
158 bytes_read
= stream
.EndRead (ar
);
159 } catch (SocketException
) {
160 Logger
.Log
.Debug ("Caught SocketException in ReadCallback");
161 } catch (IOException
) {
162 Logger
.Log
.Debug ("Caught IOException in ReadCallback");
165 // Connection hung up, we're through
166 if (bytes_read
== 0) {
175 // 0xff signifies end of message
176 end_index
= ArrayFu
.IndexOfByte (this.network_data
, (byte) 0xff, prev_index
);
178 this.buffer_stream
.Write (this.network_data
, prev_index
, (end_index
== -1 ? bytes_read
: end_index
) - prev_index
);
180 if (end_index
!= -1) {
182 MemoryStream deserialize_stream
= this.buffer_stream
;
183 this.buffer_stream
= new MemoryStream ();
185 deserialize_stream
.Seek (0, SeekOrigin
.Begin
);
188 StreamReader r
= new StreamReader (deserialize_stream
);
189 Logger
.Log
.Debug ("Received response:\n{0}\n", r
.ReadToEnd ());
190 deserialize_stream
.Seek (0, SeekOrigin
.Begin
);
193 ResponseWrapper wrapper
;
194 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (deserialize_stream
);
196 ResponseMessage resp
= wrapper
.Message
;
198 deserialize_stream
.Close ();
200 // Run the handler in an idle handler
201 // so that events are thrown in the
202 // main thread instead of this inferior
204 EventThrowingClosure closure
= new EventThrowingClosure (this, resp
);
205 GLib
.Idle
.Add (new IdleHandler (closure
.ThrowEvent
));
207 // Move past the end-of-message marker
208 prev_index
= end_index
+ 1;
210 } while (end_index
!= -1);
212 // Check to see if we're still connected, and keep
213 // looking for new data if so.
217 } catch (Exception e
) {
218 Logger
.Log
.Error ("Got an exception while trying to read data:");
219 Logger
.Log
.Error (e
);
224 private void BeginRead ()
226 NetworkStream stream
= this.client
.GetStream ();
227 Array
.Clear (this.network_data
, 0, this.network_data
.Length
);
228 stream
.BeginRead (this.network_data
, 0, this.network_data
.Length
,
229 new AsyncCallback (ReadCallback
), null);
232 public void SendAsync (RequestMessage request
)
237 SendRequest (request
);
238 } catch (IOException e
) {
240 } catch (SocketException e
) {
245 ResponseMessage resp
= new ErrorResponse (ex
);
247 if (this.AsyncResponseEvent
!= null)
248 this.AsyncResponseEvent (resp
);
253 public void SendAsyncBlocking (RequestMessage request
)
258 SendRequest (request
);
259 } catch (IOException e
) {
261 } catch (SocketException e
) {
266 ResponseMessage resp
= new ErrorResponse (ex
);
268 if (this.AsyncResponseEvent
!= null)
269 this.AsyncResponseEvent (resp
);
274 NetworkStream stream
= this.client
.GetStream ();
275 MemoryStream deserialize_stream
= new MemoryStream ();
277 // This buffer is annoyingly small on purpose, to avoid
278 // having to deal with the case of multiple messages
279 // in a single block.
280 byte [] buffer
= new byte [32];
282 while (! this.closed
) {
284 Array
.Clear (buffer
, 0, buffer
.Length
);
287 bytes_read
= stream
.Read (buffer
, 0, buffer
.Length
);
292 end_index
= ArrayFu
.IndexOfByte (buffer
, (byte) 0xff);
294 if (end_index
== -1) {
295 deserialize_stream
.Write (buffer
, 0, bytes_read
);
297 deserialize_stream
.Write (buffer
, 0, end_index
);
298 deserialize_stream
.Seek (0, SeekOrigin
.Begin
);
300 ResponseMessage resp
;
302 ResponseWrapper wrapper
;
303 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (deserialize_stream
);
305 resp
= wrapper
.Message
;
306 } catch (Exception e
) {
307 resp
= new ErrorResponse (e
);
310 if (this.AsyncResponseEvent
!= null)
311 this.AsyncResponseEvent (resp
);
313 deserialize_stream
.Close ();
314 deserialize_stream
= new MemoryStream ();
315 if (bytes_read
- end_index
- 1 > 0)
316 deserialize_stream
.Write (buffer
, end_index
+1, bytes_read
-end_index
-1);
322 public ResponseMessage
Send (RequestMessage request
)
324 if (request
.Keepalive
)
325 throw new Exception ("A blocking connection on a keepalive request is not allowed");
327 Exception throw_me
= null;
330 SendRequest (request
);
331 } catch (IOException e
) {
333 } catch (SocketException e
) {
337 if (throw_me
!= null)
338 throw new ResponseMessageException (throw_me
);
340 NetworkStream stream
= this.client
.GetStream ();
341 int bytes_read
, end_index
= -1;
344 bytes_read
= stream
.Read (this.network_data
, 0, 4096);
346 //Logger.Log.Debug ("Read {0} bytes", bytes_read);
348 if (bytes_read
> 0) {
349 // 0xff signifies end of message
350 end_index
= ArrayFu
.IndexOfByte (this.network_data
, (byte) 0xff);
352 this.buffer_stream
.Write (this.network_data
, 0,
353 end_index
== -1 ? bytes_read
: end_index
);
355 } while (bytes_read
> 0 && end_index
== -1);
357 this.buffer_stream
.Seek (0, SeekOrigin
.Begin
);
359 ResponseMessage resp
= null;
362 ResponseWrapper wrapper
;
363 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (this.buffer_stream
);
365 resp
= wrapper
.Message
;
366 } catch (Exception e
) {
367 throw_me
= new ResponseMessageException (e
);
370 this.buffer_stream
.Close ();
372 if (throw_me
!= null)