diff options
author | Dr Scofield | 2008-07-14 12:18:32 +0000 |
---|---|---|
committer | Dr Scofield | 2008-07-14 12:18:32 +0000 |
commit | 7692f3e18f49fc57b63efbe54b7ac71de2fb7e16 (patch) | |
tree | 4ee9a4279ff904a8c5fc7e2bd9f85f55bf8b52ba /OpenSim/Framework/Servers | |
parent | adds a default value of true to the new IRC bridge configuration option "nick... (diff) | |
download | opensim-SC_OLD-7692f3e18f49fc57b63efbe54b7ac71de2fb7e16.zip opensim-SC_OLD-7692f3e18f49fc57b63efbe54b7ac71de2fb7e16.tar.gz opensim-SC_OLD-7692f3e18f49fc57b63efbe54b7ac71de2fb7e16.tar.bz2 opensim-SC_OLD-7692f3e18f49fc57b63efbe54b7ac71de2fb7e16.tar.xz |
further work in progress on the HttpServer side: XmlRpc handler path
almost complete and soon to be ready for testing; OSHttpResponse code
out.
Diffstat (limited to 'OpenSim/Framework/Servers')
-rw-r--r-- | OpenSim/Framework/Servers/BaseHttpServer.cs | 2 | ||||
-rw-r--r-- | OpenSim/Framework/Servers/OSHttpHandler.cs | 25 | ||||
-rw-r--r-- | OpenSim/Framework/Servers/OSHttpRequest.cs | 39 | ||||
-rw-r--r-- | OpenSim/Framework/Servers/OSHttpRequestPump.cs | 80 | ||||
-rw-r--r-- | OpenSim/Framework/Servers/OSHttpResponse.cs | 305 | ||||
-rw-r--r-- | OpenSim/Framework/Servers/OSHttpXmlRpcHandler.cs | 222 |
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 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Collections.Specialized; | 30 | using System.Collections.Specialized; |
31 | using System.IO; | ||
31 | using System.Net; | 32 | using System.Net; |
32 | using System.Reflection; | 33 | using System.Reflection; |
33 | using System.Text.RegularExpressions; | 34 | using 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 | ||
28 | using System; | ||
28 | using System.Collections; | 29 | using System.Collections; |
29 | using System.IO; | 30 | using System.IO; |
30 | using System.Net; | 31 | using System.Net; |
31 | using System.Text; | 32 | using System.Text; |
33 | using HttpServer; | ||
32 | 34 | ||
33 | namespace OpenSim.Framework.Servers | 35 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Reflection; | ||
33 | using System.Text; | ||
34 | using System.Text.RegularExpressions; | ||
35 | using System.Xml; | ||
36 | using log4net; | ||
37 | using Nwc.XmlRpc; | ||
38 | |||
39 | namespace 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 | ||