Exemplo n.º 1
0
        public async Task Connect(string server,
                                  string subProtocol = null)
        {
            LCLogger.Debug($"Connecting WebSocket: {server}");
            Task timeoutTask = Task.Delay(CONNECT_TIMEOUT);

            ws = new ClientWebSocket();
            ws.Options.SetBuffer(RECV_BUFFER_SIZE, SEND_BUFFER_SIZE);
            if (!string.IsNullOrEmpty(subProtocol))
            {
                ws.Options.AddSubProtocol(subProtocol);
            }
            Task connectTask = ws.ConnectAsync(new Uri(server), default);

            if (await Task.WhenAny(connectTask, timeoutTask) == connectTask)
            {
                LCLogger.Debug($"Connected WebSocket: {server}");
                await connectTask;
                // 接收
                _ = StartReceive();
            }
            else
            {
                throw new TimeoutException("Connect timeout");
            }
        }
Exemplo n.º 2
0
        public async Task <object> RPC(string funcName, JsonElement body)
        {
            try {
                LCLogger.Debug($"RPC: {funcName}");
                LCLogger.Debug(body.ToString());

                if (Functions.TryGetValue(funcName, out MethodInfo mi))
                {
                    LCEngine.InitRequestContext(Request);

                    object[] ps     = ParseParameters(mi, body);
                    object   result = await LCEngine.Invoke(mi, ps);

                    if (result != null)
                    {
                        return(new Dictionary <string, object> {
                            { "result", LCCloud.Encode(result) }
                        });
                    }
                }
                return(body);
            } catch (Exception e) {
                return(StatusCode(500, e.Message));
            }
        }
Exemplo n.º 3
0
 private void OnMessage(byte[] bytes, int length)
 {
     try {
         GenericCommand command = GenericCommand.Parser.ParseFrom(bytes, 0, length);
         LCLogger.Debug($"{id} <= {FormatCommand(command)}");
         if (command.HasI)
         {
             // 应答
             int requestIndex = command.I;
             if (requestToResponses.TryGetValue(command, out TaskCompletionSource <GenericCommand> tcs))
             {
                 requestToResponses.Remove(command);
                 if (command.HasErrorMessage)
                 {
                     // 错误
                     ErrorCommand error  = command.ErrorMessage;
                     int          code   = error.Code;
                     string       detail = error.Detail;
                     // 包装成异常抛出
                     LCException exception = new LCException(code, detail);
                     tcs.TrySetException(exception);
                 }
                 else
                 {
                     tcs.TrySetResult(command);
                 }
             }
             else
             {
                 LCLogger.Error($"No request for {requestIndex}");
             }
         }
         else
         {
             if (command.Cmd == CommandType.Echo)
             {
                 // 心跳应答
                 heartBeat.Pong();
             }
             else if (command.Cmd == CommandType.Goaway)
             {
                 // 针对连接的消息
                 Reset();
             }
             else
             {
                 // 通知
                 string peerId = command.HasPeerId ? command.PeerId : defaultClientId;
                 if (idToClients.TryGetValue(peerId, out LCIMClient client))
                 {
                     // 通知具体客户端
                     client.HandleNotification(command);
                 }
             }
         }
     } catch (Exception e) {
         LCLogger.Error(e);
     }
 }
Exemplo n.º 4
0
        public object Get()
        {
            LCLogger.Debug("Ping ~~~");

            return(new Dictionary <string, string> {
                { "runtime", $"dotnet-{Environment.Version}" },
                { "version", LCCore.SDKVersion }
            });
        }
