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
;
40 internal class Client
{
42 private class EventThrowingClosure
{
44 private Client client
;
45 private ResponseMessage response
;
47 public EventThrowingClosure (Client client
, ResponseMessage response
)
50 this.response
= response
;
53 public bool ThrowEvent ()
55 if (this.client
.AsyncResponseEvent
!= null)
56 this.client
.AsyncResponseEvent (this.response
);
62 private string socket_name
;
64 private UnixClient client
;
66 private byte[] network_data
= new byte [4096];
67 private MemoryStream buffer_stream
= new MemoryStream ();
69 private bool closed
= false;
71 public delegate void AsyncResponse (ResponseMessage response
);
72 public event AsyncResponse AsyncResponseEvent
;
74 public delegate void Closed ();
75 public event Closed ClosedEvent
;
77 public Client (string client_name
)
79 // use the default socket name when passed null
80 if (client_name
== null)
81 client_name
= "socket";
83 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
84 if (storage_dir
== null)
85 throw new System
.Net
.Sockets
.SocketException ();
87 this.socket_name
= Path
.Combine (storage_dir
, client_name
);
90 public Client () : this (null)
102 bool previously_closed
= this.closed
;
104 // Important to set this before we close the
105 // UnixClient, since that will trigger the
106 // ReadCallback() method, reading 0 bytes off the
107 // wire, and we check this.closed in there.
110 if (this.client
!= null)
111 this.client
.Close ();
113 if (!previously_closed
&& this.ClosedEvent
!= null)
117 static XmlSerializer req_serializer
= new XmlSerializer (typeof (RequestWrapper
), RequestMessage
.Types
);
119 private void SendRequest (RequestMessage request
)
121 this.client
= new UnixClient (this.socket_name
);
122 NetworkStream stream
= this.client
.GetStream ();
124 // The socket may be shut down at some point here. It
125 // is the caller's responsibility to handle the error
127 req_serializer
.Serialize (stream
, new RequestWrapper (request
));
128 // Send end of message marker
129 stream
.WriteByte (0xff);
133 static XmlSerializer resp_serializer
= new XmlSerializer (typeof (ResponseWrapper
), ResponseMessage
.Types
);
135 // This function will be called from its own thread
136 private void ReadCallback (IAsyncResult ar
)
142 NetworkStream stream
= this.client
.GetStream ();
146 bytes_read
= stream
.EndRead (ar
);
147 } catch (SocketException
) {
148 Logger
.Log
.Debug ("Caught SocketException in ReadCallback");
149 } catch (IOException
) {
150 Logger
.Log
.Debug ("Caught IOException in ReadCallback");
153 // Connection hung up, we're through
154 if (bytes_read
== 0) {
163 // 0xff signifies end of message
164 end_index
= ArrayFu
.IndexOfByte (this.network_data
, (byte) 0xff, prev_index
);
166 this.buffer_stream
.Write (this.network_data
, prev_index
, (end_index
== -1 ? bytes_read
: end_index
) - prev_index
);
168 if (end_index
!= -1) {
170 MemoryStream deserialize_stream
= this.buffer_stream
;
171 this.buffer_stream
= new MemoryStream ();
173 deserialize_stream
.Seek (0, SeekOrigin
.Begin
);
175 ResponseWrapper wrapper
;
176 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (deserialize_stream
);
178 ResponseMessage resp
= wrapper
.Message
;
180 deserialize_stream
.Close ();
182 // Run the handler in an idle handler
183 // so that events are thrown in the
184 // main thread instead of this inferior
186 EventThrowingClosure closure
= new EventThrowingClosure (this, resp
);
187 GLib
.Idle
.Add (new IdleHandler (closure
.ThrowEvent
));
189 // Move past the end-of-message marker
190 prev_index
= end_index
+ 1;
192 } while (end_index
!= -1);
194 // Check to see if we're still connected, and keep
195 // looking for new data if so.
199 } catch (Exception e
) {
200 Logger
.Log
.Error ("Got an exception while trying to read data:");
201 Logger
.Log
.Error (e
);
206 private void BeginRead ()
208 NetworkStream stream
= this.client
.GetStream ();
209 Array
.Clear (this.network_data
, 0, this.network_data
.Length
);
210 stream
.BeginRead (this.network_data
, 0, this.network_data
.Length
,
211 new AsyncCallback (ReadCallback
), null);
214 public void SendAsync (RequestMessage request
)
219 SendRequest (request
);
220 } catch (IOException e
) {
222 } catch (SocketException e
) {
227 ResponseMessage resp
= new ErrorResponse (ex
);
229 if (this.AsyncResponseEvent
!= null)
230 this.AsyncResponseEvent (resp
);
235 public void SendAsyncBlocking (RequestMessage request
)
240 SendRequest (request
);
241 } catch (IOException e
) {
243 } catch (SocketException e
) {
248 ResponseMessage resp
= new ErrorResponse (ex
);
250 if (this.AsyncResponseEvent
!= null)
251 this.AsyncResponseEvent (resp
);
256 NetworkStream stream
= this.client
.GetStream ();
257 MemoryStream deserialize_stream
= new MemoryStream ();
259 // This buffer is annoyingly small on purpose, to avoid
260 // having to deal with the case of multiple messages
261 // in a single block.
262 byte [] buffer
= new byte [32];
264 while (! this.closed
) {
266 Array
.Clear (buffer
, 0, buffer
.Length
);
269 bytes_read
= stream
.Read (buffer
, 0, buffer
.Length
);
274 end_index
= ArrayFu
.IndexOfByte (buffer
, (byte) 0xff);
276 if (end_index
== -1) {
277 deserialize_stream
.Write (buffer
, 0, bytes_read
);
279 deserialize_stream
.Write (buffer
, 0, end_index
);
280 deserialize_stream
.Seek (0, SeekOrigin
.Begin
);
282 ResponseMessage resp
;
284 ResponseWrapper wrapper
;
285 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (deserialize_stream
);
287 resp
= wrapper
.Message
;
288 } catch (Exception e
) {
289 resp
= new ErrorResponse (e
);
292 if (this.AsyncResponseEvent
!= null)
293 this.AsyncResponseEvent (resp
);
295 deserialize_stream
.Close ();
296 deserialize_stream
= new MemoryStream ();
297 if (bytes_read
- end_index
- 1 > 0)
298 deserialize_stream
.Write (buffer
, end_index
+1, bytes_read
-end_index
-1);
304 public ResponseMessage
Send (RequestMessage request
)
306 if (request
.Keepalive
)
307 throw new Exception ("A blocking connection on a keepalive request is not allowed");
309 Exception throw_me
= null;
312 SendRequest (request
);
313 } catch (IOException e
) {
315 } catch (SocketException e
) {
319 if (throw_me
!= null)
320 throw new ResponseMessageException (throw_me
);
322 NetworkStream stream
= this.client
.GetStream ();
323 int bytes_read
, end_index
= -1;
326 bytes_read
= stream
.Read (this.network_data
, 0, 4096);
328 //Logger.Log.Debug ("Read {0} bytes", bytes_read);
330 if (bytes_read
> 0) {
331 // 0xff signifies end of message
332 end_index
= ArrayFu
.IndexOfByte (this.network_data
, (byte) 0xff);
334 this.buffer_stream
.Write (this.network_data
, 0,
335 end_index
== -1 ? bytes_read
: end_index
);
337 } while (bytes_read
> 0 && end_index
== -1);
339 this.buffer_stream
.Seek (0, SeekOrigin
.Begin
);
341 ResponseMessage resp
= null;
344 ResponseWrapper wrapper
;
345 wrapper
= (ResponseWrapper
) resp_serializer
.Deserialize (this.buffer_stream
);
347 resp
= wrapper
.Message
;
348 } catch (Exception e
) {
349 throw_me
= new ResponseMessageException (e
);
352 this.buffer_stream
.Close ();
354 if (throw_me
!= null)