OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
OSHttpRequestPump.cs
Go to the documentation of this file.
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 
30 using System;
31 using System.Collections.Generic;
32 using System.Collections.Specialized;
33 using System.Diagnostics;
34 using System.IO;
35 using System.Net;
36 using System.Reflection;
37 using System.Text.RegularExpressions;
38 using System.Threading;
39 using log4net;
40 using HttpServer;
41 
42 namespace OpenSim.Framework.Servers.HttpServer
43 {
51  public class OSHttpRequestPump
52  {
53  private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 
55  protected OSHttpServer _server;
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 }
OSHttpRequestPump(OSHttpServer server, OSHttpRequestQueue queue, int id)
OSHttpResponse is the OpenSim representation of an HTTP response.
int MatchOnNameValueCollection(NameValueCollection collection, Dictionary< string, Regex > regexs)
virtual Dictionary< string, Regex > Query
Dictionary of (query name, regular expression) tuples, allowing us to match on URI query fields...
An OSHttpRequestPump fetches incoming OSHttpRequest objects from the OSHttpRequestQueue and feeds the...
virtual Regex Method
Regular expression used to match against method of the incoming HTTP request. If you want to match an...
virtual Regex Path
Regular expression used to match against path of the incoming HTTP request. If you want to match any ...
OSHttpServer provides an HTTP server bound to a specific port. When instantiated with just address an...
Definition: OSHttpServer.cs:49
virtual Regex IPEndPointWhitelist
Dictionary of (header name, regular expression) tuples, allowing us to match on HTTP header fields...
OSHttpRequestQueues are used to hand over incoming HTTP requests to OSHttpRequestPump objects...
static OSHttpRequestPump[] Pumps(OSHttpServer server, OSHttpRequestQueue queue, int poolSize)
List< OSHttpHandler > MatchHandlers(OSHttpRequest req, List< OSHttpHandler > handlers)
virtual Dictionary< string, Regex > Headers
Dictionary of (header name, regular expression) tuples, allowing us to match on HTTP header fields...
OSHttpStatusCode
HTTP status codes (almost) as defined by W3C in http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html and IETF in http://tools.ietf.org/html/rfc6585