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.cs361
1 files changed, 361 insertions, 0 deletions
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs
new file mode 100644
index 0000000..2843e20
--- /dev/null
+++ b/OpenSim/Framework/WebUtil.cs
@@ -0,0 +1,361 @@
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 string[] values = parameters.GetValues(key);
267 if (values != null)
268 {
269 foreach (string value in values)
270 items.Add(String.Concat(key, "=", HttpUtility.UrlEncode(value ?? String.Empty)));
271 }
272 }
273
274 return String.Join("&", items.ToArray());
275 }
276
277 /// <summary>
278 ///
279 /// </summary>
280 /// <param name="collection"></param>
281 /// <param name="key"></param>
282 /// <returns></returns>
283 public static string GetOne(this NameValueCollection collection, string key)
284 {
285 string[] values = collection.GetValues(key);
286 if (values != null && values.Length > 0)
287 return values[0];
288
289 return null;
290 }
291
292 #endregion NameValueCollection
293
294 #region Stream
295
296 /// <summary>
297 /// Copies the contents of one stream to another, starting at the
298 /// current position of each stream
299 /// </summary>
300 /// <param name="copyFrom">The stream to copy from, at the position
301 /// where copying should begin</param>
302 /// <param name="copyTo">The stream to copy to, at the position where
303 /// bytes should be written</param>
304 /// <param name="maximumBytesToCopy">The maximum bytes to copy</param>
305 /// <returns>The total number of bytes copied</returns>
306 /// <remarks>
307 /// Copying begins at the streams' current positions. The positions are
308 /// NOT reset after copying is complete.
309 /// </remarks>
310 public static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy)
311 {
312 byte[] buffer = new byte[4096];
313 int readBytes;
314 int totalCopiedBytes = 0;
315
316 while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(4096, maximumBytesToCopy))) > 0)
317 {
318 int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
319 copyTo.Write(buffer, 0, writeBytes);
320 totalCopiedBytes += writeBytes;
321 maximumBytesToCopy -= writeBytes;
322 }
323
324 return totalCopiedBytes;
325 }
326
327 /// <summary>
328 /// Converts an entire stream to a string, regardless of current stream
329 /// position
330 /// </summary>
331 /// <param name="stream">The stream to convert to a string</param>
332 /// <returns></returns>
333 /// <remarks>When this method is done, the stream position will be
334 /// reset to its previous position before this method was called</remarks>
335 public static string GetStreamString(this Stream stream)
336 {
337 string value = null;
338
339 if (stream != null && stream.CanRead)
340 {
341 long rewindPos = -1;
342
343 if (stream.CanSeek)
344 {
345 rewindPos = stream.Position;
346 stream.Seek(0, SeekOrigin.Begin);
347 }
348
349 StreamReader reader = new StreamReader(stream);
350 value = reader.ReadToEnd();
351
352 if (rewindPos >= 0)
353 stream.Seek(rewindPos, SeekOrigin.Begin);
354 }
355
356 return value;
357 }
358
359 #endregion Stream
360 }
361}