/// <summary> /// Thread that reads messages from each websocket connect /// BIG TODO: process acknowledgements by using some sort of notification queue /// of outbound notifications /// </summary> /// <param name="sender">TODO: this needs to contain something that will identify the websocket /// in order to handle acknowledgements and take the notification off the queue.</param> /// <param name="e"></param> private async void webSocketReader_DoWork(object sender, DoWorkEventArgs e) { Log("Websocket reader starting..."); while (true) { string socketData = null; try { socketData = await ReceiveStringAsync(_ws, CancellationToken.None); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); Log("Exception occurred reading websocket:\r\n" + ex.Message); } if (string.IsNullOrEmpty(socketData)) { if (_ws.State != WebSocketState.Open) { Log("Websocket is closed - the reader is terminating..."); // dispose of the socket and remove the connection ResetWebSocket(); // TODO: reset UI. Other cleanup? break; } continue; } // it's either an acknowledgement or an event notification... // it's either an acknowledgement or an event notification... Notification notification = JsonConvert.DeserializeObject <Notification>(socketData); if (null != notification.Event) { Log($"Event notification received:\r\n{notification.Event}"); // send success response to client WebSocketResponse wsResponse = new WebSocketResponse { Timestamp = DateTime.Now, Status = "OK", StatusCode = 200 }; await SendStringAsync(_ws, wsResponse.ToString()); } else if (null != notification.Status) { Log($"Acknowledgement response received:\r\n{notification.Status} ({notification.StatusCode})"); } else { Log($"Unexpected websocket message received:\r\n{socketData}"); WebSocketResponse wsResponse = new WebSocketResponse { Timestamp = DateTime.Now, Status = "FAIL", StatusCode = 400 }; await SendStringAsync(_ws, wsResponse.ToString()); } } Log("Websocket reader terminated."); }
public async static Task <WebSocketResponse> ProcessRequest(WebSocketRequest request, IMemoryCache memoryCache) { var bl = new CacheBL(memoryCache, request.authorization, request.cacheKey, request.cacheLifespanSeconds); var response = new WebSocketResponse { cacheEntry = null, responseCode = "OK", responseMessage = "" }; switch (request.method) { case "GET": response.cacheEntry = await bl.GetFromDictionary(request.autoPopulateEndpoint); break; case "POST": bl.PostToDictionary(request.values); break; case "DELETE": bl.DeleteFromDictionary(); break; } return(response); }
public WebSocketContext(WebSocketServer webSocketServer, ReqRespHandler<WebSocketRequest, WebSocketResponse> webSocketReqHandler) { this.webSocketReqHandler = webSocketReqHandler; this.webSocketServer = webSocketServer; connectionId = System.Threading.Interlocked.Increment(ref connectionIdTotal); //------------------- //send,resp sockAsyncSender = new SocketAsyncEventArgs(); sockAsyncSender.SetBuffer(new byte[RECV_BUFF_SIZE], 0, RECV_BUFF_SIZE); sendIO = new SendIO(sockAsyncSender, 0, RECV_BUFF_SIZE, sendIO_SendCompleted); sockAsyncSender.Completed += new EventHandler<SocketAsyncEventArgs>((s, e) => { switch (e.LastOperation) { default: { } break; case SocketAsyncOperation.Send: { sendIO.ProcessWaitingData(); } break; case SocketAsyncOperation.Receive: { } break; } }); webSocketResp = new WebSocketResponse(this, sendIO); //------------------------------------------------------------------------------------ //recv,req ,new socket sockAsyncListener = new SocketAsyncEventArgs(); sockAsyncListener.SetBuffer(new byte[RECV_BUFF_SIZE], 0, RECV_BUFF_SIZE); recvIO = new RecvIO(sockAsyncListener, 0, RECV_BUFF_SIZE, HandleReceivedData); sockAsyncListener.Completed += new EventHandler<SocketAsyncEventArgs>((s, e) => { switch (e.LastOperation) { default: { } break; case SocketAsyncOperation.Send: { } break; case SocketAsyncOperation.Receive: { recvIO.ProcessReceivedData(); } break; } }); //------------------------------------------------------------------------------------ this.webSocketReqParser = new WebSocketProtocolParser(recvIO); }
private void WebSocket_OnMessage(object sender, MessageEventArgs e) { if (e.Data.IsNullOrWhiteSpace()) { return; } WebSocketResponse <MarketDiffUpdate> result = JsonConvert.DeserializeObject <WebSocketResponse <MarketDiffUpdate> >(e.Data); this.OnMarketDiffUpdate?.Invoke(this, result.Data); }
protected override void OnNewSessionConnected(SuperWebSocket.WebSocketSession session) { var response = new WebSocketResponse(); response.RemoteHandler = string.Format(@"appendMsg('{0}')","有新用户进入!"); string json = new JavaScriptSerializer().Serialize(response); foreach (var s in Sessions) { s.Send(json); } }
protected override void OnSessionClosed(SuperWebSocket.WebSocketSession session,SuperSocket.SocketBase.CloseReason value) { var response = new WebSocketResponse(); response.RemoteHandler = string.Format(@"appendMsg('{0}')","用户退出!"); string json = new JavaScriptSerializer().Serialize(response); foreach (var s in Sessions) { s.Send(json); } }
static void Main(string[] args) { string key = "--YOURKEYGOESHERE--"; JavaScriptSerializer js = new JavaScriptSerializer(); PushbulletClient Client = new PushbulletClient(key, TimeZoneInfo.Local); DateTime lastChecked = DateTime.Now; using (var ws = new WebSocket(string.Concat("wss://stream.pushbullet.com/websocket/", key))) { ws.OnMessage += (sender, e) => { WebSocketResponse response = js.Deserialize <WebSocketResponse>(e.Data); switch (response.Type) { case "nop": Console.WriteLine(string.Format("Updated {0}", DateTime.Now)); break; case "tickle": Console.WriteLine(string.Format("Tickle recieved on {0}. Go check it out.", DateTime.Now)); PushResponseFilter filter = new PushResponseFilter() { Active = true, ModifiedDate = lastChecked }; var pushes = Client.GetPushes(filter); foreach (var push in pushes.Pushes) { Console.WriteLine(push.Title); } lastChecked = DateTime.Now; break; case "push": Console.WriteLine(string.Format("New push recieved on {0}.", DateTime.Now)); Console.WriteLine("Push Type: {0}", response.Push.Type); Console.WriteLine("Response SubType: {0}", response.Subtype); break; default: Console.WriteLine("new type that is not supported"); break; } }; ws.Connect(); Console.ReadKey(true); } }
public override WebResponse OnPreprocess(Session session, WebSocketRequest request) { if (request.opcode != OpCode.Ping) { return(null); } var pong = new WebSocketResponse(); pong.opcode = OpCode.Pong; pong.content = request.content; return(pong); }
private void Ws_OnMessage(object sender, MessageEventArgs e) { if (this._HesDeadJim) { return; } JavaScriptSerializer js = new JavaScriptSerializer(); WebSocketResponse response = js.Deserialize <WebSocketResponse>(e.Data); this.BeginInvoke(new Action(() => { this.HandleResponseMainThread(response); })); }
private void WebsocketRead(Task <WebSocketReceiveResult> task, WebSocket ws, ArraySegment <byte> buffer, List <byte[]> messageParts) { switch (task.Result.MessageType) { case WebSocketMessageType.Binary: ws.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "Binary messages are not supported", CancellationTokenSource.Token); break; case WebSocketMessageType.Text: if (task.Result.EndOfMessage) { string message; if (messageParts == null) { message = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, task.Result.Count); } else { message = Encoding.UTF8.GetString(messageParts.SelectMany(a => a).Concat(buffer.Array.Skip(buffer.Offset).Take(task.Result.Count)).ToArray()); } WebSocketRequest req = JsonConvert.DeserializeObject <WebSocketRequest>(message); ApiClient client = Clients.ContainsKey(req.ClientId) ? Clients[req.ClientId].Ping() : null; WebSocketResponse res = new WebSocketResponse() { Responses = new string[req.Tasks.Length] }; for (int i = 0; i < req.Tasks.Length; ++i) { res.Responses[i] = Handle(req.Tasks[i].Url, req.Tasks[i].Request, client); } WebsocketSendPart(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(res)), 0, ws, buffer); } else { if (messageParts == null) { messageParts = new List <byte[]>(); } messageParts.Add(buffer.Array.Skip(buffer.Offset).Take(task.Result.Count).ToArray()); ws.ReceiveAsync(buffer, CancellationTokenSource.Token).ContinueWith(tsk => WebsocketRead(tsk, ws, buffer, messageParts)); } break; case WebSocketMessageType.Close: ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closed", CancellationTokenSource.Token); break; } }
protected async Task BroadcastToSelfClients(long userId, WebSocketResponse response) { foreach (var key in currentListeners.Keys.ToList()) { WebsocketListenerData?listener; if (currentListeners.TryGetValue(key, out listener)) { if (listener.userId != userId) { continue; } await listener.sendQueue.SendAsync(response); } } }
private static void AssertResponseIsValid(WebSocketResponse response) { Assert.NotNull(response); Assert.True(response.Payload?.Message == null, response.Payload?.Message); Assert.True(response.Payload?.Errors == null || response.Payload?.Errors?.Count == 0, response.Payload?.Errors?[0].Message); var data = response.Payload?.Data; Assert.NotNull(data); Assert.True(data.Errors == null || data.Errors?.Count == 0, data.Errors?[0].Message); Assert.True(data.AddAction?.Message == null, data.AddAction?.Message); Assert.True(data.EditAction?.Message == null, data.EditAction?.Message); Assert.True(data.SubscribeAdventure?.Error.Message == null, data.SubscribeAdventure?.Error.Message); Assert.True(data.Adventure?.Error?.Message == null, data.Adventure?.Error?.Message); }
public override WebResponse OnPreprocess(Session session, WebSocketRequest request) { if (request.opcode != OpCode.Close) { return(null); } if (session.state != SessionState.Opened) { return(null); } var close = new WebSocketResponse(); close.opcode = OpCode.Close; close.content = request.content; throw new CloseSessionException(close); }
public async Task <GameTokenCode> GetGameTokenCode(string accessToken) { await _ws.ConnectAsync(accessToken); JwtSecurityToken jwt = new JwtSecurityToken(accessToken); await _ws.SendAsync(JsonConvert.SerializeObject(new WebSocketRequest { Id = Guid.NewGuid().ToString(), Method = "getGameAccount", Params = new GetGameAccountParams { MasterId = jwt.Subject, } })); string responseString1 = await _ws.RecieveAsync(); GameAccount gameAccount = JsonConvert .DeserializeObject <WebSocketResponse <GameAccount[]> >(responseString1).Result[0]; await _ws.SendAsync(JsonConvert.SerializeObject(new WebSocketRequest { Id = Guid.NewGuid().ToString(), Method = "createGameTokenCode", Params = new CreateGameTokenCodeParams { AccessToken = accessToken, IgnoreLicenseAcceptance = false, Login = gameAccount.Login, MasterId = jwt.Subject } })); string responseString2 = await _ws.RecieveAsync(); WebSocketResponse <GameTokenCode> response = JsonConvert .DeserializeObject <WebSocketResponse <GameTokenCode> >(responseString2); await _ws.DisconnectAsync(); if (response.Error == null) { return(response.Result); } throw new Exception(response.Error.Code); }
//This is VERY inefficient, like oh my goodness, but until it becomes a problem, this is how it'll be. //It's inefficient because each user does the status lookup and that's entirely unnecessary. protected async Task AlertUserlistUpdate(long contentId) { foreach (var key in currentListeners.Keys.ToList()) { WebsocketListenerData?listener; if (currentListeners.TryGetValue(key, out listener)) { var statuses = await GetUserStatusesAsync(listener.userId, contentId); //Note that the listener could be invalid here, but it's OK because after this, hopefully nothing will be //holding onto it or whatever. var response = new WebSocketResponse() { type = "userlistupdate", data = statuses }; await listener.sendQueue.SendAsync(response); } } }
protected async Task ListenLoop(CancellationToken cancelToken, int lastId, BufferBlock <object> sendQueue, string token) { var userId = ValidateToken(token); UserView user; if (userId == 0) { user = new UserView() { id = userId, super = false }; } else { using (var search = services.dbFactory.CreateSearch()) { user = await search.GetById <UserView>(RequestType.user, userId, true); } } while (!cancelToken.IsCancellationRequested) { //NOTE: the ReceiveObjectAsync throws an exception on close LiveData listenResult; listenResult = await eventQueue.ListenAsync(user, lastId, cancelToken); userId = ValidateToken(token); // Validate every time lastId = listenResult.lastId; var response = new WebSocketResponse() { type = "live", data = listenResult, requestUserId = userId }; //At this point we're re-validated, so do whatever sendQueue.Post(response); } }
public void HandleWebSocket(WebSocketRequest req, WebSocketResponse resp) { if (req.OpCode == Opcode.Text) { string clientMsg = req.ReadAsString(); if (clientMsg == null) { resp.Write(""); return; } string serverMsg = null; if (clientMsg.StartsWith("LOOPBACK")) { serverMsg = "from SERVER " + clientMsg; } else { serverMsg = "server:" + (count++); } resp.Write(serverMsg); #if DEBUG System.Diagnostics.Debug.WriteLine(serverMsg); #endif } else if (req.OpCode == Opcode.Binary) { //this is binary data byte[] binaryData = req.ReadAsBinary(); #if DEBUG count++; string serverMsg = count + " binary_len" + binaryData.Length; System.Diagnostics.Debug.WriteLine(serverMsg); resp.Write(serverMsg); #endif } }
public IActionResult TestSockets() { int requestCount = 1000; WebSocketResponse response = null; Stopwatch watch = new Stopwatch(); IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); IPAddress remoteIPAddress = ipHostInfo.AddressList[0]; int remotePort = GlobalSettings.WebSocketPort; watch.Start(); for (int i = 0; i < requestCount; i++) { response = SynchronousSocketClient.StartClient(new WebSocketRequest { authorization = "Testing", cacheKey = "ComplexData", method = "GET" }, remoteIPAddress, remotePort); } watch.Stop(); return(Ok($"{requestCount} requests done in {watch.ElapsedMilliseconds}ms {JsonSerializer.Serialize(response)}")); }
public async Task <JObject> Handle(WebSocketRequest <JObject> message, CancellationToken cancellationToken) { this.logger.LogJson(message); var response = new WebSocketResponse { Type = WebSocketResponseType.Reply }; if (string.IsNullOrWhiteSpace(message.TypeName)) { return(response.ToJObject()); } var type = TypeExtensions.GetAppDomainType(message.TypeName); if (type.IsMediatorRequest()) { var command = this.mapper.Map(message.Typed ?? new JObject(), typeof(JObject), type); response.Data = await this.mediator.Result(command).NoCapture(); } this.logger.LogJson(response); return(response.ToJObject()); }
public bool ParseNegotiation(WebSocketResponse resp) { // Search for any returned neogitation offer var headerValues = resp.GetHeaderValues("Sec-WebSocket-Extensions"); if (headerValues == null) return false; for (int i = 0; i < headerValues.Count; ++i) { // If found, tokenize it HeaderParser parser = new HeaderParser(headerValues[i]); for (int cv = 0; cv < parser.Values.Count; ++cv) { HeaderValue value = parser.Values[i]; if (!string.IsNullOrEmpty(value.Key) && value.Key.StartsWith("permessage-deflate", StringComparison.OrdinalIgnoreCase)) { HTTPManager.Logger.Information("PerMessageCompression", "Enabled with header: " + headerValues[i]); HeaderValue option; if (value.TryGetOption("client_no_context_takeover", out option)) this.ClientNoContextTakeover = true; if (value.TryGetOption("server_no_context_takeover", out option)) this.ServerNoContextTakeover = true; if (value.TryGetOption("client_max_window_bits", out option)) if (option.HasValue) { int windowBits; if (int.TryParse(option.Value, out windowBits)) this.ClientMaxWindowBits = windowBits; } if (value.TryGetOption("server_max_window_bits", out option)) if (option.HasValue) { int windowBits; if (int.TryParse(option.Value, out windowBits)) this.ServerMaxWindowBits = windowBits; } return true; } } } return false; }
public void HandleWebSocket(WebSocketRequest req, WebSocketResponse resp) { resp.Write("server:" + (count++)); }
private void WebSocket_OnMessage(object sender, MessageEventArgs e) { WebSocketResponse <KLineUpdate> result = JsonConvert.DeserializeObject <WebSocketResponse <KLineUpdate> >(e.Data); this.OnCandleStickUpdate?.Invoke(this, result.Data); }
/// <summary> /// Webservice thread that reads messages from each websocket connect /// BIG TODO: process acknowledgements by using some sort of notification queue /// of outbound notifications. /// </summary> /// <param name="sender">TODO: this needs to contain something that will identify the websocket /// in order to handle acknowledgements and take the notification off the queue.</param> /// <param name="e"></param> public async Task InvokeAsync(HttpContext context, RequestDelegate next) { if (!context.WebSockets.IsWebSocketRequest) { await next(context); } else { string path = context.Request.Path.Value; string[] args = String.IsNullOrEmpty(path) ? null : path.TrimStart('/').Split('/'); CancellationToken ct = context.RequestAborted; WebSocket ws = null; if (null != args && args.Length == 1) { // accept socket request ws = await context.WebSockets.AcceptWebSocketAsync(); string topic = args[0]; this.logger.LogDebug($"Websocket connection requested; topic:{topic}."); // validate topic, and get user name from the database configuration //TODO bool validated = true; if (!validated) { this.logger.LogInformation("Topic not validated. Message rejected."); WebSocketResponse response = new WebSocketResponse { Timestamp = DateTime.Now, Status = "FAIL", StatusCode = 400 }; await SendStringAsync(ws, response.ToString()); } else { this.logger.LogInformation($"Accepted websocket request: Topic {topic}, IP: {context.Connection.RemoteIpAddress.ToString()}:{context.Connection.RemotePort}"); // store this websocket connection in our dictionary this.connections.AddConnection(topic, ws); // send success response to client WebSocketResponse response = new WebSocketResponse { Timestamp = DateTime.Now, Status = "OK", StatusCode = 200 }; await SendStringAsync(ws, response.ToString()); // Loop here until the socket is disconnected - reading and handling // each message sent by the client while (true) { string socketData = null; try { socketData = await ReceiveStringAsync(ws, ct); } catch (Exception ex) { this.logger.LogError($"Exception occurred reading from websocket:\r\n{ex.ToString()}"); } if (string.IsNullOrEmpty(socketData)) { if (ws.State != WebSocketState.Open) { this.logger.LogError($"The websocket connection on port {context.Connection.RemotePort} is closed. Terminating this subscription..."); break; } continue; } // it's either an acknowledgement or an event notification... Notification notification = JsonConvert.DeserializeObject <Notification>(socketData); if (null != notification.Event) { this.logger.LogInformation($"Event notification received:\r\n{notification.Event.ToString()}"); // send success response to client WebSocketResponse wsResponse = new WebSocketResponse { Timestamp = DateTime.Now, Status = "OK", StatusCode = 200 }; await SendStringAsync(ws, wsResponse.ToString()); // Forward notifications to Websocket connected subscribers var subs = this.subscriptions.GetSubscriptions(notification.Event.Topic, notification.Event.HubEvent); foreach (var sub in subs) { await this.notifications.SendNotification(notification, sub); } } else if (null != notification.Status) { this.logger.LogInformation($"Acknowledgement response received:\r\n{notification.Status} ({notification.StatusCode})"); } else { this.logger.LogError($"Unexpected websocket message received:\r\n{response}"); } } await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct); //TODO: remove subscription ws.Dispose(); } } else { await next(context); } } }
internal static void OnUpdate() { IsCallingCallbacks = true; try { for (int i = 0; i < ActiveConnections.Count; i++) { HTTPConnection hTTPConnection = ActiveConnections[i]; switch (hTTPConnection.State) { case HTTPConnectionStates.Processing: if (hTTPConnection.CurrentRequest.UseStreaming && hTTPConnection.CurrentRequest.Response != null && hTTPConnection.CurrentRequest.Response.HasStreamedFragments()) { hTTPConnection.HandleCallback(); } break; case HTTPConnectionStates.Redirected: SendRequest(hTTPConnection.CurrentRequest); RecycleConnection(hTTPConnection); break; case HTTPConnectionStates.WaitForRecycle: hTTPConnection.CurrentRequest.FinishStreaming(); hTTPConnection.HandleCallback(); RecycleConnection(hTTPConnection); break; case HTTPConnectionStates.Upgraded: hTTPConnection.HandleCallback(); break; case HTTPConnectionStates.WaitForProtocolShutdown: { WebSocketResponse webSocketResponse = hTTPConnection.CurrentRequest.Response as WebSocketResponse; webSocketResponse.HandleEvents(); if (webSocketResponse.IsClosed) { hTTPConnection.HandleCallback(); hTTPConnection.Dispose(); RecycleConnection(hTTPConnection); } break; } case HTTPConnectionStates.Closed: hTTPConnection.CurrentRequest.FinishStreaming(); hTTPConnection.HandleCallback(); RecycleConnection(hTTPConnection); Connections[hTTPConnection.ServerAddress].Remove(hTTPConnection); break; case HTTPConnectionStates.Free: if (hTTPConnection.IsRemovable) { hTTPConnection.Dispose(); Connections[hTTPConnection.ServerAddress].Remove(hTTPConnection); } break; } } } finally { IsCallingCallbacks = false; } if (RecycledConnections.Count > 0) { for (int j = 0; j < RecycledConnections.Count; j++) { if (RecycledConnections[j].IsFree) { ActiveConnections.Remove(RecycledConnections[j]); } } RecycledConnections.Clear(); } if (RequestQueue.Count > 0) { HTTPRequest[] array = RequestQueue.ToArray(); RequestQueue.Clear(); for (int k = 0; k < array.Length; k++) { SendRequest(array[k]); } } }
protected async Task ReceiveLoop(CancellationToken cancelToken, WebSocket socket, BufferBlock <object> sendQueue, string token) { using var memStream = new MemoryStream(); while (!cancelToken.IsCancellationRequested) { //NOTE: the ReceiveObjectAsync throws an exception on close var receiveItem = await socket.ReceiveObjectAsync <WebSocketRequest>(memStream, cancelToken); var userId = ValidateToken(token); // Validate every time services.logger.LogDebug($"WS request '{receiveItem.id}'({receiveItem.type}) from {userId}"); var response = new WebSocketResponse() { id = receiveItem.id, type = receiveItem.type, requestUserId = userId }; if (receiveItem.type == "ping") { response.data = new { serverTime = DateTime.UtcNow }; } else if (receiveItem.type == "selfbroadcast") { response.data = receiveItem.data; await BroadcastToSelfClients(userId, response); //This skips the sending of the response, because in a broadcast, you'll receive it anyway continue; } else if (receiveItem.type == "userlist") { response.data = await GetUserStatusesAsync(userId); } else if (receiveItem.type == "setuserstatus") { try { if (receiveItem.data == null) { throw new RequestException("Must set data to a dictionary of contentId:status"); } var statuses = (((JObject)receiveItem.data).ToObject <Dictionary <string, string> >() ?? throw new RequestException("Couldn't parse sent userlist!")) .ToDictionary(x => long.Parse(x.Key), y => y.Value);//(Dictionary<long, string>)receiveItem.data; //TODO: this will need to do some magic to send the userlist to everyone. I suppose if I //had a list of all waiters and their send queues.... hmmmm that would actually just work. foreach (var status in statuses) { await AddUserStatusAsync(userId, status.Key, status.Value); } } catch (Exception ex) { if (!(ex is RequestException)) { services.logger.LogWarning($"Error when user {userId} set statuses to {receiveItem.data}: {ex}"); } response.error = $"Error while setting statuses: {ex.Message}"; } } else if (receiveItem.type == "request") { try { if (receiveItem.data == null) { throw new RequestException("Must provide search criteria for request!"); } var searchRequest = ((JObject)receiveItem.data).ToObject <SearchRequests>() ?? throw new RequestException("Couldn't parse search criteria!"); using (var search = services.dbFactory.CreateSearch()) { var searchResult = await search.Search(searchRequest, userId); response.data = searchResult; } } catch (Exception ex) { response.error = $"Error during search: {ex.Message}"; } } else if (receiveItem.type == "write") { try { if (receiveItem.data == null) { throw new RequestException("Must provide write item for request!"); } var writeData = ((JObject)receiveItem.data).ToObject <WebsocketWriteData>() ?? throw new RequestException("Couldn't parse write data! Must provide type, etc"); var writeObject = writeData.@object ?? throw new RequestException("Couldn't parse 'object' before type is known!"); //((JObject)writeData.@object); var type = writeData.type; //This sucks. Wonder if I can make it better if (type == nameof(RequestType.message)) { response.data = await WriteAsync(writeObject.ToObject <MessageView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.content)) { response.data = await WriteAsync(writeObject.ToObject <ContentView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.user)) { response.data = await WriteAsync(writeObject.ToObject <UserView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.uservariable)) { response.data = await WriteAsync(writeObject.ToObject <UserVariableView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.watch)) { response.data = await WriteAsync(writeObject.ToObject <WatchView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.vote)) { response.data = await WriteAsync(writeObject.ToObject <VoteView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else if (type == nameof(RequestType.ban)) { response.data = await WriteAsync(writeObject.ToObject <BanView>() ?? throw new RequestException($"Couldn't parse {type}!"), userId, writeData.activityMessage); } else { throw new RequestException($"Unknown write type {type}"); } } catch (Exception ex) { response.error = $"{ex.GetType().Name}: {ex.Message}"; } } else { response.error = $"Unknown request type {receiveItem.type}"; } sendQueue.Post(response); } }
public async Task <ActionResult <string> > WebSocketListenAsync([FromQuery] string token, [FromQuery] int?lastId = null) { try { services.logger.LogDebug($"ws METHOD: {HttpContext.Request.Method}, HEADERS: " + JsonConvert.SerializeObject(HttpContext.Request.Headers, Formatting.None, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })); if (!HttpContext.WebSockets.IsWebSocketRequest) { return(BadRequest("You must send a websocket request to this endpoint!")); } //None of these will throw exceptions that we can match anyway, so they'll bubble up accordingly... //well that might not be entirely true if we're returning 500 errors specifically but... change it if you need. using var cancelSource = new CancellationTokenSource(); using var dualCancel = CancellationTokenSource.CreateLinkedTokenSource(cancelSource.Token, appLifetime.ApplicationStopping, appLifetime.ApplicationStopped); var sendQueue = new BufferBlock <object>(); List <Task> runningTasks = new List <Task>(); long userId = 0; int realLastId = lastId == null?eventQueue.GetCurrentLastId() : lastId.Value; using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync(acceptContextGenerator ()); try { //ALWAYS add the sendloop first so we can process outgoing messages runningTasks.Add(SendLoop(dualCancel.Token, socket, sendQueue)); //You want to keep this validation token thing inside the main exception handler, as ANY of the //below tasks could throw the token validation exception! userId = ValidateToken(token); services.logger.LogInformation($"Websocket started for user {userId}"); //ALWAYS send the lastId message, it's basically our "this is the websocket and you're connected" var response = new WebSocketResponse() { type = "lastId", data = realLastId, requestUserId = userId }; sendQueue.Post(response); if (!currentListeners.TryAdd(trackerId, new WebsocketListenerData() { userId = userId, sendQueue = sendQueue })) { throw new InvalidOperationException("INTERNAL ERROR: couldn't add you to the listener array!"); } //Can send and receive at the same time, but CAN'T send/receive multiple at the same time. runningTasks.Add(ReceiveLoop(dualCancel.Token, socket, sendQueue, token)); runningTasks.Add(ListenLoop(dualCancel.Token, realLastId, sendQueue, token)); var completedTask = await Task.WhenAny(runningTasks); runningTasks.Remove(completedTask); await completedTask; //To throw the exception, if there is one } catch (OperationCanceledException ex) { services.logger.LogDebug($"Websocket was cancelled by system, we're probably shutting down: {ex.Message}"); // ALL should output an operation cancel exception but // I'm ASSUMING that this will specifically not exit until they're ALL done... try { await Task.WhenAll(runningTasks); } catch (OperationCanceledException) { } //Fine catch (Exception exi) { services.logger.LogError($"CRITICAL: EXCEPTION THROWN FROM CANCELED WEBSOCKET WAS UNEXPECTED TYPE: {exi}"); } finally { runningTasks.Clear(); } } catch (TokenException ex) { //Note: it is OK to use sendQueue even if the sender loop isn't started, because we dump the //remaining queue anyway in the finalizer services.logger.LogError($"Token exception in websocket: {ex}"); sendQueue.Post(new WebSocketResponse() { type = "badtoken", error = ex.Message }); } catch (data.ClosedException ex) { services.logger.LogDebug($"User {userId} closed websocket on their end, this is normal: {ex.Message}"); } //ALl other unhandled exceptions catch (Exception ex) { services.logger.LogError("Unhandled exception in websocket: " + ex.ToString()); sendQueue.Post(new WebSocketResponse() { type = "unexpected", error = $"Unhandled exception: {ex}" }); } finally { if (runningTasks.Count > 0) { //Cause the cancel source to close naturally after 2 seconds, giving us enough time to send //out remaining messages, but also allowing us to close immediately if everything was already completed //(because we wait on the tasks themselves, which could complete earlier than the cancel) cancelSource.CancelAfter(2000); try { await Task.WhenAll(runningTasks); } catch (ClosedException ex) { services.logger.LogDebug($"Client closed connection manually, this is normal!: {ex.Message}"); } catch (OperationCanceledException ex) { services.logger.LogDebug($"Websocket task cancelled, this is normal: {ex.Message}"); } catch (Exception ex) { services.logger.LogError($"WEBSOCKET CRITICAL: UNHANDLED EXCEPTION DURING CANCEL: {ex}"); } } if (currentListeners.ContainsKey(trackerId) && !currentListeners.TryRemove(trackerId, out _)) { services.logger.LogDebug($"Couldn't remove listener {trackerId}, this could be a serious error!"); } //This won't catch errors in every case but do it anyway if (socket.State == WebSocketState.Open) { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Force closing due to end of task", dualCancel.Token); } } //Just return an empty result if we get all the way to the end. This shouldn't happen but... return(new EmptyResult()); } finally { //This is SO IMPORTANT that I want to do it way out here! await RemoveStatusesByTrackerAsync(); } }
public static void OnUpdate() { object locker = HTTPManager.Locker; lock (locker) { HTTPManager.IsCallingCallbacks = true; try { for (int i = 0; i < HTTPManager.ActiveConnections.Count; i++) { HTTPConnection hTTPConnection = HTTPManager.ActiveConnections[i]; switch (hTTPConnection.State) { case HTTPConnectionStates.Processing: hTTPConnection.HandleProgressCallback(); if (hTTPConnection.CurrentRequest.UseStreaming && hTTPConnection.CurrentRequest.Response != null && hTTPConnection.CurrentRequest.Response.HasStreamedFragments()) { hTTPConnection.HandleCallback(); } if (((!hTTPConnection.CurrentRequest.UseStreaming && hTTPConnection.CurrentRequest.UploadStream == null) || hTTPConnection.CurrentRequest.EnableTimoutForStreaming) && DateTime.UtcNow - hTTPConnection.StartTime > hTTPConnection.CurrentRequest.Timeout) { hTTPConnection.Abort(HTTPConnectionStates.TimedOut); } break; case HTTPConnectionStates.Redirected: HTTPManager.SendRequest(hTTPConnection.CurrentRequest); HTTPManager.RecycleConnection(hTTPConnection); break; case HTTPConnectionStates.Upgraded: hTTPConnection.HandleCallback(); break; case HTTPConnectionStates.WaitForProtocolShutdown: { WebSocketResponse webSocketResponse = hTTPConnection.CurrentRequest.Response as WebSocketResponse; if (webSocketResponse != null) { webSocketResponse.HandleEvents(); } if (webSocketResponse == null || webSocketResponse.IsClosed) { hTTPConnection.HandleCallback(); hTTPConnection.Dispose(); HTTPManager.RecycleConnection(hTTPConnection); } break; } case HTTPConnectionStates.WaitForRecycle: hTTPConnection.CurrentRequest.FinishStreaming(); hTTPConnection.HandleCallback(); HTTPManager.RecycleConnection(hTTPConnection); break; case HTTPConnectionStates.AbortRequested: { WebSocketResponse webSocketResponse = hTTPConnection.CurrentRequest.Response as WebSocketResponse; if (webSocketResponse != null) { webSocketResponse.HandleEvents(); if (webSocketResponse.IsClosed) { hTTPConnection.HandleCallback(); hTTPConnection.Dispose(); HTTPManager.RecycleConnection(hTTPConnection); } } break; } case HTTPConnectionStates.TimedOut: if (DateTime.UtcNow - hTTPConnection.TimedOutStart > TimeSpan.FromMilliseconds(500.0)) { HTTPManager.Logger.Information("HTTPManager", "Hard aborting connection becouse of a long waiting TimedOut state"); hTTPConnection.CurrentRequest.Response = null; hTTPConnection.CurrentRequest.State = HTTPRequestStates.TimedOut; hTTPConnection.HandleCallback(); HTTPManager.RecycleConnection(hTTPConnection); } break; case HTTPConnectionStates.Closed: hTTPConnection.CurrentRequest.FinishStreaming(); hTTPConnection.HandleCallback(); HTTPManager.RecycleConnection(hTTPConnection); break; } } } finally { HTTPManager.IsCallingCallbacks = false; } if (HTTPManager.RecycledConnections.Count > 0) { for (int j = 0; j < HTTPManager.RecycledConnections.Count; j++) { HTTPConnection hTTPConnection2 = HTTPManager.RecycledConnections[j]; if (hTTPConnection2.IsFree) { HTTPManager.ActiveConnections.Remove(hTTPConnection2); HTTPManager.FreeConnections.Add(hTTPConnection2); } } HTTPManager.RecycledConnections.Clear(); } if (HTTPManager.FreeConnections.Count > 0) { for (int k = 0; k < HTTPManager.FreeConnections.Count; k++) { HTTPConnection hTTPConnection3 = HTTPManager.FreeConnections[k]; if (hTTPConnection3.IsRemovable) { List <HTTPConnection> list = null; if (HTTPManager.Connections.TryGetValue(hTTPConnection3.ServerAddress, out list)) { list.Remove(hTTPConnection3); } hTTPConnection3.Dispose(); HTTPManager.FreeConnections.RemoveAt(k); k--; } } } if (HTTPManager.CanProcessFromQueue()) { if (HTTPManager.RequestQueue.Find((HTTPRequest req) => req.Priority != 0) != null) { HTTPManager.RequestQueue.Sort((HTTPRequest req1, HTTPRequest req2) => req1.Priority - req2.Priority); } HTTPRequest[] array = HTTPManager.RequestQueue.ToArray(); HTTPManager.RequestQueue.Clear(); for (int l = 0; l < array.Length; l++) { HTTPManager.SendRequest(array[l]); } } } if (HTTPManager.heartbeats != null) { HTTPManager.heartbeats.Update(); } }
public bool ParseNegotiation(WebSocketResponse resp) { // Search for any returned neogitation offer var headerValues = resp.GetHeaderValues("Sec-WebSocket-Extensions"); if (headerValues == null) { return(false); } for (int i = 0; i < headerValues.Count; ++i) { // If found, tokenize it HeaderParser parser = new HeaderParser(headerValues[i]); for (int cv = 0; cv < parser.Values.Count; ++cv) { HeaderValue value = parser.Values[i]; if (!string.IsNullOrEmpty(value.Key) && value.Key.StartsWith("permessage-deflate", StringComparison.OrdinalIgnoreCase)) { HTTPManager.Logger.Information("PerMessageCompression", "Enabled with header: " + headerValues[i]); HeaderValue option; if (value.TryGetOption("client_no_context_takeover", out option)) { this.ClientNoContextTakeover = true; } if (value.TryGetOption("server_no_context_takeover", out option)) { this.ServerNoContextTakeover = true; } if (value.TryGetOption("client_max_window_bits", out option)) { if (option.HasValue) { int windowBits; if (int.TryParse(option.Value, out windowBits)) { this.ClientMaxWindowBits = windowBits; } } } if (value.TryGetOption("server_max_window_bits", out option)) { if (option.HasValue) { int windowBits; if (int.TryParse(option.Value, out windowBits)) { this.ServerMaxWindowBits = windowBits; } } } return(true); } } } return(false); }
private static void Websocket_OnWebsocketUpdated(WebSocketResponse response, string raw) { }
public WebSocketResponse Analyze(WebSocketRequest request,WebSocketResponse response) { response.Data = request.Body; return response; }
public void HandleResponseMainThread(WebSocketResponse response) { switch (response.Type) { // Heartbeat case "nop": Logger.WriteLine("PushBullet Heartbeat"); if (!this.hasHiddenOnStartup) { this.hasHiddenOnStartup = true; this.Hide(); Logger.WriteLine("Saving access token to: " + Path.Combine(TextSettings.Folder, fileName)); TextSettings.Save(fileName, this.Client.AccessToken); } break; case "tickle": PushResponseFilter filter = new PushResponseFilter() { Active = true, ModifiedDate = lastChecked }; var pushes = Client.GetPushes(filter); foreach (var push in pushes.Pushes) { if ((push.Created - lastChecked).TotalDays > 0) { lastChecked = push.Created; using (Logger.Time("Processing Request")) { for (int i = 0; i < 3; i++) // 3 attempts. { using (Logger.Time("Attempt " + i)) { try { this._RequestHandler.NewNotification(push.Title, push.Body); break; } catch (Exception exc) { Logger.WriteException(this, "FindBestMatchAndPlay", exc); System.Threading.Thread.Sleep(500); // Give itunes a breath. } } } } } else { Logger.WriteLine("Ignoring Old PushBullet: " + push.Title); } } break; case "push": Logger.WriteLine(string.Format("New push recieved on {0}.", DateTime.Now)); Logger.WriteLine("Push Type: " + response.Push.Type); Logger.WriteLine("Response SubType: " + response.Subtype); break; default: Logger.WriteLine("PushBullet type not supported!"); break; } }