public async void GetWebRequest(int connectionID, Uri webUri, bool autoConvertResponse) { // Block all non-internet IP address if (HostnameIsPrivateIPAddress(webUri)) { midiManager.SendWebRequestFailedResponse(connectionID, WEB_REQUEST_FAILED_ERROR_CODE); return; } // Block all non http/https traffic if (webUri.Scheme != Uri.UriSchemeHttp && webUri.Scheme != Uri.UriSchemeHttps) { Console.WriteLine("Error: World attempted to open unsupported URI: " + webUri.Scheme); midiManager.SendWebRequestFailedResponse(connectionID, WEB_REQUEST_FAILED_ERROR_CODE); return; } // Block all rate limited domain+path combos // Temporarily changed to include entire host var hostAndPath = new HostnameAndPath(webUri.Host, ""); if (rateLimitedURIs.ContainsKey(hostAndPath)) { if (DateTime.Now < rateLimitedURIs[hostAndPath]) { Console.WriteLine("ERROR: Could not make web request, currently rate limited."); midiManager.SendWebRequestFailedResponse(connectionID, RATE_LIMITED_ERROR_CODE); return; } else { rateLimitedURIs.Remove(hostAndPath); } } HttpResponseMessage response; try { response = await httpClient.GetAsync(webUri, ctSource.Token); } catch (Exception e) { Console.WriteLine("HTTP request failed: " + e.Message); rateLimitedURIs.Add(hostAndPath, DateTime.Now.AddSeconds(RATE_LIMIT_TIMEOUT_SECONDS)); midiManager.SendWebRequestFailedResponse(connectionID, WEB_REQUEST_FAILED_ERROR_CODE); return; } // Rate limit unsuccessful requests if (!response.IsSuccessStatusCode) { rateLimitedURIs.Add(hostAndPath, DateTime.Now.AddSeconds(RATE_LIMIT_TIMEOUT_SECONDS)); } Console.WriteLine("Received web response (" + connectionID + "): " + webUri.AbsoluteUri); AddWebResponse(response, connectionID, autoConvertResponse); }
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); } }