aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Framework/Servers/BaseHttpServer.cs2
-rw-r--r--OpenSim/Framework/Servers/OSHttpHandler.cs25
-rw-r--r--OpenSim/Framework/Servers/OSHttpRequest.cs39
-rw-r--r--OpenSim/Framework/Servers/OSHttpRequestPump.cs80
-rw-r--r--OpenSim/Framework/Servers/OSHttpResponse.cs305
-rw-r--r--OpenSim/Framework/Servers/OSHttpXmlRpcHandler.cs222
6 files changed, 594 insertions, 79 deletions
diff --git a/OpenSim/Framework/Servers/BaseHttpServer.cs b/OpenSim/Framework/Servers/BaseHttpServer.cs
index 22698d0..9d2a804 100644
--- a/OpenSim/Framework/Servers/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/BaseHttpServer.cs
@@ -416,7 +416,7 @@ namespace OpenSim.Framework.Servers
416 { 416 {
417 try 417 try
418 { 418 {
419 response.OutputStream.Close(); 419 response.Send();
420 } 420 }
421 catch (SocketException e) 421 catch (SocketException e)
422 { 422 {
diff --git a/OpenSim/Framework/Servers/OSHttpHandler.cs b/OpenSim/Framework/Servers/OSHttpHandler.cs
index da96cca..a9f42f3 100644
--- a/OpenSim/Framework/Servers/OSHttpHandler.cs
+++ b/OpenSim/Framework/Servers/OSHttpHandler.cs
@@ -59,10 +59,18 @@ namespace OpenSim.Framework.Servers
59 { 59 {
60 Unprocessed, 60 Unprocessed,
61 Pass, 61 Pass,
62 Handled, 62 Done,
63 Detached,
64 } 63 }
65 64
65 /// <summary>
66 /// An OSHttpHandler that matches on the "content-type" header can
67 /// supply an OSHttpContentTypeChecker delegate which will be
68 /// invoked by the request matcher in OSHttpRequestPump.
69 /// </summary>
70 /// <returns>true if the handler is interested in the content;
71 /// false otherwise</returns>
72 public delegate bool OSHttpContentTypeChecker(OSHttpRequest req);
73
66 public interface OSHttpHandler 74 public interface OSHttpHandler
67 { 75 {
68 /// <summary> 76 /// <summary>
@@ -98,6 +106,19 @@ namespace OpenSim.Framework.Servers
98 get; 106 get;
99 } 107 }
100 108
109
110 /// <summary>
111 /// An OSHttpHandler that matches on the "content-type" header can
112 /// supply an OSHttpContentTypeChecker delegate which will be
113 /// invoked by the request matcher in OSHttpRequestPump.
114 /// </summary>
115 /// <returns>true if the handler is interested in the content;
116 /// false otherwise</returns>
117 OSHttpContentTypeChecker ContentTypeChecker
118 {
119 get;
120 }
121
101 OSHttpHandlerResult Process(OSHttpRequest request); 122 OSHttpHandlerResult Process(OSHttpRequest request);
102 } 123 }
103} \ No newline at end of file 124} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/OSHttpRequest.cs b/OpenSim/Framework/Servers/OSHttpRequest.cs
index c523143..7549f08 100644
--- a/OpenSim/Framework/Servers/OSHttpRequest.cs
+++ b/OpenSim/Framework/Servers/OSHttpRequest.cs
@@ -59,7 +59,7 @@ namespace OpenSim.Framework.Servers
59 private IPEndPoint _ipEndPoint; 59 private IPEndPoint _ipEndPoint;
60 60
61 private HttpRequest _request; 61 private HttpRequest _request;
62 // private HttpClientContext _context; 62 private HttpClientContext _context;
63 63
64 public string[] AcceptTypes 64 public string[] AcceptTypes
65 { 65 {
@@ -152,11 +152,28 @@ namespace OpenSim.Framework.Servers
152 get { return _ipEndPoint; } 152 get { return _ipEndPoint; }
153 } 153 }
154 154
155 public HttpRequest HttpRequest 155
156 internal HttpRequest HttpRequest
156 { 157 {
157 get { return _request; } 158 get { return _request; }
158 } 159 }
159 160
161 internal HttpClientContext HttpClientContext
162 {
163 get { return _context; }
164 }
165
166 /// <summary>
167 /// Internal whiteboard for handlers to store temporary stuff
168 /// into.
169 /// </summary>
170 internal Dictionary<string, object> Whiteboard
171 {
172 get { return _whiteboard; }
173 }
174 private Dictionary<string, object> _whiteboard = new Dictionary<string, object>();
175
176
160 public OSHttpRequest() 177 public OSHttpRequest()
161 { 178 {
162 } 179 }
@@ -185,7 +202,7 @@ namespace OpenSim.Framework.Servers
185 202
186 public OSHttpRequest(HttpClientContext context, HttpRequest req) 203 public OSHttpRequest(HttpClientContext context, HttpRequest req)
187 { 204 {
188 // _context = context; 205 _context = context;
189 _request = req; 206 _request = req;
190 207
191 _acceptTypes = req.AcceptTypes; 208 _acceptTypes = req.AcceptTypes;
@@ -215,5 +232,21 @@ namespace OpenSim.Framework.Servers
215 // _isSecureConnection = req.IsSecureConnection; 232 // _isSecureConnection = req.IsSecureConnection;
216 // _isAuthenticated = req.IsAuthenticated; 233 // _isAuthenticated = req.IsAuthenticated;
217 } 234 }
235
236 public override string ToString()
237 {
238 StringBuilder me = new StringBuilder();
239 me.Append(String.Format("OSHttpRequest: {0} {1}\n", HttpMethod, RawUrl));
240 foreach (string k in Headers.AllKeys)
241 {
242 me.Append(String.Format(" {0}: {1}\n", k, Headers[k]));
243 }
244 if (null != RemoteIPEndPoint)
245 {
246 me.Append(String.Format(" IP: {0}\n", RemoteIPEndPoint.ToString()));
247 }
248
249 return me.ToString();
250 }
218 } 251 }
219} 252}
diff --git a/OpenSim/Framework/Servers/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/OSHttpRequestPump.cs
index 4218be5..56714fa 100644
--- a/OpenSim/Framework/Servers/OSHttpRequestPump.cs
+++ b/OpenSim/Framework/Servers/OSHttpRequestPump.cs
@@ -28,6 +28,7 @@
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Collections.Specialized; 30using System.Collections.Specialized;
31using System.IO;
31using System.Net; 32using System.Net;
32using System.Reflection; 33using System.Reflection;
33using System.Text.RegularExpressions; 34using System.Text.RegularExpressions;
@@ -114,7 +115,7 @@ namespace OpenSim.Framework.Servers
114 115
115 // process req: we try each handler in turn until 116 // process req: we try each handler in turn until
116 // we are either out of handlers or get back a 117 // we are either out of handlers or get back a
117 // Handled or Detached 118 // Pass or Done
118 OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed; 119 OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed;
119 foreach (OSHttpHandler h in handlers) 120 foreach (OSHttpHandler h in handlers)
120 { 121 {
@@ -123,22 +124,35 @@ namespace OpenSim.Framework.Servers
123 // Pass: handler did not process the request, 124 // Pass: handler did not process the request,
124 // try next handler 125 // try next handler
125 if (OSHttpHandlerResult.Pass == rc) continue; 126 if (OSHttpHandlerResult.Pass == rc) continue;
126 // Detached: handler is taking over processing 127
127 // of request, we are done 128 // Handled: handler has processed the request
128 if (OSHttpHandlerResult.Detached == rc) break; 129 if (OSHttpHandlerResult.Done == rc) break;
129
130 if (OSHttpHandlerResult.Handled != rc)
131 {
132 // something went wrong
133 throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc));
134 }
135 130
136 // Handled: clean up now 131 // hmm, something went wrong
137 req.HttpRequest.AddHeader("keep-alive", "false"); 132 throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc));
133 }
134
135 if (OSHttpHandlerResult.Unprocessed == rc)
136 {
137 _log.InfoFormat("[{0}] OSHttpHandler: no handler registered for {1}", EngineID, req);
138
139 // set up response header
140 OSHttpResponse resp = new OSHttpResponse(req);
141 resp.StatusCode = (int)OSHttpStatusCode.ClientErrorNotFound;
142 resp.StatusDescription = String.Format("no handler on call for {0}", req);
143 resp.ContentType = "text/html";
144
145 // add explanatory message
146 StreamWriter body = new StreamWriter(resp.Body);
147 body.WriteLine("<html>");
148 body.WriteLine("<header><title>Ooops...</title><header>");
149 body.WriteLine(String.Format("<body><p>{0}</p></body>", resp.StatusDescription));
150 body.WriteLine("</html>");
151 body.Flush();
138 152
139 break; 153 // and ship it back
154 resp.HttpResponse.Send();
140 } 155 }
141
142 } 156 }
143 catch (Exception e) 157 catch (Exception e)
144 { 158 {
@@ -199,17 +213,37 @@ namespace OpenSim.Framework.Servers
199 NameValueCollection headers = req.HttpRequest.Headers; 213 NameValueCollection headers = req.HttpRequest.Headers;
200 foreach (string tag in headerRegexs.Keys) 214 foreach (string tag in headerRegexs.Keys)
201 { 215 {
202 if (null != headers[tag]) 216 // do we have a header "tag"?
217 if (null == headers[tag])
203 { 218 {
204 Match hm = headerRegexs[tag].Match(headers[tag]); 219 // no: remove the handler if it was added
205 if (hm.Success) { 220 // earlier and on to the next one
206 headersMatch++; 221 scoredHandlers.Remove(h);
207 continue; 222 break;
208 }
209 } 223 }
210 224
211 scoredHandlers.Remove(h); 225 // does the content of header "tag" match
212 break; 226 // the supplied regex?
227 Match hm = headerRegexs[tag].Match(headers[tag]);
228 if (!hm.Success) {
229 // no: remove the handler if it was added
230 // earlier and on to the next one
231 scoredHandlers.Remove(h);
232 break;
233 }
234
235 // if we are looking at the "content-type" tag,
236 // check wether h has a ContentTypeChecker and
237 // invoke it if it has
238 if ((null != h.ContentTypeChecker) && !h.ContentTypeChecker(req))
239 {
240 scoredHandlers.Remove(h);
241 break;
242 }
243
244 // ok: header matches
245 headersMatch++;
246 continue;
213 } 247 }
214 // check whether h got kicked out 248 // check whether h got kicked out
215 if (!scoredHandlers.ContainsKey(h)) continue; 249 if (!scoredHandlers.ContainsKey(h)) continue;
diff --git a/OpenSim/Framework/Servers/OSHttpResponse.cs b/OpenSim/Framework/Servers/OSHttpResponse.cs
index e1ab005..352c6f6 100644
--- a/OpenSim/Framework/Servers/OSHttpResponse.cs
+++ b/OpenSim/Framework/Servers/OSHttpResponse.cs
@@ -25,141 +25,346 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
28using System.Collections; 29using System.Collections;
29using System.IO; 30using System.IO;
30using System.Net; 31using System.Net;
31using System.Text; 32using System.Text;
33using HttpServer;
32 34
33namespace OpenSim.Framework.Servers 35namespace OpenSim.Framework.Servers
34{ 36{
37 /// <summary>
38 /// OSHttpResponse is the OpenSim representation of an HTTP
39 /// response.
40 /// </summary>
41 /// <remarks>
42 /// OSHttpResponse is currently dual "homed" in that it support
43 /// both the .NET HttpListenerResponse and the HttpServer
44 /// HttpResponse (similar to OSHttpRequest); this duality is only
45 /// temporary and the .NET usage will disappear once the switch to
46 /// HttpServer is completed.
47 /// </remarks>
35 public class OSHttpResponse 48 public class OSHttpResponse
36 { 49 {
37 private string _contentType; 50
38 private bool _contentTypeSet; 51 // property code below is a bit messy, will all resolve to
52 // harmony once we've completed the switch
53
54 /// <summary>
55 /// Content type property.
56 /// </summary>
57 /// <remarks>
58 /// Setting this property will also set IsContentTypeSet to
59 /// true.
60 /// </remarks>
39 public string ContentType 61 public string ContentType
40 { 62 {
41 get { return _contentType; } 63 get
64 {
65 return HttpServer ? _httpResponse.ContentType : _contentType;
66 }
42 set 67 set
43 { 68 {
44 _contentType = value; 69 if (HttpServer)
45 _contentTypeSet = true; 70 {
71 _httpResponse.ContentType = value;
72 }
73 else
74 {
75 _contentType = value;
76 _contentTypeSet = true;
77 }
46 } 78 }
47 } 79 }
80 private string _contentType;
81
82 /// <summary>
83 /// Boolean property indicating whether the content type
84 /// property actively has been set.
85 /// </summary>
86 /// <remarks>
87 /// IsContentTypeSet will go away together with .NET base.
88 /// </remarks>
48 public bool IsContentTypeSet 89 public bool IsContentTypeSet
49 { 90 {
50 get { return _contentTypeSet; } 91 get { return _contentTypeSet; }
51 } 92 }
93 private bool _contentTypeSet;
52 94
53 private long _contentLength64; 95
54 public long ContentLength64 96 /// <summary>
97 /// Length of the body content; 0 if there is no body.
98 /// </summary>
99 public long ContentLength
55 { 100 {
56 get { return _contentLength64; } 101 get
102 {
103 return HttpServer ? _httpResponse.ContentLength : _contentLength;
104 }
57 set 105 set
58 { 106 {
59 _contentLength64 = value; 107 if (HttpServer)
60 if (null != _resp) _resp.ContentLength64 = value; 108 _httpResponse.ContentLength = value;
109 else
110 _contentLength = value;
61 } 111 }
62 } 112 }
113 private long _contentLength;
63 114
64 private Encoding _contentEncoding; 115 /// <summary>
116 /// Aliases for ContentLength.
117 /// </summary>
118 public long ContentLength64
119 {
120 get { return ContentLength; }
121 set { ContentLength = value; }
122 }
123
124 /// <summary>
125 /// Encoding of the body content.
126 /// </summary>
65 public Encoding ContentEncoding 127 public Encoding ContentEncoding
66 { 128 {
67 get { return _contentEncoding; } 129 get {
130 return HttpServer ? _httpResponse.Encoding : _contentEncoding;
131 }
68 set 132 set
69 { 133 {
70 _contentEncoding = value; 134 if (HttpServer)
71 if (null != _resp) _resp.ContentEncoding = value; 135 _httpResponse.Encoding = value;
136 else
137 _contentEncoding = value;
72 } 138 }
73 } 139 }
140 private Encoding _contentEncoding;
74 141
75 public WebHeaderCollection Headers; 142 /// <summary>
76 // public CookieCollection Cookies; 143 /// Headers of the response.
144 /// </summary>
145 public WebHeaderCollection Headers
146 {
147 get
148 {
149 return HttpServer ? null : _headers;
150 }
151 }
152 private WebHeaderCollection _headers;
77 153
78 private bool _keepAlive; 154 /// <summary>
155 /// Get or set the keep alive property.
156 /// </summary>
79 public bool KeepAlive 157 public bool KeepAlive
80 { 158 {
81 get { return _keepAlive; } 159 get
160 {
161 if (HttpServer)
162 return _httpResponse.Connection == ConnectionType.KeepAlive;
163 else
164 return _keepAlive;
165 }
82 set 166 set
83 { 167 {
84 _keepAlive = value; 168 if (HttpServer)
85 if (null != _resp) _resp.KeepAlive = value; 169 _httpResponse.Connection = ConnectionType.KeepAlive;
170 else
171 _keepAlive = value;
86 } 172 }
87 } 173 }
174 private bool _keepAlive;
88 175
89 public Stream OutputStream; 176 /// <summary>
177 /// Return the output stream feeding the body.
178 /// </summary>
179 /// <remarks>
180 /// On its way out...
181 /// </remarks>
182 public Stream OutputStream
183 {
184 get
185 {
186 return HttpServer ? _httpResponse.Body : _outputStream;
187 }
188 }
189 private Stream _outputStream;
190
191
192 /// <summary>
193 /// Return the output stream feeding the body.
194 /// </summary>
195 public Stream Body
196 {
197 get
198 {
199 if (HttpServer)
200 return _httpResponse.Body;
201 throw new Exception("[OSHttpResponse] mixed .NET and HttpServer access");
202 }
203 }
90 204
91 private string _redirectLocation; 205 /// <summary>
206 /// Set a redirct location.
207 /// </summary>
92 public string RedirectLocation 208 public string RedirectLocation
93 { 209 {
94 get { return _redirectLocation; } 210 // get { return _redirectLocation; }
95 set 211 set
96 { 212 {
97 _redirectLocation = value; 213 if (HttpServer)
98 if (null != _resp) _resp.RedirectLocation = value; 214 _httpResponse.Redirect(value);
215 // else
216 // _redirectLocation = value;
99 } 217 }
100 } 218 }
219 // private string _redirectLocation;
101 220
102 private bool _sendChunked; 221
222
223 /// <summary>
224 /// Chunk transfers.
225 /// </summary>
103 public bool SendChunked 226 public bool SendChunked
104 { 227 {
105 get { return _sendChunked; } 228 get
229 {
230 return HttpServer ? _httpResponse.Chunked :_sendChunked;
231 }
232
106 set 233 set
107 { 234 {
108 _sendChunked = value; 235 if (HttpServer)
109 if (null != _resp) _resp.SendChunked = value; 236 _httpResponse.Chunked = value;
237 else
238 _sendChunked = value;
110 } 239 }
111 } 240 }
241 private bool _sendChunked;
112 242
113 private int _statusCode; 243
244 /// <summary>
245 /// HTTP status code.
246 /// </summary>
114 public int StatusCode 247 public int StatusCode
115 { 248 {
116 get { return _statusCode; } 249 get
250 {
251 return HttpServer ? (int)_httpResponse.Status : _statusCode;
252 }
253
117 set 254 set
118 { 255 {
119 _statusCode = value; 256 if (HttpServer)
120 if (null != _resp) _resp.StatusCode = value; 257 _httpResponse.Status = (HttpStatusCode)value;
258 else
259 _statusCode = value;
121 } 260 }
122 } 261 }
262 private int _statusCode;
123 263
124 private string _statusDescription; 264
265 /// <summary>
266 /// HTTP status description.
267 /// </summary>
125 public string StatusDescription 268 public string StatusDescription
126 { 269 {
127 get { return _statusDescription; } 270 get
271 {
272 return HttpServer ? _httpResponse.Reason : _statusDescription;
273 }
274
128 set 275 set
129 { 276 {
130 _statusDescription = value; 277 if (HttpServer)
131 if (null != _resp) _resp.StatusDescription = value; 278 _httpResponse.Reason = value;
279 else
280 _statusDescription = value;
132 } 281 }
133 } 282 }
283 private string _statusDescription;
134 284
135 private HttpListenerResponse _resp; 285 private HttpResponse _httpResponse;
286
287 internal bool HttpServer
288 {
289 get { return null != _httpResponse; }
290 }
291
292 internal HttpResponse HttpResponse
293 {
294 get { return _httpResponse; }
295 }
136 296
137 public OSHttpResponse() 297 public OSHttpResponse()
138 { 298 {
139 } 299 }
140 300
301 /// <summary>
302 /// Instantiate an OSHttpResponse object based on an
303 /// underlying .NET HttpListenerResponse.
304 /// </summary>
305 /// <remarks>
306 /// Almost deprecated; will go west to make once HttpServer
307 /// base takes over.
308 /// </remarks>
141 public OSHttpResponse(HttpListenerResponse resp) 309 public OSHttpResponse(HttpListenerResponse resp)
142 { 310 {
143 ContentEncoding = resp.ContentEncoding; 311 _contentEncoding = resp.ContentEncoding;
144 ContentLength64 = resp.ContentLength64; 312 _contentLength = resp.ContentLength64;
145 _contentType = resp.ContentType; 313 _contentType = resp.ContentType;
146 Headers = resp.Headers; 314 _headers = resp.Headers;
147 // Cookies = resp.Cookies; 315 // _cookies = resp.Cookies;
148 KeepAlive = resp.KeepAlive; 316 _keepAlive = resp.KeepAlive;
149 OutputStream = resp.OutputStream; 317 _outputStream = resp.OutputStream;
150 RedirectLocation = resp.RedirectLocation; 318 // _redirectLocation = resp.RedirectLocation;
151 SendChunked = resp.SendChunked; 319 _sendChunked = resp.SendChunked;
152 StatusCode = resp.StatusCode; 320 _statusCode = resp.StatusCode;
153 StatusDescription = resp.StatusDescription; 321 _statusDescription = resp.StatusDescription;
154 322
155 _contentTypeSet = false; 323 _contentTypeSet = false;
156 324
157 _resp = resp; 325 // _resp = resp;
158 } 326 }
159 327
328 /// <summary>
329 /// Instantiate an OSHttpResponse object from an OSHttpRequest
330 /// object.
331 /// </summary
332 /// <param name="req">Incoming OSHttpRequest to which we are
333 /// replying</param>
334 public OSHttpResponse(OSHttpRequest req)
335 {
336 _httpResponse = new HttpResponse(req.HttpClientContext, req.HttpRequest);
337 }
338
339 /// <summary>
340 /// Add a header field and content to the response.
341 /// </summary>
342 /// <param name="key">string containing the header field
343 /// name</param>
344 /// <param name="value">string containing the header field
345 /// value</param>
160 public void AddHeader(string key, string value) 346 public void AddHeader(string key, string value)
161 { 347 {
162 Headers.Add(key, value); 348 if (HttpServer)
349 _headers.Add(key, value);
350 else
351 _httpResponse.AddHeader(key, value);
352 }
353
354 /// <summary>
355 /// Send the response back to the remote client
356 /// </summary>
357 public void Send()
358 {
359 if (HttpServer)
360 {
361 _httpResponse.Body.Flush();
362 _httpResponse.Send();
363 }
364 else
365 {
366 OutputStream.Close();
367 }
163 } 368 }
164 } 369 }
165} 370}
diff --git a/OpenSim/Framework/Servers/OSHttpXmlRpcHandler.cs b/OpenSim/Framework/Servers/OSHttpXmlRpcHandler.cs
new file mode 100644
index 0000000..4205547
--- /dev/null
+++ b/OpenSim/Framework/Servers/OSHttpXmlRpcHandler.cs
@@ -0,0 +1,222 @@
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 OpenSim 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Net;
32using System.Reflection;
33using System.Text;
34using System.Text.RegularExpressions;
35using System.Xml;
36using log4net;
37using Nwc.XmlRpc;
38
39namespace OpenSim.Framework.Servers
40{
41 public delegate XmlRpcResponse OSHttpXmlRpcProcessor(XmlRpcRequest request);
42
43 public class OSHttpXmlRpcHandler: OSHttpHandler
44 {
45 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Regular expression used to match against path of incoming
49 /// HTTP request. If you want to match any string either use
50 /// '.*' or null. To match for the emtpy string use '^$'
51 /// </summary>
52 public Regex Path
53 {
54 get { return _pathsRegex; }
55 }
56 private Regex _pathsRegex;
57
58 /// <summary>
59 /// Dictionary of (header name, regular expression) tuples,
60 /// allowing us to match on HTTP header fields.
61 /// </summary>
62 public Dictionary<string, Regex> Headers
63 {
64 get { return _headers; }
65 }
66 private Dictionary<string, Regex> _headers;
67
68 /// <summary>
69 /// Regex to whitelist IP end points. A null value disables
70 /// checking of IP end points.
71 /// </summary>
72 /// <remarks>
73 /// This feature is currently not implemented as it requires
74 /// (trivial) changes to HttpServer.HttpListener that have not
75 /// been implemented.
76 /// </remarks>
77 public Regex IPEndPointWhitelist
78 {
79 get { return _ipEndPointRegex; }
80 }
81 private Regex _ipEndPointRegex;
82
83 /// <summary>
84 /// An OSHttpHandler that matches on the "content-type" header can
85 /// supply an OSHttpContentTypeChecker delegate which will be
86 /// invoked by the request matcher in OSHttpRequestPump.
87 /// </summary>
88 /// <returns>true if the handler is interested in the content;
89 /// false otherwise</returns>
90 public OSHttpContentTypeChecker ContentTypeChecker
91 {
92 get
93 {
94 return delegate(OSHttpRequest req)
95 {
96 XmlRpcRequest xmlRpcRequest = null;
97
98 // check whether req is already reified
99 // if not: reify (and post to whiteboard)
100 try
101 {
102 if (req.Whiteboard.ContainsKey("xmlrequest"))
103 {
104 xmlRpcRequest = req.Whiteboard["xmlrequest"] as XmlRpcRequest;
105 }
106 else
107 {
108 StreamReader body = new StreamReader(req.InputStream);
109 string requestBody = body.ReadToEnd();
110 xmlRpcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody);
111 req.Whiteboard["xmlrequest"] = xmlRpcRequest;
112 }
113 }
114 catch (XmlException)
115 {
116 _log.ErrorFormat("[OSHttpXmlRpcHandler] failed to deserialize XmlRpcRequest from {0}", req.ToString());
117 return false;
118 }
119
120 // check against methodName
121 if ((null != xmlRpcRequest)
122 && !String.IsNullOrEmpty(xmlRpcRequest.MethodName)
123 && xmlRpcRequest.MethodName == _methodName)
124 {
125 _log.DebugFormat("[OSHttpXmlRpcHandler] located handler {0} for {1}", _methodName, req.ToString());
126 return true;
127 }
128
129 return false;
130 };
131 }
132 }
133
134 // contains handler for processing XmlRpc Request
135 private OSHttpXmlRpcProcessor _handler;
136
137 // contains XmlRpc method name
138 private string _methodName;
139
140
141 /// <summary>
142 /// Instantiate an XmlRpc handler.
143 /// </summary>
144 /// <param name="handler">OSHttpXmlRpcProcessor
145 /// delegate</param>
146 /// <param name="methodName">XmlRpc method name</param>
147 /// <param name="path">XmlRpc path prefix (regular expression)</param>
148 /// <param name="headers">Dictionary with header names and
149 /// regular expressions to match content of headers</param>
150 /// <param name="whitelist">IP whitelist of remote end points
151 /// to accept (regular expression)</param>
152 /// <remarks>
153 /// Except for handler and methodName, all other parameters
154 /// can be null, in which case they are not taken into account
155 /// when the handler is being looked up.
156 /// </remarks>
157 public OSHttpXmlRpcHandler(OSHttpXmlRpcProcessor handler, string methodName, Regex path,
158 Dictionary<string, Regex> headers, Regex whitelist)
159 {
160 _handler = handler;
161 _pathsRegex = path;
162 _methodName = methodName;
163
164 if (null == _headers) _headers = new Dictionary<string, Regex>();
165 _headers.Add("content-type", new Regex(@"^(text|application)/xml", RegexOptions.IgnoreCase |
166 RegexOptions.Compiled));
167
168 _ipEndPointRegex = whitelist;
169 }
170
171
172 /// <summary>
173 /// Instantiate an XmlRpc handler.
174 /// </summary>
175 /// <param name="handler">OSHttpXmlRpcProcessor
176 /// delegate</param>
177 /// <param name="methodName">XmlRpc method name</param>
178 public OSHttpXmlRpcHandler(OSHttpXmlRpcProcessor handler, string methodName)
179 : this(handler, methodName, null, null, null)
180 {
181 }
182
183
184 /// <summary>
185 /// Invoked by OSHttpRequestPump.
186 /// </summary>
187 public OSHttpHandlerResult Process(OSHttpRequest request)
188 {
189 XmlRpcResponse xmlRpcResponse;
190 string responseString;
191
192 OSHttpResponse resp = new OSHttpResponse(request);
193
194 try
195 {
196 // reified XmlRpcRequest must still be on the whiteboard
197 XmlRpcRequest xmlRpcRequest = request.Whiteboard["xmlrequest"] as XmlRpcRequest;
198 xmlRpcResponse = _handler(xmlRpcRequest);
199 responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse);
200
201 resp.ContentType = "text/xml";
202 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
203
204 resp.SendChunked = false;
205 resp.ContentLength = buffer.Length;
206 resp.ContentEncoding = Encoding.UTF8;
207
208 resp.Body.Write(buffer, 0, buffer.Length);
209 resp.Body.Flush();
210
211 resp.Send();
212
213 }
214 catch (Exception ex)
215 {
216 _log.WarnFormat("[OSHttpXmlRpcHandler]: Error: {0}", ex.Message);
217 return OSHttpHandlerResult.Pass;
218 }
219 return OSHttpHandlerResult.Done;
220 }
221 }
222} \ No newline at end of file