aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs')
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs298
1 files changed, 298 insertions, 0 deletions
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
new file mode 100644
index 0000000..bdea278
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
@@ -0,0 +1,298 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
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 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28// #define DEBUGGING
29
30using System;
31using System.Collections.Generic;
32using System.Collections.Specialized;
33using System.Diagnostics;
34using System.IO;
35using System.Net;
36using System.Reflection;
37using System.Text.RegularExpressions;
38using System.Threading;
39using log4net;
40using HttpServer;
41
42namespace OpenSim.Framework.Servers.HttpServer
43{
44 /// <summary>
45 /// An OSHttpRequestPump fetches incoming OSHttpRequest objects
46 /// from the OSHttpRequestQueue and feeds them to all subscribed
47 /// parties. Each OSHttpRequestPump encapsulates one thread to do
48 /// the work and there is a fixed number of pumps for each
49 /// OSHttpServer object.
50 /// </summary>
51 public class OSHttpRequestPump
52 {
53 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
55 protected OSHttpServer _server;
56 protected OSHttpRequestQueue _queue;
57 protected Thread _engine;
58
59 private int _id;
60
61 public string EngineID
62 {
63 get { return String.Format("{0} pump {1}", _server.EngineID, _id); }
64 }
65
66 public OSHttpRequestPump(OSHttpServer server, OSHttpRequestQueue queue, int id)
67 {
68 _server = server;
69 _queue = queue;
70 _id = id;
71
72 _engine = new Thread(new ThreadStart(Engine));
73 _engine.IsBackground = true;
74 _engine.Start();
75 _engine.Name = string.Format ("Engine:{0}",EngineID);
76
77 ThreadTracker.Add(_engine);
78 }
79
80 public static OSHttpRequestPump[] Pumps(OSHttpServer server, OSHttpRequestQueue queue, int poolSize)
81 {
82 OSHttpRequestPump[] pumps = new OSHttpRequestPump[poolSize];
83 for (int i = 0; i < pumps.Length; i++)
84 {
85 pumps[i] = new OSHttpRequestPump(server, queue, i);
86 }
87
88 return pumps;
89 }
90
91 public void Start()
92 {
93 _engine = new Thread(new ThreadStart(Engine));
94 _engine.IsBackground = true;
95 _engine.Start();
96 _engine.Name = string.Format ("Engine:{0}",EngineID);
97
98 ThreadTracker.Add(_engine);
99 }
100
101 public void Engine()
102 {
103 OSHttpRequest req = null;
104
105 while (true)
106 {
107 try
108 {
109 // dequeue an OSHttpRequest from OSHttpServer's
110 // request queue
111 req = _queue.Dequeue();
112
113 // get a copy of the list of registered handlers
114 List<OSHttpHandler> handlers = _server.OSHttpHandlers;
115
116 // prune list and have it sorted from most
117 // specific to least specific
118 handlers = MatchHandlers(req, handlers);
119
120 // process req: we try each handler in turn until
121 // we are either out of handlers or get back a
122 // Pass or Done
123 OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed;
124 foreach (OSHttpHandler h in handlers)
125 {
126 rc = h.Process(req);
127
128 // Pass: handler did not process the request,
129 // try next handler
130 if (OSHttpHandlerResult.Pass == rc) continue;
131
132 // Handled: handler has processed the request
133 if (OSHttpHandlerResult.Done == rc) break;
134
135 // hmm, something went wrong
136 throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc));
137 }
138
139 if (OSHttpHandlerResult.Unprocessed == rc)
140 {
141 _log.InfoFormat("[{0}] OSHttpHandler: no handler registered for {1}", EngineID, req);
142
143 // set up response header
144 OSHttpResponse resp = new OSHttpResponse(req);
145 resp.StatusCode = (int)OSHttpStatusCode.ClientErrorNotFound;
146 resp.StatusDescription = String.Format("no handler on call for {0}", req);
147 resp.ContentType = "text/html";
148
149 // add explanatory message
150 StreamWriter body = new StreamWriter(resp.Body);
151 body.WriteLine("<html>");
152 body.WriteLine("<header><title>Ooops...</title><header>");
153 body.WriteLine(String.Format("<body><p>{0}</p></body>", resp.StatusDescription));
154 body.WriteLine("</html>");
155 body.Flush();
156
157 // and ship it back
158 resp.Send();
159 }
160 }
161 catch (Exception e)
162 {
163 _log.DebugFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.ToString());
164 _log.ErrorFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.Message);
165 }
166 }
167 }
168
169 protected List<OSHttpHandler> MatchHandlers(OSHttpRequest req, List<OSHttpHandler> handlers)
170 {
171 Dictionary<OSHttpHandler, int> scoredHandlers = new Dictionary<OSHttpHandler, int>();
172
173 _log.DebugFormat("[{0}] MatchHandlers for {1}", EngineID, req);
174 foreach (OSHttpHandler h in handlers)
175 {
176 // initial anchor
177 scoredHandlers[h] = 0;
178
179 // first, check whether IPEndPointWhitelist applies
180 // and, if it does, whether client is on that white
181 // list.
182 if (null != h.IPEndPointWhitelist)
183 {
184 // TODO: following code requires code changes to
185 // HttpServer.HttpRequest to become functional
186
187 IPEndPoint remote = req.RemoteIPEndPoint;
188 if (null != remote)
189 {
190 Match epm = h.IPEndPointWhitelist.Match(remote.ToString());
191 if (!epm.Success)
192 {
193 scoredHandlers.Remove(h);
194 continue;
195 }
196 }
197 }
198
199 if (null != h.Method)
200 {
201 Match m = h.Method.Match(req.HttpMethod);
202 if (!m.Success)
203 {
204 scoredHandlers.Remove(h);
205 continue;
206 }
207 scoredHandlers[h]++;
208 }
209
210 // whitelist ok, now check path
211 if (null != h.Path)
212 {
213 Match m = h.Path.Match(req.RawUrl);
214 if (!m.Success)
215 {
216 scoredHandlers.Remove(h);
217 continue;
218 }
219 scoredHandlers[h] += m.ToString().Length;
220 }
221
222 // whitelist & path ok, now check query string
223 if (null != h.Query)
224 {
225 int queriesMatch = MatchOnNameValueCollection(req.QueryString, h.Query);
226 if (0 == queriesMatch)
227 {
228 _log.DebugFormat("[{0}] request {1}", EngineID, req);
229 _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
230
231 scoredHandlers.Remove(h);
232 continue;
233 }
234 scoredHandlers[h] += queriesMatch;
235 }
236
237 // whitelist, path, query string ok, now check headers
238 if (null != h.Headers)
239 {
240 int headersMatch = MatchOnNameValueCollection(req.Headers, h.Headers);
241 if (0 == headersMatch)
242 {
243 _log.DebugFormat("[{0}] request {1}", EngineID, req);
244 _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
245
246 scoredHandlers.Remove(h);
247 continue;
248 }
249 scoredHandlers[h] += headersMatch;
250 }
251 }
252
253 List<OSHttpHandler> matchingHandlers = new List<OSHttpHandler>(scoredHandlers.Keys);
254 matchingHandlers.Sort(delegate(OSHttpHandler x, OSHttpHandler y)
255 {
256 return scoredHandlers[x] - scoredHandlers[y];
257 });
258 LogDumpHandlerList(matchingHandlers);
259 return matchingHandlers;
260 }
261
262 protected int MatchOnNameValueCollection(NameValueCollection collection, Dictionary<string, Regex> regexs)
263 {
264 int matched = 0;
265
266 foreach (string tag in regexs.Keys)
267 {
268 // do we have a header "tag"?
269 if (null == collection[tag])
270 {
271 return 0;
272 }
273
274 // does the content of collection[tag] match
275 // the supplied regex?
276 Match cm = regexs[tag].Match(collection[tag]);
277 if (!cm.Success)
278 {
279 return 0;
280 }
281
282 // ok: matches
283 matched++;
284 continue;
285 }
286
287 return matched;
288 }
289
290 [ConditionalAttribute("DEBUGGING")]
291 private void LogDumpHandlerList(List<OSHttpHandler> l)
292 {
293 _log.DebugFormat("[{0}] OSHttpHandlerList dump:", EngineID);
294 foreach (OSHttpHandler h in l)
295 _log.DebugFormat(" ", h.ToString());
296 }
297 }
298}