Exemplo n.º 5
0
        private static void OnNotification(Dictionary <string, object> notification)
        {
            if (!notification.TryGetValue("cmd", out object cmd) ||
                !"data".Equals(cmd))
            {
                return;
            }
            if (!notification.TryGetValue("msg", out object msg) ||
                !(msg is IEnumerable <object> list))
            {
                return;
            }

            foreach (object item in list)
            {
                if (item is Dictionary <string, object> dict)
                {
                    if (!dict.TryGetValue("op", out object op))
                    {
                        continue;
                    }
                    switch (op as string)
                    {
                    case "create":
                        OnCreateNotification(dict);
                        break;

                    case "update":
                        OnUpdateNotification(dict);
                        break;

                    case "enter":
                        OnEnterNotification(dict);
                        break;

                    case "leave":
                        OnLeaveNotification(dict);
                        break;

                    case "delete":
                        OnDeleteNotification(dict);
                        break;

                    case "login":
                        OnLoginNotification(dict);
                        break;

                    default:
                        LCLogger.Debug($"Not support: {op}");
                        break;
                    }
                }
            }
        }
Exemplo n.º 6
0
 private void OnClientMessage(byte[] bytes, int length)
 {
     try {
         string json = Encoding.UTF8.GetString(bytes, 0, length);
         Dictionary <string, object> msg = JsonConvert.DeserializeObject <Dictionary <string, object> >(json,
                                                                                                        LCJsonConverter.Default);
         LCLogger.Debug($"{id} <= {json}");
         if (msg.TryGetValue("i", out object i))
         {
             int requestIndex = Convert.ToInt32(i);
             if (responses.TryGetValue(requestIndex, out TaskCompletionSource <Dictionary <string, object> > tcs))
             {
                 if (msg.TryGetValue("error", out object error))
                 {
                     // 错误
                     if (error is Dictionary <string, object> dict)
                     {
                         int    code   = Convert.ToInt32(dict["code"]);
                         string detail = dict["detail"] as string;
                         tcs.SetException(new LCException(code, detail));
                     }
                     else
                     {
                         tcs.SetException(new Exception(error as string));
                     }
                 }
                 else
                 {
                     tcs.SetResult(msg);
                 }
                 responses.Remove(requestIndex);
             }
             else
             {
                 LCLogger.Error($"No request for {requestIndex}");
             }
         }
         else
         {
             if (json == "{}")
             {
                 heartBeat.Pong();
             }
             else
             {
                 // 通知
                 OnNotification?.Invoke(msg);
             }
         }
     } catch (Exception e) {
         LCLogger.Error(e);
     }
 }
Exemplo n.º 7
0
        private async Task StartReceive()
        {
            byte[] recvBuffer = new byte[RECV_BUFFER_SIZE];
            byte[] msgBuffer  = new byte[MSG_BUFFER_SIZE];
            int    offset     = 0;

            try {
                while (ws.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment <byte>(recvBuffer), default);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        LCLogger.Debug($"Receive Closed: {result.CloseStatus}");
                        if (ws.State == WebSocketState.CloseReceived)
                        {
                            // 如果是服务端主动关闭,则挥手关闭,并认为是断线
                            try {
                                Task closeTask = ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default);
                                await Task.WhenAny(closeTask, Task.Delay(CLOSE_TIMEOUT));
                            } catch (Exception e) {
                                LCLogger.Error(e);
                            } finally {
                                HandleExceptionClose();
                            }
                        }
                    }
                    else
                    {
                        // 拼合 WebSocket Message
                        int length = result.Count;
                        if (offset + length > msgBuffer.Length)
                        {
                            // 反序列化数组大小不够,则以 2x 扩充
                            byte[] newBuffer = new byte[msgBuffer.Length * 2];
                            Array.Copy(msgBuffer, newBuffer, msgBuffer.Length);
                            msgBuffer = newBuffer;
                        }
                        Array.Copy(recvBuffer, 0, msgBuffer, offset, length);
                        offset += length;
                        if (result.EndOfMessage)
                        {
                            OnMessage?.Invoke(msgBuffer, offset);
                            offset = 0;
                        }
                    }
                }
            } catch (Exception e) {
                // 客户端网络异常
                LCLogger.Error(e);
                HandleExceptionClose();
            }
        }
