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