package testing import ( "git.openprivacy.ca/cwtch.im/tapir" "git.openprivacy.ca/cwtch.im/tapir/applications" "git.openprivacy.ca/cwtch.im/tapir/networks/tor" "git.openprivacy.ca/cwtch.im/tapir/primitives" "git.openprivacy.ca/openprivacy/connectivity" torProvider "git.openprivacy.ca/openprivacy/connectivity/tor" "git.openprivacy.ca/openprivacy/log" "golang.org/x/crypto/ed25519" "io/ioutil" "os" "runtime" "runtime/pprof" "sync" "testing" "time" ) // SimpleApp is a trivial implementation of a basic p2p application type SimpleApp struct { applications.AuthApp } // NewInstance should always return a new instantiation of the application. func (ea *SimpleApp) NewInstance() tapir.Application { return new(SimpleApp) } // Init is run when the connection is first started. func (ea *SimpleApp) Init(connection tapir.Connection) { // First run the Authentication App ea.AuthApp.Init(connection) if connection.HasCapability(applications.AuthCapability) { // The code for out simple application (We just send and receive "Hello" connection.Send([]byte("Hello")) message := connection.Expect() log.Infof("Received: %q", message) } } var AuthSuccess = false // CheckConnection is a simple test that GetConnection is working. func CheckConnection(service tapir.Service, hostname string, group *sync.WaitGroup) { for { _, err := service.GetConnection(hostname) if err == nil { log.Infof("Authed!") group.Done() return } log.Infof("Waiting for Authentication...%v", err) time.Sleep(time.Second * 5) } } func TestTapir(t *testing.T) { numRoutinesStart := runtime.NumGoroutine() log.SetLevel(log.LevelDebug) log.Infof("Number of goroutines open at start: %d", runtime.NumGoroutine()) // Connect to Tor os.MkdirAll("./tor/", 0700) builder := new(torProvider.TorrcBuilder) builder.WithSocksPort(9059).WithControlPort(9060).WithHashedPassword("tapir-integration-test").Build("./tor/torrc") torDataDir := "" var err error if torDataDir, err = ioutil.TempDir("./tor/", "data-dir-"); err != nil { t.Fatalf("could not create data dir") } // Connect to Tor acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9060, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"}) if err != nil { t.Fatalf("could not launch ACN %v", err) } acn.WaitTillBootstrapped() // Generate Server Keys id, sk := primitives.InitializeEphemeralIdentity() // Init the Server running the Simple App. service := new(tor.BaseOnionService) service.Init(acn, sk, &id) // Goroutine Management sg := new(sync.WaitGroup) sg.Add(1) go func() { service.Listen(new(SimpleApp)) sg.Done() }() // Wait for server to come online time.Sleep(time.Second * 30) wg := new(sync.WaitGroup) wg.Add(2) // Init a Client to Connect to the Server client, clienthostname := genclient(acn) go connectclient(t, client, id.PublicKey(), wg) CheckConnection(service, clienthostname, wg) wg.Wait() // Wait for Garbage Collection... time.Sleep(time.Second * 60) log.Infof("Closing ACN...") client.Shutdown() service.Shutdown() acn.Close() sg.Wait() time.Sleep(time.Second * 5) log.Infof("Number of goroutines open at close: %d", runtime.NumGoroutine()) if numRoutinesStart != runtime.NumGoroutine() { pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) t.Errorf("Potential goroutine leak: Num Start:%v NumEnd: %v", numRoutinesStart, runtime.NumGoroutine()) } if !AuthSuccess { t.Fatalf("Integration Test FAILED, client did not auth with server") } } func genclient(acn connectivity.ACN) (tapir.Service, string) { id, sk := primitives.InitializeEphemeralIdentity() client := new(tor.BaseOnionService) client.Init(acn, sk, &id) return client, id.Hostname() } // Client will Connect and launch it's own Echo App goroutine. func connectclient(t *testing.T, client tapir.Service, key ed25519.PublicKey, group *sync.WaitGroup) { client.Connect(torProvider.GetTorV3Hostname(key), new(SimpleApp)) // Once connected, it shouldn't take long to authenticate and run the application. So for the purposes of this demo // we will wait a little while then exit. time.Sleep(time.Second * 5) conn, _ := client.GetConnection(torProvider.GetTorV3Hostname(key)) log.Debugf("Client has Auth: %v", conn.HasCapability(applications.AuthCapability)) // attempt to send a message that is too long var long [8195]byte err := conn.Send(long[:]) if err == nil { t.Errorf("should have errored on message being too long...") } AuthSuccess = true group.Done() }