Exemplo n.º 8
0
 private async void StartPing()
 {
     while (running)
     {
         try {
             await Task.Delay(PING_INTERVAL, heartBeatCTS.Token);
         } catch (TaskCanceledException) {
             return;
         }
         LCLogger.Debug("Ping ~~~");
         SendPing();
     }
 }
Exemplo n.º 9
0
        public async Task <object> Hook(string className, string hookName, JsonElement body)
        {
            try {
                LCLogger.Debug($"Hook: {className}#{hookName}");
                LCLogger.Debug(body.ToString());

                LCEngine.CheckHookKey(Request);

                string classHookName = GetClassHookName(className, hookName);
                if (ClassHooks.TryGetValue(classHookName, out MethodInfo mi))
                {
                    Dictionary <string, object> data = LCEngine.Decode(body);

                    LCObjectData objectData = LCObjectData.Decode(data["object"] as Dictionary <string, object>);
                    objectData.ClassName = className;
                    LCObject obj = LCObject.Create(className);
                    obj.Merge(objectData);

                    // 避免死循环
                    if (hookName.StartsWith("before"))
                    {
                        obj.DisableBeforeHook();
                    }
                    else
                    {
                        obj.DisableAfterHook();
                    }

                    LCEngine.InitRequestContext(Request);

                    LCUser user = null;
                    if (data.TryGetValue("user", out object userObj) &&
                        userObj != null)
                    {
                        user = new LCUser();
                        user.Merge(LCObjectData.Decode(userObj as Dictionary <string, object>));
                        LCEngineRequestContext.CurrentUser = user;
                    }

                    LCObject result = await LCEngine.Invoke(mi, new object[] { obj }) as LCObject;

                    if (result != null)
                    {
                        return(LCCloud.Encode(result));
                    }
                }
                return(body);
            } catch (Exception e) {
                return(StatusCode(500, e.Message));
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Sends text message.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        internal async Task SendText(string text)
        {
            LCLogger.Debug($"{id} => {text}");
            Task sendTask = client.Send(text);

            if (await Task.WhenAny(sendTask, Task.Delay(SEND_TIMEOUT)) == sendTask)
            {
                await sendTask;
            }
            else
            {
                throw new TimeoutException("Send request time out");
            }
        }
Exemplo n.º 11
0
        internal async Task SendCommand(GenericCommand command)
        {
            LCLogger.Debug($"{id} => {FormatCommand(command)}");
            byte[] bytes    = command.ToByteArray();
            Task   sendTask = ws.Send(bytes);

            if (await Task.WhenAny(sendTask, Task.Delay(SEND_TIMEOUT)) == sendTask)
            {
                await sendTask;
            }
            else
            {
                throw new TimeoutException("Send request");
            }
        }
Exemplo n.º 12
0
        public static void PrintResponse(HttpResponseMessage response, string content = null)
        {
            if (LCLogger.LogDelegate == null)
            {
                return;
            }
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("=== HTTP Response Start ===");
            sb.AppendLine($"URL: {response.RequestMessage.RequestUri}");
            sb.AppendLine($"Status Code: {response.StatusCode}");
            if (!string.IsNullOrEmpty(content))
            {
                sb.AppendLine($"Content: {content}");
            }
            sb.AppendLine("=== HTTP Response End ===");
            LCLogger.Debug(sb.ToString());
        }
Exemplo n.º 13
0
        internal static object GetFunctions(HttpRequest request)
        {
            CheckMasterKey(request);

            List <string> functions = new List <string>();

            functions.AddRange(Functions.Keys);
            functions.AddRange(ClassHooks.Keys);
            functions.AddRange(UserHooks.Keys);
            foreach (string func in functions)
            {
                LCLogger.Debug(func);
            }

            return(new Dictionary <string, List <string> > {
                { "result", functions }
            });
        }
Exemplo n.º 14
0
 public async Task Close()
 {
     LCLogger.Debug("Closing WebSocket");
     OnMessage = null;
     OnClose   = null;
     try {
         // 发送关闭帧可能会很久,所以增加超时
         // 主动挥手关闭,不会再收到 Close Frame
         Task closeTask = ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default);
         Task delayTask = Task.Delay(CLOSE_TIMEOUT);
         await Task.WhenAny(closeTask, delayTask);
     } catch (Exception e) {
         LCLogger.Error(e);
     } finally {
         ws.Abort();
         ws.Dispose();
         LCLogger.Debug("Closed WebSocket");
     }
 }
Exemplo n.º 15
0
        public async Task Connect()
        {
            try {
                LCRTMServer rtmServer = await router.GetServer();

                try {
                    LCLogger.Debug($"Primary Server");
                    await client.Connect(rtmServer.Primary, SUB_PROTOCOL);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    LCLogger.Debug($"Secondary Server");
                    await client.Connect(rtmServer.Secondary, SUB_PROTOCOL);
                }
                // 启动心跳
                heartBeat.Start();
            } catch (Exception e) {
                throw e;
            }
        }
Exemplo n.º 16
0
        public static void PrintRequest(HttpClient client, HttpRequestMessage request, string content = null)
        {
            if (LCLogger.LogDelegate == null)
            {
                return;
            }
            if (client == null)
            {
                return;
            }
            if (request == null)
            {
                return;
            }
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("=== HTTP Request Start ===");
            sb.AppendLine($"URL: {request.RequestUri}");
            sb.AppendLine($"Method: {request.Method}");
            sb.AppendLine($"Headers: ");
            foreach (var header in client.DefaultRequestHeaders)
            {
                sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
            }
            foreach (var header in request.Headers)
            {
                sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
            }
            if (request.Content != null)
            {
                foreach (var header in request.Content.Headers)
                {
                    sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
                }
            }
            if (!string.IsNullOrEmpty(content))
            {
                sb.AppendLine($"Content: {content}");
            }
            sb.AppendLine("=== HTTP Request End ===");
            LCLogger.Debug(sb.ToString());
        }
Exemplo n.º 17
0
        public async Task <object> HookLogin(JsonElement body)
        {
            try {
                LCLogger.Debug(LCEngine.OnLogin);
                LCLogger.Debug(body.ToString());

                LCEngine.CheckHookKey(Request);

                if (UserHooks.TryGetValue(LCEngine.OnLogin, out MethodInfo mi))
                {
                    LCEngine.InitRequestContext(Request);

                    Dictionary <string, object> dict = LCEngine.Decode(body);
                    return(await Invoke(mi, dict));
                }
                return(body);
            } catch (Exception e) {
                return(StatusCode(500, e.Message));
            }
        }
Exemplo n.º 18
0
        internal async Task ConnectInternal()
        {
            state = State.Connecting;
            try {
                LCRTMServer rtmServer = await router.GetServer();

                try {
                    LCLogger.Debug($"Primary Server");
                    await ws.Connect(rtmServer.Primary, SUB_PROTOCOL);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    LCLogger.Debug($"Secondary Server");
                    await ws.Connect(rtmServer.Secondary, SUB_PROTOCOL);
                }
                // 启动心跳
                heartBeat.Start();
                state = State.Open;
            } catch (Exception e) {
                state = State.Closed;
                throw e;
            }
        }
Exemplo n.º 19
0
        private async Task Reconnect()
        {
            while (true)
            {
                int reconnectCount = 0;
                // 重连策略
                while (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    try {
                        LCLogger.Debug($"Reconnecting... {reconnectCount}");
                        await Connect();

                        break;
                    } catch (Exception e) {
                        reconnectCount++;
                        LCLogger.Error(e);
                        LCLogger.Debug($"Reconnect after {RECONNECT_INTERVAL}ms");
                        await Task.Delay(RECONNECT_INTERVAL);
                    }
                }
                if (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    // 重连成功
                    LCLogger.Debug("Reconnected");
                    ws.OnMessage = OnMessage;
                    ws.OnClose   = OnDisconnect;
                    foreach (LCIMClient client in idToClients.Values)
                    {
                        client.HandleReconnected();
                    }
                    break;
                }
                else
                {
                    // 重置 Router,继续尝试重连
                    router = new LCRTMRouter();
                }
            }
        }
Exemplo n.º 20
0
        private async Task Reconnect()
        {
            while (true)
            {
                int reconnectCount = 0;
                // 重连策略
                while (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    try {
                        LCLogger.Debug($"Reconnecting... {reconnectCount}");
                        await Connect();

                        break;
                    } catch (Exception e) {
                        reconnectCount++;
                        LCLogger.Error(e);
                        LCLogger.Debug($"Reconnect after {RECONNECT_INTERVAL}ms");
                        await Task.Delay(RECONNECT_INTERVAL);
                    }
                }
                if (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    // 重连成功
                    LCLogger.Debug("Reconnected");
                    client.OnMessage = OnClientMessage;
                    client.OnClose   = OnClientDisconnect;
                    OnReconnected?.Invoke();
                    break;
                }
                else
                {
                    // 重置 Router,继续尝试重连
                    router = new LCRTMRouter();
                }
            }
        }
Exemplo n.º 21
0
 private static void PrintEnvironmentVar(string key)
 {
     LCLogger.Debug($"{key} : {Environment.GetEnvironmentVariable(key)}");
 }
Exemplo n.º 22
0
        /// <summary>
        /// Initializes the engine with the given services.
        /// </summary>
        /// <param name="services"></param>
        public static void Initialize(IServiceCollection services)
        {
            // 获取环境变量
            LCLogger.Debug("-------------------------------------------------");
            PrintEnvironmentVar("LEANCLOUD_APP_ID");
            PrintEnvironmentVar("LEANCLOUD_APP_KEY");
            PrintEnvironmentVar("LEANCLOUD_APP_MASTER_KEY");
            PrintEnvironmentVar("LEANCLOUD_APP_HOOK_KEY");
            PrintEnvironmentVar("LEANCLOUD_API_SERVER");
            PrintEnvironmentVar("LEANCLOUD_APP_PROD");
            PrintEnvironmentVar("LEANCLOUD_APP_ENV");
            PrintEnvironmentVar("LEANCLOUD_APP_INSTANCE");
            PrintEnvironmentVar("LEANCLOUD_REGION");
            PrintEnvironmentVar("LEANCLOUD_APP_ID");
            PrintEnvironmentVar("LEANCLOUD_APP_DOMAIN");
            PrintEnvironmentVar("LEANCLOUD_APP_PORT");
            LCLogger.Debug("-------------------------------------------------");

            LCApplication.Initialize(Environment.GetEnvironmentVariable("LEANCLOUD_APP_ID"),
                                     Environment.GetEnvironmentVariable("LEANCLOUD_APP_KEY"),
                                     Environment.GetEnvironmentVariable("LEANCLOUD_API_SERVER"),
                                     Environment.GetEnvironmentVariable("LEANCLOUD_APP_MASTER_KEY"));
            LCCore.HttpClient.AddAddtionalHeader(LCHookKeyName, Environment.GetEnvironmentVariable("LEANCLOUD_APP_HOOK_KEY"));

            Assembly assembly = Assembly.GetCallingAssembly();

            ClassHooks = assembly.GetTypes()
                         .SelectMany(t => t.GetMethods())
                         .Where(m => m.GetCustomAttribute <LCEngineClassHookAttribute>() != null)
                         .ToDictionary(mi => {
                LCEngineClassHookAttribute attr = mi.GetCustomAttribute <LCEngineClassHookAttribute>();
                switch (attr.HookType)
                {
                case LCEngineObjectHookType.BeforeSave:
                    return($"{BeforeSave}{attr.ClassName}");

                case LCEngineObjectHookType.AfterSave:
                    return($"{AfterSave}{attr.ClassName}");

                case LCEngineObjectHookType.BeforeUpdate:
                    return($"{BeforeUpdate}{attr.ClassName}");

                case LCEngineObjectHookType.AfterUpdate:
                    return($"{AfterUpdate}{attr.ClassName}");

                case LCEngineObjectHookType.BeforeDelete:
                    return($"{BeforeDelete}{attr.ClassName}");

                case LCEngineObjectHookType.AfterDelete:
                    return($"{AfterDelete}{attr.ClassName}");

                default:
                    throw new Exception($"Error hook type: {attr.HookType}");
                }
            });

            UserHooks = assembly.GetTypes()
                        .SelectMany(t => t.GetMethods())
                        .Where(m => m.GetCustomAttribute <LCEngineUserHookAttribute>() != null)
                        .ToDictionary(mi => {
                LCEngineUserHookAttribute attr = mi.GetCustomAttribute <LCEngineUserHookAttribute>();
                switch (attr.HookType)
                {
                case LCEngineUserHookType.OnSMSVerified:
                    return(OnSMSVerified);

                case LCEngineUserHookType.OnEmailVerified:
                    return(OnEmailVerified);

                case LCEngineUserHookType.OnLogin:
                    return(OnLogin);

                default:
                    throw new Exception($"Error hook type: {attr.HookType}");
                }
            });

            Functions = assembly.GetTypes()
                        .SelectMany(t => t.GetMethods())
                        .Where(m => m.GetCustomAttribute <LCEngineFunctionAttribute>() != null)
                        .ToDictionary(mi => mi.GetCustomAttribute <LCEngineFunctionAttribute>().FunctionName);

            assembly.GetTypes()
            .SelectMany(t => t.GetMethods())
            .Where(m => m.GetCustomAttribute <LCEngineRealtimeHookAttribute>() != null)
            .ToDictionary(mi => {
                LCEngineRealtimeHookAttribute attr = mi.GetCustomAttribute <LCEngineRealtimeHookAttribute>();
                switch (attr.HookType)
                {
                case LCEngineRealtimeHookType.ClientOnline:
                    return(ClientOnline);

                case LCEngineRealtimeHookType.ClientOffline:
                    return(ClientOffline);

                case LCEngineRealtimeHookType.MessageSent:
                    return(MessageSent);

                case LCEngineRealtimeHookType.MessageReceived:
                    return(MessageReceived);

                case LCEngineRealtimeHookType.ReceiversOffline:
                    return(ReceiversOffline);

                case LCEngineRealtimeHookType.MessageUpdate:
                    return(MessageUpdate);

                case LCEngineRealtimeHookType.ConversationStart:
                    return(ConversationStart);

                case LCEngineRealtimeHookType.ConversationStarted:
                    return(ConversationStarted);

                case LCEngineRealtimeHookType.ConversationAdd:
                    return(ConversationAdd);

                case LCEngineRealtimeHookType.ConversationAdded:
                    return(ConversationAdded);

                case LCEngineRealtimeHookType.ConversationRemove:
                    return(ConversationRemove);

                case LCEngineRealtimeHookType.ConversationRemoved:
                    return(ConversationRemoved);

                case LCEngineRealtimeHookType.ConversationUpdate:
                    return(ConversationUpdate);

                default:
                    throw new Exception($"Error hook type: {attr.HookType}");
                }
            })
            .ToList()
            .ForEach(item => {
                Functions.TryAdd(item.Key, item.Value);
            });

            services.AddCors(options => {
                options.AddPolicy(LCEngineCORS, builder => {
                    builder.AllowAnyOrigin()
                    .WithMethods(LCEngineCORSMethods)
                    .WithHeaders(LCEngineCORSHeaders)
                    .SetPreflightMaxAge(TimeSpan.FromSeconds(86400));
                });
            });
        }
Exemplo n.º 23
0
 public void Pong()
 {
     LCLogger.Debug("Pong ~~~");
     // 刷新最近 pong 时间戳
     lastPongTime = DateTimeOffset.Now;
 }