void WebSocketOpen(string[] args) { // new websocket connection with conntionID, uri, and optional UTF16 arguments try { int connectionID = Int32.Parse(args[2]); string uriDecoded = Encoding.Unicode.GetString(Convert.FromBase64String(args[3])); Uri webUri; try { webUri = new Uri(uriDecoded); } catch (UriFormatException e) { Console.WriteLine("URI incorrectly formatted: " + e.Message); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); return; } bool autoConvertResponse = false; if (args.Length > 4) { autoConvertResponse = args[4] == "UTF16"; } Console.WriteLine("Opening websocket (" + connectionID + "): " + uriDecoded); webManager.OpenWebSocketConnection(connectionID, webUri, autoConvertResponse); } catch (Exception e) { Console.WriteLine("Error parsing web request: " + e.Message); } }
public async void OpenWebSocketConnection(int connectionID, Uri webUri, bool autoConvertResponses) { // Block all non-internet IP address if (HostnameIsPrivateIPAddress(webUri)) { Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); return; } if (webUri.Scheme != "ws" && webUri.Scheme != "wss") { Console.WriteLine("Error: World attempted to open unsupported URI: " + webUri.Scheme); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); return; } // Block all rate limited domain+path combos var hostAndPath = new HostnameAndPath(webUri.Host, ""); if (rateLimitedURIs.ContainsKey(hostAndPath)) { if (DateTime.Now < rateLimitedURIs[hostAndPath]) { Console.WriteLine("ERROR: Could not open websocket connection, currently rate limited."); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); return; } else { rateLimitedURIs.Remove(hostAndPath); } } webSockets[connectionID] = new ClientWebSocket(); ClientWebSocket cws = webSockets[connectionID]; try { await cws.ConnectAsync(webUri, ctSource.Token); } catch (WebSocketException e) { rateLimitedURIs.Add(hostAndPath, DateTime.Now.AddSeconds(RATE_LIMIT_TIMEOUT_SECONDS)); Console.WriteLine("Failed to open websocket: " + e.Message); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); webSockets[connectionID] = null; return; } catch (OperationCanceledException) { return; } while (cws.State == WebSocketState.Connecting) { ; } if (cws.State == WebSocketState.Open) { midiManager.SendWebSocketOpenedResponse(connectionID); } wsBuffers[connectionID] = new ArraySegment <byte>(new byte[WEBSOCKET_BUFFER_SIZE]); wsAutoConvertMessages[connectionID] = autoConvertResponses; while (cws.State == WebSocketState.Open) { WebSocketReceiveResult wssr = null; try { wssr = await cws.ReceiveAsync(wsBuffers[connectionID], ctSource.Token); } catch (WebSocketException e) { // Aborted state. To or to not rate limit, because twitch likes to // abort any connection when a nickname is already taken... Console.WriteLine("WebSocketException: " + e.Message); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); webSockets[connectionID] = null; break; } catch (OperationCanceledException) { break; } catch (Exception e) { Console.WriteLine("Exception: " + e.Message); Console.WriteLine("Closing websocket connection " + connectionID); midiManager.SendWebSocketClosedResponse(connectionID); webSockets[connectionID] = null; break; } // Copy data to an intermediate array in case it needs to be converted from UTF8 to UTF16 byte[] message = new byte[wssr.Count]; Array.Copy(wsBuffers[connectionID].Array, 0, message, 0, wssr.Count); Console.WriteLine("Received websocket message: " + Encoding.UTF8.GetString(message)); if (wsAutoConvertMessages[connectionID] && (wssr.MessageType == WebSocketMessageType.Text)) { message = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, message); } // Send 4 bytes for response length, 1 bytes for txt/bin flag, and then response data byte[] responseData = new byte[4 + 1 + message.Length]; Array.Copy(BitConverter.GetBytes(message.Length + 1), 0, responseData, 0, 4); responseData[4] = wssr.MessageType == WebSocketMessageType.Text ? (byte)0x0 :(byte)0x1; Array.Copy(message, 0, responseData, 5, message.Length); midiManager.AddConnectionResponse((byte)connectionID, responseData); } }