Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
runtime.csharp.IRT.Transport.Client.WebSocketTransportGeneric.cs Maven / Gradle / Ivy
using System;
using System.Collections.Generic;
using System.Timers;
using IRT.Marshaller;
using IRT.Transport.Authorization;
using WebSocketSharp;
using IRT.Transport;
namespace IRT.Transport.Client {
public enum WebSocketTransportState {
Connecting,
Connected,
Stopping,
Stopped
}
public class WebSocketTransportGeneric: IClientSocketTransport, IDisposable where C: class, IClientTransportContext {
public const int DefaultTimeoutInterval = 60; // Seconds
protected class WSRequest {
public System.Timers.Timer Timer;
public Action Success;
public Action Failure;
}
private IJsonMarshaller _marshaller;
private WebSocket _ws;
private ILogger _logger;
private bool _disposed;
private bool _headersUpdated;
private AuthMethod _auth;
private string _headersUpdateID;
private Dictionary _requests;
private Dictionary _headers;
private Dispatcher _dispatcher;
private C _context;
private WebSocketTransportState _state;
public WebSocketTransportState State {
get {
return _state;
}
}
public bool Ready {
get {
return _state == WebSocketTransportState.Connected && _headersUpdated;
}
}
private string endpoint;
public string Endpoint {
get {
return endpoint;
}
set {
endpoint = value;
if (!endpoint.EndsWith("\\") && !endpoint.EndsWith("/")) {
endpoint += "/";
}
}
}
private int _timeout; // In Seconds
public WebSocketTransportGeneric(string endpoint, IJsonMarshaller marshaller, ILogger logger, int timeout = 60) {
Endpoint = endpoint;
_logger = logger;
_marshaller = marshaller;
_timeout = timeout;
_ws = new WebSocket(endpoint);
_ws.OnMessage += (sender, e) => {
if (e.IsPing) {
logger.Logf(LogLevel.Trace, "WebSocketTransport: Ping received.");
return;
}
OnMessage(e);
};
_ws.OnOpen += (sender, e) => {
OnOpen();
};
_ws.OnError += (sender, e) => {
OnError(e);
};
_ws.OnClose += (sender, e) => {
OnClose(e);
};
_ws.Compression = CompressionMethod.Deflate;
_ws.EmitOnPing = true;
_state = WebSocketTransportState.Stopped;
_headersUpdated = true;
_requests = new Dictionary();
_dispatcher = new Dispatcher();
}
private void OnMessage(MessageEventArgs e) {
_logger.Logf(LogLevel.Trace, "WebSocketTransport: Incoming message:\nType: {0}\nData: {1}", e.IsBinary ? "Binary" : "Text", e.Data);
if (e.IsBinary) {
throw new Exception("Binary data is not supported.");
}
WebSocketMessageBase messageBase;
try {
messageBase = _marshaller.Unmarshal(e.Data);
}
catch (Exception ex) {
_logger.Logf(LogLevel.Error, "Exception during message unmarshalling: {0}", ex.Message);
return;
}
switch (messageBase.Kind) {
case WebSocketMessageKind.RpcFailure:
case WebSocketMessageKind.RpcResponse: {
WebSocketResponseMessageJson msg;
try {
msg = _marshaller.Unmarshal(e.Data);
}
catch (Exception ex) {
_logger.Logf(LogLevel.Error, "Exception during RPC message unmarshalling: {0}", ex.Message);
return;
}
HandleRPCSuccess(msg);
} break;
case WebSocketMessageKind.BuzzerRequest: {
WebSocketRequestMessageJson msg;
try {
msg = _marshaller.Unmarshal(e.Data);
}
catch (Exception ex) {
_logger.Logf(LogLevel.Error, "Exception during RPC message unmarshalling: {0}", ex.Message);
return;
}
HandleBuzzer(msg);
} break;
case WebSocketMessageKind.Failure: {
WebSocketFailureMessage msg;
try {
msg = _marshaller.Unmarshal(e.Data);
} catch (Exception ex) {
_logger.Logf(LogLevel.Error, "Exception during failure message unmarshalling: {0}", ex.Message);
return;
}
HandleFailure(msg);
} break;
default:
throw new Exception("Not implemented");
}
}
private void OnClose(CloseEventArgs e) {
_logger.Logf(LogLevel.Debug, "WebSocketTransport: Closed socket. Code: {0} Reason: {1}", e.Code, e.Reason);
_state = WebSocketTransportState.Stopped;
}
private void OnError(WebSocketSharp.ErrorEventArgs e) {
_logger.Logf(LogLevel.Error, "WebSocketTransport: Error: " + e.Message);
_logger.Logf(LogLevel.Debug, "WebSocketTransport: Stacktrace:\n" + e.Exception.StackTrace);
}
private void OnOpen() {
_logger.Logf(LogLevel.Debug, "WebSocketTransport: Opened socket.");
_state = WebSocketTransportState.Connected;
}
public bool Open() {
if(_state != WebSocketTransportState.Stopped) {
return false;
}
_state = WebSocketTransportState.Connecting;
_ws.ConnectAsync();
return true;
}
public void Close() {
if (_state == WebSocketTransportState.Stopped ||
_state != WebSocketTransportState.Stopping) {
return;
}
_state = WebSocketTransportState.Stopping;
_ws.Close(CloseStatusCode.Away);
}
public void SetAuthorization(AuthMethod method) {
_auth = method;
sendHeaders();
}
public AuthMethod GetAuthorization() {
return _auth;
}
public void SetHeaders(Dictionary headers) {
_headers = headers;
sendHeaders();
}
public Dictionary GetHeaders() {
return _headers;
}
protected string GetRandomMessageId(string prefix = "") {
var id = Guid.NewGuid();
return prefix + id;
}
protected void HandleRPCFailure(string id, Exception ex) {
if (!_requests.ContainsKey(id)) {
_logger.Logf(LogLevel.Warning, "Can't handle failure, request with ID {0} was not found in the list of requests.", id);
return;
}
var req = _requests[id];
_requests.Remove(id);
if (req.Timer != null) {
req.Timer.Stop();
req.Timer = null;
}
req.Failure.Invoke(ex);
}
protected void HandleRPCSuccess(WebSocketResponseMessageJson msg) {
if (!_requests.ContainsKey(msg.Ref)) {
if (msg.Ref == _headersUpdateID) {
if (msg.Kind == WebSocketMessageKind.RpcFailure) {
_logger.Logf(LogLevel.Error, "Headers update failed {0}", msg.Data);
} else {
_logger.Logf(LogLevel.Debug, "Headers updated succesfully.");
}
_headersUpdated = true;
return;
}
_logger.Logf(LogLevel.Warning, "Can't handle RPC response, request with ID {0} was not found in the list of requests.", msg.Ref);
return;
}
var req = _requests[msg.Ref];
_requests.Remove(msg.Ref);
if (req.Timer != null) {
req.Timer.Stop();
req.Timer = null;
}
if (msg.Kind == WebSocketMessageKind.RpcFailure) {
req.Failure.Invoke(new Exception(msg.Data));
return;
}
req.Success.Invoke(msg);
}
protected void HandleBuzzer(WebSocketRequestMessageJson msg)
{
var res = new WebSocketResponseMessageJson(WebSocketMessageKind.BuzzerResponse);
res.Ref = msg.ID;
try
{
res.Data = _dispatcher.Dispatch(_context, msg.Service, msg.Method, msg.Data);
} catch (Exception ex)
{
res.Kind = WebSocketMessageKind.BuzzerFailure;
res.Data = ex.Message;
}
_ws.Send(_marshaller.Marshal(res));
}
protected void HandleFailure(WebSocketFailureMessage msg) {
_logger.Logf(LogLevel.Error, "WebSocketTransport: Failure message received from the server:\nCause: {0}\nData:\n{1}", msg.Cause, msg.Data);
}
protected void sendHeaders()
{
_headersUpdated = false;
_headersUpdateID = GetRandomMessageId("headersupdate-");
var msg = new WebSocketRequestMessageJson(WebSocketMessageKind.RpcRequest);
msg.ID = _headersUpdateID;
msg.Headers = new Dictionary();
if (_headers != null) {
foreach (var key in _headers.Keys) {
msg.Headers.Add(key, _headers[key]);
}
}
if (_auth != null) {
msg.Headers.Add("Authorization", _auth.ToValue());
}
var serialized = _marshaller.Marshal(msg);
_logger.Logf(LogLevel.Trace, "WebSocketTransport Updating headers:\n{0}", serialized);
_ws.Send(serialized);
}
public virtual void Send(string service, string method, I payload, ClientTransportCallback callback, C ctx) {
try {
if (!Ready) {
throw new Exception("WebSocketTransport is not ready.");
}
var req = new WebSocketRequestMessageJson(WebSocketMessageKind.RpcRequest);
req.ID = GetRandomMessageId();
req.Service = service;
req.Method = method;
req.Data = _marshaller.Marshal(payload);
var record = new WSRequest();
_requests.Add(req.ID, record);
record.Timer = new Timer();
record.Success = msg => {
O data;
try {
data =_marshaller.Unmarshal(msg.Data);
}
catch (Exception ex) {
callback.Failure(new TransportMarshallingException("Unexpected exception occured during unmarshalling.", ex));
return;
}
callback.Success(data);
};
record.Failure = (ex) => {
callback.Failure(new TransportException("Request failed. ", ex));
};
record.Timer.Interval = DefaultTimeoutInterval * 1000;
record.Timer.Elapsed += (sender, e) => {
HandleRPCFailure(req.ID, new Exception("Request timed out."));
};
var serialized = _marshaller.Marshal(req);
_logger.Logf(LogLevel.Trace, "WebSocketTransport: Outgoing message: \n{0}", serialized);
_ws.SendAsync(serialized, success => {
if (success) return;
HandleRPCFailure(req.ID, new Exception("Sending request failed."));
});
record.Timer.Start();
}
catch (Exception ex)
{
callback.Failure(
new TransportException("Unexpected exception occured during async request.", ex)
);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) {
return;
}
if (disposing) {
if (_ws != null) {
Close();
((IDisposable)_ws).Dispose();
}
}
_disposed = true;
}
public bool RegisterBuzzer(IServiceDispatcher buzzer) {
return _dispatcher.Register(buzzer);
}
public bool UnregisterBuzzer(string id) {
return _dispatcher.Unregister(id);
}
public void SetBuzzerContext(C context) {
_context = context;
}
public C GetBuzzerContext() {
return _context;
}
}
}