Compute lucene-style scores for our hits.
[beagle.git] / BeagleClient / Client.cs
blobbbdf16b4b173ba3d84b1bff7920fbc40d8ed4bf5
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;
33 using GLib;
35 using Beagle.Util;
37 namespace Beagle {
40 internal class Client {
42 private class EventThrowingClosure {
44 private Client client;
45 private ResponseMessage response;
47 public EventThrowingClosure (Client client, ResponseMessage response)
49 this.client = client;
50 this.response = response;
53 public bool ThrowEvent ()
55 if (this.client.AsyncResponseEvent != null)
56 this.client.AsyncResponseEvent (this.response);
58 return false;
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)
95 ~Client ()
97 Close ();
100 public void Close ()
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.
108 this.closed = true;
110 if (this.client != null)
111 this.client.Close ();
113 if (!previously_closed && this.ClosedEvent != null)
114 this.ClosedEvent ();
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
126 // correctly.
127 req_serializer.Serialize (stream, new RequestWrapper (request));
128 // Send end of message marker
129 stream.WriteByte (0xff);
130 stream.Flush ();
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)
138 if (this.closed)
139 return;
141 try {
142 NetworkStream stream = this.client.GetStream ();
143 int bytes_read = 0;
145 try {
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) {
155 this.Close ();
156 return;
159 int end_index = -1;
160 int prev_index = 0;
162 do {
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
185 // helper thread.
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.
196 if (!this.closed)
197 BeginRead ();
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)
216 Exception ex = null;
218 try {
219 SendRequest (request);
220 } catch (IOException e) {
221 ex = e;
222 } catch (SocketException e) {
223 ex = e;
226 if (ex != null) {
227 ResponseMessage resp = new ErrorResponse (ex);
229 if (this.AsyncResponseEvent != null)
230 this.AsyncResponseEvent (resp);
231 } else
232 BeginRead ();
235 public void SendAsyncBlocking (RequestMessage request)
237 Exception ex = null;
239 try {
240 SendRequest (request);
241 } catch (IOException e) {
242 ex = e;
243 } catch (SocketException e) {
244 ex = e;
247 if (ex != null) {
248 ResponseMessage resp = new ErrorResponse (ex);
250 if (this.AsyncResponseEvent != null)
251 this.AsyncResponseEvent (resp);
252 return;
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);
268 int bytes_read;
269 bytes_read = stream.Read (buffer, 0, buffer.Length);
270 if (bytes_read == 0)
271 break;
273 int end_index;
274 end_index = ArrayFu.IndexOfByte (buffer, (byte) 0xff);
276 if (end_index == -1) {
277 deserialize_stream.Write (buffer, 0, bytes_read);
278 } else {
279 deserialize_stream.Write (buffer, 0, end_index);
280 deserialize_stream.Seek (0, SeekOrigin.Begin);
282 ResponseMessage resp;
283 try {
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;
311 try {
312 SendRequest (request);
313 } catch (IOException e) {
314 throw_me = e;
315 } catch (SocketException e) {
316 throw_me = 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;
325 do {
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;
343 try {
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)
355 throw throw_me;
357 return resp;