corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27 using System;
28 using System.Net;
29 using System.Threading;
30 using OpenMetaverse.StructuredData;
31  
32 namespace OpenMetaverse.Http
33 {
34 public class EventQueueClient
35 {
36 /// <summary>=</summary>
37 public const int REQUEST_TIMEOUT = 1000 * 120;
38  
39 public delegate void ConnectedCallback();
40 public delegate void EventCallback(string eventName, OSDMap body);
41  
42 public ConnectedCallback OnConnected;
43 public EventCallback OnEvent;
44  
45 public bool Running { get { return _Running; } }
46  
47 protected Uri _Address;
48 protected bool _Dead;
49 protected bool _Running;
50 protected HttpWebRequest _Request;
51  
52 /// <summary>Number of times we've received an unknown CAPS exception in series.</summary>
53 private int _errorCount;
54 /// <summary>For exponential backoff on error.</summary>
55 private static Random _random = new Random();
56  
57 public EventQueueClient(Uri eventQueueLocation)
58 {
59 _Address = eventQueueLocation;
60 }
61  
62 public void Start()
63 {
64 _Dead = false;
65  
66 // Create an EventQueueGet request
67 OSDMap request = new OSDMap();
68 request["ack"] = new OSD();
69 request["done"] = OSD.FromBoolean(false);
70  
71 byte[] postData = OSDParser.SerializeLLSDXmlBytes(request);
72  
73 _Request = CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT, OpenWriteHandler, null, RequestCompletedHandler);
74 }
75  
76 public void Stop(bool immediate)
77 {
78 _Dead = true;
79  
80 if (immediate)
81 _Running = false;
82  
83 if (_Request != null)
84 _Request.Abort();
85 }
86  
87 void OpenWriteHandler(HttpWebRequest request)
88 {
89 _Running = true;
90 _Request = request;
91  
92 Logger.DebugLog("Capabilities event queue connected");
93  
94 // The event queue is starting up for the first time
95 if (OnConnected != null)
96 {
97 try { OnConnected(); }
98 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
99 }
100 }
101  
102 void RequestCompletedHandler(HttpWebRequest request, HttpWebResponse response, byte[] responseData, Exception error)
103 {
104 // We don't care about this request now that it has completed
105 _Request = null;
106  
107 OSDArray events = null;
108 int ack = 0;
109  
110 if (responseData != null)
111 {
112 _errorCount = 0;
113 // Got a response
114 OSDMap result = OSDParser.DeserializeLLSDXml(responseData) as OSDMap;
115  
116 if (result != null)
117 {
118 events = result["events"] as OSDArray;
119 ack = result["id"].AsInteger();
120 }
121 else
122 {
123 Logger.Log("Got an unparseable response from the event queue: \"" +
124 System.Text.Encoding.UTF8.GetString(responseData) + "\"", Helpers.LogLevel.Warning);
125 }
126 }
127 else if (error != null)
128 {
129 #region Error handling
130  
131 HttpStatusCode code = HttpStatusCode.OK;
132  
133 if (error is WebException)
134 {
135 WebException webException = (WebException)error;
136  
137 if (webException.Response != null)
138 code = ((HttpWebResponse)webException.Response).StatusCode;
139 else if (webException.Status == WebExceptionStatus.RequestCanceled)
140 goto HandlingDone;
141 }
142  
143 if (error is WebException && ((WebException)error).Response != null)
144 code = ((HttpWebResponse)((WebException)error).Response).StatusCode;
145  
146 if (code == HttpStatusCode.NotFound || code == HttpStatusCode.Gone)
147 {
148 Logger.Log(String.Format("Closing event queue at {0} due to missing caps URI", _Address), Helpers.LogLevel.Info);
149  
150 _Running = false;
151 _Dead = true;
152 }
153 else if (code == HttpStatusCode.BadGateway)
154 {
155 // This is not good (server) protocol design, but it's normal.
156 // The EventQueue server is a proxy that connects to a Squid
157 // cache which will time out periodically. The EventQueue server
158 // interprets this as a generic error and returns a 502 to us
159 // that we ignore
160 }
161 else
162 {
163 ++_errorCount;
164  
165 // Try to log a meaningful error message
166 if (code != HttpStatusCode.OK)
167 {
168 Logger.Log(String.Format("Unrecognized caps connection problem from {0}: {1}",
169 _Address, code), Helpers.LogLevel.Warning);
170 }
171 else if (error.InnerException != null)
172 {
173 Logger.Log(String.Format("Unrecognized internal caps exception from {0}: {1}",
174 _Address, error.InnerException.Message), Helpers.LogLevel.Warning);
175 }
176 else
177 {
178 Logger.Log(String.Format("Unrecognized caps exception from {0}: {1}",
179 _Address, error.Message), Helpers.LogLevel.Warning);
180 }
181 }
182  
183 #endregion Error handling
184 }
185 else
186 {
187 ++_errorCount;
188  
189 Logger.Log("No response from the event queue but no reported error either", Helpers.LogLevel.Warning);
190 }
191  
192 HandlingDone:
193  
194 #region Resume the connection
195  
196 if (_Running)
197 {
198 OSDMap osdRequest = new OSDMap();
199 if (ack != 0) osdRequest["ack"] = OSD.FromInteger(ack);
200 else osdRequest["ack"] = new OSD();
201 osdRequest["done"] = OSD.FromBoolean(_Dead);
202  
203 byte[] postData = OSDParser.SerializeLLSDXmlBytes(osdRequest);
204  
205 if (_errorCount > 0) // Exponentially back off, so we don't hammer the CPU
206 Thread.Sleep(_random.Next(500 + (int)Math.Pow(2, _errorCount)));
207  
208 // Resume the connection. The event handler for the connection opening
209 // just sets class _Request variable to the current HttpWebRequest
210 CapsBase.UploadDataAsync(_Address, null, "application/xml", postData, REQUEST_TIMEOUT,
211 delegate(HttpWebRequest newRequest) { _Request = newRequest; }, null, RequestCompletedHandler);
212  
213 // If the event queue is dead at this point, turn it off since
214 // that was the last thing we want to do
215 if (_Dead)
216 {
217 _Running = false;
218 Logger.DebugLog("Sent event queue shutdown message");
219 }
220 }
221  
222 #endregion Resume the connection
223  
224 #region Handle incoming events
225  
226 if (OnEvent != null && events != null && events.Count > 0)
227 {
228 // Fire callbacks for each event received
229 foreach (OSDMap evt in events)
230 {
231 string msg = evt["message"].AsString();
232 OSDMap body = (OSDMap)evt["body"];
233  
234 try { OnEvent(msg, body); }
235 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); }
236 }
237 }
238  
239 #endregion Handle incoming events
240 }
241 }
242 }