diff options
6 files changed, 494 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs b/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs new file mode 100644 index 0000000..a58eab1 --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs | |||
@@ -0,0 +1,50 @@ | |||
1 | using System; | ||
2 | using System.Net.Sockets; | ||
3 | |||
4 | namespace OpenSim.Region.ClientStack.TCPJSONStream | ||
5 | { | ||
6 | /// <summary> | ||
7 | /// Invoked when a client have been accepted by the <see cref="HttpListener"/> | ||
8 | /// </summary> | ||
9 | /// <remarks> | ||
10 | /// Can be used to revoke incoming connections | ||
11 | /// </remarks> | ||
12 | public class ClientAcceptedEventArgs : EventArgs | ||
13 | { | ||
14 | private readonly Socket _socket; | ||
15 | private bool _revoke; | ||
16 | |||
17 | /// <summary> | ||
18 | /// Initializes a new instance of the <see cref="ClientAcceptedEventArgs"/> class. | ||
19 | /// </summary> | ||
20 | /// <param name="socket">The socket.</param> | ||
21 | public ClientAcceptedEventArgs(Socket socket) | ||
22 | { | ||
23 | _socket = socket; | ||
24 | } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Accepted socket. | ||
28 | /// </summary> | ||
29 | public Socket Socket | ||
30 | { | ||
31 | get { return _socket; } | ||
32 | } | ||
33 | |||
34 | /// <summary> | ||
35 | /// Client should be revoked. | ||
36 | /// </summary> | ||
37 | public bool Revoked | ||
38 | { | ||
39 | get { return _revoke; } | ||
40 | } | ||
41 | |||
42 | /// <summary> | ||
43 | /// Client may not be handled. | ||
44 | /// </summary> | ||
45 | public void Revoke() | ||
46 | { | ||
47 | _revoke = true; | ||
48 | } | ||
49 | } | ||
50 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs b/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs new file mode 100644 index 0000000..b077b6a --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs | |||
@@ -0,0 +1,146 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.IO; | ||
4 | using System.Net; | ||
5 | using System.Net.Sockets; | ||
6 | using System.Text; | ||
7 | |||
8 | namespace OpenSim.Region.ClientStack.TCPJSONStream | ||
9 | { | ||
10 | public class ClientNetworkContext | ||
11 | { | ||
12 | private Socket _socket; | ||
13 | private string _remoteAddress; | ||
14 | private string _remotePort; | ||
15 | private WebSocketConnectionStage _wsConnectionStatus = WebSocketConnectionStage.Accept; | ||
16 | private int _bytesLeft; | ||
17 | private NetworkStream _stream; | ||
18 | private byte[] _buffer; | ||
19 | public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { }; | ||
20 | |||
21 | public ClientNetworkContext(IPEndPoint endPoint, int port, Stream stream, int buffersize, Socket sock) | ||
22 | { | ||
23 | _socket = sock; | ||
24 | _remoteAddress = endPoint.Address.ToString(); | ||
25 | _remotePort = port.ToString(); | ||
26 | _stream = stream as NetworkStream; | ||
27 | _buffer = new byte[buffersize]; | ||
28 | |||
29 | |||
30 | } | ||
31 | |||
32 | public void BeginRead() | ||
33 | { | ||
34 | _wsConnectionStatus = WebSocketConnectionStage.Http; | ||
35 | try | ||
36 | { | ||
37 | _stream.BeginRead(_buffer, 0, _buffer.Length, OnReceive, _wsConnectionStatus); | ||
38 | } | ||
39 | catch (IOException err) | ||
40 | { | ||
41 | //m_log.Debug(err.ToString()); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | private void OnReceive(IAsyncResult ar) | ||
46 | { | ||
47 | try | ||
48 | { | ||
49 | int bytesRead = _stream.EndRead(ar); | ||
50 | if (bytesRead == 0) | ||
51 | { | ||
52 | |||
53 | Disconnected(this, new DisconnectedEventArgs(SocketError.ConnectionReset)); | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | } | ||
58 | catch (Exception) | ||
59 | { | ||
60 | } | ||
61 | } | ||
62 | /// <summary> | ||
63 | /// send a whole buffer | ||
64 | /// </summary> | ||
65 | /// <param name="buffer">buffer to send</param> | ||
66 | /// <exception cref="ArgumentNullException"></exception> | ||
67 | public void Send(byte[] buffer) | ||
68 | { | ||
69 | if (buffer == null) | ||
70 | throw new ArgumentNullException("buffer"); | ||
71 | Send(buffer, 0, buffer.Length); | ||
72 | } | ||
73 | |||
74 | /// <summary> | ||
75 | /// Send data using the stream | ||
76 | /// </summary> | ||
77 | /// <param name="buffer">Contains data to send</param> | ||
78 | /// <param name="offset">Start position in buffer</param> | ||
79 | /// <param name="size">number of bytes to send</param> | ||
80 | /// <exception cref="ArgumentNullException"></exception> | ||
81 | /// <exception cref="ArgumentOutOfRangeException"></exception> | ||
82 | public void Send(byte[] buffer, int offset, int size) | ||
83 | { | ||
84 | |||
85 | if (offset + size > buffer.Length) | ||
86 | throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer."); | ||
87 | |||
88 | if (_stream != null && _stream.CanWrite) | ||
89 | { | ||
90 | try | ||
91 | { | ||
92 | _stream.Write(buffer, offset, size); | ||
93 | } | ||
94 | catch (IOException) | ||
95 | { | ||
96 | |||
97 | } | ||
98 | } | ||
99 | |||
100 | } | ||
101 | private void Reset() | ||
102 | { | ||
103 | if (_stream == null) | ||
104 | return; | ||
105 | _stream.Dispose(); | ||
106 | _stream = null; | ||
107 | if (_socket == null) | ||
108 | return; | ||
109 | if (_socket.Connected) | ||
110 | _socket.Disconnect(true); | ||
111 | _socket = null; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | public enum WebSocketConnectionStage | ||
116 | { | ||
117 | Reuse, | ||
118 | Accept, | ||
119 | Http, | ||
120 | WebSocket, | ||
121 | Closed | ||
122 | } | ||
123 | |||
124 | public enum FrameOpCodesRFC6455 | ||
125 | { | ||
126 | Continue = 0x0, | ||
127 | Text = 0x1, | ||
128 | Binary = 0x2, | ||
129 | Close = 0x8, | ||
130 | Ping = 0x9, | ||
131 | Pong = 0xA | ||
132 | } | ||
133 | |||
134 | public enum DataState | ||
135 | { | ||
136 | Empty = 0, | ||
137 | Waiting = 1, | ||
138 | Receiving = 2, | ||
139 | Complete = 3, | ||
140 | Closed = 4, | ||
141 | Ping = 5, | ||
142 | Pong = 6 | ||
143 | } | ||
144 | |||
145 | |||
146 | } | ||
diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs b/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs new file mode 100644 index 0000000..32880cc --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs | |||
@@ -0,0 +1,17 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Linq; | ||
4 | using System.Net.Sockets; | ||
5 | using System.Text; | ||
6 | |||
7 | namespace OpenSim.Region.ClientStack.TCPJSONStream | ||
8 | { | ||
9 | public class DisconnectedEventArgs:EventArgs | ||
10 | { | ||
11 | public SocketError Error { get; private set; } | ||
12 | public DisconnectedEventArgs(SocketError err) | ||
13 | { | ||
14 | Error = err; | ||
15 | } | ||
16 | } | ||
17 | } | ||
diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs b/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs new file mode 100644 index 0000000..379438d --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs | |||
@@ -0,0 +1,73 @@ | |||
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.Net; | ||
30 | using Nini.Config; | ||
31 | using OpenSim.Framework; | ||
32 | |||
33 | namespace OpenSim.Region.ClientStack.TCPJSONStream | ||
34 | { | ||
35 | public sealed class TCPJsonWebSocketBase : IClientNetworkServer | ||
36 | { | ||
37 | private TCPJsonWebSocketServer m_tcpServer; | ||
38 | |||
39 | public TCPJsonWebSocketBase() | ||
40 | { | ||
41 | } | ||
42 | |||
43 | public void Initialise(IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager authenticateClass) | ||
44 | { | ||
45 | m_tcpServer = new TCPJsonWebSocketServer(_listenIP,ref port, proxyPortOffsetParm, allow_alternate_port,configSource,authenticateClass); | ||
46 | } | ||
47 | |||
48 | public void NetworkStop() | ||
49 | { | ||
50 | m_tcpServer.Stop(); | ||
51 | } | ||
52 | |||
53 | public bool HandlesRegion(Location x) | ||
54 | { | ||
55 | return m_tcpServer.HandlesRegion(x); | ||
56 | } | ||
57 | |||
58 | public void AddScene(IScene x) | ||
59 | { | ||
60 | m_tcpServer.AddScene(x); | ||
61 | } | ||
62 | |||
63 | public void Start() | ||
64 | { | ||
65 | m_tcpServer.Start(); | ||
66 | } | ||
67 | |||
68 | public void Stop() | ||
69 | { | ||
70 | m_tcpServer.Stop(); | ||
71 | } | ||
72 | } | ||
73 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs b/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs new file mode 100644 index 0000000..c0f6792 --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs | |||
@@ -0,0 +1,163 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Net; | ||
4 | using System.Net.Sockets; | ||
5 | using System.Reflection; | ||
6 | using System.Text; | ||
7 | using System.Threading; | ||
8 | using Nini.Config; | ||
9 | using OpenSim.Framework; | ||
10 | using OpenSim.Region.Framework.Scenes; | ||
11 | using log4net; | ||
12 | |||
13 | namespace OpenSim.Region.ClientStack.TCPJSONStream | ||
14 | { | ||
15 | public delegate void ExceptionHandler(object source, Exception exception); | ||
16 | |||
17 | public class TCPJsonWebSocketServer | ||
18 | { | ||
19 | private readonly IPAddress _address; | ||
20 | private readonly int _port; | ||
21 | private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false); | ||
22 | private TcpListener _listener; | ||
23 | private int _pendingAccepts; | ||
24 | private bool _shutdown; | ||
25 | private int _backlogAcceptQueueLength = 5; | ||
26 | private Scene m_scene; | ||
27 | private Location m_location; | ||
28 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
29 | |||
30 | public event EventHandler<ClientAcceptedEventArgs> Accepted = delegate { }; | ||
31 | |||
32 | |||
33 | public TCPJsonWebSocketServer(IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, | ||
34 | bool allow_alternate_port, IConfigSource configSource, | ||
35 | AgentCircuitManager authenticateClass) | ||
36 | { | ||
37 | _address = _listenIP; | ||
38 | _port = (int)port; //Why is a uint passed in? | ||
39 | } | ||
40 | public void Stop() | ||
41 | { | ||
42 | _shutdown = true; | ||
43 | _listener.Stop(); | ||
44 | if (!_shutdownEvent.WaitOne()) | ||
45 | m_log.Error("[WEBSOCKETSERVER]: Failed to shutdown listener properly."); | ||
46 | _listener = null; | ||
47 | } | ||
48 | |||
49 | public bool HandlesRegion(Location x) | ||
50 | { | ||
51 | return x == m_location; | ||
52 | } | ||
53 | |||
54 | public void AddScene(IScene scene) | ||
55 | { | ||
56 | if (m_scene != null) | ||
57 | { | ||
58 | m_log.Debug("[WEBSOCKETSERVER]: AddScene() called but I already have a scene."); | ||
59 | return; | ||
60 | } | ||
61 | if (!(scene is Scene)) | ||
62 | { | ||
63 | m_log.Error("[WEBSOCKETSERVER]: AddScene() called with an unrecognized scene type " + scene.GetType()); | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | m_scene = (Scene)scene; | ||
68 | m_location = new Location(m_scene.RegionInfo.RegionHandle); | ||
69 | } | ||
70 | |||
71 | public void Start() | ||
72 | { | ||
73 | _listener = new TcpListener(_address, _port); | ||
74 | _listener.Start(_backlogAcceptQueueLength); | ||
75 | Interlocked.Increment(ref _pendingAccepts); | ||
76 | _listener.BeginAcceptSocket(OnAccept, null); | ||
77 | } | ||
78 | |||
79 | private void OnAccept(IAsyncResult ar) | ||
80 | { | ||
81 | bool beginAcceptCalled = false; | ||
82 | try | ||
83 | { | ||
84 | int count = Interlocked.Decrement(ref _pendingAccepts); | ||
85 | if (_shutdown) | ||
86 | { | ||
87 | if (count == 0) | ||
88 | _shutdownEvent.Set(); | ||
89 | return; | ||
90 | } | ||
91 | Interlocked.Increment(ref _pendingAccepts); | ||
92 | _listener.BeginAcceptSocket(OnAccept, null); | ||
93 | beginAcceptCalled = true; | ||
94 | Socket socket = _listener.EndAcceptSocket(ar); | ||
95 | if (!OnAcceptingSocket(socket)) | ||
96 | { | ||
97 | socket.Disconnect(true); | ||
98 | return; | ||
99 | } | ||
100 | ClientNetworkContext context = new ClientNetworkContext((IPEndPoint) socket.RemoteEndPoint, _port, | ||
101 | new NetworkStream(socket), 16384, socket); | ||
102 | //HttpRequestParser parser; | ||
103 | context.BeginRead(); | ||
104 | |||
105 | } | ||
106 | catch (Exception err) | ||
107 | { | ||
108 | if (ExceptionThrown == null) | ||
109 | #if DEBUG | ||
110 | throw; | ||
111 | #else | ||
112 | _logWriter.Write(this, LogPrio.Fatal, err.Message); | ||
113 | // we can't really do anything but close the connection | ||
114 | #endif | ||
115 | if (ExceptionThrown != null) | ||
116 | ExceptionThrown(this, err); | ||
117 | |||
118 | if (!beginAcceptCalled) | ||
119 | RetryBeginAccept(); | ||
120 | |||
121 | } | ||
122 | } | ||
123 | |||
124 | private void RetryBeginAccept() | ||
125 | { | ||
126 | try | ||
127 | { | ||
128 | |||
129 | _listener.BeginAcceptSocket(OnAccept, null); | ||
130 | } | ||
131 | catch (Exception err) | ||
132 | { | ||
133 | |||
134 | if (ExceptionThrown == null) | ||
135 | #if DEBUG | ||
136 | throw; | ||
137 | #else | ||
138 | // we can't really do anything but close the connection | ||
139 | #endif | ||
140 | if (ExceptionThrown != null) | ||
141 | ExceptionThrown(this, err); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | private bool OnAcceptingSocket(Socket sock) | ||
146 | { | ||
147 | ClientAcceptedEventArgs args = new ClientAcceptedEventArgs(sock); | ||
148 | Accepted(this, args); | ||
149 | return !args.Revoked; | ||
150 | } | ||
151 | /// <summary> | ||
152 | /// Catch exceptions not handled by the listener. | ||
153 | /// </summary> | ||
154 | /// <remarks> | ||
155 | /// Exceptions will be thrown during debug mode if this event is not used, | ||
156 | /// exceptions will be printed to console and suppressed during release mode. | ||
157 | /// </remarks> | ||
158 | public event ExceptionHandler ExceptionThrown = delegate { }; | ||
159 | |||
160 | |||
161 | |||
162 | } | ||
163 | } | ||
diff --git a/prebuild.xml b/prebuild.xml index 329ff73..d8b4145 100644 --- a/prebuild.xml +++ b/prebuild.xml | |||
@@ -1466,6 +1466,8 @@ | |||
1466 | </Files> | 1466 | </Files> |
1467 | </Project> | 1467 | </Project> |
1468 | 1468 | ||
1469 | |||
1470 | |||
1469 | <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenCaps" path="OpenSim/Region/ClientStack/Linden/Caps" type="Library"> | 1471 | <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenCaps" path="OpenSim/Region/ClientStack/Linden/Caps" type="Library"> |
1470 | <Configuration name="Debug"> | 1472 | <Configuration name="Debug"> |
1471 | <Options> | 1473 | <Options> |
@@ -1508,6 +1510,49 @@ | |||
1508 | </Files> | 1510 | </Files> |
1509 | </Project> | 1511 | </Project> |
1510 | 1512 | ||
1513 | <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.TCPJSONStream" path="OpenSim/Region/ClientStack/TCPJSONStream" type="Library"> | ||
1514 | <Configuration name="Debug"> | ||
1515 | <Options> | ||
1516 | <OutputPath>../../../../bin/</OutputPath> | ||
1517 | </Options> | ||
1518 | </Configuration> | ||
1519 | <Configuration name="Release"> | ||
1520 | <Options> | ||
1521 | <OutputPath>../../../../bin/</OutputPath> | ||
1522 | </Options> | ||
1523 | </Configuration> | ||
1524 | |||
1525 | <ReferencePath>../../../../bin/</ReferencePath> | ||
1526 | <Reference name="System"/> | ||
1527 | <Reference name="System.Core"/> | ||
1528 | <Reference name="System.Drawing"/> | ||
1529 | <Reference name="System.Xml"/> | ||
1530 | <Reference name="System.Web"/> | ||
1531 | <Reference name="OpenMetaverseTypes" path="../../../../bin/"/> | ||
1532 | <Reference name="OpenMetaverse.StructuredData" path="../../../../bin/"/> | ||
1533 | <Reference name="OpenMetaverse" path="../../../../bin/"/> | ||
1534 | <Reference name="OpenSim.Data"/> | ||
1535 | <Reference name="OpenSim.Framework"/> | ||
1536 | <Reference name="OpenSim.Framework.Monitoring"/> | ||
1537 | <Reference name="OpenSim.Framework.Servers"/> | ||
1538 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> | ||
1539 | <Reference name="OpenSim.Framework.Console"/> | ||
1540 | <Reference name="OpenSim.Framework.Communications"/> | ||
1541 | <Reference name="OpenSim.Region.ClientStack"/> | ||
1542 | <Reference name="OpenSim.Region.Framework"/> | ||
1543 | <Reference name="OpenSim.Services.Interfaces"/> | ||
1544 | <Reference name="Nini" path="../../../../bin/"/> | ||
1545 | <Reference name="log4net" path="../../../../bin/"/> | ||
1546 | <Reference name="C5" path="../../../../bin/"/> | ||
1547 | <Reference name="Nini" path="../../../../bin/"/> | ||
1548 | |||
1549 | <Files> | ||
1550 | <Match pattern="*.cs" recurse="true"> | ||
1551 | <Exclude name="Tests" pattern="Tests"/> | ||
1552 | </Match> | ||
1553 | </Files> | ||
1554 | </Project> | ||
1555 | |||
1511 | <Project frameworkVersion="v3_5" name="OpenSim.Region.CoreModules" path="OpenSim/Region/CoreModules" type="Library"> | 1556 | <Project frameworkVersion="v3_5" name="OpenSim.Region.CoreModules" path="OpenSim/Region/CoreModules" type="Library"> |
1512 | <Configuration name="Debug"> | 1557 | <Configuration name="Debug"> |
1513 | <Options> | 1558 | <Options> |