2using System.Net.Security;
4using System.Threading.Tasks;
21 private const int MaxSecondsIdle = 90;
45 Result.Removed += Result_Removed;
54 e.Value.Removed =
true;
55 await e.
Value.DisposeAsync();
96 if (
string.IsNullOrEmpty(Xml))
99 Response.
SetHeader(
"Cache-Control",
"max-age=0, no-cache, no-store");
101 this.Process(Xml, Request, Response);
103 return Task.CompletedTask;
119 if (l >
int.MaxValue)
123 byte[] Data =
new byte[Len];
124 Request.DataStream.Position = 0;
127 string Xml = Encoding.UTF8.GetString(Data);
128 this.Process(Xml, Request, Response);
130 return Task.CompletedTask;
137 await this.ProcessFragment(Xml, Request, Response);
154 bool HasNamespace =
false;
157 for (i = 0; i < c; i++)
183 await this.BoshError(Response,
"Invalid Request");
192 if (ch ==
'>' || ch ==
'/')
194 if (Depth == 1 && !HasNamespace)
196 Name =
" xmlns='" + XmppClientConnection.C2SNamespace +
"'";
197 Xml = Xml.Insert(i, Name);
215 HasNamespace =
false;
227 if (ch ==
'>' || ch ==
'/')
229 if (Depth == 1 && !HasNamespace)
231 Name =
" xmlns='" + XmppClientConnection.C2SNamespace +
"'";
232 Xml = Xml.Insert(i, Name);
254 Name = Xml.Substring(Start, i - Start);
280 await this.BoshError(Response,
"Invalid Request");
290 await this.BoshError(Response,
"Invalid Request");
319 await this.BoshError(Response,
"Invalid Request");
329 await this.BoshError(Response,
"Invalid Request");
339 await this.BoshError(Response,
"Invalid Request");
349 await this.BoshError(Response,
"Invalid Request");
359 await this.BoshError(Response,
"Invalid Request");
369 await this.BoshError(Response,
"Invalid Request");
398 XmlDocument Doc =
new XmlDocument()
400 PreserveWhitespace =
true
413 Body = Doc.DocumentElement;
414 if (Body is
null || Body.LocalName !=
"body" || Body.NamespaceURI !=
HttpBindNamespace)
418 string Language =
"en";
423 string NewKey =
null;
425 double Version =
double.MaxValue;
429 int WaitSeconds = 30;
431 bool Terminate =
false;
433 foreach (XmlAttribute Attribute
in Body.Attributes)
435 switch (Attribute.Name)
442 From = Attribute.
Value;
446 if (!
int.TryParse(Attribute.Value, out Hold) || Hold < 0)
454 if (!
long.TryParse(Attribute.Value, out
long l) || l < 0)
460 Sid = Attribute.Value;
464 To = Attribute.
Value;
477 if (!
int.TryParse(Attribute.Value, out WaitSeconds) || Hold <= 0)
482 if (!
long.TryParse(Attribute.Value, out l))
494 Language = Attribute.Value;
498 if (Attribute.Value ==
"terminate")
503 Key = Attribute.Value;
507 NewKey = Attribute.Value;
511 Echo = Attribute.Value;
524 StringBuilder sb =
new StringBuilder();
527 if (
string.IsNullOrEmpty(Sid))
529 if (
string.IsNullOrEmpty(To) || !this.xmppServer.IsServerDomain(To,
true) || !(Key is
null))
536 Sid = this.xmppServer.GetRandomHexString(16);
538 while (sessions.ContainsKey(Sid));
540 if (WaitSeconds > MaxSecondsIdle)
541 WaitSeconds = MaxSecondsIdle;
543 int PollingSeconds = Math.Min(5, WaitSeconds);
544 int Requests = Hold + 1;
546 Session =
new BoshSession(
this, Sid, Rid, Version, From, To, Language, Hold, WaitSeconds, PollingSeconds, Requests,
547 Ack.HasValue && Ack.Value != 0, Secure, StripPort(Request.
RemoteEndPoint), NewKey,
this.xmppServer);
548 sessions.Add(Sid, Session);
550 sb.Append(
"<body wait='");
551 sb.Append(WaitSeconds.ToString());
552 sb.Append(
"' inactivity='");
553 sb.Append(Math.Max(WaitSeconds / 2, 1).ToString());
554 sb.Append(
"' polling='");
555 sb.Append(PollingSeconds.ToString());
556 sb.Append(
"' requests='");
557 sb.Append(Requests.ToString());
558 sb.Append(
"' hold='");
559 sb.Append(Hold.ToString());
561 if (Ack.HasValue && Ack.Value != 0 && Rid.HasValue)
563 sb.Append(
"' ack='");
564 sb.Append(Rid.Value.ToString());
565 Session.RidReturnedLocked(Rid.Value);
570 sb.Append(
"' sid='");
572 sb.Append(
"' ver='");
574 sb.Append(
"' from='");
577 if (!
string.IsNullOrEmpty(Echo))
579 sb.Append(
"' echo='");
583 sb.Append(
"' xmlns='");
585 sb.Append(
"' xmlns:stream='");
586 sb.Append(XmppClientConnection.StreamNamespace);
591 sb.Append(
"</body>");
593 await this.Return(Response, sb.ToString());
597 if (!sessions.TryGetValue(Sid, out Session))
603 if (Session.State ==
XmppConnectionState.Active && !
this.xmppServer.TouchClientConnection(Session.FullJid))
612 KeyOk = Session.CheckNextKey(Key);
619 await this.BoshError(Response,
"item-not-found");
623 if (!(NewKey is
null))
624 Session.Key = NewKey;
628 Session.Terminated =
true;
630 foreach (XmlNode N
in Body.ChildNodes)
632 if (N is XmlElement E && E.LocalName ==
"presence" &&
XML.
Attribute(E,
"type") ==
"unavailable")
633 Session.IsBound =
false;
636 await Session.DisposeAsync();
640 if (Session.Terminated)
641 await Session.DisposeAsync();
643 await Session.RegisterForResponse(XmppServer.Scheduler, Rid, Response, Echo);
649 internal Task BoshError(
HttpResponse Response,
string Condition)
651 StringBuilder Xml =
new StringBuilder();
653 Xml.Append(
"<body type='terminate");
655 if (!
string.IsNullOrEmpty(Condition))
657 Xml.Append(
"' condition='");
658 Xml.Append(Condition);
661 Xml.Append(
"' xmlns='");
665 return this.Return(Response, Xml.ToString());
668 internal static void Remove(BoshSession Session)
670 sessions.Remove(Session.Sid);
673 internal async Task Return(
HttpResponse Response,
string Xml)
675 byte[] Bin = Encoding.UTF8.GetBytes(Xml);
677 Response.ContentLength = Bin.Length;
681 await Response.
Write(Bin);
687 this.httpServer.RequestResponded(Response.
Request, 500);
691 private static string StripPort(
string s)
693 int i = s.LastIndexOf(
':');
697 return s.Substring(0, i);
Helps with parsing of commong data types.
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
Static class managing the application event log. Applications and services log events on this static ...
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.
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Stream Stream
Stream object currently being used.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
Base class for all asynchronous HTTP resources. An asynchronous resource responds outside of the meth...
Represents an HTTP request.
Stream DataStream
Data stream, if data is available, or null if data is not available.
HttpRequestHeader Header
Request header.
string RemoteEndPoint
Remote end-point.
bool HasData
If the request has data.
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
HttpRequest Request
Corresponding HTTP Request
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
BinaryTcpClient ClientConnection
Current client connection
async Task Write(byte[] Data)
Returns binary data in the response.
Implements an HTTP server.
The server has not found anything matching the Request-URI. No indication is given of whether the con...
override bool HandlesSubPaths
If subpaths are handled.
Task GET(HttpRequest Request, HttpResponse Response)
Handles a GET requesst.
BoshWebClientResource(XmppServer XmppServer, HttpServer HttpServer, string ResourceName)
BOSH webclient interface
Task POST(HttpRequest Request, HttpResponse Response)
Handles a POST requesst.
override bool UserSessions
If User session is requried
bool AllowsPOST
If POST method is allowed.
bool AllowsGET
If GET method is allowed.
const string HttpBindNamespace
http://jabber.org/protocol/httpbind
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
Implements an in-memory cache.
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
GET Interface for HTTP resources.
POST Interface for HTTP resources.
XmppConnectionState
State of XMPP connection.
ContentType
DTLS Record content type.