OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
WebUtil.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.Globalization;
33 using System.IO;
34 using System.IO.Compression;
35 using System.Net;
36 using System.Net.Security;
37 using System.Reflection;
38 using System.Text;
39 using System.Web;
40 using System.Xml;
41 using System.Xml.Serialization;
42 using System.Xml.Linq;
43 using log4net;
44 using Nwc.XmlRpc;
45 using OpenMetaverse.StructuredData;
47 
48 using OpenSim.Framework.ServiceAuth;
49 
50 namespace OpenSim.Framework
51 {
55  public static class WebUtil
56  {
57  private static readonly ILog m_log =
58  LogManager.GetLogger(
59  MethodBase.GetCurrentMethod().DeclaringType);
60 
67  public static int DebugLevel { get; set; }
68 
72  public static int RequestNumber { get; set; }
73 
77  public static bool SerializeOSDRequestsPerEndpoint { get; set; }
78 
83  public const string OSHeaderRequestID = "opensim-request-id";
84 
89  public const int LongCallTime = 3000;
90 
99  public const int MaxRequestDiagLength = 200;
100 
104  private static Dictionary<string,object> m_endpointSerializer = new Dictionary<string,object>();
105 
106  private static object EndPointLock(string url)
107  {
108  System.Uri uri = new System.Uri(url);
109  string endpoint = string.Format("{0}:{1}",uri.Host,uri.Port);
110 
111  lock (m_endpointSerializer)
112  {
113  object eplock = null;
114 
115  if (! m_endpointSerializer.TryGetValue(endpoint,out eplock))
116  {
117  eplock = new object();
118  m_endpointSerializer.Add(endpoint,eplock);
119  // m_log.WarnFormat("[WEB UTIL] add a new host to end point serializer {0}",endpoint);
120  }
121 
122  return eplock;
123  }
124  }
125 
126  #region JSONRequest
127 
132  public static OSDMap PutToServiceCompressed(string url, OSDMap data, int timeout)
133  {
134  return ServiceOSDRequest(url,data, "PUT", timeout, true, false);
135  }
136 
137  public static OSDMap PutToService(string url, OSDMap data, int timeout)
138  {
139  return ServiceOSDRequest(url,data, "PUT", timeout, false, false);
140  }
141 
142  public static OSDMap PostToService(string url, OSDMap data, int timeout, bool rpc)
143  {
144  return ServiceOSDRequest(url, data, "POST", timeout, false, rpc);
145  }
146 
147  public static OSDMap PostToServiceCompressed(string url, OSDMap data, int timeout)
148  {
149  return ServiceOSDRequest(url, data, "POST", timeout, true, false);
150  }
151 
152  public static OSDMap GetFromService(string url, int timeout)
153  {
154  return ServiceOSDRequest(url, null, "GET", timeout, false, false);
155  }
156 
157  public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed, bool rpc)
158  {
159  if (SerializeOSDRequestsPerEndpoint)
160  {
161  lock (EndPointLock(url))
162  {
163  return ServiceOSDRequestWorker(url, data, method, timeout, compressed, rpc);
164  }
165  }
166  else
167  {
168  return ServiceOSDRequestWorker(url, data, method, timeout, compressed, rpc);
169  }
170  }
171 
172  public static void LogOutgoingDetail(Stream outputStream)
173  {
174  LogOutgoingDetail("", outputStream);
175  }
176 
177  public static void LogOutgoingDetail(string context, Stream outputStream)
178  {
179  using (Stream stream = Util.Copy(outputStream))
180  using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
181  {
182  string output;
183 
184  if (DebugLevel == 5)
185  {
186  char[] chars = new char[WebUtil.MaxRequestDiagLength + 1]; // +1 so we know to add "..." only if needed
187  int len = reader.Read(chars, 0, WebUtil.MaxRequestDiagLength + 1);
188  output = new string(chars, 0, len);
189  }
190  else
191  {
192  output = reader.ReadToEnd();
193  }
194 
195  LogOutgoingDetail(context, output);
196  }
197  }
198 
199  public static void LogOutgoingDetail(string type, int reqnum, string output)
200  {
201  LogOutgoingDetail(string.Format("{0} {1}: ", type, reqnum), output);
202  }
203 
204  public static void LogOutgoingDetail(string context, string output)
205  {
206  if (DebugLevel == 5)
207  {
208  if (output.Length > MaxRequestDiagLength)
209  output = output.Substring(0, MaxRequestDiagLength) + "...";
210  }
211 
212  m_log.DebugFormat("[LOGHTTP]: {0}{1}", context, Util.BinaryToASCII(output));
213  }
214 
215  public static void LogResponseDetail(int reqnum, Stream inputStream)
216  {
217  LogOutgoingDetail(string.Format("RESPONSE {0}: ", reqnum), inputStream);
218  }
219 
220  public static void LogResponseDetail(int reqnum, string input)
221  {
222  LogOutgoingDetail(string.Format("RESPONSE {0}: ", reqnum), input);
223  }
224 
225  private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed, bool rpc)
226  {
227  int reqnum = RequestNumber++;
228 
229  if (DebugLevel >= 3)
230  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} JSON-RPC {1} to {2}",
231  reqnum, method, url);
232 
233  string errorMessage = "unknown error";
234  int tickstart = Util.EnvironmentTickCount();
235  int tickdata = 0;
236  int tickcompressdata = 0;
237  int tickJsondata = 0;
238  int compsize = 0;
239  string strBuffer = null;
240 
241  try
242  {
243  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
244  request.Method = method;
245  request.Timeout = timeout;
246  request.KeepAlive = false;
247  request.MaximumAutomaticRedirections = 10;
248  request.ReadWriteTimeout = timeout / 4;
249  request.Headers[OSHeaderRequestID] = reqnum.ToString();
250 
251  // If there is some input, write it into the request
252  if (data != null)
253  {
254  strBuffer = OSDParser.SerializeJsonString(data);
255 
256  tickJsondata = Util.EnvironmentTickCountSubtract(tickstart);
257 
258  if (DebugLevel >= 5)
259  LogOutgoingDetail("SEND", reqnum, strBuffer);
260 
261  byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer);
262 
263  request.ContentType = rpc ? "application/json-rpc" : "application/json";
264 
265  if (compressed)
266  {
267  request.Headers["X-Content-Encoding"] = "gzip"; // can't set "Content-Encoding" because old OpenSims fail if they get an unrecognized Content-Encoding
268 
269  using (MemoryStream ms = new MemoryStream())
270  {
271  using (GZipStream comp = new GZipStream(ms, CompressionMode.Compress, true))
272  {
273  comp.Write(buffer, 0, buffer.Length);
274  // We need to close the gzip stream before we write it anywhere
275  // because apparently something important related to gzip compression
276  // gets written on the stream upon Dispose()
277  }
278  byte[] buf = ms.ToArray();
279 
280  tickcompressdata = Util.EnvironmentTickCountSubtract(tickstart);
281 
282  request.ContentLength = buf.Length; //Count bytes to send
283  compsize = buf.Length;
284  using (Stream requestStream = request.GetRequestStream())
285  requestStream.Write(buf, 0, (int)buf.Length);
286  }
287  }
288  else
289  {
290  compsize = buffer.Length;
291 
292  request.ContentLength = buffer.Length; //Count bytes to send
293  using (Stream requestStream = request.GetRequestStream())
294  requestStream.Write(buffer, 0, buffer.Length); //Send it
295  }
296  }
297 
298  // capture how much time was spent writing, this may seem silly
299  // but with the number concurrent requests, this often blocks
300  tickdata = Util.EnvironmentTickCountSubtract(tickstart);
301 
302  using (WebResponse response = request.GetResponse())
303  {
304  using (Stream responseStream = response.GetResponseStream())
305  {
306  using (StreamReader reader = new StreamReader(responseStream))
307  {
308  string responseStr = reader.ReadToEnd();
309  if (WebUtil.DebugLevel >= 5)
310  WebUtil.LogResponseDetail(reqnum, responseStr);
311  return CanonicalizeResults(responseStr);
312  }
313  }
314  }
315  }
316  catch (WebException we)
317  {
318  errorMessage = we.Message;
319  if (we.Status == WebExceptionStatus.ProtocolError)
320  {
321  using (HttpWebResponse webResponse = (HttpWebResponse)we.Response)
322  errorMessage = String.Format("[{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription);
323  }
324  }
325  catch (Exception ex)
326  {
327  errorMessage = ex.Message;
328  m_log.Debug("[WEB UTIL]: Exception making request: " + ex.ToString());
329  }
330  finally
331  {
332  int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
333  if (tickdiff > LongCallTime)
334  {
335  m_log.InfoFormat(
336  "[WEB UTIL]: Slow ServiceOSD request {0} {1} {2} took {3}ms, {4}ms writing({5} at Json; {6} at comp), {7} bytes ({8} uncomp): {9}",
337  reqnum,
338  method,
339  url,
340  tickdiff,
341  tickdata,
342  tickJsondata,
343  tickcompressdata,
344  compsize,
345  strBuffer != null ? strBuffer.Length : 0,
346 
347  strBuffer != null
348  ? (strBuffer.Length > MaxRequestDiagLength ? strBuffer.Remove(MaxRequestDiagLength) : strBuffer)
349  : "");
350  }
351  else if (DebugLevel >= 4)
352  {
353  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing",
354  reqnum, tickdiff, tickdata);
355  }
356  }
357 
358  m_log.DebugFormat(
359  "[LOGHTTP]: JSON-RPC request {0} {1} to {2} FAILED: {3}", reqnum, method, url, errorMessage);
360 
361  return ErrorResponseMap(errorMessage);
362  }
363 
372  private static OSDMap CanonicalizeResults(string response)
373  {
374  OSDMap result = new OSDMap();
375 
376  // Default values
377  result["Success"] = OSD.FromBoolean(true);
378  result["success"] = OSD.FromBoolean(true);
379  result["_RawResult"] = OSD.FromString(response);
380  result["_Result"] = new OSDMap();
381 
382  if (response.Equals("true",System.StringComparison.OrdinalIgnoreCase))
383  return result;
384 
385  if (response.Equals("false",System.StringComparison.OrdinalIgnoreCase))
386  {
387  result["Success"] = OSD.FromBoolean(false);
388  result["success"] = OSD.FromBoolean(false);
389  return result;
390  }
391 
392  try
393  {
394  OSD responseOSD = OSDParser.Deserialize(response);
395  if (responseOSD.Type == OSDType.Map)
396  {
397  result["_Result"] = (OSDMap)responseOSD;
398  return result;
399  }
400  }
401  catch
402  {
403  // don't need to treat this as an error... we're just guessing anyway
404 // m_log.DebugFormat("[WEB UTIL] couldn't decode <{0}>: {1}",response,e.Message);
405  }
406 
407  return result;
408  }
409 
410  #endregion JSONRequest
411 
412  #region FormRequest
413 
418  public static OSDMap PostToService(string url, NameValueCollection data)
419  {
420  return ServiceFormRequest(url,data, 30000);
421  }
422 
423  public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout)
424  {
425  lock (EndPointLock(url))
426  {
427  return ServiceFormRequestWorker(url,data,timeout);
428  }
429  }
430 
431  private static OSDMap ServiceFormRequestWorker(string url, NameValueCollection data, int timeout)
432  {
433  int reqnum = RequestNumber++;
434  string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown";
435 
436  if (DebugLevel >= 3)
437  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} ServiceForm '{1}' to {2}",
438  reqnum, method, url);
439 
440  string errorMessage = "unknown error";
441  int tickstart = Util.EnvironmentTickCount();
442  int tickdata = 0;
443  string queryString = null;
444 
445  try
446  {
447  HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
448  request.Method = "POST";
449  request.Timeout = timeout;
450  request.KeepAlive = false;
451  request.MaximumAutomaticRedirections = 10;
452  request.ReadWriteTimeout = timeout / 4;
453  request.Headers[OSHeaderRequestID] = reqnum.ToString();
454 
455  if (data != null)
456  {
457  queryString = BuildQueryString(data);
458 
459  if (DebugLevel >= 5)
460  LogOutgoingDetail("SEND", reqnum, queryString);
461 
462  byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString);
463 
464  request.ContentLength = buffer.Length;
465  request.ContentType = "application/x-www-form-urlencoded";
466  using (Stream requestStream = request.GetRequestStream())
467  requestStream.Write(buffer, 0, buffer.Length);
468  }
469 
470  // capture how much time was spent writing, this may seem silly
471  // but with the number concurrent requests, this often blocks
472  tickdata = Util.EnvironmentTickCountSubtract(tickstart);
473 
474  using (WebResponse response = request.GetResponse())
475  {
476  using (Stream responseStream = response.GetResponseStream())
477  {
478  using (StreamReader reader = new StreamReader(responseStream))
479  {
480  string responseStr = reader.ReadToEnd();
481  if (WebUtil.DebugLevel >= 5)
482  WebUtil.LogResponseDetail(reqnum, responseStr);
483  OSD responseOSD = OSDParser.Deserialize(responseStr);
484 
485  if (responseOSD.Type == OSDType.Map)
486  return (OSDMap)responseOSD;
487  }
488  }
489  }
490  }
491  catch (WebException we)
492  {
493  errorMessage = we.Message;
494  if (we.Status == WebExceptionStatus.ProtocolError)
495  {
496  using (HttpWebResponse webResponse = (HttpWebResponse)we.Response)
497  errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription);
498  }
499  }
500  catch (Exception ex)
501  {
502  errorMessage = ex.Message;
503  }
504  finally
505  {
506  int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
507  if (tickdiff > LongCallTime)
508  {
509  m_log.InfoFormat(
510  "[LOGHTTP]: Slow ServiceForm request {0} '{1}' to {2} took {3}ms, {4}ms writing, {5}",
511  reqnum, method, url, tickdiff, tickdata,
512  queryString != null
513  ? (queryString.Length > MaxRequestDiagLength) ? queryString.Remove(MaxRequestDiagLength) : queryString
514  : "");
515  }
516  else if (DebugLevel >= 4)
517  {
518  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing",
519  reqnum, tickdiff, tickdata);
520  }
521  }
522 
523  m_log.WarnFormat("[LOGHTTP]: ServiceForm request {0} '{1}' to {2} failed: {3}", reqnum, method, url, errorMessage);
524 
525  return ErrorResponseMap(errorMessage);
526  }
527 
532  private static OSDMap ErrorResponseMap(string msg)
533  {
534  OSDMap result = new OSDMap();
535  result["Success"] = "False";
536  result["Message"] = OSD.FromString("Service request failed: " + msg);
537  return result;
538  }
539 
540  #endregion FormRequest
541 
542  #region Uri
543 
555  public static Uri Combine(this Uri uri, string fragment)
556  {
557  string fragment1 = uri.Fragment;
558  string fragment2 = fragment;
559 
560  if (!fragment1.EndsWith("/"))
561  fragment1 = fragment1 + '/';
562  if (fragment2.StartsWith("/"))
563  fragment2 = fragment2.Substring(1);
564 
565  return new Uri(uri, fragment1 + fragment2);
566  }
567 
577  public static Uri Combine(this Uri uri, Uri fragment)
578  {
579  if (fragment.IsAbsoluteUri)
580  return fragment;
581 
582  string fragment1 = uri.Fragment;
583  string fragment2 = fragment.ToString();
584 
585  if (!fragment1.EndsWith("/"))
586  fragment1 = fragment1 + '/';
587  if (fragment2.StartsWith("/"))
588  fragment2 = fragment2.Substring(1);
589 
590  return new Uri(uri, fragment1 + fragment2);
591  }
592 
602  public static string AppendQuery(this Uri uri, string query)
603  {
604  if (String.IsNullOrEmpty(query))
605  return uri.ToString();
606 
607  if (query[0] == '?' || query[0] == '&')
608  query = query.Substring(1);
609 
610  string uriStr = uri.ToString();
611 
612  if (uriStr.Contains("?"))
613  return uriStr + '&' + query;
614  else
615  return uriStr + '?' + query;
616  }
617 
618  #endregion Uri
619 
620  #region NameValueCollection
621 
628  public static string BuildQueryString(NameValueCollection parameters)
629  {
630  List<string> items = new List<string>(parameters.Count);
631 
632  foreach (string key in parameters.Keys)
633  {
634  string[] values = parameters.GetValues(key);
635  if (values != null)
636  {
637  foreach (string value in values)
638  items.Add(String.Concat(key, "=", HttpUtility.UrlEncode(value ?? String.Empty)));
639  }
640  }
641 
642  return String.Join("&", items.ToArray());
643  }
644 
651  public static string GetOne(this NameValueCollection collection, string key)
652  {
653  string[] values = collection.GetValues(key);
654  if (values != null && values.Length > 0)
655  return values[0];
656 
657  return null;
658  }
659 
660  #endregion NameValueCollection
661 
662  #region Stream
663 
683  public static int CopyStream(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy)
684  {
685  byte[] buffer = new byte[4096];
686  int readBytes;
687  int totalCopiedBytes = 0;
688 
689  while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(4096, maximumBytesToCopy))) > 0)
690  {
691  int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
692  copyTo.Write(buffer, 0, writeBytes);
693  totalCopiedBytes += writeBytes;
694  maximumBytesToCopy -= writeBytes;
695  }
696 
697  return totalCopiedBytes;
698  }
699 
700  #endregion Stream
701 
702  public class QBasedComparer : IComparer
703  {
704  public int Compare(Object x, Object y)
705  {
706  float qx = GetQ(x);
707  float qy = GetQ(y);
708  return qy.CompareTo(qx); // descending order
709  }
710 
711  private float GetQ(Object o)
712  {
713  // Example: image/png;q=0.9
714 
715  float qvalue = 1F;
716  if (o is String)
717  {
718  string mime = (string)o;
719  string[] parts = mime.Split(';');
720  if (parts.Length > 1)
721  {
722  string[] kvp = parts[1].Split('=');
723  if (kvp.Length == 2 && kvp[0] == "q")
724  float.TryParse(kvp[1], NumberStyles.Number, CultureInfo.InvariantCulture, out qvalue);
725  }
726  }
727 
728  return qvalue;
729  }
730  }
731 
741  public static string[] GetPreferredImageTypes(string accept)
742  {
743  if (string.IsNullOrEmpty(accept))
744  return new string[0];
745 
746  string[] types = accept.Split(new char[] { ',' });
747  if (types.Length > 0)
748  {
749  List<string> list = new List<string>(types);
750  list.RemoveAll(delegate(string s) { return !s.ToLower().StartsWith("image"); });
751  ArrayList tlist = new ArrayList(list);
752  tlist.Sort(new QBasedComparer());
753 
754  string[] result = new string[tlist.Count];
755  for (int i = 0; i < tlist.Count; i++)
756  {
757  string mime = (string)tlist[i];
758  string[] parts = mime.Split(new char[] { ';' });
759  string[] pair = parts[0].Split(new char[] { '/' });
760  if (pair.Length == 2)
761  result[i] = pair[1].ToLower();
762  else // oops, we don't know what this is...
763  result[i] = pair[0];
764  }
765 
766  return result;
767  }
768 
769  return new string[0];
770  }
771  }
772 
773  public static class AsynchronousRestObjectRequester
774  {
775  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
776 
789  //
790  public static void MakeRequest<TRequest, TResponse>(string verb,
791  string requestUrl, TRequest obj, Action<TResponse> action)
792  {
793  MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, action, 0);
794  }
795 
796  public static void MakeRequest<TRequest, TResponse>(string verb,
797  string requestUrl, TRequest obj, Action<TResponse> action,
798  int maxConnections)
799  {
800  MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, action, maxConnections, null);
801  }
802 
817  public static void MakeRequest<TRequest, TResponse>(string verb,
818  string requestUrl, TRequest obj, Action<TResponse> action,
819  int maxConnections, IServiceAuth auth)
820  {
821  int reqnum = WebUtil.RequestNumber++;
822 
823  if (WebUtil.DebugLevel >= 3)
824  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} AsynchronousRequestObject {1} to {2}",
825  reqnum, verb, requestUrl);
826 
827  int tickstart = Util.EnvironmentTickCount();
828  int tickdata = 0;
829  int tickdiff = 0;
830 
831  Type type = typeof(TRequest);
832 
833  WebRequest request = WebRequest.Create(requestUrl);
834  HttpWebRequest ht = (HttpWebRequest)request;
835 
836  if (auth != null)
837  auth.AddAuthorization(ht.Headers);
838 
839  if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
840  ht.ServicePoint.ConnectionLimit = maxConnections;
841 
842  TResponse deserial = default(TResponse);
843 
844  request.Method = verb;
845 
846  MemoryStream buffer = null;
847 
848  try
849  {
850  if (verb == "POST")
851  {
852  request.ContentType = "text/xml";
853 
854  buffer = new MemoryStream();
855 
856  XmlWriterSettings settings = new XmlWriterSettings();
857  settings.Encoding = Encoding.UTF8;
858 
859  using (XmlWriter writer = XmlWriter.Create(buffer, settings))
860  {
861  XmlSerializer serializer = new XmlSerializer(type);
862  serializer.Serialize(writer, obj);
863  writer.Flush();
864  }
865 
866  int length = (int)buffer.Length;
867  request.ContentLength = length;
868  byte[] data = buffer.ToArray();
869 
870  if (WebUtil.DebugLevel >= 5)
871  WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data));
872 
873  request.BeginGetRequestStream(delegate(IAsyncResult res)
874  {
875  using (Stream requestStream = request.EndGetRequestStream(res))
876  requestStream.Write(data, 0, length);
877 
878  // capture how much time was spent writing
879  tickdata = Util.EnvironmentTickCountSubtract(tickstart);
880 
881  request.BeginGetResponse(delegate(IAsyncResult ar)
882  {
883  using (WebResponse response = request.EndGetResponse(ar))
884  {
885  try
886  {
887  using (Stream respStream = response.GetResponseStream())
888  {
889  deserial = XMLResponseHelper.LogAndDeserialize<TRequest, TResponse>(
890  reqnum, respStream, response.ContentLength);
891  }
892  }
893  catch (System.InvalidOperationException)
894  {
895  }
896  }
897 
898  action(deserial);
899 
900  }, null);
901  }, null);
902  }
903  else
904  {
905  request.BeginGetResponse(delegate(IAsyncResult res2)
906  {
907  try
908  {
909  // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't
910  // documented in MSDN
911  using (WebResponse response = request.EndGetResponse(res2))
912  {
913  try
914  {
915  using (Stream respStream = response.GetResponseStream())
916  {
917  deserial = XMLResponseHelper.LogAndDeserialize<TRequest, TResponse>(
918  reqnum, respStream, response.ContentLength);
919  }
920  }
921  catch (System.InvalidOperationException)
922  {
923  }
924  }
925  }
926  catch (WebException e)
927  {
928  if (e.Status == WebExceptionStatus.ProtocolError)
929  {
930  if (e.Response is HttpWebResponse)
931  {
932  using (HttpWebResponse httpResponse = (HttpWebResponse)e.Response)
933  {
934  if (httpResponse.StatusCode != HttpStatusCode.NotFound)
935  {
936  // We don't appear to be handling any other status codes, so log these feailures to that
937  // people don't spend unnecessary hours hunting phantom bugs.
938  m_log.DebugFormat(
939  "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}",
940  verb, requestUrl, httpResponse.StatusCode);
941  }
942  }
943  }
944  }
945  else
946  {
947  m_log.ErrorFormat(
948  "[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}",
949  verb, requestUrl, e.Status, e.Message);
950  }
951  }
952  catch (Exception e)
953  {
954  m_log.ErrorFormat(
955  "[ASYNC REQUEST]: Request {0} {1} failed with exception {2}{3}",
956  verb, requestUrl, e.Message, e.StackTrace);
957  }
958 
959  // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString());
960 
961  try
962  {
963  action(deserial);
964  }
965  catch (Exception e)
966  {
967  m_log.ErrorFormat(
968  "[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}{3}",
969  verb, requestUrl, e.Message, e.StackTrace);
970  }
971 
972  }, null);
973  }
974 
975  tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
976  if (tickdiff > WebUtil.LongCallTime)
977  {
978  string originalRequest = null;
979 
980  if (buffer != null)
981  {
982  originalRequest = Encoding.UTF8.GetString(buffer.ToArray());
983 
984  if (originalRequest.Length > WebUtil.MaxRequestDiagLength)
985  originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength);
986  }
987  m_log.InfoFormat(
988  "[LOGHTTP]: Slow AsynchronousRequestObject request {0} {1} to {2} took {3}ms, {4}ms writing, {5}",
989  reqnum, verb, requestUrl, tickdiff, tickdata,
990  originalRequest);
991  }
992  else if (WebUtil.DebugLevel >= 4)
993  {
994  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing",
995 
996  reqnum, tickdiff, tickdata);
997  }
998  }
999  finally
1000  {
1001  if (buffer != null)
1002  buffer.Dispose();
1003  }
1004  }
1005  }
1006 
1007  public static class SynchronousRestFormsRequester
1008  {
1009  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
1010 
1022  public static string MakeRequest(string verb, string requestUrl, string obj, int timeoutsecs, IServiceAuth auth)
1023  {
1024  int reqnum = WebUtil.RequestNumber++;
1025 
1026  if (WebUtil.DebugLevel >= 3)
1027  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} SynchronousRestForms {1} to {2}",
1028  reqnum, verb, requestUrl);
1029 
1030  int tickstart = Util.EnvironmentTickCount();
1031  int tickdata = 0;
1032 
1033  WebRequest request = WebRequest.Create(requestUrl);
1034  request.Method = verb;
1035  if (timeoutsecs > 0)
1036  request.Timeout = timeoutsecs * 1000;
1037 
1038  if (auth != null)
1039  auth.AddAuthorization(request.Headers);
1040 
1041  string respstring = String.Empty;
1042 
1043  int tickset = Util.EnvironmentTickCountSubtract(tickstart);
1044 
1045  using (MemoryStream buffer = new MemoryStream())
1046  {
1047  if ((verb == "POST") || (verb == "PUT"))
1048  {
1049  request.ContentType = "application/x-www-form-urlencoded";
1050 
1051  int length = 0;
1052  using (StreamWriter writer = new StreamWriter(buffer))
1053  {
1054  writer.Write(obj);
1055  writer.Flush();
1056  }
1057 
1058  length = (int)obj.Length;
1059  request.ContentLength = length;
1060  byte[] data = buffer.ToArray();
1061 
1062  if (WebUtil.DebugLevel >= 5)
1063  WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data));
1064 
1065  Stream requestStream = null;
1066  try
1067  {
1068  requestStream = request.GetRequestStream();
1069  requestStream.Write(data, 0, length);
1070  }
1071  catch (Exception e)
1072  {
1073  m_log.InfoFormat("[FORMS]: Error sending request to {0}: {1}. Request: {2}", requestUrl, e.Message,
1074  obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
1075  throw e;
1076  }
1077  finally
1078  {
1079  if (requestStream != null)
1080  requestStream.Dispose();
1081 
1082  // capture how much time was spent writing
1083  tickdata = Util.EnvironmentTickCountSubtract(tickstart);
1084  }
1085  }
1086 
1087  try
1088  {
1089  using (WebResponse resp = request.GetResponse())
1090  {
1091  if (resp.ContentLength != 0)
1092  {
1093  using (Stream respStream = resp.GetResponseStream())
1094  using (StreamReader reader = new StreamReader(respStream))
1095  respstring = reader.ReadToEnd();
1096  }
1097  }
1098  }
1099  catch (Exception e)
1100  {
1101  m_log.InfoFormat("[FORMS]: Error receiving response from {0}: {1}. Request: {2}", requestUrl, e.Message,
1102  obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
1103  throw e;
1104  }
1105  }
1106 
1107  int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
1108  if (tickdiff > WebUtil.LongCallTime)
1109  {
1110  m_log.InfoFormat(
1111  "[FORMS]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
1112  reqnum,
1113  verb,
1114  requestUrl,
1115  tickdiff,
1116  tickset,
1117  tickdata,
1118  obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
1119  }
1120  else if (WebUtil.DebugLevel >= 4)
1121  {
1122  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing",
1123  reqnum, tickdiff, tickdata);
1124  }
1125 
1126  if (WebUtil.DebugLevel >= 5)
1127  WebUtil.LogResponseDetail(reqnum, respstring);
1128 
1129  return respstring;
1130  }
1131 
1132  public static string MakeRequest(string verb, string requestUrl, string obj, int timeoutsecs)
1133  {
1134  return MakeRequest(verb, requestUrl, obj, timeoutsecs, null);
1135  }
1136 
1137  public static string MakeRequest(string verb, string requestUrl, string obj)
1138  {
1139  return MakeRequest(verb, requestUrl, obj, -1);
1140  }
1141 
1142  public static string MakeRequest(string verb, string requestUrl, string obj, IServiceAuth auth)
1143  {
1144  return MakeRequest(verb, requestUrl, obj, -1, auth);
1145  }
1146  }
1147 
1149  {
1150  private static readonly ILog m_log =
1151  LogManager.GetLogger(
1152  MethodBase.GetCurrentMethod().DeclaringType);
1153 
1163  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj)
1164  {
1165  return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, 0);
1166  }
1167 
1168  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, IServiceAuth auth)
1169  {
1170  return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, 0, auth);
1171  }
1185  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout)
1186  {
1187  return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, pTimeout, 0);
1188  }
1189 
1190  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout, IServiceAuth auth)
1191  {
1192  return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, pTimeout, 0, auth);
1193  }
1194 
1208  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections)
1209  {
1210  return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, pTimeout, maxConnections, null);
1211  }
1212 
1227  public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections, IServiceAuth auth)
1228  {
1229  int reqnum = WebUtil.RequestNumber++;
1230 
1231  if (WebUtil.DebugLevel >= 3)
1232  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} SynchronousRestObject {1} to {2}",
1233  reqnum, verb, requestUrl);
1234 
1235  int tickstart = Util.EnvironmentTickCount();
1236  int tickdata = 0;
1237 
1238  Type type = typeof(TRequest);
1239  TResponse deserial = default(TResponse);
1240 
1241  WebRequest request = WebRequest.Create(requestUrl);
1242  HttpWebRequest ht = (HttpWebRequest)request;
1243 
1244  if (auth != null)
1245  auth.AddAuthorization(ht.Headers);
1246 
1247  if (pTimeout != 0)
1248  request.Timeout = pTimeout;
1249 
1250  if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
1251  ht.ServicePoint.ConnectionLimit = maxConnections;
1252 
1253  request.Method = verb;
1254  MemoryStream buffer = null;
1255 
1256  try
1257  {
1258  if ((verb == "POST") || (verb == "PUT"))
1259  {
1260  request.ContentType = "text/xml";
1261 
1262  buffer = new MemoryStream();
1263 
1264  XmlWriterSettings settings = new XmlWriterSettings();
1265  settings.Encoding = Encoding.UTF8;
1266 
1267  using (XmlWriter writer = XmlWriter.Create(buffer, settings))
1268  {
1269  XmlSerializer serializer = new XmlSerializer(type);
1270  serializer.Serialize(writer, obj);
1271  writer.Flush();
1272  }
1273 
1274  int length = (int)buffer.Length;
1275  request.ContentLength = length;
1276  byte[] data = buffer.ToArray();
1277 
1278  if (WebUtil.DebugLevel >= 5)
1279  WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data));
1280 
1281  try
1282  {
1283  using (Stream requestStream = request.GetRequestStream())
1284  requestStream.Write(data, 0, length);
1285  }
1286  catch (Exception e)
1287  {
1288  m_log.DebugFormat(
1289  "[SynchronousRestObjectRequester]: Exception in making request {0} {1}: {2}{3}",
1290  verb, requestUrl, e.Message, e.StackTrace);
1291 
1292  return deserial;
1293  }
1294  finally
1295  {
1296  // capture how much time was spent writing
1297  tickdata = Util.EnvironmentTickCountSubtract(tickstart);
1298  }
1299  }
1300 
1301  try
1302  {
1303  using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
1304  {
1305  if (resp.ContentLength != 0)
1306  {
1307  using (Stream respStream = resp.GetResponseStream())
1308  {
1309  deserial = XMLResponseHelper.LogAndDeserialize<TRequest, TResponse>(
1310  reqnum, respStream, resp.ContentLength);
1311  }
1312  }
1313  else
1314  {
1315  m_log.DebugFormat(
1316  "[SynchronousRestObjectRequester]: Oops! no content found in response stream from {0} {1}",
1317  verb, requestUrl);
1318  }
1319  }
1320  }
1321  catch (WebException e)
1322  {
1323  using (HttpWebResponse hwr = (HttpWebResponse)e.Response)
1324  {
1325  if (hwr != null)
1326  {
1327  if (hwr.StatusCode == HttpStatusCode.NotFound)
1328  return deserial;
1329  if (hwr.StatusCode == HttpStatusCode.Unauthorized)
1330  {
1331  m_log.Error(string.Format(
1332  "[SynchronousRestObjectRequester]: Web request {0} requires authentication ",
1333  requestUrl));
1334  return deserial;
1335  }
1336  }
1337  else
1338  m_log.Error(string.Format(
1339  "[SynchronousRestObjectRequester]: WebException for {0} {1} {2} ",
1340  verb, requestUrl, typeof(TResponse).ToString()), e);
1341  }
1342  }
1343  catch (System.InvalidOperationException)
1344  {
1345  // This is what happens when there is invalid XML
1346  m_log.DebugFormat(
1347  "[SynchronousRestObjectRequester]: Invalid XML from {0} {1} {2}",
1348  verb, requestUrl, typeof(TResponse).ToString());
1349  }
1350  catch (Exception e)
1351  {
1352  m_log.Debug(string.Format(
1353  "[SynchronousRestObjectRequester]: Exception on response from {0} {1} ",
1354  verb, requestUrl), e);
1355  }
1356 
1357  int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
1358  if (tickdiff > WebUtil.LongCallTime)
1359  {
1360  string originalRequest = null;
1361 
1362  if (buffer != null)
1363  {
1364  originalRequest = Encoding.UTF8.GetString(buffer.ToArray());
1365 
1366  if (originalRequest.Length > WebUtil.MaxRequestDiagLength)
1367  originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength);
1368  }
1369 
1370  m_log.InfoFormat(
1371  "[LOGHTTP]: Slow SynchronousRestObject request {0} {1} to {2} took {3}ms, {4}ms writing, {5}",
1372  reqnum, verb, requestUrl, tickdiff, tickdata,
1373  originalRequest);
1374  }
1375  else if (WebUtil.DebugLevel >= 4)
1376  {
1377  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing",
1378  reqnum, tickdiff, tickdata);
1379  }
1380  }
1381  finally
1382  {
1383  if (buffer != null)
1384  buffer.Dispose();
1385  }
1386 
1387  return deserial;
1388  }
1389 
1390  public static class XMLResponseHelper
1391  {
1392  public static TResponse LogAndDeserialize<TRequest, TResponse>(int reqnum, Stream respStream, long contentLength)
1393  {
1394  XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
1395 
1396  if (WebUtil.DebugLevel >= 5)
1397  {
1398  byte[] data = new byte[contentLength];
1399  Util.ReadStream(respStream, data);
1400 
1401  WebUtil.LogResponseDetail(reqnum, System.Text.Encoding.UTF8.GetString(data));
1402 
1403  using (MemoryStream temp = new MemoryStream(data))
1404  return (TResponse)deserializer.Deserialize(temp);
1405  }
1406  else
1407  {
1408  return (TResponse)deserializer.Deserialize(respStream);
1409  }
1410  }
1411  }
1412  }
1413 
1414 
1415  public static class XMLRPCRequester
1416  {
1417  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
1418 
1419  public static Hashtable SendRequest(Hashtable ReqParams, string method, string url)
1420  {
1421  int reqnum = WebUtil.RequestNumber++;
1422 
1423  if (WebUtil.DebugLevel >= 3)
1424  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} XML-RPC '{1}' to {2}",
1425  reqnum, method, url);
1426 
1427  int tickstart = Util.EnvironmentTickCount();
1428  string responseStr = null;
1429 
1430  try
1431  {
1432  ArrayList SendParams = new ArrayList();
1433  SendParams.Add(ReqParams);
1434 
1435  XmlRpcRequest Req = new XmlRpcRequest(method, SendParams);
1436 
1437  if (WebUtil.DebugLevel >= 5)
1438  {
1439  string str = Req.ToString();
1440  str = XElement.Parse(str).ToString(SaveOptions.DisableFormatting);
1441  WebUtil.LogOutgoingDetail("SEND", reqnum, str);
1442  }
1443 
1444  XmlRpcResponse Resp = Req.Send(url, 30000);
1445 
1446  try
1447  {
1448  responseStr = Resp.ToString();
1449  responseStr = XElement.Parse(responseStr).ToString(SaveOptions.DisableFormatting);
1450 
1451  if (WebUtil.DebugLevel >= 5)
1452  WebUtil.LogResponseDetail(reqnum, responseStr);
1453  }
1454  catch (Exception e)
1455  {
1456  m_log.Error("Error parsing XML-RPC response", e);
1457  }
1458 
1459  if (Resp.IsFault)
1460  {
1461  m_log.DebugFormat(
1462  "[LOGHTTP]: XML-RPC request {0} '{1}' to {2} FAILED: FaultCode={3}, FaultMessage={4}",
1463  reqnum, method, url, Resp.FaultCode, Resp.FaultString);
1464  return null;
1465  }
1466 
1467  Hashtable RespData = (Hashtable)Resp.Value;
1468  return RespData;
1469  }
1470  finally
1471  {
1472  int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
1473  if (tickdiff > WebUtil.LongCallTime)
1474  {
1475  m_log.InfoFormat(
1476  "[LOGHTTP]: Slow XML-RPC request {0} '{1}' to {2} took {3}ms, {4}",
1477  reqnum, method, url, tickdiff,
1478  responseStr != null
1479  ? (responseStr.Length > WebUtil.MaxRequestDiagLength ? responseStr.Remove(WebUtil.MaxRequestDiagLength) : responseStr)
1480  : "");
1481  }
1482  else if (WebUtil.DebugLevel >= 4)
1483  {
1484  m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms", reqnum, tickdiff);
1485  }
1486  }
1487  }
1488 
1489  }
1490 }
OpenMetaverse.StructuredData.OSDMap OSDMap
OpenSim.Framework.SynchronousRestObjectRequester.XMLResponseHelper XMLResponseHelper
Definition: WebUtil.cs:46
Ionic.Zlib.GZipStream GZipStream
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
Definition: ICM_Api.cs:31
int Compare(Object x, Object y)
Definition: WebUtil.cs:704
OpenMetaverse.StructuredData.OSD OSD
Ionic.Zlib.CompressionMode CompressionMode