diff options
author | John Hurliman | 2010-03-03 14:38:00 -0800 |
---|---|---|
committer | John Hurliman | 2010-03-03 14:38:00 -0800 |
commit | 61a63ff85124d932cca5a2aeba9ae685fa7f146f (patch) | |
tree | ae5337063e486e32131a188c3d6b50b4695b1f09 /OpenSim/Framework/WebUtil.cs | |
parent | * Adjusted the significant movement magic value from 0.5m to 2.0m and added a... (diff) | |
download | opensim-SC_OLD-61a63ff85124d932cca5a2aeba9ae685fa7f146f.zip opensim-SC_OLD-61a63ff85124d932cca5a2aeba9ae685fa7f146f.tar.gz opensim-SC_OLD-61a63ff85124d932cca5a2aeba9ae685fa7f146f.tar.bz2 opensim-SC_OLD-61a63ff85124d932cca5a2aeba9ae685fa7f146f.tar.xz |
* Added three new helper utility files to OpenSim.Framework. MultipartForm is used for constructing multipart/form-data requests. UntrustedWebRequest places sanity checks and policy on requests to HTTP endpoints that are not in the same trust domain (useful for Hypergrid, OpenID, etc). WebUtil contains misc. functions for managing URLs and network streams
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/WebUtil.cs | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs new file mode 100644 index 0000000..d9782ff --- /dev/null +++ b/OpenSim/Framework/WebUtil.cs | |||
@@ -0,0 +1,330 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Collections.Specialized; | ||
4 | using System.IO; | ||
5 | using System.Net; | ||
6 | using System.Net.Security; | ||
7 | using System.Reflection; | ||
8 | using System.Text; | ||
9 | using System.Web; | ||
10 | using log4net; | ||
11 | using OpenSim.Framework.Servers.HttpServer; | ||
12 | using OpenMetaverse.StructuredData; | ||
13 | |||
14 | namespace OpenSim.Framework | ||
15 | { | ||
16 | /// <summary> | ||
17 | /// Miscellaneous static methods and extension methods related to the web | ||
18 | /// </summary> | ||
19 | public static class WebUtil | ||
20 | { | ||
21 | private static readonly ILog m_log = | ||
22 | LogManager.GetLogger( | ||
23 | MethodBase.GetCurrentMethod().DeclaringType); | ||
24 | |||
25 | /// <summary> | ||
26 | /// Send LLSD to an HTTP client in application/llsd+json form | ||
27 | /// </summary> | ||
28 | /// <param name="response">HTTP response to send the data in</param> | ||
29 | /// <param name="body">LLSD to send to the client</param> | ||
30 | public static void SendJSONResponse(OSHttpResponse response, OSDMap body) | ||
31 | { | ||
32 | byte[] responseData = Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(body)); | ||
33 | |||
34 | response.ContentEncoding = Encoding.UTF8; | ||
35 | response.ContentLength = responseData.Length; | ||
36 | response.ContentType = "application/llsd+json"; | ||
37 | response.Body.Write(responseData, 0, responseData.Length); | ||
38 | } | ||
39 | |||
40 | /// <summary> | ||
41 | /// Send LLSD to an HTTP client in application/llsd+xml form | ||
42 | /// </summary> | ||
43 | /// <param name="response">HTTP response to send the data in</param> | ||
44 | /// <param name="body">LLSD to send to the client</param> | ||
45 | public static void SendXMLResponse(OSHttpResponse response, OSDMap body) | ||
46 | { | ||
47 | byte[] responseData = OSDParser.SerializeLLSDXmlBytes(body); | ||
48 | |||
49 | response.ContentEncoding = Encoding.UTF8; | ||
50 | response.ContentLength = responseData.Length; | ||
51 | response.ContentType = "application/llsd+xml"; | ||
52 | response.Body.Write(responseData, 0, responseData.Length); | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Make a GET or GET-like request to a web service that returns LLSD | ||
57 | /// or JSON data | ||
58 | /// </summary> | ||
59 | public static OSDMap ServiceRequest(string url, string httpVerb) | ||
60 | { | ||
61 | string errorMessage; | ||
62 | |||
63 | try | ||
64 | { | ||
65 | HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); | ||
66 | request.Method = httpVerb; | ||
67 | |||
68 | using (WebResponse response = request.GetResponse()) | ||
69 | { | ||
70 | using (Stream responseStream = response.GetResponseStream()) | ||
71 | { | ||
72 | try | ||
73 | { | ||
74 | string responseStr = responseStream.GetStreamString(); | ||
75 | OSD responseOSD = OSDParser.Deserialize(responseStr); | ||
76 | if (responseOSD.Type == OSDType.Map) | ||
77 | return (OSDMap)responseOSD; | ||
78 | else | ||
79 | errorMessage = "Response format was invalid."; | ||
80 | } | ||
81 | catch | ||
82 | { | ||
83 | errorMessage = "Failed to parse the response."; | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | catch (Exception ex) | ||
89 | { | ||
90 | m_log.Warn("GET from URL " + url + " failed: " + ex.Message); | ||
91 | errorMessage = ex.Message; | ||
92 | } | ||
93 | |||
94 | return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; | ||
95 | } | ||
96 | |||
97 | /// <summary> | ||
98 | /// POST URL-encoded form data to a web service that returns LLSD or | ||
99 | /// JSON data | ||
100 | /// </summary> | ||
101 | public static OSDMap PostToService(string url, NameValueCollection data) | ||
102 | { | ||
103 | string errorMessage; | ||
104 | |||
105 | try | ||
106 | { | ||
107 | string queryString = BuildQueryString(data); | ||
108 | byte[] requestData = System.Text.Encoding.UTF8.GetBytes(queryString); | ||
109 | |||
110 | HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); | ||
111 | request.Method = "POST"; | ||
112 | request.ContentLength = requestData.Length; | ||
113 | request.ContentType = "application/x-www-form-urlencoded"; | ||
114 | |||
115 | using (Stream requestStream = request.GetRequestStream()) | ||
116 | requestStream.Write(requestData, 0, requestData.Length); | ||
117 | |||
118 | using (WebResponse response = request.GetResponse()) | ||
119 | { | ||
120 | using (Stream responseStream = response.GetResponseStream()) | ||
121 | { | ||
122 | try | ||
123 | { | ||
124 | string responseStr = responseStream.GetStreamString(); | ||
125 | OSD responseOSD = OSDParser.Deserialize(responseStr); | ||
126 | if (responseOSD.Type == OSDType.Map) | ||
127 | return (OSDMap)responseOSD; | ||
128 | else | ||
129 | errorMessage = "Response format was invalid."; | ||
130 | } | ||
131 | catch | ||
132 | { | ||
133 | errorMessage = "Failed to parse the response."; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | catch (Exception ex) | ||
139 | { | ||
140 | m_log.Warn("POST to URL " + url + " failed: " + ex.Message); | ||
141 | errorMessage = ex.Message; | ||
142 | } | ||
143 | |||
144 | return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; | ||
145 | } | ||
146 | |||
147 | #region Uri | ||
148 | |||
149 | /// <summary> | ||
150 | /// Combines a Uri that can contain both a base Uri and relative path | ||
151 | /// with a second relative path fragment | ||
152 | /// </summary> | ||
153 | /// <param name="uri">Starting (base) Uri</param> | ||
154 | /// <param name="fragment">Relative path fragment to append to the end | ||
155 | /// of the Uri</param> | ||
156 | /// <returns>The combined Uri</returns> | ||
157 | /// <remarks>This is similar to the Uri constructor that takes a base | ||
158 | /// Uri and the relative path, except this method can append a relative | ||
159 | /// path fragment on to an existing relative path</remarks> | ||
160 | public static Uri Combine(this Uri uri, string fragment) | ||
161 | { | ||
162 | string fragment1 = uri.Fragment; | ||
163 | string fragment2 = fragment; | ||
164 | |||
165 | if (!fragment1.EndsWith("/")) | ||
166 | fragment1 = fragment1 + '/'; | ||
167 | if (fragment2.StartsWith("/")) | ||
168 | fragment2 = fragment2.Substring(1); | ||
169 | |||
170 | return new Uri(uri, fragment1 + fragment2); | ||
171 | } | ||
172 | |||
173 | /// <summary> | ||
174 | /// Combines a Uri that can contain both a base Uri and relative path | ||
175 | /// with a second relative path fragment. If the fragment is absolute, | ||
176 | /// it will be returned without modification | ||
177 | /// </summary> | ||
178 | /// <param name="uri">Starting (base) Uri</param> | ||
179 | /// <param name="fragment">Relative path fragment to append to the end | ||
180 | /// of the Uri, or an absolute Uri to return unmodified</param> | ||
181 | /// <returns>The combined Uri</returns> | ||
182 | public static Uri Combine(this Uri uri, Uri fragment) | ||
183 | { | ||
184 | if (fragment.IsAbsoluteUri) | ||
185 | return fragment; | ||
186 | |||
187 | string fragment1 = uri.Fragment; | ||
188 | string fragment2 = fragment.ToString(); | ||
189 | |||
190 | if (!fragment1.EndsWith("/")) | ||
191 | fragment1 = fragment1 + '/'; | ||
192 | if (fragment2.StartsWith("/")) | ||
193 | fragment2 = fragment2.Substring(1); | ||
194 | |||
195 | return new Uri(uri, fragment1 + fragment2); | ||
196 | } | ||
197 | |||
198 | /// <summary> | ||
199 | /// Appends a query string to a Uri that may or may not have existing | ||
200 | /// query parameters | ||
201 | /// </summary> | ||
202 | /// <param name="uri">Uri to append the query to</param> | ||
203 | /// <param name="query">Query string to append. Can either start with ? | ||
204 | /// or just containg key/value pairs</param> | ||
205 | /// <returns>String representation of the Uri with the query string | ||
206 | /// appended</returns> | ||
207 | public static string AppendQuery(this Uri uri, string query) | ||
208 | { | ||
209 | if (String.IsNullOrEmpty(query)) | ||
210 | return uri.ToString(); | ||
211 | |||
212 | if (query[0] == '?' || query[0] == '&') | ||
213 | query = query.Substring(1); | ||
214 | |||
215 | string uriStr = uri.ToString(); | ||
216 | |||
217 | if (uriStr.Contains("?")) | ||
218 | return uriStr + '&' + query; | ||
219 | else | ||
220 | return uriStr + '?' + query; | ||
221 | } | ||
222 | |||
223 | #endregion Uri | ||
224 | |||
225 | #region NameValueCollection | ||
226 | |||
227 | /// <summary> | ||
228 | /// Convert a NameValueCollection into a query string. This is the | ||
229 | /// inverse of HttpUtility.ParseQueryString() | ||
230 | /// </summary> | ||
231 | /// <param name="parameters">Collection of key/value pairs to convert</param> | ||
232 | /// <returns>A query string with URL-escaped values</returns> | ||
233 | public static string BuildQueryString(NameValueCollection parameters) | ||
234 | { | ||
235 | List<string> items = new List<string>(parameters.Count); | ||
236 | |||
237 | foreach (string key in parameters.Keys) | ||
238 | { | ||
239 | foreach (string value in parameters.GetValues(key)) | ||
240 | items.Add(String.Concat(key, "=", HttpUtility.UrlEncode(value ?? String.Empty))); | ||
241 | } | ||
242 | |||
243 | return String.Join("&", items.ToArray()); | ||
244 | } | ||
245 | |||
246 | /// <summary> | ||
247 | /// | ||
248 | /// </summary> | ||
249 | /// <param name="collection"></param> | ||
250 | /// <param name="key"></param> | ||
251 | /// <returns></returns> | ||
252 | public static string GetOne(this NameValueCollection collection, string key) | ||
253 | { | ||
254 | string[] values = collection.GetValues(key); | ||
255 | if (values != null && values.Length > 0) | ||
256 | return values[0]; | ||
257 | |||
258 | return null; | ||
259 | } | ||
260 | |||
261 | #endregion NameValueCollection | ||
262 | |||
263 | #region Stream | ||
264 | |||
265 | /// <summary> | ||
266 | /// Copies the contents of one stream to another, starting at the | ||
267 | /// current position of each stream | ||
268 | /// </summary> | ||
269 | /// <param name="copyFrom">The stream to copy from, at the position | ||
270 | /// where copying should begin</param> | ||
271 | /// <param name="copyTo">The stream to copy to, at the position where | ||
272 | /// bytes should be written</param> | ||
273 | /// <param name="maximumBytesToCopy">The maximum bytes to copy</param> | ||
274 | /// <returns>The total number of bytes copied</returns> | ||
275 | /// <remarks> | ||
276 | /// Copying begins at the streams' current positions. The positions are | ||
277 | /// NOT reset after copying is complete. | ||
278 | /// </remarks> | ||
279 | public static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) | ||
280 | { | ||
281 | byte[] buffer = new byte[4096]; | ||
282 | int readBytes; | ||
283 | int totalCopiedBytes = 0; | ||
284 | |||
285 | while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(4096, maximumBytesToCopy))) > 0) | ||
286 | { | ||
287 | int writeBytes = Math.Min(maximumBytesToCopy, readBytes); | ||
288 | copyTo.Write(buffer, 0, writeBytes); | ||
289 | totalCopiedBytes += writeBytes; | ||
290 | maximumBytesToCopy -= writeBytes; | ||
291 | } | ||
292 | |||
293 | return totalCopiedBytes; | ||
294 | } | ||
295 | |||
296 | /// <summary> | ||
297 | /// Converts an entire stream to a string, regardless of current stream | ||
298 | /// position | ||
299 | /// </summary> | ||
300 | /// <param name="stream">The stream to convert to a string</param> | ||
301 | /// <returns></returns> | ||
302 | /// <remarks>When this method is done, the stream position will be | ||
303 | /// reset to its previous position before this method was called</remarks> | ||
304 | public static string GetStreamString(this Stream stream) | ||
305 | { | ||
306 | string value = null; | ||
307 | |||
308 | if (stream != null && stream.CanRead) | ||
309 | { | ||
310 | long rewindPos = -1; | ||
311 | |||
312 | if (stream.CanSeek) | ||
313 | { | ||
314 | rewindPos = stream.Position; | ||
315 | stream.Seek(0, SeekOrigin.Begin); | ||
316 | } | ||
317 | |||
318 | StreamReader reader = new StreamReader(stream); | ||
319 | value = reader.ReadToEnd(); | ||
320 | |||
321 | if (rewindPos >= 0) | ||
322 | stream.Seek(rewindPos, SeekOrigin.Begin); | ||
323 | } | ||
324 | |||
325 | return value; | ||
326 | } | ||
327 | |||
328 | #endregion Stream | ||
329 | } | ||
330 | } | ||