Tapir provides a framework for building Anonymous / metadata resistant Services
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.4 KiB

package testing
import (
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
// SimpleApp is a trivial implementation of a basic p2p application
type SimpleApp struct {
// 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
if connection.HasCapability(applications.AuthCapability) {
// The code for out simple application (We just send and receive "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("Waiting for Authentication...%v", err)
time.Sleep(time.Second * 5)
func TestTapir(t *testing.T) {
numRoutinesStart := runtime.NumGoroutine()
log.Infof("Number of goroutines open at start: %d", runtime.NumGoroutine())
// Connect to Tor
os.MkdirAll("./tor/", 0700)
builder := new(torProvider.TorrcBuilder)
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)
// 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)
go func() {
// Wait for server to come online
time.Sleep(time.Second * 30)
wg := new(sync.WaitGroup)
// Init a Client to Connect to the Server
client, clienthostname := genclient(acn)
go connectclient(t, client, id.PublicKey(), wg)
CheckConnection(service, clienthostname, wg)
// Wait for Garbage Collection...
time.Sleep(time.Second * 60)
log.Infof("Closing ACN...")
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