aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs22
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs116
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs6
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs60
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs361
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs2
-rw-r--r--OpenSim/Framework/Servers/Tests/OSHttpTests.cs324
8 files changed, 681 insertions, 214 deletions
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 828a852..3426d10 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -65,6 +65,7 @@ namespace OpenSim.Framework.Servers
65 /// This will control a periodic log printout of the current 'show stats' (if they are active) for this 65 /// This will control a periodic log printout of the current 'show stats' (if they are active) for this
66 /// server. 66 /// server.
67 /// </summary> 67 /// </summary>
68
68 private int m_periodDiagnosticTimerMS = 60 * 60 * 1000; 69 private int m_periodDiagnosticTimerMS = 60 * 60 * 1000;
69 private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); 70 private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000);
70 71
@@ -83,7 +84,6 @@ namespace OpenSim.Framework.Servers
83 { 84 {
84 // Random uuid for private data 85 // Random uuid for private data
85 m_osSecret = UUID.Random().ToString(); 86 m_osSecret = UUID.Random().ToString();
86
87 } 87 }
88 88
89 /// <summary> 89 /// <summary>
@@ -146,14 +146,24 @@ namespace OpenSim.Framework.Servers
146 /// Performs initialisation of the scene, such as loading configuration from disk. 146 /// Performs initialisation of the scene, such as loading configuration from disk.
147 /// </summary> 147 /// </summary>
148 public virtual void Startup() 148 public virtual void Startup()
149 { 149 {
150 m_log.Info("[STARTUP]: Beginning startup processing");
151
152 m_log.Info("[STARTUP]: version: " + m_version + Environment.NewLine);
153 // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and
154 // the clr version number doesn't match the project version number under Mono.
155 //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine);
156 m_log.InfoFormat(
157 "[STARTUP]: Operating system version: {0}, .NET platform {1}, {2}-bit\n",
158 Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32");
159
150 StartupSpecific(); 160 StartupSpecific();
151 161
152 TimeSpan timeTaken = DateTime.Now - m_startuptime; 162 TimeSpan timeTaken = DateTime.Now - m_startuptime;
153 163
154 MainConsole.Instance.OutputFormat( 164// MainConsole.Instance.OutputFormat(
155 "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.", 165// "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.",
156 timeTaken.Minutes, timeTaken.Seconds); 166// timeTaken.Minutes, timeTaken.Seconds);
157 } 167 }
158 168
159 public string osSecret 169 public string osSecret
@@ -175,4 +185,4 @@ namespace OpenSim.Framework.Servers
175 } 185 }
176 } 186 }
177 } 187 }
178} \ No newline at end of file 188}
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index f252bd5..cd14212 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -403,6 +403,7 @@ namespace OpenSim.Framework.Servers.HttpServer
403 StreamReader reader = new StreamReader(requestStream, encoding); 403 StreamReader reader = new StreamReader(requestStream, encoding);
404 404
405 string requestBody = reader.ReadToEnd(); 405 string requestBody = reader.ReadToEnd();
406 reader.Close();
406 407
407 Hashtable keysvals = new Hashtable(); 408 Hashtable keysvals = new Hashtable();
408 Hashtable headervals = new Hashtable(); 409 Hashtable headervals = new Hashtable();
@@ -460,7 +461,7 @@ namespace OpenSim.Framework.Servers.HttpServer
460 } 461 }
461 462
462 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); 463 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
463 resp.ReuseContext = true; 464 resp.ReuseContext = false;
464 HandleRequest(req, resp); 465 HandleRequest(req, resp);
465 466
466 // !!!HACK ALERT!!! 467 // !!!HACK ALERT!!!
@@ -759,7 +760,7 @@ namespace OpenSim.Framework.Servers.HttpServer
759 // Every month or so this will wrap and give bad numbers, not really a problem 760 // Every month or so this will wrap and give bad numbers, not really a problem
760 // since its just for reporting 761 // since its just for reporting
761 int tickdiff = requestEndTick - requestStartTick; 762 int tickdiff = requestEndTick - requestStartTick;
762 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture") 763 if (tickdiff > 3000 && (requestHandler == null || requestHandler.Name == null || requestHandler.Name != "GetTexture"))
763 { 764 {
764 m_log.InfoFormat( 765 m_log.InfoFormat(
765 "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", 766 "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
@@ -1024,6 +1025,19 @@ namespace OpenSim.Framework.Servers.HttpServer
1024 string responseString = String.Empty; 1025 string responseString = String.Empty;
1025 XmlRpcRequest xmlRprcRequest = null; 1026 XmlRpcRequest xmlRprcRequest = null;
1026 1027
1028 bool gridproxy = false;
1029 if (requestBody.Contains("encoding=\"utf-8"))
1030 {
1031 int channelindx = -1;
1032 int optionsindx = requestBody.IndexOf(">options<");
1033 if(optionsindx >0)
1034 {
1035 channelindx = requestBody.IndexOf(">channel<");
1036 if (optionsindx < channelindx)
1037 gridproxy = true;
1038 }
1039 }
1040
1027 try 1041 try
1028 { 1042 {
1029 xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody); 1043 xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody);
@@ -1081,6 +1095,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1081 } 1095 }
1082 xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3] 1096 xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3]
1083 1097
1098 if (gridproxy)
1099 xmlRprcRequest.Params.Add("gridproxy"); // Param[4]
1084 try 1100 try
1085 { 1101 {
1086 xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint); 1102 xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint);
@@ -1254,7 +1270,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1254 requestStream.Close(); 1270 requestStream.Close();
1255 1271
1256 //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody); 1272 //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody);
1257 response.KeepAlive = true; 1273 // response.KeepAlive = true;
1274 response.KeepAlive = false;
1258 1275
1259 OSD llsdRequest = null; 1276 OSD llsdRequest = null;
1260 OSD llsdResponse = null; 1277 OSD llsdResponse = null;
@@ -1732,10 +1749,40 @@ namespace OpenSim.Framework.Servers.HttpServer
1732 1749
1733 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) 1750 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
1734 { 1751 {
1735 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); 1752 int responsecode;
1736 int responsecode = (int)responsedata["int_response_code"]; 1753 string responseString = String.Empty;
1737 string responseString = (string)responsedata["str_response_string"]; 1754 byte[] responseData = null;
1738 string contentType = (string)responsedata["content_type"]; 1755 string contentType;
1756
1757 if (responsedata == null)
1758 {
1759 responsecode = 500;
1760 responseString = "No response could be obtained";
1761 contentType = "text/plain";
1762 responsedata = new Hashtable();
1763 }
1764 else
1765 {
1766 try
1767 {
1768 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
1769 responsecode = (int)responsedata["int_response_code"];
1770 if (responsedata["bin_response_data"] != null)
1771 responseData = (byte[])responsedata["bin_response_data"];
1772 else
1773 responseString = (string)responsedata["str_response_string"];
1774 contentType = (string)responsedata["content_type"];
1775 if (responseString == null)
1776 responseString = String.Empty;
1777 }
1778 catch
1779 {
1780 responsecode = 500;
1781 responseString = "No response could be obtained";
1782 contentType = "text/plain";
1783 responsedata = new Hashtable();
1784 }
1785 }
1739 1786
1740 if (responsedata.ContainsKey("error_status_text")) 1787 if (responsedata.ContainsKey("error_status_text"))
1741 { 1788 {
@@ -1745,16 +1792,19 @@ namespace OpenSim.Framework.Servers.HttpServer
1745 { 1792 {
1746 response.ProtocolVersion = (string)responsedata["http_protocol_version"]; 1793 response.ProtocolVersion = (string)responsedata["http_protocol_version"];
1747 } 1794 }
1748 1795/*
1749 if (responsedata.ContainsKey("keepalive")) 1796 if (responsedata.ContainsKey("keepalive"))
1750 { 1797 {
1751 bool keepalive = (bool)responsedata["keepalive"]; 1798 bool keepalive = (bool)responsedata["keepalive"];
1752 response.KeepAlive = keepalive; 1799 response.KeepAlive = keepalive;
1753
1754 } 1800 }
1755 1801
1756 if (responsedata.ContainsKey("reusecontext")) 1802 if (responsedata.ContainsKey("reusecontext"))
1757 response.ReuseContext = (bool) responsedata["reusecontext"]; 1803 response.ReuseContext = (bool) responsedata["reusecontext"];
1804*/
1805 // disable this things
1806 response.KeepAlive = false;
1807 response.ReuseContext = false;
1758 1808
1759 // Cross-Origin Resource Sharing with simple requests 1809 // Cross-Origin Resource Sharing with simple requests
1760 if (responsedata.ContainsKey("access_control_allow_origin")) 1810 if (responsedata.ContainsKey("access_control_allow_origin"))
@@ -1768,8 +1818,11 @@ namespace OpenSim.Framework.Servers.HttpServer
1768 contentType = "text/html"; 1818 contentType = "text/html";
1769 } 1819 }
1770 1820
1821
1822
1771 // The client ignores anything but 200 here for web login, so ensure that this is 200 for that 1823 // The client ignores anything but 200 here for web login, so ensure that this is 200 for that
1772 1824
1825
1773 response.StatusCode = responsecode; 1826 response.StatusCode = responsecode;
1774 1827
1775 if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently) 1828 if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
@@ -1780,25 +1833,40 @@ namespace OpenSim.Framework.Servers.HttpServer
1780 1833
1781 response.AddHeader("Content-Type", contentType); 1834 response.AddHeader("Content-Type", contentType);
1782 1835
1836 if (responsedata.ContainsKey("headers"))
1837 {
1838 Hashtable headerdata = (Hashtable)responsedata["headers"];
1839
1840 foreach (string header in headerdata.Keys)
1841 response.AddHeader(header, (string)headerdata[header]);
1842 }
1843
1783 byte[] buffer; 1844 byte[] buffer;
1784 1845
1785 if (!(contentType.Contains("image") 1846 if (responseData != null)
1786 || contentType.Contains("x-shockwave-flash")
1787 || contentType.Contains("application/x-oar")
1788 || contentType.Contains("application/vnd.ll.mesh")))
1789 { 1847 {
1790 // Text 1848 buffer = responseData;
1791 buffer = Encoding.UTF8.GetBytes(responseString);
1792 } 1849 }
1793 else 1850 else
1794 { 1851 {
1795 // Binary! 1852 if (!(contentType.Contains("image")
1796 buffer = Convert.FromBase64String(responseString); 1853 || contentType.Contains("x-shockwave-flash")
1797 } 1854 || contentType.Contains("application/x-oar")
1855 || contentType.Contains("application/vnd.ll.mesh")))
1856 {
1857 // Text
1858 buffer = Encoding.UTF8.GetBytes(responseString);
1859 }
1860 else
1861 {
1862 // Binary!
1863 buffer = Convert.FromBase64String(responseString);
1864 }
1798 1865
1799 response.SendChunked = false; 1866 response.SendChunked = false;
1800 response.ContentLength64 = buffer.Length; 1867 response.ContentLength64 = buffer.Length;
1801 response.ContentEncoding = Encoding.UTF8; 1868 response.ContentEncoding = Encoding.UTF8;
1869 }
1802 1870
1803 return buffer; 1871 return buffer;
1804 } 1872 }
@@ -1886,6 +1954,7 @@ namespace OpenSim.Framework.Servers.HttpServer
1886 m_httpListener2.Start(64); 1954 m_httpListener2.Start(64);
1887 1955
1888 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events 1956 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
1957
1889 PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000); 1958 PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000);
1890 PollServiceRequestManager.Start(); 1959 PollServiceRequestManager.Start();
1891 1960
@@ -1937,7 +2006,9 @@ namespace OpenSim.Framework.Servers.HttpServer
1937 2006
1938 public void httpServerException(object source, Exception exception) 2007 public void httpServerException(object source, Exception exception)
1939 { 2008 {
1940 m_log.Error(String.Format("[BASE HTTP SERVER]: {0} had an exception: {1} ", source.ToString(), exception.Message), exception); 2009 if (source.ToString() == "HttpServer.HttpListener" && exception.ToString().StartsWith("Mono.Security.Protocol.Tls.TlsException"))
2010 return;
2011 m_log.ErrorFormat("[BASE HTTP SERVER]: {0} had an exception {1}", source.ToString(), exception.ToString());
1941 /* 2012 /*
1942 if (HTTPDRunning)// && NotSocketErrors > 5) 2013 if (HTTPDRunning)// && NotSocketErrors > 5)
1943 { 2014 {
@@ -1984,6 +2055,7 @@ namespace OpenSim.Framework.Servers.HttpServer
1984 2055
1985 public void RemoveHTTPHandler(string httpMethod, string path) 2056 public void RemoveHTTPHandler(string httpMethod, string path)
1986 { 2057 {
2058 if (path == null) return; // Caps module isn't loaded, tries to remove handler where path = null
1987 lock (m_HTTPHandlers) 2059 lock (m_HTTPHandlers)
1988 { 2060 {
1989 if (httpMethod != null && httpMethod.Length == 0) 2061 if (httpMethod != null && httpMethod.Length == 0)
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
index 89fb5d4..17e9dc2 100644
--- a/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
@@ -148,6 +148,7 @@ namespace OpenSim.Framework.Servers.HttpServer
148 _httpResponse.Connection = ConnectionType.Close; 148 _httpResponse.Connection = ConnectionType.Close;
149 _httpResponse.KeepAlive = 0; 149 _httpResponse.KeepAlive = 0;
150 } 150 }
151
151 else 152 else
152 { 153 {
153 _httpResponse.Connection = ConnectionType.KeepAlive; 154 _httpResponse.Connection = ConnectionType.KeepAlive;
@@ -320,6 +321,11 @@ namespace OpenSim.Framework.Servers.HttpServer
320 public void Send() 321 public void Send()
321 { 322 {
322 _httpResponse.Body.Flush(); 323 _httpResponse.Body.Flush();
324
325 // disable this till they are safe to use
326 _httpResponse.Connection = ConnectionType.Close;
327 _httpResponse.Chunked = false;
328
323 _httpResponse.Send(); 329 _httpResponse.Send();
324 } 330 }
325 331
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
index 9477100..3fd3bf7 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
@@ -52,7 +52,9 @@ namespace OpenSim.Framework.Servers.HttpServer
52 { 52 {
53 LongPoll = 0, 53 LongPoll = 0,
54 LslHttp = 1, 54 LslHttp = 1,
55 Inventory = 2 55 Inventory = 2,
56 Texture = 3,
57 Mesh = 4
56 } 58 }
57 59
58 public string Url { get; set; } 60 public string Url { get; set; }
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
index caf0e98..49cd110 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using System.Reflection; 31using System.Reflection;
31using System.Text; 32using System.Text;
32using HttpServer; 33using HttpServer;
@@ -44,6 +45,24 @@ namespace OpenSim.Framework.Servers.HttpServer
44 public readonly IHttpRequest Request; 45 public readonly IHttpRequest Request;
45 public readonly int RequestTime; 46 public readonly int RequestTime;
46 public readonly UUID RequestID; 47 public readonly UUID RequestID;
48 public int contextHash;
49
50 private void GenContextHash()
51 {
52 Random rnd = new Random();
53 contextHash = 0;
54 if (Request.Headers["remote_addr"] != null)
55 contextHash = (Request.Headers["remote_addr"]).GetHashCode() << 16;
56 else
57 contextHash = rnd.Next() << 16;
58 if (Request.Headers["remote_port"] != null)
59 {
60 string[] strPorts = Request.Headers["remote_port"].Split(new char[] { ',' });
61 contextHash += Int32.Parse(strPorts[0]);
62 }
63 else
64 contextHash += rnd.Next() & 0xffff;
65 }
47 66
48 public PollServiceHttpRequest( 67 public PollServiceHttpRequest(
49 PollServiceEventArgs pPollServiceArgs, IHttpClientContext pHttpContext, IHttpRequest pRequest) 68 PollServiceEventArgs pPollServiceArgs, IHttpClientContext pHttpContext, IHttpRequest pRequest)
@@ -53,6 +72,7 @@ namespace OpenSim.Framework.Servers.HttpServer
53 Request = pRequest; 72 Request = pRequest;
54 RequestTime = System.Environment.TickCount; 73 RequestTime = System.Environment.TickCount;
55 RequestID = UUID.Random(); 74 RequestID = UUID.Random();
75 GenContextHash();
56 } 76 }
57 77
58 internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata) 78 internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata)
@@ -65,6 +85,7 @@ namespace OpenSim.Framework.Servers.HttpServer
65 response.SendChunked = false; 85 response.SendChunked = false;
66 response.ContentLength64 = buffer.Length; 86 response.ContentLength64 = buffer.Length;
67 response.ContentEncoding = Encoding.UTF8; 87 response.ContentEncoding = Encoding.UTF8;
88 response.ReuseContext = false;
68 89
69 try 90 try
70 { 91 {
@@ -93,5 +114,44 @@ namespace OpenSim.Framework.Servers.HttpServer
93 PollServiceArgs.RequestsHandled++; 114 PollServiceArgs.RequestsHandled++;
94 } 115 }
95 } 116 }
117
118 internal void DoHTTPstop(BaseHttpServer server)
119 {
120 OSHttpResponse response
121 = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext);
122
123 response.SendChunked = false;
124 response.ContentLength64 = 0;
125 response.ContentEncoding = Encoding.UTF8;
126 response.ReuseContext = false;
127 response.KeepAlive = false;
128 response.SendChunked = false;
129 response.StatusCode = 503;
130
131 try
132 {
133 response.OutputStream.Flush();
134 response.Send();
135 }
136 catch (Exception e)
137 {
138 }
139 }
140 }
141
142 class PollServiceHttpRequestComparer : IEqualityComparer<PollServiceHttpRequest>
143 {
144 public bool Equals(PollServiceHttpRequest b1, PollServiceHttpRequest b2)
145 {
146 if (b1.contextHash != b2.contextHash)
147 return false;
148 bool b = Object.ReferenceEquals(b1.HttpContext, b2.HttpContext);
149 return b;
150 }
151
152 public int GetHashCode(PollServiceHttpRequest b2)
153 {
154 return (int)b2.contextHash;
155 }
96 } 156 }
97} \ No newline at end of file 157} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 28bba70..0e4323a 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -44,183 +44,199 @@ namespace OpenSim.Framework.Servers.HttpServer
44 { 44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 46
47 /// <summary>
48 /// Is the poll service request manager running?
49 /// </summary>
50 /// <remarks>
51 /// Can be running either synchronously or asynchronously
52 /// </remarks>
53 public bool IsRunning { get; private set; }
54
55 /// <summary>
56 /// Is the poll service performing responses asynchronously (with its own threads) or synchronously (via
57 /// external calls)?
58 /// </summary>
59 public bool PerformResponsesAsync { get; private set; }
60
61 /// <summary>
62 /// Number of responses actually processed and sent to viewer (or aborted due to error).
63 /// </summary>
64 public int ResponsesProcessed { get; private set; }
65
66 private readonly BaseHttpServer m_server; 47 private readonly BaseHttpServer m_server;
67 48
49 private Dictionary<PollServiceHttpRequest, Queue<PollServiceHttpRequest>> m_bycontext;
68 private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>(); 50 private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>();
69 private static List<PollServiceHttpRequest> m_longPollRequests = new List<PollServiceHttpRequest>(); 51 private static Queue<PollServiceHttpRequest> m_slowRequests = new Queue<PollServiceHttpRequest>();
52 private static Queue<PollServiceHttpRequest> m_retryRequests = new Queue<PollServiceHttpRequest>();
70 53
71 private uint m_WorkerThreadCount = 0; 54 private uint m_WorkerThreadCount = 0;
72 private Thread[] m_workerThreads; 55 private Thread[] m_workerThreads;
56 private Thread m_retrysThread;
57
58 private bool m_running = false;
59 private int slowCount = 0;
73 60
74 private SmartThreadPool m_threadPool = new SmartThreadPool(20000, 12, 2); 61 private SmartThreadPool m_threadPool;
75 62
76// private int m_timeout = 1000; // increase timeout 250; now use the event one
77 63
78 public PollServiceRequestManager( 64 public PollServiceRequestManager(
79 BaseHttpServer pSrv, bool performResponsesAsync, uint pWorkerThreadCount, int pTimeout) 65 BaseHttpServer pSrv, bool performResponsesAsync, uint pWorkerThreadCount, int pTimeout)
80 { 66 {
81 m_server = pSrv; 67 m_server = pSrv;
82 PerformResponsesAsync = performResponsesAsync;
83 m_WorkerThreadCount = pWorkerThreadCount; 68 m_WorkerThreadCount = pWorkerThreadCount;
84 m_workerThreads = new Thread[m_WorkerThreadCount]; 69 m_workerThreads = new Thread[m_WorkerThreadCount];
85 70
86 StatsManager.RegisterStat( 71 PollServiceHttpRequestComparer preqCp = new PollServiceHttpRequestComparer();
87 new Stat( 72 m_bycontext = new Dictionary<PollServiceHttpRequest, Queue<PollServiceHttpRequest>>(preqCp);
88 "QueuedPollResponses", 73
89 "Number of poll responses queued for processing.", 74 STPStartInfo startInfo = new STPStartInfo();
90 "", 75 startInfo.IdleTimeout = 30000;
91 "", 76 startInfo.MaxWorkerThreads = 15;
92 "httpserver", 77 startInfo.MinWorkerThreads = 1;
93 m_server.Port.ToString(), 78 startInfo.ThreadPriority = ThreadPriority.Normal;
94 StatType.Pull, 79 startInfo.StartSuspended = true;
95 MeasuresOfInterest.AverageChangeOverTime, 80 startInfo.ThreadPoolName = "PoolService";
96 stat => stat.Value = m_requests.Count(), 81
97 StatVerbosity.Debug)); 82 m_threadPool = new SmartThreadPool(startInfo);
98 83
99 StatsManager.RegisterStat(
100 new Stat(
101 "ProcessedPollResponses",
102 "Number of poll responses processed.",
103 "",
104 "",
105 "httpserver",
106 m_server.Port.ToString(),
107 StatType.Pull,
108 MeasuresOfInterest.AverageChangeOverTime,
109 stat => stat.Value = ResponsesProcessed,
110 StatVerbosity.Debug));
111 } 84 }
112 85
113 public void Start() 86 public void Start()
114 { 87 {
115 IsRunning = true; 88 m_running = true;
116 89 m_threadPool.Start();
117 if (PerformResponsesAsync) 90 //startup worker threads
91 for (uint i = 0; i < m_WorkerThreadCount; i++)
118 { 92 {
119 //startup worker threads 93 m_workerThreads[i]
120 for (uint i = 0; i < m_WorkerThreadCount; i++) 94 = WorkManager.StartThread(
121 { 95 PoolWorkerJob,
122 m_workerThreads[i] 96 string.Format("PollServiceWorkerThread {0}:{1}", i, m_server.Port),
123 = WorkManager.StartThread( 97 ThreadPriority.Normal,
124 PoolWorkerJob, 98 false,
125 string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port), 99 false,
126 ThreadPriority.Normal, 100 null,
127 false, 101 int.MaxValue);
128 false,
129 null,
130 int.MaxValue);
131 }
132
133 WorkManager.StartThread(
134 this.CheckLongPollThreads,
135 string.Format("LongPollServiceWatcherThread:{0}", m_server.Port),
136 ThreadPriority.Normal,
137 false,
138 true,
139 null,
140 1000 * 60 * 10);
141 } 102 }
103
104 m_retrysThread = WorkManager.StartThread(
105 this.CheckRetries,
106 string.Format("PollServiceWatcherThread:{0}", m_server.Port),
107 ThreadPriority.Normal,
108 false,
109 true,
110 null,
111 1000 * 60 * 10);
112
113
142 } 114 }
143 115
144 private void ReQueueEvent(PollServiceHttpRequest req) 116 private void ReQueueEvent(PollServiceHttpRequest req)
145 { 117 {
146 if (IsRunning) 118 if (m_running)
147 { 119 {
148 // delay the enqueueing for 100ms. There's no need to have the event 120 lock (m_retryRequests)
149 // actively on the queue 121 m_retryRequests.Enqueue(req);
150 Timer t = new Timer(self => {
151 ((Timer)self).Dispose();
152 m_requests.Enqueue(req);
153 });
154
155 t.Change(100, Timeout.Infinite);
156
157 } 122 }
158 } 123 }
159 124
160 public void Enqueue(PollServiceHttpRequest req) 125 public void Enqueue(PollServiceHttpRequest req)
161 { 126 {
162 if (IsRunning) 127 lock (m_bycontext)
163 { 128 {
164 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) 129 Queue<PollServiceHttpRequest> ctxQeueue;
130 if (m_bycontext.TryGetValue(req, out ctxQeueue))
165 { 131 {
166 lock (m_longPollRequests) 132 ctxQeueue.Enqueue(req);
167 m_longPollRequests.Add(req);
168 } 133 }
169 else 134 else
170 m_requests.Enqueue(req); 135 {
136 ctxQeueue = new Queue<PollServiceHttpRequest>();
137 m_bycontext[req] = ctxQeueue;
138 EnqueueInt(req);
139 }
171 } 140 }
172 } 141 }
173 142
174 private void CheckLongPollThreads() 143 public void byContextDequeue(PollServiceHttpRequest req)
175 { 144 {
176 // The only purpose of this thread is to check the EQs for events. 145 Queue<PollServiceHttpRequest> ctxQeueue;
177 // If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests. 146 lock (m_bycontext)
178 // If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests.
179 // All other types of tasks (Inventory handlers, http-in, etc) don't have the long-poll nature,
180 // so if they aren't ready to be served by a worker thread (no events), they are placed
181 // directly back in the "ready-to-serve" queue by the worker thread.
182 while (IsRunning)
183 { 147 {
184 Thread.Sleep(500); 148 if (m_bycontext.TryGetValue(req, out ctxQeueue))
185 Watchdog.UpdateThread();
186
187// List<PollServiceHttpRequest> not_ready = new List<PollServiceHttpRequest>();
188 lock (m_longPollRequests)
189 { 149 {
190 if (m_longPollRequests.Count > 0 && IsRunning) 150 if (ctxQeueue.Count > 0)
191 { 151 {
192 List<PollServiceHttpRequest> ready = m_longPollRequests.FindAll(req => 152 PollServiceHttpRequest newreq = ctxQeueue.Dequeue();
193 (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ 153 EnqueueInt(newreq);
194 (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout 154 }
195 ); 155 else
156 {
157 m_bycontext.Remove(req);
158 }
159 }
160 }
161 }
196 162
197 ready.ForEach(req =>
198 {
199 m_requests.Enqueue(req);
200 m_longPollRequests.Remove(req);
201 });
202 163
203 } 164 public void EnqueueInt(PollServiceHttpRequest req)
165 {
166 if (m_running)
167 {
168 if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.LongPoll)
169 {
170 m_requests.Enqueue(req);
171 }
172 else
173 {
174 lock (m_slowRequests)
175 m_slowRequests.Enqueue(req);
176 }
177 }
178 }
179
180 private void CheckRetries()
181 {
182 while (m_running)
183
184 {
185 Thread.Sleep(100); // let the world move .. back to faster rate
186 Watchdog.UpdateThread();
187 lock (m_retryRequests)
188 {
189 while (m_retryRequests.Count > 0 && m_running)
190 m_requests.Enqueue(m_retryRequests.Dequeue());
191 }
192 slowCount++;
193 if (slowCount >= 10)
194 {
195 slowCount = 0;
204 196
197 lock (m_slowRequests)
198 {
199 while (m_slowRequests.Count > 0 && m_running)
200 m_requests.Enqueue(m_slowRequests.Dequeue());
201 }
205 } 202 }
206 } 203 }
207 } 204 }
208 205
209 public void Stop() 206 public void Stop()
210 { 207 {
211 IsRunning = false; 208 m_running = false;
212// m_timeout = -10000; // cause all to expire 209
213 Thread.Sleep(1000); // let the world move 210 Thread.Sleep(1000); // let the world move
214 211
215 foreach (Thread t in m_workerThreads) 212 foreach (Thread t in m_workerThreads)
216 Watchdog.AbortThread(t.ManagedThreadId); 213 Watchdog.AbortThread(t.ManagedThreadId);
217 214
215 // any entry in m_bycontext should have a active request on the other queues
216 // so just delete contents to easy GC
217 foreach (Queue<PollServiceHttpRequest> qu in m_bycontext.Values)
218 qu.Clear();
219 m_bycontext.Clear();
220
221 try
222 {
223 foreach (PollServiceHttpRequest req in m_retryRequests)
224 {
225 req.DoHTTPstop(m_server);
226 }
227 }
228 catch
229 {
230 }
231
218 PollServiceHttpRequest wreq; 232 PollServiceHttpRequest wreq;
233 m_retryRequests.Clear();
219 234
220 lock (m_longPollRequests) 235 lock (m_slowRequests)
221 { 236 {
222 if (m_longPollRequests.Count > 0 && IsRunning) 237 while (m_slowRequests.Count > 0)
223 m_longPollRequests.ForEach(req => m_requests.Enqueue(req)); 238 m_requests.Enqueue(m_slowRequests.Dequeue());
239
224 } 240 }
225 241
226 while (m_requests.Count() > 0) 242 while (m_requests.Count() > 0)
@@ -228,16 +244,14 @@ namespace OpenSim.Framework.Servers.HttpServer
228 try 244 try
229 { 245 {
230 wreq = m_requests.Dequeue(0); 246 wreq = m_requests.Dequeue(0);
231 ResponsesProcessed++; 247 wreq.DoHTTPstop(m_server);
232 wreq.DoHTTPGruntWork( 248
233 m_server, wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id));
234 } 249 }
235 catch 250 catch
236 { 251 {
237 } 252 }
238 } 253 }
239 254
240 m_longPollRequests.Clear();
241 m_requests.Clear(); 255 m_requests.Clear();
242 } 256 }
243 257
@@ -245,87 +259,70 @@ namespace OpenSim.Framework.Servers.HttpServer
245 259
246 private void PoolWorkerJob() 260 private void PoolWorkerJob()
247 { 261 {
248 while (IsRunning) 262 while (m_running)
249 { 263 {
250 Watchdog.UpdateThread(); 264 PollServiceHttpRequest req = m_requests.Dequeue(5000);
251 WaitPerformResponse();
252 }
253 }
254
255 public void WaitPerformResponse()
256 {
257 PollServiceHttpRequest req = m_requests.Dequeue(5000);
258// m_log.DebugFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString()));
259 265
260 if (req != null) 266 Watchdog.UpdateThread();
261 { 267 if (req != null)
262 try
263 { 268 {
264 if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id)) 269 try
265 { 270 {
266 Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id); 271 if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id))
267
268 if (responsedata == null)
269 return;
270
271 // This is the event queue.
272 // Even if we're not running we can still perform responses by explicit request.
273 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll
274 || !PerformResponsesAsync)
275 { 272 {
276 try 273 Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id);
277 { 274
278 ResponsesProcessed++; 275 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) // This is the event queue
279 req.DoHTTPGruntWork(m_server, responsedata);
280 }
281 catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream
282 {
283 // Ignore it, no need to reply
284 m_log.Error(e);
285 }
286 }
287 else
288 {
289 m_threadPool.QueueWorkItem(x =>
290 { 276 {
291 try 277 try
292 { 278 {
293 ResponsesProcessed++;
294 req.DoHTTPGruntWork(m_server, responsedata); 279 req.DoHTTPGruntWork(m_server, responsedata);
280 byContextDequeue(req);
295 } 281 }
296 catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream 282 catch (ObjectDisposedException) // Browser aborted before we could read body, server closed the stream
297 { 283 {
298 // Ignore it, no need to reply 284 // Ignore it, no need to reply
299 m_log.Error(e);
300 } 285 }
301 catch (Exception e) 286 }
287 else
288 {
289 m_threadPool.QueueWorkItem(x =>
302 { 290 {
303 m_log.Error(e); 291 try
304 } 292 {
305 293 req.DoHTTPGruntWork(m_server, responsedata);
306 return null; 294 byContextDequeue(req);
307 }, null); 295 }
308 } 296 catch (ObjectDisposedException) // Browser aborted before we could read body, server closed the stream
309 } 297 {
310 else 298 // Ignore it, no need to reply
311 { 299 }
312 if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) 300
313 { 301 return null;
314 ResponsesProcessed++; 302 }, null);
315 req.DoHTTPGruntWork( 303 }
316 m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
317 } 304 }
318 else 305 else
319 { 306 {
320 ReQueueEvent(req); 307 if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms)
308 {
309 req.DoHTTPGruntWork(m_server,
310 req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
311 byContextDequeue(req);
312 }
313 else
314 {
315 ReQueueEvent(req);
316 }
321 } 317 }
322 } 318 }
323 } 319 catch (Exception e)
324 catch (Exception e) 320 {
325 { 321 m_log.ErrorFormat("Exception in poll service thread: " + e.ToString());
326 m_log.ErrorFormat("Exception in poll service thread: " + e.ToString()); 322 }
327 } 323 }
328 } 324 }
329 } 325 }
326
330 } 327 }
331} \ No newline at end of file 328}
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index 07a09e6..b330384 100644
--- a/OpenSim/Framework/Servers/ServerBase.cs
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -871,7 +871,7 @@ namespace OpenSim.Framework.Servers
871 } 871 }
872 } 872 }
873 873
874 protected string GetVersionText() 874 public string GetVersionText()
875 { 875 {
876 return String.Format("Version: {0} (SIMULATION/{1} - SIMULATION/{2})", 876 return String.Format("Version: {0} (SIMULATION/{1} - SIMULATION/{2})",
877 m_version, VersionInfo.SimulationServiceVersionSupportedMin, VersionInfo.SimulationServiceVersionSupportedMax); 877 m_version, VersionInfo.SimulationServiceVersionSupportedMin, VersionInfo.SimulationServiceVersionSupportedMax);
diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
index 5c0e0df..1b47cc6 100644
--- a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
+++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
@@ -41,7 +41,327 @@ namespace OpenSim.Framework.Servers.Tests
41{ 41{
42 [TestFixture] 42 [TestFixture]
43 public class OSHttpTests : OpenSimTestCase 43 public class OSHttpTests : OpenSimTestCase
44 { 44 {
45 // we need an IHttpClientContext for our tests
46 public class TestHttpClientContext: IHttpClientContext
47 {
48 private bool _secured;
49 public bool IsSecured
50 {
51 get { return _secured; }
52 }
53 public bool Secured
54 {
55 get { return _secured; }
56 }
57
58 public TestHttpClientContext(bool secured)
59 {
60 _secured = secured;
61 }
62
63 public void Disconnect(SocketError error) {}
64 public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body) {}
65 public void Respond(string httpVersion, HttpStatusCode statusCode, string reason) {}
66 public void Respond(string body) {}
67 public void Send(byte[] buffer) {}
68 public void Send(byte[] buffer, int offset, int size) {}
69 public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) {}
70 public void Close() { }
71 public bool EndWhenDone { get { return false;} set { return;}}
72
73 public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
74 {
75 return new HTTPNetworkContext();
76 }
77
78 public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
79 /// <summary>
80 /// A request have been received in the context.
81 /// </summary>
82 public event EventHandler<RequestEventArgs> RequestReceived = delegate { };
83
84 public bool CanSend { get { return true; } }
85 public string RemoteEndPoint { get { return ""; } }
86 public string RemoteEndPointAddress { get { return ""; } }
87 public string RemoteEndPointPort { get { return ""; } }
88 }
89
90 public class TestHttpRequest: IHttpRequest
91 {
92 private string _uriPath;
93 public bool BodyIsComplete
94 {
95 get { return true; }
96 }
97 public string[] AcceptTypes
98 {
99 get {return _acceptTypes; }
100 }
101 private string[] _acceptTypes;
102 public Stream Body
103 {
104 get { return _body; }
105 set { _body = value;}
106 }
107 private Stream _body;
108 public ConnectionType Connection
109 {
110 get { return _connection; }
111 set { _connection = value; }
112 }
113 private ConnectionType _connection;
114 public int ContentLength
115 {
116 get { return _contentLength; }
117 set { _contentLength = value; }
118 }
119 private int _contentLength;
120 public NameValueCollection Headers
121 {
122 get { return _headers; }
123 }
124 private NameValueCollection _headers = new NameValueCollection();
125 public string HttpVersion
126 {
127 get { return _httpVersion; }
128 set { _httpVersion = value; }
129 }
130 private string _httpVersion = null;
131 public string Method
132 {
133 get { return _method; }
134 set { _method = value; }
135 }
136 private string _method = null;
137 public HttpInput QueryString
138 {
139 get { return _queryString; }
140 }
141 private HttpInput _queryString = null;
142 public Uri Uri
143 {
144 get { return _uri; }
145 set { _uri = value; }
146 }
147 private Uri _uri = null;
148 public string[] UriParts
149 {
150 get { return _uri.Segments; }
151 }
152 public HttpParam Param
153 {
154 get { return null; }
155 }
156 public HttpForm Form
157 {
158 get { return null; }
159 }
160 public bool IsAjax
161 {
162 get { return false; }
163 }
164 public RequestCookies Cookies
165 {
166 get { return null; }
167 }
168
169 public TestHttpRequest() {}
170
171 public TestHttpRequest(string contentEncoding, string contentType, string userAgent,
172 string remoteAddr, string remotePort, string[] acceptTypes,
173 ConnectionType connectionType, int contentLength, Uri uri)
174 {
175 _headers["content-encoding"] = contentEncoding;
176 _headers["content-type"] = contentType;
177 _headers["user-agent"] = userAgent;
178 _headers["remote_addr"] = remoteAddr;
179 _headers["remote_port"] = remotePort;
180
181 _acceptTypes = acceptTypes;
182 _connection = connectionType;
183 _contentLength = contentLength;
184 _uri = uri;
185 }
186
187 public void DecodeBody(FormDecoderProvider providers) {}
188 public void SetCookies(RequestCookies cookies) {}
189 public void AddHeader(string name, string value)
190 {
191 _headers.Add(name, value);
192 }
193 public int AddToBody(byte[] bytes, int offset, int length)
194 {
195 return 0;
196 }
197 public void Clear() {}
198
199 public object Clone()
200 {
201 TestHttpRequest clone = new TestHttpRequest();
202 clone._acceptTypes = _acceptTypes;
203 clone._connection = _connection;
204 clone._contentLength = _contentLength;
205 clone._uri = _uri;
206 clone._headers = new NameValueCollection(_headers);
207
208 return clone;
209 }
210 public IHttpResponse CreateResponse(IHttpClientContext context)
211 {
212 return new HttpResponse(context, this);
213 }
214 /// <summary>
215 /// Path and query (will be merged with the host header) and put in Uri
216 /// </summary>
217 /// <see cref="Uri"/>
218 public string UriPath
219 {
220 get { return _uriPath; }
221 set
222 {
223 _uriPath = value;
224
225 }
226 }
227
228 }
229
230 public class TestHttpResponse: IHttpResponse
231 {
232 public Stream Body
233 {
234 get { return _body; }
235
236 set { _body = value; }
237 }
238 private Stream _body;
239
240 public string ProtocolVersion
241 {
242 get { return _protocolVersion; }
243 set { _protocolVersion = value; }
244 }
245 private string _protocolVersion;
246
247 public bool Chunked
248 {
249 get { return _chunked; }
250
251 set { _chunked = value; }
252 }
253 private bool _chunked;
254
255 public ConnectionType Connection
256 {
257 get { return _connection; }
258
259 set { _connection = value; }
260 }
261 private ConnectionType _connection;
262
263 public Encoding Encoding
264 {
265 get { return _encoding; }
266
267 set { _encoding = value; }
268 }
269 private Encoding _encoding;
270
271 public int KeepAlive
272 {
273 get { return _keepAlive; }
274
275 set { _keepAlive = value; }
276 }
277 private int _keepAlive;
278
279 public HttpStatusCode Status
280 {
281 get { return _status; }
282
283 set { _status = value; }
284 }
285 private HttpStatusCode _status;
286
287 public string Reason
288 {
289 get { return _reason; }
290
291 set { _reason = value; }
292 }
293 private string _reason;
294
295 public long ContentLength
296 {
297 get { return _contentLength; }
298
299 set { _contentLength = value; }
300 }
301 private long _contentLength;
302
303 public string ContentType
304 {
305 get { return _contentType; }
306
307 set { _contentType = value; }
308 }
309 private string _contentType;
310
311 public bool HeadersSent
312 {
313 get { return _headersSent; }
314 }
315 private bool _headersSent;
316
317 public bool Sent
318 {
319 get { return _sent; }
320 }
321 private bool _sent;
322
323 public ResponseCookies Cookies
324 {
325 get { return _cookies; }
326 }
327 private ResponseCookies _cookies = null;
328
329 public TestHttpResponse()
330 {
331 _headersSent = false;
332 _sent = false;
333 }
334
335 public void AddHeader(string name, string value) {}
336 public void Send()
337 {
338 if (!_headersSent) SendHeaders();
339 if (_sent) throw new InvalidOperationException("stuff already sent");
340 _sent = true;
341 }
342
343 public void SendBody(byte[] buffer, int offset, int count)
344 {
345 if (!_headersSent) SendHeaders();
346 _sent = true;
347 }
348 public void SendBody(byte[] buffer)
349 {
350 if (!_headersSent) SendHeaders();
351 _sent = true;
352 }
353
354 public void SendHeaders()
355 {
356 if (_headersSent) throw new InvalidOperationException("headers already sent");
357 _headersSent = true;
358 }
359
360 public void Redirect(Uri uri) {}
361 public void Redirect(string url) {}
362 }
363
364
45 public OSHttpRequest req0; 365 public OSHttpRequest req0;
46 public OSHttpRequest req1; 366 public OSHttpRequest req1;
47 367
@@ -113,4 +433,4 @@ namespace OpenSim.Framework.Servers.Tests
113 Assert.That(rsp0.ContentType, Is.EqualTo("text/xml")); 433 Assert.That(rsp0.ContentType, Is.EqualTo("text/xml"));
114 } 434 }
115 } 435 }
116} \ No newline at end of file 436}