Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
WebSocketClientResource.cs
1using System;
2using System.Net;
3using System.Text;
4using System.Threading.Tasks;
5using System.Xml;
6using Waher.Content;
7using Waher.Events;
9
11{
16 {
17 private readonly XmppServer xmppServer;
18
25 : base(ResourceName, false, 1024 * 1024, 1024 * 1024, "xmpp")
26 {
27 this.xmppServer = XmppServer;
28
29 this.Connected += this.WebSocketClientResource_Connected;
30 }
31
32 private Task WebSocketClientResource_Connected(object Sender, WebSocketEventArgs e)
33 {
34 e.Socket.Tag = new WebSocketSession(this, e.Socket, StripPort(e.Socket.HttpRequest.RemoteEndPoint), this.xmppServer)
35 {
36 streamId = this.xmppServer.GetRandomHexString(16)
37 };
38
39 e.Socket.Closed += this.Socket_Closed;
40 e.Socket.TextReceived += this.Socket_TextReceived;
41 e.Socket.Heartbeat += this.Socket_Heartbeat;
42 e.Socket.Disposed += this.Socket_Disposed;
43
44 return Task.CompletedTask;
45 }
46
47 private Task Socket_Heartbeat(object Sender, WebSocketEventArgs e)
48 {
49 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
50 Session.TouchConnection();
51 return Task.CompletedTask;
52 }
53
54 private async Task Socket_Disposed(object Sender, EventArgs e)
55 {
56 if (Sender is WebSocket WebSocket && WebSocket.Tag is WebSocketSession WebSocketSession)
57 await WebSocketSession.DisposeAsync();
58 }
59
60 private void StreamError(WebSocketTextEventArgs e, string ErrorXml, WebSocketCloseStatus Code, string Reason)
61 {
62 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
63
64 if (!Session.streamOpen)
65 this.SendOpen(Session);
66
67 e.Socket.Send("<error xmlns=\"http://etherx.jabber.org/streams\">" +
68 ErrorXml + "</error>");
69
70 e.Socket.Close(Code, Reason);
71 }
72
73 private void StreamErrorInvalidXml(WebSocketTextEventArgs e)
74 {
75 this.StreamError(e, "<invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>",
76 WebSocketCloseStatus.NotAcceptable, "Invalid XML in request.");
77 }
78
79 private void StreamErrorHostUnknown(WebSocketTextEventArgs e)
80 {
81 this.StreamError(e, "<host-unknown xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>",
82 WebSocketCloseStatus.NotAcceptable, "Host unknown.");
83 }
84
85 private Task SendOpen(WebSocketSession Session)
86 {
87 Session.streamOpen = true;
88
89 StringBuilder Xml = new StringBuilder();
90
91 Xml.Append("<open xmlns=\"");
92 Xml.Append(FramingNamespace);
93 Xml.Append("\" from=\"");
94 Xml.Append(Session.To);
95 Xml.Append("\" id=\"");
96 Xml.Append(Session.streamId);
97 Xml.Append("\" xml:lang=\"en\" version=\"1.0\"/>");
98
99 return Session.webSocket.Send(Xml.ToString());
100 }
101
102 private async Task Socket_TextReceived(object Sender, WebSocketTextEventArgs e)
103 {
104 try
105 {
106 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
107
108 if (!Session.streamOpen)
109 {
110 XmlDocument Doc = new XmlDocument()
111 {
112 PreserveWhitespace = true
113 };
114 XmlElement Body;
115
116 try
117 {
118 Doc.LoadXml(e.Payload);
119 }
120 catch (Exception)
121 {
122 this.StreamErrorInvalidXml(e);
123 return;
124 }
125
126 Body = Doc.DocumentElement;
127 if (Body is null)
128 {
129 this.StreamErrorInvalidXml(e);
130 return;
131 }
132
133 if (Body.LocalName != "open" || Body.NamespaceURI != FramingNamespace)
134 {
135 this.StreamErrorInvalidXml(e);
136 return;
137 }
138
139 foreach (XmlAttribute Attribute in Body.Attributes)
140 {
141 switch (Attribute.Name)
142 {
143 case "from":
144 Session.From = Attribute.Value;
145 break;
146
147 case "to":
148 Session.To = Attribute.Value;
149 break;
150
151 case "version":
152 if (!CommonTypes.TryParse(Attribute.Value, out double Version) || Version < 1.0)
153 {
154 this.StreamErrorInvalidXml(e);
155 return;
156 }
157 Session.Version = Version;
158 break;
159
160 case "xml:lang":
161 Session.Language = Attribute.Value;
162 break;
163
164 default:
165 break;
166 }
167 }
168
169 if (!this.xmppServer.IsServerDomain(Session.To, true) &&
170 (!string.IsNullOrEmpty(this.xmppServer.Domain) ||
171 !IPAddress.TryParse(Session.To, out IPAddress _)))
172 {
173 this.StreamErrorHostUnknown(e);
174 return;
175 }
176
177 await this.SendOpen(Session);
178
179 await Session.InitStream();
180 }
181 else
182 await Session.ProcessFragment(e.Payload);
183 }
184 catch (Exception ex)
185 {
186 Log.Exception(ex);
187 }
188 }
189
190 private async Task Socket_Closed(object Sender, WebSocketClosedEventArgs e)
191 {
192 if (e.Socket.Tag is WebSocketSession WebSocketSession)
193 await WebSocketSession.DisposeAsync();
194 }
195
199 public const string FramingNamespace = "urn:ietf:params:xml:ns:xmpp-framing";
200
201 private static string StripPort(string s)
202 {
203 int i = s.LastIndexOf(':');
204 if (i < 0)
205 return s;
206 else
207 return s.Substring(0, i);
208 }
209
210 }
211}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:195
string ResourceName
Name of resource.
Class handling a web-socket.
Definition: WebSocket.cs:100
Task Close()
Closes the connection.
Definition: WebSocket.cs:865
object Tag
Applications can use this property to attach a value of any type to the websocket connection.
Definition: WebSocket.cs:146
async Task Send(string Payload, int MaxFrameLength)
Sends a text payload, possibly in multiple frames.
Definition: WebSocket.cs:618
HttpRequest HttpRequest
Original HTTP request made to upgrade the connection to a WebSocket connection.
Definition: WebSocket.cs:134
HTTP resource implementing the WebSocket Protocol as defined in RFC 6455: https://tools....
const string FramingNamespace
urn:ietf:params:xml:ns:xmpp-framing
WebSocketClientResource(XmppServer XmppServer, string ResourceName)
Web Socket client interface
WebSocketCloseStatus
Close status codes.
Definition: WebSocket.cs:26