diff --git a/Example/Example.csproj b/Example/Example.csproj
index 38c5b4200..ee89d9f6e 100644
--- a/Example/Example.csproj
+++ b/Example/Example.csproj
@@ -34,7 +34,7 @@
full
false
bin\Debug_Ubuntu
- DEBUG,UBUNTU
+ DEBUG
prompt
4
true
@@ -43,16 +43,12 @@
none
false
bin\Release_Ubuntu
- UBUNTU
prompt
4
true
-
- notify-sharp
-
@@ -65,7 +61,5 @@
-
-
\ No newline at end of file
diff --git a/Example/NotificationMessage.cs b/Example/NotificationMessage.cs
deleted file mode 100644
index fd1bd3071..000000000
--- a/Example/NotificationMessage.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Example
-{
- internal class NotificationMessage
- {
- public string Body {
- get; set;
- }
-
- public string Icon {
- get; set;
- }
-
- public string Summary {
- get; set;
- }
-
- public override string ToString ()
- {
- return String.Format ("{0}: {1}", Summary, Body);
- }
- }
-}
diff --git a/Example/Notifier.cs b/Example/Notifier.cs
deleted file mode 100644
index 5371c37a4..000000000
--- a/Example/Notifier.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-#if UBUNTU
-using Notifications;
-#endif
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Example
-{
- internal class Notifier : IDisposable
- {
- private volatile bool _enabled;
- private ManualResetEvent _exited;
- private Queue _queue;
- private object _sync;
-
- public Notifier ()
- {
- _enabled = true;
- _exited = new ManualResetEvent (false);
- _queue = new Queue ();
- _sync = ((ICollection) _queue).SyncRoot;
-
- ThreadPool.QueueUserWorkItem (
- state => {
- while (_enabled || Count > 0) {
- var msg = dequeue ();
- if (msg != null) {
-#if UBUNTU
- var nf = new Notification (msg.Summary, msg.Body, msg.Icon);
- nf.AddHint ("append", "allowed");
- nf.Show ();
-#else
- Console.WriteLine (msg);
-#endif
- }
- else {
- Thread.Sleep (500);
- }
- }
-
- _exited.Set ();
- }
- );
- }
-
- public int Count {
- get {
- lock (_sync)
- return _queue.Count;
- }
- }
-
- private NotificationMessage dequeue ()
- {
- lock (_sync)
- return _queue.Count > 0 ? _queue.Dequeue () : null;
- }
-
- public void Close ()
- {
- _enabled = false;
- _exited.WaitOne ();
- _exited.Close ();
- }
-
- public void Notify (NotificationMessage message)
- {
- lock (_sync) {
- if (_enabled)
- _queue.Enqueue (message);
- }
- }
-
- void IDisposable.Dispose ()
- {
- Close ();
- }
- }
-}
diff --git a/Example/Program.cs b/Example/Program.cs
index d414bb1e5..b7313a9d4 100644
--- a/Example/Program.cs
+++ b/Example/Program.cs
@@ -18,13 +18,8 @@ public static void Main (string[] args)
// If you would like to connect to the server with the secure connection,
// you should create a new instance with a wss scheme WebSocket URL.
- using (var nf = new Notifier ())
- using (var ws = new WebSocket ("ws://echo.websocket.org"))
- //using (var ws = new WebSocket ("wss://echo.websocket.org"))
- //using (var ws = new WebSocket ("ws://localhost:4649/Echo"))
+ using (var ws = new WebSocket ("ws://localhost:4649/Echo"))
//using (var ws = new WebSocket ("wss://localhost:5963/Echo"))
- //using (var ws = new WebSocket ("ws://localhost:4649/Echo?name=nobita"))
- //using (var ws = new WebSocket ("wss://localhost:5963/Echo?name=nobita"))
//using (var ws = new WebSocket ("ws://localhost:4649/Chat"))
//using (var ws = new WebSocket ("wss://localhost:5963/Chat"))
//using (var ws = new WebSocket ("ws://localhost:4649/Chat?name=nobita"))
@@ -34,32 +29,24 @@ public static void Main (string[] args)
ws.OnOpen += (sender, e) => ws.Send ("Hi, there!");
- ws.OnMessage += (sender, e) =>
- nf.Notify (
- new NotificationMessage {
- Summary = "WebSocket Message",
- Body = !e.IsPing ? e.Data : "Received a ping.",
- Icon = "notification-message-im"
- }
- );
-
- ws.OnError += (sender, e) =>
- nf.Notify (
- new NotificationMessage {
- Summary = "WebSocket Error",
- Body = e.Message,
- Icon = "notification-message-im"
- }
- );
-
- ws.OnClose += (sender, e) =>
- nf.Notify (
- new NotificationMessage {
- Summary = String.Format ("WebSocket Close ({0})", e.Code),
- Body = e.Reason,
- Icon = "notification-message-im"
- }
- );
+ ws.OnMessage += (sender, e) => {
+ var fmt = "[WebSocket Message] {0}";
+ var body = !e.IsPing ? e.Data : "A ping was received.";
+
+ Console.WriteLine (fmt, body);
+ };
+
+ ws.OnError += (sender, e) => {
+ var fmt = "[WebSocket Error] {0}";
+
+ Console.WriteLine (fmt, e.Message);
+ };
+
+ ws.OnClose += (sender, e) => {
+ var fmt = "[WebSocket Close ({0})] {1}";
+
+ Console.WriteLine (fmt, e.Code, e.Reason);
+ };
#if DEBUG
// To change the logging level.
ws.Log.Level = LogLevel.Trace;
@@ -77,13 +64,12 @@ public static void Main (string[] args)
/*
ws.SslConfiguration.ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => {
- ws.Log.Debug (
- String.Format (
- "Certificate:\n- Issuer: {0}\n- Subject: {1}",
- certificate.Issuer,
- certificate.Subject
- )
- );
+ var fmt = "Certificate:\n- Issuer: {0}\n- Subject: {1}";
+ var msg = String.Format (
+ fmt, certificate.Issuer, certificate.Subject
+ );
+
+ ws.Log.Debug (msg);
return true; // If the server certificate is valid.
};
@@ -112,10 +98,13 @@ public static void Main (string[] args)
//ws.ConnectAsync ();
Console.WriteLine ("\nType 'exit' to exit.\n");
+
while (true) {
Thread.Sleep (1000);
Console.Write ("> ");
+
var msg = Console.ReadLine ();
+
if (msg == "exit")
break;
diff --git a/Example1/AssemblyInfo.cs b/Example1/AssemblyInfo.cs
deleted file mode 100644
index a78e6c6de..000000000
--- a/Example1/AssemblyInfo.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("Example1")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("sta.blockhead")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("1.0.*")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
diff --git a/Example1/AudioStreamer.cs b/Example1/AudioStreamer.cs
deleted file mode 100644
index 711694e9a..000000000
--- a/Example1/AudioStreamer.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using WebSocketSharp;
-
-namespace Example1
-{
- internal class AudioStreamer : IDisposable
- {
- private Dictionary _audioBox;
- private uint? _id;
- private string _name;
- private Notifier _notifier;
- private Timer _timer;
- private WebSocket _websocket;
-
- public AudioStreamer (string url)
- {
- _websocket = new WebSocket (url);
-
- _audioBox = new Dictionary ();
- _id = null;
- _notifier = new Notifier ();
- _timer = new Timer (sendHeartbeat, null, -1, -1);
-
- configure ();
- }
-
- private void configure ()
- {
-#if DEBUG
- _websocket.Log.Level = LogLevel.Trace;
-#endif
- _websocket.OnOpen += (sender, e) =>
- _websocket.Send (createTextMessage ("connection", String.Empty));
-
- _websocket.OnMessage += (sender, e) => {
- if (e.IsText) {
- _notifier.Notify (processTextMessage (e.Data));
- return;
- }
-
- if (e.IsBinary) {
- processBinaryMessage (e.RawData);
- return;
- }
- };
-
- _websocket.OnError += (sender, e) =>
- _notifier.Notify (
- new NotificationMessage {
- Summary = "AudioStreamer (error)",
- Body = e.Message,
- Icon = "notification-message-im"
- }
- );
-
- _websocket.OnClose += (sender, e) =>
- _notifier.Notify (
- new NotificationMessage {
- Summary = "AudioStreamer (disconnect)",
- Body = String.Format ("code: {0} reason: {1}", e.Code, e.Reason),
- Icon = "notification-message-im"
- }
- );
- }
-
- private byte[] createBinaryMessage (float[,] bufferArray)
- {
- return new BinaryMessage {
- UserID = (uint) _id,
- ChannelNumber = (byte) bufferArray.GetLength (0),
- BufferLength = (uint) bufferArray.GetLength (1),
- BufferArray = bufferArray
- }
- .ToArray ();
- }
-
- private string createTextMessage (string type, string message)
- {
- return new TextMessage {
- UserID = _id,
- Name = _name,
- Type = type,
- Message = message
- }
- .ToString ();
- }
-
- private void processBinaryMessage (byte[] data)
- {
- var msg = BinaryMessage.Parse (data);
-
- var id = msg.UserID;
- if (id == _id)
- return;
-
- Queue queue;
- if (_audioBox.TryGetValue (id, out queue)) {
- queue.Enqueue (msg.BufferArray);
- return;
- }
-
- queue = Queue.Synchronized (new Queue ());
- queue.Enqueue (msg.BufferArray);
- _audioBox.Add (id, queue);
- }
-
- private NotificationMessage processTextMessage (string data)
- {
- var json = JObject.Parse (data);
- var id = (uint) json["user_id"];
- var name = (string) json["name"];
- var type = (string) json["type"];
-
- string body;
- if (type == "message") {
- body = String.Format ("{0}: {1}", name, (string) json["message"]);
- }
- else if (type == "start_music") {
- body = String.Format ("{0}: Started playing music!", name);
- }
- else if (type == "connection") {
- var users = (JArray) json["message"];
- var buff = new StringBuilder ("Now keeping connections:");
- foreach (JToken user in users) {
- buff.AppendFormat (
- "\n- user_id: {0} name: {1}", (uint) user["user_id"], (string) user["name"]
- );
- }
-
- body = buff.ToString ();
- }
- else if (type == "connected") {
- _id = id;
- _timer.Change (30000, 30000);
-
- body = String.Format ("user_id: {0} name: {1}", id, name);
- }
- else {
- body = "Received unknown type message.";
- }
-
- return new NotificationMessage {
- Summary = String.Format ("AudioStreamer ({0})", type),
- Body = body,
- Icon = "notification-message-im"
- };
- }
-
- private void sendHeartbeat (object state)
- {
- _websocket.Send (createTextMessage ("heartbeat", String.Empty));
- }
-
- public void Close ()
- {
- Disconnect ();
- _timer.Dispose ();
- _notifier.Close ();
- }
-
- public void Connect (string username)
- {
- _name = username;
- _websocket.Connect ();
- }
-
- public void Disconnect ()
- {
- _timer.Change (-1, -1);
- _websocket.Close (CloseStatusCode.Away);
- _audioBox.Clear ();
- _id = null;
- _name = null;
- }
-
- public void Write (string message)
- {
- _websocket.Send (createTextMessage ("message", message));
- }
-
- void IDisposable.Dispose ()
- {
- Close ();
- }
- }
-}
diff --git a/Example1/BinaryMessage.cs b/Example1/BinaryMessage.cs
deleted file mode 100644
index eafa7aa98..000000000
--- a/Example1/BinaryMessage.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-using WebSocketSharp;
-
-namespace Example1
-{
- internal class BinaryMessage
- {
- public uint UserID {
- get; set;
- }
-
- public byte ChannelNumber {
- get; set;
- }
-
- public uint BufferLength {
- get; set;
- }
-
- public float[,] BufferArray {
- get; set;
- }
-
- public static BinaryMessage Parse (byte[] data)
- {
- var id = data.SubArray (0, 4).To (ByteOrder.Big);
- var num = data.SubArray (4, 1)[0];
- var len = data.SubArray (5, 4).To (ByteOrder.Big);
- var arr = new float[num, len];
-
- var offset = 9;
- ((uint) num).Times (
- i =>
- len.Times (
- j => {
- arr[i, j] = data.SubArray (offset, 4).To (ByteOrder.Big);
- offset += 4;
- }
- )
- );
-
- return new BinaryMessage {
- UserID = id,
- ChannelNumber = num,
- BufferLength = len,
- BufferArray = arr
- };
- }
-
- public byte[] ToArray ()
- {
- var buff = new List ();
-
- var id = UserID;
- var num = ChannelNumber;
- var len = BufferLength;
- var arr = BufferArray;
-
- buff.AddRange (id.ToByteArray (ByteOrder.Big));
- buff.Add (num);
- buff.AddRange (len.ToByteArray (ByteOrder.Big));
-
- ((uint) num).Times (
- i =>
- len.Times (
- j => buff.AddRange (arr[i, j].ToByteArray (ByteOrder.Big))
- )
- );
-
- return buff.ToArray ();
- }
- }
-}
diff --git a/Example1/Example1.csproj b/Example1/Example1.csproj
deleted file mode 100644
index 81c52eff2..000000000
--- a/Example1/Example1.csproj
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
- Debug
- AnyCPU
- 9.0.21022
- 2.0
- {390E2568-57B7-4D17-91E5-C29336368CCF}
- Exe
- Example
- example1
- v3.5
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- true
-
-
- none
- false
- bin\Release
- prompt
- 4
- true
-
-
- true
- full
- false
- bin\Debug_Ubuntu
- DEBUG;UBUNTU
- prompt
- 4
- true
-
-
- none
- false
- bin\Release_Ubuntu
- prompt
- 4
- true
- UBUNTU
-
-
-
-
- False
- notify-sharp
-
-
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {B357BAC7-529E-4D81-A0D2-71041B19C8DE}
- websocket-sharp
-
-
-
\ No newline at end of file
diff --git a/Example1/NotificationMessage.cs b/Example1/NotificationMessage.cs
deleted file mode 100644
index 01f1692a8..000000000
--- a/Example1/NotificationMessage.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Example1
-{
- internal class NotificationMessage
- {
- public string Body {
- get; set;
- }
-
- public string Icon {
- get; set;
- }
-
- public string Summary {
- get; set;
- }
-
- public override string ToString ()
- {
- return String.Format ("{0}: {1}", Summary, Body);
- }
- }
-}
diff --git a/Example1/Notifier.cs b/Example1/Notifier.cs
deleted file mode 100644
index adf53ec9a..000000000
--- a/Example1/Notifier.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-#if UBUNTU
-using Notifications;
-#endif
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Example1
-{
- internal class Notifier : IDisposable
- {
- private volatile bool _enabled;
- private ManualResetEvent _exited;
- private Queue _queue;
- private object _sync;
-
- public Notifier ()
- {
- _enabled = true;
- _exited = new ManualResetEvent (false);
- _queue = new Queue ();
- _sync = ((ICollection) _queue).SyncRoot;
-
- ThreadPool.QueueUserWorkItem (
- state => {
- while (_enabled || Count > 0) {
- var msg = dequeue ();
- if (msg != null) {
-#if UBUNTU
- var nf = new Notification (msg.Summary, msg.Body, msg.Icon);
- nf.AddHint ("append", "allowed");
- nf.Show ();
-#else
- Console.WriteLine (msg);
-#endif
- }
- else {
- Thread.Sleep (500);
- }
- }
-
- _exited.Set ();
- }
- );
- }
-
- public int Count {
- get {
- lock (_sync)
- return _queue.Count;
- }
- }
-
- private NotificationMessage dequeue ()
- {
- lock (_sync)
- return _queue.Count > 0 ? _queue.Dequeue () : null;
- }
-
- public void Close ()
- {
- _enabled = false;
- _exited.WaitOne ();
- _exited.Close ();
- }
-
- public void Notify (NotificationMessage message)
- {
- lock (_sync) {
- if (_enabled)
- _queue.Enqueue (message);
- }
- }
-
- void IDisposable.Dispose ()
- {
- Close ();
- }
- }
-}
diff --git a/Example1/Program.cs b/Example1/Program.cs
deleted file mode 100644
index 88c0bedfe..000000000
--- a/Example1/Program.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Threading;
-
-namespace Example1
-{
- public class Program
- {
- public static void Main (string[] args)
- {
- // The AudioStreamer class provides a client (chat) for AudioStreamer
- // (https://github.com/agektmr/AudioStreamer).
-
- using (var streamer = new AudioStreamer ("ws://localhost:3000/socket"))
- {
- string name;
- do {
- Console.Write ("Input your name> ");
- name = Console.ReadLine ();
- }
- while (name.Length == 0);
-
- streamer.Connect (name);
-
- Console.WriteLine ("\nType 'exit' to exit.\n");
- while (true) {
- Thread.Sleep (1000);
- Console.Write ("> ");
- var msg = Console.ReadLine ();
- if (msg == "exit")
- break;
-
- streamer.Write (msg);
- }
- }
- }
- }
-}
diff --git a/Example1/TextMessage.cs b/Example1/TextMessage.cs
deleted file mode 100644
index 2b177d845..000000000
--- a/Example1/TextMessage.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Newtonsoft.Json;
-using System;
-
-namespace Example1
-{
- internal class TextMessage
- {
- [JsonProperty ("user_id")]
- public uint? UserID {
- get; set;
- }
-
- [JsonProperty ("name")]
- public string Name {
- get; set;
- }
-
- [JsonProperty ("type")]
- public string Type {
- get; set;
- }
-
- [JsonProperty ("message")]
- public string Message {
- get; set;
- }
-
- public override string ToString ()
- {
- return JsonConvert.SerializeObject (this);
- }
- }
-}
diff --git a/Example2/Chat.cs b/Example2/Chat.cs
index a6b367d96..2391bf313 100644
--- a/Example2/Chat.cs
+++ b/Example2/Chat.cs
@@ -12,18 +12,24 @@ public class Chat : WebSocketBehavior
private string _prefix;
public Chat ()
- : this (null)
{
+ _prefix = "anon#";
}
- public Chat (string prefix)
- {
- _prefix = !prefix.IsNullOrEmpty () ? prefix : "anon#";
+ public string Prefix {
+ get {
+ return _prefix;
+ }
+
+ set {
+ _prefix = !value.IsNullOrEmpty () ? value : "anon#";
+ }
}
private string getName ()
{
- var name = Context.QueryString["name"];
+ var name = QueryString["name"];
+
return !name.IsNullOrEmpty () ? name : _prefix + getNumber ();
}
@@ -34,17 +40,31 @@ private static int getNumber ()
protected override void OnClose (CloseEventArgs e)
{
- Sessions.Broadcast (String.Format ("{0} got logged off...", _name));
+ if (_name == null)
+ return;
+
+ var fmt = "{0} got logged off...";
+ var msg = String.Format (fmt, _name);
+
+ Sessions.Broadcast (msg);
}
protected override void OnMessage (MessageEventArgs e)
{
- Sessions.Broadcast (String.Format ("{0}: {1}", _name, e.Data));
+ var fmt = "{0}: {1}";
+ var msg = String.Format (fmt, _name, e.Data);
+
+ Sessions.Broadcast (msg);
}
protected override void OnOpen ()
{
_name = getName ();
+
+ var fmt = "{0} has logged in!";
+ var msg = String.Format (fmt, _name);
+
+ Sessions.Broadcast (msg);
}
}
}
diff --git a/Example2/Echo.cs b/Example2/Echo.cs
index dd780c8d1..edc8872f9 100644
--- a/Example2/Echo.cs
+++ b/Example2/Echo.cs
@@ -8,8 +8,7 @@ public class Echo : WebSocketBehavior
{
protected override void OnMessage (MessageEventArgs e)
{
- var name = Context.QueryString["name"];
- Send (!name.IsNullOrEmpty () ? String.Format ("\"{0}\" to {1}", e.Data, name) : e.Data);
+ Send (e.Data);
}
}
}
diff --git a/Example2/Program.cs b/Example2/Program.cs
index c9bd7ef3d..e83c762a4 100644
--- a/Example2/Program.cs
+++ b/Example2/Program.cs
@@ -14,8 +14,8 @@ public static void Main (string[] args)
// Create a new instance of the WebSocketServer class.
//
// If you would like to provide the secure connection, you should
- // create a new instance with the 'secure' parameter set to true,
- // or a wss scheme WebSocket URL.
+ // create a new instance with the 'secure' parameter set to true or
+ // with a wss scheme WebSocket URL.
var wssv = new WebSocketServer (4649);
//var wssv = new WebSocketServer (5963, true);
@@ -53,8 +53,8 @@ public static void Main (string[] args)
// To change the wait time for the response to the WebSocket Ping or Close.
//wssv.WaitTime = TimeSpan.FromSeconds (2);
- // Not to remove the inactive sessions periodically.
- //wssv.KeepClean = false;
+ // To remove the inactive sessions periodically.
+ //wssv.KeepClean = true;
#endif
// To provide the secure connection.
/*
@@ -73,7 +73,7 @@ public static void Main (string[] args)
// Return user name, password, and roles.
return name == "nobita"
? new NetworkCredential (name, "password", "gunfighter")
- : null; // If the user credentials aren't found.
+ : null; // If the user credentials are not found.
};
*/
@@ -88,40 +88,48 @@ public static void Main (string[] args)
/*
wssv.AddWebSocketService (
"/Chat",
- () =>
- new Chat ("Anon#") {
- // To send the Sec-WebSocket-Protocol header that has a subprotocol name.
- Protocol = "chat",
- // To ignore the Sec-WebSocket-Extensions header.
- IgnoreExtensions = true,
- // To emit a WebSocket.OnMessage event when receives a ping.
- EmitOnPing = true,
- // To validate the Origin header.
- OriginValidator = val => {
- // Check the value of the Origin header, and return true if valid.
- Uri origin;
- return !val.IsNullOrEmpty ()
- && Uri.TryCreate (val, UriKind.Absolute, out origin)
- && origin.Host == "localhost";
- },
- // To validate the cookies.
- CookiesValidator = (req, res) => {
- // Check the cookies in 'req', and set the cookies to send to
- // the client with 'res' if necessary.
- foreach (Cookie cookie in req) {
- cookie.Expired = true;
- res.Add (cookie);
- }
-
- return true; // If valid.
+ s => {
+ s.Prefix = "Anon#";
+
+ // To send the Sec-WebSocket-Protocol header that has a subprotocol name.
+ s.Protocol = "chat";
+
+ // To ignore the Sec-WebSocket-Extensions header.
+ s.IgnoreExtensions = true;
+
+ // To emit a WebSocket.OnMessage event when receives a ping.
+ s.EmitOnPing = true;
+
+ // To validate the Origin header.
+ s.OriginValidator = val => {
+ // Check the value of the Origin header, and return true if valid.
+ Uri origin;
+
+ return !val.IsNullOrEmpty ()
+ && Uri.TryCreate (val, UriKind.Absolute, out origin)
+ && origin.Host == "localhost";
+ };
+
+ // To validate the cookies.
+ s.CookiesValidator = (req, res) => {
+ // Check the cookies in 'req', and set the cookies to send to
+ // the client with 'res' if necessary.
+ foreach (var cookie in req) {
+ cookie.Expired = true;
+ res.Add (cookie);
}
- }
+
+ return true; // If valid.
+ };
+ }
);
*/
wssv.Start ();
+
if (wssv.IsListening) {
Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", wssv.Port);
+
foreach (var path in wssv.WebSocketServices.Paths)
Console.WriteLine ("- {0}", path);
}
diff --git a/Example3/Chat.cs b/Example3/Chat.cs
index b1a3f4d7d..0e3f38214 100644
--- a/Example3/Chat.cs
+++ b/Example3/Chat.cs
@@ -12,18 +12,24 @@ public class Chat : WebSocketBehavior
private string _prefix;
public Chat ()
- : this (null)
{
+ _prefix = "anon#";
}
- public Chat (string prefix)
- {
- _prefix = !prefix.IsNullOrEmpty () ? prefix : "anon#";
+ public string Prefix {
+ get {
+ return _prefix;
+ }
+
+ set {
+ _prefix = !value.IsNullOrEmpty () ? value : "anon#";
+ }
}
private string getName ()
{
- var name = Context.QueryString["name"];
+ var name = QueryString["name"];
+
return !name.IsNullOrEmpty () ? name : _prefix + getNumber ();
}
@@ -34,17 +40,31 @@ private static int getNumber ()
protected override void OnClose (CloseEventArgs e)
{
- Sessions.Broadcast (String.Format ("{0} got logged off...", _name));
+ if (_name == null)
+ return;
+
+ var fmt = "{0} got logged off...";
+ var msg = String.Format (fmt, _name);
+
+ Sessions.Broadcast (msg);
}
protected override void OnMessage (MessageEventArgs e)
{
- Sessions.Broadcast (String.Format ("{0}: {1}", _name, e.Data));
+ var fmt = "{0}: {1}";
+ var msg = String.Format (fmt, _name, e.Data);
+
+ Sessions.Broadcast (msg);
}
protected override void OnOpen ()
{
_name = getName ();
+
+ var fmt = "{0} has logged in!";
+ var msg = String.Format (fmt, _name);
+
+ Sessions.Broadcast (msg);
}
}
}
diff --git a/Example3/Echo.cs b/Example3/Echo.cs
index eb7c33410..01e2f16af 100644
--- a/Example3/Echo.cs
+++ b/Example3/Echo.cs
@@ -8,8 +8,7 @@ public class Echo : WebSocketBehavior
{
protected override void OnMessage (MessageEventArgs e)
{
- var name = Context.QueryString["name"];
- Send (!name.IsNullOrEmpty () ? String.Format ("\"{0}\" to {1}", e.Data, name) : e.Data);
+ Send (e.Data);
}
}
}
diff --git a/Example3/Program.cs b/Example3/Program.cs
index b9699531f..33744993b 100644
--- a/Example3/Program.cs
+++ b/Example3/Program.cs
@@ -15,8 +15,8 @@ public static void Main (string[] args)
// Create a new instance of the HttpServer class.
//
// If you would like to provide the secure connection, you should
- // create a new instance with the 'secure' parameter set to true,
- // or an https scheme HTTP URL.
+ // create a new instance with the 'secure' parameter set to true or
+ // with an https scheme HTTP URL.
var httpsv = new HttpServer (4649);
//var httpsv = new HttpServer (5963, true);
@@ -54,8 +54,8 @@ public static void Main (string[] args)
// To change the wait time for the response to the WebSocket Ping or Close.
//httpsv.WaitTime = TimeSpan.FromSeconds (2);
- // Not to remove the inactive WebSocket sessions periodically.
- //httpsv.KeepClean = false;
+ // To remove the inactive WebSocket sessions periodically.
+ //httpsv.KeepClean = true;
#endif
// To provide the secure connection.
/*
@@ -74,7 +74,7 @@ public static void Main (string[] args)
// Return user name, password, and roles.
return name == "nobita"
? new NetworkCredential (name, "password", "gunfighter")
- : null; // If the user credentials aren't found.
+ : null; // If the user credentials are not found.
};
*/
@@ -90,12 +90,15 @@ public static void Main (string[] args)
var res = e.Response;
var path = req.RawUrl;
+
if (path == "/")
path += "index.html";
byte[] contents;
+
if (!e.TryReadFile (path, out contents)) {
res.StatusCode = (int) HttpStatusCode.NotFound;
+
return;
}
@@ -109,6 +112,7 @@ public static void Main (string[] args)
}
res.ContentLength64 = contents.LongLength;
+
res.Close (contents, true);
};
@@ -120,40 +124,48 @@ public static void Main (string[] args)
/*
httpsv.AddWebSocketService (
"/Chat",
- () =>
- new Chat ("Anon#") {
- // To send the Sec-WebSocket-Protocol header that has a subprotocol name.
- Protocol = "chat",
- // To ignore the Sec-WebSocket-Extensions header.
- IgnoreExtensions = true,
- // To emit a WebSocket.OnMessage event when receives a ping.
- EmitOnPing = true,
- // To validate the Origin header.
- OriginValidator = val => {
- // Check the value of the Origin header, and return true if valid.
- Uri origin;
- return !val.IsNullOrEmpty ()
- && Uri.TryCreate (val, UriKind.Absolute, out origin)
- && origin.Host == "localhost";
- },
- // To validate the cookies.
- CookiesValidator = (req, res) => {
- // Check the cookies in 'req', and set the cookies to send to
- // the client with 'res' if necessary.
- foreach (Cookie cookie in req) {
- cookie.Expired = true;
- res.Add (cookie);
- }
-
- return true; // If valid.
+ s => {
+ s.Prefix = "Anon#";
+
+ // To send the Sec-WebSocket-Protocol header that has a subprotocol name.
+ s.Protocol = "chat";
+
+ // To ignore the Sec-WebSocket-Extensions header.
+ s.IgnoreExtensions = true;
+
+ // To emit a WebSocket.OnMessage event when receives a ping.
+ s.EmitOnPing = true;
+
+ // To validate the Origin header.
+ s.OriginValidator = val => {
+ // Check the value of the Origin header, and return true if valid.
+ Uri origin;
+
+ return !val.IsNullOrEmpty ()
+ && Uri.TryCreate (val, UriKind.Absolute, out origin)
+ && origin.Host == "localhost";
+ };
+
+ // To validate the cookies.
+ s.CookiesValidator = (req, res) => {
+ // Check the cookies in 'req', and set the cookies to send to
+ // the client with 'res' if necessary.
+ foreach (var cookie in req) {
+ cookie.Expired = true;
+ res.Add (cookie);
}
- }
+
+ return true; // If valid.
+ };
+ }
);
*/
httpsv.Start ();
+
if (httpsv.IsListening) {
Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", httpsv.Port);
+
foreach (var path in httpsv.WebSocketServices.Paths)
Console.WriteLine ("- {0}", path);
}
diff --git a/LICENSE.txt b/LICENSE.txt
index 71d3e3128..6903008b3 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2010-2020 sta.blockhead
+Copyright (c) 2010-2024 sta.blockhead
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index e7049b224..268712354 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ websocket-sharp supports:
- [HTTP Authentication](#http-authentication)
- [Query string, Origin header, and Cookies](#query-string-origin-header-and-cookies)
- [Connecting through the HTTP proxy server](#connecting-through-the-http-proxy-server)
-- .NET Framework **3.5** or later (includes compatible environment such as [Mono])
+- .NET Framework **3.5** or later versions of .NET Framework (includes compatible environment such as [Mono])
## Branches ##
@@ -43,25 +43,6 @@ You can add websocket-sharp to your project with the NuGet Package Manager, by u
PM> Install-Package WebSocketSharp -Pre
-### Unity Asset Store ###
-
-websocket-sharp is available on the Unity Asset Store (Sorry, Not available now).
-
-- [WebSocket-Sharp for Unity]
-
-It works with **Unity Free**, but there are some limitations:
-
-- [Security Sandbox of the Webplayer] (The server is not available in Web Player)
-- [WebGL Networking] (Not available in WebGL)
-- Incompatible platform (Not available for such UWP)
-- Lack of dll for the System.IO.Compression (The compression extension is not available on Windows)
-- .NET Socket Support for iOS/Android (iOS/Android Pro is required if your Unity is earlier than Unity 5)
-- .NET API 2.0 compatibility level for iOS/Android
-
-.NET API 2.0 compatibility level for iOS/Android may require to fix lack of some features for later than .NET Framework 2.0, such as the `System.Func<...>` delegates (so i have added them in the asset package).
-
-And it is priced at **US$15**. I believe your $15 makes this project more better, **Thank you!**
-
## Usage ##
### WebSocket Client ###
@@ -78,7 +59,7 @@ namespace Example
{
using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) {
ws.OnMessage += (sender, e) =>
- Console.WriteLine ("Laputa says: " + e.Data);
+ Console.WriteLine ("Laputa says: " + e.Data);
ws.Connect ();
ws.Send ("BALUS");
@@ -127,20 +108,20 @@ This event occurs when the WebSocket connection has been established.
```csharp
ws.OnOpen += (sender, e) => {
- ...
- };
+ ...
+ };
```
`System.EventArgs.Empty` is passed as `e`, so you do not need to use it.
##### WebSocket.OnMessage Event #####
-This event occurs when the `WebSocket` receives a message.
+This event occurs when the `WebSocket` instance receives a message.
```csharp
ws.OnMessage += (sender, e) => {
- ...
- };
+ ...
+ };
```
A `WebSocketSharp.MessageEventArgs` instance is passed as `e`.
@@ -172,23 +153,23 @@ And if you would like to notify that a **ping** has been received, via this even
```csharp
ws.EmitOnPing = true;
ws.OnMessage += (sender, e) => {
- if (e.IsPing) {
- // Do something to notify that a ping has been received.
- ...
+ if (e.IsPing) {
+ // Do something to notify that a ping has been received.
+ ...
- return;
- }
- };
+ return;
+ }
+ };
```
##### WebSocket.OnError Event #####
-This event occurs when the `WebSocket` gets an error.
+This event occurs when the `WebSocket` instance gets an error.
```csharp
ws.OnError += (sender, e) => {
- ...
- };
+ ...
+ };
```
A `WebSocketSharp.ErrorEventArgs` instance is passed as `e`.
@@ -205,8 +186,8 @@ This event occurs when the WebSocket connection has been closed.
```csharp
ws.OnClose += (sender, e) => {
- ...
- };
+ ...
+ };
```
A `WebSocketSharp.CloseEventArgs` instance is passed as `e`.
@@ -237,7 +218,7 @@ ws.Send (data);
The `WebSocket.Send` method is overloaded.
-You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte[])`, or `WebSocket.Send (System.IO.FileInfo)` method to send the data.
+You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte[])`, `WebSocket.Send (System.IO.FileInfo)`, or `WebSocket.Send (System.IO.Stream, int)` method to send the data.
If you would like to send the data asynchronously, you should use the `WebSocket.SendAsync` method.
@@ -277,7 +258,7 @@ namespace Example
protected override void OnMessage (MessageEventArgs e)
{
var msg = e.Data == "BALUS"
- ? "I've been balused already..."
+ ? "Are you kidding?"
: "I'm not available now.";
Send (msg);
@@ -289,6 +270,7 @@ namespace Example
public static void Main (string[] args)
{
var wssv = new WebSocketServer ("ws://dragonsnest.far");
+
wssv.AddWebSocketService ("/Laputa");
wssv.Start ();
Console.ReadKey (true);
@@ -340,13 +322,18 @@ public class Chat : WebSocketBehavior
private string _suffix;
public Chat ()
- : this (null)
{
+ _suffix = String.Empty;
}
- public Chat (string suffix)
- {
- _suffix = suffix ?? String.Empty;
+ public string Suffix {
+ get {
+ return _suffix;
+ }
+
+ set {
+ _suffix = value ?? String.Empty;
+ }
}
protected override void OnMessage (MessageEventArgs e)
@@ -374,16 +361,15 @@ Creating a new instance of the `WebSocketServer` class.
```csharp
var wssv = new WebSocketServer (4649);
+
wssv.AddWebSocketService ("/Echo");
wssv.AddWebSocketService ("/Chat");
-wssv.AddWebSocketService ("/ChatWithNyan", () => new Chat (" Nyan!"));
+wssv.AddWebSocketService ("/ChatWithNyan", s => s.Suffix = " Nyan!");
```
-You can add any WebSocket service to your `WebSocketServer` with the specified behavior and absolute path to the service, by using the `WebSocketServer.AddWebSocketService (string)` or `WebSocketServer.AddWebSocketService (string, Func)` method.
-
-The type of `TBehaviorWithNew` must inherit the `WebSocketBehavior` class, and must have a public parameterless constructor.
+You can add any WebSocket service to your `WebSocketServer` with the specified behavior and absolute path to the service, by using the `WebSocketServer.AddWebSocketService (string)` or `WebSocketServer.AddWebSocketService (string, Action)` method.
-The type of `TBehavior` must inherit the `WebSocketBehavior` class.
+The type of `TBehavior` must inherit the `WebSocketBehavior` class, and must have a public parameterless constructor.
So you can use a class in the above Step 2 to add the service.
@@ -404,26 +390,23 @@ wssv.Start ();
Stopping the WebSocket server.
```csharp
-wssv.Stop (code, reason);
+wssv.Stop ();
```
-The `WebSocketServer.Stop` method is overloaded.
-
-You can use the `WebSocketServer.Stop ()`, `WebSocketServer.Stop (ushort, string)`, or `WebSocketServer.Stop (WebSocketSharp.CloseStatusCode, string)` method to stop the server.
-
### HTTP Server with the WebSocket ###
I have modified the `System.Net.HttpListener`, `System.Net.HttpListenerContext`, and some other classes from **[Mono]** to create an HTTP server that allows to accept the WebSocket handshake requests.
So websocket-sharp provides the `WebSocketSharp.Server.HttpServer` class.
-You can add any WebSocket service to your `HttpServer` with the specified behavior and path to the service, by using the `HttpServer.AddWebSocketService (string)` or `HttpServer.AddWebSocketService (string, Func)` method.
+You can add any WebSocket service to your `HttpServer` with the specified behavior and path to the service, by using the `HttpServer.AddWebSocketService (string)` or `HttpServer.AddWebSocketService (string, Action)` method.
```csharp
var httpsv = new HttpServer (4649);
+
httpsv.AddWebSocketService ("/Echo");
httpsv.AddWebSocketService ("/Chat");
-httpsv.AddWebSocketService ("/ChatWithNyan", () => new Chat (" Nyan!"));
+httpsv.AddWebSocketService ("/ChatWithNyan", s => s.Suffix = " Nyan!");
```
For more information, would you see **[Example3]**?
@@ -432,7 +415,7 @@ For more information, would you see **[Example3]**?
#### Per-message Compression ####
-websocket-sharp supports the [Per-message Compression][compression] extension (but does not support it with the [context take over]).
+websocket-sharp supports the [Per-message Compression][rfc7692] extension (but does not support it with the [context take over]).
As a WebSocket client, if you would like to enable this extension, you should set the `WebSocket.Compression` property to a compression method before calling the connect method.
@@ -455,11 +438,7 @@ As a WebSocket server, if you would like to ignore the extensions requested from
```csharp
wssv.AddWebSocketService (
"/Chat",
- () =>
- new Chat () {
- // To ignore the extensions requested from a client.
- IgnoreExtensions = true
- }
+ s => s.IgnoreExtensions = true // To ignore the extensions requested from a client.
);
```
@@ -495,8 +474,9 @@ As a WebSocket server, you should create a new instance of the `WebSocketServer`
```csharp
var wssv = new WebSocketServer (5963, true);
-wssv.SslConfiguration.ServerCertificate =
- new X509Certificate2 ("/path/to/cert.pfx", "password for cert.pfx");
+wssv.SslConfiguration.ServerCertificate = new X509Certificate2 (
+ "/path/to/cert.pfx", "password for cert.pfx"
+ );
```
### HTTP Authentication ###
@@ -542,19 +522,19 @@ As a WebSocket client, if you would like to send the query string in the handsha
var ws = new WebSocket ("ws://example.com/?name=nobita");
```
-If you would like to send the Origin header in the handshake request, you should set the `WebSocket.Origin` property to an allowable value as the [Origin] header before calling the connect method.
+And if you would like to send the Origin header in the handshake request, you should set the `WebSocket.Origin` property to an allowable value as the [Origin] header before calling the connect method.
```csharp
ws.Origin = "http://example.com";
```
-And if you would like to send the cookies in the handshake request, you should set any cookie by using the `WebSocket.SetCookie (WebSocketSharp.Net.Cookie)` method before calling the connect method.
+And also if you would like to send the cookies in the handshake request, you should set any cookie by using the `WebSocket.SetCookie (WebSocketSharp.Net.Cookie)` method before calling the connect method.
```csharp
ws.SetCookie (new Cookie ("name", "nobita"));
```
-As a WebSocket server, if you would like to get the query string included in a handshake request, you should access the `WebSocketBehavior.Context.QueryString` property, such as the following.
+As a WebSocket server, if you would like to get the query string included in a handshake request, you should access the `WebSocketBehavior.QueryString` property, such as the following.
```csharp
public class Chat : WebSocketBehavior
@@ -564,42 +544,44 @@ public class Chat : WebSocketBehavior
protected override void OnOpen ()
{
- _name = Context.QueryString["name"];
+ _name = QueryString["name"];
}
...
}
```
-If you would like to get the value of the Origin header included in a handshake request, you should access the `WebSocketBehavior.Context.Origin` property.
-
-If you would like to get the cookies included in a handshake request, you should access the `WebSocketBehavior.Context.CookieCollection` property.
-
-And if you would like to validate the Origin header, cookies, or both, you should set each validation for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService (string, Func)` method with initializing, such as the following.
+And if you would like to validate the Origin header, cookies, or both, you should set each validation for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService (string, Action)` method with initializing, such as the following.
```csharp
wssv.AddWebSocketService (
"/Chat",
- () =>
- new Chat () {
- OriginValidator = val => {
- // Check the value of the Origin header, and return true if valid.
- Uri origin;
- return !val.IsNullOrEmpty ()
- && Uri.TryCreate (val, UriKind.Absolute, out origin)
- && origin.Host == "example.com";
- },
- CookiesValidator = (req, res) => {
- // Check the cookies in 'req', and set the cookies to send to
- // the client with 'res' if necessary.
- foreach (Cookie cookie in req) {
- cookie.Expired = true;
- res.Add (cookie);
- }
-
- return true; // If valid.
+ s => {
+ s.OriginValidator =
+ val => {
+ // Check the value of the Origin header, and return true if valid.
+
+ Uri origin;
+
+ return !val.IsNullOrEmpty ()
+ && Uri.TryCreate (val, UriKind.Absolute, out origin)
+ && origin.Host == "example.com";
+ };
+
+ s.CookiesValidator =
+ (req, res) => {
+ // Check the cookies in "req", and set the cookies to send to
+ // the client with "res" if necessary.
+
+ foreach (var cookie in req) {
+ cookie.Expired = true;
+
+ res.Add (cookie);
}
- }
+
+ return true; // If valid.
+ };
+ }
);
```
@@ -649,7 +631,7 @@ Examples using websocket-sharp.
### Example ###
-[Example] connects to the [Echo server].
+[Example] connects to the server executed by [Example2] or [Example3].
### Example2 ###
@@ -667,7 +649,7 @@ websocket-sharp supports **RFC 6455**, and it is based on the following referenc
- [The WebSocket Protocol][rfc6455]
- [The WebSocket API][api]
-- [Compression Extensions for WebSocket][compression]
+- [Compression Extensions for WebSocket][rfc7692]
Thanks for translating to japanese.
@@ -679,7 +661,6 @@ Thanks for translating to japanese.
websocket-sharp is provided under [The MIT License].
-[Echo server]: http://www.websocket.org/echo.html
[Example]: https://github.com/sta/websocket-sharp/tree/master/Example
[Example2]: https://github.com/sta/websocket-sharp/tree/master/Example2
[Example3]: https://github.com/sta/websocket-sharp/tree/master/Example3
@@ -689,16 +670,12 @@ websocket-sharp is provided under [The MIT License].
[NuGet Gallery: websocket-sharp]: http://www.nuget.org/packages/WebSocketSharp
[Origin]: http://tools.ietf.org/html/rfc6454#section-7
[Query]: http://tools.ietf.org/html/rfc3986#section-3.4
-[Security Sandbox of the Webplayer]: http://docs.unity3d.com/Manual/SecuritySandbox.html
[Squid]: http://www.squid-cache.org
[The MIT License]: https://raw.github.com/sta/websocket-sharp/master/LICENSE.txt
[Unity]: http://unity3d.com
-[WebGL Networking]: http://docs.unity3d.com/Manual/webgl-networking.html
-[WebSocket-Sharp for Unity]: http://u3d.as/content/sta-blockhead/websocket-sharp-for-unity
[api]: http://www.w3.org/TR/websockets
[api_ja]: http://www.hcn.zaq.ne.jp/___/WEB/WebSocket-ja.html
-[compression]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19
-[context take over]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19#section-8.1.1
+[context take over]: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1.1
[draft-hixie-thewebsocketprotocol-75]: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
[draft-ietf-hybi-thewebsocketprotocol-00]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
[draft75]: https://github.com/sta/websocket-sharp/tree/draft75
@@ -707,3 +684,4 @@ websocket-sharp is provided under [The MIT License].
[rfc2617]: http://tools.ietf.org/html/rfc2617
[rfc6455]: http://tools.ietf.org/html/rfc6455
[rfc6455_ja]: http://www.hcn.zaq.ne.jp/___/WEB/RFC6455-ja.html
+[rfc7692]: https://datatracker.ietf.org/doc/html/rfc7692
diff --git a/websocket-sharp.sln b/websocket-sharp.sln
index 3c20e06a0..6ff1c0362 100644
--- a/websocket-sharp.sln
+++ b/websocket-sharp.sln
@@ -5,8 +5,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocke
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example1", "Example1\Example1.csproj", "{390E2568-57B7-4D17-91E5-C29336368CCF}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2", "Example2\Example2.csproj", "{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3", "Example3\Example3.csproj", "{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}"
@@ -19,14 +17,6 @@ Global
Release_Ubuntu|Any CPU = Release_Ubuntu|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.Build.0 = Release|Any CPU
{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU
{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU
{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
diff --git a/websocket-sharp/CloseEventArgs.cs b/websocket-sharp/CloseEventArgs.cs
index 8127ce418..50b01ce32 100644
--- a/websocket-sharp/CloseEventArgs.cs
+++ b/websocket-sharp/CloseEventArgs.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2019 sta.blockhead
+ * Copyright (c) 2012-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,11 +35,12 @@ namespace WebSocketSharp
///
///
///
- /// That event occurs when the WebSocket connection has been closed.
+ /// The close event occurs when the WebSocket connection has been closed.
///
///
- /// If you would like to get the reason for the connection close, you should
- /// access the or property.
+ /// If you would like to get the reason for the connection close,
+ /// you should access the or
+ /// property.
///
///
public class CloseEventArgs : EventArgs
@@ -59,12 +60,6 @@ internal CloseEventArgs (PayloadData payloadData, bool clean)
_clean = clean;
}
- internal CloseEventArgs (ushort code, string reason, bool clean)
- {
- _payloadData = new PayloadData (code, reason);
- _clean = clean;
- }
-
#endregion
#region Public Properties
@@ -73,8 +68,13 @@ internal CloseEventArgs (ushort code, string reason, bool clean)
/// Gets the status code for the connection close.
///
///
- /// A that represents the status code for
- /// the connection close if present.
+ ///
+ /// A that represents the status code for
+ /// the connection close.
+ ///
+ ///
+ /// 1005 (no status) if not present.
+ ///
///
public ushort Code {
get {
@@ -86,8 +86,13 @@ public ushort Code {
/// Gets the reason for the connection close.
///
///
- /// A that represents the reason for
- /// the connection close if present.
+ ///
+ /// A that represents the reason for
+ /// the connection close.
+ ///
+ ///
+ /// An empty string if not present.
+ ///
///
public string Reason {
get {
diff --git a/websocket-sharp/ErrorEventArgs.cs b/websocket-sharp/ErrorEventArgs.cs
index 41502ab08..c02d0e000 100644
--- a/websocket-sharp/ErrorEventArgs.cs
+++ b/websocket-sharp/ErrorEventArgs.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2016 sta.blockhead
+ * Copyright (c) 2012-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -42,14 +42,15 @@ namespace WebSocketSharp
///
///
///
- /// That event occurs when the gets an error.
+ /// The error event occurs when the interface
+ /// gets an error.
///
///
/// If you would like to get the error message, you should access
/// the property.
///
///
- /// And if the error is due to an exception, you can get it by accessing
+ /// If the error is due to an exception, you can get it by accessing
/// the property.
///
///
@@ -83,8 +84,13 @@ internal ErrorEventArgs (string message, Exception exception)
/// Gets the exception that caused the error.
///
///
- /// An instance that represents the cause of
- /// the error if it is due to an exception; otherwise, .
+ ///
+ /// An instance that represents
+ /// the cause of the error.
+ ///
+ ///
+ /// if not present.
+ ///
///
public Exception Exception {
get {
diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs
index 027a9b437..0df7f3c25 100644
--- a/websocket-sharp/Ext.cs
+++ b/websocket-sharp/Ext.cs
@@ -3,9 +3,9 @@
* Ext.cs
*
* Some parts of this code are derived from Mono (http://www.mono-project.com):
- * - GetStatusDescription is derived from HttpListenerResponse.cs (System.Net)
- * - IsPredefinedScheme is derived from Uri.cs (System)
- * - MaybeUri is derived from Uri.cs (System)
+ * - The GetStatusDescription method is derived from HttpListenerResponse.cs (System.Net)
+ * - The MaybeUri method is derived from Uri.cs (System)
+ * - The isPredefinedScheme method is derived from Uri.cs (System)
*
* The MIT License
*
@@ -14,7 +14,7 @@
* Copyright (c) 2003 Ben Maurer
* Copyright (c) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
* Copyright (c) 2009 Stephane Delcroix
- * Copyright (c) 2010-2016 sta.blockhead
+ * Copyright (c) 2010-2023 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -67,7 +67,7 @@ public static class Ext
#region Private Fields
private static readonly byte[] _last = new byte[] { 0x00 };
- private static readonly int _retry = 5;
+ private static readonly int _maxRetry = 5;
private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
#endregion
@@ -77,7 +77,6 @@ public static class Ext
private static byte[] compress (this byte[] data)
{
if (data.LongLength == 0)
- //return new byte[] { 0x00, 0x00, 0x00, 0xff, 0xff };
return data;
using (var input = new MemoryStream (data))
@@ -86,18 +85,23 @@ private static byte[] compress (this byte[] data)
private static MemoryStream compress (this Stream stream)
{
- var output = new MemoryStream ();
+ var ret = new MemoryStream ();
+
if (stream.Length == 0)
- return output;
+ return ret;
stream.Position = 0;
- using (var ds = new DeflateStream (output, CompressionMode.Compress, true)) {
+
+ var mode = CompressionMode.Compress;
+
+ using (var ds = new DeflateStream (ret, mode, true)) {
stream.CopyTo (ds, 1024);
ds.Close (); // BFINAL set to 1.
- output.Write (_last, 0, 1);
- output.Position = 0;
+ ret.Write (_last, 0, 1);
+
+ ret.Position = 0;
- return output;
+ return ret;
}
}
@@ -105,6 +109,7 @@ private static byte[] compressToArray (this Stream stream)
{
using (var output = stream.compress ()) {
output.Close ();
+
return output.ToArray ();
}
}
@@ -120,16 +125,21 @@ private static byte[] decompress (this byte[] data)
private static MemoryStream decompress (this Stream stream)
{
- var output = new MemoryStream ();
+ var ret = new MemoryStream ();
+
if (stream.Length == 0)
- return output;
+ return ret;
stream.Position = 0;
- using (var ds = new DeflateStream (stream, CompressionMode.Decompress, true)) {
- ds.CopyTo (output, 1024);
- output.Position = 0;
- return output;
+ var mode = CompressionMode.Decompress;
+
+ using (var ds = new DeflateStream (stream, mode, true)) {
+ ds.CopyTo (ret, 1024);
+
+ ret.Position = 0;
+
+ return ret;
}
}
@@ -137,27 +147,39 @@ private static byte[] decompressToArray (this Stream stream)
{
using (var output = stream.decompress ()) {
output.Close ();
+
return output.ToArray ();
}
}
- private static bool isHttpMethod (this string value)
+ private static bool isPredefinedScheme (this string value)
{
- return value == "GET"
- || value == "HEAD"
- || value == "POST"
- || value == "PUT"
- || value == "DELETE"
- || value == "CONNECT"
- || value == "OPTIONS"
- || value == "TRACE";
- }
+ var c = value[0];
- private static bool isHttpMethod10 (this string value)
- {
- return value == "GET"
- || value == "HEAD"
- || value == "POST";
+ if (c == 'h')
+ return value == "http" || value == "https";
+
+ if (c == 'w')
+ return value == "ws" || value == "wss";
+
+ if (c == 'f')
+ return value == "file" || value == "ftp";
+
+ if (c == 'g')
+ return value == "gopher";
+
+ if (c == 'm')
+ return value == "mailto";
+
+ if (c == 'n') {
+ c = value[1];
+
+ return c == 'e'
+ ? value == "news" || value == "net.pipe" || value == "net.tcp"
+ : value == "nntp";
+ }
+
+ return false;
}
#endregion
@@ -166,69 +188,35 @@ private static bool isHttpMethod10 (this string value)
internal static byte[] Append (this ushort code, string reason)
{
- var bytes = code.InternalToByteArray (ByteOrder.Big);
+ var codeAsBytes = code.ToByteArray (ByteOrder.Big);
if (reason == null || reason.Length == 0)
- return bytes;
+ return codeAsBytes;
+
+ var buff = new List (codeAsBytes);
+ var reasonAsBytes = Encoding.UTF8.GetBytes (reason);
- var buff = new List (bytes);
- buff.AddRange (Encoding.UTF8.GetBytes (reason));
+ buff.AddRange (reasonAsBytes);
return buff.ToArray ();
}
- internal static void Close (
- this HttpListenerResponse response, HttpStatusCode code
+ internal static byte[] Compress (
+ this byte[] data,
+ CompressionMethod method
)
{
- response.StatusCode = (int) code;
- response.OutputStream.Close ();
+ return method == CompressionMethod.Deflate ? data.compress () : data;
}
- internal static void CloseWithAuthChallenge (
- this HttpListenerResponse response, string challenge
+ internal static Stream Compress (
+ this Stream stream,
+ CompressionMethod method
)
{
- response.Headers.InternalSet ("WWW-Authenticate", challenge, true);
- response.Close (HttpStatusCode.Unauthorized);
- }
-
- internal static byte[] Compress (this byte[] data, CompressionMethod method)
- {
- return method == CompressionMethod.Deflate
- ? data.compress ()
- : data;
- }
-
- internal static Stream Compress (this Stream stream, CompressionMethod method)
- {
- return method == CompressionMethod.Deflate
- ? stream.compress ()
- : stream;
- }
-
- internal static byte[] CompressToArray (this Stream stream, CompressionMethod method)
- {
- return method == CompressionMethod.Deflate
- ? stream.compressToArray ()
- : stream.ToByteArray ();
+ return method == CompressionMethod.Deflate ? stream.compress () : stream;
}
- ///
- /// Determines whether the specified string contains any of characters in
- /// the specified array of .
- ///
- ///
- /// true if contains any of characters in
- /// ; otherwise, false.
- ///
- ///
- /// A to test.
- ///
- ///
- /// An array of that contains one or more characters to
- /// seek.
- ///
internal static bool Contains (this string value, params char[] anyOf)
{
return anyOf != null && anyOf.Length > 0
@@ -237,7 +225,8 @@ internal static bool Contains (this string value, params char[] anyOf)
}
internal static bool Contains (
- this NameValueCollection collection, string name
+ this NameValueCollection collection,
+ string name
)
{
return collection[name] != null;
@@ -251,6 +240,7 @@ StringComparison comparisonTypeForValue
)
{
var val = collection[name];
+
if (val == null)
return false;
@@ -263,7 +253,8 @@ StringComparison comparisonTypeForValue
}
internal static bool Contains (
- this IEnumerable source, Func condition
+ this IEnumerable source,
+ Func condition
)
{
foreach (T elm in source) {
@@ -285,6 +276,7 @@ internal static bool ContainsTwice (this string[] values)
return false;
var val = values[idx];
+
for (var i = idx + 1; i < len; i++) {
if (values[i] == val)
return true;
@@ -296,41 +288,45 @@ internal static bool ContainsTwice (this string[] values)
return seek (0);
}
- internal static T[] Copy (this T[] source, int length)
+ internal static T[] Copy (this T[] sourceArray, int length)
{
var dest = new T[length];
- Array.Copy (source, 0, dest, 0, length);
+
+ Array.Copy (sourceArray, 0, dest, 0, length);
return dest;
}
- internal static T[] Copy (this T[] source, long length)
+ internal static T[] Copy (this T[] sourceArray, long length)
{
var dest = new T[length];
- Array.Copy (source, 0, dest, 0, length);
+
+ Array.Copy (sourceArray, 0, dest, 0, length);
return dest;
}
internal static void CopyTo (
- this Stream source, Stream destination, int bufferLength
+ this Stream sourceStream,
+ Stream destinationStream,
+ int bufferLength
)
{
var buff = new byte[bufferLength];
- var nread = 0;
while (true) {
- nread = source.Read (buff, 0, bufferLength);
+ var nread = sourceStream.Read (buff, 0, bufferLength);
+
if (nread <= 0)
break;
- destination.Write (buff, 0, nread);
+ destinationStream.Write (buff, 0, nread);
}
}
internal static void CopyToAsync (
- this Stream source,
- Stream destination,
+ this Stream sourceStream,
+ Stream destinationStream,
int bufferLength,
Action completed,
Action error
@@ -342,7 +338,8 @@ Action error
callback =
ar => {
try {
- var nread = source.EndRead (ar);
+ var nread = sourceStream.EndRead (ar);
+
if (nread <= 0) {
if (completed != null)
completed ();
@@ -350,8 +347,9 @@ Action error
return;
}
- destination.Write (buff, 0, nread);
- source.BeginRead (buff, 0, bufferLength, callback, null);
+ destinationStream.Write (buff, 0, nread);
+
+ sourceStream.BeginRead (buff, 0, bufferLength, callback, null);
}
catch (Exception ex) {
if (error != null)
@@ -360,7 +358,7 @@ Action error
};
try {
- source.BeginRead (buff, 0, bufferLength, callback, null);
+ sourceStream.BeginRead (buff, 0, bufferLength, callback, null);
}
catch (Exception ex) {
if (error != null)
@@ -368,21 +366,28 @@ Action error
}
}
- internal static byte[] Decompress (this byte[] data, CompressionMethod method)
+ internal static byte[] Decompress (
+ this byte[] data,
+ CompressionMethod method
+ )
{
- return method == CompressionMethod.Deflate
- ? data.decompress ()
- : data;
+ return method == CompressionMethod.Deflate ? data.decompress () : data;
}
- internal static Stream Decompress (this Stream stream, CompressionMethod method)
+ internal static Stream Decompress (
+ this Stream stream,
+ CompressionMethod method
+ )
{
return method == CompressionMethod.Deflate
? stream.decompress ()
: stream;
}
- internal static byte[] DecompressToArray (this Stream stream, CompressionMethod method)
+ internal static byte[] DecompressToArray (
+ this Stream stream,
+ CompressionMethod method
+ )
{
return method == CompressionMethod.Deflate
? stream.decompressToArray ()
@@ -390,7 +395,9 @@ internal static byte[] DecompressToArray (this Stream stream, CompressionMethod
}
internal static void Emit (
- this EventHandler eventHandler, object sender, EventArgs e
+ this EventHandler eventHandler,
+ object sender,
+ EventArgs e
)
{
if (eventHandler == null)
@@ -400,7 +407,9 @@ internal static void Emit (
}
internal static void Emit (
- this EventHandler eventHandler, object sender, TEventArgs e
+ this EventHandler eventHandler,
+ object sender,
+ TEventArgs e
)
where TEventArgs : EventArgs
{
@@ -410,59 +419,29 @@ internal static void Emit (
eventHandler (sender, e);
}
- ///
- /// Determines whether the specified equals the specified ,
- /// and invokes the specified Action<int> delegate at the same time.
- ///
- ///
- /// true if equals ;
- /// otherwise, false.
- ///
- ///
- /// An to compare.
- ///
- ///
- /// A to compare.
- ///
- ///
- /// An Action<int> delegate that references the method(s) called
- /// at the same time as comparing. An parameter to pass to
- /// the method(s) is .
- ///
- internal static bool EqualsWith (this int value, char c, Action action)
- {
- action (value);
- return value == c - 0;
- }
-
- ///
- /// Gets the absolute path from the specified .
- ///
- ///
- /// A that represents the absolute path if it's successfully found;
- /// otherwise, .
- ///
- ///
- /// A that represents the URI to get the absolute path from.
- ///
internal static string GetAbsolutePath (this Uri uri)
{
if (uri.IsAbsoluteUri)
return uri.AbsolutePath;
var original = uri.OriginalString;
+
if (original[0] != '/')
return null;
var idx = original.IndexOfAny (new[] { '?', '#' });
+
return idx > 0 ? original.Substring (0, idx) : original;
}
internal static CookieCollection GetCookies (
- this NameValueCollection headers, bool response
+ this NameValueCollection headers,
+ bool response
)
{
- var val = headers[response ? "Set-Cookie" : "Cookie"];
+ var name = response ? "Set-Cookie" : "Cookie";
+ var val = headers[name];
+
return val != null
? CookieCollection.Parse (val, response)
: new CookieCollection ();
@@ -475,184 +454,130 @@ internal static string GetDnsSafeHost (this Uri uri, bool bracketIPv6)
: uri.DnsSafeHost;
}
- internal static string GetMessage (this CloseStatusCode code)
- {
- return code == CloseStatusCode.ProtocolError
- ? "A WebSocket protocol error has occurred."
- : code == CloseStatusCode.UnsupportedData
- ? "Unsupported data has been received."
- : code == CloseStatusCode.Abnormal
- ? "An exception has occurred."
- : code == CloseStatusCode.InvalidData
- ? "Invalid data has been received."
- : code == CloseStatusCode.PolicyViolation
- ? "A policy violation has occurred."
- : code == CloseStatusCode.TooBig
- ? "A too big message has been received."
- : code == CloseStatusCode.MandatoryExtension
- ? "WebSocket client didn't receive expected extension(s)."
- : code == CloseStatusCode.ServerError
- ? "WebSocket server got an internal error."
- : code == CloseStatusCode.TlsHandshakeFailure
- ? "An error has occurred during a TLS handshake."
- : String.Empty;
+ internal static string GetErrorMessage (this ushort code)
+ {
+ switch (code) {
+ case 1002:
+ return "A protocol error has occurred.";
+ case 1003:
+ return "Unsupported data has been received.";
+ case 1006:
+ return "An abnormal error has occurred.";
+ case 1007:
+ return "Invalid data has been received.";
+ case 1008:
+ return "A policy violation has occurred.";
+ case 1009:
+ return "A too big message has been received.";
+ case 1010:
+ return "The client did not receive expected extension(s).";
+ case 1011:
+ return "The server got an internal error.";
+ case 1015:
+ return "An error has occurred during a TLS handshake.";
+ default:
+ return String.Empty;
+ }
+ }
+
+ internal static string GetErrorMessage (this CloseStatusCode code)
+ {
+ return ((ushort) code).GetErrorMessage ();
}
- ///
- /// Gets the name from the specified string that contains a pair of
- /// name and value separated by a character.
- ///
- ///
- ///
- /// A that represents the name.
- ///
- ///
- /// if the name is not present.
- ///
- ///
- ///
- /// A that contains a pair of name and value.
- ///
- ///
- /// A used to separate name and value.
- ///
internal static string GetName (this string nameAndValue, char separator)
{
var idx = nameAndValue.IndexOf (separator);
+
return idx > 0 ? nameAndValue.Substring (0, idx).Trim () : null;
}
internal static string GetUTF8DecodedString (this byte[] bytes)
{
- return Encoding.UTF8.GetString (bytes);
+ try {
+ return Encoding.UTF8.GetString (bytes);
+ }
+ catch {
+ return null;
+ }
}
internal static byte[] GetUTF8EncodedBytes (this string s)
{
- return Encoding.UTF8.GetBytes (s);
+ try {
+ return Encoding.UTF8.GetBytes (s);
+ }
+ catch {
+ return null;
+ }
}
- ///
- /// Gets the value from the specified string that contains a pair of
- /// name and value separated by a character.
- ///
- ///
- ///
- /// A that represents the value.
- ///
- ///
- /// if the value is not present.
- ///
- ///
- ///
- /// A that contains a pair of name and value.
- ///
- ///
- /// A used to separate name and value.
- ///
internal static string GetValue (this string nameAndValue, char separator)
{
return nameAndValue.GetValue (separator, false);
}
- ///
- /// Gets the value from the specified string that contains a pair of
- /// name and value separated by a character.
- ///
- ///
- ///
- /// A that represents the value.
- ///
- ///
- /// if the value is not present.
- ///
- ///
- ///
- /// A that contains a pair of name and value.
- ///
- ///
- /// A used to separate name and value.
- ///
- ///
- /// A : true if unquotes the value; otherwise,
- /// false.
- ///
internal static string GetValue (
- this string nameAndValue, char separator, bool unquote
+ this string nameAndValue,
+ char separator,
+ bool unquote
)
{
var idx = nameAndValue.IndexOf (separator);
+
if (idx < 0 || idx == nameAndValue.Length - 1)
return null;
var val = nameAndValue.Substring (idx + 1).Trim ();
- return unquote ? val.Unquote () : val;
- }
-
- internal static byte[] InternalToByteArray (
- this ushort value, ByteOrder order
- )
- {
- var ret = BitConverter.GetBytes (value);
-
- if (!order.IsHostOrder ())
- Array.Reverse (ret);
-
- return ret;
- }
-
- internal static byte[] InternalToByteArray (
- this ulong value, ByteOrder order
- )
- {
- var ret = BitConverter.GetBytes (value);
-
- if (!order.IsHostOrder ())
- Array.Reverse (ret);
- return ret;
+ return unquote ? val.Unquote () : val;
}
internal static bool IsCompressionExtension (
- this string value, CompressionMethod method
+ this string value,
+ CompressionMethod method
)
{
- return value.StartsWith (method.ToExtensionString ());
- }
+ var extStr = method.ToExtensionString ();
+ var compType = StringComparison.Ordinal;
- internal static bool IsControl (this byte opcode)
- {
- return opcode > 0x7 && opcode < 0x10;
+ return value.StartsWith (extStr, compType);
}
- internal static bool IsControl (this Opcode opcode)
+ internal static bool IsEqualTo (
+ this int value,
+ char c,
+ Action beforeComparing
+ )
{
- return opcode >= Opcode.Close;
- }
+ beforeComparing (value);
- internal static bool IsData (this byte opcode)
- {
- return opcode == 0x1 || opcode == 0x2;
+ return value == c - 0;
}
- internal static bool IsData (this Opcode opcode)
+ internal static bool IsHttpMethod (this string value)
{
- return opcode == Opcode.Text || opcode == Opcode.Binary;
+ return value == "GET"
+ || value == "HEAD"
+ || value == "POST"
+ || value == "PUT"
+ || value == "DELETE"
+ || value == "CONNECT"
+ || value == "OPTIONS"
+ || value == "TRACE";
}
- internal static bool IsHttpMethod (this string value, Version version)
+ internal static bool IsPortNumber (this int value)
{
- return version == HttpVersion.Version10
- ? value.isHttpMethod10 ()
- : value.isHttpMethod ();
+ return value > 0 && value < 65536;
}
- internal static bool IsPortNumber (this int value)
+ internal static bool IsReserved (this CloseStatusCode code)
{
- return value > 0 && value < 65536;
+ return ((ushort) code).IsReservedStatusCode ();
}
- internal static bool IsReserved (this ushort code)
+ internal static bool IsReservedStatusCode (this ushort code)
{
return code == 1004
|| code == 1005
@@ -660,15 +585,7 @@ internal static bool IsReserved (this ushort code)
|| code == 1015;
}
- internal static bool IsReserved (this CloseStatusCode code)
- {
- return code == CloseStatusCode.Undefined
- || code == CloseStatusCode.NoStatus
- || code == CloseStatusCode.Abnormal
- || code == CloseStatusCode.TlsHandshakeFailure;
- }
-
- internal static bool IsSupported (this byte opcode)
+ internal static bool IsSupportedOpcode (this byte opcode)
{
return Enum.IsDefined (typeof (Opcode), opcode);
}
@@ -679,16 +596,19 @@ internal static bool IsText (this string value)
for (var i = 0; i < len; i++) {
var c = value[i];
+
if (c < 0x20) {
if ("\r\n\t".IndexOf (c) == -1)
return false;
if (c == '\n') {
i++;
+
if (i == len)
break;
c = value[i];
+
if (" \t".IndexOf (c) == -1)
return false;
}
@@ -720,36 +640,55 @@ internal static bool IsToken (this string value)
}
internal static bool KeepsAlive (
- this NameValueCollection headers, Version version
+ this NameValueCollection headers,
+ Version version
)
{
- var comparison = StringComparison.OrdinalIgnoreCase;
- return version < HttpVersion.Version11
- ? headers.Contains ("Connection", "keep-alive", comparison)
- : !headers.Contains ("Connection", "close", comparison);
+ var compType = StringComparison.OrdinalIgnoreCase;
+
+ return version > HttpVersion.Version10
+ ? !headers.Contains ("Connection", "close", compType)
+ : headers.Contains ("Connection", "keep-alive", compType);
+ }
+
+ internal static bool MaybeUri (this string value)
+ {
+ var idx = value.IndexOf (':');
+
+ if (idx < 2 || idx > 9)
+ return false;
+
+ var schm = value.Substring (0, idx);
+
+ return schm.isPredefinedScheme ();
}
internal static string Quote (this string value)
{
- return String.Format ("\"{0}\"", value.Replace ("\"", "\\\""));
+ var fmt = "\"{0}\"";
+ var val = value.Replace ("\"", "\\\"");
+
+ return String.Format (fmt, val);
}
internal static byte[] ReadBytes (this Stream stream, int length)
{
- var buff = new byte[length];
+ var ret = new byte[length];
+
var offset = 0;
var retry = 0;
- var nread = 0;
while (length > 0) {
- nread = stream.Read (buff, offset, length);
+ var nread = stream.Read (ret, offset, length);
+
if (nread <= 0) {
- if (retry < _retry) {
+ if (retry < _maxRetry) {
retry++;
+
continue;
}
- return buff.SubArray (0, offset);
+ return ret.SubArray (0, offset);
}
retry = 0;
@@ -758,26 +697,29 @@ internal static byte[] ReadBytes (this Stream stream, int length)
length -= nread;
}
- return buff;
+ return ret;
}
internal static byte[] ReadBytes (
- this Stream stream, long length, int bufferLength
+ this Stream stream,
+ long length,
+ int bufferLength
)
{
using (var dest = new MemoryStream ()) {
var buff = new byte[bufferLength];
var retry = 0;
- var nread = 0;
while (length > 0) {
if (length < bufferLength)
bufferLength = (int) length;
- nread = stream.Read (buff, 0, bufferLength);
+ var nread = stream.Read (buff, 0, bufferLength);
+
if (nread <= 0) {
- if (retry < _retry) {
+ if (retry < _maxRetry) {
retry++;
+
continue;
}
@@ -787,10 +729,12 @@ internal static byte[] ReadBytes (
retry = 0;
dest.Write (buff, 0, nread);
+
length -= nread;
}
dest.Close ();
+
return dest.ToArray ();
}
}
@@ -802,7 +746,8 @@ internal static void ReadBytesAsync (
Action error
)
{
- var buff = new byte[length];
+ var ret = new byte[length];
+
var offset = 0;
var retry = 0;
@@ -811,23 +756,25 @@ Action error
ar => {
try {
var nread = stream.EndRead (ar);
+
if (nread <= 0) {
- if (retry < _retry) {
+ if (retry < _maxRetry) {
retry++;
- stream.BeginRead (buff, offset, length, callback, null);
+
+ stream.BeginRead (ret, offset, length, callback, null);
return;
}
if (completed != null)
- completed (buff.SubArray (0, offset));
+ completed (ret.SubArray (0, offset));
return;
}
if (nread == length) {
if (completed != null)
- completed (buff);
+ completed (ret);
return;
}
@@ -837,7 +784,7 @@ Action error
offset += nread;
length -= nread;
- stream.BeginRead (buff, offset, length, callback, null);
+ stream.BeginRead (ret, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null)
@@ -846,7 +793,7 @@ Action error
};
try {
- stream.BeginRead (buff, offset, length, callback, null);
+ stream.BeginRead (ret, offset, length, callback, null);
}
catch (Exception ex) {
if (error != null)
@@ -863,6 +810,7 @@ Action error
)
{
var dest = new MemoryStream ();
+
var buff = new byte[bufferLength];
var retry = 0;
@@ -879,9 +827,11 @@ Action error
ar => {
try {
var nread = stream.EndRead (ar);
+
if (nread <= 0) {
- if (retry < _retry) {
+ if (retry < _maxRetry) {
retry++;
+
read (len);
return;
@@ -889,10 +839,14 @@ Action error
if (completed != null) {
dest.Close ();
- completed (dest.ToArray ());
+
+ var ret = dest.ToArray ();
+
+ completed (ret);
}
dest.Dispose ();
+
return;
}
@@ -901,10 +855,14 @@ Action error
if (nread == len) {
if (completed != null) {
dest.Close ();
- completed (dest.ToArray ());
+
+ var ret = dest.ToArray ();
+
+ completed (ret);
}
dest.Dispose ();
+
return;
}
@@ -914,6 +872,7 @@ Action error
}
catch (Exception ex) {
dest.Dispose ();
+
if (error != null)
error (ex);
}
@@ -927,6 +886,7 @@ Action error
}
catch (Exception ex) {
dest.Dispose ();
+
if (error != null)
error (ex);
}
@@ -934,38 +894,43 @@ Action error
internal static T[] Reverse (this T[] array)
{
- var len = array.Length;
+ var len = array.LongLength;
var ret = new T[len];
var end = len - 1;
- for (var i = 0; i <= end; i++)
+
+ for (long i = 0; i <= end; i++)
ret[i] = array[end - i];
return ret;
}
internal static IEnumerable SplitHeaderValue (
- this string value, params char[] separators
+ this string value,
+ params char[] separators
)
{
var len = value.Length;
+ var end = len - 1;
var buff = new StringBuilder (32);
- var end = len - 1;
var escaped = false;
var quoted = false;
for (var i = 0; i <= end; i++) {
var c = value[i];
+
buff.Append (c);
if (c == '"') {
if (escaped) {
escaped = false;
+
continue;
}
quoted = !quoted;
+
continue;
}
@@ -984,9 +949,11 @@ internal static IEnumerable SplitHeaderValue (
continue;
buff.Length -= 1;
+
yield return buff.ToString ();
buff.Length = 0;
+
continue;
}
}
@@ -996,18 +963,40 @@ internal static IEnumerable SplitHeaderValue (
internal static byte[] ToByteArray (this Stream stream)
{
- using (var output = new MemoryStream ()) {
- stream.Position = 0;
- stream.CopyTo (output, 1024);
- output.Close ();
+ stream.Position = 0;
- return output.ToArray ();
+ using (var buff = new MemoryStream ()) {
+ stream.CopyTo (buff, 1024);
+ buff.Close ();
+
+ return buff.ToArray ();
}
}
+ internal static byte[] ToByteArray (this ushort value, ByteOrder order)
+ {
+ var ret = BitConverter.GetBytes (value);
+
+ if (!order.IsHostOrder ())
+ Array.Reverse (ret);
+
+ return ret;
+ }
+
+ internal static byte[] ToByteArray (this ulong value, ByteOrder order)
+ {
+ var ret = BitConverter.GetBytes (value);
+
+ if (!order.IsHostOrder ())
+ Array.Reverse (ret);
+
+ return ret;
+ }
+
internal static CompressionMethod ToCompressionMethod (this string value)
{
var methods = Enum.GetValues (typeof (CompressionMethod));
+
foreach (CompressionMethod method in methods) {
if (method.ToExtensionString () == value)
return method;
@@ -1017,19 +1006,27 @@ internal static CompressionMethod ToCompressionMethod (this string value)
}
internal static string ToExtensionString (
- this CompressionMethod method, params string[] parameters
+ this CompressionMethod method,
+ params string[] parameters
)
{
if (method == CompressionMethod.None)
return String.Empty;
- var name = String.Format (
- "permessage-{0}", method.ToString ().ToLower ()
- );
+ var name = method.ToString ().ToLower ();
+ var ename = String.Format ("permessage-{0}", name);
+
+ if (parameters == null || parameters.Length == 0)
+ return ename;
- return parameters != null && parameters.Length > 0
- ? String.Format ("{0}; {1}", name, parameters.ToString ("; "))
- : name;
+ var eparams = parameters.ToString ("; ");
+
+ return String.Format ("{0}; {1}", ename, eparams);
+ }
+
+ internal static int ToInt32 (this string numericString)
+ {
+ return Int32.Parse (numericString);
}
internal static System.Net.IPAddress ToIPAddress (this string value)
@@ -1038,11 +1035,13 @@ internal static System.Net.IPAddress ToIPAddress (this string value)
return null;
System.Net.IPAddress addr;
+
if (System.Net.IPAddress.TryParse (value, out addr))
return addr;
try {
var addrs = System.Net.Dns.GetHostAddresses (value);
+
return addrs[0];
}
catch {
@@ -1058,26 +1057,38 @@ this IEnumerable source
}
internal static string ToString (
- this System.Net.IPAddress address, bool bracketIPv6
+ this System.Net.IPAddress address,
+ bool bracketIPv6
)
{
return bracketIPv6
&& address.AddressFamily == AddressFamily.InterNetworkV6
- ? String.Format ("[{0}]", address.ToString ())
+ ? String.Format ("[{0}]", address)
: address.ToString ();
}
internal static ushort ToUInt16 (this byte[] source, ByteOrder sourceOrder)
{
- return BitConverter.ToUInt16 (source.ToHostOrder (sourceOrder), 0);
+ var val = source.ToHostOrder (sourceOrder);
+
+ return BitConverter.ToUInt16 (val, 0);
}
internal static ulong ToUInt64 (this byte[] source, ByteOrder sourceOrder)
{
- return BitConverter.ToUInt64 (source.ToHostOrder (sourceOrder), 0);
+ var val = source.ToHostOrder (sourceOrder);
+
+ return BitConverter.ToUInt64 (val, 0);
}
- internal static IEnumerable Trim (this IEnumerable source)
+ internal static Version ToVersion (this string versionString)
+ {
+ return new Version (versionString);
+ }
+
+ internal static IEnumerable TrimEach (
+ this IEnumerable source
+ )
{
foreach (var elm in source)
yield return elm.Trim ();
@@ -1086,17 +1097,20 @@ internal static IEnumerable Trim (this IEnumerable source)
internal static string TrimSlashFromEnd (this string value)
{
var ret = value.TrimEnd ('/');
+
return ret.Length > 0 ? ret : "/";
}
internal static string TrimSlashOrBackslashFromEnd (this string value)
{
var ret = value.TrimEnd ('/', '\\');
+
return ret.Length > 0 ? ret : value[0].ToString ();
}
internal static bool TryCreateVersion (
- this string versionString, out Version result
+ this string versionString,
+ out Version result
)
{
result = null;
@@ -1111,78 +1125,75 @@ internal static bool TryCreateVersion (
return true;
}
- ///
- /// Tries to create a new for WebSocket with
- /// the specified .
- ///
- ///
- /// true if the was successfully created;
- /// otherwise, false.
- ///
- ///
- /// A that represents a WebSocket URL to try.
- ///
- ///
- /// When this method returns, a that
- /// represents the WebSocket URL or
- /// if is invalid.
- ///
- ///
- /// When this method returns, a that
- /// represents an error message or
- /// if is valid.
- ///
internal static bool TryCreateWebSocketUri (
- this string uriString, out Uri result, out string message
+ this string uriString,
+ out Uri result,
+ out string message
)
{
result = null;
message = null;
var uri = uriString.ToUri ();
+
if (uri == null) {
message = "An invalid URI string.";
+
return false;
}
if (!uri.IsAbsoluteUri) {
message = "A relative URI.";
+
return false;
}
var schm = uri.Scheme;
- if (!(schm == "ws" || schm == "wss")) {
- message = "The scheme part is not 'ws' or 'wss'.";
+ var valid = schm == "ws" || schm == "wss";
+
+ if (!valid) {
+ message = "The scheme part is not \"ws\" or \"wss\".";
+
return false;
}
var port = uri.Port;
+
if (port == 0) {
message = "The port part is zero.";
+
return false;
}
if (uri.Fragment.Length > 0) {
message = "It includes the fragment component.";
+
return false;
}
- result = port != -1
- ? uri
- : new Uri (
- String.Format (
- "{0}://{1}:{2}{3}",
- schm,
- uri.Host,
- schm == "ws" ? 80 : 443,
- uri.PathAndQuery
- )
- );
+ if (port == -1) {
+ port = schm == "ws" ? 80 : 443;
+ uriString = String.Format (
+ "{0}://{1}:{2}{3}",
+ schm,
+ uri.Host,
+ port,
+ uri.PathAndQuery
+ );
+
+ result = new Uri (uriString);
+ }
+ else {
+ result = uri;
+ }
return true;
}
- internal static bool TryGetUTF8DecodedString (this byte[] bytes, out string s)
+ internal static bool TryGetUTF8DecodedString (
+ this byte[] bytes,
+ out string s
+ )
{
s = null;
@@ -1196,7 +1207,10 @@ internal static bool TryGetUTF8DecodedString (this byte[] bytes, out string s)
return true;
}
- internal static bool TryGetUTF8EncodedBytes (this string s, out byte[] bytes)
+ internal static bool TryGetUTF8EncodedBytes (
+ this string s,
+ out byte[] bytes
+ )
{
bytes = null;
@@ -1211,7 +1225,8 @@ internal static bool TryGetUTF8EncodedBytes (this string s, out byte[] bytes)
}
internal static bool TryOpenRead (
- this FileInfo fileInfo, out FileStream fileStream
+ this FileInfo fileInfo,
+ out FileStream fileStream
)
{
fileStream = null;
@@ -1228,32 +1243,39 @@ internal static bool TryOpenRead (
internal static string Unquote (this string value)
{
- var start = value.IndexOf ('"');
- if (start == -1)
+ var first = value.IndexOf ('"');
+
+ if (first == -1)
return value;
- var end = value.LastIndexOf ('"');
- if (end == start)
+ var last = value.LastIndexOf ('"');
+
+ if (last == first)
return value;
- var len = end - start - 1;
+ var len = last - first - 1;
+
return len > 0
- ? value.Substring (start + 1, len).Replace ("\\\"", "\"")
+ ? value.Substring (first + 1, len).Replace ("\\\"", "\"")
: String.Empty;
}
internal static bool Upgrades (
- this NameValueCollection headers, string protocol
+ this NameValueCollection headers,
+ string protocol
)
{
- var comparison = StringComparison.OrdinalIgnoreCase;
- return headers.Contains ("Upgrade", protocol, comparison)
- && headers.Contains ("Connection", "Upgrade", comparison);
+ var compType = StringComparison.OrdinalIgnoreCase;
+
+ return headers.Contains ("Upgrade", protocol, compType)
+ && headers.Contains ("Connection", "Upgrade", compType);
}
internal static string UrlDecode (this string value, Encoding encoding)
{
- return HttpUtility.UrlDecode (value, encoding);
+ return value.IndexOfAny (new[] { '%', '+' }) > -1
+ ? HttpUtility.UrlDecode (value, encoding)
+ : value;
}
internal static string UrlEncode (this string value, Encoding encoding)
@@ -1262,7 +1284,9 @@ internal static string UrlEncode (this string value, Encoding encoding)
}
internal static void WriteBytes (
- this Stream stream, byte[] bytes, int bufferLength
+ this Stream stream,
+ byte[] bytes,
+ int bufferLength
)
{
using (var src = new MemoryStream (bytes))
@@ -1278,6 +1302,7 @@ Action error
)
{
var src = new MemoryStream (bytes);
+
src.CopyToAsync (
stream,
bufferLength,
@@ -1289,6 +1314,7 @@ Action error
},
ex => {
src.Dispose ();
+
if (error != null)
error (ex);
}
@@ -1448,10 +1474,8 @@ public static bool IsEnclosedIn (this string value, char c)
return false;
var len = value.Length;
- if (len < 2)
- return false;
- return value[0] == c && value[len - 1] == c;
+ return len > 1 ? value[0] == c && value[len - 1] == c : false;
}
///
@@ -1507,8 +1531,9 @@ public static bool IsLocal (this System.Net.IPAddress address)
return true;
}
- var host = System.Net.Dns.GetHostName ();
- var addrs = System.Net.Dns.GetHostAddresses (host);
+ var name = System.Net.Dns.GetHostName ();
+ var addrs = System.Net.Dns.GetHostAddresses (name);
+
foreach (var addr in addrs) {
if (address.Equals (addr))
return true;
@@ -1533,76 +1558,6 @@ public static bool IsNullOrEmpty (this string value)
return value == null || value.Length == 0;
}
- ///
- /// Determines whether the specified string is a predefined scheme.
- ///
- ///
- /// true if is a predefined scheme;
- /// otherwise, false.
- ///
- ///
- /// A to test.
- ///
- public static bool IsPredefinedScheme (this string value)
- {
- if (value == null || value.Length < 2)
- return false;
-
- var c = value[0];
- if (c == 'h')
- return value == "http" || value == "https";
-
- if (c == 'w')
- return value == "ws" || value == "wss";
-
- if (c == 'f')
- return value == "file" || value == "ftp";
-
- if (c == 'g')
- return value == "gopher";
-
- if (c == 'm')
- return value == "mailto";
-
- if (c == 'n') {
- c = value[1];
- return c == 'e'
- ? value == "news" || value == "net.pipe" || value == "net.tcp"
- : value == "nntp";
- }
-
- return false;
- }
-
- ///
- /// Determines whether the specified string is a URI string.
- ///
- ///
- /// true if may be a URI string;
- /// otherwise, false.
- ///
- ///
- /// A to test.
- ///
- public static bool MaybeUri (this string value)
- {
- if (value == null)
- return false;
-
- if (value.Length == 0)
- return false;
-
- var idx = value.IndexOf (':');
- if (idx == -1)
- return false;
-
- if (idx >= 10)
- return false;
-
- var schm = value.Substring (0, idx);
- return schm.IsPredefinedScheme ();
- }
-
///
/// Retrieves a sub-array from the specified array. A sub-array starts at
/// the specified index in the array.
@@ -1614,11 +1569,11 @@ public static bool MaybeUri (this string value)
/// An array of T from which to retrieve a sub-array.
///
///
- /// An that represents the zero-based index in the array
+ /// An that specifies the zero-based index in the array
/// at which retrieving starts.
///
///
- /// An that represents the number of elements to retrieve.
+ /// An that specifies the number of elements to retrieve.
///
///
/// The type of elements in the array.
@@ -1656,6 +1611,7 @@ public static T[] SubArray (this T[] array, int startIndex, int length)
throw new ArgumentNullException ("array");
var len = array.Length;
+
if (len == 0) {
if (startIndex != 0)
throw new ArgumentOutOfRangeException ("startIndex");
@@ -1678,10 +1634,11 @@ public static T[] SubArray (this T[] array, int startIndex, int length)
if (length == len)
return array;
- var subArray = new T[length];
- Array.Copy (array, startIndex, subArray, 0, length);
+ var ret = new T[length];
- return subArray;
+ Array.Copy (array, startIndex, ret, 0, length);
+
+ return ret;
}
///
@@ -1695,11 +1652,11 @@ public static T[] SubArray (this T[] array, int startIndex, int length)
/// An array of T from which to retrieve a sub-array.
///
///
- /// A that represents the zero-based index in the array
+ /// A that specifies the zero-based index in the array
/// at which retrieving starts.
///
///
- /// A that represents the number of elements to retrieve.
+ /// A that specifies the number of elements to retrieve.
///
///
/// The type of elements in the array.
@@ -1737,6 +1694,7 @@ public static T[] SubArray (this T[] array, long startIndex, long length)
throw new ArgumentNullException ("array");
var len = array.LongLength;
+
if (len == 0) {
if (startIndex != 0)
throw new ArgumentOutOfRangeException ("startIndex");
@@ -1759,94 +1717,11 @@ public static T[] SubArray (this T[] array, long startIndex, long length)
if (length == len)
return array;
- var subArray = new T[length];
- Array.Copy (array, startIndex, subArray, 0, length);
+ var ret = new T[length];
- return subArray;
- }
+ Array.Copy (array, startIndex, ret, 0, length);
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// An that specifies the number of times to execute.
- ///
- ///
- /// An delegate to execute.
- ///
- public static void Times (this int n, Action action)
- {
- if (n <= 0)
- return;
-
- if (action == null)
- return;
-
- for (int i = 0; i < n; i++)
- action ();
- }
-
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// A that specifies the number of times to execute.
- ///
- ///
- /// An delegate to execute.
- ///
- public static void Times (this long n, Action action)
- {
- if (n <= 0)
- return;
-
- if (action == null)
- return;
-
- for (long i = 0; i < n; i++)
- action ();
- }
-
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// A that specifies the number of times to execute.
- ///
- ///
- /// An delegate to execute.
- ///
- public static void Times (this uint n, Action action)
- {
- if (n == 0)
- return;
-
- if (action == null)
- return;
-
- for (uint i = 0; i < n; i++)
- action ();
- }
-
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// A that specifies the number of times to execute.
- ///
- ///
- /// An delegate to execute.
- ///
- public static void Times (this ulong n, Action action)
- {
- if (n == 0)
- return;
-
- if (action == null)
- return;
-
- for (ulong i = 0; i < n; i++)
- action ();
+ return ret;
}
///
@@ -1901,195 +1776,6 @@ public static void Times (this long n, Action action)
action (i);
}
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// A that specifies the number of times to execute.
- ///
- ///
- ///
- /// An Action<uint> delegate to execute.
- ///
- ///
- /// The parameter is the zero-based count of iteration.
- ///
- ///
- public static void Times (this uint n, Action action)
- {
- if (n == 0)
- return;
-
- if (action == null)
- return;
-
- for (uint i = 0; i < n; i++)
- action (i);
- }
-
- ///
- /// Executes the specified delegate times.
- ///
- ///
- /// A that specifies the number of times to execute.
- ///
- ///
- ///
- /// An Action<ulong> delegate to execute.
- ///
- ///
- /// The parameter is the zero-based count of iteration.
- ///
- ///
- public static void Times (this ulong n, Action action)
- {
- if (n == 0)
- return;
-
- if (action == null)
- return;
-
- for (ulong i = 0; i < n; i++)
- action (i);
- }
-
- ///
- /// Converts the specified byte array to the specified type value.
- ///
- ///
- ///
- /// A T converted from .
- ///
- ///
- /// The default value of T if not converted.
- ///
- ///
- ///
- /// An array of to convert.
- ///
- ///
- ///
- /// One of the enum values.
- ///
- ///
- /// It specifies the byte order of .
- ///
- ///
- ///
- ///
- /// The type of the return.
- ///
- ///
- /// , , ,
- /// , , ,
- /// , , ,
- /// or .
- ///
- ///
- ///
- /// is .
- ///
- [Obsolete ("This method will be removed.")]
- public static T To (this byte[] source, ByteOrder sourceOrder)
- where T : struct
- {
- if (source == null)
- throw new ArgumentNullException ("source");
-
- if (source.Length == 0)
- return default (T);
-
- var type = typeof (T);
- var val = source.ToHostOrder (sourceOrder);
-
- return type == typeof (Boolean)
- ? (T)(object) BitConverter.ToBoolean (val, 0)
- : type == typeof (Char)
- ? (T)(object) BitConverter.ToChar (val, 0)
- : type == typeof (Double)
- ? (T)(object) BitConverter.ToDouble (val, 0)
- : type == typeof (Int16)
- ? (T)(object) BitConverter.ToInt16 (val, 0)
- : type == typeof (Int32)
- ? (T)(object) BitConverter.ToInt32 (val, 0)
- : type == typeof (Int64)
- ? (T)(object) BitConverter.ToInt64 (val, 0)
- : type == typeof (Single)
- ? (T)(object) BitConverter.ToSingle (val, 0)
- : type == typeof (UInt16)
- ? (T)(object) BitConverter.ToUInt16 (val, 0)
- : type == typeof (UInt32)
- ? (T)(object) BitConverter.ToUInt32 (val, 0)
- : type == typeof (UInt64)
- ? (T)(object) BitConverter.ToUInt64 (val, 0)
- : default (T);
- }
-
- ///
- /// Converts the specified value to a byte array.
- ///
- ///
- /// An array of converted from .
- ///
- ///
- /// A T to convert.
- ///
- ///
- ///
- /// One of the enum values.
- ///
- ///
- /// It specifies the byte order of the return.
- ///
- ///
- ///
- ///
- /// The type of .
- ///
- ///
- /// , , ,
- /// , , ,
- /// , , ,
- /// , or .
- ///
- ///
- [Obsolete ("This method will be removed.")]
- public static byte[] ToByteArray (this T value, ByteOrder order)
- where T : struct
- {
- var type = typeof (T);
- var bytes = type == typeof (Boolean)
- ? BitConverter.GetBytes ((Boolean)(object) value)
- : type == typeof (Byte)
- ? new byte[] { (Byte)(object) value }
- : type == typeof (Char)
- ? BitConverter.GetBytes ((Char)(object) value)
- : type == typeof (Double)
- ? BitConverter.GetBytes ((Double)(object) value)
- : type == typeof (Int16)
- ? BitConverter.GetBytes ((Int16)(object) value)
- : type == typeof (Int32)
- ? BitConverter.GetBytes ((Int32)(object) value)
- : type == typeof (Int64)
- ? BitConverter.GetBytes ((Int64)(object) value)
- : type == typeof (Single)
- ? BitConverter.GetBytes ((Single)(object) value)
- : type == typeof (UInt16)
- ? BitConverter.GetBytes ((UInt16)(object) value)
- : type == typeof (UInt32)
- ? BitConverter.GetBytes ((UInt32)(object) value)
- : type == typeof (UInt64)
- ? BitConverter.GetBytes ((UInt64)(object) value)
- : WebSocket.EmptyBytes;
-
- if (bytes.Length > 1) {
- if (!order.IsHostOrder ())
- Array.Reverse (bytes);
- }
-
- return bytes;
- }
-
///
/// Converts the order of elements in the specified byte array to
/// host (this computer architecture) byte order.
@@ -2164,19 +1850,18 @@ public static string ToString (this T[] array, string separator)
throw new ArgumentNullException ("array");
var len = array.Length;
+
if (len == 0)
return String.Empty;
- if (separator == null)
- separator = String.Empty;
-
var buff = new StringBuilder (64);
var end = len - 1;
for (var i = 0; i < end; i++)
buff.AppendFormat ("{0}{1}", array[i], separator);
- buff.Append (array[end].ToString ());
+ buff.AppendFormat ("{0}", array[end]);
+
return buff.ToString ();
}
@@ -2196,62 +1881,15 @@ public static string ToString (this T[] array, string separator)
///
public static Uri ToUri (this string value)
{
- Uri ret;
- Uri.TryCreate (
- value, value.MaybeUri () ? UriKind.Absolute : UriKind.Relative, out ret
- );
-
- return ret;
- }
-
- ///
- /// Sends the specified content data with the HTTP response.
- ///
- ///
- /// A that represents the HTTP response
- /// used to send the content data.
- ///
- ///
- /// An array of that specifies the content data to send.
- ///
- ///
- ///
- /// is .
- ///
- ///
- /// -or-
- ///
- ///
- /// is .
- ///
- ///
- [Obsolete ("This method will be removed.")]
- public static void WriteContent (
- this HttpListenerResponse response, byte[] content
- )
- {
- if (response == null)
- throw new ArgumentNullException ("response");
-
- if (content == null)
- throw new ArgumentNullException ("content");
-
- var len = content.LongLength;
- if (len == 0) {
- response.Close ();
- return;
- }
-
- response.ContentLength64 = len;
+ if (value == null || value.Length == 0)
+ return null;
- var output = response.OutputStream;
+ var kind = value.MaybeUri () ? UriKind.Absolute : UriKind.Relative;
+ Uri ret;
- if (len <= Int32.MaxValue)
- output.Write (content, 0, (int) len);
- else
- output.WriteBytes (content, 1024);
+ Uri.TryCreate (value, kind, out ret);
- output.Close ();
+ return ret;
}
#endregion
diff --git a/websocket-sharp/HttpBase.cs b/websocket-sharp/HttpBase.cs
index a7dbd4026..c4a244f45 100644
--- a/websocket-sharp/HttpBase.cs
+++ b/websocket-sharp/HttpBase.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2014 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,6 +30,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading;
using WebSocketSharp.Net;
@@ -41,20 +42,31 @@ internal abstract class HttpBase
#region Private Fields
private NameValueCollection _headers;
- private const int _headersMaxLength = 8192;
+ private static readonly int _maxMessageHeaderLength;
+ private string _messageBody;
+ private byte[] _messageBodyData;
private Version _version;
#endregion
- #region Internal Fields
+ #region Protected Fields
- internal byte[] EntityBodyData;
+ protected static readonly string CrLf;
+ protected static readonly string CrLfHt;
+ protected static readonly string CrLfSp;
#endregion
- #region Protected Fields
+ #region Static Constructor
- protected const string CrLf = "\r\n";
+ static HttpBase ()
+ {
+ _maxMessageHeaderLength = 8192;
+
+ CrLf = "\r\n";
+ CrLfHt = "\r\n\t";
+ CrLfSp = "\r\n ";
+ }
#endregion
@@ -68,20 +80,40 @@ protected HttpBase (Version version, NameValueCollection headers)
#endregion
- #region Public Properties
+ #region Internal Properties
- public string EntityBody {
+ internal byte[] MessageBodyData {
get {
- if (EntityBodyData == null || EntityBodyData.LongLength == 0)
- return String.Empty;
+ return _messageBodyData;
+ }
+ }
+
+ #endregion
+
+ #region Protected Properties
+
+ protected string HeaderSection {
+ get {
+ var buff = new StringBuilder (64);
+
+ var fmt = "{0}: {1}{2}";
+
+ foreach (var key in _headers.AllKeys)
+ buff.AppendFormat (fmt, key, _headers[key], CrLf);
+
+ buff.Append (CrLf);
+
+ return buff.ToString ();
+ }
+ }
- Encoding enc = null;
+ #endregion
- var contentType = _headers["Content-Type"];
- if (contentType != null && contentType.Length > 0)
- enc = HttpUtility.GetEncoding (contentType);
+ #region Public Properties
- return (enc ?? Encoding.UTF8).GetString (EntityBodyData);
+ public bool HasMessageBody {
+ get {
+ return _messageBodyData != null;
}
}
@@ -91,6 +123,17 @@ public NameValueCollection Headers {
}
}
+ public string MessageBody {
+ get {
+ if (_messageBody == null)
+ _messageBody = getMessageBody ();
+
+ return _messageBody;
+ }
+ }
+
+ public abstract string MessageHeader { get; }
+
public Version ProtocolVersion {
get {
return _version;
@@ -101,14 +144,35 @@ public Version ProtocolVersion {
#region Private Methods
- private static byte[] readEntityBody (Stream stream, string length)
+ private string getMessageBody ()
+ {
+ if (_messageBodyData == null || _messageBodyData.LongLength == 0)
+ return String.Empty;
+
+ var contentType = _headers["Content-Type"];
+
+ var enc = contentType != null && contentType.Length > 0
+ ? HttpUtility.GetEncoding (contentType)
+ : Encoding.UTF8;
+
+ return enc.GetString (_messageBodyData);
+ }
+
+ private static byte[] readMessageBodyFrom (Stream stream, string length)
{
long len;
- if (!Int64.TryParse (length, out len))
- throw new ArgumentException ("Cannot be parsed.", "length");
- if (len < 0)
- throw new ArgumentOutOfRangeException ("length", "Less than zero.");
+ if (!Int64.TryParse (length, out len)) {
+ var msg = "It could not be parsed.";
+
+ throw new ArgumentException (msg, "length");
+ }
+
+ if (len < 0) {
+ var msg = "Less than zero.";
+
+ throw new ArgumentOutOfRangeException ("length", msg);
+ }
return len > 1024
? stream.ReadBytes (len, 1024)
@@ -117,62 +181,93 @@ private static byte[] readEntityBody (Stream stream, string length)
: null;
}
- private static string[] readHeaders (Stream stream, int maxLength)
+ private static string[] readMessageHeaderFrom (Stream stream)
{
var buff = new List ();
var cnt = 0;
- Action add = i => {
- if (i == -1)
- throw new EndOfStreamException ("The header cannot be read from the data source.");
-
- buff.Add ((byte) i);
- cnt++;
- };
-
- var read = false;
- while (cnt < maxLength) {
- if (stream.ReadByte ().EqualsWith ('\r', add) &&
- stream.ReadByte ().EqualsWith ('\n', add) &&
- stream.ReadByte ().EqualsWith ('\r', add) &&
- stream.ReadByte ().EqualsWith ('\n', add)) {
- read = true;
- break;
+ Action add =
+ i => {
+ if (i == -1) {
+ var msg = "The header could not be read from the data stream.";
+
+ throw new EndOfStreamException (msg);
+ }
+
+ buff.Add ((byte) i);
+
+ cnt++;
+ };
+
+ var end = false;
+
+ do {
+ end = stream.ReadByte ().IsEqualTo ('\r', add)
+ && stream.ReadByte ().IsEqualTo ('\n', add)
+ && stream.ReadByte ().IsEqualTo ('\r', add)
+ && stream.ReadByte ().IsEqualTo ('\n', add);
+
+ if (cnt > _maxMessageHeaderLength) {
+ var msg = "The length of the header is greater than the max length.";
+
+ throw new InvalidOperationException (msg);
}
}
+ while (!end);
- if (!read)
- throw new WebSocketException ("The length of header part is greater than the max length.");
+ var bytes = buff.ToArray ();
- return Encoding.UTF8.GetString (buff.ToArray ())
- .Replace (CrLf + " ", " ")
- .Replace (CrLf + "\t", " ")
+ return Encoding.UTF8.GetString (bytes)
+ .Replace (CrLfSp, " ")
+ .Replace (CrLfHt, " ")
.Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries);
}
#endregion
+ #region Internal Methods
+
+ internal void WriteTo (Stream stream)
+ {
+ var bytes = ToByteArray ();
+
+ stream.Write (bytes, 0, bytes.Length);
+ }
+
+ #endregion
+
#region Protected Methods
- protected static T Read (Stream stream, Func parser, int millisecondsTimeout)
+ protected static T Read (
+ Stream stream,
+ Func parser,
+ int millisecondsTimeout
+ )
where T : HttpBase
{
+ T ret = null;
+
var timeout = false;
var timer = new Timer (
- state => {
- timeout = true;
- stream.Close ();
- },
- null,
- millisecondsTimeout,
- -1);
-
- T http = null;
+ state => {
+ timeout = true;
+
+ stream.Close ();
+ },
+ null,
+ millisecondsTimeout,
+ -1
+ );
+
Exception exception = null;
+
try {
- http = parser (readHeaders (stream, _headersMaxLength));
- var contentLen = http.Headers["Content-Length"];
+ var header = readMessageHeaderFrom (stream);
+ ret = parser (header);
+
+ var contentLen = ret.Headers["Content-Length"];
+
if (contentLen != null && contentLen.Length > 0)
- http.EntityBodyData = readEntityBody (stream, contentLen);
+ ret._messageBodyData = readMessageBodyFrom (stream, contentLen);
}
catch (Exception ex) {
exception = ex;
@@ -182,16 +277,19 @@ protected static T Read (Stream stream, Func parser, int millise
timer.Dispose ();
}
- var msg = timeout
- ? "A timeout has occurred while reading an HTTP request/response."
- : exception != null
- ? "An exception has occurred while reading an HTTP request/response."
- : null;
+ if (timeout) {
+ var msg = "A timeout has occurred.";
+
+ throw new WebSocketException (msg);
+ }
+
+ if (exception != null) {
+ var msg = "An exception has occurred.";
- if (msg != null)
throw new WebSocketException (msg, exception);
+ }
- return http;
+ return ret;
}
#endregion
@@ -200,9 +298,20 @@ protected static T Read (Stream stream, Func parser, int millise
public byte[] ToByteArray ()
{
- return Encoding.UTF8.GetBytes (ToString ());
+ var headerData = Encoding.UTF8.GetBytes (MessageHeader);
+
+ return _messageBodyData != null
+ ? headerData.Concat (_messageBodyData).ToArray ()
+ : headerData;
+ }
+
+ public override string ToString ()
+ {
+ return _messageBodyData != null
+ ? MessageHeader + MessageBody
+ : MessageHeader;
}
-
+
#endregion
}
}
diff --git a/websocket-sharp/HttpRequest.cs b/websocket-sharp/HttpRequest.cs
index fe74d5afb..dd51d0101 100644
--- a/websocket-sharp/HttpRequest.cs
+++ b/websocket-sharp/HttpRequest.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2015 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -47,38 +47,56 @@ internal class HttpRequest : HttpBase
private CookieCollection _cookies;
private string _method;
- private string _uri;
+ private string _target;
#endregion
#region Private Constructors
- private HttpRequest (string method, string uri, Version version, NameValueCollection headers)
+ private HttpRequest (
+ string method,
+ string target,
+ Version version,
+ NameValueCollection headers
+ )
: base (version, headers)
{
_method = method;
- _uri = uri;
+ _target = target;
}
#endregion
#region Internal Constructors
- internal HttpRequest (string method, string uri)
- : this (method, uri, HttpVersion.Version11, new NameValueCollection ())
+ internal HttpRequest (string method, string target)
+ : this (method, target, HttpVersion.Version11, new NameValueCollection ())
{
Headers["User-Agent"] = "websocket-sharp/1.0";
}
#endregion
+ #region Internal Properties
+
+ internal string RequestLine {
+ get {
+ var fmt = "{0} {1} HTTP/{2}{3}";
+
+ return String.Format (fmt, _method, _target, ProtocolVersion, CrLf);
+ }
+ }
+
+ #endregion
+
#region Public Properties
public AuthenticationResponse AuthenticationResponse {
get {
- var res = Headers["Authorization"];
- return res != null && res.Length > 0
- ? AuthenticationResponse.Parse (res)
+ var val = Headers["Authorization"];
+
+ return val != null && val.Length > 0
+ ? AuthenticationResponse.Parse (val)
: null;
}
}
@@ -106,9 +124,15 @@ public bool IsWebSocketRequest {
}
}
- public string RequestUri {
+ public override string MessageHeader {
get {
- return _uri;
+ return RequestLine + HeaderSection;
+ }
+ }
+
+ public string RequestTarget {
+ get {
+ return _target;
}
}
@@ -116,59 +140,82 @@ public string RequestUri {
#region Internal Methods
- internal static HttpRequest CreateConnectRequest (Uri uri)
+ internal static HttpRequest CreateConnectRequest (Uri targetUri)
{
- var host = uri.DnsSafeHost;
- var port = uri.Port;
- var authority = String.Format ("{0}:{1}", host, port);
- var req = new HttpRequest ("CONNECT", authority);
- req.Headers["Host"] = port == 80 ? host : authority;
+ var fmt = "{0}:{1}";
+ var host = targetUri.DnsSafeHost;
+ var port = targetUri.Port;
+ var authority = String.Format (fmt, host, port);
- return req;
+ var ret = new HttpRequest ("CONNECT", authority);
+
+ ret.Headers["Host"] = port != 80 ? authority : host;
+
+ return ret;
}
- internal static HttpRequest CreateWebSocketRequest (Uri uri)
+ internal static HttpRequest CreateWebSocketHandshakeRequest (Uri targetUri)
{
- var req = new HttpRequest ("GET", uri.PathAndQuery);
- var headers = req.Headers;
+ var ret = new HttpRequest ("GET", targetUri.PathAndQuery);
+
+ var headers = ret.Headers;
+
+ var port = targetUri.Port;
+ var schm = targetUri.Scheme;
+ var isDefaultPort = (port == 80 && schm == "ws")
+ || (port == 443 && schm == "wss");
- // Only includes a port number in the Host header value if it's non-default.
- // See: https://tools.ietf.org/html/rfc6455#page-17
- var port = uri.Port;
- var schm = uri.Scheme;
- headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss")
- ? uri.DnsSafeHost
- : uri.Authority;
+ headers["Host"] = !isDefaultPort
+ ? targetUri.Authority
+ : targetUri.DnsSafeHost;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
- return req;
+ return ret;
}
internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout)
{
- var buff = ToByteArray ();
- stream.Write (buff, 0, buff.Length);
+ WriteTo (stream);
- return Read (stream, HttpResponse.Parse, millisecondsTimeout);
+ return HttpResponse.ReadResponse (stream, millisecondsTimeout);
}
- internal static HttpRequest Parse (string[] headerParts)
+ internal static HttpRequest Parse (string[] messageHeader)
{
- var requestLine = headerParts[0].Split (new[] { ' ' }, 3);
- if (requestLine.Length != 3)
- throw new ArgumentException ("Invalid request line: " + headerParts[0]);
+ var len = messageHeader.Length;
+
+ if (len == 0) {
+ var msg = "An empty request header.";
+
+ throw new ArgumentException (msg);
+ }
+
+ var rlParts = messageHeader[0].Split (new[] { ' ' }, 3);
+
+ if (rlParts.Length != 3) {
+ var msg = "It includes an invalid request line.";
+
+ throw new ArgumentException (msg);
+ }
+
+ var method = rlParts[0];
+ var target = rlParts[1];
+ var ver = rlParts[2].Substring (5).ToVersion ();
var headers = new WebHeaderCollection ();
- for (int i = 1; i < headerParts.Length; i++)
- headers.InternalSet (headerParts[i], false);
- return new HttpRequest (
- requestLine[0], requestLine[1], new Version (requestLine[2].Substring (5)), headers);
+ for (var i = 1; i < len; i++)
+ headers.InternalSet (messageHeader[i], false);
+
+ return new HttpRequest (method, target, ver, headers);
}
- internal static HttpRequest Read (Stream stream, int millisecondsTimeout)
+ internal static HttpRequest ReadRequest (
+ Stream stream,
+ int millisecondsTimeout
+ )
{
return Read (stream, Parse, millisecondsTimeout);
}
@@ -183,33 +230,22 @@ public void SetCookies (CookieCollection cookies)
return;
var buff = new StringBuilder (64);
- foreach (var cookie in cookies.Sorted)
- if (!cookie.Expired)
- buff.AppendFormat ("{0}; ", cookie.ToString ());
- var len = buff.Length;
- if (len > 2) {
- buff.Length = len - 2;
- Headers["Cookie"] = buff.ToString ();
- }
- }
+ foreach (var cookie in cookies.Sorted) {
+ if (cookie.Expired)
+ continue;
- public override string ToString ()
- {
- var output = new StringBuilder (64);
- output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf);
+ buff.AppendFormat ("{0}; ", cookie);
+ }
- var headers = Headers;
- foreach (var key in headers.AllKeys)
- output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
+ var len = buff.Length;
- output.Append (CrLf);
+ if (len <= 2)
+ return;
- var entity = EntityBody;
- if (entity.Length > 0)
- output.Append (entity);
+ buff.Length = len - 2;
- return output.ToString ();
+ Headers["Cookie"] = buff.ToString ();
}
#endregion
diff --git a/websocket-sharp/HttpResponse.cs b/websocket-sharp/HttpResponse.cs
index 831b72783..fb2f9d312 100644
--- a/websocket-sharp/HttpResponse.cs
+++ b/websocket-sharp/HttpResponse.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2014 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,14 +38,19 @@ internal class HttpResponse : HttpBase
{
#region Private Fields
- private string _code;
+ private int _code;
private string _reason;
#endregion
#region Private Constructors
- private HttpResponse (string code, string reason, Version version, NameValueCollection headers)
+ private HttpResponse (
+ int code,
+ string reason,
+ Version version,
+ NameValueCollection headers
+ )
: base (version, headers)
{
_code = code;
@@ -56,67 +61,118 @@ private HttpResponse (string code, string reason, Version version, NameValueColl
#region Internal Constructors
+ internal HttpResponse (int code)
+ : this (code, code.GetStatusDescription ())
+ {
+ }
+
internal HttpResponse (HttpStatusCode code)
- : this (code, code.GetDescription ())
+ : this ((int) code)
{
}
- internal HttpResponse (HttpStatusCode code, string reason)
- : this (((int) code).ToString (), reason, HttpVersion.Version11, new NameValueCollection ())
+ internal HttpResponse (int code, string reason)
+ : this (
+ code,
+ reason,
+ HttpVersion.Version11,
+ new NameValueCollection ()
+ )
{
Headers["Server"] = "websocket-sharp/1.0";
}
+ internal HttpResponse (HttpStatusCode code, string reason)
+ : this ((int) code, reason)
+ {
+ }
+
+ #endregion
+
+ #region Internal Properties
+
+ internal string StatusLine {
+ get {
+ return _reason != null
+ ? String.Format (
+ "HTTP/{0} {1} {2}{3}",
+ ProtocolVersion,
+ _code,
+ _reason,
+ CrLf
+ )
+ : String.Format (
+ "HTTP/{0} {1}{2}",
+ ProtocolVersion,
+ _code,
+ CrLf
+ );
+ }
+ }
+
#endregion
#region Public Properties
- public CookieCollection Cookies {
+ public bool CloseConnection {
get {
- return Headers.GetCookies (true);
+ var compType = StringComparison.OrdinalIgnoreCase;
+
+ return Headers.Contains ("Connection", "close", compType);
}
}
- public bool HasConnectionClose {
+ public CookieCollection Cookies {
get {
- var comparison = StringComparison.OrdinalIgnoreCase;
- return Headers.Contains ("Connection", "close", comparison);
+ return Headers.GetCookies (true);
}
}
public bool IsProxyAuthenticationRequired {
get {
- return _code == "407";
+ return _code == 407;
}
}
public bool IsRedirect {
get {
- return _code == "301" || _code == "302";
+ return _code == 301 || _code == 302;
+ }
+ }
+
+ public bool IsSuccess {
+ get {
+ return _code >= 200 && _code <= 299;
}
}
public bool IsUnauthorized {
get {
- return _code == "401";
+ return _code == 401;
}
}
public bool IsWebSocketResponse {
get {
return ProtocolVersion > HttpVersion.Version10
- && _code == "101"
+ && _code == 101
&& Headers.Upgrades ("websocket");
}
}
+ public override string MessageHeader {
+ get {
+ return StatusLine + HeaderSection;
+ }
+ }
+
public string Reason {
get {
return _reason;
}
}
- public string StatusCode {
+ public int StatusCode {
get {
return _code;
}
@@ -128,46 +184,69 @@ public string StatusCode {
internal static HttpResponse CreateCloseResponse (HttpStatusCode code)
{
- var res = new HttpResponse (code);
- res.Headers["Connection"] = "close";
+ var ret = new HttpResponse (code);
+
+ ret.Headers["Connection"] = "close";
- return res;
+ return ret;
}
internal static HttpResponse CreateUnauthorizedResponse (string challenge)
{
- var res = new HttpResponse (HttpStatusCode.Unauthorized);
- res.Headers["WWW-Authenticate"] = challenge;
+ var ret = new HttpResponse (HttpStatusCode.Unauthorized);
+
+ ret.Headers["WWW-Authenticate"] = challenge;
- return res;
+ return ret;
}
- internal static HttpResponse CreateWebSocketResponse ()
+ internal static HttpResponse CreateWebSocketHandshakeResponse ()
{
- var res = new HttpResponse (HttpStatusCode.SwitchingProtocols);
+ var ret = new HttpResponse (HttpStatusCode.SwitchingProtocols);
+
+ var headers = ret.Headers;
- var headers = res.Headers;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
- return res;
+ return ret;
}
- internal static HttpResponse Parse (string[] headerParts)
+ internal static HttpResponse Parse (string[] messageHeader)
{
- var statusLine = headerParts[0].Split (new[] { ' ' }, 3);
- if (statusLine.Length != 3)
- throw new ArgumentException ("Invalid status line: " + headerParts[0]);
+ var len = messageHeader.Length;
+
+ if (len == 0) {
+ var msg = "An empty response header.";
+
+ throw new ArgumentException (msg);
+ }
+
+ var slParts = messageHeader[0].Split (new[] { ' ' }, 3);
+ var plen = slParts.Length;
+
+ if (plen < 2) {
+ var msg = "It includes an invalid status line.";
+
+ throw new ArgumentException (msg);
+ }
+
+ var code = slParts[1].ToInt32 ();
+ var reason = plen == 3 ? slParts[2] : null;
+ var ver = slParts[0].Substring (5).ToVersion ();
var headers = new WebHeaderCollection ();
- for (int i = 1; i < headerParts.Length; i++)
- headers.InternalSet (headerParts[i], true);
- return new HttpResponse (
- statusLine[1], statusLine[2], new Version (statusLine[0].Substring (5)), headers);
+ for (var i = 1; i < len; i++)
+ headers.InternalSet (messageHeader[i], true);
+
+ return new HttpResponse (code, reason, ver, headers);
}
- internal static HttpResponse Read (Stream stream, int millisecondsTimeout)
+ internal static HttpResponse ReadResponse (
+ Stream stream,
+ int millisecondsTimeout
+ )
{
return Read (stream, Parse, millisecondsTimeout);
}
@@ -182,26 +261,12 @@ public void SetCookies (CookieCollection cookies)
return;
var headers = Headers;
- foreach (var cookie in cookies.Sorted)
- headers.Add ("Set-Cookie", cookie.ToResponseString ());
- }
- public override string ToString ()
- {
- var output = new StringBuilder (64);
- output.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
+ foreach (var cookie in cookies.Sorted) {
+ var val = cookie.ToResponseString ();
- var headers = Headers;
- foreach (var key in headers.AllKeys)
- output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf);
-
- output.Append (CrLf);
-
- var entity = EntityBody;
- if (entity.Length > 0)
- output.Append (entity);
-
- return output.ToString ();
+ headers.Add ("Set-Cookie", val);
+ }
}
#endregion
diff --git a/websocket-sharp/LogData.cs b/websocket-sharp/LogData.cs
index 9c0843093..df8c96b89 100644
--- a/websocket-sharp/LogData.cs
+++ b/websocket-sharp/LogData.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2013-2015 sta.blockhead
+ * Copyright (c) 2013-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -53,6 +53,7 @@ internal LogData (LogLevel level, StackFrame caller, string message)
_level = level;
_caller = caller;
_message = message ?? String.Empty;
+
_date = DateTime.Now;
}
@@ -64,7 +65,8 @@ internal LogData (LogLevel level, StackFrame caller, string message)
/// Gets the information of the logging method caller.
///
///
- /// A that provides the information of the logging method caller.
+ /// A that provides the information of
+ /// the logging method caller.
///
public StackFrame Caller {
get {
@@ -76,7 +78,8 @@ public StackFrame Caller {
/// Gets the date and time when the log data was created.
///
///
- /// A that represents the date and time when the log data was created.
+ /// A that represents the date and time when
+ /// the log data was created.
///
public DateTime Date {
get {
@@ -88,7 +91,8 @@ public DateTime Date {
/// Gets the logging level of the log data.
///
///
- /// One of the enum values, indicates the logging level of the log data.
+ /// One of the enum values that represents
+ /// the logging level of the log data.
///
public LogLevel Level {
get {
@@ -113,34 +117,36 @@ public string Message {
#region Public Methods
///
- /// Returns a that represents the current .
+ /// Returns a string that represents the current instance.
///
///
- /// A that represents the current .
+ /// A that represents the current instance.
///
public override string ToString ()
{
- var header = String.Format ("{0}|{1,-5}|", _date, _level);
+ var date = String.Format ("[{0}]", _date);
+ var level = String.Format ("{0,-5}", _level.ToString ().ToUpper ());
+
var method = _caller.GetMethod ();
var type = method.DeclaringType;
#if DEBUG
- var lineNum = _caller.GetFileLineNumber ();
- var headerAndCaller =
- String.Format ("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum);
+ var num = _caller.GetFileLineNumber ();
+ var caller = String.Format ("{0}.{1}:{2}", type.Name, method.Name, num);
#else
- var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name);
+ var caller = String.Format ("{0}.{1}", type.Name, method.Name);
#endif
var msgs = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n');
+
if (msgs.Length <= 1)
- return String.Format ("{0}{1}", headerAndCaller, _message);
+ return String.Format ("{0} {1} {2} {3}", date, level, caller, _message);
+
+ var buff = new StringBuilder (64);
- var buff = new StringBuilder (String.Format ("{0}{1}\n", headerAndCaller, msgs[0]), 64);
+ buff.AppendFormat ("{0} {1} {2}\n\n", date, level, caller);
- var fmt = String.Format ("{{0,{0}}}{{1}}\n", header.Length);
- for (var i = 1; i < msgs.Length; i++)
- buff.AppendFormat (fmt, "", msgs[i]);
+ for (var i = 0; i < msgs.Length; i++)
+ buff.AppendFormat (" {0}\n", msgs[i]);
- buff.Length--;
return buff.ToString ();
}
diff --git a/websocket-sharp/LogLevel.cs b/websocket-sharp/LogLevel.cs
index ef9967728..5ff1d8fed 100644
--- a/websocket-sharp/LogLevel.cs
+++ b/websocket-sharp/LogLevel.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2013-2015 sta.blockhead
+ * Copyright (c) 2013-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -58,6 +58,10 @@ public enum LogLevel
///
/// Specifies the top logging level.
///
- Fatal
+ Fatal,
+ ///
+ /// Specifies not to output logs.
+ ///
+ None
}
}
diff --git a/websocket-sharp/Logger.cs b/websocket-sharp/Logger.cs
index 17850e67e..ad6135803 100644
--- a/websocket-sharp/Logger.cs
+++ b/websocket-sharp/Logger.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2013-2015 sta.blockhead
+ * Copyright (c) 2013-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -37,17 +37,17 @@ namespace WebSocketSharp
///
///
///
- /// If you output a log with lower than the value of the property,
+ /// If you output a log with lower than the current logging level,
/// it cannot be outputted.
///
///
- /// The default output action writes a log to the standard output stream and the log file
- /// if the property has a valid path to it.
+ /// The default output method writes a log to the standard output
+ /// stream and the text file if it has a valid path.
///
///
- /// If you would like to use the custom output action, you should set
- /// the property to any Action<LogData, string>
- /// delegate.
+ /// If you would like to use the custom output method, you should
+ /// specify it with the constructor or the
+ /// property.
///
///
public class Logger
@@ -67,7 +67,7 @@ public class Logger
/// Initializes a new instance of the class.
///
///
- /// This constructor initializes the current logging level with .
+ /// This constructor initializes the logging level with the Error level.
///
public Logger ()
: this (LogLevel.Error, null, null)
@@ -76,10 +76,11 @@ public Logger ()
///
/// Initializes a new instance of the class with
- /// the specified logging .
+ /// the specified logging level.
///
///
- /// One of the enum values.
+ /// One of the enum values that specifies
+ /// the logging level.
///
public Logger (LogLevel level)
: this (level, null, null)
@@ -88,25 +89,26 @@ public Logger (LogLevel level)
///
/// Initializes a new instance of the class with
- /// the specified logging , path to the log ,
- /// and action.
+ /// the specified logging level, path to the log file, and delegate
+ /// used to output a log.
///
///
- /// One of the enum values.
+ /// One of the enum values that specifies
+ /// the logging level.
///
///
- /// A that represents the path to the log file.
+ /// A that specifies the path to the log file.
///
///
- /// An Action<LogData, string> delegate that references the method(s) used to
- /// output a log. A parameter passed to this delegate is
- /// .
+ /// An that specifies
+ /// the delegate used to output a log.
///
public Logger (LogLevel level, string file, Action output)
{
_level = level;
_file = file;
_output = output ?? defaultOutput;
+
_sync = new object ();
}
@@ -115,10 +117,10 @@ public Logger (LogLevel level, string file, Action output)
#region Public Properties
///
- /// Gets or sets the current path to the log file.
+ /// Gets or sets the path to the log file.
///
///
- /// A that represents the current path to the log file if any.
+ /// A that represents the path to the log file if any.
///
public string File {
get {
@@ -126,11 +128,8 @@ public string File {
}
set {
- lock (_sync) {
+ lock (_sync)
_file = value;
- Warn (
- String.Format ("The current path to the log file has been changed to {0}.", _file));
- }
}
}
@@ -141,7 +140,12 @@ public string File {
/// A log with lower than the value of this property cannot be outputted.
///
///
- /// One of the enum values, specifies the current logging level.
+ ///
+ /// One of the enum values.
+ ///
+ ///
+ /// It represents the current logging level.
+ ///
///
public LogLevel Level {
get {
@@ -149,25 +153,28 @@ public LogLevel Level {
}
set {
- lock (_sync) {
+ lock (_sync)
_level = value;
- Warn (String.Format ("The current logging level has been changed to {0}.", _level));
- }
}
}
///
- /// Gets or sets the current output action used to output a log.
+ /// Gets or sets the delegate used to output a log.
///
///
///
- /// An Action<LogData, string> delegate that references the method(s) used to
- /// output a log. A parameter passed to this delegate is the value of
+ /// An delegate.
+ ///
+ ///
+ /// It references the method used to output a log.
+ ///
+ ///
+ /// The string parameter passed to the delegate is the value of
/// the property.
///
///
- /// If the value to set is , the current output action is changed to
- /// the default output action.
+ /// If the value to set is , the default
+ /// output method is set.
///
///
public Action Output {
@@ -176,10 +183,8 @@ public Action Output {
}
set {
- lock (_sync) {
+ lock (_sync)
_output = value ?? defaultOutput;
- Warn ("The current output action has been changed.");
- }
}
}
@@ -189,10 +194,12 @@ public Action Output {
private static void defaultOutput (LogData data, string path)
{
- var log = data.ToString ();
- Console.WriteLine (log);
+ var val = data.ToString ();
+
+ Console.WriteLine (val);
+
if (path != null && path.Length > 0)
- writeToFile (log, path);
+ writeToFile (val, path);
}
private void output (string message, LogLevel level)
@@ -201,13 +208,16 @@ private void output (string message, LogLevel level)
if (_level > level)
return;
- LogData data = null;
try {
- data = new LogData (level, new StackFrame (2, true), message);
+ var data = new LogData (level, new StackFrame (2, true), message);
+
_output (data, _file);
}
catch (Exception ex) {
- data = new LogData (LogLevel.Fatal, new StackFrame (0, true), ex.Message);
+ var data = new LogData (
+ LogLevel.Fatal, new StackFrame (0, true), ex.Message
+ );
+
Console.WriteLine (data.ToString ());
}
}
@@ -225,14 +235,14 @@ private static void writeToFile (string value, string path)
#region Public Methods
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Debug level.
///
///
- /// If the current logging level is higher than ,
- /// this method doesn't output as a log.
+ /// If the current logging level is higher than the Debug level,
+ /// this method does not output the message as a log.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Debug (string message)
{
@@ -243,14 +253,14 @@ public void Debug (string message)
}
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Error level.
///
///
- /// If the current logging level is higher than ,
- /// this method doesn't output as a log.
+ /// If the current logging level is higher than the Error level,
+ /// this method does not output the message as a log.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Error (string message)
{
@@ -261,25 +271,28 @@ public void Error (string message)
}
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Fatal level.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Fatal (string message)
{
+ if (_level > LogLevel.Fatal)
+ return;
+
output (message, LogLevel.Fatal);
}
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Info level.
///
///
- /// If the current logging level is higher than ,
- /// this method doesn't output as a log.
+ /// If the current logging level is higher than the Info level,
+ /// this method does not output the message as a log.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Info (string message)
{
@@ -290,14 +303,14 @@ public void Info (string message)
}
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Trace level.
///
///
- /// If the current logging level is higher than ,
- /// this method doesn't output as a log.
+ /// If the current logging level is higher than the Trace level,
+ /// this method does not output the message as a log.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Trace (string message)
{
@@ -308,14 +321,14 @@ public void Trace (string message)
}
///
- /// Outputs as a log with .
+ /// Outputs the specified message as a log with the Warn level.
///
///
- /// If the current logging level is higher than ,
- /// this method doesn't output as a log.
+ /// If the current logging level is higher than the Warn level,
+ /// this method does not output the message as a log.
///
///
- /// A that represents the message to output as a log.
+ /// A that specifies the message to output.
///
public void Warn (string message)
{
diff --git a/websocket-sharp/MessageEventArgs.cs b/websocket-sharp/MessageEventArgs.cs
index 7940f98b7..63add90f7 100644
--- a/websocket-sharp/MessageEventArgs.cs
+++ b/websocket-sharp/MessageEventArgs.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2016 sta.blockhead
+ * Copyright (c) 2012-2022 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,8 +35,8 @@ namespace WebSocketSharp
///
///
///
- /// That event occurs when the receives
- /// a message or a ping if the
+ /// The message event occurs when the interface
+ /// receives a message or a ping if the
/// property is set to true.
///
///
@@ -97,13 +97,19 @@ internal Opcode Opcode {
/// Gets the message data as a .
///
///
- /// A that represents the message data if its type is
- /// text or ping and if decoding it to a string has successfully done;
- /// otherwise, .
+ ///
+ /// A that represents the message data
+ /// if the message type is text or ping.
+ ///
+ ///
+ /// if the message type is binary or
+ /// the message data could not be UTF-8-decoded.
+ ///
///
public string Data {
get {
setData ();
+
return _data;
}
}
@@ -153,6 +159,7 @@ public bool IsText {
public byte[] RawData {
get {
setData ();
+
return _rawData;
}
}
@@ -168,10 +175,12 @@ private void setData ()
if (_opcode == Opcode.Binary) {
_dataSet = true;
+
return;
}
string data;
+
if (_rawData.TryGetUTF8DecodedString (out data))
_data = data;
diff --git a/websocket-sharp/Net/AuthenticationBase.cs b/websocket-sharp/Net/AuthenticationBase.cs
deleted file mode 100644
index 107750499..000000000
--- a/websocket-sharp/Net/AuthenticationBase.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-#region License
-/*
- * AuthenticationBase.cs
- *
- * The MIT License
- *
- * Copyright (c) 2014 sta.blockhead
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#endregion
-
-using System;
-using System.Collections.Specialized;
-using System.Text;
-
-namespace WebSocketSharp.Net
-{
- internal abstract class AuthenticationBase
- {
- #region Private Fields
-
- private AuthenticationSchemes _scheme;
-
- #endregion
-
- #region Internal Fields
-
- internal NameValueCollection Parameters;
-
- #endregion
-
- #region Protected Constructors
-
- protected AuthenticationBase (AuthenticationSchemes scheme, NameValueCollection parameters)
- {
- _scheme = scheme;
- Parameters = parameters;
- }
-
- #endregion
-
- #region Public Properties
-
- public string Algorithm {
- get {
- return Parameters["algorithm"];
- }
- }
-
- public string Nonce {
- get {
- return Parameters["nonce"];
- }
- }
-
- public string Opaque {
- get {
- return Parameters["opaque"];
- }
- }
-
- public string Qop {
- get {
- return Parameters["qop"];
- }
- }
-
- public string Realm {
- get {
- return Parameters["realm"];
- }
- }
-
- public AuthenticationSchemes Scheme {
- get {
- return _scheme;
- }
- }
-
- #endregion
-
- #region Internal Methods
-
- internal static string CreateNonceValue ()
- {
- var src = new byte[16];
- var rand = new Random ();
- rand.NextBytes (src);
-
- var res = new StringBuilder (32);
- foreach (var b in src)
- res.Append (b.ToString ("x2"));
-
- return res.ToString ();
- }
-
- internal static NameValueCollection ParseParameters (string value)
- {
- var res = new NameValueCollection ();
- foreach (var param in value.SplitHeaderValue (',')) {
- var i = param.IndexOf ('=');
- var name = i > 0 ? param.Substring (0, i).Trim () : null;
- var val = i < 0
- ? param.Trim ().Trim ('"')
- : i < param.Length - 1
- ? param.Substring (i + 1).Trim ().Trim ('"')
- : String.Empty;
-
- res.Add (name, val);
- }
-
- return res;
- }
-
- internal abstract string ToBasicString ();
-
- internal abstract string ToDigestString ();
-
- #endregion
-
- #region Public Methods
-
- public override string ToString ()
- {
- return _scheme == AuthenticationSchemes.Basic
- ? ToBasicString ()
- : _scheme == AuthenticationSchemes.Digest
- ? ToDigestString ()
- : String.Empty;
- }
-
- #endregion
- }
-}
diff --git a/websocket-sharp/Net/AuthenticationChallenge.cs b/websocket-sharp/Net/AuthenticationChallenge.cs
index 3472204b9..726740801 100644
--- a/websocket-sharp/Net/AuthenticationChallenge.cs
+++ b/websocket-sharp/Net/AuthenticationChallenge.cs
@@ -4,7 +4,7 @@
*
* The MIT License
*
- * Copyright (c) 2013-2014 sta.blockhead
+ * Copyright (c) 2013-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,27 +32,52 @@
namespace WebSocketSharp.Net
{
- internal class AuthenticationChallenge : AuthenticationBase
+ internal class AuthenticationChallenge
{
+ #region Private Fields
+
+ private NameValueCollection _parameters;
+ private AuthenticationSchemes _scheme;
+
+ #endregion
+
#region Private Constructors
- private AuthenticationChallenge (AuthenticationSchemes scheme, NameValueCollection parameters)
- : base (scheme, parameters)
+ private AuthenticationChallenge (
+ AuthenticationSchemes scheme,
+ NameValueCollection parameters
+ )
{
+ _scheme = scheme;
+ _parameters = parameters;
}
#endregion
#region Internal Constructors
- internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm)
- : base (scheme, new NameValueCollection ())
+ internal AuthenticationChallenge (
+ AuthenticationSchemes scheme,
+ string realm
+ )
+ : this (scheme, new NameValueCollection ())
{
- Parameters["realm"] = realm;
+ _parameters["realm"] = realm;
+
if (scheme == AuthenticationSchemes.Digest) {
- Parameters["nonce"] = CreateNonceValue ();
- Parameters["algorithm"] = "MD5";
- Parameters["qop"] = "auth";
+ _parameters["nonce"] = CreateNonceValue ();
+ _parameters["algorithm"] = "MD5";
+ _parameters["qop"] = "auth";
+ }
+ }
+
+ #endregion
+
+ #region Internal Properties
+
+ internal NameValueCollection Parameters {
+ get {
+ return _parameters;
}
}
@@ -60,15 +85,51 @@ internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm)
#region Public Properties
+ public string Algorithm {
+ get {
+ return _parameters["algorithm"];
+ }
+ }
+
public string Domain {
get {
- return Parameters["domain"];
+ return _parameters["domain"];
+ }
+ }
+
+ public string Nonce {
+ get {
+ return _parameters["nonce"];
+ }
+ }
+
+ public string Opaque {
+ get {
+ return _parameters["opaque"];
+ }
+ }
+
+ public string Qop {
+ get {
+ return _parameters["qop"];
+ }
+ }
+
+ public string Realm {
+ get {
+ return _parameters["realm"];
+ }
+ }
+
+ public AuthenticationSchemes Scheme {
+ get {
+ return _scheme;
}
}
public string Stale {
get {
- return Parameters["stale"];
+ return _parameters["stale"];
}
}
@@ -86,59 +147,132 @@ internal static AuthenticationChallenge CreateDigestChallenge (string realm)
return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm);
}
+ internal static string CreateNonceValue ()
+ {
+ var rand = new Random ();
+ var bytes = new byte[16];
+
+ rand.NextBytes (bytes);
+
+ var buff = new StringBuilder (32);
+
+ foreach (var b in bytes)
+ buff.Append (b.ToString ("x2"));
+
+ return buff.ToString ();
+ }
+
internal static AuthenticationChallenge Parse (string value)
{
var chal = value.Split (new[] { ' ' }, 2);
+
if (chal.Length != 2)
return null;
var schm = chal[0].ToLower ();
- return schm == "basic"
- ? new AuthenticationChallenge (
- AuthenticationSchemes.Basic, ParseParameters (chal[1]))
- : schm == "digest"
- ? new AuthenticationChallenge (
- AuthenticationSchemes.Digest, ParseParameters (chal[1]))
- : null;
+
+ if (schm == "basic") {
+ var parameters = ParseParameters (chal[1]);
+
+ return new AuthenticationChallenge (
+ AuthenticationSchemes.Basic,
+ parameters
+ );
+ }
+
+ if (schm == "digest") {
+ var parameters = ParseParameters (chal[1]);
+
+ return new AuthenticationChallenge (
+ AuthenticationSchemes.Digest,
+ parameters
+ );
+ }
+
+ return null;
}
- internal override string ToBasicString ()
+ internal static NameValueCollection ParseParameters (string value)
{
- return String.Format ("Basic realm=\"{0}\"", Parameters["realm"]);
+ var ret = new NameValueCollection ();
+
+ foreach (var param in value.SplitHeaderValue (',')) {
+ var i = param.IndexOf ('=');
+
+ var name = i > 0 ? param.Substring (0, i).Trim () : null;
+ var val = i < 0
+ ? param.Trim ().Trim ('"')
+ : i < param.Length - 1
+ ? param.Substring (i + 1).Trim ().Trim ('"')
+ : String.Empty;
+
+ ret.Add (name, val);
+ }
+
+ return ret;
}
- internal override string ToDigestString ()
+ internal string ToBasicString ()
{
- var output = new StringBuilder (128);
+ return String.Format ("Basic realm=\"{0}\"", _parameters["realm"]);
+ }
+
+ internal string ToDigestString ()
+ {
+ var buff = new StringBuilder (128);
- var domain = Parameters["domain"];
- if (domain != null)
- output.AppendFormat (
+ var domain = _parameters["domain"];
+ var realm = _parameters["realm"];
+ var nonce = _parameters["nonce"];
+
+ if (domain != null) {
+ buff.AppendFormat (
"Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"",
- Parameters["realm"],
+ realm,
domain,
- Parameters["nonce"]);
- else
- output.AppendFormat (
- "Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]);
+ nonce
+ );
+ }
+ else {
+ buff.AppendFormat ("Digest realm=\"{0}\", nonce=\"{1}\"", realm, nonce);
+ }
+
+ var opaque = _parameters["opaque"];
- var opaque = Parameters["opaque"];
if (opaque != null)
- output.AppendFormat (", opaque=\"{0}\"", opaque);
+ buff.AppendFormat (", opaque=\"{0}\"", opaque);
+
+ var stale = _parameters["stale"];
- var stale = Parameters["stale"];
if (stale != null)
- output.AppendFormat (", stale={0}", stale);
+ buff.AppendFormat (", stale={0}", stale);
+
+ var algo = _parameters["algorithm"];
- var algo = Parameters["algorithm"];
if (algo != null)
- output.AppendFormat (", algorithm={0}", algo);
+ buff.AppendFormat (", algorithm={0}", algo);
+
+ var qop = _parameters["qop"];
- var qop = Parameters["qop"];
if (qop != null)
- output.AppendFormat (", qop=\"{0}\"", qop);
+ buff.AppendFormat (", qop=\"{0}\"", qop);
+
+ return buff.ToString ();
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public override string ToString ()
+ {
+ if (_scheme == AuthenticationSchemes.Basic)
+ return ToBasicString ();
+
+ if (_scheme == AuthenticationSchemes.Digest)
+ return ToDigestString ();
- return output.ToString ();
+ return String.Empty;
}
#endregion
diff --git a/websocket-sharp/Net/AuthenticationResponse.cs b/websocket-sharp/Net/AuthenticationResponse.cs
index 0257d85b2..28fbd2236 100644
--- a/websocket-sharp/Net/AuthenticationResponse.cs
+++ b/websocket-sharp/Net/AuthenticationResponse.cs
@@ -2,13 +2,13 @@
/*
* AuthenticationResponse.cs
*
- * ParseBasicCredentials is derived from System.Net.HttpListenerContext.cs of Mono
- * (http://www.mono-project.com).
+ * The ParseBasicCredentials method is derived from HttpListenerContext.cs
+ * (System.Net) of Mono (http://www.mono-project.com).
*
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2013-2014 sta.blockhead
+ * Copyright (c) 2013-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,19 +38,25 @@
namespace WebSocketSharp.Net
{
- internal class AuthenticationResponse : AuthenticationBase
+ internal class AuthenticationResponse
{
#region Private Fields
- private uint _nonceCount;
+ private uint _nonceCount;
+ private NameValueCollection _parameters;
+ private AuthenticationSchemes _scheme;
#endregion
#region Private Constructors
- private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollection parameters)
- : base (scheme, parameters)
+ private AuthenticationResponse (
+ AuthenticationSchemes scheme,
+ NameValueCollection parameters
+ )
{
+ _scheme = scheme;
+ _parameters = parameters;
}
#endregion
@@ -58,12 +64,20 @@ private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollectio
#region Internal Constructors
internal AuthenticationResponse (NetworkCredential credentials)
- : this (AuthenticationSchemes.Basic, new NameValueCollection (), credentials, 0)
+ : this (
+ AuthenticationSchemes.Basic,
+ new NameValueCollection (),
+ credentials,
+ 0
+ )
{
}
internal AuthenticationResponse (
- AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount)
+ AuthenticationChallenge challenge,
+ NetworkCredential credentials,
+ uint nonceCount
+ )
: this (challenge.Scheme, challenge.Parameters, credentials, nonceCount)
{
}
@@ -72,13 +86,15 @@ internal AuthenticationResponse (
AuthenticationSchemes scheme,
NameValueCollection parameters,
NetworkCredential credentials,
- uint nonceCount)
- : base (scheme, parameters)
+ uint nonceCount
+ )
+ : this (scheme, parameters)
{
- Parameters["username"] = credentials.Username;
- Parameters["password"] = credentials.Password;
- Parameters["uri"] = credentials.Domain;
+ _parameters["username"] = credentials.Username;
+ _parameters["password"] = credentials.Password;
+ _parameters["uri"] = credentials.Domain;
_nonceCount = nonceCount;
+
if (scheme == AuthenticationSchemes.Digest)
initAsDigest ();
}
@@ -89,9 +105,13 @@ internal AuthenticationResponse (
internal uint NonceCount {
get {
- return _nonceCount < UInt32.MaxValue
- ? _nonceCount
- : 0;
+ return _nonceCount < UInt32.MaxValue ? _nonceCount : 0;
+ }
+ }
+
+ internal NameValueCollection Parameters {
+ get {
+ return _parameters;
}
}
@@ -99,39 +119,75 @@ internal uint NonceCount {
#region Public Properties
+ public string Algorithm {
+ get {
+ return _parameters["algorithm"];
+ }
+ }
+
public string Cnonce {
get {
- return Parameters["cnonce"];
+ return _parameters["cnonce"];
}
}
public string Nc {
get {
- return Parameters["nc"];
+ return _parameters["nc"];
+ }
+ }
+
+ public string Nonce {
+ get {
+ return _parameters["nonce"];
+ }
+ }
+
+ public string Opaque {
+ get {
+ return _parameters["opaque"];
}
}
public string Password {
get {
- return Parameters["password"];
+ return _parameters["password"];
+ }
+ }
+
+ public string Qop {
+ get {
+ return _parameters["qop"];
+ }
+ }
+
+ public string Realm {
+ get {
+ return _parameters["realm"];
}
}
public string Response {
get {
- return Parameters["response"];
+ return _parameters["response"];
+ }
+ }
+
+ public AuthenticationSchemes Scheme {
+ get {
+ return _scheme;
}
}
public string Uri {
get {
- return Parameters["uri"];
+ return _parameters["uri"];
}
}
public string UserName {
get {
- return Parameters["username"];
+ return _parameters["username"];
}
}
@@ -139,16 +195,26 @@ public string UserName {
#region Private Methods
- private static string createA1 (string username, string password, string realm)
+ private static string createA1 (
+ string username,
+ string password,
+ string realm
+ )
{
return String.Format ("{0}:{1}:{2}", username, realm, password);
}
private static string createA1 (
- string username, string password, string realm, string nonce, string cnonce)
+ string username,
+ string password,
+ string realm,
+ string nonce,
+ string cnonce
+ )
{
- return String.Format (
- "{0}:{1}:{2}", hash (createA1 (username, password, realm)), nonce, cnonce);
+ var a1 = createA1 (username, password, realm);
+
+ return String.Format ("{0}:{1}:{2}", hash (a1), nonce, cnonce);
}
private static string createA2 (string method, string uri)
@@ -163,33 +229,39 @@ private static string createA2 (string method, string uri, string entity)
private static string hash (string value)
{
- var src = Encoding.UTF8.GetBytes (value);
+ var buff = new StringBuilder (64);
+
var md5 = MD5.Create ();
- var hashed = md5.ComputeHash (src);
+ var bytes = Encoding.UTF8.GetBytes (value);
+ var res = md5.ComputeHash (bytes);
- var res = new StringBuilder (64);
- foreach (var b in hashed)
- res.Append (b.ToString ("x2"));
+ foreach (var b in res)
+ buff.Append (b.ToString ("x2"));
- return res.ToString ();
+ return buff.ToString ();
}
private void initAsDigest ()
{
- var qops = Parameters["qop"];
+ var qops = _parameters["qop"];
+
if (qops != null) {
- if (qops.Split (',').Contains (qop => qop.Trim ().ToLower () == "auth")) {
- Parameters["qop"] = "auth";
- Parameters["cnonce"] = CreateNonceValue ();
- Parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount);
+ var hasAuth = qops.Split (',').Contains (
+ qop => qop.Trim ().ToLower () == "auth"
+ );
+
+ if (hasAuth) {
+ _parameters["qop"] = "auth";
+ _parameters["cnonce"] = AuthenticationChallenge.CreateNonceValue ();
+ _parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount);
}
else {
- Parameters["qop"] = null;
+ _parameters["qop"] = null;
}
}
- Parameters["method"] = "GET";
- Parameters["response"] = CreateRequestDigest (Parameters);
+ _parameters["method"] = "GET";
+ _parameters["response"] = CreateRequestDigest (_parameters);
}
#endregion
@@ -198,8 +270,8 @@ private void initAsDigest ()
internal static string CreateRequestDigest (NameValueCollection parameters)
{
- var user = parameters["username"];
- var pass = parameters["password"];
+ var uname = parameters["username"];
+ var passwd = parameters["password"];
var realm = parameters["realm"];
var nonce = parameters["nonce"];
var uri = parameters["uri"];
@@ -210,8 +282,8 @@ internal static string CreateRequestDigest (NameValueCollection parameters)
var method = parameters["method"];
var a1 = algo != null && algo.ToLower () == "md5-sess"
- ? createA1 (user, pass, realm, nonce, cnonce)
- : createA1 (user, pass, realm);
+ ? createA1 (uname, passwd, realm, nonce, cnonce)
+ : createA1 (uname, passwd, realm);
var a2 = qop != null && qop.ToLower () == "auth-int"
? createA2 (method, uri, parameters["entity"])
@@ -219,89 +291,142 @@ internal static string CreateRequestDigest (NameValueCollection parameters)
var secret = hash (a1);
var data = qop != null
- ? String.Format ("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2))
+ ? String.Format (
+ "{0}:{1}:{2}:{3}:{4}",
+ nonce,
+ nc,
+ cnonce,
+ qop,
+ hash (a2)
+ )
: String.Format ("{0}:{1}", nonce, hash (a2));
- return hash (String.Format ("{0}:{1}", secret, data));
+ var keyed = String.Format ("{0}:{1}", secret, data);
+
+ return hash (keyed);
}
internal static AuthenticationResponse Parse (string value)
{
try {
var cred = value.Split (new[] { ' ' }, 2);
+
if (cred.Length != 2)
return null;
var schm = cred[0].ToLower ();
- return schm == "basic"
- ? new AuthenticationResponse (
- AuthenticationSchemes.Basic, ParseBasicCredentials (cred[1]))
- : schm == "digest"
- ? new AuthenticationResponse (
- AuthenticationSchemes.Digest, ParseParameters (cred[1]))
- : null;
+
+ if (schm == "basic") {
+ var parameters = ParseBasicCredentials (cred[1]);
+
+ return new AuthenticationResponse (
+ AuthenticationSchemes.Basic,
+ parameters
+ );
+ }
+
+ if (schm == "digest") {
+ var parameters = AuthenticationChallenge.ParseParameters (cred[1]);
+
+ return new AuthenticationResponse (
+ AuthenticationSchemes.Digest,
+ parameters
+ );
+ }
+
+ return null;
}
catch {
+ return null;
}
-
- return null;
}
internal static NameValueCollection ParseBasicCredentials (string value)
{
+ var ret = new NameValueCollection ();
+
// Decode the basic-credentials (a Base64 encoded string).
- var userPass = Encoding.Default.GetString (Convert.FromBase64String (value));
+
+ var bytes = Convert.FromBase64String (value);
+ var userPass = Encoding.UTF8.GetString (bytes);
// The format is [\]:.
- var i = userPass.IndexOf (':');
- var user = userPass.Substring (0, i);
- var pass = i < userPass.Length - 1 ? userPass.Substring (i + 1) : String.Empty;
- // Check if 'domain' exists.
- i = user.IndexOf ('\\');
- if (i > -1)
- user = user.Substring (i + 1);
+ var idx = userPass.IndexOf (':');
+ var uname = userPass.Substring (0, idx);
+ var passwd = idx < userPass.Length - 1
+ ? userPass.Substring (idx + 1)
+ : String.Empty;
- var res = new NameValueCollection ();
- res["username"] = user;
- res["password"] = pass;
+ // Check if exists.
- return res;
+ idx = uname.IndexOf ('\\');
+
+ if (idx > -1)
+ uname = uname.Substring (idx + 1);
+
+ ret["username"] = uname;
+ ret["password"] = passwd;
+
+ return ret;
}
- internal override string ToBasicString ()
+ internal string ToBasicString ()
{
- var userPass = String.Format ("{0}:{1}", Parameters["username"], Parameters["password"]);
- var cred = Convert.ToBase64String (Encoding.UTF8.GetBytes (userPass));
+ var uname = _parameters["username"];
+ var passwd = _parameters["password"];
+ var userPass = String.Format ("{0}:{1}", uname, passwd);
+
+ var bytes = Encoding.UTF8.GetBytes (userPass);
+ var cred = Convert.ToBase64String (bytes);
return "Basic " + cred;
}
- internal override string ToDigestString ()
+ internal string ToDigestString ()
{
- var output = new StringBuilder (256);
- output.AppendFormat (
+ var buff = new StringBuilder (256);
+
+ var uname = _parameters["username"];
+ var realm = _parameters["realm"];
+ var nonce = _parameters["nonce"];
+ var uri = _parameters["uri"];
+ var res = _parameters["response"];
+
+ buff.AppendFormat (
"Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
- Parameters["username"],
- Parameters["realm"],
- Parameters["nonce"],
- Parameters["uri"],
- Parameters["response"]);
+ uname,
+ realm,
+ nonce,
+ uri,
+ res
+ );
+
+ var opaque = _parameters["opaque"];
- var opaque = Parameters["opaque"];
if (opaque != null)
- output.AppendFormat (", opaque=\"{0}\"", opaque);
+ buff.AppendFormat (", opaque=\"{0}\"", opaque);
+
+ var algo = _parameters["algorithm"];
- var algo = Parameters["algorithm"];
if (algo != null)
- output.AppendFormat (", algorithm={0}", algo);
+ buff.AppendFormat (", algorithm={0}", algo);
+
+ var qop = _parameters["qop"];
- var qop = Parameters["qop"];
- if (qop != null)
- output.AppendFormat (
- ", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]);
+ if (qop != null) {
+ var cnonce = _parameters["cnonce"];
+ var nc = _parameters["nc"];
- return output.ToString ();
+ buff.AppendFormat (
+ ", qop={0}, cnonce=\"{1}\", nc={2}",
+ qop,
+ cnonce,
+ nc
+ );
+ }
+
+ return buff.ToString ();
}
#endregion
@@ -310,12 +435,28 @@ internal override string ToDigestString ()
public IIdentity ToIdentity ()
{
- var schm = Scheme;
- return schm == AuthenticationSchemes.Basic
- ? new HttpBasicIdentity (Parameters["username"], Parameters["password"]) as IIdentity
- : schm == AuthenticationSchemes.Digest
- ? new HttpDigestIdentity (Parameters)
- : null;
+ if (_scheme == AuthenticationSchemes.Basic) {
+ var uname = _parameters["username"];
+ var passwd = _parameters["password"];
+
+ return new HttpBasicIdentity (uname, passwd);
+ }
+
+ if (_scheme == AuthenticationSchemes.Digest)
+ return new HttpDigestIdentity (_parameters);
+
+ return null;
+ }
+
+ public override string ToString ()
+ {
+ if (_scheme == AuthenticationSchemes.Basic)
+ return ToBasicString ();
+
+ if (_scheme == AuthenticationSchemes.Digest)
+ return ToDigestString ();
+
+ return String.Empty;
}
#endregion
diff --git a/websocket-sharp/Net/Chunk.cs b/websocket-sharp/Net/Chunk.cs
index 7b6268b7f..9ed28f864 100644
--- a/websocket-sharp/Net/Chunk.cs
+++ b/websocket-sharp/Net/Chunk.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
- * Copyright (c) 2014-2015 sta.blockhead
+ * Copyright (c) 2014-2021 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -74,13 +74,15 @@ public int ReadLeft {
public int Read (byte[] buffer, int offset, int count)
{
var left = _data.Length - _offset;
+
if (left == 0)
- return left;
+ return 0;
if (count > left)
count = left;
Buffer.BlockCopy (_data, _offset, buffer, offset, count);
+
_offset += count;
return count;
diff --git a/websocket-sharp/Net/ChunkStream.cs b/websocket-sharp/Net/ChunkStream.cs
index a5271b573..3de4374db 100644
--- a/websocket-sharp/Net/ChunkStream.cs
+++ b/websocket-sharp/Net/ChunkStream.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2003 Ximian, Inc (http://www.ximian.com)
- * Copyright (c) 2012-2015 sta.blockhead
+ * Copyright (c) 2012-2023 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -53,8 +53,11 @@ internal class ChunkStream
private int _chunkRead;
private int _chunkSize;
private List _chunks;
+ private int _count;
+ private byte[] _endBuffer;
private bool _gotIt;
private WebHeaderCollection _headers;
+ private int _offset;
private StringBuilder _saved;
private bool _sawCr;
private InputChunkState _state;
@@ -67,24 +70,31 @@ internal class ChunkStream
public ChunkStream (WebHeaderCollection headers)
{
_headers = headers;
+
_chunkSize = -1;
_chunks = new List ();
_saved = new StringBuilder ();
}
- public ChunkStream (byte[] buffer, int offset, int count, WebHeaderCollection headers)
- : this (headers)
- {
- Write (buffer, offset, count);
- }
-
#endregion
#region Internal Properties
- internal WebHeaderCollection Headers {
+ internal int Count {
get {
- return _headers;
+ return _count;
+ }
+ }
+
+ internal byte[] EndBuffer {
+ get {
+ return _endBuffer;
+ }
+ }
+
+ internal int Offset {
+ get {
+ return _offset;
}
}
@@ -92,15 +102,15 @@ internal WebHeaderCollection Headers {
#region Public Properties
- public int ChunkLeft {
+ public WebHeaderCollection Headers {
get {
- return _chunkSize - _chunkRead;
+ return _headers;
}
}
- public bool WantMore {
+ public bool WantsMore {
get {
- return _state != InputChunkState.End;
+ return _state < InputChunkState.End;
}
}
@@ -111,19 +121,22 @@ public bool WantMore {
private int read (byte[] buffer, int offset, int count)
{
var nread = 0;
-
var cnt = _chunks.Count;
+
for (var i = 0; i < cnt; i++) {
var chunk = _chunks[i];
+
if (chunk == null)
continue;
if (chunk.ReadLeft == 0) {
_chunks[i] = null;
+
continue;
}
nread += chunk.Read (buffer, offset + nread, count - nread);
+
if (nread == count)
break;
}
@@ -131,12 +144,6 @@ private int read (byte[] buffer, int offset, int count)
return nread;
}
- private static string removeChunkExtension (string value)
- {
- var idx = value.IndexOf (';');
- return idx > -1 ? value.Substring (0, idx) : value;
- }
-
private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
{
if (!_sawCr) {
@@ -144,6 +151,7 @@ private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
throwProtocolViolation ("CR is expected.");
_sawCr = true;
+
if (offset == length)
return InputChunkState.DataEnded;
}
@@ -154,11 +162,17 @@ private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length)
return InputChunkState.None;
}
- private InputChunkState setChunkSize (byte[] buffer, ref int offset, int length)
+ private InputChunkState setChunkSize (
+ byte[] buffer,
+ ref int offset,
+ int length
+ )
{
byte b = 0;
+
while (offset < length) {
b = buffer[offset++];
+
if (_sawCr) {
if (b != 10)
throwProtocolViolation ("LF is expected.");
@@ -168,71 +182,77 @@ private InputChunkState setChunkSize (byte[] buffer, ref int offset, int length)
if (b == 13) {
_sawCr = true;
+
continue;
}
if (b == 10)
throwProtocolViolation ("LF is unexpected.");
- if (b == 32) // SP
+ if (_gotIt)
+ continue;
+
+ if (b == 32 || b == 59) { // SP or ';'
_gotIt = true;
- if (!_gotIt)
- _saved.Append ((char) b);
+ continue;
+ }
- if (_saved.Length > 20)
- throwProtocolViolation ("The chunk size is too long.");
+ _saved.Append ((char) b);
}
- if (!_sawCr || b != 10)
+ if (_saved.Length > 20)
+ throwProtocolViolation ("The chunk size is too big.");
+
+ if (b != 10)
return InputChunkState.None;
- _chunkRead = 0;
+ var s = _saved.ToString ();
+
try {
- _chunkSize = Int32.Parse (
- removeChunkExtension (_saved.ToString ()), NumberStyles.HexNumber);
+ _chunkSize = Int32.Parse (s, NumberStyles.HexNumber);
}
catch {
throwProtocolViolation ("The chunk size cannot be parsed.");
}
+ _chunkRead = 0;
+
if (_chunkSize == 0) {
_trailerState = 2;
+
return InputChunkState.Trailer;
}
return InputChunkState.Data;
}
- private InputChunkState setTrailer (byte[] buffer, ref int offset, int length)
+ private InputChunkState setTrailer (
+ byte[] buffer,
+ ref int offset,
+ int length
+ )
{
- // Check if no trailer.
- if (_trailerState == 2 && buffer[offset] == 13 && _saved.Length == 0) {
- offset++;
- if (offset < length && buffer[offset] == 10) {
- offset++;
- return InputChunkState.End;
- }
-
- offset--;
- }
+ while (offset < length) {
+ if (_trailerState == 4) // CR LF CR LF
+ break;
- while (offset < length && _trailerState < 4) {
var b = buffer[offset++];
+
_saved.Append ((char) b);
- if (_saved.Length > 4196)
- throwProtocolViolation ("The trailer is too long.");
- if (_trailerState == 1 || _trailerState == 3) {
+ if (_trailerState == 1 || _trailerState == 3) { // CR or CR LF CR
if (b != 10)
throwProtocolViolation ("LF is expected.");
_trailerState++;
+
continue;
}
if (b == 13) {
_trailerState++;
+
continue;
}
@@ -242,31 +262,52 @@ private InputChunkState setTrailer (byte[] buffer, ref int offset, int length)
_trailerState = 0;
}
+ var len = _saved.Length;
+
+ if (len > 4196)
+ throwProtocolViolation ("The trailer is too long.");
+
if (_trailerState < 4)
return InputChunkState.Trailer;
- _saved.Length -= 2;
- var reader = new StringReader (_saved.ToString ());
+ if (len == 2)
+ return InputChunkState.End;
+
+ _saved.Length = len - 2;
+
+ var val = _saved.ToString ();
+ var reader = new StringReader (val);
+
+ while (true) {
+ var line = reader.ReadLine ();
+
+ if (line == null || line.Length == 0)
+ break;
- string line;
- while ((line = reader.ReadLine ()) != null && line.Length > 0)
_headers.Add (line);
+ }
return InputChunkState.End;
}
private static void throwProtocolViolation (string message)
{
- throw new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
+ throw new WebException (
+ message,
+ null,
+ WebExceptionStatus.ServerProtocolViolation,
+ null
+ );
}
- private void write (byte[] buffer, ref int offset, int length)
+ private void write (byte[] buffer, int offset, int length)
{
if (_state == InputChunkState.End)
throwProtocolViolation ("The chunks were ended.");
if (_state == InputChunkState.None) {
_state = setChunkSize (buffer, ref offset, length);
+
if (_state == InputChunkState.None)
return;
@@ -275,64 +316,92 @@ private void write (byte[] buffer, ref int offset, int length)
_gotIt = false;
}
- if (_state == InputChunkState.Data && offset < length) {
+ if (_state == InputChunkState.Data) {
+ if (offset >= length)
+ return;
+
_state = writeData (buffer, ref offset, length);
+
if (_state == InputChunkState.Data)
return;
}
- if (_state == InputChunkState.DataEnded && offset < length) {
+ if (_state == InputChunkState.DataEnded) {
+ if (offset >= length)
+ return;
+
_state = seekCrLf (buffer, ref offset, length);
+
if (_state == InputChunkState.DataEnded)
return;
_sawCr = false;
}
- if (_state == InputChunkState.Trailer && offset < length) {
+ if (_state == InputChunkState.Trailer) {
+ if (offset >= length)
+ return;
+
_state = setTrailer (buffer, ref offset, length);
+
if (_state == InputChunkState.Trailer)
return;
_saved.Length = 0;
}
- if (offset < length)
- write (buffer, ref offset, length);
+ if (_state == InputChunkState.End) {
+ _endBuffer = buffer;
+ _offset = offset;
+ _count = length - offset;
+
+ return;
+ }
+
+ if (offset >= length)
+ return;
+
+ write (buffer, offset, length);
}
- private InputChunkState writeData (byte[] buffer, ref int offset, int length)
+ private InputChunkState writeData (
+ byte[] buffer,
+ ref int offset,
+ int length
+ )
{
var cnt = length - offset;
var left = _chunkSize - _chunkRead;
+
if (cnt > left)
cnt = left;
var data = new byte[cnt];
+
Buffer.BlockCopy (buffer, offset, data, 0, cnt);
- _chunks.Add (new Chunk (data));
+
+ var chunk = new Chunk (data);
+
+ _chunks.Add (chunk);
offset += cnt;
_chunkRead += cnt;
- return _chunkRead == _chunkSize ? InputChunkState.DataEnded : InputChunkState.Data;
+ return _chunkRead == _chunkSize
+ ? InputChunkState.DataEnded
+ : InputChunkState.Data;
}
#endregion
#region Internal Methods
- internal void ResetBuffer ()
+ internal void ResetChunkStore ()
{
_chunkRead = 0;
_chunkSize = -1;
- _chunks.Clear ();
- }
- internal int WriteAndReadBack (byte[] buffer, int offset, int writeCount, int readCount)
- {
- Write (buffer, offset, writeCount);
- return Read (buffer, offset, readCount);
+ _chunks.Clear ();
}
#endregion
@@ -352,7 +421,7 @@ public void Write (byte[] buffer, int offset, int count)
if (count <= 0)
return;
- write (buffer, ref offset, offset + count);
+ write (buffer, offset, offset + count);
}
#endregion
diff --git a/websocket-sharp/Net/ChunkedRequestStream.cs b/websocket-sharp/Net/ChunkedRequestStream.cs
index 913b505c3..f4a583925 100644
--- a/websocket-sharp/Net/ChunkedRequestStream.cs
+++ b/websocket-sharp/Net/ChunkedRequestStream.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2015 sta.blockhead
+ * Copyright (c) 2012-2023 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -46,7 +46,7 @@ internal class ChunkedRequestStream : RequestStream
{
#region Private Fields
- private const int _bufferLength = 8192;
+ private static readonly int _bufferLength;
private HttpListenerContext _context;
private ChunkStream _decoder;
private bool _disposed;
@@ -54,27 +54,60 @@ internal class ChunkedRequestStream : RequestStream
#endregion
+ #region Static Constructor
+
+ static ChunkedRequestStream ()
+ {
+ _bufferLength = 8192;
+ }
+
+ #endregion
+
#region Internal Constructors
internal ChunkedRequestStream (
- Stream stream, byte[] buffer, int offset, int count, HttpListenerContext context)
- : base (stream, buffer, offset, count)
+ Stream innerStream,
+ byte[] initialBuffer,
+ int offset,
+ int count,
+ HttpListenerContext context
+ )
+ : base (innerStream, initialBuffer, offset, count, -1)
{
_context = context;
- _decoder = new ChunkStream ((WebHeaderCollection) context.Request.Headers);
+
+ _decoder = new ChunkStream (
+ (WebHeaderCollection) context.Request.Headers
+ );
}
#endregion
#region Internal Properties
- internal ChunkStream Decoder {
+ internal bool HasRemainingBuffer {
get {
- return _decoder;
+ return _decoder.Count + Count > 0;
}
+ }
+
+ internal byte[] RemainingBuffer {
+ get {
+ using (var buff = new MemoryStream ()) {
+ var cnt = _decoder.Count;
+
+ if (cnt > 0)
+ buff.Write (_decoder.EndBuffer, _decoder.Offset, cnt);
+
+ cnt = Count;
- set {
- _decoder = value;
+ if (cnt > 0)
+ buff.Write (InitialBuffer, Offset, cnt);
+
+ buff.Close ();
+
+ return buff.ToArray ();
+ }
}
}
@@ -86,26 +119,34 @@ private void onRead (IAsyncResult asyncResult)
{
var rstate = (ReadBufferState) asyncResult.AsyncState;
var ares = rstate.AsyncResult;
+
try {
var nread = base.EndRead (asyncResult);
+
_decoder.Write (ares.Buffer, ares.Offset, nread);
+
nread = _decoder.Read (rstate.Buffer, rstate.Offset, rstate.Count);
+
rstate.Offset += nread;
rstate.Count -= nread;
- if (rstate.Count == 0 || !_decoder.WantMore || nread == 0) {
- _noMoreData = !_decoder.WantMore && nread == 0;
+
+ if (rstate.Count == 0 || !_decoder.WantsMore || nread == 0) {
+ _noMoreData = !_decoder.WantsMore && nread == 0;
+
ares.Count = rstate.InitialCount - rstate.Count;
+
ares.Complete ();
return;
}
- ares.Offset = 0;
- ares.Count = Math.Min (_bufferLength, _decoder.ChunkLeft + 6);
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
}
catch (Exception ex) {
- _context.Connection.SendError (ex.Message, 400);
+ _context.ErrorMessage = "I/O operation aborted";
+
+ _context.SendError ();
+
ares.Complete (ex);
}
}
@@ -115,45 +156,65 @@ private void onRead (IAsyncResult asyncResult)
#region Public Methods
public override IAsyncResult BeginRead (
- byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ byte[] buffer,
+ int offset,
+ int count,
+ AsyncCallback callback,
+ object state
+ )
{
if (_disposed)
- throw new ObjectDisposedException (GetType ().ToString ());
+ throw new ObjectDisposedException (ObjectName);
if (buffer == null)
throw new ArgumentNullException ("buffer");
- if (offset < 0)
- throw new ArgumentOutOfRangeException ("offset", "A negative value.");
+ if (offset < 0) {
+ var msg = "A negative value.";
- if (count < 0)
- throw new ArgumentOutOfRangeException ("count", "A negative value.");
+ throw new ArgumentOutOfRangeException ("offset", msg);
+ }
+
+ if (count < 0) {
+ var msg = "A negative value.";
+
+ throw new ArgumentOutOfRangeException ("count", msg);
+ }
var len = buffer.Length;
- if (offset + count > len)
- throw new ArgumentException (
- "The sum of 'offset' and 'count' is greater than 'buffer' length.");
+
+ if (offset + count > len) {
+ var msg = "The sum of offset and count is greater than the length of buffer.";
+
+ throw new ArgumentException (msg);
+ }
var ares = new HttpStreamAsyncResult (callback, state);
+
if (_noMoreData) {
ares.Complete ();
+
return ares;
}
var nread = _decoder.Read (buffer, offset, count);
+
offset += nread;
count -= nread;
+
if (count == 0) {
- // Got all we wanted, no need to bother the decoder yet.
ares.Count = nread;
+
ares.Complete ();
return ares;
}
- if (!_decoder.WantMore) {
+ if (!_decoder.WantsMore) {
_noMoreData = nread == 0;
+
ares.Count = nread;
+
ares.Complete ();
return ares;
@@ -164,7 +225,9 @@ public override IAsyncResult BeginRead (
ares.Count = _bufferLength;
var rstate = new ReadBufferState (buffer, offset, count, ares);
+
rstate.InitialCount += nread;
+
base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate);
return ares;
@@ -175,27 +238,35 @@ public override void Close ()
if (_disposed)
return;
- _disposed = true;
base.Close ();
+
+ _disposed = true;
}
public override int EndRead (IAsyncResult asyncResult)
{
if (_disposed)
- throw new ObjectDisposedException (GetType ().ToString ());
+ throw new ObjectDisposedException (ObjectName);
if (asyncResult == null)
throw new ArgumentNullException ("asyncResult");
var ares = asyncResult as HttpStreamAsyncResult;
- if (ares == null)
- throw new ArgumentException ("A wrong IAsyncResult.", "asyncResult");
+
+ if (ares == null) {
+ var msg = "A wrong IAsyncResult instance.";
+
+ throw new ArgumentException (msg, "asyncResult");
+ }
if (!ares.IsCompleted)
ares.AsyncWaitHandle.WaitOne ();
- if (ares.HasException)
- throw new HttpListenerException (400, "I/O operation aborted.");
+ if (ares.HasException) {
+ var msg = "The I/O operation has been aborted.";
+
+ throw new HttpListenerException (995, msg);
+ }
return ares.Count;
}
@@ -203,6 +274,7 @@ public override int EndRead (IAsyncResult asyncResult)
public override int Read (byte[] buffer, int offset, int count)
{
var ares = BeginRead (buffer, offset, count, null, null);
+
return EndRead (ares);
}
diff --git a/websocket-sharp/Net/ClientSslConfiguration.cs b/websocket-sharp/Net/ClientSslConfiguration.cs
index 800bcb30d..33438a93c 100644
--- a/websocket-sharp/Net/ClientSslConfiguration.cs
+++ b/websocket-sharp/Net/ClientSslConfiguration.cs
@@ -5,7 +5,7 @@
* The MIT License
*
* Copyright (c) 2014 liryna
- * Copyright (c) 2014-2017 sta.blockhead
+ * Copyright (c) 2014-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -42,7 +42,8 @@
namespace WebSocketSharp.Net
{
///
- /// Stores the parameters for the used by clients.
+ /// Stores the parameters for an instance used by
+ /// a client.
///
public class ClientSslConfiguration
{
@@ -60,29 +61,35 @@ public class ClientSslConfiguration
#region Public Constructors
///
- /// Initializes a new instance of the class.
- ///
- public ClientSslConfiguration ()
- {
- _enabledSslProtocols = SslProtocols.Default;
- }
-
- ///
- /// Initializes a new instance of the class
- /// with the specified .
+ /// Initializes a new instance of the
+ /// class with the specified target host name.
///
///
- /// A that represents the target host server name.
+ /// A that specifies the name of the server that
+ /// will share a secure connection with the client.
///
+ ///
+ /// is an empty string.
+ ///
+ ///
+ /// is .
+ ///
public ClientSslConfiguration (string targetHost)
{
+ if (targetHost == null)
+ throw new ArgumentNullException ("targetHost");
+
+ if (targetHost.Length == 0)
+ throw new ArgumentException ("An empty string.", "targetHost");
+
_targetHost = targetHost;
- _enabledSslProtocols = SslProtocols.Default;
+
+ _enabledSslProtocols = SslProtocols.None;
}
///
- /// Copies the parameters from the specified to
- /// a new instance of the class.
+ /// Initializes a new instance of the
+ /// class copying from the specified configuration.
///
///
/// A from which to copy.
@@ -131,15 +138,16 @@ public bool CheckCertificateRevocation {
}
///
- /// Gets or sets the certificates from which to select one to
- /// supply to the server.
+ /// Gets or sets the collection of the certificates from which to select
+ /// one to supply to the server.
///
///
///
- /// A or .
+ /// A that contains
+ /// the certificates from which to select.
///
///
- /// That collection contains client certificates from which to select.
+ /// if not present.
///
///
/// The default value is .
@@ -156,21 +164,23 @@ public X509CertificateCollection ClientCertificates {
}
///
- /// Gets or sets the callback used to select the certificate to
- /// supply to the server.
+ /// Gets or sets the callback used to select the certificate to supply to
+ /// the server.
///
///
- /// No certificate is supplied if the callback returns
- /// .
+ /// No certificate is supplied if the callback returns .
///
///
///
- /// A delegate that
- /// invokes the method called for selecting the certificate.
+ /// A delegate.
+ ///
+ ///
+ /// It represents the delegate called when the client selects
+ /// the certificate.
///
///
- /// The default value is a delegate that invokes a method that
- /// only returns .
+ /// The default value invokes a method that only returns
+ /// .
///
///
public LocalCertificateSelectionCallback ClientCertificateSelectionCallback {
@@ -187,15 +197,17 @@ public LocalCertificateSelectionCallback ClientCertificateSelectionCallback {
}
///
- /// Gets or sets the protocols used for authentication.
+ /// Gets or sets the enabled versions of the SSL/TLS protocols.
///
///
///
- /// The enum values that represent
- /// the protocols used for authentication.
+ /// Any of the enum values.
///
///
- /// The default value is .
+ /// It represents the enabled versions of the SSL/TLS protocols.
+ ///
+ ///
+ /// The default value is .
///
///
public SslProtocols EnabledSslProtocols {
@@ -209,20 +221,22 @@ public SslProtocols EnabledSslProtocols {
}
///
- /// Gets or sets the callback used to validate the certificate
- /// supplied by the server.
+ /// Gets or sets the callback used to validate the certificate supplied by
+ /// the server.
///
///
/// The certificate is valid if the callback returns true.
///
///
///
- /// A delegate that
- /// invokes the method called for validating the certificate.
+ /// A delegate.
+ ///
+ ///
+ /// It represents the delegate called when the client validates
+ /// the certificate.
///
///
- /// The default value is a delegate that invokes a method that
- /// only returns true.
+ /// The default value invokes a method that only returns true.
///
///
public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
@@ -239,24 +253,30 @@ public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
}
///
- /// Gets or sets the target host server name.
+ /// Gets or sets the target host name.
///
///
- ///
- /// A or
- /// if not specified.
- ///
- ///
- /// That string represents the name of the server that
- /// will share a secure connection with a client.
- ///
+ /// A that represents the name of the server that
+ /// will share a secure connection with the client.
///
+ ///
+ /// The value specified for a set operation is an empty string.
+ ///
+ ///
+ /// The value specified for a set operation is .
+ ///
public string TargetHost {
get {
return _targetHost;
}
set {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (value.Length == 0)
+ throw new ArgumentException ("An empty string.", "value");
+
_targetHost = value;
}
}
diff --git a/websocket-sharp/Net/Cookie.cs b/websocket-sharp/Net/Cookie.cs
index 1c5a4bf2d..149b5041e 100644
--- a/websocket-sharp/Net/Cookie.cs
+++ b/websocket-sharp/Net/Cookie.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2019 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -56,7 +56,8 @@ namespace WebSocketSharp.Net
///
/// -
///
- /// Netscape specification
+ ///
+ /// Netscape specification
///
///
/// -
@@ -116,9 +117,6 @@ static Cookie ()
#region Internal Constructors
- ///
- /// Initializes a new instance of the class.
- ///
internal Cookie ()
{
init (String.Empty, String.Empty, String.Empty, String.Empty);
@@ -145,33 +143,33 @@ internal Cookie ()
///
/// A that specifies the value of the cookie.
///
- ///
- /// is .
- ///
///
///
/// is an empty string.
///
///
- /// - or -
+ /// -or-
///
///
/// starts with a dollar sign.
///
///
- /// - or -
+ /// -or-
///
///
/// contains an invalid character.
///
///
- /// - or -
+ /// -or-
///
///
/// is a string not enclosed in double quotes
- /// that contains an invalid character.
+ /// although it contains a reserved character.
///
///
+ ///
+ /// is .
+ ///
public Cookie (string name, string value)
: this (name, value, String.Empty, String.Empty)
{
@@ -198,33 +196,33 @@ public Cookie (string name, string value)
/// A that specifies the value of the Path
/// attribute of the cookie.
///
- ///
- /// is .
- ///
///
///
/// is an empty string.
///
///
- /// - or -
+ /// -or-
///
///
/// starts with a dollar sign.
///
///
- /// - or -
+ /// -or-
///
///
/// contains an invalid character.
///
///
- /// - or -
+ /// -or-
///
///
/// is a string not enclosed in double quotes
- /// that contains an invalid character.
+ /// although it contains a reserved character.
///
///
+ ///
+ /// is .
+ ///
public Cookie (string name, string value, string path)
: this (name, value, path, String.Empty)
{
@@ -255,33 +253,33 @@ public Cookie (string name, string value, string path)
/// A that specifies the value of the Domain
/// attribute of the cookie.
///
- ///
- /// is .
- ///
///
///
/// is an empty string.
///
///
- /// - or -
+ /// -or-
///
///
/// starts with a dollar sign.
///
///
- /// - or -
+ /// -or-
///
///
/// contains an invalid character.
///
///
- /// - or -
+ /// -or-
///
///
/// is a string not enclosed in double quotes
- /// that contains an invalid character.
+ /// although it contains a reserved character.
///
///
+ ///
+ /// is .
+ ///
public Cookie (string name, string value, string path, string domain)
{
if (name == null)
@@ -292,11 +290,13 @@ public Cookie (string name, string value, string path, string domain)
if (name[0] == '$') {
var msg = "It starts with a dollar sign.";
+
throw new ArgumentException (msg, "name");
}
if (!name.IsToken ()) {
var msg = "It contains an invalid character.";
+
throw new ArgumentException (msg, "name");
}
@@ -306,6 +306,7 @@ public Cookie (string name, string value, string path, string domain)
if (value.Contains (_reservedCharsForValue)) {
if (!value.IsEnclosedIn ('"')) {
var msg = "A string not enclosed in double quotes.";
+
throw new ArgumentException (msg, "value");
}
}
@@ -333,6 +334,7 @@ internal int MaxAge {
: _expires;
var span = expires - DateTime.Now;
+
return span > TimeSpan.Zero
? (int) span.TotalSeconds
: 0;
@@ -447,7 +449,7 @@ internal set {
/// the cookie is valid for.
///
///
- /// An empty string if this attribute is not needed.
+ /// An empty string if not necessary.
///
///
public string Domain {
@@ -490,7 +492,7 @@ public bool Expired {
/// the cookie expires on.
///
///
- /// if this attribute is not needed.
+ /// if not necessary.
///
///
/// The default value is .
@@ -542,26 +544,26 @@ public bool HttpOnly {
/// RFC 2616.
///
///
- ///
- /// The value specified for a set operation is .
- ///
///
///
/// The value specified for a set operation is an empty string.
///
///
- /// - or -
+ /// -or-
///
///
/// The value specified for a set operation starts with a dollar sign.
///
///
- /// - or -
+ /// -or-
///
///
/// The value specified for a set operation contains an invalid character.
///
///
+ ///
+ /// The value specified for a set operation is .
+ ///
public string Name {
get {
return _name;
@@ -576,11 +578,13 @@ public string Name {
if (value[0] == '$') {
var msg = "It starts with a dollar sign.";
+
throw new ArgumentException (msg, "value");
}
if (!value.IsToken ()) {
var msg = "It contains an invalid character.";
+
throw new ArgumentException (msg, "value");
}
@@ -627,11 +631,12 @@ public string Port {
internal set {
int[] ports;
+
if (!tryCreatePorts (value, out ports))
return;
- _port = value;
_ports = ports;
+ _port = value;
}
}
@@ -683,7 +688,7 @@ public DateTime TimeStamp {
///
///
/// The value specified for a set operation is a string not enclosed in
- /// double quotes that contains an invalid character.
+ /// double quotes although it contains a reserved character.
///
public string Value {
get {
@@ -697,6 +702,7 @@ public string Value {
if (value.Contains (_reservedCharsForValue)) {
if (!value.IsEnclosedIn ('"')) {
var msg = "A string not enclosed in double quotes.";
+
throw new ArgumentException (msg, "value");
}
}
@@ -714,7 +720,10 @@ public string Value {
/// management that the cookie conforms to.
///
///
- /// 0 or 1. 0 if not present.
+ /// 0 or 1.
+ ///
+ ///
+ /// 0 if not present.
///
///
/// The default value is 0.
@@ -764,13 +773,14 @@ private string toResponseStringVersion0 ()
buff.AppendFormat ("{0}={1}", _name, _value);
if (_expires != DateTime.MinValue) {
- buff.AppendFormat (
- "; Expires={0}",
- _expires.ToUniversalTime ().ToString (
- "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
- CultureInfo.CreateSpecificCulture ("en-US")
- )
- );
+ var expires = _expires
+ .ToUniversalTime ()
+ .ToString (
+ "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
+ CultureInfo.CreateSpecificCulture ("en-US")
+ );
+
+ buff.AppendFormat ("; Expires={0}", expires);
}
if (!_path.IsNullOrEmpty ())
@@ -813,13 +823,18 @@ private string toResponseStringVersion1 ()
buff.Append ("; Port");
}
- if (_comment != null)
- buff.AppendFormat ("; Comment={0}", HttpUtility.UrlEncode (_comment));
+ if (_comment != null) {
+ var comment = HttpUtility.UrlEncode (_comment);
+
+ buff.AppendFormat ("; Comment={0}", comment);
+ }
if (_commentUri != null) {
var url = _commentUri.OriginalString;
+
buff.AppendFormat (
- "; CommentURL={0}", !url.IsToken () ? url.Quote () : url
+ "; CommentURL={0}",
+ !url.IsToken () ? url.Quote () : url
);
}
@@ -842,8 +857,10 @@ private static bool tryCreatePorts (string value, out int[] result)
for (var i = 0; i < len; i++) {
var s = arr[i].Trim ();
+
if (s.Length == 0) {
res[i] = Int32.MinValue;
+
continue;
}
@@ -852,6 +869,7 @@ private static bool tryCreatePorts (string value, out int[] result)
}
result = res;
+
return true;
}
@@ -914,24 +932,21 @@ internal string ToRequestString (Uri uri)
return buff.ToString ();
}
- ///
- /// Returns a string that represents the current cookie instance.
- ///
- ///
- /// A that is suitable for the Set-Cookie response
- /// header.
- ///
internal string ToResponseString ()
{
- return _name.Length == 0
- ? String.Empty
- : _version == 0
- ? toResponseStringVersion0 ()
- : toResponseStringVersion1 ();
+ if (_name.Length == 0)
+ return String.Empty;
+
+ if (_version == 0)
+ return toResponseStringVersion0 ();
+
+ return toResponseStringVersion1 ();
}
internal static bool TryCreate (
- string name, string value, out Cookie result
+ string name,
+ string value,
+ out Cookie result
)
{
result = null;
@@ -970,6 +985,7 @@ internal static bool TryCreate (
public override bool Equals (object comparand)
{
var cookie = comparand as Cookie;
+
if (cookie == null)
return false;
@@ -991,13 +1007,13 @@ public override bool Equals (object comparand)
///
public override int GetHashCode ()
{
- return hash (
- StringComparer.InvariantCultureIgnoreCase.GetHashCode (_name),
- _value.GetHashCode (),
- _path.GetHashCode (),
- StringComparer.InvariantCultureIgnoreCase.GetHashCode (_domain),
- _version
- );
+ var i = StringComparer.InvariantCultureIgnoreCase.GetHashCode (_name);
+ var j = _value.GetHashCode ();
+ var k = _path.GetHashCode ();
+ var l = StringComparer.InvariantCultureIgnoreCase.GetHashCode (_domain);
+ var m = _version;
+
+ return hash (i, j, k, l, m);
}
///
diff --git a/websocket-sharp/Net/CookieCollection.cs b/websocket-sharp/Net/CookieCollection.cs
index 8c0322bda..9a8ce641b 100644
--- a/websocket-sharp/Net/CookieCollection.cs
+++ b/websocket-sharp/Net/CookieCollection.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2019 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -85,6 +85,7 @@ internal IList List {
internal IEnumerable Sorted {
get {
var list = new List (_list);
+
if (list.Count > 1)
list.Sort (compareForSorted);
@@ -223,8 +224,10 @@ public object SyncRoot {
private void add (Cookie cookie)
{
var idx = search (cookie);
+
if (idx == -1) {
_list.Add (cookie);
+
return;
}
@@ -240,11 +243,16 @@ private static int compareForSort (Cookie x, Cookie y)
private static int compareForSorted (Cookie x, Cookie y)
{
var ret = x.Version - y.Version;
- return ret != 0
- ? ret
- : (ret = x.Name.CompareTo (y.Name)) != 0
- ? ret
- : y.Path.Length - x.Path.Length;
+
+ if (ret != 0)
+ return ret;
+
+ ret = x.Name.CompareTo (y.Name);
+
+ if (ret != 0)
+ return ret;
+
+ return y.Path.Length - x.Path.Length;
}
private static CookieCollection parseRequest (string value)
@@ -253,22 +261,25 @@ private static CookieCollection parseRequest (string value)
Cookie cookie = null;
var ver = 0;
-
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
+
var pairs = value.SplitHeaderValue (',', ';').ToList ();
for (var i = 0; i < pairs.Count; i++) {
var pair = pairs[i].Trim ();
+
if (pair.Length == 0)
continue;
var idx = pair.IndexOf ('=');
+
if (idx == -1) {
if (cookie == null)
continue;
if (pair.Equals ("$port", caseInsensitive)) {
cookie.Port = "\"\"";
+
continue;
}
@@ -278,6 +289,7 @@ private static CookieCollection parseRequest (string value)
if (idx == 0) {
if (cookie != null) {
ret.add (cookie);
+
cookie = null;
}
@@ -293,11 +305,15 @@ private static CookieCollection parseRequest (string value)
if (val.Length == 0)
continue;
+ var s = val.Unquote ();
+
int num;
- if (!Int32.TryParse (val.Unquote (), out num))
+
+ if (!Int32.TryParse (s, out num))
continue;
ver = num;
+
continue;
}
@@ -309,6 +325,7 @@ private static CookieCollection parseRequest (string value)
continue;
cookie.Path = val;
+
continue;
}
@@ -320,6 +337,7 @@ private static CookieCollection parseRequest (string value)
continue;
cookie.Domain = val;
+
continue;
}
@@ -331,6 +349,7 @@ private static CookieCollection parseRequest (string value)
continue;
cookie.Port = val;
+
continue;
}
@@ -355,37 +374,43 @@ private static CookieCollection parseResponse (string value)
var ret = new CookieCollection ();
Cookie cookie = null;
-
var caseInsensitive = StringComparison.InvariantCultureIgnoreCase;
+
var pairs = value.SplitHeaderValue (',', ';').ToList ();
for (var i = 0; i < pairs.Count; i++) {
var pair = pairs[i].Trim ();
+
if (pair.Length == 0)
continue;
var idx = pair.IndexOf ('=');
+
if (idx == -1) {
if (cookie == null)
continue;
if (pair.Equals ("port", caseInsensitive)) {
cookie.Port = "\"\"";
+
continue;
}
if (pair.Equals ("discard", caseInsensitive)) {
cookie.Discard = true;
+
continue;
}
if (pair.Equals ("secure", caseInsensitive)) {
cookie.Secure = true;
+
continue;
}
if (pair.Equals ("httponly", caseInsensitive)) {
cookie.HttpOnly = true;
+
continue;
}
@@ -395,6 +420,7 @@ private static CookieCollection parseResponse (string value)
if (idx == 0) {
if (cookie != null) {
ret.add (cookie);
+
cookie = null;
}
@@ -413,11 +439,15 @@ private static CookieCollection parseResponse (string value)
if (val.Length == 0)
continue;
+ var s = val.Unquote ();
+
int num;
- if (!Int32.TryParse (val.Unquote (), out num))
+
+ if (!Int32.TryParse (s, out num))
continue;
cookie.Version = num;
+
continue;
}
@@ -437,22 +467,30 @@ private static CookieCollection parseResponse (string value)
continue;
var buff = new StringBuilder (val, 32);
+
buff.AppendFormat (", {0}", pairs[i].Trim ());
+ var s = buff.ToString ();
+ var fmts = new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" };
+ var provider = CultureInfo.CreateSpecificCulture ("en-US");
+ var style = DateTimeStyles.AdjustToUniversal
+ | DateTimeStyles.AssumeUniversal;
+
DateTime expires;
- if (
- !DateTime.TryParseExact (
- buff.ToString (),
- new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
- CultureInfo.CreateSpecificCulture ("en-US"),
- DateTimeStyles.AdjustToUniversal
- | DateTimeStyles.AssumeUniversal,
- out expires
- )
- )
+
+ var done = DateTime.TryParseExact (
+ s,
+ fmts,
+ provider,
+ style,
+ out expires
+ );
+
+ if (!done)
continue;
cookie.Expires = expires.ToLocalTime ();
+
continue;
}
@@ -463,11 +501,15 @@ out expires
if (val.Length == 0)
continue;
- int num;
- if (!Int32.TryParse (val.Unquote (), out num))
+ var s = val.Unquote ();
+
+ int maxAge;
+
+ if (!Int32.TryParse (s, out maxAge))
continue;
- cookie.MaxAge = num;
+ cookie.MaxAge = maxAge;
+
continue;
}
@@ -479,6 +521,7 @@ out expires
continue;
cookie.Path = val;
+
continue;
}
@@ -490,6 +533,7 @@ out expires
continue;
cookie.Domain = val;
+
continue;
}
@@ -501,6 +545,7 @@ out expires
continue;
cookie.Port = val;
+
continue;
}
@@ -512,6 +557,7 @@ out expires
continue;
cookie.Comment = urlDecode (val, Encoding.UTF8);
+
continue;
}
@@ -523,6 +569,7 @@ out expires
continue;
cookie.CommentUri = val.Unquote ().ToUri ();
+
continue;
}
@@ -534,6 +581,7 @@ out expires
continue;
cookie.SameSite = val.Unquote ();
+
continue;
}
@@ -579,9 +627,7 @@ private static string urlDecode (string s, Encoding encoding)
internal static CookieCollection Parse (string value, bool response)
{
try {
- return response
- ? parseResponse (value)
- : parseRequest (value);
+ return response ? parseResponse (value) : parseRequest (value);
}
catch (Exception ex) {
throw new CookieException ("It could not be parsed.", ex);
@@ -591,16 +637,19 @@ internal static CookieCollection Parse (string value, bool response)
internal void SetOrRemove (Cookie cookie)
{
var idx = search (cookie);
+
if (idx == -1) {
if (cookie.Expired)
return;
_list.Add (cookie);
+
return;
}
if (cookie.Expired) {
_list.RemoveAt (idx);
+
return;
}
@@ -615,8 +664,10 @@ internal void SetOrRemove (CookieCollection cookies)
internal void Sort ()
{
- if (_list.Count > 1)
- _list.Sort (compareForSort);
+ if (_list.Count < 2)
+ return;
+
+ _list.Sort (compareForSort);
}
#endregion
@@ -629,16 +680,17 @@ internal void Sort ()
///
/// A to add.
///
- ///
- /// The collection is read-only.
- ///
///
/// is .
///
+ ///
+ /// The collection is read-only.
+ ///
public void Add (Cookie cookie)
{
if (_readOnly) {
var msg = "The collection is read-only.";
+
throw new InvalidOperationException (msg);
}
@@ -654,16 +706,17 @@ public void Add (Cookie cookie)
///
/// A that contains the cookies to add.
///
- ///
- /// The collection is read-only.
- ///
///
/// is .
///
+ ///
+ /// The collection is read-only.
+ ///
public void Add (CookieCollection cookies)
{
if (_readOnly) {
var msg = "The collection is read-only.";
+
throw new InvalidOperationException (msg);
}
@@ -684,6 +737,7 @@ public void Clear ()
{
if (_readOnly) {
var msg = "The collection is read-only.";
+
throw new InvalidOperationException (msg);
}
@@ -723,26 +777,30 @@ public bool Contains (Cookie cookie)
/// An that specifies the zero-based index in
/// the array at which copying starts.
///
+ ///
+ /// The space from to the end of
+ /// is not enough to copy to.
+ ///
///
/// is .
///
///
/// is less than zero.
///
- ///
- /// The space from to the end of
- /// is not enough to copy to.
- ///
public void CopyTo (Cookie[] array, int index)
{
if (array == null)
throw new ArgumentNullException ("array");
- if (index < 0)
- throw new ArgumentOutOfRangeException ("index", "Less than zero.");
+ if (index < 0) {
+ var msg = "Less than zero.";
+
+ throw new ArgumentOutOfRangeException ("index", msg);
+ }
if (array.Length - index < _list.Count) {
var msg = "The available space of the array is not enough to copy to.";
+
throw new ArgumentException (msg);
}
@@ -776,16 +834,17 @@ public IEnumerator GetEnumerator ()
///
/// A to remove.
///
- ///
- /// The collection is read-only.
- ///
///
/// is .
///
+ ///
+ /// The collection is read-only.
+ ///
public bool Remove (Cookie cookie)
{
if (_readOnly) {
var msg = "The collection is read-only.";
+
throw new InvalidOperationException (msg);
}
@@ -793,10 +852,12 @@ public bool Remove (Cookie cookie)
throw new ArgumentNullException ("cookie");
var idx = search (cookie);
+
if (idx == -1)
return false;
_list.RemoveAt (idx);
+
return true;
}
diff --git a/websocket-sharp/Net/CookieException.cs b/websocket-sharp/Net/CookieException.cs
index 2a5abe98a..3c9ab3f9e 100644
--- a/websocket-sharp/Net/CookieException.cs
+++ b/websocket-sharp/Net/CookieException.cs
@@ -7,7 +7,7 @@
*
* The MIT License
*
- * Copyright (c) 2012-2019 sta.blockhead
+ * Copyright (c) 2012-2024 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -66,10 +66,11 @@ internal CookieException (string message, Exception innerException)
///
/// Initializes a new instance of the class
- /// with the serialized data.
+ /// with the specified serialized data.
///
///
- /// A that holds the serialized object data.
+ /// A that contains the serialized
+ /// object data.
///
///
/// A that specifies the source for
@@ -79,7 +80,8 @@ internal CookieException (string message, Exception innerException)
/// is .
///
protected CookieException (
- SerializationInfo serializationInfo, StreamingContext streamingContext
+ SerializationInfo serializationInfo,
+ StreamingContext streamingContext
)
: base (serializationInfo, streamingContext)
{
@@ -122,7 +124,8 @@ public CookieException ()
)
]
public override void GetObjectData (
- SerializationInfo serializationInfo, StreamingContext streamingContext
+ SerializationInfo serializationInfo,
+ StreamingContext streamingContext
)
{
base.GetObjectData (serializationInfo, streamingContext);
@@ -154,7 +157,8 @@ public override void GetObjectData (
)
]
void ISerializable.GetObjectData (
- SerializationInfo serializationInfo, StreamingContext streamingContext
+ SerializationInfo serializationInfo,
+ StreamingContext streamingContext
)
{
base.GetObjectData (serializationInfo, streamingContext);
diff --git a/websocket-sharp/Net/EndPointListener.cs b/websocket-sharp/Net/EndPointListener.cs
index 67fa26393..3c8b3653a 100644
--- a/websocket-sharp/Net/EndPointListener.cs
+++ b/websocket-sharp/Net/EndPointListener.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2016 sta.blockhead
+ * Copyright (c) 2012-2023 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -61,16 +61,16 @@ internal sealed class EndPointListener
{
#region Private Fields
- private List _all; // host == '+'
- private static readonly string _defaultCertFolderPath;
- private IPEndPoint _endpoint;
- private Dictionary _prefixes;
- private bool _secure;
- private Socket _socket;
- private ServerSslConfiguration _sslConfig;
- private List _unhandled; // host == '*'
- private Dictionary _unregistered;
- private object _unregisteredSync;
+ private List _all; // host == '+'
+ private Dictionary _connections;
+ private object _connectionsSync;
+ private static readonly string _defaultCertFolderPath;
+ private IPEndPoint _endpoint;
+ private List _prefixes;
+ private bool _secure;
+ private Socket _socket;
+ private ServerSslConfiguration _sslConfig;
+ private List _unhandled; // host == '*'
#endregion
@@ -78,8 +78,9 @@ internal sealed class EndPointListener
static EndPointListener ()
{
- _defaultCertFolderPath =
- Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+ _defaultCertFolderPath = Environment.GetFolderPath (
+ Environment.SpecialFolder.ApplicationData
+ );
}
#endregion
@@ -94,27 +95,43 @@ internal EndPointListener (
bool reuseAddress
)
{
+ _endpoint = endpoint;
+
if (secure) {
- var cert =
- getCertificate (endpoint.Port, certificateFolderPath, sslConfig.ServerCertificate);
+ var cert = getCertificate (
+ endpoint.Port,
+ certificateFolderPath,
+ sslConfig.ServerCertificate
+ );
+
+ if (cert == null) {
+ var msg = "No server certificate could be found.";
- if (cert == null)
- throw new ArgumentException ("No server certificate could be found.");
+ throw new ArgumentException (msg);
+ }
_secure = true;
_sslConfig = new ServerSslConfiguration (sslConfig);
_sslConfig.ServerCertificate = cert;
}
- _endpoint = endpoint;
- _prefixes = new Dictionary ();
- _unregistered = new Dictionary ();
- _unregisteredSync = ((ICollection) _unregistered).SyncRoot;
- _socket =
- new Socket (endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
- if (reuseAddress)
- _socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ _prefixes = new List ();
+ _connections = new Dictionary ();
+ _connectionsSync = ((ICollection) _connections).SyncRoot;
+
+ _socket = new Socket (
+ endpoint.Address.AddressFamily,
+ SocketType.Stream,
+ ProtocolType.Tcp
+ );
+
+ if (reuseAddress) {
+ _socket.SetSocketOption (
+ SocketOptionLevel.Socket,
+ SocketOptionName.ReuseAddress,
+ true
+ );
+ }
_socket.Bind (endpoint);
_socket.Listen (500);
@@ -153,33 +170,59 @@ public ServerSslConfiguration SslConfiguration {
#region Private Methods
- private static void addSpecial (List prefixes, HttpListenerPrefix prefix)
+ private static void addSpecial (
+ List prefixes,
+ HttpListenerPrefix prefix
+ )
{
var path = prefix.Path;
+
foreach (var pref in prefixes) {
- if (pref.Path == path)
- throw new HttpListenerException (87, "The prefix is already in use.");
+ if (pref.Path == path) {
+ var msg = "The prefix is already in use.";
+
+ throw new HttpListenerException (87, msg);
+ }
}
prefixes.Add (prefix);
}
- private static RSACryptoServiceProvider createRSAFromFile (string filename)
+ private void clearConnections ()
{
- byte[] pvk = null;
- using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
- pvk = new byte[fs.Length];
- fs.Read (pvk, 0, pvk.Length);
+ HttpConnection[] conns = null;
+
+ lock (_connectionsSync) {
+ var cnt = _connections.Count;
+
+ if (cnt == 0)
+ return;
+
+ conns = new HttpConnection[cnt];
+
+ _connections.Values.CopyTo (conns, 0);
+ _connections.Clear ();
}
+ foreach (var conn in conns)
+ conn.Close (true);
+ }
+
+ private static RSACryptoServiceProvider createRSAFromFile (string path)
+ {
var rsa = new RSACryptoServiceProvider ();
- rsa.ImportCspBlob (pvk);
+
+ var key = File.ReadAllBytes (path);
+
+ rsa.ImportCspBlob (key);
return rsa;
}
private static X509Certificate2 getCertificate (
- int port, string folderPath, X509Certificate2 defaultCertificate
+ int port,
+ string folderPath,
+ X509Certificate2 defaultCertificate
)
{
if (folderPath == null || folderPath.Length == 0)
@@ -188,17 +231,21 @@ private static X509Certificate2 getCertificate (
try {
var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port));
var key = Path.Combine (folderPath, String.Format ("{0}.key", port));
- if (File.Exists (cer) && File.Exists (key)) {
- var cert = new X509Certificate2 (cer);
- cert.PrivateKey = createRSAFromFile (key);
- return cert;
- }
+ var exists = File.Exists (cer) && File.Exists (key);
+
+ if (!exists)
+ return defaultCertificate;
+
+ var cert = new X509Certificate2 (cer);
+
+ cert.PrivateKey = createRSAFromFile (key);
+
+ return cert;
}
catch {
+ return defaultCertificate;
}
-
- return defaultCertificate;
}
private void leaveIfNoPrefix ()
@@ -207,14 +254,16 @@ private void leaveIfNoPrefix ()
return;
var prefs = _unhandled;
+
if (prefs != null && prefs.Count > 0)
return;
prefs = _all;
+
if (prefs != null && prefs.Count > 0)
return;
- EndPointManager.RemoveEndPoint (_endpoint);
+ Close ();
}
private static void onAccept (IAsyncResult asyncResult)
@@ -222,20 +271,23 @@ private static void onAccept (IAsyncResult asyncResult)
var lsnr = (EndPointListener) asyncResult.AsyncState;
Socket sock = null;
+
try {
sock = lsnr._socket.EndAccept (asyncResult);
}
- catch (SocketException) {
- // TODO: Should log the error code when this class has a logging.
- }
catch (ObjectDisposedException) {
return;
}
+ catch (Exception) {
+ // TODO: Logging.
+ }
try {
lsnr._socket.BeginAccept (onAccept, lsnr);
}
- catch {
+ catch (Exception) {
+ // TODO: Logging.
+
if (sock != null)
sock.Close ();
@@ -248,33 +300,42 @@ private static void onAccept (IAsyncResult asyncResult)
processAccepted (sock, lsnr);
}
- private static void processAccepted (Socket socket, EndPointListener listener)
+ private static void processAccepted (
+ Socket socket,
+ EndPointListener listener
+ )
{
HttpConnection conn = null;
+
try {
conn = new HttpConnection (socket, listener);
- lock (listener._unregisteredSync)
- listener._unregistered[conn] = conn;
-
- conn.BeginReadRequest ();
}
- catch {
- if (conn != null) {
- conn.Close (true);
- return;
- }
+ catch (Exception) {
+ // TODO: Logging.
socket.Close ();
+
+ return;
}
+
+ lock (listener._connectionsSync)
+ listener._connections.Add (conn, conn);
+
+ conn.BeginReadRequest ();
}
- private static bool removeSpecial (List prefixes, HttpListenerPrefix prefix)
+ private static bool removeSpecial (
+ List prefixes,
+ HttpListenerPrefix prefix
+ )
{
var path = prefix.Path;
var cnt = prefixes.Count;
+
for (var i = 0; i < cnt; i++) {
if (prefixes[i].Path == path) {
prefixes.RemoveAt (i);
+
return true;
}
}
@@ -283,29 +344,34 @@ private static bool removeSpecial (List prefixes, HttpListen
}
private static HttpListener searchHttpListenerFromSpecial (
- string path, List prefixes
+ string path,
+ List prefixes
)
{
if (prefixes == null)
return null;
- HttpListener bestMatch = null;
+ HttpListener ret = null;
var bestLen = -1;
+
foreach (var pref in prefixes) {
var prefPath = pref.Path;
-
var len = prefPath.Length;
+
if (len < bestLen)
continue;
- if (path.StartsWith (prefPath)) {
- bestLen = len;
- bestMatch = pref.Listener;
- }
+ var match = path.StartsWith (prefPath, StringComparison.Ordinal);
+
+ if (!match)
+ continue;
+
+ bestLen = len;
+ ret = pref.Listener;
}
- return bestMatch;
+ return ret;
}
#endregion
@@ -325,8 +391,8 @@ internal static bool CertificateExists (int port, string folderPath)
internal void RemoveConnection (HttpConnection connection)
{
- lock (_unregisteredSync)
- _unregistered.Remove (connection);
+ lock (_connectionsSync)
+ _connections.Remove (connection);
}
internal bool TrySearchHttpListener (Uri uri, out HttpListener listener)
@@ -340,48 +406,53 @@ internal bool TrySearchHttpListener (Uri uri, out HttpListener listener)
var dns = Uri.CheckHostName (host) == UriHostNameType.Dns;
var port = uri.Port.ToString ();
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
- var pathSlash = path[path.Length - 1] != '/' ? path + "/" : path;
+
+ if (path[path.Length - 1] != '/')
+ path += "/";
if (host != null && host.Length > 0) {
+ var prefs = _prefixes;
var bestLen = -1;
- foreach (var pref in _prefixes.Keys) {
+
+ foreach (var pref in prefs) {
if (dns) {
var prefHost = pref.Host;
- if (Uri.CheckHostName (prefHost) == UriHostNameType.Dns && prefHost != host)
- continue;
+ var prefDns = Uri.CheckHostName (prefHost) == UriHostNameType.Dns;
+
+ if (prefDns) {
+ if (prefHost != host)
+ continue;
+ }
}
if (pref.Port != port)
continue;
var prefPath = pref.Path;
-
var len = prefPath.Length;
+
if (len < bestLen)
continue;
- if (path.StartsWith (prefPath) || pathSlash.StartsWith (prefPath)) {
- bestLen = len;
- listener = _prefixes[pref];
- }
+ var match = path.StartsWith (prefPath, StringComparison.Ordinal);
+
+ if (!match)
+ continue;
+
+ bestLen = len;
+ listener = pref.Listener;
}
if (bestLen != -1)
return true;
}
- var prefs = _unhandled;
- listener = searchHttpListenerFromSpecial (path, prefs);
- if (listener == null && pathSlash != path)
- listener = searchHttpListenerFromSpecial (pathSlash, prefs);
+ listener = searchHttpListenerFromSpecial (path, _unhandled);
if (listener != null)
return true;
- prefs = _all;
- listener = searchHttpListenerFromSpecial (path, prefs);
- if (listener == null && pathSlash != path)
- listener = searchHttpListenerFromSpecial (pathSlash, prefs);
+ listener = searchHttpListenerFromSpecial (path, _all);
return listener != null;
}
@@ -390,9 +461,10 @@ internal bool TrySearchHttpListener (Uri uri, out HttpListener listener)
#region Public Methods
- public void AddPrefix (HttpListenerPrefix prefix, HttpListener listener)
+ public void AddPrefix (HttpListenerPrefix prefix)
{
List current, future;
+
if (prefix.Host == "*") {
do {
current = _unhandled;
@@ -400,10 +472,12 @@ public void AddPrefix (HttpListenerPrefix prefix, HttpListener listener)
? new List (current)
: new List ();
- prefix.Listener = listener;
addSpecial (future, prefix);
}
- while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
+ while (
+ Interlocked.CompareExchange (ref _unhandled, future, current)
+ != current
+ );
return;
}
@@ -415,97 +489,112 @@ public void AddPrefix (HttpListenerPrefix prefix, HttpListener listener)
? new List (current)
: new List ();
- prefix.Listener = listener;
addSpecial (future, prefix);
}
- while (Interlocked.CompareExchange (ref _all, future, current) != current);
+ while (
+ Interlocked.CompareExchange (ref _all, future, current)
+ != current
+ );
return;
}
- Dictionary prefs, prefs2;
do {
- prefs = _prefixes;
- if (prefs.ContainsKey (prefix)) {
- if (prefs[prefix] != listener) {
- throw new HttpListenerException (
- 87, String.Format ("There's another listener for {0}.", prefix)
- );
+ current = _prefixes;
+
+ var idx = current.IndexOf (prefix);
+
+ if (idx > -1) {
+ if (current[idx].Listener != prefix.Listener) {
+ var fmt = "There is another listener for {0}.";
+ var msg = String.Format (fmt, prefix);
+
+ throw new HttpListenerException (87, msg);
}
return;
}
- prefs2 = new Dictionary (prefs);
- prefs2[prefix] = listener;
+ future = new List (current);
+
+ future.Add (prefix);
}
- while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs);
+ while (
+ Interlocked.CompareExchange (ref _prefixes, future, current)
+ != current
+ );
}
public void Close ()
{
_socket.Close ();
- HttpConnection[] conns = null;
- lock (_unregisteredSync) {
- if (_unregistered.Count == 0)
- return;
-
- var keys = _unregistered.Keys;
- conns = new HttpConnection[keys.Count];
- keys.CopyTo (conns, 0);
- _unregistered.Clear ();
- }
-
- for (var i = conns.Length - 1; i >= 0; i--)
- conns[i].Close (true);
+ clearConnections ();
+ EndPointManager.RemoveEndPoint (_endpoint);
}
- public void RemovePrefix (HttpListenerPrefix prefix, HttpListener listener)
+ public void RemovePrefix (HttpListenerPrefix prefix)
{
List current, future;
+
if (prefix.Host == "*") {
do {
current = _unhandled;
+
if (current == null)
break;
future = new List (current);
+
if (!removeSpecial (future, prefix))
- break; // The prefix wasn't found.
+ break;
}
- while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
+ while (
+ Interlocked.CompareExchange (ref _unhandled, future, current)
+ != current
+ );
leaveIfNoPrefix ();
+
return;
}
if (prefix.Host == "+") {
do {
current = _all;
+
if (current == null)
break;
future = new List (current);
+
if (!removeSpecial (future, prefix))
- break; // The prefix wasn't found.
+ break;
}
- while (Interlocked.CompareExchange (ref _all, future, current) != current);
+ while (
+ Interlocked.CompareExchange (ref _all, future, current)
+ != current
+ );
leaveIfNoPrefix ();
+
return;
}
- Dictionary prefs, prefs2;
do {
- prefs = _prefixes;
- if (!prefs.ContainsKey (prefix))
+ current = _prefixes;
+
+ if (!current.Contains (prefix))
break;
- prefs2 = new Dictionary (prefs);
- prefs2.Remove (prefix);
+ future = new List (current);
+
+ future.Remove (prefix);
}
- while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs);
+ while (
+ Interlocked.CompareExchange (ref _prefixes, future, current)
+ != current
+ );
leaveIfNoPrefix ();
}
diff --git a/websocket-sharp/Net/EndPointManager.cs b/websocket-sharp/Net/EndPointManager.cs
index c12349d56..ac4582b24 100644
--- a/websocket-sharp/Net/EndPointManager.cs
+++ b/websocket-sharp/Net/EndPointManager.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2016 sta.blockhead
+ * Copyright (c) 2012-2020 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -80,50 +80,74 @@ private EndPointManager ()
private static void addPrefix (string uriPrefix, HttpListener listener)
{
- var pref = new HttpListenerPrefix (uriPrefix);
+ var pref = new HttpListenerPrefix (uriPrefix, listener);
var addr = convertToIPAddress (pref.Host);
- if (addr == null)
- throw new HttpListenerException (87, "Includes an invalid host.");
- if (!addr.IsLocal ())
- throw new HttpListenerException (87, "Includes an invalid host.");
+ if (addr == null) {
+ var msg = "The URI prefix includes an invalid host.";
+
+ throw new HttpListenerException (87, msg);
+ }
+
+ if (!addr.IsLocal ()) {
+ var msg = "The URI prefix includes an invalid host.";
+
+ throw new HttpListenerException (87, msg);
+ }
int port;
- if (!Int32.TryParse (pref.Port, out port))
- throw new HttpListenerException (87, "Includes an invalid port.");
- if (!port.IsPortNumber ())
- throw new HttpListenerException (87, "Includes an invalid port.");
+ if (!Int32.TryParse (pref.Port, out port)) {
+ var msg = "The URI prefix includes an invalid port.";
+
+ throw new HttpListenerException (87, msg);
+ }
+
+ if (!port.IsPortNumber ()) {
+ var msg = "The URI prefix includes an invalid port.";
+
+ throw new HttpListenerException (87, msg);
+ }
var path = pref.Path;
- if (path.IndexOf ('%') != -1)
- throw new HttpListenerException (87, "Includes an invalid path.");
- if (path.IndexOf ("//", StringComparison.Ordinal) != -1)
- throw new HttpListenerException (87, "Includes an invalid path.");
+ if (path.IndexOf ('%') != -1) {
+ var msg = "The URI prefix includes an invalid path.";
+
+ throw new HttpListenerException (87, msg);
+ }
+
+ if (path.IndexOf ("//", StringComparison.Ordinal) != -1) {
+ var msg = "The URI prefix includes an invalid path.";
+
+ throw new HttpListenerException (87, msg);
+ }
var endpoint = new IPEndPoint (addr, port);
EndPointListener lsnr;
+
if (_endpoints.TryGetValue (endpoint, out lsnr)) {
- if (lsnr.IsSecure ^ pref.IsSecure)
- throw new HttpListenerException (87, "Includes an invalid scheme.");
+ if (lsnr.IsSecure ^ pref.IsSecure) {
+ var msg = "The URI prefix includes an invalid scheme.";
+
+ throw new HttpListenerException (87, msg);
+ }
}
else {
- lsnr =
- new EndPointListener (
- endpoint,
- pref.IsSecure,
- listener.CertificateFolderPath,
- listener.SslConfiguration,
- listener.ReuseAddress
- );
+ lsnr = new EndPointListener (
+ endpoint,
+ pref.IsSecure,
+ listener.CertificateFolderPath,
+ listener.SslConfiguration,
+ listener.ReuseAddress
+ );
_endpoints.Add (endpoint, lsnr);
}
- lsnr.AddPrefix (pref, listener);
+ lsnr.AddPrefix (pref);
}
private static IPAddress convertToIPAddress (string hostname)
@@ -139,9 +163,10 @@ private static IPAddress convertToIPAddress (string hostname)
private static void removePrefix (string uriPrefix, HttpListener listener)
{
- var pref = new HttpListenerPrefix (uriPrefix);
+ var pref = new HttpListenerPrefix (uriPrefix, listener);
var addr = convertToIPAddress (pref.Host);
+
if (addr == null)
return;
@@ -149,6 +174,7 @@ private static void removePrefix (string uriPrefix, HttpListener listener)
return;
int port;
+
if (!Int32.TryParse (pref.Port, out port))
return;
@@ -156,6 +182,7 @@ private static void removePrefix (string uriPrefix, HttpListener listener)
return;
var path = pref.Path;
+
if (path.IndexOf ('%') != -1)
return;
@@ -165,13 +192,14 @@ private static void removePrefix (string uriPrefix, HttpListener listener)
var endpoint = new IPEndPoint (addr, port);
EndPointListener lsnr;
+
if (!_endpoints.TryGetValue (endpoint, out lsnr))
return;
if (lsnr.IsSecure ^ pref.IsSecure)
return;
- lsnr.RemovePrefix (pref, listener);
+ lsnr.RemovePrefix (pref);
}
#endregion
@@ -180,16 +208,8 @@ private static void removePrefix (string uriPrefix, HttpListener listener)
internal static bool RemoveEndPoint (IPEndPoint endpoint)
{
- lock (((ICollection) _endpoints).SyncRoot) {
- EndPointListener lsnr;
- if (!_endpoints.TryGetValue (endpoint, out lsnr))
- return false;
-
- _endpoints.Remove (endpoint);
- lsnr.Close ();
-
- return true;
- }
+ lock (((ICollection) _endpoints).SyncRoot)
+ return _endpoints.Remove (endpoint);
}
#endregion
@@ -199,6 +219,7 @@ internal static bool RemoveEndPoint (IPEndPoint endpoint)
public static void AddListener (HttpListener listener)
{
var added = new List ();
+
lock (((ICollection) _endpoints).SyncRoot) {
try {
foreach (var pref in listener.Prefixes) {
diff --git a/websocket-sharp/Net/HttpConnection.cs b/websocket-sharp/Net/HttpConnection.cs
index 572d785c2..1f8a497ad 100644
--- a/websocket-sharp/Net/HttpConnection.cs
+++ b/websocket-sharp/Net/HttpConnection.cs
@@ -8,7 +8,7 @@
* The MIT License
*
* Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2016 sta.blockhead
+ * Copyright (c) 2012-2023 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -60,17 +60,17 @@ internal sealed class HttpConnection
{
#region Private Fields
+ private int _attempts;
private byte[] _buffer;
- private const int _bufferLength = 8192;
+ private static readonly int _bufferLength;
private HttpListenerContext _context;
- private bool _contextRegistered;
private StringBuilder _currentLine;
+ private EndPointListener _endPointListener;
private InputState _inputState;
private RequestStream _inputStream;
- private HttpListener _lastListener;
private LineState _lineState;
- private EndPointListener _listener;
private EndPoint _localEndPoint;
+ private static readonly int _maxInputLength;
private ResponseStream _outputStream;
private int _position;
private EndPoint _remoteEndPoint;
@@ -86,14 +86,25 @@ internal sealed class HttpConnection
#endregion
+ #region Static Constructor
+
+ static HttpConnection ()
+ {
+ _bufferLength = 8192;
+ _maxInputLength = 32768;
+ }
+
+ #endregion
+
#region Internal Constructors
internal HttpConnection (Socket socket, EndPointListener listener)
{
_socket = socket;
- _listener = listener;
+ _endPointListener = listener;
var netStream = new NetworkStream (socket, false);
+
if (listener.IsSecure) {
var sslConf = listener.SslConfiguration;
var sslStream = new SslStream (
@@ -116,14 +127,15 @@ internal HttpConnection (Socket socket, EndPointListener listener)
_stream = netStream;
}
+ _buffer = new byte[_bufferLength];
_localEndPoint = socket.LocalEndPoint;
_remoteEndPoint = socket.RemoteEndPoint;
_sync = new object ();
- _timeout = 90000; // 90k ms for first request, 15k ms from then on.
_timeoutCanceled = new Dictionary ();
_timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite);
- init ();
+ // 90k ms for first request, 15k ms from then on.
+ init (new MemoryStream (), 90000);
}
#endregion
@@ -188,8 +200,8 @@ private void close ()
closeSocket ();
}
- unregisterContext ();
- removeConnection ();
+ _context.Unregister ();
+ _endPointListener.RemoveConnection (this);
}
private void closeSocket ()
@@ -201,15 +213,43 @@ private void closeSocket ()
}
_socket.Close ();
+
_socket = null;
}
+ private static MemoryStream createRequestBuffer (
+ RequestStream inputStream
+ )
+ {
+ var ret = new MemoryStream ();
+
+ if (inputStream is ChunkedRequestStream) {
+ var crs = (ChunkedRequestStream) inputStream;
+
+ if (crs.HasRemainingBuffer) {
+ var buff = crs.RemainingBuffer;
+
+ ret.Write (buff, 0, buff.Length);
+ }
+
+ return ret;
+ }
+
+ var cnt = inputStream.Count;
+
+ if (cnt > 0)
+ ret.Write (inputStream.InitialBuffer, inputStream.Offset, cnt);
+
+ return ret;
+ }
+
private void disposeRequestBuffer ()
{
if (_requestBuffer == null)
return;
_requestBuffer.Dispose ();
+
_requestBuffer = null;
}
@@ -218,10 +258,8 @@ private void disposeStream ()
if (_stream == null)
return;
- _inputStream = null;
- _outputStream = null;
-
_stream.Dispose ();
+
_stream = null;
}
@@ -237,23 +275,29 @@ private void disposeTimer ()
}
_timer.Dispose ();
+
_timer = null;
}
- private void init ()
+ private void init (MemoryStream requestBuffer, int timeout)
{
+ _requestBuffer = requestBuffer;
+ _timeout = timeout;
+
_context = new HttpListenerContext (this);
+ _currentLine = new StringBuilder (64);
_inputState = InputState.RequestLine;
_inputStream = null;
_lineState = LineState.None;
_outputStream = null;
_position = 0;
- _requestBuffer = new MemoryStream ();
}
private static void onRead (IAsyncResult asyncResult)
{
var conn = (HttpConnection) asyncResult.AsyncState;
+ var current = conn._attempts;
+
if (conn._socket == null)
return;
@@ -261,77 +305,42 @@ private static void onRead (IAsyncResult asyncResult)
if (conn._socket == null)
return;
- var nread = -1;
- var len = 0;
- try {
- var current = conn._reuses;
- if (!conn._timeoutCanceled[current]) {
- conn._timer.Change (Timeout.Infinite, Timeout.Infinite);
- conn._timeoutCanceled[current] = true;
- }
+ conn._timer.Change (Timeout.Infinite, Timeout.Infinite);
+ conn._timeoutCanceled[current] = true;
+ var nread = 0;
+
+ try {
nread = conn._stream.EndRead (asyncResult);
- conn._requestBuffer.Write (conn._buffer, 0, nread);
- len = (int) conn._requestBuffer.Length;
}
- catch (Exception ex) {
- if (conn._requestBuffer != null && conn._requestBuffer.Length > 0) {
- conn.SendError (ex.Message, 400);
- return;
- }
+ catch (Exception) {
+ // TODO: Logging.
conn.close ();
+
return;
}
if (nread <= 0) {
conn.close ();
+
return;
}
- if (conn.processInput (conn._requestBuffer.GetBuffer (), len)) {
- if (!conn._context.HasError)
- conn._context.Request.FinishInitialization ();
-
- if (conn._context.HasError) {
- conn.SendError ();
- return;
- }
-
- HttpListener lsnr;
- if (!conn._listener.TrySearchHttpListener (conn._context.Request.Url, out lsnr)) {
- conn.SendError (null, 404);
- return;
- }
-
- if (conn._lastListener != lsnr) {
- conn.removeConnection ();
- if (!lsnr.AddConnection (conn)) {
- conn.close ();
- return;
- }
-
- conn._lastListener = lsnr;
- }
-
- conn._context.Listener = lsnr;
- if (!conn._context.Authenticate ())
- return;
-
- if (conn._context.Register ())
- conn._contextRegistered = true;
+ conn._requestBuffer.Write (conn._buffer, 0, nread);
+ if (conn.processRequestBuffer ())
return;
- }
- conn._stream.BeginRead (conn._buffer, 0, _bufferLength, onRead, conn);
+ conn.BeginReadRequest ();
}
}
private static void onTimeout (object state)
{
var conn = (HttpConnection) state;
- var current = conn._reuses;
+ var current = conn._attempts;
+
if (conn._socket == null)
return;
@@ -342,107 +351,185 @@ private static void onTimeout (object state)
if (conn._timeoutCanceled[current])
return;
- conn.SendError (null, 408);
+ conn._context.SendError (408);
}
}
- // true -> Done processing.
- // false -> Need more input.
private bool processInput (byte[] data, int length)
{
- if (_currentLine == null)
- _currentLine = new StringBuilder (64);
+ // This method returns a bool:
+ // - true Done processing
+ // - false Need more input
+
+ var req = _context.Request;
- var nread = 0;
try {
- string line;
- while ((line = readLineFrom (data, _position, length, out nread)) != null) {
+ while (true) {
+ int nread;
+ var line = readLineFrom (data, _position, length, out nread);
+
_position += nread;
+
+ if (line == null)
+ break;
+
if (line.Length == 0) {
if (_inputState == InputState.RequestLine)
continue;
- if (_position > 32768)
+ if (_position > _maxInputLength)
_context.ErrorMessage = "Headers too long";
- _currentLine = null;
return true;
}
if (_inputState == InputState.RequestLine) {
- _context.Request.SetRequestLine (line);
+ req.SetRequestLine (line);
+
_inputState = InputState.Headers;
}
else {
- _context.Request.AddHeader (line);
+ req.AddHeader (line);
}
- if (_context.HasError)
+ if (_context.HasErrorMessage)
return true;
}
}
- catch (Exception ex) {
- _context.ErrorMessage = ex.Message;
+ catch (Exception) {
+ // TODO: Logging.
+
+ _context.ErrorMessage = "Processing failure";
+
return true;
}
- _position += nread;
- if (_position >= 32768) {
+ if (_position >= _maxInputLength) {
_context.ErrorMessage = "Headers too long";
+
return true;
}
return false;
}
- private string readLineFrom (byte[] buffer, int offset, int length, out int read)
+ private bool processRequestBuffer ()
{
- read = 0;
+ // This method returns a bool:
+ // - true Done processing
+ // - false Need more write
+
+ var data = _requestBuffer.GetBuffer ();
+ var len = (int) _requestBuffer.Length;
+
+ if (!processInput (data, len))
+ return false;
+
+ var req = _context.Request;
+
+ if (!_context.HasErrorMessage)
+ req.FinishInitialization ();
+
+ if (_context.HasErrorMessage) {
+ _context.SendError ();
- for (var i = offset; i < length && _lineState != LineState.Lf; i++) {
- read++;
+ return true;
+ }
+
+ var uri = req.Url;
+ HttpListener httplsnr;
+
+ if (!_endPointListener.TrySearchHttpListener (uri, out httplsnr)) {
+ _context.SendError (404);
+
+ return true;
+ }
+
+ httplsnr.RegisterContext (_context);
+
+ return true;
+ }
+
+ private string readLineFrom (
+ byte[] buffer,
+ int offset,
+ int length,
+ out int nread
+ )
+ {
+ nread = 0;
+
+ for (var i = offset; i < length; i++) {
+ nread++;
var b = buffer[i];
- if (b == 13)
+
+ if (b == 13) {
_lineState = LineState.Cr;
- else if (b == 10)
+
+ continue;
+ }
+
+ if (b == 10) {
_lineState = LineState.Lf;
- else
- _currentLine.Append ((char) b);
+
+ break;
+ }
+
+ _currentLine.Append ((char) b);
}
if (_lineState != LineState.Lf)
return null;
- var line = _currentLine.ToString ();
+ var ret = _currentLine.ToString ();
_currentLine.Length = 0;
_lineState = LineState.None;
- return line;
+ return ret;
}
- private void removeConnection ()
+ private MemoryStream takeOverRequestBuffer ()
{
- if (_lastListener != null)
- _lastListener.RemoveConnection (this);
- else
- _listener.RemoveConnection (this);
- }
+ if (_inputStream != null)
+ return createRequestBuffer (_inputStream);
- private void unregisterContext ()
- {
- if (!_contextRegistered)
- return;
+ var ret = new MemoryStream ();
- _context.Unregister ();
- _contextRegistered = false;
+ var buff = _requestBuffer.GetBuffer ();
+ var len = (int) _requestBuffer.Length;
+ var cnt = len - _position;
+
+ if (cnt > 0)
+ ret.Write (buff, _position, cnt);
+
+ disposeRequestBuffer ();
+
+ return ret;
}
#endregion
#region Internal Methods
+ internal void BeginReadRequest ()
+ {
+ _attempts++;
+
+ _timeoutCanceled.Add (_attempts, false);
+ _timer.Change (_timeout, Timeout.Infinite);
+
+ try {
+ _stream.BeginRead (_buffer, 0, _bufferLength, onRead, this);
+ }
+ catch (Exception) {
+ // TODO: Logging.
+
+ close ();
+ }
+ }
+
internal void Close (bool force)
{
if (_socket == null)
@@ -457,6 +544,7 @@ internal void Close (bool force)
_outputStream.Close (true);
close ();
+
return;
}
@@ -464,19 +552,30 @@ internal void Close (bool force)
if (_context.Response.CloseConnection) {
close ();
+
return;
}
if (!_context.Request.FlushInput ()) {
close ();
+
return;
}
- disposeRequestBuffer ();
- unregisterContext ();
- init ();
+ _context.Unregister ();
_reuses++;
+
+ var buff = takeOverRequestBuffer ();
+ var len = buff.Length;
+
+ init (buff, 15000);
+
+ if (len > 0) {
+ if (processRequestBuffer ())
+ return;
+ }
+
BeginReadRequest ();
}
}
@@ -485,24 +584,6 @@ internal void Close (bool force)
#region Public Methods
- public void BeginReadRequest ()
- {
- if (_buffer == null)
- _buffer = new byte[_bufferLength];
-
- if (_reuses == 1)
- _timeout = 15000;
-
- try {
- _timeoutCanceled.Add (_reuses, false);
- _timer.Change (_timeout, Timeout.Infinite);
- _stream.BeginRead (_buffer, 0, _bufferLength, onRead, this);
- }
- catch {
- close ();
- }
- }
-
public void Close ()
{
Close (false);
@@ -520,24 +601,31 @@ public RequestStream GetRequestStream (long contentLength, bool chunked)
var buff = _requestBuffer.GetBuffer ();
var len = (int) _requestBuffer.Length;
var cnt = len - _position;
- disposeRequestBuffer ();
_inputStream = chunked
? new ChunkedRequestStream (
- _stream, buff, _position, cnt, _context
+ _stream,
+ buff,
+ _position,
+ cnt,
+ _context
)
: new RequestStream (
- _stream, buff, _position, cnt, contentLength
+ _stream,
+ buff,
+ _position,
+ cnt,
+ contentLength
);
+ disposeRequestBuffer ();
+
return _inputStream;
}
}
public ResponseStream GetResponseStream ()
{
- // TODO: Can we get this stream before reading the input?
-
lock (_sync) {
if (_socket == null)
return null;
@@ -547,51 +635,13 @@ public ResponseStream GetResponseStream ()
var lsnr = _context.Listener;
var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true;
+
_outputStream = new ResponseStream (_stream, _context.Response, ignore);
return _outputStream;
}
}
- public void SendError ()
- {
- SendError (_context.ErrorMessage, _context.ErrorStatus);
- }
-
- public void SendError (string message, int status)
- {
- if (_socket == null)
- return;
-
- lock (_sync) {
- if (_socket == null)
- return;
-
- try {
- var res = _context.Response;
- res.StatusCode = status;
- res.ContentType = "text/html";
-
- var content = new StringBuilder (64);
- content.AppendFormat ("{0} {1}", status, res.StatusDescription);
- if (message != null && message.Length > 0)
- content.AppendFormat (" ({0})
", message);
- else
- content.Append ("