* Filters/FilterPackage.cs, Filters/FilterRPM.cs,
[beagle.git] / BeagleClient / Client.cs
blobf85f39c5916a6540eaa0febb92544168545e3f89
1 //
2 // Client.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
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.
27 using System;
28 using System.IO;
29 using System.Net.Sockets;
30 using System.Threading;
31 using System.Xml.Serialization;
32 using Mono.Unix;
34 using GLib;
36 using Beagle.Util;
38 namespace Beagle {
41 internal class Client {
43 private class EventThrowingClosure {
45 private Client client;
46 private ResponseMessage response;
48 public EventThrowingClosure (Client client, ResponseMessage response)
50 this.client = client;
51 this.response = response;
54 public bool ThrowEvent ()
56 if (this.client.AsyncResponseEvent != null)
57 this.client.AsyncResponseEvent (this.response);
59 return false;
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)
96 ~Client ()
98 Close ();
101 public void Close ()
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.
109 this.closed = true;
111 if (this.client != null)
112 this.client.Close ();
114 if (!previously_closed && this.ClosedEvent != null)
115 this.ClosedEvent ();
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
127 // correctly.
128 #if ENABLE_XML_DUMP
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);
136 mem_stream.Close ();
137 #else
138 XmlFu.SerializeUtf8 (req_serializer, stream, new RequestWrapper (request));
139 #endif
140 // Send end of message marker
141 stream.WriteByte (0xff);
142 stream.Flush ();
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)
150 if (this.closed)
151 return;
153 try {
154 NetworkStream stream = this.client.GetStream ();
155 int bytes_read = 0;
157 try {
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) {
167 this.Close ();
168 return;
171 int end_index = -1;
172 int prev_index = 0;
174 do {
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);
187 #if ENABLE_XML_DUMP
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);
191 #endif
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
203 // helper thread.
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.
214 if (!this.closed)
215 BeginRead ();
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)
234 Exception ex = null;
236 try {
237 SendRequest (request);
238 } catch (IOException e) {
239 ex = e;
240 } catch (SocketException e) {
241 ex = e;
244 if (ex != null) {
245 ResponseMessage resp = new ErrorResponse (ex);
247 if (this.AsyncResponseEvent != null)
248 this.AsyncResponseEvent (resp);
249 } else
250 BeginRead ();
253 public void SendAsyncBlocking (RequestMessage request)
255 Exception ex = null;
257 try {
258 SendRequest (request);
259 } catch (IOException e) {
260 ex = e;
261 } catch (SocketException e) {
262 ex = e;
265 if (ex != null) {
266 ResponseMessage resp = new ErrorResponse (ex);
268 if (this.AsyncResponseEvent != null)
269 this.AsyncResponseEvent (resp);
270 return;
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);
286 int bytes_read;
287 bytes_read = stream.Read (buffer, 0, buffer.Length);
288 if (bytes_read == 0)
289 break;
291 int end_index;
292 end_index = ArrayFu.IndexOfByte (buffer, (byte) 0xff);
294 if (end_index == -1) {
295 deserialize_stream.Write (buffer, 0, bytes_read);
296 } else {
297 deserialize_stream.Write (buffer, 0, end_index);
298 deserialize_stream.Seek (0, SeekOrigin.Begin);
300 ResponseMessage resp;
301 try {
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;
329 try {
330 SendRequest (request);
331 } catch (IOException e) {
332 throw_me = e;
333 } catch (SocketException e) {
334 throw_me = 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;
343 do {
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;
361 try {
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)
373 throw throw_me;
375 return resp;