package main import ( "fmt" "net" "os" "strings" "sync" "strconv" ) // WARNING: This assumes that the proxy have not received any prior // requests for the app "five-thousand" // Listening on port 5300 is the app "five-thousand" which should route // to two servers: (1) tcp-echo.fly.dev:5001 and (2) tcp-echo.fly.dev:5002 // (1) should return the data in uppercase // (2) should return the data as is // this function asserts that responses from connections to the app are // different, so the proxy is indeed alternating the targets. func test_alternating_targets() { message := "all lowercase message\n" response := connect_to("localhost:5300", message) second_response := connect_to("localhost:5300", message) uppercase_message := strings.ToUpper(message) // The proxy tries the targets in order. If it is the first connection // the first target will be used, and thus the message should be returned // as uppercase // for the second connection, the second target is used and the message // should be returned as is if response != uppercase_message { panic("Alternating targets failed") } if second_response != message { panic("Alternating targets failed") } } // App "six-thousand" has three targets. The first two are similar to // app "five-thousand", and the last one is a bad target that never works. // This function shows that all requests are still correctly made, even // though a server is down. func test_bad_targets_are_ignored() { message := "some message\n" uppercase_message := strings.ToUpper(message) for requests := 0; requests < 10; requests++ { response := connect_to("localhost:6001", message) // Depending on the target, it's possible that the returned response // is either the originar version or its uppercase version // Both are fine for our purpose here, we just want to assert // that the requests are going through if response != message && response != uppercase_message { panic("Bad targets are ignored failed") } } } // App "seven-thousand" has four ports and two targets. // This test shows that // 1 - All ports work (we are successfully connecting through all four ports) // 2 - The connections are being processed concurrently // ^^^^ Actually this is NOT TRUE! // The users could be being processed serially by the proxy, and only // concurrently through the goroutines. How to test that the proxy is concurrent? // Manually we can see it's the case using telnet and such, but how to automate that? func attempt_to_test_concurrent_users() { var wg sync.WaitGroup ip_with_port := []string { "localhost:7001", "localhost:7200", "localhost:7300", "localhost:7400"} for simulated_user := 0; simulated_user < 3; simulated_user++ { for _, ip := range ip_with_port { wg.Add(1) go func (user string, ip string) { defer wg.Done() connect_to(ip, "testing message\n") print("Completed a request from user: ", user, "\n") }(strconv.Itoa(simulated_user), ip) } } wg.Wait() } // Returns the response from the server func connect_to(ip string, message string) string { connection, err := net.Dial("tcp", ip) if err != nil { fmt.Println("something wrong") os.Exit(1) } connection.Write([]byte(message)) response_buffer := make([]byte, 1024) bytes_read, err := connection.Read(response_buffer) if err != nil { println("something wrong") os.Exit(1) } return string(response_buffer[:bytes_read]) } func main() { test_alternating_targets() test_bad_targets_are_ignored() // See the comment on this function, I don't think it works as a test attempt_to_test_concurrent_users() }