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.cs357
1 files changed, 357 insertions, 0 deletions
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs
new file mode 100644
index 0000000..16e44af
--- /dev/null
+++ b/OpenSim/Framework/WebUtil.cs
@@ -0,0 +1,357 @@
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 try
150 {
151 string responseStr = responseStream.GetStreamString();
152 OSD responseOSD = OSDParser.Deserialize(responseStr);
153 if (responseOSD.Type == OSDType.Map)
154 return (OSDMap)responseOSD;
155 else
156 errorMessage = "Response format was invalid.";
157 }
158 catch
159 {
160 errorMessage = "Failed to parse the response.";
161 }
162 }
163 }
164 }
165 catch (Exception ex)
166 {
167 m_log.Warn("POST to URL " + url + " failed: " + ex.Message);
168 errorMessage = ex.Message;
169 }
170
171 return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } };
172 }
173
174 #region Uri
175
176 /// <summary>
177 /// Combines a Uri that can contain both a base Uri and relative path
178 /// with a second relative path fragment
179 /// </summary>
180 /// <param name="uri">Starting (base) Uri</param>
181 /// <param name="fragment">Relative path fragment to append to the end
182 /// of the Uri</param>
183 /// <returns>The combined Uri</returns>
184 /// <remarks>This is similar to the Uri constructor that takes a base
185 /// Uri and the relative path, except this method can append a relative
186 /// path fragment on to an existing relative path</remarks>
187 public static Uri Combine(this Uri uri, string fragment)
188 {
189 string fragment1 = uri.Fragment;
190 string fragment2 = fragment;
191
192 if (!fragment1.EndsWith("/"))
193 fragment1 = fragment1 + '/';
194 if (fragment2.StartsWith("/"))
195 fragment2 = fragment2.Substring(1);
196
197 return new Uri(uri, fragment1 + fragment2);
198 }
199
200 /// <summary>
201 /// Combines a Uri that can contain both a base Uri and relative path
202 /// with a second relative path fragment. If the fragment is absolute,
203 /// it will be returned without modification
204 /// </summary>
205 /// <param name="uri">Starting (base) Uri</param>
206 /// <param name="fragment">Relative path fragment to append to the end
207 /// of the Uri, or an absolute Uri to return unmodified</param>
208 /// <returns>The combined Uri</returns>
209 public static Uri Combine(this Uri uri, Uri fragment)
210 {
211 if (fragment.IsAbsoluteUri)
212 return fragment;
213
214 string fragment1 = uri.Fragment;
215 string fragment2 = fragment.ToString();
216
217 if (!fragment1.EndsWith("/"))
218 fragment1 = fragment1 + '/';
219 if (fragment2.StartsWith("/"))
220 fragment2 = fragment2.Substring(1);
221
222 return new Uri(uri, fragment1 + fragment2);
223 }
224
225 /// <summary>
226 /// Appends a query string to a Uri that may or may not have existing
227 /// query parameters
228 /// </summary>
229 /// <param name="uri">Uri to append the query to</param>
230 /// <param name="query">Query string to append. Can either start with ?
231 /// or just containg key/value pairs</param>
232 /// <returns>String representation of the Uri with the query string
233 /// appended</returns>
234 public static string AppendQuery(this Uri uri, string query)
235 {
236 if (String.IsNullOrEmpty(query))
237 return uri.ToString();
238
239 if (query[0] == '?' || query[0] == '&')
240 query = query.Substring(1);
241
242 string uriStr = uri.ToString();
243
244 if (uriStr.Contains("?"))
245 return uriStr + '&' + query;
246 else
247 return uriStr + '?' + query;
248 }
249
250 #endregion Uri
251
252 #region NameValueCollection
253
254 /// <summary>
255 /// Convert a NameValueCollection into a query string. This is the
256 /// inverse of HttpUtility.ParseQueryString()
257 /// </summary>
258 /// <param name="parameters">Collection of key/value pairs to convert</param>
259 /// <returns>A query string with URL-escaped values</returns>
260 public static string BuildQueryString(NameValueCollection parameters)
261 {
262 List<string> items = new List<string>(parameters.Count);
263
264 foreach (string key in parameters.Keys)
265 {
266 foreach (string value in parameters.GetValues(key))
267 items.Add(String.Concat(key, "=", HttpUtility.UrlEncode(value ?? String.Empty)));
268 }
269
270 return String.Join("&", items.ToArray());
271 }
272
273 /// <summary>
274 ///
275 /// </summary>
276 /// <param name="collection"></param>
277 /// <param name="key"></param>
278 /// <returns></returns>
279 public static string GetOne(this NameValueCollection collection, string key)
280 {
281 string[] values = collection.GetValues(key);
282 if (values != null && values.Length > 0)
283 return values[0];
284
285 return null;
286 }
287
288 #endregion NameValueCollection
289
290 #region Stream
291
292 /// <summary>
293 /// Copies the contents of one stream to another, starting at the
294 /// current position of each stream
295 /// </summary>
296 /// <param name="copyFrom">The stream to copy from, at the position
297 /// where copying should begin</param>
298 /// <param name="copyTo">The stream to copy to, at the position where
299 /// bytes should be written</param>
300 /// <param name="maximumBytesToCopy">The maximum bytes to copy</param>
301 /// <returns>The total number of bytes copied</returns>
302 /// <remarks>
303 /// Copying begins at the streams' current positions. The positions are
304 /// NOT reset after copying is complete.
305 /// </remarks>
306 public static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy)
307 {
308 byte[] buffer = new byte[4096];
309 int readBytes;
310 int totalCopiedBytes = 0;
311
312 while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(4096, maximumBytesToCopy))) > 0)
313 {
314 int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
315 copyTo.Write(buffer, 0, writeBytes);
316 totalCopiedBytes += writeBytes;
317 maximumBytesToCopy -= writeBytes;
318 }
319
320 return totalCopiedBytes;
321 }
322
323 /// <summary>
324 /// Converts an entire stream to a string, regardless of current stream
325 /// position
326 /// </summary>
327 /// <param name="stream">The stream to convert to a string</param>
328 /// <returns></returns>
329 /// <remarks>When this method is done, the stream position will be
330 /// reset to its previous position before this method was called</remarks>
331 public static string GetStreamString(this Stream stream)
332 {
333 string value = null;
334
335 if (stream != null && stream.CanRead)
336 {
337 long rewindPos = -1;
338
339 if (stream.CanSeek)
340 {
341 rewindPos = stream.Position;
342 stream.Seek(0, SeekOrigin.Begin);
343 }
344
345 StreamReader reader = new StreamReader(stream);
346 value = reader.ReadToEnd();
347
348 if (rewindPos >= 0)
349 stream.Seek(rewindPos, SeekOrigin.Begin);
350 }
351
352 return value;
353 }
354
355 #endregion Stream
356 }
357}