mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交Unity 联机Pro
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 008d5591a2cd6704fb39796bd33ff71a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,37 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
namespace BestHTTP.SignalR.Authentication
|
||||
{
|
||||
public delegate void OnAuthenticationSuccededDelegate(IAuthenticationProvider provider);
|
||||
public delegate void OnAuthenticationFailedDelegate(IAuthenticationProvider provider, string reason);
|
||||
|
||||
public interface IAuthenticationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The authentication must be run before any request made to build up the SignalR protocol
|
||||
/// </summary>
|
||||
bool IsPreAuthRequired { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This event must be called when the pre-authentication succeded. When IsPreAuthRequired is false, no-one will subscribe to this event.
|
||||
/// </summary>
|
||||
event OnAuthenticationSuccededDelegate OnAuthenticationSucceded;
|
||||
|
||||
/// <summary>
|
||||
/// This event must be called when the pre-authentication failed. When IsPreAuthRequired is false, no-one will subscribe to this event.
|
||||
/// </summary>
|
||||
event OnAuthenticationFailedDelegate OnAuthenticationFailed;
|
||||
|
||||
/// <summary>
|
||||
/// This function called once, when the before the SignalR negotiation begins. If IsPreAuthRequired is false, then this step will be skipped.
|
||||
/// </summary>
|
||||
void StartAuthentication();
|
||||
|
||||
/// <summary>
|
||||
/// This function will be called for every request before sending it.
|
||||
/// </summary>
|
||||
void PrepareRequest(HTTPRequest request, RequestTypes type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e95e3e74ee7feaf4b872a3d4d23dc0a6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1299
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Connection.cs
Normal file
1299
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Connection.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 878e20e1ec06ef542946893b5e37c3b6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
196
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Enums.cs
Normal file
196
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Enums.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
namespace BestHTTP.SignalR
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible transport types.
|
||||
/// </summary>
|
||||
public enum TransportTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Transport using WebSockets.
|
||||
/// </summary>
|
||||
WebSocket,
|
||||
|
||||
/// <summary>
|
||||
/// Transport using ServerSentEvents protocol.
|
||||
/// </summary>
|
||||
ServerSentEvents,
|
||||
|
||||
/// <summary>
|
||||
/// Transport using long-polling requests.
|
||||
/// </summary>
|
||||
LongPoll
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server sent message types
|
||||
/// </summary>
|
||||
public enum MessageTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty json object {} sent by the server to check keep alive.
|
||||
/// </summary>
|
||||
KeepAlive,
|
||||
|
||||
/// <summary>
|
||||
/// A no-hub message that contains data.
|
||||
/// </summary>
|
||||
Data,
|
||||
|
||||
/// <summary>
|
||||
/// A message that can hold multiple data message alongside with other information.
|
||||
/// </summary>
|
||||
Multiple,
|
||||
|
||||
/// <summary>
|
||||
/// A method call result.
|
||||
/// </summary>
|
||||
Result,
|
||||
|
||||
/// <summary>
|
||||
/// A message about a failed method call.
|
||||
/// </summary>
|
||||
Failure,
|
||||
|
||||
/// <summary>
|
||||
/// A message with all information to be able to call a method on the client.
|
||||
/// </summary>
|
||||
MethodCall,
|
||||
|
||||
/// <summary>
|
||||
/// A long running server-method's progress.
|
||||
/// </summary>
|
||||
Progress
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible SignalR Connection states.
|
||||
/// </summary>
|
||||
public enum ConnectionStates
|
||||
{
|
||||
/// <summary>
|
||||
/// The initial state of the connection.
|
||||
/// </summary>
|
||||
Initial,
|
||||
|
||||
/// <summary>
|
||||
/// The client authenticates itself with the server. This state is skipped if no AuthenticationProvider is present.
|
||||
/// </summary>
|
||||
Authenticating,
|
||||
|
||||
/// <summary>
|
||||
/// The client sent out the negotiation request to the server.
|
||||
/// </summary>
|
||||
Negotiating,
|
||||
|
||||
/// <summary>
|
||||
/// The client received the negotiation data, created the transport and wait's for the transport's connection.
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// The transport connected and started successfully.
|
||||
/// </summary>
|
||||
Connected,
|
||||
|
||||
/// <summary>
|
||||
/// The client started the reconnect process.
|
||||
/// </summary>
|
||||
Reconnecting,
|
||||
|
||||
/// <summary>
|
||||
/// The connection is closed.
|
||||
/// </summary>
|
||||
Closed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible types of SignalR requests.
|
||||
/// </summary>
|
||||
public enum RequestTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Request to the /negotiate path to negotiate protocol parameters.
|
||||
/// </summary>
|
||||
Negotiate,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /connect path to connect to the server. With long-polling, it's like a regular poll request.
|
||||
/// </summary>
|
||||
Connect,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /start path to start the protocol.
|
||||
/// </summary>
|
||||
Start,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /poll path to get new messages. Not used with the WebSocketTransport.
|
||||
/// </summary>
|
||||
Poll,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /send path to send a message to the server. Not used with the WebSocketTransport.
|
||||
/// </summary>
|
||||
Send,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /reconnect path to initiate a reconnection. It's used instead of the Connect type.
|
||||
/// </summary>
|
||||
Reconnect,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /abort path to close the connection.
|
||||
/// </summary>
|
||||
Abort,
|
||||
|
||||
/// <summary>
|
||||
/// Request to the /ping path to ping the server keeping the asp.net session alive.
|
||||
/// </summary>
|
||||
Ping
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Possible states of a transport.
|
||||
/// </summary>
|
||||
public enum TransportStates
|
||||
{
|
||||
/// <summary>
|
||||
/// Initial state
|
||||
/// </summary>
|
||||
Initial,
|
||||
|
||||
/// <summary>
|
||||
/// Connecting
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// Reconnecting
|
||||
/// </summary>
|
||||
Reconnecting,
|
||||
|
||||
/// <summary>
|
||||
/// Sending Start request
|
||||
/// </summary>
|
||||
Starting,
|
||||
|
||||
/// <summary>
|
||||
/// Start request finished successfully
|
||||
/// </summary>
|
||||
Started,
|
||||
|
||||
/// <summary>
|
||||
/// Sending Abort request
|
||||
/// </summary>
|
||||
Closing,
|
||||
|
||||
/// <summary>
|
||||
/// The transport closed after Abort request sent
|
||||
/// </summary>
|
||||
Closed
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fafa6e4c8bf63324c82241199e91f437
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7208796729371c74c9d7c3398b19f8f3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
391
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Hubs/Hub.cs
Normal file
391
JNFrame2/Assets/Plugins/Best HTTP/Source/SignalR/Hubs/Hub.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.SignalR.Messages;
|
||||
using System.Text;
|
||||
|
||||
namespace BestHTTP.SignalR.Hubs
|
||||
{
|
||||
public delegate void OnMethodCallDelegate(Hub hub, string method, params object[] args);
|
||||
public delegate void OnMethodCallCallbackDelegate(Hub hub, MethodCallMessage methodCall);
|
||||
|
||||
public delegate void OnMethodResultDelegate(Hub hub, ClientMessage originalMessage, ResultMessage result);
|
||||
public delegate void OnMethodFailedDelegate(Hub hub, ClientMessage originalMessage, FailureMessage error);
|
||||
public delegate void OnMethodProgressDelegate(Hub hub, ClientMessage originialMessage, ProgressMessage progress);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a clientside Hub. This class can be used as a base class to encapsulate proxy functionalities.
|
||||
/// </summary>
|
||||
public class Hub : IHub
|
||||
{
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Name of this hub.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Server and user set state of the hub.
|
||||
/// </summary>
|
||||
public Dictionary<string, object> State
|
||||
{
|
||||
// Create only when we need to.
|
||||
get
|
||||
{
|
||||
if (state == null)
|
||||
state = new Dictionary<string, object>();
|
||||
return state;
|
||||
}
|
||||
}
|
||||
private Dictionary<string, object> state;
|
||||
|
||||
/// <summary>
|
||||
/// Event called every time when the server sends an order to call a method on the client.
|
||||
/// </summary>
|
||||
public event OnMethodCallDelegate OnMethodCall;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Privates
|
||||
|
||||
/// <summary>
|
||||
/// Table of the sent messages. These messages will be removed from this table when a Result message is received from the server.
|
||||
/// </summary>
|
||||
private Dictionary<UInt64, ClientMessage> SentMessages = new Dictionary<ulong, ClientMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// Methodname -> callback delegate mapping. This table stores the server callable functions.
|
||||
/// </summary>
|
||||
private Dictionary<string, OnMethodCallCallbackDelegate> MethodTable = new Dictionary<string, OnMethodCallCallbackDelegate>();
|
||||
|
||||
/// <summary>
|
||||
/// A reusable StringBuilder to save some GC allocs
|
||||
/// </summary>
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
#endregion
|
||||
|
||||
Connection IHub.Connection { get; set; }
|
||||
|
||||
public Hub(string name)
|
||||
:this(name, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Hub(string name, Connection manager)
|
||||
{
|
||||
this.Name = name;
|
||||
(this as IHub).Connection = manager;
|
||||
}
|
||||
|
||||
#region Public Hub Functions
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback function to the given method.
|
||||
/// </summary>
|
||||
public void On(string method, OnMethodCallCallbackDelegate callback)
|
||||
{
|
||||
MethodTable[method] = callback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes callback from the given method.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
public void Off(string method)
|
||||
{
|
||||
MethodTable[method] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the server to call a method with the given arguments.
|
||||
/// </summary>
|
||||
/// <returns>True if the plugin was able to send out the message</returns>
|
||||
public bool Call(string method, params object[] args)
|
||||
{
|
||||
return Call(method, null, null, null, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the server to call a method with the given arguments.
|
||||
/// The onResult callback will be called when the server successfully called the function.
|
||||
/// </summary>
|
||||
/// <returns>True if the plugin was able to send out the message</returns>
|
||||
public bool Call(string method, OnMethodResultDelegate onResult, params object[] args)
|
||||
{
|
||||
return Call(method, onResult, null, null, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the server to call a method with the given arguments.
|
||||
/// The onResult callback will be called when the server successfully called the function.
|
||||
/// The onResultError will be called when the server can't call the function, or when the function throws an exception.
|
||||
/// </summary>
|
||||
/// <returns>True if the plugin was able to send out the message</returns>
|
||||
public bool Call(string method, OnMethodResultDelegate onResult, OnMethodFailedDelegate onResultError, params object[] args)
|
||||
{
|
||||
return Call(method, onResult, onResultError, null, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the server to call a method with the given arguments.
|
||||
/// The onResult callback will be called when the server successfully called the function.
|
||||
/// The onProgress callback called multiple times when the method is a long running function and reports back its progress.
|
||||
/// </summary>
|
||||
/// <returns>True if the plugin was able to send out the message</returns>
|
||||
public bool Call(string method, OnMethodResultDelegate onResult, OnMethodProgressDelegate onProgress, params object[] args)
|
||||
{
|
||||
return Call(method, onResult, null, onProgress, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orders the server to call a method with the given arguments.
|
||||
/// The onResult callback will be called when the server successfully called the function.
|
||||
/// The onResultError will be called when the server can't call the function, or when the function throws an exception.
|
||||
/// The onProgress callback called multiple times when the method is a long running function and reports back its progress.
|
||||
/// </summary>
|
||||
/// <returns>True if the plugin was able to send out the message</returns>
|
||||
public bool Call(string method, OnMethodResultDelegate onResult, OnMethodFailedDelegate onResultError, OnMethodProgressDelegate onProgress, params object[] args)
|
||||
{
|
||||
IHub thisHub = this as IHub;
|
||||
|
||||
// Start over the counter if we are reached the max value if the long type.
|
||||
// While we are using this property only here, we don't want to make it static to avoid another thread synchronization, neither we want to make it a Hub-instance field to achieve better debuggability.
|
||||
|
||||
long newValue, originalValue;
|
||||
do
|
||||
{
|
||||
originalValue = thisHub.Connection.ClientMessageCounter;
|
||||
newValue = (originalValue % long.MaxValue) + 1;
|
||||
} while (System.Threading.Interlocked.CompareExchange(ref thisHub.Connection.ClientMessageCounter, newValue, originalValue) != originalValue);
|
||||
|
||||
// Create and send the client message
|
||||
return thisHub.Call(new ClientMessage(this, method, args, (ulong)thisHub.Connection.ClientMessageCounter, onResult, onResultError, onProgress));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IHub Implementation
|
||||
|
||||
bool IHub.Call(ClientMessage msg)
|
||||
{
|
||||
IHub thisHub = this as IHub;
|
||||
|
||||
if (!thisHub.Connection.SendJson(BuildMessage(msg)))
|
||||
return false;
|
||||
|
||||
SentMessages.Add(msg.CallIdx, msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if this hub sent the message with the given id.
|
||||
/// </summary>
|
||||
bool IHub.HasSentMessageId(UInt64 id)
|
||||
{
|
||||
return SentMessages.ContainsKey(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on the manager's close.
|
||||
/// </summary>
|
||||
void IHub.Close()
|
||||
{
|
||||
SentMessages.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the client receives an order to call a hub-function.
|
||||
/// </summary>
|
||||
void IHub.OnMethod(MethodCallMessage msg)
|
||||
{
|
||||
// Merge the newly received states with the old one
|
||||
MergeState(msg.State);
|
||||
|
||||
if (OnMethodCall != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnMethodCall(this, msg.Method, msg.Arguments);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub - " + this.Name, "IHub.OnMethod - OnMethodCall", ex);
|
||||
}
|
||||
}
|
||||
|
||||
OnMethodCallCallbackDelegate callback;
|
||||
if (MethodTable.TryGetValue(msg.Method, out callback) && callback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(this, msg);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub - " + this.Name, "IHub.OnMethod - callback", ex);
|
||||
}
|
||||
}
|
||||
else if (OnMethodCall == null)
|
||||
HTTPManager.Logger.Warning("Hub - " + this.Name, string.Format("[Client] {0}.{1} (args: {2})", this.Name, msg.Method, msg.Arguments.Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the client receives back messages as a result of a server method call.
|
||||
/// </summary>
|
||||
void IHub.OnMessage(IServerMessage msg)
|
||||
{
|
||||
ClientMessage originalMsg;
|
||||
|
||||
UInt64 id = (msg as IHubMessage).InvocationId;
|
||||
if (!SentMessages.TryGetValue(id, out originalMsg))
|
||||
{
|
||||
// This can happen when a result message removes the ClientMessage from the SentMessages dictionary,
|
||||
// then a late come progress message tries to access it
|
||||
HTTPManager.Logger.Warning("Hub - " + this.Name, "OnMessage - Sent message not found with id: " + id.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
switch(msg.Type)
|
||||
{
|
||||
case MessageTypes.Result:
|
||||
ResultMessage result = msg as ResultMessage;
|
||||
|
||||
// Merge the incoming State before firing the events
|
||||
MergeState(result.State);
|
||||
|
||||
if (originalMsg.ResultCallback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
originalMsg.ResultCallback(this, originalMsg, result);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub " + this.Name, "IHub.OnMessage - ResultCallback", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SentMessages.Remove(id);
|
||||
|
||||
break;
|
||||
|
||||
case MessageTypes.Failure:
|
||||
FailureMessage error = msg as FailureMessage;
|
||||
|
||||
// Merge the incoming State before firing the events
|
||||
MergeState(error.State);
|
||||
|
||||
if (originalMsg.ResultErrorCallback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
originalMsg.ResultErrorCallback(this, originalMsg, error);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub " + this.Name, "IHub.OnMessage - ResultErrorCallback", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SentMessages.Remove(id);
|
||||
break;
|
||||
|
||||
case MessageTypes.Progress:
|
||||
if (originalMsg.ProgressCallback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
originalMsg.ProgressCallback(this, originalMsg, msg as ProgressMessage);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub " + this.Name, "IHub.OnMessage - ProgressCallback", ex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Merges the current and the new states.
|
||||
/// </summary>
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
private void MergeState(IDictionary<string, Newtonsoft.Json.Linq.JToken> state)
|
||||
#else
|
||||
private void MergeState(IDictionary<string, object> state)
|
||||
#endif
|
||||
{
|
||||
if (state != null && state.Count > 0)
|
||||
foreach (var kvp in state)
|
||||
this.State[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a JSon string from the given message.
|
||||
/// </summary>
|
||||
private string BuildMessage(ClientMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.Append("{\"H\":\"");
|
||||
builder.Append(this.Name);
|
||||
builder.Append("\",\"M\":\"");
|
||||
builder.Append(msg.Method);
|
||||
builder.Append("\",\"A\":");
|
||||
|
||||
string jsonEncoded = string.Empty;
|
||||
|
||||
// Arguments
|
||||
if (msg.Args != null && msg.Args.Length > 0)
|
||||
jsonEncoded = (this as IHub).Connection.JsonEncoder.Encode(msg.Args);
|
||||
else
|
||||
jsonEncoded = "[]";
|
||||
|
||||
builder.Append(jsonEncoded);
|
||||
|
||||
builder.Append(",\"I\":\"");
|
||||
builder.Append(msg.CallIdx.ToString());
|
||||
builder.Append("\"");
|
||||
|
||||
// State, if any
|
||||
if (msg.Hub.state != null && msg.Hub.state.Count > 0)
|
||||
{
|
||||
builder.Append(",\"S\":");
|
||||
|
||||
jsonEncoded = (this as IHub).Connection.JsonEncoder.Encode(msg.Hub.state);
|
||||
builder.Append(jsonEncoded);
|
||||
}
|
||||
|
||||
builder.Append("}");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Hub - " + this.Name, "Send", ex);
|
||||
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// reset the StringBuilder instance, to reuse next time
|
||||
builder.Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00b4b8fd0e14de941b796a56a1272233
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
|
||||
using BestHTTP.SignalR.Messages;
|
||||
|
||||
namespace BestHTTP.SignalR.Hubs
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to be able to hide internally used functions and properties.
|
||||
/// </summary>
|
||||
public interface IHub
|
||||
{
|
||||
Connection Connection { get; set; }
|
||||
|
||||
bool Call(ClientMessage msg);
|
||||
bool HasSentMessageId(UInt64 id);
|
||||
void Close();
|
||||
void OnMethod(MethodCallMessage msg);
|
||||
void OnMessage(IServerMessage msg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1813781a232e6c74ba3e00896327ace6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c593c51184e979a4d8e4bb2cc2a9b1d0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using BestHTTP.JSON;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BestHTTP.SignalR.JsonEncoders
|
||||
{
|
||||
public sealed class DefaultJsonEncoder : IJsonEncoder
|
||||
{
|
||||
public string Encode(object obj)
|
||||
{
|
||||
return Json.Encode(obj);
|
||||
}
|
||||
|
||||
public IDictionary<string, object> DecodeMessage(string json)
|
||||
{
|
||||
bool ok = false;
|
||||
IDictionary<string, object> result = Json.Decode(json, ref ok) as IDictionary<string, object>;
|
||||
return ok ? result : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a4bf647dcdcdbf44ab1edc744192ca0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BestHTTP.SignalR.JsonEncoders
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to be able to write custom Json encoders/decoders.
|
||||
/// </summary>
|
||||
public interface IJsonEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// This function must create a json formatted string from the given object. If the encoding fails, it should return null.
|
||||
/// </summary>
|
||||
string Encode(object obj);
|
||||
|
||||
/// <summary>
|
||||
/// This function must create a dictionary the Json formatted string parameter. If the decoding fails, it should return null.
|
||||
/// </summary>
|
||||
IDictionary<string, object> DecodeMessage(string json);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b494cd3e9ad9bd243bd68251a27c86b9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ea7772f76aa54748aa8b466ba3113a9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,71 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
|
||||
using BestHTTP.SignalR.Hubs;
|
||||
|
||||
namespace BestHTTP.SignalR.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// This struct represents a message from the client.
|
||||
/// It holds every data and reference needed to construct the string represented message that will be sent to the wire.
|
||||
/// </summary>
|
||||
public struct ClientMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to the source Hub. The Name and the State of the hub will be user.
|
||||
/// </summary>
|
||||
public readonly Hub Hub;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the method on the server to be called.
|
||||
/// </summary>
|
||||
public readonly string Method;
|
||||
|
||||
/// <summary>
|
||||
/// Arguments of the method.
|
||||
/// </summary>
|
||||
public readonly object[] Args;
|
||||
|
||||
/// <summary>
|
||||
/// Unique id on the client of this message
|
||||
/// </summary>
|
||||
public readonly UInt64 CallIdx;
|
||||
|
||||
/// <summary>
|
||||
/// The delegate that will be called when the server will sends a result of this method call.
|
||||
/// </summary>
|
||||
public readonly OnMethodResultDelegate ResultCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The delegate that will be called when the server sends an error-result to this method call.
|
||||
/// </summary>
|
||||
public readonly OnMethodFailedDelegate ResultErrorCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The delegate that will be called when the server sends a progress message to this method call.
|
||||
/// </summary>
|
||||
public readonly OnMethodProgressDelegate ProgressCallback;
|
||||
|
||||
public ClientMessage(Hub hub,
|
||||
string method,
|
||||
object[] args,
|
||||
UInt64 callIdx,
|
||||
OnMethodResultDelegate resultCallback,
|
||||
OnMethodFailedDelegate resultErrorCallback,
|
||||
OnMethodProgressDelegate progressCallback)
|
||||
{
|
||||
Hub = hub;
|
||||
Method = method;
|
||||
Args = args;
|
||||
|
||||
CallIdx = callIdx;
|
||||
|
||||
ResultCallback = resultCallback;
|
||||
ResultErrorCallback = resultErrorCallback;
|
||||
ProgressCallback = progressCallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af18bb2021dd2f045b2d168d5769fab1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
|
||||
namespace BestHTTP.SignalR.Messages
|
||||
{
|
||||
public interface IServerMessage
|
||||
{
|
||||
MessageTypes Type { get; }
|
||||
void Parse(object data);
|
||||
}
|
||||
|
||||
public interface IHubMessage
|
||||
{
|
||||
UInt64 InvocationId { get; }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25c60db0524552144bf03b547ae8a585
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,354 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
using Newtonsoft.Json.Linq;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BestHTTP.SignalR.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Keep-alive message sent by the server. No data sent with it.
|
||||
/// </summary>
|
||||
public sealed class KeepAliveMessage : IServerMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.KeepAlive; } }
|
||||
void IServerMessage.Parse(object data) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message that may contains multiple sub-messages and additional informations.
|
||||
/// </summary>
|
||||
public sealed class MultiMessage : IServerMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.Multiple; } }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the sent message
|
||||
/// </summary>
|
||||
public string MessageId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if it's an initialization message, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsInitialization { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Group token may be sent, if the group changed that the client belongs to.
|
||||
/// </summary>
|
||||
public string GroupsToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The server suggests that the client should do a reconnect turn.
|
||||
/// </summary>
|
||||
public bool ShouldReconnect { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional poll delay sent by the server.
|
||||
/// </summary>
|
||||
public TimeSpan? PollDelay { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of server messages sent inside this message.
|
||||
/// </summary>
|
||||
public List<IServerMessage> Data { get; private set; }
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
IDictionary<string, object> dic = data as IDictionary<string, object>;
|
||||
object value;
|
||||
|
||||
this.MessageId = dic["C"].ToString();
|
||||
|
||||
if (dic.TryGetValue("S", out value))
|
||||
IsInitialization = int.Parse(value.ToString()) == 1 ? true : false;
|
||||
else
|
||||
IsInitialization = false;
|
||||
|
||||
if (dic.TryGetValue("G", out value))
|
||||
GroupsToken = value.ToString();
|
||||
|
||||
if (dic.TryGetValue("T", out value))
|
||||
ShouldReconnect = int.Parse(value.ToString()) == 1 ? true : false;
|
||||
else
|
||||
ShouldReconnect = false;
|
||||
|
||||
if (dic.TryGetValue("L", out value))
|
||||
PollDelay = TimeSpan.FromMilliseconds(double.Parse(value.ToString()));
|
||||
|
||||
IEnumerable enumerable = dic["M"] as IEnumerable;
|
||||
|
||||
if (enumerable != null)
|
||||
{
|
||||
Data = new List<IServerMessage>();
|
||||
|
||||
foreach (object subData in enumerable)
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
IDictionary<string, JToken> subObj = subData as IDictionary<string, JToken>;
|
||||
#else
|
||||
IDictionary<string, object> subObj = subData as IDictionary<string, object>;
|
||||
#endif
|
||||
|
||||
IServerMessage subMsg = null;
|
||||
|
||||
if (subObj != null)
|
||||
{
|
||||
if (subObj.ContainsKey("H"))
|
||||
subMsg = new MethodCallMessage();
|
||||
else if (subObj.ContainsKey("I"))
|
||||
subMsg = new ProgressMessage();
|
||||
else
|
||||
subMsg = new DataMessage();
|
||||
}
|
||||
else
|
||||
subMsg = new DataMessage();
|
||||
|
||||
subMsg.Parse(subData);
|
||||
|
||||
Data.Add(subMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simple non-hub data message. It holds only one Data property.
|
||||
/// </summary>
|
||||
public sealed class DataMessage : IServerMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.Data; } }
|
||||
|
||||
public object Data { get; private set; }
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
this.Data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Hub message that orders the client to call a method.
|
||||
/// </summary>
|
||||
public sealed class MethodCallMessage : IServerMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.MethodCall; } }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the Hub that the method is called on.
|
||||
/// </summary>
|
||||
public string Hub { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the Method.
|
||||
/// </summary>
|
||||
public string Method { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arguments of the method call.
|
||||
/// </summary>
|
||||
public object[] Arguments { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// State changes of the hub. It's handled automatically by the Hub.
|
||||
/// </summary>
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
public IDictionary<string, JToken> State { get; private set; }
|
||||
#else
|
||||
public IDictionary<string, object> State { get; private set; }
|
||||
#endif
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
IDictionary<string, JToken> dic = data as IDictionary<string, JToken>;
|
||||
#else
|
||||
IDictionary<string, object> dic = data as IDictionary<string, object>;
|
||||
#endif
|
||||
|
||||
Hub = dic["H"].ToString();
|
||||
Method = dic["M"].ToString();
|
||||
|
||||
List<object> args = new List<object>();
|
||||
foreach (object arg in dic["A"] as IEnumerable)
|
||||
args.Add(arg);
|
||||
Arguments = args.ToArray();
|
||||
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
JToken value;
|
||||
if (dic.TryGetValue("S", out value))
|
||||
State = value as IDictionary<string, JToken>;
|
||||
#else
|
||||
object value;
|
||||
if (dic.TryGetValue("S", out value))
|
||||
State = value as IDictionary<string, object>;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message of a server side method invocation result.
|
||||
/// </summary>
|
||||
public sealed class ResultMessage : IServerMessage, IHubMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.Result; } }
|
||||
|
||||
/// <summary>
|
||||
/// The unique id that the client set when called the server side method. Used by the plugin to deliver this message to the good Hub.
|
||||
/// </summary>
|
||||
public UInt64 InvocationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The return value of the server side method call, or null if the method's return type is void.
|
||||
/// </summary>
|
||||
public object ReturnValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// State changes of the hub. It's handled automatically by the Hub.
|
||||
/// </summary>
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
public IDictionary<string, JToken> State { get; private set; }
|
||||
#else
|
||||
public IDictionary<string, object> State { get; private set; }
|
||||
#endif
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
IDictionary<string, object> dic = data as IDictionary<string, object>;
|
||||
|
||||
InvocationId = UInt64.Parse(dic["I"].ToString());
|
||||
|
||||
object value;
|
||||
if (dic.TryGetValue("R", out value))
|
||||
ReturnValue = value;
|
||||
|
||||
if (dic.TryGetValue("S", out value))
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
State = value as IDictionary<string, JToken>;
|
||||
#else
|
||||
State = value as IDictionary<string, object>;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FailureMessage : IServerMessage, IHubMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.Failure; } }
|
||||
|
||||
/// <summary>
|
||||
/// The unique id that the client set when called the server side method. Used by the plugin to deliver this message to the good Hub.
|
||||
/// </summary>
|
||||
public UInt64 InvocationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if it's a hub error.
|
||||
/// </summary>
|
||||
public bool IsHubError { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the method call failed, it contains the error message to detail what happened.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary that may contain additional error data (can only be present for hub errors). It can be null.
|
||||
/// </summary>
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
public IDictionary<string, JToken> AdditionalData { get; private set; }
|
||||
#else
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Stack trace of the error. It present only if detailed error reporting is turned on on the server (https://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubconfiguration.enabledetailederrors%28v=vs.118%29.aspx).
|
||||
/// </summary>
|
||||
public string StackTrace { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// State changes of the hub. It's handled automatically by the Hub.
|
||||
/// </summary>
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
public IDictionary<string, JToken> State { get; private set; }
|
||||
#else
|
||||
public IDictionary<string, object> State { get; private set; }
|
||||
#endif
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
IDictionary<string, object> dic = data as IDictionary<string, object>;
|
||||
|
||||
InvocationId = UInt64.Parse(dic["I"].ToString());
|
||||
|
||||
object value;
|
||||
|
||||
if (dic.TryGetValue("E", out value))
|
||||
ErrorMessage = value.ToString();
|
||||
|
||||
if (dic.TryGetValue("H", out value))
|
||||
IsHubError = int.Parse(value.ToString()) == 1 ? true : false;
|
||||
|
||||
if (dic.TryGetValue("D", out value))
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
AdditionalData = value as IDictionary<string, JToken>;
|
||||
#else
|
||||
AdditionalData = value as IDictionary<string, object>;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dic.TryGetValue("T", out value))
|
||||
StackTrace = value.ToString();
|
||||
|
||||
if (dic.TryGetValue("S", out value))
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
State = value as IDictionary<string, JToken>;
|
||||
#else
|
||||
State = value as IDictionary<string, object>;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a server method is a long running method the server can send the information about the progress of execution of the method to the client.
|
||||
/// </summary>
|
||||
public sealed class ProgressMessage : IServerMessage, IHubMessage
|
||||
{
|
||||
MessageTypes IServerMessage.Type { get { return MessageTypes.Progress; } }
|
||||
|
||||
/// <summary>
|
||||
/// The unique id that the client set when called the server side method. Used by the plugin to deliver this message to the good Hub.
|
||||
/// </summary>
|
||||
public UInt64 InvocationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current progress of the long running method.
|
||||
/// </summary>
|
||||
public double Progress { get; private set; }
|
||||
|
||||
void IServerMessage.Parse(object data)
|
||||
{
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
IDictionary<string, JToken> dic = data as IDictionary<string, JToken>;
|
||||
#else
|
||||
IDictionary<string, object> dic = data as IDictionary<string, object>;
|
||||
#endif
|
||||
|
||||
#if BESTHTTP_SIGNALR_WITH_JSONDOTNET
|
||||
IDictionary<string, JToken> P = dic["P"] as IDictionary<string, JToken>;
|
||||
#else
|
||||
IDictionary<string, object> P = dic["P"] as IDictionary<string, object>;
|
||||
#endif
|
||||
|
||||
InvocationId = UInt64.Parse(P["I"].ToString());
|
||||
Progress = double.Parse(P["D"].ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a128625937e3c5b459b3aa221061a03f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,276 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.JSON;
|
||||
|
||||
namespace BestHTTP.SignalR
|
||||
{
|
||||
public sealed class NegotiationData
|
||||
{
|
||||
#region Public Negotiate data
|
||||
|
||||
/// <summary>
|
||||
/// Path to the SignalR endpoint. Currently not used by the client.
|
||||
/// </summary>
|
||||
public string Url { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket endpoint.
|
||||
/// </summary>
|
||||
public string WebSocketServerUrl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection token assigned by the server. See this article for more details: http://www.asp.net/signalr/overview/security/introduction-to-security#connectiontoken.
|
||||
/// This value needs to be sent in each subsequent request as the value of the connectionToken parameter
|
||||
/// </summary>
|
||||
public string ConnectionToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of the connection.
|
||||
/// </summary>
|
||||
public string ConnectionId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time in seconds the client should wait before attempting to reconnect if it has not received a keep alive message.
|
||||
/// If the server is configured to not send keep alive messages this value is null.
|
||||
/// </summary>
|
||||
public TimeSpan? KeepAliveTimeout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time within which the client should try to reconnect if the connection goes away.
|
||||
/// </summary>
|
||||
public TimeSpan DisconnectTimeout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timeout of poll requests.
|
||||
/// </summary>
|
||||
public TimeSpan ConnectionTimeout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the server supports websockets.
|
||||
/// </summary>
|
||||
public bool TryWebSockets { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of the protocol used for communication.
|
||||
/// </summary>
|
||||
public string ProtocolVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time the client should try to connect to the server using a given transport.
|
||||
/// </summary>
|
||||
public TimeSpan TransportConnectTimeout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The wait time before restablishing a long poll connection after data is sent from the server. The default value is 0.
|
||||
/// </summary>
|
||||
public TimeSpan LongPollDelay { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that called when the negotiation data received and parsed successfully.
|
||||
/// </summary>
|
||||
public Action<NegotiationData> OnReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that called when an error happens.
|
||||
/// </summary>
|
||||
public Action<NegotiationData, string> OnError;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private
|
||||
|
||||
private HTTPRequest NegotiationRequest;
|
||||
private IConnection Connection;
|
||||
|
||||
#endregion
|
||||
|
||||
public NegotiationData(Connection connection)
|
||||
{
|
||||
this.Connection = connection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start to get the negotiation data.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
NegotiationRequest = new HTTPRequest(Connection.BuildUri(RequestTypes.Negotiate), HTTPMethods.Get, true, true, OnNegotiationRequestFinished);
|
||||
Connection.PrepareRequest(NegotiationRequest, RequestTypes.Negotiate);
|
||||
NegotiationRequest.Send();
|
||||
|
||||
HTTPManager.Logger.Information("NegotiationData", "Negotiation request sent");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abort the negotiation request.
|
||||
/// </summary>
|
||||
public void Abort()
|
||||
{
|
||||
if (NegotiationRequest != null)
|
||||
{
|
||||
OnReceived = null;
|
||||
OnError = null;
|
||||
NegotiationRequest.Abort();
|
||||
}
|
||||
}
|
||||
|
||||
#region Request Event Handler
|
||||
|
||||
private void OnNegotiationRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
NegotiationRequest = null;
|
||||
|
||||
switch (req.State)
|
||||
{
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("NegotiationData", "Negotiation data arrived: " + resp.DataAsText);
|
||||
|
||||
int idx = resp.DataAsText.IndexOf("{");
|
||||
if (idx < 0)
|
||||
{
|
||||
RaiseOnError("Invalid negotiation text: " + resp.DataAsText);
|
||||
return;
|
||||
}
|
||||
|
||||
var Negotiation = Parse(resp.DataAsText.Substring(idx));
|
||||
|
||||
if (Negotiation == null)
|
||||
{
|
||||
RaiseOnError("Parsing Negotiation data failed: " + resp.DataAsText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OnReceived != null)
|
||||
{
|
||||
OnReceived(this);
|
||||
OnReceived = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
RaiseOnError(string.Format("Negotiation request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText,
|
||||
req.CurrentUri));
|
||||
break;
|
||||
|
||||
case HTTPRequestStates.Error:
|
||||
RaiseOnError(req.Exception != null ? (req.Exception.Message + " " + req.Exception.StackTrace) : string.Empty);
|
||||
break;
|
||||
|
||||
default:
|
||||
RaiseOnError(req.State.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private void RaiseOnError(string err)
|
||||
{
|
||||
HTTPManager.Logger.Error("NegotiationData", "Negotiation request failed with error: " + err);
|
||||
|
||||
if (OnError != null)
|
||||
{
|
||||
OnError(this, err);
|
||||
OnError = null;
|
||||
}
|
||||
}
|
||||
|
||||
private NegotiationData Parse(string str)
|
||||
{
|
||||
bool success = false;
|
||||
Dictionary<string, object> dict = Json.Decode(str, ref success) as Dictionary<string, object>;
|
||||
if (!success)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
this.Url = GetString(dict, "Url");
|
||||
|
||||
if (dict.ContainsKey("webSocketServerUrl"))
|
||||
this.WebSocketServerUrl = GetString(dict, "webSocketServerUrl");
|
||||
|
||||
this.ConnectionToken = Uri.EscapeDataString(GetString(dict, "ConnectionToken"));
|
||||
this.ConnectionId = GetString(dict, "ConnectionId");
|
||||
|
||||
if (dict.ContainsKey("KeepAliveTimeout"))
|
||||
this.KeepAliveTimeout = TimeSpan.FromSeconds(GetDouble(dict, "KeepAliveTimeout"));
|
||||
|
||||
this.DisconnectTimeout = TimeSpan.FromSeconds(GetDouble(dict, "DisconnectTimeout"));
|
||||
|
||||
if (dict.ContainsKey("ConnectionTimeout"))
|
||||
this.ConnectionTimeout = TimeSpan.FromSeconds(GetDouble(dict, "ConnectionTimeout"));
|
||||
else
|
||||
this.ConnectionTimeout = TimeSpan.FromSeconds(120);
|
||||
|
||||
this.TryWebSockets = (bool)Get(dict, "TryWebSockets");
|
||||
this.ProtocolVersion = GetString(dict, "ProtocolVersion");
|
||||
this.TransportConnectTimeout = TimeSpan.FromSeconds(GetDouble(dict, "TransportConnectTimeout"));
|
||||
|
||||
if (dict.ContainsKey("LongPollDelay"))
|
||||
this.LongPollDelay = TimeSpan.FromSeconds(GetDouble(dict, "LongPollDelay"));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("NegotiationData", "Parse", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static object Get(Dictionary<string, object> from, string key)
|
||||
{
|
||||
object value;
|
||||
if (!from.TryGetValue(key, out value))
|
||||
throw new System.Exception(string.Format("Can't get {0} from Negotiation data!", key));
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string GetString(Dictionary<string, object> from, string key)
|
||||
{
|
||||
return Get(from, key) as string;
|
||||
}
|
||||
|
||||
private static List<string> GetStringList(Dictionary<string, object> from, string key)
|
||||
{
|
||||
List<object> value = Get(from, key) as List<object>;
|
||||
|
||||
List<string> result = new List<string>(value.Count);
|
||||
for (int i = 0; i < value.Count; ++i)
|
||||
{
|
||||
string str = value[i] as string;
|
||||
if (str != null)
|
||||
result.Add(str);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int GetInt(Dictionary<string, object> from, string key)
|
||||
{
|
||||
return (int)(double)Get(from, key);
|
||||
}
|
||||
|
||||
private static double GetDouble(Dictionary<string, object> from, string key)
|
||||
{
|
||||
return (double)Get(from, key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c20f605e63af7ef4eb7782aa663f0196
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c390001e221f7e1429a38eb6222413c5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,256 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
|
||||
using BestHTTP.Extensions;
|
||||
using BestHTTP.SignalR.Messages;
|
||||
|
||||
namespace BestHTTP.SignalR.Transports
|
||||
{
|
||||
public sealed class PollingTransport : PostSendTransportBase, IHeartbeat
|
||||
{
|
||||
#region Overridden Properties
|
||||
|
||||
public override bool SupportsKeepAlive { get { return false; } }
|
||||
public override TransportTypes Type { get { return TransportTypes.LongPoll; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Privates
|
||||
|
||||
/// <summary>
|
||||
/// When we received the last poll.
|
||||
/// </summary>
|
||||
private DateTime LastPoll;
|
||||
|
||||
/// <summary>
|
||||
/// How much time we have to wait before we can send out a new poll request. This value sent by the server.
|
||||
/// </summary>
|
||||
private TimeSpan PollDelay;
|
||||
|
||||
/// <summary>
|
||||
/// How much time we wait to a poll request to finish. It's value is the server sent negotiation's ConnectionTimeout + 10sec.
|
||||
/// </summary>
|
||||
private TimeSpan PollTimeout;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the the current poll request.
|
||||
/// </summary>
|
||||
private HTTPRequest pollRequest;
|
||||
|
||||
#endregion
|
||||
|
||||
public PollingTransport(Connection connection)
|
||||
: base("longPolling", connection)
|
||||
{
|
||||
this.LastPoll = DateTime.MinValue;
|
||||
this.PollTimeout = connection.NegotiationResult.ConnectionTimeout + TimeSpan.FromSeconds(10);
|
||||
}
|
||||
|
||||
#region Overrides from TransportBase
|
||||
|
||||
/// <summary>
|
||||
/// Polling transport specific connection logic. It's a regular GET request to the /connect path.
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Sending Open Request");
|
||||
|
||||
// Skip the Connecting state if we are reconnecting. If the connect succeeds, we will set the Started state directly
|
||||
if (this.State != TransportStates.Reconnecting)
|
||||
this.State = TransportStates.Connecting;
|
||||
|
||||
RequestTypes requestType = this.State == TransportStates.Reconnecting ? RequestTypes.Reconnect : RequestTypes.Connect;
|
||||
|
||||
var request = new HTTPRequest(Connection.BuildUri(requestType, this), HTTPMethods.Get, true, true, OnConnectRequestFinished);
|
||||
|
||||
Connection.PrepareRequest(request, requestType);
|
||||
|
||||
request.Send();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
HTTPManager.Heartbeats.Unsubscribe(this);
|
||||
|
||||
if (pollRequest != null)
|
||||
{
|
||||
pollRequest.Abort();
|
||||
pollRequest = null;
|
||||
}
|
||||
|
||||
// Should we abort the send requests in the sendRequestQueue?
|
||||
}
|
||||
|
||||
protected override void Started()
|
||||
{
|
||||
LastPoll = DateTime.UtcNow;
|
||||
HTTPManager.Heartbeats.Subscribe(this);
|
||||
}
|
||||
|
||||
protected override void Aborted()
|
||||
{
|
||||
HTTPManager.Heartbeats.Unsubscribe(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Request Handlers
|
||||
|
||||
void OnConnectRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
// error reason if there is any. We will call the manager's Error function if it's not empty.
|
||||
string reason = string.Empty;
|
||||
|
||||
switch (req.State)
|
||||
{
|
||||
// The request finished without any problem.
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Connect - Request Finished Successfully! " + resp.DataAsText);
|
||||
|
||||
OnConnected();
|
||||
|
||||
IServerMessage msg = TransportBase.Parse(Connection.JsonEncoder, resp.DataAsText);
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
Connection.OnMessage(msg);
|
||||
|
||||
MultiMessage multiple = msg as MultiMessage;
|
||||
if (multiple != null && multiple.PollDelay.HasValue)
|
||||
PollDelay = multiple.PollDelay.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
reason = string.Format("Connect - Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText);
|
||||
break;
|
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
|
||||
case HTTPRequestStates.Error:
|
||||
reason = "Connect - Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception");
|
||||
break;
|
||||
|
||||
// The request aborted, initiated by the user.
|
||||
case HTTPRequestStates.Aborted:
|
||||
reason = "Connect - Request Aborted!";
|
||||
break;
|
||||
|
||||
// Connecting to the server is timed out.
|
||||
case HTTPRequestStates.ConnectionTimedOut:
|
||||
reason = "Connect - Connection Timed Out!";
|
||||
break;
|
||||
|
||||
// The request didn't finished in the given time.
|
||||
case HTTPRequestStates.TimedOut:
|
||||
reason = "Connect - Processing the request Timed Out!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(reason))
|
||||
Connection.Error(reason);
|
||||
}
|
||||
|
||||
void OnPollRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
// When Stop() called on the transport.
|
||||
// In Stop() we set the pollRequest to null, but a new poll request can be made after a quick reconnection, and there is a chanse that
|
||||
// in this handler function we can null out the new request. So we return early here.
|
||||
if (req.IsCancellationRequested)
|
||||
{
|
||||
HTTPManager.Logger.Warning("Transport - " + this.Name, "Poll - Request Aborted!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the pollRequest to null, now we can send out a new one
|
||||
pollRequest = null;
|
||||
|
||||
// error reason if there is any. We will call the manager's Error function if it's not empty.
|
||||
string reason = string.Empty;
|
||||
|
||||
switch (req.State)
|
||||
{
|
||||
// The request finished without any problem.
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Poll - Request Finished Successfully! " + resp.DataAsText);
|
||||
|
||||
IServerMessage msg = TransportBase.Parse(Connection.JsonEncoder, resp.DataAsText);
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
Connection.OnMessage(msg);
|
||||
|
||||
MultiMessage multiple = msg as MultiMessage;
|
||||
if (multiple != null && multiple.PollDelay.HasValue)
|
||||
PollDelay = multiple.PollDelay.Value;
|
||||
|
||||
LastPoll = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
else
|
||||
reason = string.Format("Poll - Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText);
|
||||
break;
|
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
|
||||
case HTTPRequestStates.Error:
|
||||
reason = "Poll - Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception");
|
||||
break;
|
||||
|
||||
// Connecting to the server is timed out.
|
||||
case HTTPRequestStates.ConnectionTimedOut:
|
||||
reason = "Poll - Connection Timed Out!";
|
||||
break;
|
||||
|
||||
// The request didn't finished in the given time.
|
||||
case HTTPRequestStates.TimedOut:
|
||||
reason = "Poll - Processing the request Timed Out!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(reason))
|
||||
Connection.Error(reason);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Polling transport speficic function. Sends a GET request to the /poll path to receive messages.
|
||||
/// </summary>
|
||||
private void Poll()
|
||||
{
|
||||
pollRequest = new HTTPRequest(Connection.BuildUri(RequestTypes.Poll, this), HTTPMethods.Get, true, true, OnPollRequestFinished);
|
||||
|
||||
Connection.PrepareRequest(pollRequest, RequestTypes.Poll);
|
||||
|
||||
pollRequest.Timeout = this.PollTimeout;
|
||||
|
||||
pollRequest.Send();
|
||||
}
|
||||
|
||||
#region IHeartbeat Implementation
|
||||
|
||||
void IHeartbeat.OnHeartbeatUpdate(TimeSpan dif)
|
||||
{
|
||||
switch(State)
|
||||
{
|
||||
case TransportStates.Started:
|
||||
if (pollRequest == null && DateTime.UtcNow >= (LastPoll + PollDelay + Connection.NegotiationResult.LongPollDelay))
|
||||
Poll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 702a1cd6038bbc347a4b568983058f63
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,100 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.SignalR.Messages;
|
||||
|
||||
namespace BestHTTP.SignalR.Transports
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for implementations that must send the data in unique requests. These are currently the LongPolling and ServerSentEvents transports.
|
||||
/// </summary>
|
||||
public abstract class PostSendTransportBase : TransportBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Sent out send requests. Keeping a reference to them for future use.
|
||||
/// </summary>
|
||||
protected List<HTTPRequest> sendRequestQueue = new List<HTTPRequest>();
|
||||
|
||||
public PostSendTransportBase(string name, Connection con)
|
||||
: base(name, con)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region Send Implementation
|
||||
|
||||
protected override void SendImpl(string json)
|
||||
{
|
||||
var request = new HTTPRequest(Connection.BuildUri(RequestTypes.Send, this), HTTPMethods.Post, true, true, OnSendRequestFinished);
|
||||
|
||||
request.FormUsage = Forms.HTTPFormUsage.UrlEncoded;
|
||||
request.AddField("data", json);
|
||||
|
||||
Connection.PrepareRequest(request, RequestTypes.Send);
|
||||
|
||||
request.Send();
|
||||
|
||||
sendRequestQueue.Add(request);
|
||||
}
|
||||
|
||||
void OnSendRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
sendRequestQueue.Remove(req);
|
||||
|
||||
// error reason if there is any. We will call the manager's Error function if it's not empty.
|
||||
string reason = string.Empty;
|
||||
|
||||
switch (req.State)
|
||||
{
|
||||
// The request finished without any problem.
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Send - Request Finished Successfully! " + resp.DataAsText);
|
||||
|
||||
if (!string.IsNullOrEmpty(resp.DataAsText))
|
||||
{
|
||||
IServerMessage msg = TransportBase.Parse(Connection.JsonEncoder, resp.DataAsText);
|
||||
|
||||
if (msg != null)
|
||||
Connection.OnMessage(msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
reason = string.Format("Send - Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText);
|
||||
break;
|
||||
|
||||
// The request finished with an unexpected error. The request's Exception property may contain more info about the error.
|
||||
case HTTPRequestStates.Error:
|
||||
reason = "Send - Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception");
|
||||
break;
|
||||
|
||||
// The request aborted, initiated by the user.
|
||||
case HTTPRequestStates.Aborted:
|
||||
reason = "Send - Request Aborted!";
|
||||
break;
|
||||
|
||||
// Connecting to the server is timed out.
|
||||
case HTTPRequestStates.ConnectionTimedOut:
|
||||
reason = "Send - Connection Timed Out!";
|
||||
break;
|
||||
|
||||
// The request didn't finished in the given time.
|
||||
case HTTPRequestStates.TimedOut:
|
||||
reason = "Send - Processing the request Timed Out!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(reason))
|
||||
Connection.Error(reason);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f47176f2538ccf54e855235ecb7647f3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,171 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
#if !BESTHTTP_DISABLE_SERVERSENT_EVENTS
|
||||
|
||||
using System;
|
||||
|
||||
using BestHTTP.ServerSentEvents;
|
||||
using BestHTTP.SignalR.Messages;
|
||||
|
||||
namespace BestHTTP.SignalR.Transports
|
||||
{
|
||||
/// <summary>
|
||||
/// A SignalR transport implementation that uses the Server-Sent Events protocol.
|
||||
/// </summary>
|
||||
public sealed class ServerSentEventsTransport : PostSendTransportBase
|
||||
{
|
||||
#region Overridden Properties
|
||||
|
||||
/// <summary>
|
||||
/// It's a persistent connection. We support the keep-alive mechanism.
|
||||
/// </summary>
|
||||
public override bool SupportsKeepAlive { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the transport.
|
||||
/// </summary>
|
||||
public override TransportTypes Type { get { return TransportTypes.ServerSentEvents; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Privates
|
||||
|
||||
/// <summary>
|
||||
/// The EventSource object.
|
||||
/// </summary>
|
||||
private EventSource EventSource;
|
||||
|
||||
#endregion
|
||||
|
||||
public ServerSentEventsTransport(Connection con)
|
||||
: base("serverSentEvents", con)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region Overrides from TransportBase
|
||||
|
||||
public override void Connect()
|
||||
{
|
||||
if (EventSource != null)
|
||||
{
|
||||
HTTPManager.Logger.Warning("ServerSentEventsTransport", "Start - EventSource already created!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the Connecting state if we are reconnecting. If the connect succeeds, we will set the Started state directly
|
||||
if (this.State != TransportStates.Reconnecting)
|
||||
this.State = TransportStates.Connecting;
|
||||
|
||||
RequestTypes requestType = this.State == TransportStates.Reconnecting ? RequestTypes.Reconnect : RequestTypes.Connect;
|
||||
|
||||
Uri uri = Connection.BuildUri(requestType, this);
|
||||
|
||||
EventSource = new EventSource(uri);
|
||||
|
||||
EventSource.OnOpen += OnEventSourceOpen;
|
||||
EventSource.OnMessage += OnEventSourceMessage;
|
||||
EventSource.OnError += OnEventSourceError;
|
||||
EventSource.OnClosed += OnEventSourceClosed;
|
||||
|
||||
#if !UNITY_WEBGL || UNITY_EDITOR
|
||||
// Disable internal retry
|
||||
EventSource.OnRetry += (es) => false;
|
||||
#endif
|
||||
|
||||
// Start connecting to the server.
|
||||
EventSource.Open();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
EventSource.OnOpen -= OnEventSourceOpen;
|
||||
EventSource.OnMessage -= OnEventSourceMessage;
|
||||
EventSource.OnError -= OnEventSourceError;
|
||||
EventSource.OnClosed -= OnEventSourceClosed;
|
||||
|
||||
EventSource.Close();
|
||||
|
||||
EventSource = null;
|
||||
}
|
||||
|
||||
protected override void Started()
|
||||
{
|
||||
// Nothing to do here for this transport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A custom Abort function where we will start to close the EventSource object.
|
||||
/// </summary>
|
||||
public override void Abort()
|
||||
{
|
||||
base.Abort();
|
||||
|
||||
EventSource.Close();
|
||||
}
|
||||
|
||||
protected override void Aborted()
|
||||
{
|
||||
if (this.State == TransportStates.Closing)
|
||||
this.State = TransportStates.Closed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventSource Event Handlers
|
||||
|
||||
private void OnEventSourceOpen(EventSource eventSource)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "OnEventSourceOpen");
|
||||
}
|
||||
|
||||
private void OnEventSourceMessage(EventSource eventSource, BestHTTP.ServerSentEvents.Message message)
|
||||
{
|
||||
if (message.Data.Equals("initialized"))
|
||||
{
|
||||
base.OnConnected();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IServerMessage msg = TransportBase.Parse(Connection.JsonEncoder, message.Data);
|
||||
|
||||
if (msg != null)
|
||||
Connection.OnMessage(msg);
|
||||
|
||||
}
|
||||
|
||||
private void OnEventSourceError(EventSource eventSource, string error)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "OnEventSourceError");
|
||||
|
||||
// We are in a reconnecting phase, we have to connect now.
|
||||
if (this.State == TransportStates.Reconnecting)
|
||||
{
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Already closed?
|
||||
if (this.State == TransportStates.Closed)
|
||||
return;
|
||||
|
||||
// Closing? Then we are closed now.
|
||||
if (this.State == TransportStates.Closing)
|
||||
this.State = TransportStates.Closed;
|
||||
else // Errored when we didn't expected it.
|
||||
Connection.Error(error);
|
||||
}
|
||||
|
||||
private void OnEventSourceClosed(ServerSentEvents.EventSource eventSource)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "OnEventSourceClosed");
|
||||
|
||||
OnEventSourceError(eventSource, "EventSource Closed!");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8c1fc0aacfa94c4793a2e8958a34058
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,386 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.SignalR.Messages;
|
||||
using BestHTTP.SignalR.JsonEncoders;
|
||||
|
||||
namespace BestHTTP.SignalR.Transports
|
||||
{
|
||||
public delegate void OnTransportStateChangedDelegate(TransportBase transport, TransportStates oldState, TransportStates newState);
|
||||
|
||||
public abstract class TransportBase
|
||||
{
|
||||
private const int MaxRetryCount = 5;
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Name of the transport.
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the manager has to check the last message received time and reconnect if too much time passes.
|
||||
/// </summary>
|
||||
public abstract bool SupportsKeepAlive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the transport. Used mainly by the manager in its BuildUri function.
|
||||
/// </summary>
|
||||
public abstract TransportTypes Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the manager.
|
||||
/// </summary>
|
||||
public IConnection Connection { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current state of the transport.
|
||||
/// </summary>
|
||||
public TransportStates State
|
||||
{
|
||||
get { return _state; }
|
||||
protected set
|
||||
{
|
||||
TransportStates old = _state;
|
||||
_state = value;
|
||||
|
||||
if (OnStateChanged != null)
|
||||
OnStateChanged(this, old, _state);
|
||||
}
|
||||
}
|
||||
public TransportStates _state;
|
||||
|
||||
/// <summary>
|
||||
/// Thi event called when the transport's State set to a new value.
|
||||
/// </summary>
|
||||
public event OnTransportStateChangedDelegate OnStateChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
public TransportBase(string name, Connection connection)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Connection = connection;
|
||||
this.State = TransportStates.Initial;
|
||||
}
|
||||
|
||||
#region Abstract functions
|
||||
|
||||
/// <summary>
|
||||
/// Start to connect to the server
|
||||
/// </summary>
|
||||
public abstract void Connect();
|
||||
|
||||
/// <summary>
|
||||
/// Stop the connection
|
||||
/// </summary>
|
||||
public abstract void Stop();
|
||||
|
||||
/// <summary>
|
||||
/// The transport specific implementation to send the given json string to the server.
|
||||
/// </summary>
|
||||
protected abstract void SendImpl(string json);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the Start request finished successfully, or after a reconnect.
|
||||
/// Manager.TransportOpened(); called from the TransportBase after this call
|
||||
/// </summary>
|
||||
protected abstract void Started();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the abort request finished successfully.
|
||||
/// </summary>
|
||||
protected abstract void Aborted();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Called after a succesful connect/reconnect. The transport implementations have to call this function.
|
||||
/// </summary>
|
||||
protected void OnConnected()
|
||||
{
|
||||
if (this.State != TransportStates.Reconnecting)
|
||||
{
|
||||
// Send the Start request
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Connection.TransportReconnected();
|
||||
|
||||
Started();
|
||||
|
||||
this.State = TransportStates.Started;
|
||||
}
|
||||
}
|
||||
|
||||
#region Start Request Sending
|
||||
|
||||
/// <summary>
|
||||
/// Sends out the /start request to the server.
|
||||
/// </summary>
|
||||
protected void Start()
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Sending Start Request");
|
||||
|
||||
this.State = TransportStates.Starting;
|
||||
|
||||
if (this.Connection.Protocol > ProtocolVersions.Protocol_2_0)
|
||||
{
|
||||
var request = new HTTPRequest(Connection.BuildUri(RequestTypes.Start, this), HTTPMethods.Get, true, true, OnStartRequestFinished);
|
||||
|
||||
request.Tag = 0;
|
||||
request.MaxRetries = 0;
|
||||
|
||||
request.Timeout = Connection.NegotiationResult.ConnectionTimeout + TimeSpan.FromSeconds(10);
|
||||
|
||||
Connection.PrepareRequest(request, RequestTypes.Start);
|
||||
|
||||
request.Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The transport and the signalr protocol now started
|
||||
this.State = TransportStates.Started;
|
||||
|
||||
Started();
|
||||
|
||||
Connection.TransportStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
switch (req.State)
|
||||
{
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Start - Returned: " + resp.DataAsText);
|
||||
|
||||
string response = Connection.ParseResponse(resp.DataAsText);
|
||||
|
||||
if (response != "started")
|
||||
{
|
||||
Connection.Error(string.Format("Expected 'started' response, but '{0}' found!", response));
|
||||
return;
|
||||
}
|
||||
|
||||
// The transport and the signalr protocol now started
|
||||
this.State = TransportStates.Started;
|
||||
|
||||
Started();
|
||||
|
||||
Connection.TransportStarted();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
HTTPManager.Logger.Warning("Transport - " + this.Name, string.Format("Start - request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText,
|
||||
req.CurrentUri));
|
||||
goto default;
|
||||
|
||||
default:
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Start request state: " + req.State.ToString());
|
||||
|
||||
// The request may not reached the server. Try it again.
|
||||
int retryCount = (int)req.Tag;
|
||||
if (retryCount++ < MaxRetryCount)
|
||||
{
|
||||
req.Tag = retryCount;
|
||||
req.Send();
|
||||
}
|
||||
else
|
||||
Connection.Error("Failed to send Start request.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abort Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Will abort the transport. In SignalR 'Abort'ing is a graceful process, while 'Close'ing is a hard-abortion...
|
||||
/// </summary>
|
||||
public virtual void Abort()
|
||||
{
|
||||
if (this.State != TransportStates.Started)
|
||||
return;
|
||||
|
||||
this.State = TransportStates.Closing;
|
||||
|
||||
var request = new HTTPRequest(Connection.BuildUri(RequestTypes.Abort, this), HTTPMethods.Get, true, true, OnAbortRequestFinished);
|
||||
|
||||
// Retry counter
|
||||
request.Tag = 0;
|
||||
request.MaxRetries = 0;
|
||||
|
||||
Connection.PrepareRequest(request, RequestTypes.Abort);
|
||||
|
||||
request.Send();
|
||||
}
|
||||
|
||||
protected void AbortFinished()
|
||||
{
|
||||
this.State = TransportStates.Closed;
|
||||
|
||||
Connection.TransportAborted();
|
||||
|
||||
this.Aborted();
|
||||
}
|
||||
|
||||
private void OnAbortRequestFinished(HTTPRequest req, HTTPResponse resp)
|
||||
{
|
||||
switch (req.State)
|
||||
{
|
||||
case HTTPRequestStates.Finished:
|
||||
if (resp.IsSuccess)
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Abort - Returned: " + resp.DataAsText);
|
||||
|
||||
if (this.State == TransportStates.Closing)
|
||||
AbortFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
HTTPManager.Logger.Warning("Transport - " + this.Name, string.Format("Abort - Handshake request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
|
||||
resp.StatusCode,
|
||||
resp.Message,
|
||||
resp.DataAsText,
|
||||
req.CurrentUri));
|
||||
|
||||
// try again
|
||||
goto default;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Abort request state: " + req.State.ToString());
|
||||
|
||||
// The request may not reached the server. Try it again.
|
||||
int retryCount = (int)req.Tag;
|
||||
if (retryCount++ < MaxRetryCount)
|
||||
{
|
||||
req.Tag = retryCount;
|
||||
req.Send();
|
||||
}
|
||||
else
|
||||
Connection.Error("Failed to send Abort request!");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Send Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Sends the given json string to the wire.
|
||||
/// </summary>
|
||||
/// <param name="jsonStr"></param>
|
||||
public void Send(string jsonStr)
|
||||
{
|
||||
try
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Sending: " + jsonStr);
|
||||
|
||||
SendImpl(jsonStr);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("Transport - " + this.Name, "Send", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Start the reconnect process
|
||||
/// </summary>
|
||||
public void Reconnect()
|
||||
{
|
||||
HTTPManager.Logger.Information("Transport - " + this.Name, "Reconnecting");
|
||||
|
||||
Stop();
|
||||
|
||||
this.State = TransportStates.Reconnecting;
|
||||
|
||||
Connect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the json string is successfully parsed will return with an IServerMessage implementation.
|
||||
/// </summary>
|
||||
public static IServerMessage Parse(IJsonEncoder encoder, string json)
|
||||
{
|
||||
// Nothing to parse?
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
HTTPManager.Logger.Error("MessageFactory", "Parse - called with empty or null string!");
|
||||
return null;
|
||||
}
|
||||
|
||||
// We don't have to do further decoding, if it's an empty json object, then it's a KeepAlive message from the server
|
||||
if (json.Length == 2 && json == "{}")
|
||||
return new KeepAliveMessage();
|
||||
|
||||
IDictionary<string, object> msg = null;
|
||||
|
||||
try
|
||||
{
|
||||
// try to decode the json message with the encoder
|
||||
msg = encoder.DecodeMessage(json);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("MessageFactory", "Parse - encoder.DecodeMessage", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (msg == null)
|
||||
{
|
||||
HTTPManager.Logger.Error("MessageFactory", "Parse - Json Decode failed for json string: \"" + json + "\"");
|
||||
return null;
|
||||
}
|
||||
|
||||
// "C" is for message id
|
||||
IServerMessage result = null;
|
||||
if (!msg.ContainsKey("C"))
|
||||
{
|
||||
// If there are no ErrorMessage in the object, then it was a success
|
||||
if (!msg.ContainsKey("E"))
|
||||
result = new ResultMessage();
|
||||
else
|
||||
result = new FailureMessage();
|
||||
}
|
||||
else
|
||||
result = new MultiMessage();
|
||||
|
||||
try
|
||||
{
|
||||
result.Parse(msg);
|
||||
}
|
||||
catch
|
||||
{
|
||||
HTTPManager.Logger.Error("MessageFactory", "Can't parse msg: " + json);
|
||||
throw;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 469a77c43ea68f14da659401301834f5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,174 @@
|
||||
#if !BESTHTTP_DISABLE_SIGNALR
|
||||
#if !BESTHTTP_DISABLE_WEBSOCKET
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using BestHTTP;
|
||||
using BestHTTP.JSON;
|
||||
using BestHTTP.SignalR.Hubs;
|
||||
using BestHTTP.SignalR.Messages;
|
||||
using BestHTTP.SignalR.JsonEncoders;
|
||||
|
||||
namespace BestHTTP.SignalR.Transports
|
||||
{
|
||||
public sealed class WebSocketTransport : TransportBase
|
||||
{
|
||||
#region Overridden Properties
|
||||
|
||||
public override bool SupportsKeepAlive { get { return true; } }
|
||||
public override TransportTypes Type { get { return TransportTypes.WebSocket; } }
|
||||
|
||||
#endregion
|
||||
|
||||
private WebSocket.WebSocket wSocket;
|
||||
|
||||
public WebSocketTransport(Connection connection)
|
||||
: base("webSockets", connection)
|
||||
{
|
||||
}
|
||||
|
||||
#region Overrides from TransportBase
|
||||
|
||||
/// <summary>
|
||||
/// Websocket transport specific connection logic. It will create a WebSocket instance, and starts to connect to the server.
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
if (wSocket != null)
|
||||
{
|
||||
HTTPManager.Logger.Warning("WebSocketTransport", "Start - WebSocket already created!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the Connecting state if we are reconnecting. If the connect succeeds, we will set the Started state directly
|
||||
if (this.State != TransportStates.Reconnecting)
|
||||
this.State = TransportStates.Connecting;
|
||||
|
||||
RequestTypes requestType = this.State == TransportStates.Reconnecting ? RequestTypes.Reconnect : RequestTypes.Connect;
|
||||
|
||||
Uri uri = Connection.BuildUri(requestType, this);
|
||||
|
||||
// Create the WebSocket instance
|
||||
wSocket = new WebSocket.WebSocket(uri);
|
||||
|
||||
// Set up eventhandlers
|
||||
wSocket.OnOpen += WSocket_OnOpen;
|
||||
wSocket.OnMessage += WSocket_OnMessage;
|
||||
wSocket.OnClosed += WSocket_OnClosed;
|
||||
wSocket.OnError += WSocket_OnError;
|
||||
|
||||
#if !UNITY_WEBGL || UNITY_EDITOR
|
||||
// prepare the internal http request
|
||||
wSocket.OnInternalRequestCreated = (ws, internalRequest) => Connection.PrepareRequest(internalRequest, requestType);
|
||||
#endif
|
||||
|
||||
// start opening the websocket protocol
|
||||
wSocket.Open();
|
||||
}
|
||||
|
||||
protected override void SendImpl(string json)
|
||||
{
|
||||
if (wSocket != null && wSocket.IsOpen)
|
||||
wSocket.Send(json);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (wSocket != null)
|
||||
{
|
||||
wSocket.OnOpen = null;
|
||||
wSocket.OnMessage = null;
|
||||
wSocket.OnClosed = null;
|
||||
wSocket.OnError = null;
|
||||
wSocket.Close();
|
||||
wSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Started()
|
||||
{
|
||||
// Nothing to be done here for this transport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The /abort request successfully finished
|
||||
/// </summary>
|
||||
protected override void Aborted()
|
||||
{
|
||||
// if the websocket is still open, close it
|
||||
if (wSocket != null && wSocket.IsOpen)
|
||||
{
|
||||
wSocket.Close();
|
||||
wSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WebSocket Events
|
||||
|
||||
void WSocket_OnOpen(WebSocket.WebSocket webSocket)
|
||||
{
|
||||
if (webSocket != wSocket)
|
||||
return;
|
||||
|
||||
HTTPManager.Logger.Information("WebSocketTransport", "WSocket_OnOpen");
|
||||
|
||||
OnConnected();
|
||||
}
|
||||
|
||||
void WSocket_OnMessage(WebSocket.WebSocket webSocket, string message)
|
||||
{
|
||||
if (webSocket != wSocket)
|
||||
return;
|
||||
|
||||
IServerMessage msg = TransportBase.Parse(Connection.JsonEncoder, message);
|
||||
|
||||
if (msg != null)
|
||||
Connection.OnMessage(msg);
|
||||
}
|
||||
|
||||
void WSocket_OnClosed(WebSocket.WebSocket webSocket, ushort code, string message)
|
||||
{
|
||||
if (webSocket != wSocket)
|
||||
return;
|
||||
|
||||
string reason = code.ToString() + " : " + message;
|
||||
|
||||
HTTPManager.Logger.Information("WebSocketTransport", "WSocket_OnClosed " + reason);
|
||||
|
||||
if (this.State == TransportStates.Closing)
|
||||
this.State = TransportStates.Closed;
|
||||
else
|
||||
Connection.Error(reason);
|
||||
}
|
||||
|
||||
void WSocket_OnError(WebSocket.WebSocket webSocket, string reason)
|
||||
{
|
||||
if (webSocket != wSocket)
|
||||
return;
|
||||
|
||||
// On WP8.1, somehow we receive an exception that the remote server forcibly closed the connection instead of the
|
||||
// WebSocket closed packet... Also, even the /abort request didn't finished.
|
||||
if (this.State == TransportStates.Closing ||
|
||||
this.State == TransportStates.Closed)
|
||||
{
|
||||
base.AbortFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HTTPManager.Logger.Level == Logger.Loglevels.All)
|
||||
HTTPManager.Logger.Error("WebSocketTransport", "WSocket_OnError " + reason);
|
||||
|
||||
this.State = TransportStates.Closed;
|
||||
Connection.Error(reason);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5efa5f8b9a034084b87f47b2b74b8173
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user