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;
8using Waher.Events;
11
13{
18 {
19 private readonly XmppServer xmppServer;
20
27 : base(ResourceName, false, 1024 * 1024, 1024 * 1024, "xmpp")
28 {
29 this.xmppServer = XmppServer;
30
31 this.Connected += this.WebSocketClientResource_Connected;
32 }
33
34 private Task WebSocketClientResource_Connected(object Sender, WebSocketEventArgs e)
35 {
36 e.Socket.Tag = new WebSocketSession(this, e.Socket,
37 e.Socket.HttpRequest.RemoteEndPoint.RemovePortNumber(), this.xmppServer)
38 {
39 streamId = this.xmppServer.GetRandomHexString(16)
40 };
41
42 e.Socket.Closed += this.Socket_Closed;
43 e.Socket.TextReceived += this.Socket_TextReceived;
44 e.Socket.Heartbeat += this.Socket_Heartbeat;
45 e.Socket.Disposed += this.Socket_Disposed;
46
47 return Task.CompletedTask;
48 }
49
50 private Task Socket_Heartbeat(object Sender, WebSocketEventArgs e)
51 {
52 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
53 Session.TouchConnection();
54 return Task.CompletedTask;
55 }
56
57 private async Task Socket_Disposed(object Sender, EventArgs e)
58 {
59 if (Sender is WebSocket WebSocket && WebSocket.Tag is WebSocketSession WebSocketSession)
60 await WebSocketSession.DisposeAsync();
61 }
62
63 private void StreamError(WebSocketTextEventArgs e, string ErrorXml, WebSocketCloseStatus Code, string Reason)
64 {
65 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
66
67 if (!Session.streamOpen)
68 this.SendOpen(Session);
69
70 e.Socket.Send("<error xmlns=\"http://etherx.jabber.org/streams\">" +
71 ErrorXml + "</error>");
72
73 e.Socket.Close(Code, Reason);
74 }
75
76 private void StreamErrorInvalidXml(WebSocketTextEventArgs e)
77 {
78 this.StreamError(e, "<invalid-xml xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>",
79 WebSocketCloseStatus.NotAcceptable, "Invalid XML in request.");
80 }
81
82 private void StreamErrorHostUnknown(WebSocketTextEventArgs e)
83 {
84 this.StreamError(e, "<host-unknown xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>",
85 WebSocketCloseStatus.NotAcceptable, "Host unknown.");
86 }
87
88 private Task SendOpen(WebSocketSession Session)
89 {
90 Session.streamOpen = true;
91
92 StringBuilder Xml = new StringBuilder();
93
94 Xml.Append("<open xmlns=\"");
95 Xml.Append(FramingNamespace);
96 Xml.Append("\" from=\"");
97 Xml.Append(Session.To);
98 Xml.Append("\" id=\"");
99 Xml.Append(Session.streamId);
100 Xml.Append("\" xml:lang=\"en\" version=\"1.0\"/>");
101
102 return Session.webSocket.Send(Xml.ToString());
103 }
104
105 private async Task Socket_TextReceived(object Sender, WebSocketTextEventArgs e)
106 {
107 try
108 {
109 WebSocketSession Session = (WebSocketSession)e.Socket.Tag;
110
111 if (!Session.streamOpen)
112 {
113 XmlDocument Doc;
114 XmlElement Body;
115
116 try
117 {
118 Doc = XML.ParseXml(e.Payload, true);
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 }
202}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Helps with common XML-related tasks.
Definition: XML.cs:21
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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:1657
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:243
string ResourceName
Name of resource.
Class handling a web-socket.
Definition: WebSocket.cs:17
Task Close()
Closes the connection.
Definition: WebSocket.cs:906
object Tag
Applications can use this property to attach a value of any type to the websocket connection.
Definition: WebSocket.cs:89
async Task< bool > Send(string Payload, int MaxFrameLength)
Sends a text payload, possibly in multiple frames.
Definition: WebSocket.cs:607
HttpRequest HttpRequest
Original HTTP request made to upgrade the connection to a WebSocket connection.
Definition: WebSocket.cs:67
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
Definition: ImplTypes.g.cs:58